diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 6778ec7e0de..7b9b54a19bb 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -28,6 +28,7 @@ go.sum @ajm188 @deepthi @harshit-gangal @mattlord @rohit-nayak-ps @systay @froui /go/test/endtoend/onlineddl @rohit-nayak-ps @shlomi-noach /go/test/endtoend/messaging @mattlord @rohit-nayak-ps @derekperkins /go/test/endtoend/schemadiff @shlomi-noach @mattlord +/go/test/endtoend/transaction @harshit-gangal @systay @frouioui @GuptaManan100 /go/test/endtoend/*throttler* @shlomi-noach @mattlord @timvaillancourt /go/test/endtoend/vtgate @harshit-gangal @systay @frouioui /go/test/endtoend/vtorc @deepthi @shlomi-noach @GuptaManan100 @timvaillancourt diff --git a/.github/workflows/assign_milestone.yml b/.github/workflows/assign_milestone.yml index 3ae0a89b4a9..6687a239b57 100644 --- a/.github/workflows/assign_milestone.yml +++ b/.github/workflows/assign_milestone.yml @@ -12,7 +12,7 @@ env: jobs: build: name: Assign Milestone - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 permissions: pull-requests: write diff --git a/.github/workflows/auto_approve_pr.yml b/.github/workflows/auto_approve_pr.yml index de553694927..e584337f78b 100644 --- a/.github/workflows/auto_approve_pr.yml +++ b/.github/workflows/auto_approve_pr.yml @@ -9,7 +9,7 @@ permissions: jobs: auto_approve: name: Auto Approve Pull Request - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 permissions: pull-requests: write # only given on local PRs, forks run with `read` access diff --git a/.github/workflows/check_label.yml b/.github/workflows/check_label.yml index 15b3ae3ec15..2c6439a29f9 100644 --- a/.github/workflows/check_label.yml +++ b/.github/workflows/check_label.yml @@ -9,7 +9,7 @@ jobs: check_pull_request_labels: name: Check Pull Request labels timeout-minutes: 10 - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 if: github.repository == 'vitessio/vitess' steps: - name: Release Notes label diff --git a/.github/workflows/check_make_vtadmin_authz_testgen.yml b/.github/workflows/check_make_vtadmin_authz_testgen.yml index f69df918be1..26543c8a948 100644 --- a/.github/workflows/check_make_vtadmin_authz_testgen.yml +++ b/.github/workflows/check_make_vtadmin_authz_testgen.yml @@ -6,7 +6,7 @@ permissions: read-all jobs: build: name: Check Make vtadmin_authz_testgen - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI run: | @@ -61,7 +61,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.vtadmin_changes == 'true' run: | sudo apt-get update - sudo apt-get install -y make unzip g++ etcd curl git wget + sudo apt-get install -y make unzip g++ etcd-client etcd-server curl git wget sudo service etcd stop go mod download go install golang.org/x/tools/cmd/goimports@latest diff --git a/.github/workflows/check_make_vtadmin_web_proto.yml b/.github/workflows/check_make_vtadmin_web_proto.yml index 0f8d404a256..1c1bf2cae4f 100644 --- a/.github/workflows/check_make_vtadmin_web_proto.yml +++ b/.github/workflows/check_make_vtadmin_web_proto.yml @@ -6,7 +6,7 @@ permissions: read-all jobs: build: name: Check Make VTAdmin Web Proto - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI run: | diff --git a/.github/workflows/close_stale_pull_requests.yml b/.github/workflows/close_stale_pull_requests.yml index 17fef550aa3..dbb71ac79b9 100644 --- a/.github/workflows/close_stale_pull_requests.yml +++ b/.github/workflows/close_stale_pull_requests.yml @@ -9,7 +9,7 @@ permissions: read-all jobs: close_stale_pull_requests: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 permissions: pull-requests: write diff --git a/.github/workflows/cluster_endtoend_12.yml b/.github/workflows/cluster_endtoend_12.yml index 236e6e2835c..7c98855b783 100644 --- a/.github/workflows/cluster_endtoend_12.yml +++ b/.github/workflows/cluster_endtoend_12.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (12) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # Get key to latest MySQL repo 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.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 -qq update + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_13.yml b/.github/workflows/cluster_endtoend_13.yml index 945d3448287..947d393bbbb 100644 --- a/.github/workflows/cluster_endtoend_13.yml +++ b/.github/workflows/cluster_endtoend_13.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (13) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # Get key to latest MySQL repo 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.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 -qq update + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_15.yml b/.github/workflows/cluster_endtoend_15.yml index 0c57e651013..aedc1bbd691 100644 --- a/.github/workflows/cluster_endtoend_15.yml +++ b/.github/workflows/cluster_endtoend_15.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (15) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # Get key to latest MySQL repo 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.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 -qq update + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_18.yml b/.github/workflows/cluster_endtoend_18.yml index 0c04f3e1b76..9886b617d51 100644 --- a/.github/workflows/cluster_endtoend_18.yml +++ b/.github/workflows/cluster_endtoend_18.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (18) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # Get key to latest MySQL repo 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.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 -qq update + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_21.yml b/.github/workflows/cluster_endtoend_21.yml index 7d14a8b99ed..e6bccee0004 100644 --- a/.github/workflows/cluster_endtoend_21.yml +++ b/.github/workflows/cluster_endtoend_21.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (21) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # Get key to latest MySQL repo 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.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 -qq update + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 @@ -112,6 +121,13 @@ jobs: # install JUnit report formatter go install github.com/vitessio/go-junit-report@HEAD + - name: Install Minio + if: steps.skip-workflow.outputs.skip-workflow == 'false' + run: | + wget https://dl.min.io/server/minio/release/linux-amd64/minio + chmod +x minio + mv minio /usr/local/bin + - 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: | diff --git a/.github/workflows/cluster_endtoend_backup_pitr.yml b/.github/workflows/cluster_endtoend_backup_pitr.yml index 99047933d69..1f744e85004 100644 --- a/.github/workflows/cluster_endtoend_backup_pitr.yml +++ b/.github/workflows/cluster_endtoend_backup_pitr.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (backup_pitr) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # Get key to latest MySQL repo 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.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 -qq update + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_backup_pitr_mysqlshell.yml b/.github/workflows/cluster_endtoend_backup_pitr_mysqlshell.yml index 8da7e054666..6ed73f260df 100644 --- a/.github/workflows/cluster_endtoend_backup_pitr_mysqlshell.yml +++ b/.github/workflows/cluster_endtoend_backup_pitr_mysqlshell.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (backup_pitr_mysqlshell) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # Get key to latest MySQL repo 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.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 -qq update + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_backup_pitr_xtrabackup.yml b/.github/workflows/cluster_endtoend_backup_pitr_xtrabackup.yml index 4012d1b1e4d..9bb6a4b56f8 100644 --- a/.github/workflows/cluster_endtoend_backup_pitr_xtrabackup.yml +++ b/.github/workflows/cluster_endtoend_backup_pitr_xtrabackup.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (backup_pitr_xtrabackup) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI diff --git a/.github/workflows/cluster_endtoend_ers_prs_newfeatures_heavy.yml b/.github/workflows/cluster_endtoend_ers_prs_newfeatures_heavy.yml index 177fc77a5c4..e859749ceca 100644 --- a/.github/workflows/cluster_endtoend_ers_prs_newfeatures_heavy.yml +++ b/.github/workflows/cluster_endtoend_ers_prs_newfeatures_heavy.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (ers_prs_newfeatures_heavy) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # Get key to latest MySQL repo 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.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 -qq update + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_mysql80.yml b/.github/workflows/cluster_endtoend_mysql80.yml index d0ff268c5fb..653db141b7d 100644 --- a/.github/workflows/cluster_endtoend_mysql80.yml +++ b/.github/workflows/cluster_endtoend_mysql80.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (mysql80) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # Get key to latest MySQL repo 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.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 -qq update + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_mysql_server_vault.yml b/.github/workflows/cluster_endtoend_mysql_server_vault.yml index 38cdcbadf95..9de4aacd499 100644 --- a/.github/workflows/cluster_endtoend_mysql_server_vault.yml +++ b/.github/workflows/cluster_endtoend_mysql_server_vault.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (mysql_server_vault) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # Get key to latest MySQL repo 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.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 -qq update + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_onlineddl_revert.yml b/.github/workflows/cluster_endtoend_onlineddl_revert.yml index dd9990b4761..54c85944190 100644 --- a/.github/workflows/cluster_endtoend_onlineddl_revert.yml +++ b/.github/workflows/cluster_endtoend_onlineddl_revert.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (onlineddl_revert) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -97,10 +97,19 @@ jobs: # Get key to latest MySQL repo 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.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 -qq update + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_onlineddl_scheduler.yml b/.github/workflows/cluster_endtoend_onlineddl_scheduler.yml index 144e06915ce..9e117ac0f04 100644 --- a/.github/workflows/cluster_endtoend_onlineddl_scheduler.yml +++ b/.github/workflows/cluster_endtoend_onlineddl_scheduler.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (onlineddl_scheduler) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -97,10 +97,19 @@ jobs: # Get key to latest MySQL repo 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.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 -qq update + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_onlineddl_vrepl.yml b/.github/workflows/cluster_endtoend_onlineddl_vrepl.yml index 5cd394f2d42..5056e46d895 100644 --- a/.github/workflows/cluster_endtoend_onlineddl_vrepl.yml +++ b/.github/workflows/cluster_endtoend_onlineddl_vrepl.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (onlineddl_vrepl) - runs-on: gh-hosted-runners-16cores-1 + runs-on: gh-hosted-runners-16cores-1-24.04 steps: - name: Skip CI @@ -97,10 +97,19 @@ jobs: # Get key to latest MySQL repo 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.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 -qq update + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_onlineddl_vrepl_stress.yml b/.github/workflows/cluster_endtoend_onlineddl_vrepl_stress.yml index 993a2d5193e..3597b37ede7 100644 --- a/.github/workflows/cluster_endtoend_onlineddl_vrepl_stress.yml +++ b/.github/workflows/cluster_endtoend_onlineddl_vrepl_stress.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (onlineddl_vrepl_stress) - runs-on: gh-hosted-runners-16cores-1 + runs-on: gh-hosted-runners-16cores-1-24.04 steps: - name: Skip CI @@ -97,10 +97,19 @@ jobs: # Get key to latest MySQL repo 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.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 -qq update + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_onlineddl_vrepl_stress_suite.yml b/.github/workflows/cluster_endtoend_onlineddl_vrepl_stress_suite.yml index 015db29723e..a5322578981 100644 --- a/.github/workflows/cluster_endtoend_onlineddl_vrepl_stress_suite.yml +++ b/.github/workflows/cluster_endtoend_onlineddl_vrepl_stress_suite.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (onlineddl_vrepl_stress_suite) - runs-on: gh-hosted-runners-16cores-1 + runs-on: gh-hosted-runners-16cores-1-24.04 steps: - name: Skip CI @@ -97,10 +97,19 @@ jobs: # Get key to latest MySQL repo 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.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 -qq update + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_onlineddl_vrepl_suite.yml b/.github/workflows/cluster_endtoend_onlineddl_vrepl_suite.yml index 550408f5def..12bde509048 100644 --- a/.github/workflows/cluster_endtoend_onlineddl_vrepl_suite.yml +++ b/.github/workflows/cluster_endtoend_onlineddl_vrepl_suite.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (onlineddl_vrepl_suite) - runs-on: gh-hosted-runners-16cores-1 + runs-on: gh-hosted-runners-16cores-1-24.04 steps: - name: Skip CI @@ -97,10 +97,19 @@ jobs: # Get key to latest MySQL repo 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.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 -qq update + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_schemadiff_vrepl.yml b/.github/workflows/cluster_endtoend_schemadiff_vrepl.yml index 9c2cac74843..e452d28606a 100644 --- a/.github/workflows/cluster_endtoend_schemadiff_vrepl.yml +++ b/.github/workflows/cluster_endtoend_schemadiff_vrepl.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (schemadiff_vrepl) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -97,10 +97,19 @@ jobs: # Get key to latest MySQL repo 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.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 -qq update + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_tabletmanager_consul.yml b/.github/workflows/cluster_endtoend_tabletmanager_consul.yml index 4846fdcc2ac..3e8ffce0ac5 100644 --- a/.github/workflows/cluster_endtoend_tabletmanager_consul.yml +++ b/.github/workflows/cluster_endtoend_tabletmanager_consul.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (tabletmanager_consul) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # Get key to latest MySQL repo 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.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 -qq update + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_tabletmanager_tablegc.yml b/.github/workflows/cluster_endtoend_tabletmanager_tablegc.yml index 435d28a722a..cc75ef22937 100644 --- a/.github/workflows/cluster_endtoend_tabletmanager_tablegc.yml +++ b/.github/workflows/cluster_endtoend_tabletmanager_tablegc.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (tabletmanager_tablegc) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # Get key to latest MySQL repo 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.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 -qq update + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_tabletmanager_throttler_topo.yml b/.github/workflows/cluster_endtoend_tabletmanager_throttler_topo.yml index 6cf480aa1f1..0837be5356e 100644 --- a/.github/workflows/cluster_endtoend_tabletmanager_throttler_topo.yml +++ b/.github/workflows/cluster_endtoend_tabletmanager_throttler_topo.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (tabletmanager_throttler_topo) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # Get key to latest MySQL repo 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.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 -qq update + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_topo_connection_cache.yml b/.github/workflows/cluster_endtoend_topo_connection_cache.yml index 056bfb6d8a4..da6ee53ad53 100644 --- a/.github/workflows/cluster_endtoend_topo_connection_cache.yml +++ b/.github/workflows/cluster_endtoend_topo_connection_cache.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (topo_connection_cache) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # Get key to latest MySQL repo 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.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 -qq update + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_vreplication_across_db_versions.yml b/.github/workflows/cluster_endtoend_vreplication_across_db_versions.yml index aacf9d157fd..a38506ddd7d 100644 --- a/.github/workflows/cluster_endtoend_vreplication_across_db_versions.yml +++ b/.github/workflows/cluster_endtoend_vreplication_across_db_versions.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (vreplication_across_db_versions) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # Get key to latest MySQL repo 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.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 -qq update + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_vreplication_basic.yml b/.github/workflows/cluster_endtoend_vreplication_basic.yml index ead3b816df2..283d0451a34 100644 --- a/.github/workflows/cluster_endtoend_vreplication_basic.yml +++ b/.github/workflows/cluster_endtoend_vreplication_basic.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (vreplication_basic) - runs-on: gh-hosted-runners-16cores-1 + runs-on: gh-hosted-runners-16cores-1-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # Get key to latest MySQL repo 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.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 -qq update + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_vreplication_cellalias.yml b/.github/workflows/cluster_endtoend_vreplication_cellalias.yml index d9f1aeac45e..b316c075614 100644 --- a/.github/workflows/cluster_endtoend_vreplication_cellalias.yml +++ b/.github/workflows/cluster_endtoend_vreplication_cellalias.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (vreplication_cellalias) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # Get key to latest MySQL repo 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.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 -qq update + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_vreplication_copy_parallel.yml b/.github/workflows/cluster_endtoend_vreplication_copy_parallel.yml index 9cdc4a9091a..48f506709e7 100644 --- a/.github/workflows/cluster_endtoend_vreplication_copy_parallel.yml +++ b/.github/workflows/cluster_endtoend_vreplication_copy_parallel.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (vreplication_copy_parallel) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # Get key to latest MySQL repo 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.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 -qq update + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_vreplication_foreign_key_stress.yml b/.github/workflows/cluster_endtoend_vreplication_foreign_key_stress.yml index f95f70e8550..a1293e3688e 100644 --- a/.github/workflows/cluster_endtoend_vreplication_foreign_key_stress.yml +++ b/.github/workflows/cluster_endtoend_vreplication_foreign_key_stress.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (vreplication_foreign_key_stress) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # Get key to latest MySQL repo 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.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 -qq update + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_vreplication_mariadb_to_mysql.yml b/.github/workflows/cluster_endtoend_vreplication_mariadb_to_mysql.yml index 2a6af9b91bd..2013d89a83d 100644 --- a/.github/workflows/cluster_endtoend_vreplication_mariadb_to_mysql.yml +++ b/.github/workflows/cluster_endtoend_vreplication_mariadb_to_mysql.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (vreplication_mariadb_to_mysql) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # Get key to latest MySQL repo 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.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 -qq update + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_vreplication_migrate.yml b/.github/workflows/cluster_endtoend_vreplication_migrate.yml index 6b6768eb676..e7a8a8cb5ce 100644 --- a/.github/workflows/cluster_endtoend_vreplication_migrate.yml +++ b/.github/workflows/cluster_endtoend_vreplication_migrate.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (vreplication_migrate) - runs-on: gh-hosted-runners-16cores-1 + runs-on: gh-hosted-runners-16cores-1-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # Get key to latest MySQL repo 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.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 -qq update + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_vreplication_multi_tenant.yml b/.github/workflows/cluster_endtoend_vreplication_multi_tenant.yml index 78b6c616691..c0c80a8dc61 100644 --- a/.github/workflows/cluster_endtoend_vreplication_multi_tenant.yml +++ b/.github/workflows/cluster_endtoend_vreplication_multi_tenant.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (vreplication_multi_tenant) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # Get key to latest MySQL repo 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.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 -qq update + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_vreplication_partial_movetables_and_materialize.yml b/.github/workflows/cluster_endtoend_vreplication_partial_movetables_and_materialize.yml index 30367611574..0b80b82fd7f 100644 --- a/.github/workflows/cluster_endtoend_vreplication_partial_movetables_and_materialize.yml +++ b/.github/workflows/cluster_endtoend_vreplication_partial_movetables_and_materialize.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (vreplication_partial_movetables_and_materialize) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # Get key to latest MySQL repo 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.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 -qq update + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_vreplication_v2.yml b/.github/workflows/cluster_endtoend_vreplication_v2.yml index 23871ada3b8..fcbe7057b4c 100644 --- a/.github/workflows/cluster_endtoend_vreplication_v2.yml +++ b/.github/workflows/cluster_endtoend_vreplication_v2.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (vreplication_v2) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # Get key to latest MySQL repo 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.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 -qq update + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_vreplication_vtctldclient_vdiff2_movetables_tz.yml b/.github/workflows/cluster_endtoend_vreplication_vtctldclient_vdiff2_movetables_tz.yml index dd1eb76ca0e..40f2002f9a3 100644 --- a/.github/workflows/cluster_endtoend_vreplication_vtctldclient_vdiff2_movetables_tz.yml +++ b/.github/workflows/cluster_endtoend_vreplication_vtctldclient_vdiff2_movetables_tz.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (vreplication_vtctldclient_vdiff2_movetables_tz) - runs-on: gh-hosted-runners-16cores-1 + runs-on: gh-hosted-runners-16cores-1-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # Get key to latest MySQL repo 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.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 -qq update + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_vstream.yml b/.github/workflows/cluster_endtoend_vstream.yml index 3c99a687abc..acfefb40758 100644 --- a/.github/workflows/cluster_endtoend_vstream.yml +++ b/.github/workflows/cluster_endtoend_vstream.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (vstream) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # Get key to latest MySQL repo 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.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 -qq update + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_vtbackup.yml b/.github/workflows/cluster_endtoend_vtbackup.yml index e47e8fbad47..947598bc307 100644 --- a/.github/workflows/cluster_endtoend_vtbackup.yml +++ b/.github/workflows/cluster_endtoend_vtbackup.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (vtbackup) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # Get key to latest MySQL repo 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.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 -qq update + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_vtctlbackup_sharded_clustertest_heavy.yml b/.github/workflows/cluster_endtoend_vtctlbackup_sharded_clustertest_heavy.yml index 8f9a27bc750..c6f8712a35a 100644 --- a/.github/workflows/cluster_endtoend_vtctlbackup_sharded_clustertest_heavy.yml +++ b/.github/workflows/cluster_endtoend_vtctlbackup_sharded_clustertest_heavy.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (vtctlbackup_sharded_clustertest_heavy) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # Get key to latest MySQL repo 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.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 -qq update + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_vtgate_concurrentdml.yml b/.github/workflows/cluster_endtoend_vtgate_concurrentdml.yml index db9ea451081..2cd81fe04ef 100644 --- a/.github/workflows/cluster_endtoend_vtgate_concurrentdml.yml +++ b/.github/workflows/cluster_endtoend_vtgate_concurrentdml.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (vtgate_concurrentdml) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # Get key to latest MySQL repo 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.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 -qq update + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_vtgate_foreignkey_stress.yml b/.github/workflows/cluster_endtoend_vtgate_foreignkey_stress.yml index 2fada310f7e..19620d9e3a2 100644 --- a/.github/workflows/cluster_endtoend_vtgate_foreignkey_stress.yml +++ b/.github/workflows/cluster_endtoend_vtgate_foreignkey_stress.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (vtgate_foreignkey_stress) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # Get key to latest MySQL repo 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.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 -qq update + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_vtgate_gen4.yml b/.github/workflows/cluster_endtoend_vtgate_gen4.yml index 04813704cd2..06961af4ee7 100644 --- a/.github/workflows/cluster_endtoend_vtgate_gen4.yml +++ b/.github/workflows/cluster_endtoend_vtgate_gen4.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (vtgate_gen4) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # Get key to latest MySQL repo 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.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 -qq update + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_vtgate_general_heavy.yml b/.github/workflows/cluster_endtoend_vtgate_general_heavy.yml index bdb6b8b149d..e0b21df421a 100644 --- a/.github/workflows/cluster_endtoend_vtgate_general_heavy.yml +++ b/.github/workflows/cluster_endtoend_vtgate_general_heavy.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (vtgate_general_heavy) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # Get key to latest MySQL repo 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.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 -qq update + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_vtgate_godriver.yml b/.github/workflows/cluster_endtoend_vtgate_godriver.yml index e23742a5650..23c9da550c2 100644 --- a/.github/workflows/cluster_endtoend_vtgate_godriver.yml +++ b/.github/workflows/cluster_endtoend_vtgate_godriver.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (vtgate_godriver) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # Get key to latest MySQL repo 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.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 -qq update + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_vtgate_partial_keyspace.yml b/.github/workflows/cluster_endtoend_vtgate_partial_keyspace.yml index 4d082900790..f5d1850aca3 100644 --- a/.github/workflows/cluster_endtoend_vtgate_partial_keyspace.yml +++ b/.github/workflows/cluster_endtoend_vtgate_partial_keyspace.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (vtgate_partial_keyspace) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # Get key to latest MySQL repo 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.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 -qq update + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_vtgate_plantests.yml b/.github/workflows/cluster_endtoend_vtgate_plantests.yml new file mode 100644 index 00000000000..93ed6a55f05 --- /dev/null +++ b/.github/workflows/cluster_endtoend_vtgate_plantests.yml @@ -0,0 +1,166 @@ +# DO NOT MODIFY: THIS FILE IS GENERATED USING "make generate_ci_workflows" + +name: Cluster (vtgate_plantests) +on: [push, pull_request] +concurrency: + group: format('{0}-{1}', ${{ github.ref }}, 'Cluster (vtgate_plantests)') + 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 (vtgate_plantests) + runs-on: ubuntu-24.04 + + 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 -s\ + -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@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + + - name: Check for changes in relevant files + if: steps.skip-workflow.outputs.skip-workflow == 'false' + uses: dorny/paths-filter@ebc4d7e9ebcb0b1eb21480bb8f43113e996ac77a # 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' + - 'go.sum' + - 'go.mod' + - 'proto/*.proto' + - 'tools/**' + - 'config/**' + - 'bootstrap.sh' + - '.github/workflows/cluster_endtoend_vtgate_plantests.yml' + + - name: Set up Go + if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' + uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 + with: + go-version-file: go.mod + + - name: Set up python + if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' + uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f # v5.1.1 + + - 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 A8D3785C + # Setup MySQL 8.0 + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 -qq update + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + + # Install everything else we need, and configure + sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 + + 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 vtgate_plantests | 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 + + - name: Test Summary + if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' && always() + uses: test-summary/action@31493c76ec9e7aa675f1585d3ed6f1da69269a86 # v2.4 + with: + paths: "report.xml" + show: "fail" diff --git a/.github/workflows/cluster_endtoend_vtgate_queries.yml b/.github/workflows/cluster_endtoend_vtgate_queries.yml index 46242a6b7f4..80462086a57 100644 --- a/.github/workflows/cluster_endtoend_vtgate_queries.yml +++ b/.github/workflows/cluster_endtoend_vtgate_queries.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (vtgate_queries) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # Get key to latest MySQL repo 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.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 -qq update + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_vtgate_readafterwrite.yml b/.github/workflows/cluster_endtoend_vtgate_readafterwrite.yml index b39aa3693e6..686db1e12ab 100644 --- a/.github/workflows/cluster_endtoend_vtgate_readafterwrite.yml +++ b/.github/workflows/cluster_endtoend_vtgate_readafterwrite.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (vtgate_readafterwrite) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # Get key to latest MySQL repo 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.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 -qq update + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_vtgate_reservedconn.yml b/.github/workflows/cluster_endtoend_vtgate_reservedconn.yml index 0032704b240..0f4ae20b0a7 100644 --- a/.github/workflows/cluster_endtoend_vtgate_reservedconn.yml +++ b/.github/workflows/cluster_endtoend_vtgate_reservedconn.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (vtgate_reservedconn) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # Get key to latest MySQL repo 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.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 -qq update + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_vtgate_schema.yml b/.github/workflows/cluster_endtoend_vtgate_schema.yml index be619a0815c..2dca36298e1 100644 --- a/.github/workflows/cluster_endtoend_vtgate_schema.yml +++ b/.github/workflows/cluster_endtoend_vtgate_schema.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (vtgate_schema) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # Get key to latest MySQL repo 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.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 -qq update + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_vtgate_schema_tracker.yml b/.github/workflows/cluster_endtoend_vtgate_schema_tracker.yml index a02b4ec5435..711f38253cb 100644 --- a/.github/workflows/cluster_endtoend_vtgate_schema_tracker.yml +++ b/.github/workflows/cluster_endtoend_vtgate_schema_tracker.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (vtgate_schema_tracker) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # Get key to latest MySQL repo 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.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 -qq update + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_vtgate_tablet_healthcheck_cache.yml b/.github/workflows/cluster_endtoend_vtgate_tablet_healthcheck_cache.yml index ff0c6990512..55996d7fa86 100644 --- a/.github/workflows/cluster_endtoend_vtgate_tablet_healthcheck_cache.yml +++ b/.github/workflows/cluster_endtoend_vtgate_tablet_healthcheck_cache.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (vtgate_tablet_healthcheck_cache) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # Get key to latest MySQL repo 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.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 -qq update + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_vtgate_topo.yml b/.github/workflows/cluster_endtoend_vtgate_topo.yml index e0f4397d56c..f47e0a8dd63 100644 --- a/.github/workflows/cluster_endtoend_vtgate_topo.yml +++ b/.github/workflows/cluster_endtoend_vtgate_topo.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (vtgate_topo) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # Get key to latest MySQL repo 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.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 -qq update + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_vtgate_topo_consul.yml b/.github/workflows/cluster_endtoend_vtgate_topo_consul.yml index a10a9947218..1b62ac79041 100644 --- a/.github/workflows/cluster_endtoend_vtgate_topo_consul.yml +++ b/.github/workflows/cluster_endtoend_vtgate_topo_consul.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (vtgate_topo_consul) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # Get key to latest MySQL repo 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.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 -qq update + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_vtgate_topo_etcd.yml b/.github/workflows/cluster_endtoend_vtgate_topo_etcd.yml index 42e3b3d919c..f88d3b9af3a 100644 --- a/.github/workflows/cluster_endtoend_vtgate_topo_etcd.yml +++ b/.github/workflows/cluster_endtoend_vtgate_topo_etcd.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (vtgate_topo_etcd) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # Get key to latest MySQL repo 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.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 -qq update + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_vtgate_transaction.yml b/.github/workflows/cluster_endtoend_vtgate_transaction.yml index bc400dde33f..9ae7b465695 100644 --- a/.github/workflows/cluster_endtoend_vtgate_transaction.yml +++ b/.github/workflows/cluster_endtoend_vtgate_transaction.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (vtgate_transaction) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # Get key to latest MySQL repo 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.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 -qq update + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_vtgate_unsharded.yml b/.github/workflows/cluster_endtoend_vtgate_unsharded.yml index 8c128e95b0f..d348606dc31 100644 --- a/.github/workflows/cluster_endtoend_vtgate_unsharded.yml +++ b/.github/workflows/cluster_endtoend_vtgate_unsharded.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (vtgate_unsharded) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # Get key to latest MySQL repo 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.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 -qq update + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_vtgate_vindex_heavy.yml b/.github/workflows/cluster_endtoend_vtgate_vindex_heavy.yml index e260443a242..16bdee60217 100644 --- a/.github/workflows/cluster_endtoend_vtgate_vindex_heavy.yml +++ b/.github/workflows/cluster_endtoend_vtgate_vindex_heavy.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (vtgate_vindex_heavy) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # Get key to latest MySQL repo 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.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 -qq update + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_vtgate_vschema.yml b/.github/workflows/cluster_endtoend_vtgate_vschema.yml index 72e9ac45949..fb176119769 100644 --- a/.github/workflows/cluster_endtoend_vtgate_vschema.yml +++ b/.github/workflows/cluster_endtoend_vtgate_vschema.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (vtgate_vschema) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # Get key to latest MySQL repo 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.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 -qq update + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_vtorc.yml b/.github/workflows/cluster_endtoend_vtorc.yml index 23544b28d01..92c2ea3d5d7 100644 --- a/.github/workflows/cluster_endtoend_vtorc.yml +++ b/.github/workflows/cluster_endtoend_vtorc.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (vtorc) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -105,10 +105,19 @@ jobs: # Get key to latest MySQL repo 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.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 -qq update + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_vttablet_prscomplex.yml b/.github/workflows/cluster_endtoend_vttablet_prscomplex.yml index eaf6583a22d..9868c382a69 100644 --- a/.github/workflows/cluster_endtoend_vttablet_prscomplex.yml +++ b/.github/workflows/cluster_endtoend_vttablet_prscomplex.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (vttablet_prscomplex) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -96,10 +96,19 @@ jobs: # Get key to latest MySQL repo 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.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 -qq update + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 diff --git a/.github/workflows/cluster_endtoend_xb_backup.yml b/.github/workflows/cluster_endtoend_xb_backup.yml index dae2eca922f..26dbfea2def 100644 --- a/.github/workflows/cluster_endtoend_xb_backup.yml +++ b/.github/workflows/cluster_endtoend_xb_backup.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (xb_backup) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI diff --git a/.github/workflows/cluster_endtoend_xb_recovery.yml b/.github/workflows/cluster_endtoend_xb_recovery.yml index 0dad33aee7f..43360690d6b 100644 --- a/.github/workflows/cluster_endtoend_xb_recovery.yml +++ b/.github/workflows/cluster_endtoend_xb_recovery.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Cluster (xb_recovery) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI diff --git a/.github/workflows/code_freeze.yml b/.github/workflows/code_freeze.yml index 92ef037ee6b..a66fb6e8b2b 100644 --- a/.github/workflows/code_freeze.yml +++ b/.github/workflows/code_freeze.yml @@ -7,7 +7,7 @@ permissions: read-all jobs: build: name: Code Freeze - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Fail if Code Freeze is enabled run: | diff --git a/.github/workflows/codecov.yml b/.github/workflows/codecov.yml index 7846749a013..fc30974212b 100644 --- a/.github/workflows/codecov.yml +++ b/.github/workflows/codecov.yml @@ -9,7 +9,7 @@ permissions: read-all jobs: test: name: Code Coverage - runs-on: gh-hosted-runners-16cores-1 + runs-on: gh-hosted-runners-16cores-1-24.04 steps: - name: Check out code diff --git a/.github/workflows/codeql_analysis.yml b/.github/workflows/codeql_analysis.yml index e1a474ce9a2..c523e34c697 100644 --- a/.github/workflows/codeql_analysis.yml +++ b/.github/workflows/codeql_analysis.yml @@ -14,7 +14,7 @@ permissions: read-all jobs: analyze: name: Analyze - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 permissions: actions: read contents: read @@ -59,13 +59,13 @@ jobs: sudo rm -rf /etc/mysql # Install mysql80 sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 # Install everything else we need, and configure - sudo apt-get install -y make unzip g++ etcd curl git wget eatmydata + sudo apt-get install -y make unzip g++ etcd-client etcd-server curl git wget eatmydata sudo service mysql stop sudo service etcd 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 diff --git a/.github/workflows/create_release.yml b/.github/workflows/create_release.yml index a42bbc0ff2d..424cc5bfe15 100644 --- a/.github/workflows/create_release.yml +++ b/.github/workflows/create_release.yml @@ -12,7 +12,7 @@ permissions: read-all jobs: build: name: Create Release - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 permissions: contents: write steps: diff --git a/.github/workflows/docker_build_images.yml b/.github/workflows/docker_build_images.yml index 827946a43dc..2024d677205 100644 --- a/.github/workflows/docker_build_images.yml +++ b/.github/workflows/docker_build_images.yml @@ -16,7 +16,7 @@ permissions: read-all jobs: build_and_push_vttestserver: name: Build and push vttestserver - runs-on: gh-hosted-runners-16cores-1 + runs-on: gh-hosted-runners-16cores-1-24.04 if: github.repository == 'vitessio/vitess' strategy: @@ -70,7 +70,7 @@ jobs: build_and_push_lite: name: Build and push lite - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 if: github.repository == 'vitessio/vitess' strategy: @@ -132,7 +132,7 @@ jobs: build_and_push_components: name: Build and push - runs-on: gh-hosted-runners-16cores-1 + runs-on: gh-hosted-runners-16cores-1-24.04 if: github.repository == 'vitessio/vitess' && needs.build_and_push_lite.result == 'success' needs: - build_and_push_lite @@ -226,7 +226,7 @@ jobs: slack_notification: name: Slack Notification if failed - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 needs: - build_and_push_vttestserver - build_and_push_lite diff --git a/.github/workflows/docker_test_cluster_10.yml b/.github/workflows/docker_test_cluster_10.yml index ce1c9e7277b..10a7c1551c0 100644 --- a/.github/workflows/docker_test_cluster_10.yml +++ b/.github/workflows/docker_test_cluster_10.yml @@ -5,7 +5,7 @@ jobs: build: name: Docker Test Cluster 10 - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI diff --git a/.github/workflows/docker_test_cluster_25.yml b/.github/workflows/docker_test_cluster_25.yml index eb2319b8c9f..34463ce3830 100644 --- a/.github/workflows/docker_test_cluster_25.yml +++ b/.github/workflows/docker_test_cluster_25.yml @@ -5,7 +5,7 @@ jobs: build: name: Docker Test Cluster 25 - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI diff --git a/.github/workflows/e2e_race.yml b/.github/workflows/e2e_race.yml index 78c8d0b1753..50e1e4b9485 100644 --- a/.github/workflows/e2e_race.yml +++ b/.github/workflows/e2e_race.yml @@ -5,7 +5,7 @@ jobs: build: name: End-to-End Test (Race) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI run: | @@ -65,13 +65,13 @@ jobs: # Get key to latest MySQL repo 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.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 + sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils sudo service mysql stop sudo service etcd stop sudo ln -s /etc/apparmor.d/usr.sbin.mysqld /etc/apparmor.d/disable/ diff --git a/.github/workflows/endtoend.yml b/.github/workflows/endtoend.yml index 9e06e4ee44b..f061e8bee68 100644 --- a/.github/workflows/endtoend.yml +++ b/.github/workflows/endtoend.yml @@ -5,7 +5,7 @@ jobs: build: name: End-to-End Test - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI run: | @@ -63,7 +63,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' run: | sudo apt-get update - sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd curl git wget + sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd-client etcd-server curl git wget sudo service mysql stop sudo service etcd stop sudo ln -s /etc/apparmor.d/usr.sbin.mysqld /etc/apparmor.d/disable/ diff --git a/.github/workflows/local_example.yml b/.github/workflows/local_example.yml index 65669158415..fc9724aa6ac 100644 --- a/.github/workflows/local_example.yml +++ b/.github/workflows/local_example.yml @@ -4,8 +4,8 @@ permissions: read-all jobs: build: - name: Local example using ${{ matrix.topo }} on ubuntu-22.04 - runs-on: gh-hosted-runners-16cores-1 + name: Local example using ${{ matrix.topo }} on Ubuntu + runs-on: gh-hosted-runners-16cores-1-24.04 strategy: matrix: topo: [consul,etcd,zk2] @@ -67,24 +67,6 @@ jobs: - name: Get dependencies if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.examples == 'true' run: | - if [ ${{matrix.os}} = "ubuntu-22.04" ]; then - # Get key to latest MySQL repo - 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.32-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 - 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 - elif [ ${{matrix.os}} = "macos-latest" ]; then - brew install mysql@5.7 make unzip etcd curl git wget - fi go mod download - name: Run make minimaltools diff --git a/.github/workflows/region_example.yml b/.github/workflows/region_example.yml index c031ea315b5..c17f43eec11 100644 --- a/.github/workflows/region_example.yml +++ b/.github/workflows/region_example.yml @@ -4,8 +4,8 @@ permissions: read-all jobs: build: - name: Region Sharding example using ${{ matrix.topo }} on ubuntu-22.04 - runs-on: gh-hosted-runners-16cores-1 + name: Region Sharding example using ${{ matrix.topo }} on Ubuntu + runs-on: gh-hosted-runners-16cores-1-24.04 strategy: matrix: topo: [etcd] @@ -67,24 +67,6 @@ jobs: - name: Get dependencies if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.examples == 'true' run: | - if [ ${{matrix.os}} = "ubuntu-22.04" ]; then - # Get key to latest MySQL repo - 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.32-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 - 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 - elif [ ${{matrix.os}} = "macos-latest" ]; then - brew install mysql@5.7 make unzip etcd curl git wget - fi go mod download - name: Run make minimaltools diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml index 7d20694b850..de90706da27 100644 --- a/.github/workflows/scorecards.yml +++ b/.github/workflows/scorecards.yml @@ -14,7 +14,7 @@ permissions: read-all jobs: analysis: name: Scorecard analysis - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 permissions: # Needed to upload the results to code-scanning dashboard. security-events: write @@ -53,7 +53,7 @@ jobs: # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF # format to the repository Actions tab. - name: "Upload artifact" - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 with: name: SARIF file path: results.sarif @@ -62,6 +62,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard (optional). # Commenting out will disable upload of results to your repo's Code Scanning dashboard - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@v3 + uses: github/codeql-action/upload-sarif@f09c1c0a94de965c15400f5634aa42fac8fb8f88 # v3.27.5 with: sarif_file: results.sarif diff --git a/.github/workflows/static_checks_etc.yml b/.github/workflows/static_checks_etc.yml index cd896d5fd29..cd2cce93827 100644 --- a/.github/workflows/static_checks_etc.yml +++ b/.github/workflows/static_checks_etc.yml @@ -9,7 +9,7 @@ permissions: read-all jobs: build: name: Static Code Checks Etc - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -152,7 +152,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && (steps.changes.outputs.parser_changes == 'true' || steps.changes.outputs.go_files == 'true') run: | sudo apt-get update - sudo apt-get install -y make unzip g++ etcd curl git wget + sudo apt-get install -y make unzip g++ etcd-client etcd-server curl git wget sudo service etcd stop go mod download diff --git a/.github/workflows/unit_race.yml b/.github/workflows/unit_race.yml index e764cd33d9d..8ffb74bd2cb 100644 --- a/.github/workflows/unit_race.yml +++ b/.github/workflows/unit_race.yml @@ -15,7 +15,7 @@ jobs: build: name: Unit Test (Race) - runs-on: gh-hosted-runners-16cores-1 + runs-on: gh-hosted-runners-16cores-1-24.04 steps: - name: Skip CI run: | diff --git a/.github/workflows/unit_race_evalengine.yml b/.github/workflows/unit_race_evalengine.yml index 5a5cb637681..d6d248f066e 100644 --- a/.github/workflows/unit_race_evalengine.yml +++ b/.github/workflows/unit_race_evalengine.yml @@ -15,7 +15,7 @@ jobs: build: name: Unit Test (Evalengine_Race) - runs-on: gh-hosted-runners-16cores-1 + runs-on: gh-hosted-runners-16cores-1-24.04 steps: - name: Skip CI run: | diff --git a/.github/workflows/unit_test_evalengine_mysql57.yml b/.github/workflows/unit_test_evalengine_mysql57.yml index 09cb392c794..ecc366b38fe 100644 --- a/.github/workflows/unit_test_evalengine_mysql57.yml +++ b/.github/workflows/unit_test_evalengine_mysql57.yml @@ -16,7 +16,7 @@ env: jobs: test: name: Unit Test (evalengine_mysql57) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -89,30 +89,37 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.unit_tests == 'true' run: | export DEBIAN_FRONTEND="noninteractive" - sudo apt-get -qq update + sudo apt-get update # Uninstall any previously installed MySQL first sudo systemctl stop apparmor - sudo DEBIAN_FRONTEND="noninteractive" apt-get -qq remove -y --purge mysql-server mysql-client mysql-common - sudo apt-get -qq -y autoremove - sudo apt-get -qq -y autoclean + 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 - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 echo mysql-apt-config mysql-apt-config/select-server select mysql-5.7 | sudo debconf-set-selections sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* - sudo apt-get -qq update - sudo DEBIAN_FRONTEND="noninteractive" apt-get -qq install -y mysql-client=5.7* mysql-community-server=5.7* mysql-server=5.7* libncurses6 + sudo apt-get update + # We have to install this old version of libaio1. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo DEBIAN_FRONTEND="noninteractive" apt-get install -y mysql-client=5.7* mysql-community-server=5.7* mysql-server=5.7* libncurses6 - sudo apt-get -qq install -y make unzip g++ curl git wget ant openjdk-11-jdk eatmydata + 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/ diff --git a/.github/workflows/unit_test_evalengine_mysql80.yml b/.github/workflows/unit_test_evalengine_mysql80.yml index 5578829d48d..e6e802b52d8 100644 --- a/.github/workflows/unit_test_evalengine_mysql80.yml +++ b/.github/workflows/unit_test_evalengine_mysql80.yml @@ -16,7 +16,7 @@ env: jobs: test: name: Unit Test (evalengine_mysql80) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -89,27 +89,27 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.unit_tests == 'true' run: | export DEBIAN_FRONTEND="noninteractive" - sudo apt-get -qq update + sudo apt-get update # Uninstall any previously installed MySQL first sudo systemctl stop apparmor - sudo DEBIAN_FRONTEND="noninteractive" apt-get -qq remove -y --purge mysql-server mysql-client mysql-common - sudo apt-get -qq -y autoremove - sudo apt-get -qq -y autoclean + 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 - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 -qq update - sudo DEBIAN_FRONTEND="noninteractive" apt-get -qq install -y mysql-server mysql-client + sudo apt-get update + sudo DEBIAN_FRONTEND="noninteractive" apt-get install -y mysql-server mysql-client - sudo apt-get -qq install -y make unzip g++ curl git wget ant openjdk-11-jdk eatmydata + 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/ diff --git a/.github/workflows/unit_test_evalengine_mysql84.yml b/.github/workflows/unit_test_evalengine_mysql84.yml index 2a86d5bfabd..46736dac349 100644 --- a/.github/workflows/unit_test_evalengine_mysql84.yml +++ b/.github/workflows/unit_test_evalengine_mysql84.yml @@ -16,7 +16,7 @@ env: jobs: test: name: Unit Test (evalengine_mysql84) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -89,27 +89,27 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.unit_tests == 'true' run: | export DEBIAN_FRONTEND="noninteractive" - sudo apt-get -qq update + sudo apt-get update # Uninstall any previously installed MySQL first sudo systemctl stop apparmor - sudo DEBIAN_FRONTEND="noninteractive" apt-get -qq remove -y --purge mysql-server mysql-client mysql-common - sudo apt-get -qq -y autoremove - sudo apt-get -qq -y autoclean + 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 - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-1_all.deb echo mysql-apt-config mysql-apt-config/select-server select mysql-8.4-lts | sudo debconf-set-selections sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* - sudo apt-get -qq update - sudo DEBIAN_FRONTEND="noninteractive" apt-get -qq install -y mysql-server mysql-client + sudo apt-get update + sudo DEBIAN_FRONTEND="noninteractive" apt-get install -y mysql-server mysql-client - sudo apt-get -qq install -y make unzip g++ curl git wget ant openjdk-11-jdk eatmydata + 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/ diff --git a/.github/workflows/unit_test_mysql57.yml b/.github/workflows/unit_test_mysql57.yml index bd4b6ed8c3c..3eaf02d1538 100644 --- a/.github/workflows/unit_test_mysql57.yml +++ b/.github/workflows/unit_test_mysql57.yml @@ -16,7 +16,7 @@ env: jobs: test: name: Unit Test (mysql57) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -89,30 +89,37 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.unit_tests == 'true' run: | export DEBIAN_FRONTEND="noninteractive" - sudo apt-get -qq update + sudo apt-get update # Uninstall any previously installed MySQL first sudo systemctl stop apparmor - sudo DEBIAN_FRONTEND="noninteractive" apt-get -qq remove -y --purge mysql-server mysql-client mysql-common - sudo apt-get -qq -y autoremove - sudo apt-get -qq -y autoclean + 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 - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 echo mysql-apt-config mysql-apt-config/select-server select mysql-5.7 | sudo debconf-set-selections sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* - sudo apt-get -qq update - sudo DEBIAN_FRONTEND="noninteractive" apt-get -qq install -y mysql-client=5.7* mysql-community-server=5.7* mysql-server=5.7* libncurses6 + sudo apt-get update + # We have to install this old version of libaio1. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo DEBIAN_FRONTEND="noninteractive" apt-get install -y mysql-client=5.7* mysql-community-server=5.7* mysql-server=5.7* libncurses6 - sudo apt-get -qq install -y make unzip g++ curl git wget ant openjdk-11-jdk eatmydata + 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/ diff --git a/.github/workflows/unit_test_mysql80.yml b/.github/workflows/unit_test_mysql80.yml index 03f31a25cc8..c036e6dd477 100644 --- a/.github/workflows/unit_test_mysql80.yml +++ b/.github/workflows/unit_test_mysql80.yml @@ -16,7 +16,7 @@ env: jobs: test: name: Unit Test (mysql80) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -89,27 +89,27 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.unit_tests == 'true' run: | export DEBIAN_FRONTEND="noninteractive" - sudo apt-get -qq update + sudo apt-get update # Uninstall any previously installed MySQL first sudo systemctl stop apparmor - sudo DEBIAN_FRONTEND="noninteractive" apt-get -qq remove -y --purge mysql-server mysql-client mysql-common - sudo apt-get -qq -y autoremove - sudo apt-get -qq -y autoclean + 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 - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 -qq update - sudo DEBIAN_FRONTEND="noninteractive" apt-get -qq install -y mysql-server mysql-client + sudo apt-get update + sudo DEBIAN_FRONTEND="noninteractive" apt-get install -y mysql-server mysql-client - sudo apt-get -qq install -y make unzip g++ curl git wget ant openjdk-11-jdk eatmydata + 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/ diff --git a/.github/workflows/unit_test_mysql84.yml b/.github/workflows/unit_test_mysql84.yml index 95a56aeff81..84447ce390b 100644 --- a/.github/workflows/unit_test_mysql84.yml +++ b/.github/workflows/unit_test_mysql84.yml @@ -16,7 +16,7 @@ env: jobs: test: name: Unit Test (mysql84) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -89,27 +89,27 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.unit_tests == 'true' run: | export DEBIAN_FRONTEND="noninteractive" - sudo apt-get -qq update + sudo apt-get update # Uninstall any previously installed MySQL first sudo systemctl stop apparmor - sudo DEBIAN_FRONTEND="noninteractive" apt-get -qq remove -y --purge mysql-server mysql-client mysql-common - sudo apt-get -qq -y autoremove - sudo apt-get -qq -y autoclean + 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 - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-1_all.deb echo mysql-apt-config mysql-apt-config/select-server select mysql-8.4-lts | sudo debconf-set-selections sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* - sudo apt-get -qq update - sudo DEBIAN_FRONTEND="noninteractive" apt-get -qq install -y mysql-server mysql-client + sudo apt-get update + sudo DEBIAN_FRONTEND="noninteractive" apt-get install -y mysql-server mysql-client - sudo apt-get -qq install -y make unzip g++ curl git wget ant openjdk-11-jdk eatmydata + 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/ diff --git a/.github/workflows/update_golang_dependencies.yml b/.github/workflows/update_golang_dependencies.yml index a614a634d83..33c5157097c 100644 --- a/.github/workflows/update_golang_dependencies.yml +++ b/.github/workflows/update_golang_dependencies.yml @@ -14,7 +14,7 @@ jobs: contents: write pull-requests: write name: Update Golang Dependencies - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Set up Go uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 diff --git a/.github/workflows/update_golang_version.yml b/.github/workflows/update_golang_version.yml index 503cad50ce8..ad5dceeafa5 100644 --- a/.github/workflows/update_golang_version.yml +++ b/.github/workflows/update_golang_version.yml @@ -17,7 +17,7 @@ jobs: matrix: branch: [ main, release-21.0, release-20.0, release-19.0 ] name: Update Golang Version - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Check out code uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 @@ -39,7 +39,7 @@ jobs: if [ ${{ matrix.branch }} == "main" ]; then go run ./go/tools/go-upgrade/go-upgrade.go upgrade --main --allow-major-upgrade - else if [ ${{ matrix.branch }} == "release-21.0" ]; then + elif [ ${{ matrix.branch }} == "release-21.0" ]; then go run ./go/tools/go-upgrade/go-upgrade.go upgrade else go run ./go/tools/go-upgrade/go-upgrade.go upgrade --workflow-update=false diff --git a/.github/workflows/upgrade_downgrade_test_backups_e2e.yml b/.github/workflows/upgrade_downgrade_test_backups_e2e.yml index f1c4f335f0d..a56aad2f523 100644 --- a/.github/workflows/upgrade_downgrade_test_backups_e2e.yml +++ b/.github/workflows/upgrade_downgrade_test_backups_e2e.yml @@ -13,7 +13,7 @@ jobs: upgrade_downgrade_test_e2e: timeout-minutes: 60 name: Run Upgrade Downgrade Test - Backups - E2E - runs-on: gh-hosted-runners-16cores-1 + runs-on: gh-hosted-runners-16cores-1-24.04 steps: - name: Skip CI @@ -87,7 +87,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' run: | sudo apt-get update - sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd curl git wget eatmydata + sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata sudo service mysql stop sudo service etcd stop sudo ln -s /etc/apparmor.d/usr.sbin.mysqld /etc/apparmor.d/disable/ 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 73c24c5e72e..7ee6c2db799 100644 --- a/.github/workflows/upgrade_downgrade_test_backups_e2e_next_release.yml +++ b/.github/workflows/upgrade_downgrade_test_backups_e2e_next_release.yml @@ -14,7 +14,7 @@ jobs: upgrade_downgrade_test_e2e: timeout-minutes: 60 name: Run Upgrade Downgrade Test - Backups - E2E - Next Release - runs-on: gh-hosted-runners-16cores-1 + runs-on: gh-hosted-runners-16cores-1-24.04 steps: - name: Skip CI @@ -89,7 +89,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' run: | sudo apt-get update - sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd curl git wget eatmydata + sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata sudo service mysql stop sudo service etcd stop sudo ln -s /etc/apparmor.d/usr.sbin.mysqld /etc/apparmor.d/disable/ diff --git a/.github/workflows/upgrade_downgrade_test_backups_manual.yml b/.github/workflows/upgrade_downgrade_test_backups_manual.yml index 9c866affb0b..00aab1b78ff 100644 --- a/.github/workflows/upgrade_downgrade_test_backups_manual.yml +++ b/.github/workflows/upgrade_downgrade_test_backups_manual.yml @@ -15,7 +15,7 @@ jobs: upgrade_downgrade_test_manual: timeout-minutes: 40 name: Run Upgrade Downgrade Test - Backups - Manual - runs-on: gh-hosted-runners-16cores-1 + runs-on: gh-hosted-runners-16cores-1-24.04 steps: - name: Skip CI @@ -102,14 +102,14 @@ jobs: # Install MySQL 8.0 sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 # Install everything else we need, and configure - sudo apt-get install -y make unzip g++ etcd curl git wget eatmydata grep + sudo apt-get install -y make unzip g++ etcd-client etcd-server curl git wget eatmydata grep sudo service mysql stop sudo service etcd 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 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 802ee78d940..768268271a1 100644 --- a/.github/workflows/upgrade_downgrade_test_backups_manual_next_release.yml +++ b/.github/workflows/upgrade_downgrade_test_backups_manual_next_release.yml @@ -15,7 +15,7 @@ jobs: upgrade_downgrade_test_manual: timeout-minutes: 40 name: Run Upgrade Downgrade Test - Backups - Manual - Next Release - runs-on: gh-hosted-runners-16cores-1 + runs-on: gh-hosted-runners-16cores-1-24.04 steps: - name: Skip CI @@ -103,14 +103,14 @@ jobs: # Install MySQL 8.0 sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 # Install everything else we need, and configure - sudo apt-get install -y make unzip g++ etcd curl git wget eatmydata grep + sudo apt-get install -y make unzip g++ etcd-client etcd-server curl git wget eatmydata grep sudo service mysql stop sudo service etcd 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 diff --git a/.github/workflows/upgrade_downgrade_test_onlineddl_flow.yml b/.github/workflows/upgrade_downgrade_test_onlineddl_flow.yml index 89e5f7c4d25..72426e70a61 100644 --- a/.github/workflows/upgrade_downgrade_test_onlineddl_flow.yml +++ b/.github/workflows/upgrade_downgrade_test_onlineddl_flow.yml @@ -16,7 +16,7 @@ jobs: upgrade_downgrade_test: name: Run Upgrade Downgrade Test - Online DDL flow - runs-on: gh-hosted-runners-16cores-1 + runs-on: gh-hosted-runners-16cores-1-24.04 steps: - name: Skip CI @@ -108,13 +108,13 @@ jobs: sudo rm -rf /etc/mysql # Install mysql80 sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 # Install everything else we need, and configure - sudo apt-get install -y make unzip g++ etcd curl git wget eatmydata + sudo apt-get install -y make unzip g++ etcd-client etcd-server curl git wget eatmydata sudo service mysql stop sudo service etcd 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 diff --git a/.github/workflows/upgrade_downgrade_test_query_serving_queries.yml b/.github/workflows/upgrade_downgrade_test_query_serving_queries.yml index f7d68dfee04..5ac1a55334c 100644 --- a/.github/workflows/upgrade_downgrade_test_query_serving_queries.yml +++ b/.github/workflows/upgrade_downgrade_test_query_serving_queries.yml @@ -16,7 +16,7 @@ jobs: upgrade_downgrade_test: name: Run Upgrade Downgrade Test - Query Serving (Queries) - runs-on: gh-hosted-runners-16cores-1 + runs-on: gh-hosted-runners-16cores-1-24.04 steps: - name: Skip CI @@ -100,13 +100,13 @@ jobs: sudo rm -rf /etc/mysql # Install mysql80 sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 # Install everything else we need, and configure - sudo apt-get install -y make unzip g++ etcd curl git wget eatmydata + sudo apt-get install -y make unzip g++ etcd-client etcd-server curl git wget eatmydata sudo service mysql stop sudo service etcd 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 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 e3a34bd43f8..c65fff08c52 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 @@ -16,7 +16,7 @@ jobs: upgrade_downgrade_test: name: Run Upgrade Downgrade Test - Query Serving (Queries) Next Release - runs-on: gh-hosted-runners-16cores-1 + runs-on: gh-hosted-runners-16cores-1-24.04 steps: - name: Skip CI @@ -101,13 +101,13 @@ jobs: sudo rm -rf /etc/mysql # Install mysql80 sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 # Install everything else we need, and configure - sudo apt-get install -y make unzip g++ etcd curl git wget eatmydata + sudo apt-get install -y make unzip g++ etcd-client etcd-server curl git wget eatmydata sudo service mysql stop sudo service etcd 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 diff --git a/.github/workflows/upgrade_downgrade_test_query_serving_schema.yml b/.github/workflows/upgrade_downgrade_test_query_serving_schema.yml index a9162d2f199..4b5fad0ab29 100644 --- a/.github/workflows/upgrade_downgrade_test_query_serving_schema.yml +++ b/.github/workflows/upgrade_downgrade_test_query_serving_schema.yml @@ -16,7 +16,7 @@ jobs: upgrade_downgrade_test: name: Run Upgrade Downgrade Test - Query Serving (Schema) - runs-on: gh-hosted-runners-16cores-1 + runs-on: gh-hosted-runners-16cores-1-24.04 steps: - name: Skip CI @@ -100,13 +100,13 @@ jobs: sudo rm -rf /etc/mysql # Install mysql80 sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 # Install everything else we need, and configure - sudo apt-get install -y make unzip g++ etcd curl git wget eatmydata + sudo apt-get install -y make unzip g++ etcd-client etcd-server curl git wget eatmydata sudo service mysql stop sudo service etcd 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 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 bf5f63d8836..6157079db6f 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 @@ -16,7 +16,7 @@ jobs: upgrade_downgrade_test: name: Run Upgrade Downgrade Test - Query Serving (Schema) Next Release - runs-on: gh-hosted-runners-16cores-1 + runs-on: gh-hosted-runners-16cores-1-24.04 steps: - name: Skip CI @@ -101,13 +101,13 @@ jobs: sudo rm -rf /etc/mysql # Install mysql80 sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 # Install everything else we need, and configure - sudo apt-get install -y make unzip g++ etcd curl git wget eatmydata + sudo apt-get install -y make unzip g++ etcd-client etcd-server curl git wget eatmydata sudo service mysql stop sudo service etcd 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 diff --git a/.github/workflows/upgrade_downgrade_test_reparent_new_vtctl.yml b/.github/workflows/upgrade_downgrade_test_reparent_new_vtctl.yml index ac6615b5788..931d9174c31 100644 --- a/.github/workflows/upgrade_downgrade_test_reparent_new_vtctl.yml +++ b/.github/workflows/upgrade_downgrade_test_reparent_new_vtctl.yml @@ -16,7 +16,7 @@ jobs: upgrade_downgrade_test: name: Run Upgrade Downgrade Test - Reparent New Vtctl - runs-on: gh-hosted-runners-16cores-1 + runs-on: gh-hosted-runners-16cores-1-24.04 steps: - name: Skip CI @@ -101,13 +101,13 @@ jobs: sudo rm -rf /etc/mysql # Install mysql80 sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 # Install everything else we need, and configure - sudo apt-get install -y make unzip g++ etcd curl git wget eatmydata + sudo apt-get install -y make unzip g++ etcd-client etcd-server curl git wget eatmydata sudo service mysql stop sudo service etcd 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 diff --git a/.github/workflows/upgrade_downgrade_test_reparent_new_vttablet.yml b/.github/workflows/upgrade_downgrade_test_reparent_new_vttablet.yml index e163ab35ea4..d77b7be53b3 100644 --- a/.github/workflows/upgrade_downgrade_test_reparent_new_vttablet.yml +++ b/.github/workflows/upgrade_downgrade_test_reparent_new_vttablet.yml @@ -16,7 +16,7 @@ jobs: upgrade_downgrade_test: name: Run Upgrade Downgrade Test - Reparent New VTTablet - runs-on: gh-hosted-runners-16cores-1 + runs-on: gh-hosted-runners-16cores-1-24.04 steps: - name: Skip CI @@ -101,13 +101,13 @@ jobs: sudo rm -rf /etc/mysql # Install mysql80 sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 # Install everything else we need, and configure - sudo apt-get install -y make unzip g++ etcd curl git wget eatmydata + sudo apt-get install -y make unzip g++ etcd-client etcd-server curl git wget eatmydata sudo service mysql stop sudo service etcd 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 diff --git a/.github/workflows/upgrade_downgrade_test_reparent_old_vtctl.yml b/.github/workflows/upgrade_downgrade_test_reparent_old_vtctl.yml index 93e94466b74..701025d7ecc 100644 --- a/.github/workflows/upgrade_downgrade_test_reparent_old_vtctl.yml +++ b/.github/workflows/upgrade_downgrade_test_reparent_old_vtctl.yml @@ -16,7 +16,7 @@ jobs: upgrade_downgrade_test: name: Run Upgrade Downgrade Test - Reparent Old Vtctl - runs-on: gh-hosted-runners-16cores-1 + runs-on: gh-hosted-runners-16cores-1-24.04 steps: - name: Skip CI @@ -100,13 +100,13 @@ jobs: sudo rm -rf /etc/mysql # Install mysql80 sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 # Install everything else we need, and configure - sudo apt-get install -y make unzip g++ etcd curl git wget eatmydata + sudo apt-get install -y make unzip g++ etcd-client etcd-server curl git wget eatmydata sudo service mysql stop sudo service etcd 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 diff --git a/.github/workflows/upgrade_downgrade_test_reparent_old_vttablet.yml b/.github/workflows/upgrade_downgrade_test_reparent_old_vttablet.yml index 12f6b514aa1..8b121d4af10 100644 --- a/.github/workflows/upgrade_downgrade_test_reparent_old_vttablet.yml +++ b/.github/workflows/upgrade_downgrade_test_reparent_old_vttablet.yml @@ -16,7 +16,7 @@ jobs: upgrade_downgrade_test: name: Run Upgrade Downgrade Test - Reparent Old VTTablet - runs-on: gh-hosted-runners-16cores-1 + runs-on: gh-hosted-runners-16cores-1-24.04 steps: - name: Skip CI @@ -100,13 +100,13 @@ jobs: sudo rm -rf /etc/mysql # Install mysql80 sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 # Install everything else we need, and configure - sudo apt-get install -y make unzip g++ etcd curl git wget eatmydata + sudo apt-get install -y make unzip g++ etcd-client etcd-server curl git wget eatmydata sudo service mysql stop sudo service etcd 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 diff --git a/.github/workflows/upgrade_downgrade_test_semi_sync.yml b/.github/workflows/upgrade_downgrade_test_semi_sync.yml index 16dac6e7fd2..f12e323654e 100644 --- a/.github/workflows/upgrade_downgrade_test_semi_sync.yml +++ b/.github/workflows/upgrade_downgrade_test_semi_sync.yml @@ -13,7 +13,7 @@ jobs: upgrade_downgrade_test_e2e: timeout-minutes: 60 name: Run Semi Sync Upgrade Downgrade Test - runs-on: gh-hosted-runners-16cores-1 + runs-on: gh-hosted-runners-16cores-1-24.04 steps: - name: Skip CI @@ -87,7 +87,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' run: | sudo apt-get update - sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd curl git wget eatmydata + sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata sudo service mysql stop sudo service etcd stop sudo ln -s /etc/apparmor.d/usr.sbin.mysqld /etc/apparmor.d/disable/ diff --git a/.github/workflows/vitess_tester_vtgate.yml b/.github/workflows/vitess_tester_vtgate.yml index 5f8c52d454b..7d5cf89a3ec 100644 --- a/.github/workflows/vitess_tester_vtgate.yml +++ b/.github/workflows/vitess_tester_vtgate.yml @@ -16,7 +16,7 @@ env: jobs: build: name: Run endtoend tests on Vitess Tester (vtgate) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -95,7 +95,7 @@ jobs: # Get key to latest MySQL repo 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.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 -qq update diff --git a/.github/workflows/vtadmin_web_build.yml b/.github/workflows/vtadmin_web_build.yml index 096b70cd002..822fa8a0920 100644 --- a/.github/workflows/vtadmin_web_build.yml +++ b/.github/workflows/vtadmin_web_build.yml @@ -16,7 +16,7 @@ permissions: read-all jobs: build: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI run: | diff --git a/.github/workflows/vtadmin_web_lint.yml b/.github/workflows/vtadmin_web_lint.yml index 5435b4b54fe..694c24734a6 100644 --- a/.github/workflows/vtadmin_web_lint.yml +++ b/.github/workflows/vtadmin_web_lint.yml @@ -16,7 +16,7 @@ permissions: read-all jobs: lint: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI run: | diff --git a/.github/workflows/vtadmin_web_unit_tests.yml b/.github/workflows/vtadmin_web_unit_tests.yml index b765a68fa76..72a887c1926 100644 --- a/.github/workflows/vtadmin_web_unit_tests.yml +++ b/.github/workflows/vtadmin_web_unit_tests.yml @@ -16,7 +16,7 @@ permissions: read-all jobs: unit-tests: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI run: | diff --git a/.github/workflows/vtop_example.yml b/.github/workflows/vtop_example.yml new file mode 100644 index 00000000000..fb5ae87c101 --- /dev/null +++ b/.github/workflows/vtop_example.yml @@ -0,0 +1,97 @@ +name: vtop_example +on: [push, pull_request] +concurrency: + group: format('{0}-{1}', ${{ github.ref }}, 'vtop_example') + cancel-in-progress: true + +jobs: + build: + name: VTop Example + runs-on: self-hosted + + 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 -s\ + -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@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + + - name: Check for changes in relevant files + if: steps.skip-workflow.outputs.skip-workflow == 'false' + uses: dorny/paths-filter@ebc4d7e9ebcb0b1eb21480bb8f43113e996ac77a # v3.0.1 + id: changes + with: + token: '' + filters: | + end_to_end: + - 'go/**/*.go' + - 'go/vt/sidecardb/**/*.sql' + - 'test.go' + - 'Makefile' + - 'build.env' + - 'go.[sumod]' + - 'proto/*.proto' + - 'tools/**' + - 'config/**' + - 'bootstrap.sh' + - 'examples/**' + - 'test/**' + - '.github/workflows/vtop_example.yml' + + - name: Set up Go + if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' + uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 + with: + go-version-file: go.mod + + - name: Tune the OS + if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' + run: | + echo '1024 65535' | sudo tee -a /proc/sys/net/ipv4/ip_local_port_range + + - name: Get dependencies + if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' + run: | + # Install everything we need, and configure + sudo apt-get install -y eatmydata make + go mod download + + # needed for vtctldclient + - name: Build vitess + if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' + run: | + make build + + - name: Install kubectl & kind + if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' + run: | + make install_kubectl_kind + + - name: vtop_example + if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' + timeout-minutes: 60 + run: | + source build.env + eatmydata -- go run test.go -docker=false -skip-build -print-log -follow -retry=1 -timeout=60m vtop_example \ No newline at end of file diff --git a/Makefile b/Makefile index 385a3238d47..13f7fde28e0 100644 --- a/Makefile +++ b/Makefile @@ -286,7 +286,7 @@ $(PROTO_GO_OUTS): minimaltools install_protoc-gen-go proto/*.proto # This rule builds the bootstrap images for all flavors. DOCKER_IMAGES_FOR_TEST = mysql80 percona80 DOCKER_IMAGES = common $(DOCKER_IMAGES_FOR_TEST) -BOOTSTRAP_VERSION=38 +BOOTSTRAP_VERSION=39 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 diff --git a/build.env b/build.env index 5c51b4dc660..58abbf41d1b 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.23.3 || echo "Go version reported: `go version`. Version 1.23.3+ recommended. See https://vitess.io/contributing/build-from-source for install instructions." +goversion_min 1.23.4 || echo "Go version reported: `go version`. Version 1.23.4+ recommended. See https://vitess.io/contributing/build-from-source for install instructions." mkdir -p dist mkdir -p bin diff --git a/changelog/19.0/19.0.8/changelog.md b/changelog/19.0/19.0.8/changelog.md new file mode 100644 index 00000000000..97995e779b7 --- /dev/null +++ b/changelog/19.0/19.0.8/changelog.md @@ -0,0 +1,29 @@ +# Changelog of Vitess v19.0.8 + +### Bug fixes +#### Topology + * [release-19.0] Close zookeeper topo connection on disconnect (#17136) [#17191](https://github.com/vitessio/vitess/pull/17191) +#### VTTablet + * [release-19.0] Fix deadlock in messager and health streamer (#17230) [#17233](https://github.com/vitessio/vitess/pull/17233) + * [release-19.0] Fix potential deadlock in health streamer (#17261) [#17268](https://github.com/vitessio/vitess/pull/17268) +### CI/Build +#### Build/CI + * [release-19.0] Specify Ubuntu 24.04 for all jobs (#17278) [#17280](https://github.com/vitessio/vitess/pull/17280) +#### Cluster management + * [release-19.0] Fix flakiness in `TestListenerShutdown` (#17024) [#17187](https://github.com/vitessio/vitess/pull/17187) +#### General + * [release-19.0] Upgrade the Golang version to `go1.22.9` [#17214](https://github.com/vitessio/vitess/pull/17214) +### Enhancement +#### Query Serving + * [release-19.0] Fix to prevent stopping buffering prematurely (#17013) [#17203](https://github.com/vitessio/vitess/pull/17203) +### Internal Cleanup +#### Build/CI + * [release-19.0] Change the name of the vitess-tester repository (#16917) [#17028](https://github.com/vitessio/vitess/pull/17028) +### Release +#### General + * [release-19.0] Bump to `v19.0.8-SNAPSHOT` after the `v19.0.7` release [#17158](https://github.com/vitessio/vitess/pull/17158) + * [release-19.0] Code Freeze for `v19.0.8` [#17310](https://github.com/vitessio/vitess/pull/17310) +### Testing +#### Build/CI + * [release-19.0] Flakes: Address flakiness in TestZkConnClosedOnDisconnect (#17194) [#17195](https://github.com/vitessio/vitess/pull/17195) + diff --git a/changelog/19.0/19.0.8/release_notes.md b/changelog/19.0/19.0.8/release_notes.md new file mode 100644 index 00000000000..ffb601fba4f --- /dev/null +++ b/changelog/19.0/19.0.8/release_notes.md @@ -0,0 +1,7 @@ +# Release of Vitess v19.0.8 +The entire changelog for this release can be found [here](https://github.com/vitessio/vitess/blob/main/changelog/19.0/19.0.8/changelog.md). + +The release includes 11 merged Pull Requests. + +Thanks to all our contributors: @app/vitess-bot, @frouioui, @vitess-bot + diff --git a/changelog/19.0/README.md b/changelog/19.0/README.md index 008c92c2aec..5893d3b1f4c 100644 --- a/changelog/19.0/README.md +++ b/changelog/19.0/README.md @@ -1,4 +1,8 @@ ## v19.0 +* **[19.0.8](19.0.8)** + * [Changelog](19.0.8/changelog.md) + * [Release Notes](19.0.8/release_notes.md) + * **[19.0.7](19.0.7)** * [Changelog](19.0.7/changelog.md) * [Release Notes](19.0.7/release_notes.md) diff --git a/changelog/20.0/20.0.4/changelog.md b/changelog/20.0/20.0.4/changelog.md new file mode 100644 index 00000000000..ec4af560368 --- /dev/null +++ b/changelog/20.0/20.0.4/changelog.md @@ -0,0 +1,27 @@ +# Changelog of Vitess v20.0.4 + +### Bug fixes +#### Query Serving + * [release-20.0] Use proper keyspace when updating the query graph of a reference DML (#17226) [#17257](https://github.com/vitessio/vitess/pull/17257) +#### Topology + * [release-20.0] Close zookeeper topo connection on disconnect (#17136) [#17192](https://github.com/vitessio/vitess/pull/17192) +#### VTTablet + * [release-20.0] Fix deadlock in messager and health streamer (#17230) [#17234](https://github.com/vitessio/vitess/pull/17234) + * [release-20.0] Fix potential deadlock in health streamer (#17261) [#17269](https://github.com/vitessio/vitess/pull/17269) +### CI/Build +#### Build/CI + * [release-20.0] Specify Ubuntu 24.04 for all jobs (#17278) [#17281](https://github.com/vitessio/vitess/pull/17281) +#### Cluster management + * [release-20.0] Fix flakiness in `TestListenerShutdown` (#17024) [#17188](https://github.com/vitessio/vitess/pull/17188) +#### General + * [release-20.0] Upgrade the Golang version to `go1.22.9` [#17212](https://github.com/vitessio/vitess/pull/17212) +### Enhancement +#### Query Serving + * [release-20.0] Fix to prevent stopping buffering prematurely (#17013) [#17204](https://github.com/vitessio/vitess/pull/17204) +### Internal Cleanup +#### Build/CI + * [release-20.0] Change the name of the vitess-tester repository (#16917) [#17029](https://github.com/vitessio/vitess/pull/17029) +### Testing +#### Build/CI + * [release-20.0] Flakes: Address flakiness in TestZkConnClosedOnDisconnect (#17194) [#17196](https://github.com/vitessio/vitess/pull/17196) + diff --git a/changelog/20.0/20.0.4/release_notes.md b/changelog/20.0/20.0.4/release_notes.md new file mode 100644 index 00000000000..42dc5b2b8a3 --- /dev/null +++ b/changelog/20.0/20.0.4/release_notes.md @@ -0,0 +1,7 @@ +# Release of Vitess v20.0.4 +The entire changelog for this release can be found [here](https://github.com/vitessio/vitess/blob/main/changelog/20.0/20.0.4/changelog.md). + +The release includes 10 merged Pull Requests. + +Thanks to all our contributors: @app/vitess-bot, @frouioui + diff --git a/changelog/20.0/README.md b/changelog/20.0/README.md index f41ea711fb8..2fe6e3d9d61 100644 --- a/changelog/20.0/README.md +++ b/changelog/20.0/README.md @@ -1,4 +1,8 @@ ## v20.0 +* **[20.0.4](20.0.4)** + * [Changelog](20.0.4/changelog.md) + * [Release Notes](20.0.4/release_notes.md) + * **[20.0.3](20.0.3)** * [Changelog](20.0.3/changelog.md) * [Release Notes](20.0.3/release_notes.md) diff --git a/changelog/21.0/21.0.0/summary.md b/changelog/21.0/21.0.0/summary.md index 512aa45a12f..1c34f5ad81a 100644 --- a/changelog/21.0/21.0.0/summary.md +++ b/changelog/21.0/21.0.0/summary.md @@ -9,6 +9,7 @@ - [Deprecated VTTablet Flags](#vttablet-flags) - [Deletion of deprecated metrics](#metric-deletion) - [Deprecated Metrics](#deprecations-metrics) + - **[RPC Changes](#rpc-changes)** - **[Traffic Mirroring](#traffic-mirroring)** - **[Atomic Distributed Transaction Support](#atomic-transaction)** - **[New VTGate Shutdown Behavior](#new-vtgate-shutdown-behavior)** @@ -77,6 +78,12 @@ The following metrics are now deprecated and will be deleted in a future release | `vttablet` | `QueryCacheHits` | `QueryEnginePlanCacheHits` | | `vttablet` | `QueryCacheMisses` | `QueryEnginePlanCacheMisses` | +### RPC Changes + +These are the RPC changes made in this release - +1. `ReadReparentJournalInfo` RPC has been added in TabletManagerClient interface, that is going to be used in EmergencyReparentShard for better errant GTID detection. +2. `PrimaryStatus` RPC in TabletManagerClient interface has been updated to also return the server UUID of the primary. This is going to be used in the vttablets so that they can do their own errant GTID detection in `SetReplicationSource`. + ### Traffic Mirroring Traffic mirroring is intended to help reduce some of the uncertainty inherent to `MoveTables SwitchTraffic`. When diff --git a/changelog/21.0/21.0.1/changelog.md b/changelog/21.0/21.0.1/changelog.md new file mode 100644 index 00000000000..1410591c3d5 --- /dev/null +++ b/changelog/21.0/21.0.1/changelog.md @@ -0,0 +1,57 @@ +# Changelog of Vitess v21.0.1 + +### Bug fixes +#### Backup and Restore + * [release-21.0] Fix how we cancel the context in the builtin backup engine (#17285) [#17291](https://github.com/vitessio/vitess/pull/17291) + * [release-21.0] S3: optional endpoint resolver and correct retrier [#17307](https://github.com/vitessio/vitess/pull/17307) +#### Cluster management + * [release-21.0] Fix panic in vttablet when closing topo server twice (#17094) [#17122](https://github.com/vitessio/vitess/pull/17122) +#### Online DDL + * [release-21.0] Online DDL: fix defer function, potential connection pool exhaustion (#17207) [#17210](https://github.com/vitessio/vitess/pull/17210) +#### Query Serving + * [release-21.0] bugfix: treat EXPLAIN like SELECT (#17054) [#17058](https://github.com/vitessio/vitess/pull/17058) + * [release-21.0] Delegate Column Availability Checks to MySQL for Single-Route Queries (#17077) [#17087](https://github.com/vitessio/vitess/pull/17087) + * [release-21.0] bugfix: Handle CTEs with columns named in the CTE def (#17179) [#17181](https://github.com/vitessio/vitess/pull/17181) + * [release-21.0] Use proper keyspace when updating the query graph of a reference DML (#17226) [#17258](https://github.com/vitessio/vitess/pull/17258) +#### Topology + * [release-21.0] Close zookeeper topo connection on disconnect (#17136) [#17193](https://github.com/vitessio/vitess/pull/17193) +#### VReplication + * [release-21.0] VReplication: Qualify and SQL escape tables in created AutoIncrement VSchema definitions (#17174) [#17176](https://github.com/vitessio/vitess/pull/17176) +#### VTTablet + * [release-21.0] Fix deadlock in messager and health streamer (#17230) [#17235](https://github.com/vitessio/vitess/pull/17235) + * [release-21.0] Fix potential deadlock in health streamer (#17261) [#17270](https://github.com/vitessio/vitess/pull/17270) +### CI/Build +#### Build/CI + * [release-21.0] Specify Ubuntu 24.04 for all jobs (#17278) [#17282](https://github.com/vitessio/vitess/pull/17282) +#### Cluster management + * [release-21.0] Fix flakiness in `TestListenerShutdown` (#17024) [#17189](https://github.com/vitessio/vitess/pull/17189) +#### General + * [release-21.0] Upgrade the Golang version to `go1.23.3` [#17211](https://github.com/vitessio/vitess/pull/17211) +### Dependencies +#### Java + * [release-21.0] java package updates for grpc and protobuf and release plugins (#17100) [#17105](https://github.com/vitessio/vitess/pull/17105) +### Documentation +#### Documentation + * [Direct PR][release-21.0] Add RPC changes segment in the summary doc [#17034](https://github.com/vitessio/vitess/pull/17034) +### Enhancement +#### Online DDL + * [release-21.0] Improve Schema Engine's TablesWithSize80 query (#17066) [#17091](https://github.com/vitessio/vitess/pull/17091) +#### Query Serving + * [release-21.0] Fix to prevent stopping buffering prematurely (#17013) [#17205](https://github.com/vitessio/vitess/pull/17205) +#### VReplication + * [release-21.0] Binlog: Improve ZstdInMemoryDecompressorMaxSize management (#17220) [#17241](https://github.com/vitessio/vitess/pull/17241) +### Internal Cleanup +#### Build/CI + * [release-21.0] Change the name of the vitess-tester repository (#16917) [#17030](https://github.com/vitessio/vitess/pull/17030) +### Regression +#### Backup and Restore + * [release-21.0] Fix unreachable errors when taking a backup (#17062) [#17112](https://github.com/vitessio/vitess/pull/17112) +### Release +#### General + * [release-21.0] Bump to `v21.0.1-SNAPSHOT` after the `v21.0.0` release [#17098](https://github.com/vitessio/vitess/pull/17098) +### Testing +#### Build/CI + * [release-21.0] Flakes: Address flakiness in TestZkConnClosedOnDisconnect (#17194) [#17197](https://github.com/vitessio/vitess/pull/17197) +#### Query Serving + * [release-21.0] fix: flaky test on twopc transaction (#17068) [#17070](https://github.com/vitessio/vitess/pull/17070) + diff --git a/changelog/21.0/21.0.1/release_notes.md b/changelog/21.0/21.0.1/release_notes.md new file mode 100644 index 00000000000..e42d16379a6 --- /dev/null +++ b/changelog/21.0/21.0.1/release_notes.md @@ -0,0 +1,7 @@ +# Release of Vitess v21.0.1 +The entire changelog for this release can be found [here](https://github.com/vitessio/vitess/blob/main/changelog/21.0/21.0.1/changelog.md). + +The release includes 25 merged Pull Requests. + +Thanks to all our contributors: @GuptaManan100, @app/vitess-bot, @frouioui, @vitess-bot + diff --git a/changelog/21.0/README.md b/changelog/21.0/README.md index a77e98bcaba..f3a98feb55a 100644 --- a/changelog/21.0/README.md +++ b/changelog/21.0/README.md @@ -1,4 +1,8 @@ ## v21.0 +* **[21.0.1](21.0.1)** + * [Changelog](21.0.1/changelog.md) + * [Release Notes](21.0.1/release_notes.md) + * **[21.0.0](21.0.0)** * [Changelog](21.0.0/changelog.md) * [Release Notes](21.0.0/release_notes.md) diff --git a/changelog/22.0/22.0.0/changelog.md b/changelog/22.0/22.0.0/changelog.md new file mode 100644 index 00000000000..03f2f714b7f --- /dev/null +++ b/changelog/22.0/22.0.0/changelog.md @@ -0,0 +1,5 @@ +# Changelog of Vitess v22.0.0 + +### Enhancement +#### Cluster management + * Prefer not promoting replicas that are taking backups [#16997](https://github.com/vitessio/vitess/pull/16997) diff --git a/changelog/22.0/22.0.0/summary.md b/changelog/22.0/22.0.0/summary.md index 6846913bed5..ebc0c485fc1 100644 --- a/changelog/22.0/22.0.0/summary.md +++ b/changelog/22.0/22.0.0/summary.md @@ -3,7 +3,13 @@ ### Table of Contents - **[Major Changes](#major-changes)** + - **[Deprecations and Deletions](#deprecations-and-deletions)** + - [Deprecated VTTablet Flags](#vttablet-flags) - **[RPC Changes](#rpc-changes)** + - **[Prefer not promoting a replica that is currently taking a backup](#reparents-prefer-not-backing-up)** + - **[VTOrc Config File Changes](#vtorc-config-file-changes)** +- **[Minor Changes](#minor-changes)** + - **[VTTablet Flags](#flags-vttablet)** ## Major Changes @@ -13,3 +19,51 @@ These are the RPC changes made in this release - 1. `GetTransactionInfo` RPC has been added to both `VtctldServer`, and `TabletManagerClient` interface. These RPCs are used to fecilitate the users in reading the state of an unresolved distributed transaction. This can be useful in debugging what went wrong and how to fix the problem. + +### Deprecations and Deletions + +#### Deprecated VTTablet Flags + +- `twopc_enable` flag is deprecated. Usage of TwoPC commit will be determined by the `transaction_mode` set on VTGate via flag or session variable. + +### Prefer not promoting a replica that is currently taking a backup + +Emergency reparents now prefer not promoting replicas that are currently taking backups with a backup engine other than +`builtin`. Note that if there's only one suitable replica to promote, and it is taking a backup, it will still be +promoted. + +For planned reparents, hosts taking backups with a backup engine other than `builtin` are filtered out of the list of +valid candidates. This means they will never get promoted - not even if there's no other candidates. + +Note that behavior for `builtin` backups remains unchanged: a replica that is currently taking a `builtin` backup will +never be promoted, neither by planned nor by emergency reparents. + +### VTOrc Config File Changes + +The configuration file for VTOrc has been updated to now support dynamic fields. The old `--config` parameter has been removed. The alternative is to use the `--config-file` parameter. The configuration can now be provided in json, yaml or any other format that [viper](https://github.com/spf13/viper) supports. + +The following fields can be dynamically changed - +1. `instance-poll-time` +2. `prevent-cross-cell-failover` +3. `snapshot-topology-interval` +4. `reasonable-replication-lag` +5. `audit-to-backend` +6. `audit-to-syslog` +7. `audit-purge-duration` +8. `wait-replicas-timeout` +9. `tolerable-replication-lag` +10. `topo-information-refresh-duration` +11. `recovery-poll-duration` +12. `allow-emergency-reparent` +13. `change-tablets-with-errant-gtid-to-drained` + +To upgrade to the newer version of the configuration file, first switch to using the flags in your current deployment before upgrading. Then you can switch to using the configuration file in the newer release. + + +## Minor Changes + +#### VTTablet Flags + +- `twopc_abandon_age` flag now supports values in the time.Duration format (e.g., 1s, 2m, 1h). +While the flag will continue to accept float values (interpreted as seconds) for backward compatibility, +**float inputs are deprecated** and will be removed in a future release. diff --git a/changelog/22.0/README.md b/changelog/22.0/README.md index 0e790e5dc0b..755f7e29563 100644 --- a/changelog/22.0/README.md +++ b/changelog/22.0/README.md @@ -1,2 +1,3 @@ ## v22.0 * **[22.0.0](22.0.0)** + * [Changelog](22.0.0/changelog.md) diff --git a/docker/bootstrap/CHANGELOG.md b/docker/bootstrap/CHANGELOG.md index 24ed5261df9..b52783d0c30 100644 --- a/docker/bootstrap/CHANGELOG.md +++ b/docker/bootstrap/CHANGELOG.md @@ -149,4 +149,8 @@ List of changes between bootstrap image versions. ## [38] - 2024-11-10 ### Changes -- Update build to golang 1.23.3 \ No newline at end of file +- Update build to golang 1.23.3 + +## [39] - 2024-12-04 +### Changes +- Update build to golang 1.23.4 \ No newline at end of file diff --git a/docker/bootstrap/Dockerfile.common b/docker/bootstrap/Dockerfile.common index f9654cc22d8..3ace19543f1 100644 --- a/docker/bootstrap/Dockerfile.common +++ b/docker/bootstrap/Dockerfile.common @@ -1,4 +1,4 @@ -FROM --platform=linux/amd64 golang:1.23.3-bullseye +FROM --platform=linux/amd64 golang:1.23.4-bullseye # Install Vitess build dependencies RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ diff --git a/docker/lite/Dockerfile b/docker/lite/Dockerfile index 59fd9297bbf..fff1414fab6 100644 --- a/docker/lite/Dockerfile +++ b/docker/lite/Dockerfile @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -FROM --platform=linux/amd64 golang:1.23.3-bullseye AS builder +FROM --platform=linux/amd64 golang:1.23.4-bullseye AS builder # Allows docker builds to set the BUILD_NUMBER ARG BUILD_NUMBER diff --git a/docker/lite/Dockerfile.percona80 b/docker/lite/Dockerfile.percona80 index 953a42c9fea..a839fb35514 100644 --- a/docker/lite/Dockerfile.percona80 +++ b/docker/lite/Dockerfile.percona80 @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -FROM --platform=linux/amd64 golang:1.23.3-bullseye AS builder +FROM --platform=linux/amd64 golang:1.23.4-bullseye AS builder # Allows docker builds to set the BUILD_NUMBER ARG BUILD_NUMBER diff --git a/docker/vttestserver/Dockerfile.mysql80 b/docker/vttestserver/Dockerfile.mysql80 index ed5d0b78acb..74b9564ef48 100644 --- a/docker/vttestserver/Dockerfile.mysql80 +++ b/docker/vttestserver/Dockerfile.mysql80 @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -FROM --platform=linux/amd64 golang:1.23.3-bullseye AS builder +FROM --platform=linux/amd64 golang:1.23.4-bullseye AS builder # Allows docker builds to set the BUILD_NUMBER ARG BUILD_NUMBER diff --git a/examples/common/scripts/vtctld-up.sh b/examples/common/scripts/vtctld-up.sh index 6902a851997..4a4b2587c4f 100755 --- a/examples/common/scripts/vtctld-up.sh +++ b/examples/common/scripts/vtctld-up.sh @@ -33,6 +33,7 @@ vtctld \ --port $vtctld_web_port \ --grpc_port $grpc_port \ --pid_file $VTDATAROOT/tmp/vtctld.pid \ + --pprof-http \ > $VTDATAROOT/tmp/vtctld.out 2>&1 & for _ in {0..300}; do diff --git a/examples/common/scripts/vtgate-up.sh b/examples/common/scripts/vtgate-up.sh index dbaaad02367..fd7860cf6ba 100755 --- a/examples/common/scripts/vtgate-up.sh +++ b/examples/common/scripts/vtgate-up.sh @@ -41,6 +41,7 @@ vtgate \ --pid_file $VTDATAROOT/tmp/vtgate.pid \ --enable_buffer \ --mysql_auth_server_impl none \ + --pprof-http \ > $VTDATAROOT/tmp/vtgate.out 2>&1 & # Block waiting for vtgate to be listening diff --git a/examples/common/scripts/vtorc-up.sh b/examples/common/scripts/vtorc-up.sh index 23ca4e62b48..807f522b1f7 100755 --- a/examples/common/scripts/vtorc-up.sh +++ b/examples/common/scripts/vtorc-up.sh @@ -11,7 +11,9 @@ vtorc \ $TOPOLOGY_FLAGS \ --logtostderr \ --alsologtostderr \ - --config="${script_dir}/../vtorc/config.json" \ + --config-path="${script_dir}/../vtorc/" \ + --config-name="config.yaml" \ + --config-type="yml" \ --port $port \ > "${log_dir}/vtorc.out" 2>&1 & diff --git a/examples/common/scripts/vttablet-up.sh b/examples/common/scripts/vttablet-up.sh index daa40aee894..282cd0553ea 100755 --- a/examples/common/scripts/vttablet-up.sh +++ b/examples/common/scripts/vttablet-up.sh @@ -54,6 +54,7 @@ vttablet \ --service_map 'grpc-queryservice,grpc-tabletmanager,grpc-updatestream' \ --pid_file $VTDATAROOT/$tablet_dir/vttablet.pid \ --heartbeat_on_demand_duration=5s \ + --pprof-http \ > $VTDATAROOT/$tablet_dir/vttablet.out 2>&1 & # Block waiting for the tablet to be listening diff --git a/examples/common/vtorc/config.json b/examples/common/vtorc/config.json deleted file mode 100644 index 53b012c2162..00000000000 --- a/examples/common/vtorc/config.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "RecoveryPeriodBlockSeconds": 1, - "InstancePollSeconds": 1 -} \ No newline at end of file diff --git a/examples/common/vtorc/config.yaml b/examples/common/vtorc/config.yaml new file mode 100644 index 00000000000..26af59afac8 --- /dev/null +++ b/examples/common/vtorc/config.yaml @@ -0,0 +1,13 @@ +instance-poll-time: 1s +prevent-cross-cell-failover: false +snapshot-topology-interval: 0h +reasonable-replication-lag: 10s +audit-to-backend: false +audit-to-syslog: false +audit-purge-duration: 168h +wait-replicas-timeout: 30s +tolerable-replication-lag: 0s +topo-information-refresh-duration: 15s +recovery-poll-duration: 1s +allow-emergency-reparent: true +change-tablets-with-errant-gtid-to-drained: false diff --git a/examples/operator/101_initial_cluster.yaml b/examples/operator/101_initial_cluster.yaml index c26219254f1..c044141dd4b 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 @@ -79,7 +79,7 @@ spec: cpu: 100m memory: 128Mi extraFlags: - recovery-period-block-duration: 5s + instance-poll-time: 1s partitionings: - equal: parts: 1 @@ -155,23 +155,6 @@ stringData: # Vitess defaults ############################################################################### - # Vitess-internal database. - CREATE DATABASE IF NOT EXISTS _vt; - # Note that definitions of local_metadata and shard_metadata should be the same - # as in production which is defined in go/vt/mysqlctl/metadata_tables.go. - CREATE TABLE IF NOT EXISTS _vt.local_metadata ( - name VARCHAR(255) NOT NULL, - value VARCHAR(255) NOT NULL, - db_name VARBINARY(255) NOT NULL, - PRIMARY KEY (db_name, name) - ) ENGINE=InnoDB; - CREATE TABLE IF NOT EXISTS _vt.shard_metadata ( - name VARCHAR(255) NOT NULL, - value MEDIUMBLOB NOT NULL, - db_name VARBINARY(255) NOT NULL, - PRIMARY KEY (db_name, name) - ) ENGINE=InnoDB; - # Admin user with all privileges. CREATE USER 'vt_dba'@'localhost'; GRANT ALL ON *.* TO 'vt_dba'@'localhost'; @@ -200,12 +183,10 @@ stringData: ON *.* TO 'vt_allprivs'@'localhost'; # User for slave replication connections. - # TODO: Should we set a password on this since it allows remote connections? CREATE USER 'vt_repl'@'%'; GRANT REPLICATION SLAVE ON *.* TO 'vt_repl'@'%'; - # User for Vitess filtered replication (binlog player). - # Same permissions as vt_app. + # User for Vitess VReplication (base vstreamers and vplayer). CREATE USER 'vt_filtered'@'localhost'; GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, RELOAD, PROCESS, FILE, REFERENCES, INDEX, ALTER, SHOW DATABASES, CREATE TEMPORARY TABLES, @@ -213,6 +194,13 @@ stringData: SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE, CREATE USER, EVENT, TRIGGER ON *.* TO 'vt_filtered'@'localhost'; + # User for general MySQL monitoring. + CREATE USER 'vt_monitoring'@'localhost'; + GRANT SELECT, PROCESS, SUPER, REPLICATION CLIENT, RELOAD + ON *.* TO 'vt_monitoring'@'localhost'; + GRANT SELECT, UPDATE, DELETE, DROP + ON performance_schema.* TO 'vt_monitoring'@'localhost'; + # custom sql is used to add custom scripts like creating users/passwords. We use it in our tests # {{custom_sql}} diff --git a/examples/operator/201_customer_tablets.yaml b/examples/operator/201_customer_tablets.yaml index 5800a5e05df..a16723ade57 100644 --- a/examples/operator/201_customer_tablets.yaml +++ b/examples/operator/201_customer_tablets.yaml @@ -75,7 +75,7 @@ spec: cpu: 100m memory: 128Mi extraFlags: - recovery-period-block-duration: 5s + instance-poll-time: 1s partitionings: - equal: parts: 1 diff --git a/examples/operator/302_new_shards.yaml b/examples/operator/302_new_shards.yaml index 2e15bc40d28..b954dab7beb 100644 --- a/examples/operator/302_new_shards.yaml +++ b/examples/operator/302_new_shards.yaml @@ -75,7 +75,7 @@ spec: cpu: 100m memory: 128Mi extraFlags: - recovery-period-block-duration: 5s + instance-poll-time: 1s partitionings: - equal: parts: 1 diff --git a/examples/operator/306_down_shard_0.yaml b/examples/operator/306_down_shard_0.yaml index 4bdb694d678..7206d1b4e64 100644 --- a/examples/operator/306_down_shard_0.yaml +++ b/examples/operator/306_down_shard_0.yaml @@ -75,7 +75,7 @@ spec: cpu: 100m memory: 128Mi extraFlags: - recovery-period-block-duration: 5s + instance-poll-time: 1s partitionings: - equal: parts: 1 diff --git a/examples/operator/401_scheduled_backups.yaml b/examples/operator/401_scheduled_backups.yaml index 0e74ada8478..9e8ed29aa9f 100644 --- a/examples/operator/401_scheduled_backups.yaml +++ b/examples/operator/401_scheduled_backups.yaml @@ -116,7 +116,7 @@ spec: cpu: 100m memory: 128Mi extraFlags: - recovery-period-block-duration: 5s + instance-poll-time: 1s partitionings: - equal: parts: 1 diff --git a/go.mod b/go.mod index ccc4cc32b3b..6fd800b80ab 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module vitess.io/vitess -go 1.23.3 +go 1.23.4 require ( cloud.google.com/go/storage v1.46.0 @@ -66,13 +66,13 @@ require ( go.etcd.io/etcd/client/pkg/v3 v3.5.17 go.etcd.io/etcd/client/v3 v3.5.17 go.uber.org/mock v0.2.0 - golang.org/x/crypto v0.29.0 // indirect + golang.org/x/crypto v0.31.0 // indirect golang.org/x/mod v0.22.0 // indirect golang.org/x/net v0.31.0 golang.org/x/oauth2 v0.24.0 - golang.org/x/sys v0.27.0 - golang.org/x/term v0.26.0 - golang.org/x/text v0.20.0 // indirect + golang.org/x/sys v0.28.0 + golang.org/x/term v0.27.0 + golang.org/x/text v0.21.0 // indirect golang.org/x/time v0.8.0 golang.org/x/tools v0.27.0 google.golang.org/api v0.205.0 @@ -108,7 +108,7 @@ require ( github.com/xlab/treeprint v1.2.0 go.uber.org/goleak v1.3.0 golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f - golang.org/x/sync v0.9.0 + golang.org/x/sync v0.10.0 gonum.org/v1/gonum v0.14.0 modernc.org/sqlite v1.33.1 ) diff --git a/go.sum b/go.sum index 167a620da78..3a7c5e07599 100644 --- a/go.sum +++ b/go.sum @@ -624,8 +624,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U 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.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ= -golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg= +golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f h1:XdNn9LlyWAhLVp6P/i8QYBW+hlyhrhei9uErw2B5GJo= golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f/go.mod h1:D5SMRVC3C2/4+F/DB1wZsLRnSNimn2Sp/NPsCrsv8ak= @@ -673,8 +673,8 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/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-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ= -golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= +golang.org/x/sync v0.10.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= @@ -712,17 +712,17 @@ golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b/go.mod h1:oPkhp1MJrh7nUepCBc 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.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= -golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.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.26.0 h1:WEQa6V3Gja/BhNxg540hBip/kkaYtRg3cxg4oXSw4AU= -golang.org/x/term v0.26.0/go.mod h1:Si5m1o57C5nBNQo5z1iq+XDijt21BDBDp2bK0QI8e3E= +golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= +golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug= -golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg= golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/go/cmd/vtorc/cli/cli.go b/go/cmd/vtorc/cli/cli.go index 1233c1e2ac2..b79793c6492 100644 --- a/go/cmd/vtorc/cli/cli.go +++ b/go/cmd/vtorc/cli/cli.go @@ -20,6 +20,7 @@ import ( "github.com/spf13/cobra" "vitess.io/vitess/go/acl" + "vitess.io/vitess/go/viperutil/debug" "vitess.io/vitess/go/vt/log" "vitess.io/vitess/go/vt/servenv" "vitess.io/vitess/go/vt/vtorc/config" @@ -29,8 +30,7 @@ import ( ) var ( - configFile string - Main = &cobra.Command{ + Main = &cobra.Command{ Use: "vtorc", Short: "VTOrc is the automated fault detection and repair tool in Vitess.", Example: `vtorc \ @@ -51,22 +51,16 @@ var ( func run(cmd *cobra.Command, args []string) { servenv.Init() - config.UpdateConfigValuesFromFlags() inst.RegisterStats() log.Info("starting vtorc") - if len(configFile) > 0 { - config.ForceRead(configFile) - } else { - config.Read("/etc/vtorc.conf.json", "conf/vtorc.conf.json", "vtorc.conf.json") - } - if config.Config.AuditToSyslog { + if config.GetAuditToSyslog() { inst.EnableAuditSyslog() } config.MarkConfigurationLoaded() // Log final config values to debug if something goes wrong. - config.LogConfigValues() + log.Infof("Running with Configuration - %v", debug.AllSettings()) server.StartVTOrcDiscovery() server.RegisterVTOrcAPIEndpoints() @@ -96,7 +90,5 @@ func init() { servenv.MoveFlagsToCobraCommand(Main) logic.RegisterFlags(Main.Flags()) - config.RegisterFlags(Main.Flags()) acl.RegisterFlags(Main.Flags()) - Main.Flags().StringVar(&configFile, "config", "", "config file name") } diff --git a/go/flags/endtoend/vtcombo.txt b/go/flags/endtoend/vtcombo.txt index 01a391d0cad..7348fcf1753 100644 --- a/go/flags/endtoend/vtcombo.txt +++ b/go/flags/endtoend/vtcombo.txt @@ -395,8 +395,7 @@ Flags: --transaction_limit_per_user float Maximum number of transactions a single user is allowed to use at any time, represented as fraction of -transaction_cap. (default 0.4) --transaction_mode string SINGLE: disallow multi-db transactions, MULTI: allow multi-db transactions with best effort commit, TWOPC: allow multi-db transactions with 2pc commit (default "MULTI") --truncate-error-len int truncate errors sent to client if they are longer than this value (0 means do not truncate) - --twopc_abandon_age float time in seconds. Any unresolved transaction older than this time will be sent to the coordinator to be resolved. - --twopc_enable if the flag is on, 2pc is enabled. Other 2pc flags must be supplied. + --twopc_abandon_age time.Duration Any unresolved transaction older than this time will be sent to the coordinator to be resolved. NOTE: Providing time as seconds (float64) is deprecated. Use time.Duration format (e.g., '1s', '2m', '1h'). (default 15m0s) --tx-throttler-config string Synonym to -tx_throttler_config (default "target_replication_lag_sec:2 max_replication_lag_sec:10 initial_rate:100 max_increase:1 emergency_decrease:0.5 min_duration_between_increases_sec:40 max_duration_between_increases_sec:62 min_duration_between_decreases_sec:20 spread_backlog_across_sec:20 age_bad_rate_after_sec:180 bad_rate_increase:0.1 max_rate_approach_threshold:0.9") --tx-throttler-default-priority int Default priority assigned to queries that lack priority information (default 100) --tx-throttler-dry-run If present, the transaction throttler only records metrics about requests received and throttled, but does not actually throttle any requests. @@ -414,7 +413,7 @@ Flags: --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_experimental_flags int (Bitmask) of experimental features in vreplication to enable (default 7) --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) diff --git a/go/flags/endtoend/vtorc.txt b/go/flags/endtoend/vtorc.txt index d34b4404df7..efccb0afdfc 100644 --- a/go/flags/endtoend/vtorc.txt +++ b/go/flags/endtoend/vtorc.txt @@ -25,7 +25,6 @@ Flags: --catch-sigpipe catch and ignore SIGPIPE on stdout and stderr if specified --change-tablets-with-errant-gtid-to-drained Whether VTOrc should be changing the type of tablets with errant GTIDs to DRAINED --clusters_to_watch strings Comma-separated list of keyspaces or keyspace/shards that this instance will monitor and repair. Defaults to all clusters in the topology. Example: "ks1,ks2/-80" - --config string config file name --config-file string Full path of the config file (with extension) to use. If set, --config-path, --config-type, and --config-name are ignored. --config-file-not-found-handling ConfigFileNotFoundHandling Behavior when a config file is not found. (Options: error, exit, ignore, warn) (default warn) --config-name string Name of the config file (without extension) to search for. (default "vtconfig") diff --git a/go/flags/endtoend/vttablet.txt b/go/flags/endtoend/vttablet.txt index 8be7b620469..e4c6fde66af 100644 --- a/go/flags/endtoend/vttablet.txt +++ b/go/flags/endtoend/vttablet.txt @@ -395,8 +395,7 @@ Flags: --transaction_limit_by_subcomponent Include CallerID.subcomponent when considering who the user is for the purpose of transaction limit. --transaction_limit_by_username Include VTGateCallerID.username when considering who the user is for the purpose of transaction limit. (default true) --transaction_limit_per_user float Maximum number of transactions a single user is allowed to use at any time, represented as fraction of -transaction_cap. (default 0.4) - --twopc_abandon_age float time in seconds. Any unresolved transaction older than this time will be sent to the coordinator to be resolved. - --twopc_enable if the flag is on, 2pc is enabled. Other 2pc flags must be supplied. + --twopc_abandon_age time.Duration Any unresolved transaction older than this time will be sent to the coordinator to be resolved. NOTE: Providing time as seconds (float64) is deprecated. Use time.Duration format (e.g., '1s', '2m', '1h'). (default 15m0s) --tx-throttler-config string Synonym to -tx_throttler_config (default "target_replication_lag_sec:2 max_replication_lag_sec:10 initial_rate:100 max_increase:1 emergency_decrease:0.5 min_duration_between_increases_sec:40 max_duration_between_increases_sec:62 min_duration_between_decreases_sec:20 spread_backlog_across_sec:20 age_bad_rate_after_sec:180 bad_rate_increase:0.1 max_rate_approach_threshold:0.9") --tx-throttler-default-priority int Default priority assigned to queries that lack priority information (default 100) --tx-throttler-dry-run If present, the transaction throttler only records metrics about requests received and throttled, but does not actually throttle any requests. @@ -414,7 +413,7 @@ Flags: --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_experimental_flags int (Bitmask) of experimental features in vreplication to enable (default 7) --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) diff --git a/go/flagutil/float_to_duration.go b/go/flagutil/float_to_duration.go new file mode 100644 index 00000000000..fe07d42bfaa --- /dev/null +++ b/go/flagutil/float_to_duration.go @@ -0,0 +1,71 @@ +/* +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 ( + "errors" + "strconv" + "time" + + "github.com/spf13/pflag" +) + +// FloatOrDuration is a flag that can be set with either a float64 (interpreted as seconds) or a time.Duration +// The parsed value is stored in the Duration field, and the target pointer is updated with the parsed value +type FloatOrDuration struct { + Target *time.Duration // Pointer to the external duration + Duration time.Duration // Stores the current parsed value +} + +// String returns the current value as a string +func (f *FloatOrDuration) String() string { + return f.Duration.String() +} + +// Set parses the input and updates the duration +func (f *FloatOrDuration) Set(value string) error { + // Try to parse as float64 first (interpreted as seconds) + if floatVal, err := strconv.ParseFloat(value, 64); err == nil { + f.Duration = time.Duration(floatVal * float64(time.Second)) + *f.Target = f.Duration // Update the target pointer + return nil + } + + // Try to parse as time.Duration + if duration, err := time.ParseDuration(value); err == nil { + f.Duration = duration + *f.Target = f.Duration // Update the target pointer + return nil + } + + return errors.New("value must be either a float64 (interpreted as seconds) or a valid time.Duration") +} + +// Type returns the type description +func (f *FloatOrDuration) Type() string { + return "time.Duration" +} + +// FloatDuration defines a flag with the specified name, default value, and usage string and binds it to a time.Duration variable. +func FloatDuration(fs *pflag.FlagSet, p *time.Duration, name string, defaultValue time.Duration, usage string) { + *p = defaultValue + fd := FloatOrDuration{ + Target: p, + Duration: defaultValue, + } + fs.Var(&fd, name, usage) +} diff --git a/go/flagutil/float_to_duration_test.go b/go/flagutil/float_to_duration_test.go new file mode 100644 index 00000000000..50b3a36e94a --- /dev/null +++ b/go/flagutil/float_to_duration_test.go @@ -0,0 +1,104 @@ +/* +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" + "time" + + "github.com/spf13/pflag" + "github.com/stretchr/testify/assert" +) + +// TestFloatOrDuration_ValidFloat64Input verifies that a float64 input +// (representing seconds) is correctly converted to a time.Duration. +func TestFloatOrDuration_ValidFloat64Input(t *testing.T) { + var duration time.Duration + fs := pflag.NewFlagSet("test", pflag.ContinueOnError) + + FloatDuration(fs, &duration, "test_flag", 10*time.Second, "Test flag") + err := fs.Parse([]string{"--test_flag=2"}) + assert.NoError(t, err) + assert.Equal(t, 2*time.Second, duration) +} + +// TestFloatOrDuration_ValidDurationInput verifies that a valid time.Duration +// input (e.g., "1m30s") is parsed and stored correctly. +func TestFloatOrDuration_ValidDurationInput(t *testing.T) { + var duration time.Duration + fs := pflag.NewFlagSet("test", pflag.ContinueOnError) + + FloatDuration(fs, &duration, "test_flag", 10*time.Second, "Test flag") + err := fs.Parse([]string{"--test_flag=1m30s"}) + assert.NoError(t, err) + assert.Equal(t, 90*time.Second, duration) +} + +// TestFloatOrDuration_DefaultValue ensures that the default value is correctly +// assigned to the duration when the flag is not provided. +func TestFloatOrDuration_DefaultValue(t *testing.T) { + var duration time.Duration + fs := pflag.NewFlagSet("test", pflag.ContinueOnError) + + defaultValue := 15 * time.Second + FloatDuration(fs, &duration, "test_flag", defaultValue, "Test flag") + err := fs.Parse([]string{}) + assert.NoError(t, err) + assert.Equal(t, defaultValue, duration) +} + +// TestFloatOrDuration_InvalidInput verifies that an invalid input string +// results in an appropriate error. +func TestFloatOrDuration_InvalidInput(t *testing.T) { + var duration time.Duration + fs := pflag.NewFlagSet("test", pflag.ContinueOnError) + + FloatDuration(fs, &duration, "test_flag", 10*time.Second, "Test flag") + err := fs.Parse([]string{"--test_flag=invalid"}) + assert.Error(t, err) + assert.Contains(t, err.Error(), "value must be either a float64 (interpreted as seconds) or a valid time.Duration") +} + +// TestFloatOrDuration_MultipleFlags ensures that multiple FloatDuration flags +// can coexist and maintain independent values. +func TestFloatOrDuration_MultipleFlags(t *testing.T) { + var duration1, duration2 time.Duration + fs := pflag.NewFlagSet("test", pflag.ContinueOnError) + + FloatDuration(fs, &duration1, "flag1", 10*time.Second, "First test flag") + FloatDuration(fs, &duration2, "flag2", 20*time.Second, "Second test flag") + + err := fs.Parse([]string{"--flag1=2.5", "--flag2=1m"}) + assert.NoError(t, err) + assert.Equal(t, 2500*time.Millisecond, duration1) + assert.Equal(t, 1*time.Minute, duration2) +} + +// TestFloatOrDuration_HelpMessage verifies that the help message includes +// the correct flag name, description, and default value. +func TestFloatOrDuration_HelpMessage(t *testing.T) { + var duration time.Duration + fs := pflag.NewFlagSet("test", pflag.ContinueOnError) + + defaultValue := 10 * time.Second + FloatDuration(fs, &duration, "test_flag", defaultValue, "Test flag with default value") + + helpOutput := fs.FlagUsages() + assert.Contains(t, helpOutput, "--test_flag time.Duration") + assert.Contains(t, helpOutput, "Test flag with default value") + assert.Contains(t, helpOutput, "(default 10s)") +} diff --git a/go/mysql/binlog_event_compression.go b/go/mysql/binlog_event_compression.go index 7455218d4b5..37d28431087 100644 --- a/go/mysql/binlog_event_compression.go +++ b/go/mysql/binlog_event_compression.go @@ -98,6 +98,11 @@ type TransactionPayload struct { payload []byte reader io.Reader iterator func() (BinlogEvent, error) + // StreamingContents tells the consumer that we are streaming the + // decompressed payload and they should also stream the events. + // This ensures that neither the producer nor the consumer are + // holding the entire payload's contents in memory. + StreamingContents bool } // IsTransactionPayload returns true if a compressed transaction @@ -292,6 +297,8 @@ func (tp *TransactionPayload) decompress() error { } compressedTrxPayloadsUsingStream.Add(1) tp.reader = streamDecoder + // Signal the consumer to also stream the contents. + tp.StreamingContents = true return nil } diff --git a/go/mysql/binlog_event_mysql56_test.go b/go/mysql/binlog_event_mysql56_test.go index 861d98c6e4f..5844779de63 100644 --- a/go/mysql/binlog_event_mysql56_test.go +++ b/go/mysql/binlog_event_mysql56_test.go @@ -186,9 +186,11 @@ func TestMysql56DecodeTransactionPayload(t *testing.T) { } } if tc.inMemory { + require.False(t, tp.StreamingContents) require.Equal(t, memDecodingCnt+1, compressedTrxPayloadsInMem.Get()) require.Equal(t, tc.want, eventStrs) } else { + require.True(t, tp.StreamingContents) require.Equal(t, streamDecodingCnt+1, compressedTrxPayloadsUsingStream.Get()) require.Len(t, eventStrs, len(tc.want)) totalSize := 0 diff --git a/go/mysql/sqlerror/constants.go b/go/mysql/sqlerror/constants.go index da2351bf00d..5fedf567998 100644 --- a/go/mysql/sqlerror/constants.go +++ b/go/mysql/sqlerror/constants.go @@ -203,6 +203,7 @@ const ( ERBlobKeyWithoutLength = ErrorCode(1170) ERPrimaryCantHaveNull = ErrorCode(1171) ERTooManyRows = ErrorCode(1172) + ERErrorDuringCommit = ErrorCode(1180) ERLockOrActiveTransaction = ErrorCode(1192) ERUnknownSystemVariable = ErrorCode(1193) ERSetConstantsOnly = ErrorCode(1204) @@ -301,6 +302,195 @@ const ( ERServerIsntAvailable = ErrorCode(3168) ) +// HandlerErrorCode is for errors thrown by the handler, and which are then embedded in other errors. +// See https://github.com/mysql/mysql-server/blob/trunk/include/my_base.h +type HandlerErrorCode uint16 + +func (e HandlerErrorCode) ToString() string { + return strconv.FormatUint(uint64(e), 10) +} + +const ( + // Didn't find key on read or update + HaErrKeyNotFound = HandlerErrorCode(120) + // Duplicate key on write + HaErrFoundDuppKey = HandlerErrorCode(121) + // Internal error + HaErrInternalError = HandlerErrorCode(122) + // Uppdate with is recoverable + HaErrRecordChanged = HandlerErrorCode(123) + // Wrong index given to function + HaErrWrongIndex = HandlerErrorCode(124) + // Transaction has been rolled back + HaErrRolledBack = HandlerErrorCode(125) + // Indexfile is crashed + HaErrCrashed = HandlerErrorCode(126) + // Record-file is crashed + HaErrWrongInRecord = HandlerErrorCode(127) + // Record-file is crashed + HaErrOutOfMem = HandlerErrorCode(128) + // not a MYI file - no signature + HaErrNotATable = HandlerErrorCode(130) + // Command not supported + HaErrWrongCommand = HandlerErrorCode(131) + // old database file + HaErrOldFile = HandlerErrorCode(132) + // No record read in update() + HaErrNoActiveRecord = HandlerErrorCode(133) + // A record is not there + HaErrRecordDeleted = HandlerErrorCode(134) + // No more room in file + HaErrRecordFileFull = HandlerErrorCode(135) + // No more room in file + HaErrIndexFileFull = HandlerErrorCode(136) + // end in next/prev/first/last + HaErrEndOfFile = HandlerErrorCode(137) + // unsupported extension used + HaErrUnsupported = HandlerErrorCode(138) + // Too big row + HaErrTooBigRow = HandlerErrorCode(139) + // Wrong create option + HaWrongCreateOption = HandlerErrorCode(140) + // Duplicate unique on write + HaErrFoundDuppUnique = HandlerErrorCode(141) + // Can't open charset + HaErrUnknownCharset = HandlerErrorCode(142) + // conflicting tables in MERGE + HaErrWrongMrgTableDef = HandlerErrorCode(143) + // Last (automatic?) repair failed + HaErrCrashedOnRepair = HandlerErrorCode(144) + // Table must be repaired + HaErrCrashedOnUsage = HandlerErrorCode(145) + // Lock wait timeout + HaErrLockWaitTimeout = HandlerErrorCode(146) + // Lock table is full + HaErrLockTableFull = HandlerErrorCode(147) + // Updates not allowed + HaErrReadOnlyTransaction = HandlerErrorCode(148) + // Deadlock found when trying to get lock + HaErrLockDeadlock = HandlerErrorCode(149) + // Cannot add a foreign key constr. + HaErrCannotAddForeign = HandlerErrorCode(150) + // Cannot add a child row + HaErrNoReferencedRow = HandlerErrorCode(151) + // Cannot delete a parent row + HaErrRowIsReferenced = HandlerErrorCode(152) + // No savepoint with that name + HaErrNoSavepoint = HandlerErrorCode(153) + // Non unique key block size + HaErrNonUniqueBlockSize = HandlerErrorCode(154) + // The table does not exist in engine + HaErrNoSuchTable = HandlerErrorCode(155) + // The table existed in storage engine + HaErrTableExist = HandlerErrorCode(156) + // Could not connect to storage engine + HaErrNoConnection = HandlerErrorCode(157) + // NULLs are not supported in spatial index + HaErrNullInSpatial = HandlerErrorCode(158) + // The table changed in storage engine + HaErrTableDefChanged = HandlerErrorCode(159) + // There's no partition in table for given value + HaErrNoPartitionFound = HandlerErrorCode(160) + // Row-based binlogging of row failed + HaErrRbrLoggingFailed = HandlerErrorCode(161) + // Index needed in foreign key constraint + HaErrDropIndexFk = HandlerErrorCode(162) + // Upholding foreign key constraints would lead to a duplicate key error in some other table. + HaErrForeignDuplicateKey = HandlerErrorCode(163) + // The table changed in storage engine + HaErrTableNeedsUpgrade = HandlerErrorCode(164) + // The table is not writable + HaErrTableReadonly = HandlerErrorCode(165) + // Failed to get next autoinc value + HaErrAutoincReadFailed = HandlerErrorCode(166) + // Failed to set row autoinc value + HaErrAutoincErange = HandlerErrorCode(167) + // Generic error + HaErrGeneric = HandlerErrorCode(168) + // row not actually updated: new values same as the old values + HaErrRecordIsTheSame = HandlerErrorCode(169) + // It is not possible to log this statement + HaErrLoggingImpossible = HandlerErrorCode(170) + // The event was corrupt, leading to illegal data being read + HaErrCorruptEvent = HandlerErrorCode(171) + // New file format + HaErrNewFile = HandlerErrorCode(172) + // The event could not be processed no other handler error happened + HaErrRowsEventApply = HandlerErrorCode(173) + // Error during initialization + HaErrInitialization = HandlerErrorCode(174) + // File too short + HaErrFileTooShort = HandlerErrorCode(175) + // Wrong CRC on page + HaErrWrongCrc = HandlerErrorCode(176) + // Too many active concurrent transactions + HaErrTooManyConcurrentTrxs = HandlerErrorCode(177) + // There's no explicitly listed partition in table for the given value + HaErrNotInLockPartitions = HandlerErrorCode(178) + // Index column length exceeds limit + HaErrIndexColTooLong = HandlerErrorCode(179) + // InnoDB index corrupted + HaErrIndexCorrupt = HandlerErrorCode(180) + // Undo log record too big + HaErrUndoRecTooBig = HandlerErrorCode(181) + // Invalid InnoDB Doc ID + HaFtsInvalidDocid = HandlerErrorCode(182) + // Table being used in foreign key check + HaErrTableInFkCheck = HandlerErrorCode(183) + // The tablespace existed in storage engine + HaErrTablespaceExists = HandlerErrorCode(184) + // Table has too many columns + HaErrTooManyFields = HandlerErrorCode(185) + // Row in wrong partition + HaErrRowInWrongPartition = HandlerErrorCode(186) + // InnoDB is in read only mode. + HaErrInnodbReadOnly = HandlerErrorCode(187) + // FTS query exceeds result cache limit + HaErrFtsExceedResultCacheLimit = HandlerErrorCode(188) + // Temporary file write failure + HaErrTempFileWriteFailure = HandlerErrorCode(189) + // Innodb is in force recovery mode + HaErrInnodbForcedRecovery = HandlerErrorCode(190) + // Too many words in a phrase + HaErrFtsTooManyWordsInPhrase = HandlerErrorCode(191) + // FK cascade depth exceeded + HaErrFkDepthExceeded = HandlerErrorCode(192) + // Option Missing during Create + HaMissingCreateOption = HandlerErrorCode(193) + // Out of memory in storage engine + HaErrSeOutOfMemory = HandlerErrorCode(194) + // Table/Clustered index is corrupted. + HaErrTableCorrupt = HandlerErrorCode(195) + // The query was interrupted + HaErrQueryInterrupted = HandlerErrorCode(196) + // Missing Tablespace + HaErrTablespaceMissing = HandlerErrorCode(197) + // Tablespace is not empty + HaErrTablespaceIsNotEmpty = HandlerErrorCode(198) + // Invalid Filename + HaErrWrongFileName = HandlerErrorCode(199) + // Operation is not allowed + HaErrNotAllowedCommand = HandlerErrorCode(200) + // Compute generated column value failed + HaErrComputeFailed = HandlerErrorCode(201) + // Table's row format has changed in the storage engine. Information in the data-dictionary needs to be updated. + HaErrRowFormatChanged = HandlerErrorCode(202) + // Don't wait for record lock + HaErrNoWaitLock = HandlerErrorCode(203) + // No more room in disk + HaErrDiskFullNowait = HandlerErrorCode(204) + // No session temporary space available + HaErrNoSessionTemp = HandlerErrorCode(205) + // Wrong or Invalid table name + HaErrWrongTableName = HandlerErrorCode(206) + // Path is too long for the OS + HaErrTooLongPath = HandlerErrorCode(207) + // Histogram sampling initialization failed + HaErrSamplingInitFailed = HandlerErrorCode(208) + // Too many sub-expression in search string + HaErrFtsTooManyNestedExp = HandlerErrorCode(209) +) + // Sql states for errors. // Originally found in include/mysql/sql_state.h const ( diff --git a/go/mysql/sqlerror/sql_error.go b/go/mysql/sqlerror/sql_error.go index f2a5fb46388..4600f0927cc 100644 --- a/go/mysql/sqlerror/sql_error.go +++ b/go/mysql/sqlerror/sql_error.go @@ -53,6 +53,18 @@ func NewSQLError(number ErrorCode, sqlState string, msg string) *SQLError { } } +var handlerErrExtract = regexp.MustCompile(`Got error ([0-9]*) [-] .* (from storage engine|during COMMIT|during ROLLBACK)`) + +func (se *SQLError) HaErrorCode() HandlerErrorCode { + match := handlerErrExtract.FindStringSubmatch(se.Message) + if len(match) >= 1 { + if code, err := strconv.ParseUint(match[1], 10, 16); err == nil { + return HandlerErrorCode(code) + } + } + return 0 +} + // Error implements the error interface func (se *SQLError) Error() string { var buf strings.Builder diff --git a/go/mysql/sqlerror/sql_error_test.go b/go/mysql/sqlerror/sql_error_test.go index b38cec26388..9e73138d60f 100644 --- a/go/mysql/sqlerror/sql_error_test.go +++ b/go/mysql/sqlerror/sql_error_test.go @@ -57,6 +57,7 @@ func TestNewSQLErrorFromError(t *testing.T) { var tCases = []struct { err error num ErrorCode + ha HandlerErrorCode ss string }{ { @@ -179,6 +180,24 @@ func TestNewSQLErrorFromError(t *testing.T) { num: ERDupEntry, ss: SSConstraintViolation, }, + { + err: fmt.Errorf("ERROR HY000: Got error 204 - 'No more room in disk' during COMMIT"), + num: ERUnknownError, + ss: SSUnknownSQLState, + ha: HaErrDiskFullNowait, + }, + { + err: fmt.Errorf("COMMIT failed w/ error: Got error 204 - 'No more room in disk' during COMMIT (errno 1180) (sqlstate HY000) during query: commit"), + num: ERErrorDuringCommit, + ss: SSUnknownSQLState, + ha: HaErrDiskFullNowait, + }, + { + err: fmt.Errorf("COMMIT failed w/ error: Got error 149 - 'Lock deadlock; Retry transaction' during COMMIT (errno 1180) (sqlstate HY000) during query: commit"), + num: ERErrorDuringCommit, + ss: SSUnknownSQLState, + ha: HaErrLockDeadlock, + }, } for _, tc := range tCases { @@ -187,6 +206,8 @@ func TestNewSQLErrorFromError(t *testing.T) { require.ErrorAs(t, NewSQLErrorFromError(tc.err), &err) assert.Equal(t, tc.num, err.Number()) assert.Equal(t, tc.ss, err.SQLState()) + ha := err.HaErrorCode() + assert.Equal(t, tc.ha, ha) }) } } diff --git a/go/osutil/loadavg.go b/go/osutil/loadavg.go new file mode 100644 index 00000000000..0c71f9e18b1 --- /dev/null +++ b/go/osutil/loadavg.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 osutil + +import ( + "fmt" + "strconv" + "strings" +) + +// parseLoadAvg parses the load average from the content of /proc/loadavg or sysctl output. +// Input such as "1.00 0.99 0.98 1/1 1", "2.83 3.01 3.36" +func parseLoadAvg(content string) (float64, error) { + fields := strings.Fields(content) + if len(fields) == 0 { + return 0, fmt.Errorf("unexpected loadavg content: %s", content) + } + return strconv.ParseFloat(fields[0], 64) +} diff --git a/go/osutil/loadavg_darwin.go b/go/osutil/loadavg_darwin.go new file mode 100644 index 00000000000..f7b5b6e492a --- /dev/null +++ b/go/osutil/loadavg_darwin.go @@ -0,0 +1,40 @@ +//go:build darwin + +/* +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 osutil + +import ( + "fmt" + "os/exec" +) + +// LoadAvg returns the past 1 minute system load average. This works on linux and darwin systems. +// On other systems, it returns 0 with no error. +func LoadAvg() (float64, error) { + cmd := exec.Command("sysctl", "-n", "vm.loadavg") + // Sample output: `{ 2.83 3.01 3.36 }` + output, err := cmd.CombinedOutput() + if err != nil { + return 0, err + } + if len(output) < 1 { + return 0, fmt.Errorf("unexpected sysctl output: %q", output) + } + output = output[1:] // Remove the leading `{ ` + return parseLoadAvg(string(output)) +} diff --git a/go/osutil/loadavg_linux.go b/go/osutil/loadavg_linux.go new file mode 100644 index 00000000000..7663bf60ab7 --- /dev/null +++ b/go/osutil/loadavg_linux.go @@ -0,0 +1,33 @@ +//go:build linux + +/* +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 osutil + +import ( + "os" +) + +// LoadAvg returns the past 1 minute system load average. This works on linux and darwin systems. +// On other systems, it returns 0 with no error. +func LoadAvg() (float64, error) { + content, err := os.ReadFile("/proc/loadavg") + if err != nil { + return 0, err + } + return parseLoadAvg(string(content)) +} diff --git a/go/osutil/loadavg_other.go b/go/osutil/loadavg_other.go new file mode 100644 index 00000000000..a516cb46a66 --- /dev/null +++ b/go/osutil/loadavg_other.go @@ -0,0 +1,25 @@ +//go:build !linux && !darwin + +/* +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 osutil + +// LoadAvg returns the past 1 minute system load average. This works on linux and darwin systems. +// On other systems, it returns 0 with no error. +func LoadAvg() (float64, error) { + return 0, nil +} diff --git a/go/osutil/loadavg_test.go b/go/osutil/loadavg_test.go new file mode 100644 index 00000000000..5f3831648c6 --- /dev/null +++ b/go/osutil/loadavg_test.go @@ -0,0 +1,77 @@ +/* +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 osutil + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestLoadAvgValue(t *testing.T) { + tcases := []struct { + input string + loadavg float64 + isError bool + }{ + { + input: "", + isError: true, + }, + { + input: "{}", + isError: true, + }, + { + input: "{ x y z }", + isError: true, + }, + { + input: "1", + loadavg: 1.0, + }, + { + input: "0.00 0.00 0.00 1/1 1", + loadavg: 0.0, + }, + { + input: "2.72 2.89 3.17", + loadavg: 2.72, + }, + { + input: " 2.72 2.89 3.17", + loadavg: 2.72, + }, + } + for _, tcase := range tcases { + t.Run(tcase.input, func(t *testing.T) { + loadavg, err := parseLoadAvg(tcase.input) + if tcase.isError { + assert.Error(t, err) + } else { + assert.NoError(t, err) + assert.Equal(t, tcase.loadavg, loadavg) + } + }) + } +} + +func TestLoadAvg(t *testing.T) { + loadavg, err := LoadAvg() + assert.NoError(t, err) + assert.GreaterOrEqual(t, loadavg, 0.0) +} diff --git a/go/sqltypes/value.go b/go/sqltypes/value.go index 438b51a13ba..7fb6fa80396 100644 --- a/go/sqltypes/value.go +++ b/go/sqltypes/value.go @@ -26,6 +26,7 @@ import ( "math/big" "strconv" "strings" + "time" "google.golang.org/protobuf/encoding/protowire" @@ -435,6 +436,100 @@ func (v Value) String() string { return fmt.Sprintf("%v(%s)", Type(v.typ), v.val) } +// ToTime returns the value as a time.Time in UTC. +// NULL values are returned as zero time. +func (v Value) ToTime() (time.Time, error) { + return v.ToTimeInLocation(time.UTC) +} + +// ToTimeInLocation returns the value as a time.Time in the provided location. +// NULL values are returned as zero time. +func (v Value) ToTimeInLocation(loc *time.Location) (time.Time, error) { + if v.Type() == Null { + return time.Time{}, nil + } + switch v.Type() { + case Datetime, Timestamp: + return datetimeToNative(v, loc) + case Date: + return dateToNative(v, loc) + default: + return time.Time{}, ErrIncompatibleTypeCast + } +} + +// ErrInvalidTime is returned when we fail to parse a datetime +// string from MySQL. This should never happen unless things are +// seriously messed up. +var ErrInvalidTime = errors.New("invalid MySQL time string") + +var isoTimeFormat = "2006-01-02 15:04:05.999999" +var isoNullTime = "0000-00-00 00:00:00.000000" +var isoTimeLength = len(isoTimeFormat) + +// parseISOTime pases a time string in MySQL's textual datetime format. +// This is very similar to ISO8601, with some differences: +// +// - There is no T separator between the date and time sections; +// a space is used instead. +// - There is never a timezone section in the string, as these datetimes +// are not timezone-aware. There isn't a Z value for UTC times for +// the same reason. +// +// Note that this function can handle both DATE (which should _always_ have +// a length of 10) and DATETIME strings (which have a variable length, 18+ +// depending on the number of decimal sub-second places). +// +// Also note that this function handles the case where MySQL returns a NULL +// time (with a string where all sections are zeroes) by returning a zeroed +// out time.Time object. NULL time strings are not considered a parsing error. +// +// See: isoTimeFormat +func parseISOTime(tstr string, loc *time.Location, minLen, maxLen int) (t time.Time, err error) { + tlen := len(tstr) + if tlen < minLen || tlen > maxLen { + err = ErrInvalidTime + return + } + + if tstr == isoNullTime[:tlen] { + // This is what MySQL would send when the date is NULL, + // so return an empty time.Time instead. + // This is not a parsing error + return + } + + if loc == nil { + loc = time.UTC + } + + // Since the time format returned from MySQL never has a Timezone + // section, ParseInLocation will initialize the time.Time struct + // with the default `loc` we're passing here. + return time.ParseInLocation(isoTimeFormat[:tlen], tstr, loc) +} + +// datetimeToNative converts a Datetime Value into a time.Time +func datetimeToNative(v Value, loc *time.Location) (time.Time, error) { + // Valid format string offsets for a DATETIME + // |DATETIME |19+ + // |------------------|------| + // "2006-01-02 15:04:05.999999" + return parseISOTime(v.ToString(), loc, 19, isoTimeLength) +} + +// dateToNative converts a Date Value into a time.Time. +// Note that there's no specific type in the Go stdlib to represent +// dates without time components, so the returned Time will have +// their hours/mins/seconds zeroed out. +func dateToNative(v Value, loc *time.Location) (time.Time, error) { + // Valid format string offsets for a DATE + // |DATE |10 + // |---------| + // "2006-01-02 00:00:00.000000" + return parseISOTime(v.ToString(), loc, 10, 10) +} + // EncodeSQL encodes the value into an SQL statement. Can be binary. func (v Value) EncodeSQL(b BinWriter) { switch { diff --git a/go/sqltypes/value_test.go b/go/sqltypes/value_test.go index fbf9be5b19b..99f8566472e 100644 --- a/go/sqltypes/value_test.go +++ b/go/sqltypes/value_test.go @@ -18,8 +18,10 @@ package sqltypes import ( "math" + "reflect" "strings" "testing" + "time" "github.com/stretchr/testify/assert" @@ -611,6 +613,155 @@ func TestToUint32(t *testing.T) { } } +var randomLocation = time.FixedZone("Nowhere", 3*60*60) + +func DatetimeValue(str string) Value { + return TestValue(Datetime, str) +} + +func DateValue(str string) Value { + return TestValue(Date, str) +} + +func TestDatetimeToNative(t *testing.T) { + tcases := []struct { + val Value + loc *time.Location + out time.Time + err bool + }{{ + val: DatetimeValue("1899-08-24 17:20:00"), + out: time.Date(1899, 8, 24, 17, 20, 0, 0, time.UTC), + }, { + val: DatetimeValue("1952-03-11 01:02:03"), + loc: time.Local, + out: time.Date(1952, 3, 11, 1, 2, 3, 0, time.Local), + }, { + val: DatetimeValue("1952-03-11 01:02:03"), + loc: randomLocation, + out: time.Date(1952, 3, 11, 1, 2, 3, 0, randomLocation), + }, { + val: DatetimeValue("1952-03-11 01:02:03"), + loc: time.UTC, + out: time.Date(1952, 3, 11, 1, 2, 3, 0, time.UTC), + }, { + val: DatetimeValue("1899-08-24 17:20:00.000000"), + out: time.Date(1899, 8, 24, 17, 20, 0, 0, time.UTC), + }, { + val: DatetimeValue("1899-08-24 17:20:00.000001"), + out: time.Date(1899, 8, 24, 17, 20, 0, int(1*time.Microsecond), time.UTC), + }, { + val: DatetimeValue("1899-08-24 17:20:00.123456"), + out: time.Date(1899, 8, 24, 17, 20, 0, int(123456*time.Microsecond), time.UTC), + }, { + val: DatetimeValue("1899-08-24 17:20:00.222"), + out: time.Date(1899, 8, 24, 17, 20, 0, int(222*time.Millisecond), time.UTC), + }, { + val: DatetimeValue("1899-08-24 17:20:00.1234567"), + err: true, + }, { + val: DatetimeValue("1899-08-24 17:20:00.1"), + out: time.Date(1899, 8, 24, 17, 20, 0, int(100*time.Millisecond), time.UTC), + }, { + val: DatetimeValue("0000-00-00 00:00:00"), + out: time.Time{}, + }, { + val: DatetimeValue("0000-00-00 00:00:00.0"), + out: time.Time{}, + }, { + val: DatetimeValue("0000-00-00 00:00:00.000"), + out: time.Time{}, + }, { + val: DatetimeValue("0000-00-00 00:00:00.000000"), + out: time.Time{}, + }, { + val: DatetimeValue("0000-00-00 00:00:00.0000000"), + err: true, + }, { + val: DatetimeValue("1899-08-24T17:20:00.000000"), + err: true, + }, { + val: DatetimeValue("1899-02-31 17:20:00.000000"), + err: true, + }, { + val: DatetimeValue("1899-08-24 17:20:00."), + out: time.Date(1899, 8, 24, 17, 20, 0, 0, time.UTC), + }, { + val: DatetimeValue("0000-00-00 00:00:00.000001"), + err: true, + }, { + val: DatetimeValue("1899-08-24 17:20:00 +02:00"), + err: true, + }, { + val: DatetimeValue("1899-08-24"), + err: true, + }, { + val: DatetimeValue("This is not a valid timestamp"), + err: true, + }} + + for _, tcase := range tcases { + got, err := datetimeToNative(tcase.val, tcase.loc) + if tcase.err && err == nil { + t.Errorf("datetimeToNative(%v, %#v) succeeded; expected error", tcase.val, tcase.loc) + } + if !tcase.err && err != nil { + t.Errorf("datetimeToNative(%v, %#v) failed: %v", tcase.val, tcase.loc, err) + } + if !reflect.DeepEqual(got, tcase.out) { + t.Errorf("datetimeToNative(%v, %#v): %v, want %v", tcase.val, tcase.loc, got, tcase.out) + } + } +} + +func TestDateToNative(t *testing.T) { + tcases := []struct { + val Value + loc *time.Location + out time.Time + err bool + }{{ + val: DateValue("1899-08-24"), + out: time.Date(1899, 8, 24, 0, 0, 0, 0, time.UTC), + }, { + val: DateValue("1952-03-11"), + loc: time.Local, + out: time.Date(1952, 3, 11, 0, 0, 0, 0, time.Local), + }, { + val: DateValue("1952-03-11"), + loc: randomLocation, + out: time.Date(1952, 3, 11, 0, 0, 0, 0, randomLocation), + }, { + val: DateValue("0000-00-00"), + out: time.Time{}, + }, { + val: DateValue("1899-02-31"), + err: true, + }, { + val: DateValue("1899-08-24 17:20:00"), + err: true, + }, { + val: DateValue("0000-00-00 00:00:00"), + err: true, + }, { + val: DateValue("This is not a valid timestamp"), + err: true, + }} + + for _, tcase := range tcases { + got, err := dateToNative(tcase.val, tcase.loc) + if tcase.err && err == nil { + t.Errorf("dateToNative(%v, %#v) succeeded; expected error", tcase.val, tcase.loc) + } + if !tcase.err && err != nil { + t.Errorf("dateToNative(%v, %#v) failed: %v", tcase.val, tcase.loc, err) + } + if !reflect.DeepEqual(got, tcase.out) { + t.Errorf("dateToNative(%v, %#v): %v, want %v", tcase.val, tcase.loc, got, tcase.out) + } + } +} + func TestEncodeSQLStringBuilder(t *testing.T) { testcases := []struct { in Value diff --git a/go/test/endtoend/backup/s3/s3_builtin_test.go b/go/test/endtoend/backup/s3/s3_builtin_test.go new file mode 100644 index 00000000000..9c83e5f8fec --- /dev/null +++ b/go/test/endtoend/backup/s3/s3_builtin_test.go @@ -0,0 +1,434 @@ +/* +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 s3 + +import ( + "context" + "io" + "os" + "os/exec" + "path" + "strconv" + "strings" + "testing" + "time" + + "log" + + "github.com/minio/minio-go" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "vitess.io/vitess/go/mysql/fakesqldb" + "vitess.io/vitess/go/mysql/replication" + "vitess.io/vitess/go/vt/logutil" + "vitess.io/vitess/go/vt/mysqlctl" + "vitess.io/vitess/go/vt/mysqlctl/backupstats" + "vitess.io/vitess/go/vt/mysqlctl/blackbox" + "vitess.io/vitess/go/vt/mysqlctl/s3backupstorage" +) + +/* + These tests use Minio to emulate AWS S3. It allows us to run the tests on + GitHub Actions without having the security burden of carrying out AWS secrets + in our GitHub repo. + + Minio is almost a drop-in replacement for AWS S3, if you want to run these + tests against a true AWS S3 Bucket, you can do so by not running the TestMain + and setting the 'AWS_*' environment variables to your own values. + + This package and file are named 'endtoend', but it's more an integration test. + However, we don't want our CI infra to mistake this for a regular unit-test, + hence the rename to 'endtoend'. +*/ + +func TestMain(m *testing.M) { + f := func() int { + minioPath, err := exec.LookPath("minio") + if err != nil { + log.Fatalf("minio binary not found: %v", err) + } + + dataDir, err := os.MkdirTemp("", "") + if err != nil { + log.Fatalf("could not create temporary directory: %v", err) + } + err = os.MkdirAll(dataDir, 0755) + if err != nil { + log.Fatalf("failed to create MinIO data directory: %v", err) + } + + cmd := exec.Command(minioPath, "server", dataDir, "--console-address", ":9001") + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + + err = cmd.Start() + if err != nil { + log.Fatalf("failed to start MinIO: %v", err) + } + defer func() { + cmd.Process.Kill() + }() + + // Local MinIO credentials + accessKey := "minioadmin" + secretKey := "minioadmin" + minioEndpoint := "http://localhost:9000" + bucketName := "test-bucket" + region := "us-east-1" + + client, err := minio.New("localhost:9000", accessKey, secretKey, false) + if err != nil { + log.Fatalf("failed to create MinIO client: %v", err) + } + waitForMinio(client) + + err = client.MakeBucket(bucketName, region) + if err != nil { + log.Fatalf("failed to create test bucket: %v", err) + } + + // Same env variables that are used between AWS S3 and Minio + os.Setenv("AWS_ACCESS_KEY_ID", accessKey) + os.Setenv("AWS_SECRET_ACCESS_KEY", secretKey) + os.Setenv("AWS_BUCKET", bucketName) + os.Setenv("AWS_ENDPOINT", minioEndpoint) + os.Setenv("AWS_REGION", region) + + return m.Run() + } + + os.Exit(f()) +} + +func waitForMinio(client *minio.Client) { + for i := 0; i < 60; i++ { + _, err := client.ListBuckets() + if err == nil { + return + } + time.Sleep(1 * time.Second) + } + log.Fatalf("MinIO server did not become ready in time") +} + +func checkEnvForS3(t *testing.T) { + // We never want to skip the tests if we are running on CI. + // We will always run these tests on CI with the TestMain and Minio. + // There should not be a need to skip the tests due to missing ENV vars. + if os.Getenv("GITHUB_ACTIONS") != "" { + return + } + + envRequired := []string{ + "AWS_ACCESS_KEY_ID", + "AWS_SECRET_ACCESS_KEY", + "AWS_BUCKET", + "AWS_ENDPOINT", + "AWS_REGION", + } + + var missing []string + for _, s := range envRequired { + if os.Getenv(s) == "" { + missing = append(missing, s) + } + } + if len(missing) > 0 { + t.Skipf("missing AWS secrets to run this test: please set: %s", strings.Join(missing, ", ")) + } +} + +type backupTestConfig struct { + concurrency int + addFileReturnFn func(s3 *s3backupstorage.S3BackupHandle, ctx context.Context, filename string, filesize int64, firstAdd bool) (io.WriteCloser, error) + checkCleanupError bool + expectedResult mysqlctl.BackupResult + expectedStats blackbox.StatSummary +} + +func runBackupTest(t *testing.T, cfg backupTestConfig) { + checkEnvForS3(t) + s3backupstorage.InitFlag(s3backupstorage.FakeConfig{ + Region: os.Getenv("AWS_REGION"), + Endpoint: os.Getenv("AWS_ENDPOINT"), + Bucket: os.Getenv("AWS_BUCKET"), + ForcePath: true, + }) + + ctx := context.Background() + backupRoot, keyspace, shard, ts := blackbox.SetupCluster(ctx, t, 2, 2) + + be := &mysqlctl.BuiltinBackupEngine{} + + // Configure a tight deadline to force a timeout + oldDeadline := blackbox.SetBuiltinBackupMysqldDeadline(time.Second) + defer blackbox.SetBuiltinBackupMysqldDeadline(oldDeadline) + + fakeStats := backupstats.NewFakeStats() + logger := logutil.NewMemoryLogger() + + bh, err := s3backupstorage.NewFakeS3BackupHandle(ctx, t.Name(), time.Now().Format(mysqlctl.BackupTimestampFormat), logger, fakeStats) + require.NoError(t, err) + t.Cleanup(func() { + err := bh.AbortBackup(ctx) + if cfg.checkCleanupError { + require.NoError(t, err) + } + }) + bh.AddFileReturnF = cfg.addFileReturnFn + + // Spin up a fake daemon to be used in backups. It needs to be allowed to receive: + // "STOP REPLICA", "START REPLICA", in that order. + fakedb := fakesqldb.New(t) + defer fakedb.Close() + mysqld := mysqlctl.NewFakeMysqlDaemon(fakedb) + defer mysqld.Close() + mysqld.ExpectedExecuteSuperQueryList = []string{"STOP REPLICA", "START REPLICA"} + + backupResult, err := be.ExecuteBackup(ctx, mysqlctl.BackupParams{ + Logger: logger, + Mysqld: mysqld, + Cnf: &mysqlctl.Mycnf{ + InnodbDataHomeDir: path.Join(backupRoot, "innodb"), + InnodbLogGroupHomeDir: path.Join(backupRoot, "log"), + DataDir: path.Join(backupRoot, "datadir"), + }, + Concurrency: cfg.concurrency, + HookExtraEnv: map[string]string{}, + TopoServer: ts, + Keyspace: keyspace, + Shard: shard, + Stats: fakeStats, + MysqlShutdownTimeout: blackbox.MysqlShutdownTimeout, + }, bh) + + require.Equal(t, cfg.expectedResult, backupResult) + switch cfg.expectedResult { + case mysqlctl.BackupUsable: + require.NoError(t, err) + case mysqlctl.BackupUnusable, mysqlctl.BackupEmpty: + require.Error(t, err) + } + + ss := blackbox.GetStats(fakeStats) + require.Equal(t, cfg.expectedStats.DestinationCloseStats, ss.DestinationCloseStats) + require.Equal(t, cfg.expectedStats.DestinationOpenStats, ss.DestinationOpenStats) + require.Equal(t, cfg.expectedStats.DestinationWriteStats, ss.DestinationWriteStats) + require.Equal(t, cfg.expectedStats.SourceCloseStats, ss.SourceCloseStats) + require.Equal(t, cfg.expectedStats.SourceOpenStats, ss.SourceOpenStats) + require.Equal(t, cfg.expectedStats.SourceReadStats, ss.SourceReadStats) +} + +func TestExecuteBackupS3FailEachFileOnce(t *testing.T) { + runBackupTest(t, backupTestConfig{ + concurrency: 2, + + // Modify the fake S3 storage to always fail when trying to write a file for the first time + addFileReturnFn: s3backupstorage.FailFirstWrite, + checkCleanupError: true, + expectedResult: mysqlctl.BackupUsable, + + // Even though we have 4 files, we expect '8' for all the values below as we re-do every file once. + expectedStats: blackbox.StatSummary{ + DestinationCloseStats: 8, + DestinationOpenStats: 8, + DestinationWriteStats: 8, + SourceCloseStats: 8, + SourceOpenStats: 8, + SourceReadStats: 8, + }, + }) +} + +func TestExecuteBackupS3FailEachFileTwice(t *testing.T) { + runBackupTest(t, backupTestConfig{ + concurrency: 1, + + // Modify the fake S3 storage to always fail when trying to write a file for the first time + addFileReturnFn: s3backupstorage.FailAllWrites, + + // If the code works as expected by this test, no files will be created on S3 and AbortBackup will + // fail, for this reason, let's not check the error return. + // We still call AbortBackup anyway in the event that the code is not behaving as expected and some + // files were created by mistakes, we delete them. + checkCleanupError: false, + expectedResult: mysqlctl.BackupUnusable, + + // All stats here must be equal to 5, we have four files, we go each of them, they all fail. + // The logic decides to retry each file once, we retry the first failed file, it fails again + // but since it has reached the limit of retries, the backup will fail anyway, thus we don't + // retry the other 3 files. + expectedStats: blackbox.StatSummary{ + DestinationCloseStats: 5, + DestinationOpenStats: 5, + DestinationWriteStats: 5, + SourceCloseStats: 5, + SourceOpenStats: 5, + SourceReadStats: 5, + }, + }) +} + +type restoreTestConfig struct { + readFileReturnFn func(s3 *s3backupstorage.S3BackupHandle, ctx context.Context, filename string, firstRead bool) (io.ReadCloser, error) + expectSuccess bool + expectedStats blackbox.StatSummary +} + +func runRestoreTest(t *testing.T, cfg restoreTestConfig) { + checkEnvForS3(t) + s3backupstorage.InitFlag(s3backupstorage.FakeConfig{ + Region: os.Getenv("AWS_REGION"), + Endpoint: os.Getenv("AWS_ENDPOINT"), + Bucket: os.Getenv("AWS_BUCKET"), + ForcePath: true, + }) + + ctx := context.Background() + backupRoot, keyspace, shard, ts := blackbox.SetupCluster(ctx, t, 2, 2) + + fakeStats := backupstats.NewFakeStats() + logger := logutil.NewMemoryLogger() + + be := &mysqlctl.BuiltinBackupEngine{} + dirName := time.Now().Format(mysqlctl.BackupTimestampFormat) + name := t.Name() + "-" + strconv.Itoa(int(time.Now().Unix())) + bh, err := s3backupstorage.NewFakeS3BackupHandle(ctx, name, dirName, logger, fakeStats) + require.NoError(t, err) + t.Cleanup(func() { + require.NoError(t, bh.AbortBackup(ctx)) + }) + + // Spin up a fake daemon to be used in backups. It needs to be allowed to receive: + // "STOP REPLICA", "START REPLICA", in that order. + fakedb := fakesqldb.New(t) + defer fakedb.Close() + mysqld := mysqlctl.NewFakeMysqlDaemon(fakedb) + defer mysqld.Close() + mysqld.ExpectedExecuteSuperQueryList = []string{"STOP REPLICA", "START REPLICA"} + + backupResult, err := be.ExecuteBackup(ctx, mysqlctl.BackupParams{ + Logger: logutil.NewConsoleLogger(), + Mysqld: mysqld, + Cnf: &mysqlctl.Mycnf{ + InnodbDataHomeDir: path.Join(backupRoot, "innodb"), + InnodbLogGroupHomeDir: path.Join(backupRoot, "log"), + DataDir: path.Join(backupRoot, "datadir"), + }, + Stats: backupstats.NewFakeStats(), + Concurrency: 1, + HookExtraEnv: map[string]string{}, + TopoServer: ts, + Keyspace: keyspace, + Shard: shard, + MysqlShutdownTimeout: blackbox.MysqlShutdownTimeout, + }, bh) + + require.NoError(t, err) + require.Equal(t, mysqlctl.BackupUsable, backupResult) + + // Backup is done, let's move on to the restore now + + restoreBh, err := s3backupstorage.NewFakeS3RestoreHandle(ctx, name, logger, fakeStats) + require.NoError(t, err) + restoreBh.ReadFileReturnF = cfg.readFileReturnFn + + fakedb = fakesqldb.New(t) + defer fakedb.Close() + mysqld = mysqlctl.NewFakeMysqlDaemon(fakedb) + defer mysqld.Close() + mysqld.ExpectedExecuteSuperQueryList = []string{"STOP REPLICA", "START REPLICA"} + + restoreParams := mysqlctl.RestoreParams{ + Cnf: &mysqlctl.Mycnf{ + InnodbDataHomeDir: path.Join(backupRoot, "innodb"), + InnodbLogGroupHomeDir: path.Join(backupRoot, "log"), + DataDir: path.Join(backupRoot, "datadir"), + BinLogPath: path.Join(backupRoot, "binlog"), + RelayLogPath: path.Join(backupRoot, "relaylog"), + RelayLogIndexPath: path.Join(backupRoot, "relaylogindex"), + RelayLogInfoPath: path.Join(backupRoot, "relayloginfo"), + }, + Logger: logger, + Mysqld: mysqld, + Concurrency: 1, + 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: blackbox.MysqlShutdownTimeout, + } + + // Successful restore. + bm, err := be.ExecuteRestore(ctx, restoreParams, restoreBh) + + if cfg.expectSuccess { + assert.NoError(t, err) + assert.NotNil(t, bm) + } else { + assert.Error(t, err) + } + + ss := blackbox.GetStats(fakeStats) + require.Equal(t, cfg.expectedStats.DestinationCloseStats, ss.DestinationCloseStats) + require.Equal(t, cfg.expectedStats.DestinationOpenStats, ss.DestinationOpenStats) + require.Equal(t, cfg.expectedStats.DestinationWriteStats, ss.DestinationWriteStats) + require.Equal(t, cfg.expectedStats.SourceCloseStats, ss.SourceCloseStats) + require.Equal(t, cfg.expectedStats.SourceOpenStats, ss.SourceOpenStats) + require.Equal(t, cfg.expectedStats.SourceReadStats, ss.SourceReadStats) +} + +func TestExecuteRestoreS3FailEachFileOnce(t *testing.T) { + runRestoreTest(t, restoreTestConfig{ + readFileReturnFn: s3backupstorage.FailFirstRead, + expectSuccess: true, + expectedStats: blackbox.StatSummary{ + DestinationCloseStats: 8, + DestinationOpenStats: 8, + DestinationWriteStats: 4, // 4, because on the first attempt, we fail to read before writing to the filesystem + SourceCloseStats: 8, + SourceOpenStats: 8, + SourceReadStats: 8, + }, + }) +} + +func TestExecuteRestoreS3FailEachFileTwice(t *testing.T) { + runRestoreTest(t, restoreTestConfig{ + readFileReturnFn: s3backupstorage.FailAllReadExpectManifest, + expectSuccess: false, + + // Everything except destination writes must be equal to 5: + // +1 for every file on the first attempt (= 4), and +1 for the first file we try for the second time. + // Since we fail early as soon as a second-attempt-file fails, we won't see a value above 5. + expectedStats: blackbox.StatSummary{ + DestinationCloseStats: 5, + DestinationOpenStats: 5, + DestinationWriteStats: 0, // 0, because on the both attempts, we fail to read before writing to the filesystem + SourceCloseStats: 5, + SourceOpenStats: 5, + SourceReadStats: 5, + }, + }) +} diff --git a/go/test/endtoend/backup/vtbackup/backup_only_test.go b/go/test/endtoend/backup/vtbackup/backup_only_test.go index 4e018986100..c7a09c70d13 100644 --- a/go/test/endtoend/backup/vtbackup/backup_only_test.go +++ b/go/test/endtoend/backup/vtbackup/backup_only_test.go @@ -58,7 +58,6 @@ func TestTabletInitialBackup(t *testing.T) { // - Take a Second Backup // - Bring up a second replica, and restore from the second backup // - list the backups, remove them - defer cluster.PanicHandler(t) waitForReplicationToCatchup([]cluster.Vttablet{*replica1, *replica2}) @@ -102,7 +101,6 @@ func TestTabletBackupOnly(t *testing.T) { // - Take a Second Backup // - Bring up a second replica, and restore from the second backup // - list the backups, remove them - defer cluster.PanicHandler(t) // Reset the tablet object values in order on init tablet in the next step. primary.VttabletProcess.ServingStatus = "NOT_SERVING" diff --git a/go/test/endtoend/backup/vtbackup/main_test.go b/go/test/endtoend/backup/vtbackup/main_test.go index 367956c9827..6e1840b2979 100644 --- a/go/test/endtoend/backup/vtbackup/main_test.go +++ b/go/test/endtoend/backup/vtbackup/main_test.go @@ -52,7 +52,6 @@ var ( ) func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitCode, err := func() (int, error) { diff --git a/go/test/endtoend/backup/vtctlbackup/backup_utils.go b/go/test/endtoend/backup/vtctlbackup/backup_utils.go index 7c03b776c74..86b2612a044 100644 --- a/go/test/endtoend/backup/vtctlbackup/backup_utils.go +++ b/go/test/endtoend/backup/vtctlbackup/backup_utils.go @@ -405,7 +405,6 @@ func TestBackup(t *testing.T, setupType int, streamMode string, stripes int, cDe }, // } - defer cluster.PanicHandler(t) // setup cluster for the testing code, err := LaunchCluster(setupType, streamMode, stripes, cDetails) require.Nilf(t, err, "setup failed with status code %d", code) @@ -1507,7 +1506,6 @@ func getLastBackup(t *testing.T) string { func TestBackupEngineSelector(t *testing.T) { defer setDefaultCommonArgs() - defer cluster.PanicHandler(t) // launch the custer with xtrabackup as the default engine code, err := LaunchCluster(XtraBackup, "xbstream", 0, &CompressionDetails{CompressorEngineName: "pgzip"}) @@ -1548,7 +1546,6 @@ func TestBackupEngineSelector(t *testing.T) { func TestRestoreAllowedBackupEngines(t *testing.T) { defer setDefaultCommonArgs() - defer cluster.PanicHandler(t) backupMsg := "right after xtrabackup backup" diff --git a/go/test/endtoend/backup/vtctlbackup/pitr_test_framework.go b/go/test/endtoend/backup/vtctlbackup/pitr_test_framework.go index 4c84c3e63bc..1b04dbc7aab 100644 --- a/go/test/endtoend/backup/vtctlbackup/pitr_test_framework.go +++ b/go/test/endtoend/backup/vtctlbackup/pitr_test_framework.go @@ -95,7 +95,6 @@ func waitForReplica(t *testing.T, replicaIndex int) int { // in between, it makes writes to the database, and takes notes: what data was available in what backup. // It then restores each and every one of those backups, in random order, and expects to find the specific data associated with the backup. func ExecTestIncrementalBackupAndRestoreToPos(t *testing.T, tcase *PITRTestCase) { - defer cluster.PanicHandler(t) t.Run(tcase.Name, func(t *testing.T) { // setup cluster for the testing @@ -339,7 +338,6 @@ func ExecTestIncrementalBackupAndRestoreToPos(t *testing.T, tcase *PITRTestCase) // ExecTestIncrementalBackupAndRestoreToPos func ExecTestIncrementalBackupAndRestoreToTimestamp(t *testing.T, tcase *PITRTestCase) { - defer cluster.PanicHandler(t) var lastInsertedRowTimestamp time.Time insertRowOnPrimary := func(t *testing.T, hint string) { @@ -605,7 +603,6 @@ func ExecTestIncrementalBackupAndRestoreToTimestamp(t *testing.T, tcase *PITRTes // Specifically, it's designed to test how incremental backups are taken by interleaved replicas, so that they successfully build on // one another. func ExecTestIncrementalBackupOnTwoTablets(t *testing.T, tcase *PITRTestCase) { - defer cluster.PanicHandler(t) t.Run(tcase.Name, func(t *testing.T) { // setup cluster for the testing diff --git a/go/test/endtoend/cellalias/cell_alias_test.go b/go/test/endtoend/cellalias/cell_alias_test.go index 07e8d687f4e..6e8f901a245 100644 --- a/go/test/endtoend/cellalias/cell_alias_test.go +++ b/go/test/endtoend/cellalias/cell_alias_test.go @@ -90,7 +90,6 @@ var ( ) func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitcode, err := func() (int, error) { @@ -232,7 +231,6 @@ func TestMain(m *testing.M) { } func TestAlias(t *testing.T) { - defer cluster.PanicHandler(t) insertInitialValues(t) defer deleteInitialValues(t) @@ -296,7 +294,6 @@ func TestAlias(t *testing.T) { } func TestAddAliasWhileVtgateUp(t *testing.T) { - defer cluster.PanicHandler(t) insertInitialValues(t) defer deleteInitialValues(t) diff --git a/go/test/endtoend/cluster/cluster_process.go b/go/test/endtoend/cluster/cluster_process.go index 6f800a70a49..2ac46a8d145 100644 --- a/go/test/endtoend/cluster/cluster_process.go +++ b/go/test/endtoend/cluster/cluster_process.go @@ -1043,7 +1043,6 @@ func (cluster *LocalProcessCluster) StreamTabletHealthUntil(ctx context.Context, // Teardown brings down the cluster by invoking teardown for individual processes func (cluster *LocalProcessCluster) Teardown() { - PanicHandler(nil) cluster.mx.Lock() defer cluster.mx.Unlock() if cluster.teardownCompleted { @@ -1301,7 +1300,6 @@ func (cluster *LocalProcessCluster) NewVTOrcProcess(config VTOrcConfiguration) * VtctlProcess: *base, LogDir: cluster.TmpDirectory, Config: config, - WebPort: cluster.GetAndReservePort(), Port: cluster.GetAndReservePort(), } } diff --git a/go/test/endtoend/cluster/cluster_util.go b/go/test/endtoend/cluster/cluster_util.go index 061e632dde7..cfc2071a746 100644 --- a/go/test/endtoend/cluster/cluster_util.go +++ b/go/test/endtoend/cluster/cluster_util.go @@ -126,15 +126,6 @@ func VerifyRowsInTablet(t *testing.T, vttablet *Vttablet, ksName string, expecte VerifyRowsInTabletForTable(t, vttablet, ksName, expectedRows, "vt_insert_test") } -// PanicHandler handles the panic in the testcase. -func PanicHandler(t testing.TB) { - err := recover() - if t == nil { - return - } - require.Nilf(t, err, "panic occured in testcase %v", t.Name()) -} - // ListBackups Lists back preset in shard func (cluster LocalProcessCluster) ListBackups(shardKsName string) ([]string, error) { output, err := cluster.VtctldClientProcess.ExecuteCommandWithOutput("GetBackups", shardKsName) diff --git a/go/test/endtoend/cluster/vtctldclient_process.go b/go/test/endtoend/cluster/vtctldclient_process.go index 892f03e27fe..8b401f59070 100644 --- a/go/test/endtoend/cluster/vtctldclient_process.go +++ b/go/test/endtoend/cluster/vtctldclient_process.go @@ -41,6 +41,7 @@ type VtctldClientProcess struct { TempDirectory string ZoneName string VtctldClientMajorVersion int + Quiet bool } // ExecuteCommand executes any vtctldclient command @@ -72,7 +73,9 @@ func (vtctldclient *VtctldClientProcess) ExecuteCommandWithOutput(args ...string filterDoubleDashArgs(pArgs, vtctldclient.VtctldClientMajorVersion)..., ) msg := binlogplayer.LimitString(strings.Join(tmpProcess.Args, " "), 256) // limit log line length - log.Infof("Executing vtctldclient with command: %v (attempt %d of %d)", msg, i+1, retries) + if !vtctldclient.Quiet { + log.Infof("Executing vtctldclient with command: %v (attempt %d of %d)", msg, i+1, retries) + } resultByte, err = tmpProcess.CombinedOutput() resultStr = string(resultByte) if err == nil || !shouldRetry(resultStr) { diff --git a/go/test/endtoend/cluster/vtgate_process.go b/go/test/endtoend/cluster/vtgate_process.go index c01f7c6e93b..1290156a1cd 100644 --- a/go/test/endtoend/cluster/vtgate_process.go +++ b/go/test/endtoend/cluster/vtgate_process.go @@ -132,6 +132,7 @@ func (vtgate *VtgateProcess) Setup() (err error) { return err } vtgate.proc.Stderr = errFile + vtgate.ErrorLog = errFile.Name() vtgate.proc.Env = append(vtgate.proc.Env, os.Environ()...) vtgate.proc.Env = append(vtgate.proc.Env, DefaultVttestEnv) diff --git a/go/test/endtoend/cluster/vtorc_process.go b/go/test/endtoend/cluster/vtorc_process.go index 4fcb68e292d..af101a8bebd 100644 --- a/go/test/endtoend/cluster/vtorc_process.go +++ b/go/test/endtoend/cluster/vtorc_process.go @@ -43,20 +43,28 @@ type VTOrcProcess struct { ExtraArgs []string ConfigPath string Config VTOrcConfiguration - WebPort int + NoOverride bool proc *exec.Cmd exit chan error } type VTOrcConfiguration struct { - Debug bool - ListenAddress string - RecoveryPeriodBlockSeconds int - TopologyRefreshSeconds int `json:",omitempty"` - PreventCrossDataCenterPrimaryFailover bool `json:",omitempty"` - LockShardTimeoutSeconds int `json:",omitempty"` - ReplicationLagQuery string `json:",omitempty"` - FailPrimaryPromotionOnLagMinutes int `json:",omitempty"` + InstancePollTime string `json:"instance-poll-time,omitempty"` + SnapshotTopologyInterval string `json:"snapshot-topology-interval,omitempty"` + PreventCrossCellFailover bool `json:"prevent-cross-cell-failover,omitempty"` + ReasonableReplicationLag string `json:"reasonable-replication-lag,omitempty"` + AuditToBackend bool `json:"audit-to-backend,omitempty"` + AuditToSyslog bool `json:"audit-to-syslog,omitempty"` + AuditPurgeDuration string `json:"audit-purge-duration,omitempty"` + WaitReplicasTimeout string `json:"wait-replicas-timeout,omitempty"` + TolerableReplicationLag string `json:"tolerable-replication-lag,omitempty"` + TopoInformationRefreshDuration string `json:"topo-information-refresh-duration,omitempty"` + RecoveryPollDuration string `json:"recovery-poll-duration,omitempty"` + AllowEmergencyReparent string `json:"allow-emergency-reparent,omitempty"` + ChangeTabletsWithErrantGtidToDrained bool `json:"change-tablets-with-errant-gtid-to-drained,omitempty"` + LockShardTimeoutSeconds int `json:",omitempty"` + ReplicationLagQuery string `json:",omitempty"` + FailPrimaryPromotionOnLagMinutes int `json:",omitempty"` } // ToJSONString will marshal this configuration as JSON @@ -65,12 +73,12 @@ func (config *VTOrcConfiguration) ToJSONString() string { return string(b) } -func (config *VTOrcConfiguration) AddDefaults(webPort int) { - config.Debug = true - if config.RecoveryPeriodBlockSeconds == 0 { - config.RecoveryPeriodBlockSeconds = 1 - } - config.ListenAddress = fmt.Sprintf(":%d", webPort) +func (config *VTOrcConfiguration) addValuesToCheckOverride() { + config.InstancePollTime = "10h" +} + +func (orc *VTOrcProcess) RewriteConfiguration() error { + return os.WriteFile(orc.ConfigPath, []byte(orc.Config.ToJSONString()), 0644) } // Setup starts orc process with required arguements @@ -91,7 +99,9 @@ func (orc *VTOrcProcess) Setup() (err error) { orc.ConfigPath = configFile.Name() // Add the default configurations and print them out - orc.Config.AddDefaults(orc.WebPort) + if !orc.NoOverride { + orc.Config.addValuesToCheckOverride() + } log.Errorf("configuration - %v", orc.Config.ToJSONString()) _, err = configFile.WriteString(orc.Config.ToJSONString()) if err != nil { @@ -111,15 +121,18 @@ func (orc *VTOrcProcess) Setup() (err error) { "--topo_implementation", orc.TopoImplementation, "--topo_global_server_address", orc.TopoGlobalAddress, "--topo_global_root", orc.TopoGlobalRoot, - "--config", orc.ConfigPath, + "--config-file", orc.ConfigPath, "--port", fmt.Sprintf("%d", orc.Port), - // This parameter is overriden from the config file, added here to just verify that we indeed use the config file paramter over the flag - "--recovery-period-block-duration", "10h", - "--instance-poll-time", "1s", - // Faster topo information refresh speeds up the tests. This doesn't add any significant load either - "--topo-information-refresh-duration", "3s", "--bind-address", "127.0.0.1", ) + if !orc.NoOverride { + orc.proc.Args = append(orc.proc.Args, + // This parameter is overriden from the config file. This verifies that we indeed use the flag value over the config file. + "--instance-poll-time", "1s", + // Faster topo information refresh speeds up the tests. This doesn't add any significant load either. + "--topo-information-refresh-duration", "3s", + ) + } if *isCoverage { orc.proc.Args = append(orc.proc.Args, "--test.coverprofile="+getCoveragePath("orc.out")) diff --git a/go/test/endtoend/cluster/vttablet_process.go b/go/test/endtoend/cluster/vttablet_process.go index 6c7a85ec533..6bc6e6b8d7f 100644 --- a/go/test/endtoend/cluster/vttablet_process.go +++ b/go/test/endtoend/cluster/vttablet_process.go @@ -459,6 +459,16 @@ func (vttablet *VttabletProcess) QueryTablet(query string, keyspace string, useD return executeQuery(conn, query) } +// MultiQueryTablet lets you execute a query in this tablet and get the result +func (vttablet *VttabletProcess) MultiQueryTablet(sql string, keyspace string, useDb bool) error { + conn, err := vttablet.TabletConn(keyspace, useDb) + if err != nil { + return err + } + defer conn.Close() + return executeMultiQuery(conn, sql) +} + // SemiSyncExtensionLoaded returns what type of semi-sync extension is loaded func (vttablet *VttabletProcess) SemiSyncExtensionLoaded() (mysql.SemiSyncType, error) { conn, err := vttablet.TabletConn("", false) diff --git a/go/test/endtoend/clustertest/add_keyspace_test.go b/go/test/endtoend/clustertest/add_keyspace_test.go index edee87d035e..b8422b52eb3 100644 --- a/go/test/endtoend/clustertest/add_keyspace_test.go +++ b/go/test/endtoend/clustertest/add_keyspace_test.go @@ -61,7 +61,6 @@ primary key (id) ) func TestAddKeyspace(t *testing.T) { - defer cluster.PanicHandler(t) if err := clusterInstance.StartKeyspace(*testKeyspace, []string{"-80", "80-"}, 0, false); err != nil { log.Errorf("failed to AddKeyspace %v: %v", *testKeyspace, err) t.Fatal(err) diff --git a/go/test/endtoend/clustertest/etcd_test.go b/go/test/endtoend/clustertest/etcd_test.go index 5239d960c47..f47a002a3cf 100644 --- a/go/test/endtoend/clustertest/etcd_test.go +++ b/go/test/endtoend/clustertest/etcd_test.go @@ -24,12 +24,9 @@ import ( "github.com/stretchr/testify/require" clientv3 "go.etcd.io/etcd/client/v3" - - "vitess.io/vitess/go/test/endtoend/cluster" ) func TestEtcdServer(t *testing.T) { - defer cluster.PanicHandler(t) // Confirm the basic etcd cluster health. etcdHealthURL := fmt.Sprintf("http://%s:%d/health", clusterInstance.Hostname, clusterInstance.TopoPort) diff --git a/go/test/endtoend/clustertest/main_test.go b/go/test/endtoend/clustertest/main_test.go index 35da40a3edb..3fc2524208b 100644 --- a/go/test/endtoend/clustertest/main_test.go +++ b/go/test/endtoend/clustertest/main_test.go @@ -60,7 +60,6 @@ var ( ) func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitCode := func() int { diff --git a/go/test/endtoend/clustertest/vtctld_test.go b/go/test/endtoend/clustertest/vtctld_test.go index c61f7820bb7..18d06cf3299 100644 --- a/go/test/endtoend/clustertest/vtctld_test.go +++ b/go/test/endtoend/clustertest/vtctld_test.go @@ -30,8 +30,6 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - - "vitess.io/vitess/go/test/endtoend/cluster" ) var ( @@ -44,7 +42,6 @@ var ( ) func TestVtctldProcess(t *testing.T) { - defer cluster.PanicHandler(t) url := fmt.Sprintf("http://%s:%d/api/keyspaces/", clusterInstance.Hostname, clusterInstance.VtctldHTTPPort) testURL(t, url, "keyspace url") diff --git a/go/test/endtoend/clustertest/vtgate_test.go b/go/test/endtoend/clustertest/vtgate_test.go index 2f72682a391..264b292f482 100644 --- a/go/test/endtoend/clustertest/vtgate_test.go +++ b/go/test/endtoend/clustertest/vtgate_test.go @@ -32,11 +32,9 @@ import ( "github.com/stretchr/testify/require" "vitess.io/vitess/go/mysql" - "vitess.io/vitess/go/test/endtoend/cluster" ) func TestVtgateProcess(t *testing.T) { - defer cluster.PanicHandler(t) verifyVtgateVariables(t, clusterInstance.VtgateProcess.VerifyURL) ctx := context.Background() conn, err := mysql.Connect(ctx, &vtParams) diff --git a/go/test/endtoend/clustertest/vttablet_test.go b/go/test/endtoend/clustertest/vttablet_test.go index 5e7d5e27182..86fc2a6983c 100644 --- a/go/test/endtoend/clustertest/vttablet_test.go +++ b/go/test/endtoend/clustertest/vttablet_test.go @@ -25,12 +25,9 @@ import ( "testing" "github.com/stretchr/testify/require" - - "vitess.io/vitess/go/test/endtoend/cluster" ) func TestVttabletProcess(t *testing.T) { - defer cluster.PanicHandler(t) firstTabletPort := clusterInstance.Keyspaces[0].Shards[0].Vttablets[0].HTTPPort testURL(t, fmt.Sprintf("http://localhost:%d/debug/vars/", firstTabletPort), "tablet debug var url") resp, err := http.Get(fmt.Sprintf("http://localhost:%d/debug/vars", firstTabletPort)) @@ -48,7 +45,6 @@ func TestVttabletProcess(t *testing.T) { } func TestDeleteTablet(t *testing.T) { - defer cluster.PanicHandler(t) primary := clusterInstance.Keyspaces[0].Shards[0].PrimaryTablet() require.NotNil(t, primary) _, err := clusterInstance.VtctldClientProcess.ExecuteCommandWithOutput("DeleteTablets", "--allow-primary", primary.Alias) diff --git a/go/test/endtoend/encryption/encryptedreplication/encrypted_replication_test.go b/go/test/endtoend/encryption/encryptedreplication/encrypted_replication_test.go index 7dea6cf525f..460ae310d7f 100644 --- a/go/test/endtoend/encryption/encryptedreplication/encrypted_replication_test.go +++ b/go/test/endtoend/encryption/encryptedreplication/encrypted_replication_test.go @@ -42,7 +42,6 @@ var ( // This test makes sure that we can use SSL replication with Vitess func TestSecure(t *testing.T) { - defer cluster.PanicHandler(t) testReplicationBase(t, true) testReplicationBase(t, false) } diff --git a/go/test/endtoend/encryption/encryptedtransport/encrypted_transport_test.go b/go/test/endtoend/encryption/encryptedtransport/encrypted_transport_test.go index 1363e07b2cd..86c847125a7 100644 --- a/go/test/endtoend/encryption/encryptedtransport/encrypted_transport_test.go +++ b/go/test/endtoend/encryption/encryptedtransport/encrypted_transport_test.go @@ -102,7 +102,6 @@ var ( ) func TestSecureTransport(t *testing.T) { - defer cluster.PanicHandler(t) flag.Parse() // initialize cluster diff --git a/go/test/endtoend/keyspace/keyspace_test.go b/go/test/endtoend/keyspace/keyspace_test.go index 2a665c66214..f65301b9bb4 100644 --- a/go/test/endtoend/keyspace/keyspace_test.go +++ b/go/test/endtoend/keyspace/keyspace_test.go @@ -81,7 +81,6 @@ var ( ) func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitCode := func() int { @@ -167,7 +166,6 @@ func checkDurabilityPolicy(t *testing.T, durabilityPolicy string) { } func TestGetSrvKeyspaceNames(t *testing.T) { - defer cluster.PanicHandler(t) data, err := clusterForKSTest.VtctldClientProcess.ExecuteCommandWithOutput("GetSrvKeyspaceNames", cell) require.Nil(t, err) @@ -180,7 +178,6 @@ func TestGetSrvKeyspaceNames(t *testing.T) { } func TestGetSrvKeyspacePartitions(t *testing.T) { - defer cluster.PanicHandler(t) shardedSrvKeyspace := getSrvKeyspace(t, cell, keyspaceShardedName) otherShardRefFound := false for _, partition := range shardedSrvKeyspace.Partitions { @@ -209,20 +206,17 @@ func TestGetSrvKeyspacePartitions(t *testing.T) { } func TestShardNames(t *testing.T) { - defer cluster.PanicHandler(t) output, err := clusterForKSTest.VtctldClientProcess.GetSrvKeyspaces(keyspaceShardedName, cell) require.NoError(t, err) require.NotNil(t, output[cell], "no srvkeyspace for cell %s", cell) } func TestGetKeyspace(t *testing.T) { - defer cluster.PanicHandler(t) _, err := clusterForKSTest.VtctldClientProcess.GetKeyspace(keyspaceUnshardedName) require.Nil(t, err) } func TestDeleteKeyspace(t *testing.T) { - defer cluster.PanicHandler(t) _ = clusterForKSTest.VtctldClientProcess.CreateKeyspace("test_delete_keyspace", sidecar.DefaultName) _ = clusterForKSTest.VtctldClientProcess.ExecuteCommand("CreateShard", "test_delete_keyspace/0") _ = clusterForKSTest.InitTablet(&cluster.Vttablet{ @@ -353,7 +347,6 @@ func TestDeleteKeyspace(t *testing.T) { } */ func TestShardCountForAllKeyspaces(t *testing.T) { - defer cluster.PanicHandler(t) testShardCountForKeyspace(t, keyspaceUnshardedName, 1) testShardCountForKeyspace(t, keyspaceShardedName, 2) } @@ -370,7 +363,6 @@ func testShardCountForKeyspace(t *testing.T, keyspace string, count int) { } func TestShardNameForAllKeyspaces(t *testing.T) { - defer cluster.PanicHandler(t) testShardNameForKeyspace(t, keyspaceUnshardedName, []string{"test_ks_unsharded"}) testShardNameForKeyspace(t, keyspaceShardedName, []string{"-80", "80-"}) } @@ -389,7 +381,6 @@ func testShardNameForKeyspace(t *testing.T, keyspace string, shardNames []string } func TestKeyspaceToShardName(t *testing.T) { - defer cluster.PanicHandler(t) var id []byte srvKeyspace := getSrvKeyspace(t, cell, keyspaceShardedName) diff --git a/go/test/endtoend/messaging/main_test.go b/go/test/endtoend/messaging/main_test.go index 49477ebe631..c654869316b 100644 --- a/go/test/endtoend/messaging/main_test.go +++ b/go/test/endtoend/messaging/main_test.go @@ -104,7 +104,6 @@ var ( ) func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitcode, err := func() (int, error) { diff --git a/go/test/endtoend/messaging/message_test.go b/go/test/endtoend/messaging/message_test.go index 7e1190c16bb..e91a8dcc335 100644 --- a/go/test/endtoend/messaging/message_test.go +++ b/go/test/endtoend/messaging/message_test.go @@ -375,7 +375,6 @@ func TestUnsharded(t *testing.T) { // TestReparenting checks the client connection count after reparenting. func TestReparenting(t *testing.T) { - defer cluster.PanicHandler(t) name := "sharded_message" ctx := context.Background() @@ -435,7 +434,6 @@ func TestReparenting(t *testing.T) { // TestConnection validate the connection count and message streaming. func TestConnection(t *testing.T) { - defer cluster.PanicHandler(t) name := "sharded_message" @@ -494,7 +492,6 @@ func TestConnection(t *testing.T) { } func testMessaging(t *testing.T, name, ks string) { - defer cluster.PanicHandler(t) ctx := context.Background() stream, err := VtgateGrpcConn(ctx, clusterInstance) require.Nil(t, err) diff --git a/go/test/endtoend/mysqlctl/mysqlctl_test.go b/go/test/endtoend/mysqlctl/mysqlctl_test.go index f93724fa4a8..070114da420 100644 --- a/go/test/endtoend/mysqlctl/mysqlctl_test.go +++ b/go/test/endtoend/mysqlctl/mysqlctl_test.go @@ -40,7 +40,6 @@ var ( ) func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitCode := func() int { @@ -139,7 +138,6 @@ func initCluster(shardNames []string, totalTabletsRequired int) { } func TestRestart(t *testing.T) { - defer cluster.PanicHandler(t) err := primaryTablet.MysqlctlProcess.Stop() require.NoError(t, err) primaryTablet.MysqlctlProcess.CleanupFiles(primaryTablet.TabletUID) @@ -148,7 +146,6 @@ func TestRestart(t *testing.T) { } func TestAutoDetect(t *testing.T) { - defer cluster.PanicHandler(t) err := clusterInstance.Keyspaces[0].Shards[0].Vttablets[0].VttabletProcess.Setup() require.NoError(t, err) diff --git a/go/test/endtoend/mysqlctld/mysqlctld_test.go b/go/test/endtoend/mysqlctld/mysqlctld_test.go index beb155830e2..432beb0c6d5 100644 --- a/go/test/endtoend/mysqlctld/mysqlctld_test.go +++ b/go/test/endtoend/mysqlctld/mysqlctld_test.go @@ -44,7 +44,6 @@ var ( ) func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitCode := func() int { @@ -141,7 +140,6 @@ func initCluster(shardNames []string, totalTabletsRequired int) error { } func TestRestart(t *testing.T) { - defer cluster.PanicHandler(t) err := primaryTablet.MysqlctldProcess.Stop() require.Nil(t, err) require.Truef(t, primaryTablet.MysqlctldProcess.WaitForMysqlCtldShutdown(), "Mysqlctld has not stopped...") @@ -151,7 +149,6 @@ func TestRestart(t *testing.T) { } func TestAutoDetect(t *testing.T) { - defer cluster.PanicHandler(t) err := clusterInstance.Keyspaces[0].Shards[0].Vttablets[0].VttabletProcess.Setup() require.Nil(t, err, "error should be nil") diff --git a/go/test/endtoend/mysqlserver/main_test.go b/go/test/endtoend/mysqlserver/main_test.go index 18b169e33d7..20da69e18e8 100644 --- a/go/test/endtoend/mysqlserver/main_test.go +++ b/go/test/endtoend/mysqlserver/main_test.go @@ -61,7 +61,6 @@ END; ) func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() // setting grpc max size diff --git a/go/test/endtoend/mysqlserver/mysql_server_test.go b/go/test/endtoend/mysqlserver/mysql_server_test.go index 6b691582c66..ee6e973593b 100644 --- a/go/test/endtoend/mysqlserver/mysql_server_test.go +++ b/go/test/endtoend/mysqlserver/mysql_server_test.go @@ -35,14 +35,12 @@ import ( "vitess.io/vitess/go/mysql/sqlerror" "vitess.io/vitess/go/mysql" - "vitess.io/vitess/go/test/endtoend/cluster" _ "github.com/go-sql-driver/mysql" ) // TestMultiStmt checks that multiStatements=True and multiStatements=False work properly. func TestMultiStatement(t *testing.T) { - defer cluster.PanicHandler(t) ctx := context.Background() // connect database with multiStatements=True @@ -70,7 +68,6 @@ func TestMultiStatement(t *testing.T) { // TestLargeComment add large comment in insert stmt and validate the insert process. func TestLargeComment(t *testing.T) { - defer cluster.PanicHandler(t) ctx := context.Background() conn, err := mysql.Connect(ctx, &vtParams) @@ -89,7 +86,6 @@ func TestLargeComment(t *testing.T) { // TestInsertLargerThenGrpcLimit insert blob larger then grpc limit and verify the error. func TestInsertLargerThenGrpcLimit(t *testing.T) { - defer cluster.PanicHandler(t) ctx := context.Background() @@ -109,7 +105,6 @@ func TestInsertLargerThenGrpcLimit(t *testing.T) { // TestTimeout executes sleep(5) with query_timeout of 1 second, and verifies the error. func TestTimeout(t *testing.T) { - defer cluster.PanicHandler(t) ctx := context.Background() conn, err := mysql.Connect(ctx, &vtParams) @@ -125,7 +120,6 @@ func TestTimeout(t *testing.T) { // TestInvalidField tries to fetch invalid column and verifies the error. func TestInvalidField(t *testing.T) { - defer cluster.PanicHandler(t) ctx := context.Background() conn, err := mysql.Connect(ctx, &vtParams) @@ -141,7 +135,6 @@ func TestInvalidField(t *testing.T) { // TestWarnings validates the behaviour of SHOW WARNINGS. func TestWarnings(t *testing.T) { - defer cluster.PanicHandler(t) ctx := context.Background() conn, err := mysql.Connect(ctx, &vtParams) @@ -183,7 +176,6 @@ func TestWarnings(t *testing.T) { // TestSelectWithUnauthorizedUser verifies that an unauthorized user // is not able to read from the table. func TestSelectWithUnauthorizedUser(t *testing.T) { - defer cluster.PanicHandler(t) ctx := context.Background() tmpVtParam := vtParams @@ -202,7 +194,6 @@ func TestSelectWithUnauthorizedUser(t *testing.T) { // TestPartitionedTable validates that partitioned tables are recognized by schema engine func TestPartitionedTable(t *testing.T) { - defer cluster.PanicHandler(t) tablet := clusterInstance.Keyspaces[0].Shards[0].PrimaryTablet() diff --git a/go/test/endtoend/onlineddl/flow/onlineddl_flow_test.go b/go/test/endtoend/onlineddl/flow/onlineddl_flow_test.go index c442c042f8a..ee8141860f4 100644 --- a/go/test/endtoend/onlineddl/flow/onlineddl_flow_test.go +++ b/go/test/endtoend/onlineddl/flow/onlineddl_flow_test.go @@ -63,7 +63,6 @@ import ( "vitess.io/vitess/go/test/endtoend/throttler" "vitess.io/vitess/go/vt/log" "vitess.io/vitess/go/vt/schema" - vttablet "vitess.io/vitess/go/vt/vttablet/common" throttlebase "vitess.io/vitess/go/vt/vttablet/tabletserver/throttle/base" "vitess.io/vitess/go/vt/vttablet/tabletserver/throttle/throttlerapp" ) @@ -121,7 +120,6 @@ const ( ) func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitcode, err := func() (int, error) { @@ -145,9 +143,6 @@ func TestMain(m *testing.M) { "--heartbeat_on_demand_duration", "5s", "--migration_check_interval", "2s", "--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", @@ -201,7 +196,6 @@ func TestMain(m *testing.M) { } func TestOnlineDDLFlow(t *testing.T) { - defer cluster.PanicHandler(t) ctx := context.Background() require.NotNil(t, clusterInstance) diff --git a/go/test/endtoend/onlineddl/revert/onlineddl_revert_test.go b/go/test/endtoend/onlineddl/revert/onlineddl_revert_test.go index af88806fb26..a13077ef87b 100644 --- a/go/test/endtoend/onlineddl/revert/onlineddl_revert_test.go +++ b/go/test/endtoend/onlineddl/revert/onlineddl_revert_test.go @@ -137,7 +137,6 @@ type revertibleTestCase struct { } func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitcode, err := func() (int, error) { @@ -204,7 +203,6 @@ func TestMain(m *testing.M) { } func TestRevertSchemaChanges(t *testing.T) { - defer cluster.PanicHandler(t) shards = clusterInstance.Keyspaces[0].Shards require.Equal(t, 1, len(shards)) diff --git a/go/test/endtoend/onlineddl/scheduler/onlineddl_scheduler_test.go b/go/test/endtoend/onlineddl/scheduler/onlineddl_scheduler_test.go index 7b8180e80fb..5f6423b2556 100644 --- a/go/test/endtoend/onlineddl/scheduler/onlineddl_scheduler_test.go +++ b/go/test/endtoend/onlineddl/scheduler/onlineddl_scheduler_test.go @@ -239,7 +239,6 @@ func waitForMessage(t *testing.T, uuid string, messageSubstring string) { } func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitcode, err := func() (int, error) { @@ -321,7 +320,6 @@ func TestSchedulerSchemaChanges(t *testing.T) { } func testScheduler(t *testing.T) { - defer cluster.PanicHandler(t) shards = clusterInstance.Keyspaces[0].Shards require.Equal(t, 1, len(shards)) @@ -484,7 +482,7 @@ func testScheduler(t *testing.T) { testTableSequentialTimes(t, t1uuid, t2uuid) }) t.Run("Postpone launch CREATE", func(t *testing.T) { - t1uuid = testOnlineDDLStatement(t, createParams(createT1IfNotExistsStatement, ddlStrategy+" --postpone-launch", "vtgate", "", "", true)) // skip wait + t1uuid = testOnlineDDLStatement(t, createParams(createT1IfNotExistsStatement, ddlStrategy+" --postpone-launch --cut-over-threshold=14s", "vtgate", "", "", true)) // skip wait time.Sleep(2 * time.Second) rs := onlineddl.ReadMigrations(t, &vtParams, t1uuid) require.NotNil(t, rs) @@ -501,6 +499,10 @@ func testScheduler(t *testing.T) { for _, row := range rs.Named().Rows { postponeLaunch := row.AsInt64("postpone_launch", 0) assert.Equal(t, int64(0), postponeLaunch) + + cutOverThresholdSeconds := row.AsInt64("cutover_threshold_seconds", 0) + // Threshold supplied in DDL strategy + assert.EqualValues(t, 14, cutOverThresholdSeconds) } status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, t1uuid, normalWaitTime, schema.OnlineDDLStatusComplete, schema.OnlineDDLStatusFailed) fmt.Printf("# Migration status (for debug purposes): <%s>\n", status) @@ -580,6 +582,9 @@ func testScheduler(t *testing.T) { assert.Equal(t, int64(1), postponeCompletion) } }) + t.Run("set cut-over threshold", func(t *testing.T) { + onlineddl.CheckSetMigrationCutOverThreshold(t, &vtParams, shards, t1uuid, 17700*time.Millisecond, "") + }) 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) @@ -592,6 +597,11 @@ func testScheduler(t *testing.T) { for _, row := range rs.Named().Rows { postponeCompletion := row.AsInt64("postpone_completion", 0) assert.Equal(t, int64(0), postponeCompletion) + + cutOverThresholdSeconds := row.AsInt64("cutover_threshold_seconds", 0) + // Expect 17800*time.Millisecond to be truncated to 17 seconds + assert.EqualValues(t, 17, cutOverThresholdSeconds) + assert.False(t, row["shadow_analyzed_timestamp"].IsNull()) } }) @@ -1062,6 +1072,10 @@ func testScheduler(t *testing.T) { for _, row := range rs.Named().Rows { retries := row.AsInt64("retries", 0) assert.Greater(t, retries, int64(0)) + + cutOverThresholdSeconds := row.AsInt64("cutover_threshold_seconds", 0) + // No explicit cut-over threshold given. Expect the default 10s + assert.EqualValues(t, 10, cutOverThresholdSeconds) } }) }) @@ -1088,6 +1102,13 @@ func testScheduler(t *testing.T) { executedUUID := testOnlineDDLStatement(t, createParams(trivialAlterT1Statement, ddlStrategy, "vtctl", "", "", true)) // skip wait require.Equal(t, uuid, executedUUID) + t.Run("set low cut-over threshold", func(t *testing.T) { + onlineddl.CheckSetMigrationCutOverThreshold(t, &vtParams, shards, uuid, 2*time.Second, "cut-over min value") + }) + t.Run("set high cut-over threshold", func(t *testing.T) { + onlineddl.CheckSetMigrationCutOverThreshold(t, &vtParams, shards, uuid, 2000*time.Second, "cut-over max value") + }) + // expect it to complete status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, uuid, normalWaitTime, schema.OnlineDDLStatusComplete, schema.OnlineDDLStatusFailed) fmt.Printf("# Migration status (for debug purposes): <%s>\n", status) @@ -1098,6 +1119,10 @@ func testScheduler(t *testing.T) { for _, row := range rs.Named().Rows { retries := row.AsInt64("retries", 0) assert.Greater(t, retries, int64(0)) + + cutOverThresholdSeconds := row.AsInt64("cutover_threshold_seconds", 0) + // Teh default remains unchanged. + assert.EqualValues(t, 10, cutOverThresholdSeconds) } }) }) @@ -1566,7 +1591,6 @@ func testScheduler(t *testing.T) { } func testSingleton(t *testing.T) { - defer cluster.PanicHandler(t) shards = clusterInstance.Keyspaces[0].Shards require.Equal(t, 1, len(shards)) @@ -1817,7 +1841,6 @@ DROP TABLE IF EXISTS stress_test }) } func testDeclarative(t *testing.T) { - defer cluster.PanicHandler(t) shards = clusterInstance.Keyspaces[0].Shards require.Equal(t, 1, len(shards)) @@ -2489,7 +2512,6 @@ func testDeclarative(t *testing.T) { } func testForeignKeys(t *testing.T) { - defer cluster.PanicHandler(t) var ( createStatements = []string{ diff --git a/go/test/endtoend/onlineddl/vrepl/onlineddl_vrepl_test.go b/go/test/endtoend/onlineddl/vrepl/onlineddl_vrepl_test.go index 92dfa2b0c4a..a7c38527152 100644 --- a/go/test/endtoend/onlineddl/vrepl/onlineddl_vrepl_test.go +++ b/go/test/endtoend/onlineddl/vrepl/onlineddl_vrepl_test.go @@ -160,7 +160,6 @@ const ( ) func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitcode, err := func() (int, error) { @@ -224,7 +223,6 @@ func TestMain(m *testing.M) { } func TestVreplSchemaChanges(t *testing.T) { - defer cluster.PanicHandler(t) shards = clusterInstance.Keyspaces[0].Shards require.Equal(t, 2, len(shards)) 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 e0dd9701cf8..f7b222c175d 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 @@ -38,7 +38,6 @@ import ( "vitess.io/vitess/go/test/endtoend/throttler" "vitess.io/vitess/go/vt/log" "vitess.io/vitess/go/vt/schema" - vttablet "vitess.io/vitess/go/vt/vttablet/common" ) type WriteMetrics struct { @@ -160,7 +159,6 @@ func nextOpOrder() int64 { } func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitcode, err := func() (int, error) { @@ -184,9 +182,6 @@ 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", @@ -230,7 +225,6 @@ func TestMain(m *testing.M) { } func TestVreplMiniStressSchemaChanges(t *testing.T) { - defer cluster.PanicHandler(t) ctx := context.Background() 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 440b921f9ba..1e52db38bd0 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 @@ -51,7 +51,6 @@ import ( "vitess.io/vitess/go/timer" "vitess.io/vitess/go/vt/log" "vitess.io/vitess/go/vt/schema" - vttablet "vitess.io/vitess/go/vt/vttablet/common" ) type testcase struct { @@ -408,7 +407,6 @@ func mysqlParams() *mysql.ConnParams { } func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitcode, err := func() (int, error) { @@ -436,9 +434,6 @@ 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", @@ -482,7 +477,6 @@ func TestMain(m *testing.M) { } func TestVreplStressSchemaChanges(t *testing.T) { - defer cluster.PanicHandler(t) shards = clusterInstance.Keyspaces[0].Shards require.Equal(t, 1, len(shards)) 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 972421c96da..4a2f7f1a3ce 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 @@ -67,7 +67,6 @@ const ( // Use $VREPL_SUITE_TEST_FILTER environment variable to filter tests by name. func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() testsFilter = os.Getenv(testFilterEnvVar) @@ -133,7 +132,6 @@ func TestMain(m *testing.M) { } func TestVreplSuiteSchemaChanges(t *testing.T) { - defer cluster.PanicHandler(t) shards := clusterInstance.Keyspaces[0].Shards require.Equal(t, 1, len(shards)) diff --git a/go/test/endtoend/onlineddl/vtgate_util.go b/go/test/endtoend/onlineddl/vtgate_util.go index 639b3ce8eb4..ffb6423a346 100644 --- a/go/test/endtoend/onlineddl/vtgate_util.go +++ b/go/test/endtoend/onlineddl/vtgate_util.go @@ -255,6 +255,16 @@ func CheckForceMigrationCutOver(t *testing.T, vtParams *mysql.ConnParams, shards } } +// CheckSetMigrationCutOverThreshold sets the cut-over threshold for a given migration. +func CheckSetMigrationCutOverThreshold(t *testing.T, vtParams *mysql.ConnParams, shards []cluster.Shard, uuid string, threshold time.Duration, expectError string) { + query, err := sqlparser.ParseAndBind("alter vitess_migration %a cutover_threshold %a", + sqltypes.StringBindVariable(uuid), + sqltypes.StringBindVariable(threshold.String()), + ) + require.NoError(t, err) + _ = VtgateExecQuery(t, vtParams, query, expectError) +} + // 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 { ksName := shards[0].PrimaryTablet().VttabletProcess.Keyspace diff --git a/go/test/endtoend/preparestmt/main_test.go b/go/test/endtoend/preparestmt/main_test.go index 018e9d266fd..0e067062c94 100644 --- a/go/test/endtoend/preparestmt/main_test.go +++ b/go/test/endtoend/preparestmt/main_test.go @@ -162,7 +162,6 @@ var ( ) func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitcode, err := func() (int, error) { diff --git a/go/test/endtoend/preparestmt/stmt_methods_test.go b/go/test/endtoend/preparestmt/stmt_methods_test.go index 24fb58bff81..5768c6eec7a 100644 --- a/go/test/endtoend/preparestmt/stmt_methods_test.go +++ b/go/test/endtoend/preparestmt/stmt_methods_test.go @@ -27,20 +27,16 @@ import ( "github.com/icrowley/fake" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - - "vitess.io/vitess/go/test/endtoend/cluster" ) // TestSelect simple select the data without any condition. func TestSelect(t *testing.T) { - defer cluster.PanicHandler(t) dbo := Connect(t) defer dbo.Close() selectWhere(t, dbo, "") } func TestSelectDatabase(t *testing.T) { - defer cluster.PanicHandler(t) dbo := Connect(t) defer dbo.Close() prepare, err := dbo.Prepare("select database()") @@ -58,7 +54,6 @@ func TestSelectDatabase(t *testing.T) { // TestInsertUpdateDelete validates all insert, update and // delete method on prepared statements. func TestInsertUpdateDelete(t *testing.T) { - defer cluster.PanicHandler(t) dbo := Connect(t) defer dbo.Close() // prepare insert statement @@ -134,7 +129,6 @@ func testReplica(t *testing.T) { // testcount validates inserted rows count with expected count. func testcount(t *testing.T, dbo *sql.DB, except int) { - defer cluster.PanicHandler(t) r, err := dbo.Query("SELECT count(1) FROM " + tableName) require.Nil(t, err) @@ -148,7 +142,6 @@ func testcount(t *testing.T, dbo *sql.DB, except int) { // TestAutoIncColumns test insertion of row without passing // the value of auto increment columns (here it is id). func TestAutoIncColumns(t *testing.T) { - defer cluster.PanicHandler(t) dbo := Connect(t) defer dbo.Close() // insert a row without id @@ -227,7 +220,6 @@ func reconnectAndTest(t *testing.T) { // TestColumnParameter query database using column // parameter. func TestColumnParameter(t *testing.T) { - defer cluster.PanicHandler(t) dbo := Connect(t) defer dbo.Close() @@ -267,7 +259,6 @@ func TestColumnParameter(t *testing.T) { // TestWrongTableName query database using invalid // tablename and validate error. func TestWrongTableName(t *testing.T) { - defer cluster.PanicHandler(t) dbo := Connect(t) defer dbo.Close() execWithError(t, dbo, []uint16{1146}, "select * from teseting_table;") @@ -319,7 +310,6 @@ func getStringToString(x sql.NullString) string { } func TestSelectDBA(t *testing.T) { - defer cluster.PanicHandler(t) dbo := Connect(t) defer dbo.Close() @@ -381,7 +371,6 @@ func TestSelectDBA(t *testing.T) { } func TestSelectLock(t *testing.T) { - defer cluster.PanicHandler(t) dbo := Connect(t) defer dbo.Close() @@ -417,7 +406,6 @@ func TestSelectLock(t *testing.T) { } func TestShowColumns(t *testing.T) { - defer cluster.PanicHandler(t) dbo := Connect(t) defer dbo.Close() @@ -438,7 +426,6 @@ func TestShowColumns(t *testing.T) { } func TestBinaryColumn(t *testing.T) { - defer cluster.PanicHandler(t) dbo := Connect(t, "interpolateParams=false") defer dbo.Close() diff --git a/go/test/endtoend/recovery/pitr/shardedpitr_test.go b/go/test/endtoend/recovery/pitr/shardedpitr_test.go index f2a76662918..3bb2399737e 100644 --- a/go/test/endtoend/recovery/pitr/shardedpitr_test.go +++ b/go/test/endtoend/recovery/pitr/shardedpitr_test.go @@ -126,7 +126,6 @@ var ( // - asserting that restoring to restoreTime2 (going from 2 shards to 2 shards with past time) is working, it will assert for both shards // - asserting that restoring to restoreTime3 is working, we should get complete data after restoring, as we have in existing shards. func TestPITRRecovery(t *testing.T) { - defer cluster.PanicHandler(nil) initializeCluster(t) defer clusterInstance.Teardown() @@ -525,7 +524,6 @@ func launchRecoveryTablet(t *testing.T, tablet *cluster.Vttablet, binlogServer * tablet.MysqlctlProcess = *mysqlctlProcess extraArgs := []string{"--db-credentials-file", dbCredentialFile} tablet.MysqlctlProcess.InitDBFile = initDBFileWithPassword - tablet.VttabletProcess.DbPassword = mysqlPassword tablet.MysqlctlProcess.ExtraArgs = extraArgs err = tablet.MysqlctlProcess.Start() require.NoError(t, err) @@ -545,6 +543,7 @@ func launchRecoveryTablet(t *testing.T, tablet *cluster.Vttablet, binlogServer * clusterInstance.VtTabletExtraArgs, clusterInstance.DefaultCharset) tablet.Alias = tablet.VttabletProcess.TabletPath + tablet.VttabletProcess.DbPassword = mysqlPassword tablet.VttabletProcess.SupportsBackup = true tablet.VttabletProcess.Keyspace = restoreKeyspaceName tablet.VttabletProcess.ExtraArgs = []string{ diff --git a/go/test/endtoend/recovery/unshardedrecovery/recovery.go b/go/test/endtoend/recovery/unshardedrecovery/recovery.go index 1ebb7c2647f..ae6b152271b 100644 --- a/go/test/endtoend/recovery/unshardedrecovery/recovery.go +++ b/go/test/endtoend/recovery/unshardedrecovery/recovery.go @@ -72,7 +72,6 @@ var ( // TestMainImpl creates cluster for unsharded recovery testing. func TestMainImpl(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitCode, err := func() (int, error) { @@ -201,7 +200,6 @@ func TestMainImpl(m *testing.M) { // // 7. check that vtgate queries work correctly func TestRecoveryImpl(t *testing.T) { - defer cluster.PanicHandler(t) defer tabletsTeardown() verifyInitialReplication(t) diff --git a/go/test/endtoend/reparent/emergencyreparent/ers_test.go b/go/test/endtoend/reparent/emergencyreparent/ers_test.go index 584bccfdfb7..0d2eb8935d2 100644 --- a/go/test/endtoend/reparent/emergencyreparent/ers_test.go +++ b/go/test/endtoend/reparent/emergencyreparent/ers_test.go @@ -31,7 +31,6 @@ import ( ) func TestTrivialERS(t *testing.T) { - defer cluster.PanicHandler(t) clusterInstance := utils.SetupReparentCluster(t, "semi_sync") defer utils.TeardownCluster(clusterInstance) tablets := clusterInstance.Keyspaces[0].Shards[0].Vttablets @@ -56,7 +55,6 @@ func TestTrivialERS(t *testing.T) { } func TestReparentIgnoreReplicas(t *testing.T) { - defer cluster.PanicHandler(t) clusterInstance := utils.SetupReparentCluster(t, "semi_sync") defer utils.TeardownCluster(clusterInstance) tablets := clusterInstance.Keyspaces[0].Shards[0].Vttablets @@ -98,7 +96,6 @@ func TestReparentIgnoreReplicas(t *testing.T) { } func TestReparentDownPrimary(t *testing.T) { - defer cluster.PanicHandler(t) clusterInstance := utils.SetupReparentCluster(t, "semi_sync") defer utils.TeardownCluster(clusterInstance) tablets := clusterInstance.Keyspaces[0].Shards[0].Vttablets @@ -134,7 +131,6 @@ func TestReparentDownPrimary(t *testing.T) { } func TestReparentNoChoiceDownPrimary(t *testing.T) { - defer cluster.PanicHandler(t) clusterInstance := utils.SetupReparentCluster(t, "semi_sync") defer utils.TeardownCluster(clusterInstance) tablets := clusterInstance.Keyspaces[0].Shards[0].Vttablets @@ -170,7 +166,6 @@ func TestReparentNoChoiceDownPrimary(t *testing.T) { func TestSemiSyncSetupCorrectly(t *testing.T) { t.Run("semi-sync enabled", func(t *testing.T) { - defer cluster.PanicHandler(t) clusterInstance := utils.SetupReparentCluster(t, "semi_sync") defer utils.TeardownCluster(clusterInstance) tablets := clusterInstance.Keyspaces[0].Shards[0].Vttablets @@ -198,7 +193,6 @@ func TestSemiSyncSetupCorrectly(t *testing.T) { }) t.Run("semi-sync disabled", func(t *testing.T) { - defer cluster.PanicHandler(t) clusterInstance := utils.SetupReparentCluster(t, "none") defer utils.TeardownCluster(clusterInstance) tablets := clusterInstance.Keyspaces[0].Shards[0].Vttablets @@ -228,7 +222,6 @@ func TestSemiSyncSetupCorrectly(t *testing.T) { // TestERSPromoteRdonly tests that we never end up promoting a rdonly instance as the primary func TestERSPromoteRdonly(t *testing.T) { - defer cluster.PanicHandler(t) clusterInstance := utils.SetupReparentCluster(t, "semi_sync") defer utils.TeardownCluster(clusterInstance) tablets := clusterInstance.Keyspaces[0].Shards[0].Vttablets @@ -256,7 +249,6 @@ func TestERSPromoteRdonly(t *testing.T) { // TestERSPreventCrossCellPromotion tests that we promote a replica in the same cell as the previous primary if prevent cross cell promotion flag is set func TestERSPreventCrossCellPromotion(t *testing.T) { - defer cluster.PanicHandler(t) clusterInstance := utils.SetupReparentCluster(t, "semi_sync") defer utils.TeardownCluster(clusterInstance) tablets := clusterInstance.Keyspaces[0].Shards[0].Vttablets @@ -279,7 +271,6 @@ func TestERSPreventCrossCellPromotion(t *testing.T) { // TestPullFromRdonly tests that if a rdonly tablet is the most advanced, then our promoted primary should have // caught up to it by pulling transactions from it func TestPullFromRdonly(t *testing.T) { - defer cluster.PanicHandler(t) clusterInstance := utils.SetupReparentCluster(t, "semi_sync") defer utils.TeardownCluster(clusterInstance) tablets := clusterInstance.Keyspaces[0].Shards[0].Vttablets @@ -351,7 +342,6 @@ func TestPullFromRdonly(t *testing.T) { // replicas which do not have any replication status and also succeeds if the io thread // is stopped on the primary elect. func TestNoReplicationStatusAndIOThreadStopped(t *testing.T) { - defer cluster.PanicHandler(t) clusterInstance := utils.SetupReparentCluster(t, "semi_sync") defer utils.TeardownCluster(clusterInstance) tablets := clusterInstance.Keyspaces[0].Shards[0].Vttablets @@ -451,7 +441,6 @@ func TestERSForInitialization(t *testing.T) { } func TestRecoverWithMultipleFailures(t *testing.T) { - defer cluster.PanicHandler(t) clusterInstance := utils.SetupReparentCluster(t, "semi_sync") defer utils.TeardownCluster(clusterInstance) tablets := clusterInstance.Keyspaces[0].Shards[0].Vttablets @@ -479,7 +468,6 @@ func TestRecoverWithMultipleFailures(t *testing.T) { // TestERSFailFast tests that ERS will fail fast if it cannot find any tablet which can be safely promoted instead of promoting // a tablet and hanging while inserting a row in the reparent journal on getting semi-sync ACKs func TestERSFailFast(t *testing.T) { - defer cluster.PanicHandler(t) clusterInstance := utils.SetupReparentCluster(t, "semi_sync") defer utils.TeardownCluster(clusterInstance) tablets := clusterInstance.Keyspaces[0].Shards[0].Vttablets @@ -519,7 +507,6 @@ func TestERSFailFast(t *testing.T) { // TestReplicationStopped checks that ERS ignores the tablets that have sql thread stopped. // If there are more than 1, we also fail. func TestReplicationStopped(t *testing.T) { - defer cluster.PanicHandler(t) clusterInstance := utils.SetupReparentCluster(t, "semi_sync") defer utils.TeardownCluster(clusterInstance) tablets := clusterInstance.Keyspaces[0].Shards[0].Vttablets diff --git a/go/test/endtoend/reparent/newfeaturetest/reparent_test.go b/go/test/endtoend/reparent/newfeaturetest/reparent_test.go index b6f34af7294..a041ca04c68 100644 --- a/go/test/endtoend/reparent/newfeaturetest/reparent_test.go +++ b/go/test/endtoend/reparent/newfeaturetest/reparent_test.go @@ -36,7 +36,6 @@ import ( // The test takes down the vttablets of the primary and a rdonly tablet and runs ERS with the // default values of remote_operation_timeout, lock-timeout flags and wait_replicas_timeout subflag. func TestRecoverWithMultipleVttabletFailures(t *testing.T) { - defer cluster.PanicHandler(t) clusterInstance := utils.SetupReparentCluster(t, "semi_sync") defer utils.TeardownCluster(clusterInstance) tablets := clusterInstance.Keyspaces[0].Shards[0].Vttablets @@ -68,7 +67,6 @@ func TestRecoverWithMultipleVttabletFailures(t *testing.T) { // and ERS succeeds. func TestSingleReplicaERS(t *testing.T) { // Set up a cluster with none durability policy - defer cluster.PanicHandler(t) clusterInstance := utils.SetupReparentCluster(t, "none") defer utils.TeardownCluster(clusterInstance) tablets := clusterInstance.Keyspaces[0].Shards[0].Vttablets @@ -104,7 +102,6 @@ func TestSingleReplicaERS(t *testing.T) { // TestTabletRestart tests that a running tablet can be restarted and everything is still fine func TestTabletRestart(t *testing.T) { - defer cluster.PanicHandler(t) clusterInstance := utils.SetupReparentCluster(t, "semi_sync") defer utils.TeardownCluster(clusterInstance) tablets := clusterInstance.Keyspaces[0].Shards[0].Vttablets @@ -117,7 +114,6 @@ func TestTabletRestart(t *testing.T) { // Tests ensures that ChangeTabletType works even when semi-sync plugins are not loaded. func TestChangeTypeWithoutSemiSync(t *testing.T) { - defer cluster.PanicHandler(t) clusterInstance := utils.SetupReparentCluster(t, "none") defer utils.TeardownCluster(clusterInstance) tablets := clusterInstance.Keyspaces[0].Shards[0].Vttablets @@ -163,7 +159,6 @@ func TestChangeTypeWithoutSemiSync(t *testing.T) { // TestERSWithWriteInPromoteReplica tests that ERS doesn't fail even if there is a // write that happens when PromoteReplica is called. func TestERSWithWriteInPromoteReplica(t *testing.T) { - defer cluster.PanicHandler(t) clusterInstance := utils.SetupReparentCluster(t, "semi_sync") defer utils.TeardownCluster(clusterInstance) tablets := clusterInstance.Keyspaces[0].Shards[0].Vttablets @@ -181,7 +176,6 @@ func TestERSWithWriteInPromoteReplica(t *testing.T) { } func TestBufferingWithMultipleDisruptions(t *testing.T) { - defer cluster.PanicHandler(t) clusterInstance := utils.SetupShardedReparentCluster(t, "semi_sync") defer utils.TeardownCluster(clusterInstance) diff --git a/go/test/endtoend/reparent/plannedreparent/reparent_range_based_test.go b/go/test/endtoend/reparent/plannedreparent/reparent_range_based_test.go index 2d89893569d..91471b1cebb 100644 --- a/go/test/endtoend/reparent/plannedreparent/reparent_range_based_test.go +++ b/go/test/endtoend/reparent/plannedreparent/reparent_range_based_test.go @@ -28,7 +28,6 @@ import ( ) func TestReparentGracefulRangeBased(t *testing.T) { - defer cluster.PanicHandler(t) ctx := context.Background() utils.ShardName = "0000000000000000-ffffffffffffffff" diff --git a/go/test/endtoend/reparent/plannedreparent/reparent_test.go b/go/test/endtoend/reparent/plannedreparent/reparent_test.go index d3907b0bc5b..94e37d715f4 100644 --- a/go/test/endtoend/reparent/plannedreparent/reparent_test.go +++ b/go/test/endtoend/reparent/plannedreparent/reparent_test.go @@ -36,7 +36,6 @@ import ( ) func TestPrimaryToSpareStateChangeImpossible(t *testing.T) { - defer cluster.PanicHandler(t) clusterInstance := utils.SetupReparentCluster(t, "semi_sync") defer utils.TeardownCluster(clusterInstance) tablets := clusterInstance.Keyspaces[0].Shards[0].Vttablets @@ -48,7 +47,6 @@ func TestPrimaryToSpareStateChangeImpossible(t *testing.T) { } func TestReparentCrossCell(t *testing.T) { - defer cluster.PanicHandler(t) clusterInstance := utils.SetupReparentCluster(t, "semi_sync") defer utils.TeardownCluster(clusterInstance) tablets := clusterInstance.Keyspaces[0].Shards[0].Vttablets @@ -62,7 +60,6 @@ func TestReparentCrossCell(t *testing.T) { } func TestReparentGraceful(t *testing.T) { - defer cluster.PanicHandler(t) clusterInstance := utils.SetupReparentCluster(t, "semi_sync") defer utils.TeardownCluster(clusterInstance) tablets := clusterInstance.Keyspaces[0].Shards[0].Vttablets @@ -85,7 +82,6 @@ func TestReparentGraceful(t *testing.T) { // TestPRSWithDrainedLaggingTablet tests that PRS succeeds even if we have a lagging drained tablet func TestPRSWithDrainedLaggingTablet(t *testing.T) { - defer cluster.PanicHandler(t) clusterInstance := utils.SetupReparentCluster(t, "semi_sync") defer utils.TeardownCluster(clusterInstance) tablets := clusterInstance.Keyspaces[0].Shards[0].Vttablets @@ -112,7 +108,6 @@ func TestPRSWithDrainedLaggingTablet(t *testing.T) { } func TestReparentReplicaOffline(t *testing.T) { - defer cluster.PanicHandler(t) clusterInstance := utils.SetupReparentCluster(t, "semi_sync") defer utils.TeardownCluster(clusterInstance) tablets := clusterInstance.Keyspaces[0].Shards[0].Vttablets @@ -130,7 +125,6 @@ func TestReparentReplicaOffline(t *testing.T) { } func TestReparentAvoid(t *testing.T) { - defer cluster.PanicHandler(t) clusterInstance := utils.SetupReparentCluster(t, "semi_sync") defer utils.TeardownCluster(clusterInstance) tablets := clusterInstance.Keyspaces[0].Shards[0].Vttablets @@ -178,14 +172,12 @@ func TestReparentAvoid(t *testing.T) { } func TestReparentFromOutside(t *testing.T) { - defer cluster.PanicHandler(t) clusterInstance := utils.SetupReparentCluster(t, "semi_sync") defer utils.TeardownCluster(clusterInstance) reparentFromOutside(t, clusterInstance, false) } func TestReparentFromOutsideWithNoPrimary(t *testing.T) { - defer cluster.PanicHandler(t) clusterInstance := utils.SetupReparentCluster(t, "semi_sync") defer utils.TeardownCluster(clusterInstance) tablets := clusterInstance.Keyspaces[0].Shards[0].Vttablets @@ -285,7 +277,6 @@ func reparentFromOutside(t *testing.T, clusterInstance *cluster.LocalProcessClus } func TestReparentWithDownReplica(t *testing.T) { - defer cluster.PanicHandler(t) clusterInstance := utils.SetupReparentCluster(t, "semi_sync") defer utils.TeardownCluster(clusterInstance) tablets := clusterInstance.Keyspaces[0].Shards[0].Vttablets @@ -332,7 +323,6 @@ func TestReparentWithDownReplica(t *testing.T) { } func TestChangeTypeSemiSync(t *testing.T) { - defer cluster.PanicHandler(t) clusterInstance := utils.SetupReparentCluster(t, "semi_sync") defer utils.TeardownCluster(clusterInstance) tablets := clusterInstance.Keyspaces[0].Shards[0].Vttablets @@ -399,7 +389,6 @@ func TestChangeTypeSemiSync(t *testing.T) { // 1. When PRS is run with the cross_cell durability policy setup, then the semi-sync settings on all the tablets are as expected // 2. Bringing up a new vttablet should have its replication and semi-sync setup correctly without any manual intervention func TestCrossCellDurability(t *testing.T) { - defer cluster.PanicHandler(t) clusterInstance := utils.SetupReparentCluster(t, "cross_cell") defer utils.TeardownCluster(clusterInstance) tablets := clusterInstance.Keyspaces[0].Shards[0].Vttablets @@ -439,7 +428,6 @@ func TestCrossCellDurability(t *testing.T) { // TestFullStatus tests that the RPC FullStatus works as intended. func TestFullStatus(t *testing.T) { - defer cluster.PanicHandler(t) clusterInstance := utils.SetupReparentCluster(t, "semi_sync") defer utils.TeardownCluster(clusterInstance) tablets := clusterInstance.Keyspaces[0].Shards[0].Vttablets diff --git a/go/test/endtoend/reparent/prscomplex/main_test.go b/go/test/endtoend/reparent/prscomplex/main_test.go index 88e3d6c09fa..c2dafb8589f 100644 --- a/go/test/endtoend/reparent/prscomplex/main_test.go +++ b/go/test/endtoend/reparent/prscomplex/main_test.go @@ -44,7 +44,6 @@ var ( ) func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitCode := func() int { diff --git a/go/test/endtoend/reparent/prssettingspool/main_test.go b/go/test/endtoend/reparent/prssettingspool/main_test.go index 4364836841b..c6b59fd6372 100644 --- a/go/test/endtoend/reparent/prssettingspool/main_test.go +++ b/go/test/endtoend/reparent/prssettingspool/main_test.go @@ -43,7 +43,6 @@ var ( ) func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitCode := func() int { diff --git a/go/test/endtoend/reparent/semisync/semi_sync_test.go b/go/test/endtoend/reparent/semisync/semi_sync_test.go index 07cf4a7abc8..df9bf192e65 100644 --- a/go/test/endtoend/reparent/semisync/semi_sync_test.go +++ b/go/test/endtoend/reparent/semisync/semi_sync_test.go @@ -33,7 +33,6 @@ func TestSemiSyncUpgradeDowngrade(t *testing.T) { if ver != 21 { t.Skip("We only want to run this test for v21 release") } - defer cluster.PanicHandler(t) clusterInstance := utils.SetupReparentCluster(t, "semi_sync") defer utils.TeardownCluster(clusterInstance) tablets := clusterInstance.Keyspaces[0].Shards[0].Vttablets 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 b4ecb367e8b..c850e22945c 100644 --- a/go/test/endtoend/schemadiff/vrepl/schemadiff_vrepl_suite_test.go +++ b/go/test/endtoend/schemadiff/vrepl/schemadiff_vrepl_suite_test.go @@ -68,7 +68,6 @@ var ( ) func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitcode, err := func() (int, error) { @@ -132,7 +131,6 @@ func TestMain(m *testing.M) { } func TestSchemadiffSchemaChanges(t *testing.T) { - defer cluster.PanicHandler(t) shards := clusterInstance.Keyspaces[0].Shards require.Equal(t, 1, len(shards)) @@ -275,7 +273,6 @@ func testSingle(t *testing.T, testName string) { } // func TestRandomSchemaChanges(t *testing.T) { -// defer cluster.PanicHandler(t) // hints := &schemadiff.DiffHints{AutoIncrementStrategy: schemadiff.AutoIncrementIgnore} // // count := 20 diff --git a/go/test/endtoend/sharded/sharded_keyspace_test.go b/go/test/endtoend/sharded/sharded_keyspace_test.go index 192355fa6ef..3e5f2b3add7 100644 --- a/go/test/endtoend/sharded/sharded_keyspace_test.go +++ b/go/test/endtoend/sharded/sharded_keyspace_test.go @@ -73,7 +73,6 @@ var ( ) func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitcode, err := func() (int, error) { @@ -102,7 +101,6 @@ func TestMain(m *testing.M) { } func TestShardedKeyspace(t *testing.T) { - defer cluster.PanicHandler(t) shard1 := clusterInstance.Keyspaces[0].Shards[0] shard2 := clusterInstance.Keyspaces[0].Shards[1] diff --git a/go/test/endtoend/stress/stress_test.go b/go/test/endtoend/stress/stress_test.go index 30a5ee69c1a..1bf716274d4 100644 --- a/go/test/endtoend/stress/stress_test.go +++ b/go/test/endtoend/stress/stress_test.go @@ -42,7 +42,6 @@ var ( ) func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitCode := func() int { @@ -84,7 +83,6 @@ func TestMain(m *testing.M) { // The stressor is started on its own goroutine while the end-to-end test // is executed on the same cluster. func TestSimpleStressTest(t *testing.T) { - defer cluster.PanicHandler(t) cfg := stress.DefaultConfig cfg.ConnParams = &vtParams diff --git a/go/test/endtoend/tabletgateway/buffer/buffer_test_helpers.go b/go/test/endtoend/tabletgateway/buffer/buffer_test_helpers.go index ca4fe5f6094..920e2193453 100644 --- a/go/test/endtoend/tabletgateway/buffer/buffer_test_helpers.go +++ b/go/test/endtoend/tabletgateway/buffer/buffer_test_helpers.go @@ -272,7 +272,6 @@ type BufferingTest struct { } func (bt *BufferingTest) Test(t *testing.T) { - defer cluster.PanicHandler(t) clusterInstance, exitCode := bt.createCluster() if exitCode != 0 { t.Fatal("failed to start cluster") diff --git a/go/test/endtoend/tabletgateway/main_test.go b/go/test/endtoend/tabletgateway/main_test.go index 354be6969d3..cf179c49845 100644 --- a/go/test/endtoend/tabletgateway/main_test.go +++ b/go/test/endtoend/tabletgateway/main_test.go @@ -61,7 +61,6 @@ var ( ) func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitCode := func() int { diff --git a/go/test/endtoend/tabletgateway/vtgate_test.go b/go/test/endtoend/tabletgateway/vtgate_test.go index 1f4f8758e16..d9c87fdc7f3 100644 --- a/go/test/endtoend/tabletgateway/vtgate_test.go +++ b/go/test/endtoend/tabletgateway/vtgate_test.go @@ -40,7 +40,6 @@ import ( ) func TestVtgateHealthCheck(t *testing.T) { - defer cluster.PanicHandler(t) // Healthcheck interval on tablet is set to 1s, so sleep for 2s time.Sleep(2 * time.Second) verifyVtgateVariables(t, clusterInstance.VtgateProcess.VerifyURL) @@ -54,7 +53,6 @@ func TestVtgateHealthCheck(t *testing.T) { } func TestVtgateReplicationStatusCheck(t *testing.T) { - defer cluster.PanicHandler(t) // Healthcheck interval on tablet is set to 1s, so sleep for 2s time.Sleep(2 * time.Second) verifyVtgateVariables(t, clusterInstance.VtgateProcess.VerifyURL) @@ -104,7 +102,6 @@ func TestVtgateReplicationStatusCheck(t *testing.T) { } func TestVtgateReplicationStatusCheckWithTabletTypeChange(t *testing.T) { - defer cluster.PanicHandler(t) // Healthcheck interval on tablet is set to 1s, so sleep for 2s time.Sleep(2 * time.Second) verifyVtgateVariables(t, clusterInstance.VtgateProcess.VerifyURL) @@ -180,7 +177,6 @@ func retryNTimes(t *testing.T, maxRetries int, f func() bool) { func TestReplicaTransactions(t *testing.T) { // TODO(deepthi): this test seems to depend on previous test. Fix tearDown so that tests are independent - defer cluster.PanicHandler(t) // Healthcheck interval on tablet is set to 1s, so sleep for 2s time.Sleep(2 * time.Second) ctx := context.Background() @@ -287,7 +283,6 @@ func TestReplicaTransactions(t *testing.T) { // TestStreamingRPCStuck tests that StreamExecute calls don't get stuck on the vttablets if a client stop reading from a stream. func TestStreamingRPCStuck(t *testing.T) { - defer cluster.PanicHandler(t) ctx := context.Background() vtConn, err := mysql.Connect(ctx, &vtParams) require.NoError(t, err) diff --git a/go/test/endtoend/tabletmanager/commands_test.go b/go/test/endtoend/tabletmanager/commands_test.go index d5d946a164c..67127ab740f 100644 --- a/go/test/endtoend/tabletmanager/commands_test.go +++ b/go/test/endtoend/tabletmanager/commands_test.go @@ -29,7 +29,6 @@ import ( "vitess.io/vitess/go/json2" "vitess.io/vitess/go/mysql" - "vitess.io/vitess/go/test/endtoend/cluster" "vitess.io/vitess/go/test/endtoend/utils" vtctldatapb "vitess.io/vitess/go/vt/proto/vtctldata" @@ -44,7 +43,6 @@ var ( // TabletCommands tests the basic tablet commands func TestTabletCommands(t *testing.T) { - defer cluster.PanicHandler(t) ctx := context.Background() conn, err := mysql.Connect(ctx, &primaryTabletParams) @@ -185,7 +183,6 @@ func assertExecuteMultiFetch(t *testing.T, qr string) { func TestHook(t *testing.T) { // test a regular program works - defer cluster.PanicHandler(t) runHookAndAssert(t, []string{ "ExecuteHook", primaryTablet.Alias, "test.sh", "--", "--flag1", "--param1=hello"}, 0, false, "") @@ -226,7 +223,6 @@ func runHookAndAssert(t *testing.T, params []string, expectedStatus int64, expec func TestShardReplicationFix(t *testing.T) { // make sure the replica is in the replication graph, 2 nodes: 1 primary, 1 replica - defer cluster.PanicHandler(t) result, err := clusterInstance.VtctldClientProcess.GetShardReplication(keyspaceName, shardName, cell) require.Nil(t, err, "error should be Nil") require.NotNil(t, result[cell], "result should not be Nil") @@ -250,7 +246,6 @@ func TestShardReplicationFix(t *testing.T) { } func TestGetSchema(t *testing.T) { - defer cluster.PanicHandler(t) res, err := clusterInstance.VtctldClientProcess.ExecuteCommandWithOutput("GetSchema", "--include-views", "--tables", "t1,v1", diff --git a/go/test/endtoend/tabletmanager/custom_rule_topo_test.go b/go/test/endtoend/tabletmanager/custom_rule_topo_test.go index 0c6e056af36..e692bf94de4 100644 --- a/go/test/endtoend/tabletmanager/custom_rule_topo_test.go +++ b/go/test/endtoend/tabletmanager/custom_rule_topo_test.go @@ -33,7 +33,6 @@ import ( func TestTopoCustomRule(t *testing.T) { - defer cluster.PanicHandler(t) ctx := context.Background() conn, err := mysql.Connect(ctx, &primaryTabletParams) require.NoError(t, err) diff --git a/go/test/endtoend/tabletmanager/lock_unlock_test.go b/go/test/endtoend/tabletmanager/lock_unlock_test.go index 79286438698..f636f52c353 100644 --- a/go/test/endtoend/tabletmanager/lock_unlock_test.go +++ b/go/test/endtoend/tabletmanager/lock_unlock_test.go @@ -30,12 +30,10 @@ import ( "github.com/stretchr/testify/assert" "vitess.io/vitess/go/mysql" - "vitess.io/vitess/go/test/endtoend/cluster" ) // TestLockAndUnlock tests the lock ability by locking a replica and asserting it does not see changes func TestLockAndUnlock(t *testing.T) { - defer cluster.PanicHandler(t) ctx := context.Background() conn, err := mysql.Connect(ctx, &primaryTabletParams) @@ -76,7 +74,6 @@ func TestLockAndUnlock(t *testing.T) { // TestStartReplicationUntilAfter tests by writing three rows, noting the gtid after each, and then replaying them one by one func TestStartReplicationUntilAfter(t *testing.T) { - defer cluster.PanicHandler(t) ctx := context.Background() conn, err := mysql.Connect(ctx, &primaryTabletParams) @@ -130,7 +127,6 @@ func TestStartReplicationUntilAfter(t *testing.T) { // TestLockAndTimeout tests that the lock times out and updates can be seen after timeout func TestLockAndTimeout(t *testing.T) { - defer cluster.PanicHandler(t) ctx := context.Background() primaryConn, err := mysql.Connect(ctx, &primaryTabletParams) diff --git a/go/test/endtoend/tabletmanager/main_test.go b/go/test/endtoend/tabletmanager/main_test.go index 3c2eb68df5e..b613f061522 100644 --- a/go/test/endtoend/tabletmanager/main_test.go +++ b/go/test/endtoend/tabletmanager/main_test.go @@ -79,7 +79,6 @@ var ( ) func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitCode := func() int { @@ -105,7 +104,6 @@ func TestMain(m *testing.M) { "--heartbeat_enable", "--health_check_interval", tabletHealthcheckRefreshInterval.String(), "--unhealthy_threshold", tabletUnhealthyThreshold.String(), - "--twopc_enable", "--twopc_abandon_age", "200", } diff --git a/go/test/endtoend/tabletmanager/primary/tablet_test.go b/go/test/endtoend/tabletmanager/primary/tablet_test.go index 297e5540fac..aaff0ff00a0 100644 --- a/go/test/endtoend/tabletmanager/primary/tablet_test.go +++ b/go/test/endtoend/tabletmanager/primary/tablet_test.go @@ -69,7 +69,6 @@ var ( ) func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitCode := func() int { @@ -116,7 +115,6 @@ func TestMain(m *testing.M) { } func TestRepeatedInitShardPrimary(t *testing.T) { - defer cluster.PanicHandler(t) // Test that using InitShardPrimary can go back and forth between 2 hosts. // Make replica tablet as primary @@ -155,7 +153,6 @@ func TestRepeatedInitShardPrimary(t *testing.T) { } func TestPrimaryRestartSetsPTSTimestamp(t *testing.T) { - defer cluster.PanicHandler(t) // Test that PTS timestamp is set when we restart the PRIMARY vttablet. // PTS = PrimaryTermStart. // See StreamHealthResponse.primary_term_start_timestamp for details. diff --git a/go/test/endtoend/tabletmanager/qps_test.go b/go/test/endtoend/tabletmanager/qps_test.go index 0611feada12..0ce41f04a63 100644 --- a/go/test/endtoend/tabletmanager/qps_test.go +++ b/go/test/endtoend/tabletmanager/qps_test.go @@ -24,12 +24,10 @@ import ( "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 TestQPS(t *testing.T) { - defer cluster.PanicHandler(t) ctx := context.Background() vtParams := mysql.ConnParams{ diff --git a/go/test/endtoend/tabletmanager/replication_manager/tablet_test.go b/go/test/endtoend/tabletmanager/replication_manager/tablet_test.go index df8c1f26c4e..75c6e8d4cc8 100644 --- a/go/test/endtoend/tabletmanager/replication_manager/tablet_test.go +++ b/go/test/endtoend/tabletmanager/replication_manager/tablet_test.go @@ -73,7 +73,6 @@ var ( ) func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitCode := func() int { diff --git a/go/test/endtoend/tabletmanager/tablegc/tablegc_test.go b/go/test/endtoend/tabletmanager/tablegc/tablegc_test.go index 685c361cef7..b1abec3a6b7 100644 --- a/go/test/endtoend/tabletmanager/tablegc/tablegc_test.go +++ b/go/test/endtoend/tabletmanager/tablegc/tablegc_test.go @@ -83,7 +83,6 @@ var ( ) func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitCode := func() int { diff --git a/go/test/endtoend/tabletmanager/tablet_health_test.go b/go/test/endtoend/tabletmanager/tablet_health_test.go index bf3747fde29..061528682d2 100644 --- a/go/test/endtoend/tabletmanager/tablet_health_test.go +++ b/go/test/endtoend/tabletmanager/tablet_health_test.go @@ -39,7 +39,6 @@ import ( // TabletReshuffle test if a vttablet can be pointed at an existing mysql func TestTabletReshuffle(t *testing.T) { - defer cluster.PanicHandler(t) ctx := context.Background() conn, err := mysql.Connect(ctx, &primaryTabletParams) @@ -92,7 +91,6 @@ func TestTabletReshuffle(t *testing.T) { func TestHealthCheck(t *testing.T) { // Add one replica that starts not initialized - defer cluster.PanicHandler(t) ctx := context.Background() clusterInstance.DisableVTOrcRecoveries(t) defer clusterInstance.EnableVTOrcRecoveries(t) @@ -200,7 +198,6 @@ func TestHealthCheck(t *testing.T) { // TestHealthCheckSchemaChangeSignal tests the tables and views, which report their schemas have changed in the output of a StreamHealth. func TestHealthCheckSchemaChangeSignal(t *testing.T) { // Add one replica that starts not initialized - defer cluster.PanicHandler(t) ctx := context.Background() vtParams := clusterInstance.GetVTParams(keyspaceName) @@ -381,7 +378,6 @@ func TestHealthCheckDrainedStateDoesNotShutdownQueryService(t *testing.T) { // - the query service won't be shutdown // Wait if tablet is not in service state - defer cluster.PanicHandler(t) clusterInstance.DisableVTOrcRecoveries(t) defer clusterInstance.EnableVTOrcRecoveries(t) err := rdonlyTablet.VttabletProcess.WaitForTabletStatus("SERVING") diff --git a/go/test/endtoend/tabletmanager/tablet_security_policy_test.go b/go/test/endtoend/tabletmanager/tablet_security_policy_test.go index b3b11405abb..90397a737ef 100644 --- a/go/test/endtoend/tabletmanager/tablet_security_policy_test.go +++ b/go/test/endtoend/tabletmanager/tablet_security_policy_test.go @@ -29,7 +29,6 @@ import ( ) func TestFallbackSecurityPolicy(t *testing.T) { - defer cluster.PanicHandler(t) ctx := context.Background() mTablet := clusterInstance.NewVttabletInstance("replica", 0, "") @@ -84,7 +83,6 @@ func assertAllowedURLTest(t *testing.T, url string) { } func TestDenyAllSecurityPolicy(t *testing.T) { - defer cluster.PanicHandler(t) ctx := context.Background() mTablet := clusterInstance.NewVttabletInstance("replica", 0, "") @@ -116,7 +114,6 @@ func TestDenyAllSecurityPolicy(t *testing.T) { } func TestReadOnlySecurityPolicy(t *testing.T) { - defer cluster.PanicHandler(t) ctx := context.Background() mTablet := clusterInstance.NewVttabletInstance("replica", 0, "") diff --git a/go/test/endtoend/tabletmanager/tablet_test.go b/go/test/endtoend/tabletmanager/tablet_test.go index 1d8e897a4d2..2ded055230f 100644 --- a/go/test/endtoend/tabletmanager/tablet_test.go +++ b/go/test/endtoend/tabletmanager/tablet_test.go @@ -31,7 +31,6 @@ import ( // TestEnsureDB tests that vttablet creates the db as needed func TestEnsureDB(t *testing.T) { - defer cluster.PanicHandler(t) // Create new tablet tablet := clusterInstance.NewVttabletInstance("replica", 0, "") @@ -67,7 +66,6 @@ func TestEnsureDB(t *testing.T) { // TestResetReplicationParameters tests that the RPC ResetReplicationParameters works as intended. func TestResetReplicationParameters(t *testing.T) { - defer cluster.PanicHandler(t) // Create new tablet tablet := clusterInstance.NewVttabletInstance("replica", 0, "") diff --git a/go/test/endtoend/tabletmanager/throttler_topo/throttler_test.go b/go/test/endtoend/tabletmanager/throttler_topo/throttler_test.go index 08cea643940..226238a46c6 100644 --- a/go/test/endtoend/tabletmanager/throttler_topo/throttler_test.go +++ b/go/test/endtoend/tabletmanager/throttler_topo/throttler_test.go @@ -100,7 +100,6 @@ var ( ) func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitCode := func() int { @@ -175,6 +174,16 @@ func throttledApps(tablet *cluster.Vttablet) (resp *http.Response, respBody stri return resp, respBody, err } +func vitessThrottleCheck(tablet *cluster.Vttablet, skipRequestHeartbeats bool) (*vtctldatapb.CheckThrottlerResponse, error) { + flags := &throttle.CheckFlags{ + Scope: base.ShardScope, + SkipRequestHeartbeats: skipRequestHeartbeats, + MultiMetricsEnabled: true, + } + resp, err := throttler.CheckThrottler(clusterInstance, tablet, throttlerapp.VitessName, flags) + return resp, err +} + func throttleCheck(tablet *cluster.Vttablet, skipRequestHeartbeats bool) (*vtctldatapb.CheckThrottlerResponse, error) { flags := &throttle.CheckFlags{ Scope: base.ShardScope, @@ -215,7 +224,7 @@ func warmUpHeartbeat(t *testing.T) tabletmanagerdatapb.CheckThrottlerResponseCod } // waitForThrottleCheckStatus waits for the tablet to return the provided HTTP code in a throttle check -func waitForThrottleCheckStatus(t *testing.T, tablet *cluster.Vttablet, wantCode tabletmanagerdatapb.CheckThrottlerResponseCode) bool { +func waitForThrottleCheckStatus(t *testing.T, tablet *cluster.Vttablet, wantCode tabletmanagerdatapb.CheckThrottlerResponseCode) (*tabletmanagerdatapb.CheckThrottlerResponse, bool) { _ = warmUpHeartbeat(t) ctx, cancel := context.WithTimeout(context.Background(), onDemandHeartbeatDuration*4) defer cancel() @@ -229,11 +238,11 @@ func waitForThrottleCheckStatus(t *testing.T, tablet *cluster.Vttablet, wantCode if wantCode == resp.Check.ResponseCode { // Wait for any cached check values to be cleared and the new // status value to be in effect everywhere before returning. - return true + return resp.Check, true } select { case <-ctx.Done(): - return assert.EqualValues(t, wantCode, resp.Check.StatusCode, "response: %+v", resp) + return resp.Check, false case <-ticker.C: } } @@ -258,7 +267,6 @@ func vtgateExec(t *testing.T, query string, expectError string) *sqltypes.Result } func TestInitialThrottler(t *testing.T) { - defer cluster.PanicHandler(t) t.Run("validating OK response from disabled throttler", func(t *testing.T) { waitForThrottleCheckStatus(t, primaryTablet, tabletmanagerdatapb.CheckThrottlerResponseCode_OK) @@ -305,6 +313,17 @@ func TestInitialThrottler(t *testing.T) { waitForThrottleCheckStatus(t, primaryTablet, tabletmanagerdatapb.CheckThrottlerResponseCode_THRESHOLD_EXCEEDED) }) t.Run("setting high threshold", func(t *testing.T) { + { + req := &vtctldatapb.UpdateThrottlerConfigRequest{MetricName: base.LoadAvgMetricName.String(), Threshold: 5555} + _, err := throttler.UpdateThrottlerTopoConfig(clusterInstance, req, nil, nil) + assert.NoError(t, err) + } + { + req := &vtctldatapb.UpdateThrottlerConfigRequest{MetricName: base.MysqldLoadAvgMetricName.String(), Threshold: 5555} + _, err := throttler.UpdateThrottlerTopoConfig(clusterInstance, req, nil, nil) + assert.NoError(t, err) + } + req := &vtctldatapb.UpdateThrottlerConfigRequest{Threshold: extremelyHighThreshold.Seconds()} _, err := throttler.UpdateThrottlerTopoConfig(clusterInstance, req, nil, nil) assert.NoError(t, err) @@ -317,6 +336,19 @@ func TestInitialThrottler(t *testing.T) { t.Run("validating OK response from throttler with high threshold", func(t *testing.T) { waitForThrottleCheckStatus(t, primaryTablet, tabletmanagerdatapb.CheckThrottlerResponseCode_OK) }) + t.Run("validating vitess app throttler check", func(t *testing.T) { + resp, err := vitessThrottleCheck(primaryTablet, true) + require.NoError(t, err) + for _, metricName := range base.KnownMetricNames { + t.Run(metricName.String(), func(t *testing.T) { + assert.Contains(t, resp.Check.Metrics, metricName.String()) + metric := resp.Check.Metrics[metricName.String()] + require.NotNil(t, metric) + assert.Equal(t, tabletmanagerdatapb.CheckThrottlerResponseCode_OK, metric.ResponseCode, "metric: %+v", metric) + }) + } + }) + t.Run("setting low threshold", func(t *testing.T) { req := &vtctldatapb.UpdateThrottlerConfigRequest{Threshold: throttler.DefaultThreshold.Seconds()} _, err := throttler.UpdateThrottlerTopoConfig(clusterInstance, req, nil, nil) @@ -391,7 +423,6 @@ func TestInitialThrottler(t *testing.T) { } func TestThrottleViaApplySchema(t *testing.T) { - defer cluster.PanicHandler(t) t.Run("throttling via ApplySchema", func(t *testing.T) { vtctlParams := &cluster.ApplySchemaParams{DDLStrategy: "online"} _, err := clusterInstance.VtctldClientProcess.ApplySchemaWithOutput( @@ -429,12 +460,11 @@ func TestThrottleViaApplySchema(t *testing.T) { require.NotNil(t, keyspace.Keyspace.ThrottlerConfig.ThrottledApps) // ThrottledApps will actually be empty at this point, but more specifically we want to see that "online-ddl" is not there. appRule, ok := keyspace.Keyspace.ThrottlerConfig.ThrottledApps[throttlerapp.OnlineDDLName.String()] - assert.True(t, ok, "app rule: %v", appRule) + assert.False(t, ok, "app rule: %v", appRule) }) } func TestThrottlerAfterMetricsCollected(t *testing.T) { - defer cluster.PanicHandler(t) // By this time metrics will have been collected. We expect no lag, and something like: // {"StatusCode":200,"Value":0.282278,"Threshold":1,"Message":""} @@ -463,7 +493,6 @@ func TestThrottlerAfterMetricsCollected(t *testing.T) { } func TestLag(t *testing.T) { - defer cluster.PanicHandler(t) // Temporarily disable VTOrc recoveries because we want to // STOP replication specifically in order to increase the // lag and we DO NOT want VTOrc to try and fix this. @@ -602,7 +631,6 @@ func TestLag(t *testing.T) { } func TestNoReplicas(t *testing.T) { - defer cluster.PanicHandler(t) t.Run("changing replica to RDONLY", func(t *testing.T) { err := clusterInstance.VtctldClientProcess.ExecuteCommand("ChangeTabletType", replicaTablet.Alias, "RDONLY") assert.NoError(t, err) @@ -620,7 +648,6 @@ func TestNoReplicas(t *testing.T) { } func TestCustomQuery(t *testing.T) { - defer cluster.PanicHandler(t) t.Run("enabling throttler with custom query and threshold", func(t *testing.T) { req := &vtctldatapb.UpdateThrottlerConfigRequest{Enable: true, Threshold: customThreshold, CustomQuery: customQuery} @@ -688,7 +715,6 @@ func TestCustomQuery(t *testing.T) { } func TestRestoreDefaultQuery(t *testing.T) { - defer cluster.PanicHandler(t) // Validate going back from custom-query to default-query (replication lag) still works. t.Run("enabling throttler with default query and threshold", func(t *testing.T) { @@ -779,16 +805,16 @@ func TestUpdateAppCheckedMetrics(t *testing.T) { } waitForThrottleCheckStatus(t, primaryTablet, tabletmanagerdatapb.CheckThrottlerResponseCode_THRESHOLD_EXCEEDED) }) - t.Run("assigning 'loadavg' metrics to 'test' app", func(t *testing.T) { + t.Run("assigning 'threads_running' metrics to 'test' app", func(t *testing.T) { { - req := &vtctldatapb.UpdateThrottlerConfigRequest{MetricName: "loadavg", Threshold: 7777} + req := &vtctldatapb.UpdateThrottlerConfigRequest{MetricName: base.ThreadsRunningMetricName.String(), Threshold: 7777} _, err := throttler.UpdateThrottlerTopoConfig(clusterInstance, req, nil, nil) assert.NoError(t, err) } { req := &vtctldatapb.UpdateThrottlerConfigRequest{} appCheckedMetrics := map[string]*topodatapb.ThrottlerConfig_MetricNames{ - testAppName.String(): {Names: []string{"loadavg"}}, + testAppName.String(): {Names: []string{base.ThreadsRunningMetricName.String()}}, } _, err := throttler.UpdateThrottlerTopoConfig(clusterInstance, req, nil, appCheckedMetrics) assert.NoError(t, err) @@ -802,18 +828,18 @@ func TestUpdateAppCheckedMetrics(t *testing.T) { for _, tablet := range []cluster.Vttablet{*primaryTablet, *replicaTablet} { throttler.WaitForThrottlerStatusEnabled(t, &clusterInstance.VtctldClientProcess, &tablet, true, &throttler.Config{Query: throttler.DefaultQuery, Threshold: unreasonablyLowThreshold.Seconds()}, throttlerEnabledTimeout) } - t.Run("validating OK response from throttler since it's checking loadavg", func(t *testing.T) { - if !waitForThrottleCheckStatus(t, primaryTablet, tabletmanagerdatapb.CheckThrottlerResponseCode_OK) { + t.Run("validating OK response from throttler since it's checking threads_running", func(t *testing.T) { + if _, ok := waitForThrottleCheckStatus(t, primaryTablet, tabletmanagerdatapb.CheckThrottlerResponseCode_OK); !ok { t.Logf("throttler primary status: %+v", throttleStatus(t, primaryTablet)) t.Logf("throttler replica status: %+v", throttleStatus(t, replicaTablet)) } }) }) - t.Run("assigning 'loadavg,lag' metrics to 'test' app", func(t *testing.T) { + t.Run("assigning 'threads_running,lag' metrics to 'test' app", func(t *testing.T) { { req := &vtctldatapb.UpdateThrottlerConfigRequest{} appCheckedMetrics := map[string]*topodatapb.ThrottlerConfig_MetricNames{ - testAppName.String(): {Names: []string{"loadavg,lag"}}, + testAppName.String(): {Names: []string{base.ThreadsRunningMetricName.String(), base.LagMetricName.String()}}, } _, err := throttler.UpdateThrottlerTopoConfig(clusterInstance, req, nil, appCheckedMetrics) assert.NoError(t, err) @@ -831,9 +857,51 @@ func TestUpdateAppCheckedMetrics(t *testing.T) { waitForThrottleCheckStatus(t, primaryTablet, tabletmanagerdatapb.CheckThrottlerResponseCode_THRESHOLD_EXCEEDED) }) }) + t.Run("assigning 'mysqld-loadavg,mysqld-datadir-used-ratio' metrics to 'test' app", func(t *testing.T) { + { + req := &vtctldatapb.UpdateThrottlerConfigRequest{MetricName: base.MysqldDatadirUsedRatioMetricName.String(), Threshold: 0.9999} + _, err := throttler.UpdateThrottlerTopoConfig(clusterInstance, req, nil, nil) + assert.NoError(t, err) + } + { + req := &vtctldatapb.UpdateThrottlerConfigRequest{MetricName: base.MysqldLoadAvgMetricName.String(), Threshold: 5555} + _, err := throttler.UpdateThrottlerTopoConfig(clusterInstance, req, nil, nil) + assert.NoError(t, err) + } + { + req := &vtctldatapb.UpdateThrottlerConfigRequest{} + appCheckedMetrics := map[string]*topodatapb.ThrottlerConfig_MetricNames{ + testAppName.String(): {Names: []string{base.MysqldDatadirUsedRatioMetricName.String(), base.MysqldLoadAvgMetricName.String()}}, + } + _, err := throttler.UpdateThrottlerTopoConfig(clusterInstance, req, nil, appCheckedMetrics) + assert.NoError(t, err) + } + { + req := &vtctldatapb.UpdateThrottlerConfigRequest{Threshold: extremelyHighThreshold.Seconds()} + _, err := throttler.UpdateThrottlerTopoConfig(clusterInstance, req, nil, nil) + assert.NoError(t, err) + } + // Wait for the throttler to be enabled everywhere with new config. + for _, tablet := range []cluster.Vttablet{*primaryTablet, *replicaTablet} { + throttler.WaitForThrottlerStatusEnabled(t, &clusterInstance.VtctldClientProcess, &tablet, true, &throttler.Config{Query: throttler.DefaultQuery, Threshold: extremelyHighThreshold.Seconds()}, throttlerEnabledTimeout) + } + t.Run("validating OK response from throttler since it's checking mysqld-loadavg,mysqld-datadir-used-ratio", func(t *testing.T) { + resp, ok := waitForThrottleCheckStatus(t, primaryTablet, tabletmanagerdatapb.CheckThrottlerResponseCode_OK) + if !ok { + t.Logf("response: %+v", resp) + t.Logf("throttler primary status: %+v", throttleStatus(t, primaryTablet)) + t.Logf("throttler replica status: %+v", throttleStatus(t, replicaTablet)) + } + require.Contains(t, resp.Metrics, base.MysqldDatadirUsedRatioMetricName.String()) + require.Contains(t, resp.Metrics, base.MysqldLoadAvgMetricName.String()) + assert.NotContains(t, resp.Metrics, base.ThreadsRunningMetricName.String()) + + assert.NotZero(t, resp.Metrics[base.MysqldDatadirUsedRatioMetricName.String()].Value) + }) + }) t.Run("removing assignment from 'test' app and restoring defaults", func(t *testing.T) { { - req := &vtctldatapb.UpdateThrottlerConfigRequest{MetricName: "loadavg", Threshold: 0} + req := &vtctldatapb.UpdateThrottlerConfigRequest{MetricName: base.ThreadsRunningMetricName.String(), Threshold: 0} _, err := throttler.UpdateThrottlerTopoConfig(clusterInstance, req, nil, nil) assert.NoError(t, err) } diff --git a/go/test/endtoend/throttler/util.go b/go/test/endtoend/throttler/util.go index fccad19c324..a426355e01c 100644 --- a/go/test/endtoend/throttler/util.go +++ b/go/test/endtoend/throttler/util.go @@ -529,7 +529,7 @@ func WaitForValidData(t *testing.T, tablet *cluster.Vttablet, timeout time.Durat selfCheckURL := fmt.Sprintf("http://localhost:%d/throttler/check-self", tablet.HTTPPort) ctx, cancel := context.WithTimeout(context.Background(), timeout) defer cancel() - ticker := time.NewTicker(500 * time.Millisecond) + ticker := time.NewTicker(time.Second) defer ticker.Stop() for { @@ -548,8 +548,10 @@ func WaitForValidData(t *testing.T, tablet *cluster.Vttablet, timeout time.Durat } select { case <-ctx.Done(): - t.Errorf("timed out waiting for %s tablet's throttler to return a valid result after %v; last seen value: %+v", - tablet.Alias, timeout, checkResp) + respByte, _ := io.ReadAll(checkResp.Body) + body := string(respByte) + require.Failf(t, "time out", "waiting for %s tablet's throttler to return a valid result after %v; last seen result: %+v", + tablet.Alias, timeout, body) return case <-ticker.C: } diff --git a/go/test/endtoend/topoconncache/main_test.go b/go/test/endtoend/topoconncache/main_test.go index 26eb3918a0b..074bf875165 100644 --- a/go/test/endtoend/topoconncache/main_test.go +++ b/go/test/endtoend/topoconncache/main_test.go @@ -97,7 +97,6 @@ Topology: We create a keyspace with two shards , having 3 tablets each. Primarie to 'zone1' and replicas/rdonly belongs to cell2. */ func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitcode, err := func() (int, error) { diff --git a/go/test/endtoend/topoconncache/topo_conn_cache_test.go b/go/test/endtoend/topoconncache/topo_conn_cache_test.go index 082ecc5717f..f676af318cd 100644 --- a/go/test/endtoend/topoconncache/topo_conn_cache_test.go +++ b/go/test/endtoend/topoconncache/topo_conn_cache_test.go @@ -37,7 +37,6 @@ import ( 4. 'ListAllTablets' should return all the new tablets. */ func TestVtctldListAllTablets(t *testing.T) { - defer cluster.PanicHandler(t) url := fmt.Sprintf("http://%s:%d/api/keyspaces/", clusterInstance.Hostname, clusterInstance.VtctldHTTPPort) testURL(t, url, "keyspace url") diff --git a/go/test/endtoend/topotest/consul/main_test.go b/go/test/endtoend/topotest/consul/main_test.go index 0f6fa6ce554..c6d48f44930 100644 --- a/go/test/endtoend/topotest/consul/main_test.go +++ b/go/test/endtoend/topotest/consul/main_test.go @@ -63,7 +63,6 @@ CREATE TABLE t1 ( ) func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitCode := func() int { @@ -99,7 +98,6 @@ func TestMain(m *testing.M) { } func TestTopoRestart(t *testing.T) { - defer cluster.PanicHandler(t) ctx := context.Background() vtParams := mysql.ConnParams{ Host: "localhost", diff --git a/go/test/endtoend/topotest/etcd2/main_test.go b/go/test/endtoend/topotest/etcd2/main_test.go index 67b0dbbc8f7..ee2b542109b 100644 --- a/go/test/endtoend/topotest/etcd2/main_test.go +++ b/go/test/endtoend/topotest/etcd2/main_test.go @@ -64,7 +64,6 @@ CREATE TABLE t1 ( ) func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitCode := func() int { @@ -99,7 +98,6 @@ func TestMain(m *testing.M) { } func TestTopoDownServingQuery(t *testing.T) { - defer cluster.PanicHandler(t) ctx := context.Background() vtParams := mysql.ConnParams{ Host: "localhost", diff --git a/go/test/endtoend/topotest/zk2/main_test.go b/go/test/endtoend/topotest/zk2/main_test.go index 48636331747..c6569519a3d 100644 --- a/go/test/endtoend/topotest/zk2/main_test.go +++ b/go/test/endtoend/topotest/zk2/main_test.go @@ -63,7 +63,6 @@ CREATE TABLE t1 ( ) func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitCode := func() int { @@ -99,7 +98,6 @@ func TestMain(m *testing.M) { } func TestTopoDownServingQuery(t *testing.T) { - defer cluster.PanicHandler(t) ctx := context.Background() vtParams := mysql.ConnParams{ Host: "localhost", diff --git a/go/test/endtoend/transaction/restart/main_test.go b/go/test/endtoend/transaction/restart/main_test.go index 01185b5fa59..caa3111ad49 100644 --- a/go/test/endtoend/transaction/restart/main_test.go +++ b/go/test/endtoend/transaction/restart/main_test.go @@ -41,7 +41,6 @@ var ( ) func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitCode := func() int { diff --git a/go/test/endtoend/transaction/rollback/txn_rollback_shutdown_test.go b/go/test/endtoend/transaction/rollback/txn_rollback_shutdown_test.go index 2dff9f7b95f..c7bef098c05 100644 --- a/go/test/endtoend/transaction/rollback/txn_rollback_shutdown_test.go +++ b/go/test/endtoend/transaction/rollback/txn_rollback_shutdown_test.go @@ -48,7 +48,6 @@ var ( ) func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitCode := func() int { @@ -87,7 +86,6 @@ func TestMain(m *testing.M) { } func TestTransactionRollBackWhenShutDown(t *testing.T) { - defer cluster.PanicHandler(t) ctx := context.Background() conn, err := mysql.Connect(ctx, &vtParams) require.NoError(t, err) @@ -122,7 +120,6 @@ func TestTransactionRollBackWhenShutDown(t *testing.T) { } func TestErrorInAutocommitSession(t *testing.T) { - defer cluster.PanicHandler(t) ctx := context.Background() conn, err := mysql.Connect(ctx, &vtParams) require.NoError(t, err) diff --git a/go/test/endtoend/transaction/single/main_test.go b/go/test/endtoend/transaction/single/main_test.go index ec2dbd6378a..1eab3cea276 100644 --- a/go/test/endtoend/transaction/single/main_test.go +++ b/go/test/endtoend/transaction/single/main_test.go @@ -46,7 +46,6 @@ var ( ) func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitCode := func() int { diff --git a/go/test/endtoend/transaction/twopc/fuzz/fuzzer_test.go b/go/test/endtoend/transaction/twopc/fuzz/fuzzer_test.go index 75bc46bacab..da6486242df 100644 --- a/go/test/endtoend/transaction/twopc/fuzz/fuzzer_test.go +++ b/go/test/endtoend/transaction/twopc/fuzz/fuzzer_test.go @@ -53,7 +53,9 @@ var ( } insertIntoFuzzUpdate = "INSERT INTO twopc_fuzzer_update (id, col) VALUES (%d, %d)" + insertIntoFuzzMulti = "INSERT INTO twopc_fuzzer_multi (id) VALUES (%d)" updateFuzzUpdate = "UPDATE twopc_fuzzer_update SET col = col + %d WHERE id = %d" + updateFuzzUpdateMulti = "UPDATE twopc_fuzzer_update join twopc_fuzzer_multi using (id) SET col = col + %d WHERE id = %d" insertIntoFuzzInsert = "INSERT INTO twopc_fuzzer_insert (id, updateSet, threadId) VALUES (%d, %d, %d)" selectFromFuzzUpdate = "SELECT col FROM twopc_fuzzer_update WHERE id = %d" selectIdFromFuzzInsert = "SELECT threadId FROM twopc_fuzzer_insert WHERE updateSet = %d AND id = %d ORDER BY col" @@ -294,6 +296,10 @@ func (fz *fuzzer) initialize(t *testing.T, conn *mysql.Conn) { for _, id := range updateSet { _, err := conn.ExecuteFetch(fmt.Sprintf(insertIntoFuzzUpdate, id, 0), 0, false) require.NoError(t, err) + // We insert the same id values in multi table as we in the update table. We use this for running + // multi-table updates and inserts. + _, err = conn.ExecuteFetch(fmt.Sprintf(insertIntoFuzzMulti, id), 0, false) + require.NoError(t, err) } } } @@ -331,12 +337,20 @@ func (fz *fuzzer) generateAndExecuteTransaction(threadId int) { _, _ = conn.ExecuteFetch(finalCommand, 0, false) } +func getUpdateQuery(incrementVal int32, id int) string { + if rand.Intn(2) == 1 { + return fmt.Sprintf(updateFuzzUpdateMulti, incrementVal, id) + } + return fmt.Sprintf(updateFuzzUpdate, incrementVal, id) +} + // generateUpdateQueries generates the queries to run updates on the twopc_fuzzer_update table. // It takes the update set index and the value to increment the set by. func (fz *fuzzer) generateUpdateQueries(updateSet int, incrementVal int32) []string { var queries []string for _, id := range fz.updateRowsVals[updateSet] { - queries = append(queries, fmt.Sprintf(updateFuzzUpdate, incrementVal, id)) + // Use multi table DML queries half the time. + queries = append(queries, getUpdateQuery(incrementVal, id)) } rand.Shuffle(len(queries), func(i, j int) { queries[i], queries[j] = queries[j], queries[i] @@ -427,7 +441,7 @@ func (fz *fuzzer) randomDML() string { } // Generate UPDATE updateId := fz.updateRowsVals[rand.Intn(len(fz.updateRowsVals))][rand.Intn(len(updateRowBaseVals))] - return fmt.Sprintf(updateFuzzUpdate, rand.Intn(100000), updateId) + return getUpdateQuery(rand.Int31n(100000), updateId) } /* diff --git a/go/test/endtoend/transaction/twopc/fuzz/main_test.go b/go/test/endtoend/transaction/twopc/fuzz/main_test.go index 1b05615d51a..4d168fbdde0 100644 --- a/go/test/endtoend/transaction/twopc/fuzz/main_test.go +++ b/go/test/endtoend/transaction/twopc/fuzz/main_test.go @@ -48,7 +48,6 @@ var ( ) func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitcode := func() int { @@ -70,7 +69,6 @@ func TestMain(m *testing.M) { "--tablet_refresh_interval", "2s", ) clusterInstance.VtTabletExtraArgs = append(clusterInstance.VtTabletExtraArgs, - "--twopc_enable", "--twopc_abandon_age", "1", "--migration_check_interval", "2s", ) @@ -122,9 +120,9 @@ func start(t *testing.T) (*mysql.Conn, func()) { } func cleanup(t *testing.T) { - cluster.PanicHandler(t) utils.ClearOutTable(t, vtParams, "twopc_fuzzer_insert") utils.ClearOutTable(t, vtParams, "twopc_fuzzer_update") + utils.ClearOutTable(t, vtParams, "twopc_fuzzer_multi") utils.ClearOutTable(t, vtParams, "twopc_t1") } diff --git a/go/test/endtoend/transaction/twopc/fuzz/schema.sql b/go/test/endtoend/transaction/twopc/fuzz/schema.sql index 5173166bfd4..b070466087d 100644 --- a/go/test/endtoend/transaction/twopc/fuzz/schema.sql +++ b/go/test/endtoend/transaction/twopc/fuzz/schema.sql @@ -4,6 +4,11 @@ create table twopc_fuzzer_update ( primary key (id) ) Engine=InnoDB; +create table twopc_fuzzer_multi ( + id bigint, + primary key (id) +) Engine=InnoDB; + create table twopc_fuzzer_insert ( id bigint, updateSet bigint, diff --git a/go/test/endtoend/transaction/twopc/fuzz/vschema.json b/go/test/endtoend/transaction/twopc/fuzz/vschema.json index 415b5958f54..83107bc96ff 100644 --- a/go/test/endtoend/transaction/twopc/fuzz/vschema.json +++ b/go/test/endtoend/transaction/twopc/fuzz/vschema.json @@ -3,6 +3,9 @@ "vindexes": { "reverse_bits": { "type": "reverse_bits" + }, + "xxhash": { + "type": "xxhash" } }, "tables": { @@ -22,6 +25,14 @@ } ] }, + "twopc_fuzzer_multi": { + "column_vindexes": [ + { + "column": "id", + "name": "xxhash" + } + ] + }, "twopc_t1": { "column_vindexes": [ { diff --git a/go/test/endtoend/transaction/twopc/main_test.go b/go/test/endtoend/transaction/twopc/main_test.go index 2f27198fd2e..6d09c174a4d 100644 --- a/go/test/endtoend/transaction/twopc/main_test.go +++ b/go/test/endtoend/transaction/twopc/main_test.go @@ -61,7 +61,6 @@ var ( ) func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitcode := func() int { @@ -82,7 +81,6 @@ func TestMain(m *testing.M) { "--grpc_use_effective_callerid", ) clusterInstance.VtTabletExtraArgs = append(clusterInstance.VtTabletExtraArgs, - "--twopc_enable", "--twopc_abandon_age", "1", "--queryserver-config-transaction-cap", "3", "--queryserver-config-transaction-timeout", "400s", @@ -140,9 +138,11 @@ func start(t *testing.T) (*mysql.Conn, func()) { } func cleanup(t *testing.T) { - cluster.PanicHandler(t) twopcutil.ClearOutTable(t, vtParams, "twopc_user") twopcutil.ClearOutTable(t, vtParams, "twopc_t1") + twopcutil.ClearOutTable(t, vtParams, "twopc_lookup") + twopcutil.ClearOutTable(t, vtParams, "lookup_unique") + twopcutil.ClearOutTable(t, vtParams, "lookup") sm.reset() } @@ -162,7 +162,6 @@ func startWithMySQL(t *testing.T) (utils.MySQLCompare, func()) { return mcmp, func() { deleteAll() mcmp.Close() - cluster.PanicHandler(t) } } diff --git a/go/test/endtoend/transaction/twopc/metric/main_test.go b/go/test/endtoend/transaction/twopc/metric/main_test.go index 73cc380a900..61a43017ef9 100644 --- a/go/test/endtoend/transaction/twopc/metric/main_test.go +++ b/go/test/endtoend/transaction/twopc/metric/main_test.go @@ -48,7 +48,6 @@ var ( ) func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitcode := func() int { @@ -69,7 +68,6 @@ func TestMain(m *testing.M) { "--grpc_use_effective_callerid", ) clusterInstance.VtTabletExtraArgs = append(clusterInstance.VtTabletExtraArgs, - "--twopc_enable", "--twopc_abandon_age", "1", "--queryserver-config-transaction-cap", "100", ) @@ -111,7 +109,6 @@ func start(t *testing.T) (*mysql.Conn, func()) { } func cleanup(t *testing.T) { - cluster.PanicHandler(t) twopcutil.ClearOutTable(t, vtParams, "twopc_user") twopcutil.ClearOutTable(t, vtParams, "twopc_t1") } diff --git a/go/test/endtoend/transaction/twopc/schema.sql b/go/test/endtoend/transaction/twopc/schema.sql index 7c289a03c2a..aff839eabe9 100644 --- a/go/test/endtoend/transaction/twopc/schema.sql +++ b/go/test/endtoend/transaction/twopc/schema.sql @@ -18,4 +18,27 @@ create table twopc_t1 id bigint, col bigint, primary key (id) -) Engine=InnoDB; \ No newline at end of file +) Engine=InnoDB; + +create table twopc_lookup +( + id bigint, + col bigint, + col_unique bigint, + primary key (id) +) Engine=InnoDB; + +create table lookup +( + col varchar(128), + id bigint, + keyspace_id varbinary(100), + primary key (id) +) Engine = InnoDB; + +create table lookup_unique +( + col_unique varchar(128), + keyspace_id varbinary(100), + primary key (col_unique) +) Engine = InnoDB; diff --git a/go/test/endtoend/transaction/twopc/stress/main_test.go b/go/test/endtoend/transaction/twopc/stress/main_test.go index 76cd05df50a..4da4f86bdff 100644 --- a/go/test/endtoend/transaction/twopc/stress/main_test.go +++ b/go/test/endtoend/transaction/twopc/stress/main_test.go @@ -48,7 +48,6 @@ var ( ) func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitcode := func() int { @@ -70,7 +69,6 @@ func TestMain(m *testing.M) { "--tablet_refresh_interval", "2s", ) clusterInstance.VtTabletExtraArgs = append(clusterInstance.VtTabletExtraArgs, - "--twopc_enable", "--twopc_abandon_age", "1", "--migration_check_interval", "2s", "--onterm_timeout", "1s", @@ -124,7 +122,6 @@ func start(t *testing.T) (*mysql.Conn, func()) { } func cleanup(t *testing.T) { - cluster.PanicHandler(t) utils.ClearOutTable(t, vtParams, "twopc_t1") utils.ClearOutTable(t, vtParams, "twopc_settings") } diff --git a/go/test/endtoend/transaction/twopc/twopc_test.go b/go/test/endtoend/transaction/twopc/twopc_test.go index 033d93f8792..72a4f950a5a 100644 --- a/go/test/endtoend/transaction/twopc/twopc_test.go +++ b/go/test/endtoend/transaction/twopc/twopc_test.go @@ -1349,24 +1349,15 @@ func TestSemiSyncRequiredWithTwoPC(t *testing.T) { // cleanup all the old data. conn, closer := start(t) defer closer() - - out, err := clusterInstance.VtctldClientProcess.ExecuteCommandWithOutput("SetKeyspaceDurabilityPolicy", keyspaceName, "--durability-policy=none") - require.NoError(t, err, out) defer func() { - clusterInstance.VtctldClientProcess.ExecuteCommandWithOutput("SetKeyspaceDurabilityPolicy", keyspaceName, "--durability-policy=semi_sync") - for _, shard := range clusterInstance.Keyspaces[0].Shards { - clusterInstance.VtctldClientProcess.PlannedReparentShard(keyspaceName, shard.Name, shard.Vttablets[0].Alias) - } + reparentAllShards(t, clusterInstance, 0) }() - // After changing the durability policy for the given keyspace to none, we run PRS. - shard := clusterInstance.Keyspaces[0].Shards[2] - newPrimary := shard.Vttablets[1] - _, err = clusterInstance.VtctldClientProcess.ExecuteCommandWithOutput( - "PlannedReparentShard", - fmt.Sprintf("%s/%s", keyspaceName, shard.Name), - "--new-primary", newPrimary.Alias) - require.NoError(t, err) + reparentAllShards(t, clusterInstance, 0) + out, err := clusterInstance.VtctldClientProcess.ExecuteCommandWithOutput("SetKeyspaceDurabilityPolicy", keyspaceName, "--durability-policy=none") + require.NoError(t, err, out) + // After changing the durability policy for the given keyspace to none, we run PRS to ensure the changes have taken effect. + reparentAllShards(t, clusterInstance, 1) // A new distributed transaction should fail. utils.Exec(t, conn, "begin") @@ -1376,6 +1367,26 @@ func TestSemiSyncRequiredWithTwoPC(t *testing.T) { _, err = utils.ExecAllowError(t, conn, "commit") require.Error(t, err) require.ErrorContains(t, err, "two-pc is enabled, but semi-sync is not") + + _, err = clusterInstance.VtctldClientProcess.ExecuteCommandWithOutput("SetKeyspaceDurabilityPolicy", keyspaceName, "--durability-policy=semi_sync") + require.NoError(t, err) + reparentAllShards(t, clusterInstance, 0) + + // Transaction should now succeed. + utils.Exec(t, conn, "begin") + utils.Exec(t, conn, "insert into twopc_t1(id, col) values(4, 4)") + utils.Exec(t, conn, "insert into twopc_t1(id, col) values(6, 4)") + utils.Exec(t, conn, "insert into twopc_t1(id, col) values(9, 4)") + _, err = utils.ExecAllowError(t, conn, "commit") + require.NoError(t, err) +} + +// reparentAllShards reparents all the shards to the given tablet index for that shard. +func reparentAllShards(t *testing.T, clusterInstance *cluster.LocalProcessCluster, idx int) { + for _, shard := range clusterInstance.Keyspaces[0].Shards { + err := clusterInstance.VtctldClientProcess.PlannedReparentShard(keyspaceName, shard.Name, shard.Vttablets[idx].Alias) + require.NoError(t, err) + } } // TestReadTransactionStatus tests that read transaction state rpc works as expected. @@ -1395,8 +1406,6 @@ func TestReadTransactionStatus(t *testing.T) { "insert into twopc_t1(id, col) values(6, 4)", "insert into twopc_t1(id, col) values(9, 4)", }) - // Allow enough time for the commit to have started. - time.Sleep(1 * time.Second) // Create a tablet manager client and use it to read the transaction state. tmc := grpctmclient.NewClient() @@ -1405,12 +1414,24 @@ func TestReadTransactionStatus(t *testing.T) { defer cancel() primaryTablet := getTablet(clusterInstance.Keyspaces[0].Shards[2].FindPrimaryTablet().GrpcPort) + // Wait for the transaction to show up in the unresolved list. var unresTransaction *querypb.TransactionMetadata - for _, shard := range clusterInstance.Keyspaces[0].Shards { - urtRes, err := tmc.GetUnresolvedTransactions(ctx, getTablet(shard.FindPrimaryTablet().GrpcPort), 1) - require.NoError(t, err) - if len(urtRes) > 0 { - unresTransaction = urtRes[0] + timeout := time.After(10 * time.Second) + for { + for _, shard := range clusterInstance.Keyspaces[0].Shards { + urtRes, err := tmc.GetUnresolvedTransactions(ctx, getTablet(shard.FindPrimaryTablet().GrpcPort), 1) + require.NoError(t, err) + if len(urtRes) > 0 { + unresTransaction = urtRes[0] + } + } + if unresTransaction != nil { + break + } + select { + case <-timeout: + require.Fail(t, "timed out waiting for unresolved transaction") + default: } } require.NotNil(t, unresTransaction) @@ -1439,6 +1460,228 @@ func TestReadTransactionStatus(t *testing.T) { wg.Wait() } +// TestVindexes tests that different vindexes work well with two-phase commit. +func TestVindexes(t *testing.T) { + testcases := []struct { + name string + initQueries []string + testQueries []string + logExpected map[string][]string + }{ + { + name: "Lookup Single Update", + initQueries: []string{ + "insert into twopc_lookup(id, col, col_unique) values(4, 4, 6)", + "insert into twopc_lookup(id, col, col_unique) values(6, 4, 9)", + "insert into twopc_lookup(id, col, col_unique) values(9, 4, 4)", + }, + testQueries: []string{ + "begin", + "update twopc_lookup set col = 9 where col_unique = 9", + "commit", + }, + logExpected: map[string][]string{ + "ks.redo_statement:80-": { + "insert:[VARCHAR(\"dtid-3\") INT64(1) BLOB(\"delete from lookup where col = 4 and id = 6 and keyspace_id = _binary'`\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0' limit 10001\")]", + "insert:[VARCHAR(\"dtid-3\") INT64(2) BLOB(\"insert into lookup(col, id, keyspace_id) values (9, 6, _binary'`\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0')\")]", + "delete:[VARCHAR(\"dtid-3\") INT64(1) BLOB(\"delete from lookup where col = 4 and id = 6 and keyspace_id = _binary'`\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0' limit 10001\")]", + "delete:[VARCHAR(\"dtid-3\") INT64(2) BLOB(\"insert into lookup(col, id, keyspace_id) values (9, 6, _binary'`\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0')\")]", + }, + "ks.twopc_lookup:40-80": { + "update:[INT64(6) INT64(9) INT64(9)]", + }, + "ks.lookup:80-": { + "delete:[VARCHAR(\"4\") INT64(6) VARBINARY(\"`\\x00\\x00\\x00\\x00\\x00\\x00\\x00\")]", + "insert:[VARCHAR(\"9\") INT64(6) VARBINARY(\"`\\x00\\x00\\x00\\x00\\x00\\x00\\x00\")]", + }, + }, + }, + { + name: "Lookup-Unique Single Update", + initQueries: []string{ + "insert into twopc_lookup(id, col, col_unique) values(4, 4, 6)", + "insert into twopc_lookup(id, col, col_unique) values(6, 4, 9)", + "insert into twopc_lookup(id, col, col_unique) values(9, 4, 4)", + }, + testQueries: []string{ + "begin", + "update twopc_lookup set col_unique = 20 where col_unique = 9", + "commit", + }, + logExpected: map[string][]string{ + "ks.redo_statement:80-": { + "insert:[VARCHAR(\"dtid-3\") INT64(1) BLOB(\"delete from lookup_unique where col_unique = 9 and keyspace_id = _binary'`\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0' limit 10001\")]", + "insert:[VARCHAR(\"dtid-3\") INT64(2) BLOB(\"insert into lookup_unique(col_unique, keyspace_id) values (20, _binary'`\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0')\")]", + "delete:[VARCHAR(\"dtid-3\") INT64(1) BLOB(\"delete from lookup_unique where col_unique = 9 and keyspace_id = _binary'`\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0' limit 10001\")]", + "delete:[VARCHAR(\"dtid-3\") INT64(2) BLOB(\"insert into lookup_unique(col_unique, keyspace_id) values (20, _binary'`\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0')\")]", + }, + "ks.twopc_lookup:40-80": { + "update:[INT64(6) INT64(4) INT64(20)]", + }, + "ks.lookup_unique:80-": { + "delete:[VARCHAR(\"9\") VARBINARY(\"`\\x00\\x00\\x00\\x00\\x00\\x00\\x00\")]", + "insert:[VARCHAR(\"20\") VARBINARY(\"`\\x00\\x00\\x00\\x00\\x00\\x00\\x00\")]", + }, + }, + }, + { + name: "Lookup And Lookup-Unique Single Delete", + initQueries: []string{ + "insert into twopc_lookup(id, col, col_unique) values(4, 4, 6)", + "insert into twopc_lookup(id, col, col_unique) values(6, 4, 9)", + "insert into twopc_lookup(id, col, col_unique) values(9, 4, 4)", + }, + testQueries: []string{ + "begin", + "delete from twopc_lookup where col_unique = 9", + "commit", + }, + logExpected: map[string][]string{ + "ks.redo_statement:80-": { + "insert:[VARCHAR(\"dtid-3\") INT64(1) BLOB(\"delete from lookup where col = 4 and id = 6 and keyspace_id = _binary'`\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0' limit 10001\")]", + "insert:[VARCHAR(\"dtid-3\") INT64(2) BLOB(\"delete from lookup_unique where col_unique = 9 and keyspace_id = _binary'`\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0' limit 10001\")]", + "delete:[VARCHAR(\"dtid-3\") INT64(1) BLOB(\"delete from lookup where col = 4 and id = 6 and keyspace_id = _binary'`\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0' limit 10001\")]", + "delete:[VARCHAR(\"dtid-3\") INT64(2) BLOB(\"delete from lookup_unique where col_unique = 9 and keyspace_id = _binary'`\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0' limit 10001\")]", + }, + "ks.twopc_lookup:40-80": { + "delete:[INT64(6) INT64(4) INT64(9)]", + }, + "ks.lookup_unique:80-": { + "delete:[VARCHAR(\"9\") VARBINARY(\"`\\x00\\x00\\x00\\x00\\x00\\x00\\x00\")]", + }, + "ks.lookup:80-": { + "delete:[VARCHAR(\"4\") INT64(6) VARBINARY(\"`\\x00\\x00\\x00\\x00\\x00\\x00\\x00\")]", + }, + }, + }, + { + name: "Lookup And Lookup-Unique Single Insertion", + initQueries: []string{ + "insert into twopc_lookup(id, col, col_unique) values(4, 4, 6)", + "insert into twopc_lookup(id, col, col_unique) values(6, 4, 9)", + "insert into twopc_lookup(id, col, col_unique) values(9, 4, 4)", + }, + testQueries: []string{ + "begin", + "insert into twopc_lookup(id, col, col_unique) values(20, 4, 22)", + "commit", + }, + logExpected: map[string][]string{ + "ks.redo_statement:80-": { + "insert:[VARCHAR(\"dtid-3\") INT64(1) BLOB(\"insert into lookup(col, id, keyspace_id) values (4, 20, _binary'(\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0')\")]", + "delete:[VARCHAR(\"dtid-3\") INT64(1) BLOB(\"insert into lookup(col, id, keyspace_id) values (4, 20, _binary'(\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0')\")]", + }, + "ks.lookup:80-": { + "insert:[VARCHAR(\"4\") INT64(20) VARBINARY(\"(\\x00\\x00\\x00\\x00\\x00\\x00\\x00\")]", + }, + "ks.lookup_unique:-40": { + "insert:[VARCHAR(\"22\") VARBINARY(\"(\\x00\\x00\\x00\\x00\\x00\\x00\\x00\")]", + }, + "ks.twopc_lookup:-40": { + "insert:[INT64(20) INT64(4) INT64(22)]", + }, + }, + }, + { + name: "Lookup And Lookup-Unique Mix", + initQueries: []string{ + "insert into twopc_lookup(id, col, col_unique) values(4, 4, 6)", + "insert into twopc_lookup(id, col, col_unique) values(6, 4, 9)", + "insert into twopc_lookup(id, col, col_unique) values(9, 4, 4)", + }, + testQueries: []string{ + "begin", + "insert into twopc_lookup(id, col, col_unique) values(20, 4, 22)", + "update twopc_lookup set col = 9 where col_unique = 9", + "delete from twopc_lookup where id = 9", + "commit", + }, + logExpected: map[string][]string{ + "ks.redo_statement:80-": { + "insert:[VARCHAR(\"dtid-3\") INT64(1) BLOB(\"insert into lookup(col, id, keyspace_id) values (4, 20, _binary'(\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0')\")]", + "insert:[VARCHAR(\"dtid-3\") INT64(2) BLOB(\"delete from lookup where col = 4 and id = 6 and keyspace_id = _binary'`\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0' limit 10001\")]", + "insert:[VARCHAR(\"dtid-3\") INT64(3) BLOB(\"insert into lookup(col, id, keyspace_id) values (9, 6, _binary'`\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0')\")]", + "insert:[VARCHAR(\"dtid-3\") INT64(4) BLOB(\"delete from lookup where col = 4 and id = 9 and keyspace_id = _binary'\\x90\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0' limit 10001\")]", + "insert:[VARCHAR(\"dtid-3\") INT64(5) BLOB(\"delete from lookup_unique where col_unique = 4 and keyspace_id = _binary'\\x90\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0' limit 10001\")]", + "insert:[VARCHAR(\"dtid-3\") INT64(6) BLOB(\"delete from twopc_lookup where id = 9 limit 10001 /* INT64 */\")]", + "delete:[VARCHAR(\"dtid-3\") INT64(1) BLOB(\"insert into lookup(col, id, keyspace_id) values (4, 20, _binary'(\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0')\")]", + "delete:[VARCHAR(\"dtid-3\") INT64(2) BLOB(\"delete from lookup where col = 4 and id = 6 and keyspace_id = _binary'`\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0' limit 10001\")]", + "delete:[VARCHAR(\"dtid-3\") INT64(3) BLOB(\"insert into lookup(col, id, keyspace_id) values (9, 6, _binary'`\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0')\")]", + "delete:[VARCHAR(\"dtid-3\") INT64(4) BLOB(\"delete from lookup where col = 4 and id = 9 and keyspace_id = _binary'\\x90\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0' limit 10001\")]", + "delete:[VARCHAR(\"dtid-3\") INT64(5) BLOB(\"delete from lookup_unique where col_unique = 4 and keyspace_id = _binary'\\x90\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0' limit 10001\")]", + "delete:[VARCHAR(\"dtid-3\") INT64(6) BLOB(\"delete from twopc_lookup where id = 9 limit 10001 /* INT64 */\")]", + }, + "ks.redo_statement:40-80": { + "insert:[VARCHAR(\"dtid-3\") INT64(1) BLOB(\"update twopc_lookup set col = 9 where col_unique = 9 limit 10001 /* INT64 */\")]", + "delete:[VARCHAR(\"dtid-3\") INT64(1) BLOB(\"update twopc_lookup set col = 9 where col_unique = 9 limit 10001 /* INT64 */\")]", + }, + "ks.twopc_lookup:-40": { + "insert:[INT64(20) INT64(4) INT64(22)]", + }, + "ks.twopc_lookup:40-80": { + "update:[INT64(6) INT64(9) INT64(9)]", + }, + "ks.twopc_lookup:80-": { + "delete:[INT64(9) INT64(4) INT64(4)]", + }, + "ks.lookup_unique:-40": { + "insert:[VARCHAR(\"22\") VARBINARY(\"(\\x00\\x00\\x00\\x00\\x00\\x00\\x00\")]", + }, + "ks.lookup_unique:80-": { + "delete:[VARCHAR(\"4\") VARBINARY(\"\\x90\\x00\\x00\\x00\\x00\\x00\\x00\\x00\")]", + }, + "ks.lookup:80-": { + "insert:[VARCHAR(\"4\") INT64(20) VARBINARY(\"(\\x00\\x00\\x00\\x00\\x00\\x00\\x00\")]", + "delete:[VARCHAR(\"4\") INT64(6) VARBINARY(\"`\\x00\\x00\\x00\\x00\\x00\\x00\\x00\")]", + "insert:[VARCHAR(\"9\") INT64(6) VARBINARY(\"`\\x00\\x00\\x00\\x00\\x00\\x00\\x00\")]", + "delete:[VARCHAR(\"4\") INT64(9) VARBINARY(\"\\x90\\x00\\x00\\x00\\x00\\x00\\x00\\x00\")]", + }, + }, + }, + } + + for _, tt := range testcases { + t.Run(tt.name, func(t *testing.T) { + defer cleanup(t) + + vtgateConn, err := cluster.DialVTGate(context.Background(), t.Name(), vtgateGrpcAddress, "dt_user", "") + require.NoError(t, err) + defer vtgateConn.Close() + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + ch := make(chan *binlogdatapb.VEvent) + runVStream(t, ctx, ch, vtgateConn) + + conn := vtgateConn.Session("", nil) + qCtx, cancel := context.WithCancel(context.Background()) + defer cancel() + + // initial insert + for _, query := range tt.initQueries { + execute(qCtx, t, conn, query) + } + + // ignore initial change + tableMap := make(map[string][]*querypb.Field) + dtMap := make(map[string]string) + _ = retrieveTransitionsWithTimeout(t, ch, tableMap, dtMap, 2*time.Second) + + // Insert into multiple shards + for _, query := range tt.testQueries { + execute(qCtx, t, conn, query) + } + + // Below check ensures that the transaction is resolved by the resolver on receiving unresolved transaction signal from MM. + logTable := retrieveTransitionsWithTimeout(t, ch, tableMap, dtMap, 2*time.Second) + for key, val := range tt.logExpected { + assert.EqualValues(t, val, logTable[key], key) + } + }) + } +} + func getTablet(tabletGrpcPort int) *tabletpb.Tablet { portMap := make(map[string]int32) portMap["grpc"] = int32(tabletGrpcPort) diff --git a/go/test/endtoend/transaction/twopc/vschema.json b/go/test/endtoend/transaction/twopc/vschema.json index bca58b05c1e..0c22f40d54b 100644 --- a/go/test/endtoend/transaction/twopc/vschema.json +++ b/go/test/endtoend/transaction/twopc/vschema.json @@ -6,6 +6,24 @@ }, "reverse_bits": { "type": "reverse_bits" + }, + "lookup_vdx": { + "type": "lookup", + "params": { + "table": "lookup", + "from": "col,id", + "to": "keyspace_id" + }, + "owner": "twopc_lookup" + }, + "lookup_unique_vdx": { + "type": "lookup_unique", + "params": { + "table": "lookup_unique", + "from": "col_unique", + "to": "keyspace_id" + }, + "owner": "twopc_lookup" } }, "tables": { @@ -32,6 +50,41 @@ "name": "reverse_bits" } ] + }, + "twopc_lookup": { + "column_vindexes": [ + { + "column": "id", + "name": "reverse_bits" + }, + { + "columns": [ + "col", + "id" + ], + "name": "lookup_vdx" + }, + { + "column": "col_unique", + "name": "lookup_unique_vdx" + } + ] + }, + "lookup": { + "column_vindexes": [ + { + "column": "col", + "name": "xxhash" + } + ] + }, + "lookup_unique": { + "column_vindexes": [ + { + "column": "col_unique", + "name": "xxhash" + } + ] } } } \ No newline at end of file diff --git a/go/test/endtoend/transaction/tx_test.go b/go/test/endtoend/transaction/tx_test.go index 753dcfb46bd..89531952b13 100644 --- a/go/test/endtoend/transaction/tx_test.go +++ b/go/test/endtoend/transaction/tx_test.go @@ -46,7 +46,6 @@ var ( ) func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitcode, err := func() (int, error) { @@ -57,7 +56,6 @@ func TestMain(m *testing.M) { clusterInstance.VtgateGrpcPort = clusterInstance.GetAndReservePort() // Set extra tablet args for twopc clusterInstance.VtTabletExtraArgs = []string{ - "--twopc_enable", "--twopc_abandon_age", "3600", } @@ -95,7 +93,6 @@ func TestMain(m *testing.M) { // TestTransactionModes tests transactions using twopc mode func TestTransactionModes(t *testing.T) { - defer cluster.PanicHandler(t) ctx := context.Background() conn, err := mysql.Connect(ctx, &vtParams) @@ -141,7 +138,6 @@ func TestTransactionModes(t *testing.T) { // TestTransactionIsolation tests transaction isolation level. func TestTransactionIsolation(t *testing.T) { - defer cluster.PanicHandler(t) ctx := context.Background() conn, err := mysql.Connect(ctx, &vtParams) @@ -248,6 +244,5 @@ func start(t *testing.T) func() { return func() { deleteAll() - cluster.PanicHandler(t) } } diff --git a/go/test/endtoend/utils/cmp.go b/go/test/endtoend/utils/cmp.go index 3b47e1f68dc..7d94c181abd 100644 --- a/go/test/endtoend/utils/cmp.go +++ b/go/test/endtoend/utils/cmp.go @@ -215,6 +215,18 @@ func (mcmp *MySQLCompare) Exec(query string) *sqltypes.Result { return vtQr } +// ExecAssert is the same as Exec, but it only does assertions, it won't FailNow +func (mcmp *MySQLCompare) ExecAssert(query string) *sqltypes.Result { + mcmp.t.Helper() + vtQr, err := mcmp.VtConn.ExecuteFetch(query, 1000, true) + assert.NoError(mcmp.t, err, "[Vitess Error] for query: "+query) + + mysqlQr, err := mcmp.MySQLConn.ExecuteFetch(query, 1000, true) + assert.NoError(mcmp.t, err, "[MySQL Error] for query: "+query) + compareVitessAndMySQLResults(mcmp.t, query, mcmp.VtConn, vtQr, mysqlQr, CompareOptions{}) + return vtQr +} + // ExecNoCompare executes the query on vitess and mysql but does not compare the result with each other. func (mcmp *MySQLCompare) ExecNoCompare(query string) (*sqltypes.Result, *sqltypes.Result) { mcmp.t.Helper() diff --git a/go/test/endtoend/utils/mysql_test.go b/go/test/endtoend/utils/mysql_test.go index 41b74583f69..4d3d992e879 100644 --- a/go/test/endtoend/utils/mysql_test.go +++ b/go/test/endtoend/utils/mysql_test.go @@ -50,7 +50,6 @@ var ( ) func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) exitCode := func() int { clusterInstance = cluster.NewCluster(cell, "localhost") diff --git a/go/test/endtoend/utils/mysqlvsvitess/main_test.go b/go/test/endtoend/utils/mysqlvsvitess/main_test.go index 8f162fae41d..f064afb895d 100644 --- a/go/test/endtoend/utils/mysqlvsvitess/main_test.go +++ b/go/test/endtoend/utils/mysqlvsvitess/main_test.go @@ -64,7 +64,6 @@ var ( ) func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) exitCode := func() int { clusterInstance = cluster.NewCluster(cell, "localhost") diff --git a/go/test/endtoend/utils/utils.go b/go/test/endtoend/utils/utils.go index 35404981164..baa82821306 100644 --- a/go/test/endtoend/utils/utils.go +++ b/go/test/endtoend/utils/utils.go @@ -32,6 +32,7 @@ import ( "vitess.io/vitess/go/mysql" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/test/endtoend/cluster" + "vitess.io/vitess/go/vt/vtgate/engine" ) // AssertContains ensures the given query result contains the expected results. @@ -160,6 +161,19 @@ func Exec(t testing.TB, conn *mysql.Conn, query string) *sqltypes.Result { return qr } +// ExecTrace executes the given query with trace using the given connection. The trace result is returned. +// The test fails if the query produces an error. +func ExecTrace(t testing.TB, conn *mysql.Conn, query string) engine.PrimitiveDescription { + t.Helper() + qr, err := conn.ExecuteFetch(fmt.Sprintf("vexplain trace %s", query), 10000, false) + require.NoError(t, err, "for query: "+query) + + // Extract the trace result and format it with indentation for pretty printing + pd, err := engine.PrimitiveDescriptionFromString(qr.Rows[0][0].ToString()) + require.NoError(t, err) + return pd +} + // 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 { diff --git a/go/test/endtoend/vault/vault_test.go b/go/test/endtoend/vault/vault_test.go index f8e19c07a0c..aab68159ca3 100644 --- a/go/test/endtoend/vault/vault_test.go +++ b/go/test/endtoend/vault/vault_test.go @@ -99,7 +99,6 @@ var ( ) func TestVaultAuth(t *testing.T) { - defer cluster.PanicHandler(nil) // Instantiate Vitess Cluster objects and start topo initializeClusterEarly(t) diff --git a/go/test/endtoend/versionupgrade/upgrade_test.go b/go/test/endtoend/versionupgrade/upgrade_test.go index 181b5dfc9ad..48e552c3a7c 100644 --- a/go/test/endtoend/versionupgrade/upgrade_test.go +++ b/go/test/endtoend/versionupgrade/upgrade_test.go @@ -72,7 +72,6 @@ var ( // TestMain is the main entry point func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitcode, err := func() (int, error) { @@ -131,12 +130,10 @@ func TestMain(m *testing.M) { } func TestShards(t *testing.T) { - defer cluster.PanicHandler(t) assert.Equal(t, 2, len(clusterInstance.Keyspaces[0].Shards)) } func TestDeploySchema(t *testing.T) { - defer cluster.PanicHandler(t) if clusterInstance.ReusingVTDATAROOT { // we assume data is already deployed @@ -163,7 +160,6 @@ func TestDeploySchema(t *testing.T) { } func TestTablesExist(t *testing.T) { - defer cluster.PanicHandler(t) checkTables(t, "", totalTableCount) } diff --git a/go/test/endtoend/vreplication/cluster_test.go b/go/test/endtoend/vreplication/cluster_test.go index 119843651bc..dc5a72e5e88 100644 --- a/go/test/endtoend/vreplication/cluster_test.go +++ b/go/test/endtoend/vreplication/cluster_test.go @@ -39,7 +39,6 @@ import ( "vitess.io/vitess/go/vt/mysqlctl" "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" - vttablet "vitess.io/vitess/go/vt/vttablet/common" vtctldatapb "vitess.io/vitess/go/vt/proto/vtctldata" ) @@ -101,18 +100,6 @@ func (cc *ClusterConfig) enableGTIDCompression() func() { } } -// 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 diff --git a/go/test/endtoend/vreplication/fk_test.go b/go/test/endtoend/vreplication/fk_test.go index 34881cbcd1a..f977d5a74cd 100644 --- a/go/test/endtoend/vreplication/fk_test.go +++ b/go/test/endtoend/vreplication/fk_test.go @@ -29,7 +29,6 @@ import ( "vitess.io/vitess/go/mysql" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/log" - vttablet "vitess.io/vitess/go/vt/vttablet/common" binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" ) @@ -43,9 +42,6 @@ func TestFKWorkflow(t *testing.T) { 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 }() diff --git a/go/test/endtoend/vreplication/lookupindex_helper_test.go b/go/test/endtoend/vreplication/lookupindex_helper_test.go new file mode 100644 index 00000000000..864a5e0f7fc --- /dev/null +++ b/go/test/endtoend/vreplication/lookupindex_helper_test.go @@ -0,0 +1,113 @@ +/* +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" + "strings" + "testing" + + "github.com/stretchr/testify/require" + "github.com/tidwall/gjson" + + "vitess.io/vitess/go/vt/sqlparser" + + binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" +) + +type lookupIndex struct { + typ string + name string + tableKeyspace string + table string + columns []string + ownerTable string + ownerTableKeyspace string + ignoreNulls bool + + t *testing.T +} + +func (li *lookupIndex) String() string { + return li.typ + " " + li.name + " on " + li.tableKeyspace + "." + li.table + " (" + li.columns[0] + ")" +} + +func (li *lookupIndex) create() { + cols := strings.Join(li.columns, ",") + args := []string{ + "LookupVindex", + "--name", li.name, + "--table-keyspace=" + li.ownerTableKeyspace, + "create", + "--keyspace=" + li.tableKeyspace, + "--type=" + li.typ, + "--table-owner=" + li.ownerTable, + "--table-owner-columns=" + cols, + "--tablet-types=PRIMARY", + } + if li.ignoreNulls { + args = append(args, "--ignore-nulls") + } + + err := vc.VtctldClient.ExecuteCommand(args...) + require.NoError(li.t, err, "error executing LookupVindex create: %v", err) + waitForWorkflowState(li.t, vc, fmt.Sprintf("%s.%s", li.ownerTableKeyspace, li.name), binlogdatapb.VReplicationWorkflowState_Running.String()) + li.expectWriteOnly(true) +} + +func (li *lookupIndex) cancel() { + panic("not implemented") +} + +func (li *lookupIndex) externalize() { + args := []string{ + "LookupVindex", + "--name", li.name, + "--table-keyspace=" + li.ownerTableKeyspace, + "externalize", + "--keyspace=" + li.tableKeyspace, + } + err := vc.VtctldClient.ExecuteCommand(args...) + require.NoError(li.t, err, "error executing LookupVindex externalize: %v", err) + li.expectWriteOnly(false) +} + +func (li *lookupIndex) show() error { + return nil +} + +func (li *lookupIndex) expectWriteOnly(expected bool) { + vschema, err := vc.VtctldClient.ExecuteCommandWithOutput("GetVSchema", li.ownerTableKeyspace) + require.NoError(li.t, err, "error executing GetVSchema: %v", err) + vdx := gjson.Get(vschema, fmt.Sprintf("vindexes.%s", li.name)) + require.NotNil(li.t, vdx, "lookup vindex %s not found", li.name) + want := "" + if expected { + want = "true" + } + require.Equal(li.t, want, vdx.Get("params.write_only").String(), "expected write_only parameter to be %s", want) +} + +func getNumRowsInQuery(t *testing.T, query string) int { + stmt, err := sqlparser.NewTestParser().Parse(query) + require.NoError(t, err) + insertStmt, ok := stmt.(*sqlparser.Insert) + require.True(t, ok) + rows, ok := insertStmt.Rows.(sqlparser.Values) + require.True(t, ok) + return len(rows) +} diff --git a/go/test/endtoend/vreplication/lookupindex_test.go b/go/test/endtoend/vreplication/lookupindex_test.go new file mode 100644 index 00000000000..348a0ee5906 --- /dev/null +++ b/go/test/endtoend/vreplication/lookupindex_test.go @@ -0,0 +1,204 @@ +/* +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 ( + "testing" + + "github.com/stretchr/testify/require" + + "vitess.io/vitess/go/test/endtoend/cluster" + vttablet "vitess.io/vitess/go/vt/vttablet/common" +) + +type TestClusterSpec struct { + keyspaceName string + vschema string + schema string +} + +var lookupClusterSpec = TestClusterSpec{ + keyspaceName: "lookup", + vschema: ` +{ + "sharded": true, + "vindexes": { + "reverse_bits": { + "type": "reverse_bits" + } + }, + "tables": { + "t1": { + "column_vindexes": [ + { + "column": "c1", + "name": "reverse_bits" + } + ] + } + } +} +`, + schema: ` +create table t1( + c1 int, + c2 int, + val varchar(128), + primary key(c1) +); +`, +} + +func setupLookupIndexKeyspace(t *testing.T) map[string]*cluster.VttabletProcess { + tablets := make(map[string]*cluster.VttabletProcess) + if _, err := vc.AddKeyspace(t, []*Cell{vc.Cells["zone1"]}, lookupClusterSpec.keyspaceName, "-80,80-", + lookupClusterSpec.vschema, lookupClusterSpec.schema, defaultReplicas, defaultRdonly, 200, nil); err != nil { + require.NoError(t, err) + } + defaultCell := vc.Cells[vc.CellNames[0]] + ks := vc.Cells[defaultCell.Name].Keyspaces[lookupClusterSpec.keyspaceName] + targetTab1 = ks.Shards["-80"].Tablets["zone1-200"].Vttablet + targetTab2 = ks.Shards["80-"].Tablets["zone1-300"].Vttablet + tablets["-80"] = targetTab1 + tablets["80-"] = targetTab2 + return tablets +} + +type lookupTestCase struct { + name string + li *lookupIndex + initQuery string + runningQuery string + postExternalizeQuery string + cleanupQuery string +} + +func TestLookupIndex(t *testing.T) { + setSidecarDBName("_vt") + origDefaultReplicas := defaultReplicas + origDefaultRdonly := defaultRdonly + defer func() { + defaultReplicas = origDefaultReplicas + defaultRdonly = origDefaultRdonly + }() + defaultReplicas = 1 + defaultRdonly = 0 + vc = setupMinimalCluster(t) + defer vc.TearDown() + vttablet.InitVReplicationConfigDefaults() + + _ = setupLookupIndexKeyspace(t) + + initQuery := "insert into t1 (c1, c2, val) values (1, 1, 'val1'), (2, 2, 'val2'), (3, 3, 'val3')" + runningQuery := "insert into t1 (c1, c2, val) values (4, 4, 'val4'), (5, 5, 'val5'), (6, 6, 'val6')" + postExternalizeQuery := "insert into t1 (c1, c2, val) values (7, 7, 'val7'), (8, 8, 'val8'), (9, 9, 'val9')" + cleanupQuery := "delete from t1" + + testCases := []lookupTestCase{ + { + name: "non-unique lookup index, one column", + li: &lookupIndex{ + typ: "consistent_lookup", + name: "t1_c2_lookup", + tableKeyspace: lookupClusterSpec.keyspaceName, + table: "t1", + columns: []string{"c2"}, + ownerTable: "t1", + ownerTableKeyspace: lookupClusterSpec.keyspaceName, + ignoreNulls: true, + t: t, + }, + }, + { + name: "lookup index, two columns", + li: &lookupIndex{ + typ: "lookup", + name: "t1_c2_val_lookup", + tableKeyspace: lookupClusterSpec.keyspaceName, + table: "t1", + columns: []string{"c2", "val"}, + ownerTable: "t1", + ownerTableKeyspace: lookupClusterSpec.keyspaceName, + ignoreNulls: true, + t: t, + }, + }, + { + name: "unique lookup index, one column", + li: &lookupIndex{ + typ: "lookup_unique", + name: "t1_c2_unique_lookup", + tableKeyspace: lookupClusterSpec.keyspaceName, + table: "t1", + columns: []string{"c2"}, + ownerTable: "t1", + ownerTableKeyspace: lookupClusterSpec.keyspaceName, + ignoreNulls: true, + t: t, + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + tc.initQuery = initQuery + tc.runningQuery = runningQuery + tc.postExternalizeQuery = postExternalizeQuery + tc.cleanupQuery = cleanupQuery + testLookupVindex(t, &tc) + }) + } +} + +func testLookupVindex(t *testing.T, tc *lookupTestCase) { + vtgateConn, cancel := getVTGateConn() + defer cancel() + var totalRows int + li := tc.li + + t.Run("init data", func(t *testing.T) { + totalRows += getNumRowsInQuery(t, tc.initQuery) + _, err := vtgateConn.ExecuteFetch(tc.initQuery, 1000, false) + require.NoError(t, err) + }) + + t.Run("create", func(t *testing.T) { + tc.li.create() + + lks := li.tableKeyspace + vindexName := li.name + waitForRowCount(t, vtgateConn, lks, vindexName, totalRows) + totalRows += getNumRowsInQuery(t, tc.runningQuery) + _, err := vtgateConn.ExecuteFetch(tc.runningQuery, 1000, false) + require.NoError(t, err) + waitForRowCount(t, vtgateConn, tc.li.ownerTableKeyspace, li.name, totalRows) + }) + + t.Run("externalize", func(t *testing.T) { + tc.li.externalize() + totalRows += getNumRowsInQuery(t, tc.postExternalizeQuery) + _, err := vtgateConn.ExecuteFetch(tc.postExternalizeQuery, 1000, false) + require.NoError(t, err) + waitForRowCount(t, vtgateConn, tc.li.ownerTableKeyspace, li.name, totalRows) + }) + + t.Run("cleanup", func(t *testing.T) { + _, err := vtgateConn.ExecuteFetch(tc.cleanupQuery, 1000, false) + require.NoError(t, err) + waitForRowCount(t, vtgateConn, tc.li.ownerTableKeyspace, li.name, 0) + }) +} diff --git a/go/test/endtoend/vreplication/vdiff2_test.go b/go/test/endtoend/vreplication/vdiff2_test.go index aaf4cae5375..612ba00236b 100644 --- a/go/test/endtoend/vreplication/vdiff2_test.go +++ b/go/test/endtoend/vreplication/vdiff2_test.go @@ -36,7 +36,6 @@ import ( "vitess.io/vitess/go/test/endtoend/cluster" "vitess.io/vitess/go/vt/log" "vitess.io/vitess/go/vt/sqlparser" - vttablet "vitess.io/vitess/go/vt/vttablet/common" binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" tabletmanagerdatapb "vitess.io/vitess/go/vt/proto/tabletmanagerdata" @@ -140,9 +139,6 @@ func TestVDiff2(t *testing.T) { 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, ",")}) diff --git a/go/test/endtoend/vreplication/vdiff_helper_test.go b/go/test/endtoend/vreplication/vdiff_helper_test.go index 561edfe8b7e..fd223d78082 100644 --- a/go/test/endtoend/vreplication/vdiff_helper_test.go +++ b/go/test/endtoend/vreplication/vdiff_helper_test.go @@ -35,7 +35,7 @@ import ( ) const ( - vdiffTimeout = 120 * time.Second // We can leverage auto retry on error with this longer-than-usual timeout + vdiffTimeout = 180 * 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 @@ -71,7 +71,8 @@ func doVtctlclientVDiff(t *testing.T, keyspace, workflow, cells string, want *ex ksWorkflow := fmt.Sprintf("%s.%s", keyspace, workflow) t.Run(fmt.Sprintf("vtctlclient vdiff %s", ksWorkflow), func(t *testing.T) { // update-table-stats is needed in order to test progress reports. - uuid, _ := performVDiff2Action(t, true, ksWorkflow, cells, "create", "", false, "--auto-retry", "--update-table-stats") + uuid, _ := performVDiff2Action(t, true, ksWorkflow, cells, "create", "", false, "--auto-retry", + "--update-table-stats", fmt.Sprintf("--filtered_replication_wait_time=%v", vdiffTimeout/2)) info := waitForVDiff2ToComplete(t, true, ksWorkflow, cells, uuid, time.Time{}) require.NotNil(t, info) require.Equal(t, workflow, info.Workflow) @@ -164,7 +165,7 @@ func doVtctldclientVDiff(t *testing.T, keyspace, workflow, cells string, want *e 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. - flags := []string{"--auto-retry", "--update-table-stats"} + flags := []string{"--auto-retry", "--update-table-stats", fmt.Sprintf("--filtered-replication-wait-time=%v", vdiffTimeout/2)} if len(extraFlags) > 0 { flags = append(flags, extraFlags...) } diff --git a/go/test/endtoend/vreplication/vreplication_test.go b/go/test/endtoend/vreplication/vreplication_test.go index 04a5eabc33b..d3193298a0c 100644 --- a/go/test/endtoend/vreplication/vreplication_test.go +++ b/go/test/endtoend/vreplication/vreplication_test.go @@ -293,7 +293,6 @@ 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") @@ -595,8 +594,6 @@ func TestCellAliasVreplicationWorkflow(t *testing.T) { cells := []string{"zone1", "zone2"} resetCompression := mainClusterConfig.enableGTIDCompression() defer resetCompression() - resetExperimentalFlags := setAllVTTabletExperimentalFlags() - defer resetExperimentalFlags() vc = NewVitessCluster(t, &clusterOptions{cells: cells}) defer vc.TearDown() diff --git a/go/test/endtoend/vtadmin/main_test.go b/go/test/endtoend/vtadmin/main_test.go index fd4ecbdb7fe..9233cd5b0aa 100644 --- a/go/test/endtoend/vtadmin/main_test.go +++ b/go/test/endtoend/vtadmin/main_test.go @@ -51,7 +51,6 @@ create table u_b ) func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitCode := func() int { @@ -92,7 +91,6 @@ func TestMain(m *testing.M) { // TestVtadminAPIs tests the vtadmin APIs. func TestVtadminAPIs(t *testing.T) { - defer cluster.PanicHandler(t) // Test the vtadmin APIs t.Run("keyspaces api", func(t *testing.T) { diff --git a/go/test/endtoend/vtgate/concurrentdml/main_test.go b/go/test/endtoend/vtgate/concurrentdml/main_test.go index 6ee5619b742..734962b0d33 100644 --- a/go/test/endtoend/vtgate/concurrentdml/main_test.go +++ b/go/test/endtoend/vtgate/concurrentdml/main_test.go @@ -66,7 +66,6 @@ INSERT INTO t1_seq (id, next_id, cache) values(0, 1, 1000); ) func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitCode := func() int { @@ -108,7 +107,6 @@ func TestMain(m *testing.M) { } func TestInsertIgnoreOnLookupUniqueVindex(t *testing.T) { - defer cluster.PanicHandler(t) ctx := context.Background() vtParams := mysql.ConnParams{ Host: "localhost", @@ -137,7 +135,6 @@ func TestInsertIgnoreOnLookupUniqueVindex(t *testing.T) { func TestOpenTxBlocksInSerial(t *testing.T) { t.Skip("Update and Insert in same transaction does not work with the unique consistent lookup having same value.") - defer cluster.PanicHandler(t) ctx := context.Background() vtParams := mysql.ConnParams{ Host: "localhost", @@ -169,7 +166,6 @@ func TestOpenTxBlocksInSerial(t *testing.T) { func TestOpenTxBlocksInConcurrent(t *testing.T) { t.Skip("Update and Insert in same transaction does not work with the unique consistent lookup having same value.") - defer cluster.PanicHandler(t) ctx := context.Background() vtParams := mysql.ConnParams{ Host: "localhost", @@ -207,7 +203,6 @@ func TestOpenTxBlocksInConcurrent(t *testing.T) { } func TestUpdateLookupUniqueVindex(t *testing.T) { - defer cluster.PanicHandler(t) ctx := context.Background() vtParams := mysql.ConnParams{ Host: "localhost", diff --git a/go/test/endtoend/vtgate/connectiondrain/main_test.go b/go/test/endtoend/vtgate/connectiondrain/main_test.go index 6dae9b72be9..6257baf8e40 100644 --- a/go/test/endtoend/vtgate/connectiondrain/main_test.go +++ b/go/test/endtoend/vtgate/connectiondrain/main_test.go @@ -40,7 +40,6 @@ var ( ) func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() os.Exit(m.Run()) } @@ -87,7 +86,6 @@ func start(t *testing.T, vtParams mysql.ConnParams) (*mysql.Conn, func()) { return vtConn, func() { deleteAll() vtConn.Close() - cluster.PanicHandler(t) } } diff --git a/go/test/endtoend/vtgate/consolidator/main_test.go b/go/test/endtoend/vtgate/consolidator/main_test.go index 021db7e513e..0d5eae3eca9 100644 --- a/go/test/endtoend/vtgate/consolidator/main_test.go +++ b/go/test/endtoend/vtgate/consolidator/main_test.go @@ -65,7 +65,6 @@ var ( ) func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitCode := func() int { diff --git a/go/test/endtoend/vtgate/createdb_plugin/main_test.go b/go/test/endtoend/vtgate/createdb_plugin/main_test.go index 5bfec3890b5..e2925bf928d 100644 --- a/go/test/endtoend/vtgate/createdb_plugin/main_test.go +++ b/go/test/endtoend/vtgate/createdb_plugin/main_test.go @@ -43,7 +43,6 @@ var ( ) func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitCode := func() int { @@ -81,7 +80,6 @@ func TestMain(m *testing.M) { } func TestDBDDLPlugin(t *testing.T) { - defer cluster.PanicHandler(t) ctx := context.Background() vtParams := mysql.ConnParams{ Host: "localhost", 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 71f4a2353f7..374a92f395f 100644 --- a/go/test/endtoend/vtgate/errors_as_warnings/main_test.go +++ b/go/test/endtoend/vtgate/errors_as_warnings/main_test.go @@ -66,7 +66,6 @@ var ( ) func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitCode := func() int { diff --git a/go/test/endtoend/vtgate/foreignkey/main_test.go b/go/test/endtoend/vtgate/foreignkey/main_test.go index b4d610785b5..fe418c1c0ea 100644 --- a/go/test/endtoend/vtgate/foreignkey/main_test.go +++ b/go/test/endtoend/vtgate/foreignkey/main_test.go @@ -98,7 +98,6 @@ type fkReference struct { } func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitCode := func() int { @@ -197,7 +196,6 @@ func start(t *testing.T) (utils.MySQLCompare, func()) { return mcmp, func() { deleteAll() mcmp.Close() - cluster.PanicHandler(t) } } 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 34114152e4e..42498ad80f0 100644 --- a/go/test/endtoend/vtgate/foreignkey/stress/fk_stress_test.go +++ b/go/test/endtoend/vtgate/foreignkey/stress/fk_stress_test.go @@ -335,7 +335,6 @@ var ( ) func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitcode, err := func() (int, error) { @@ -653,7 +652,6 @@ func ExecuteFKTest(t *testing.T, tcase *testCase) { } func TestStressFK(t *testing.T) { - defer cluster.PanicHandler(t) t.Run("validate replication health", func(t *testing.T) { validateReplicationIsHealthy(t, replicaNoFK) diff --git a/go/test/endtoend/vtgate/gen4/column_name_test.go b/go/test/endtoend/vtgate/gen4/column_name_test.go index d23c03c9f6b..0f5a83a5092 100644 --- a/go/test/endtoend/vtgate/gen4/column_name_test.go +++ b/go/test/endtoend/vtgate/gen4/column_name_test.go @@ -26,11 +26,9 @@ import ( "github.com/stretchr/testify/require" "vitess.io/vitess/go/mysql" - "vitess.io/vitess/go/test/endtoend/cluster" ) func TestColumnNames(t *testing.T) { - defer cluster.PanicHandler(t) ctx := context.Background() conn, err := mysql.Connect(ctx, &vtParams) diff --git a/go/test/endtoend/vtgate/gen4/main_test.go b/go/test/endtoend/vtgate/gen4/main_test.go index 4c94e8e2ec8..e8280b3aa06 100644 --- a/go/test/endtoend/vtgate/gen4/main_test.go +++ b/go/test/endtoend/vtgate/gen4/main_test.go @@ -64,7 +64,6 @@ var ( ) func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitCode := func() int { @@ -152,6 +151,5 @@ func start(t *testing.T) (utils.MySQLCompare, func()) { return mcmp, func() { deleteAll() mcmp.Close() - cluster.PanicHandler(t) } } diff --git a/go/test/endtoend/vtgate/gen4/system_schema_test.go b/go/test/endtoend/vtgate/gen4/system_schema_test.go index fc4983935e9..d01d4d972a1 100644 --- a/go/test/endtoend/vtgate/gen4/system_schema_test.go +++ b/go/test/endtoend/vtgate/gen4/system_schema_test.go @@ -28,11 +28,9 @@ import ( "github.com/stretchr/testify/require" "vitess.io/vitess/go/mysql" - "vitess.io/vitess/go/test/endtoend/cluster" ) func TestDbNameOverride(t *testing.T) { - defer cluster.PanicHandler(t) ctx := context.Background() conn, err := mysql.Connect(ctx, &vtParams) require.Nil(t, err) @@ -55,7 +53,6 @@ func TestDbNameOverride(t *testing.T) { } func TestInformationSchemaQuery(t *testing.T) { - defer cluster.PanicHandler(t) ctx := context.Background() conn, err := mysql.Connect(ctx, &vtParams) require.NoError(t, err) @@ -90,7 +87,6 @@ func assertSingleRowIsReturned(t *testing.T, conn *mysql.Conn, predicate string, } func TestInformationSchemaWithSubquery(t *testing.T) { - defer cluster.PanicHandler(t) ctx := context.Background() conn, err := mysql.Connect(ctx, &vtParams) require.NoError(t, err) @@ -101,7 +97,6 @@ func TestInformationSchemaWithSubquery(t *testing.T) { } func TestInformationSchemaQueryGetsRoutedToTheRightTableAndKeyspace(t *testing.T) { - defer cluster.PanicHandler(t) ctx := context.Background() conn, err := mysql.Connect(ctx, &vtParams) require.NoError(t, err) @@ -113,7 +108,6 @@ func TestInformationSchemaQueryGetsRoutedToTheRightTableAndKeyspace(t *testing.T } func TestFKConstraintUsingInformationSchema(t *testing.T) { - defer cluster.PanicHandler(t) ctx := context.Background() conn, err := mysql.Connect(ctx, &vtParams) require.NoError(t, err) @@ -131,7 +125,6 @@ func TestFKConstraintUsingInformationSchema(t *testing.T) { } func TestConnectWithSystemSchema(t *testing.T) { - defer cluster.PanicHandler(t) ctx := context.Background() for _, dbname := range []string{"information_schema", "mysql", "performance_schema", "sys"} { connParams := vtParams @@ -144,7 +137,6 @@ func TestConnectWithSystemSchema(t *testing.T) { } func TestUseSystemSchema(t *testing.T) { - defer cluster.PanicHandler(t) ctx := context.Background() conn, err := mysql.Connect(ctx, &vtParams) require.NoError(t, err) @@ -156,7 +148,6 @@ func TestUseSystemSchema(t *testing.T) { } func TestSystemSchemaQueryWithoutQualifier(t *testing.T) { - defer cluster.PanicHandler(t) ctx := context.Background() conn, err := mysql.Connect(ctx, &vtParams) require.NoError(t, err) @@ -191,7 +182,6 @@ func TestSystemSchemaQueryWithoutQualifier(t *testing.T) { } func TestMultipleSchemaPredicates(t *testing.T) { - defer cluster.PanicHandler(t) ctx := context.Background() conn, err := mysql.Connect(ctx, &vtParams) require.NoError(t, err) @@ -217,7 +207,6 @@ func TestMultipleSchemaPredicates(t *testing.T) { } func TestQuerySystemTables(t *testing.T) { - defer cluster.PanicHandler(t) ctx := context.Background() conn, err := mysql.Connect(ctx, &vtParams) require.NoError(t, err) diff --git a/go/test/endtoend/vtgate/godriver/main_test.go b/go/test/endtoend/vtgate/godriver/main_test.go index 587c189d2ea..91605394cf1 100644 --- a/go/test/endtoend/vtgate/godriver/main_test.go +++ b/go/test/endtoend/vtgate/godriver/main_test.go @@ -86,7 +86,6 @@ create table my_message( ) func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitCode := func() int { @@ -125,7 +124,6 @@ func TestMain(m *testing.M) { } func TestStreamMessaging(t *testing.T) { - defer cluster.PanicHandler(t) cnf := vitessdriver.Configuration{ Protocol: "grpc", diff --git a/go/test/endtoend/vtgate/grpc_api/main_test.go b/go/test/endtoend/vtgate/grpc_api/main_test.go index 3c8605f79a0..87d30f4ce26 100644 --- a/go/test/endtoend/vtgate/grpc_api/main_test.go +++ b/go/test/endtoend/vtgate/grpc_api/main_test.go @@ -75,7 +75,6 @@ var ( ) func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitcode := func() int { diff --git a/go/test/endtoend/vtgate/keyspace_watches/keyspace_watch_test.go b/go/test/endtoend/vtgate/keyspace_watches/keyspace_watch_test.go index 4971d03060b..1eb31663577 100644 --- a/go/test/endtoend/vtgate/keyspace_watches/keyspace_watch_test.go +++ b/go/test/endtoend/vtgate/keyspace_watches/keyspace_watch_test.go @@ -117,7 +117,6 @@ func createCluster(extraVTGateArgs []string) (*cluster.LocalProcessCluster, int) } func TestRoutingWithKeyspacesToWatch(t *testing.T) { - defer cluster.PanicHandler(t) clusterInstance, exitCode := createCluster(nil) defer clusterInstance.Teardown() @@ -141,7 +140,6 @@ func TestRoutingWithKeyspacesToWatch(t *testing.T) { } func TestVSchemaDDLWithKeyspacesToWatch(t *testing.T) { - defer cluster.PanicHandler(t) extraVTGateArgs := []string{ "--vschema_ddl_authorized_users", "%", diff --git a/go/test/endtoend/vtgate/main_test.go b/go/test/endtoend/vtgate/main_test.go index b276508f269..6c43a70632b 100644 --- a/go/test/endtoend/vtgate/main_test.go +++ b/go/test/endtoend/vtgate/main_test.go @@ -54,7 +54,6 @@ var ( ) func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitCode := func() int { clusterInstance = cluster.NewCluster(Cell, "localhost") @@ -122,6 +121,5 @@ func start(t *testing.T) (*mysql.Conn, func()) { return conn, func() { deleteAll() conn.Close() - cluster.PanicHandler(t) } } diff --git a/go/test/endtoend/vtgate/misc_test.go b/go/test/endtoend/vtgate/misc_test.go index f3804a2a45f..bbcb338fa50 100644 --- a/go/test/endtoend/vtgate/misc_test.go +++ b/go/test/endtoend/vtgate/misc_test.go @@ -28,7 +28,6 @@ import ( "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" ) @@ -783,7 +782,6 @@ func TestJoinWithMergedRouteWithPredicate(t *testing.T) { func TestRowCountExceed(t *testing.T) { 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)) diff --git a/go/test/endtoend/vtgate/mysql80/main_test.go b/go/test/endtoend/vtgate/mysql80/main_test.go index 4f5897d1f59..b970fb66b12 100644 --- a/go/test/endtoend/vtgate/mysql80/main_test.go +++ b/go/test/endtoend/vtgate/mysql80/main_test.go @@ -35,7 +35,6 @@ var ( ) func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitCode := func() int { diff --git a/go/test/endtoend/vtgate/mysql80/misc_test.go b/go/test/endtoend/vtgate/mysql80/misc_test.go index b29eb13ecdc..5132bf87aba 100644 --- a/go/test/endtoend/vtgate/mysql80/misc_test.go +++ b/go/test/endtoend/vtgate/mysql80/misc_test.go @@ -23,15 +23,12 @@ import ( "vitess.io/vitess/go/test/endtoend/utils" - "vitess.io/vitess/go/test/endtoend/cluster" - "github.com/stretchr/testify/require" "vitess.io/vitess/go/mysql" ) func TestFunctionInDefault(t *testing.T) { - defer cluster.PanicHandler(t) ctx := context.Background() conn, err := mysql.Connect(ctx, &vtParams) require.NoError(t, err) @@ -223,7 +220,6 @@ func BenchmarkReservedConnWhenSettingSysVar(b *testing.B) { } func TestJsonFunctions(t *testing.T) { - defer cluster.PanicHandler(t) ctx := context.Background() conn, err := mysql.Connect(ctx, &vtParams) require.NoError(t, err) diff --git a/go/test/endtoend/vtgate/partialfailure/main_test.go b/go/test/endtoend/vtgate/partialfailure/main_test.go index 9e39e7b5dd5..d5b6a639c68 100644 --- a/go/test/endtoend/vtgate/partialfailure/main_test.go +++ b/go/test/endtoend/vtgate/partialfailure/main_test.go @@ -45,7 +45,6 @@ var ( ) func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitCode := func() int { diff --git a/go/test/endtoend/vtgate/plan_tests/main_test.go b/go/test/endtoend/vtgate/plan_tests/main_test.go new file mode 100644 index 00000000000..d3915af0c8d --- /dev/null +++ b/go/test/endtoend/vtgate/plan_tests/main_test.go @@ -0,0 +1,230 @@ +/* +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 plan_tests + +import ( + "encoding/json" + "fmt" + "os" + "testing" + + "github.com/stretchr/testify/require" + + "vitess.io/vitess/go/mysql" + "vitess.io/vitess/go/test/endtoend/cluster" + "vitess.io/vitess/go/test/endtoend/utils" + "vitess.io/vitess/go/vt/vtgate/engine" + "vitess.io/vitess/go/vt/vtgate/planbuilder" +) + +var ( + clusterInstance *cluster.LocalProcessCluster + vtParams mysql.ConnParams + mysqlParams mysql.ConnParams + uks = "main" + sks = "user" + cell = "plantests" +) + +func TestMain(m *testing.M) { + vschema := readFile("vschemas/schema.json") + userVs := extractUserKS(vschema) + mainVs := extractMainKS(vschema) + sSQL := readFile("schemas/user.sql") + uSQL := readFile("schemas/main.sql") + + exitCode := func() int { + clusterInstance = cluster.NewCluster(cell, "localhost") + defer clusterInstance.Teardown() + + // Start topo server + err := clusterInstance.StartTopo() + if err != nil { + fmt.Println(err.Error()) + return 1 + } + + // Start unsharded keyspace + uKeyspace := &cluster.Keyspace{ + Name: uks, + SchemaSQL: uSQL, + VSchema: mainVs, + } + err = clusterInstance.StartUnshardedKeyspace(*uKeyspace, 0, false) + if err != nil { + fmt.Println(err.Error()) + return 1 + } + + // Start sharded keyspace + skeyspace := &cluster.Keyspace{ + Name: sks, + SchemaSQL: sSQL, + VSchema: userVs, + } + err = clusterInstance.StartKeyspace(*skeyspace, []string{"-80", "80-"}, 0, false) + if err != nil { + fmt.Println(err.Error()) + return 1 + } + + // TODO: (@GuptaManan100/@systay): Also run the tests with normalizer on. + clusterInstance.VtGateExtraArgs = append(clusterInstance.VtGateExtraArgs, + "--normalize_queries=false", + "--schema_change_signal=false", + ) + + // Start vtgate + err = clusterInstance.StartVtgate() + if err != nil { + fmt.Println(err.Error()) + return 1 + } + + vtParams = clusterInstance.GetVTParams(sks) + + // create mysql instance and connection parameters + conn, closer, err := utils.NewMySQL(clusterInstance, sks, sSQL, uSQL) + if err != nil { + fmt.Println(err.Error()) + return 1 + } + defer closer() + mysqlParams = conn + + return m.Run() + }() + os.Exit(exitCode) +} + +func readFile(filename string) string { + schema, err := os.ReadFile(locateFile(filename)) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + return string(schema) +} + +func start(t *testing.T) (utils.MySQLCompare, func()) { + mcmp, err := utils.NewMySQLCompare(t, vtParams, mysqlParams) + require.NoError(t, err) + return mcmp, func() { + mcmp.Close() + } +} + +func readJSONTests(filename string) []planbuilder.PlanTest { + var output []planbuilder.PlanTest + file, err := os.Open(locateFile(filename)) + if err != nil { + panic(err) + } + defer file.Close() + dec := json.NewDecoder(file) + err = dec.Decode(&output) + if err != nil { + panic(err) + } + return output +} + +func locateFile(name string) string { + return "../../../../vt/vtgate/planbuilder/testdata/" + name +} + +// verifyTestExpectations verifies the expectations of the test. +func verifyTestExpectations(t *testing.T, pd engine.PrimitiveDescription, test planbuilder.PlanTest) { + // 1. Verify that the Join primitive sees atleast 1 row on the left side. + engine.WalkPrimitiveDescription(pd, func(description engine.PrimitiveDescription) { + if description.OperatorType == "Join" { + require.NotZero(t, description.Inputs[0].RowsReceived[0]) + } + }) + + // 2. Verify that the plan description matches the expected plan description. + planBytes, err := test.Plan.MarshalJSON() + require.NoError(t, err) + mp := make(map[string]any) + err = json.Unmarshal(planBytes, &mp) + require.NoError(t, err) + pdExpected, err := engine.PrimitiveDescriptionFromMap(mp["Instructions"].(map[string]any)) + require.NoError(t, err) + require.Empty(t, pdExpected.Equals(pd), "Expected: %v\nGot: %v", string(planBytes), pd) +} + +func extractUserKS(jsonString string) string { + var result map[string]any + if err := json.Unmarshal([]byte(jsonString), &result); err != nil { + panic(err.Error()) + } + + keyspaces, ok := result["keyspaces"].(map[string]any) + if !ok { + panic("Keyspaces not found") + } + + user, ok := keyspaces["user"].(map[string]any) + if !ok { + panic("User keyspace not found") + } + + tables, ok := user["tables"].(map[string]any) + if !ok { + panic("Tables not found") + } + + userTbl, ok := tables["user"].(map[string]any) + if !ok { + panic("User table not found") + } + + delete(userTbl, "auto_increment") // TODO: we should have an unsharded keyspace where this could live + + // Marshal the inner part back to JSON string + userJson, err := json.Marshal(user) + if err != nil { + panic(err.Error()) + } + + return string(userJson) +} + +func extractMainKS(jsonString string) string { + var result map[string]any + if err := json.Unmarshal([]byte(jsonString), &result); err != nil { + panic(err.Error()) + } + + keyspaces, ok := result["keyspaces"].(map[string]any) + if !ok { + panic("Keyspaces not found") + } + + main, ok := keyspaces["main"].(map[string]any) + if !ok { + panic("main keyspace not found") + } + + // Marshal the inner part back to JSON string + mainJson, err := json.Marshal(main) + if err != nil { + panic(err.Error()) + } + + return string(mainJson) +} diff --git a/go/test/endtoend/vtgate/plan_tests/plan_e2e_test.go b/go/test/endtoend/vtgate/plan_tests/plan_e2e_test.go new file mode 100644 index 00000000000..1594e9b392c --- /dev/null +++ b/go/test/endtoend/vtgate/plan_tests/plan_e2e_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 plan_tests + +import ( + "testing" + + "vitess.io/vitess/go/test/endtoend/utils" +) + +func TestSelectCases(t *testing.T) { + mcmp, closer := start(t) + defer closer() + tests := readJSONTests("select_cases.json") + for _, test := range tests { + mcmp.Run(test.Comment, func(mcmp *utils.MySQLCompare) { + if test.SkipE2E { + mcmp.AsT().Skip(test.Query) + } + mcmp.Exec(test.Query) + pd := utils.ExecTrace(mcmp.AsT(), mcmp.VtConn, test.Query) + verifyTestExpectations(mcmp.AsT(), pd, test) + if mcmp.VtConn.IsClosed() { + mcmp.AsT().Fatal("vtgate connection is closed") + } + }) + } +} diff --git a/go/test/endtoend/vtgate/prefixfanout/main_test.go b/go/test/endtoend/vtgate/prefixfanout/main_test.go index 928808fd48e..a96ee4ce7f5 100644 --- a/go/test/endtoend/vtgate/prefixfanout/main_test.go +++ b/go/test/endtoend/vtgate/prefixfanout/main_test.go @@ -109,7 +109,6 @@ PRIMARY KEY (c1) ) func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitCode := func() int { @@ -158,7 +157,6 @@ func TestMain(m *testing.M) { } func TestCFCPrefixQueryNoHash(t *testing.T) { - defer cluster.PanicHandler(t) ctx := context.Background() vtParams := clusterInstance.GetVTParams(sKs) conn, err := mysql.Connect(ctx, &vtParams) @@ -196,7 +194,6 @@ func TestCFCPrefixQueryNoHash(t *testing.T) { } func TestCFCPrefixQueryWithHash(t *testing.T) { - defer cluster.PanicHandler(t) ctx := context.Background() vtParams := clusterInstance.GetVTParams(sKsMD5) @@ -239,7 +236,6 @@ func TestCFCPrefixQueryWithHash(t *testing.T) { } func TestCFCInsert(t *testing.T) { - defer cluster.PanicHandler(t) ctx := context.Background() vtParams := clusterInstance.GetVTParams(sKs) diff --git a/go/test/endtoend/vtgate/queries/aggregation/aggregation_test.go b/go/test/endtoend/vtgate/queries/aggregation/aggregation_test.go index d206f58e17c..62d23749cd7 100644 --- a/go/test/endtoend/vtgate/queries/aggregation/aggregation_test.go +++ b/go/test/endtoend/vtgate/queries/aggregation/aggregation_test.go @@ -27,7 +27,6 @@ import ( "github.com/stretchr/testify/require" "vitess.io/vitess/go/sqltypes" - "vitess.io/vitess/go/test/endtoend/cluster" "vitess.io/vitess/go/test/endtoend/utils" ) @@ -66,7 +65,6 @@ func start(t *testing.T) (utils.MySQLCompare, func()) { return mcmp, func() { deleteAll() mcmp.Close() - cluster.PanicHandler(t) } } diff --git a/go/test/endtoend/vtgate/queries/aggregation/main_test.go b/go/test/endtoend/vtgate/queries/aggregation/main_test.go index 02013a9b0e2..bd1c1aa3b7d 100644 --- a/go/test/endtoend/vtgate/queries/aggregation/main_test.go +++ b/go/test/endtoend/vtgate/queries/aggregation/main_test.go @@ -44,7 +44,6 @@ var ( ) func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitCode := func() int { diff --git a/go/test/endtoend/vtgate/queries/benchmark/main_test.go b/go/test/endtoend/vtgate/queries/benchmark/main_test.go index 40a215c8007..0410f52993a 100644 --- a/go/test/endtoend/vtgate/queries/benchmark/main_test.go +++ b/go/test/endtoend/vtgate/queries/benchmark/main_test.go @@ -65,7 +65,6 @@ var shards4 = []string{ } func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitCode := func() int { @@ -169,6 +168,5 @@ func start(b *testing.B) (*mysql.Conn, func()) { return conn, func() { deleteAll() conn.Close() - cluster.PanicHandler(b) } } diff --git a/go/test/endtoend/vtgate/queries/derived/derived_test.go b/go/test/endtoend/vtgate/queries/derived/derived_test.go index cb106564b2f..fe467f31c20 100644 --- a/go/test/endtoend/vtgate/queries/derived/derived_test.go +++ b/go/test/endtoend/vtgate/queries/derived/derived_test.go @@ -21,7 +21,6 @@ import ( "github.com/stretchr/testify/require" - "vitess.io/vitess/go/test/endtoend/cluster" "vitess.io/vitess/go/test/endtoend/utils" ) @@ -44,7 +43,6 @@ func start(t *testing.T) (utils.MySQLCompare, func()) { return mcmp, func() { deleteAll() mcmp.Close() - cluster.PanicHandler(t) } } diff --git a/go/test/endtoend/vtgate/queries/derived/main_test.go b/go/test/endtoend/vtgate/queries/derived/main_test.go index 3b44811f95c..0bab24a966a 100644 --- a/go/test/endtoend/vtgate/queries/derived/main_test.go +++ b/go/test/endtoend/vtgate/queries/derived/main_test.go @@ -44,7 +44,6 @@ var ( ) func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitCode := func() int { diff --git a/go/test/endtoend/vtgate/queries/dml/main_test.go b/go/test/endtoend/vtgate/queries/dml/main_test.go index 0c4d58aa614..bc72acc1159 100644 --- a/go/test/endtoend/vtgate/queries/dml/main_test.go +++ b/go/test/endtoend/vtgate/queries/dml/main_test.go @@ -66,7 +66,6 @@ var ( ) func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitCode := func() int { @@ -148,6 +147,5 @@ func start(t *testing.T) (utils.MySQLCompare, func()) { return mcmp, func() { deleteAll() mcmp.Close() - cluster.PanicHandler(t) } } diff --git a/go/test/endtoend/vtgate/queries/foundrows/found_rows_test.go b/go/test/endtoend/vtgate/queries/foundrows/found_rows_test.go index f52e2eff532..e50e9210c55 100644 --- a/go/test/endtoend/vtgate/queries/foundrows/found_rows_test.go +++ b/go/test/endtoend/vtgate/queries/foundrows/found_rows_test.go @@ -21,12 +21,10 @@ import ( "github.com/stretchr/testify/require" - "vitess.io/vitess/go/test/endtoend/cluster" "vitess.io/vitess/go/test/endtoend/utils" ) func TestFoundRows(t *testing.T) { - defer cluster.PanicHandler(t) mcmp, err := utils.NewMySQLCompare(t, vtParams, mysqlParams) require.NoError(t, err) defer mcmp.Close() diff --git a/go/test/endtoend/vtgate/queries/foundrows/main_test.go b/go/test/endtoend/vtgate/queries/foundrows/main_test.go index 8f992863008..248b6cd9434 100644 --- a/go/test/endtoend/vtgate/queries/foundrows/main_test.go +++ b/go/test/endtoend/vtgate/queries/foundrows/main_test.go @@ -46,7 +46,6 @@ var ( ) func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitCode := func() int { diff --git a/go/test/endtoend/vtgate/queries/informationschema/informationschema_test.go b/go/test/endtoend/vtgate/queries/informationschema/informationschema_test.go index c5568b2db49..e158bd96e33 100644 --- a/go/test/endtoend/vtgate/queries/informationschema/informationschema_test.go +++ b/go/test/endtoend/vtgate/queries/informationschema/informationschema_test.go @@ -25,7 +25,6 @@ import ( "github.com/stretchr/testify/require" "vitess.io/vitess/go/mysql" - "vitess.io/vitess/go/test/endtoend/cluster" "vitess.io/vitess/go/test/endtoend/utils" ) @@ -47,7 +46,6 @@ func start(t *testing.T) (utils.MySQLCompare, func()) { return mcmp, func() { deleteAll() mcmp.Close() - cluster.PanicHandler(t) } } @@ -114,7 +112,6 @@ func TestFKConstraintUsingInformationSchema(t *testing.T) { } func TestConnectWithSystemSchema(t *testing.T) { - defer cluster.PanicHandler(t) for _, dbname := range []string{"information_schema", "mysql", "performance_schema", "sys"} { vtConnParams := vtParams vtConnParams.DbName = dbname diff --git a/go/test/endtoend/vtgate/queries/informationschema/main_test.go b/go/test/endtoend/vtgate/queries/informationschema/main_test.go index 3696617281e..76d5f44ebae 100644 --- a/go/test/endtoend/vtgate/queries/informationschema/main_test.go +++ b/go/test/endtoend/vtgate/queries/informationschema/main_test.go @@ -52,7 +52,6 @@ var ( ) func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitCode := func() int { diff --git a/go/test/endtoend/vtgate/queries/kill/main_test.go b/go/test/endtoend/vtgate/queries/kill/main_test.go index 99608030246..61ddec43589 100644 --- a/go/test/endtoend/vtgate/queries/kill/main_test.go +++ b/go/test/endtoend/vtgate/queries/kill/main_test.go @@ -50,7 +50,6 @@ var ( ) func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitCode := func() int { diff --git a/go/test/endtoend/vtgate/queries/lookup_queries/main_test.go b/go/test/endtoend/vtgate/queries/lookup_queries/main_test.go index a587f124762..818b834511a 100644 --- a/go/test/endtoend/vtgate/queries/lookup_queries/main_test.go +++ b/go/test/endtoend/vtgate/queries/lookup_queries/main_test.go @@ -49,7 +49,6 @@ var ( ) func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitCode := func() int { @@ -119,7 +118,6 @@ func start(t *testing.T) (utils.MySQLCompare, func()) { return mcmp, func() { deleteAll() mcmp.Close() - cluster.PanicHandler(t) } } diff --git a/go/test/endtoend/vtgate/queries/misc/main_test.go b/go/test/endtoend/vtgate/queries/misc/main_test.go index f20072031a8..ee9be542634 100644 --- a/go/test/endtoend/vtgate/queries/misc/main_test.go +++ b/go/test/endtoend/vtgate/queries/misc/main_test.go @@ -48,7 +48,6 @@ var ( ) func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitCode := func() int { diff --git a/go/test/endtoend/vtgate/queries/misc/misc_test.go b/go/test/endtoend/vtgate/queries/misc/misc_test.go index fd869c4ba5b..8738baf3267 100644 --- a/go/test/endtoend/vtgate/queries/misc/misc_test.go +++ b/go/test/endtoend/vtgate/queries/misc/misc_test.go @@ -30,7 +30,6 @@ 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" ) @@ -50,7 +49,6 @@ func start(t *testing.T) (utils.MySQLCompare, func()) { return mcmp, func() { deleteAll() mcmp.Close() - cluster.PanicHandler(t) } } diff --git a/go/test/endtoend/vtgate/queries/no_scatter/main_test.go b/go/test/endtoend/vtgate/queries/no_scatter/main_test.go index c4b0974c24b..a1478dcd2ac 100644 --- a/go/test/endtoend/vtgate/queries/no_scatter/main_test.go +++ b/go/test/endtoend/vtgate/queries/no_scatter/main_test.go @@ -40,7 +40,6 @@ var ( ) func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitCode := func() int { diff --git a/go/test/endtoend/vtgate/queries/no_scatter/queries_test.go b/go/test/endtoend/vtgate/queries/no_scatter/queries_test.go index 7bf702afc15..b302a0f4dc7 100644 --- a/go/test/endtoend/vtgate/queries/no_scatter/queries_test.go +++ b/go/test/endtoend/vtgate/queries/no_scatter/queries_test.go @@ -23,7 +23,6 @@ import ( "github.com/stretchr/testify/require" "vitess.io/vitess/go/mysql" - "vitess.io/vitess/go/test/endtoend/cluster" "vitess.io/vitess/go/test/endtoend/utils" ) @@ -43,7 +42,6 @@ func start(t *testing.T) (*mysql.Conn, func()) { return vtConn, func() { deleteAll() vtConn.Close() - cluster.PanicHandler(t) } } diff --git a/go/test/endtoend/vtgate/queries/normalize/main_test.go b/go/test/endtoend/vtgate/queries/normalize/main_test.go index 8f4d97209dd..8c75d38284d 100644 --- a/go/test/endtoend/vtgate/queries/normalize/main_test.go +++ b/go/test/endtoend/vtgate/queries/normalize/main_test.go @@ -39,7 +39,6 @@ var ( ) func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitCode := func() int { diff --git a/go/test/endtoend/vtgate/queries/orderby/main_test.go b/go/test/endtoend/vtgate/queries/orderby/main_test.go index 9f18377ee3f..353745722b7 100644 --- a/go/test/endtoend/vtgate/queries/orderby/main_test.go +++ b/go/test/endtoend/vtgate/queries/orderby/main_test.go @@ -44,7 +44,6 @@ var ( ) func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitCode := func() int { diff --git a/go/test/endtoend/vtgate/queries/orderby/orderby_test.go b/go/test/endtoend/vtgate/queries/orderby/orderby_test.go index c36b52a4e6a..716f01fb5c7 100644 --- a/go/test/endtoend/vtgate/queries/orderby/orderby_test.go +++ b/go/test/endtoend/vtgate/queries/orderby/orderby_test.go @@ -22,8 +22,6 @@ import ( "vitess.io/vitess/go/test/endtoend/utils" "github.com/stretchr/testify/require" - - "vitess.io/vitess/go/test/endtoend/cluster" ) func start(t *testing.T) (utils.MySQLCompare, func()) { @@ -44,7 +42,6 @@ func start(t *testing.T) (utils.MySQLCompare, func()) { return mcmp, func() { deleteAll() mcmp.Close() - cluster.PanicHandler(t) } } diff --git a/go/test/endtoend/vtgate/queries/orderby/without_schematracker/main_test.go b/go/test/endtoend/vtgate/queries/orderby/without_schematracker/main_test.go index 00221e9c9f3..373c1327074 100644 --- a/go/test/endtoend/vtgate/queries/orderby/without_schematracker/main_test.go +++ b/go/test/endtoend/vtgate/queries/orderby/without_schematracker/main_test.go @@ -44,7 +44,6 @@ var ( ) func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitCode := func() int { diff --git a/go/test/endtoend/vtgate/queries/orderby/without_schematracker/orderby_test.go b/go/test/endtoend/vtgate/queries/orderby/without_schematracker/orderby_test.go index a20c7ad54c6..956815d2a0d 100644 --- a/go/test/endtoend/vtgate/queries/orderby/without_schematracker/orderby_test.go +++ b/go/test/endtoend/vtgate/queries/orderby/without_schematracker/orderby_test.go @@ -22,8 +22,6 @@ import ( "vitess.io/vitess/go/test/endtoend/utils" "github.com/stretchr/testify/require" - - "vitess.io/vitess/go/test/endtoend/cluster" ) func start(t *testing.T) (utils.MySQLCompare, func()) { @@ -44,7 +42,6 @@ func start(t *testing.T) (utils.MySQLCompare, func()) { return mcmp, func() { deleteAll() mcmp.Close() - cluster.PanicHandler(t) } } diff --git a/go/test/endtoend/vtgate/queries/random/main_test.go b/go/test/endtoend/vtgate/queries/random/main_test.go index e3256f60796..85c8840924d 100644 --- a/go/test/endtoend/vtgate/queries/random/main_test.go +++ b/go/test/endtoend/vtgate/queries/random/main_test.go @@ -44,7 +44,6 @@ var ( ) func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitCode := func() int { diff --git a/go/test/endtoend/vtgate/queries/random/random_test.go b/go/test/endtoend/vtgate/queries/random/random_test.go index 2d210ee7f99..20c7934d91f 100644 --- a/go/test/endtoend/vtgate/queries/random/random_test.go +++ b/go/test/endtoend/vtgate/queries/random/random_test.go @@ -27,7 +27,6 @@ import ( "github.com/stretchr/testify/require" - "vitess.io/vitess/go/test/endtoend/cluster" "vitess.io/vitess/go/test/endtoend/utils" ) @@ -61,7 +60,6 @@ func start(t *testing.T) (utils.MySQLCompare, func()) { return mcmp, func() { deleteAll() mcmp.Close() - cluster.PanicHandler(t) } } diff --git a/go/test/endtoend/vtgate/queries/reference/main_test.go b/go/test/endtoend/vtgate/queries/reference/main_test.go index c350038bf6e..03ee429e4c0 100644 --- a/go/test/endtoend/vtgate/queries/reference/main_test.go +++ b/go/test/endtoend/vtgate/queries/reference/main_test.go @@ -53,7 +53,6 @@ var ( ) func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitCode := func() int { diff --git a/go/test/endtoend/vtgate/queries/reference/reference_test.go b/go/test/endtoend/vtgate/queries/reference/reference_test.go index 08e9cbe13b1..ce942833729 100644 --- a/go/test/endtoend/vtgate/queries/reference/reference_test.go +++ b/go/test/endtoend/vtgate/queries/reference/reference_test.go @@ -24,8 +24,6 @@ import ( "vitess.io/vitess/go/mysql" "vitess.io/vitess/go/test/endtoend/utils" - - "vitess.io/vitess/go/test/endtoend/cluster" ) func start(t *testing.T) (*mysql.Conn, func()) { @@ -35,7 +33,6 @@ func start(t *testing.T) (*mysql.Conn, func()) { return vtConn, func() { vtConn.Close() - cluster.PanicHandler(t) } } @@ -171,3 +168,12 @@ func TestMultiReferenceQuery(t *testing.T) { utils.Exec(t, conn, query) } + +func TestDMLReferenceUsingShardedKS(t *testing.T) { + utils.SkipIfBinaryIsBelowVersion(t, 22, "vtgate") + conn, closer := start(t) + defer closer() + + utils.Exec(t, conn, "use sks") + utils.Exec(t, conn, "update zip_detail set zip_id = 1 where id = 1") +} diff --git a/go/test/endtoend/vtgate/queries/subquery/main_test.go b/go/test/endtoend/vtgate/queries/subquery/main_test.go index 9eaf3b4caa0..bc8580bd38d 100644 --- a/go/test/endtoend/vtgate/queries/subquery/main_test.go +++ b/go/test/endtoend/vtgate/queries/subquery/main_test.go @@ -44,7 +44,6 @@ var ( ) func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitCode := func() int { diff --git a/go/test/endtoend/vtgate/queries/subquery/subquery_test.go b/go/test/endtoend/vtgate/queries/subquery/subquery_test.go index 4298bbe80fc..135b86195a5 100644 --- a/go/test/endtoend/vtgate/queries/subquery/subquery_test.go +++ b/go/test/endtoend/vtgate/queries/subquery/subquery_test.go @@ -25,7 +25,6 @@ import ( "vitess.io/vitess/go/sqltypes" - "vitess.io/vitess/go/test/endtoend/cluster" "vitess.io/vitess/go/test/endtoend/utils" ) @@ -47,7 +46,6 @@ func start(t *testing.T) (utils.MySQLCompare, func()) { return mcmp, func() { deleteAll() mcmp.Close() - cluster.PanicHandler(t) } } diff --git a/go/test/endtoend/vtgate/queries/timeout/main_test.go b/go/test/endtoend/vtgate/queries/timeout/main_test.go index 06e8a786469..81fcfb26095 100644 --- a/go/test/endtoend/vtgate/queries/timeout/main_test.go +++ b/go/test/endtoend/vtgate/queries/timeout/main_test.go @@ -48,7 +48,6 @@ var ( ) func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitCode := func() int { diff --git a/go/test/endtoend/vtgate/queries/timeout/timeout_test.go b/go/test/endtoend/vtgate/queries/timeout/timeout_test.go index 565c3c07a4f..d1d718add25 100644 --- a/go/test/endtoend/vtgate/queries/timeout/timeout_test.go +++ b/go/test/endtoend/vtgate/queries/timeout/timeout_test.go @@ -25,7 +25,6 @@ import ( "github.com/stretchr/testify/require" "vitess.io/vitess/go/mysql" - "vitess.io/vitess/go/test/endtoend/cluster" "vitess.io/vitess/go/test/endtoend/utils" ) @@ -45,7 +44,6 @@ func start(t *testing.T) (utils.MySQLCompare, func()) { return mcmp, func() { deleteAll() mcmp.Close() - cluster.PanicHandler(t) } } diff --git a/go/test/endtoend/vtgate/queries/tpch/main_test.go b/go/test/endtoend/vtgate/queries/tpch/main_test.go index 103adb336ab..403ddd510ce 100644 --- a/go/test/endtoend/vtgate/queries/tpch/main_test.go +++ b/go/test/endtoend/vtgate/queries/tpch/main_test.go @@ -43,7 +43,6 @@ var ( ) func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitCode := func() int { diff --git a/go/test/endtoend/vtgate/queries/tpch/tpch_test.go b/go/test/endtoend/vtgate/queries/tpch/tpch_test.go index c4bf71cafa1..efa322a5e1c 100644 --- a/go/test/endtoend/vtgate/queries/tpch/tpch_test.go +++ b/go/test/endtoend/vtgate/queries/tpch/tpch_test.go @@ -21,7 +21,6 @@ import ( "github.com/stretchr/testify/require" - "vitess.io/vitess/go/test/endtoend/cluster" "vitess.io/vitess/go/test/endtoend/utils" ) @@ -43,7 +42,6 @@ func start(t *testing.T) (utils.MySQLCompare, func()) { return mcmp, func() { deleteAll() mcmp.Close() - cluster.PanicHandler(t) } } diff --git a/go/test/endtoend/vtgate/queries/union/main_test.go b/go/test/endtoend/vtgate/queries/union/main_test.go index 06ec07a6c2f..a5f45f84156 100644 --- a/go/test/endtoend/vtgate/queries/union/main_test.go +++ b/go/test/endtoend/vtgate/queries/union/main_test.go @@ -43,7 +43,6 @@ var ( ) func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitCode := func() int { diff --git a/go/test/endtoend/vtgate/queries/union/union_test.go b/go/test/endtoend/vtgate/queries/union/union_test.go index 03f98950f44..26371af3c87 100644 --- a/go/test/endtoend/vtgate/queries/union/union_test.go +++ b/go/test/endtoend/vtgate/queries/union/union_test.go @@ -19,7 +19,6 @@ package union import ( "testing" - "vitess.io/vitess/go/test/endtoend/cluster" "vitess.io/vitess/go/test/endtoend/utils" "github.com/stretchr/testify/assert" @@ -44,7 +43,6 @@ func start(t *testing.T) (utils.MySQLCompare, func()) { return mcmp, func() { deleteAll() mcmp.Close() - cluster.PanicHandler(t) } } diff --git a/go/test/endtoend/vtgate/queries/vexplain/main_test.go b/go/test/endtoend/vtgate/queries/vexplain/main_test.go index c1c401bc573..96b6a1c41d1 100644 --- a/go/test/endtoend/vtgate/queries/vexplain/main_test.go +++ b/go/test/endtoend/vtgate/queries/vexplain/main_test.go @@ -43,7 +43,6 @@ var ( ) func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitCode := func() int { diff --git a/go/test/endtoend/vtgate/queries/vexplain/vexplain_test.go b/go/test/endtoend/vtgate/queries/vexplain/vexplain_test.go index 45baf7af903..1a8ec2b4c37 100644 --- a/go/test/endtoend/vtgate/queries/vexplain/vexplain_test.go +++ b/go/test/endtoend/vtgate/queries/vexplain/vexplain_test.go @@ -24,7 +24,6 @@ import ( "github.com/stretchr/testify/require" "vitess.io/vitess/go/sqltypes" - "vitess.io/vitess/go/test/endtoend/cluster" "vitess.io/vitess/go/mysql" "vitess.io/vitess/go/test/endtoend/utils" @@ -49,7 +48,6 @@ func start(t *testing.T) (*mysql.Conn, func()) { return vtConn, func() { deleteAll() vtConn.Close() - cluster.PanicHandler(t) } } diff --git a/go/test/endtoend/vtgate/readafterwrite/raw_test.go b/go/test/endtoend/vtgate/readafterwrite/raw_test.go index 0549a9b06b0..ce6db45d24e 100644 --- a/go/test/endtoend/vtgate/readafterwrite/raw_test.go +++ b/go/test/endtoend/vtgate/readafterwrite/raw_test.go @@ -100,7 +100,6 @@ CREATE TABLE test_vdx ( ) func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitCode := func() int { @@ -143,7 +142,6 @@ func TestMain(m *testing.M) { } func TestRAWSettings(t *testing.T) { - defer cluster.PanicHandler(t) conn, err := mysql.Connect(context.Background(), &vtParams) require.NoError(t, err) defer conn.Close() diff --git a/go/test/endtoend/vtgate/reservedconn/main_test.go b/go/test/endtoend/vtgate/reservedconn/main_test.go index 8c0278604f7..00f569d9eb9 100644 --- a/go/test/endtoend/vtgate/reservedconn/main_test.go +++ b/go/test/endtoend/vtgate/reservedconn/main_test.go @@ -101,7 +101,6 @@ CREATE TABLE test_vdx ( ) func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitCode := func() int { diff --git a/go/test/endtoend/vtgate/reservedconn/reconnect1/main_test.go b/go/test/endtoend/vtgate/reservedconn/reconnect1/main_test.go index 9a4d7c50dbd..3280d64d433 100644 --- a/go/test/endtoend/vtgate/reservedconn/reconnect1/main_test.go +++ b/go/test/endtoend/vtgate/reservedconn/reconnect1/main_test.go @@ -63,7 +63,6 @@ var ( ) func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitCode := func() int { diff --git a/go/test/endtoend/vtgate/reservedconn/reconnect2/main_test.go b/go/test/endtoend/vtgate/reservedconn/reconnect2/main_test.go index 915d76051a4..81bb6b90ee5 100644 --- a/go/test/endtoend/vtgate/reservedconn/reconnect2/main_test.go +++ b/go/test/endtoend/vtgate/reservedconn/reconnect2/main_test.go @@ -64,7 +64,6 @@ var ( ) func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitCode := func() int { diff --git a/go/test/endtoend/vtgate/reservedconn/reconnect3/main_test.go b/go/test/endtoend/vtgate/reservedconn/reconnect3/main_test.go index 20d255941db..decf2f0dfdd 100644 --- a/go/test/endtoend/vtgate/reservedconn/reconnect3/main_test.go +++ b/go/test/endtoend/vtgate/reservedconn/reconnect3/main_test.go @@ -40,7 +40,6 @@ var ( ) func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitCode := func() int { diff --git a/go/test/endtoend/vtgate/reservedconn/reconnect4/main_test.go b/go/test/endtoend/vtgate/reservedconn/reconnect4/main_test.go index d4a61665a6d..320cbc87172 100644 --- a/go/test/endtoend/vtgate/reservedconn/reconnect4/main_test.go +++ b/go/test/endtoend/vtgate/reservedconn/reconnect4/main_test.go @@ -40,7 +40,6 @@ var ( ) func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitCode := func() int { diff --git a/go/test/endtoend/vtgate/reservedconn/sysvar_test.go b/go/test/endtoend/vtgate/reservedconn/sysvar_test.go index e7e0cfb0259..8bda7dea121 100644 --- a/go/test/endtoend/vtgate/reservedconn/sysvar_test.go +++ b/go/test/endtoend/vtgate/reservedconn/sysvar_test.go @@ -29,11 +29,9 @@ import ( "github.com/stretchr/testify/require" "vitess.io/vitess/go/mysql" - "vitess.io/vitess/go/test/endtoend/cluster" ) func TestSetSysVarSingle(t *testing.T) { - defer cluster.PanicHandler(t) ctx := context.Background() type queriesWithExpectations struct { name, expr string diff --git a/go/test/endtoend/vtgate/reservedconn/udv_test.go b/go/test/endtoend/vtgate/reservedconn/udv_test.go index 55f4c54c612..14b65dbcd35 100644 --- a/go/test/endtoend/vtgate/reservedconn/udv_test.go +++ b/go/test/endtoend/vtgate/reservedconn/udv_test.go @@ -31,11 +31,9 @@ import ( "github.com/stretchr/testify/require" "vitess.io/vitess/go/mysql" - "vitess.io/vitess/go/test/endtoend/cluster" ) func TestSetUDV(t *testing.T) { - defer cluster.PanicHandler(t) ctx := context.Background() type queriesWithExpectations struct { @@ -123,7 +121,6 @@ func TestSetUDV(t *testing.T) { } func TestMysqlDumpInitialLog(t *testing.T) { - defer cluster.PanicHandler(t) ctx := context.Background() conn, err := mysql.Connect(ctx, &vtParams) diff --git a/go/test/endtoend/vtgate/schema/schema_test.go b/go/test/endtoend/vtgate/schema/schema_test.go index 6b2e8ef7e61..4c28e29ca0d 100644 --- a/go/test/endtoend/vtgate/schema/schema_test.go +++ b/go/test/endtoend/vtgate/schema/schema_test.go @@ -55,7 +55,6 @@ var ( ) func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitcode, err := func() (int, error) { @@ -101,7 +100,6 @@ func TestMain(m *testing.M) { } func TestSchemaChange(t *testing.T) { - defer cluster.PanicHandler(t) testWithInitialSchema(t) testWithAlterSchema(t) testWithAlterDatabase(t) diff --git a/go/test/endtoend/vtgate/schematracker/loadkeyspace/schema_load_keyspace_test.go b/go/test/endtoend/vtgate/schematracker/loadkeyspace/schema_load_keyspace_test.go index 9586206221e..ec201487887 100644 --- a/go/test/endtoend/vtgate/schematracker/loadkeyspace/schema_load_keyspace_test.go +++ b/go/test/endtoend/vtgate/schematracker/loadkeyspace/schema_load_keyspace_test.go @@ -57,7 +57,6 @@ var ( ) func TestLoadKeyspaceWithNoTablet(t *testing.T) { - defer cluster.PanicHandler(t) var err error clusterInstance = cluster.NewCluster(cell, hostname) @@ -100,7 +99,6 @@ func TestLoadKeyspaceWithNoTablet(t *testing.T) { } func TestNoInitialKeyspace(t *testing.T) { - defer cluster.PanicHandler(t) var err error clusterInstance = cluster.NewCluster(cell, hostname) 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 3bb4f6dfd9f..1943fefa9d7 100644 --- a/go/test/endtoend/vtgate/schematracker/restarttablet/schema_restart_test.go +++ b/go/test/endtoend/vtgate/schematracker/restarttablet/schema_restart_test.go @@ -64,7 +64,6 @@ var ( ) func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitcode := func() int { @@ -116,7 +115,6 @@ func TestMain(m *testing.M) { } func TestVSchemaTrackerInit(t *testing.T) { - defer cluster.PanicHandler(t) ctx := context.Background() conn, err := mysql.Connect(ctx, &vtParams) require.NoError(t, err) @@ -137,7 +135,6 @@ func TestVSchemaTrackerInit(t *testing.T) { // properly handles primary tablet restarts -- meaning that we maintain // the exact same vschema state as before the restart. func TestVSchemaTrackerKeyspaceReInit(t *testing.T) { - defer cluster.PanicHandler(t) primaryTablet := clusterInstance.Keyspaces[0].Shards[0].PrimaryTablet() 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 50042f3142a..5f82bd5d71a 100644 --- a/go/test/endtoend/vtgate/schematracker/sharded/st_sharded_test.go +++ b/go/test/endtoend/vtgate/schematracker/sharded/st_sharded_test.go @@ -48,7 +48,6 @@ var ( ) func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitCode := func() int { @@ -161,7 +160,6 @@ func TestNewTable(t *testing.T) { } func TestAmbiguousColumnJoin(t *testing.T) { - defer cluster.PanicHandler(t) ctx := context.Background() conn, err := mysql.Connect(ctx, &vtParams) require.NoError(t, err) 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 09bd97eb9fe..6fbd3f3d33c 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 @@ -122,7 +122,6 @@ create table t8( ) func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitCode := func() int { @@ -208,7 +207,6 @@ func TestMain(m *testing.M) { } func TestAddColumn(t *testing.T) { - defer cluster.PanicHandler(t) 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 5ecf89a5db7..4256727915d 100644 --- a/go/test/endtoend/vtgate/schematracker/unsharded/st_unsharded_test.go +++ b/go/test/endtoend/vtgate/schematracker/unsharded/st_unsharded_test.go @@ -49,7 +49,6 @@ var ( ) func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitCode := func() int { @@ -112,7 +111,6 @@ func TestMain(m *testing.M) { } func TestNewUnshardedTable(t *testing.T) { - defer cluster.PanicHandler(t) // create a sql connection ctx := context.Background() @@ -182,7 +180,6 @@ func TestNewUnshardedTable(t *testing.T) { // 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) { - defer cluster.PanicHandler(t) // create a sql connection ctx := context.Background() diff --git a/go/test/endtoend/vtgate/sec_vind/main_test.go b/go/test/endtoend/vtgate/sec_vind/main_test.go index 7aa5df76a83..7ec0d5c0682 100644 --- a/go/test/endtoend/vtgate/sec_vind/main_test.go +++ b/go/test/endtoend/vtgate/sec_vind/main_test.go @@ -44,7 +44,6 @@ var ( ) func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitCode := func() int { @@ -101,7 +100,6 @@ func start(t *testing.T) (*mysql.Conn, func()) { return conn, func() { deleteAll() conn.Close() - cluster.PanicHandler(t) } } diff --git a/go/test/endtoend/vtgate/sequence/seq_test.go b/go/test/endtoend/vtgate/sequence/seq_test.go index 1bda37094b2..0fc1c810eb3 100644 --- a/go/test/endtoend/vtgate/sequence/seq_test.go +++ b/go/test/endtoend/vtgate/sequence/seq_test.go @@ -174,7 +174,6 @@ CREATE TABLE allDefaults ( ) func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitCode := func() int { @@ -216,7 +215,6 @@ func TestMain(m *testing.M) { } func TestSeq(t *testing.T) { - defer cluster.PanicHandler(t) ctx := context.Background() vtParams := mysql.ConnParams{ Host: "localhost", @@ -274,7 +272,6 @@ func TestSeq(t *testing.T) { } func TestDotTableSeq(t *testing.T) { - defer cluster.PanicHandler(t) ctx := context.Background() vtParams := mysql.ConnParams{ Host: "localhost", @@ -297,7 +294,6 @@ func TestDotTableSeq(t *testing.T) { } func TestInsertAllDefaults(t *testing.T) { - defer cluster.PanicHandler(t) ctx := context.Background() vtParams := mysql.ConnParams{ Host: "localhost", diff --git a/go/test/endtoend/vtgate/tablet_healthcheck/reparent_test.go b/go/test/endtoend/vtgate/tablet_healthcheck/reparent_test.go index d6357ce8f2a..77e9a58cf69 100644 --- a/go/test/endtoend/vtgate/tablet_healthcheck/reparent_test.go +++ b/go/test/endtoend/vtgate/tablet_healthcheck/reparent_test.go @@ -89,7 +89,6 @@ create table corder( // TestMain sets up the vitess cluster for any subsequent tests func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitCode := func() int { diff --git a/go/test/endtoend/vtgate/tablet_healthcheck_cache/correctness_test.go b/go/test/endtoend/vtgate/tablet_healthcheck_cache/correctness_test.go index 50529d9fdf9..3457a2cab3c 100644 --- a/go/test/endtoend/vtgate/tablet_healthcheck_cache/correctness_test.go +++ b/go/test/endtoend/vtgate/tablet_healthcheck_cache/correctness_test.go @@ -86,7 +86,6 @@ create table corder( // TestMain sets up the vitess cluster for any subsequent tests func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitCode := func() int { diff --git a/go/test/endtoend/vtgate/unsharded/main_test.go b/go/test/endtoend/vtgate/unsharded/main_test.go index e1818735ed1..307bb7fcf23 100644 --- a/go/test/endtoend/vtgate/unsharded/main_test.go +++ b/go/test/endtoend/vtgate/unsharded/main_test.go @@ -147,7 +147,6 @@ END; ) func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitCode := func() int { @@ -192,7 +191,6 @@ func TestMain(m *testing.M) { func TestSelectIntoAndLoadFrom(t *testing.T) { // Test is skipped because it requires secure-file-priv variable to be set to not NULL or empty. t.Skip() - defer cluster.PanicHandler(t) ctx := context.Background() vtParams := mysql.ConnParams{ Host: "localhost", @@ -227,7 +225,6 @@ func TestSelectIntoAndLoadFrom(t *testing.T) { } func TestEmptyStatement(t *testing.T) { - defer cluster.PanicHandler(t) ctx := context.Background() vtParams := mysql.ConnParams{ Host: "localhost", @@ -244,7 +241,6 @@ func TestEmptyStatement(t *testing.T) { } func TestTopoDownServingQuery(t *testing.T) { - defer cluster.PanicHandler(t) ctx := context.Background() vtParams := mysql.ConnParams{ Host: "localhost", @@ -264,7 +260,6 @@ func TestTopoDownServingQuery(t *testing.T) { } func TestInsertAllDefaults(t *testing.T) { - defer cluster.PanicHandler(t) ctx := context.Background() vtParams := mysql.ConnParams{ Host: "localhost", @@ -279,7 +274,6 @@ func TestInsertAllDefaults(t *testing.T) { } func TestDDLUnsharded(t *testing.T) { - defer cluster.PanicHandler(t) ctx := context.Background() vtParams := mysql.ConnParams{ Host: "localhost", @@ -300,7 +294,6 @@ func TestDDLUnsharded(t *testing.T) { } func TestCallProcedure(t *testing.T) { - defer cluster.PanicHandler(t) ctx := context.Background() vtParams := mysql.ConnParams{ Host: "localhost", @@ -347,7 +340,6 @@ func TestCallProcedure(t *testing.T) { } func TestTempTable(t *testing.T) { - defer cluster.PanicHandler(t) ctx := context.Background() vtParams := mysql.ConnParams{ Host: "localhost", @@ -372,7 +364,6 @@ func TestTempTable(t *testing.T) { } func TestReservedConnDML(t *testing.T) { - defer cluster.PanicHandler(t) ctx := context.Background() vtParams := mysql.ConnParams{ Host: "localhost", @@ -395,7 +386,6 @@ func TestReservedConnDML(t *testing.T) { } func TestNumericPrecisionScale(t *testing.T) { - defer cluster.PanicHandler(t) ctx := context.Background() vtParams := mysql.ConnParams{ Host: "localhost", diff --git a/go/test/endtoend/vtgate/vindex_bindvars/main_test.go b/go/test/endtoend/vtgate/vindex_bindvars/main_test.go index 3251668e155..84c2c825784 100644 --- a/go/test/endtoend/vtgate/vindex_bindvars/main_test.go +++ b/go/test/endtoend/vtgate/vindex_bindvars/main_test.go @@ -265,7 +265,6 @@ CREATE TABLE thex ( ) func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitCode := func() int { @@ -304,7 +303,6 @@ func TestMain(m *testing.M) { } func TestVindexHexTypes(t *testing.T) { - defer cluster.PanicHandler(t) ctx := context.Background() conn, err := mysql.Connect(ctx, &vtParams) require.Nil(t, err) @@ -326,7 +324,6 @@ func TestVindexHexTypes(t *testing.T) { } func TestVindexBindVarOverlap(t *testing.T) { - defer cluster.PanicHandler(t) ctx := context.Background() conn, err := mysql.Connect(ctx, &vtParams) require.Nil(t, err) diff --git a/go/test/endtoend/vtgate/vschema/vschema_test.go b/go/test/endtoend/vtgate/vschema/vschema_test.go index eec54f8f47f..5cd01449b71 100644 --- a/go/test/endtoend/vtgate/vschema/vschema_test.go +++ b/go/test/endtoend/vtgate/vschema/vschema_test.go @@ -18,21 +18,25 @@ package vschema import ( "context" + "encoding/json" "flag" "fmt" "os" + "path" "testing" + "time" - "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" ) var ( clusterInstance *cluster.LocalProcessCluster + configFile string vtParams mysql.ConnParams hostname = "localhost" keyspaceName = "ks" @@ -53,7 +57,6 @@ var ( ) func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) flag.Parse() exitcode, err := func() (int, error) { @@ -66,7 +69,21 @@ func TestMain(m *testing.M) { } // List of users authorized to execute vschema ddl operations - clusterInstance.VtGateExtraArgs = []string{"--vschema_ddl_authorized_users=%", "--schema_change_signal=false"} + if utils.BinaryIsAtLeastAtVersion(22, "vtgate") { + timeNow := time.Now().Unix() + configFile = path.Join(os.TempDir(), fmt.Sprintf("vtgate-config-%d.json", timeNow)) + err := writeConfig(configFile, map[string]string{ + "vschema_ddl_authorized_users": "%", + }) + if err != nil { + return 1, err + } + defer os.Remove(configFile) + + clusterInstance.VtGateExtraArgs = []string{fmt.Sprintf("--config-file=%s", configFile), "--schema_change_signal=false"} + } else { + clusterInstance.VtGateExtraArgs = []string{"--vschema_ddl_authorized_users=%", "--schema_change_signal=false"} + } // Start keyspace keyspace := &cluster.Keyspace{ @@ -96,8 +113,16 @@ func TestMain(m *testing.M) { } +func writeConfig(path string, cfg map[string]string) error { + file, err := os.Create(path) + if err != nil { + return err + } + defer file.Close() + return json.NewEncoder(file).Encode(cfg) +} + func TestVSchema(t *testing.T) { - defer cluster.PanicHandler(t) ctx := context.Background() conn, err := mysql.Connect(ctx, &vtParams) require.NoError(t, err) @@ -138,4 +163,15 @@ func TestVSchema(t *testing.T) { utils.AssertMatches(t, conn, "delete from vt_user", `[]`) + if utils.BinaryIsAtLeastAtVersion(22, "vtgate") { + writeConfig(configFile, map[string]string{ + "vschema_ddl_authorized_users": "", + }) + + require.EventuallyWithT(t, func(t *assert.CollectT) { + _, err = conn.ExecuteFetch("ALTER VSCHEMA DROP TABLE main", 1000, false) + assert.Error(t, err) + assert.ErrorContains(t, err, "is not authorized to perform vschema operations") + }, 5*time.Second, 100*time.Millisecond) + } } diff --git a/go/test/endtoend/vtorc/api/api_test.go b/go/test/endtoend/vtorc/api/api_test.go index 670e8c803fa..3fe43fa8f8f 100644 --- a/go/test/endtoend/vtorc/api/api_test.go +++ b/go/test/endtoend/vtorc/api/api_test.go @@ -33,10 +33,8 @@ import ( // TestAPIEndpoints tests the various API endpoints that VTOrc offers. func TestAPIEndpoints(t *testing.T) { - defer cluster.PanicHandler(t) utils.SetupVttabletsAndVTOrcs(t, clusterInfo, 2, 1, nil, cluster.VTOrcConfiguration{ - PreventCrossDataCenterPrimaryFailover: true, - RecoveryPeriodBlockSeconds: 5, + PreventCrossCellFailover: true, }, 1, "") keyspace := &clusterInfo.ClusterInstance.Keyspaces[0] shard0 := &keyspace.Shards[0] diff --git a/go/test/endtoend/vtorc/api/config_test.go b/go/test/endtoend/vtorc/api/config_test.go new file mode 100644 index 00000000000..821b0f8071e --- /dev/null +++ b/go/test/endtoend/vtorc/api/config_test.go @@ -0,0 +1,203 @@ +/* +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 api + +import ( + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "vitess.io/vitess/go/test/endtoend/cluster" + "vitess.io/vitess/go/test/endtoend/vtorc/utils" +) + +// TestDynamicConfigs tests the dyanamic configurations that VTOrc offers. +func TestDynamicConfigs(t *testing.T) { + utils.SetupVttabletsAndVTOrcs(t, clusterInfo, 2, 1, nil, cluster.VTOrcConfiguration{}, 1, "") + vtorc := clusterInfo.ClusterInstance.VTOrcProcesses[0] + + // Restart VTOrc without any flag overrides so that all the configurations can be tested. + err := vtorc.TearDown() + require.NoError(t, err) + vtorc.Config = cluster.VTOrcConfiguration{} + vtorc.NoOverride = true + err = vtorc.Setup() + require.NoError(t, err) + + // Call API with retry to ensure VTOrc is up + status, resp := utils.MakeAPICallRetry(t, vtorc, "/debug/health", func(code int, response string) bool { + return code != 200 + }) + // Verify when VTOrc is healthy, it has also run the first discovery. + assert.Equal(t, 200, status) + assert.Contains(t, resp, `"Healthy": true,`) + + t.Run("InstancePollTime", func(t *testing.T) { + // Get configuration and verify the output. + waitForConfig(t, vtorc, `"instance-poll-time": 5000000000`) + // Update configuration and verify the output. + vtorc.Config.InstancePollTime = "10h" + err := vtorc.RewriteConfiguration() + assert.NoError(t, err) + // Wait until the config has been updated and seen. + waitForConfig(t, vtorc, `"instance-poll-time": "10h"`) + }) + + t.Run("PreventCrossCellFailover", func(t *testing.T) { + // Get configuration and verify the output. + waitForConfig(t, vtorc, `"prevent-cross-cell-failover": false`) + // Update configuration and verify the output. + vtorc.Config.PreventCrossCellFailover = true + err := vtorc.RewriteConfiguration() + assert.NoError(t, err) + // Wait until the config has been updated and seen. + waitForConfig(t, vtorc, `"prevent-cross-cell-failover": true`) + }) + + t.Run("SnapshotTopologyInterval", func(t *testing.T) { + // Get configuration and verify the output. + waitForConfig(t, vtorc, `"snapshot-topology-interval": 0`) + // Update configuration and verify the output. + vtorc.Config.SnapshotTopologyInterval = "10h" + err := vtorc.RewriteConfiguration() + assert.NoError(t, err) + // Wait until the config has been updated and seen. + waitForConfig(t, vtorc, `"snapshot-topology-interval": "10h"`) + }) + + t.Run("ReasonableReplicationLag", func(t *testing.T) { + // Get configuration and verify the output. + waitForConfig(t, vtorc, `"reasonable-replication-lag": 10000000000`) + // Update configuration and verify the output. + vtorc.Config.ReasonableReplicationLag = "10h" + err := vtorc.RewriteConfiguration() + assert.NoError(t, err) + // Wait until the config has been updated and seen. + waitForConfig(t, vtorc, `"reasonable-replication-lag": "10h"`) + }) + + t.Run("AuditToBackend", func(t *testing.T) { + // Get configuration and verify the output. + waitForConfig(t, vtorc, `"audit-to-backend": false`) + // Update configuration and verify the output. + vtorc.Config.AuditToBackend = true + err := vtorc.RewriteConfiguration() + assert.NoError(t, err) + // Wait until the config has been updated and seen. + waitForConfig(t, vtorc, `"audit-to-backend": true`) + }) + + t.Run("AuditToSyslog", func(t *testing.T) { + // Get configuration and verify the output. + waitForConfig(t, vtorc, `"audit-to-syslog": false`) + // Update configuration and verify the output. + vtorc.Config.AuditToSyslog = true + err := vtorc.RewriteConfiguration() + assert.NoError(t, err) + // Wait until the config has been updated and seen. + waitForConfig(t, vtorc, `"audit-to-syslog": true`) + }) + + t.Run("AuditPurgeDuration", func(t *testing.T) { + // Get configuration and verify the output. + waitForConfig(t, vtorc, `"audit-purge-duration": 604800000000000`) + // Update configuration and verify the output. + vtorc.Config.AuditPurgeDuration = "10h" + err := vtorc.RewriteConfiguration() + assert.NoError(t, err) + // Wait until the config has been updated and seen. + waitForConfig(t, vtorc, `"audit-purge-duration": "10h"`) + }) + + t.Run("WaitReplicasTimeout", func(t *testing.T) { + // Get configuration and verify the output. + waitForConfig(t, vtorc, `"wait-replicas-timeout": 30000000000`) + // Update configuration and verify the output. + vtorc.Config.WaitReplicasTimeout = "10h" + err := vtorc.RewriteConfiguration() + assert.NoError(t, err) + // Wait until the config has been updated and seen. + waitForConfig(t, vtorc, `"wait-replicas-timeout": "10h"`) + }) + + t.Run("TolerableReplicationLag", func(t *testing.T) { + // Get configuration and verify the output. + waitForConfig(t, vtorc, `"tolerable-replication-lag": 0`) + // Update configuration and verify the output. + vtorc.Config.TolerableReplicationLag = "10h" + err := vtorc.RewriteConfiguration() + assert.NoError(t, err) + // Wait until the config has been updated and seen. + waitForConfig(t, vtorc, `"tolerable-replication-lag": "10h"`) + }) + + t.Run("TopoInformationRefreshDuration", func(t *testing.T) { + // Get configuration and verify the output. + waitForConfig(t, vtorc, `"topo-information-refresh-duration": 15000000000`) + // Update configuration and verify the output. + vtorc.Config.TopoInformationRefreshDuration = "10h" + err := vtorc.RewriteConfiguration() + assert.NoError(t, err) + // Wait until the config has been updated and seen. + waitForConfig(t, vtorc, `"topo-information-refresh-duration": "10h"`) + }) + + t.Run("RecoveryPollDuration", func(t *testing.T) { + // Get configuration and verify the output. + waitForConfig(t, vtorc, `"recovery-poll-duration": 1000000000`) + // Update configuration and verify the output. + vtorc.Config.RecoveryPollDuration = "10h" + err := vtorc.RewriteConfiguration() + assert.NoError(t, err) + // Wait until the config has been updated and seen. + waitForConfig(t, vtorc, `"recovery-poll-duration": "10h"`) + }) + + t.Run("AllowEmergencyReparent", func(t *testing.T) { + // Get configuration and verify the output. + waitForConfig(t, vtorc, `"allow-emergency-reparent": true`) + // Update configuration and verify the output. + vtorc.Config.AllowEmergencyReparent = "false" + err := vtorc.RewriteConfiguration() + assert.NoError(t, err) + // Wait until the config has been updated and seen. + waitForConfig(t, vtorc, `"allow-emergency-reparent": "false"`) + }) + + t.Run("ChangeTabletsWithErrantGtidToDrained", func(t *testing.T) { + // Get configuration and verify the output. + waitForConfig(t, vtorc, `"change-tablets-with-errant-gtid-to-drained": false`) + // Update configuration and verify the output. + vtorc.Config.ChangeTabletsWithErrantGtidToDrained = true + err := vtorc.RewriteConfiguration() + assert.NoError(t, err) + // Wait until the config has been updated and seen. + waitForConfig(t, vtorc, `"change-tablets-with-errant-gtid-to-drained": true`) + }) +} + +// waitForConfig waits for the expectedConfig to be present in the VTOrc configuration. +func waitForConfig(t *testing.T, vtorc *cluster.VTOrcProcess, expectedConfig string) { + t.Helper() + status, _ := utils.MakeAPICallRetry(t, vtorc, "/api/config", func(_ int, response string) bool { + return !strings.Contains(response, expectedConfig) + }) + require.EqualValues(t, 200, status) +} diff --git a/go/test/endtoend/vtorc/api/main_test.go b/go/test/endtoend/vtorc/api/main_test.go index f89326bc856..cc3e796b293 100644 --- a/go/test/endtoend/vtorc/api/main_test.go +++ b/go/test/endtoend/vtorc/api/main_test.go @@ -21,7 +21,6 @@ import ( "os" "testing" - "vitess.io/vitess/go/test/endtoend/cluster" "vitess.io/vitess/go/test/endtoend/vtorc/utils" ) @@ -53,8 +52,6 @@ func TestMain(m *testing.M) { return m.Run(), nil }() - cluster.PanicHandler(nil) - if clusterInfo != nil { // stop vtorc first otherwise its logs get polluted // with instances being unreachable triggering unnecessary operations diff --git a/go/test/endtoend/vtorc/general/main_test.go b/go/test/endtoend/vtorc/general/main_test.go index 6db0792de3a..0cd88cd378c 100644 --- a/go/test/endtoend/vtorc/general/main_test.go +++ b/go/test/endtoend/vtorc/general/main_test.go @@ -21,7 +21,6 @@ import ( "os" "testing" - "vitess.io/vitess/go/test/endtoend/cluster" "vitess.io/vitess/go/test/endtoend/vtorc/utils" ) @@ -47,8 +46,6 @@ func TestMain(m *testing.M) { return m.Run(), nil }() - cluster.PanicHandler(nil) - if clusterInfo != nil { // stop vtorc first otherwise its logs get polluted // with instances being unreachable triggering unnecessary operations diff --git a/go/test/endtoend/vtorc/general/vtorc_test.go b/go/test/endtoend/vtorc/general/vtorc_test.go index 329601deb0c..a4ed71945be 100644 --- a/go/test/endtoend/vtorc/general/vtorc_test.go +++ b/go/test/endtoend/vtorc/general/vtorc_test.go @@ -40,9 +40,8 @@ import ( // verify that with multiple vtorc instances, we still only have 1 PlannedReparentShard call func TestPrimaryElection(t *testing.T) { defer utils.PrintVTOrcLogsOnFailure(t, clusterInfo.ClusterInstance) - defer cluster.PanicHandler(t) utils.SetupVttabletsAndVTOrcs(t, clusterInfo, 2, 1, nil, cluster.VTOrcConfiguration{ - PreventCrossDataCenterPrimaryFailover: true, + PreventCrossCellFailover: true, }, 2, "") keyspace := &clusterInfo.ClusterInstance.Keyspaces[0] shard0 := &keyspace.Shards[0] @@ -62,15 +61,71 @@ func TestPrimaryElection(t *testing.T) { require.Len(t, res.Rows, 1, "There should only be 1 primary tablet which was elected") } +// TestErrantGTIDOnPreviousPrimary tests that VTOrc is able to detect errant GTIDs on a previously demoted primary +// if it has an errant GTID. +func TestErrantGTIDOnPreviousPrimary(t *testing.T) { + defer utils.PrintVTOrcLogsOnFailure(t, clusterInfo.ClusterInstance) + utils.SetupVttabletsAndVTOrcs(t, clusterInfo, 3, 0, []string{"--change-tablets-with-errant-gtid-to-drained"}, cluster.VTOrcConfiguration{}, 1, "") + keyspace := &clusterInfo.ClusterInstance.Keyspaces[0] + shard0 := &keyspace.Shards[0] + + // find primary from topo + curPrimary := utils.ShardPrimaryTablet(t, clusterInfo, keyspace, shard0) + assert.NotNil(t, curPrimary, "should have elected a primary") + vtOrcProcess := clusterInfo.ClusterInstance.VTOrcProcesses[0] + utils.WaitForSuccessfulRecoveryCount(t, vtOrcProcess, logic.ElectNewPrimaryRecoveryName, 1) + utils.WaitForSuccessfulPRSCount(t, vtOrcProcess, keyspace.Name, shard0.Name, 1) + + var replica, otherReplica *cluster.Vttablet + for _, tablet := range shard0.Vttablets { + // we know we have only two tablets, so the "other" one must be the new primary + if tablet.Alias != curPrimary.Alias { + if replica == nil { + replica = tablet + } else { + otherReplica = tablet + } + } + } + require.NotNil(t, replica, "should be able to find a replica") + require.NotNil(t, otherReplica, "should be able to find 2nd replica") + utils.CheckReplication(t, clusterInfo, curPrimary, []*cluster.Vttablet{replica, otherReplica}, 15*time.Second) + + // Disable global recoveries for the cluster. + vtOrcProcess.DisableGlobalRecoveries(t) + + // Run PRS to promote a different replica. + output, err := clusterInfo.ClusterInstance.VtctldClientProcess.ExecuteCommandWithOutput( + "PlannedReparentShard", + fmt.Sprintf("%s/%s", keyspace.Name, shard0.Name), + "--new-primary", replica.Alias) + require.NoError(t, err, "error in PlannedReparentShard output - %s", output) + + // Stop replicatin on the previous primary to simulate it not reparenting properly. + // Also insert an errant GTID on the previous primary. + err = utils.RunSQLs(t, []string{ + "STOP REPLICA", + "RESET REPLICA ALL", + "set global read_only=OFF", + "insert into vt_ks.vt_insert_test(id, msg) values (10173, 'test 178342')", + }, curPrimary, "") + require.NoError(t, err) + + // Wait for VTOrc to detect the errant GTID and change the tablet to a drained type. + vtOrcProcess.EnableGlobalRecoveries(t) + + // Wait for the tablet to be drained. + utils.WaitForTabletType(t, curPrimary, "drained") +} + // Cases to test: // 1. create cluster with 1 replica and 1 rdonly, let orc choose primary // verify rdonly is not elected, only replica // verify replication is setup func TestSingleKeyspace(t *testing.T) { defer utils.PrintVTOrcLogsOnFailure(t, clusterInfo.ClusterInstance) - defer cluster.PanicHandler(t) utils.SetupVttabletsAndVTOrcs(t, clusterInfo, 1, 1, []string{"--clusters_to_watch", "ks"}, cluster.VTOrcConfiguration{ - PreventCrossDataCenterPrimaryFailover: true, + PreventCrossCellFailover: true, }, 1, "") keyspace := &clusterInfo.ClusterInstance.Keyspaces[0] shard0 := &keyspace.Shards[0] @@ -87,9 +142,8 @@ func TestSingleKeyspace(t *testing.T) { // verify replication is setup func TestKeyspaceShard(t *testing.T) { defer utils.PrintVTOrcLogsOnFailure(t, clusterInfo.ClusterInstance) - defer cluster.PanicHandler(t) utils.SetupVttabletsAndVTOrcs(t, clusterInfo, 1, 1, []string{"--clusters_to_watch", "ks/0"}, cluster.VTOrcConfiguration{ - PreventCrossDataCenterPrimaryFailover: true, + PreventCrossCellFailover: true, }, 1, "") keyspace := &clusterInfo.ClusterInstance.Keyspaces[0] shard0 := &keyspace.Shards[0] @@ -109,9 +163,8 @@ func TestKeyspaceShard(t *testing.T) { // 6. disable recoveries and make sure the detected problems are set correctly. func TestVTOrcRepairs(t *testing.T) { defer utils.PrintVTOrcLogsOnFailure(t, clusterInfo.ClusterInstance) - defer cluster.PanicHandler(t) utils.SetupVttabletsAndVTOrcs(t, clusterInfo, 3, 0, []string{"--change-tablets-with-errant-gtid-to-drained"}, cluster.VTOrcConfiguration{ - PreventCrossDataCenterPrimaryFailover: true, + PreventCrossCellFailover: true, }, 1, "") keyspace := &clusterInfo.ClusterInstance.Keyspaces[0] shard0 := &keyspace.Shards[0] @@ -288,9 +341,8 @@ func TestRepairAfterTER(t *testing.T) { // test fails intermittently on CI, skip until it can be fixed. t.SkipNow() defer utils.PrintVTOrcLogsOnFailure(t, clusterInfo.ClusterInstance) - defer cluster.PanicHandler(t) utils.SetupVttabletsAndVTOrcs(t, clusterInfo, 2, 0, nil, cluster.VTOrcConfiguration{ - PreventCrossDataCenterPrimaryFailover: true, + PreventCrossCellFailover: true, }, 1, "") keyspace := &clusterInfo.ClusterInstance.Keyspaces[0] shard0 := &keyspace.Shards[0] @@ -326,7 +378,7 @@ func TestSemiSync(t *testing.T) { newCluster := utils.SetupNewClusterSemiSync(t) defer utils.PrintVTOrcLogsOnFailure(t, newCluster.ClusterInstance) utils.StartVTOrcs(t, newCluster, nil, cluster.VTOrcConfiguration{ - PreventCrossDataCenterPrimaryFailover: true, + PreventCrossCellFailover: true, }, 1) defer func() { utils.StopVTOrcs(t, newCluster) @@ -422,9 +474,8 @@ func TestSemiSync(t *testing.T) { // TestVTOrcWithPrs tests that VTOrc works fine even when PRS is called from vtctld func TestVTOrcWithPrs(t *testing.T) { defer utils.PrintVTOrcLogsOnFailure(t, clusterInfo.ClusterInstance) - defer cluster.PanicHandler(t) utils.SetupVttabletsAndVTOrcs(t, clusterInfo, 4, 0, nil, cluster.VTOrcConfiguration{ - PreventCrossDataCenterPrimaryFailover: true, + PreventCrossCellFailover: true, }, 1, "") keyspace := &clusterInfo.ClusterInstance.Keyspaces[0] shard0 := &keyspace.Shards[0] @@ -456,8 +507,6 @@ func TestVTOrcWithPrs(t *testing.T) { "--new-primary", replica.Alias) require.NoError(t, err, "error in PlannedReparentShard output - %s", output) - time.Sleep(40 * time.Second) - // check that the replica gets promoted utils.CheckPrimaryTablet(t, clusterInfo, replica, true) // Verify that VTOrc didn't run any other recovery @@ -474,7 +523,6 @@ func TestVTOrcWithPrs(t *testing.T) { // TestMultipleDurabilities tests that VTOrc works with 2 keyspaces having 2 different durability policies func TestMultipleDurabilities(t *testing.T) { defer utils.PrintVTOrcLogsOnFailure(t, clusterInfo.ClusterInstance) - defer cluster.PanicHandler(t) // Setup a normal cluster and start vtorc utils.SetupVttabletsAndVTOrcs(t, clusterInfo, 1, 1, nil, cluster.VTOrcConfiguration{}, 1, "") // Setup a semi-sync cluster @@ -495,7 +543,6 @@ func TestMultipleDurabilities(t *testing.T) { // TestDrainedTablet tests that we don't forget drained tablets and they still show up in the vtorc output. func TestDrainedTablet(t *testing.T) { defer utils.PrintVTOrcLogsOnFailure(t, clusterInfo.ClusterInstance) - defer cluster.PanicHandler(t) // Setup a normal cluster and start vtorc utils.SetupVttabletsAndVTOrcs(t, clusterInfo, 2, 0, nil, cluster.VTOrcConfiguration{}, 1, "") @@ -560,7 +607,7 @@ func TestDurabilityPolicySetLater(t *testing.T) { // Now start the vtorc instances utils.StartVTOrcs(t, newCluster, nil, cluster.VTOrcConfiguration{ - PreventCrossDataCenterPrimaryFailover: true, + PreventCrossCellFailover: true, }, 1) defer func() { utils.StopVTOrcs(t, newCluster) @@ -583,11 +630,10 @@ func TestDurabilityPolicySetLater(t *testing.T) { func TestFullStatusConnectionPooling(t *testing.T) { defer utils.PrintVTOrcLogsOnFailure(t, clusterInfo.ClusterInstance) - defer cluster.PanicHandler(t) utils.SetupVttabletsAndVTOrcs(t, clusterInfo, 4, 0, []string{ "--tablet_manager_grpc_concurrency=1", }, cluster.VTOrcConfiguration{ - PreventCrossDataCenterPrimaryFailover: true, + PreventCrossCellFailover: true, }, 1, "") keyspace := &clusterInfo.ClusterInstance.Keyspaces[0] shard0 := &keyspace.Shards[0] diff --git a/go/test/endtoend/vtorc/primaryfailure/main_test.go b/go/test/endtoend/vtorc/primaryfailure/main_test.go index a3e50bd0cc9..cd03df01bd6 100644 --- a/go/test/endtoend/vtorc/primaryfailure/main_test.go +++ b/go/test/endtoend/vtorc/primaryfailure/main_test.go @@ -21,7 +21,6 @@ import ( "os" "testing" - "vitess.io/vitess/go/test/endtoend/cluster" "vitess.io/vitess/go/test/endtoend/vtorc/utils" ) @@ -53,8 +52,6 @@ func TestMain(m *testing.M) { return m.Run(), nil }() - cluster.PanicHandler(nil) - if clusterInfo != nil { // stop vtorc first otherwise its logs get polluted // with instances being unreachable triggering unnecessary operations diff --git a/go/test/endtoend/vtorc/primaryfailure/primary_failure_test.go b/go/test/endtoend/vtorc/primaryfailure/primary_failure_test.go index 886aa3a580a..9017d35a8c5 100644 --- a/go/test/endtoend/vtorc/primaryfailure/primary_failure_test.go +++ b/go/test/endtoend/vtorc/primaryfailure/primary_failure_test.go @@ -39,12 +39,11 @@ import ( // Also tests that VTOrc can handle multiple failures, if the durability policies allow it func TestDownPrimary(t *testing.T) { defer utils.PrintVTOrcLogsOnFailure(t, clusterInfo.ClusterInstance) - defer cluster.PanicHandler(t) // We specify the --wait-replicas-timeout to a small value because we spawn a cross-cell replica later in the test. // If that replica is more advanced than the same-cell-replica, then we try to promote the cross-cell replica as an intermediate source. // If we don't specify a small value of --wait-replicas-timeout, then we would end up waiting for 30 seconds for the dead-primary to respond, failing this test. utils.SetupVttabletsAndVTOrcs(t, clusterInfo, 2, 1, []string{"--remote_operation_timeout=10s", "--wait-replicas-timeout=5s"}, cluster.VTOrcConfiguration{ - PreventCrossDataCenterPrimaryFailover: true, + PreventCrossCellFailover: true, }, 1, "semi_sync") keyspace := &clusterInfo.ClusterInstance.Keyspaces[0] shard0 := &keyspace.Shards[0] @@ -116,7 +115,6 @@ func TestDownPrimary(t *testing.T) { // bring down primary before VTOrc has started, let vtorc repair. func TestDownPrimaryBeforeVTOrc(t *testing.T) { defer utils.PrintVTOrcLogsOnFailure(t, clusterInfo.ClusterInstance) - defer cluster.PanicHandler(t) utils.SetupVttabletsAndVTOrcs(t, clusterInfo, 2, 1, nil, cluster.VTOrcConfiguration{}, 0, "none") keyspace := &clusterInfo.ClusterInstance.Keyspaces[0] shard0 := &keyspace.Shards[0] @@ -150,7 +148,7 @@ func TestDownPrimaryBeforeVTOrc(t *testing.T) { // Start a VTOrc instance utils.StartVTOrcs(t, clusterInfo, []string{"--remote_operation_timeout=10s"}, cluster.VTOrcConfiguration{ - PreventCrossDataCenterPrimaryFailover: true, + PreventCrossCellFailover: true, }, 1) vtOrcProcess := clusterInfo.ClusterInstance.VTOrcProcesses[0] @@ -172,7 +170,6 @@ func TestDownPrimaryBeforeVTOrc(t *testing.T) { // delete the primary record and let vtorc repair. func TestDeletedPrimaryTablet(t *testing.T) { defer utils.PrintVTOrcLogsOnFailure(t, clusterInfo.ClusterInstance) - defer cluster.PanicHandler(t) utils.SetupVttabletsAndVTOrcs(t, clusterInfo, 2, 1, []string{"--remote_operation_timeout=10s"}, cluster.VTOrcConfiguration{}, 1, "none") keyspace := &clusterInfo.ClusterInstance.Keyspaces[0] shard0 := &keyspace.Shards[0] @@ -239,12 +236,11 @@ func TestDeletedPrimaryTablet(t *testing.T) { // that primary is unreachable. This help us save few seconds depending on value of `RemoteOperationTimeout` flag. func TestDeadPrimaryRecoversImmediately(t *testing.T) { defer utils.PrintVTOrcLogsOnFailure(t, clusterInfo.ClusterInstance) - defer cluster.PanicHandler(t) // We specify the --wait-replicas-timeout to a small value because we spawn a cross-cell replica later in the test. // If that replica is more advanced than the same-cell-replica, then we try to promote the cross-cell replica as an intermediate source. // If we don't specify a small value of --wait-replicas-timeout, then we would end up waiting for 30 seconds for the dead-primary to respond, failing this test. utils.SetupVttabletsAndVTOrcs(t, clusterInfo, 2, 1, []string{"--remote_operation_timeout=10s", "--wait-replicas-timeout=5s"}, cluster.VTOrcConfiguration{ - PreventCrossDataCenterPrimaryFailover: true, + PreventCrossCellFailover: true, }, 1, "semi_sync") keyspace := &clusterInfo.ClusterInstance.Keyspaces[0] shard0 := &keyspace.Shards[0] @@ -322,9 +318,8 @@ func TestDeadPrimaryRecoversImmediately(t *testing.T) { // covers part of the test case master-failover-lost-replicas from orchestrator func TestCrossDataCenterFailure(t *testing.T) { defer utils.PrintVTOrcLogsOnFailure(t, clusterInfo.ClusterInstance) - defer cluster.PanicHandler(t) utils.SetupVttabletsAndVTOrcs(t, clusterInfo, 2, 1, nil, cluster.VTOrcConfiguration{ - PreventCrossDataCenterPrimaryFailover: true, + PreventCrossCellFailover: true, }, 1, "") keyspace := &clusterInfo.ClusterInstance.Keyspaces[0] shard0 := &keyspace.Shards[0] @@ -368,9 +363,8 @@ func TestCrossDataCenterFailure(t *testing.T) { // In case of no viable candidates, we should error out func TestCrossDataCenterFailureError(t *testing.T) { defer utils.PrintVTOrcLogsOnFailure(t, clusterInfo.ClusterInstance) - defer cluster.PanicHandler(t) utils.SetupVttabletsAndVTOrcs(t, clusterInfo, 1, 1, nil, cluster.VTOrcConfiguration{ - PreventCrossDataCenterPrimaryFailover: true, + PreventCrossCellFailover: true, }, 1, "") keyspace := &clusterInfo.ClusterInstance.Keyspaces[0] shard0 := &keyspace.Shards[0] @@ -415,9 +409,8 @@ func TestLostRdonlyOnPrimaryFailure(t *testing.T) { // were detected by vtorc and could be configured to have their sources detached t.Skip() defer utils.PrintVTOrcLogsOnFailure(t, clusterInfo.ClusterInstance) - defer cluster.PanicHandler(t) utils.SetupVttabletsAndVTOrcs(t, clusterInfo, 2, 2, nil, cluster.VTOrcConfiguration{ - PreventCrossDataCenterPrimaryFailover: true, + PreventCrossCellFailover: true, }, 1, "") keyspace := &clusterInfo.ClusterInstance.Keyspaces[0] shard0 := &keyspace.Shards[0] @@ -495,7 +488,6 @@ func TestLostRdonlyOnPrimaryFailure(t *testing.T) { // covers the test case master-failover-fail-promotion-lag-minutes-success from orchestrator func TestPromotionLagSuccess(t *testing.T) { defer utils.PrintVTOrcLogsOnFailure(t, clusterInfo.ClusterInstance) - defer cluster.PanicHandler(t) utils.SetupVttabletsAndVTOrcs(t, clusterInfo, 2, 1, nil, cluster.VTOrcConfiguration{ ReplicationLagQuery: "select 59", FailPrimaryPromotionOnLagMinutes: 1, @@ -545,7 +537,6 @@ func TestPromotionLagFailure(t *testing.T) { // was smaller than the configured value, otherwise it would fail the promotion t.Skip() defer utils.PrintVTOrcLogsOnFailure(t, clusterInfo.ClusterInstance) - defer cluster.PanicHandler(t) utils.SetupVttabletsAndVTOrcs(t, clusterInfo, 3, 1, nil, cluster.VTOrcConfiguration{ ReplicationLagQuery: "select 61", FailPrimaryPromotionOnLagMinutes: 1, @@ -598,7 +589,6 @@ func TestPromotionLagFailure(t *testing.T) { // That is the replica which should be promoted in case of primary failure func TestDownPrimaryPromotionRule(t *testing.T) { defer utils.PrintVTOrcLogsOnFailure(t, clusterInfo.ClusterInstance) - defer cluster.PanicHandler(t) utils.SetupVttabletsAndVTOrcs(t, clusterInfo, 2, 1, nil, cluster.VTOrcConfiguration{ LockShardTimeoutSeconds: 5, }, 1, "test") @@ -646,7 +636,6 @@ func TestDownPrimaryPromotionRule(t *testing.T) { // It should also be caught up when it is promoted func TestDownPrimaryPromotionRuleWithLag(t *testing.T) { defer utils.PrintVTOrcLogsOnFailure(t, clusterInfo.ClusterInstance) - defer cluster.PanicHandler(t) utils.SetupVttabletsAndVTOrcs(t, clusterInfo, 2, 1, nil, cluster.VTOrcConfiguration{ LockShardTimeoutSeconds: 5, }, 1, "test") @@ -726,10 +715,9 @@ func TestDownPrimaryPromotionRuleWithLag(t *testing.T) { // It should also be caught up when it is promoted func TestDownPrimaryPromotionRuleWithLagCrossCenter(t *testing.T) { defer utils.PrintVTOrcLogsOnFailure(t, clusterInfo.ClusterInstance) - defer cluster.PanicHandler(t) utils.SetupVttabletsAndVTOrcs(t, clusterInfo, 2, 1, nil, cluster.VTOrcConfiguration{ - LockShardTimeoutSeconds: 5, - PreventCrossDataCenterPrimaryFailover: true, + LockShardTimeoutSeconds: 5, + PreventCrossCellFailover: true, }, 1, "test") keyspace := &clusterInfo.ClusterInstance.Keyspaces[0] shard0 := &keyspace.Shards[0] diff --git a/go/test/endtoend/vtorc/readtopologyinstance/main_test.go b/go/test/endtoend/vtorc/readtopologyinstance/main_test.go index fa8dc116782..823655ed785 100644 --- a/go/test/endtoend/vtorc/readtopologyinstance/main_test.go +++ b/go/test/endtoend/vtorc/readtopologyinstance/main_test.go @@ -55,8 +55,7 @@ func TestReadTopologyInstanceBufferable(t *testing.T) { "--topo_global_root", clusterInfo.ClusterInstance.VtctlProcess.TopoGlobalRoot, } servenv.ParseFlags("vtorc") - config.Config.RecoveryPeriodBlockSeconds = 1 - config.Config.InstancePollSeconds = 1 + config.SetInstancePollTime(1 * time.Second) config.MarkConfigurationLoaded() server.StartVTOrcDiscovery() diff --git a/go/test/endtoend/vtorc/utils/utils.go b/go/test/endtoend/vtorc/utils/utils.go index 680d1bfa39a..456d55518dd 100644 --- a/go/test/endtoend/vtorc/utils/utils.go +++ b/go/test/endtoend/vtorc/utils/utils.go @@ -376,7 +376,6 @@ func CheckPrimaryTablet(t *testing.T, clusterInfo *VTOrcClusterInfo, tablet *clu for { now := time.Now() if now.Sub(start) > time.Second*60 { - //log.Exitf("error") assert.FailNow(t, "failed to elect primary before timeout") } tabletInfo, err := clusterInfo.ClusterInstance.VtctldClientProcess.GetTablet(tablet.Alias) @@ -775,10 +774,10 @@ func MakeAPICallRetry(t *testing.T, vtorc *cluster.VTOrcProcess, url string, ret for { select { case <-timeout: - t.Fatal("timed out waiting for api to work") + t.Fatalf("timed out waiting for api to work. Last response - %s", response) return default: - status, response, _ := MakeAPICall(t, vtorc, url) + status, response, _ = MakeAPICall(t, vtorc, url) if retry(status, response) { time.Sleep(1 * time.Second) break diff --git a/go/test/fuzzing/oss_fuzz_build.sh b/go/test/fuzzing/oss_fuzz_build.sh index 2cb991c4215..cde31291af1 100755 --- a/go/test/fuzzing/oss_fuzz_build.sh +++ b/go/test/fuzzing/oss_fuzz_build.sh @@ -54,10 +54,10 @@ mv api_marshal_fuzzer.go $SRC/vitess/go/test/fuzzing/ compile_go_fuzzer vitess.io/vitess/go/test/fuzzing FuzzAPIMarshal api_marshal_fuzzer # collation fuzzer -mv ./go/mysql/collations/uca_test.go \ - ./go/mysql/collations/uca_test_fuzz.go +mv ./go/mysql/collations/colldata/uca_test.go \ + ./go/mysql/collations/colldata/uca_test_fuzz.go -compile_go_fuzzer vitess.io/vitess/go/mysql/collations FuzzCollations fuzz_collations +compile_go_fuzzer vitess.io/vitess/go/mysql/collations/colldata FuzzCollations fuzz_collations compile_go_fuzzer vitess.io/vitess/go/vt/vtgate/planbuilder FuzzTestBuilder fuzz_test_builder gofuzz diff --git a/go/test/vschemawrapper/vschema_wrapper.go b/go/test/vschemawrapper/vschema_wrapper.go index a1b87f5569c..3f9f072afc6 100644 --- a/go/test/vschemawrapper/vschema_wrapper.go +++ b/go/test/vschemawrapper/vschema_wrapper.go @@ -33,6 +33,7 @@ import ( "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vtgate/engine" + econtext "vitess.io/vitess/go/vt/vtgate/executorcontext" "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" "vitess.io/vitess/go/vt/vtgate/semantics" "vitess.io/vitess/go/vt/vtgate/vindexes" @@ -40,7 +41,10 @@ import ( var _ plancontext.VSchema = (*VSchemaWrapper)(nil) +// VSchemaWrapper is a wrapper around VSchema that implements the ContextVSchema interface. +// It is used in tests to provide a VSchema implementation. type VSchemaWrapper struct { + Vcursor *econtext.VCursorImpl V *vindexes.VSchema Keyspace *vindexes.Keyspace TabletType_ topodatapb.TabletType @@ -53,6 +57,30 @@ type VSchemaWrapper struct { Env *vtenv.Environment } +func NewVschemaWrapper( + env *vtenv.Environment, + vschema *vindexes.VSchema, + builder func(string, plancontext.VSchema, string) (*engine.Plan, error), +) (*VSchemaWrapper, error) { + ss := econtext.NewAutocommitSession(&vtgatepb.Session{}) + vcursor, err := econtext.NewVCursorImpl(ss, sqlparser.MarginComments{}, nil, nil, nil, vschema, nil, nil, nil, econtext.VCursorConfig{ + Collation: env.CollationEnv().DefaultConnectionCharset(), + DefaultTabletType: topodatapb.TabletType_PRIMARY, + SetVarEnabled: true, + }) + if err != nil { + return nil, err + } + return &VSchemaWrapper{ + Env: env, + V: vschema, + Vcursor: vcursor, + TestBuilder: builder, + TabletType_: topodatapb.TabletType_PRIMARY, + SysVarEnabled: true, + }, nil +} + func (vw *VSchemaWrapper) GetPrepareData(stmtName string) *vtgatepb.PrepareData { switch stmtName { case "prep_one_param": @@ -244,34 +272,7 @@ func (vw *VSchemaWrapper) FindView(tab sqlparser.TableName) sqlparser.SelectStat } func (vw *VSchemaWrapper) FindTableOrVindex(tab sqlparser.TableName) (*vindexes.Table, vindexes.Vindex, string, topodatapb.TabletType, key.Destination, error) { - if tab.Qualifier.IsEmpty() && tab.Name.String() == "dual" { - ksName := vw.getActualKeyspace() - var ks *vindexes.Keyspace - if ksName == "" { - ks = vw.getfirstKeyspace() - ksName = ks.Name - } else { - ks = vw.V.Keyspaces[ksName].Keyspace - } - tbl := &vindexes.Table{ - Name: sqlparser.NewIdentifierCS("dual"), - Keyspace: ks, - Type: vindexes.TypeReference, - } - return tbl, nil, ksName, topodatapb.TabletType_PRIMARY, nil, nil - } - destKeyspace, destTabletType, destTarget, err := topoproto.ParseDestination(tab.Qualifier.String(), topodatapb.TabletType_PRIMARY) - if err != nil { - return nil, nil, destKeyspace, destTabletType, destTarget, err - } - if destKeyspace == "" { - destKeyspace = vw.getActualKeyspace() - } - table, vindex, err := vw.V.FindTableOrVindex(destKeyspace, tab.Name.String(), topodatapb.TabletType_PRIMARY) - if err != nil { - return nil, nil, destKeyspace, destTabletType, destTarget, err - } - return table, vindex, destKeyspace, destTabletType, destTarget, nil + return vw.Vcursor.FindTableOrVindex(tab) } func (vw *VSchemaWrapper) getfirstKeyspace() (ks *vindexes.Keyspace) { @@ -299,12 +300,12 @@ func (vw *VSchemaWrapper) getActualKeyspace() string { return ks.Name } -func (vw *VSchemaWrapper) DefaultKeyspace() (*vindexes.Keyspace, error) { +func (vw *VSchemaWrapper) SelectedKeyspace() (*vindexes.Keyspace, error) { return vw.V.Keyspaces["main"].Keyspace, nil } func (vw *VSchemaWrapper) AnyKeyspace() (*vindexes.Keyspace, error) { - return vw.DefaultKeyspace() + return vw.SelectedKeyspace() } func (vw *VSchemaWrapper) FirstSortedKeyspace() (*vindexes.Keyspace, error) { diff --git a/go/textutil/strings.go b/go/textutil/strings.go index 2a923cd3259..a17daf8d382 100644 --- a/go/textutil/strings.go +++ b/go/textutil/strings.go @@ -90,6 +90,19 @@ func SingleWordCamel(w string) string { return strings.ToUpper(w[0:1]) + strings.ToLower(w[1:]) } +var multiWordSplitterRegexp = regexp.MustCompile(`[-_.\s]+`) + +// PascalCase turns a string into PascalCase by splitting it into words and +// capitalizing the first letter of each word. +func PascalCase(w string) string { + var b strings.Builder + words := multiWordSplitterRegexp.Split(w, -1) + for _, word := range words { + b.WriteString(SingleWordCamel(word)) + } + return b.String() +} + // ValueIsSimulatedNull returns true if the slice value represents // a NULL or unknown/unspecified value. This is used to distinguish // between a zero value empty slice and a user provided value of an diff --git a/go/textutil/strings_test.go b/go/textutil/strings_test.go index d65c187c4cb..828f39847cf 100644 --- a/go/textutil/strings_test.go +++ b/go/textutil/strings_test.go @@ -122,6 +122,64 @@ func TestSingleWordCamel(t *testing.T) { } } +func TestPascalCase(t *testing.T) { + tt := []struct { + word string + expect string + }{ + { + word: "", + expect: "", + }, + { + word: "_", + expect: "", + }, + { + word: "_a", + expect: "A", + }, + { + word: "A", + expect: "A", + }, + { + word: "mysql", + expect: "Mysql", + }, + { + word: "mySQL", + expect: "Mysql", + }, + { + word: "foo-bar", + expect: "FooBar", + }, + { + word: "mysql-server", + expect: "MysqlServer", + }, + { + word: "io_util", + expect: "IoUtil", + }, + { + word: "there and back again", + expect: "ThereAndBackAgain", + }, + { + word: "combine_all_OF the\tabove", + expect: "CombineAllOfTheAbove", + }, + } + for _, tc := range tt { + t.Run(tc.word, func(t *testing.T) { + pascal := PascalCase(tc.word) + assert.Equal(t, tc.expect, pascal) + }) + } +} + func TestValueIsSimulatedNull(t *testing.T) { tt := []struct { name string diff --git a/go/viperutil/debug/debug.go b/go/viperutil/debug/debug.go index 66cbc7f2962..662634a5675 100644 --- a/go/viperutil/debug/debug.go +++ b/go/viperutil/debug/debug.go @@ -25,3 +25,13 @@ import ( func Debug() { registry.Combined().Debug() } + +// WriteConfigAs writes the config into the given filename. +func WriteConfigAs(filename string) error { + return registry.Combined().WriteConfigAs(filename) +} + +// AllSettings gets all the settings in the configuration. +func AllSettings() map[string]any { + return registry.Combined().AllSettings() +} diff --git a/go/viperutil/internal/sync/sync.go b/go/viperutil/internal/sync/sync.go index 6bee1a14e72..f69829f734d 100644 --- a/go/viperutil/internal/sync/sync.go +++ b/go/viperutil/internal/sync/sync.go @@ -86,7 +86,7 @@ func (v *Viper) Set(key string, value any) { v.m.Lock() defer v.m.Unlock() - // We must not update v.disk here; explicit calls to Set will supercede all + // We must not update v.disk here; explicit calls to Set will supersede all // future config reloads. v.live.Set(key, value) diff --git a/go/vt/binlog/binlogplayer/dbclient.go b/go/vt/binlog/binlogplayer/dbclient.go index c3463b4cc2c..4cbfd962528 100644 --- a/go/vt/binlog/binlogplayer/dbclient.go +++ b/go/vt/binlog/binlogplayer/dbclient.go @@ -40,6 +40,7 @@ type DBClient interface { Commit() error Rollback() error Close() + IsClosed() bool ExecuteFetch(query string, maxrows int) (qr *sqltypes.Result, err error) ExecuteFetchMulti(query string, maxrows int) (qrs []*sqltypes.Result, err error) SupportsCapability(capability capabilities.FlavorCapability) (bool, error) @@ -125,6 +126,10 @@ func (dc *dbClientImpl) Close() { dc.dbConn.Close() } +func (dc *dbClientImpl) IsClosed() bool { + return dc.dbConn.IsClosed() +} + func (dc *dbClientImpl) SupportsCapability(capability capabilities.FlavorCapability) (bool, error) { return dc.dbConn.SupportsCapability(capability) } diff --git a/go/vt/binlog/binlogplayer/fake_dbclient.go b/go/vt/binlog/binlogplayer/fake_dbclient.go index 234dfd528e0..69bbd06f7c6 100644 --- a/go/vt/binlog/binlogplayer/fake_dbclient.go +++ b/go/vt/binlog/binlogplayer/fake_dbclient.go @@ -56,6 +56,10 @@ func (dc *fakeDBClient) Rollback() error { func (dc *fakeDBClient) Close() { } +func (dc *fakeDBClient) IsClosed() bool { + return false +} + func (dc *fakeDBClient) ExecuteFetch(query string, maxrows int) (qr *sqltypes.Result, err error) { query = strings.ToLower(query) switch { diff --git a/go/vt/binlog/binlogplayer/mock_dbclient.go b/go/vt/binlog/binlogplayer/mock_dbclient.go index f0a811a30cf..12005a16c2e 100644 --- a/go/vt/binlog/binlogplayer/mock_dbclient.go +++ b/go/vt/binlog/binlogplayer/mock_dbclient.go @@ -181,6 +181,10 @@ func (dc *MockDBClient) Rollback() error { func (dc *MockDBClient) Close() { } +func (dc *MockDBClient) IsClosed() bool { + return false +} + // ExecuteFetch is part of the DBClient interface func (dc *MockDBClient) ExecuteFetch(query string, maxrows int) (qr *sqltypes.Result, err error) { // Serialize ExecuteFetch to enforce a strict order on shared dbClients. diff --git a/go/vt/logutil/logger.go b/go/vt/logutil/logger.go index 47c3f124238..46d7c0052da 100644 --- a/go/vt/logutil/logger.go +++ b/go/vt/logutil/logger.go @@ -20,6 +20,7 @@ import ( "fmt" "io" "runtime" + "slices" "strings" "sync" "time" @@ -246,6 +247,12 @@ func (ml *MemoryLogger) Clear() { ml.mu.Unlock() } +func (ml *MemoryLogger) LogEvents() []*logutilpb.Event { + ml.mu.Lock() + defer ml.mu.Unlock() + return slices.Clone(ml.Events) +} + // LoggerWriter is an adapter that implements the io.Writer interface. type LoggerWriter struct { logger Logger diff --git a/go/vt/mysqlctl/azblobbackupstorage/azblob.go b/go/vt/mysqlctl/azblobbackupstorage/azblob.go index 3ba6b187a2f..dbd146495e8 100644 --- a/go/vt/mysqlctl/azblobbackupstorage/azblob.go +++ b/go/vt/mysqlctl/azblobbackupstorage/azblob.go @@ -32,8 +32,9 @@ import ( "github.com/Azure/azure-storage-blob-go/azblob" "github.com/spf13/pflag" + "vitess.io/vitess/go/vt/mysqlctl/errors" + "vitess.io/vitess/go/viperutil" - "vitess.io/vitess/go/vt/concurrency" "vitess.io/vitess/go/vt/log" "vitess.io/vitess/go/vt/mysqlctl/backupstorage" "vitess.io/vitess/go/vt/servenv" @@ -203,9 +204,9 @@ type AZBlobBackupHandle struct { name string readOnly bool waitGroup sync.WaitGroup - errors concurrency.AllErrorRecorder ctx context.Context cancel context.CancelFunc + errors.PerFileErrorRecorder } // Directory implements BackupHandle. @@ -218,21 +219,6 @@ func (bh *AZBlobBackupHandle) Name() string { return bh.name } -// RecordError is part of the concurrency.ErrorRecorder interface. -func (bh *AZBlobBackupHandle) RecordError(err error) { - bh.errors.RecordError(err) -} - -// HasErrors is part of the concurrency.ErrorRecorder interface. -func (bh *AZBlobBackupHandle) HasErrors() bool { - return bh.errors.HasErrors() -} - -// Error is part of the concurrency.ErrorRecorder interface. -func (bh *AZBlobBackupHandle) Error() error { - return bh.errors.Error() -} - // AddFile implements BackupHandle. func (bh *AZBlobBackupHandle) AddFile(ctx context.Context, filename string, filesize int64) (io.WriteCloser, error) { if bh.readOnly { @@ -263,7 +249,7 @@ func (bh *AZBlobBackupHandle) AddFile(ctx context.Context, filename string, file }) if err != nil { reader.CloseWithError(err) - bh.RecordError(err) + bh.RecordError(filename, err) } }() diff --git a/go/vt/mysqlctl/backupengine.go b/go/vt/mysqlctl/backupengine.go index fb3d0e2d125..eeb14039d01 100644 --- a/go/vt/mysqlctl/backupengine.go +++ b/go/vt/mysqlctl/backupengine.go @@ -272,7 +272,7 @@ func getBackupManifestInto(ctx context.Context, backup backupstorage.BackupHandl if err := json.NewDecoder(file).Decode(outManifest); err != nil { return vterrors.Wrap(err, "can't decode MANIFEST") } - return nil + return backup.Error() } // IncrementalBackupDetails lists some incremental backup specific information diff --git a/go/vt/mysqlctl/backupstorage/interface.go b/go/vt/mysqlctl/backupstorage/interface.go index 92bc71d63aa..4fd37b3163a 100644 --- a/go/vt/mysqlctl/backupstorage/interface.go +++ b/go/vt/mysqlctl/backupstorage/interface.go @@ -25,7 +25,8 @@ import ( "github.com/spf13/pflag" - "vitess.io/vitess/go/vt/concurrency" + "vitess.io/vitess/go/vt/mysqlctl/errors" + "vitess.io/vitess/go/vt/servenv" ) @@ -89,9 +90,9 @@ type BackupHandle interface { // ReadCloser is closed. ReadFile(ctx context.Context, filename string) (io.ReadCloser, error) - // concurrency.ErrorRecorder is embedded here to coordinate reporting and - // handling of errors among all the components involved in taking a backup. - concurrency.ErrorRecorder + // BackupErrorRecorder is embedded here to coordinate reporting and + // handling of errors among all the components involved in taking/restoring a backup. + errors.BackupErrorRecorder } // BackupStorage is the interface to the storage system diff --git a/go/vt/mysqlctl/backup_blackbox_race_test.go b/go/vt/mysqlctl/blackbox/backup_race_test.go similarity index 97% rename from go/vt/mysqlctl/backup_blackbox_race_test.go rename to go/vt/mysqlctl/blackbox/backup_race_test.go index 5414ebc5fa6..fd39dfe4b06 100644 --- a/go/vt/mysqlctl/backup_blackbox_race_test.go +++ b/go/vt/mysqlctl/blackbox/backup_race_test.go @@ -16,8 +16,8 @@ See the License for the specific language governing permissions and limitations under the License. */ -// Package mysqlctl_test is the blackbox tests for package mysqlctl. -package mysqlctl_test +// Package blackbox is the blackbox tests for package mysqlctl. +package blackbox import ( "fmt" @@ -75,7 +75,7 @@ func TestExecuteBackupWithFailureOnLastFile(t *testing.T) { require.NoError(t, createBackupFiles(path.Join(dataDir, "test2"), 2, "ibd")) defer os.RemoveAll(backupRoot) - needIt, err := needInnoDBRedoLogSubdir() + needIt, err := NeedInnoDBRedoLogSubdir() require.NoError(t, err) if needIt { fpath := path.Join("log", mysql.DynamicRedoLogSubdir) @@ -144,7 +144,7 @@ func TestExecuteBackupWithFailureOnLastFile(t *testing.T) { TopoServer: ts, Keyspace: keyspace, Shard: shard, - MysqlShutdownTimeout: mysqlShutdownTimeout, + MysqlShutdownTimeout: MysqlShutdownTimeout, }, bh) require.ErrorContains(t, err, "cannot add file: 3") diff --git a/go/vt/mysqlctl/backup_blackbox_test.go b/go/vt/mysqlctl/blackbox/backup_test.go similarity index 57% rename from go/vt/mysqlctl/backup_blackbox_test.go rename to go/vt/mysqlctl/blackbox/backup_test.go index 15244fb8782..b7e35304904 100644 --- a/go/vt/mysqlctl/backup_blackbox_test.go +++ b/go/vt/mysqlctl/blackbox/backup_test.go @@ -14,12 +14,15 @@ See the License for the specific language governing permissions and limitations under the License. */ -// Package mysqlctl_test is the blackbox tests for package mysqlctl. -package mysqlctl_test +// Package blackbox is the blackbox tests for package mysqlctl. +package blackbox import ( + "bytes" "context" + "errors" "fmt" + "io" "os" "path" "strings" @@ -31,7 +34,6 @@ import ( "vitess.io/vitess/go/test/utils" - "vitess.io/vitess/go/mysql/capabilities" "vitess.io/vitess/go/mysql/replication" "vitess.io/vitess/go/sqltypes" @@ -48,40 +50,6 @@ 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 - - return old -} - -func createBackupDir(root string, dirs ...string) error { - for _, dir := range dirs { - if err := os.MkdirAll(path.Join(root, dir), 0755); err != nil { - return err - } - } - - return nil -} - -func createBackupFiles(root string, fileCount int, ext string) error { - for i := 0; i < fileCount; i++ { - f, err := os.Create(path.Join(root, fmt.Sprintf("%d.%s", i, ext))) - if err != nil { - return err - } - if _, err := f.Write([]byte("hello, world!")); err != nil { - return err - } - defer f.Close() - } - - return nil -} - func TestExecuteBackup(t *testing.T) { ctx := utils.LeakCheckContext(t) @@ -97,7 +65,7 @@ func TestExecuteBackup(t *testing.T) { require.NoError(t, createBackupFiles(path.Join(dataDir, "test2"), 2, "ibd")) defer os.RemoveAll(backupRoot) - needIt, err := needInnoDBRedoLogSubdir() + needIt, err := NeedInnoDBRedoLogSubdir() require.NoError(t, err) if needIt { fpath := path.Join("log", mysql.DynamicRedoLogSubdir) @@ -133,8 +101,8 @@ func TestExecuteBackup(t *testing.T) { be := &mysqlctl.BuiltinBackupEngine{} // Configure a tight deadline to force a timeout - oldDeadline := setBuiltinBackupMysqldDeadline(time.Second) - defer setBuiltinBackupMysqldDeadline(oldDeadline) + oldDeadline := SetBuiltinBackupMysqldDeadline(time.Second) + defer SetBuiltinBackupMysqldDeadline(oldDeadline) bh := filebackupstorage.NewBackupHandle(nil, "", "", false) @@ -163,7 +131,7 @@ func TestExecuteBackup(t *testing.T) { Keyspace: keyspace, Shard: shard, Stats: fakeStats, - MysqlShutdownTimeout: mysqlShutdownTimeout, + MysqlShutdownTimeout: MysqlShutdownTimeout, }, bh) require.NoError(t, err) @@ -221,7 +189,7 @@ func TestExecuteBackup(t *testing.T) { TopoServer: ts, Keyspace: keyspace, Shard: shard, - MysqlShutdownTimeout: mysqlShutdownTimeout, + MysqlShutdownTimeout: MysqlShutdownTimeout, }, bh) assert.Error(t, err) @@ -243,7 +211,7 @@ func TestExecuteBackupWithSafeUpgrade(t *testing.T) { require.NoError(t, createBackupFiles(path.Join(dataDir, "test2"), 2, "ibd")) defer os.RemoveAll(backupRoot) - needIt, err := needInnoDBRedoLogSubdir() + needIt, err := NeedInnoDBRedoLogSubdir() require.NoError(t, err) if needIt { fpath := path.Join("log", mysql.DynamicRedoLogSubdir) @@ -279,8 +247,8 @@ func TestExecuteBackupWithSafeUpgrade(t *testing.T) { be := &mysqlctl.BuiltinBackupEngine{} // Configure a tight deadline to force a timeout - oldDeadline := setBuiltinBackupMysqldDeadline(time.Second) - defer setBuiltinBackupMysqldDeadline(oldDeadline) + oldDeadline := SetBuiltinBackupMysqldDeadline(time.Second) + defer SetBuiltinBackupMysqldDeadline(oldDeadline) bh := filebackupstorage.NewBackupHandle(nil, "", "", false) @@ -310,7 +278,7 @@ func TestExecuteBackupWithSafeUpgrade(t *testing.T) { Shard: shard, Stats: backupstats.NewFakeStats(), UpgradeSafe: true, - MysqlShutdownTimeout: mysqlShutdownTimeout, + MysqlShutdownTimeout: MysqlShutdownTimeout, }, bh) require.NoError(t, err) @@ -336,7 +304,7 @@ func TestExecuteBackupWithCanceledContext(t *testing.T) { require.NoError(t, createBackupFiles(path.Join(dataDir, "test2"), 2, "ibd")) defer os.RemoveAll(backupRoot) - needIt, err := needInnoDBRedoLogSubdir() + needIt, err := NeedInnoDBRedoLogSubdir() require.NoError(t, err) if needIt { fpath := path.Join("log", mysql.DynamicRedoLogSubdir) @@ -397,12 +365,12 @@ func TestExecuteBackupWithCanceledContext(t *testing.T) { TopoServer: ts, Keyspace: keyspace, Shard: shard, - MysqlShutdownTimeout: mysqlShutdownTimeout, + MysqlShutdownTimeout: MysqlShutdownTimeout, }, bh) require.Error(t, err) // all four files will fail - require.ErrorContains(t, err, "context canceled;context canceled;context canceled;context canceled") + require.ErrorContains(t, err, "context canceled; context canceled; context canceled; context canceled") assert.Equal(t, mysqlctl.BackupUnusable, backupResult) } @@ -425,7 +393,7 @@ func TestExecuteRestoreWithTimedOutContext(t *testing.T) { require.NoError(t, createBackupFiles(path.Join(dataDir, "test2"), 2, "ibd")) defer os.RemoveAll(backupRoot) - needIt, err := needInnoDBRedoLogSubdir() + needIt, err := NeedInnoDBRedoLogSubdir() require.NoError(t, err) if needIt { fpath := path.Join("log", mysql.DynamicRedoLogSubdir) @@ -482,7 +450,7 @@ func TestExecuteRestoreWithTimedOutContext(t *testing.T) { TopoServer: ts, Keyspace: keyspace, Shard: shard, - MysqlShutdownTimeout: mysqlShutdownTimeout, + MysqlShutdownTimeout: MysqlShutdownTimeout, }, bh) require.NoError(t, err) @@ -521,7 +489,7 @@ func TestExecuteRestoreWithTimedOutContext(t *testing.T) { RestoreToTimestamp: time.Time{}, DryRun: false, Stats: fakeStats, - MysqlShutdownTimeout: mysqlShutdownTimeout, + MysqlShutdownTimeout: MysqlShutdownTimeout, } // Successful restore. @@ -587,24 +555,374 @@ func TestExecuteRestoreWithTimedOutContext(t *testing.T) { } } -// needInnoDBRedoLogSubdir indicates whether we need to create a redo log subdirectory. -// Starting with MySQL 8.0.30, the InnoDB redo logs are stored in a subdirectory of the -// (/. by default) called "#innodb_redo". See: -// -// https://dev.mysql.com/doc/refman/8.0/en/innodb-redo-log.html#innodb-modifying-redo-log-capacity -func needInnoDBRedoLogSubdir() (needIt bool, err error) { - mysqldVersionStr, err := mysqlctl.GetVersionString() - if err != nil { - return needIt, err +type rwCloseFailFirstCall struct { + *bytes.Buffer + firstDone bool +} + +func (w *rwCloseFailFirstCall) Write(p []byte) (n int, err error) { + if w.firstDone { + return w.Buffer.Write(p) } - _, sv, err := mysqlctl.ParseVersionString(mysqldVersionStr) - if err != nil { - return needIt, err + w.firstDone = true + return 0, errors.New("failing first write") +} + +func (w *rwCloseFailFirstCall) Read(p []byte) (n int, err error) { + if w.firstDone { + return w.Buffer.Read(p) } - versionStr := fmt.Sprintf("%d.%d.%d", sv.Major, sv.Minor, sv.Patch) - capableOf := mysql.ServerVersionCapableOf(versionStr) - if capableOf == nil { - return needIt, fmt.Errorf("cannot determine database flavor details for version %s", versionStr) + w.firstDone = true + return 0, errors.New("failing first read") +} + +func (w *rwCloseFailFirstCall) Close() error { + return nil +} + +func newWriteCloseFailFirstWrite(firstWriteDone bool) *rwCloseFailFirstCall { + return &rwCloseFailFirstCall{ + Buffer: bytes.NewBuffer(nil), + firstDone: firstWriteDone, } - return capableOf(capabilities.DynamicRedoLogCapacityFlavorCapability) +} + +func TestExecuteBackupFailToWriteEachFileOnlyOnce(t *testing.T) { + ctx := utils.LeakCheckContext(t) + backupRoot, keyspace, shard, ts := SetupCluster(ctx, t, 2, 2) + + bufferPerFiles := make(map[string]*rwCloseFailFirstCall) + be := &mysqlctl.BuiltinBackupEngine{} + bh := &mysqlctl.FakeBackupHandle{} + bh.AddFileReturnF = func(filename string) mysqlctl.FakeBackupHandleAddFileReturn { + // This mimics what happens with the other BackupHandles where doing AddFile will either truncate or override + // any existing data if the same filename already exists. + _, isRetry := bufferPerFiles[filename] + newBuffer := newWriteCloseFailFirstWrite(isRetry) + bufferPerFiles[filename] = newBuffer + return mysqlctl.FakeBackupHandleAddFileReturn{WriteCloser: newBuffer} + } + + // Spin up a fake daemon to be used in backups. It needs to be allowed to receive: + // "STOP REPLICA", "START REPLICA", in that order. + fakedb := fakesqldb.New(t) + defer fakedb.Close() + mysqld := mysqlctl.NewFakeMysqlDaemon(fakedb) + defer mysqld.Close() + mysqld.ExpectedExecuteSuperQueryList = []string{"STOP REPLICA", "START REPLICA"} + + logger := logutil.NewMemoryLogger() + ctx, cancel := context.WithCancel(ctx) + backupResult, err := be.ExecuteBackup(ctx, mysqlctl.BackupParams{ + Logger: logger, + Mysqld: mysqld, + Cnf: &mysqlctl.Mycnf{ + InnodbDataHomeDir: path.Join(backupRoot, "innodb"), + InnodbLogGroupHomeDir: path.Join(backupRoot, "log"), + DataDir: path.Join(backupRoot, "datadir"), + }, + Stats: backupstats.NewFakeStats(), + Concurrency: 1, + HookExtraEnv: map[string]string{}, + TopoServer: ts, + Keyspace: keyspace, + Shard: shard, + MysqlShutdownTimeout: MysqlShutdownTimeout, + }, bh) + cancel() + + expectedLogs := []string{ + "Backing up file: test1/0.ibd (attempt 1/2)", + "Backing up file: test1/1.ibd (attempt 1/2)", + "Backing up file: test2/0.ibd (attempt 1/2)", + "Backing up file: test2/1.ibd (attempt 1/2)", + + "Backing up file: test1/0.ibd (attempt 2/2)", + "Backing up file: test1/1.ibd (attempt 2/2)", + "Backing up file: test2/0.ibd (attempt 2/2)", + "Backing up file: test2/1.ibd (attempt 2/2)", + + "Backing up file MANIFEST (attempt 1/2)", + "Failed backing up MANIFEST (attempt 1/2)", + "Backing up file MANIFEST (attempt 2/2)", + "Completed backing up MANIFEST (attempt 2/2)", + } + + // Sleep just long enough for everything to complete. + // It's not flaky, the race detector detects a race when there isn't, + // the machine is just too slow to propagate the ctxCancel() to all goroutines. + time.Sleep(2 * time.Second) + AssertLogs(t, expectedLogs, logger) + + require.NoError(t, err) + require.Equal(t, mysqlctl.BackupUsable, backupResult) +} + +func TestExecuteBackupFailToWriteFileTwice(t *testing.T) { + ctx := utils.LeakCheckContext(t) + backupRoot, keyspace, shard, ts := SetupCluster(ctx, t, 1, 1) + + bufferPerFiles := make(map[string]*rwCloseFailFirstCall) + be := &mysqlctl.BuiltinBackupEngine{} + bh := &mysqlctl.FakeBackupHandle{} + bh.AddFileReturnF = func(filename string) mysqlctl.FakeBackupHandleAddFileReturn { + newBuffer := newWriteCloseFailFirstWrite(false) + bufferPerFiles[filename] = newBuffer + + return mysqlctl.FakeBackupHandleAddFileReturn{WriteCloser: newBuffer} + } + + // Spin up a fake daemon to be used in backups. It needs to be allowed to receive: + // "STOP REPLICA", "START REPLICA", in that order. + fakedb := fakesqldb.New(t) + defer fakedb.Close() + mysqld := mysqlctl.NewFakeMysqlDaemon(fakedb) + defer mysqld.Close() + mysqld.ExpectedExecuteSuperQueryList = []string{"STOP REPLICA", "START REPLICA"} + + logger := logutil.NewMemoryLogger() + fakeStats := backupstats.NewFakeStats() + ctx, cancel := context.WithCancel(ctx) + backupResult, err := be.ExecuteBackup(ctx, mysqlctl.BackupParams{ + Logger: logger, + Mysqld: mysqld, + Cnf: &mysqlctl.Mycnf{ + InnodbDataHomeDir: path.Join(backupRoot, "innodb"), + InnodbLogGroupHomeDir: path.Join(backupRoot, "log"), + DataDir: path.Join(backupRoot, "datadir"), + }, + Stats: fakeStats, + Concurrency: 1, + HookExtraEnv: map[string]string{}, + TopoServer: ts, + Keyspace: keyspace, + Shard: shard, + MysqlShutdownTimeout: MysqlShutdownTimeout, + }, bh) + cancel() + + // Sleep just long enough for everything to complete. + // It's not flaky, the race detector detects a race when there isn't, + // the machine is just too slow to propagate the ctxCancel() to all goroutines. + time.Sleep(2 * time.Second) + + expectedLogs := []string{ + "Backing up file: test1/0.ibd (attempt 1/2)", + "Backing up file: test1/0.ibd (attempt 2/2)", + } + AssertLogs(t, expectedLogs, logger) + + ss := GetStats(fakeStats) + require.Equal(t, 2, ss.DestinationCloseStats) + require.Equal(t, 2, ss.DestinationOpenStats) + require.Equal(t, 2, ss.DestinationWriteStats) + require.Equal(t, 2, ss.SourceCloseStats) + require.Equal(t, 2, ss.SourceOpenStats) + require.Equal(t, 2, ss.SourceReadStats) + require.ErrorContains(t, err, "failing first write") + require.Equal(t, mysqlctl.BackupUnusable, backupResult) +} + +func TestExecuteRestoreFailToReadEachFileOnlyOnce(t *testing.T) { + ctx := utils.LeakCheckContext(t) + backupRoot, keyspace, shard, ts := SetupCluster(ctx, t, 2, 2) + + be := &mysqlctl.BuiltinBackupEngine{} + bufferPerFiles := make(map[string]*rwCloseFailFirstCall) + bh := &mysqlctl.FakeBackupHandle{} + bh.AddFileReturnF = func(filename string) mysqlctl.FakeBackupHandleAddFileReturn { + // let's never make it fail for now + newBuffer := newWriteCloseFailFirstWrite(true) + bufferPerFiles[filename] = newBuffer + return mysqlctl.FakeBackupHandleAddFileReturn{WriteCloser: newBuffer} + } + + // Spin up a fake daemon to be used in backups. It needs to be allowed to receive: + // "STOP REPLICA", "START REPLICA", in that order. + fakedb := fakesqldb.New(t) + defer fakedb.Close() + mysqld := mysqlctl.NewFakeMysqlDaemon(fakedb) + defer mysqld.Close() + mysqld.ExpectedExecuteSuperQueryList = []string{"STOP REPLICA", "START REPLICA"} + + backupResult, err := be.ExecuteBackup(ctx, mysqlctl.BackupParams{ + Logger: logutil.NewConsoleLogger(), + Mysqld: mysqld, + Cnf: &mysqlctl.Mycnf{ + InnodbDataHomeDir: path.Join(backupRoot, "innodb"), + InnodbLogGroupHomeDir: path.Join(backupRoot, "log"), + DataDir: path.Join(backupRoot, "datadir"), + }, + Stats: backupstats.NewFakeStats(), + Concurrency: 1, + HookExtraEnv: map[string]string{}, + TopoServer: ts, + Keyspace: keyspace, + Shard: shard, + MysqlShutdownTimeout: MysqlShutdownTimeout, + }, bh) + + require.NoError(t, err) + require.Equal(t, mysqlctl.BackupUsable, backupResult) + + // let's mark each file in the buffer as if it is their first read + for key := range bufferPerFiles { + bufferPerFiles[key].firstDone = false + } + + // Now try to restore the above backup. + fakeBh := &mysqlctl.FakeBackupHandle{} + fakeBh.ReadFileReturnF = func(ctx context.Context, filename string) (io.ReadCloser, error) { + return bufferPerFiles[filename], nil + } + + fakedb = fakesqldb.New(t) + defer fakedb.Close() + mysqld = mysqlctl.NewFakeMysqlDaemon(fakedb) + defer mysqld.Close() + mysqld.ExpectedExecuteSuperQueryList = []string{"STOP REPLICA", "START REPLICA"} + + fakeStats := backupstats.NewFakeStats() + logger := logutil.NewMemoryLogger() + + restoreParams := mysqlctl.RestoreParams{ + Cnf: &mysqlctl.Mycnf{ + InnodbDataHomeDir: path.Join(backupRoot, "innodb"), + InnodbLogGroupHomeDir: path.Join(backupRoot, "log"), + DataDir: path.Join(backupRoot, "datadir"), + BinLogPath: path.Join(backupRoot, "binlog"), + RelayLogPath: path.Join(backupRoot, "relaylog"), + RelayLogIndexPath: path.Join(backupRoot, "relaylogindex"), + RelayLogInfoPath: path.Join(backupRoot, "relayloginfo"), + }, + Logger: logger, + Mysqld: mysqld, + Concurrency: 1, + 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. + bm, err := be.ExecuteRestore(ctx, restoreParams, fakeBh) + assert.NoError(t, err) + assert.NotNil(t, bm) + + ss := GetStats(fakeStats) + require.Equal(t, 8, ss.DestinationCloseStats) + require.Equal(t, 8, ss.DestinationOpenStats) + require.Equal(t, 4, ss.DestinationWriteStats) + require.Equal(t, 8, ss.SourceCloseStats) + require.Equal(t, 8, ss.SourceOpenStats) + require.Equal(t, 8, ss.SourceReadStats) +} + +func TestExecuteRestoreFailToReadEachFileTwice(t *testing.T) { + ctx := utils.LeakCheckContext(t) + backupRoot, keyspace, shard, ts := SetupCluster(ctx, t, 2, 2) + + be := &mysqlctl.BuiltinBackupEngine{} + bufferPerFiles := make(map[string]*rwCloseFailFirstCall) + bh := &mysqlctl.FakeBackupHandle{} + bh.AddFileReturnF = func(filename string) mysqlctl.FakeBackupHandleAddFileReturn { + // let's never make it fail for now + newBuffer := newWriteCloseFailFirstWrite(true) + bufferPerFiles[filename] = newBuffer + return mysqlctl.FakeBackupHandleAddFileReturn{WriteCloser: newBuffer} + } + + // Spin up a fake daemon to be used in backups. It needs to be allowed to receive: + // "STOP REPLICA", "START REPLICA", in that order. + fakedb := fakesqldb.New(t) + defer fakedb.Close() + mysqld := mysqlctl.NewFakeMysqlDaemon(fakedb) + defer mysqld.Close() + mysqld.ExpectedExecuteSuperQueryList = []string{"STOP REPLICA", "START REPLICA"} + + backupResult, err := be.ExecuteBackup(ctx, mysqlctl.BackupParams{ + Logger: logutil.NewConsoleLogger(), + Mysqld: mysqld, + Cnf: &mysqlctl.Mycnf{ + InnodbDataHomeDir: path.Join(backupRoot, "innodb"), + InnodbLogGroupHomeDir: path.Join(backupRoot, "log"), + DataDir: path.Join(backupRoot, "datadir"), + }, + Stats: backupstats.NewFakeStats(), + Concurrency: 1, + HookExtraEnv: map[string]string{}, + TopoServer: ts, + Keyspace: keyspace, + Shard: shard, + MysqlShutdownTimeout: MysqlShutdownTimeout, + }, bh) + + require.NoError(t, err) + require.Equal(t, mysqlctl.BackupUsable, backupResult) + + // Now try to restore the above backup. + fakeBh := &mysqlctl.FakeBackupHandle{} + fakeBh.ReadFileReturnF = func(ctx context.Context, filename string) (io.ReadCloser, error) { + // always make it fail, expect if it is the MANIFEST file, otherwise we won't start restoring the other files + buffer := bufferPerFiles[filename] + if filename != "MANIFEST" { + buffer.firstDone = false + } + return buffer, nil + } + + fakedb = fakesqldb.New(t) + defer fakedb.Close() + mysqld = mysqlctl.NewFakeMysqlDaemon(fakedb) + defer mysqld.Close() + mysqld.ExpectedExecuteSuperQueryList = []string{"STOP REPLICA", "START REPLICA"} + + fakeStats := backupstats.NewFakeStats() + logger := logutil.NewMemoryLogger() + + restoreParams := mysqlctl.RestoreParams{ + Cnf: &mysqlctl.Mycnf{ + InnodbDataHomeDir: path.Join(backupRoot, "innodb"), + InnodbLogGroupHomeDir: path.Join(backupRoot, "log"), + DataDir: path.Join(backupRoot, "datadir"), + BinLogPath: path.Join(backupRoot, "binlog"), + RelayLogPath: path.Join(backupRoot, "relaylog"), + RelayLogIndexPath: path.Join(backupRoot, "relaylogindex"), + RelayLogInfoPath: path.Join(backupRoot, "relayloginfo"), + }, + Logger: logger, + Mysqld: mysqld, + Concurrency: 1, + 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. + bm, err := be.ExecuteRestore(ctx, restoreParams, fakeBh) + assert.ErrorContains(t, err, "failing first read") + assert.Nil(t, bm) + + ss := GetStats(fakeStats) + require.Equal(t, 5, ss.DestinationCloseStats) + require.Equal(t, 5, ss.DestinationOpenStats) + require.Equal(t, 0, ss.DestinationWriteStats) + require.Equal(t, 5, ss.SourceCloseStats) + require.Equal(t, 5, ss.SourceOpenStats) + require.Equal(t, 5, ss.SourceReadStats) } diff --git a/go/vt/mysqlctl/blackbox/utils.go b/go/vt/mysqlctl/blackbox/utils.go new file mode 100644 index 00000000000..c7d34ae3cf6 --- /dev/null +++ b/go/vt/mysqlctl/blackbox/utils.go @@ -0,0 +1,196 @@ +/* +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 blackbox + +import ( + "context" + "fmt" + "os" + "path" + "slices" + "strconv" + "testing" + "time" + + "github.com/stretchr/testify/require" + + "vitess.io/vitess/go/mysql" + "vitess.io/vitess/go/mysql/capabilities" + "vitess.io/vitess/go/vt/logutil" + "vitess.io/vitess/go/vt/mysqlctl" + "vitess.io/vitess/go/vt/mysqlctl/backupstats" + "vitess.io/vitess/go/vt/mysqlctl/filebackupstorage" + logutilpb "vitess.io/vitess/go/vt/proto/logutil" + "vitess.io/vitess/go/vt/proto/topodata" + "vitess.io/vitess/go/vt/proto/vttime" + "vitess.io/vitess/go/vt/topo" + "vitess.io/vitess/go/vt/topo/memorytopo" +) + +type StatSummary struct { + DestinationCloseStats int + DestinationOpenStats int + DestinationWriteStats int + SourceCloseStats int + SourceOpenStats int + SourceReadStats int +} + +func GetStats(stats *backupstats.FakeStats) StatSummary { + var ss StatSummary + + for _, sr := range stats.ScopeReturns { + switch sr.ScopeV[backupstats.ScopeOperation] { + case "Destination:Close": + ss.DestinationCloseStats += len(sr.TimedIncrementCalls) + case "Destination:Open": + ss.DestinationOpenStats += len(sr.TimedIncrementCalls) + case "Destination:Write": + if len(sr.TimedIncrementBytesCalls) > 0 { + ss.DestinationWriteStats++ + } + case "Source:Close": + ss.SourceCloseStats += len(sr.TimedIncrementCalls) + case "Source:Open": + ss.SourceOpenStats += len(sr.TimedIncrementCalls) + case "Source:Read": + if len(sr.TimedIncrementBytesCalls) > 0 { + ss.SourceReadStats++ + } + } + } + return ss +} + +func AssertLogs(t *testing.T, expectedLogs []string, logger *logutil.MemoryLogger) { + for _, log := range expectedLogs { + require.Truef(t, slices.ContainsFunc(logger.LogEvents(), func(event *logutilpb.Event) bool { + return event.GetValue() == log + }), "%s is missing from the logs", log) + } +} + +func SetupCluster(ctx context.Context, t *testing.T, dirs, filesPerDir int) (backupRoot string, keyspace string, shard string, ts *topo.Server) { + // Set up local backup directory + id := fmt.Sprintf("%d", time.Now().UnixNano()) + backupRoot = fmt.Sprintf("testdata/builtinbackup_test_%s", id) + filebackupstorage.FileBackupStorageRoot = backupRoot + require.NoError(t, createBackupDir(backupRoot, "innodb", "log", "datadir")) + dataDir := path.Join(backupRoot, "datadir") + // Add some files under data directory to force backup to execute semaphore acquire inside + // backupFiles() method (https://github.com/vitessio/vitess/blob/main/go/vt/mysqlctl/builtinbackupengine.go#L483). + for dirI := range dirs { + dirName := "test" + strconv.Itoa(dirI+1) + require.NoError(t, createBackupDir(dataDir, dirName)) + require.NoError(t, createBackupFiles(path.Join(dataDir, dirName), filesPerDir, "ibd")) + } + t.Cleanup(func() { + require.NoError(t, os.RemoveAll(backupRoot)) + }) + + needIt, err := NeedInnoDBRedoLogSubdir() + require.NoError(t, err) + if needIt { + fpath := path.Join("log", mysql.DynamicRedoLogSubdir) + if err := createBackupDir(backupRoot, fpath); err != nil { + require.Failf(t, err.Error(), "failed to create directory: %s", fpath) + } + } + + // Set up topo + keyspace, shard = "mykeyspace", "-" + ts = memorytopo.NewServer(ctx, "cell1") + t.Cleanup(func() { + ts.Close() + }) + + require.NoError(t, ts.CreateKeyspace(ctx, keyspace, &topodata.Keyspace{})) + require.NoError(t, ts.CreateShard(ctx, keyspace, shard)) + + tablet := topo.NewTablet(100, "cell1", "mykeyspace-00-80-0100") + tablet.Keyspace = keyspace + tablet.Shard = shard + + require.NoError(t, ts.CreateTablet(ctx, tablet)) + + _, err = ts.UpdateShardFields(ctx, keyspace, shard, func(si *topo.ShardInfo) error { + si.PrimaryAlias = &topodata.TabletAlias{Uid: 100, Cell: "cell1"} + + now := time.Now() + si.PrimaryTermStartTime = &vttime.Time{Seconds: int64(now.Second()), Nanoseconds: int32(now.Nanosecond())} + + return nil + }) + require.NoError(t, err) + return backupRoot, keyspace, shard, ts +} + +// NeedInnoDBRedoLogSubdir indicates whether we need to create a redo log subdirectory. +// Starting with MySQL 8.0.30, the InnoDB redo logs are stored in a subdirectory of the +// (/. by default) called "#innodb_redo". See: +// +// https://dev.mysql.com/doc/refman/8.0/en/innodb-redo-log.html#innodb-modifying-redo-log-capacity +func NeedInnoDBRedoLogSubdir() (needIt bool, err error) { + mysqldVersionStr, err := mysqlctl.GetVersionString() + if err != nil { + return needIt, err + } + _, sv, err := mysqlctl.ParseVersionString(mysqldVersionStr) + if err != nil { + return needIt, err + } + versionStr := fmt.Sprintf("%d.%d.%d", sv.Major, sv.Minor, sv.Patch) + capableOf := mysql.ServerVersionCapableOf(versionStr) + if capableOf == nil { + return needIt, fmt.Errorf("cannot determine database flavor details for version %s", versionStr) + } + return capableOf(capabilities.DynamicRedoLogCapacityFlavorCapability) +} + +const MysqlShutdownTimeout = 1 * time.Minute + +func SetBuiltinBackupMysqldDeadline(t time.Duration) time.Duration { + old := mysqlctl.BuiltinBackupMysqldTimeout + mysqlctl.BuiltinBackupMysqldTimeout = t + + return old +} + +func createBackupDir(root string, dirs ...string) error { + for _, dir := range dirs { + if err := os.MkdirAll(path.Join(root, dir), 0755); err != nil { + return err + } + } + + return nil +} + +func createBackupFiles(root string, fileCount int, ext string) error { + for i := 0; i < fileCount; i++ { + f, err := os.Create(path.Join(root, fmt.Sprintf("%d.%s", i, ext))) + if err != nil { + return err + } + if _, err := f.Write([]byte("hello, world!")); err != nil { + return err + } + defer f.Close() + } + + return nil +} diff --git a/go/vt/mysqlctl/builtinbackupengine.go b/go/vt/mysqlctl/builtinbackupengine.go index f3cbe5364a0..2046a238400 100644 --- a/go/vt/mysqlctl/builtinbackupengine.go +++ b/go/vt/mysqlctl/builtinbackupengine.go @@ -29,18 +29,17 @@ import ( "os" "path" "path/filepath" - "sync" + "strconv" "sync/atomic" "time" "github.com/spf13/pflag" - "golang.org/x/sync/semaphore" + "golang.org/x/sync/errgroup" "vitess.io/vitess/go/ioutil" "vitess.io/vitess/go/mysql" "vitess.io/vitess/go/mysql/replication" "vitess.io/vitess/go/protoutil" - "vitess.io/vitess/go/vt/concurrency" "vitess.io/vitess/go/vt/log" "vitess.io/vitess/go/vt/logutil" stats "vitess.io/vitess/go/vt/mysqlctl/backupstats" @@ -60,6 +59,8 @@ const ( builtinBackupEngineName = "builtin" AutoIncrementalFromPos = "auto" dataDictionaryFile = "mysql.ibd" + + maxRetriesPerFile = 1 ) var ( @@ -149,6 +150,13 @@ type FileEntry struct { // ParentPath is an optional prefix to the Base path. If empty, it is ignored. Useful // for writing files in a temporary directory ParentPath string + + // RetryCount specifies how many times we retried restoring/backing up this FileEntry. + // If we fail to restore/backup this FileEntry, we will retry up to maxRetriesPerFile times. + // Every time the builtin backup engine retries this file, we increment this field by 1. + // We don't care about adding this information to the MANIFEST and also to not cause any compatibility issue + // we are adding the - json tag to let Go know it can ignore the field. + RetryCount int `json:"-"` } func init() { @@ -585,6 +593,11 @@ func (be *BuiltinBackupEngine) backupFiles( mysqlVersion string, incrDetails *IncrementalBackupDetails, ) (finalErr error) { + // backupFiles always wait for AddFiles to finish its work before returning, unless there has been a + // non-recoverable error in the process, in both cases we can cancel the context safely. + ctx, cancel := context.WithCancel(ctx) + defer cancel() + // Get the files to backup. // We don't care about totalSize because we add each file separately. var fes []FileEntry @@ -599,35 +612,82 @@ func (be *BuiltinBackupEngine) backupFiles( } params.Logger.Infof("found %v files to backup", len(fes)) - // Backup with the provided concurrency. - sema := semaphore.NewWeighted(int64(params.Concurrency)) - wg := sync.WaitGroup{} + // The error here can be ignored safely. Failed FileEntry's are handled in the next 'if' statement. + _ = be.backupFileEntries(ctx, fes, bh, params) + // BackupHandle supports the BackupErrorRecorder interface for tracking errors + // across any goroutines that fan out to take the backup. This means that we + // don't need a local error recorder and can put everything through the bh. + // + // This handles the scenario where bh.AddFile() encounters an error asynchronously, + // which ordinarily would be lost in the context of `be.backupFile`, i.e. if an + // error were encountered + // [here](https://github.com/vitessio/vitess/blob/d26b6c7975b12a87364e471e2e2dfa4e253c2a5b/go/vt/mysqlctl/s3backupstorage/s3.go#L139-L142). + // + // All the errors are grouped per file, if one or more files failed, we back them up + // once more concurrently, if any of the retry fail, we fail-fast by canceling the context + // and return an error. There is no reason to continue processing the other retries, if + // one of them failed. + if files := bh.GetFailedFiles(); len(files) > 0 { + newFEs := make([]FileEntry, len(fes)) + for _, file := range files { + fileNb, err := strconv.Atoi(file) + if err != nil { + return vterrors.Wrapf(err, "failed to retry file '%s'", file) + } + oldFes := fes[fileNb] + newFEs[fileNb] = FileEntry{ + Base: oldFes.Base, + Name: oldFes.Name, + ParentPath: oldFes.ParentPath, + RetryCount: 1, + } + bh.ResetErrorForFile(file) + } + err = be.backupFileEntries(ctx, newFEs, bh, params) + if err != nil { + return err + } + } + + // Backup the MANIFEST file and apply retry logic. + var manifestErr error + for currentRetry := 0; currentRetry <= maxRetriesPerFile; currentRetry++ { + manifestErr = be.backupManifest(ctx, params, bh, backupPosition, purgedPosition, fromPosition, fromBackupName, serverUUID, mysqlVersion, incrDetails, fes, currentRetry) + if manifestErr == nil { + break + } + bh.ResetErrorForFile(backupManifestFileName) + } + if manifestErr != nil { + return manifestErr + } + return nil +} + +// backupFileEntries iterates over a slice of FileEntry, backing them up concurrently up to the defined concurrency limit. +// This function will ignore empty FileEntry, allowing the retry mechanism to send a partially empty slice, to not +// mess up the index of retriable FileEntry. +// This function does not leave any background operation behind itself, all calls to bh.AddFile will be finished or canceled. +func (be *BuiltinBackupEngine) backupFileEntries(ctx context.Context, fes []FileEntry, bh backupstorage.BackupHandle, params BackupParams) error { ctxCancel, cancel := context.WithCancel(ctx) - defer cancel() + defer func() { + // If we reached this defer in all cases we can cancel the context. + // The only ways to get here are: a panic, an error when ending the backup, a successful backup. + // For all three options, it is safe to cancel the context, there should be no pending operations + // that 1) haven't completed, 2) we care about anymore. + cancel() + }() + g := errgroup.Group{} + g.SetLimit(params.Concurrency) for i := range fes { - wg.Add(1) - go func(i int) { - defer wg.Done() + if fes[i].Name == "" { + continue + } + g.Go(func() error { fe := &fes[i] - // Wait until we are ready to go, return if we encounter an error - acqErr := sema.Acquire(ctxCancel, 1) - if acqErr != nil { - log.Errorf("Unable to acquire semaphore needed to backup file: %s, err: %s", fe.Name, acqErr.Error()) - bh.RecordError(acqErr) - cancel() - return - } - defer sema.Release(1) - - // First check if we have any error, if we have, there is no point trying backing up this file. - // We check for errors before checking if the context is canceled on purpose, if there was an - // error, the context would have been canceled already. - if bh.HasErrors() { - params.Logger.Errorf("Failed to restore files due to error: %v", bh.Error()) - return - } + name := fmt.Sprintf("%v", i) // Check for context cancellation explicitly because, the way semaphore code is written, theoretically we might // end up not throwing an error even after cancellation. Please see https://cs.opensource.google/go/x/sync/+/refs/tags/v0.1.0:semaphore/semaphore.go;l=66, @@ -636,83 +696,30 @@ func (be *BuiltinBackupEngine) backupFiles( select { case <-ctxCancel.Done(): log.Errorf("Context canceled or timed out during %q backup", fe.Name) - bh.RecordError(vterrors.Errorf(vtrpc.Code_CANCELED, "context canceled")) - return + bh.RecordError(name, vterrors.Errorf(vtrpc.Code_CANCELED, "context canceled")) + return nil default: } // Backup the individual file. - name := fmt.Sprintf("%v", i) - if err := be.backupFile(ctxCancel, params, bh, fe, name); err != nil { - bh.RecordError(err) - cancel() + var errBackupFile error + if errBackupFile = be.backupFile(ctxCancel, params, bh, fe, name); errBackupFile != nil { + bh.RecordError(name, vterrors.Wrapf(errBackupFile, "failed to backup file '%s'", name)) + if fe.RetryCount >= maxRetriesPerFile { + // this is the last attempt, and we have an error, we can cancel everything and fail fast. + cancel() + } } - }(i) - } - - wg.Wait() - - // BackupHandle supports the ErrorRecorder interface for tracking errors - // across any goroutines that fan out to take the backup. This means that we - // don't need a local error recorder and can put everything through the bh. - // - // This handles the scenario where bh.AddFile() encounters an error asynchronously, - // which ordinarily would be lost in the context of `be.backupFile`, i.e. if an - // error were encountered - // [here](https://github.com/vitessio/vitess/blob/d26b6c7975b12a87364e471e2e2dfa4e253c2a5b/go/vt/mysqlctl/s3backupstorage/s3.go#L139-L142). - if bh.HasErrors() { - return bh.Error() + return nil + }) } + _ = g.Wait() - // open the MANIFEST - wc, err := bh.AddFile(ctx, backupManifestFileName, backupstorage.FileSizeUnknown) - if err != nil { - return vterrors.Wrapf(err, "cannot add %v to backup", backupManifestFileName) - } - defer func() { - closeErr := wc.Close() - if finalErr == nil { - finalErr = closeErr - } - }() - - // JSON-encode and write the MANIFEST - bm := &builtinBackupManifest{ - // Common base fields - BackupManifest: BackupManifest{ - BackupName: bh.Name(), - BackupMethod: builtinBackupEngineName, - Position: backupPosition, - PurgedPosition: purgedPosition, - FromPosition: fromPosition, - FromBackup: fromBackupName, - Incremental: !fromPosition.IsZero(), - ServerUUID: serverUUID, - TabletAlias: params.TabletAlias, - Keyspace: params.Keyspace, - Shard: params.Shard, - BackupTime: params.BackupTime.UTC().Format(time.RFC3339), - FinishedTime: time.Now().UTC().Format(time.RFC3339), - MySQLVersion: mysqlVersion, - UpgradeSafe: params.UpgradeSafe, - IncrementalDetails: incrDetails, - }, - - // Builtin-specific fields - FileEntries: fes, - SkipCompress: !backupStorageCompress, - CompressionEngine: CompressionEngineName, - ExternalDecompressor: ManifestExternalDecompressorCmd, - } - data, err := json.MarshalIndent(bm, "", " ") + err := bh.EndBackup(ctx) if err != nil { - return vterrors.Wrapf(err, "cannot JSON encode %v", backupManifestFileName) - } - if _, err := wc.Write([]byte(data)); err != nil { - return vterrors.Wrapf(err, "cannot write %v", backupManifestFileName) + return err } - - return nil + return bh.Error() } type backupPipe struct { @@ -725,6 +732,7 @@ type backupPipe struct { crc32 hash.Hash32 nn int64 done chan struct{} + failed chan struct{} closed int32 } @@ -735,6 +743,7 @@ func newBackupWriter(filename string, writerBufferSize int, maxSize int64, w io. filename: filename, maxSize: maxSize, done: make(chan struct{}), + failed: make(chan struct{}), } } @@ -744,10 +753,16 @@ func newBackupReader(filename string, maxSize int64, r io.Reader) *backupPipe { r: r, filename: filename, done: make(chan struct{}), + failed: make(chan struct{}), maxSize: maxSize, } } +func retryToString(retry int) string { + // We convert the retry number to an attempt number, increasing retry by one, so it looks more human friendly + return fmt.Sprintf("(attempt %d/%d)", retry+1, maxRetriesPerFile+1) +} + func (bp *backupPipe) Read(p []byte) (int, error) { nn, err := bp.r.Read(p) _, _ = bp.crc32.Write(p[:nn]) @@ -762,9 +777,17 @@ func (bp *backupPipe) Write(p []byte) (int, error) { return nn, err } -func (bp *backupPipe) Close() error { +func (bp *backupPipe) Close(isDone bool) (err error) { if atomic.CompareAndSwapInt32(&bp.closed, 0, 1) { - close(bp.done) + // If we fail to Flush the writer we must report this backup as a failure. + defer func() { + if isDone && err == nil { + close(bp.done) + return + } + close(bp.failed) + }() + if bp.w != nil { if err := bp.w.Flush(); err != nil { return err @@ -778,28 +801,31 @@ func (bp *backupPipe) HashString() string { return hex.EncodeToString(bp.crc32.Sum(nil)) } -func (bp *backupPipe) ReportProgress(ctx context.Context, period time.Duration, logger logutil.Logger, restore bool) { - messageStr := "restoring " +func (bp *backupPipe) ReportProgress(ctx context.Context, period time.Duration, logger logutil.Logger, restore bool, retryStr string) { + messageStr := "restoring" if !restore { - messageStr = "backing up " + messageStr = "backing up" } tick := time.NewTicker(period) defer tick.Stop() for { select { case <-ctx.Done(): - logger.Infof("Canceled %s of %q file", messageStr, bp.filename) + logger.Infof("Canceled %s of %q file %s", messageStr, bp.filename, retryStr) return case <-bp.done: - logger.Infof("Completed %s %q", messageStr, bp.filename) + logger.Infof("Completed %s %q %s", messageStr, bp.filename, retryStr) + return + case <-bp.failed: + logger.Infof("Failed %s %q %s", messageStr, bp.filename, retryStr) return case <-tick.C: written := float64(atomic.LoadInt64(&bp.nn)) if bp.maxSize == 0 { - logger.Infof("%s %q: %.02fkb", messageStr, bp.filename, written/1024.0) + logger.Infof("%s %q %s: %.02fkb", messageStr, bp.filename, retryStr, written/1024.0) } else { maxSize := float64(bp.maxSize) - logger.Infof("%s %q: %.02f%% (%.02f/%.02fkb)", messageStr, bp.filename, 100.0*written/maxSize, written/1024.0, maxSize/1024.0) + logger.Infof("%s %q %s: %.02f%% (%.02f/%.02fkb)", messageStr, bp.filename, retryStr, 100.0*written/maxSize, written/1024.0, maxSize/1024.0) } } } @@ -807,12 +833,15 @@ func (bp *backupPipe) ReportProgress(ctx context.Context, period time.Duration, // backupFile backs up an individual file. func (be *BuiltinBackupEngine) backupFile(ctx context.Context, params BackupParams, bh backupstorage.BackupHandle, fe *FileEntry, name string) (finalErr error) { - ctx, cancel := context.WithCancel(ctx) - defer func() { - if finalErr != nil { - cancel() - } - }() + // We need another context that does not live outside of this function. + // Reporting progress, compressing and writing are operations that will be + // over by the time we exit this function, they can use this cancelable context. + // However, AddFile is something that may continue in the background even after + // this function exits. In this case, we give it the parent context so the caller + // has more control over when to cancel AddFile. + cancelableCtx, cancel := context.WithCancel(ctx) + defer cancel() + // Open the source file for reading. openSourceAt := time.Now() source, err := fe.open(params.Cnf, true) @@ -835,11 +864,12 @@ func (be *BuiltinBackupEngine) backupFile(ctx context.Context, params BackupPara return err } + retryStr := retryToString(fe.RetryCount) br := newBackupReader(fe.Name, fi.Size(), timedSource) - go br.ReportProgress(ctx, builtinBackupProgress, params.Logger, false /*restore*/) + go br.ReportProgress(cancelableCtx, builtinBackupProgress, params.Logger, false /*restore*/, retryStr) // Open the destination file for writing, and a buffer. - params.Logger.Infof("Backing up file: %v", fe.Name) + params.Logger.Infof("Backing up file: %v %s", fe.Name, retryStr) openDestAt := time.Now() dest, err := bh.AddFile(ctx, name, fi.Size()) if err != nil { @@ -871,19 +901,20 @@ func (be *BuiltinBackupEngine) backupFile(ctx context.Context, params BackupPara defer func() { // Close the backupPipe to finish writing on destination. - if err := bw.Close(); err != nil { + if err := bw.Close(createAndCopyErr == nil); err != nil { createAndCopyErr = errors.Join(createAndCopyErr, vterrors.Wrapf(err, "cannot flush destination: %v", name)) } - if err := br.Close(); err != nil { + if err := br.Close(createAndCopyErr == nil); err != nil { createAndCopyErr = errors.Join(createAndCopyErr, vterrors.Wrap(err, "failed to close the source reader")) } + }() // Create the gzip compression pipe, if necessary. if backupStorageCompress { var compressor io.WriteCloser if ExternalCompressorCmd != "" { - compressor, err = newExternalCompressor(ctx, ExternalCompressorCmd, writer, params.Logger) + compressor, err = newExternalCompressor(cancelableCtx, ExternalCompressorCmd, writer, params.Logger) } else { compressor, err = newBuiltinCompressor(CompressionEngineName, writer, params.Logger) } @@ -894,13 +925,13 @@ func (be *BuiltinBackupEngine) backupFile(ctx context.Context, params BackupPara compressStats := params.Stats.Scope(stats.Operation("Compressor:Write")) writer = ioutil.NewMeteredWriter(compressor, compressStats.TimedIncrementBytes) - closer := ioutil.NewTimeoutCloser(ctx, compressor, closeTimeout) + closer := ioutil.NewTimeoutCloser(cancelableCtx, compressor, closeTimeout) defer func() { // Close gzip to flush it, after that all data is sent to writer. closeCompressorAt := time.Now() - params.Logger.Infof("closing compressor") + params.Logger.Infof("Closing compressor for file: %s %s", fe.Name, retryStr) if cerr := closer.Close(); err != nil { - cerr = vterrors.Wrapf(cerr, "failed to close compressor %v", name) + cerr = vterrors.Wrapf(cerr, "failed to close compressor %v", fe.Name) params.Logger.Error(cerr) createAndCopyErr = errors.Join(createAndCopyErr, cerr) } @@ -930,6 +961,94 @@ func (be *BuiltinBackupEngine) backupFile(ctx context.Context, params BackupPara return nil } +func (be *BuiltinBackupEngine) backupManifest( + ctx context.Context, + params BackupParams, + bh backupstorage.BackupHandle, + backupPosition replication.Position, + purgedPosition replication.Position, + fromPosition replication.Position, + fromBackupName string, + serverUUID string, + mysqlVersion string, + incrDetails *IncrementalBackupDetails, + fes []FileEntry, + currentAttempt int, +) (finalErr error) { + retryStr := retryToString(currentAttempt) + params.Logger.Infof("Backing up file %s %s", backupManifestFileName, retryStr) + defer func() { + state := "Completed" + if finalErr != nil { + state = "Failed" + } + params.Logger.Infof("%s backing up %s %s", state, backupManifestFileName, retryStr) + }() + + // Creating this function allows us to ensure we always close the writer no matter what, + // and in case of success that we close it before calling bh.EndBackup. + addAndWrite := func() (addAndWriteError error) { + // open the MANIFEST + wc, err := bh.AddFile(ctx, backupManifestFileName, backupstorage.FileSizeUnknown) + if err != nil { + return vterrors.Wrapf(err, "cannot add %v to backup %s", backupManifestFileName, retryStr) + } + defer func() { + if err := wc.Close(); err != nil { + addAndWriteError = errors.Join(addAndWriteError, vterrors.Wrapf(err, "cannot close backup: %v", backupManifestFileName)) + } + }() + + // JSON-encode and write the MANIFEST + bm := &builtinBackupManifest{ + // Common base fields + BackupManifest: BackupManifest{ + BackupName: bh.Name(), + BackupMethod: builtinBackupEngineName, + Position: backupPosition, + PurgedPosition: purgedPosition, + FromPosition: fromPosition, + FromBackup: fromBackupName, + Incremental: !fromPosition.IsZero(), + ServerUUID: serverUUID, + TabletAlias: params.TabletAlias, + Keyspace: params.Keyspace, + Shard: params.Shard, + BackupTime: params.BackupTime.UTC().Format(time.RFC3339), + FinishedTime: time.Now().UTC().Format(time.RFC3339), + MySQLVersion: mysqlVersion, + UpgradeSafe: params.UpgradeSafe, + IncrementalDetails: incrDetails, + }, + + // Builtin-specific fields + FileEntries: fes, + SkipCompress: !backupStorageCompress, + CompressionEngine: CompressionEngineName, + ExternalDecompressor: ManifestExternalDecompressorCmd, + } + data, err := json.MarshalIndent(bm, "", " ") + if err != nil { + return vterrors.Wrapf(err, "cannot JSON encode %v %s", backupManifestFileName, retryStr) + } + if _, err := wc.Write(data); err != nil { + return vterrors.Wrapf(err, "cannot write %v %s", backupManifestFileName, retryStr) + } + return nil + } + + err := addAndWrite() + if err != nil { + return err + } + + err = bh.EndBackup(ctx) + if err != nil { + return err + } + return bh.Error() +} + // 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, params.MysqlShutdownTimeout); err != nil { @@ -989,8 +1108,8 @@ func (be *BuiltinBackupEngine) executeRestoreIncrementalBackup(ctx context.Conte // we return the position from which replication should start // otherwise an error is returned func (be *BuiltinBackupEngine) ExecuteRestore(ctx context.Context, params RestoreParams, bh backupstorage.BackupHandle) (*BackupManifest, error) { - var bm builtinBackupManifest - if err := getBackupManifestInto(ctx, bh, &bm); err != nil { + bm, err := be.restoreManifest(ctx, params, bh) + if err != nil { return nil, err } @@ -999,7 +1118,6 @@ func (be *BuiltinBackupEngine) ExecuteRestore(ctx context.Context, params Restor return nil, err } - var err error if bm.Incremental { err = be.executeRestoreIncrementalBackup(ctx, params, bh, bm) } else { @@ -1012,6 +1130,26 @@ func (be *BuiltinBackupEngine) ExecuteRestore(ctx context.Context, params Restor return &bm.BackupManifest, nil } +func (be *BuiltinBackupEngine) restoreManifest(ctx context.Context, params RestoreParams, bh backupstorage.BackupHandle) (bm builtinBackupManifest, finalErr error) { + var retryCount int + defer func() { + state := "Completed" + if finalErr != nil { + state = "Failed" + } + params.Logger.Infof("%s restoring %s %s", state, backupManifestFileName, retryToString(retryCount)) + }() + + for ; retryCount <= maxRetriesPerFile; retryCount++ { + params.Logger.Infof("Restoring file %s %s", backupManifestFileName, retryToString(retryCount)) + if finalErr = getBackupManifestInto(ctx, bh, &bm); finalErr == nil { + break + } + params.Logger.Infof("Failed restoring %s %s", backupManifestFileName, retryToString(retryCount)) + } + return +} + // restoreFiles will copy all the files from the BackupStorage to the // right place. func (be *BuiltinBackupEngine) restoreFiles(ctx context.Context, params RestoreParams, bh backupstorage.BackupHandle, bm builtinBackupManifest) (createdDir string, err error) { @@ -1032,67 +1170,79 @@ func (be *BuiltinBackupEngine) restoreFiles(ctx context.Context, params RestoreP } } fes := bm.FileEntries - sema := semaphore.NewWeighted(int64(params.Concurrency)) - rec := concurrency.AllErrorRecorder{} - wg := sync.WaitGroup{} + _ = be.restoreFileEntries(ctx, fes, bh, bm, params, createdDir) + if files := bh.GetFailedFiles(); len(files) > 0 { + newFEs := make([]FileEntry, len(fes)) + for _, file := range files { + fileNb, err := strconv.Atoi(file) + if err != nil { + return "", vterrors.Wrapf(err, "failed to retry file '%s'", file) + } + oldFes := fes[fileNb] + newFEs[fileNb] = FileEntry{ + Base: oldFes.Base, + Name: oldFes.Name, + ParentPath: oldFes.ParentPath, + Hash: oldFes.Hash, + RetryCount: 1, + } + bh.ResetErrorForFile(file) + } + err = be.restoreFileEntries(ctx, newFEs, bh, bm, params, createdDir) + if err != nil { + return "", err + } + } + return createdDir, nil +} - ctxCancel, cancel := context.WithCancel(ctx) - defer cancel() +func (be *BuiltinBackupEngine) restoreFileEntries(ctx context.Context, fes []FileEntry, bh backupstorage.BackupHandle, bm builtinBackupManifest, params RestoreParams, createdDir string) error { + g, ctx := errgroup.WithContext(ctx) + g.SetLimit(params.Concurrency) for i := range fes { - wg.Add(1) - go func(i int) { - defer wg.Done() + if fes[i].Name == "" { + continue + } + g.Go(func() error { fe := &fes[i] - // Wait until we are ready to go, return if we encounter an error - acqErr := sema.Acquire(ctxCancel, 1) - if acqErr != nil { - log.Errorf("Unable to acquire semaphore needed to restore file: %s, err: %s", fe.Name, acqErr.Error()) - rec.RecordError(acqErr) - cancel() - return - } - defer sema.Release(1) - - // First check if we have any error, if we have, there is no point trying to restore this file. - // We check for errors before checking if the context is canceled on purpose, if there was an - // error, the context would have been canceled already. - if rec.HasErrors() { - params.Logger.Errorf("Failed to restore files due to error: %v", bh.Error()) - return - } - + name := fmt.Sprintf("%v", i) // Check for context cancellation explicitly because, the way semaphore code is written, theoretically we might // end up not throwing an error even after cancellation. Please see https://cs.opensource.google/go/x/sync/+/refs/tags/v0.1.0:semaphore/semaphore.go;l=66, // which suggests that if the context is already done, `Acquire()` may still succeed without blocking. This introduces // unpredictability in my test cases, so in order to avoid that, I am adding this cancellation check. select { - case <-ctxCancel.Done(): + case <-ctx.Done(): log.Errorf("Context canceled or timed out during %q restore", fe.Name) - rec.RecordError(vterrors.Errorf(vtrpc.Code_CANCELED, "context canceled")) - return + bh.RecordError(name, vterrors.Errorf(vtrpc.Code_CANCELED, "context canceled")) + return nil default: } fe.ParentPath = createdDir + // And restore the file. - name := fmt.Sprintf("%v", i) - params.Logger.Infof("Copying file %v: %v", name, fe.Name) - err := be.restoreFile(ctxCancel, params, bh, fe, bm, name) - if err != nil { - rec.RecordError(vterrors.Wrapf(err, "can't restore file %v to %v", name, fe.Name)) - cancel() + params.Logger.Infof("Copying file %v: %v %s", name, fe.Name, retryToString(fe.RetryCount)) + if errRestore := be.restoreFile(ctx, params, bh, fe, bm, name); errRestore != nil { + bh.RecordError(name, vterrors.Wrapf(errRestore, "failed to restore file %v to %v", name, fe.Name)) + if fe.RetryCount >= maxRetriesPerFile { + // this is the last attempt, and we have an error, we can return an error, which will let errgroup + // know it can cancel the context + return errRestore + } } - }(i) + return nil + }) } - wg.Wait() - return createdDir, rec.Error() + _ = g.Wait() + return bh.Error() } // restoreFile restores an individual file. func (be *BuiltinBackupEngine) restoreFile(ctx context.Context, params RestoreParams, bh backupstorage.BackupHandle, fe *FileEntry, bm builtinBackupManifest, name string) (finalErr error) { ctx, cancel := context.WithCancel(ctx) defer cancel() + // Open the source file for reading. openSourceAt := time.Now() source, err := bh.ReadFile(ctx, name) @@ -1110,8 +1260,15 @@ func (be *BuiltinBackupEngine) restoreFile(ctx context.Context, params RestorePa params.Stats.Scope(stats.Operation("Source:Close")).TimedIncrement(time.Since(closeSourceAt)) }() - br := newBackupReader(name, 0, timedSource) - go br.ReportProgress(ctx, builtinBackupProgress, params.Logger, true) + // Create the backup/source reader and start reporting progress + retryStr := retryToString(fe.RetryCount) + br := newBackupReader(fe.Name, 0, timedSource) + go br.ReportProgress(ctx, builtinBackupProgress, params.Logger, true, retryStr) + defer func() { + if err := br.Close(finalErr == nil); err != nil { + finalErr = vterrors.Wrap(finalErr, "failed to close the source reader") + } + }() var reader io.Reader = br // Open the destination file for writing. @@ -1197,10 +1354,6 @@ func (be *BuiltinBackupEngine) restoreFile(ctx context.Context, params RestorePa return vterrors.Wrap(err, "failed to flush destination buffer") } - if err := br.Close(); err != nil { - return vterrors.Wrap(err, "failed to close the source reader") - } - return nil } diff --git a/go/vt/mysqlctl/cephbackupstorage/ceph.go b/go/vt/mysqlctl/cephbackupstorage/ceph.go index f8e33dbe641..62330b869f0 100644 --- a/go/vt/mysqlctl/cephbackupstorage/ceph.go +++ b/go/vt/mysqlctl/cephbackupstorage/ceph.go @@ -32,9 +32,10 @@ import ( minio "github.com/minio/minio-go" "github.com/spf13/pflag" - "vitess.io/vitess/go/vt/concurrency" - "vitess.io/vitess/go/vt/log" "vitess.io/vitess/go/vt/mysqlctl/backupstorage" + + "vitess.io/vitess/go/vt/log" + errorsbackup "vitess.io/vitess/go/vt/mysqlctl/errors" "vitess.io/vitess/go/vt/servenv" ) @@ -69,23 +70,8 @@ type CephBackupHandle struct { dir string name string readOnly bool - errors concurrency.AllErrorRecorder waitGroup sync.WaitGroup -} - -// RecordError is part of the concurrency.ErrorRecorder interface. -func (bh *CephBackupHandle) RecordError(err error) { - bh.errors.RecordError(err) -} - -// HasErrors is part of the concurrency.ErrorRecorder interface. -func (bh *CephBackupHandle) HasErrors() bool { - return bh.errors.HasErrors() -} - -// Error is part of the concurrency.ErrorRecorder interface. -func (bh *CephBackupHandle) Error() error { - return bh.errors.Error() + errorsbackup.PerFileErrorRecorder } // Directory implements BackupHandle. @@ -109,7 +95,7 @@ func (bh *CephBackupHandle) AddFile(ctx context.Context, filename string, filesi defer bh.waitGroup.Done() // ceph bucket name is where the backups will go - //backup handle dir field contains keyspace/shard value + // backup handle dir field contains keyspace/shard value bucket := alterBucketName(bh.dir) // Give PutObject() the read end of the pipe. @@ -120,7 +106,7 @@ func (bh *CephBackupHandle) AddFile(ctx context.Context, filename string, filesi // Signal the writer that an error occurred, in case it's not done writing yet. reader.CloseWithError(err) // In case the error happened after the writer finished, we need to remember it. - bh.RecordError(err) + bh.RecordError(filename, err) } }() // Give our caller the write end of the pipe. diff --git a/go/vt/mysqlctl/errors/errors.go b/go/vt/mysqlctl/errors/errors.go new file mode 100644 index 00000000000..02485901e50 --- /dev/null +++ b/go/vt/mysqlctl/errors/errors.go @@ -0,0 +1,106 @@ +/* +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 errors + +import ( + "errors" + "strings" + "sync" +) + +type BackupErrorRecorder interface { + RecordError(string, error) + HasErrors() bool + Error() error + GetFailedFiles() []string + ResetErrorForFile(string) +} + +// PerFileErrorRecorder records errors and group them by filename. +// This is particularly useful when processing several files at the same time +// and wanting to know which files failed. +type PerFileErrorRecorder struct { + mu sync.Mutex + errors map[string][]error +} + +// RecordError records a possible error: +// - does nothing if err is nil +func (pfer *PerFileErrorRecorder) RecordError(filename string, err error) { + if err == nil { + return + } + + pfer.mu.Lock() + defer pfer.mu.Unlock() + + if pfer.errors == nil { + pfer.errors = make(map[string][]error, 1) + } + pfer.errors[filename] = append(pfer.errors[filename], err) +} + +// HasErrors returns true if we ever recorded an error +func (pfer *PerFileErrorRecorder) HasErrors() bool { + pfer.mu.Lock() + defer pfer.mu.Unlock() + return len(pfer.errors) > 0 +} + +// Error returns all the errors that were recorded +func (pfer *PerFileErrorRecorder) Error() error { + pfer.mu.Lock() + defer pfer.mu.Unlock() + if pfer.errors == nil { + return nil + } + + var errs []string + for _, fileErrs := range pfer.errors { + for _, err := range fileErrs { + errs = append(errs, err.Error()) + } + } + if len(errs) == 0 { + return nil + } + return errors.New(strings.Join(errs, "; ")) +} + +// GetFailedFiles returns a slice of filenames, each of this file have at least 1 error. +func (pfer *PerFileErrorRecorder) GetFailedFiles() []string { + pfer.mu.Lock() + defer pfer.mu.Unlock() + if pfer.errors == nil { + return nil + } + files := make([]string, 0, len(pfer.errors)) + for filename := range pfer.errors { + files = append(files, filename) + } + return files +} + +// ResetErrorForFile removes all the errors of a given file. +func (pfer *PerFileErrorRecorder) ResetErrorForFile(filename string) { + pfer.mu.Lock() + defer pfer.mu.Unlock() + if pfer.errors == nil { + return + } + delete(pfer.errors, filename) +} diff --git a/go/vt/mysqlctl/fakebackupstorage.go b/go/vt/mysqlctl/fakebackupstorage.go index 75587191157..582b422cf58 100644 --- a/go/vt/mysqlctl/fakebackupstorage.go +++ b/go/vt/mysqlctl/fakebackupstorage.go @@ -21,20 +21,21 @@ import ( "fmt" "io" - "vitess.io/vitess/go/vt/concurrency" "vitess.io/vitess/go/vt/mysqlctl/backupstorage" + "vitess.io/vitess/go/vt/mysqlctl/errors" ) type FakeBackupHandle struct { Dir string NameV string ReadOnly bool - Errors concurrency.AllErrorRecorder + errors.PerFileErrorRecorder AbortBackupCalls []context.Context AbortBackupReturn error AddFileCalls []FakeBackupHandleAddFileCall AddFileReturn FakeBackupHandleAddFileReturn + AddFileReturnF func(filename string) FakeBackupHandleAddFileReturn EndBackupCalls []context.Context EndBackupReturn error ReadFileCalls []FakeBackupHandleReadFileCall @@ -57,18 +58,6 @@ type FakeBackupHandleReadFileCall struct { Filename string } -func (fbh *FakeBackupHandle) RecordError(err error) { - fbh.Errors.RecordError(err) -} - -func (fbh *FakeBackupHandle) HasErrors() bool { - return fbh.Errors.HasErrors() -} - -func (fbh *FakeBackupHandle) Error() error { - return fbh.Errors.Error() -} - func (fbh *FakeBackupHandle) Directory() string { return fbh.Dir } @@ -79,6 +68,11 @@ func (fbh *FakeBackupHandle) Name() string { func (fbh *FakeBackupHandle) AddFile(ctx context.Context, filename string, filesize int64) (io.WriteCloser, error) { fbh.AddFileCalls = append(fbh.AddFileCalls, FakeBackupHandleAddFileCall{ctx, filename, filesize}) + + if fbh.AddFileReturnF != nil { + r := fbh.AddFileReturnF(filename) + return r.WriteCloser, r.Err + } return fbh.AddFileReturn.WriteCloser, fbh.AddFileReturn.Err } diff --git a/go/vt/mysqlctl/filebackupstorage/file.go b/go/vt/mysqlctl/filebackupstorage/file.go index 99148d9169b..bd73c55e70c 100644 --- a/go/vt/mysqlctl/filebackupstorage/file.go +++ b/go/vt/mysqlctl/filebackupstorage/file.go @@ -27,8 +27,9 @@ import ( "github.com/spf13/pflag" + "vitess.io/vitess/go/vt/mysqlctl/errors" + "vitess.io/vitess/go/ioutil" - "vitess.io/vitess/go/vt/concurrency" stats "vitess.io/vitess/go/vt/mysqlctl/backupstats" "vitess.io/vitess/go/vt/mysqlctl/backupstorage" "vitess.io/vitess/go/vt/servenv" @@ -59,7 +60,7 @@ type FileBackupHandle struct { dir string name string readOnly bool - errors concurrency.AllErrorRecorder + errors.PerFileErrorRecorder } func NewBackupHandle( @@ -79,21 +80,6 @@ func NewBackupHandle( } } -// RecordError is part of the concurrency.ErrorRecorder interface. -func (fbh *FileBackupHandle) RecordError(err error) { - fbh.errors.RecordError(err) -} - -// HasErrors is part of the concurrency.ErrorRecorder interface. -func (fbh *FileBackupHandle) HasErrors() bool { - return fbh.errors.HasErrors() -} - -// Error is part of the concurrency.ErrorRecorder interface. -func (fbh *FileBackupHandle) Error() error { - return fbh.errors.Error() -} - // Directory is part of the BackupHandle interface func (fbh *FileBackupHandle) Directory() string { return fbh.dir diff --git a/go/vt/mysqlctl/gcsbackupstorage/gcs.go b/go/vt/mysqlctl/gcsbackupstorage/gcs.go index 814395a225a..adecbb9bbba 100644 --- a/go/vt/mysqlctl/gcsbackupstorage/gcs.go +++ b/go/vt/mysqlctl/gcsbackupstorage/gcs.go @@ -32,8 +32,9 @@ import ( "google.golang.org/api/iterator" "google.golang.org/api/option" + "vitess.io/vitess/go/vt/mysqlctl/errors" + "vitess.io/vitess/go/trace" - "vitess.io/vitess/go/vt/concurrency" "vitess.io/vitess/go/vt/mysqlctl/backupstorage" "vitess.io/vitess/go/vt/servenv" ) @@ -65,22 +66,7 @@ type GCSBackupHandle struct { dir string name string readOnly bool - errors concurrency.AllErrorRecorder -} - -// RecordError is part of the concurrency.ErrorRecorder interface. -func (bh *GCSBackupHandle) RecordError(err error) { - bh.errors.RecordError(err) -} - -// HasErrors is part of the concurrency.ErrorRecorder interface. -func (bh *GCSBackupHandle) HasErrors() bool { - return bh.errors.HasErrors() -} - -// Error is part of the concurrency.ErrorRecorder interface. -func (bh *GCSBackupHandle) Error() error { - return bh.errors.Error() + errors.PerFileErrorRecorder } // Directory implements BackupHandle. diff --git a/go/vt/mysqlctl/mysqld.go b/go/vt/mysqlctl/mysqld.go index 72c1d8f6658..d7435705a8a 100644 --- a/go/vt/mysqlctl/mysqld.go +++ b/go/vt/mysqlctl/mysqld.go @@ -47,6 +47,7 @@ import ( "vitess.io/vitess/config" "vitess.io/vitess/go/mysql" "vitess.io/vitess/go/mysql/sqlerror" + "vitess.io/vitess/go/osutil" "vitess.io/vitess/go/protoutil" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/dbconfigs" @@ -1332,18 +1333,7 @@ func hostMetrics(ctx context.Context, cnf *Mycnf) (*mysqlctlpb.HostMetricsRespon _ = func() error { metric := newMetric("loadavg") - if runtime.GOOS != "linux" { - return withError(metric, fmt.Errorf("loadavg metric is only available on Linux")) - } - content, err := os.ReadFile("/proc/loadavg") - if err != nil { - return withError(metric, err) - } - fields := strings.Fields(string(content)) - if len(fields) == 0 { - return withError(metric, fmt.Errorf("unexpected /proc/loadavg content")) - } - loadAvg, err := strconv.ParseFloat(fields[0], 64) + loadAvg, err := osutil.LoadAvg() if err != nil { return withError(metric, err) } diff --git a/go/vt/mysqlctl/mysqlshellbackupengine.go b/go/vt/mysqlctl/mysqlshellbackupengine.go index b7405ce7eaa..681e62d10cd 100644 --- a/go/vt/mysqlctl/mysqlshellbackupengine.go +++ b/go/vt/mysqlctl/mysqlshellbackupengine.go @@ -56,8 +56,6 @@ var ( // disable redo logging and double write buffer mysqlShellSpeedUpRestore = false - mysqlShellBackupBinaryName = "mysqlsh" - // use when checking if we need to create the directory on the local filesystem or not. knownObjectStoreParams = []string{"s3BucketName", "osBucketName", "azureContainerName"} @@ -87,7 +85,9 @@ type MySQLShellBackupManifest struct { } func init() { - BackupRestoreEngineMap[mysqlShellBackupEngineName] = &MySQLShellBackupEngine{} + BackupRestoreEngineMap[mysqlShellBackupEngineName] = &MySQLShellBackupEngine{ + binaryName: "mysqlsh", + } for _, cmd := range []string{"vtcombo", "vttablet", "vtbackup", "vttestserver", "vtctldclient"} { servenv.OnParseFor(cmd, registerMysqlShellBackupEngineFlags) @@ -106,6 +106,7 @@ func registerMysqlShellBackupEngineFlags(fs *pflag.FlagSet) { // MySQLShellBackupEngine encapsulates the logic to implement the restoration // of a mysql-shell based backup. type MySQLShellBackupEngine struct { + binaryName string } const ( @@ -164,41 +165,37 @@ func (be *MySQLShellBackupEngine) ExecuteBackup(ctx context.Context, params Back return BackupUnusable, vterrors.Wrap(err, "failed to fetch position") } - cmd := exec.CommandContext(ctx, mysqlShellBackupBinaryName, args...) + cmd := exec.CommandContext(ctx, be.binaryName, args...) params.Logger.Infof("running %s", cmd.String()) - cmdOut, err := cmd.StdoutPipe() - if err != nil { - return BackupUnusable, vterrors.Wrap(err, "cannot create stdout pipe") - } - cmdOriginalErr, err := cmd.StderrPipe() - if err != nil { - return BackupUnusable, vterrors.Wrap(err, "cannot create stderr pipe") - } - if err := cmd.Start(); err != nil { - return BackupUnusable, vterrors.Wrap(err, "can't start mysqlshell") - } + stdoutReader, stdoutWriter := io.Pipe() + stderrReader, stderrWriter := io.Pipe() + lockWaiterReader, lockWaiterWriter := io.Pipe() - pipeReader, pipeWriter := io.Pipe() - cmdErr := io.TeeReader(cmdOriginalErr, pipeWriter) + cmd.Stdout = stdoutWriter + cmd.Stderr = stderrWriter + combinedErr := io.TeeReader(stderrReader, lockWaiterWriter) cmdWg := &sync.WaitGroup{} cmdWg.Add(3) - go releaseReadLock(ctx, pipeReader, params, cmdWg, lockAcquired) - go scanLinesToLogger(mysqlShellBackupEngineName+" stdout", cmdOut, params.Logger, cmdWg.Done) - go scanLinesToLogger(mysqlShellBackupEngineName+" stderr", cmdErr, params.Logger, cmdWg.Done) + go releaseReadLock(ctx, lockWaiterReader, params, cmdWg, lockAcquired) + go scanLinesToLogger(mysqlShellBackupEngineName+" stdout", stdoutReader, params.Logger, cmdWg.Done) + go scanLinesToLogger(mysqlShellBackupEngineName+" stderr", combinedErr, params.Logger, cmdWg.Done) - // Get exit status. - if err := cmd.Wait(); err != nil { - pipeWriter.Close() // make sure we close the writer so the goroutines above will complete. - return BackupUnusable, vterrors.Wrap(err, mysqlShellBackupEngineName+" failed") - } + // we run the command, wait for it to complete and close all pipes so the goroutines can complete on their own. + // after that we can process if an error has happened or not. + err = cmd.Run() - // close the pipeWriter and wait for the goroutines to have read all the logs - pipeWriter.Close() + stdoutWriter.Close() + stderrWriter.Close() + lockWaiterWriter.Close() cmdWg.Wait() + if err != nil { + return BackupUnusable, vterrors.Wrap(err, mysqlShellBackupEngineName+" failed") + } + // open the MANIFEST params.Logger.Infof("Writing backup MANIFEST") mwc, err := bh.AddFile(ctx, backupManifestFileName, backupstorage.FileSizeUnknown) @@ -371,7 +368,7 @@ func (be *MySQLShellBackupEngine) ExecuteRestore(ctx context.Context, params Res if err := cmd.Wait(); err != nil { return nil, vterrors.Wrap(err, mysqlShellBackupEngineName+" failed") } - params.Logger.Infof("%s completed successfully", mysqlShellBackupBinaryName) + params.Logger.Infof("%s completed successfully", be.binaryName) // disable local_infile now that the restore is done. err = params.Mysqld.ExecuteSuperQuery(ctx, "SET GLOBAL LOCAL_INFILE=0") diff --git a/go/vt/mysqlctl/mysqlshellbackupengine_test.go b/go/vt/mysqlctl/mysqlshellbackupengine_test.go index 67f27b5382e..80491ec2afc 100644 --- a/go/vt/mysqlctl/mysqlshellbackupengine_test.go +++ b/go/vt/mysqlctl/mysqlshellbackupengine_test.go @@ -325,13 +325,10 @@ func generateTestFile(t *testing.T, name, contents string) { // during ExecuteBackup(), even if the backup didn't succeed. func TestMySQLShellBackupEngine_ExecuteBackup_ReleaseLock(t *testing.T) { originalLocation := mysqlShellBackupLocation - originalBinary := mysqlShellBackupBinaryName mysqlShellBackupLocation = "logical" - mysqlShellBackupBinaryName = path.Join(t.TempDir(), "test.sh") - defer func() { // restore the original values. + defer func() { // restore the original value. mysqlShellBackupLocation = originalLocation - mysqlShellBackupBinaryName = originalBinary }() logger := logutil.NewMemoryLogger() @@ -340,7 +337,6 @@ func TestMySQLShellBackupEngine_ExecuteBackup_ReleaseLock(t *testing.T) { mysql := NewFakeMysqlDaemon(fakedb) defer mysql.Close() - be := &MySQLShellBackupEngine{} params := BackupParams{ TabletAlias: "test", Logger: logger, @@ -351,6 +347,7 @@ func TestMySQLShellBackupEngine_ExecuteBackup_ReleaseLock(t *testing.T) { } t.Run("lock released if we see the mysqlsh lock being acquired", func(t *testing.T) { + be := &MySQLShellBackupEngine{binaryName: path.Join(t.TempDir(), "mysqlsh.sh")} logger.Clear() manifestBuffer := ioutil.NewBytesBufferWriter() bs.StartBackupReturn.BackupHandle = &FakeBackupHandle{ @@ -359,7 +356,8 @@ func TestMySQLShellBackupEngine_ExecuteBackup_ReleaseLock(t *testing.T) { } // this simulates mysql shell completing without any issues. - generateTestFile(t, mysqlShellBackupBinaryName, fmt.Sprintf("#!/bin/bash\n>&2 echo %s", mysqlShellLockMessage)) + generateTestFile(t, be.binaryName, fmt.Sprintf( + "#!/bin/bash\n>&2 echo %s; echo \"backup completed\"; sleep 0.01", mysqlShellLockMessage)) bh, err := bs.StartBackup(context.Background(), t.TempDir(), t.Name()) require.NoError(t, err) @@ -380,7 +378,8 @@ func TestMySQLShellBackupEngine_ExecuteBackup_ReleaseLock(t *testing.T) { "failed to release the global lock after mysqlsh") }) - t.Run("lock released if when we don't see mysqlsh released it", func(t *testing.T) { + t.Run("lock released if we don't see mysqlsh release it", func(t *testing.T) { + be := &MySQLShellBackupEngine{binaryName: path.Join(t.TempDir(), "mysqlsh.sh")} mysql.GlobalReadLock = false // clear lock status. logger.Clear() manifestBuffer := ioutil.NewBytesBufferWriter() @@ -390,7 +389,7 @@ func TestMySQLShellBackupEngine_ExecuteBackup_ReleaseLock(t *testing.T) { } // this simulates mysqlshell completing, but we don't see the message that is released its lock. - generateTestFile(t, mysqlShellBackupBinaryName, "#!/bin/bash\nexit 0") + generateTestFile(t, be.binaryName, "#!/bin/bash\nexit 0") bh, err := bs.StartBackup(context.Background(), t.TempDir(), t.Name()) require.NoError(t, err) @@ -407,6 +406,7 @@ func TestMySQLShellBackupEngine_ExecuteBackup_ReleaseLock(t *testing.T) { }) t.Run("lock released when backup fails", func(t *testing.T) { + be := &MySQLShellBackupEngine{binaryName: path.Join(t.TempDir(), "mysqlsh.sh")} mysql.GlobalReadLock = false // clear lock status. logger.Clear() manifestBuffer := ioutil.NewBytesBufferWriter() @@ -416,7 +416,7 @@ func TestMySQLShellBackupEngine_ExecuteBackup_ReleaseLock(t *testing.T) { } // this simulates the backup process failing. - generateTestFile(t, mysqlShellBackupBinaryName, "#!/bin/bash\nexit 1") + generateTestFile(t, be.binaryName, "#!/bin/bash\nexit 1") bh, err := bs.StartBackup(context.Background(), t.TempDir(), t.Name()) require.NoError(t, err) diff --git a/go/vt/mysqlctl/s3backupstorage/s3.go b/go/vt/mysqlctl/s3backupstorage/s3.go index b3a8117aafa..97861e83729 100644 --- a/go/vt/mysqlctl/s3backupstorage/s3.go +++ b/go/vt/mysqlctl/s3backupstorage/s3.go @@ -40,6 +40,7 @@ import ( "time" "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/aws/retry" "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/feature/s3/manager" "github.com/aws/aws-sdk-go-v2/service/s3" @@ -48,7 +49,8 @@ import ( "github.com/aws/smithy-go/middleware" "github.com/spf13/pflag" - "vitess.io/vitess/go/vt/concurrency" + errorsbackup "vitess.io/vitess/go/vt/mysqlctl/errors" + "vitess.io/vitess/go/vt/log" stats "vitess.io/vitess/go/vt/mysqlctl/backupstats" "vitess.io/vitess/go/vt/mysqlctl/backupstorage" @@ -144,8 +146,8 @@ type S3BackupHandle struct { dir string name string readOnly bool - errors concurrency.AllErrorRecorder waitGroup sync.WaitGroup + errorsbackup.PerFileErrorRecorder } // Directory is part of the backupstorage.BackupHandle interface. @@ -158,39 +160,23 @@ func (bh *S3BackupHandle) Name() string { return bh.name } -// RecordError is part of the concurrency.ErrorRecorder interface. -func (bh *S3BackupHandle) RecordError(err error) { - bh.errors.RecordError(err) -} - -// HasErrors is part of the concurrency.ErrorRecorder interface. -func (bh *S3BackupHandle) HasErrors() bool { - return bh.errors.HasErrors() -} - -// Error is part of the concurrency.ErrorRecorder interface. -func (bh *S3BackupHandle) Error() error { - return bh.errors.Error() -} - // AddFile is part of the backupstorage.BackupHandle interface. func (bh *S3BackupHandle) AddFile(ctx context.Context, filename string, filesize int64) (io.WriteCloser, error) { if bh.readOnly { return nil, fmt.Errorf("AddFile cannot be called on read-only backup") } - // Calculate s3 upload part size using the source filesize - partSizeBytes := manager.DefaultUploadPartSize - if filesize > 0 { - minimumPartSize := float64(filesize) / float64(manager.MaxUploadParts) - // Round up to ensure large enough partsize - calculatedPartSizeBytes := int64(math.Ceil(minimumPartSize)) - if calculatedPartSizeBytes > partSizeBytes { - partSizeBytes = calculatedPartSizeBytes - } - } + partSizeBytes := calculateUploadPartSize(filesize) reader, writer := io.Pipe() + bh.handleAddFile(ctx, filename, partSizeBytes, reader, func(err error) { + reader.CloseWithError(err) + }) + + return writer, nil +} + +func (bh *S3BackupHandle) handleAddFile(ctx context.Context, filename string, partSizeBytes int64, reader io.Reader, closer func(error)) { bh.waitGroup.Add(1) go func() { @@ -221,12 +207,24 @@ func (bh *S3BackupHandle) AddFile(ctx context.Context, filename string, filesize }) }) if err != nil { - reader.CloseWithError(err) - bh.RecordError(err) + closer(err) + bh.RecordError(filename, err) } }() +} - return writer, nil +func calculateUploadPartSize(filesize int64) int64 { + // Calculate s3 upload part size using the source filesize + partSizeBytes := manager.DefaultUploadPartSize + if filesize > 0 { + minimumPartSize := float64(filesize) / float64(manager.MaxUploadParts) + // Round up to ensure large enough partsize + calculatedPartSizeBytes := int64(math.Ceil(minimumPartSize)) + if calculatedPartSizeBytes > partSizeBytes { + partSizeBytes = calculatedPartSizeBytes + } + } + return partSizeBytes } // EndBackup is part of the backupstorage.BackupHandle interface. @@ -505,13 +503,24 @@ func (bs *S3BackupStorage) client() (*s3.Client, error) { return nil, err } - bs._client = s3.NewFromConfig(cfg, func(o *s3.Options) { - o.UsePathStyle = forcePath - if retryCount >= 0 { - o.RetryMaxAttempts = retryCount - o.Retryer = &ClosedConnectionRetryer{} - } - }, s3.WithEndpointResolverV2(newEndpointResolver())) + options := []func(options *s3.Options){ + func(o *s3.Options) { + o.UsePathStyle = forcePath + if retryCount >= 0 { + o.RetryMaxAttempts = retryCount + o.Retryer = &ClosedConnectionRetryer{ + awsRetryer: retry.NewStandard(func(options *retry.StandardOptions) { + options.MaxAttempts = retryCount + }), + } + } + }, + } + if endpoint != "" { + options = append(options, s3.WithEndpointResolverV2(newEndpointResolver())) + } + + bs._client = s3.NewFromConfig(cfg, options...) if len(bucket) == 0 { return nil, fmt.Errorf("--s3_backup_storage_bucket required") diff --git a/go/vt/mysqlctl/s3backupstorage/s3_mock.go b/go/vt/mysqlctl/s3backupstorage/s3_mock.go new file mode 100644 index 00000000000..f244c4d63b1 --- /dev/null +++ b/go/vt/mysqlctl/s3backupstorage/s3_mock.go @@ -0,0 +1,223 @@ +/* +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 s3backupstorage + +import ( + "context" + "errors" + "fmt" + "io" + "sync" + + "vitess.io/vitess/go/vt/logutil" + "vitess.io/vitess/go/vt/mysqlctl/backupstats" + "vitess.io/vitess/go/vt/mysqlctl/backupstorage" +) + +type FakeS3BackupHandle struct { + *S3BackupHandle + + AddFileReturnF func(s3 *S3BackupHandle, ctx context.Context, filename string, filesize int64, firstAdd bool) (io.WriteCloser, error) + ReadFileReturnF func(s3 *S3BackupHandle, ctx context.Context, filename string, firstRead bool) (io.ReadCloser, error) + + mu sync.Mutex + addPerFile map[string]int + readPerFile map[string]int +} + +type FakeConfig struct { + Region string + Endpoint string + Bucket string + ForcePath bool +} + +func InitFlag(cfg FakeConfig) { + region = cfg.Region + endpoint = cfg.Endpoint + bucket = cfg.Bucket + forcePath = cfg.ForcePath +} + +func NewFakeS3BackupHandle(ctx context.Context, dir, name string, logger logutil.Logger, stats backupstats.Stats) (*FakeS3BackupHandle, error) { + s := newS3BackupStorage() + bs := s.WithParams(backupstorage.Params{ + Logger: logger, + Stats: stats, + }) + bh, err := bs.StartBackup(ctx, dir, name) + if err != nil { + return nil, err + } + return &FakeS3BackupHandle{ + S3BackupHandle: bh.(*S3BackupHandle), + addPerFile: make(map[string]int), + readPerFile: make(map[string]int), + }, nil +} + +func NewFakeS3RestoreHandle(ctx context.Context, dir string, logger logutil.Logger, stats backupstats.Stats) (*FakeS3BackupHandle, error) { + s := newS3BackupStorage() + bs := s.WithParams(backupstorage.Params{ + Logger: logger, + Stats: stats, + }) + bhs, err := bs.ListBackups(ctx, dir) + if err != nil { + return nil, err + } + return &FakeS3BackupHandle{ + S3BackupHandle: bhs[0].(*S3BackupHandle), + addPerFile: make(map[string]int), + readPerFile: make(map[string]int), + }, nil +} + +func (fbh *FakeS3BackupHandle) Directory() string { + return fbh.S3BackupHandle.Directory() +} + +func (fbh *FakeS3BackupHandle) Name() string { + return fbh.S3BackupHandle.Name() +} + +func (fbh *FakeS3BackupHandle) AddFile(ctx context.Context, filename string, filesize int64) (io.WriteCloser, error) { + fbh.mu.Lock() + defer func() { + fbh.addPerFile[filename] += 1 + fbh.mu.Unlock() + }() + + if fbh.AddFileReturnF != nil { + return fbh.AddFileReturnF(fbh.S3BackupHandle, ctx, filename, filesize, fbh.addPerFile[filename] == 0) + } + return fbh.S3BackupHandle.AddFile(ctx, filename, filesize) +} + +func (fbh *FakeS3BackupHandle) EndBackup(ctx context.Context) error { + return fbh.S3BackupHandle.EndBackup(ctx) +} + +func (fbh *FakeS3BackupHandle) AbortBackup(ctx context.Context) error { + return fbh.S3BackupHandle.AbortBackup(ctx) +} + +func (fbh *FakeS3BackupHandle) ReadFile(ctx context.Context, filename string) (io.ReadCloser, error) { + fbh.mu.Lock() + defer func() { + fbh.readPerFile[filename] += 1 + fbh.mu.Unlock() + }() + + if fbh.ReadFileReturnF != nil { + return fbh.ReadFileReturnF(fbh.S3BackupHandle, ctx, filename, fbh.readPerFile[filename] == 0) + } + return fbh.S3BackupHandle.ReadFile(ctx, filename) +} + +func (fbh *FakeS3BackupHandle) RecordError(s string, err error) { + fbh.S3BackupHandle.RecordError(s, err) +} + +func (fbh *FakeS3BackupHandle) HasErrors() bool { + return fbh.S3BackupHandle.HasErrors() +} + +func (fbh *FakeS3BackupHandle) Error() error { + return fbh.S3BackupHandle.Error() +} + +func (fbh *FakeS3BackupHandle) GetFailedFiles() []string { + return fbh.S3BackupHandle.GetFailedFiles() +} + +func (fbh *FakeS3BackupHandle) ResetErrorForFile(s string) { + fbh.S3BackupHandle.ResetErrorForFile(s) +} + +type failReadPipeReader struct { + *io.PipeReader +} + +func (fwr *failReadPipeReader) Read(p []byte) (n int, err error) { + return 0, errors.New("failing read") +} + +func FailFirstWrite(s3bh *S3BackupHandle, ctx context.Context, filename string, filesize int64, firstAdd bool) (io.WriteCloser, error) { + if s3bh.readOnly { + return nil, fmt.Errorf("AddFile cannot be called on read-only backup") + } + + partSizeBytes := calculateUploadPartSize(filesize) + reader, writer := io.Pipe() + r := io.Reader(reader) + + if firstAdd { + r = &failReadPipeReader{PipeReader: reader} + } + + s3bh.handleAddFile(ctx, filename, partSizeBytes, r, func(err error) { + reader.CloseWithError(err) + }) + return writer, nil +} + +func FailAllWrites(s3bh *S3BackupHandle, ctx context.Context, filename string, filesize int64, _ bool) (io.WriteCloser, error) { + if s3bh.readOnly { + return nil, fmt.Errorf("AddFile cannot be called on read-only backup") + } + + partSizeBytes := calculateUploadPartSize(filesize) + reader, writer := io.Pipe() + r := &failReadPipeReader{PipeReader: reader} + + s3bh.handleAddFile(ctx, filename, partSizeBytes, r, func(err error) { + r.PipeReader.CloseWithError(err) + }) + return writer, nil +} + +type failRead struct{} + +func (fr *failRead) Read(p []byte) (n int, err error) { + return 0, errors.New("failing read") +} + +func (fr *failRead) Close() error { + return nil +} + +func FailFirstRead(s3bh *S3BackupHandle, ctx context.Context, filename string, firstRead bool) (io.ReadCloser, error) { + rc, err := s3bh.ReadFile(ctx, filename) + if err != nil { + return nil, err + } + if firstRead { + return &failRead{}, nil + } + return rc, nil +} + +// FailAllReadExpectManifest is used to fail every attempt at reading a file from S3. +// Only the MANIFEST file is allowed to be read, because otherwise we wouldn't even try to read the normal files. +func FailAllReadExpectManifest(s3bh *S3BackupHandle, ctx context.Context, filename string, _ bool) (io.ReadCloser, error) { + const manifestFileName = "MANIFEST" + if filename == manifestFileName { + return s3bh.ReadFile(ctx, filename) + } + return &failRead{}, nil +} diff --git a/go/vt/proto/replicationdata/replicationdata.pb.go b/go/vt/proto/replicationdata/replicationdata.pb.go index 4568ae2d437..d5462e1ea2b 100644 --- a/go/vt/proto/replicationdata/replicationdata.pb.go +++ b/go/vt/proto/replicationdata/replicationdata.pb.go @@ -114,6 +114,7 @@ type Status struct { HasReplicationFilters bool `protobuf:"varint,22,opt,name=has_replication_filters,json=hasReplicationFilters,proto3" json:"has_replication_filters,omitempty"` SslAllowed bool `protobuf:"varint,23,opt,name=ssl_allowed,json=sslAllowed,proto3" json:"ssl_allowed,omitempty"` ReplicationLagUnknown bool `protobuf:"varint,24,opt,name=replication_lag_unknown,json=replicationLagUnknown,proto3" json:"replication_lag_unknown,omitempty"` + BackupRunning bool `protobuf:"varint,25,opt,name=backup_running,json=backupRunning,proto3" json:"backup_running,omitempty"` } func (x *Status) Reset() { @@ -300,6 +301,13 @@ func (x *Status) GetReplicationLagUnknown() bool { return false } +func (x *Status) GetBackupRunning() bool { + if x != nil { + return x.BackupRunning + } + return false +} + // Configuration holds replication configuration information gathered from performance_schema and global variables. type Configuration struct { state protoimpl.MessageState @@ -692,7 +700,7 @@ var File_replicationdata_proto protoreflect.FileDescriptor var file_replicationdata_proto_rawDesc = []byte{ 0x0a, 0x15, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0f, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x64, 0x61, 0x74, 0x61, 0x22, 0x96, 0x07, 0x0a, 0x06, 0x53, 0x74, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x64, 0x61, 0x74, 0x61, 0x22, 0xbd, 0x07, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 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, 0x36, 0x0a, 0x17, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6c, @@ -749,105 +757,108 @@ var file_replicationdata_proto_rawDesc = []byte{ 0x17, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6c, 0x61, 0x67, 0x5f, 0x75, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x18, 0x18, 0x20, 0x01, 0x28, 0x08, 0x52, 0x15, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x61, 0x67, 0x55, 0x6e, - 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x4a, 0x04, 0x08, 0x03, 0x10, - 0x04, 0x22, 0x6e, 0x0a, 0x0d, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x12, 0x2d, 0x0a, 0x12, 0x68, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x5f, - 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x01, 0x52, 0x11, - 0x68, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, - 0x6c, 0x12, 0x2e, 0x0a, 0x13, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x5f, 0x6e, 0x65, 0x74, - 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x11, - 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x4e, 0x65, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, - 0x74, 0x22, 0x77, 0x0a, 0x15, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x2f, 0x0a, 0x06, 0x62, 0x65, - 0x66, 0x6f, 0x72, 0x65, 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, 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x12, 0x2d, 0x0a, 0x05, 0x61, - 0x66, 0x74, 0x65, 0x72, 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, 0x61, 0x66, 0x74, 0x65, 0x72, 0x22, 0x71, 0x0a, 0x0d, 0x50, 0x72, - 0x69, 0x6d, 0x61, 0x72, 0x79, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 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, 0x23, 0x0a, 0x0d, 0x66, 0x69, 0x6c, 0x65, 0x5f, - 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, - 0x66, 0x69, 0x6c, 0x65, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, - 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x75, 0x75, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0a, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x55, 0x75, 0x69, 0x64, 0x22, 0xc8, 0x08, - 0x0a, 0x0a, 0x46, 0x75, 0x6c, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1b, 0x0a, 0x09, - 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, - 0x08, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x49, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x65, 0x72, - 0x76, 0x65, 0x72, 0x5f, 0x75, 0x75, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, - 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x55, 0x75, 0x69, 0x64, 0x12, 0x46, 0x0a, 0x12, 0x72, 0x65, - 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x18, 0x03, 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, - 0x11, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x12, 0x45, 0x0a, 0x0e, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x5f, 0x73, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x18, 0x04, 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, 0x12, 0x1f, 0x0a, 0x0b, 0x67, 0x74, 0x69, - 0x64, 0x5f, 0x70, 0x75, 0x72, 0x67, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, - 0x67, 0x74, 0x69, 0x64, 0x50, 0x75, 0x72, 0x67, 0x65, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, - 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, - 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x27, 0x0a, 0x0f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x5f, - 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x76, - 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x1b, 0x0a, - 0x09, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x08, 0x72, 0x65, 0x61, 0x64, 0x4f, 0x6e, 0x6c, 0x79, 0x12, 0x1b, 0x0a, 0x09, 0x67, 0x74, - 0x69, 0x64, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x67, - 0x74, 0x69, 0x64, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x62, 0x69, 0x6e, 0x6c, 0x6f, - 0x67, 0x5f, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, - 0x62, 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0x28, 0x0a, 0x10, - 0x62, 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x5f, 0x72, 0x6f, 0x77, 0x5f, 0x69, 0x6d, 0x61, 0x67, 0x65, - 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x62, 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x52, 0x6f, - 0x77, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x12, 0x26, 0x0a, 0x0f, 0x6c, 0x6f, 0x67, 0x5f, 0x62, 0x69, - 0x6e, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x0d, 0x6c, 0x6f, 0x67, 0x42, 0x69, 0x6e, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x2e, - 0x0a, 0x13, 0x6c, 0x6f, 0x67, 0x5f, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x5f, 0x75, 0x70, - 0x64, 0x61, 0x74, 0x65, 0x73, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x6c, 0x6f, 0x67, - 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x12, 0x39, - 0x0a, 0x19, 0x73, 0x65, 0x6d, 0x69, 0x5f, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x70, 0x72, 0x69, 0x6d, - 0x61, 0x72, 0x79, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x0e, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x16, 0x73, 0x65, 0x6d, 0x69, 0x53, 0x79, 0x6e, 0x63, 0x50, 0x72, 0x69, 0x6d, 0x61, - 0x72, 0x79, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x39, 0x0a, 0x19, 0x73, 0x65, 0x6d, - 0x69, 0x5f, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x5f, 0x65, - 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, 0x73, 0x65, - 0x6d, 0x69, 0x53, 0x79, 0x6e, 0x63, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x45, 0x6e, 0x61, - 0x62, 0x6c, 0x65, 0x64, 0x12, 0x37, 0x0a, 0x18, 0x73, 0x65, 0x6d, 0x69, 0x5f, 0x73, 0x79, 0x6e, - 0x63, 0x5f, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x18, 0x10, 0x20, 0x01, 0x28, 0x08, 0x52, 0x15, 0x73, 0x65, 0x6d, 0x69, 0x53, 0x79, 0x6e, 0x63, - 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x37, 0x0a, - 0x18, 0x73, 0x65, 0x6d, 0x69, 0x5f, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x72, 0x65, 0x70, 0x6c, 0x69, - 0x63, 0x61, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x11, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x15, 0x73, 0x65, 0x6d, 0x69, 0x53, 0x79, 0x6e, 0x63, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, - 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x39, 0x0a, 0x19, 0x73, 0x65, 0x6d, 0x69, 0x5f, 0x73, - 0x79, 0x6e, 0x63, 0x5f, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x5f, 0x63, 0x6c, 0x69, 0x65, - 0x6e, 0x74, 0x73, 0x18, 0x12, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x16, 0x73, 0x65, 0x6d, 0x69, 0x53, - 0x79, 0x6e, 0x63, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, - 0x73, 0x12, 0x39, 0x0a, 0x19, 0x73, 0x65, 0x6d, 0x69, 0x5f, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x70, - 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x13, - 0x20, 0x01, 0x28, 0x04, 0x52, 0x16, 0x73, 0x65, 0x6d, 0x69, 0x53, 0x79, 0x6e, 0x63, 0x50, 0x72, - 0x69, 0x6d, 0x61, 0x72, 0x79, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x45, 0x0a, 0x20, - 0x73, 0x65, 0x6d, 0x69, 0x5f, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x77, 0x61, 0x69, 0x74, 0x5f, 0x66, - 0x6f, 0x72, 0x5f, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, - 0x18, 0x14, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x1b, 0x73, 0x65, 0x6d, 0x69, 0x53, 0x79, 0x6e, 0x63, - 0x57, 0x61, 0x69, 0x74, 0x46, 0x6f, 0x72, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x43, 0x6f, - 0x75, 0x6e, 0x74, 0x12, 0x26, 0x0a, 0x0f, 0x73, 0x75, 0x70, 0x65, 0x72, 0x5f, 0x72, 0x65, 0x61, - 0x64, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x15, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x73, 0x75, - 0x70, 0x65, 0x72, 0x52, 0x65, 0x61, 0x64, 0x4f, 0x6e, 0x6c, 0x79, 0x12, 0x5b, 0x0a, 0x19, 0x72, - 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x16, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, - 0x2e, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x64, 0x61, 0x74, 0x61, - 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x18, - 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2a, 0x3b, 0x0a, 0x13, 0x53, 0x74, 0x6f, 0x70, - 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x6f, 0x64, 0x65, 0x12, - 0x12, 0x0a, 0x0e, 0x49, 0x4f, 0x41, 0x4e, 0x44, 0x53, 0x51, 0x4c, 0x54, 0x48, 0x52, 0x45, 0x41, - 0x44, 0x10, 0x00, 0x12, 0x10, 0x0a, 0x0c, 0x49, 0x4f, 0x54, 0x48, 0x52, 0x45, 0x41, 0x44, 0x4f, - 0x4e, 0x4c, 0x59, 0x10, 0x01, 0x42, 0x2e, 0x5a, 0x2c, 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, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x64, 0x61, 0x74, 0x61, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x12, 0x25, 0x0a, 0x0e, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x5f, + 0x72, 0x75, 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x18, 0x19, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x62, + 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x75, 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x4a, 0x04, 0x08, 0x02, + 0x10, 0x03, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x22, 0x6e, 0x0a, 0x0d, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2d, 0x0a, 0x12, 0x68, 0x65, 0x61, + 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x01, 0x52, 0x11, 0x68, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, + 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, 0x2e, 0x0a, 0x13, 0x72, 0x65, 0x70, 0x6c, + 0x69, 0x63, 0x61, 0x5f, 0x6e, 0x65, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x11, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x4e, 0x65, + 0x74, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x22, 0x77, 0x0a, 0x15, 0x53, 0x74, 0x6f, 0x70, + 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x12, 0x2f, 0x0a, 0x06, 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65, 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, 0x62, 0x65, 0x66, 0x6f, + 0x72, 0x65, 0x12, 0x2d, 0x0a, 0x05, 0x61, 0x66, 0x74, 0x65, 0x72, 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, 0x61, 0x66, 0x74, 0x65, + 0x72, 0x22, 0x71, 0x0a, 0x0d, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 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, 0x23, + 0x0a, 0x0d, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x66, 0x69, 0x6c, 0x65, 0x50, 0x6f, 0x73, 0x69, 0x74, + 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x75, 0x75, + 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x55, 0x75, 0x69, 0x64, 0x22, 0xc8, 0x08, 0x0a, 0x0a, 0x46, 0x75, 0x6c, 0x6c, 0x53, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x49, 0x64, + 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x75, 0x75, 0x69, 0x64, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x55, 0x75, 0x69, + 0x64, 0x12, 0x46, 0x0a, 0x12, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x03, 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, 0x11, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x45, 0x0a, 0x0e, 0x70, 0x72, 0x69, + 0x6d, 0x61, 0x72, 0x79, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x04, 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, + 0x12, 0x1f, 0x0a, 0x0b, 0x67, 0x74, 0x69, 0x64, 0x5f, 0x70, 0x75, 0x72, 0x67, 0x65, 0x64, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x67, 0x74, 0x69, 0x64, 0x50, 0x75, 0x72, 0x67, 0x65, + 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x27, 0x0a, 0x0f, 0x76, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x07, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6d, + 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x6f, 0x6e, 0x6c, + 0x79, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x72, 0x65, 0x61, 0x64, 0x4f, 0x6e, 0x6c, + 0x79, 0x12, 0x1b, 0x0a, 0x09, 0x67, 0x74, 0x69, 0x64, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x09, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x67, 0x74, 0x69, 0x64, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x23, + 0x0a, 0x0d, 0x62, 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x5f, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x18, + 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x62, 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x46, 0x6f, 0x72, + 0x6d, 0x61, 0x74, 0x12, 0x28, 0x0a, 0x10, 0x62, 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x5f, 0x72, 0x6f, + 0x77, 0x5f, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x62, + 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x52, 0x6f, 0x77, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x12, 0x26, 0x0a, + 0x0f, 0x6c, 0x6f, 0x67, 0x5f, 0x62, 0x69, 0x6e, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, + 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x6c, 0x6f, 0x67, 0x42, 0x69, 0x6e, 0x45, 0x6e, + 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x2e, 0x0a, 0x13, 0x6c, 0x6f, 0x67, 0x5f, 0x72, 0x65, 0x70, + 0x6c, 0x69, 0x63, 0x61, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x18, 0x0d, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x11, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x55, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x73, 0x12, 0x39, 0x0a, 0x19, 0x73, 0x65, 0x6d, 0x69, 0x5f, 0x73, 0x79, + 0x6e, 0x63, 0x5f, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, + 0x65, 0x64, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, 0x73, 0x65, 0x6d, 0x69, 0x53, 0x79, + 0x6e, 0x63, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, + 0x12, 0x39, 0x0a, 0x19, 0x73, 0x65, 0x6d, 0x69, 0x5f, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x72, 0x65, + 0x70, 0x6c, 0x69, 0x63, 0x61, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x0f, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x16, 0x73, 0x65, 0x6d, 0x69, 0x53, 0x79, 0x6e, 0x63, 0x52, 0x65, 0x70, + 0x6c, 0x69, 0x63, 0x61, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x37, 0x0a, 0x18, 0x73, + 0x65, 0x6d, 0x69, 0x5f, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, + 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x10, 0x20, 0x01, 0x28, 0x08, 0x52, 0x15, 0x73, + 0x65, 0x6d, 0x69, 0x53, 0x79, 0x6e, 0x63, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x53, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x12, 0x37, 0x0a, 0x18, 0x73, 0x65, 0x6d, 0x69, 0x5f, 0x73, 0x79, 0x6e, + 0x63, 0x5f, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x18, 0x11, 0x20, 0x01, 0x28, 0x08, 0x52, 0x15, 0x73, 0x65, 0x6d, 0x69, 0x53, 0x79, 0x6e, 0x63, + 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x39, 0x0a, + 0x19, 0x73, 0x65, 0x6d, 0x69, 0x5f, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x70, 0x72, 0x69, 0x6d, 0x61, + 0x72, 0x79, 0x5f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x12, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x16, 0x73, 0x65, 0x6d, 0x69, 0x53, 0x79, 0x6e, 0x63, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, + 0x79, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x39, 0x0a, 0x19, 0x73, 0x65, 0x6d, 0x69, + 0x5f, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x5f, 0x74, 0x69, + 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x13, 0x20, 0x01, 0x28, 0x04, 0x52, 0x16, 0x73, 0x65, 0x6d, + 0x69, 0x53, 0x79, 0x6e, 0x63, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x54, 0x69, 0x6d, 0x65, + 0x6f, 0x75, 0x74, 0x12, 0x45, 0x0a, 0x20, 0x73, 0x65, 0x6d, 0x69, 0x5f, 0x73, 0x79, 0x6e, 0x63, + 0x5f, 0x77, 0x61, 0x69, 0x74, 0x5f, 0x66, 0x6f, 0x72, 0x5f, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, + 0x61, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x1b, 0x73, + 0x65, 0x6d, 0x69, 0x53, 0x79, 0x6e, 0x63, 0x57, 0x61, 0x69, 0x74, 0x46, 0x6f, 0x72, 0x52, 0x65, + 0x70, 0x6c, 0x69, 0x63, 0x61, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x26, 0x0a, 0x0f, 0x73, 0x75, + 0x70, 0x65, 0x72, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x15, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x0d, 0x73, 0x75, 0x70, 0x65, 0x72, 0x52, 0x65, 0x61, 0x64, 0x4f, 0x6e, + 0x6c, 0x79, 0x12, 0x5b, 0x0a, 0x19, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, + 0x16, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x18, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2a, + 0x3b, 0x0a, 0x13, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x12, 0x0a, 0x0e, 0x49, 0x4f, 0x41, 0x4e, 0x44, 0x53, + 0x51, 0x4c, 0x54, 0x48, 0x52, 0x45, 0x41, 0x44, 0x10, 0x00, 0x12, 0x10, 0x0a, 0x0c, 0x49, 0x4f, + 0x54, 0x48, 0x52, 0x45, 0x41, 0x44, 0x4f, 0x4e, 0x4c, 0x59, 0x10, 0x01, 0x42, 0x2e, 0x5a, 0x2c, + 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, 0x72, 0x65, 0x70, + 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x64, 0x61, 0x74, 0x61, 0x62, 0x06, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/go/vt/proto/replicationdata/replicationdata_vtproto.pb.go b/go/vt/proto/replicationdata/replicationdata_vtproto.pb.go index b1b771d1561..b3d638a1327 100644 --- a/go/vt/proto/replicationdata/replicationdata_vtproto.pb.go +++ b/go/vt/proto/replicationdata/replicationdata_vtproto.pb.go @@ -48,6 +48,7 @@ func (m *Status) CloneVT() *Status { r.HasReplicationFilters = m.HasReplicationFilters r.SslAllowed = m.SslAllowed r.ReplicationLagUnknown = m.ReplicationLagUnknown + r.BackupRunning = m.BackupRunning if len(m.unknownFields) > 0 { r.unknownFields = make([]byte, len(m.unknownFields)) copy(r.unknownFields, m.unknownFields) @@ -182,6 +183,18 @@ func (m *Status) MarshalToSizedBufferVT(dAtA []byte) (int, error) { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } + if m.BackupRunning { + i-- + if m.BackupRunning { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xc8 + } if m.ReplicationLagUnknown { i-- if m.ReplicationLagUnknown { @@ -817,6 +830,9 @@ func (m *Status) SizeVT() (n int) { if m.ReplicationLagUnknown { n += 3 } + if m.BackupRunning { + n += 3 + } n += len(m.unknownFields) return n } @@ -1545,6 +1561,26 @@ func (m *Status) UnmarshalVT(dAtA []byte) error { } } m.ReplicationLagUnknown = bool(v != 0) + case 25: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field BackupRunning", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.BackupRunning = bool(v != 0) default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) diff --git a/go/vt/proto/vtadmin/vtadmin.pb.go b/go/vt/proto/vtadmin/vtadmin.pb.go index e85385ec409..8b6a6997c8d 100644 --- a/go/vt/proto/vtadmin/vtadmin.pb.go +++ b/go/vt/proto/vtadmin/vtadmin.pb.go @@ -1203,8 +1203,12 @@ type ApplySchemaRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - 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"` + ClusterId string `protobuf:"bytes,1,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` + // Request.Sql will be overriden by this Sql field. + Sql string `protobuf:"bytes,2,opt,name=sql,proto3" json:"sql,omitempty"` + // Request.CallerId will be overriden by this CallerId field. + CallerId string `protobuf:"bytes,3,opt,name=caller_id,json=callerId,proto3" json:"caller_id,omitempty"` + Request *vtctldata.ApplySchemaRequest `protobuf:"bytes,4,opt,name=request,proto3" json:"request,omitempty"` } func (x *ApplySchemaRequest) Reset() { @@ -1244,6 +1248,20 @@ func (x *ApplySchemaRequest) GetClusterId() string { return "" } +func (x *ApplySchemaRequest) GetSql() string { + if x != nil { + return x.Sql + } + return "" +} + +func (x *ApplySchemaRequest) GetCallerId() string { + if x != nil { + return x.CallerId + } + return "" +} + func (x *ApplySchemaRequest) GetRequest() *vtctldata.ApplySchemaRequest { if x != nil { return x.Request @@ -7731,11 +7749,14 @@ var file_vtadmin_proto_rawDesc = []byte{ 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 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, 0x52, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 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, + 0x22, 0x9b, 0x01, 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, 0x10, 0x0a, 0x03, 0x73, 0x71, 0x6c, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x73, 0x71, 0x6c, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x61, 0x6c, 0x6c, + 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x61, 0x6c, + 0x6c, 0x65, 0x72, 0x49, 0x64, 0x12, 0x37, 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x18, 0x04, 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, diff --git a/go/vt/proto/vtadmin/vtadmin_vtproto.pb.go b/go/vt/proto/vtadmin/vtadmin_vtproto.pb.go index bc0746b7b8a..82cca2cea06 100644 --- a/go/vt/proto/vtadmin/vtadmin_vtproto.pb.go +++ b/go/vt/proto/vtadmin/vtadmin_vtproto.pb.go @@ -454,6 +454,8 @@ func (m *ApplySchemaRequest) CloneVT() *ApplySchemaRequest { } r := new(ApplySchemaRequest) r.ClusterId = m.ClusterId + r.Sql = m.Sql + r.CallerId = m.CallerId r.Request = m.Request.CloneVT() if len(m.unknownFields) > 0 { r.unknownFields = make([]byte, len(m.unknownFields)) @@ -4038,6 +4040,20 @@ func (m *ApplySchemaRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- + dAtA[i] = 0x22 + } + if len(m.CallerId) > 0 { + i -= len(m.CallerId) + copy(dAtA[i:], m.CallerId) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.CallerId))) + i-- + dAtA[i] = 0x1a + } + if len(m.Sql) > 0 { + i -= len(m.Sql) + copy(dAtA[i:], m.Sql) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Sql))) + i-- dAtA[i] = 0x12 } if len(m.ClusterId) > 0 { @@ -10321,6 +10337,14 @@ func (m *ApplySchemaRequest) SizeVT() (n int) { if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } + l = len(m.Sql) + if l > 0 { + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + l = len(m.CallerId) + if l > 0 { + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } if m.Request != nil { l = m.Request.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) @@ -15873,6 +15897,70 @@ func (m *ApplySchemaRequest) UnmarshalVT(dAtA []byte) error { m.ClusterId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Sql", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.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 protohelpers.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Sql = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CallerId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.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 protohelpers.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.CallerId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Request", wireType) } diff --git a/go/vt/proto/vtgate/vtgate.pb.go b/go/vt/proto/vtgate/vtgate.pb.go index f2b2bc47736..b996f919256 100644 --- a/go/vt/proto/vtgate/vtgate.pb.go +++ b/go/vt/proto/vtgate/vtgate.pb.go @@ -1529,6 +1529,8 @@ type Session_ShardSession struct { // reserved connection if a dedicated connection is needed ReservedId int64 `protobuf:"varint,4,opt,name=reserved_id,json=reservedId,proto3" json:"reserved_id,omitempty"` VindexOnly bool `protobuf:"varint,5,opt,name=vindex_only,json=vindexOnly,proto3" json:"vindex_only,omitempty"` + // rows_affected tracks if any query has modified the rows. + RowsAffected bool `protobuf:"varint,6,opt,name=rows_affected,json=rowsAffected,proto3" json:"rows_affected,omitempty"` } func (x *Session_ShardSession) Reset() { @@ -1596,6 +1598,13 @@ func (x *Session_ShardSession) GetVindexOnly() bool { return false } +func (x *Session_ShardSession) GetRowsAffected() bool { + if x != nil { + return x.RowsAffected + } + return false +} + var File_vtgate_proto protoreflect.FileDescriptor var file_vtgate_proto_rawDesc = []byte{ @@ -1604,7 +1613,7 @@ var file_vtgate_proto_rawDesc = []byte{ 0x74, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x0b, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x0e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x0b, 0x76, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x22, 0xfb, 0x0e, 0x0a, 0x07, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x25, + 0x74, 0x6f, 0x22, 0xa0, 0x0f, 0x0a, 0x07, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x25, 0x0a, 0x0e, 0x69, 0x6e, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x69, 0x6e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x43, 0x0a, 0x0e, 0x73, 0x68, 0x61, 0x72, 0x64, 0x5f, 0x73, @@ -1690,7 +1699,7 @@ var file_vtgate_proto_rawDesc = []byte{ 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x2b, 0x0a, 0x11, 0x6d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x1b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x6d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x65, - 0x78, 0x74, 0x1a, 0xd8, 0x01, 0x0a, 0x0c, 0x53, 0x68, 0x61, 0x72, 0x64, 0x53, 0x65, 0x73, 0x73, + 0x78, 0x74, 0x1a, 0xfd, 0x01, 0x0a, 0x0c, 0x53, 0x68, 0x61, 0x72, 0x64, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x25, 0x0a, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x01, 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, 0x25, 0x0a, 0x0e, 0x74, 0x72, @@ -1703,195 +1712,197 @@ var file_vtgate_proto_rawDesc = []byte{ 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x49, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x76, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x0a, 0x76, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x4f, 0x6e, 0x6c, 0x79, 0x1a, 0x5c, 0x0a, - 0x19, 0x55, 0x73, 0x65, 0x72, 0x44, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x56, 0x61, 0x72, 0x69, - 0x61, 0x62, 0x6c, 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, 0x71, 0x75, - 0x65, 0x72, 0x79, 0x2e, 0x42, 0x69, 0x6e, 0x64, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, - 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x42, 0x0a, 0x14, 0x53, - 0x79, 0x73, 0x74, 0x65, 0x6d, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 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, 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, - 0x3f, 0x0a, 0x11, 0x41, 0x64, 0x76, 0x69, 0x73, 0x6f, 0x72, 0x79, 0x4c, 0x6f, 0x63, 0x6b, 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, 0x03, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, - 0x1a, 0x58, 0x0a, 0x15, 0x50, 0x72, 0x65, 0x70, 0x61, 0x72, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, - 0x6d, 0x65, 0x6e, 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, 0x29, 0x0a, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x76, 0x74, 0x67, - 0x61, 0x74, 0x65, 0x2e, 0x50, 0x72, 0x65, 0x70, 0x61, 0x72, 0x65, 0x44, 0x61, 0x74, 0x61, 0x52, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, - 0x22, 0x5d, 0x0a, 0x0b, 0x50, 0x72, 0x65, 0x70, 0x61, 0x72, 0x65, 0x44, 0x61, 0x74, 0x61, 0x12, - 0x2b, 0x0a, 0x11, 0x70, 0x72, 0x65, 0x70, 0x61, 0x72, 0x65, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, - 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x70, 0x72, 0x65, 0x70, - 0x61, 0x72, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x21, 0x0a, 0x0c, - 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x05, 0x52, 0x0b, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x22, - 0xac, 0x01, 0x0a, 0x0e, 0x52, 0x65, 0x61, 0x64, 0x41, 0x66, 0x74, 0x65, 0x72, 0x57, 0x72, 0x69, - 0x74, 0x65, 0x12, 0x31, 0x0a, 0x15, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x61, 0x66, 0x74, 0x65, 0x72, - 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x5f, 0x67, 0x74, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x12, 0x72, 0x65, 0x61, 0x64, 0x41, 0x66, 0x74, 0x65, 0x72, 0x57, 0x72, 0x69, 0x74, - 0x65, 0x47, 0x74, 0x69, 0x64, 0x12, 0x37, 0x0a, 0x18, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x61, 0x66, - 0x74, 0x65, 0x72, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, - 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x15, 0x72, 0x65, 0x61, 0x64, 0x41, 0x66, 0x74, - 0x65, 0x72, 0x57, 0x72, 0x69, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x2e, - 0x0a, 0x13, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x72, 0x61, 0x63, 0x6b, 0x5f, - 0x67, 0x74, 0x69, 0x64, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x73, 0x65, 0x73, - 0x73, 0x69, 0x6f, 0x6e, 0x54, 0x72, 0x61, 0x63, 0x6b, 0x47, 0x74, 0x69, 0x64, 0x73, 0x22, 0xaa, - 0x01, 0x0a, 0x0e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x2c, 0x0a, 0x09, 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, 0x08, 0x63, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x49, 0x64, 0x12, - 0x29, 0x0a, 0x07, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x0f, 0x2e, 0x76, 0x74, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, - 0x6e, 0x52, 0x07, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x27, 0x0a, 0x05, 0x71, 0x75, - 0x65, 0x72, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x71, 0x75, 0x65, 0x72, - 0x79, 0x2e, 0x42, 0x6f, 0x75, 0x6e, 0x64, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x05, 0x71, 0x75, - 0x65, 0x72, 0x79, 0x4a, 0x04, 0x08, 0x04, 0x10, 0x05, 0x4a, 0x04, 0x08, 0x05, 0x10, 0x06, 0x4a, - 0x04, 0x08, 0x06, 0x10, 0x07, 0x4a, 0x04, 0x08, 0x07, 0x10, 0x08, 0x22, 0x8f, 0x01, 0x0a, 0x0f, - 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x25, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, - 0x2e, 0x76, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x50, 0x43, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, - 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x29, 0x0a, 0x07, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, - 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x76, 0x74, 0x67, 0x61, 0x74, 0x65, - 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, - 0x6e, 0x12, 0x2a, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 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, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0xb3, 0x01, - 0x0a, 0x13, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x42, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2c, 0x0a, 0x09, 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, 0x08, 0x63, 0x61, 0x6c, 0x6c, 0x65, - 0x72, 0x49, 0x64, 0x12, 0x29, 0x0a, 0x07, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x76, 0x74, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x53, 0x65, - 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x2b, - 0x0a, 0x07, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x11, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x42, 0x6f, 0x75, 0x6e, 0x64, 0x51, 0x75, 0x65, - 0x72, 0x79, 0x52, 0x07, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x73, 0x4a, 0x04, 0x08, 0x04, 0x10, - 0x05, 0x4a, 0x04, 0x08, 0x05, 0x10, 0x06, 0x4a, 0x04, 0x08, 0x06, 0x10, 0x07, 0x4a, 0x04, 0x08, - 0x07, 0x10, 0x08, 0x22, 0x9a, 0x01, 0x0a, 0x14, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x42, - 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x25, 0x0a, 0x05, - 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x76, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x52, 0x50, 0x43, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x05, 0x65, 0x72, - 0x72, 0x6f, 0x72, 0x12, 0x29, 0x0a, 0x07, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x76, 0x74, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x53, 0x65, - 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x30, - 0x0a, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x16, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x57, 0x69, - 0x74, 0x68, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, - 0x22, 0xaa, 0x01, 0x0a, 0x14, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x45, 0x78, 0x65, 0x63, 0x75, - 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2c, 0x0a, 0x09, 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, 0x08, 0x63, - 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x49, 0x64, 0x12, 0x27, 0x0a, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x42, - 0x6f, 0x75, 0x6e, 0x64, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, - 0x12, 0x29, 0x0a, 0x07, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x0f, 0x2e, 0x76, 0x74, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, - 0x6f, 0x6e, 0x52, 0x07, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4a, 0x04, 0x08, 0x03, 0x10, - 0x04, 0x4a, 0x04, 0x08, 0x04, 0x10, 0x05, 0x4a, 0x04, 0x08, 0x05, 0x10, 0x06, 0x22, 0x6e, 0x0a, - 0x15, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 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, + 0x08, 0x52, 0x0a, 0x76, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x4f, 0x6e, 0x6c, 0x79, 0x12, 0x23, 0x0a, + 0x0d, 0x72, 0x6f, 0x77, 0x73, 0x5f, 0x61, 0x66, 0x66, 0x65, 0x63, 0x74, 0x65, 0x64, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x72, 0x6f, 0x77, 0x73, 0x41, 0x66, 0x66, 0x65, 0x63, 0x74, + 0x65, 0x64, 0x1a, 0x5c, 0x0a, 0x19, 0x55, 0x73, 0x65, 0x72, 0x44, 0x65, 0x66, 0x69, 0x6e, 0x65, + 0x64, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 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, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x42, 0x69, 0x6e, 0x64, 0x56, 0x61, 0x72, + 0x69, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, + 0x1a, 0x42, 0x0a, 0x14, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, + 0x6c, 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, 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, 0x3f, 0x0a, 0x11, 0x41, 0x64, 0x76, 0x69, 0x73, 0x6f, 0x72, 0x79, + 0x4c, 0x6f, 0x63, 0x6b, 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, 0x03, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x58, 0x0a, 0x15, 0x50, 0x72, 0x65, 0x70, 0x61, 0x72, 0x65, + 0x53, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 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, 0x29, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x13, 0x2e, 0x76, 0x74, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x50, 0x72, 0x65, 0x70, 0x61, 0x72, 0x65, + 0x44, 0x61, 0x74, 0x61, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x4a, + 0x04, 0x08, 0x03, 0x10, 0x04, 0x22, 0x5d, 0x0a, 0x0b, 0x50, 0x72, 0x65, 0x70, 0x61, 0x72, 0x65, + 0x44, 0x61, 0x74, 0x61, 0x12, 0x2b, 0x0a, 0x11, 0x70, 0x72, 0x65, 0x70, 0x61, 0x72, 0x65, 0x5f, + 0x73, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x10, 0x70, 0x72, 0x65, 0x70, 0x61, 0x72, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, + 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x5f, 0x63, 0x6f, 0x75, 0x6e, + 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x43, + 0x6f, 0x75, 0x6e, 0x74, 0x22, 0xac, 0x01, 0x0a, 0x0e, 0x52, 0x65, 0x61, 0x64, 0x41, 0x66, 0x74, + 0x65, 0x72, 0x57, 0x72, 0x69, 0x74, 0x65, 0x12, 0x31, 0x0a, 0x15, 0x72, 0x65, 0x61, 0x64, 0x5f, + 0x61, 0x66, 0x74, 0x65, 0x72, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x5f, 0x67, 0x74, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x72, 0x65, 0x61, 0x64, 0x41, 0x66, 0x74, 0x65, + 0x72, 0x57, 0x72, 0x69, 0x74, 0x65, 0x47, 0x74, 0x69, 0x64, 0x12, 0x37, 0x0a, 0x18, 0x72, 0x65, + 0x61, 0x64, 0x5f, 0x61, 0x66, 0x74, 0x65, 0x72, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x5f, 0x74, + 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x15, 0x72, 0x65, + 0x61, 0x64, 0x41, 0x66, 0x74, 0x65, 0x72, 0x57, 0x72, 0x69, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, + 0x6f, 0x75, 0x74, 0x12, 0x2e, 0x0a, 0x13, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x74, + 0x72, 0x61, 0x63, 0x6b, 0x5f, 0x67, 0x74, 0x69, 0x64, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x11, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x54, 0x72, 0x61, 0x63, 0x6b, 0x47, 0x74, + 0x69, 0x64, 0x73, 0x22, 0xaa, 0x01, 0x0a, 0x0e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2c, 0x0a, 0x09, 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, 0x08, 0x63, 0x61, 0x6c, 0x6c, + 0x65, 0x72, 0x49, 0x64, 0x12, 0x29, 0x0a, 0x07, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x76, 0x74, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x53, + 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, + 0x27, 0x0a, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, + 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x42, 0x6f, 0x75, 0x6e, 0x64, 0x51, 0x75, 0x65, 0x72, + 0x79, 0x52, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x4a, 0x04, 0x08, 0x04, 0x10, 0x05, 0x4a, 0x04, + 0x08, 0x05, 0x10, 0x06, 0x4a, 0x04, 0x08, 0x06, 0x10, 0x07, 0x4a, 0x04, 0x08, 0x07, 0x10, 0x08, + 0x22, 0x8f, 0x01, 0x0a, 0x0f, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x25, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x76, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x50, 0x43, 0x45, + 0x72, 0x72, 0x6f, 0x72, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x29, 0x0a, 0x07, 0x73, + 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x76, + 0x74, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x73, + 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x2a, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, + 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, 0x72, 0x65, 0x73, 0x75, - 0x6c, 0x74, 0x12, 0x29, 0x0a, 0x07, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x76, 0x74, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x53, 0x65, 0x73, - 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x5d, 0x0a, - 0x19, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2c, 0x0a, 0x09, 0x63, 0x61, + 0x6c, 0x74, 0x22, 0xb3, 0x01, 0x0a, 0x13, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x42, 0x61, + 0x74, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2c, 0x0a, 0x09, 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, 0x08, - 0x63, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x74, 0x69, 0x64, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x64, 0x74, 0x69, 0x64, 0x22, 0x1c, 0x0a, 0x1a, - 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xef, 0x02, 0x0a, 0x0c, 0x56, - 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x6d, - 0x69, 0x6e, 0x69, 0x6d, 0x69, 0x7a, 0x65, 0x5f, 0x73, 0x6b, 0x65, 0x77, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x0c, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x69, 0x7a, 0x65, 0x53, 0x6b, 0x65, 0x77, - 0x12, 0x2d, 0x0a, 0x12, 0x68, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x5f, 0x69, 0x6e, - 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x11, 0x68, 0x65, - 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, - 0x26, 0x0a, 0x0f, 0x73, 0x74, 0x6f, 0x70, 0x5f, 0x6f, 0x6e, 0x5f, 0x72, 0x65, 0x73, 0x68, 0x61, - 0x72, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x73, 0x74, 0x6f, 0x70, 0x4f, 0x6e, - 0x52, 0x65, 0x73, 0x68, 0x61, 0x72, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x65, 0x6c, 0x6c, 0x73, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x12, 0x27, 0x0a, - 0x0f, 0x63, 0x65, 0x6c, 0x6c, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x63, 0x65, 0x6c, 0x6c, 0x50, 0x72, 0x65, 0x66, - 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, - 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x74, 0x61, - 0x62, 0x6c, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x3c, 0x0a, 0x1a, 0x73, 0x74, 0x72, - 0x65, 0x61, 0x6d, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x68, 0x65, 0x61, - 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x18, 0x73, - 0x74, 0x72, 0x65, 0x61, 0x6d, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x48, 0x65, 0x61, - 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x73, 0x12, 0x43, 0x0a, 0x1e, 0x69, 0x6e, 0x63, 0x6c, 0x75, - 0x64, 0x65, 0x5f, 0x72, 0x65, 0x73, 0x68, 0x61, 0x72, 0x64, 0x5f, 0x6a, 0x6f, 0x75, 0x72, 0x6e, - 0x61, 0x6c, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x1b, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x52, 0x65, 0x73, 0x68, 0x61, 0x72, 0x64, 0x4a, - 0x6f, 0x75, 0x72, 0x6e, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x22, 0xf6, 0x01, 0x0a, - 0x0e, 0x56, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x63, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x49, 0x64, 0x12, 0x29, 0x0a, 0x07, 0x73, 0x65, 0x73, 0x73, + 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x76, 0x74, 0x67, 0x61, + 0x74, 0x65, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x73, 0x65, 0x73, 0x73, + 0x69, 0x6f, 0x6e, 0x12, 0x2b, 0x0a, 0x07, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x73, 0x18, 0x03, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x42, 0x6f, 0x75, + 0x6e, 0x64, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x07, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x73, + 0x4a, 0x04, 0x08, 0x04, 0x10, 0x05, 0x4a, 0x04, 0x08, 0x05, 0x10, 0x06, 0x4a, 0x04, 0x08, 0x06, + 0x10, 0x07, 0x4a, 0x04, 0x08, 0x07, 0x10, 0x08, 0x22, 0x9a, 0x01, 0x0a, 0x14, 0x45, 0x78, 0x65, + 0x63, 0x75, 0x74, 0x65, 0x42, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x25, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x0f, 0x2e, 0x76, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x50, 0x43, 0x45, 0x72, 0x72, 0x6f, + 0x72, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x29, 0x0a, 0x07, 0x73, 0x65, 0x73, 0x73, + 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x76, 0x74, 0x67, 0x61, + 0x74, 0x65, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x73, 0x65, 0x73, 0x73, + 0x69, 0x6f, 0x6e, 0x12, 0x30, 0x0a, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x18, 0x03, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x52, 0x65, 0x73, + 0x75, 0x6c, 0x74, 0x57, 0x69, 0x74, 0x68, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x07, 0x72, 0x65, + 0x73, 0x75, 0x6c, 0x74, 0x73, 0x22, 0xaa, 0x01, 0x0a, 0x14, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, + 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2c, + 0x0a, 0x09, 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, 0x08, 0x63, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x49, 0x64, 0x12, 0x27, 0x0a, 0x05, + 0x71, 0x75, 0x65, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x71, 0x75, + 0x65, 0x72, 0x79, 0x2e, 0x42, 0x6f, 0x75, 0x6e, 0x64, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x05, + 0x71, 0x75, 0x65, 0x72, 0x79, 0x12, 0x29, 0x0a, 0x07, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x76, 0x74, 0x67, 0x61, 0x74, 0x65, 0x2e, + 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, + 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x4a, 0x04, 0x08, 0x04, 0x10, 0x05, 0x4a, 0x04, 0x08, 0x05, + 0x10, 0x06, 0x22, 0x6e, 0x0a, 0x15, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x45, 0x78, 0x65, 0x63, + 0x75, 0x74, 0x65, 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, 0x12, 0x29, 0x0a, 0x07, 0x73, 0x65, 0x73, 0x73, 0x69, + 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x76, 0x74, 0x67, 0x61, 0x74, + 0x65, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x73, 0x65, 0x73, 0x73, 0x69, + 0x6f, 0x6e, 0x22, 0x5d, 0x0a, 0x19, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x54, 0x72, 0x61, + 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2c, 0x0a, 0x09, 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, 0x08, 0x63, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x49, 0x64, 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, 0x27, 0x0a, 0x05, 0x76, 0x67, 0x74, 0x69, 0x64, 0x18, 0x03, 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, 0x2a, 0x0a, - 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x04, 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, 0x2a, 0x0a, 0x05, 0x66, 0x6c, 0x61, - 0x67, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x76, 0x74, 0x67, 0x61, 0x74, - 0x65, 0x2e, 0x56, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x52, 0x05, - 0x66, 0x6c, 0x61, 0x67, 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, 0x92, 0x01, 0x0a, 0x0e, 0x50, 0x72, 0x65, 0x70, 0x61, 0x72, 0x65, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2c, 0x0a, 0x09, 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, 0x08, 0x63, 0x61, 0x6c, - 0x6c, 0x65, 0x72, 0x49, 0x64, 0x12, 0x29, 0x0a, 0x07, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x76, 0x74, 0x67, 0x61, 0x74, 0x65, 0x2e, - 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, - 0x12, 0x27, 0x0a, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x11, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x42, 0x6f, 0x75, 0x6e, 0x64, 0x51, 0x75, 0x65, - 0x72, 0x79, 0x52, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x22, 0x89, 0x01, 0x0a, 0x0f, 0x50, 0x72, - 0x65, 0x70, 0x61, 0x72, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x25, 0x0a, - 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x76, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x50, 0x43, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x05, 0x65, - 0x72, 0x72, 0x6f, 0x72, 0x12, 0x29, 0x0a, 0x07, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x76, 0x74, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x53, - 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, - 0x24, 0x0a, 0x06, 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, 0x06, 0x66, - 0x69, 0x65, 0x6c, 0x64, 0x73, 0x22, 0x6e, 0x0a, 0x13, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x53, 0x65, - 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2c, 0x0a, 0x09, + 0x72, 0x49, 0x44, 0x52, 0x08, 0x63, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x49, 0x64, 0x12, 0x12, 0x0a, + 0x04, 0x64, 0x74, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x64, 0x74, 0x69, + 0x64, 0x22, 0x1c, 0x0a, 0x1a, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x54, 0x72, 0x61, 0x6e, + 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0xef, 0x02, 0x0a, 0x0c, 0x56, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x46, 0x6c, 0x61, 0x67, 0x73, + 0x12, 0x23, 0x0a, 0x0d, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x69, 0x7a, 0x65, 0x5f, 0x73, 0x6b, 0x65, + 0x77, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x69, 0x7a, + 0x65, 0x53, 0x6b, 0x65, 0x77, 0x12, 0x2d, 0x0a, 0x12, 0x68, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, + 0x61, 0x74, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x11, 0x68, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x49, 0x6e, 0x74, 0x65, + 0x72, 0x76, 0x61, 0x6c, 0x12, 0x26, 0x0a, 0x0f, 0x73, 0x74, 0x6f, 0x70, 0x5f, 0x6f, 0x6e, 0x5f, + 0x72, 0x65, 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x73, + 0x74, 0x6f, 0x70, 0x4f, 0x6e, 0x52, 0x65, 0x73, 0x68, 0x61, 0x72, 0x64, 0x12, 0x14, 0x0a, 0x05, + 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x63, 0x65, 0x6c, + 0x6c, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x63, 0x65, 0x6c, 0x6c, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x65, + 0x72, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x63, 0x65, 0x6c, + 0x6c, 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x74, + 0x61, 0x62, 0x6c, 0x65, 0x74, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0b, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x3c, + 0x0a, 0x1a, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x5f, 0x68, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x73, 0x18, 0x07, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x18, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x73, 0x12, 0x43, 0x0a, 0x1e, + 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x72, 0x65, 0x73, 0x68, 0x61, 0x72, 0x64, 0x5f, + 0x6a, 0x6f, 0x75, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x08, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x1b, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x52, 0x65, 0x73, + 0x68, 0x61, 0x72, 0x64, 0x4a, 0x6f, 0x75, 0x72, 0x6e, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, + 0x73, 0x22, 0xf6, 0x01, 0x0a, 0x0e, 0x56, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x2c, 0x0a, 0x09, 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, 0x08, 0x63, 0x61, 0x6c, 0x6c, 0x65, 0x72, + 0x49, 0x64, 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, 0x27, 0x0a, 0x05, 0x76, 0x67, 0x74, + 0x69, 0x64, 0x18, 0x03, 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, 0x2a, 0x0a, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x04, 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, 0x2a, + 0x0a, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, + 0x76, 0x74, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x56, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x46, 0x6c, + 0x61, 0x67, 0x73, 0x52, 0x05, 0x66, 0x6c, 0x61, 0x67, 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, 0x92, 0x01, 0x0a, 0x0e, 0x50, 0x72, + 0x65, 0x70, 0x61, 0x72, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2c, 0x0a, 0x09, 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, 0x08, 0x63, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x49, 0x64, 0x12, 0x29, 0x0a, 0x07, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x76, 0x74, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x73, 0x65, - 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x3d, 0x0a, 0x14, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x53, 0x65, - 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x25, 0x0a, - 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x76, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x50, 0x43, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x05, 0x65, - 0x72, 0x72, 0x6f, 0x72, 0x2a, 0x44, 0x0a, 0x0f, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x0f, 0x0a, 0x0b, 0x55, 0x4e, 0x53, 0x50, 0x45, - 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x49, 0x4e, 0x47, - 0x4c, 0x45, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x4d, 0x55, 0x4c, 0x54, 0x49, 0x10, 0x02, 0x12, - 0x09, 0x0a, 0x05, 0x54, 0x57, 0x4f, 0x50, 0x43, 0x10, 0x03, 0x2a, 0x3c, 0x0a, 0x0b, 0x43, 0x6f, - 0x6d, 0x6d, 0x69, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x0a, 0x0a, 0x06, 0x4e, 0x4f, 0x52, - 0x4d, 0x41, 0x4c, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x50, 0x52, 0x45, 0x10, 0x01, 0x12, 0x08, - 0x0a, 0x04, 0x50, 0x4f, 0x53, 0x54, 0x10, 0x02, 0x12, 0x0e, 0x0a, 0x0a, 0x41, 0x55, 0x54, 0x4f, - 0x43, 0x4f, 0x4d, 0x4d, 0x49, 0x54, 0x10, 0x03, 0x42, 0x36, 0x0a, 0x0f, 0x69, 0x6f, 0x2e, 0x76, - 0x69, 0x74, 0x65, 0x73, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 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, 0x67, 0x61, 0x74, 0x65, - 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x27, 0x0a, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x42, 0x6f, 0x75, + 0x6e, 0x64, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x22, 0x89, + 0x01, 0x0a, 0x0f, 0x50, 0x72, 0x65, 0x70, 0x61, 0x72, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x25, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x0f, 0x2e, 0x76, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x50, 0x43, 0x45, 0x72, 0x72, + 0x6f, 0x72, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x29, 0x0a, 0x07, 0x73, 0x65, 0x73, + 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x76, 0x74, 0x67, + 0x61, 0x74, 0x65, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x73, 0x65, 0x73, + 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x24, 0x0a, 0x06, 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, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x22, 0x6e, 0x0a, 0x13, 0x43, 0x6c, + 0x6f, 0x73, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x2c, 0x0a, 0x09, 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, 0x08, 0x63, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x49, 0x64, 0x12, + 0x29, 0x0a, 0x07, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x0f, 0x2e, 0x76, 0x74, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x52, 0x07, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x3d, 0x0a, 0x14, 0x43, 0x6c, + 0x6f, 0x73, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x25, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x0f, 0x2e, 0x76, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x50, 0x43, 0x45, 0x72, 0x72, + 0x6f, 0x72, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x2a, 0x44, 0x0a, 0x0f, 0x54, 0x72, 0x61, + 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x0f, 0x0a, 0x0b, + 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0a, 0x0a, + 0x06, 0x53, 0x49, 0x4e, 0x47, 0x4c, 0x45, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x4d, 0x55, 0x4c, + 0x54, 0x49, 0x10, 0x02, 0x12, 0x09, 0x0a, 0x05, 0x54, 0x57, 0x4f, 0x50, 0x43, 0x10, 0x03, 0x2a, + 0x3c, 0x0a, 0x0b, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x0a, + 0x0a, 0x06, 0x4e, 0x4f, 0x52, 0x4d, 0x41, 0x4c, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x50, 0x52, + 0x45, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x50, 0x4f, 0x53, 0x54, 0x10, 0x02, 0x12, 0x0e, 0x0a, + 0x0a, 0x41, 0x55, 0x54, 0x4f, 0x43, 0x4f, 0x4d, 0x4d, 0x49, 0x54, 0x10, 0x03, 0x42, 0x36, 0x0a, + 0x0f, 0x69, 0x6f, 0x2e, 0x76, 0x69, 0x74, 0x65, 0x73, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 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, 0x67, 0x61, 0x74, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/go/vt/proto/vtgate/vtgate_vtproto.pb.go b/go/vt/proto/vtgate/vtgate_vtproto.pb.go index 5d9393b881c..5df6dbbffef 100644 --- a/go/vt/proto/vtgate/vtgate_vtproto.pb.go +++ b/go/vt/proto/vtgate/vtgate_vtproto.pb.go @@ -35,6 +35,7 @@ func (m *Session_ShardSession) CloneVT() *Session_ShardSession { r.TabletAlias = m.TabletAlias.CloneVT() r.ReservedId = m.ReservedId r.VindexOnly = m.VindexOnly + r.RowsAffected = m.RowsAffected if len(m.unknownFields) > 0 { r.unknownFields = make([]byte, len(m.unknownFields)) copy(r.unknownFields, m.unknownFields) @@ -513,6 +514,16 @@ func (m *Session_ShardSession) MarshalToSizedBufferVT(dAtA []byte) (int, error) i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } + if m.RowsAffected { + i-- + if m.RowsAffected { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x30 + } if m.VindexOnly { i-- if m.VindexOnly { @@ -1903,6 +1914,9 @@ func (m *Session_ShardSession) SizeVT() (n int) { if m.VindexOnly { n += 2 } + if m.RowsAffected { + n += 2 + } n += len(m.unknownFields) return n } @@ -2563,6 +2577,26 @@ func (m *Session_ShardSession) UnmarshalVT(dAtA []byte) error { } } m.VindexOnly = bool(v != 0) + case 6: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field RowsAffected", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.RowsAffected = bool(v != 0) default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) diff --git a/go/vt/schema/online_ddl.go b/go/vt/schema/online_ddl.go index 59363b00889..8de54d98b1e 100644 --- a/go/vt/schema/online_ddl.go +++ b/go/vt/schema/online_ddl.go @@ -24,6 +24,7 @@ import ( "regexp" "strconv" "strings" + "time" vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" "vitess.io/vitess/go/vt/sqlparser" @@ -116,6 +117,7 @@ type OnlineDDL struct { Retries int64 `json:"retries,omitempty"` ReadyToComplete int64 `json:"ready_to_complete,omitempty"` WasReadyToComplete int64 `json:"was_ready_to_complete,omitempty"` + CutOverThreshold time.Duration `json:"cutover_threshold,omitempty"` } // ParseOnlineDDLStatement parses the given SQL into a statement and returns the action type of the DDL statement, or error diff --git a/go/vt/schemamanager/local_controller.go b/go/vt/schemamanager/local_controller.go index b95bb1398bf..92b7afa83c0 100644 --- a/go/vt/schemamanager/local_controller.go +++ b/go/vt/schemamanager/local_controller.go @@ -208,8 +208,10 @@ func (controller *LocalController) writeToLogDir(ctx context.Context, result *Ex rowsReturned := uint64(0) rowsAffected := uint64(0) for _, queryResult := range result.SuccessShards { - rowsReturned += uint64(len(queryResult.Result.Rows)) - rowsAffected += queryResult.Result.RowsAffected + for _, result := range queryResult.Results { + rowsReturned += uint64(len(result.Rows)) + rowsAffected += result.RowsAffected + } } logFile.WriteString(fmt.Sprintf("-- Rows returned: %d\n", rowsReturned)) logFile.WriteString(fmt.Sprintf("-- Rows affected: %d\n", rowsAffected)) diff --git a/go/vt/schemamanager/local_controller_test.go b/go/vt/schemamanager/local_controller_test.go index 1784a76e133..74d66033c63 100644 --- a/go/vt/schemamanager/local_controller_test.go +++ b/go/vt/schemamanager/local_controller_test.go @@ -138,8 +138,8 @@ func TestLocalControllerSchemaChange(t *testing.T) { result := &ExecuteResult{ Sqls: []string{"create table test_table (id int)"}, SuccessShards: []ShardResult{{ - Shard: "0", - Result: &querypb.QueryResult{}, + Shard: "0", + Results: []*querypb.QueryResult{{}}, }}, } logPath := path.Join(controller.logDir, controller.sqlFilename) diff --git a/go/vt/schemamanager/schemamanager.go b/go/vt/schemamanager/schemamanager.go index 1ccc52d6a6d..8a695a1164e 100644 --- a/go/vt/schemamanager/schemamanager.go +++ b/go/vt/schemamanager/schemamanager.go @@ -85,8 +85,8 @@ type ShardWithError struct { // ShardResult contains sql execute information on a particular shard type ShardResult struct { - Shard string - Result *querypb.QueryResult + Shard string + Results []*querypb.QueryResult // Position is a replication position that is guaranteed to be after the // schema change was applied. It can be used to wait for replicas to receive // the schema change via replication. diff --git a/go/vt/schemamanager/schemamanager_test.go b/go/vt/schemamanager/schemamanager_test.go index 129600d0527..76b638960fe 100644 --- a/go/vt/schemamanager/schemamanager_test.go +++ b/go/vt/schemamanager/schemamanager_test.go @@ -293,6 +293,13 @@ func (client *fakeTabletManagerClient) ExecuteFetchAsDba(ctx context.Context, ta return client.TabletManagerClient.ExecuteFetchAsDba(ctx, tablet, usePool, req) } +func (client *fakeTabletManagerClient) ExecuteMultiFetchAsDba(ctx context.Context, tablet *topodatapb.Tablet, usePool bool, req *tabletmanagerdatapb.ExecuteMultiFetchAsDbaRequest) ([]*querypb.QueryResult, error) { + if client.EnableExecuteFetchAsDbaError { + return nil, fmt.Errorf("ExecuteMultiFetchAsDba occur an unknown error") + } + return client.TabletManagerClient.ExecuteMultiFetchAsDba(ctx, tablet, usePool, req) +} + // newFakeTopo returns a topo with: // - a keyspace named 'test_keyspace'. // - 3 shards named '1', '2', '3'. diff --git a/go/vt/schemamanager/tablet_executor.go b/go/vt/schemamanager/tablet_executor.go index 68270e0babc..60e86bf7faf 100644 --- a/go/vt/schemamanager/tablet_executor.go +++ b/go/vt/schemamanager/tablet_executor.go @@ -249,14 +249,12 @@ func (exec *TabletExecutor) executeAlterMigrationThrottle(ctx context.Context, a throttlerConfig.ThrottledApps = make(map[string]*topodatapb.ThrottledAppRule) } if req.ThrottledApp != nil && req.ThrottledApp.Name != "" { - // TODO(shlomi) in v22: replace the following line with the commented out block - throttlerConfig.ThrottledApps[req.ThrottledApp.Name] = req.ThrottledApp - // timeNow := time.Now() - // if protoutil.TimeFromProto(req.ThrottledApp.ExpiresAt).After(timeNow) { - // throttlerConfig.ThrottledApps[req.ThrottledApp.Name] = req.ThrottledApp - // } else { - // delete(throttlerConfig.ThrottledApps, req.ThrottledApp.Name) - // } + timeNow := time.Now() + if protoutil.TimeFromProto(req.ThrottledApp.ExpiresAt).After(timeNow) { + throttlerConfig.ThrottledApps[req.ThrottledApp.Name] = req.ThrottledApp + } else { + delete(throttlerConfig.ThrottledApps, req.ThrottledApp.Name) + } } return throttlerConfig @@ -572,13 +570,15 @@ func (exec *TabletExecutor) executeOneTablet( errChan chan ShardWithError, successChan chan ShardResult) { - var result *querypb.QueryResult + var results []*querypb.QueryResult var err error if viaQueryService { - result, err = exec.tmc.ExecuteQuery(ctx, tablet, &tabletmanagerdatapb.ExecuteQueryRequest{ + result, reserr := exec.tmc.ExecuteQuery(ctx, tablet, &tabletmanagerdatapb.ExecuteQueryRequest{ Query: []byte(sql), MaxRows: 10, }) + results = []*querypb.QueryResult{result} + err = reserr } else { if exec.ddlStrategySetting != nil && exec.ddlStrategySetting.IsAllowZeroInDateFlag() { // --allow-zero-in-date Applies to DDLs @@ -588,14 +588,14 @@ func (exec *TabletExecutor) executeOneTablet( return } } - request := &tabletmanagerdatapb.ExecuteFetchAsDbaRequest{ - Query: []byte(sql), + request := &tabletmanagerdatapb.ExecuteMultiFetchAsDbaRequest{ + Sql: []byte(sql), MaxRows: 10, } if exec.ddlStrategySetting != nil && exec.ddlStrategySetting.IsAllowForeignKeysFlag() { request.DisableForeignKeyChecks = true } - result, err = exec.tmc.ExecuteFetchAsDba(ctx, tablet, false, request) + results, err = exec.tmc.ExecuteMultiFetchAsDba(ctx, tablet, false, request) } if err != nil { @@ -614,7 +614,7 @@ func (exec *TabletExecutor) executeOneTablet( } successChan <- ShardResult{ Shard: tablet.Shard, - Result: result, + Results: results, Position: pos, } } diff --git a/go/vt/servenv/grpc_server.go b/go/vt/servenv/grpc_server.go index 96fe3c25ea9..8d30ee6d253 100644 --- a/go/vt/servenv/grpc_server.go +++ b/go/vt/servenv/grpc_server.go @@ -169,6 +169,11 @@ func GRPCPort() int { return gRPCPort } +// GRPCPort returns the value of the `--grpc_bind_address` flag. +func GRPCBindAddress() string { + return gRPCBindAddress +} + // isGRPCEnabled returns true if gRPC server is set func isGRPCEnabled() bool { if gRPCPort != 0 { diff --git a/go/vt/servenv/servenv.go b/go/vt/servenv/servenv.go index 4aa9818eb7d..22bf3523dfc 100644 --- a/go/vt/servenv/servenv.go +++ b/go/vt/servenv/servenv.go @@ -370,6 +370,14 @@ func moveFlags(name string, fs *pflag.FlagSet) { // functions. func CobraPreRunE(cmd *cobra.Command, args []string) error { _flag.TrickGlog() + // Register logging on config file change. + ch := make(chan struct{}) + viperutil.NotifyConfigReload(ch) + go func() { + for range ch { + log.Infof("Change in configuration - %v", viperdebug.AllSettings()) + } + }() watchCancel, err := viperutil.LoadConfig() if err != nil { @@ -377,6 +385,10 @@ func CobraPreRunE(cmd *cobra.Command, args []string) error { } OnTerm(watchCancel) + // Register a function to be called on termination that closes the channel. + // This is done after the watchCancel has registered to ensure that we don't end up + // sending on a closed channel. + OnTerm(func() { close(ch) }) HTTPHandleFunc("/debug/config", viperdebug.HandlerFunc) logutil.PurgeLogs() diff --git a/go/vt/sidecardb/schema/onlineddl/schema_migrations.sql b/go/vt/sidecardb/schema/onlineddl/schema_migrations.sql index e0b4e27b180..3e2a72d6ae5 100644 --- a/go/vt/sidecardb/schema/onlineddl/schema_migrations.sql +++ b/go/vt/sidecardb/schema/onlineddl/schema_migrations.sql @@ -45,6 +45,7 @@ CREATE TABLE IF NOT EXISTS schema_migrations `message_timestamp` timestamp(6) NULL DEFAULT NULL, `eta_seconds` bigint NOT NULL DEFAULT '-1', `rows_copied` bigint unsigned NOT NULL DEFAULT '0', + `vreplication_lag_seconds` bigint unsigned NOT NULL DEFAULT '0', `table_rows` bigint NOT NULL DEFAULT '0', `added_unique_keys` int unsigned NOT NULL DEFAULT '0', `removed_unique_keys` int unsigned NOT NULL DEFAULT '0', @@ -76,6 +77,7 @@ CREATE TABLE IF NOT EXISTS schema_migrations `removed_foreign_key_names` text NOT NULL, `last_cutover_attempt_timestamp` timestamp NULL DEFAULT NULL, `force_cutover` tinyint unsigned NOT NULL DEFAULT '0', + `cutover_threshold_seconds` int 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/sqlparser/ast.go b/go/vt/sqlparser/ast.go index 3d4f56ef628..92c67d55999 100644 --- a/go/vt/sqlparser/ast.go +++ b/go/vt/sqlparser/ast.go @@ -491,11 +491,12 @@ type ( // AlterMigration represents a ALTER VITESS_MIGRATION statement AlterMigration struct { - Type AlterMigrationType - UUID string - Expire string - Ratio *Literal - Shards string + Type AlterMigrationType + UUID string + Expire string + Ratio *Literal + Threshold string + Shards string } // AlterTable represents a ALTER TABLE statement. diff --git a/go/vt/sqlparser/ast_equals.go b/go/vt/sqlparser/ast_equals.go index 502a4a78d88..c4c0b34c271 100644 --- a/go/vt/sqlparser/ast_equals.go +++ b/go/vt/sqlparser/ast_equals.go @@ -1790,6 +1790,7 @@ func (cmp *Comparator) RefOfAlterMigration(a, b *AlterMigration) bool { } return a.UUID == b.UUID && a.Expire == b.Expire && + a.Threshold == b.Threshold && a.Shards == b.Shards && a.Type == b.Type && cmp.RefOfLiteral(a.Ratio, b.Ratio) diff --git a/go/vt/sqlparser/ast_format.go b/go/vt/sqlparser/ast_format.go index b9af85d410e..dff17911d8e 100644 --- a/go/vt/sqlparser/ast_format.go +++ b/go/vt/sqlparser/ast_format.go @@ -322,8 +322,13 @@ func (node *AlterMigration) Format(buf *TrackedBuffer) { alterType = "force_cutover" case ForceCutOverAllMigrationType: alterType = "force_cutover all" + case SetCutOverThresholdMigrationType: + alterType = "cutover_threshold" } buf.astPrintf(node, " %#s", alterType) + if node.Threshold != "" { + buf.astPrintf(node, " '%#s'", node.Threshold) + } if node.Expire != "" { buf.astPrintf(node, " expire '%#s'", node.Expire) } diff --git a/go/vt/sqlparser/ast_format_fast.go b/go/vt/sqlparser/ast_format_fast.go index 1950a79a613..9855c70a21d 100644 --- a/go/vt/sqlparser/ast_format_fast.go +++ b/go/vt/sqlparser/ast_format_fast.go @@ -448,9 +448,16 @@ func (node *AlterMigration) FormatFast(buf *TrackedBuffer) { alterType = "force_cutover" case ForceCutOverAllMigrationType: alterType = "force_cutover all" + case SetCutOverThresholdMigrationType: + alterType = "cutover_threshold" } buf.WriteByte(' ') buf.WriteString(alterType) + if node.Threshold != "" { + buf.WriteString(" '") + buf.WriteString(node.Threshold) + buf.WriteByte('\'') + } if node.Expire != "" { buf.WriteString(" expire '") buf.WriteString(node.Expire) diff --git a/go/vt/sqlparser/cached_size.go b/go/vt/sqlparser/cached_size.go index 9703d189b7a..524291a03b4 100644 --- a/go/vt/sqlparser/cached_size.go +++ b/go/vt/sqlparser/cached_size.go @@ -208,7 +208,7 @@ func (cached *AlterMigration) CachedSize(alloc bool) int64 { } size := int64(0) if alloc { - size += int64(64) + size += int64(80) } // field UUID string size += hack.RuntimeAllocSize(int64(len(cached.UUID))) @@ -216,6 +216,8 @@ func (cached *AlterMigration) CachedSize(alloc bool) int64 { size += hack.RuntimeAllocSize(int64(len(cached.Expire))) // field Ratio *vitess.io/vitess/go/vt/sqlparser.Literal size += cached.Ratio.CachedSize(true) + // field Threshold string + size += hack.RuntimeAllocSize(int64(len(cached.Threshold))) // field Shards string size += hack.RuntimeAllocSize(int64(len(cached.Shards))) return size diff --git a/go/vt/sqlparser/constants.go b/go/vt/sqlparser/constants.go index 1c8f831274c..bdf35a2654d 100644 --- a/go/vt/sqlparser/constants.go +++ b/go/vt/sqlparser/constants.go @@ -915,6 +915,7 @@ const ( UnthrottleAllMigrationType ForceCutOverMigrationType ForceCutOverAllMigrationType + SetCutOverThresholdMigrationType ) // ColumnStorage constants diff --git a/go/vt/sqlparser/keywords.go b/go/vt/sqlparser/keywords.go index e32d7d0b72a..5c1b881df30 100644 --- a/go/vt/sqlparser/keywords.go +++ b/go/vt/sqlparser/keywords.go @@ -205,6 +205,7 @@ var keywords = []keyword{ {"current_timestamp", CURRENT_TIMESTAMP}, {"current_user", CURRENT_USER}, {"cursor", UNUSED}, + {"cutover_threshold", CUTOVER_THRESHOLD}, {"data", DATA}, {"database", DATABASE}, {"databases", DATABASES}, diff --git a/go/vt/sqlparser/parse_test.go b/go/vt/sqlparser/parse_test.go index 3a769f05688..d3d5fd469e7 100644 --- a/go/vt/sqlparser/parse_test.go +++ b/go/vt/sqlparser/parse_test.go @@ -2482,6 +2482,8 @@ var ( 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' cutover_threshold '17s'", }, { input: "alter vitess_migration '9748c3b7_7fdb_11eb_ac2c_f875a4d24e90' FORCE_CUTOVER", output: "alter vitess_migration '9748c3b7_7fdb_11eb_ac2c_f875a4d24e90' force_cutover", diff --git a/go/vt/sqlparser/sql.go b/go/vt/sqlparser/sql.go index 682efc9f6ec..befd9b44c3b 100644 --- a/go/vt/sqlparser/sql.go +++ b/go/vt/sqlparser/sql.go @@ -340,431 +340,432 @@ const CLEANUP = 57647 const THROTTLE = 57648 const UNTHROTTLE = 57649 const FORCE_CUTOVER = 57650 -const EXPIRE = 57651 -const RATIO = 57652 -const VITESS_THROTTLER = 57653 -const BEGIN = 57654 -const START = 57655 -const TRANSACTION = 57656 -const COMMIT = 57657 -const ROLLBACK = 57658 -const SAVEPOINT = 57659 -const RELEASE = 57660 -const WORK = 57661 -const CONSISTENT = 57662 -const SNAPSHOT = 57663 -const UNRESOLVED = 57664 -const TRANSACTIONS = 57665 -const BIT = 57666 -const TINYINT = 57667 -const SMALLINT = 57668 -const MEDIUMINT = 57669 -const INT = 57670 -const INTEGER = 57671 -const BIGINT = 57672 -const INTNUM = 57673 -const REAL = 57674 -const DOUBLE = 57675 -const FLOAT_TYPE = 57676 -const FLOAT4_TYPE = 57677 -const FLOAT8_TYPE = 57678 -const DECIMAL_TYPE = 57679 -const NUMERIC = 57680 -const TIME = 57681 -const TIMESTAMP = 57682 -const DATETIME = 57683 -const YEAR = 57684 -const CHAR = 57685 -const VARCHAR = 57686 -const BOOL = 57687 -const CHARACTER = 57688 -const VARBINARY = 57689 -const NCHAR = 57690 -const TEXT = 57691 -const TINYTEXT = 57692 -const MEDIUMTEXT = 57693 -const LONGTEXT = 57694 -const BLOB = 57695 -const TINYBLOB = 57696 -const MEDIUMBLOB = 57697 -const LONGBLOB = 57698 -const JSON = 57699 -const JSON_SCHEMA_VALID = 57700 -const JSON_SCHEMA_VALIDATION_REPORT = 57701 -const ENUM = 57702 -const GEOMETRY = 57703 -const POINT = 57704 -const LINESTRING = 57705 -const POLYGON = 57706 -const GEOMCOLLECTION = 57707 -const GEOMETRYCOLLECTION = 57708 -const MULTIPOINT = 57709 -const MULTILINESTRING = 57710 -const MULTIPOLYGON = 57711 -const ASCII = 57712 -const UNICODE = 57713 -const VECTOR = 57714 -const NULLX = 57715 -const AUTO_INCREMENT = 57716 -const APPROXNUM = 57717 -const SIGNED = 57718 -const UNSIGNED = 57719 -const ZEROFILL = 57720 -const PURGE = 57721 -const BEFORE = 57722 -const CODE = 57723 -const COLLATION = 57724 -const COLUMNS = 57725 -const DATABASES = 57726 -const ENGINES = 57727 -const EVENT = 57728 -const EXTENDED = 57729 -const FIELDS = 57730 -const FULL = 57731 -const FUNCTION = 57732 -const GTID_EXECUTED = 57733 -const KEYSPACES = 57734 -const OPEN = 57735 -const PLUGINS = 57736 -const PRIVILEGES = 57737 -const PROCESSLIST = 57738 -const SCHEMAS = 57739 -const TABLES = 57740 -const TRIGGERS = 57741 -const USER = 57742 -const VGTID_EXECUTED = 57743 -const VITESS_KEYSPACES = 57744 -const VITESS_METADATA = 57745 -const VITESS_MIGRATIONS = 57746 -const VITESS_REPLICATION_STATUS = 57747 -const VITESS_SHARDS = 57748 -const VITESS_TABLETS = 57749 -const VITESS_TARGET = 57750 -const VSCHEMA = 57751 -const VITESS_THROTTLED_APPS = 57752 -const NAMES = 57753 -const GLOBAL = 57754 -const SESSION = 57755 -const ISOLATION = 57756 -const LEVEL = 57757 -const READ = 57758 -const WRITE = 57759 -const ONLY = 57760 -const REPEATABLE = 57761 -const COMMITTED = 57762 -const UNCOMMITTED = 57763 -const SERIALIZABLE = 57764 -const ADDDATE = 57765 -const CURRENT_TIMESTAMP = 57766 -const DATABASE = 57767 -const CURRENT_DATE = 57768 -const CURDATE = 57769 -const DATE_ADD = 57770 -const DATE_SUB = 57771 -const NOW = 57772 -const SUBDATE = 57773 -const CURTIME = 57774 -const CURRENT_TIME = 57775 -const LOCALTIME = 57776 -const LOCALTIMESTAMP = 57777 -const CURRENT_USER = 57778 -const UTC_DATE = 57779 -const UTC_TIME = 57780 -const UTC_TIMESTAMP = 57781 -const SYSDATE = 57782 -const DAY = 57783 -const DAY_HOUR = 57784 -const DAY_MICROSECOND = 57785 -const DAY_MINUTE = 57786 -const DAY_SECOND = 57787 -const HOUR = 57788 -const HOUR_MICROSECOND = 57789 -const HOUR_MINUTE = 57790 -const HOUR_SECOND = 57791 -const MICROSECOND = 57792 -const MINUTE = 57793 -const MINUTE_MICROSECOND = 57794 -const MINUTE_SECOND = 57795 -const MONTH = 57796 -const QUARTER = 57797 -const SECOND = 57798 -const SECOND_MICROSECOND = 57799 -const YEAR_MONTH = 57800 -const WEEK = 57801 -const SQL_TSI_DAY = 57802 -const SQL_TSI_WEEK = 57803 -const SQL_TSI_HOUR = 57804 -const SQL_TSI_MINUTE = 57805 -const SQL_TSI_MONTH = 57806 -const SQL_TSI_QUARTER = 57807 -const SQL_TSI_SECOND = 57808 -const SQL_TSI_MICROSECOND = 57809 -const SQL_TSI_YEAR = 57810 -const REPLACE = 57811 -const CONVERT = 57812 -const CAST = 57813 -const SUBSTR = 57814 -const SUBSTRING = 57815 -const MID = 57816 -const SEPARATOR = 57817 -const TIMESTAMPADD = 57818 -const TIMESTAMPDIFF = 57819 -const WEIGHT_STRING = 57820 -const LTRIM = 57821 -const RTRIM = 57822 -const TRIM = 57823 -const JSON_ARRAY = 57824 -const JSON_OBJECT = 57825 -const JSON_QUOTE = 57826 -const JSON_DEPTH = 57827 -const JSON_TYPE = 57828 -const JSON_LENGTH = 57829 -const JSON_VALID = 57830 -const JSON_ARRAY_APPEND = 57831 -const JSON_ARRAY_INSERT = 57832 -const JSON_INSERT = 57833 -const JSON_MERGE = 57834 -const JSON_MERGE_PATCH = 57835 -const JSON_MERGE_PRESERVE = 57836 -const JSON_REMOVE = 57837 -const JSON_REPLACE = 57838 -const JSON_SET = 57839 -const JSON_UNQUOTE = 57840 -const COUNT = 57841 -const AVG = 57842 -const MAX = 57843 -const MIN = 57844 -const SUM = 57845 -const GROUP_CONCAT = 57846 -const BIT_AND = 57847 -const BIT_OR = 57848 -const BIT_XOR = 57849 -const STD = 57850 -const STDDEV = 57851 -const STDDEV_POP = 57852 -const STDDEV_SAMP = 57853 -const VAR_POP = 57854 -const VAR_SAMP = 57855 -const VARIANCE = 57856 -const ANY_VALUE = 57857 -const REGEXP_INSTR = 57858 -const REGEXP_LIKE = 57859 -const REGEXP_REPLACE = 57860 -const REGEXP_SUBSTR = 57861 -const ExtractValue = 57862 -const UpdateXML = 57863 -const GET_LOCK = 57864 -const RELEASE_LOCK = 57865 -const RELEASE_ALL_LOCKS = 57866 -const IS_FREE_LOCK = 57867 -const IS_USED_LOCK = 57868 -const LOCATE = 57869 -const POSITION = 57870 -const ST_GeometryCollectionFromText = 57871 -const ST_GeometryFromText = 57872 -const ST_LineStringFromText = 57873 -const ST_MultiLineStringFromText = 57874 -const ST_MultiPointFromText = 57875 -const ST_MultiPolygonFromText = 57876 -const ST_PointFromText = 57877 -const ST_PolygonFromText = 57878 -const ST_GeometryCollectionFromWKB = 57879 -const ST_GeometryFromWKB = 57880 -const ST_LineStringFromWKB = 57881 -const ST_MultiLineStringFromWKB = 57882 -const ST_MultiPointFromWKB = 57883 -const ST_MultiPolygonFromWKB = 57884 -const ST_PointFromWKB = 57885 -const ST_PolygonFromWKB = 57886 -const ST_AsBinary = 57887 -const ST_AsText = 57888 -const ST_Dimension = 57889 -const ST_Envelope = 57890 -const ST_IsSimple = 57891 -const ST_IsEmpty = 57892 -const ST_GeometryType = 57893 -const ST_X = 57894 -const ST_Y = 57895 -const ST_Latitude = 57896 -const ST_Longitude = 57897 -const ST_EndPoint = 57898 -const ST_IsClosed = 57899 -const ST_Length = 57900 -const ST_NumPoints = 57901 -const ST_StartPoint = 57902 -const ST_PointN = 57903 -const ST_Area = 57904 -const ST_Centroid = 57905 -const ST_ExteriorRing = 57906 -const ST_InteriorRingN = 57907 -const ST_NumInteriorRings = 57908 -const ST_NumGeometries = 57909 -const ST_GeometryN = 57910 -const ST_LongFromGeoHash = 57911 -const ST_PointFromGeoHash = 57912 -const ST_LatFromGeoHash = 57913 -const ST_GeoHash = 57914 -const ST_AsGeoJSON = 57915 -const ST_GeomFromGeoJSON = 57916 -const MATCH = 57917 -const AGAINST = 57918 -const BOOLEAN = 57919 -const LANGUAGE = 57920 -const WITH = 57921 -const QUERY = 57922 -const EXPANSION = 57923 -const WITHOUT = 57924 -const VALIDATION = 57925 -const ROLLUP = 57926 -const UNUSED = 57927 -const ARRAY = 57928 -const BYTE = 57929 -const CUME_DIST = 57930 -const DESCRIPTION = 57931 -const DENSE_RANK = 57932 -const EMPTY = 57933 -const EXCEPT = 57934 -const FIRST_VALUE = 57935 -const GROUPING = 57936 -const GROUPS = 57937 -const JSON_TABLE = 57938 -const LAG = 57939 -const LAST_VALUE = 57940 -const LATERAL = 57941 -const LEAD = 57942 -const NTH_VALUE = 57943 -const NTILE = 57944 -const OF = 57945 -const OVER = 57946 -const PERCENT_RANK = 57947 -const RANK = 57948 -const RECURSIVE = 57949 -const ROW_NUMBER = 57950 -const SYSTEM = 57951 -const WINDOW = 57952 -const ACTIVE = 57953 -const ADMIN = 57954 -const AUTOEXTEND_SIZE = 57955 -const BUCKETS = 57956 -const CLONE = 57957 -const COLUMN_FORMAT = 57958 -const COMPONENT = 57959 -const DEFINITION = 57960 -const ENFORCED = 57961 -const ENGINE_ATTRIBUTE = 57962 -const EXCLUDE = 57963 -const FOLLOWING = 57964 -const GET_MASTER_PUBLIC_KEY = 57965 -const HISTOGRAM = 57966 -const HISTORY = 57967 -const INACTIVE = 57968 -const INVISIBLE = 57969 -const LOCKED = 57970 -const MASTER_COMPRESSION_ALGORITHMS = 57971 -const MASTER_PUBLIC_KEY_PATH = 57972 -const MASTER_TLS_CIPHERSUITES = 57973 -const MASTER_ZSTD_COMPRESSION_LEVEL = 57974 -const NESTED = 57975 -const NETWORK_NAMESPACE = 57976 -const NOWAIT = 57977 -const NULLS = 57978 -const OJ = 57979 -const OLD = 57980 -const OPTIONAL = 57981 -const ORDINALITY = 57982 -const ORGANIZATION = 57983 -const OTHERS = 57984 -const PARTIAL = 57985 -const PATH = 57986 -const PERSIST = 57987 -const PERSIST_ONLY = 57988 -const PRECEDING = 57989 -const PRIVILEGE_CHECKS_USER = 57990 -const PROCESS = 57991 -const RANDOM = 57992 -const REFERENCE = 57993 -const REQUIRE_ROW_FORMAT = 57994 -const RESOURCE = 57995 -const RESPECT = 57996 -const RESTART = 57997 -const RETAIN = 57998 -const REUSE = 57999 -const ROLE = 58000 -const SECONDARY = 58001 -const SECONDARY_ENGINE = 58002 -const SECONDARY_ENGINE_ATTRIBUTE = 58003 -const SECONDARY_LOAD = 58004 -const SECONDARY_UNLOAD = 58005 -const SIMPLE = 58006 -const SKIP = 58007 -const SRID = 58008 -const THREAD_PRIORITY = 58009 -const TIES = 58010 -const UNBOUNDED = 58011 -const VCPU = 58012 -const VISIBLE = 58013 -const RETURNING = 58014 -const FORMAT_BYTES = 58015 -const FORMAT_PICO_TIME = 58016 -const PS_CURRENT_THREAD_ID = 58017 -const PS_THREAD_ID = 58018 -const GTID_SUBSET = 58019 -const GTID_SUBTRACT = 58020 -const WAIT_FOR_EXECUTED_GTID_SET = 58021 -const WAIT_UNTIL_SQL_THREAD_AFTER_GTIDS = 58022 -const FORMAT = 58023 -const TREE = 58024 -const VITESS = 58025 -const TRADITIONAL = 58026 -const VTEXPLAIN = 58027 -const VEXPLAIN = 58028 -const PLAN = 58029 -const LOCAL = 58030 -const LOW_PRIORITY = 58031 -const NO_WRITE_TO_BINLOG = 58032 -const LOGS = 58033 -const ERROR = 58034 -const GENERAL = 58035 -const HOSTS = 58036 -const OPTIMIZER_COSTS = 58037 -const USER_RESOURCES = 58038 -const SLOW = 58039 -const CHANNEL = 58040 -const RELAY = 58041 -const EXPORT = 58042 -const CURRENT = 58043 -const ROW = 58044 -const ROWS = 58045 -const AVG_ROW_LENGTH = 58046 -const CONNECTION = 58047 -const CHECKSUM = 58048 -const DELAY_KEY_WRITE = 58049 -const ENCRYPTION = 58050 -const ENGINE = 58051 -const INSERT_METHOD = 58052 -const MAX_ROWS = 58053 -const MIN_ROWS = 58054 -const PACK_KEYS = 58055 -const PASSWORD = 58056 -const FIXED = 58057 -const DYNAMIC = 58058 -const COMPRESSED = 58059 -const REDUNDANT = 58060 -const COMPACT = 58061 -const ROW_FORMAT = 58062 -const STATS_AUTO_RECALC = 58063 -const STATS_PERSISTENT = 58064 -const STATS_SAMPLE_PAGES = 58065 -const STORAGE = 58066 -const MEMORY = 58067 -const DISK = 58068 -const PARTITIONS = 58069 -const LINEAR = 58070 -const RANGE = 58071 -const LIST = 58072 -const SUBPARTITION = 58073 -const SUBPARTITIONS = 58074 -const HASH = 58075 +const CUTOVER_THRESHOLD = 57651 +const EXPIRE = 57652 +const RATIO = 57653 +const VITESS_THROTTLER = 57654 +const BEGIN = 57655 +const START = 57656 +const TRANSACTION = 57657 +const COMMIT = 57658 +const ROLLBACK = 57659 +const SAVEPOINT = 57660 +const RELEASE = 57661 +const WORK = 57662 +const CONSISTENT = 57663 +const SNAPSHOT = 57664 +const UNRESOLVED = 57665 +const TRANSACTIONS = 57666 +const BIT = 57667 +const TINYINT = 57668 +const SMALLINT = 57669 +const MEDIUMINT = 57670 +const INT = 57671 +const INTEGER = 57672 +const BIGINT = 57673 +const INTNUM = 57674 +const REAL = 57675 +const DOUBLE = 57676 +const FLOAT_TYPE = 57677 +const FLOAT4_TYPE = 57678 +const FLOAT8_TYPE = 57679 +const DECIMAL_TYPE = 57680 +const NUMERIC = 57681 +const TIME = 57682 +const TIMESTAMP = 57683 +const DATETIME = 57684 +const YEAR = 57685 +const CHAR = 57686 +const VARCHAR = 57687 +const BOOL = 57688 +const CHARACTER = 57689 +const VARBINARY = 57690 +const NCHAR = 57691 +const TEXT = 57692 +const TINYTEXT = 57693 +const MEDIUMTEXT = 57694 +const LONGTEXT = 57695 +const BLOB = 57696 +const TINYBLOB = 57697 +const MEDIUMBLOB = 57698 +const LONGBLOB = 57699 +const JSON = 57700 +const JSON_SCHEMA_VALID = 57701 +const JSON_SCHEMA_VALIDATION_REPORT = 57702 +const ENUM = 57703 +const GEOMETRY = 57704 +const POINT = 57705 +const LINESTRING = 57706 +const POLYGON = 57707 +const GEOMCOLLECTION = 57708 +const GEOMETRYCOLLECTION = 57709 +const MULTIPOINT = 57710 +const MULTILINESTRING = 57711 +const MULTIPOLYGON = 57712 +const ASCII = 57713 +const UNICODE = 57714 +const VECTOR = 57715 +const NULLX = 57716 +const AUTO_INCREMENT = 57717 +const APPROXNUM = 57718 +const SIGNED = 57719 +const UNSIGNED = 57720 +const ZEROFILL = 57721 +const PURGE = 57722 +const BEFORE = 57723 +const CODE = 57724 +const COLLATION = 57725 +const COLUMNS = 57726 +const DATABASES = 57727 +const ENGINES = 57728 +const EVENT = 57729 +const EXTENDED = 57730 +const FIELDS = 57731 +const FULL = 57732 +const FUNCTION = 57733 +const GTID_EXECUTED = 57734 +const KEYSPACES = 57735 +const OPEN = 57736 +const PLUGINS = 57737 +const PRIVILEGES = 57738 +const PROCESSLIST = 57739 +const SCHEMAS = 57740 +const TABLES = 57741 +const TRIGGERS = 57742 +const USER = 57743 +const VGTID_EXECUTED = 57744 +const VITESS_KEYSPACES = 57745 +const VITESS_METADATA = 57746 +const VITESS_MIGRATIONS = 57747 +const VITESS_REPLICATION_STATUS = 57748 +const VITESS_SHARDS = 57749 +const VITESS_TABLETS = 57750 +const VITESS_TARGET = 57751 +const VSCHEMA = 57752 +const VITESS_THROTTLED_APPS = 57753 +const NAMES = 57754 +const GLOBAL = 57755 +const SESSION = 57756 +const ISOLATION = 57757 +const LEVEL = 57758 +const READ = 57759 +const WRITE = 57760 +const ONLY = 57761 +const REPEATABLE = 57762 +const COMMITTED = 57763 +const UNCOMMITTED = 57764 +const SERIALIZABLE = 57765 +const ADDDATE = 57766 +const CURRENT_TIMESTAMP = 57767 +const DATABASE = 57768 +const CURRENT_DATE = 57769 +const CURDATE = 57770 +const DATE_ADD = 57771 +const DATE_SUB = 57772 +const NOW = 57773 +const SUBDATE = 57774 +const CURTIME = 57775 +const CURRENT_TIME = 57776 +const LOCALTIME = 57777 +const LOCALTIMESTAMP = 57778 +const CURRENT_USER = 57779 +const UTC_DATE = 57780 +const UTC_TIME = 57781 +const UTC_TIMESTAMP = 57782 +const SYSDATE = 57783 +const DAY = 57784 +const DAY_HOUR = 57785 +const DAY_MICROSECOND = 57786 +const DAY_MINUTE = 57787 +const DAY_SECOND = 57788 +const HOUR = 57789 +const HOUR_MICROSECOND = 57790 +const HOUR_MINUTE = 57791 +const HOUR_SECOND = 57792 +const MICROSECOND = 57793 +const MINUTE = 57794 +const MINUTE_MICROSECOND = 57795 +const MINUTE_SECOND = 57796 +const MONTH = 57797 +const QUARTER = 57798 +const SECOND = 57799 +const SECOND_MICROSECOND = 57800 +const YEAR_MONTH = 57801 +const WEEK = 57802 +const SQL_TSI_DAY = 57803 +const SQL_TSI_WEEK = 57804 +const SQL_TSI_HOUR = 57805 +const SQL_TSI_MINUTE = 57806 +const SQL_TSI_MONTH = 57807 +const SQL_TSI_QUARTER = 57808 +const SQL_TSI_SECOND = 57809 +const SQL_TSI_MICROSECOND = 57810 +const SQL_TSI_YEAR = 57811 +const REPLACE = 57812 +const CONVERT = 57813 +const CAST = 57814 +const SUBSTR = 57815 +const SUBSTRING = 57816 +const MID = 57817 +const SEPARATOR = 57818 +const TIMESTAMPADD = 57819 +const TIMESTAMPDIFF = 57820 +const WEIGHT_STRING = 57821 +const LTRIM = 57822 +const RTRIM = 57823 +const TRIM = 57824 +const JSON_ARRAY = 57825 +const JSON_OBJECT = 57826 +const JSON_QUOTE = 57827 +const JSON_DEPTH = 57828 +const JSON_TYPE = 57829 +const JSON_LENGTH = 57830 +const JSON_VALID = 57831 +const JSON_ARRAY_APPEND = 57832 +const JSON_ARRAY_INSERT = 57833 +const JSON_INSERT = 57834 +const JSON_MERGE = 57835 +const JSON_MERGE_PATCH = 57836 +const JSON_MERGE_PRESERVE = 57837 +const JSON_REMOVE = 57838 +const JSON_REPLACE = 57839 +const JSON_SET = 57840 +const JSON_UNQUOTE = 57841 +const COUNT = 57842 +const AVG = 57843 +const MAX = 57844 +const MIN = 57845 +const SUM = 57846 +const GROUP_CONCAT = 57847 +const BIT_AND = 57848 +const BIT_OR = 57849 +const BIT_XOR = 57850 +const STD = 57851 +const STDDEV = 57852 +const STDDEV_POP = 57853 +const STDDEV_SAMP = 57854 +const VAR_POP = 57855 +const VAR_SAMP = 57856 +const VARIANCE = 57857 +const ANY_VALUE = 57858 +const REGEXP_INSTR = 57859 +const REGEXP_LIKE = 57860 +const REGEXP_REPLACE = 57861 +const REGEXP_SUBSTR = 57862 +const ExtractValue = 57863 +const UpdateXML = 57864 +const GET_LOCK = 57865 +const RELEASE_LOCK = 57866 +const RELEASE_ALL_LOCKS = 57867 +const IS_FREE_LOCK = 57868 +const IS_USED_LOCK = 57869 +const LOCATE = 57870 +const POSITION = 57871 +const ST_GeometryCollectionFromText = 57872 +const ST_GeometryFromText = 57873 +const ST_LineStringFromText = 57874 +const ST_MultiLineStringFromText = 57875 +const ST_MultiPointFromText = 57876 +const ST_MultiPolygonFromText = 57877 +const ST_PointFromText = 57878 +const ST_PolygonFromText = 57879 +const ST_GeometryCollectionFromWKB = 57880 +const ST_GeometryFromWKB = 57881 +const ST_LineStringFromWKB = 57882 +const ST_MultiLineStringFromWKB = 57883 +const ST_MultiPointFromWKB = 57884 +const ST_MultiPolygonFromWKB = 57885 +const ST_PointFromWKB = 57886 +const ST_PolygonFromWKB = 57887 +const ST_AsBinary = 57888 +const ST_AsText = 57889 +const ST_Dimension = 57890 +const ST_Envelope = 57891 +const ST_IsSimple = 57892 +const ST_IsEmpty = 57893 +const ST_GeometryType = 57894 +const ST_X = 57895 +const ST_Y = 57896 +const ST_Latitude = 57897 +const ST_Longitude = 57898 +const ST_EndPoint = 57899 +const ST_IsClosed = 57900 +const ST_Length = 57901 +const ST_NumPoints = 57902 +const ST_StartPoint = 57903 +const ST_PointN = 57904 +const ST_Area = 57905 +const ST_Centroid = 57906 +const ST_ExteriorRing = 57907 +const ST_InteriorRingN = 57908 +const ST_NumInteriorRings = 57909 +const ST_NumGeometries = 57910 +const ST_GeometryN = 57911 +const ST_LongFromGeoHash = 57912 +const ST_PointFromGeoHash = 57913 +const ST_LatFromGeoHash = 57914 +const ST_GeoHash = 57915 +const ST_AsGeoJSON = 57916 +const ST_GeomFromGeoJSON = 57917 +const MATCH = 57918 +const AGAINST = 57919 +const BOOLEAN = 57920 +const LANGUAGE = 57921 +const WITH = 57922 +const QUERY = 57923 +const EXPANSION = 57924 +const WITHOUT = 57925 +const VALIDATION = 57926 +const ROLLUP = 57927 +const UNUSED = 57928 +const ARRAY = 57929 +const BYTE = 57930 +const CUME_DIST = 57931 +const DESCRIPTION = 57932 +const DENSE_RANK = 57933 +const EMPTY = 57934 +const EXCEPT = 57935 +const FIRST_VALUE = 57936 +const GROUPING = 57937 +const GROUPS = 57938 +const JSON_TABLE = 57939 +const LAG = 57940 +const LAST_VALUE = 57941 +const LATERAL = 57942 +const LEAD = 57943 +const NTH_VALUE = 57944 +const NTILE = 57945 +const OF = 57946 +const OVER = 57947 +const PERCENT_RANK = 57948 +const RANK = 57949 +const RECURSIVE = 57950 +const ROW_NUMBER = 57951 +const SYSTEM = 57952 +const WINDOW = 57953 +const ACTIVE = 57954 +const ADMIN = 57955 +const AUTOEXTEND_SIZE = 57956 +const BUCKETS = 57957 +const CLONE = 57958 +const COLUMN_FORMAT = 57959 +const COMPONENT = 57960 +const DEFINITION = 57961 +const ENFORCED = 57962 +const ENGINE_ATTRIBUTE = 57963 +const EXCLUDE = 57964 +const FOLLOWING = 57965 +const GET_MASTER_PUBLIC_KEY = 57966 +const HISTOGRAM = 57967 +const HISTORY = 57968 +const INACTIVE = 57969 +const INVISIBLE = 57970 +const LOCKED = 57971 +const MASTER_COMPRESSION_ALGORITHMS = 57972 +const MASTER_PUBLIC_KEY_PATH = 57973 +const MASTER_TLS_CIPHERSUITES = 57974 +const MASTER_ZSTD_COMPRESSION_LEVEL = 57975 +const NESTED = 57976 +const NETWORK_NAMESPACE = 57977 +const NOWAIT = 57978 +const NULLS = 57979 +const OJ = 57980 +const OLD = 57981 +const OPTIONAL = 57982 +const ORDINALITY = 57983 +const ORGANIZATION = 57984 +const OTHERS = 57985 +const PARTIAL = 57986 +const PATH = 57987 +const PERSIST = 57988 +const PERSIST_ONLY = 57989 +const PRECEDING = 57990 +const PRIVILEGE_CHECKS_USER = 57991 +const PROCESS = 57992 +const RANDOM = 57993 +const REFERENCE = 57994 +const REQUIRE_ROW_FORMAT = 57995 +const RESOURCE = 57996 +const RESPECT = 57997 +const RESTART = 57998 +const RETAIN = 57999 +const REUSE = 58000 +const ROLE = 58001 +const SECONDARY = 58002 +const SECONDARY_ENGINE = 58003 +const SECONDARY_ENGINE_ATTRIBUTE = 58004 +const SECONDARY_LOAD = 58005 +const SECONDARY_UNLOAD = 58006 +const SIMPLE = 58007 +const SKIP = 58008 +const SRID = 58009 +const THREAD_PRIORITY = 58010 +const TIES = 58011 +const UNBOUNDED = 58012 +const VCPU = 58013 +const VISIBLE = 58014 +const RETURNING = 58015 +const FORMAT_BYTES = 58016 +const FORMAT_PICO_TIME = 58017 +const PS_CURRENT_THREAD_ID = 58018 +const PS_THREAD_ID = 58019 +const GTID_SUBSET = 58020 +const GTID_SUBTRACT = 58021 +const WAIT_FOR_EXECUTED_GTID_SET = 58022 +const WAIT_UNTIL_SQL_THREAD_AFTER_GTIDS = 58023 +const FORMAT = 58024 +const TREE = 58025 +const VITESS = 58026 +const TRADITIONAL = 58027 +const VTEXPLAIN = 58028 +const VEXPLAIN = 58029 +const PLAN = 58030 +const LOCAL = 58031 +const LOW_PRIORITY = 58032 +const NO_WRITE_TO_BINLOG = 58033 +const LOGS = 58034 +const ERROR = 58035 +const GENERAL = 58036 +const HOSTS = 58037 +const OPTIMIZER_COSTS = 58038 +const USER_RESOURCES = 58039 +const SLOW = 58040 +const CHANNEL = 58041 +const RELAY = 58042 +const EXPORT = 58043 +const CURRENT = 58044 +const ROW = 58045 +const ROWS = 58046 +const AVG_ROW_LENGTH = 58047 +const CONNECTION = 58048 +const CHECKSUM = 58049 +const DELAY_KEY_WRITE = 58050 +const ENCRYPTION = 58051 +const ENGINE = 58052 +const INSERT_METHOD = 58053 +const MAX_ROWS = 58054 +const MIN_ROWS = 58055 +const PACK_KEYS = 58056 +const PASSWORD = 58057 +const FIXED = 58058 +const DYNAMIC = 58059 +const COMPRESSED = 58060 +const REDUNDANT = 58061 +const COMPACT = 58062 +const ROW_FORMAT = 58063 +const STATS_AUTO_RECALC = 58064 +const STATS_PERSISTENT = 58065 +const STATS_SAMPLE_PAGES = 58066 +const STORAGE = 58067 +const MEMORY = 58068 +const DISK = 58069 +const PARTITIONS = 58070 +const LINEAR = 58071 +const RANGE = 58072 +const LIST = 58073 +const SUBPARTITION = 58074 +const SUBPARTITIONS = 58075 +const HASH = 58076 var yyToknames = [...]string{ "$end", @@ -1092,6 +1093,7 @@ var yyToknames = [...]string{ "THROTTLE", "UNTHROTTLE", "FORCE_CUTOVER", + "CUTOVER_THRESHOLD", "EXPIRE", "RATIO", "VITESS_THROTTLER", @@ -1537,29 +1539,29 @@ var yyExca = [...]int{ -2, 40, -1, 52, 1, 157, - 751, 157, + 752, 157, -2, 165, -1, 53, 149, 165, 191, 165, - 363, 165, + 364, 165, -2, 523, -1, 61, - 38, 783, - 254, 783, - 265, 783, - 300, 797, - 301, 797, - -2, 785, + 38, 784, + 254, 784, + 265, 784, + 300, 798, + 301, 798, + -2, 786, -1, 66, - 256, 821, - -2, 819, + 256, 822, + -2, 820, -1, 122, - 253, 1620, + 253, 1621, -2, 131, -1, 124, 1, 158, - 751, 158, + 752, 158, -2, 165, -1, 135, 150, 408, @@ -1568,214 +1570,214 @@ var yyExca = [...]int{ -1, 154, 149, 165, 191, 165, - 363, 165, + 364, 165, -2, 532, - -1, 748, + -1, 749, 177, 41, -2, 43, - -1, 957, - 95, 1637, - -2, 1481, -1, 958, 95, 1638, - 236, 1642, -2, 1482, -1, 959, - 236, 1641, + 95, 1639, + 236, 1643, + -2, 1483, + -1, 960, + 236, 1642, -2, 42, - -1, 1043, - 65, 895, - -2, 908, - -1, 1131, - 264, 1108, - 269, 1108, + -1, 1044, + 65, 896, + -2, 909, + -1, 1132, + 264, 1109, + 269, 1109, -2, 419, - -1, 1216, + -1, 1217, 1, 580, - 751, 580, + 752, 580, -2, 165, - -1, 1527, - 236, 1642, - -2, 1482, - -1, 1739, - 65, 896, - -2, 912, + -1, 1528, + 236, 1643, + -2, 1483, -1, 1740, 65, 897, -2, 913, - -1, 1800, + -1, 1741, + 65, 898, + -2, 914, + -1, 1801, 149, 165, 191, 165, - 363, 165, + 364, 165, -2, 458, - -1, 1883, + -1, 1884, 150, 408, 259, 408, -2, 512, - -1, 1892, - 264, 1109, - 269, 1109, + -1, 1893, + 264, 1110, + 269, 1110, -2, 420, - -1, 2341, - 236, 1646, - -2, 1640, -1, 2342, - 236, 1642, - -2, 1638, - -1, 2445, + 236, 1647, + -2, 1641, + -1, 2343, + 236, 1643, + -2, 1639, + -1, 2446, 149, 165, 191, 165, - 363, 165, + 364, 165, -2, 459, - -1, 2452, + -1, 2453, 28, 186, -2, 188, - -1, 2915, + -1, 2917, 86, 96, 96, 96, - -2, 975, - -1, 2983, - 726, 701, - -2, 675, - -1, 3207, - 55, 1585, - -2, 1579, - -1, 4044, - 726, 701, - -2, 689, - -1, 4136, - 98, 633, - 104, 633, - 114, 633, - 193, 633, - 194, 633, - 195, 633, - 196, 633, - 197, 633, - 198, 633, - 199, 633, - 200, 633, - 201, 633, - 202, 633, - 203, 633, - 204, 633, - 205, 633, - 206, 633, - 207, 633, - 208, 633, - 209, 633, - 210, 633, - 211, 633, - 212, 633, - 213, 633, - 214, 633, - 215, 633, - 216, 633, - 217, 633, - 218, 633, - 219, 633, - 220, 633, - 221, 633, - 222, 633, - 223, 633, - 224, 633, - 225, 633, - 226, 633, - 227, 633, - 228, 633, - 229, 633, - 230, 633, - 231, 633, - 232, 633, - 233, 633, - 234, 633, - -2, 2017, + -2, 976, + -1, 2985, + 727, 702, + -2, 676, + -1, 3210, + 55, 1586, + -2, 1580, + -1, 4047, + 727, 702, + -2, 690, + -1, 4139, + 98, 634, + 104, 634, + 114, 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, + 223, 634, + 224, 634, + 225, 634, + 226, 634, + 227, 634, + 228, 634, + 229, 634, + 230, 634, + 231, 634, + 232, 634, + 233, 634, + 234, 634, + -2, 2019, } const yyPrivate = 57344 -const yyLast = 56607 +const yyLast = 57002 var yyAct = [...]int{ - 973, 3695, 3696, 87, 3694, 4025, 4179, 4211, 961, 4115, - 4134, 2146, 3358, 2370, 4007, 4224, 4103, 1282, 4178, 1284, - 968, 3644, 960, 2134, 3259, 2442, 3266, 2072, 3930, 3220, - 5, 3307, 3494, 3316, 3321, 4005, 3318, 3317, 3315, 3320, - 3319, 3073, 3631, 2372, 3158, 3336, 3274, 2517, 2013, 752, - 3335, 3224, 3221, 3534, 3047, 3072, 3540, 3218, 2875, 3737, - 2397, 747, 3338, 3208, 3524, 2480, 746, 922, 2949, 3365, - 1860, 1803, 780, 3029, 2504, 2980, 2485, 1759, 1093, 4076, - 921, 2951, 2950, 2430, 163, 2413, 1041, 2548, 87, 2881, - 2418, 1061, 1038, 1068, 2867, 1908, 2851, 2900, 2326, 2416, - 926, 41, 1163, 42, 1139, 2130, 2294, 1041, 2080, 2417, - 2293, 2526, 3021, 2503, 43, 3562, 2168, 2405, 1890, 2487, - 2565, 2942, 1103, 1126, 1121, 149, 1792, 2917, 1772, 2420, - 100, 1720, 1540, 2174, 104, 105, 2105, 2094, 1465, 1448, - 2009, 1897, 1100, 1097, 1869, 3223, 762, 1132, 1989, 2476, - 1101, 1127, 1128, 1791, 2477, 1777, 1078, 1129, 757, 1080, - 1742, 1050, 2201, 1523, 2182, 2888, 3732, 1499, 749, 1047, - 2849, 2071, 85, 107, 1272, 2021, 167, 127, 2398, 125, - 126, 3495, 1882, 1060, 132, 3724, 1040, 133, 1044, 1045, - 1073, 1212, 93, 923, 3551, 99, 1549, 1036, 1048, 98, - 4212, 750, 1046, 739, 1544, 3632, 1072, 1063, 1280, 3304, - 2519, 1258, 84, 2519, 2520, 2521, 4060, 3003, 3002, 2563, - 2971, 106, 1466, 3624, 1035, 4161, 3037, 3038, 3587, 128, - 1053, 4055, 4056, 2367, 2368, 2087, 134, 3699, 3699, 1168, - 682, 1143, 4061, 1094, 2086, 2085, 2084, 2083, 2082, 2052, - 1228, 679, 2847, 680, 1165, 2616, 3204, 3162, 4234, 2552, - 4177, 4182, 4202, 1176, 4155, 1110, 1105, 1182, 1183, 1184, - 3498, 1187, 1188, 1189, 1190, 1087, 740, 1193, 1194, 1195, - 1196, 1197, 1198, 1199, 1200, 1201, 1202, 1203, 1204, 1205, - 1206, 1207, 1208, 1209, 1142, 1088, 1054, 1763, 1039, 1062, - 1753, 128, 1117, 2551, 1116, 1115, 1037, 1118, 2, 1761, - 1482, 95, 3326, 1169, 1172, 1173, 1974, 4034, 95, 2877, - 724, 3497, 3326, 1229, 2394, 4217, 2973, 1764, 1462, 2393, - 1476, 1459, 4056, 3698, 3698, 3323, 2996, 4008, 724, 1762, - 2391, 2812, 95, 4165, 2092, 3384, 1185, 4130, 2993, 1034, - 4216, 4163, 927, 111, 112, 113, 3926, 116, 3925, 1167, - 122, 718, 2108, 191, 1166, 4192, 674, 95, 4164, 128, - 190, 3936, 3324, 1086, 1090, 925, 4162, 3637, 737, 738, - 3638, 4159, 3324, 86, 976, 977, 978, 718, 1029, 1030, - 1031, 1032, 86, 129, 3656, 1043, 86, 1450, 3645, 4104, - 4112, 3330, 2545, 1119, 2623, 3935, 2891, 2139, 4139, 172, - 3410, 3330, 976, 977, 978, 1871, 3256, 3257, 2848, 1086, - 1090, 925, 718, 1075, 1076, 2926, 3655, 1793, 2925, 1794, - 713, 2927, 2437, 2438, 2892, 3255, 1461, 3036, 4116, 2064, - 2065, 2620, 1472, 2550, 2436, 1464, 3020, 3751, 1248, 1253, - 1254, 1277, 1027, 1026, 4026, 2931, 2974, 1211, 1236, 1479, - 1249, 1480, 1481, 1237, 1242, 2938, 95, 718, 718, 169, - 718, 1478, 170, 2017, 2621, 95, 2455, 2454, 698, 95, - 1236, 718, 3362, 3360, 3392, 1237, 1114, 3094, 1221, 1222, - 3390, 696, 4144, 1235, 1443, 1234, 2884, 2885, 189, 2614, - 2063, 732, 2067, 736, 4039, 1995, 2369, 2210, 730, 718, - 2401, 3366, 4142, 1724, 4120, 4183, 3327, 3022, 2572, 2527, - 1224, 2496, 4148, 4149, 2401, 86, 3327, 1789, 88, 2981, - 1964, 693, 3276, 3277, 2566, 3978, 4184, 3979, 3006, 4143, - 708, 1460, 4120, 1466, 1112, 2490, 3353, 1990, 2590, 4214, - 2591, 1186, 2592, 1269, 3354, 703, 1442, 1255, 1250, 1274, - 1449, 1257, 1243, 1500, 1276, 1217, 706, 1256, 2570, 716, - 1275, 1251, 1252, 2573, 1965, 719, 1966, 717, 3363, 3361, - 2617, 3024, 2618, 2568, 3626, 3381, 3625, 1501, 1502, 1503, - 1504, 1505, 1506, 1507, 1509, 1508, 1510, 1511, 2593, 1192, - 1191, 719, 3910, 1727, 1079, 1152, 2202, 1141, 95, 1141, - 2569, 2204, 1150, 173, 2018, 2209, 2205, 2530, 3622, 2206, - 2207, 2208, 179, 2571, 2203, 2211, 2212, 2213, 2214, 2215, - 2216, 2217, 2218, 2219, 1122, 3703, 719, 683, 1123, 685, - 699, 3275, 721, 2414, 720, 689, 1123, 687, 691, 700, - 692, 1476, 686, 3278, 697, 3095, 1875, 688, 701, 702, - 705, 709, 710, 711, 707, 704, 4156, 695, 722, 1162, - 1161, 3161, 3010, 1514, 3011, 1160, 1113, 1159, 1158, 3537, - 1157, 719, 719, 1156, 719, 2579, 2575, 2577, 2578, 2576, - 2580, 2581, 2582, 1155, 1154, 719, 3278, 1149, 2489, 4235, - 4189, 1089, 1083, 1081, 1098, 2399, 2400, 1098, 1171, 1135, - 1140, 1096, 1140, 1281, 1134, 1281, 1281, 1098, 1170, 2399, - 2400, 1134, 2010, 719, 1074, 1999, 1870, 1997, 1998, 1996, - 2000, 2001, 2002, 3621, 3025, 2556, 2555, 2006, 1471, 1468, - 1469, 1470, 1475, 1477, 1474, 1451, 1473, 1089, 1083, 1081, - 164, 3189, 1179, 3187, 3298, 1153, 1467, 3005, 1790, 3041, - 1868, 2975, 1151, 1472, 1867, 1041, 1524, 1529, 1530, 1866, - 1533, 1535, 1536, 1537, 1538, 1539, 2991, 1542, 1543, 1545, - 1545, 2007, 1545, 1545, 1550, 1550, 1550, 1553, 1554, 1555, + 974, 3698, 3699, 87, 3697, 4137, 4214, 4028, 4118, 3361, + 4227, 4181, 4106, 3647, 4182, 969, 1285, 961, 3262, 2135, + 2014, 2443, 2170, 3269, 2371, 1283, 3497, 4010, 2147, 3933, + 3319, 3310, 3324, 3321, 3320, 3318, 3323, 3322, 2073, 3223, + 4008, 5, 3634, 2373, 3161, 3076, 3339, 3277, 2518, 753, + 3338, 3227, 3224, 3543, 962, 3221, 3740, 3050, 3537, 923, + 747, 3211, 2877, 3075, 1804, 2398, 2481, 781, 2414, 1760, + 748, 2951, 4079, 3032, 3368, 1861, 3527, 2417, 2982, 922, + 3341, 2505, 2486, 3565, 2952, 2549, 1042, 1164, 87, 163, + 1094, 927, 2953, 2431, 42, 1062, 1039, 743, 2883, 1069, + 1909, 2902, 2419, 2869, 41, 2418, 2853, 1042, 2327, 2294, + 2169, 2295, 2131, 3023, 2527, 149, 43, 1891, 2504, 2081, + 2406, 2488, 1104, 2944, 1122, 2566, 1127, 1793, 2919, 1773, + 2421, 1721, 2890, 1541, 2175, 100, 2106, 2095, 104, 105, + 1466, 1449, 1898, 2010, 1101, 1098, 3226, 763, 1133, 1870, + 1102, 2477, 1990, 1128, 1130, 1129, 2478, 1792, 1079, 1778, + 1081, 1051, 758, 1140, 750, 2399, 1743, 2183, 3735, 2202, + 1048, 2851, 1524, 85, 2072, 2022, 1061, 1041, 107, 1045, + 3727, 1500, 99, 1273, 167, 3554, 3498, 1883, 127, 125, + 126, 132, 1046, 1213, 1037, 133, 924, 1049, 1064, 751, + 1074, 84, 1545, 98, 1047, 106, 4215, 1281, 3635, 3307, + 1259, 740, 1073, 93, 2520, 2521, 2522, 4063, 1550, 2520, + 3005, 3004, 2564, 2973, 3627, 1036, 4164, 683, 3040, 3590, + 3041, 4059, 2368, 2369, 4058, 1054, 2088, 128, 2087, 1166, + 2086, 134, 2085, 4064, 1169, 2084, 1095, 3329, 2083, 2053, + 1229, 4158, 1183, 1184, 1185, 2849, 1188, 1189, 1190, 1191, + 3326, 2618, 1194, 1195, 1196, 1197, 1198, 1199, 1200, 1201, + 1202, 1203, 1204, 1205, 1206, 1207, 1208, 1209, 1210, 1088, + 3207, 4185, 1089, 1055, 741, 2, 680, 1040, 681, 1764, + 1038, 1143, 95, 1975, 1119, 2553, 1762, 3702, 3702, 3329, + 1144, 2879, 4220, 725, 3165, 1111, 2392, 3327, 4237, 128, + 1170, 1173, 1174, 1118, 1117, 1116, 95, 4037, 95, 1765, + 1063, 1463, 1177, 1106, 1460, 2975, 1763, 4219, 4180, 1230, + 111, 112, 113, 1186, 116, 4059, 3333, 122, 725, 2552, + 191, 1483, 95, 675, 4205, 3501, 928, 3500, 4168, 4166, + 1035, 2395, 2394, 2995, 2998, 738, 739, 190, 4011, 3327, + 719, 1754, 2109, 2814, 2093, 1030, 1031, 1032, 1033, 1087, + 1091, 926, 1044, 4167, 4165, 3387, 4133, 128, 3929, 3928, + 129, 1168, 190, 1167, 977, 978, 979, 719, 3333, 977, + 978, 979, 3640, 3701, 3701, 3641, 172, 1451, 4195, 3939, + 1076, 1077, 1120, 4147, 86, 129, 1087, 1091, 926, 4162, + 3659, 3648, 4107, 4115, 2546, 86, 86, 3938, 719, 2625, + 2140, 172, 4142, 4145, 3413, 1872, 3012, 2850, 3013, 1462, + 714, 3259, 3260, 4151, 4152, 2551, 2438, 2439, 4119, 2893, + 3258, 1479, 2933, 3279, 3280, 1794, 1115, 1795, 1222, 1223, + 4146, 3330, 2065, 2066, 3039, 719, 169, 3658, 2928, 170, + 2622, 2927, 2437, 3022, 2929, 1278, 3754, 2894, 1254, 1255, + 1249, 1028, 1110, 1027, 1237, 1112, 4029, 2940, 699, 1238, + 1225, 169, 1250, 1243, 170, 189, 2018, 95, 719, 2623, + 1480, 697, 1481, 1482, 2456, 2455, 3365, 1212, 95, 95, + 719, 3395, 1237, 3330, 1113, 2370, 719, 1238, 3363, 4042, + 189, 2886, 2887, 1444, 4123, 1236, 2616, 1235, 3393, 4123, + 2068, 1790, 2064, 733, 737, 731, 3097, 3369, 2528, 3024, + 1187, 694, 1996, 1725, 1461, 4186, 3981, 3356, 3982, 2983, + 709, 3008, 2567, 2402, 2573, 3357, 1965, 86, 4217, 2592, + 88, 2593, 3278, 2594, 1991, 704, 4187, 1266, 1270, 1268, + 1450, 1252, 1253, 2569, 3281, 1443, 1275, 707, 2211, 1218, + 717, 1258, 2497, 3027, 1115, 720, 1107, 1256, 718, 1277, + 3913, 1251, 1244, 1109, 1108, 1276, 2619, 1257, 2620, 1501, + 1966, 3629, 1967, 3366, 3628, 2595, 2491, 1265, 1267, 2574, + 173, 1080, 720, 3625, 1142, 3364, 1142, 1193, 1192, 179, + 1153, 3384, 1151, 1502, 1503, 1504, 1505, 1506, 1507, 1508, + 1510, 1509, 1511, 1512, 2531, 173, 1728, 1123, 2019, 2571, + 95, 1124, 1113, 720, 179, 3281, 3706, 1114, 684, 2415, + 686, 700, 1124, 722, 1162, 721, 690, 1161, 688, 692, + 701, 693, 1160, 687, 4159, 698, 1159, 1158, 689, 702, + 703, 706, 710, 711, 712, 708, 705, 2203, 696, 723, + 720, 2570, 2205, 1157, 1156, 1155, 2210, 2206, 1150, 1876, + 2207, 2208, 2209, 1163, 2572, 2204, 2212, 2213, 2214, 2215, + 2216, 2217, 2218, 2219, 2220, 3098, 3540, 1099, 1090, 1084, + 1082, 1099, 1136, 720, 2976, 1097, 4238, 1141, 1263, 1141, + 4192, 1099, 1264, 1075, 1282, 720, 1282, 1282, 3624, 3164, + 1135, 720, 1269, 2580, 2576, 2578, 2579, 2577, 2581, 2582, + 2583, 2584, 2011, 1871, 3028, 1090, 1084, 1082, 164, 2400, + 2401, 2557, 3173, 2556, 2007, 1452, 1180, 1262, 3192, 2490, + 3190, 3301, 2000, 1791, 1998, 1999, 1997, 2001, 2002, 2003, + 1154, 3007, 1152, 164, 1869, 1114, 1042, 1525, 1530, 1531, + 1868, 1534, 1536, 1537, 1538, 1539, 1540, 1867, 1543, 1544, + 1546, 1546, 3010, 1546, 1546, 1551, 1551, 1551, 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, @@ -1788,1079 +1790,1090 @@ var yyAct = [...]int{ 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, 1521, 3697, 3697, 1270, 1677, 4033, 1679, 1680, 1681, - 1682, 1683, 2549, 1440, 1441, 2972, 3585, 3586, 1439, 1550, - 1550, 1550, 1550, 1550, 1550, 974, 2940, 1120, 3654, 1534, - 1223, 1220, 974, 723, 1690, 1691, 1692, 1693, 1694, 1695, - 1696, 1697, 1698, 1699, 1700, 1701, 1702, 1703, 2995, 1525, - 1517, 1518, 1519, 1520, 714, 4118, 974, 94, 1514, 2621, - 1531, 4079, 2852, 2854, 2622, 1717, 94, 1864, 1082, 715, - 94, 1551, 1552, 1227, 1546, 1896, 1547, 1548, 3538, 165, - 3328, 3329, 1215, 4118, 1458, 3482, 177, 4117, 1141, 89, - 3328, 3329, 2994, 3332, 1233, 3382, 673, 1232, 4157, 1238, - 1239, 1240, 1241, 3332, 1976, 1975, 1977, 1978, 1979, 3170, - 3019, 1515, 1516, 3018, 1082, 4117, 4147, 3008, 2547, 1723, - 4021, 2493, 190, 1278, 1279, 3576, 3558, 185, 1041, 2922, - 1752, 3028, 1041, 2887, 1141, 2824, 1246, 1109, 1041, 2142, - 1111, 1265, 1781, 1267, 2638, 129, 1678, 1226, 3169, 1471, - 1468, 1469, 1470, 1475, 1477, 1474, 4228, 1473, 2882, 681, - 4146, 172, 2494, 2443, 1714, 1715, 1178, 1467, 2166, 2492, - 166, 171, 168, 174, 175, 176, 178, 180, 181, 182, - 183, 1264, 1266, 1141, 1895, 1514, 184, 186, 187, 188, - 124, 1140, 1506, 1507, 1509, 1508, 1510, 1511, 1994, 94, - 1511, 3254, 2649, 2495, 1259, 1141, 1753, 1494, 2183, 1057, - 119, 2022, 1273, 2491, 4047, 1684, 1685, 1686, 1687, 1688, - 1689, 169, 1733, 1734, 170, 2184, 104, 105, 1731, 1164, - 3031, 3617, 1735, 1715, 3550, 3030, 2853, 1140, 1040, 1114, - 2110, 1106, 1144, 1134, 1114, 1210, 1721, 1146, 1108, 1107, - 189, 1147, 1145, 2567, 2111, 1512, 1513, 2109, 1141, 2076, - 2003, 1795, 2175, 1231, 3068, 107, 4193, 2158, 2147, 2148, - 2149, 2150, 2160, 2151, 2152, 2153, 2165, 2161, 2154, 2155, - 2162, 2163, 2164, 2156, 2157, 2159, 1140, 1482, 120, 1141, - 2649, 1262, 1134, 1137, 1138, 1263, 1098, 1112, 1888, 2509, - 1131, 1135, 3031, 1481, 2175, 1268, 2658, 3030, 1140, 1214, - 1729, 1872, 1873, 1874, 1134, 1137, 1138, 3746, 1098, 1718, - 1730, 1130, 1131, 1135, 3592, 2546, 3591, 1732, 2534, 1959, - 1261, 1755, 1898, 1898, 1905, 1910, 2015, 1911, 1758, 1913, - 1915, 1881, 1941, 1919, 1921, 1923, 1925, 1927, 1900, 3262, - 1039, 1037, 1245, 1991, 1904, 1992, 1786, 1787, 1993, 1281, - 2169, 1140, 1260, 1247, 2181, 173, 1144, 1134, 1855, 2023, - 1899, 1146, 1480, 1481, 179, 1147, 1145, 1894, 1949, 1950, - 1753, 2539, 1500, 1216, 1955, 1956, 1863, 2539, 4226, 1482, - 2544, 4227, 1140, 4225, 1177, 2542, 1148, 1878, 1174, 1879, - 3263, 1152, 1877, 1891, 1150, 4236, 1501, 1502, 1503, 1504, - 1505, 1506, 1507, 1509, 1508, 1510, 1511, 1230, 1213, 2098, - 2099, 2096, 2097, 4185, 2543, 3265, 1902, 3577, 1052, 1113, - 2541, 3048, 1753, 1984, 1113, 742, 1479, 4230, 1480, 1481, - 2180, 1736, 4080, 724, 4013, 3260, 2095, 3651, 1945, 3652, - 3918, 1482, 3917, 1937, 2011, 3908, 1940, 1982, 1942, 2098, - 2099, 2628, 2629, 3668, 1971, 3276, 3277, 976, 977, 978, - 3667, 3599, 3261, 1502, 1503, 1504, 1505, 1506, 1507, 1509, - 1508, 1510, 1511, 128, 1117, 3598, 1116, 1115, 3588, 1482, - 1789, 4081, 1500, 4014, 3305, 1496, 3294, 1497, 2947, 2946, - 2945, 4237, 164, 2499, 1983, 1985, 3267, 1969, 1482, 1968, - 2028, 1498, 1512, 1513, 1495, 3050, 1501, 1502, 1503, 1504, - 1505, 1506, 1507, 1509, 1508, 1510, 1511, 1967, 1981, 1281, - 1281, 1957, 1951, 1948, 2050, 1970, 2024, 2025, 1479, 1947, - 1480, 1481, 2331, 4198, 1753, 87, 2328, 3070, 87, 1946, - 2029, 1500, 4186, 3040, 1917, 2330, 1728, 2036, 2037, 2038, - 1482, 3582, 2331, 724, 2929, 3357, 724, 2049, 2515, 1482, - 2514, 972, 1445, 4042, 3275, 1501, 1502, 1503, 1504, 1505, - 1506, 1507, 1509, 1508, 1510, 1511, 3278, 2166, 3060, 3059, - 3058, 1500, 2513, 3052, 2512, 3056, 2511, 3051, 2510, 3049, - 1479, 4187, 1480, 1481, 3054, 1504, 1505, 1506, 1507, 1509, - 1508, 1510, 1511, 3053, 4041, 1501, 1502, 1503, 1504, 1505, - 1506, 1507, 1509, 1508, 1510, 1511, 2137, 2137, 2135, 2135, - 2138, 1482, 3055, 3057, 101, 4017, 2697, 2100, 1479, 4016, - 1480, 1481, 1482, 4015, 102, 42, 1478, 1753, 42, 1482, - 2873, 4213, 4196, 1753, 1482, 3913, 2026, 1479, 1766, 1480, - 1481, 4126, 1753, 2030, 1482, 2032, 2033, 2034, 2035, 4173, - 1753, 3399, 2039, 3897, 2221, 1488, 1489, 1490, 1491, 1492, - 1493, 1487, 1484, 3896, 2051, 3745, 2158, 2147, 2148, 2149, - 2150, 2160, 2151, 2152, 2153, 2165, 2161, 2154, 2155, 2162, - 2163, 2164, 2156, 2157, 2159, 1753, 3264, 1767, 3743, 1479, - 3664, 1480, 1481, 1713, 85, 2873, 1753, 85, 1479, 2077, - 1480, 1481, 1712, 4124, 1753, 1753, 1714, 1715, 1711, 1482, - 1478, 1753, 2107, 3596, 4122, 1753, 4035, 2057, 2058, 110, - 3581, 165, 1753, 101, 2170, 3367, 3991, 1753, 177, 103, - 109, 3364, 108, 102, 2112, 3297, 2113, 4030, 2115, 2116, - 2117, 2118, 2119, 2120, 2122, 2124, 2125, 2126, 2127, 2128, - 2129, 1710, 3296, 2956, 1482, 2943, 1708, 1710, 2141, 1704, - 1479, 1706, 1480, 1481, 1707, 1705, 2339, 1709, 2341, 185, - 2114, 1479, 1482, 1480, 1481, 2873, 4111, 3945, 1479, 2245, - 1480, 1481, 1482, 1479, 2611, 1480, 1481, 2603, 1525, 2185, - 2186, 2187, 2188, 1479, 1753, 1480, 1481, 2340, 2406, 2407, - 2602, 3989, 1753, 2199, 2220, 1753, 2176, 2329, 2327, 1482, - 1500, 2561, 166, 171, 168, 174, 175, 176, 178, 180, - 181, 182, 183, 2237, 2560, 1482, 2873, 4090, 184, 186, - 187, 188, 2699, 1482, 1501, 1502, 1503, 1504, 1505, 1506, - 1507, 1509, 1508, 1510, 1511, 1482, 3986, 1753, 3944, 1482, - 2873, 4086, 3998, 1753, 1500, 2396, 2637, 2422, 1479, 2375, - 1480, 1481, 3635, 4032, 2338, 3921, 1753, 2344, 2345, 2339, - 1482, 2341, 2873, 3909, 3968, 1753, 104, 105, 1501, 1502, - 1503, 1504, 1505, 1506, 1507, 1509, 1508, 1510, 1511, 2053, - 1482, 2647, 3635, 1753, 2873, 3633, 3901, 104, 105, 2452, - 2411, 2646, 2386, 1479, 2019, 1480, 1481, 2539, 1753, 3900, - 2374, 3556, 1753, 1482, 2685, 2779, 1753, 3940, 2106, 3287, - 3286, 1479, 103, 1480, 1481, 3523, 1753, 2695, 3268, 1980, - 1103, 1479, 3272, 1480, 1481, 3284, 3285, 3516, 1753, 3271, - 1972, 3513, 1753, 3282, 3283, 2461, 2462, 2463, 2464, 2456, - 2447, 2457, 2458, 2459, 2460, 2446, 1053, 2424, 1479, 1962, - 1480, 1481, 3511, 1753, 1103, 2466, 2428, 1958, 2468, 2469, - 2470, 2471, 2387, 3273, 1479, 1954, 1480, 1481, 3269, 3282, - 3281, 2362, 1479, 3270, 1480, 1481, 2380, 1753, 2381, 2389, - 2482, 1953, 1482, 1952, 1479, 2450, 1480, 1481, 1479, 2870, - 1480, 1481, 1482, 2488, 2409, 2528, 1753, 86, 44, 45, - 88, 2897, 1753, 103, 2434, 2433, 1768, 1087, 2432, 1479, - 1271, 1480, 1481, 2449, 2448, 2918, 92, 1482, 2621, 3004, - 48, 76, 77, 2889, 74, 78, 2525, 1088, 1482, 1479, - 3643, 1480, 1481, 1859, 2985, 2982, 75, 3553, 2498, 1501, - 1502, 1503, 1504, 1505, 1506, 1507, 1509, 1508, 1510, 1511, - 2918, 1143, 1479, 2961, 1480, 1481, 2978, 2979, 109, 2483, - 3219, 1898, 2479, 2472, 2474, 2475, 1482, 2868, 62, 2502, - 2497, 3549, 2501, 2533, 3474, 1753, 2536, 2889, 2537, 2553, - 95, 1482, 3249, 2919, 3472, 1753, 2873, 2872, 2140, 1753, - 1859, 1858, 2621, 2921, 1482, 2483, 2532, 2540, 2535, 2531, - 2897, 1482, 1801, 1800, 1142, 3552, 2896, 2451, 3549, 3468, - 1753, 2557, 190, 2554, 1478, 2558, 2559, 1482, 2919, 4074, - 3465, 1753, 4046, 2976, 2873, 2897, 3502, 110, 2621, 1482, - 83, 1479, 1478, 1480, 1481, 129, 1482, 151, 109, 3600, - 108, 1479, 3284, 1480, 1481, 3192, 1754, 1756, 103, 1482, - 1770, 172, 2435, 2626, 3549, 2779, 2564, 2682, 3463, 1753, - 2645, 2539, 1041, 1041, 1041, 2681, 1479, 2897, 1480, 1481, - 2539, 2522, 2404, 3461, 1753, 1757, 2365, 1479, 2140, 1480, - 1481, 1482, 1535, 2078, 1535, 162, 3459, 1753, 2062, 2005, - 1933, 150, 1788, 3457, 1753, 1125, 3601, 3602, 3603, 1124, - 2641, 1042, 1482, 95, 4152, 4093, 3932, 1760, 2596, 3455, - 1753, 169, 3898, 1753, 170, 1479, 3758, 1480, 1481, 1482, - 2644, 1769, 2341, 3616, 3613, 51, 54, 57, 56, 59, - 1479, 73, 1480, 1481, 82, 79, 1884, 1885, 161, 160, - 189, 3453, 1753, 1479, 1482, 1480, 1481, 1934, 1935, 1936, - 1479, 2340, 1480, 1481, 3594, 3415, 3414, 1861, 61, 91, - 90, 2481, 2613, 71, 72, 58, 1479, 1482, 1480, 1481, - 3355, 80, 81, 3451, 1753, 3310, 1482, 2619, 1479, 3306, - 1480, 1481, 1482, 2986, 95, 1479, 1482, 1480, 1481, 2478, - 1482, 2473, 2467, 2627, 3449, 1753, 1482, 2465, 1479, 1987, - 1480, 1481, 1893, 1889, 1716, 1857, 2633, 121, 2630, 2631, - 2632, 3447, 1753, 1482, 63, 64, 2107, 65, 66, 67, - 68, 3308, 2953, 2952, 1215, 3359, 3563, 3564, 3933, 2496, - 1479, 2378, 1480, 1481, 2055, 4208, 3445, 1753, 4206, 2634, - 4180, 2636, 155, 1886, 158, 4054, 1883, 3973, 156, 157, - 2639, 1479, 2640, 1480, 1481, 173, 3566, 1482, 3302, 3443, - 1753, 1482, 3301, 3569, 179, 3300, 3219, 2657, 1479, 3905, - 1480, 1481, 3568, 2635, 3441, 1753, 1482, 2953, 3439, 1753, - 1482, 60, 3437, 1753, 2965, 2597, 1482, 3238, 3435, 1753, - 3237, 2384, 1482, 1479, 2642, 1480, 1481, 1482, 3241, 3239, - 2823, 2056, 1482, 3242, 3240, 3421, 1753, 1482, 2605, 2606, - 4050, 1482, 3723, 2608, 3722, 1482, 1479, 678, 1480, 1481, - 3934, 1765, 2609, 2693, 2395, 1479, 2811, 1480, 1481, 2654, - 3557, 1479, 2855, 1480, 1481, 1479, 3197, 1480, 1481, 1479, - 3196, 1480, 1481, 1482, 3542, 1479, 1055, 1480, 1481, 3397, - 1753, 1041, 3541, 2844, 1753, 4012, 3736, 2137, 3738, 2135, - 2858, 3545, 1479, 2004, 1480, 1481, 3206, 3721, 2842, 1753, - 1025, 89, 2817, 1753, 2894, 2895, 1482, 2856, 2794, 1753, - 1058, 1482, 3280, 2422, 2786, 1753, 1041, 2914, 1059, 2777, - 1753, 741, 164, 2936, 2775, 1753, 2957, 1056, 2589, 2762, - 1753, 2893, 2183, 2859, 3618, 2861, 1479, 2653, 1480, 1481, - 1479, 1482, 1480, 1481, 3209, 3211, 1482, 2874, 3375, 2184, - 1482, 2588, 2106, 3212, 1482, 1479, 1067, 1480, 1481, 1479, - 2587, 1480, 1481, 2586, 2585, 1479, 2948, 1480, 1481, 2584, - 1066, 1479, 1482, 1480, 1481, 3604, 1479, 1482, 1480, 1481, - 2883, 1479, 1482, 1480, 1481, 1721, 1479, 2846, 1480, 1481, - 1479, 1181, 1480, 1481, 1479, 1929, 1480, 1481, 2760, 1753, - 2583, 1180, 1482, 3571, 42, 2952, 2866, 101, 2939, 2941, - 3034, 94, 2912, 2911, 1715, 1482, 2913, 102, 159, 1444, - 2932, 1482, 1479, 2886, 1480, 1481, 2916, 2871, 3605, 3606, - 3607, 2990, 3547, 2758, 1753, 2103, 2101, 2102, 2756, 1753, - 2992, 129, 2754, 1753, 103, 1482, 3518, 2920, 1930, 1931, - 1932, 2488, 2923, 1482, 4222, 1479, 2969, 1480, 1481, 2930, - 1479, 2933, 1480, 1481, 2752, 1753, 2406, 2407, 2600, 2750, - 1753, 4129, 4031, 3001, 2748, 1753, 2178, 3525, 3928, 2944, - 3279, 2179, 2955, 2910, 2390, 1482, 2625, 2958, 2959, 1482, - 1479, 2061, 1480, 1481, 3514, 1479, 2954, 1480, 1481, 1479, - 2060, 1480, 1481, 1479, 108, 1480, 1481, 3480, 2962, 2966, - 2967, 2968, 2963, 2746, 1753, 1482, 3997, 3195, 70, 2241, - 2998, 1479, 109, 1480, 1481, 3194, 1479, 152, 1480, 1481, - 153, 1479, 3996, 1480, 1481, 3976, 3744, 3476, 1881, 1482, - 3742, 3044, 3045, 2987, 2988, 2744, 1753, 3243, 3741, 2906, - 2907, 1479, 101, 1480, 1481, 2997, 3734, 3614, 103, 3546, - 3544, 165, 102, 110, 1479, 3311, 1480, 1481, 177, 2523, - 1479, 1876, 1480, 1481, 109, 110, 108, 2742, 1753, 1065, - 3733, 2740, 1753, 3535, 1482, 2889, 109, 3707, 1482, 3042, - 3023, 3061, 2870, 1482, 1479, 3026, 1480, 1481, 4209, 2324, - 3098, 2683, 1479, 2376, 1480, 1481, 1482, 2738, 1753, 185, - 4210, 4209, 3, 1482, 1782, 3079, 3080, 3081, 3082, 3083, - 3084, 3085, 3086, 3087, 3088, 1774, 114, 115, 1482, 2356, - 4210, 2736, 1753, 4018, 1479, 3096, 1480, 1481, 1479, 2999, - 1480, 1481, 3580, 3062, 97, 2075, 1754, 2363, 10, 1, - 1033, 1447, 166, 171, 168, 174, 175, 176, 178, 180, - 181, 182, 183, 1446, 1479, 3584, 1480, 1481, 184, 186, - 187, 188, 2073, 4141, 694, 9, 2734, 1753, 2366, 2074, - 2732, 1753, 8, 1719, 4181, 3412, 2388, 4137, 1479, 4138, - 1480, 1481, 1973, 3100, 1963, 3646, 1716, 2292, 2730, 1753, - 3046, 3929, 3314, 3156, 2529, 3411, 3032, 3612, 3063, 3033, - 2486, 1133, 154, 1482, 2444, 2445, 4106, 1482, 118, 1091, - 2728, 1753, 2902, 2905, 2906, 2907, 2903, 117, 2904, 2908, - 3043, 1136, 3174, 1479, 1244, 1480, 1481, 1479, 2524, 1480, - 1481, 3636, 1479, 2235, 1480, 1481, 3163, 2937, 2453, 1807, - 1805, 3165, 1806, 1482, 2977, 1479, 1804, 1480, 1481, 1809, - 1808, 2422, 1479, 1482, 1480, 1481, 4078, 3089, 3383, 2684, - 3481, 2066, 2329, 2327, 2329, 2327, 3136, 1479, 1482, 1480, - 1481, 731, 2909, 3226, 1482, 87, 725, 192, 2422, 2422, - 2422, 2422, 2422, 2500, 1796, 3174, 3146, 3147, 3148, 3149, - 3150, 110, 1775, 2059, 3164, 3403, 3166, 1175, 2422, 2723, - 1753, 2422, 109, 684, 108, 3288, 3231, 1482, 2562, 3248, - 690, 1482, 103, 2318, 2319, 2320, 2321, 2322, 1532, 2054, - 3173, 3193, 2015, 1482, 2924, 1085, 1077, 3185, 2377, 2860, - 2343, 3191, 1482, 2346, 2347, 2719, 1753, 3186, 3188, 3190, - 1482, 3198, 3200, 1084, 3906, 2717, 1753, 3227, 3213, 3214, - 3201, 2424, 1479, 3539, 1480, 1481, 1479, 3205, 1480, 1481, - 2710, 1753, 3207, 2876, 3331, 3210, 2708, 1753, 2364, 3233, - 3234, 3230, 3236, 3203, 3339, 1044, 1045, 1482, 2424, 2424, - 2424, 2424, 2424, 104, 105, 3252, 3244, 3232, 4011, 1046, - 3235, 3735, 1479, 1482, 1480, 1481, 3258, 3250, 2424, 3401, - 3251, 2424, 1479, 2840, 1480, 1481, 4091, 2934, 1771, 1482, - 3501, 2656, 3290, 3199, 3291, 2839, 3289, 1479, 2173, 1480, - 1481, 1522, 756, 1479, 2835, 1480, 1481, 1482, 2421, 3702, - 2093, 754, 2834, 1482, 753, 3292, 3293, 3312, 751, 2862, - 2488, 3343, 3333, 1482, 2890, 1486, 3340, 1482, 1485, 962, - 2850, 1783, 3216, 3350, 3344, 1482, 1479, 2901, 1480, 1481, - 1479, 2899, 1480, 1481, 2898, 3222, 2598, 2429, 3565, 2833, - 3222, 3561, 1479, 3368, 1480, 1481, 3371, 1482, 4133, 2423, - 3370, 1479, 3378, 1480, 1481, 2832, 2419, 2869, 913, 1479, - 912, 1480, 1481, 763, 755, 745, 975, 911, 3388, 3385, - 3386, 2831, 3387, 910, 3341, 3389, 3342, 3391, 3007, 3393, - 3404, 3405, 3406, 3407, 3408, 3356, 3009, 2935, 3352, 2830, - 1463, 1738, 1741, 2385, 1104, 2821, 1479, 3380, 1480, 1481, - 2902, 2905, 2906, 2907, 2903, 2820, 2904, 2908, 4037, 2819, - 3563, 3564, 1479, 2624, 1480, 1481, 3409, 2818, 1535, 1737, - 4044, 3322, 1535, 3630, 3303, 3313, 2983, 2516, 1479, 69, - 1480, 1481, 2643, 46, 4006, 4075, 2648, 905, 3526, 2815, - 3528, 902, 3704, 3705, 3706, 3159, 1479, 3160, 1480, 1481, - 3496, 4057, 1479, 4058, 1480, 1481, 901, 3500, 4059, 2651, - 2230, 2652, 1479, 1457, 1480, 1481, 1479, 2660, 1480, 1481, - 1454, 2662, 2663, 4154, 1479, 2068, 1480, 1481, 96, 36, - 2669, 2670, 2671, 2672, 2673, 2674, 2675, 2676, 2677, 2678, - 35, 2680, 34, 3379, 33, 3225, 1479, 32, 1480, 1481, - 2422, 26, 25, 3527, 24, 3529, 23, 22, 29, 3531, - 19, 21, 3536, 3578, 2686, 2687, 2688, 2689, 20, 2691, - 2692, 3543, 2694, 1482, 18, 3325, 2696, 4176, 3548, 4221, - 2701, 2702, 123, 2703, 55, 1482, 2706, 2707, 2709, 2711, - 2712, 2713, 2714, 2715, 2716, 2718, 2720, 2721, 2722, 2724, - 743, 2726, 2727, 2729, 2731, 2733, 2735, 2737, 2739, 2741, - 2743, 2745, 2747, 2749, 2751, 2753, 2755, 2757, 2759, 2761, - 2763, 2764, 2765, 3570, 2767, 3579, 2769, 3334, 2771, 2772, - 3343, 2774, 2776, 2778, 3572, 3340, 3503, 2781, 3505, 3506, - 3507, 2785, 3595, 3344, 3597, 2790, 2791, 2792, 2793, 3573, - 2424, 3567, 1482, 52, 3640, 3641, 3373, 3374, 2804, 2805, - 2806, 2807, 2808, 2809, 3533, 2810, 2813, 2814, 50, 131, - 130, 1482, 53, 49, 2816, 3589, 3590, 2803, 1218, 2822, - 47, 31, 1482, 30, 2825, 2826, 2827, 2828, 2829, 1482, - 17, 16, 15, 14, 1482, 2836, 2837, 3560, 2838, 1482, - 13, 2841, 2843, 2388, 12, 2845, 11, 7, 1482, 1064, - 6, 39, 1070, 1070, 1482, 2857, 3574, 3575, 1482, 38, - 37, 28, 1479, 27, 1480, 1481, 40, 3623, 4, 3642, - 2970, 3627, 3628, 3629, 1479, 2518, 1480, 1481, 0, 0, - 0, 0, 0, 0, 2802, 0, 0, 3658, 0, 0, - 0, 0, 0, 2664, 0, 0, 0, 1482, 0, 0, - 0, 0, 1482, 2801, 0, 0, 0, 0, 0, 1482, - 2679, 0, 3669, 0, 2800, 0, 1482, 0, 0, 0, - 1482, 2799, 0, 0, 1482, 0, 2798, 0, 1482, 0, - 0, 2797, 1482, 0, 0, 0, 1482, 0, 0, 0, - 2796, 1479, 0, 1480, 1481, 0, 2795, 0, 1482, 0, - 2789, 0, 0, 0, 0, 3710, 0, 3711, 3712, 3713, - 1479, 0, 1480, 1481, 0, 3720, 0, 1482, 3727, 0, - 3729, 1479, 0, 1480, 1481, 0, 0, 3700, 1479, 0, - 1480, 1481, 1482, 1479, 3730, 1480, 1481, 0, 1479, 2788, - 1480, 1481, 0, 3226, 2787, 0, 87, 1479, 3226, 1480, - 1481, 2784, 0, 1479, 0, 1480, 1481, 1479, 2783, 1480, - 1481, 0, 2782, 0, 0, 0, 2780, 0, 0, 0, - 2773, 0, 1482, 3752, 2770, 0, 0, 3731, 2768, 0, - 0, 0, 2137, 3663, 2135, 3760, 3740, 3750, 3739, 0, - 2766, 0, 3747, 0, 3749, 0, 1479, 0, 1480, 1481, - 1482, 1479, 0, 1480, 1481, 0, 0, 0, 1479, 2725, - 1480, 1481, 0, 3912, 0, 1479, 0, 1480, 1481, 1479, - 3764, 1480, 1481, 1479, 2705, 1480, 1481, 1479, 0, 1480, - 1481, 1479, 0, 1480, 1481, 1479, 0, 1480, 1481, 0, - 0, 3619, 3620, 0, 0, 0, 42, 1479, 0, 1480, - 1481, 0, 3904, 3931, 3903, 0, 0, 0, 0, 0, - 0, 0, 0, 3919, 2704, 0, 1479, 0, 1480, 1481, - 3923, 3924, 0, 0, 0, 0, 3902, 0, 0, 0, - 0, 1479, 0, 1480, 1481, 0, 3970, 3971, 0, 0, - 0, 0, 2700, 3754, 0, 3074, 3075, 3076, 3077, 3078, - 0, 0, 0, 0, 3728, 0, 0, 0, 0, 0, - 2137, 0, 2135, 3974, 0, 3093, 0, 3914, 3915, 3916, - 0, 1479, 0, 1480, 1481, 0, 0, 0, 3761, 3762, - 0, 0, 0, 3222, 0, 0, 0, 0, 0, 3977, - 0, 4019, 3226, 3980, 0, 0, 0, 0, 3756, 1479, - 0, 1480, 1481, 0, 0, 0, 1553, 1554, 1555, 1556, - 1557, 1558, 1559, 1560, 1561, 1562, 1563, 1564, 1565, 1566, - 1567, 1568, 1569, 1570, 1571, 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, 1652, 1653, 1654, 1655, 1656, 1657, 1658, - 1659, 1660, 1661, 1662, 1663, 1664, 1665, 1666, 1667, 1673, - 1674, 1675, 1676, 1690, 1691, 1692, 1693, 1694, 1695, 1696, - 1697, 1698, 1699, 1700, 1701, 1702, 1703, 4020, 4004, 0, - 3975, 4003, 0, 0, 0, 3225, 3994, 0, 0, 1482, - 3225, 4038, 0, 4000, 0, 4002, 0, 0, 1482, 0, - 0, 3064, 0, 0, 1743, 0, 0, 0, 0, 87, - 0, 0, 0, 0, 1743, 3228, 0, 0, 1751, 1482, - 4023, 1744, 0, 1482, 0, 0, 0, 0, 1751, 4022, - 0, 1744, 0, 3246, 0, 0, 4045, 3907, 4040, 4027, - 0, 0, 1482, 0, 4043, 0, 2382, 2383, 1750, 1748, - 1749, 1745, 0, 1746, 0, 3911, 1739, 1740, 1750, 1748, - 1749, 1745, 0, 1746, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 1747, 0, 0, 0, - 0, 2698, 0, 0, 0, 0, 1747, 0, 0, 0, - 2690, 0, 0, 0, 0, 0, 0, 0, 0, 4063, - 0, 0, 4064, 0, 0, 0, 0, 4088, 0, 42, - 0, 2661, 87, 0, 0, 2655, 0, 0, 0, 0, - 4073, 0, 0, 0, 0, 1483, 0, 0, 4082, 0, - 0, 0, 0, 3138, 2650, 3140, 0, 0, 1479, 4092, - 1480, 1481, 0, 0, 3931, 4108, 4094, 1479, 4119, 1480, - 1481, 3151, 3152, 3153, 3154, 4105, 1541, 0, 4097, 4102, - 3377, 4099, 4098, 4096, 4101, 4100, 0, 0, 1479, 4127, - 1480, 1481, 1479, 0, 1480, 1481, 0, 0, 0, 4024, - 0, 4132, 3394, 3395, 3225, 3396, 3398, 3400, 0, 4150, - 0, 1479, 0, 1480, 1481, 0, 4140, 0, 4145, 0, - 0, 0, 0, 0, 4158, 4119, 4160, 0, 0, 0, - 4171, 0, 42, 3413, 0, 0, 0, 0, 3416, 0, - 3418, 3419, 3420, 3422, 3423, 3424, 3425, 3426, 3427, 3428, - 3429, 3430, 3431, 3432, 3433, 3434, 3436, 3438, 3440, 3442, - 3444, 3446, 3448, 3450, 3452, 3454, 3456, 3458, 3460, 3462, - 3464, 3466, 3467, 3469, 3470, 3471, 3473, 2015, 4194, 3475, - 4191, 3477, 3478, 3479, 4201, 4205, 3483, 3484, 3485, 3486, - 3487, 3488, 3489, 3490, 3491, 3492, 3493, 2137, 4207, 2135, - 4204, 4203, 4119, 4215, 4200, 3499, 4190, 4175, 4170, 3504, - 4223, 4089, 3222, 3508, 3509, 4084, 3510, 3512, 0, 3515, - 3517, 4231, 3519, 3520, 3521, 3522, 4229, 0, 4174, 0, - 0, 0, 3530, 0, 0, 0, 0, 1825, 0, 4029, - 4240, 4241, 3971, 4239, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 2137, 0, 2135, 4238, 0, - 0, 0, 0, 4036, 0, 0, 0, 3554, 3555, 0, - 0, 3559, 0, 4048, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 1825, 0, 0, 4166, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 4083, 0, 0, 0, 0, 0, 1773, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 3634, 0, 0, 0, - 0, 0, 1722, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1812, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1862, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 3653, 0, 0, 3657, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 676, 0, 1812, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 3670, 0, - 0, 1028, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 1826, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 4188, 0, 0, 0, 1716, 0, 0, - 0, 0, 0, 1099, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 3693, 0, 0, 0, 0, 1826, 0, 0, - 0, 0, 2020, 0, 0, 3701, 0, 0, 0, 0, - 0, 0, 3708, 1839, 1842, 1843, 1844, 1845, 1846, 1847, - 0, 1848, 1849, 1851, 1852, 1850, 1853, 1854, 1827, 1828, - 1829, 1830, 1810, 1811, 1840, 0, 1813, 0, 1814, 1815, - 1816, 1817, 1818, 1819, 1820, 1821, 1822, 0, 0, 1823, - 1831, 1832, 1833, 1834, 0, 1835, 1836, 1837, 1838, 0, - 0, 1824, 1839, 1842, 1843, 1844, 1845, 1846, 1847, 0, - 1848, 1849, 1851, 1852, 1850, 1853, 1854, 1827, 1828, 1829, - 1830, 1810, 1811, 1840, 0, 1813, 0, 1814, 1815, 1816, - 1817, 1818, 1819, 1820, 1821, 1822, 0, 0, 1823, 1831, - 1832, 1833, 1834, 0, 1835, 1836, 1837, 1838, 0, 0, - 1824, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 3920, 0, 0, 0, 0, 0, 0, 0, 0, - 3927, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 3937, 3938, 3939, 0, 3941, 0, 3942, 3943, 0, 0, - 0, 0, 3946, 3947, 3948, 3949, 3950, 3951, 3952, 3953, - 3954, 3955, 3956, 3957, 3958, 3959, 3960, 3961, 3962, 3963, - 3964, 3965, 3966, 3967, 0, 3969, 3972, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 3691, 0, 0, - 0, 3981, 3982, 3983, 3984, 3985, 3987, 3988, 3990, 3992, - 3993, 3995, 0, 0, 0, 3999, 0, 0, 0, 4001, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 2088, - 2089, 2090, 2091, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 2104, 1841, 0, 0, 0, - 0, 0, 0, 0, 4028, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 2143, 2144, 0, 0, 0, 0, 2167, 0, 0, 2171, - 2172, 0, 0, 0, 2177, 1841, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 2189, - 2190, 2191, 2192, 2193, 2194, 2195, 2196, 2197, 2198, 0, - 2200, 0, 0, 0, 2222, 2223, 2224, 2225, 2226, 2227, - 2228, 2229, 2231, 0, 2236, 0, 2238, 2239, 2240, 0, - 2242, 2243, 2244, 0, 2246, 2247, 2248, 2249, 2250, 2251, - 2252, 2253, 2254, 2255, 2256, 2257, 2258, 2259, 2260, 2261, - 2262, 2263, 2264, 2265, 2266, 2267, 2268, 2269, 2270, 2271, - 2272, 2273, 2274, 2275, 2276, 2277, 2278, 2279, 2280, 2281, - 2282, 2283, 2284, 2285, 2286, 2287, 2288, 2289, 2290, 2291, - 2295, 2296, 2297, 2298, 2299, 2300, 2301, 2302, 2303, 2304, - 2305, 2306, 2307, 2308, 2309, 2310, 2311, 2312, 2313, 2314, - 2315, 2316, 2317, 0, 0, 0, 0, 0, 2323, 0, - 2325, 0, 2332, 2333, 2334, 2335, 2336, 2337, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 2348, 2349, 2350, 2351, 2352, 2353, 2354, 2355, 0, - 2357, 2358, 2359, 2360, 2361, 4010, 0, 0, 0, 0, - 0, 4053, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 4068, 0, 0, 0, 1070, - 0, 4071, 0, 4072, 0, 0, 0, 0, 0, 0, - 0, 0, 190, 0, 0, 0, 1219, 0, 1225, 0, - 0, 0, 0, 0, 0, 0, 4087, 0, 0, 0, - 2402, 2403, 0, 0, 0, 129, 0, 151, 958, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 172, 4113, 4114, 0, 0, 2441, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 4121, 4123, 4125, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1453, 0, 0, 0, 4131, 162, 0, 0, 0, 0, - 0, 150, 0, 0, 195, 0, 4153, 195, 0, 0, - 0, 729, 0, 0, 0, 0, 735, 0, 0, 0, - 0, 169, 0, 0, 170, 0, 0, 195, 2484, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 195, 4172, 0, 138, 139, 161, 160, - 189, 0, 0, 1716, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 735, 195, - 735, 0, 0, 0, 0, 0, 0, 0, 4195, 4197, - 4199, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 4052, - 0, 4220, 0, 0, 0, 0, 0, 4062, 0, 0, - 0, 0, 0, 0, 0, 95, 0, 0, 1023, 4232, - 4233, 0, 0, 963, 1024, 976, 977, 978, 964, 0, - 0, 965, 966, 0, 967, 0, 0, 0, 0, 0, - 0, 0, 155, 136, 158, 143, 135, 0, 156, 157, - 972, 0, 979, 980, 0, 173, 0, 0, 0, 0, - 0, 0, 0, 0, 179, 144, 0, 0, 0, 1716, - 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, 3345, 3346, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 981, 982, 983, 984, 985, 986, 987, + 1676, 1677, 1440, 1522, 1271, 2550, 1678, 4036, 1680, 1681, + 1682, 1683, 1684, 1441, 1442, 2974, 3331, 3332, 3588, 3589, + 1551, 1551, 1551, 1551, 1551, 1551, 1535, 1121, 4150, 3335, + 1515, 975, 4082, 975, 724, 1691, 1692, 1693, 1694, 1695, + 1696, 1697, 1698, 1699, 1700, 1701, 1702, 1703, 1704, 1518, + 1519, 1520, 1521, 1526, 2997, 715, 4121, 975, 1515, 1532, + 3657, 4121, 1467, 3700, 3700, 1083, 1718, 165, 3331, 3332, + 716, 1224, 4149, 1547, 177, 1548, 1549, 1234, 1459, 94, + 2624, 3335, 1977, 1976, 1978, 1979, 1980, 1717, 4120, 1221, + 94, 94, 165, 4120, 1552, 1553, 3541, 1216, 2996, 177, + 2977, 1897, 1083, 2993, 1233, 1142, 1239, 1240, 1241, 1242, + 2008, 1865, 89, 1172, 2623, 185, 3044, 3485, 1228, 1135, + 1724, 674, 3385, 1171, 1753, 4160, 2548, 2854, 2856, 1042, + 1279, 1280, 3021, 1042, 4024, 3020, 1467, 1516, 1517, 1042, + 185, 4231, 3579, 3561, 2640, 2924, 2889, 2826, 2143, 1782, + 3031, 719, 1679, 1179, 1227, 1142, 1715, 3172, 166, 171, + 168, 174, 175, 176, 178, 180, 181, 182, 183, 1716, + 1477, 1247, 2884, 2494, 184, 186, 187, 188, 1142, 682, + 2444, 1515, 124, 166, 171, 168, 174, 175, 176, 178, + 180, 181, 182, 183, 2402, 3257, 1512, 1142, 2184, 184, + 186, 187, 188, 2651, 1260, 1495, 3265, 1058, 1141, 1274, + 1896, 2176, 4050, 1165, 2495, 2185, 119, 3620, 3553, 1754, + 1732, 2493, 94, 1232, 1736, 2023, 2568, 1734, 1735, 2077, + 1041, 104, 105, 2004, 1796, 3071, 2510, 1716, 1685, 1686, + 1687, 1688, 1689, 1690, 1477, 1995, 4196, 3749, 1722, 1507, + 1508, 1510, 1509, 1511, 1512, 2496, 1482, 3266, 1141, 1115, + 1211, 3595, 4188, 1145, 1135, 2492, 1481, 1482, 1147, 3594, + 2535, 107, 1148, 1146, 1906, 1483, 1142, 2176, 1905, 2660, + 1895, 1141, 3268, 1473, 2540, 3034, 1465, 1135, 1138, 1139, + 3033, 1099, 2540, 1149, 120, 1132, 1136, 1873, 1874, 1875, + 1141, 2855, 3263, 2651, 1730, 1142, 1135, 1138, 1139, 2545, + 1099, 1889, 2543, 1153, 1132, 1136, 1131, 1151, 1053, 1756, + 3580, 3034, 3279, 3280, 1215, 1733, 3033, 2544, 1719, 3264, + 1960, 4233, 1882, 2547, 2016, 2542, 1038, 4083, 1759, 4016, + 1040, 1911, 1731, 1912, 1985, 1914, 1916, 3921, 1901, 1920, + 1922, 1924, 1926, 1928, 2182, 1942, 3920, 1473, 3051, 2332, + 1282, 1983, 1261, 3270, 1950, 1951, 720, 1787, 1788, 2167, + 1956, 1957, 1900, 4229, 1856, 1217, 4230, 1231, 4228, 1141, + 2111, 1178, 3654, 2024, 3655, 1175, 4084, 1246, 4017, 1864, + 2400, 2401, 1899, 1899, 2112, 1513, 1514, 2110, 1248, 4239, + 1992, 3911, 1993, 1879, 3671, 1994, 3670, 1880, 1141, 1892, + 3602, 1878, 2332, 1145, 1135, 1984, 2329, 1972, 1147, 1737, + 3601, 1754, 1148, 1146, 1480, 2331, 1481, 1482, 725, 2181, + 1903, 3278, 1982, 3073, 1214, 3591, 1946, 2099, 2100, 2097, + 2098, 973, 3053, 3281, 977, 978, 979, 1483, 3308, 3297, + 1114, 2949, 1483, 2099, 2100, 2630, 2631, 1938, 2012, 1483, + 1941, 2948, 1943, 2947, 2096, 2500, 2697, 1986, 2159, 2148, + 2149, 2150, 2151, 2161, 2152, 2153, 2154, 2166, 2162, 2155, + 2156, 2163, 2164, 2165, 2157, 2158, 2160, 1483, 1971, 1970, + 190, 1969, 128, 1483, 1968, 4240, 1118, 1117, 1116, 1483, + 1958, 2978, 1505, 1506, 1507, 1508, 1510, 1509, 1511, 1512, + 1790, 2029, 1952, 129, 1949, 151, 3063, 3062, 3061, 2407, + 2408, 3055, 1948, 3059, 1947, 3054, 1918, 3052, 1729, 172, + 1282, 1282, 3057, 1483, 3360, 1446, 2025, 2026, 1483, 4201, + 1754, 3056, 2875, 4216, 2051, 1754, 87, 4189, 4045, 87, + 2030, 4199, 1754, 3552, 3585, 3402, 725, 2037, 2038, 2039, + 3058, 3060, 4044, 162, 3267, 4020, 2931, 2050, 725, 150, + 1489, 1490, 1491, 1492, 1493, 1494, 1488, 1485, 4019, 1472, + 1469, 1470, 1471, 1476, 1478, 1475, 2516, 1474, 2515, 169, + 4018, 3943, 170, 1754, 1767, 2699, 1480, 1468, 1481, 1482, + 2514, 1480, 2513, 1481, 1482, 1479, 1754, 2942, 1480, 101, + 1481, 1482, 4176, 1754, 1885, 1886, 161, 160, 189, 102, + 3916, 1483, 2138, 2138, 2136, 2136, 1754, 2139, 2875, 1754, + 4129, 1754, 1483, 2512, 3900, 2511, 1480, 42, 1481, 1482, + 42, 3899, 1480, 1768, 1481, 1482, 3748, 2101, 1480, 1717, + 1481, 1482, 1483, 1472, 1469, 1470, 1471, 1476, 1478, 1475, + 3746, 1474, 4038, 2027, 1479, 1754, 2875, 4114, 2875, 4093, + 2031, 1468, 2033, 2034, 2035, 2036, 2687, 3667, 1501, 2040, + 1754, 1497, 1480, 1498, 1481, 1482, 1714, 1480, 1713, 1481, + 1482, 2052, 2222, 1483, 2875, 4089, 2236, 1499, 1513, 1514, + 1496, 1712, 1502, 1503, 1504, 1505, 1506, 1507, 1508, 1510, + 1509, 1511, 1512, 4127, 1754, 1483, 85, 3599, 1715, 85, + 155, 1887, 158, 2078, 1884, 1754, 156, 157, 4001, 1754, + 3948, 1716, 3584, 173, 2108, 3370, 1483, 2058, 2059, 2171, + 3638, 4035, 179, 1503, 1504, 1505, 1506, 1507, 1508, 1510, + 1509, 1511, 1512, 2114, 1483, 2116, 2117, 2118, 2119, 2120, + 2121, 2123, 2125, 2126, 2127, 2128, 2129, 2130, 1483, 2113, + 1480, 3367, 1481, 1482, 3300, 2649, 2319, 2320, 2321, 2322, + 2323, 1480, 2656, 1481, 1482, 2648, 2342, 110, 2142, 2115, + 2341, 3924, 1754, 2344, 2246, 2340, 2347, 2348, 109, 101, + 108, 1480, 3299, 1481, 1482, 103, 3271, 4125, 1754, 102, + 3275, 3025, 1526, 2186, 2187, 2188, 2189, 3274, 2875, 3912, + 3947, 2328, 2330, 3638, 1754, 2875, 3636, 2200, 3994, 1754, + 2177, 2365, 2221, 2540, 1754, 3559, 1754, 2781, 1754, 3904, + 2238, 1483, 1480, 2958, 1481, 1482, 3992, 1754, 3290, 3289, + 3903, 3276, 2945, 2920, 1483, 1711, 3272, 3287, 3288, 103, + 2655, 3273, 3285, 3286, 1480, 2647, 1481, 1482, 3285, 3284, + 1754, 164, 1483, 1754, 2899, 1754, 2623, 3006, 2423, 2339, + 1705, 1711, 2345, 2346, 1483, 1480, 1709, 1481, 1482, 2342, + 2613, 1707, 1483, 2412, 1708, 1706, 2605, 1710, 2340, 1860, + 2987, 104, 105, 1480, 2604, 1481, 1482, 2980, 2981, 3646, + 1501, 2562, 3043, 1771, 2561, 1483, 2453, 1480, 2397, 1481, + 1482, 2921, 104, 105, 1754, 2375, 1483, 2376, 1501, 2387, + 2054, 2923, 2020, 2107, 1502, 1503, 1504, 1505, 1506, 1507, + 1508, 1510, 1509, 1511, 1512, 1981, 3989, 1754, 2875, 2874, + 2701, 1104, 1502, 1503, 1504, 1505, 1506, 1507, 1508, 1510, + 1509, 1511, 1512, 1973, 3971, 1754, 2984, 159, 1963, 2425, + 2963, 2462, 2463, 2464, 2465, 2457, 3574, 2458, 2459, 2460, + 2461, 2447, 1054, 2448, 1770, 1104, 2891, 2872, 1959, 2363, + 1480, 2467, 1481, 1482, 2469, 2470, 2471, 2472, 2429, 2388, + 2381, 103, 2382, 1480, 1955, 1481, 1482, 3526, 1754, 2452, + 2390, 1954, 2483, 2141, 1754, 95, 1953, 1769, 3521, 2451, + 109, 1480, 1272, 1481, 1482, 3556, 1501, 2529, 2639, 2410, + 1483, 2489, 2891, 1480, 1483, 1481, 1482, 1479, 2434, 2435, + 2433, 1480, 1088, 1481, 1482, 1089, 2450, 2920, 2449, 2541, + 1502, 1503, 1504, 1505, 1506, 1507, 1508, 1510, 1509, 1511, + 1512, 1860, 1859, 2899, 1480, 2870, 1481, 1482, 4077, 2526, + 1802, 1801, 2499, 2898, 4049, 1480, 152, 1481, 1482, 153, + 1502, 1503, 1504, 1505, 1506, 1507, 1508, 1510, 1509, 1511, + 1512, 3222, 2484, 3555, 1479, 2480, 2473, 2475, 2476, 2875, + 2899, 2498, 3552, 2534, 1483, 2503, 2537, 2502, 2538, 3552, + 165, 3252, 3505, 2540, 3287, 2921, 3195, 177, 3603, 2436, + 2781, 2623, 2554, 4190, 2684, 2623, 3519, 1754, 2484, 2533, + 2536, 2532, 1143, 2683, 2899, 1501, 2540, 1483, 2523, 2405, + 1758, 1144, 1483, 2366, 2141, 2079, 2063, 2555, 1483, 2558, + 2006, 1899, 1483, 2559, 2560, 1789, 1483, 1126, 185, 1502, + 1503, 1504, 1505, 1506, 1507, 1508, 1510, 1509, 1511, 1512, + 1483, 1755, 1757, 1125, 1483, 3604, 3605, 3606, 1483, 1480, + 4155, 1481, 1482, 1480, 2628, 1481, 1482, 4096, 3935, 1043, + 1761, 3901, 3311, 1042, 1042, 1042, 3516, 1754, 2565, 3761, + 3619, 166, 171, 168, 174, 175, 176, 178, 180, 181, + 182, 183, 110, 1536, 3616, 1536, 3597, 184, 186, 187, + 188, 3418, 1483, 109, 3417, 108, 1862, 2482, 3572, 3514, + 1754, 2643, 3358, 103, 3477, 1754, 3313, 3309, 2988, 1483, + 3475, 1754, 2598, 1483, 3471, 1754, 2479, 2955, 3468, 1754, + 2342, 1501, 1483, 1480, 2341, 1481, 1482, 1483, 2474, 2646, + 2468, 1483, 3466, 1754, 2466, 1483, 3464, 1754, 1988, 1483, + 3462, 1754, 95, 1894, 1483, 1502, 1503, 1504, 1505, 1506, + 1507, 1508, 1510, 1509, 1511, 1512, 1480, 1890, 1481, 1482, + 1483, 1480, 1858, 1481, 1482, 2615, 2666, 1480, 1754, 1481, + 1482, 1480, 121, 1481, 1482, 1480, 1216, 1481, 1482, 2954, + 2621, 3566, 3567, 2681, 3460, 1754, 3362, 3936, 1483, 1480, + 2497, 1481, 1482, 1480, 2629, 1481, 1482, 1480, 1934, 1481, + 1482, 3458, 1754, 2379, 4211, 3456, 1754, 2635, 2056, 4209, + 2632, 2633, 2634, 1483, 3454, 1754, 4053, 1483, 2108, 3452, + 1754, 1483, 4183, 3450, 1754, 679, 4057, 3448, 1754, 3976, + 1483, 3446, 1754, 2955, 1483, 3569, 2636, 4033, 2638, 3305, + 3304, 1480, 1483, 1481, 1482, 3303, 3222, 2641, 2967, 2642, + 1483, 2599, 3444, 1754, 1483, 1935, 1936, 1937, 1480, 1766, + 1481, 1482, 1480, 3244, 1481, 1482, 3571, 1483, 3245, 2659, + 3241, 1480, 2637, 1481, 1482, 2057, 1480, 3240, 1481, 1482, + 1480, 3908, 1481, 1482, 1480, 1056, 1481, 1482, 1480, 3242, + 1481, 1482, 3937, 1480, 3243, 1481, 1482, 1483, 2644, 742, + 2396, 2825, 2385, 1483, 3560, 3442, 1754, 3200, 3199, 1480, + 3621, 1481, 1482, 3440, 1754, 2607, 2608, 4015, 1483, 3739, + 2610, 3548, 3438, 1754, 3545, 2695, 3517, 2167, 1483, 2611, + 3741, 2813, 3544, 2857, 3424, 1754, 1057, 1480, 3209, 1481, + 1482, 2005, 3400, 1754, 1026, 3283, 2846, 1754, 3246, 2938, + 2908, 2909, 1042, 2138, 3726, 2136, 3725, 2860, 1483, 2844, + 1754, 2959, 1480, 2184, 1481, 1482, 1480, 2591, 1481, 1482, + 1480, 1483, 1481, 1482, 2590, 2896, 2897, 1182, 2858, 1480, + 2185, 1481, 1482, 1480, 2423, 1481, 1482, 1042, 2916, 2819, + 1754, 1480, 2589, 1481, 1482, 2796, 1754, 2588, 2861, 1480, + 2863, 1481, 1482, 1480, 2587, 1481, 1482, 1059, 1483, 3724, + 2788, 1754, 1483, 2895, 2586, 1060, 1480, 2107, 1481, 1482, + 2779, 1754, 1483, 2876, 2585, 1181, 2159, 2148, 2149, 2150, + 2151, 2161, 2152, 2153, 2154, 2166, 2162, 2155, 2156, 2163, + 2164, 2165, 2157, 2158, 2160, 3378, 1480, 2954, 1481, 1482, + 2777, 1754, 1480, 3037, 1481, 1482, 1483, 1722, 2848, 1445, + 2885, 2914, 1068, 2764, 1754, 2994, 42, 1480, 129, 1481, + 1482, 101, 3550, 1483, 103, 2913, 1067, 1480, 2915, 1481, + 1482, 102, 2868, 4225, 2941, 2943, 2971, 1483, 1716, 3212, + 3214, 1483, 2104, 2102, 2103, 2888, 2934, 1483, 3215, 2873, + 2762, 1754, 2992, 1483, 2760, 1754, 101, 1480, 2918, 1481, + 1482, 2602, 103, 4132, 2758, 1754, 102, 4034, 3931, 2922, + 1480, 3528, 1481, 1482, 2925, 1483, 110, 2407, 2408, 2489, + 2932, 3282, 2912, 2391, 1483, 2935, 2627, 109, 2957, 108, + 3003, 2179, 3198, 2960, 2961, 2062, 2180, 103, 2756, 1754, + 3197, 2061, 2946, 108, 4000, 3999, 3607, 1480, 1483, 1481, + 1482, 1480, 3979, 1481, 1482, 2754, 1754, 1483, 3747, 2956, + 3745, 1480, 3744, 1481, 1482, 1483, 3737, 3736, 3617, 2752, + 1754, 3549, 2964, 3483, 2242, 2968, 2969, 2970, 2965, 2750, + 1754, 110, 1483, 3547, 3000, 2748, 1754, 1483, 3314, 1882, + 2524, 1483, 109, 1930, 108, 1480, 1877, 1481, 1482, 3608, + 3609, 3610, 110, 2989, 2990, 1066, 109, 2746, 1754, 3538, + 4212, 2891, 1480, 109, 1481, 1482, 2744, 1754, 3710, 2999, + 3047, 3048, 4213, 4212, 4213, 3067, 1480, 2872, 1481, 1482, + 1480, 1483, 1481, 1482, 3101, 1483, 1480, 2685, 1481, 1482, + 2742, 1754, 1480, 1483, 1481, 1482, 1931, 1932, 1933, 2740, + 1754, 2377, 1483, 3026, 2325, 1783, 3064, 2738, 1754, 1483, + 3045, 1775, 114, 115, 1480, 4021, 1481, 1482, 3029, 1483, + 3583, 3, 97, 1480, 3479, 1481, 1482, 1, 2076, 2736, + 1754, 10, 1034, 3415, 2357, 3082, 3083, 3084, 3085, 3086, + 3087, 3088, 3089, 3090, 3091, 2074, 1448, 1480, 9, 1481, + 1482, 1755, 2364, 3001, 2075, 3099, 1480, 8, 1481, 1482, + 1447, 3587, 3065, 4144, 1480, 695, 1481, 1482, 2367, 1720, + 4184, 4140, 4141, 2734, 1754, 1974, 1964, 2732, 1754, 3649, + 2293, 1480, 1483, 1481, 1482, 3414, 1480, 3932, 1481, 1482, + 1480, 2389, 1481, 1482, 2730, 1754, 1483, 3317, 2530, 3615, + 1483, 2725, 1754, 2487, 1483, 1134, 154, 3141, 3049, 3143, + 2445, 3406, 3103, 2446, 1483, 3159, 3066, 3035, 4109, 118, + 3036, 1092, 117, 1137, 1483, 3154, 3155, 3156, 3157, 1245, + 1480, 2979, 1481, 1482, 1480, 2525, 1481, 1482, 1483, 3639, + 3046, 2939, 1480, 2454, 1481, 1482, 1808, 1806, 1807, 1805, + 1810, 1480, 3177, 1481, 1482, 1809, 4081, 3386, 1480, 2686, + 1481, 1482, 3166, 3168, 3484, 2067, 732, 2911, 1480, 726, + 1481, 1482, 192, 2423, 2721, 1754, 3092, 2328, 2330, 2328, + 2330, 1483, 1797, 1776, 2060, 1176, 685, 3291, 2719, 1754, + 2563, 691, 3404, 3139, 1533, 3229, 2842, 87, 2501, 2055, + 2423, 2423, 2423, 2423, 2423, 3196, 2841, 3149, 3150, 3151, + 3152, 3153, 2926, 1086, 1078, 3177, 2712, 1754, 2378, 2862, + 2423, 1085, 3909, 2423, 3230, 3542, 3167, 3208, 3169, 3210, + 2837, 1480, 1483, 1481, 1482, 2878, 3176, 1483, 3234, 3213, + 3206, 2016, 3251, 4014, 3738, 1480, 4094, 1481, 1482, 1480, + 3204, 1481, 1482, 1480, 2936, 1481, 1482, 1483, 3194, 3188, + 1772, 1483, 3504, 1480, 2425, 1481, 1482, 1483, 2658, 2174, + 3203, 1523, 3201, 1480, 2950, 1481, 1482, 757, 2422, 3216, + 3217, 3705, 2094, 755, 754, 752, 3334, 1480, 1045, 1481, + 1482, 2425, 2425, 2425, 2425, 2425, 3342, 3253, 3233, 1483, + 3254, 1046, 3202, 3236, 3237, 3235, 3239, 1483, 3238, 104, + 105, 2425, 3247, 1047, 2425, 1483, 2864, 3255, 3189, 3191, + 3193, 2892, 1487, 3261, 2710, 1754, 1486, 963, 2852, 2836, + 1480, 1784, 1481, 1482, 1483, 2903, 3292, 2901, 3294, 2900, + 2600, 2430, 3295, 3296, 3568, 3293, 1483, 3564, 4136, 2835, + 2424, 2420, 2871, 2834, 914, 3346, 913, 764, 756, 2833, + 746, 976, 912, 3343, 911, 3344, 3345, 3219, 3009, 2489, + 3336, 3315, 3359, 3011, 2937, 3347, 3355, 1464, 3353, 1739, + 1742, 1480, 2386, 1481, 1482, 1105, 1480, 3383, 1481, 1482, + 3225, 2832, 4040, 2626, 3412, 3225, 1483, 1738, 4047, 2823, + 3325, 3371, 3374, 3373, 3633, 3306, 1480, 2822, 1481, 1482, + 1480, 2985, 1481, 1482, 3381, 2517, 1480, 69, 1481, 1482, + 46, 4009, 3391, 4078, 906, 903, 2821, 3388, 3389, 3707, + 3390, 3708, 3709, 3392, 3162, 3394, 3163, 3396, 2820, 4060, + 4061, 3407, 3408, 3409, 3410, 3411, 902, 4062, 1480, 2231, + 1481, 1482, 1458, 1455, 4157, 2069, 1480, 96, 1481, 1482, + 36, 35, 34, 33, 1480, 32, 1481, 1482, 26, 25, + 24, 1536, 23, 22, 29, 1536, 19, 2645, 21, 20, + 3316, 2650, 18, 1480, 3328, 1481, 1482, 4179, 2817, 4224, + 123, 3529, 55, 3531, 52, 1480, 50, 1481, 1482, 131, + 130, 53, 49, 1219, 2653, 47, 2654, 3499, 31, 30, + 17, 16, 2662, 15, 3503, 14, 2664, 2665, 13, 12, + 11, 7, 6, 39, 38, 2671, 2672, 2673, 2674, 2675, + 2676, 2677, 2678, 2679, 2680, 37, 2682, 28, 27, 40, + 4, 3228, 2972, 2519, 3382, 1480, 0, 1481, 1482, 0, + 0, 0, 1717, 2423, 0, 0, 3534, 1483, 0, 2688, + 2689, 2690, 2691, 744, 2693, 2694, 3581, 2696, 3530, 3539, + 3532, 2698, 3546, 0, 0, 2703, 2704, 0, 2705, 3551, + 0, 2708, 2709, 2711, 2713, 2714, 2715, 2716, 2717, 2718, + 2720, 2722, 2723, 2724, 2726, 3337, 2728, 2729, 2731, 2733, + 2735, 2737, 2739, 2741, 2743, 2745, 2747, 2749, 2751, 2753, + 2755, 2757, 2759, 2761, 2763, 2765, 2766, 2767, 3573, 2769, + 3575, 2771, 3570, 2773, 2774, 3346, 2776, 2778, 2780, 0, + 3576, 0, 2783, 3343, 3536, 3506, 2787, 3508, 3509, 3510, + 2792, 2793, 2794, 2795, 2425, 3347, 3582, 1483, 0, 2812, + 3598, 0, 3600, 2806, 2807, 2808, 2809, 2810, 2811, 1483, + 0, 2815, 2816, 3592, 3593, 3376, 3377, 3563, 1483, 2818, + 0, 0, 1065, 1483, 2824, 1071, 1071, 0, 1483, 2827, + 2828, 2829, 2830, 2831, 0, 0, 3577, 3578, 0, 0, + 2838, 2839, 0, 2840, 0, 1483, 2843, 2845, 2389, 1483, + 2847, 0, 0, 3643, 3644, 0, 1480, 0, 1481, 1482, + 2859, 1483, 0, 0, 0, 3626, 0, 1483, 0, 3630, + 3631, 3632, 1483, 0, 3645, 0, 1483, 0, 0, 0, + 0, 1483, 0, 0, 2904, 2907, 2908, 2909, 2905, 2805, + 2906, 2910, 0, 0, 3566, 3567, 0, 0, 0, 0, + 1483, 2804, 0, 0, 0, 3661, 0, 1483, 0, 0, + 2803, 1483, 0, 0, 0, 2802, 0, 0, 0, 0, + 2801, 0, 0, 1483, 0, 0, 0, 0, 1483, 0, + 0, 0, 0, 3672, 0, 0, 0, 2800, 0, 1483, + 0, 2799, 0, 1483, 0, 0, 1480, 0, 1481, 1482, + 0, 0, 3694, 2798, 0, 0, 0, 0, 1480, 2797, + 1481, 1482, 1483, 0, 2791, 0, 0, 1480, 2790, 1481, + 1482, 0, 1480, 2789, 1481, 1482, 1483, 1480, 3723, 1481, + 1482, 3730, 0, 3732, 0, 3713, 0, 3714, 3715, 3716, + 1483, 0, 2786, 3703, 1480, 1483, 1481, 1482, 1480, 2785, + 1481, 1482, 0, 2784, 0, 0, 3229, 0, 3733, 87, + 1480, 3229, 1481, 1482, 0, 2782, 1480, 0, 1481, 1482, + 2775, 1480, 0, 1481, 1482, 1480, 1483, 1481, 1482, 0, + 1480, 2772, 1481, 1482, 0, 2770, 0, 1483, 0, 3666, + 2138, 1483, 2136, 0, 3763, 3734, 3743, 3755, 3742, 1480, + 3753, 1481, 1482, 0, 2768, 3750, 1480, 3752, 1481, 1482, + 1480, 0, 1481, 1482, 1483, 0, 0, 0, 2727, 0, + 3622, 3623, 1480, 0, 1481, 1482, 3915, 1480, 0, 1481, + 1482, 0, 2707, 3767, 0, 0, 0, 2706, 1480, 0, + 1481, 1482, 1480, 0, 1481, 1482, 0, 0, 0, 0, + 42, 2904, 2907, 2908, 2909, 2905, 0, 2906, 2910, 0, + 0, 1480, 0, 1481, 1482, 0, 3907, 3906, 2702, 0, + 0, 0, 0, 0, 3934, 1480, 3922, 1481, 1482, 2700, + 0, 3905, 0, 2692, 3926, 3927, 0, 0, 0, 1480, + 0, 1481, 1482, 0, 1480, 0, 1481, 1482, 0, 3973, + 3974, 3077, 3078, 3079, 3080, 3081, 2663, 3757, 3764, 3765, + 0, 0, 3731, 0, 0, 3917, 3918, 3919, 2138, 0, + 2136, 3096, 3977, 0, 0, 1480, 0, 1481, 1482, 0, + 0, 0, 0, 0, 0, 0, 1480, 0, 1481, 1482, + 1480, 0, 1481, 1482, 0, 0, 0, 0, 0, 3225, + 0, 0, 3980, 0, 4022, 3229, 3983, 0, 3759, 0, + 4013, 0, 0, 1480, 0, 1481, 1482, 0, 0, 1554, + 1555, 1556, 1557, 1558, 1559, 1560, 1561, 1562, 1563, 1564, + 1565, 1566, 1567, 1568, 1569, 1570, 1571, 1572, 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, 1653, 1654, 1655, 1656, + 1657, 1658, 1659, 1660, 1661, 1662, 1663, 1664, 1665, 1666, + 1667, 1668, 1674, 1675, 1676, 1677, 1691, 1692, 1693, 1694, + 1695, 1696, 1697, 1698, 1699, 1700, 1701, 1702, 1703, 1704, + 3978, 4023, 3228, 4007, 3997, 4006, 1483, 3228, 1744, 0, + 0, 4003, 0, 4005, 4041, 0, 0, 0, 0, 0, + 0, 0, 1752, 0, 0, 1745, 3910, 0, 0, 0, + 0, 3231, 87, 0, 0, 0, 4026, 0, 1717, 1483, + 0, 0, 0, 4025, 0, 0, 1744, 0, 0, 3249, + 2383, 2384, 1751, 1749, 1750, 1746, 0, 1747, 0, 4030, + 1752, 4043, 0, 1745, 0, 0, 0, 4046, 0, 0, + 4048, 0, 3914, 0, 0, 0, 0, 0, 0, 0, + 1748, 0, 0, 0, 0, 0, 0, 0, 1740, 1741, + 1751, 1749, 1750, 1746, 4055, 1747, 0, 0, 2657, 0, + 0, 0, 4065, 0, 0, 0, 0, 0, 0, 1484, + 0, 0, 0, 0, 0, 0, 0, 0, 1748, 0, + 0, 0, 0, 42, 0, 0, 4066, 0, 0, 4067, + 4091, 2652, 0, 0, 0, 87, 0, 0, 0, 0, + 1542, 0, 0, 0, 0, 0, 0, 0, 4076, 0, + 0, 0, 0, 0, 0, 1480, 0, 1481, 1482, 0, + 0, 4085, 0, 0, 1717, 0, 0, 0, 4027, 4097, + 0, 0, 0, 4095, 0, 3934, 4111, 3380, 4100, 4108, + 4105, 4102, 4101, 4099, 4104, 4103, 0, 0, 1480, 0, + 1481, 1482, 0, 0, 0, 0, 0, 0, 0, 3397, + 3398, 3228, 3399, 3401, 3403, 4130, 0, 0, 4153, 0, + 0, 0, 0, 4143, 0, 4135, 0, 4122, 4148, 0, + 0, 0, 0, 0, 4161, 0, 42, 0, 4163, 0, + 3416, 0, 0, 4174, 0, 3419, 0, 3421, 3422, 3423, + 3425, 3426, 3427, 3428, 3429, 3430, 3431, 3432, 3433, 3434, + 3435, 3436, 3437, 3439, 3441, 3443, 3445, 3447, 3449, 3451, + 3453, 3455, 3457, 3459, 3461, 3463, 3465, 3467, 3469, 3470, + 3472, 3473, 3474, 3476, 4122, 4194, 3478, 2016, 3480, 3481, + 3482, 4193, 4204, 3486, 3487, 3488, 3489, 3490, 3491, 3492, + 3493, 3494, 3495, 3496, 4210, 2138, 4208, 2136, 4197, 4207, + 4206, 4203, 3502, 4178, 4173, 4218, 3507, 4092, 4032, 4087, + 3511, 3512, 0, 3513, 3515, 4226, 3518, 3520, 3225, 3522, + 3523, 3524, 3525, 4234, 4232, 0, 0, 0, 0, 3533, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 4051, 4243, 4244, 3974, 4242, 0, 0, 0, + 0, 4122, 0, 2138, 0, 2136, 0, 4241, 0, 0, + 4039, 0, 0, 0, 3557, 3558, 0, 0, 3562, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 4169, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 4086, + 1774, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 86, 44, 45, 88, 0, 0, 1863, 0, + 0, 0, 0, 3637, 0, 0, 0, 0, 0, 0, + 0, 92, 0, 0, 0, 48, 76, 77, 0, 74, + 78, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 75, 0, 0, 0, 0, 0, 1723, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 3656, 0, + 0, 3660, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 62, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 95, 0, 1024, 0, 0, + 2332, 0, 0, 1025, 0, 3673, 0, 0, 0, 0, + 0, 0, 4191, 2137, 0, 0, 677, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1029, 0, 0, 0, + 0, 0, 0, 0, 0, 83, 2021, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 3696, + 0, 0, 0, 0, 0, 0, 0, 0, 1100, 0, + 0, 0, 3704, 0, 0, 0, 0, 0, 0, 3711, + 0, 0, 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, 1008, 1009, + 1010, 1011, 1012, 1013, 1014, 1015, 1016, 1017, 1018, 1019, + 1020, 1021, 1022, 1023, 0, 0, 0, 0, 0, 0, + 51, 54, 57, 56, 59, 0, 73, 0, 0, 82, + 79, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 61, 91, 90, 0, 0, 71, 72, + 58, 0, 0, 0, 0, 0, 80, 81, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 3923, 0, + 0, 0, 0, 0, 0, 0, 0, 3930, 0, 0, + 63, 64, 0, 65, 66, 67, 68, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 3940, 3941, 3942, + 0, 3944, 0, 3945, 3946, 0, 0, 0, 0, 3949, + 3950, 3951, 3952, 3953, 3954, 3955, 3956, 3957, 3958, 3959, + 3960, 3961, 3962, 3963, 3964, 3965, 3966, 3967, 3968, 3969, + 3970, 0, 3972, 3975, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 2089, 2090, 2091, 2092, 60, 3984, 3985, + 3986, 3987, 3988, 3990, 3991, 3993, 3995, 3996, 3998, 2105, + 0, 0, 4002, 0, 0, 0, 4004, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 2144, 2145, 0, 0, 0, 0, + 2168, 4031, 0, 2172, 2173, 0, 0, 0, 2178, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 2190, 2191, 2192, 2193, 2194, 2195, 2196, + 2197, 2198, 2199, 0, 2201, 0, 0, 89, 2223, 2224, + 2225, 2226, 2227, 2228, 2229, 2230, 2232, 0, 2237, 0, + 2239, 2240, 2241, 0, 2243, 2244, 2245, 0, 2247, 2248, + 2249, 2250, 2251, 2252, 2253, 2254, 2255, 2256, 2257, 2258, + 2259, 2260, 2261, 2262, 2263, 2264, 2265, 2266, 2267, 2268, + 2269, 2270, 2271, 2272, 2273, 2274, 2275, 2276, 2277, 2278, + 2279, 2280, 2281, 2282, 2283, 2284, 2285, 2286, 2287, 2288, + 2289, 2290, 2291, 2292, 2296, 2297, 2298, 2299, 2300, 2301, + 2302, 2303, 2304, 2305, 2306, 2307, 2308, 2309, 2310, 2311, + 2312, 2313, 2314, 2315, 2316, 2317, 2318, 0, 0, 0, + 0, 0, 2324, 0, 2326, 0, 2333, 2334, 2335, 2336, + 2337, 2338, 0, 0, 0, 0, 0, 94, 0, 0, + 0, 0, 0, 0, 0, 2349, 2350, 2351, 2352, 2353, + 2354, 2355, 2356, 0, 2358, 2359, 2360, 2361, 2362, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 190, 0, 0, 1071, 4177, 0, 0, 0, 4056, 0, + 0, 0, 0, 1826, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 129, 0, 151, 0, 0, 0, 0, + 0, 0, 4071, 0, 2403, 2404, 0, 0, 4074, 172, + 4075, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 70, 0, 0, 0, 0, 0, + 2442, 0, 0, 4090, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 162, 0, 190, 0, 0, 1826, 150, + 0, 0, 1220, 0, 1226, 0, 1881, 0, 0, 4116, + 4117, 0, 0, 0, 959, 0, 0, 0, 129, 169, + 151, 0, 170, 4124, 4126, 4128, 0, 0, 0, 0, + 0, 0, 0, 0, 172, 0, 0, 0, 0, 0, + 0, 4134, 2485, 0, 138, 139, 161, 160, 189, 0, + 0, 0, 0, 4156, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1454, 0, 162, 0, + 195, 0, 0, 195, 150, 0, 0, 730, 0, 0, + 0, 0, 736, 0, 0, 0, 0, 0, 0, 0, + 1813, 4175, 0, 195, 169, 0, 0, 170, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 195, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1885, + 1886, 161, 160, 189, 0, 4198, 4200, 4202, 0, 0, + 0, 0, 0, 0, 736, 195, 736, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 155, 136, 158, 143, 135, 1813, 156, 157, 4223, 0, + 0, 0, 0, 173, 0, 0, 0, 0, 0, 0, + 0, 0, 179, 144, 1827, 0, 4235, 4236, 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, 0, + 0, 0, 0, 0, 0, 155, 1887, 158, 0, 1884, + 0, 156, 157, 0, 0, 0, 0, 0, 173, 1827, + 1840, 1843, 1844, 1845, 1846, 1847, 1848, 179, 1849, 1850, + 1852, 1853, 1851, 1854, 1855, 1828, 1829, 1830, 1831, 1811, + 1812, 1841, 0, 1814, 0, 1815, 1816, 1817, 1818, 1819, + 1820, 1821, 1822, 1823, 0, 0, 1824, 1832, 1833, 1834, + 1835, 0, 1836, 1837, 1838, 1839, 0, 0, 1825, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 164, 0, 0, 0, 1840, 1843, 1844, 1845, 1846, + 1847, 1848, 0, 1849, 1850, 1852, 1853, 1851, 1854, 1855, + 1828, 1829, 1830, 1831, 1811, 1812, 1841, 0, 1814, 0, + 1815, 1816, 1817, 1818, 1819, 1820, 1821, 1822, 1823, 0, + 0, 1824, 1832, 1833, 1834, 1835, 0, 1836, 1837, 1838, + 1839, 0, 0, 1825, 0, 0, 0, 0, 0, 0, + 2661, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 2667, 2668, 2669, 2670, 0, 0, 164, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 159, 0, 0, + 0, 0, 1786, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1542, 0, 0, 0, 0, + 0, 1803, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 159, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1842, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1944, 0, 152, 0, 0, 153, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1989, + 165, 0, 0, 0, 0, 0, 0, 177, 0, 0, + 0, 0, 0, 0, 0, 0, 2017, 0, 1842, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 2028, 0, 0, 0, 0, 0, 0, 2032, + 0, 152, 0, 0, 153, 0, 0, 0, 185, 1774, + 2043, 2044, 2045, 2046, 2047, 2048, 2049, 0, 0, 0, + 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, 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, 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, 195, + 0, 195, 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, 736, 0, + 736, 736, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 736, 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, 1528, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 2082, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 3042, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 3068, + 3069, 3070, 0, 0, 3072, 0, 0, 3074, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 3093, 3094, 3095, + 0, 0, 0, 0, 0, 0, 3100, 0, 0, 0, + 0, 3102, 0, 0, 3104, 3105, 3106, 0, 0, 0, + 3107, 3108, 0, 0, 3109, 0, 3110, 0, 0, 0, + 0, 0, 0, 3111, 0, 3112, 0, 0, 0, 3113, + 0, 3114, 0, 0, 3115, 0, 3116, 0, 3117, 0, + 3118, 0, 3119, 0, 3120, 0, 3121, 0, 3122, 0, + 3123, 0, 3124, 0, 3125, 0, 3126, 0, 3127, 0, + 3128, 0, 3129, 0, 3130, 0, 3131, 0, 3132, 0, + 0, 0, 3133, 0, 3134, 0, 3135, 0, 0, 3136, + 0, 3137, 0, 3138, 0, 2296, 3140, 0, 0, 3142, + 0, 0, 3144, 3145, 3146, 3147, 0, 0, 0, 0, + 3148, 2296, 2296, 2296, 2296, 2296, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 3158, 0, 0, 0, + 0, 0, 0, 0, 3171, 0, 0, 3175, 0, 0, + 0, 0, 0, 0, 0, 0, 3178, 3179, 3180, 3181, + 3182, 3183, 0, 0, 0, 3184, 3185, 0, 3186, 0, + 3187, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 195, 0, 0, 0, 736, 736, 0, 0, + 0, 0, 0, 0, 1071, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 195, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 3220, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 736, 0, 0, 195, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 3250, 736, 0, 2409, 0, 0, 0, 0, 195, 0, + 0, 2413, 736, 2416, 0, 0, 2082, 0, 0, 0, + 0, 0, 736, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 736, 0, 736, 0, 0, + 0, 0, 0, 0, 0, 736, 0, 0, 1528, 736, + 3312, 0, 736, 736, 736, 736, 0, 736, 0, 736, + 736, 0, 736, 736, 736, 736, 736, 736, 0, 0, + 0, 0, 0, 0, 0, 1528, 736, 736, 1528, 736, + 1528, 195, 736, 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, 736, 0, 195, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 736, 0, 0, + 736, 0, 195, 195, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 3405, 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, 736, 3420, 0, 0, 0, 1024, + 0, 0, 0, 0, 0, 1025, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 2137, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 2082, 0, 0, 0, + 0, 0, 0, 2575, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 2596, 2597, 0, 0, 2601, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 2606, 0, 0, 0, 0, 0, 0, 2609, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 2612, 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, 1008, 1009, 1010, 1011, 1012, 1013, 1014, 1015, 1016, 1017, - 1018, 1019, 1020, 1021, 1022, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 164, 0, 0, 0, 3347, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 1785, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1802, 2659, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 2665, 2666, 2667, 2668, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 3348, 3349, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 159, 0, - 0, 1541, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 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, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1988, 0, 0, 0, 0, 0, 0, - 0, 928, 0, 0, 0, 0, 0, 932, 0, 0, - 2016, 929, 930, 0, 0, 0, 931, 933, 0, 0, - 0, 0, 0, 0, 0, 0, 2027, 0, 0, 0, - 0, 0, 0, 2031, 0, 0, 0, 152, 0, 0, - 153, 0, 0, 0, 2042, 2043, 2044, 2045, 2046, 2047, - 2048, 0, 0, 0, 0, 0, 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, 0, 0, - 0, 0, 0, 0, 0, 1773, 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, 190, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 1880, 0, 0, 0, - 0, 0, 195, 0, 195, 0, 0, 0, 129, 0, - 151, 0, 166, 171, 168, 174, 175, 176, 178, 180, - 181, 182, 183, 0, 172, 0, 0, 0, 184, 186, - 187, 188, 1023, 0, 0, 2331, 0, 0, 1024, 0, - 0, 735, 0, 735, 735, 0, 0, 0, 2136, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 162, 0, - 0, 0, 0, 0, 150, 735, 195, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 169, 0, 0, 170, 0, 0, - 0, 0, 0, 0, 1527, 0, 0, 2081, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 1884, - 1885, 161, 160, 189, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 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, 1008, 1009, 1010, 1011, 1012, 1013, - 1014, 1015, 1016, 1017, 1018, 1019, 1020, 1021, 1022, 0, - 0, 0, 0, 0, 0, 0, 0, 3039, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 3065, 3066, 3067, 0, 0, 3069, - 0, 0, 3071, 0, 0, 155, 1886, 158, 0, 1883, - 0, 156, 157, 0, 0, 0, 0, 0, 173, 0, - 0, 0, 3090, 3091, 3092, 0, 0, 179, 0, 0, - 0, 3097, 0, 0, 0, 0, 3099, 0, 0, 3101, - 3102, 3103, 0, 0, 0, 3104, 3105, 0, 0, 3106, - 0, 3107, 0, 0, 0, 0, 0, 0, 3108, 0, - 3109, 0, 0, 0, 3110, 0, 3111, 0, 0, 3112, - 0, 3113, 0, 3114, 0, 3115, 0, 3116, 0, 3117, - 0, 3118, 0, 3119, 0, 3120, 0, 3121, 0, 3122, - 0, 3123, 0, 3124, 0, 3125, 0, 3126, 0, 3127, - 0, 3128, 0, 3129, 0, 0, 0, 3130, 0, 3131, - 0, 3132, 0, 0, 3133, 0, 3134, 0, 3135, 0, - 2295, 3137, 0, 0, 3139, 0, 0, 3141, 3142, 3143, - 3144, 0, 0, 0, 0, 3145, 2295, 2295, 2295, 2295, - 2295, 0, 0, 0, 0, 195, 0, 0, 0, 735, - 735, 3155, 0, 0, 0, 164, 0, 0, 0, 3168, - 0, 0, 3172, 0, 0, 0, 0, 0, 195, 0, - 0, 3175, 3176, 3177, 3178, 3179, 3180, 0, 0, 0, - 3181, 3182, 0, 3183, 0, 3184, 0, 0, 0, 735, - 0, 0, 195, 0, 0, 0, 0, 2408, 0, 0, - 0, 0, 0, 0, 735, 2412, 0, 2415, 0, 1070, - 2081, 195, 0, 0, 0, 735, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 735, 0, 0, 0, 0, - 3217, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 735, 0, - 735, 159, 0, 0, 0, 3247, 0, 0, 735, 0, - 0, 1527, 735, 0, 0, 735, 735, 735, 735, 0, - 735, 0, 735, 735, 0, 735, 735, 735, 735, 735, - 735, 0, 0, 0, 0, 0, 0, 0, 1527, 735, - 735, 1527, 735, 1527, 195, 735, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 195, 3309, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 735, 0, 195, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 735, 0, 0, 735, 0, 195, 195, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 152, 0, 195, 153, 0, 0, 0, 0, 0, 195, - 0, 0, 0, 0, 0, 0, 0, 0, 195, 195, - 195, 195, 195, 195, 195, 195, 195, 735, 0, 0, - 0, 0, 0, 0, 165, 0, 0, 0, 0, 0, - 0, 177, 0, 0, 0, 0, 0, 0, 0, 0, - 2081, 3402, 0, 0, 1023, 0, 0, 2574, 0, 0, - 1024, 0, 0, 0, 0, 0, 0, 0, 2594, 2595, - 2136, 0, 2599, 0, 0, 0, 0, 0, 0, 3417, - 0, 0, 185, 0, 2604, 0, 0, 0, 0, 0, - 0, 2607, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 2610, 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, 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, 1008, 1009, 1010, 1011, - 1012, 1013, 1014, 1015, 1016, 1017, 1018, 1019, 1020, 1021, - 1022, 0, 0, 0, 0, 0, 0, 735, 735, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 735, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 195, 0, 0, 0, 0, 0, 0, + 1018, 1019, 1020, 1021, 1022, 1023, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 736, 736, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 736, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 195, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 3618, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 735, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1527, 0, 0, 0, 3615, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1527, 0, 957, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 3642, 0, 0, 0, + 736, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1528, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1528, 0, 0, 0, 0, 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, 0, 0, + 0, 0, 0, 0, 3662, 0, 3663, 0, 3664, 0, + 3665, 0, 0, 0, 0, 0, 0, 0, 3668, 3669, + 0, 0, 0, 0, 0, 0, 0, 0, 3674, 0, + 0, 958, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 3675, 0, 3676, 0, 3677, 0, 3678, 0, + 3679, 0, 3680, 0, 3681, 0, 3682, 0, 3683, 0, + 3684, 0, 3685, 0, 3686, 0, 3687, 0, 3688, 0, + 3689, 0, 3690, 0, 0, 3691, 0, 0, 0, 3692, + 0, 3693, 0, 0, 0, 0, 0, 3695, 0, 0, + 0, 0, 0, 713, 0, 0, 0, 0, 0, 735, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 3712, + 0, 0, 0, 0, 2343, 0, 0, 0, 3717, 0, + 3718, 3719, 0, 3720, 0, 3721, 0, 0, 0, 0, + 3722, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 735, 0, 735, 0, 0, 0, 3751, 195, 0, + 0, 0, 0, 736, 0, 0, 0, 0, 0, 0, + 3760, 0, 0, 3762, 0, 0, 2917, 0, 0, 0, + 0, 0, 0, 0, 0, 3766, 0, 0, 0, 0, + 0, 0, 0, 195, 0, 0, 736, 0, 0, 0, + 0, 3902, 0, 0, 0, 0, 0, 0, 0, 0, + 195, 0, 0, 0, 736, 0, 0, 2343, 195, 0, + 195, 0, 195, 195, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 736, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 712, 0, 0, 0, 0, - 0, 734, 0, 0, 0, 0, 0, 0, 3659, 0, - 3660, 0, 3661, 0, 3662, 0, 0, 0, 0, 0, - 0, 0, 3665, 3666, 0, 0, 0, 0, 0, 0, - 0, 0, 3671, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 3672, 0, 3673, 0, - 3674, 0, 3675, 734, 3676, 734, 3677, 0, 3678, 0, - 3679, 0, 3680, 0, 3681, 0, 3682, 2342, 3683, 0, - 3684, 0, 3685, 0, 3686, 0, 3687, 0, 0, 3688, - 0, 0, 0, 3689, 0, 3690, 0, 0, 0, 0, - 0, 3692, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 2966, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 195, 0, 3709, 0, 0, 735, 0, 0, 0, - 2915, 0, 3714, 0, 3715, 3716, 0, 3717, 0, 3718, - 0, 0, 0, 0, 3719, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 195, 0, 0, 735, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 3748, 0, 195, 0, 0, 0, 735, 0, 0, - 2342, 195, 0, 195, 3757, 195, 195, 3759, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 3763, - 735, 0, 0, 0, 0, 2964, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 3899, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 736, 0, 0, 0, 0, + 0, 0, 736, 736, 736, 195, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 3014, 3015, 3016, + 3017, 3018, 3019, 736, 0, 0, 0, 0, 0, 736, + 736, 0, 0, 736, 0, 736, 0, 0, 0, 0, + 4012, 736, 0, 0, 0, 0, 2082, 3030, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 735, 0, - 0, 0, 0, 0, 0, 735, 735, 735, 195, 0, - 0, 3012, 3013, 3014, 3015, 3016, 3017, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 735, 0, 0, 0, - 0, 0, 735, 735, 0, 0, 735, 0, 735, 2081, - 3027, 0, 0, 0, 735, 0, 0, 0, 0, 0, + 0, 3038, 0, 0, 0, 0, 736, 0, 0, 0, + 0, 736, 0, 0, 0, 736, 736, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 3035, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 735, - 0, 0, 0, 0, 735, 0, 0, 0, 735, 735, - 0, 0, 0, 0, 4009, 0, 0, 0, 0, 0, 0, 0, 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, 0, 195, 195, 0, 0, 195, 0, 195, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 195, 0, 0, 0, 914, 0, 0, 195, 0, 0, + 0, 195, 195, 0, 0, 195, 0, 195, 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, 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, 735, 0, 0, 0, 0, + 195, 0, 0, 0, 0, 0, 0, 195, 0, 0, + 0, 0, 736, 95, 0, 0, 1024, 0, 0, 0, + 0, 964, 1025, 977, 978, 979, 965, 0, 0, 966, + 967, 0, 968, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 973, 0, + 980, 981, 0, 0, 0, 0, 0, 4054, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1528, 0, 2343, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 3348, + 3349, 0, 0, 4068, 0, 0, 4069, 0, 4070, 0, + 0, 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, 1008, 1009, 1010, + 1011, 1012, 1013, 1014, 1015, 1016, 1017, 1018, 1019, 1020, + 1021, 1022, 1023, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 733, 0, 0, 0, 0, 0, 0, 0, 0, 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, + 4154, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 3302, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 4170, + 0, 4171, 0, 4172, 0, 0, 0, 0, 0, 0, + 0, 3340, 0, 0, 0, 735, 1439, 735, 735, 0, + 0, 0, 0, 0, 0, 3354, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 3351, 3352, 0, 0, 735, + 0, 0, 0, 0, 0, 0, 3372, 0, 0, 3375, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 1527, - 0, 2342, 0, 0, 1095, 0, 1102, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1527, 0, + 0, 195, 0, 4221, 0, 4222, 0, 0, 0, 195, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 4051, 0, 0, 0, 0, 0, 0, 0, 0, + 736, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 736, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 929, 0, 0, 0, 0, 0, 933, 0, 195, 0, + 930, 931, 0, 195, 0, 932, 934, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 4065, 0, 0, - 4066, 0, 4067, 0, 0, 0, 0, 0, 0, 0, + 915, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 734, 1438, 734, 734, - 0, 0, 0, 0, 0, 0, 0, 0, 3299, 0, + 0, 0, 736, 0, 0, 0, 0, 0, 195, 0, + 0, 0, 0, 0, 3535, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 734, 736, + 0, 0, 0, 0, 0, 0, 736, 0, 0, 0, + 736, 736, 0, 0, 0, 736, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 734, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 3337, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 3351, 1526, - 0, 0, 0, 0, 4151, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 3369, 0, - 0, 3372, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 4167, 0, 4168, 0, 4169, 0, 0, + 0, 1528, 736, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 195, 195, 195, 195, 195, 195, + 1096, 0, 1103, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 3596, + 0, 0, 0, 195, 195, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 3611, + 0, 3612, 3613, 3614, 0, 0, 0, 0, 195, 0, + 0, 0, 0, 735, 735, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 736, 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, 195, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 735, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 735, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 735, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 735, + 0, 0, 0, 0, 736, 0, 0, 0, 0, 735, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 4218, 0, 4219, - 0, 195, 0, 0, 0, 0, 195, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 735, 0, 735, 0, 0, 0, 0, 0, + 0, 0, 735, 0, 0, 1527, 735, 0, 0, 735, + 735, 735, 735, 0, 735, 0, 735, 735, 0, 735, + 735, 735, 735, 735, 735, 0, 0, 0, 0, 0, + 0, 0, 1527, 735, 735, 1527, 735, 1527, 0, 735, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 735, 0, 0, 0, 0, 0, 0, 0, 736, + 0, 0, 0, 0, 735, 0, 0, 735, 0, 0, + 0, 736, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 735, 3532, 0, 0, 0, - 0, 195, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 736, 0, 0, 0, 0, 0, 0, 0, + 0, 735, 0, 0, 0, 0, 0, 195, 0, 0, + 736, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 736, 0, 0, 0, 1528, 0, + 0, 736, 736, 1528, 195, 195, 195, 195, 195, 0, + 0, 0, 0, 0, 0, 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, 0, 0, 0, 0, 0, 0, - 0, 0, 735, 0, 0, 0, 0, 0, 0, 735, - 0, 0, 0, 735, 735, 0, 0, 0, 735, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1527, 735, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 195, 195, 195, - 195, 195, 195, 0, 734, 734, 0, 0, 0, 0, - 0, 3593, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 195, 195, 0, 0, 0, - 0, 3608, 0, 3609, 3610, 3611, 0, 0, 0, 0, - 0, 0, 0, 0, 734, 0, 0, 0, 0, 0, - 195, 0, 0, 0, 0, 0, 0, 0, 0, 734, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 734, 0, 0, 0, 0, 0, 735, 0, 0, 0, - 734, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 195, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 736, 0, 0, 1528, 0, + 0, 0, 0, 736, 0, 0, 0, 0, 195, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 734, 0, 734, 0, 0, 0, 0, - 0, 0, 0, 734, 0, 0, 1526, 734, 0, 0, - 734, 734, 734, 734, 0, 734, 735, 734, 734, 0, - 734, 734, 734, 734, 734, 734, 0, 0, 0, 0, - 0, 0, 0, 1526, 734, 734, 1526, 734, 1526, 0, - 734, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 1283, 0, 1283, - 1283, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 734, 0, 0, 0, 0, 0, 0, 0, - 0, 1452, 0, 0, 0, 734, 0, 0, 734, 0, + 0, 0, 195, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 195, 0, 0, 195, 0, 0, 0, + 0, 735, 735, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 735, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 735, 734, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 735, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 735, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1527, 0, 0, + 0, 0, 0, 0, 0, 0, 2146, 0, 0, 0, + 0, 0, 0, 0, 0, 1527, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 736, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1284, 0, 1284, 1284, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1453, 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, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 916, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 4052, 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, 735, 0, 0, 0, 0, 195, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 193, 0, 0, 678, 0, 0, 195, 0, 195, 195, + 195, 0, 0, 0, 0, 0, 0, 0, 736, 736, + 0, 0, 0, 678, 0, 0, 0, 0, 0, 0, + 735, 0, 0, 0, 0, 0, 0, 0, 0, 1052, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1072, 1072, 0, 0, + 0, 0, 0, 735, 0, 678, 0, 736, 736, 736, + 736, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 735, 0, 0, 735, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 735, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 195, - 0, 0, 735, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 735, 0, 0, 0, - 1527, 0, 0, 735, 735, 1527, 195, 195, 195, 195, - 195, 0, 0, 0, 0, 0, 0, 0, 195, 0, - 0, 0, 0, 0, 195, 0, 195, 0, 0, 195, - 195, 195, 915, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 735, 193, 0, - 1527, 677, 734, 734, 0, 735, 0, 0, 0, 0, - 195, 0, 0, 0, 0, 0, 0, 734, 0, 0, - 0, 677, 0, 0, 195, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 1051, 0, 0, - 0, 0, 0, 0, 195, 0, 0, 195, 0, 0, - 0, 0, 0, 0, 1071, 1071, 0, 0, 0, 0, - 0, 0, 0, 677, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1725, 1726, 0, 734, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 1526, 0, - 0, 0, 0, 0, 0, 0, 0, 2145, 0, 0, - 0, 0, 0, 0, 0, 0, 1526, 0, 0, 0, - 0, 0, 0, 0, 0, 1779, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1797, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1856, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1865, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1095, 735, 1892, 0, 0, 0, - 0, 0, 0, 0, 1901, 0, 0, 0, 1903, 0, - 0, 1906, 1907, 1909, 1909, 0, 1909, 0, 1909, 1909, - 0, 1918, 1909, 1909, 1909, 1909, 1909, 0, 0, 0, - 0, 0, 195, 0, 0, 1938, 1939, 0, 1095, 0, - 0, 1944, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 734, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1986, 0, 0, 4049, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 2008, 0, 195, 2012, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 734, 0, 0, 0, 0, 0, 195, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1283, 0, 0, 0, 195, 0, 195, - 195, 195, 0, 0, 734, 0, 0, 0, 0, 735, - 735, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 734, 0, 0, 734, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 734, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 735, 735, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 735, 0, 0, 0, 0, 0, 0, 735, 735, 735, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 735, 0, 1726, 1727, 0, 0, 735, 735, 0, 0, + 735, 0, 735, 0, 0, 0, 0, 0, 735, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 734, 0, 0, 0, 0, 0, 0, - 734, 734, 734, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1780, 0, 736, 0, 736, 0, 195, 0, + 0, 0, 0, 735, 0, 0, 0, 1798, 735, 0, + 0, 0, 735, 735, 0, 0, 0, 1528, 1857, 0, + 0, 195, 0, 0, 736, 0, 736, 0, 1866, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 734, 0, 0, 0, 0, 0, 734, 734, 0, - 0, 734, 0, 734, 0, 0, 0, 0, 0, 734, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1283, 1283, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 2069, 0, - 0, 0, 0, 0, 734, 0, 0, 0, 0, 734, - 0, 0, 0, 734, 734, 0, 0, 0, 0, 0, + 0, 1096, 0, 1893, 0, 0, 0, 0, 0, 0, + 0, 1902, 0, 0, 0, 1904, 0, 0, 1907, 1908, + 1910, 1910, 0, 1910, 0, 1910, 1910, 0, 1919, 1910, + 1910, 1910, 1910, 1910, 736, 0, 0, 0, 0, 0, + 0, 0, 1939, 1940, 0, 1096, 0, 195, 1945, 0, + 736, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 736, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 735, + 1987, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 2009, 0, 0, 2013, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 735, 0, 735, 0, 195, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 2131, - 0, 0, 0, 0, 0, 0, 0, 0, 1527, 0, - 0, 0, 195, 0, 0, 735, 0, 735, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 736, 0, + 1284, 0, 0, 0, 0, 736, 0, 736, 0, 0, + 0, 0, 0, 1527, 0, 735, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 736, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 677, 0, 677, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 734, 0, 0, 0, 0, 735, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 195, 0, - 0, 735, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 735, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 677, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1526, 0, 734, 0, 1528, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1283, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 735, - 0, 0, 0, 0, 0, 0, 735, 0, 735, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 2379, 0, 0, 0, 0, 735, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 2392, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1779, 0, 0, 1283, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 1095, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1284, 1284, 0, 0, 0, 0, 736, 0, 0, 0, + 0, 0, 0, 0, 0, 2070, 0, 0, 0, 678, + 0, 678, 195, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 736, 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, 1102, 0, 0, 0, 0, 0, - 0, 2505, 2506, 2507, 0, 0, 0, 735, 0, 0, + 0, 0, 0, 0, 0, 0, 2132, 0, 0, 0, + 0, 0, 0, 678, 0, 0, 0, 735, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1095, 195, 0, 0, 0, 0, 1102, 1901, - 0, 0, 1901, 0, 1901, 0, 0, 0, 734, 0, - 2538, 735, 195, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 734, 0, 0, 0, 0, 0, 0, 677, + 0, 735, 0, 0, 0, 0, 736, 0, 0, 0, + 0, 1529, 0, 0, 0, 0, 736, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1528, 736, 0, 736, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1095, 0, 0, 0, 0, - 2131, 0, 1051, 0, 2131, 2131, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 2928, 0, 0, - 0, 0, 0, 0, 0, 0, 677, 735, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 735, 0, 0, - 0, 0, 0, 0, 0, 677, 0, 1527, 735, 0, + 0, 0, 0, 0, 0, 0, 2930, 0, 0, 0, + 0, 0, 0, 0, 736, 2343, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 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, 736, 0, 0, 735, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 735, 0, 0, 0, + 0, 0, 0, 735, 0, 0, 736, 735, 735, 0, + 1284, 0, 735, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1527, 735, + 736, 0, 0, 0, 0, 195, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 736, + 0, 736, 0, 0, 0, 0, 0, 0, 0, 2380, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 2393, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1780, 0, 0, 1284, 0, 0, 0, 0, 0, 0, + 0, 735, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1096, 0, 0, 0, 0, 0, 0, + 0, 0, 678, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1052, 0, 0, 0, 0, + 0, 735, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 678, + 0, 1103, 0, 0, 0, 0, 0, 0, 2506, 2507, + 2508, 0, 0, 0, 0, 0, 0, 0, 678, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1096, + 0, 0, 0, 0, 0, 1103, 1902, 0, 0, 1902, + 0, 1902, 0, 0, 0, 0, 0, 2539, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1529, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1096, 0, 0, 0, 0, 2132, 0, 0, + 0, 2132, 2132, 0, 0, 1529, 735, 0, 1529, 0, + 1529, 678, 0, 0, 0, 0, 0, 0, 735, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1961, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 678, 0, 0, 735, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 2015, 678, 0, 0, 0, 735, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 678, + 0, 735, 0, 0, 0, 1527, 678, 0, 735, 735, + 1527, 0, 0, 0, 0, 2041, 2042, 678, 678, 678, + 678, 678, 678, 678, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 2617, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 3298, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 735, 0, 0, 1527, 0, 0, 0, 0, 735, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1284, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 734, 0, 0, 0, 0, 735, 2342, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1528, 0, 734, 0, 0, - 0, 0, 0, 0, 734, 0, 0, 0, 734, 734, - 0, 0, 0, 734, 0, 0, 195, 735, 0, 0, - 0, 2615, 1528, 0, 0, 1528, 0, 1528, 677, 1526, - 734, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 1960, 0, - 0, 0, 0, 0, 0, 0, 0, 735, 0, 0, - 0, 0, 0, 677, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 2014, - 677, 735, 0, 0, 0, 0, 195, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 677, 1283, 0, 0, - 735, 0, 735, 677, 0, 0, 0, 0, 0, 0, - 0, 0, 2040, 2041, 677, 677, 677, 677, 677, 677, - 677, 734, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 3379, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 734, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 678, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 735, 0, 0, 0, 0, 0, 0, 0, 0, + 1529, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1529, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 2865, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 734, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 734, 0, + 2880, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 3586, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 677, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 734, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 2863, - 0, 0, 0, 0, 0, 0, 0, 734, 0, 0, - 0, 0, 0, 2878, 0, 0, 0, 0, 0, 0, - 0, 734, 0, 0, 0, 1526, 0, 0, 734, 734, - 1526, 0, 0, 0, 0, 0, 0, 1528, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1528, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 735, 735, 0, 0, 0, + 0, 0, 0, 0, 2015, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 3295, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 2962, 0, + 0, 0, 0, 0, 735, 735, 735, 735, 1961, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 2960, 734, 0, 0, 1526, 0, 0, 0, 0, - 734, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 2392, 0, - 0, 0, 0, 0, 0, 2984, 0, 0, 0, 1901, - 1901, 0, 0, 0, 2989, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1072, 2393, 0, 0, 0, 0, + 0, 0, 2986, 0, 0, 0, 1902, 1902, 0, 0, + 0, 2991, 0, 1052, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 3002, 0, + 678, 0, 0, 0, 0, 0, 0, 2015, 678, 0, + 678, 0, 678, 2432, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 3000, 3376, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 2014, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1960, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1071, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 2131, 0, 0, 0, 0, 0, 0, 0, - 1051, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 677, 0, 0, - 734, 0, 0, 0, 2014, 677, 0, 677, 0, 677, - 2431, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 2131, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 2132, 0, 0, 0, 0, 2509, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 735, 0, 735, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1527, 0, 0, 0, 0, 0, + 2132, 735, 0, 735, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 2508, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 3583, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 3157, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 1283, - 0, 0, 0, 0, 734, 734, 0, 0, 0, 0, + 0, 735, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 678, 0, 0, 0, 735, 0, 0, + 678, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 735, 678, 678, 0, 0, 678, 0, 2603, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 678, 0, 0, + 0, 0, 0, 0, 678, 3160, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1284, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 678, 0, 0, 0, 0, 0, 0, 2614, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1910, 0, + 0, 0, 0, 0, 0, 735, 0, 0, 0, 0, + 0, 0, 735, 0, 735, 0, 3205, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1284, 0, 0, 0, 0, 0, 0, 3232, 1910, 0, + 0, 0, 0, 735, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1529, 0, 2015, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1909, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 3202, 0, - 0, 0, 0, 734, 734, 734, 734, 0, 0, 0, - 677, 0, 1283, 0, 0, 0, 0, 677, 0, 3229, - 1909, 0, 0, 0, 0, 0, 0, 0, 677, 677, - 0, 0, 677, 0, 2601, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 677, 0, 0, 0, 0, 0, - 0, 677, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 677, 0, 0, - 0, 0, 0, 0, 2612, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1095, 0, 0, 0, 0, 0, 0, - 0, 2392, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1096, 0, 0, 0, 0, 0, 0, 0, 2393, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1528, 0, 2014, 0, 0, 0, 0, - 734, 0, 734, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1526, 0, 0, 0, 0, 0, 0, - 734, 0, 734, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 735, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 735, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 734, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 734, 0, 0, 0, - 0, 1856, 0, 0, 0, 0, 0, 0, 0, 734, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 735, 0, 0, 0, 0, 0, 0, + 0, 678, 0, 735, 0, 0, 0, 0, 0, 1961, + 1857, 0, 0, 1527, 735, 0, 735, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 734, 0, 0, 0, 0, 0, - 0, 734, 0, 734, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 677, 0, - 0, 0, 0, 0, 0, 0, 1960, 0, 0, 0, - 0, 0, 734, 0, 0, 0, 0, 0, 0, 0, + 0, 735, 735, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 678, 0, + 0, 0, 0, 678, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 735, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 2392, 2392, 0, 0, 0, - 0, 0, 0, 0, 0, 677, 0, 0, 0, 0, - 677, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 3647, 3648, 3649, 3650, 0, 0, + 0, 0, 0, 735, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 678, 0, + 0, 0, 0, 0, 0, 0, 0, 735, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 2393, 2393, 735, 0, 735, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 677, 0, 0, 0, 0, + 0, 1529, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 678, 678, 678, 678, 678, 678, + 0, 0, 0, 3650, 3651, 3652, 3653, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 734, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 678, 678, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 678, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 734, 0, 1528, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 677, 677, 677, 677, 677, 677, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 677, - 677, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 734, 0, 677, 0, 0, 0, 0, 0, - 0, 3725, 734, 3725, 0, 0, 0, 0, 0, 0, - 0, 0, 1526, 734, 0, 734, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 3753, 0, 3755, 0, 0, 0, 0, 0, 0, - 734, 734, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 734, 0, 0, 0, 0, 0, 0, 0, - 0, 2392, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 3922, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1283, 0, 734, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 734, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 734, 0, 734, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 3728, 0, 3728, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 3725, 0, 0, 0, 0, - 0, 0, 3725, 0, 3725, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 3756, 0, 3758, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 2392, 0, 0, 0, 0, 0, 0, - 0, 1071, 0, 677, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1528, 0, 0, 0, 0, 1528, - 677, 677, 677, 677, 677, 0, 0, 0, 0, 0, - 0, 0, 3245, 0, 0, 0, 0, 0, 1960, 0, - 677, 0, 0, 677, 3253, 2014, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 677, 0, + 2393, 0, 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, 1284, + 0, 0, 0, 0, 0, 1072, 0, 678, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1528, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 677, 0, 0, 0, 0, 0, - 0, 0, 0, 2392, 0, 0, 0, 0, 677, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1529, 0, + 0, 0, 0, 1529, 678, 678, 678, 678, 678, 0, + 0, 0, 0, 0, 0, 0, 3248, 0, 0, 0, + 0, 0, 1961, 0, 678, 0, 0, 678, 3256, 2015, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 677, 0, - 0, 677, 0, 0, 0, 0, 0, 2392, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 3785, - 3787, 3786, 3852, 3853, 3854, 3855, 3856, 3857, 3858, 3788, - 3789, 805, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 3728, 0, 0, 0, 0, 0, + 0, 3728, 0, 3728, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 678, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 2393, 0, 0, 0, 0, 0, 1529, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 678, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 678, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 4069, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 4077, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 2392, 0, 4085, 0, 0, 0, + 0, 0, 0, 678, 0, 0, 678, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1283, 1283, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 4135, 0, 0, 677, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 4077, 0, 0, 0, 0, 0, 0, + 0, 0, 2393, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 677, 0, 0, 0, 0, 2392, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 1856, 0, 4135, 0, - 0, 677, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 3793, 0, 0, 0, 0, 0, 0, 0, - 0, 677, 0, 677, 677, 677, 0, 3801, 3802, 0, - 0, 3877, 3876, 3875, 0, 0, 3873, 3874, 3872, 0, + 0, 0, 0, 0, 0, 0, 2393, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 678, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 3878, 928, 0, 781, 782, 3879, 3880, 932, - 3881, 784, 785, 929, 930, 0, 779, 783, 931, 933, + 0, 0, 4072, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 4080, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 2393, 0, 4088, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 678, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1284, 1284, 3788, 3790, 3789, 3855, 3856, 3857, 3858, 3859, + 3860, 3861, 3791, 3792, 806, 0, 678, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 678, 0, 678, 678, + 678, 0, 4138, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 3782, 3783, 3784, 3790, 3791, - 3792, 3803, 3850, 3851, 3859, 3861, 884, 3860, 3862, 3863, - 3864, 3867, 3868, 3869, 3870, 3865, 3866, 3871, 3765, 3769, - 3766, 3767, 3768, 3780, 3770, 3771, 3772, 3773, 3774, 3775, - 3776, 3777, 3778, 3779, 3781, 3882, 3883, 3884, 3885, 3886, - 3887, 3796, 3800, 3799, 3797, 3798, 3794, 3795, 3822, 3821, - 3823, 3824, 3825, 3826, 3827, 3828, 3830, 3829, 3831, 3832, - 3833, 3834, 3835, 3836, 3804, 3805, 3808, 3809, 3807, 3806, - 3810, 3819, 3820, 3811, 3812, 3813, 3814, 3815, 3816, 3818, - 3817, 3837, 3838, 3839, 3840, 3841, 3843, 3842, 3846, 3847, - 3845, 3844, 3849, 3848, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1960, 0, 0, 0, 934, 0, 935, - 0, 0, 939, 0, 0, 0, 941, 940, 0, 942, - 904, 903, 1528, 0, 936, 937, 1960, 938, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 4080, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 2393, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1857, 0, 4138, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 3888, 3889, 3890, 3891, 3892, 3893, 3894, 3895, - 0, 0, 1960, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -2870,1620 +2883,1793 @@ 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, 3796, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 3804, 3805, 0, 0, 3880, 3879, 3878, 1961, 0, + 3876, 3877, 3875, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1529, 0, 0, + 0, 1961, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 3881, 929, 0, 782, + 783, 3882, 3883, 933, 3884, 785, 786, 930, 931, 0, + 780, 784, 932, 934, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1961, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 3785, + 3786, 3787, 3793, 3794, 3795, 3806, 3853, 3854, 3862, 3864, + 885, 3863, 3865, 3866, 3867, 3870, 3871, 3872, 3873, 3868, + 3869, 3874, 3768, 3772, 3769, 3770, 3771, 3783, 3773, 3774, + 3775, 3776, 3777, 3778, 3779, 3780, 3781, 3782, 3784, 3885, + 3886, 3887, 3888, 3889, 3890, 3799, 3803, 3802, 3800, 3801, + 3797, 3798, 3825, 3824, 3826, 3827, 3828, 3829, 3830, 3831, + 3833, 3832, 3834, 3835, 3836, 3837, 3838, 3839, 3807, 3808, + 3811, 3812, 3810, 3809, 3813, 3822, 3823, 3814, 3815, 3816, + 3817, 3818, 3819, 3821, 3820, 3840, 3841, 3842, 3843, 3844, + 3846, 3845, 3849, 3850, 3848, 3847, 3852, 3851, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 935, 0, 936, 0, 0, 940, 0, 0, 0, + 942, 941, 0, 943, 905, 904, 0, 0, 937, 938, + 0, 939, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 3891, 3892, 3893, 3894, + 3895, 3896, 3897, 3898, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1961, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 678, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 1960, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 677, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1529, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 4110, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1528, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 4107, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1960, 0, 0, 396, 0, 0, 0, 0, 0, 0, - 1421, 1405, 528, 0, 1347, 1424, 1315, 1334, 1434, 1337, - 1340, 1384, 1293, 1362, 416, 1331, 1319, 1288, 1326, 1289, - 1317, 1349, 270, 1314, 1407, 1366, 1423, 366, 267, 1295, - 1286, 204, 505, 1320, 430, 1336, 203, 1386, 487, 252, - 377, 374, 583, 282, 273, 269, 250, 317, 385, 428, - 518, 422, 1430, 370, 1372, 0, 497, 401, 0, 0, - 2014, 1411, 1410, 1341, 1351, 1413, 1360, 1398, 1346, 1385, - 1303, 1371, 1425, 1332, 1381, 1426, 323, 248, 325, 202, - 413, 498, 286, 0, 0, 0, 0, 4109, 507, 959, - 0, 0, 0, 0, 4110, 0, 0, 0, 0, 238, - 0, 0, 245, 0, 0, 0, 351, 360, 359, 339, - 340, 342, 344, 350, 357, 363, 336, 345, 1328, 1378, - 609, 1420, 1329, 1380, 265, 321, 272, 264, 580, 1431, - 1412, 1292, 1359, 1419, 1354, 596, 0, 0, 229, 1422, - 1353, 0, 1383, 0, 1437, 1287, 1374, 0, 1290, 1294, - 1433, 1417, 1323, 275, 0, 0, 0, 0, 0, 0, - 0, 1350, 1361, 1395, 1399, 1344, 0, 0, 0, 0, - 0, 0, 0, 0, 1321, 0, 1370, 0, 0, 0, - 1299, 1291, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1348, 0, 0, 0, 0, 1302, - 0, 1322, 1396, 0, 1285, 297, 1296, 402, 257, 0, - 453, 1403, 1416, 1345, 629, 1418, 1343, 1342, 1390, 1300, - 1409, 1335, 365, 1298, 330, 197, 225, 0, 1333, 412, - 461, 473, 1408, 1318, 1327, 253, 1325, 471, 426, 604, - 233, 284, 458, 432, 469, 440, 287, 1369, 1388, 470, - 372, 585, 450, 601, 630, 631, 263, 406, 615, 522, - 623, 648, 226, 260, 420, 506, 607, 494, 397, 581, - 582, 329, 493, 295, 201, 369, 636, 224, 479, 371, - 242, 231, 587, 612, 299, 289, 456, 643, 213, 517, - 598, 239, 483, 0, 0, 651, 247, 504, 610, 599, - 215, 594, 503, 393, 326, 327, 214, 0, 457, 268, - 293, 0, 0, 258, 415, 589, 590, 256, 652, 228, - 622, 220, 1297, 621, 408, 584, 595, 394, 383, 219, - 593, 392, 382, 334, 355, 356, 280, 307, 447, 375, - 448, 306, 308, 404, 403, 405, 207, 608, 626, 0, - 208, 0, 499, 611, 653, 452, 212, 234, 235, 237, - 1313, 279, 283, 291, 294, 303, 304, 313, 367, 419, - 446, 442, 451, 1404, 579, 602, 616, 628, 634, 635, - 637, 638, 639, 640, 641, 644, 642, 407, 311, 495, - 333, 373, 1393, 1436, 425, 472, 240, 606, 496, 199, - 1307, 1312, 1305, 0, 254, 255, 1375, 575, 1308, 1306, - 1364, 1365, 1309, 1427, 1428, 1429, 1414, 654, 655, 656, - 657, 658, 659, 660, 661, 662, 663, 664, 665, 666, - 667, 668, 669, 670, 671, 649, 508, 514, 509, 510, - 511, 512, 513, 0, 515, 1397, 1301, 0, 1310, 1311, - 398, 1406, 591, 592, 672, 384, 486, 603, 335, 349, - 352, 341, 361, 0, 362, 337, 338, 343, 346, 347, - 348, 353, 354, 358, 364, 249, 210, 390, 399, 578, - 312, 216, 217, 218, 524, 525, 526, 527, 619, 620, - 624, 205, 462, 463, 464, 465, 292, 614, 309, 468, - 467, 331, 332, 379, 449, 540, 542, 553, 557, 559, - 561, 567, 570, 541, 543, 554, 558, 560, 562, 568, - 571, 530, 532, 534, 536, 549, 548, 545, 573, 574, - 551, 556, 535, 547, 552, 565, 572, 569, 529, 533, - 537, 546, 564, 563, 544, 555, 566, 550, 538, 531, - 539, 1368, 196, 221, 368, 1432, 454, 288, 650, 618, - 484, 613, 206, 223, 1304, 262, 1316, 1324, 0, 1330, - 1338, 1339, 1352, 1355, 1356, 1357, 1358, 1376, 1377, 1379, - 1387, 1389, 1392, 1394, 1401, 1415, 1435, 198, 200, 209, - 222, 232, 236, 243, 261, 276, 278, 285, 298, 310, - 318, 319, 322, 328, 380, 386, 387, 388, 389, 409, - 410, 411, 414, 417, 418, 421, 423, 424, 427, 431, - 435, 436, 437, 439, 441, 443, 455, 460, 474, 475, - 476, 477, 478, 481, 482, 488, 489, 490, 491, 492, - 500, 501, 516, 586, 588, 605, 625, 632, 480, 301, - 302, 444, 445, 314, 315, 646, 647, 300, 600, 633, - 597, 645, 627, 438, 378, 1367, 1373, 381, 281, 305, - 320, 1382, 617, 502, 227, 466, 290, 251, 1400, 1402, - 211, 246, 230, 259, 274, 277, 324, 391, 400, 429, - 434, 296, 271, 244, 459, 241, 485, 519, 520, 521, - 523, 395, 266, 433, 1363, 1391, 376, 576, 577, 316, - 396, 0, 0, 0, 0, 0, 0, 1421, 1405, 528, - 0, 1347, 1424, 1315, 1334, 1434, 1337, 1340, 1384, 1293, - 1362, 416, 1331, 1319, 1288, 1326, 1289, 1317, 1349, 270, - 1314, 1407, 1366, 1423, 366, 267, 1295, 1286, 204, 505, - 1320, 430, 1336, 203, 1386, 487, 252, 377, 374, 583, - 282, 273, 269, 250, 317, 385, 428, 518, 422, 1430, - 370, 1372, 0, 497, 401, 0, 0, 0, 1411, 1410, - 1341, 1351, 1413, 1360, 1398, 1346, 1385, 1303, 1371, 1425, - 1332, 1381, 1426, 323, 248, 325, 202, 413, 498, 286, - 0, 0, 0, 0, 0, 507, 194, 0, 0, 0, + 0, 0, 0, 0, 0, 1961, 0, 0, 397, 0, + 0, 0, 0, 0, 0, 1422, 1406, 529, 0, 1348, + 1425, 1316, 1335, 1435, 1338, 1341, 1385, 1294, 1363, 417, + 1332, 1320, 1289, 1327, 1290, 1318, 1350, 271, 1315, 1408, + 1367, 1424, 367, 268, 1296, 1287, 204, 506, 1321, 431, + 1337, 203, 1387, 488, 253, 378, 375, 584, 283, 274, + 270, 250, 318, 386, 429, 519, 423, 1431, 371, 1373, + 0, 498, 402, 0, 0, 2015, 1412, 1411, 1342, 1352, + 1414, 1361, 1399, 1347, 1386, 1304, 1372, 1426, 1333, 1382, + 1427, 324, 248, 326, 202, 414, 499, 287, 0, 0, + 0, 0, 4112, 508, 960, 0, 0, 0, 0, 4113, + 0, 0, 0, 0, 238, 0, 0, 245, 0, 0, + 0, 352, 361, 360, 340, 341, 343, 345, 351, 358, + 364, 337, 346, 1329, 1379, 610, 1421, 1330, 1381, 266, + 322, 273, 265, 581, 1432, 1413, 1293, 1360, 1420, 1355, + 597, 0, 0, 229, 1423, 1354, 0, 1384, 0, 1438, + 1288, 1375, 0, 1291, 1295, 1434, 1418, 1324, 276, 0, + 0, 0, 0, 0, 0, 0, 1351, 1362, 1396, 1400, + 1345, 0, 0, 0, 0, 0, 0, 0, 0, 1322, + 0, 1371, 0, 0, 0, 1300, 1292, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1349, + 0, 0, 0, 0, 1303, 0, 1323, 1397, 0, 1286, + 298, 1297, 403, 258, 0, 454, 1404, 1417, 1346, 630, + 1419, 1344, 1343, 1391, 1301, 1410, 1336, 366, 1299, 331, + 197, 225, 0, 1334, 413, 462, 474, 1409, 1319, 1328, + 254, 1326, 472, 427, 605, 233, 285, 459, 433, 470, + 441, 288, 1370, 1389, 471, 373, 586, 451, 602, 631, + 632, 264, 407, 616, 523, 624, 649, 226, 261, 421, + 507, 608, 495, 398, 582, 583, 330, 494, 296, 201, + 370, 637, 224, 480, 372, 242, 231, 588, 613, 300, + 252, 290, 457, 644, 213, 518, 599, 239, 484, 0, + 0, 652, 247, 505, 611, 600, 215, 595, 504, 394, + 327, 328, 214, 0, 458, 269, 294, 0, 0, 259, + 416, 590, 591, 257, 653, 228, 623, 220, 1298, 622, + 409, 585, 596, 395, 384, 219, 594, 393, 383, 335, + 356, 357, 281, 308, 448, 376, 449, 307, 309, 405, + 404, 406, 207, 609, 627, 0, 208, 0, 500, 612, + 654, 453, 212, 234, 235, 237, 1314, 280, 284, 292, + 295, 304, 305, 314, 368, 420, 447, 443, 452, 1405, + 580, 603, 617, 629, 635, 636, 638, 639, 640, 641, + 642, 645, 643, 408, 312, 496, 334, 374, 1394, 1437, + 426, 473, 240, 607, 497, 199, 1308, 1313, 1306, 0, + 255, 256, 1376, 576, 1309, 1307, 1365, 1366, 1310, 1428, + 1429, 1430, 1415, 655, 656, 657, 658, 659, 660, 661, + 662, 663, 664, 665, 666, 667, 668, 669, 670, 671, + 672, 650, 509, 515, 510, 511, 512, 513, 514, 0, + 516, 1398, 1302, 0, 1311, 1312, 399, 1407, 592, 593, + 673, 385, 487, 604, 336, 350, 353, 342, 362, 0, + 363, 338, 339, 344, 347, 348, 349, 354, 355, 359, + 365, 249, 210, 391, 400, 579, 313, 216, 217, 218, + 525, 526, 527, 528, 620, 621, 625, 205, 463, 464, + 465, 466, 293, 615, 310, 469, 468, 332, 333, 380, + 450, 541, 543, 554, 558, 560, 562, 568, 571, 542, + 544, 555, 559, 561, 563, 569, 572, 531, 533, 535, + 537, 550, 549, 546, 574, 575, 552, 557, 536, 548, + 553, 566, 573, 570, 530, 534, 538, 547, 565, 564, + 545, 556, 567, 551, 539, 532, 540, 1369, 196, 221, + 369, 1433, 455, 289, 651, 619, 485, 614, 206, 223, + 1305, 263, 1317, 1325, 0, 1331, 1339, 1340, 1353, 1356, + 1357, 1358, 1359, 1377, 1378, 1380, 1388, 1390, 1393, 1395, + 1402, 1416, 1436, 198, 200, 209, 222, 232, 236, 243, + 262, 277, 279, 286, 299, 311, 319, 320, 323, 329, + 381, 387, 388, 389, 390, 410, 411, 412, 415, 418, + 419, 422, 424, 425, 428, 432, 436, 437, 438, 440, + 442, 444, 456, 461, 475, 476, 477, 478, 479, 482, + 483, 489, 490, 491, 492, 493, 501, 502, 517, 587, + 589, 606, 626, 633, 481, 302, 303, 445, 446, 315, + 316, 647, 648, 301, 601, 634, 598, 646, 628, 439, + 379, 1368, 1374, 382, 282, 306, 321, 1383, 618, 503, + 227, 467, 291, 251, 1401, 1403, 211, 246, 230, 260, + 275, 278, 325, 392, 401, 430, 435, 297, 272, 244, + 460, 241, 486, 520, 521, 522, 524, 396, 267, 434, + 1364, 1392, 377, 577, 578, 317, 397, 0, 0, 0, + 0, 0, 0, 1422, 1406, 529, 0, 1348, 1425, 1316, + 1335, 1435, 1338, 1341, 1385, 1294, 1363, 417, 1332, 1320, + 1289, 1327, 1290, 1318, 1350, 271, 1315, 1408, 1367, 1424, + 367, 268, 1296, 1287, 204, 506, 1321, 431, 1337, 203, + 1387, 488, 253, 378, 375, 584, 283, 274, 270, 250, + 318, 386, 429, 519, 423, 1431, 371, 1373, 0, 498, + 402, 0, 0, 0, 1412, 1411, 1342, 1352, 1414, 1361, + 1399, 1347, 1386, 1304, 1372, 1426, 1333, 1382, 1427, 324, + 248, 326, 202, 414, 499, 287, 0, 0, 0, 0, + 0, 508, 194, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 238, 0, 0, 245, 0, 0, 0, 352, + 361, 360, 340, 341, 343, 345, 351, 358, 364, 337, + 346, 1329, 1379, 610, 1421, 1330, 1381, 266, 322, 273, + 265, 581, 1432, 1413, 1293, 1360, 1420, 1355, 597, 0, + 0, 229, 1423, 1354, 0, 1384, 0, 1438, 1288, 1375, + 0, 1291, 1295, 1434, 1418, 1324, 276, 0, 0, 0, + 0, 0, 0, 0, 1351, 1362, 1396, 1400, 1345, 0, + 0, 0, 0, 0, 0, 3257, 0, 1322, 0, 1371, + 0, 0, 0, 1300, 1292, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1349, 0, 0, + 0, 0, 1303, 0, 1323, 1397, 0, 1286, 298, 1297, + 403, 258, 0, 454, 1404, 1417, 1346, 630, 1419, 1344, + 1343, 1391, 1301, 1410, 1336, 366, 1299, 331, 197, 225, + 0, 1334, 413, 462, 474, 1409, 1319, 1328, 254, 1326, + 472, 427, 605, 233, 285, 459, 433, 470, 441, 288, + 1370, 1389, 471, 373, 586, 451, 602, 631, 632, 264, + 407, 616, 523, 624, 649, 226, 261, 421, 507, 608, + 495, 398, 582, 583, 330, 494, 296, 201, 370, 637, + 224, 480, 372, 242, 231, 588, 613, 300, 252, 290, + 457, 644, 213, 518, 599, 239, 484, 0, 0, 652, + 247, 505, 611, 600, 215, 595, 504, 394, 327, 328, + 214, 0, 458, 269, 294, 0, 0, 259, 416, 590, + 591, 257, 653, 228, 623, 220, 1298, 622, 409, 585, + 596, 395, 384, 219, 594, 393, 383, 335, 356, 357, + 281, 308, 448, 376, 449, 307, 309, 405, 404, 406, + 207, 609, 627, 0, 208, 0, 500, 612, 654, 453, + 212, 234, 235, 237, 1314, 280, 284, 292, 295, 304, + 305, 314, 368, 420, 447, 443, 452, 1405, 580, 603, + 617, 629, 635, 636, 638, 639, 640, 641, 642, 645, + 643, 408, 312, 496, 334, 374, 1394, 1437, 426, 473, + 240, 607, 497, 199, 1308, 1313, 1306, 0, 255, 256, + 1376, 576, 1309, 1307, 1365, 1366, 1310, 1428, 1429, 1430, + 1415, 655, 656, 657, 658, 659, 660, 661, 662, 663, + 664, 665, 666, 667, 668, 669, 670, 671, 672, 650, + 509, 515, 510, 511, 512, 513, 514, 0, 516, 1398, + 1302, 0, 1311, 1312, 399, 1407, 592, 593, 673, 385, + 487, 604, 336, 350, 353, 342, 362, 0, 363, 338, + 339, 344, 347, 348, 349, 354, 355, 359, 365, 249, + 210, 391, 400, 579, 313, 216, 217, 218, 525, 526, + 527, 528, 620, 621, 625, 205, 463, 464, 465, 466, + 293, 615, 310, 469, 468, 332, 333, 380, 450, 541, + 543, 554, 558, 560, 562, 568, 571, 542, 544, 555, + 559, 561, 563, 569, 572, 531, 533, 535, 537, 550, + 549, 546, 574, 575, 552, 557, 536, 548, 553, 566, + 573, 570, 530, 534, 538, 547, 565, 564, 545, 556, + 567, 551, 539, 532, 540, 1369, 196, 221, 369, 1433, + 455, 289, 651, 619, 485, 614, 206, 223, 1305, 263, + 1317, 1325, 0, 1331, 1339, 1340, 1353, 1356, 1357, 1358, + 1359, 1377, 1378, 1380, 1388, 1390, 1393, 1395, 1402, 1416, + 1436, 198, 200, 209, 222, 232, 236, 243, 262, 277, + 279, 286, 299, 311, 319, 320, 323, 329, 381, 387, + 388, 389, 390, 410, 411, 412, 415, 418, 419, 422, + 424, 425, 428, 432, 436, 437, 438, 440, 442, 444, + 456, 461, 475, 476, 477, 478, 479, 482, 483, 489, + 490, 491, 492, 493, 501, 502, 517, 587, 589, 606, + 626, 633, 481, 302, 303, 445, 446, 315, 316, 647, + 648, 301, 601, 634, 598, 646, 628, 439, 379, 1368, + 1374, 382, 282, 306, 321, 1383, 618, 503, 227, 467, + 291, 251, 1401, 1403, 211, 246, 230, 260, 275, 278, + 325, 392, 401, 430, 435, 297, 272, 244, 460, 241, + 486, 520, 521, 522, 524, 396, 267, 434, 1364, 1392, + 377, 577, 578, 317, 397, 0, 0, 0, 0, 0, + 0, 1422, 1406, 529, 0, 1348, 1425, 1316, 1335, 1435, + 1338, 1341, 1385, 1294, 1363, 417, 1332, 1320, 1289, 1327, + 1290, 1318, 1350, 271, 1315, 1408, 1367, 1424, 367, 268, + 1296, 1287, 204, 506, 1321, 431, 1337, 203, 1387, 488, + 253, 378, 375, 584, 283, 274, 270, 250, 318, 386, + 429, 519, 423, 1431, 371, 1373, 0, 498, 402, 0, + 0, 0, 1412, 1411, 1342, 1352, 1414, 1361, 1399, 1347, + 1386, 1304, 1372, 1426, 1333, 1382, 1427, 324, 248, 326, + 202, 414, 499, 287, 0, 0, 0, 0, 0, 508, + 725, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 238, 0, 0, 245, 0, 0, 0, 352, 361, 360, + 340, 341, 343, 345, 351, 358, 364, 337, 346, 1329, + 1379, 610, 1421, 1330, 1381, 266, 322, 273, 265, 581, + 1432, 1413, 1293, 1360, 1420, 1355, 597, 0, 0, 229, + 1423, 1354, 0, 1384, 0, 1438, 1288, 1375, 0, 1291, + 1295, 1434, 1418, 1324, 276, 0, 0, 0, 0, 0, + 0, 0, 1351, 1362, 1396, 1400, 1345, 0, 0, 0, + 0, 0, 0, 3218, 0, 1322, 0, 1371, 0, 0, + 0, 1300, 1292, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1349, 0, 0, 0, 0, + 1303, 0, 1323, 1397, 0, 1286, 298, 1297, 403, 258, + 0, 454, 1404, 1417, 1346, 630, 1419, 1344, 1343, 1391, + 1301, 1410, 1336, 366, 1299, 331, 197, 225, 0, 1334, + 413, 462, 474, 1409, 1319, 1328, 254, 1326, 472, 427, + 605, 233, 285, 459, 433, 470, 441, 288, 1370, 1389, + 471, 373, 586, 451, 602, 631, 632, 264, 407, 616, + 523, 624, 649, 226, 261, 421, 507, 608, 495, 398, + 582, 583, 330, 494, 296, 201, 370, 637, 224, 480, + 372, 242, 231, 588, 613, 300, 252, 290, 457, 644, + 213, 518, 599, 239, 484, 0, 0, 652, 247, 505, + 611, 600, 215, 595, 504, 394, 327, 328, 214, 0, + 458, 269, 294, 0, 0, 259, 416, 590, 591, 257, + 653, 228, 623, 220, 1298, 622, 409, 585, 596, 395, + 384, 219, 594, 393, 383, 335, 356, 357, 281, 308, + 448, 376, 449, 307, 309, 405, 404, 406, 207, 609, + 627, 0, 208, 0, 500, 612, 654, 453, 212, 234, + 235, 237, 1314, 280, 284, 292, 295, 304, 305, 314, + 368, 420, 447, 443, 452, 1405, 580, 603, 617, 629, + 635, 636, 638, 639, 640, 641, 642, 645, 643, 408, + 312, 496, 334, 374, 1394, 1437, 426, 473, 240, 607, + 497, 199, 1308, 1313, 1306, 0, 255, 256, 1376, 576, + 1309, 1307, 1365, 1366, 1310, 1428, 1429, 1430, 1415, 655, + 656, 657, 658, 659, 660, 661, 662, 663, 664, 665, + 666, 667, 668, 669, 670, 671, 672, 650, 509, 515, + 510, 511, 512, 513, 514, 0, 516, 1398, 1302, 0, + 1311, 1312, 399, 1407, 592, 593, 673, 385, 487, 604, + 336, 350, 353, 342, 362, 0, 363, 338, 339, 344, + 347, 348, 349, 354, 355, 359, 365, 249, 210, 391, + 400, 579, 313, 216, 217, 218, 525, 526, 527, 528, + 620, 621, 625, 205, 463, 464, 465, 466, 293, 615, + 310, 469, 468, 332, 333, 380, 450, 541, 543, 554, + 558, 560, 562, 568, 571, 542, 544, 555, 559, 561, + 563, 569, 572, 531, 533, 535, 537, 550, 549, 546, + 574, 575, 552, 557, 536, 548, 553, 566, 573, 570, + 530, 534, 538, 547, 565, 564, 545, 556, 567, 551, + 539, 532, 540, 1369, 196, 221, 369, 1433, 455, 289, + 651, 619, 485, 614, 206, 223, 1305, 263, 1317, 1325, + 0, 1331, 1339, 1340, 1353, 1356, 1357, 1358, 1359, 1377, + 1378, 1380, 1388, 1390, 1393, 1395, 1402, 1416, 1436, 198, + 200, 209, 222, 232, 236, 243, 262, 277, 279, 286, + 299, 311, 319, 320, 323, 329, 381, 387, 388, 389, + 390, 410, 411, 412, 415, 418, 419, 422, 424, 425, + 428, 432, 436, 437, 438, 440, 442, 444, 456, 461, + 475, 476, 477, 478, 479, 482, 483, 489, 490, 491, + 492, 493, 501, 502, 517, 587, 589, 606, 626, 633, + 481, 302, 303, 445, 446, 315, 316, 647, 648, 301, + 601, 634, 598, 646, 628, 439, 379, 1368, 1374, 382, + 282, 306, 321, 1383, 618, 503, 227, 467, 291, 251, + 1401, 1403, 211, 246, 230, 260, 275, 278, 325, 392, + 401, 430, 435, 297, 272, 244, 460, 241, 486, 520, + 521, 522, 524, 396, 267, 434, 1364, 1392, 377, 577, + 578, 317, 397, 0, 0, 0, 0, 0, 0, 1422, + 1406, 529, 0, 1348, 1425, 1316, 1335, 1435, 1338, 1341, + 1385, 1294, 1363, 417, 1332, 1320, 1289, 1327, 1290, 1318, + 1350, 271, 1315, 1408, 1367, 1424, 367, 268, 1296, 1287, + 204, 506, 1321, 431, 1337, 203, 1387, 488, 253, 378, + 375, 584, 283, 274, 270, 250, 318, 386, 429, 519, + 423, 1431, 371, 1373, 0, 498, 402, 0, 0, 0, + 1412, 1411, 1342, 1352, 1414, 1361, 1399, 1347, 1386, 1304, + 1372, 1426, 1333, 1382, 1427, 324, 248, 326, 202, 414, + 499, 287, 0, 0, 0, 0, 0, 508, 960, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 238, 0, + 0, 245, 0, 0, 0, 352, 361, 360, 340, 341, + 343, 345, 351, 358, 364, 337, 346, 1329, 1379, 610, + 1421, 1330, 1381, 266, 322, 273, 265, 581, 1432, 1413, + 1293, 1360, 1420, 1355, 597, 0, 0, 229, 1423, 1354, + 0, 1384, 0, 1438, 1288, 1375, 0, 1291, 1295, 1434, + 1418, 1324, 276, 0, 0, 0, 0, 0, 0, 0, + 1351, 1362, 1396, 1400, 1345, 0, 0, 0, 0, 0, + 0, 2411, 0, 1322, 0, 1371, 0, 0, 0, 1300, + 1292, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1349, 0, 0, 0, 0, 1303, 0, + 1323, 1397, 0, 1286, 298, 1297, 403, 258, 0, 454, + 1404, 1417, 1346, 630, 1419, 1344, 1343, 1391, 1301, 1410, + 1336, 366, 1299, 331, 197, 225, 0, 1334, 413, 462, + 474, 1409, 1319, 1328, 254, 1326, 472, 427, 605, 233, + 285, 459, 433, 470, 441, 288, 1370, 1389, 471, 373, + 586, 451, 602, 631, 632, 264, 407, 616, 523, 624, + 649, 226, 261, 421, 507, 608, 495, 398, 582, 583, + 330, 494, 296, 201, 370, 637, 224, 480, 372, 242, + 231, 588, 613, 300, 252, 290, 457, 644, 213, 518, + 599, 239, 484, 0, 0, 652, 247, 505, 611, 600, + 215, 595, 504, 394, 327, 328, 214, 0, 458, 269, + 294, 0, 0, 259, 416, 590, 591, 257, 653, 228, + 623, 220, 1298, 622, 409, 585, 596, 395, 384, 219, + 594, 393, 383, 335, 356, 357, 281, 308, 448, 376, + 449, 307, 309, 405, 404, 406, 207, 609, 627, 0, + 208, 0, 500, 612, 654, 453, 212, 234, 235, 237, + 1314, 280, 284, 292, 295, 304, 305, 314, 368, 420, + 447, 443, 452, 1405, 580, 603, 617, 629, 635, 636, + 638, 639, 640, 641, 642, 645, 643, 408, 312, 496, + 334, 374, 1394, 1437, 426, 473, 240, 607, 497, 199, + 1308, 1313, 1306, 0, 255, 256, 1376, 576, 1309, 1307, + 1365, 1366, 1310, 1428, 1429, 1430, 1415, 655, 656, 657, + 658, 659, 660, 661, 662, 663, 664, 665, 666, 667, + 668, 669, 670, 671, 672, 650, 509, 515, 510, 511, + 512, 513, 514, 0, 516, 1398, 1302, 0, 1311, 1312, + 399, 1407, 592, 593, 673, 385, 487, 604, 336, 350, + 353, 342, 362, 0, 363, 338, 339, 344, 347, 348, + 349, 354, 355, 359, 365, 249, 210, 391, 400, 579, + 313, 216, 217, 218, 525, 526, 527, 528, 620, 621, + 625, 205, 463, 464, 465, 466, 293, 615, 310, 469, + 468, 332, 333, 380, 450, 541, 543, 554, 558, 560, + 562, 568, 571, 542, 544, 555, 559, 561, 563, 569, + 572, 531, 533, 535, 537, 550, 549, 546, 574, 575, + 552, 557, 536, 548, 553, 566, 573, 570, 530, 534, + 538, 547, 565, 564, 545, 556, 567, 551, 539, 532, + 540, 1369, 196, 221, 369, 1433, 455, 289, 651, 619, + 485, 614, 206, 223, 1305, 263, 1317, 1325, 0, 1331, + 1339, 1340, 1353, 1356, 1357, 1358, 1359, 1377, 1378, 1380, + 1388, 1390, 1393, 1395, 1402, 1416, 1436, 198, 200, 209, + 222, 232, 236, 243, 262, 277, 279, 286, 299, 311, + 319, 320, 323, 329, 381, 387, 388, 389, 390, 410, + 411, 412, 415, 418, 419, 422, 424, 425, 428, 432, + 436, 437, 438, 440, 442, 444, 456, 461, 475, 476, + 477, 478, 479, 482, 483, 489, 490, 491, 492, 493, + 501, 502, 517, 587, 589, 606, 626, 633, 481, 302, + 303, 445, 446, 315, 316, 647, 648, 301, 601, 634, + 598, 646, 628, 439, 379, 1368, 1374, 382, 282, 306, + 321, 1383, 618, 503, 227, 467, 291, 251, 1401, 1403, + 211, 246, 230, 260, 275, 278, 325, 392, 401, 430, + 435, 297, 272, 244, 460, 241, 486, 520, 521, 522, + 524, 396, 267, 434, 1364, 1392, 377, 577, 578, 317, + 397, 0, 0, 0, 0, 0, 0, 1422, 1406, 529, + 0, 1348, 1425, 1316, 1335, 1435, 1338, 1341, 1385, 1294, + 1363, 417, 1332, 1320, 1289, 1327, 1290, 1318, 1350, 271, + 1315, 1408, 1367, 1424, 367, 268, 1296, 1287, 204, 506, + 1321, 431, 1337, 203, 1387, 488, 253, 378, 375, 584, + 283, 274, 270, 250, 318, 386, 429, 519, 423, 1431, + 371, 1373, 0, 498, 402, 0, 0, 0, 1412, 1411, + 1342, 1352, 1414, 1361, 1399, 1347, 1386, 1304, 1372, 1426, + 1333, 1382, 1427, 324, 248, 326, 202, 414, 499, 287, + 0, 95, 0, 0, 0, 508, 725, 0, 0, 0, 0, 0, 0, 0, 0, 0, 238, 0, 0, 245, - 0, 0, 0, 351, 360, 359, 339, 340, 342, 344, - 350, 357, 363, 336, 345, 1328, 1378, 609, 1420, 1329, - 1380, 265, 321, 272, 264, 580, 1431, 1412, 1292, 1359, - 1419, 1354, 596, 0, 0, 229, 1422, 1353, 0, 1383, - 0, 1437, 1287, 1374, 0, 1290, 1294, 1433, 1417, 1323, - 275, 0, 0, 0, 0, 0, 0, 0, 1350, 1361, - 1395, 1399, 1344, 0, 0, 0, 0, 0, 0, 3254, - 0, 1321, 0, 1370, 0, 0, 0, 1299, 1291, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1348, 0, 0, 0, 0, 1302, 0, 1322, 1396, - 0, 1285, 297, 1296, 402, 257, 0, 453, 1403, 1416, - 1345, 629, 1418, 1343, 1342, 1390, 1300, 1409, 1335, 365, - 1298, 330, 197, 225, 0, 1333, 412, 461, 473, 1408, - 1318, 1327, 253, 1325, 471, 426, 604, 233, 284, 458, - 432, 469, 440, 287, 1369, 1388, 470, 372, 585, 450, - 601, 630, 631, 263, 406, 615, 522, 623, 648, 226, - 260, 420, 506, 607, 494, 397, 581, 582, 329, 493, - 295, 201, 369, 636, 224, 479, 371, 242, 231, 587, - 612, 299, 289, 456, 643, 213, 517, 598, 239, 483, - 0, 0, 651, 247, 504, 610, 599, 215, 594, 503, - 393, 326, 327, 214, 0, 457, 268, 293, 0, 0, - 258, 415, 589, 590, 256, 652, 228, 622, 220, 1297, - 621, 408, 584, 595, 394, 383, 219, 593, 392, 382, - 334, 355, 356, 280, 307, 447, 375, 448, 306, 308, - 404, 403, 405, 207, 608, 626, 0, 208, 0, 499, - 611, 653, 452, 212, 234, 235, 237, 1313, 279, 283, - 291, 294, 303, 304, 313, 367, 419, 446, 442, 451, - 1404, 579, 602, 616, 628, 634, 635, 637, 638, 639, - 640, 641, 644, 642, 407, 311, 495, 333, 373, 1393, - 1436, 425, 472, 240, 606, 496, 199, 1307, 1312, 1305, - 0, 254, 255, 1375, 575, 1308, 1306, 1364, 1365, 1309, - 1427, 1428, 1429, 1414, 654, 655, 656, 657, 658, 659, + 0, 0, 0, 352, 361, 360, 340, 341, 343, 345, + 351, 358, 364, 337, 346, 1329, 1379, 610, 1421, 1330, + 1381, 266, 322, 273, 265, 581, 1432, 1413, 1293, 1360, + 1420, 1355, 597, 0, 0, 229, 1423, 1354, 0, 1384, + 0, 1438, 1288, 1375, 0, 1291, 1295, 1434, 1418, 1324, + 276, 0, 0, 0, 0, 0, 0, 0, 1351, 1362, + 1396, 1400, 1345, 0, 0, 0, 0, 0, 0, 0, + 0, 1322, 0, 1371, 0, 0, 0, 1300, 1292, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1349, 0, 0, 0, 0, 1303, 0, 1323, 1397, + 0, 1286, 298, 1297, 403, 258, 0, 454, 1404, 1417, + 1346, 630, 1419, 1344, 1343, 1391, 1301, 1410, 1336, 366, + 1299, 331, 197, 225, 0, 1334, 413, 462, 474, 1409, + 1319, 1328, 254, 1326, 472, 427, 605, 233, 285, 459, + 433, 470, 441, 288, 1370, 1389, 471, 373, 586, 451, + 602, 631, 632, 264, 407, 616, 523, 624, 649, 226, + 261, 421, 507, 608, 495, 398, 582, 583, 330, 494, + 296, 201, 370, 637, 224, 480, 372, 242, 231, 588, + 613, 300, 252, 290, 457, 644, 213, 518, 599, 239, + 484, 0, 0, 652, 247, 505, 611, 600, 215, 595, + 504, 394, 327, 328, 214, 0, 458, 269, 294, 0, + 0, 259, 416, 590, 591, 257, 653, 228, 623, 220, + 1298, 622, 409, 585, 596, 395, 384, 219, 594, 393, + 383, 335, 356, 357, 281, 308, 448, 376, 449, 307, + 309, 405, 404, 406, 207, 609, 627, 0, 208, 0, + 500, 612, 654, 453, 212, 234, 235, 237, 1314, 280, + 284, 292, 295, 304, 305, 314, 368, 420, 447, 443, + 452, 1405, 580, 603, 617, 629, 635, 636, 638, 639, + 640, 641, 642, 645, 643, 408, 312, 496, 334, 374, + 1394, 1437, 426, 473, 240, 607, 497, 199, 1308, 1313, + 1306, 0, 255, 256, 1376, 576, 1309, 1307, 1365, 1366, + 1310, 1428, 1429, 1430, 1415, 655, 656, 657, 658, 659, 660, 661, 662, 663, 664, 665, 666, 667, 668, 669, - 670, 671, 649, 508, 514, 509, 510, 511, 512, 513, - 0, 515, 1397, 1301, 0, 1310, 1311, 398, 1406, 591, - 592, 672, 384, 486, 603, 335, 349, 352, 341, 361, - 0, 362, 337, 338, 343, 346, 347, 348, 353, 354, - 358, 364, 249, 210, 390, 399, 578, 312, 216, 217, - 218, 524, 525, 526, 527, 619, 620, 624, 205, 462, - 463, 464, 465, 292, 614, 309, 468, 467, 331, 332, - 379, 449, 540, 542, 553, 557, 559, 561, 567, 570, - 541, 543, 554, 558, 560, 562, 568, 571, 530, 532, - 534, 536, 549, 548, 545, 573, 574, 551, 556, 535, - 547, 552, 565, 572, 569, 529, 533, 537, 546, 564, - 563, 544, 555, 566, 550, 538, 531, 539, 1368, 196, - 221, 368, 1432, 454, 288, 650, 618, 484, 613, 206, - 223, 1304, 262, 1316, 1324, 0, 1330, 1338, 1339, 1352, - 1355, 1356, 1357, 1358, 1376, 1377, 1379, 1387, 1389, 1392, - 1394, 1401, 1415, 1435, 198, 200, 209, 222, 232, 236, - 243, 261, 276, 278, 285, 298, 310, 318, 319, 322, - 328, 380, 386, 387, 388, 389, 409, 410, 411, 414, - 417, 418, 421, 423, 424, 427, 431, 435, 436, 437, - 439, 441, 443, 455, 460, 474, 475, 476, 477, 478, - 481, 482, 488, 489, 490, 491, 492, 500, 501, 516, - 586, 588, 605, 625, 632, 480, 301, 302, 444, 445, - 314, 315, 646, 647, 300, 600, 633, 597, 645, 627, - 438, 378, 1367, 1373, 381, 281, 305, 320, 1382, 617, - 502, 227, 466, 290, 251, 1400, 1402, 211, 246, 230, - 259, 274, 277, 324, 391, 400, 429, 434, 296, 271, - 244, 459, 241, 485, 519, 520, 521, 523, 395, 266, - 433, 1363, 1391, 376, 576, 577, 316, 396, 0, 0, - 0, 0, 0, 0, 1421, 1405, 528, 0, 1347, 1424, - 1315, 1334, 1434, 1337, 1340, 1384, 1293, 1362, 416, 1331, - 1319, 1288, 1326, 1289, 1317, 1349, 270, 1314, 1407, 1366, - 1423, 366, 267, 1295, 1286, 204, 505, 1320, 430, 1336, - 203, 1386, 487, 252, 377, 374, 583, 282, 273, 269, - 250, 317, 385, 428, 518, 422, 1430, 370, 1372, 0, - 497, 401, 0, 0, 0, 1411, 1410, 1341, 1351, 1413, - 1360, 1398, 1346, 1385, 1303, 1371, 1425, 1332, 1381, 1426, - 323, 248, 325, 202, 413, 498, 286, 0, 0, 0, - 0, 0, 507, 724, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 238, 0, 0, 245, 0, 0, 0, - 351, 360, 359, 339, 340, 342, 344, 350, 357, 363, - 336, 345, 1328, 1378, 609, 1420, 1329, 1380, 265, 321, - 272, 264, 580, 1431, 1412, 1292, 1359, 1419, 1354, 596, - 0, 0, 229, 1422, 1353, 0, 1383, 0, 1437, 1287, - 1374, 0, 1290, 1294, 1433, 1417, 1323, 275, 0, 0, - 0, 0, 0, 0, 0, 1350, 1361, 1395, 1399, 1344, - 0, 0, 0, 0, 0, 0, 3215, 0, 1321, 0, - 1370, 0, 0, 0, 1299, 1291, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 1348, 0, - 0, 0, 0, 1302, 0, 1322, 1396, 0, 1285, 297, - 1296, 402, 257, 0, 453, 1403, 1416, 1345, 629, 1418, - 1343, 1342, 1390, 1300, 1409, 1335, 365, 1298, 330, 197, - 225, 0, 1333, 412, 461, 473, 1408, 1318, 1327, 253, - 1325, 471, 426, 604, 233, 284, 458, 432, 469, 440, - 287, 1369, 1388, 470, 372, 585, 450, 601, 630, 631, - 263, 406, 615, 522, 623, 648, 226, 260, 420, 506, - 607, 494, 397, 581, 582, 329, 493, 295, 201, 369, - 636, 224, 479, 371, 242, 231, 587, 612, 299, 289, - 456, 643, 213, 517, 598, 239, 483, 0, 0, 651, - 247, 504, 610, 599, 215, 594, 503, 393, 326, 327, - 214, 0, 457, 268, 293, 0, 0, 258, 415, 589, - 590, 256, 652, 228, 622, 220, 1297, 621, 408, 584, - 595, 394, 383, 219, 593, 392, 382, 334, 355, 356, - 280, 307, 447, 375, 448, 306, 308, 404, 403, 405, - 207, 608, 626, 0, 208, 0, 499, 611, 653, 452, - 212, 234, 235, 237, 1313, 279, 283, 291, 294, 303, - 304, 313, 367, 419, 446, 442, 451, 1404, 579, 602, - 616, 628, 634, 635, 637, 638, 639, 640, 641, 644, - 642, 407, 311, 495, 333, 373, 1393, 1436, 425, 472, - 240, 606, 496, 199, 1307, 1312, 1305, 0, 254, 255, - 1375, 575, 1308, 1306, 1364, 1365, 1309, 1427, 1428, 1429, - 1414, 654, 655, 656, 657, 658, 659, 660, 661, 662, - 663, 664, 665, 666, 667, 668, 669, 670, 671, 649, - 508, 514, 509, 510, 511, 512, 513, 0, 515, 1397, - 1301, 0, 1310, 1311, 398, 1406, 591, 592, 672, 384, - 486, 603, 335, 349, 352, 341, 361, 0, 362, 337, - 338, 343, 346, 347, 348, 353, 354, 358, 364, 249, - 210, 390, 399, 578, 312, 216, 217, 218, 524, 525, - 526, 527, 619, 620, 624, 205, 462, 463, 464, 465, - 292, 614, 309, 468, 467, 331, 332, 379, 449, 540, - 542, 553, 557, 559, 561, 567, 570, 541, 543, 554, - 558, 560, 562, 568, 571, 530, 532, 534, 536, 549, - 548, 545, 573, 574, 551, 556, 535, 547, 552, 565, - 572, 569, 529, 533, 537, 546, 564, 563, 544, 555, - 566, 550, 538, 531, 539, 1368, 196, 221, 368, 1432, - 454, 288, 650, 618, 484, 613, 206, 223, 1304, 262, - 1316, 1324, 0, 1330, 1338, 1339, 1352, 1355, 1356, 1357, - 1358, 1376, 1377, 1379, 1387, 1389, 1392, 1394, 1401, 1415, - 1435, 198, 200, 209, 222, 232, 236, 243, 261, 276, - 278, 285, 298, 310, 318, 319, 322, 328, 380, 386, - 387, 388, 389, 409, 410, 411, 414, 417, 418, 421, - 423, 424, 427, 431, 435, 436, 437, 439, 441, 443, - 455, 460, 474, 475, 476, 477, 478, 481, 482, 488, - 489, 490, 491, 492, 500, 501, 516, 586, 588, 605, - 625, 632, 480, 301, 302, 444, 445, 314, 315, 646, - 647, 300, 600, 633, 597, 645, 627, 438, 378, 1367, - 1373, 381, 281, 305, 320, 1382, 617, 502, 227, 466, - 290, 251, 1400, 1402, 211, 246, 230, 259, 274, 277, - 324, 391, 400, 429, 434, 296, 271, 244, 459, 241, - 485, 519, 520, 521, 523, 395, 266, 433, 1363, 1391, - 376, 576, 577, 316, 396, 0, 0, 0, 0, 0, - 0, 1421, 1405, 528, 0, 1347, 1424, 1315, 1334, 1434, - 1337, 1340, 1384, 1293, 1362, 416, 1331, 1319, 1288, 1326, - 1289, 1317, 1349, 270, 1314, 1407, 1366, 1423, 366, 267, - 1295, 1286, 204, 505, 1320, 430, 1336, 203, 1386, 487, - 252, 377, 374, 583, 282, 273, 269, 250, 317, 385, - 428, 518, 422, 1430, 370, 1372, 0, 497, 401, 0, - 0, 0, 1411, 1410, 1341, 1351, 1413, 1360, 1398, 1346, - 1385, 1303, 1371, 1425, 1332, 1381, 1426, 323, 248, 325, - 202, 413, 498, 286, 0, 0, 0, 0, 0, 507, - 959, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 238, 0, 0, 245, 0, 0, 0, 351, 360, 359, - 339, 340, 342, 344, 350, 357, 363, 336, 345, 1328, - 1378, 609, 1420, 1329, 1380, 265, 321, 272, 264, 580, - 1431, 1412, 1292, 1359, 1419, 1354, 596, 0, 0, 229, - 1422, 1353, 0, 1383, 0, 1437, 1287, 1374, 0, 1290, - 1294, 1433, 1417, 1323, 275, 0, 0, 0, 0, 0, - 0, 0, 1350, 1361, 1395, 1399, 1344, 0, 0, 0, - 0, 0, 0, 2410, 0, 1321, 0, 1370, 0, 0, - 0, 1299, 1291, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1348, 0, 0, 0, 0, - 1302, 0, 1322, 1396, 0, 1285, 297, 1296, 402, 257, - 0, 453, 1403, 1416, 1345, 629, 1418, 1343, 1342, 1390, - 1300, 1409, 1335, 365, 1298, 330, 197, 225, 0, 1333, - 412, 461, 473, 1408, 1318, 1327, 253, 1325, 471, 426, - 604, 233, 284, 458, 432, 469, 440, 287, 1369, 1388, - 470, 372, 585, 450, 601, 630, 631, 263, 406, 615, - 522, 623, 648, 226, 260, 420, 506, 607, 494, 397, - 581, 582, 329, 493, 295, 201, 369, 636, 224, 479, - 371, 242, 231, 587, 612, 299, 289, 456, 643, 213, - 517, 598, 239, 483, 0, 0, 651, 247, 504, 610, - 599, 215, 594, 503, 393, 326, 327, 214, 0, 457, - 268, 293, 0, 0, 258, 415, 589, 590, 256, 652, - 228, 622, 220, 1297, 621, 408, 584, 595, 394, 383, - 219, 593, 392, 382, 334, 355, 356, 280, 307, 447, - 375, 448, 306, 308, 404, 403, 405, 207, 608, 626, - 0, 208, 0, 499, 611, 653, 452, 212, 234, 235, - 237, 1313, 279, 283, 291, 294, 303, 304, 313, 367, - 419, 446, 442, 451, 1404, 579, 602, 616, 628, 634, - 635, 637, 638, 639, 640, 641, 644, 642, 407, 311, - 495, 333, 373, 1393, 1436, 425, 472, 240, 606, 496, - 199, 1307, 1312, 1305, 0, 254, 255, 1375, 575, 1308, - 1306, 1364, 1365, 1309, 1427, 1428, 1429, 1414, 654, 655, - 656, 657, 658, 659, 660, 661, 662, 663, 664, 665, - 666, 667, 668, 669, 670, 671, 649, 508, 514, 509, - 510, 511, 512, 513, 0, 515, 1397, 1301, 0, 1310, - 1311, 398, 1406, 591, 592, 672, 384, 486, 603, 335, - 349, 352, 341, 361, 0, 362, 337, 338, 343, 346, - 347, 348, 353, 354, 358, 364, 249, 210, 390, 399, - 578, 312, 216, 217, 218, 524, 525, 526, 527, 619, - 620, 624, 205, 462, 463, 464, 465, 292, 614, 309, - 468, 467, 331, 332, 379, 449, 540, 542, 553, 557, - 559, 561, 567, 570, 541, 543, 554, 558, 560, 562, - 568, 571, 530, 532, 534, 536, 549, 548, 545, 573, - 574, 551, 556, 535, 547, 552, 565, 572, 569, 529, - 533, 537, 546, 564, 563, 544, 555, 566, 550, 538, - 531, 539, 1368, 196, 221, 368, 1432, 454, 288, 650, - 618, 484, 613, 206, 223, 1304, 262, 1316, 1324, 0, - 1330, 1338, 1339, 1352, 1355, 1356, 1357, 1358, 1376, 1377, - 1379, 1387, 1389, 1392, 1394, 1401, 1415, 1435, 198, 200, - 209, 222, 232, 236, 243, 261, 276, 278, 285, 298, - 310, 318, 319, 322, 328, 380, 386, 387, 388, 389, - 409, 410, 411, 414, 417, 418, 421, 423, 424, 427, - 431, 435, 436, 437, 439, 441, 443, 455, 460, 474, - 475, 476, 477, 478, 481, 482, 488, 489, 490, 491, - 492, 500, 501, 516, 586, 588, 605, 625, 632, 480, - 301, 302, 444, 445, 314, 315, 646, 647, 300, 600, - 633, 597, 645, 627, 438, 378, 1367, 1373, 381, 281, - 305, 320, 1382, 617, 502, 227, 466, 290, 251, 1400, - 1402, 211, 246, 230, 259, 274, 277, 324, 391, 400, - 429, 434, 296, 271, 244, 459, 241, 485, 519, 520, - 521, 523, 395, 266, 433, 1363, 1391, 376, 576, 577, - 316, 396, 0, 0, 0, 0, 0, 0, 1421, 1405, - 528, 0, 1347, 1424, 1315, 1334, 1434, 1337, 1340, 1384, - 1293, 1362, 416, 1331, 1319, 1288, 1326, 1289, 1317, 1349, - 270, 1314, 1407, 1366, 1423, 366, 267, 1295, 1286, 204, - 505, 1320, 430, 1336, 203, 1386, 487, 252, 377, 374, - 583, 282, 273, 269, 250, 317, 385, 428, 518, 422, - 1430, 370, 1372, 0, 497, 401, 0, 0, 0, 1411, - 1410, 1341, 1351, 1413, 1360, 1398, 1346, 1385, 1303, 1371, - 1425, 1332, 1381, 1426, 323, 248, 325, 202, 413, 498, - 286, 0, 95, 0, 0, 0, 507, 724, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 238, 0, 0, - 245, 0, 0, 0, 351, 360, 359, 339, 340, 342, - 344, 350, 357, 363, 336, 345, 1328, 1378, 609, 1420, - 1329, 1380, 265, 321, 272, 264, 580, 1431, 1412, 1292, - 1359, 1419, 1354, 596, 0, 0, 229, 1422, 1353, 0, - 1383, 0, 1437, 1287, 1374, 0, 1290, 1294, 1433, 1417, - 1323, 275, 0, 0, 0, 0, 0, 0, 0, 1350, - 1361, 1395, 1399, 1344, 0, 0, 0, 0, 0, 0, - 0, 0, 1321, 0, 1370, 0, 0, 0, 1299, 1291, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1348, 0, 0, 0, 0, 1302, 0, 1322, - 1396, 0, 1285, 297, 1296, 402, 257, 0, 453, 1403, - 1416, 1345, 629, 1418, 1343, 1342, 1390, 1300, 1409, 1335, - 365, 1298, 330, 197, 225, 0, 1333, 412, 461, 473, - 1408, 1318, 1327, 253, 1325, 471, 426, 604, 233, 284, - 458, 432, 469, 440, 287, 1369, 1388, 470, 372, 585, - 450, 601, 630, 631, 263, 406, 615, 522, 623, 648, - 226, 260, 420, 506, 607, 494, 397, 581, 582, 329, - 493, 295, 201, 369, 636, 224, 479, 371, 242, 231, - 587, 612, 299, 289, 456, 643, 213, 517, 598, 239, - 483, 0, 0, 651, 247, 504, 610, 599, 215, 594, - 503, 393, 326, 327, 214, 0, 457, 268, 293, 0, - 0, 258, 415, 589, 590, 256, 652, 228, 622, 220, - 1297, 621, 408, 584, 595, 394, 383, 219, 593, 392, - 382, 334, 355, 356, 280, 307, 447, 375, 448, 306, - 308, 404, 403, 405, 207, 608, 626, 0, 208, 0, - 499, 611, 653, 452, 212, 234, 235, 237, 1313, 279, - 283, 291, 294, 303, 304, 313, 367, 419, 446, 442, - 451, 1404, 579, 602, 616, 628, 634, 635, 637, 638, - 639, 640, 641, 644, 642, 407, 311, 495, 333, 373, - 1393, 1436, 425, 472, 240, 606, 496, 199, 1307, 1312, - 1305, 0, 254, 255, 1375, 575, 1308, 1306, 1364, 1365, - 1309, 1427, 1428, 1429, 1414, 654, 655, 656, 657, 658, - 659, 660, 661, 662, 663, 664, 665, 666, 667, 668, - 669, 670, 671, 649, 508, 514, 509, 510, 511, 512, - 513, 0, 515, 1397, 1301, 0, 1310, 1311, 398, 1406, - 591, 592, 672, 384, 486, 603, 335, 349, 352, 341, - 361, 0, 362, 337, 338, 343, 346, 347, 348, 353, - 354, 358, 364, 249, 210, 390, 399, 578, 312, 216, - 217, 218, 524, 525, 526, 527, 619, 620, 624, 205, - 462, 463, 464, 465, 292, 614, 309, 468, 467, 331, - 332, 379, 449, 540, 542, 553, 557, 559, 561, 567, - 570, 541, 543, 554, 558, 560, 562, 568, 571, 530, - 532, 534, 536, 549, 548, 545, 573, 574, 551, 556, - 535, 547, 552, 565, 572, 569, 529, 533, 537, 546, - 564, 563, 544, 555, 566, 550, 538, 531, 539, 1368, - 196, 221, 368, 1432, 454, 288, 650, 618, 484, 613, - 206, 223, 1304, 262, 1316, 1324, 0, 1330, 1338, 1339, - 1352, 1355, 1356, 1357, 1358, 1376, 1377, 1379, 1387, 1389, - 1392, 1394, 1401, 1415, 1435, 198, 200, 209, 222, 232, - 236, 243, 261, 276, 278, 285, 298, 310, 318, 319, - 322, 328, 380, 386, 387, 388, 389, 409, 410, 411, - 414, 417, 418, 421, 423, 424, 427, 431, 435, 436, - 437, 439, 441, 443, 455, 460, 474, 475, 476, 477, - 478, 481, 482, 488, 489, 490, 491, 492, 500, 501, - 516, 586, 588, 605, 625, 632, 480, 301, 302, 444, - 445, 314, 315, 646, 647, 300, 600, 633, 597, 645, - 627, 438, 378, 1367, 1373, 381, 281, 305, 320, 1382, - 617, 502, 227, 466, 290, 251, 1400, 1402, 211, 246, - 230, 259, 274, 277, 324, 391, 400, 429, 434, 296, - 271, 244, 459, 241, 485, 519, 520, 521, 523, 395, - 266, 433, 1363, 1391, 376, 576, 577, 316, 396, 0, - 0, 0, 0, 0, 0, 1421, 1405, 528, 0, 1347, - 1424, 1315, 1334, 1434, 1337, 1340, 1384, 1293, 1362, 416, - 1331, 1319, 1288, 1326, 1289, 1317, 1349, 270, 1314, 1407, - 1366, 1423, 366, 267, 1295, 1286, 204, 505, 1320, 430, - 1336, 203, 1386, 487, 252, 377, 374, 583, 282, 273, - 269, 250, 317, 385, 428, 518, 422, 1430, 370, 1372, - 0, 497, 401, 0, 0, 0, 1411, 1410, 1341, 1351, - 1413, 1360, 1398, 1346, 1385, 1303, 1371, 1425, 1332, 1381, - 1426, 323, 248, 325, 202, 413, 498, 286, 0, 0, - 0, 0, 0, 507, 194, 0, 0, 0, 0, 0, + 670, 671, 672, 650, 509, 515, 510, 511, 512, 513, + 514, 0, 516, 1398, 1302, 0, 1311, 1312, 399, 1407, + 592, 593, 673, 385, 487, 604, 336, 350, 353, 342, + 362, 0, 363, 338, 339, 344, 347, 348, 349, 354, + 355, 359, 365, 249, 210, 391, 400, 579, 313, 216, + 217, 218, 525, 526, 527, 528, 620, 621, 625, 205, + 463, 464, 465, 466, 293, 615, 310, 469, 468, 332, + 333, 380, 450, 541, 543, 554, 558, 560, 562, 568, + 571, 542, 544, 555, 559, 561, 563, 569, 572, 531, + 533, 535, 537, 550, 549, 546, 574, 575, 552, 557, + 536, 548, 553, 566, 573, 570, 530, 534, 538, 547, + 565, 564, 545, 556, 567, 551, 539, 532, 540, 1369, + 196, 221, 369, 1433, 455, 289, 651, 619, 485, 614, + 206, 223, 1305, 263, 1317, 1325, 0, 1331, 1339, 1340, + 1353, 1356, 1357, 1358, 1359, 1377, 1378, 1380, 1388, 1390, + 1393, 1395, 1402, 1416, 1436, 198, 200, 209, 222, 232, + 236, 243, 262, 277, 279, 286, 299, 311, 319, 320, + 323, 329, 381, 387, 388, 389, 390, 410, 411, 412, + 415, 418, 419, 422, 424, 425, 428, 432, 436, 437, + 438, 440, 442, 444, 456, 461, 475, 476, 477, 478, + 479, 482, 483, 489, 490, 491, 492, 493, 501, 502, + 517, 587, 589, 606, 626, 633, 481, 302, 303, 445, + 446, 315, 316, 647, 648, 301, 601, 634, 598, 646, + 628, 439, 379, 1368, 1374, 382, 282, 306, 321, 1383, + 618, 503, 227, 467, 291, 251, 1401, 1403, 211, 246, + 230, 260, 275, 278, 325, 392, 401, 430, 435, 297, + 272, 244, 460, 241, 486, 520, 521, 522, 524, 396, + 267, 434, 1364, 1392, 377, 577, 578, 317, 397, 0, + 0, 0, 0, 0, 0, 1422, 1406, 529, 0, 1348, + 1425, 1316, 1335, 1435, 1338, 1341, 1385, 1294, 1363, 417, + 1332, 1320, 1289, 1327, 1290, 1318, 1350, 271, 1315, 1408, + 1367, 1424, 367, 268, 1296, 1287, 204, 506, 1321, 431, + 1337, 203, 1387, 488, 253, 378, 375, 584, 283, 274, + 270, 250, 318, 386, 429, 519, 423, 1431, 371, 1373, + 0, 498, 402, 0, 0, 0, 1412, 1411, 1342, 1352, + 1414, 1361, 1399, 1347, 1386, 1304, 1372, 1426, 1333, 1382, + 1427, 324, 248, 326, 202, 414, 499, 287, 0, 0, + 0, 0, 0, 508, 194, 0, 0, 0, 0, 0, 0, 0, 0, 0, 238, 0, 0, 245, 0, 0, - 0, 351, 360, 359, 339, 340, 342, 344, 350, 357, - 363, 336, 345, 1328, 1378, 609, 1420, 1329, 1380, 265, - 321, 272, 264, 580, 1431, 1412, 1292, 1359, 1419, 1354, - 596, 0, 0, 229, 1422, 1353, 0, 1383, 0, 1437, - 1287, 1374, 0, 1290, 1294, 1433, 1417, 1323, 275, 0, - 0, 0, 0, 0, 0, 0, 1350, 1361, 1395, 1399, - 1344, 0, 0, 0, 0, 0, 0, 0, 0, 1321, - 0, 1370, 0, 0, 0, 1299, 1291, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 1348, - 0, 0, 0, 0, 1302, 0, 1322, 1396, 0, 1285, - 297, 1296, 402, 257, 0, 453, 1403, 1416, 1345, 629, - 1418, 1343, 1342, 1390, 1300, 1409, 1335, 365, 1298, 330, - 197, 225, 0, 1333, 412, 461, 473, 1408, 1318, 1327, - 253, 1325, 471, 426, 604, 233, 284, 458, 432, 469, - 440, 287, 1369, 1388, 470, 372, 585, 450, 601, 630, - 631, 263, 406, 615, 522, 623, 648, 226, 260, 420, - 506, 607, 494, 397, 581, 582, 329, 493, 295, 201, - 369, 636, 224, 479, 371, 242, 231, 587, 612, 299, - 289, 456, 643, 213, 517, 598, 239, 483, 0, 0, - 651, 247, 504, 610, 599, 215, 594, 503, 393, 326, - 327, 214, 0, 457, 268, 293, 0, 0, 258, 415, - 589, 590, 256, 652, 228, 622, 220, 1297, 621, 408, - 584, 595, 394, 383, 219, 593, 392, 382, 334, 355, - 356, 280, 307, 447, 375, 448, 306, 308, 404, 403, - 405, 207, 608, 626, 0, 208, 0, 499, 611, 653, - 452, 212, 234, 235, 237, 1313, 279, 283, 291, 294, - 303, 304, 313, 367, 419, 446, 442, 451, 1404, 579, - 602, 616, 628, 634, 635, 637, 638, 639, 640, 641, - 644, 642, 407, 311, 495, 333, 373, 1393, 1436, 425, - 472, 240, 606, 496, 199, 1307, 1312, 1305, 0, 254, - 255, 1375, 575, 1308, 1306, 1364, 1365, 1309, 1427, 1428, - 1429, 1414, 654, 655, 656, 657, 658, 659, 660, 661, + 0, 352, 361, 360, 340, 341, 343, 345, 351, 358, + 364, 337, 346, 1329, 1379, 610, 1421, 1330, 1381, 266, + 322, 273, 265, 581, 1432, 1413, 1293, 1360, 1420, 1355, + 597, 0, 0, 229, 1423, 1354, 0, 1384, 0, 1438, + 1288, 1375, 0, 1291, 1295, 1434, 1418, 1324, 276, 0, + 0, 0, 0, 0, 0, 0, 1351, 1362, 1396, 1400, + 1345, 0, 0, 0, 0, 0, 0, 0, 0, 1322, + 0, 1371, 0, 0, 0, 1300, 1292, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1349, + 0, 0, 0, 0, 1303, 0, 1323, 1397, 0, 1286, + 298, 1297, 403, 258, 0, 454, 1404, 1417, 1346, 630, + 1419, 1344, 1343, 1391, 1301, 1410, 1336, 366, 1299, 331, + 197, 225, 0, 1334, 413, 462, 474, 1409, 1319, 1328, + 254, 1326, 472, 427, 605, 233, 285, 459, 433, 470, + 441, 288, 1370, 1389, 471, 373, 586, 451, 602, 631, + 632, 264, 407, 616, 523, 624, 649, 226, 261, 421, + 507, 608, 495, 398, 582, 583, 330, 494, 296, 201, + 370, 637, 224, 480, 372, 242, 231, 588, 613, 300, + 252, 290, 457, 644, 213, 518, 599, 239, 484, 0, + 0, 652, 247, 505, 611, 600, 215, 595, 504, 394, + 327, 328, 214, 0, 458, 269, 294, 0, 0, 259, + 416, 590, 591, 257, 653, 228, 623, 220, 1298, 622, + 409, 585, 596, 395, 384, 219, 594, 393, 383, 335, + 356, 357, 281, 308, 448, 376, 449, 307, 309, 405, + 404, 406, 207, 609, 627, 0, 208, 0, 500, 612, + 654, 453, 212, 234, 235, 237, 1314, 280, 284, 292, + 295, 304, 305, 314, 368, 420, 447, 443, 452, 1405, + 580, 603, 617, 629, 635, 636, 638, 639, 640, 641, + 642, 645, 643, 408, 312, 496, 334, 374, 1394, 1437, + 426, 473, 240, 607, 497, 199, 1308, 1313, 1306, 0, + 255, 256, 1376, 576, 1309, 1307, 1365, 1366, 1310, 1428, + 1429, 1430, 1415, 655, 656, 657, 658, 659, 660, 661, 662, 663, 664, 665, 666, 667, 668, 669, 670, 671, - 649, 508, 514, 509, 510, 511, 512, 513, 0, 515, - 1397, 1301, 0, 1310, 1311, 398, 1406, 591, 592, 672, - 384, 486, 603, 335, 349, 352, 341, 361, 0, 362, - 337, 338, 343, 346, 347, 348, 353, 354, 358, 364, - 249, 210, 390, 399, 578, 312, 216, 217, 218, 524, - 525, 526, 527, 619, 620, 624, 205, 462, 463, 464, - 465, 292, 614, 309, 468, 467, 331, 332, 379, 449, - 540, 542, 553, 557, 559, 561, 567, 570, 541, 543, - 554, 558, 560, 562, 568, 571, 530, 532, 534, 536, - 549, 548, 545, 573, 574, 551, 556, 535, 547, 552, - 565, 572, 569, 529, 533, 537, 546, 564, 563, 544, - 555, 566, 550, 538, 531, 539, 1368, 196, 221, 368, - 1432, 454, 288, 650, 618, 484, 613, 206, 223, 1304, - 262, 1316, 1324, 0, 1330, 1338, 1339, 1352, 1355, 1356, - 1357, 1358, 1376, 1377, 1379, 1387, 1389, 1392, 1394, 1401, - 1415, 1435, 198, 200, 209, 222, 232, 236, 243, 261, - 276, 278, 285, 298, 310, 318, 319, 322, 328, 380, - 386, 387, 388, 389, 409, 410, 411, 414, 417, 418, - 421, 423, 424, 427, 431, 435, 436, 437, 439, 441, - 443, 455, 460, 474, 475, 476, 477, 478, 481, 482, - 488, 489, 490, 491, 492, 500, 501, 516, 586, 588, - 605, 625, 632, 480, 301, 302, 444, 445, 314, 315, - 646, 647, 300, 600, 633, 597, 645, 627, 438, 378, - 1367, 1373, 381, 281, 305, 320, 1382, 617, 502, 227, - 466, 290, 251, 1400, 1402, 211, 246, 230, 259, 274, - 277, 324, 391, 400, 429, 434, 296, 271, 244, 459, - 241, 485, 519, 520, 521, 523, 395, 266, 433, 1363, - 1391, 376, 576, 577, 316, 396, 0, 0, 0, 0, - 0, 0, 1421, 1405, 528, 0, 1347, 1424, 1315, 1334, - 1434, 1337, 1340, 1384, 1293, 1362, 416, 1331, 1319, 1288, - 1326, 1289, 1317, 1349, 270, 1314, 1407, 1366, 1423, 366, - 267, 1295, 1286, 204, 505, 1320, 430, 1336, 203, 1386, - 487, 252, 377, 374, 583, 282, 273, 269, 250, 317, - 385, 428, 518, 422, 1430, 370, 1372, 0, 497, 401, - 0, 0, 0, 1411, 1410, 1341, 1351, 1413, 1360, 1398, - 1346, 1385, 1303, 1371, 1425, 1332, 1381, 1426, 323, 248, - 325, 202, 413, 498, 286, 0, 0, 0, 0, 0, - 507, 724, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 238, 0, 0, 245, 0, 0, 0, 351, 360, - 359, 339, 340, 342, 344, 350, 357, 363, 336, 345, - 1328, 1378, 609, 1420, 1329, 1380, 265, 321, 272, 264, - 580, 1431, 1412, 1292, 1359, 1419, 1354, 596, 0, 0, - 229, 1422, 1353, 0, 1383, 0, 1437, 1287, 1374, 0, - 1290, 1294, 1433, 1417, 1323, 275, 0, 0, 0, 0, - 0, 0, 0, 1350, 1361, 1395, 1399, 1344, 0, 0, - 0, 0, 0, 0, 0, 0, 1321, 0, 1370, 0, - 0, 0, 1299, 1291, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 1348, 0, 0, 0, - 0, 1302, 0, 1322, 1396, 0, 1285, 297, 1296, 402, - 257, 0, 453, 1403, 1416, 1345, 629, 1418, 1343, 1342, - 1390, 1300, 1409, 1335, 365, 1298, 330, 197, 225, 0, - 1333, 412, 461, 473, 1408, 1318, 1327, 253, 1325, 471, - 426, 604, 233, 284, 458, 432, 469, 440, 287, 1369, - 1388, 470, 372, 585, 450, 601, 630, 631, 263, 406, - 615, 522, 623, 648, 226, 260, 420, 506, 607, 494, - 397, 581, 582, 329, 493, 295, 201, 369, 636, 224, - 479, 371, 242, 231, 587, 612, 299, 289, 456, 643, - 213, 517, 598, 239, 483, 0, 0, 651, 247, 504, - 610, 599, 215, 594, 503, 393, 326, 327, 214, 0, - 457, 268, 293, 0, 0, 258, 415, 589, 590, 256, - 652, 228, 622, 220, 1297, 621, 408, 584, 595, 394, - 383, 219, 593, 392, 382, 334, 355, 356, 280, 307, - 447, 375, 448, 306, 308, 404, 403, 405, 207, 608, - 626, 0, 208, 0, 499, 611, 653, 452, 212, 234, - 235, 237, 1313, 279, 283, 291, 294, 303, 304, 313, - 367, 419, 446, 442, 451, 1404, 579, 602, 616, 628, - 634, 635, 637, 638, 639, 640, 641, 644, 642, 407, - 311, 495, 333, 373, 1393, 1436, 425, 472, 240, 606, - 496, 199, 1307, 1312, 1305, 0, 254, 255, 1375, 575, - 1308, 1306, 1364, 1365, 1309, 1427, 1428, 1429, 1414, 654, - 655, 656, 657, 658, 659, 660, 661, 662, 663, 664, - 665, 666, 667, 668, 669, 670, 671, 649, 508, 514, - 509, 510, 511, 512, 513, 0, 515, 1397, 1301, 0, - 1310, 1311, 398, 1406, 591, 592, 672, 384, 486, 603, - 335, 349, 352, 341, 361, 0, 362, 337, 338, 343, - 346, 347, 348, 353, 354, 358, 364, 249, 210, 390, - 399, 578, 312, 216, 217, 218, 524, 525, 526, 527, - 619, 620, 624, 205, 462, 463, 464, 465, 292, 614, - 309, 468, 467, 331, 332, 379, 449, 540, 542, 553, - 557, 559, 561, 567, 570, 541, 543, 554, 558, 560, - 562, 568, 571, 530, 532, 534, 536, 549, 548, 545, - 573, 574, 551, 556, 535, 547, 552, 565, 572, 569, - 529, 533, 537, 546, 564, 563, 544, 555, 566, 550, - 538, 531, 539, 1368, 196, 221, 368, 1432, 454, 288, - 650, 618, 484, 613, 206, 223, 1304, 262, 1316, 1324, - 0, 1330, 1338, 1339, 1352, 1355, 1356, 1357, 1358, 1376, - 1377, 1379, 1387, 1389, 1392, 1394, 1401, 1415, 1435, 198, - 200, 209, 222, 232, 236, 243, 261, 276, 278, 285, - 298, 310, 318, 319, 322, 328, 380, 386, 387, 388, - 389, 409, 410, 411, 414, 417, 418, 421, 423, 424, - 427, 431, 435, 436, 437, 439, 441, 443, 455, 460, - 474, 475, 476, 477, 478, 481, 482, 488, 489, 490, - 491, 492, 500, 501, 516, 586, 588, 605, 625, 632, - 480, 301, 302, 444, 445, 314, 315, 646, 647, 300, - 600, 633, 597, 645, 627, 438, 378, 1367, 1373, 381, - 281, 305, 320, 1382, 617, 502, 227, 466, 290, 251, - 1400, 1402, 211, 246, 230, 259, 274, 277, 324, 391, - 400, 429, 434, 296, 271, 244, 459, 241, 485, 519, - 520, 521, 523, 395, 266, 433, 1363, 1391, 376, 576, - 577, 316, 396, 0, 0, 0, 0, 0, 0, 1421, - 1405, 528, 0, 1347, 1424, 1315, 1334, 1434, 1337, 1340, - 1384, 1293, 1362, 416, 1331, 1319, 1288, 1326, 1289, 1317, - 1349, 270, 1314, 1407, 1366, 1423, 366, 267, 1295, 1286, - 204, 505, 1320, 430, 1336, 203, 1386, 487, 252, 377, - 374, 583, 282, 273, 269, 250, 317, 385, 428, 518, - 422, 1430, 370, 1372, 0, 497, 401, 0, 0, 0, - 1411, 1410, 1341, 1351, 1413, 1360, 1398, 1346, 1385, 1303, - 1371, 1425, 1332, 1381, 1426, 323, 248, 325, 202, 413, - 498, 286, 0, 0, 0, 0, 0, 507, 959, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 238, 0, - 0, 245, 0, 0, 0, 351, 360, 359, 339, 340, - 342, 344, 350, 357, 363, 336, 345, 1328, 1378, 609, - 1420, 1329, 1380, 265, 321, 272, 264, 580, 1431, 1412, - 1292, 1359, 1419, 1354, 596, 0, 0, 229, 1422, 1353, - 0, 1383, 0, 1437, 1287, 1374, 0, 1290, 1294, 1433, - 1417, 1323, 275, 0, 0, 0, 0, 0, 0, 0, - 1350, 1361, 1395, 1399, 1344, 0, 0, 0, 0, 0, - 0, 0, 0, 1321, 0, 1370, 0, 0, 0, 1299, - 1291, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1348, 0, 0, 0, 0, 1302, 0, - 1322, 1396, 0, 1285, 297, 1296, 402, 257, 0, 453, - 1403, 1416, 1345, 629, 1418, 1343, 1342, 1390, 1300, 1409, - 1335, 365, 1298, 330, 197, 225, 0, 1333, 412, 461, - 473, 1408, 1318, 1327, 253, 1325, 471, 426, 604, 233, - 284, 458, 432, 469, 440, 287, 1369, 1388, 470, 372, - 585, 450, 601, 630, 631, 263, 406, 615, 522, 623, - 648, 226, 260, 420, 506, 607, 494, 397, 581, 582, - 329, 493, 295, 201, 369, 636, 224, 479, 371, 242, - 231, 587, 612, 299, 289, 456, 643, 213, 517, 598, - 239, 483, 0, 0, 651, 247, 504, 610, 599, 215, - 594, 503, 393, 326, 327, 214, 0, 457, 268, 293, - 0, 0, 258, 415, 589, 590, 256, 652, 228, 622, - 220, 1297, 621, 408, 584, 595, 394, 383, 219, 593, - 392, 382, 334, 355, 356, 280, 307, 447, 375, 448, - 306, 308, 404, 403, 405, 207, 608, 626, 0, 208, - 0, 499, 611, 653, 452, 212, 234, 235, 237, 1313, - 279, 283, 291, 294, 303, 304, 313, 367, 419, 446, - 442, 451, 1404, 579, 602, 616, 628, 634, 635, 637, - 638, 639, 640, 641, 644, 642, 407, 311, 495, 333, - 373, 1393, 1436, 425, 472, 240, 606, 496, 199, 1307, - 1312, 1305, 0, 254, 255, 1375, 575, 1308, 1306, 1364, - 1365, 1309, 1427, 1428, 1429, 1414, 654, 655, 656, 657, + 672, 650, 509, 515, 510, 511, 512, 513, 514, 0, + 516, 1398, 1302, 0, 1311, 1312, 399, 1407, 592, 593, + 673, 385, 487, 604, 336, 350, 353, 342, 362, 0, + 363, 338, 339, 344, 347, 348, 349, 354, 355, 359, + 365, 249, 210, 391, 400, 579, 313, 216, 217, 218, + 525, 526, 527, 528, 620, 621, 625, 205, 463, 464, + 465, 466, 293, 615, 310, 469, 468, 332, 333, 380, + 450, 541, 543, 554, 558, 560, 562, 568, 571, 542, + 544, 555, 559, 561, 563, 569, 572, 531, 533, 535, + 537, 550, 549, 546, 574, 575, 552, 557, 536, 548, + 553, 566, 573, 570, 530, 534, 538, 547, 565, 564, + 545, 556, 567, 551, 539, 532, 540, 1369, 196, 221, + 369, 1433, 455, 289, 651, 619, 485, 614, 206, 223, + 1305, 263, 1317, 1325, 0, 1331, 1339, 1340, 1353, 1356, + 1357, 1358, 1359, 1377, 1378, 1380, 1388, 1390, 1393, 1395, + 1402, 1416, 1436, 198, 200, 209, 222, 232, 236, 243, + 262, 277, 279, 286, 299, 311, 319, 320, 323, 329, + 381, 387, 388, 389, 390, 410, 411, 412, 415, 418, + 419, 422, 424, 425, 428, 432, 436, 437, 438, 440, + 442, 444, 456, 461, 475, 476, 477, 478, 479, 482, + 483, 489, 490, 491, 492, 493, 501, 502, 517, 587, + 589, 606, 626, 633, 481, 302, 303, 445, 446, 315, + 316, 647, 648, 301, 601, 634, 598, 646, 628, 439, + 379, 1368, 1374, 382, 282, 306, 321, 1383, 618, 503, + 227, 467, 291, 251, 1401, 1403, 211, 246, 230, 260, + 275, 278, 325, 392, 401, 430, 435, 297, 272, 244, + 460, 241, 486, 520, 521, 522, 524, 396, 267, 434, + 1364, 1392, 377, 577, 578, 317, 397, 0, 0, 0, + 0, 0, 0, 1422, 1406, 529, 0, 1348, 1425, 1316, + 1335, 1435, 1338, 1341, 1385, 1294, 1363, 417, 1332, 1320, + 1289, 1327, 1290, 1318, 1350, 271, 1315, 1408, 1367, 1424, + 367, 268, 1296, 1287, 204, 506, 1321, 431, 1337, 203, + 1387, 488, 253, 378, 375, 584, 283, 274, 270, 250, + 318, 386, 429, 519, 423, 1431, 371, 1373, 0, 498, + 402, 0, 0, 0, 1412, 1411, 1342, 1352, 1414, 1361, + 1399, 1347, 1386, 1304, 1372, 1426, 1333, 1382, 1427, 324, + 248, 326, 202, 414, 499, 287, 0, 0, 0, 0, + 0, 508, 725, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 238, 0, 0, 245, 0, 0, 0, 352, + 361, 360, 340, 341, 343, 345, 351, 358, 364, 337, + 346, 1329, 1379, 610, 1421, 1330, 1381, 266, 322, 273, + 265, 581, 1432, 1413, 1293, 1360, 1420, 1355, 597, 0, + 0, 229, 1423, 1354, 0, 1384, 0, 1438, 1288, 1375, + 0, 1291, 1295, 1434, 1418, 1324, 276, 0, 0, 0, + 0, 0, 0, 0, 1351, 1362, 1396, 1400, 1345, 0, + 0, 0, 0, 0, 0, 0, 0, 1322, 0, 1371, + 0, 0, 0, 1300, 1292, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1349, 0, 0, + 0, 0, 1303, 0, 1323, 1397, 0, 1286, 298, 1297, + 403, 258, 0, 454, 1404, 1417, 1346, 630, 1419, 1344, + 1343, 1391, 1301, 1410, 1336, 366, 1299, 331, 197, 225, + 0, 1334, 413, 462, 474, 1409, 1319, 1328, 254, 1326, + 472, 427, 605, 233, 285, 459, 433, 470, 441, 288, + 1370, 1389, 471, 373, 586, 451, 602, 631, 632, 264, + 407, 616, 523, 624, 649, 226, 261, 421, 507, 608, + 495, 398, 582, 583, 330, 494, 296, 201, 370, 637, + 224, 480, 372, 242, 231, 588, 613, 300, 252, 290, + 457, 644, 213, 518, 599, 239, 484, 0, 0, 652, + 247, 505, 611, 600, 215, 595, 504, 394, 327, 328, + 214, 0, 458, 269, 294, 0, 0, 259, 416, 590, + 591, 257, 653, 228, 623, 220, 1298, 622, 409, 585, + 596, 395, 384, 219, 594, 393, 383, 335, 356, 357, + 281, 308, 448, 376, 449, 307, 309, 405, 404, 406, + 207, 609, 627, 0, 208, 0, 500, 612, 654, 453, + 212, 234, 235, 237, 1314, 280, 284, 292, 295, 304, + 305, 314, 368, 420, 447, 443, 452, 1405, 580, 603, + 617, 629, 635, 636, 638, 639, 640, 641, 642, 645, + 643, 408, 312, 496, 334, 374, 1394, 1437, 426, 473, + 240, 607, 497, 199, 1308, 1313, 1306, 0, 255, 256, + 1376, 576, 1309, 1307, 1365, 1366, 1310, 1428, 1429, 1430, + 1415, 655, 656, 657, 658, 659, 660, 661, 662, 663, + 664, 665, 666, 667, 668, 669, 670, 671, 672, 650, + 509, 515, 510, 511, 512, 513, 514, 0, 516, 1398, + 1302, 0, 1311, 1312, 399, 1407, 592, 593, 673, 385, + 487, 604, 336, 350, 353, 342, 362, 0, 363, 338, + 339, 344, 347, 348, 349, 354, 355, 359, 365, 249, + 210, 391, 400, 579, 313, 216, 217, 218, 525, 526, + 527, 528, 620, 621, 625, 205, 463, 464, 465, 466, + 293, 615, 310, 469, 468, 332, 333, 380, 450, 541, + 543, 554, 558, 560, 562, 568, 571, 542, 544, 555, + 559, 561, 563, 569, 572, 531, 533, 535, 537, 550, + 549, 546, 574, 575, 552, 557, 536, 548, 553, 566, + 573, 570, 530, 534, 538, 547, 565, 564, 545, 556, + 567, 551, 539, 532, 540, 1369, 196, 221, 369, 1433, + 455, 289, 651, 619, 485, 614, 206, 223, 1305, 263, + 1317, 1325, 0, 1331, 1339, 1340, 1353, 1356, 1357, 1358, + 1359, 1377, 1378, 1380, 1388, 1390, 1393, 1395, 1402, 1416, + 1436, 198, 200, 209, 222, 232, 236, 243, 262, 277, + 279, 286, 299, 311, 319, 320, 323, 329, 381, 387, + 388, 389, 390, 410, 411, 412, 415, 418, 419, 422, + 424, 425, 428, 432, 436, 437, 438, 440, 442, 444, + 456, 461, 475, 476, 477, 478, 479, 482, 483, 489, + 490, 491, 492, 493, 501, 502, 517, 587, 589, 606, + 626, 633, 481, 302, 303, 445, 446, 315, 316, 647, + 648, 301, 601, 634, 598, 646, 628, 439, 379, 1368, + 1374, 382, 282, 306, 321, 1383, 618, 503, 227, 467, + 291, 251, 1401, 1403, 211, 246, 230, 260, 275, 278, + 325, 392, 401, 430, 435, 297, 272, 244, 460, 241, + 486, 520, 521, 522, 524, 396, 267, 434, 1364, 1392, + 377, 577, 578, 317, 397, 0, 0, 0, 0, 0, + 0, 1422, 1406, 529, 0, 1348, 1425, 1316, 1335, 1435, + 1338, 1341, 1385, 1294, 1363, 417, 1332, 1320, 1289, 1327, + 1290, 1318, 1350, 271, 1315, 1408, 1367, 1424, 367, 268, + 1296, 1287, 204, 506, 1321, 431, 1337, 203, 1387, 488, + 253, 378, 375, 584, 283, 274, 270, 250, 318, 386, + 429, 519, 423, 1431, 371, 1373, 0, 498, 402, 0, + 0, 0, 1412, 1411, 1342, 1352, 1414, 1361, 1399, 1347, + 1386, 1304, 1372, 1426, 1333, 1382, 1427, 324, 248, 326, + 202, 414, 499, 287, 0, 0, 0, 0, 0, 508, + 960, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 238, 0, 0, 245, 0, 0, 0, 352, 361, 360, + 340, 341, 343, 345, 351, 358, 364, 337, 346, 1329, + 1379, 610, 1421, 1330, 1381, 266, 322, 273, 265, 581, + 1432, 1413, 1293, 1360, 1420, 1355, 597, 0, 0, 229, + 1423, 1354, 0, 1384, 0, 1438, 1288, 1375, 0, 1291, + 1295, 1434, 1418, 1324, 276, 0, 0, 0, 0, 0, + 0, 0, 1351, 1362, 1396, 1400, 1345, 0, 0, 0, + 0, 0, 0, 0, 0, 1322, 0, 1371, 0, 0, + 0, 1300, 1292, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1349, 0, 0, 0, 0, + 1303, 0, 1323, 1397, 0, 1286, 298, 1297, 403, 258, + 0, 454, 1404, 1417, 1346, 630, 1419, 1344, 1343, 1391, + 1301, 1410, 1336, 366, 1299, 331, 197, 225, 0, 1334, + 413, 462, 474, 1409, 1319, 1328, 254, 1326, 472, 427, + 605, 233, 285, 459, 433, 470, 441, 288, 1370, 1389, + 471, 373, 586, 451, 602, 631, 632, 264, 407, 616, + 523, 624, 649, 226, 261, 421, 507, 608, 495, 398, + 582, 583, 330, 494, 296, 201, 370, 637, 224, 480, + 372, 242, 231, 588, 613, 300, 252, 290, 457, 644, + 213, 518, 599, 239, 484, 0, 0, 652, 247, 505, + 611, 600, 215, 595, 504, 394, 327, 328, 214, 0, + 458, 269, 294, 0, 0, 259, 416, 590, 591, 257, + 653, 228, 623, 220, 1298, 622, 409, 585, 596, 395, + 384, 219, 594, 393, 383, 335, 356, 357, 281, 308, + 448, 376, 449, 307, 309, 405, 404, 406, 207, 609, + 627, 0, 208, 0, 500, 612, 654, 453, 212, 234, + 235, 237, 1314, 280, 284, 292, 295, 304, 305, 314, + 368, 420, 447, 443, 452, 1405, 580, 603, 617, 629, + 635, 636, 638, 639, 640, 641, 642, 645, 643, 408, + 312, 496, 334, 374, 1394, 1437, 426, 473, 240, 607, + 497, 199, 1308, 1313, 1306, 0, 255, 256, 1376, 576, + 1309, 1307, 1365, 1366, 1310, 1428, 1429, 1430, 1415, 655, + 656, 657, 658, 659, 660, 661, 662, 663, 664, 665, + 666, 667, 668, 669, 670, 671, 672, 650, 509, 515, + 510, 511, 512, 513, 514, 0, 516, 1398, 1302, 0, + 1311, 1312, 399, 1407, 592, 593, 673, 385, 487, 604, + 336, 350, 353, 342, 362, 0, 363, 338, 339, 344, + 347, 348, 349, 354, 355, 359, 365, 249, 210, 391, + 400, 579, 313, 216, 217, 218, 525, 526, 527, 528, + 620, 621, 625, 205, 463, 464, 465, 466, 293, 615, + 310, 469, 468, 332, 333, 380, 450, 541, 543, 554, + 558, 560, 562, 568, 571, 542, 544, 555, 559, 561, + 563, 569, 572, 531, 533, 535, 537, 550, 549, 546, + 574, 575, 552, 557, 536, 548, 553, 566, 573, 570, + 530, 534, 538, 547, 565, 564, 545, 556, 567, 551, + 539, 532, 540, 1369, 196, 221, 369, 1433, 455, 289, + 651, 619, 485, 614, 206, 223, 1305, 263, 1317, 1325, + 0, 1331, 1339, 1340, 1353, 1356, 1357, 1358, 1359, 1377, + 1378, 1380, 1388, 1390, 1393, 1395, 1402, 1416, 1436, 198, + 200, 209, 222, 232, 236, 243, 262, 277, 279, 286, + 299, 311, 319, 320, 323, 329, 381, 387, 388, 389, + 390, 410, 411, 412, 415, 418, 419, 422, 424, 425, + 428, 432, 436, 437, 438, 440, 442, 444, 456, 461, + 475, 476, 477, 478, 479, 482, 483, 489, 490, 491, + 492, 493, 501, 502, 517, 587, 589, 606, 626, 633, + 481, 302, 303, 445, 446, 315, 316, 647, 648, 301, + 601, 634, 598, 646, 628, 439, 379, 1368, 1374, 382, + 282, 306, 321, 1383, 618, 503, 227, 467, 291, 251, + 1401, 1403, 211, 246, 230, 260, 275, 278, 325, 392, + 401, 430, 435, 297, 272, 244, 460, 241, 486, 520, + 521, 522, 524, 396, 267, 434, 1364, 1392, 377, 577, + 578, 317, 397, 0, 0, 0, 0, 0, 0, 0, + 0, 529, 0, 778, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 417, 0, 0, 0, 765, 0, 0, + 0, 271, 770, 0, 0, 0, 367, 268, 0, 0, + 204, 506, 0, 431, 0, 203, 0, 488, 253, 378, + 375, 584, 283, 274, 270, 250, 318, 386, 429, 519, + 423, 777, 371, 0, 0, 498, 402, 0, 0, 0, + 0, 0, 0, 0, 0, 772, 773, 0, 0, 0, + 0, 0, 0, 0, 0, 324, 248, 326, 202, 414, + 499, 287, 0, 95, 0, 0, 1024, 508, 960, 749, + 926, 964, 1025, 977, 978, 979, 965, 0, 238, 966, + 967, 245, 968, 0, 925, 808, 810, 809, 875, 876, + 877, 878, 879, 880, 881, 811, 812, 806, 973, 610, + 980, 981, 0, 266, 322, 273, 265, 581, 0, 0, + 2233, 2234, 2235, 0, 597, 0, 0, 229, 0, 0, + 0, 0, 0, 0, 0, 745, 762, 0, 776, 0, + 0, 0, 276, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 759, + 760, 0, 0, 0, 0, 920, 0, 761, 0, 0, + 769, 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, 1008, 1009, 1010, + 1011, 1012, 1013, 1014, 1015, 1016, 1017, 1018, 1019, 1020, + 1021, 1022, 1023, 771, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 298, 0, 403, 258, 0, 454, + 919, 0, 0, 630, 0, 0, 917, 0, 0, 0, + 0, 366, 0, 331, 197, 225, 0, 0, 413, 462, + 474, 0, 0, 0, 970, 0, 472, 427, 605, 233, + 285, 459, 433, 470, 441, 288, 0, 0, 471, 373, + 586, 451, 602, 631, 632, 264, 407, 616, 523, 624, + 649, 226, 261, 421, 507, 608, 495, 398, 582, 583, + 330, 494, 296, 201, 370, 637, 224, 480, 372, 242, + 231, 588, 613, 300, 252, 290, 457, 644, 213, 518, + 599, 239, 484, 0, 0, 652, 247, 505, 611, 600, + 215, 595, 504, 394, 327, 328, 214, 0, 458, 269, + 294, 0, 0, 259, 416, 971, 972, 257, 653, 816, + 623, 220, 0, 622, 409, 585, 596, 395, 384, 219, + 594, 393, 383, 335, 824, 825, 281, 308, 901, 900, + 899, 307, 309, 897, 898, 896, 207, 609, 627, 0, + 208, 0, 500, 612, 654, 453, 212, 234, 235, 237, + 0, 280, 284, 292, 295, 304, 305, 314, 368, 420, + 447, 443, 452, 0, 580, 603, 617, 629, 635, 636, + 638, 639, 640, 641, 642, 645, 643, 408, 312, 496, + 334, 374, 0, 0, 426, 473, 240, 607, 497, 907, + 929, 918, 782, 783, 908, 909, 933, 910, 785, 786, + 930, 931, 779, 780, 784, 932, 934, 655, 656, 657, 658, 659, 660, 661, 662, 663, 664, 665, 666, 667, - 668, 669, 670, 671, 649, 508, 514, 509, 510, 511, - 512, 513, 0, 515, 1397, 1301, 0, 1310, 1311, 398, - 1406, 591, 592, 672, 384, 486, 603, 335, 349, 352, - 341, 361, 0, 362, 337, 338, 343, 346, 347, 348, - 353, 354, 358, 364, 249, 210, 390, 399, 578, 312, - 216, 217, 218, 524, 525, 526, 527, 619, 620, 624, - 205, 462, 463, 464, 465, 292, 614, 309, 468, 467, - 331, 332, 379, 449, 540, 542, 553, 557, 559, 561, - 567, 570, 541, 543, 554, 558, 560, 562, 568, 571, - 530, 532, 534, 536, 549, 548, 545, 573, 574, 551, - 556, 535, 547, 552, 565, 572, 569, 529, 533, 537, - 546, 564, 563, 544, 555, 566, 550, 538, 531, 539, - 1368, 196, 221, 368, 1432, 454, 288, 650, 618, 484, - 613, 206, 223, 1304, 262, 1316, 1324, 0, 1330, 1338, - 1339, 1352, 1355, 1356, 1357, 1358, 1376, 1377, 1379, 1387, - 1389, 1392, 1394, 1401, 1415, 1435, 198, 200, 209, 222, - 232, 236, 243, 261, 276, 278, 285, 298, 310, 318, - 319, 322, 328, 380, 386, 387, 388, 389, 409, 410, - 411, 414, 417, 418, 421, 423, 424, 427, 431, 435, - 436, 437, 439, 441, 443, 455, 460, 474, 475, 476, - 477, 478, 481, 482, 488, 489, 490, 491, 492, 500, - 501, 516, 586, 588, 605, 625, 632, 480, 301, 302, - 444, 445, 314, 315, 646, 647, 300, 600, 633, 597, - 645, 627, 438, 378, 1367, 1373, 381, 281, 305, 320, - 1382, 617, 502, 227, 466, 290, 251, 1400, 1402, 211, - 246, 230, 259, 274, 277, 324, 391, 400, 429, 434, - 296, 271, 244, 459, 241, 485, 519, 520, 521, 523, - 395, 266, 433, 1363, 1391, 376, 576, 577, 316, 396, - 0, 0, 0, 0, 0, 0, 0, 0, 528, 0, - 777, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 416, 0, 0, 0, 764, 0, 0, 0, 270, 769, - 0, 0, 0, 366, 267, 0, 0, 204, 505, 0, - 430, 0, 203, 0, 487, 252, 377, 374, 583, 282, - 273, 269, 250, 317, 385, 428, 518, 422, 776, 370, - 0, 0, 497, 401, 0, 0, 0, 0, 0, 0, - 0, 0, 771, 772, 0, 0, 0, 0, 0, 0, - 0, 0, 323, 248, 325, 202, 413, 498, 286, 0, - 95, 0, 0, 1023, 507, 959, 748, 925, 963, 1024, - 976, 977, 978, 964, 0, 238, 965, 966, 245, 967, - 0, 924, 807, 809, 808, 874, 875, 876, 877, 878, - 879, 880, 810, 811, 805, 972, 609, 979, 980, 0, - 265, 321, 272, 264, 580, 0, 0, 2232, 2233, 2234, - 0, 596, 0, 0, 229, 0, 0, 0, 0, 0, - 0, 0, 744, 761, 0, 775, 0, 0, 0, 275, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 758, 759, 0, 0, - 0, 0, 919, 0, 760, 0, 0, 768, 981, 982, + 668, 669, 670, 671, 672, 650, 509, 515, 510, 511, + 512, 513, 514, 0, 516, 921, 768, 767, 0, 774, + 775, 0, 804, 805, 807, 813, 814, 815, 826, 873, + 874, 882, 884, 885, 883, 886, 887, 888, 891, 892, + 893, 894, 889, 890, 895, 787, 791, 788, 789, 790, + 802, 792, 793, 794, 795, 796, 797, 798, 799, 800, + 801, 803, 944, 945, 946, 947, 948, 949, 819, 823, + 822, 820, 821, 817, 818, 845, 844, 846, 847, 848, + 849, 850, 851, 853, 852, 854, 855, 856, 857, 858, + 859, 827, 828, 831, 832, 830, 829, 833, 842, 843, + 834, 835, 836, 837, 838, 839, 841, 840, 860, 861, + 862, 863, 864, 866, 865, 869, 870, 868, 867, 872, + 871, 766, 196, 221, 369, 0, 455, 289, 651, 619, + 485, 614, 206, 223, 935, 263, 936, 0, 0, 940, + 0, 0, 0, 942, 941, 0, 943, 905, 904, 0, + 0, 937, 938, 0, 939, 0, 0, 198, 200, 209, + 222, 232, 236, 243, 262, 277, 279, 286, 299, 311, + 319, 320, 323, 329, 381, 387, 388, 389, 390, 410, + 411, 412, 415, 418, 419, 422, 424, 425, 428, 432, + 436, 437, 438, 440, 442, 444, 456, 461, 475, 476, + 477, 478, 479, 482, 483, 489, 490, 491, 492, 493, + 501, 502, 517, 587, 589, 606, 626, 633, 481, 950, + 951, 952, 953, 954, 955, 956, 957, 301, 601, 634, + 598, 646, 628, 439, 379, 0, 0, 382, 282, 306, + 321, 0, 618, 503, 227, 467, 291, 251, 975, 0, + 211, 246, 230, 260, 275, 278, 325, 392, 401, 430, + 435, 297, 272, 244, 460, 241, 486, 520, 521, 522, + 524, 396, 267, 434, 397, 0, 377, 577, 578, 317, + 0, 0, 0, 529, 0, 778, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 417, 0, 0, 0, 765, + 0, 0, 0, 271, 770, 0, 0, 0, 367, 268, + 0, 0, 204, 506, 0, 431, 0, 203, 0, 488, + 253, 378, 375, 584, 283, 274, 270, 250, 318, 386, + 429, 519, 423, 777, 371, 0, 0, 498, 402, 0, + 0, 0, 0, 0, 0, 0, 0, 772, 773, 0, + 0, 0, 0, 0, 0, 2440, 0, 324, 248, 326, + 202, 414, 499, 287, 0, 95, 0, 0, 1024, 508, + 960, 749, 926, 964, 1025, 977, 978, 979, 965, 0, + 238, 966, 967, 245, 968, 0, 925, 808, 810, 809, + 875, 876, 877, 878, 879, 880, 881, 811, 812, 806, + 973, 610, 980, 981, 2441, 266, 322, 273, 265, 581, + 0, 0, 0, 0, 0, 0, 597, 0, 0, 229, + 0, 0, 0, 0, 0, 0, 0, 745, 762, 0, + 776, 0, 0, 0, 276, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 759, 760, 0, 0, 0, 0, 920, 0, 761, + 0, 0, 769, 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, 1008, + 1009, 1010, 1011, 1012, 1013, 1014, 1015, 1016, 1017, 1018, + 1019, 1020, 1021, 1022, 1023, 771, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 298, 0, 403, 258, + 0, 454, 919, 0, 0, 630, 0, 0, 917, 0, + 0, 0, 0, 366, 0, 331, 197, 225, 0, 0, + 413, 462, 474, 0, 0, 0, 970, 0, 472, 427, + 605, 233, 285, 459, 433, 470, 441, 288, 0, 0, + 471, 373, 586, 451, 602, 631, 632, 264, 407, 616, + 523, 624, 649, 226, 261, 421, 507, 608, 495, 398, + 582, 583, 330, 494, 296, 201, 370, 637, 224, 480, + 372, 242, 231, 588, 613, 300, 252, 290, 457, 644, + 213, 518, 599, 239, 484, 0, 0, 652, 247, 505, + 611, 600, 215, 595, 504, 394, 327, 328, 214, 0, + 458, 269, 294, 0, 0, 259, 416, 971, 972, 257, + 653, 816, 623, 220, 0, 622, 409, 585, 596, 395, + 384, 219, 594, 393, 383, 335, 824, 825, 281, 308, + 901, 900, 899, 307, 309, 897, 898, 896, 207, 609, + 627, 0, 208, 0, 500, 612, 654, 453, 212, 234, + 235, 237, 0, 280, 284, 292, 295, 304, 305, 314, + 368, 420, 447, 443, 452, 0, 580, 603, 617, 629, + 635, 636, 638, 639, 640, 641, 642, 645, 643, 408, + 312, 496, 334, 374, 0, 0, 426, 473, 240, 607, + 497, 907, 929, 918, 782, 783, 908, 909, 933, 910, + 785, 786, 930, 931, 779, 780, 784, 932, 934, 655, + 656, 657, 658, 659, 660, 661, 662, 663, 664, 665, + 666, 667, 668, 669, 670, 671, 672, 650, 509, 515, + 510, 511, 512, 513, 514, 0, 516, 921, 768, 767, + 0, 774, 775, 0, 804, 805, 807, 813, 814, 815, + 826, 873, 874, 882, 884, 885, 883, 886, 887, 888, + 891, 892, 893, 894, 889, 890, 895, 787, 791, 788, + 789, 790, 802, 792, 793, 794, 795, 796, 797, 798, + 799, 800, 801, 803, 944, 945, 946, 947, 948, 949, + 819, 823, 822, 820, 821, 817, 818, 845, 844, 846, + 847, 848, 849, 850, 851, 853, 852, 854, 855, 856, + 857, 858, 859, 827, 828, 831, 832, 830, 829, 833, + 842, 843, 834, 835, 836, 837, 838, 839, 841, 840, + 860, 861, 862, 863, 864, 866, 865, 869, 870, 868, + 867, 872, 871, 766, 196, 221, 369, 0, 455, 289, + 651, 619, 485, 614, 206, 223, 935, 263, 936, 0, + 0, 940, 0, 0, 0, 942, 941, 0, 943, 905, + 904, 0, 0, 937, 938, 0, 939, 0, 0, 198, + 200, 209, 222, 232, 236, 243, 262, 277, 279, 286, + 299, 311, 319, 320, 323, 329, 381, 387, 388, 389, + 390, 410, 411, 412, 415, 418, 419, 422, 424, 425, + 428, 432, 436, 437, 438, 440, 442, 444, 456, 461, + 475, 476, 477, 478, 479, 482, 483, 489, 490, 491, + 492, 493, 501, 502, 517, 587, 589, 606, 626, 633, + 481, 950, 951, 952, 953, 954, 955, 956, 957, 301, + 601, 634, 598, 646, 628, 439, 379, 0, 0, 382, + 282, 306, 321, 0, 618, 503, 227, 467, 291, 251, + 975, 0, 211, 246, 230, 260, 275, 278, 325, 392, + 401, 430, 435, 297, 272, 244, 460, 241, 486, 520, + 521, 522, 524, 396, 267, 434, 397, 0, 377, 577, + 578, 317, 0, 0, 86, 529, 0, 778, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 417, 0, 0, + 0, 765, 0, 0, 0, 271, 770, 0, 0, 0, + 367, 268, 0, 0, 204, 506, 0, 431, 0, 203, + 0, 488, 253, 378, 375, 584, 283, 274, 270, 250, + 318, 386, 429, 519, 423, 777, 371, 0, 0, 498, + 402, 0, 0, 0, 0, 0, 0, 0, 0, 772, + 773, 0, 0, 0, 0, 0, 0, 0, 0, 324, + 248, 326, 202, 414, 499, 287, 0, 95, 0, 0, + 1024, 508, 960, 749, 926, 964, 1025, 977, 978, 979, + 965, 0, 238, 966, 967, 245, 968, 0, 925, 808, + 810, 809, 875, 876, 877, 878, 879, 880, 881, 811, + 812, 806, 973, 610, 980, 981, 0, 266, 322, 273, + 265, 581, 0, 0, 0, 0, 0, 0, 597, 0, + 0, 229, 0, 0, 0, 0, 0, 0, 0, 745, + 762, 0, 776, 0, 0, 0, 276, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 759, 760, 0, 0, 0, 0, 920, + 0, 761, 0, 0, 769, 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, 1008, 1009, 1010, 1011, 1012, 1013, 1014, 1015, 1016, + 1017, 1018, 1019, 1020, 1021, 1022, 1023, 771, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 298, 0, + 403, 258, 0, 454, 919, 0, 0, 630, 0, 0, + 917, 0, 0, 0, 0, 366, 0, 331, 197, 225, + 0, 0, 413, 462, 474, 0, 0, 0, 970, 0, + 472, 427, 605, 233, 285, 459, 433, 470, 441, 288, + 0, 0, 471, 373, 586, 451, 602, 631, 632, 264, + 407, 616, 523, 624, 649, 226, 261, 421, 507, 608, + 495, 398, 582, 583, 330, 494, 296, 201, 370, 637, + 224, 480, 372, 242, 231, 588, 613, 300, 252, 290, + 457, 644, 213, 518, 599, 239, 484, 0, 0, 652, + 247, 505, 611, 600, 215, 595, 504, 394, 327, 328, + 214, 0, 458, 269, 294, 0, 0, 259, 416, 971, + 972, 257, 653, 816, 623, 220, 0, 622, 409, 585, + 596, 395, 384, 219, 594, 393, 383, 335, 824, 825, + 281, 308, 901, 900, 899, 307, 309, 897, 898, 896, + 207, 609, 627, 0, 208, 0, 500, 612, 654, 453, + 212, 234, 235, 237, 0, 280, 284, 292, 295, 304, + 305, 314, 368, 420, 447, 443, 452, 0, 580, 603, + 617, 629, 635, 636, 638, 639, 640, 641, 642, 645, + 643, 408, 312, 496, 334, 374, 0, 0, 426, 473, + 240, 607, 497, 907, 929, 918, 782, 783, 908, 909, + 933, 910, 785, 786, 930, 931, 779, 780, 784, 932, + 934, 655, 656, 657, 658, 659, 660, 661, 662, 663, + 664, 665, 666, 667, 668, 669, 670, 671, 672, 650, + 509, 515, 510, 511, 512, 513, 514, 0, 516, 921, + 768, 767, 0, 774, 775, 0, 804, 805, 807, 813, + 814, 815, 826, 873, 874, 882, 884, 885, 883, 886, + 887, 888, 891, 892, 893, 894, 889, 890, 895, 787, + 791, 788, 789, 790, 802, 792, 793, 794, 795, 796, + 797, 798, 799, 800, 801, 803, 944, 945, 946, 947, + 948, 949, 819, 823, 822, 820, 821, 817, 818, 845, + 844, 846, 847, 848, 849, 850, 851, 853, 852, 854, + 855, 856, 857, 858, 859, 827, 828, 831, 832, 830, + 829, 833, 842, 843, 834, 835, 836, 837, 838, 839, + 841, 840, 860, 861, 862, 863, 864, 866, 865, 869, + 870, 868, 867, 872, 871, 766, 196, 221, 369, 94, + 455, 289, 651, 619, 485, 614, 206, 223, 935, 263, + 936, 0, 0, 940, 0, 0, 0, 942, 941, 0, + 943, 905, 904, 0, 0, 937, 938, 0, 939, 0, + 0, 198, 200, 209, 222, 232, 236, 243, 262, 277, + 279, 286, 299, 311, 319, 320, 323, 329, 381, 387, + 388, 389, 390, 410, 411, 412, 415, 418, 419, 422, + 424, 425, 428, 432, 436, 437, 438, 440, 442, 444, + 456, 461, 475, 476, 477, 478, 479, 482, 483, 489, + 490, 491, 492, 493, 501, 502, 517, 587, 589, 606, + 626, 633, 481, 950, 951, 952, 953, 954, 955, 956, + 957, 301, 601, 634, 598, 646, 628, 439, 379, 0, + 0, 382, 282, 306, 321, 0, 618, 503, 227, 467, + 291, 251, 975, 0, 211, 246, 230, 260, 275, 278, + 325, 392, 401, 430, 435, 297, 272, 244, 460, 241, + 486, 520, 521, 522, 524, 396, 267, 434, 397, 0, + 377, 577, 578, 317, 0, 0, 0, 529, 0, 778, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 417, + 0, 0, 0, 765, 0, 0, 0, 271, 770, 0, + 0, 0, 367, 268, 0, 0, 204, 506, 0, 431, + 0, 203, 0, 488, 253, 378, 375, 584, 283, 274, + 270, 250, 318, 386, 429, 519, 423, 777, 371, 0, + 0, 498, 402, 0, 0, 0, 0, 0, 0, 0, + 0, 772, 773, 0, 0, 0, 0, 0, 0, 0, + 0, 324, 248, 326, 202, 414, 499, 287, 0, 95, + 0, 0, 1024, 508, 960, 749, 926, 964, 1025, 977, + 978, 979, 965, 0, 238, 966, 967, 245, 968, 0, + 925, 808, 810, 809, 875, 876, 877, 878, 879, 880, + 881, 811, 812, 806, 973, 610, 980, 981, 0, 266, + 322, 273, 265, 581, 0, 0, 0, 0, 0, 0, + 597, 0, 0, 229, 0, 0, 0, 0, 0, 0, + 0, 745, 762, 0, 776, 0, 0, 0, 276, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 759, 760, 0, 0, 0, + 0, 920, 0, 761, 0, 0, 769, 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, 1008, 1009, 1010, 1011, 1012, 1013, 1014, + 1015, 1016, 1017, 1018, 1019, 1020, 1021, 1022, 1023, 771, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 298, 0, 403, 258, 0, 454, 919, 0, 0, 630, + 0, 0, 917, 0, 0, 0, 0, 366, 0, 331, + 197, 225, 0, 0, 413, 462, 474, 0, 0, 0, + 970, 0, 472, 427, 605, 233, 285, 459, 433, 470, + 441, 288, 4098, 0, 471, 373, 586, 451, 602, 631, + 632, 264, 407, 616, 523, 624, 649, 226, 261, 421, + 507, 608, 495, 398, 582, 583, 330, 494, 296, 201, + 370, 637, 224, 480, 372, 242, 231, 588, 613, 300, + 252, 290, 457, 644, 213, 518, 599, 239, 484, 0, + 0, 652, 247, 505, 611, 600, 215, 595, 504, 394, + 327, 328, 214, 0, 458, 269, 294, 0, 0, 259, + 416, 971, 972, 257, 653, 816, 623, 220, 0, 622, + 409, 585, 596, 395, 384, 219, 594, 393, 383, 335, + 824, 825, 281, 308, 901, 900, 899, 307, 309, 897, + 898, 896, 207, 609, 627, 0, 208, 0, 500, 612, + 654, 453, 212, 234, 235, 237, 0, 280, 284, 292, + 295, 304, 305, 314, 368, 420, 447, 443, 452, 0, + 580, 603, 617, 629, 635, 636, 638, 639, 640, 641, + 642, 645, 643, 408, 312, 496, 334, 374, 0, 0, + 426, 473, 240, 607, 497, 907, 929, 918, 782, 783, + 908, 909, 933, 910, 785, 786, 930, 931, 779, 780, + 784, 932, 934, 655, 656, 657, 658, 659, 660, 661, + 662, 663, 664, 665, 666, 667, 668, 669, 670, 671, + 672, 650, 509, 515, 510, 511, 512, 513, 514, 0, + 516, 921, 768, 767, 0, 774, 775, 0, 804, 805, + 807, 813, 814, 815, 826, 873, 874, 882, 884, 885, + 883, 886, 887, 888, 891, 892, 893, 894, 889, 890, + 895, 787, 791, 788, 789, 790, 802, 792, 793, 794, + 795, 796, 797, 798, 799, 800, 801, 803, 944, 945, + 946, 947, 948, 949, 819, 823, 822, 820, 821, 817, + 818, 845, 844, 846, 847, 848, 849, 850, 851, 853, + 852, 854, 855, 856, 857, 858, 859, 827, 828, 831, + 832, 830, 829, 833, 842, 843, 834, 835, 836, 837, + 838, 839, 841, 840, 860, 861, 862, 863, 864, 866, + 865, 869, 870, 868, 867, 872, 871, 766, 196, 221, + 369, 0, 455, 289, 651, 619, 485, 614, 206, 223, + 935, 263, 936, 0, 0, 940, 0, 0, 0, 942, + 941, 0, 943, 905, 904, 0, 0, 937, 938, 0, + 939, 0, 0, 198, 200, 209, 222, 232, 236, 243, + 262, 277, 279, 286, 299, 311, 319, 320, 323, 329, + 381, 387, 388, 389, 390, 410, 411, 412, 415, 418, + 419, 422, 424, 425, 428, 432, 436, 437, 438, 440, + 442, 444, 456, 461, 475, 476, 477, 478, 479, 482, + 483, 489, 490, 491, 492, 493, 501, 502, 517, 587, + 589, 606, 626, 633, 481, 950, 951, 952, 953, 954, + 955, 956, 957, 301, 601, 634, 598, 646, 628, 439, + 379, 0, 0, 382, 282, 306, 321, 0, 618, 503, + 227, 467, 291, 251, 975, 0, 211, 246, 230, 260, + 275, 278, 325, 392, 401, 430, 435, 297, 272, 244, + 460, 241, 486, 520, 521, 522, 524, 396, 267, 434, + 397, 0, 377, 577, 578, 317, 0, 0, 0, 529, + 0, 778, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 417, 0, 0, 0, 765, 0, 0, 0, 271, + 770, 0, 0, 0, 367, 268, 0, 0, 204, 506, + 0, 431, 0, 203, 0, 488, 253, 378, 375, 584, + 283, 274, 270, 250, 318, 386, 429, 519, 423, 777, + 371, 0, 0, 498, 402, 0, 0, 0, 0, 0, + 0, 0, 0, 772, 773, 0, 0, 0, 0, 0, + 0, 0, 0, 324, 248, 326, 202, 414, 499, 287, + 0, 95, 0, 1754, 1024, 508, 960, 749, 926, 964, + 1025, 977, 978, 979, 965, 0, 238, 966, 967, 245, + 968, 0, 925, 808, 810, 809, 875, 876, 877, 878, + 879, 880, 881, 811, 812, 806, 973, 610, 980, 981, + 0, 266, 322, 273, 265, 581, 0, 0, 0, 0, + 0, 0, 597, 0, 0, 229, 0, 0, 0, 0, + 0, 0, 0, 745, 762, 0, 776, 0, 0, 0, + 276, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 759, 760, 0, + 0, 0, 0, 920, 0, 761, 0, 0, 769, 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, 1008, 1009, 1010, 1011, 1012, 1013, 1014, 1015, 1016, 1017, 1018, 1019, 1020, 1021, 1022, - 770, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 297, 0, 402, 257, 0, 453, 918, 0, 0, - 629, 0, 0, 916, 0, 0, 0, 0, 365, 0, - 330, 197, 225, 0, 0, 412, 461, 473, 0, 0, - 0, 969, 0, 471, 426, 604, 233, 284, 458, 432, - 469, 440, 287, 0, 0, 470, 372, 585, 450, 601, - 630, 631, 263, 406, 615, 522, 623, 648, 226, 260, - 420, 506, 607, 494, 397, 581, 582, 329, 493, 295, - 201, 369, 636, 224, 479, 371, 242, 231, 587, 612, - 299, 289, 456, 643, 213, 517, 598, 239, 483, 0, - 0, 651, 247, 504, 610, 599, 215, 594, 503, 393, - 326, 327, 214, 0, 457, 268, 293, 0, 0, 258, - 415, 970, 971, 256, 652, 815, 622, 220, 0, 621, - 408, 584, 595, 394, 383, 219, 593, 392, 382, 334, - 823, 824, 280, 307, 900, 899, 898, 306, 308, 896, - 897, 895, 207, 608, 626, 0, 208, 0, 499, 611, - 653, 452, 212, 234, 235, 237, 0, 279, 283, 291, - 294, 303, 304, 313, 367, 419, 446, 442, 451, 0, - 579, 602, 616, 628, 634, 635, 637, 638, 639, 640, - 641, 644, 642, 407, 311, 495, 333, 373, 0, 0, - 425, 472, 240, 606, 496, 906, 928, 917, 781, 782, - 907, 908, 932, 909, 784, 785, 929, 930, 778, 779, - 783, 931, 933, 654, 655, 656, 657, 658, 659, 660, - 661, 662, 663, 664, 665, 666, 667, 668, 669, 670, - 671, 649, 508, 514, 509, 510, 511, 512, 513, 0, - 515, 920, 767, 766, 0, 773, 774, 0, 803, 804, - 806, 812, 813, 814, 825, 872, 873, 881, 883, 884, - 882, 885, 886, 887, 890, 891, 892, 893, 888, 889, - 894, 786, 790, 787, 788, 789, 801, 791, 792, 793, - 794, 795, 796, 797, 798, 799, 800, 802, 943, 944, - 945, 946, 947, 948, 818, 822, 821, 819, 820, 816, - 817, 844, 843, 845, 846, 847, 848, 849, 850, 852, - 851, 853, 854, 855, 856, 857, 858, 826, 827, 830, - 831, 829, 828, 832, 841, 842, 833, 834, 835, 836, - 837, 838, 840, 839, 859, 860, 861, 862, 863, 865, - 864, 868, 869, 867, 866, 871, 870, 765, 196, 221, - 368, 0, 454, 288, 650, 618, 484, 613, 206, 223, - 934, 262, 935, 0, 0, 939, 0, 0, 0, 941, - 940, 0, 942, 904, 903, 0, 0, 936, 937, 0, - 938, 0, 0, 198, 200, 209, 222, 232, 236, 243, - 261, 276, 278, 285, 298, 310, 318, 319, 322, 328, - 380, 386, 387, 388, 389, 409, 410, 411, 414, 417, - 418, 421, 423, 424, 427, 431, 435, 436, 437, 439, - 441, 443, 455, 460, 474, 475, 476, 477, 478, 481, - 482, 488, 489, 490, 491, 492, 500, 501, 516, 586, - 588, 605, 625, 632, 480, 949, 950, 951, 952, 953, - 954, 955, 956, 300, 600, 633, 597, 645, 627, 438, - 378, 0, 0, 381, 281, 305, 320, 0, 617, 502, - 227, 466, 290, 251, 974, 0, 211, 246, 230, 259, - 274, 277, 324, 391, 400, 429, 434, 296, 271, 244, - 459, 241, 485, 519, 520, 521, 523, 395, 266, 433, - 396, 0, 376, 576, 577, 316, 0, 0, 0, 528, - 0, 777, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 416, 0, 0, 0, 764, 0, 0, 0, 270, - 769, 0, 0, 0, 366, 267, 0, 0, 204, 505, - 0, 430, 0, 203, 0, 487, 252, 377, 374, 583, - 282, 273, 269, 250, 317, 385, 428, 518, 422, 776, - 370, 0, 0, 497, 401, 0, 0, 0, 0, 0, - 0, 0, 0, 771, 772, 0, 0, 0, 0, 0, - 0, 2439, 0, 323, 248, 325, 202, 413, 498, 286, - 0, 95, 0, 0, 1023, 507, 959, 748, 925, 963, - 1024, 976, 977, 978, 964, 0, 238, 965, 966, 245, - 967, 0, 924, 807, 809, 808, 874, 875, 876, 877, - 878, 879, 880, 810, 811, 805, 972, 609, 979, 980, - 2440, 265, 321, 272, 264, 580, 0, 0, 0, 0, - 0, 0, 596, 0, 0, 229, 0, 0, 0, 0, - 0, 0, 0, 744, 761, 0, 775, 0, 0, 0, - 275, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 758, 759, 0, - 0, 0, 0, 919, 0, 760, 0, 0, 768, 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, 1008, 1009, 1010, 1011, - 1012, 1013, 1014, 1015, 1016, 1017, 1018, 1019, 1020, 1021, - 1022, 770, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 297, 0, 402, 257, 0, 453, 918, 0, - 0, 629, 0, 0, 916, 0, 0, 0, 0, 365, - 0, 330, 197, 225, 0, 0, 412, 461, 473, 0, - 0, 0, 969, 0, 471, 426, 604, 233, 284, 458, - 432, 469, 440, 287, 0, 0, 470, 372, 585, 450, - 601, 630, 631, 263, 406, 615, 522, 623, 648, 226, - 260, 420, 506, 607, 494, 397, 581, 582, 329, 493, - 295, 201, 369, 636, 224, 479, 371, 242, 231, 587, - 612, 299, 289, 456, 643, 213, 517, 598, 239, 483, - 0, 0, 651, 247, 504, 610, 599, 215, 594, 503, - 393, 326, 327, 214, 0, 457, 268, 293, 0, 0, - 258, 415, 970, 971, 256, 652, 815, 622, 220, 0, - 621, 408, 584, 595, 394, 383, 219, 593, 392, 382, - 334, 823, 824, 280, 307, 900, 899, 898, 306, 308, - 896, 897, 895, 207, 608, 626, 0, 208, 0, 499, - 611, 653, 452, 212, 234, 235, 237, 0, 279, 283, - 291, 294, 303, 304, 313, 367, 419, 446, 442, 451, - 0, 579, 602, 616, 628, 634, 635, 637, 638, 639, - 640, 641, 644, 642, 407, 311, 495, 333, 373, 0, - 0, 425, 472, 240, 606, 496, 906, 928, 917, 781, - 782, 907, 908, 932, 909, 784, 785, 929, 930, 778, - 779, 783, 931, 933, 654, 655, 656, 657, 658, 659, + 1023, 771, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 298, 0, 403, 258, 0, 454, 919, 0, + 0, 630, 0, 0, 917, 0, 0, 0, 0, 366, + 0, 331, 197, 225, 0, 0, 413, 462, 474, 0, + 0, 0, 970, 0, 472, 427, 605, 233, 285, 459, + 433, 470, 441, 288, 0, 0, 471, 373, 586, 451, + 602, 631, 632, 264, 407, 616, 523, 624, 649, 226, + 261, 421, 507, 608, 495, 398, 582, 583, 330, 494, + 296, 201, 370, 637, 224, 480, 372, 242, 231, 588, + 613, 300, 252, 290, 457, 644, 213, 518, 599, 239, + 484, 0, 0, 652, 247, 505, 611, 600, 215, 595, + 504, 394, 327, 328, 214, 0, 458, 269, 294, 0, + 0, 259, 416, 971, 972, 257, 653, 816, 623, 220, + 0, 622, 409, 585, 596, 395, 384, 219, 594, 393, + 383, 335, 824, 825, 281, 308, 901, 900, 899, 307, + 309, 897, 898, 896, 207, 609, 627, 0, 208, 0, + 500, 612, 654, 453, 212, 234, 235, 237, 0, 280, + 284, 292, 295, 304, 305, 314, 368, 420, 447, 443, + 452, 0, 580, 603, 617, 629, 635, 636, 638, 639, + 640, 641, 642, 645, 643, 408, 312, 496, 334, 374, + 0, 0, 426, 473, 240, 607, 497, 907, 929, 918, + 782, 783, 908, 909, 933, 910, 785, 786, 930, 931, + 779, 780, 784, 932, 934, 655, 656, 657, 658, 659, 660, 661, 662, 663, 664, 665, 666, 667, 668, 669, - 670, 671, 649, 508, 514, 509, 510, 511, 512, 513, - 0, 515, 920, 767, 766, 0, 773, 774, 0, 803, - 804, 806, 812, 813, 814, 825, 872, 873, 881, 883, - 884, 882, 885, 886, 887, 890, 891, 892, 893, 888, - 889, 894, 786, 790, 787, 788, 789, 801, 791, 792, - 793, 794, 795, 796, 797, 798, 799, 800, 802, 943, - 944, 945, 946, 947, 948, 818, 822, 821, 819, 820, - 816, 817, 844, 843, 845, 846, 847, 848, 849, 850, - 852, 851, 853, 854, 855, 856, 857, 858, 826, 827, - 830, 831, 829, 828, 832, 841, 842, 833, 834, 835, - 836, 837, 838, 840, 839, 859, 860, 861, 862, 863, - 865, 864, 868, 869, 867, 866, 871, 870, 765, 196, - 221, 368, 0, 454, 288, 650, 618, 484, 613, 206, - 223, 934, 262, 935, 0, 0, 939, 0, 0, 0, - 941, 940, 0, 942, 904, 903, 0, 0, 936, 937, - 0, 938, 0, 0, 198, 200, 209, 222, 232, 236, - 243, 261, 276, 278, 285, 298, 310, 318, 319, 322, - 328, 380, 386, 387, 388, 389, 409, 410, 411, 414, - 417, 418, 421, 423, 424, 427, 431, 435, 436, 437, - 439, 441, 443, 455, 460, 474, 475, 476, 477, 478, - 481, 482, 488, 489, 490, 491, 492, 500, 501, 516, - 586, 588, 605, 625, 632, 480, 949, 950, 951, 952, - 953, 954, 955, 956, 300, 600, 633, 597, 645, 627, - 438, 378, 0, 0, 381, 281, 305, 320, 0, 617, - 502, 227, 466, 290, 251, 974, 0, 211, 246, 230, - 259, 274, 277, 324, 391, 400, 429, 434, 296, 271, - 244, 459, 241, 485, 519, 520, 521, 523, 395, 266, - 433, 396, 0, 376, 576, 577, 316, 0, 0, 86, - 528, 0, 777, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 416, 0, 0, 0, 764, 0, 0, 0, - 270, 769, 0, 0, 0, 366, 267, 0, 0, 204, - 505, 0, 430, 0, 203, 0, 487, 252, 377, 374, - 583, 282, 273, 269, 250, 317, 385, 428, 518, 422, - 776, 370, 0, 0, 497, 401, 0, 0, 0, 0, - 0, 0, 0, 0, 771, 772, 0, 0, 0, 0, - 0, 0, 0, 0, 323, 248, 325, 202, 413, 498, - 286, 0, 95, 0, 0, 1023, 507, 959, 748, 925, - 963, 1024, 976, 977, 978, 964, 0, 238, 965, 966, - 245, 967, 0, 924, 807, 809, 808, 874, 875, 876, - 877, 878, 879, 880, 810, 811, 805, 972, 609, 979, - 980, 0, 265, 321, 272, 264, 580, 0, 0, 0, - 0, 0, 0, 596, 0, 0, 229, 0, 0, 0, - 0, 0, 0, 0, 744, 761, 0, 775, 0, 0, - 0, 275, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 758, 759, - 0, 0, 0, 0, 919, 0, 760, 0, 0, 768, - 981, 982, 983, 984, 985, 986, 987, 988, 989, 990, + 670, 671, 672, 650, 509, 515, 510, 511, 512, 513, + 514, 0, 516, 921, 768, 767, 0, 774, 775, 0, + 804, 805, 807, 813, 814, 815, 826, 873, 874, 882, + 884, 885, 883, 886, 887, 888, 891, 892, 893, 894, + 889, 890, 895, 787, 791, 788, 789, 790, 802, 792, + 793, 794, 795, 796, 797, 798, 799, 800, 801, 803, + 944, 945, 946, 947, 948, 949, 819, 823, 822, 820, + 821, 817, 818, 845, 844, 846, 847, 848, 849, 850, + 851, 853, 852, 854, 855, 856, 857, 858, 859, 827, + 828, 831, 832, 830, 829, 833, 842, 843, 834, 835, + 836, 837, 838, 839, 841, 840, 860, 861, 862, 863, + 864, 866, 865, 869, 870, 868, 867, 872, 871, 766, + 196, 221, 369, 0, 455, 289, 651, 619, 485, 614, + 206, 223, 935, 263, 936, 0, 0, 940, 0, 0, + 0, 942, 941, 0, 943, 905, 904, 0, 0, 937, + 938, 0, 939, 0, 0, 198, 200, 209, 222, 232, + 236, 243, 262, 277, 279, 286, 299, 311, 319, 320, + 323, 329, 381, 387, 388, 389, 390, 410, 411, 412, + 415, 418, 419, 422, 424, 425, 428, 432, 436, 437, + 438, 440, 442, 444, 456, 461, 475, 476, 477, 478, + 479, 482, 483, 489, 490, 491, 492, 493, 501, 502, + 517, 587, 589, 606, 626, 633, 481, 950, 951, 952, + 953, 954, 955, 956, 957, 301, 601, 634, 598, 646, + 628, 439, 379, 0, 0, 382, 282, 306, 321, 0, + 618, 503, 227, 467, 291, 251, 975, 0, 211, 246, + 230, 260, 275, 278, 325, 392, 401, 430, 435, 297, + 272, 244, 460, 241, 486, 520, 521, 522, 524, 396, + 267, 434, 397, 0, 377, 577, 578, 317, 0, 0, + 0, 529, 0, 778, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 417, 0, 0, 0, 765, 0, 0, + 0, 271, 770, 0, 0, 0, 367, 268, 0, 0, + 204, 506, 0, 431, 0, 203, 0, 488, 253, 378, + 375, 584, 283, 274, 270, 250, 318, 386, 429, 519, + 423, 777, 371, 0, 0, 498, 402, 0, 0, 0, + 0, 0, 0, 0, 0, 772, 773, 0, 0, 0, + 0, 0, 0, 0, 0, 324, 248, 326, 202, 414, + 499, 287, 0, 95, 0, 0, 1024, 508, 960, 749, + 926, 964, 1025, 977, 978, 979, 965, 0, 238, 966, + 967, 245, 968, 0, 925, 808, 810, 809, 875, 876, + 877, 878, 879, 880, 881, 811, 812, 806, 973, 610, + 980, 981, 0, 266, 322, 273, 265, 581, 0, 0, + 0, 0, 0, 0, 597, 0, 0, 229, 0, 0, + 0, 0, 0, 0, 0, 745, 762, 0, 776, 0, + 0, 0, 276, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 759, + 760, 1070, 0, 0, 0, 920, 0, 761, 0, 0, + 769, 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, 1008, 1009, 1010, 1011, 1012, 1013, 1014, 1015, 1016, 1017, 1018, 1019, 1020, - 1021, 1022, 770, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 297, 0, 402, 257, 0, 453, 918, - 0, 0, 629, 0, 0, 916, 0, 0, 0, 0, - 365, 0, 330, 197, 225, 0, 0, 412, 461, 473, - 0, 0, 0, 969, 0, 471, 426, 604, 233, 284, - 458, 432, 469, 440, 287, 0, 0, 470, 372, 585, - 450, 601, 630, 631, 263, 406, 615, 522, 623, 648, - 226, 260, 420, 506, 607, 494, 397, 581, 582, 329, - 493, 295, 201, 369, 636, 224, 479, 371, 242, 231, - 587, 612, 299, 289, 456, 643, 213, 517, 598, 239, - 483, 0, 0, 651, 247, 504, 610, 599, 215, 594, - 503, 393, 326, 327, 214, 0, 457, 268, 293, 0, - 0, 258, 415, 970, 971, 256, 652, 815, 622, 220, - 0, 621, 408, 584, 595, 394, 383, 219, 593, 392, - 382, 334, 823, 824, 280, 307, 900, 899, 898, 306, - 308, 896, 897, 895, 207, 608, 626, 0, 208, 0, - 499, 611, 653, 452, 212, 234, 235, 237, 0, 279, - 283, 291, 294, 303, 304, 313, 367, 419, 446, 442, - 451, 0, 579, 602, 616, 628, 634, 635, 637, 638, - 639, 640, 641, 644, 642, 407, 311, 495, 333, 373, - 0, 0, 425, 472, 240, 606, 496, 906, 928, 917, - 781, 782, 907, 908, 932, 909, 784, 785, 929, 930, - 778, 779, 783, 931, 933, 654, 655, 656, 657, 658, - 659, 660, 661, 662, 663, 664, 665, 666, 667, 668, - 669, 670, 671, 649, 508, 514, 509, 510, 511, 512, - 513, 0, 515, 920, 767, 766, 0, 773, 774, 0, - 803, 804, 806, 812, 813, 814, 825, 872, 873, 881, - 883, 884, 882, 885, 886, 887, 890, 891, 892, 893, - 888, 889, 894, 786, 790, 787, 788, 789, 801, 791, - 792, 793, 794, 795, 796, 797, 798, 799, 800, 802, - 943, 944, 945, 946, 947, 948, 818, 822, 821, 819, - 820, 816, 817, 844, 843, 845, 846, 847, 848, 849, - 850, 852, 851, 853, 854, 855, 856, 857, 858, 826, - 827, 830, 831, 829, 828, 832, 841, 842, 833, 834, - 835, 836, 837, 838, 840, 839, 859, 860, 861, 862, - 863, 865, 864, 868, 869, 867, 866, 871, 870, 765, - 196, 221, 368, 94, 454, 288, 650, 618, 484, 613, - 206, 223, 934, 262, 935, 0, 0, 939, 0, 0, - 0, 941, 940, 0, 942, 904, 903, 0, 0, 936, - 937, 0, 938, 0, 0, 198, 200, 209, 222, 232, - 236, 243, 261, 276, 278, 285, 298, 310, 318, 319, - 322, 328, 380, 386, 387, 388, 389, 409, 410, 411, - 414, 417, 418, 421, 423, 424, 427, 431, 435, 436, - 437, 439, 441, 443, 455, 460, 474, 475, 476, 477, - 478, 481, 482, 488, 489, 490, 491, 492, 500, 501, - 516, 586, 588, 605, 625, 632, 480, 949, 950, 951, - 952, 953, 954, 955, 956, 300, 600, 633, 597, 645, - 627, 438, 378, 0, 0, 381, 281, 305, 320, 0, - 617, 502, 227, 466, 290, 251, 974, 0, 211, 246, - 230, 259, 274, 277, 324, 391, 400, 429, 434, 296, - 271, 244, 459, 241, 485, 519, 520, 521, 523, 395, - 266, 433, 396, 0, 376, 576, 577, 316, 0, 0, - 0, 528, 0, 777, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 416, 0, 0, 0, 764, 0, 0, - 0, 270, 769, 0, 0, 0, 366, 267, 0, 0, - 204, 505, 0, 430, 0, 203, 0, 487, 252, 377, - 374, 583, 282, 273, 269, 250, 317, 385, 428, 518, - 422, 776, 370, 0, 0, 497, 401, 0, 0, 0, - 0, 0, 0, 0, 0, 771, 772, 0, 0, 0, - 0, 0, 0, 0, 0, 323, 248, 325, 202, 413, - 498, 286, 0, 95, 0, 0, 1023, 507, 959, 748, - 925, 963, 1024, 976, 977, 978, 964, 0, 238, 965, - 966, 245, 967, 0, 924, 807, 809, 808, 874, 875, - 876, 877, 878, 879, 880, 810, 811, 805, 972, 609, - 979, 980, 0, 265, 321, 272, 264, 580, 0, 0, - 0, 0, 0, 0, 596, 0, 0, 229, 0, 0, - 0, 0, 0, 0, 0, 744, 761, 0, 775, 0, - 0, 0, 275, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 758, - 759, 0, 0, 0, 0, 919, 0, 760, 0, 0, - 768, 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, 1008, 1009, - 1010, 1011, 1012, 1013, 1014, 1015, 1016, 1017, 1018, 1019, - 1020, 1021, 1022, 770, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 297, 0, 402, 257, 0, 453, - 918, 0, 0, 629, 0, 0, 916, 0, 0, 0, - 0, 365, 0, 330, 197, 225, 0, 0, 412, 461, - 473, 0, 0, 0, 969, 0, 471, 426, 604, 233, - 284, 458, 432, 469, 440, 287, 4095, 0, 470, 372, - 585, 450, 601, 630, 631, 263, 406, 615, 522, 623, - 648, 226, 260, 420, 506, 607, 494, 397, 581, 582, - 329, 493, 295, 201, 369, 636, 224, 479, 371, 242, - 231, 587, 612, 299, 289, 456, 643, 213, 517, 598, - 239, 483, 0, 0, 651, 247, 504, 610, 599, 215, - 594, 503, 393, 326, 327, 214, 0, 457, 268, 293, - 0, 0, 258, 415, 970, 971, 256, 652, 815, 622, - 220, 0, 621, 408, 584, 595, 394, 383, 219, 593, - 392, 382, 334, 823, 824, 280, 307, 900, 899, 898, - 306, 308, 896, 897, 895, 207, 608, 626, 0, 208, - 0, 499, 611, 653, 452, 212, 234, 235, 237, 0, - 279, 283, 291, 294, 303, 304, 313, 367, 419, 446, - 442, 451, 0, 579, 602, 616, 628, 634, 635, 637, - 638, 639, 640, 641, 644, 642, 407, 311, 495, 333, - 373, 0, 0, 425, 472, 240, 606, 496, 906, 928, - 917, 781, 782, 907, 908, 932, 909, 784, 785, 929, - 930, 778, 779, 783, 931, 933, 654, 655, 656, 657, + 1021, 1022, 1023, 771, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 298, 0, 403, 258, 0, 454, + 919, 0, 0, 630, 0, 0, 917, 0, 0, 0, + 0, 366, 0, 331, 197, 225, 0, 0, 413, 462, + 474, 0, 0, 0, 970, 0, 472, 427, 605, 233, + 285, 459, 433, 470, 441, 288, 0, 0, 471, 373, + 586, 451, 602, 631, 632, 264, 407, 616, 523, 624, + 649, 226, 261, 421, 507, 608, 495, 398, 582, 583, + 330, 494, 296, 201, 370, 637, 224, 480, 372, 242, + 231, 588, 613, 300, 252, 290, 457, 644, 213, 518, + 599, 239, 484, 0, 0, 652, 247, 505, 611, 600, + 215, 595, 504, 394, 327, 328, 214, 0, 458, 269, + 294, 0, 0, 259, 416, 971, 972, 257, 653, 816, + 623, 220, 0, 622, 409, 585, 596, 395, 384, 219, + 594, 393, 383, 335, 824, 825, 281, 308, 901, 900, + 899, 307, 309, 897, 898, 896, 207, 609, 627, 0, + 208, 0, 500, 612, 654, 453, 212, 234, 235, 237, + 0, 280, 284, 292, 295, 304, 305, 314, 368, 420, + 447, 443, 452, 0, 580, 603, 617, 629, 635, 636, + 638, 639, 640, 641, 642, 645, 643, 408, 312, 496, + 334, 374, 0, 0, 426, 473, 240, 607, 497, 907, + 929, 918, 782, 783, 908, 909, 933, 910, 785, 786, + 930, 931, 779, 780, 784, 932, 934, 655, 656, 657, 658, 659, 660, 661, 662, 663, 664, 665, 666, 667, - 668, 669, 670, 671, 649, 508, 514, 509, 510, 511, - 512, 513, 0, 515, 920, 767, 766, 0, 773, 774, - 0, 803, 804, 806, 812, 813, 814, 825, 872, 873, - 881, 883, 884, 882, 885, 886, 887, 890, 891, 892, - 893, 888, 889, 894, 786, 790, 787, 788, 789, 801, - 791, 792, 793, 794, 795, 796, 797, 798, 799, 800, - 802, 943, 944, 945, 946, 947, 948, 818, 822, 821, - 819, 820, 816, 817, 844, 843, 845, 846, 847, 848, - 849, 850, 852, 851, 853, 854, 855, 856, 857, 858, - 826, 827, 830, 831, 829, 828, 832, 841, 842, 833, - 834, 835, 836, 837, 838, 840, 839, 859, 860, 861, - 862, 863, 865, 864, 868, 869, 867, 866, 871, 870, - 765, 196, 221, 368, 0, 454, 288, 650, 618, 484, - 613, 206, 223, 934, 262, 935, 0, 0, 939, 0, - 0, 0, 941, 940, 0, 942, 904, 903, 0, 0, - 936, 937, 0, 938, 0, 0, 198, 200, 209, 222, - 232, 236, 243, 261, 276, 278, 285, 298, 310, 318, - 319, 322, 328, 380, 386, 387, 388, 389, 409, 410, - 411, 414, 417, 418, 421, 423, 424, 427, 431, 435, - 436, 437, 439, 441, 443, 455, 460, 474, 475, 476, - 477, 478, 481, 482, 488, 489, 490, 491, 492, 500, - 501, 516, 586, 588, 605, 625, 632, 480, 949, 950, - 951, 952, 953, 954, 955, 956, 300, 600, 633, 597, - 645, 627, 438, 378, 0, 0, 381, 281, 305, 320, - 0, 617, 502, 227, 466, 290, 251, 974, 0, 211, - 246, 230, 259, 274, 277, 324, 391, 400, 429, 434, - 296, 271, 244, 459, 241, 485, 519, 520, 521, 523, - 395, 266, 433, 396, 0, 376, 576, 577, 316, 0, - 0, 0, 528, 0, 777, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 416, 0, 0, 0, 764, 0, - 0, 0, 270, 769, 0, 0, 0, 366, 267, 0, - 0, 204, 505, 0, 430, 0, 203, 0, 487, 252, - 377, 374, 583, 282, 273, 269, 250, 317, 385, 428, - 518, 422, 776, 370, 0, 0, 497, 401, 0, 0, - 0, 0, 0, 0, 0, 0, 771, 772, 0, 0, - 0, 0, 0, 0, 0, 0, 323, 248, 325, 202, - 413, 498, 286, 0, 95, 0, 1753, 1023, 507, 959, - 748, 925, 963, 1024, 976, 977, 978, 964, 0, 238, - 965, 966, 245, 967, 0, 924, 807, 809, 808, 874, - 875, 876, 877, 878, 879, 880, 810, 811, 805, 972, - 609, 979, 980, 0, 265, 321, 272, 264, 580, 0, - 0, 0, 0, 0, 0, 596, 0, 0, 229, 0, - 0, 0, 0, 0, 0, 0, 744, 761, 0, 775, - 0, 0, 0, 275, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 758, 759, 0, 0, 0, 0, 919, 0, 760, 0, - 0, 768, 981, 982, 983, 984, 985, 986, 987, 988, + 668, 669, 670, 671, 672, 650, 509, 515, 510, 511, + 512, 513, 514, 0, 516, 921, 768, 767, 0, 774, + 775, 0, 804, 805, 807, 813, 814, 815, 826, 873, + 874, 882, 884, 885, 883, 886, 887, 888, 891, 892, + 893, 894, 889, 890, 895, 787, 791, 788, 789, 790, + 802, 792, 793, 794, 795, 796, 797, 798, 799, 800, + 801, 803, 944, 945, 946, 947, 948, 949, 819, 823, + 822, 820, 821, 817, 818, 845, 844, 846, 847, 848, + 849, 850, 851, 853, 852, 854, 855, 856, 857, 858, + 859, 827, 828, 831, 832, 830, 829, 833, 842, 843, + 834, 835, 836, 837, 838, 839, 841, 840, 860, 861, + 862, 863, 864, 866, 865, 869, 870, 868, 867, 872, + 871, 766, 196, 221, 369, 0, 455, 289, 651, 619, + 485, 614, 206, 223, 935, 263, 936, 0, 0, 940, + 0, 0, 0, 942, 941, 0, 943, 905, 904, 0, + 0, 937, 938, 0, 939, 0, 0, 198, 200, 209, + 222, 232, 236, 243, 262, 277, 279, 286, 299, 311, + 319, 320, 323, 329, 381, 387, 388, 389, 390, 410, + 411, 412, 415, 418, 419, 422, 424, 425, 428, 432, + 436, 437, 438, 440, 442, 444, 456, 461, 475, 476, + 477, 478, 479, 482, 483, 489, 490, 491, 492, 493, + 501, 502, 517, 587, 589, 606, 626, 633, 481, 950, + 951, 952, 953, 954, 955, 956, 957, 301, 601, 634, + 598, 646, 628, 439, 379, 0, 0, 382, 282, 306, + 321, 0, 618, 503, 227, 467, 291, 251, 975, 0, + 211, 246, 230, 260, 275, 278, 325, 392, 401, 430, + 435, 297, 272, 244, 460, 241, 486, 520, 521, 522, + 524, 396, 267, 434, 397, 0, 377, 577, 578, 317, + 0, 0, 0, 529, 0, 778, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 417, 0, 0, 0, 765, + 0, 0, 0, 271, 770, 0, 0, 0, 367, 268, + 0, 0, 204, 506, 0, 431, 0, 203, 0, 488, + 253, 378, 375, 584, 283, 274, 270, 250, 318, 386, + 429, 519, 423, 777, 371, 0, 0, 498, 402, 0, + 0, 0, 0, 0, 0, 0, 0, 772, 773, 0, + 0, 0, 0, 0, 0, 0, 0, 324, 248, 326, + 202, 414, 499, 287, 0, 95, 0, 0, 1024, 508, + 960, 749, 926, 964, 1025, 977, 978, 979, 965, 0, + 238, 966, 967, 245, 968, 0, 925, 808, 810, 809, + 875, 876, 877, 878, 879, 880, 881, 811, 812, 806, + 973, 610, 980, 981, 0, 266, 322, 273, 265, 581, + 0, 0, 0, 0, 0, 0, 597, 0, 0, 229, + 0, 0, 0, 0, 0, 0, 0, 745, 762, 0, + 776, 0, 0, 0, 276, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 759, 760, 0, 0, 0, 0, 920, 0, 761, + 0, 0, 769, 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, 1008, 1009, 1010, 1011, 1012, 1013, 1014, 1015, 1016, 1017, 1018, - 1019, 1020, 1021, 1022, 770, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 297, 0, 402, 257, 0, - 453, 918, 0, 0, 629, 0, 0, 916, 0, 0, - 0, 0, 365, 0, 330, 197, 225, 0, 0, 412, - 461, 473, 0, 0, 0, 969, 0, 471, 426, 604, - 233, 284, 458, 432, 469, 440, 287, 0, 0, 470, - 372, 585, 450, 601, 630, 631, 263, 406, 615, 522, - 623, 648, 226, 260, 420, 506, 607, 494, 397, 581, - 582, 329, 493, 295, 201, 369, 636, 224, 479, 371, - 242, 231, 587, 612, 299, 289, 456, 643, 213, 517, - 598, 239, 483, 0, 0, 651, 247, 504, 610, 599, - 215, 594, 503, 393, 326, 327, 214, 0, 457, 268, - 293, 0, 0, 258, 415, 970, 971, 256, 652, 815, - 622, 220, 0, 621, 408, 584, 595, 394, 383, 219, - 593, 392, 382, 334, 823, 824, 280, 307, 900, 899, - 898, 306, 308, 896, 897, 895, 207, 608, 626, 0, - 208, 0, 499, 611, 653, 452, 212, 234, 235, 237, - 0, 279, 283, 291, 294, 303, 304, 313, 367, 419, - 446, 442, 451, 0, 579, 602, 616, 628, 634, 635, - 637, 638, 639, 640, 641, 644, 642, 407, 311, 495, - 333, 373, 0, 0, 425, 472, 240, 606, 496, 906, - 928, 917, 781, 782, 907, 908, 932, 909, 784, 785, - 929, 930, 778, 779, 783, 931, 933, 654, 655, 656, - 657, 658, 659, 660, 661, 662, 663, 664, 665, 666, - 667, 668, 669, 670, 671, 649, 508, 514, 509, 510, - 511, 512, 513, 0, 515, 920, 767, 766, 0, 773, - 774, 0, 803, 804, 806, 812, 813, 814, 825, 872, - 873, 881, 883, 884, 882, 885, 886, 887, 890, 891, - 892, 893, 888, 889, 894, 786, 790, 787, 788, 789, - 801, 791, 792, 793, 794, 795, 796, 797, 798, 799, - 800, 802, 943, 944, 945, 946, 947, 948, 818, 822, - 821, 819, 820, 816, 817, 844, 843, 845, 846, 847, - 848, 849, 850, 852, 851, 853, 854, 855, 856, 857, - 858, 826, 827, 830, 831, 829, 828, 832, 841, 842, - 833, 834, 835, 836, 837, 838, 840, 839, 859, 860, - 861, 862, 863, 865, 864, 868, 869, 867, 866, 871, - 870, 765, 196, 221, 368, 0, 454, 288, 650, 618, - 484, 613, 206, 223, 934, 262, 935, 0, 0, 939, - 0, 0, 0, 941, 940, 0, 942, 904, 903, 0, - 0, 936, 937, 0, 938, 0, 0, 198, 200, 209, - 222, 232, 236, 243, 261, 276, 278, 285, 298, 310, - 318, 319, 322, 328, 380, 386, 387, 388, 389, 409, - 410, 411, 414, 417, 418, 421, 423, 424, 427, 431, - 435, 436, 437, 439, 441, 443, 455, 460, 474, 475, - 476, 477, 478, 481, 482, 488, 489, 490, 491, 492, - 500, 501, 516, 586, 588, 605, 625, 632, 480, 949, - 950, 951, 952, 953, 954, 955, 956, 300, 600, 633, - 597, 645, 627, 438, 378, 0, 0, 381, 281, 305, - 320, 0, 617, 502, 227, 466, 290, 251, 974, 0, - 211, 246, 230, 259, 274, 277, 324, 391, 400, 429, - 434, 296, 271, 244, 459, 241, 485, 519, 520, 521, - 523, 395, 266, 433, 396, 0, 376, 576, 577, 316, - 0, 0, 0, 528, 0, 777, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 416, 0, 0, 0, 764, - 0, 0, 0, 270, 769, 0, 0, 0, 366, 267, - 0, 0, 204, 505, 0, 430, 0, 203, 0, 487, - 252, 377, 374, 583, 282, 273, 269, 250, 317, 385, - 428, 518, 422, 776, 370, 0, 0, 497, 401, 0, - 0, 0, 0, 0, 0, 0, 0, 771, 772, 0, - 0, 0, 0, 0, 0, 0, 0, 323, 248, 325, - 202, 413, 498, 286, 0, 95, 0, 0, 1023, 507, - 959, 748, 925, 963, 1024, 976, 977, 978, 964, 0, - 238, 965, 966, 245, 967, 0, 924, 807, 809, 808, - 874, 875, 876, 877, 878, 879, 880, 810, 811, 805, - 972, 609, 979, 980, 0, 265, 321, 272, 264, 580, - 0, 0, 0, 0, 0, 0, 596, 0, 0, 229, - 0, 0, 0, 0, 0, 0, 0, 744, 761, 0, - 775, 0, 0, 0, 275, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 758, 759, 1069, 0, 0, 0, 919, 0, 760, - 0, 0, 768, 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, - 1008, 1009, 1010, 1011, 1012, 1013, 1014, 1015, 1016, 1017, - 1018, 1019, 1020, 1021, 1022, 770, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 297, 0, 402, 257, - 0, 453, 918, 0, 0, 629, 0, 0, 916, 0, - 0, 0, 0, 365, 0, 330, 197, 225, 0, 0, - 412, 461, 473, 0, 0, 0, 969, 0, 471, 426, - 604, 233, 284, 458, 432, 469, 440, 287, 0, 0, - 470, 372, 585, 450, 601, 630, 631, 263, 406, 615, - 522, 623, 648, 226, 260, 420, 506, 607, 494, 397, - 581, 582, 329, 493, 295, 201, 369, 636, 224, 479, - 371, 242, 231, 587, 612, 299, 289, 456, 643, 213, - 517, 598, 239, 483, 0, 0, 651, 247, 504, 610, - 599, 215, 594, 503, 393, 326, 327, 214, 0, 457, - 268, 293, 0, 0, 258, 415, 970, 971, 256, 652, - 815, 622, 220, 0, 621, 408, 584, 595, 394, 383, - 219, 593, 392, 382, 334, 823, 824, 280, 307, 900, - 899, 898, 306, 308, 896, 897, 895, 207, 608, 626, - 0, 208, 0, 499, 611, 653, 452, 212, 234, 235, - 237, 0, 279, 283, 291, 294, 303, 304, 313, 367, - 419, 446, 442, 451, 0, 579, 602, 616, 628, 634, - 635, 637, 638, 639, 640, 641, 644, 642, 407, 311, - 495, 333, 373, 0, 0, 425, 472, 240, 606, 496, - 906, 928, 917, 781, 782, 907, 908, 932, 909, 784, - 785, 929, 930, 778, 779, 783, 931, 933, 654, 655, + 1019, 1020, 1021, 1022, 1023, 771, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 298, 0, 403, 258, + 0, 454, 919, 0, 0, 630, 0, 0, 917, 0, + 0, 0, 0, 366, 0, 331, 197, 225, 0, 0, + 413, 462, 474, 0, 0, 0, 970, 0, 472, 427, + 605, 233, 285, 459, 433, 470, 441, 288, 0, 0, + 471, 373, 586, 451, 602, 631, 632, 264, 407, 616, + 523, 624, 649, 226, 261, 421, 507, 608, 495, 398, + 582, 583, 330, 494, 296, 201, 370, 637, 224, 480, + 372, 242, 231, 588, 613, 300, 252, 290, 457, 644, + 213, 518, 599, 239, 484, 0, 0, 652, 247, 505, + 611, 600, 215, 595, 504, 394, 327, 328, 214, 0, + 458, 269, 294, 0, 0, 259, 416, 971, 972, 257, + 653, 816, 623, 220, 0, 622, 409, 585, 596, 395, + 384, 219, 594, 393, 383, 335, 824, 825, 281, 308, + 901, 900, 899, 307, 309, 897, 898, 896, 207, 609, + 627, 0, 208, 0, 500, 612, 654, 453, 212, 234, + 235, 237, 0, 280, 284, 292, 295, 304, 305, 314, + 368, 420, 447, 443, 452, 0, 580, 603, 617, 629, + 635, 636, 638, 639, 640, 641, 642, 645, 643, 408, + 312, 496, 334, 374, 0, 0, 426, 473, 240, 607, + 497, 907, 929, 918, 782, 783, 908, 909, 933, 910, + 785, 786, 930, 931, 779, 780, 784, 932, 934, 655, 656, 657, 658, 659, 660, 661, 662, 663, 664, 665, - 666, 667, 668, 669, 670, 671, 649, 508, 514, 509, - 510, 511, 512, 513, 0, 515, 920, 767, 766, 0, - 773, 774, 0, 803, 804, 806, 812, 813, 814, 825, - 872, 873, 881, 883, 884, 882, 885, 886, 887, 890, - 891, 892, 893, 888, 889, 894, 786, 790, 787, 788, - 789, 801, 791, 792, 793, 794, 795, 796, 797, 798, - 799, 800, 802, 943, 944, 945, 946, 947, 948, 818, - 822, 821, 819, 820, 816, 817, 844, 843, 845, 846, - 847, 848, 849, 850, 852, 851, 853, 854, 855, 856, - 857, 858, 826, 827, 830, 831, 829, 828, 832, 841, - 842, 833, 834, 835, 836, 837, 838, 840, 839, 859, - 860, 861, 862, 863, 865, 864, 868, 869, 867, 866, - 871, 870, 765, 196, 221, 368, 0, 454, 288, 650, - 618, 484, 613, 206, 223, 934, 262, 935, 0, 0, - 939, 0, 0, 0, 941, 940, 0, 942, 904, 903, - 0, 0, 936, 937, 0, 938, 0, 0, 198, 200, - 209, 222, 232, 236, 243, 261, 276, 278, 285, 298, - 310, 318, 319, 322, 328, 380, 386, 387, 388, 389, - 409, 410, 411, 414, 417, 418, 421, 423, 424, 427, - 431, 435, 436, 437, 439, 441, 443, 455, 460, 474, - 475, 476, 477, 478, 481, 482, 488, 489, 490, 491, - 492, 500, 501, 516, 586, 588, 605, 625, 632, 480, - 949, 950, 951, 952, 953, 954, 955, 956, 300, 600, - 633, 597, 645, 627, 438, 378, 0, 0, 381, 281, - 305, 320, 0, 617, 502, 227, 466, 290, 251, 974, - 0, 211, 246, 230, 259, 274, 277, 324, 391, 400, - 429, 434, 296, 271, 244, 459, 241, 485, 519, 520, - 521, 523, 395, 266, 433, 396, 0, 376, 576, 577, - 316, 0, 0, 0, 528, 0, 777, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 416, 0, 0, 0, - 764, 0, 0, 0, 270, 769, 0, 0, 0, 366, - 267, 0, 0, 204, 505, 0, 430, 0, 203, 0, - 487, 252, 377, 374, 583, 282, 273, 269, 250, 317, - 385, 428, 518, 422, 776, 370, 0, 0, 497, 401, - 0, 0, 0, 0, 0, 0, 0, 0, 771, 772, - 0, 0, 0, 0, 0, 0, 0, 0, 323, 248, - 325, 202, 413, 498, 286, 0, 95, 0, 0, 1023, - 507, 959, 748, 925, 963, 1024, 976, 977, 978, 964, - 0, 238, 965, 966, 245, 967, 0, 924, 807, 809, - 808, 874, 875, 876, 877, 878, 879, 880, 810, 811, - 805, 972, 609, 979, 980, 0, 265, 321, 272, 264, - 580, 0, 0, 0, 0, 0, 0, 596, 0, 0, - 229, 0, 0, 0, 0, 0, 0, 0, 744, 761, - 0, 775, 0, 0, 0, 275, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 758, 759, 0, 0, 0, 0, 919, 0, - 760, 0, 0, 768, 981, 982, 983, 984, 985, 986, + 666, 667, 668, 669, 670, 671, 672, 650, 509, 515, + 510, 511, 512, 513, 514, 0, 516, 921, 768, 767, + 0, 774, 775, 0, 804, 805, 807, 813, 814, 815, + 826, 873, 874, 882, 884, 885, 883, 886, 887, 888, + 891, 892, 893, 894, 889, 890, 895, 787, 791, 788, + 789, 790, 802, 792, 793, 794, 795, 796, 797, 798, + 799, 800, 801, 803, 944, 945, 946, 947, 948, 949, + 819, 823, 822, 820, 821, 817, 818, 845, 844, 846, + 847, 848, 849, 850, 851, 853, 852, 854, 855, 856, + 857, 858, 859, 827, 828, 831, 832, 830, 829, 833, + 842, 843, 834, 835, 836, 837, 838, 839, 841, 840, + 860, 861, 862, 863, 864, 866, 865, 869, 870, 868, + 867, 872, 871, 766, 196, 221, 369, 0, 455, 289, + 651, 619, 485, 614, 206, 223, 935, 263, 936, 0, + 0, 940, 0, 0, 0, 942, 941, 0, 943, 905, + 904, 0, 0, 937, 938, 0, 939, 0, 0, 198, + 200, 209, 222, 232, 236, 243, 262, 277, 279, 286, + 299, 311, 319, 320, 323, 329, 381, 387, 388, 389, + 390, 410, 411, 412, 415, 418, 419, 422, 424, 425, + 428, 432, 436, 437, 438, 440, 442, 444, 456, 461, + 475, 476, 477, 478, 479, 482, 483, 489, 490, 491, + 492, 493, 501, 502, 517, 587, 589, 606, 626, 633, + 481, 950, 951, 952, 953, 954, 955, 956, 957, 301, + 601, 634, 598, 646, 628, 439, 379, 0, 0, 382, + 282, 306, 321, 0, 618, 503, 227, 467, 291, 251, + 975, 0, 211, 246, 230, 260, 275, 278, 325, 392, + 401, 430, 435, 297, 272, 244, 460, 241, 486, 520, + 521, 522, 524, 396, 267, 434, 397, 0, 377, 577, + 578, 317, 0, 0, 0, 529, 0, 778, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 417, 0, 0, + 0, 765, 0, 0, 0, 271, 770, 0, 0, 0, + 367, 268, 0, 0, 204, 506, 0, 431, 0, 203, + 0, 488, 253, 378, 375, 584, 283, 274, 270, 250, + 318, 386, 429, 519, 423, 777, 371, 0, 0, 498, + 402, 0, 0, 0, 0, 0, 0, 0, 0, 772, + 773, 0, 0, 0, 0, 0, 0, 0, 0, 324, + 248, 326, 202, 414, 499, 287, 0, 95, 0, 0, + 1024, 508, 960, 749, 926, 964, 1025, 977, 978, 979, + 965, 0, 238, 966, 967, 245, 968, 0, 925, 808, + 810, 809, 875, 876, 877, 878, 879, 880, 881, 811, + 812, 806, 973, 610, 980, 981, 0, 266, 322, 273, + 265, 581, 0, 0, 0, 0, 0, 0, 597, 0, + 0, 229, 0, 0, 0, 0, 0, 0, 0, 745, + 762, 0, 776, 0, 0, 0, 276, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 759, 760, 0, 0, 0, 0, 920, + 0, 761, 0, 0, 769, 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, 1008, 1009, 1010, 1011, 1012, 1013, 1014, 1015, 1016, - 1017, 1018, 1019, 1020, 1021, 1022, 770, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 297, 0, 402, - 257, 0, 453, 918, 0, 0, 629, 0, 0, 916, - 0, 0, 0, 0, 365, 0, 330, 197, 225, 0, - 0, 412, 461, 473, 0, 0, 0, 969, 0, 471, - 426, 604, 233, 284, 458, 432, 469, 440, 287, 0, - 0, 470, 372, 585, 450, 601, 630, 631, 263, 406, - 615, 522, 623, 648, 226, 260, 420, 506, 607, 494, - 397, 581, 582, 329, 493, 295, 201, 369, 636, 224, - 479, 371, 242, 231, 587, 612, 299, 289, 456, 643, - 213, 517, 598, 239, 483, 0, 0, 651, 247, 504, - 610, 599, 215, 594, 503, 393, 326, 327, 214, 0, - 457, 268, 293, 0, 0, 258, 415, 970, 971, 256, - 652, 815, 622, 220, 0, 621, 408, 584, 595, 394, - 383, 219, 593, 392, 382, 334, 823, 824, 280, 307, - 900, 899, 898, 306, 308, 896, 897, 895, 207, 608, - 626, 0, 208, 0, 499, 611, 653, 452, 212, 234, - 235, 237, 0, 279, 283, 291, 294, 303, 304, 313, - 367, 419, 446, 442, 451, 0, 579, 602, 616, 628, - 634, 635, 637, 638, 639, 640, 641, 644, 642, 407, - 311, 495, 333, 373, 0, 0, 425, 472, 240, 606, - 496, 906, 928, 917, 781, 782, 907, 908, 932, 909, - 784, 785, 929, 930, 778, 779, 783, 931, 933, 654, - 655, 656, 657, 658, 659, 660, 661, 662, 663, 664, - 665, 666, 667, 668, 669, 670, 671, 649, 508, 514, - 509, 510, 511, 512, 513, 0, 515, 920, 767, 766, - 0, 773, 774, 0, 803, 804, 806, 812, 813, 814, - 825, 872, 873, 881, 883, 884, 882, 885, 886, 887, - 890, 891, 892, 893, 888, 889, 894, 786, 790, 787, - 788, 789, 801, 791, 792, 793, 794, 795, 796, 797, - 798, 799, 800, 802, 943, 944, 945, 946, 947, 948, - 818, 822, 821, 819, 820, 816, 817, 844, 843, 845, - 846, 847, 848, 849, 850, 852, 851, 853, 854, 855, - 856, 857, 858, 826, 827, 830, 831, 829, 828, 832, - 841, 842, 833, 834, 835, 836, 837, 838, 840, 839, - 859, 860, 861, 862, 863, 865, 864, 868, 869, 867, - 866, 871, 870, 765, 196, 221, 368, 0, 454, 288, - 650, 618, 484, 613, 206, 223, 934, 262, 935, 0, - 0, 939, 0, 0, 0, 941, 940, 0, 942, 904, - 903, 0, 0, 936, 937, 0, 938, 0, 0, 198, - 200, 209, 222, 232, 236, 243, 261, 276, 278, 285, - 298, 310, 318, 319, 322, 328, 380, 386, 387, 388, - 389, 409, 410, 411, 414, 417, 418, 421, 423, 424, - 427, 431, 435, 436, 437, 439, 441, 443, 455, 460, - 474, 475, 476, 477, 478, 481, 482, 488, 489, 490, - 491, 492, 500, 501, 516, 586, 588, 605, 625, 632, - 480, 949, 950, 951, 952, 953, 954, 955, 956, 300, - 600, 633, 597, 645, 627, 438, 378, 0, 0, 381, - 281, 305, 320, 0, 617, 502, 227, 466, 290, 251, - 974, 0, 211, 246, 230, 259, 274, 277, 324, 391, - 400, 429, 434, 296, 271, 244, 459, 241, 485, 519, - 520, 521, 523, 395, 266, 433, 396, 0, 376, 576, - 577, 316, 0, 0, 0, 528, 0, 777, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 416, 0, 0, - 0, 764, 0, 0, 0, 270, 769, 0, 0, 0, - 366, 267, 0, 0, 204, 505, 0, 430, 0, 203, - 0, 487, 252, 377, 374, 583, 282, 273, 269, 250, - 317, 385, 428, 518, 422, 776, 370, 0, 0, 497, - 401, 0, 0, 0, 0, 0, 0, 0, 0, 771, - 772, 0, 0, 0, 0, 0, 0, 0, 0, 323, - 248, 325, 202, 413, 498, 286, 0, 95, 0, 0, - 1023, 507, 959, 748, 925, 963, 1024, 976, 977, 978, - 964, 0, 238, 965, 966, 245, 967, 0, 924, 807, - 809, 808, 874, 875, 876, 877, 878, 879, 880, 810, - 811, 805, 972, 609, 979, 980, 0, 265, 321, 272, - 264, 580, 0, 0, 0, 0, 0, 0, 596, 0, - 0, 229, 0, 0, 0, 0, 0, 0, 0, 744, - 761, 0, 775, 0, 0, 0, 275, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 758, 759, 0, 0, 0, 0, 919, - 0, 760, 0, 0, 768, 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, 1008, 1009, 1010, 1011, 1012, 1013, 1014, 1015, - 1016, 1017, 1018, 1019, 1020, 1021, 1022, 3171, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 297, 0, - 402, 257, 0, 453, 918, 0, 0, 629, 0, 0, - 916, 0, 0, 0, 0, 365, 0, 330, 197, 225, - 0, 0, 412, 461, 473, 0, 0, 0, 969, 0, - 471, 426, 604, 233, 284, 458, 432, 469, 440, 287, - 0, 0, 470, 372, 585, 450, 601, 630, 631, 263, - 406, 615, 522, 623, 648, 226, 260, 420, 506, 607, - 494, 397, 581, 582, 329, 493, 295, 201, 369, 636, - 224, 479, 371, 242, 231, 587, 612, 299, 289, 456, - 643, 213, 517, 598, 239, 483, 0, 0, 651, 247, - 504, 610, 599, 215, 594, 503, 393, 326, 327, 214, - 0, 457, 268, 293, 0, 0, 258, 415, 970, 971, - 256, 652, 815, 622, 220, 0, 621, 408, 584, 595, - 394, 383, 219, 593, 392, 382, 334, 823, 824, 280, - 307, 900, 899, 898, 306, 308, 896, 897, 895, 207, - 608, 626, 0, 208, 0, 499, 611, 653, 452, 212, - 234, 235, 237, 0, 279, 283, 291, 294, 303, 304, - 313, 367, 419, 446, 442, 451, 0, 579, 602, 616, - 628, 634, 635, 637, 638, 639, 640, 641, 644, 642, - 407, 311, 495, 333, 373, 0, 0, 425, 472, 240, - 606, 496, 906, 928, 917, 781, 782, 907, 908, 932, - 909, 784, 785, 929, 930, 778, 779, 783, 931, 933, - 654, 655, 656, 657, 658, 659, 660, 661, 662, 663, - 664, 665, 666, 667, 668, 669, 670, 671, 649, 508, - 514, 509, 510, 511, 512, 513, 0, 515, 920, 767, - 766, 0, 773, 774, 0, 803, 804, 806, 812, 813, - 814, 825, 872, 873, 881, 883, 884, 882, 885, 886, - 887, 890, 891, 892, 893, 888, 889, 894, 786, 790, - 787, 788, 789, 801, 791, 792, 793, 794, 795, 796, - 797, 798, 799, 800, 802, 943, 944, 945, 946, 947, - 948, 818, 822, 821, 819, 820, 816, 817, 844, 843, - 845, 846, 847, 848, 849, 850, 852, 851, 853, 854, - 855, 856, 857, 858, 826, 827, 830, 831, 829, 828, - 832, 841, 842, 833, 834, 835, 836, 837, 838, 840, - 839, 859, 860, 861, 862, 863, 865, 864, 868, 869, - 867, 866, 871, 870, 765, 196, 221, 368, 0, 454, - 288, 650, 618, 484, 613, 206, 223, 934, 262, 935, - 0, 0, 939, 0, 0, 0, 941, 940, 0, 942, - 904, 903, 0, 0, 936, 937, 0, 938, 0, 0, - 198, 200, 209, 222, 232, 236, 243, 261, 276, 278, - 285, 298, 310, 318, 319, 322, 328, 380, 386, 387, - 388, 389, 409, 410, 411, 414, 417, 418, 421, 423, - 424, 427, 431, 435, 436, 437, 439, 441, 443, 455, - 460, 474, 475, 476, 477, 478, 481, 482, 488, 489, - 490, 491, 492, 500, 501, 516, 586, 588, 605, 625, - 632, 480, 949, 950, 951, 952, 953, 954, 955, 956, - 300, 600, 633, 597, 645, 627, 438, 378, 0, 0, - 381, 281, 305, 320, 0, 617, 502, 227, 466, 290, - 251, 974, 0, 211, 246, 230, 259, 274, 277, 324, - 391, 400, 429, 434, 296, 271, 244, 459, 241, 485, - 519, 520, 521, 523, 395, 266, 433, 396, 0, 376, - 576, 577, 316, 0, 0, 0, 528, 0, 777, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 416, 0, - 0, 0, 764, 0, 0, 0, 270, 769, 0, 0, - 0, 366, 267, 0, 0, 204, 505, 0, 430, 0, - 203, 0, 487, 252, 377, 374, 583, 282, 273, 269, - 250, 317, 385, 428, 518, 422, 776, 370, 0, 0, - 497, 401, 0, 0, 0, 0, 0, 0, 0, 0, - 771, 772, 0, 0, 0, 0, 0, 0, 0, 0, - 323, 248, 325, 202, 413, 498, 286, 0, 95, 0, - 0, 1023, 507, 959, 748, 925, 963, 1024, 976, 977, - 978, 964, 0, 238, 965, 966, 245, 967, 0, 924, - 807, 809, 808, 874, 875, 876, 877, 878, 879, 880, - 810, 811, 805, 972, 609, 979, 980, 0, 265, 321, - 272, 264, 580, 0, 0, 0, 0, 0, 0, 596, - 0, 0, 229, 0, 0, 0, 0, 0, 0, 0, - 744, 761, 0, 775, 0, 0, 0, 275, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 758, 759, 0, 0, 0, 0, - 919, 0, 760, 0, 0, 768, 981, 982, 983, 984, + 1017, 1018, 1019, 1020, 1021, 1022, 1023, 3174, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 298, 0, + 403, 258, 0, 454, 919, 0, 0, 630, 0, 0, + 917, 0, 0, 0, 0, 366, 0, 331, 197, 225, + 0, 0, 413, 462, 474, 0, 0, 0, 970, 0, + 472, 427, 605, 233, 285, 459, 433, 470, 441, 288, + 0, 0, 471, 373, 586, 451, 602, 631, 632, 264, + 407, 616, 523, 624, 649, 226, 261, 421, 507, 608, + 495, 398, 582, 583, 330, 494, 296, 201, 370, 637, + 224, 480, 372, 242, 231, 588, 613, 300, 252, 290, + 457, 644, 213, 518, 599, 239, 484, 0, 0, 652, + 247, 505, 611, 600, 215, 595, 504, 394, 327, 328, + 214, 0, 458, 269, 294, 0, 0, 259, 416, 971, + 972, 257, 653, 816, 623, 220, 0, 622, 409, 585, + 596, 395, 384, 219, 594, 393, 383, 335, 824, 825, + 281, 308, 901, 900, 899, 307, 309, 897, 898, 896, + 207, 609, 627, 0, 208, 0, 500, 612, 654, 453, + 212, 234, 235, 237, 0, 280, 284, 292, 295, 304, + 305, 314, 368, 420, 447, 443, 452, 0, 580, 603, + 617, 629, 635, 636, 638, 639, 640, 641, 642, 645, + 643, 408, 312, 496, 334, 374, 0, 0, 426, 473, + 240, 607, 497, 907, 929, 918, 782, 783, 908, 909, + 933, 910, 785, 786, 930, 931, 779, 780, 784, 932, + 934, 655, 656, 657, 658, 659, 660, 661, 662, 663, + 664, 665, 666, 667, 668, 669, 670, 671, 672, 650, + 509, 515, 510, 511, 512, 513, 514, 0, 516, 921, + 768, 767, 0, 774, 775, 0, 804, 805, 807, 813, + 814, 815, 826, 873, 874, 882, 884, 885, 883, 886, + 887, 888, 891, 892, 893, 894, 889, 890, 895, 787, + 791, 788, 789, 790, 802, 792, 793, 794, 795, 796, + 797, 798, 799, 800, 801, 803, 944, 945, 946, 947, + 948, 949, 819, 823, 822, 820, 821, 817, 818, 845, + 844, 846, 847, 848, 849, 850, 851, 853, 852, 854, + 855, 856, 857, 858, 859, 827, 828, 831, 832, 830, + 829, 833, 842, 843, 834, 835, 836, 837, 838, 839, + 841, 840, 860, 861, 862, 863, 864, 866, 865, 869, + 870, 868, 867, 872, 871, 766, 196, 221, 369, 0, + 455, 289, 651, 619, 485, 614, 206, 223, 935, 263, + 936, 0, 0, 940, 0, 0, 0, 942, 941, 0, + 943, 905, 904, 0, 0, 937, 938, 0, 939, 0, + 0, 198, 200, 209, 222, 232, 236, 243, 262, 277, + 279, 286, 299, 311, 319, 320, 323, 329, 381, 387, + 388, 389, 390, 410, 411, 412, 415, 418, 419, 422, + 424, 425, 428, 432, 436, 437, 438, 440, 442, 444, + 456, 461, 475, 476, 477, 478, 479, 482, 483, 489, + 490, 491, 492, 493, 501, 502, 517, 587, 589, 606, + 626, 633, 481, 950, 951, 952, 953, 954, 955, 956, + 957, 301, 601, 634, 598, 646, 628, 439, 379, 0, + 0, 382, 282, 306, 321, 0, 618, 503, 227, 467, + 291, 251, 975, 0, 211, 246, 230, 260, 275, 278, + 325, 392, 401, 430, 435, 297, 272, 244, 460, 241, + 486, 520, 521, 522, 524, 396, 267, 434, 397, 0, + 377, 577, 578, 317, 0, 0, 0, 529, 0, 778, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 417, + 0, 0, 0, 765, 0, 0, 0, 271, 770, 0, + 0, 0, 367, 268, 0, 0, 204, 506, 0, 431, + 0, 203, 0, 488, 253, 378, 375, 584, 283, 274, + 270, 250, 318, 386, 429, 519, 423, 777, 371, 0, + 0, 498, 402, 0, 0, 0, 0, 0, 0, 0, + 0, 772, 773, 0, 0, 0, 0, 0, 0, 0, + 0, 324, 248, 326, 202, 414, 499, 287, 0, 95, + 0, 0, 1024, 508, 960, 749, 926, 964, 1025, 977, + 978, 979, 965, 0, 238, 966, 967, 245, 968, 0, + 925, 808, 810, 809, 875, 876, 877, 878, 879, 880, + 881, 811, 812, 806, 973, 610, 980, 981, 0, 266, + 322, 273, 265, 581, 0, 0, 0, 0, 0, 0, + 597, 0, 0, 229, 0, 0, 0, 0, 0, 0, + 0, 745, 762, 0, 776, 0, 0, 0, 276, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 759, 760, 0, 0, 0, + 0, 920, 0, 761, 0, 0, 769, 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, 1008, 1009, 1010, 1011, 1012, 1013, 1014, - 1015, 1016, 1017, 1018, 1019, 1020, 1021, 1022, 3167, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 297, - 0, 402, 257, 0, 453, 918, 0, 0, 629, 0, - 0, 916, 0, 0, 0, 0, 365, 0, 330, 197, - 225, 0, 0, 412, 461, 473, 0, 0, 0, 969, - 0, 471, 426, 604, 233, 284, 458, 432, 469, 440, - 287, 0, 0, 470, 372, 585, 450, 601, 630, 631, - 263, 406, 615, 522, 623, 648, 226, 260, 420, 506, - 607, 494, 397, 581, 582, 329, 493, 295, 201, 369, - 636, 224, 479, 371, 242, 231, 587, 612, 299, 289, - 456, 643, 213, 517, 598, 239, 483, 0, 0, 651, - 247, 504, 610, 599, 215, 594, 503, 393, 326, 327, - 214, 0, 457, 268, 293, 0, 0, 258, 415, 970, - 971, 256, 652, 815, 622, 220, 0, 621, 408, 584, - 595, 394, 383, 219, 593, 392, 382, 334, 823, 824, - 280, 307, 900, 899, 898, 306, 308, 896, 897, 895, - 207, 608, 626, 0, 208, 0, 499, 611, 653, 452, - 212, 234, 235, 237, 0, 279, 283, 291, 294, 303, - 304, 313, 367, 419, 446, 442, 451, 0, 579, 602, - 616, 628, 634, 635, 637, 638, 639, 640, 641, 644, - 642, 407, 311, 495, 333, 373, 0, 0, 425, 472, - 240, 606, 496, 906, 928, 917, 781, 782, 907, 908, - 932, 909, 784, 785, 929, 930, 778, 779, 783, 931, - 933, 654, 655, 656, 657, 658, 659, 660, 661, 662, - 663, 664, 665, 666, 667, 668, 669, 670, 671, 649, - 508, 514, 509, 510, 511, 512, 513, 0, 515, 920, - 767, 766, 0, 773, 774, 0, 803, 804, 806, 812, - 813, 814, 825, 872, 873, 881, 883, 884, 882, 885, - 886, 887, 890, 891, 892, 893, 888, 889, 894, 786, - 790, 787, 788, 789, 801, 791, 792, 793, 794, 795, - 796, 797, 798, 799, 800, 802, 943, 944, 945, 946, - 947, 948, 818, 822, 821, 819, 820, 816, 817, 844, - 843, 845, 846, 847, 848, 849, 850, 852, 851, 853, - 854, 855, 856, 857, 858, 826, 827, 830, 831, 829, - 828, 832, 841, 842, 833, 834, 835, 836, 837, 838, - 840, 839, 859, 860, 861, 862, 863, 865, 864, 868, - 869, 867, 866, 871, 870, 765, 196, 221, 368, 0, - 454, 288, 650, 618, 484, 613, 206, 223, 934, 262, - 935, 0, 0, 939, 0, 0, 0, 941, 940, 0, - 942, 904, 903, 0, 0, 936, 937, 0, 938, 0, - 0, 198, 200, 209, 222, 232, 236, 243, 261, 276, - 278, 285, 298, 310, 318, 319, 322, 328, 380, 386, - 387, 388, 389, 409, 410, 411, 414, 417, 418, 421, - 423, 424, 427, 431, 435, 436, 437, 439, 441, 443, - 455, 460, 474, 475, 476, 477, 478, 481, 482, 488, - 489, 490, 491, 492, 500, 501, 516, 586, 588, 605, - 625, 632, 480, 949, 950, 951, 952, 953, 954, 955, - 956, 300, 600, 633, 597, 645, 627, 438, 378, 0, - 0, 381, 281, 305, 320, 0, 617, 502, 227, 466, - 290, 251, 974, 0, 211, 246, 230, 259, 274, 277, - 324, 391, 400, 429, 434, 296, 271, 244, 459, 241, - 485, 519, 520, 521, 523, 395, 266, 433, 396, 0, - 376, 576, 577, 316, 0, 0, 0, 528, 0, 777, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 416, - 0, 0, 0, 764, 0, 0, 0, 270, 769, 0, - 0, 0, 366, 267, 0, 0, 204, 505, 0, 430, - 0, 203, 0, 487, 252, 377, 374, 583, 282, 273, - 269, 250, 317, 385, 428, 518, 422, 776, 370, 0, - 0, 497, 401, 0, 0, 0, 0, 0, 0, 0, - 0, 771, 772, 0, 0, 0, 0, 0, 0, 0, - 0, 323, 248, 325, 202, 413, 498, 286, 0, 95, - 0, 0, 1023, 507, 959, 1090, 925, 963, 1024, 976, - 977, 978, 964, 0, 238, 965, 966, 245, 967, 0, - 924, 807, 809, 808, 874, 875, 876, 877, 878, 879, - 880, 810, 811, 805, 972, 609, 979, 980, 0, 265, - 321, 272, 264, 580, 0, 0, 0, 0, 0, 0, - 596, 0, 0, 229, 0, 0, 0, 0, 0, 0, - 0, 0, 761, 0, 775, 0, 0, 0, 275, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 758, 759, 0, 0, 0, - 0, 919, 0, 760, 0, 0, 768, 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, 1008, 1009, 1010, 1011, 1012, 1013, - 1014, 1015, 1016, 1017, 1018, 1019, 1020, 1021, 1022, 770, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 297, 0, 402, 257, 0, 453, 918, 0, 0, 629, - 0, 0, 916, 0, 0, 0, 0, 365, 0, 330, - 197, 225, 0, 0, 412, 461, 473, 0, 0, 0, - 969, 0, 471, 426, 604, 233, 284, 458, 432, 469, - 440, 287, 0, 0, 470, 372, 585, 450, 601, 630, - 631, 263, 406, 615, 522, 623, 648, 226, 260, 420, - 506, 607, 494, 397, 581, 582, 329, 493, 295, 201, - 369, 636, 224, 479, 371, 242, 231, 587, 612, 299, - 289, 456, 643, 213, 517, 598, 239, 483, 0, 0, - 651, 247, 504, 610, 599, 215, 594, 503, 393, 326, - 327, 214, 0, 457, 268, 293, 0, 0, 258, 415, - 970, 971, 256, 652, 815, 622, 220, 0, 621, 408, - 584, 595, 394, 383, 219, 593, 392, 382, 334, 823, - 824, 280, 307, 900, 899, 898, 306, 308, 896, 897, - 895, 207, 608, 626, 0, 208, 0, 499, 611, 653, - 452, 212, 234, 235, 237, 0, 279, 283, 291, 294, - 303, 304, 313, 367, 419, 446, 442, 451, 0, 579, - 602, 616, 628, 634, 635, 637, 638, 639, 640, 641, - 644, 642, 407, 311, 495, 333, 373, 0, 0, 425, - 472, 240, 606, 496, 906, 928, 917, 781, 782, 907, - 908, 932, 909, 784, 785, 929, 930, 778, 779, 783, - 931, 933, 654, 655, 656, 657, 658, 659, 660, 661, + 1015, 1016, 1017, 1018, 1019, 1020, 1021, 1022, 1023, 3170, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 298, 0, 403, 258, 0, 454, 919, 0, 0, 630, + 0, 0, 917, 0, 0, 0, 0, 366, 0, 331, + 197, 225, 0, 0, 413, 462, 474, 0, 0, 0, + 970, 0, 472, 427, 605, 233, 285, 459, 433, 470, + 441, 288, 0, 0, 471, 373, 586, 451, 602, 631, + 632, 264, 407, 616, 523, 624, 649, 226, 261, 421, + 507, 608, 495, 398, 582, 583, 330, 494, 296, 201, + 370, 637, 224, 480, 372, 242, 231, 588, 613, 300, + 252, 290, 457, 644, 213, 518, 599, 239, 484, 0, + 0, 652, 247, 505, 611, 600, 215, 595, 504, 394, + 327, 328, 214, 0, 458, 269, 294, 0, 0, 259, + 416, 971, 972, 257, 653, 816, 623, 220, 0, 622, + 409, 585, 596, 395, 384, 219, 594, 393, 383, 335, + 824, 825, 281, 308, 901, 900, 899, 307, 309, 897, + 898, 896, 207, 609, 627, 0, 208, 0, 500, 612, + 654, 453, 212, 234, 235, 237, 0, 280, 284, 292, + 295, 304, 305, 314, 368, 420, 447, 443, 452, 0, + 580, 603, 617, 629, 635, 636, 638, 639, 640, 641, + 642, 645, 643, 408, 312, 496, 334, 374, 0, 0, + 426, 473, 240, 607, 497, 907, 929, 918, 782, 783, + 908, 909, 933, 910, 785, 786, 930, 931, 779, 780, + 784, 932, 934, 655, 656, 657, 658, 659, 660, 661, 662, 663, 664, 665, 666, 667, 668, 669, 670, 671, - 649, 508, 514, 509, 510, 511, 512, 513, 0, 515, - 920, 767, 766, 0, 773, 774, 0, 803, 804, 806, - 812, 813, 814, 825, 872, 873, 881, 883, 884, 882, - 885, 886, 887, 890, 891, 892, 893, 888, 889, 894, - 786, 790, 787, 788, 789, 801, 791, 792, 793, 794, - 795, 796, 797, 798, 799, 800, 802, 943, 944, 945, - 946, 947, 948, 818, 822, 821, 819, 820, 816, 817, - 844, 843, 845, 846, 847, 848, 849, 850, 852, 851, - 853, 854, 855, 856, 857, 858, 826, 827, 830, 831, - 829, 828, 832, 841, 842, 833, 834, 835, 836, 837, - 838, 840, 839, 859, 860, 861, 862, 863, 865, 864, - 868, 869, 867, 866, 871, 870, 765, 196, 221, 368, - 0, 454, 288, 650, 618, 484, 613, 206, 223, 934, - 262, 935, 0, 0, 939, 0, 0, 0, 941, 940, - 0, 942, 904, 903, 0, 0, 936, 937, 0, 938, - 0, 0, 198, 200, 209, 222, 232, 236, 243, 261, - 276, 278, 285, 298, 310, 318, 319, 322, 328, 380, - 386, 387, 388, 389, 409, 410, 411, 414, 417, 418, - 421, 423, 424, 427, 431, 435, 436, 437, 439, 441, - 443, 455, 460, 474, 475, 476, 477, 478, 481, 482, - 488, 489, 490, 491, 492, 500, 501, 516, 586, 588, - 605, 625, 632, 480, 949, 950, 951, 952, 953, 954, - 955, 956, 300, 600, 633, 597, 645, 627, 438, 378, - 0, 0, 381, 281, 305, 320, 0, 617, 502, 227, - 466, 290, 251, 974, 0, 211, 246, 230, 259, 274, - 277, 324, 391, 400, 429, 434, 296, 271, 244, 459, - 241, 485, 519, 520, 521, 523, 395, 266, 433, 396, - 0, 376, 576, 577, 316, 0, 0, 0, 528, 0, - 777, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 416, 0, 0, 0, 764, 0, 0, 0, 270, 769, - 0, 0, 0, 366, 267, 0, 0, 204, 505, 0, - 430, 0, 203, 0, 487, 252, 377, 374, 583, 282, - 273, 269, 250, 317, 385, 428, 518, 422, 776, 370, - 0, 0, 497, 401, 0, 0, 0, 0, 0, 0, - 0, 0, 771, 772, 0, 0, 0, 0, 0, 0, - 0, 0, 323, 248, 325, 202, 413, 498, 286, 0, - 95, 0, 0, 1023, 507, 959, 1090, 925, 963, 1024, - 976, 977, 978, 964, 0, 238, 965, 966, 245, 967, - 0, 924, 807, 809, 808, 874, 875, 876, 877, 878, - 879, 880, 810, 811, 805, 972, 609, 979, 980, 0, - 265, 321, 272, 264, 580, 0, 0, 0, 0, 0, - 0, 596, 0, 0, 229, 0, 0, 0, 0, 0, - 0, 0, 0, 761, 0, 775, 0, 0, 0, 275, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 758, 759, 0, 0, - 0, 0, 919, 0, 760, 0, 0, 768, 981, 982, + 672, 650, 509, 515, 510, 511, 512, 513, 514, 0, + 516, 921, 768, 767, 0, 774, 775, 0, 804, 805, + 807, 813, 814, 815, 826, 873, 874, 882, 884, 885, + 883, 886, 887, 888, 891, 892, 893, 894, 889, 890, + 895, 787, 791, 788, 789, 790, 802, 792, 793, 794, + 795, 796, 797, 798, 799, 800, 801, 803, 944, 945, + 946, 947, 948, 949, 819, 823, 822, 820, 821, 817, + 818, 845, 844, 846, 847, 848, 849, 850, 851, 853, + 852, 854, 855, 856, 857, 858, 859, 827, 828, 831, + 832, 830, 829, 833, 842, 843, 834, 835, 836, 837, + 838, 839, 841, 840, 860, 861, 862, 863, 864, 866, + 865, 869, 870, 868, 867, 872, 871, 766, 196, 221, + 369, 0, 455, 289, 651, 619, 485, 614, 206, 223, + 935, 263, 936, 0, 0, 940, 0, 0, 0, 942, + 941, 0, 943, 905, 904, 0, 0, 937, 938, 0, + 939, 0, 0, 198, 200, 209, 222, 232, 236, 243, + 262, 277, 279, 286, 299, 311, 319, 320, 323, 329, + 381, 387, 388, 389, 390, 410, 411, 412, 415, 418, + 419, 422, 424, 425, 428, 432, 436, 437, 438, 440, + 442, 444, 456, 461, 475, 476, 477, 478, 479, 482, + 483, 489, 490, 491, 492, 493, 501, 502, 517, 587, + 589, 606, 626, 633, 481, 950, 951, 952, 953, 954, + 955, 956, 957, 301, 601, 634, 598, 646, 628, 439, + 379, 0, 0, 382, 282, 306, 321, 0, 618, 503, + 227, 467, 291, 251, 975, 0, 211, 246, 230, 260, + 275, 278, 325, 392, 401, 430, 435, 297, 272, 244, + 460, 241, 486, 520, 521, 522, 524, 396, 267, 434, + 397, 0, 377, 577, 578, 317, 0, 0, 0, 529, + 0, 778, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 417, 0, 0, 0, 765, 0, 0, 0, 271, + 770, 0, 0, 0, 367, 268, 0, 0, 204, 506, + 0, 431, 0, 203, 0, 488, 253, 378, 375, 584, + 283, 274, 270, 250, 318, 386, 429, 519, 423, 777, + 371, 0, 0, 498, 402, 0, 0, 0, 0, 0, + 0, 0, 0, 772, 773, 0, 0, 0, 0, 0, + 0, 0, 0, 324, 248, 326, 202, 414, 499, 287, + 0, 95, 0, 0, 1024, 508, 960, 1091, 926, 964, + 1025, 977, 978, 979, 965, 0, 238, 966, 967, 245, + 968, 0, 925, 808, 810, 809, 875, 876, 877, 878, + 879, 880, 881, 811, 812, 806, 973, 610, 980, 981, + 0, 266, 322, 273, 265, 581, 0, 0, 0, 0, + 0, 0, 597, 0, 0, 229, 0, 0, 0, 0, + 0, 0, 0, 0, 762, 0, 776, 0, 0, 0, + 276, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 759, 760, 0, + 0, 0, 0, 920, 0, 761, 0, 0, 769, 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, 1008, 1009, 1010, 1011, 1012, 1013, 1014, 1015, 1016, 1017, 1018, 1019, 1020, 1021, 1022, - 2123, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 297, 0, 402, 257, 0, 453, 918, 0, 0, - 629, 0, 0, 916, 0, 0, 0, 0, 365, 0, - 330, 197, 225, 0, 0, 412, 461, 473, 0, 0, - 0, 969, 0, 471, 426, 604, 233, 284, 458, 432, - 469, 440, 287, 0, 0, 470, 372, 585, 450, 601, - 630, 631, 263, 406, 615, 522, 623, 648, 226, 260, - 420, 506, 607, 494, 397, 581, 582, 329, 493, 295, - 201, 369, 636, 224, 479, 371, 242, 231, 587, 612, - 299, 289, 456, 643, 213, 517, 598, 239, 483, 0, - 0, 651, 247, 504, 610, 599, 215, 594, 503, 393, - 326, 327, 214, 0, 457, 268, 293, 0, 0, 258, - 415, 970, 971, 256, 652, 815, 622, 220, 0, 621, - 408, 584, 595, 394, 383, 219, 593, 392, 382, 334, - 823, 824, 280, 307, 900, 899, 898, 306, 308, 896, - 897, 895, 207, 608, 626, 0, 208, 0, 499, 611, - 653, 452, 212, 234, 235, 237, 0, 279, 283, 291, - 294, 303, 304, 313, 367, 419, 446, 442, 451, 0, - 579, 602, 616, 628, 634, 635, 637, 638, 639, 640, - 641, 644, 642, 407, 311, 495, 333, 373, 0, 0, - 425, 472, 240, 606, 496, 906, 928, 917, 781, 782, - 907, 908, 932, 909, 784, 785, 929, 930, 778, 779, - 783, 931, 933, 654, 655, 656, 657, 658, 659, 660, - 661, 662, 663, 664, 665, 666, 667, 668, 669, 670, - 671, 649, 508, 514, 509, 510, 511, 512, 513, 0, - 515, 920, 767, 766, 0, 773, 774, 0, 803, 804, - 806, 812, 813, 814, 825, 872, 873, 881, 883, 884, - 882, 885, 886, 887, 890, 891, 892, 893, 888, 889, - 894, 786, 790, 787, 788, 789, 801, 791, 792, 793, - 794, 795, 796, 797, 798, 799, 800, 802, 943, 944, - 945, 946, 947, 948, 818, 822, 821, 819, 820, 816, - 817, 844, 843, 845, 846, 847, 848, 849, 850, 852, - 851, 853, 854, 855, 856, 857, 858, 826, 827, 830, - 831, 829, 828, 832, 841, 842, 833, 834, 835, 836, - 837, 838, 840, 839, 859, 860, 861, 862, 863, 865, - 864, 868, 869, 867, 866, 871, 870, 765, 196, 221, - 368, 0, 454, 288, 650, 618, 484, 613, 206, 223, - 934, 262, 935, 0, 0, 939, 0, 0, 0, 941, - 940, 0, 942, 904, 903, 0, 0, 936, 937, 0, - 938, 0, 0, 198, 200, 209, 222, 232, 236, 243, - 261, 276, 278, 285, 298, 310, 318, 319, 322, 328, - 380, 386, 387, 388, 389, 409, 410, 411, 414, 417, - 418, 421, 423, 424, 427, 431, 435, 436, 437, 439, - 441, 443, 455, 460, 474, 475, 476, 477, 478, 481, - 482, 488, 489, 490, 491, 492, 500, 501, 516, 586, - 588, 605, 625, 632, 480, 949, 950, 951, 952, 953, - 954, 955, 956, 300, 600, 633, 597, 645, 627, 438, - 378, 0, 0, 381, 281, 305, 320, 0, 617, 502, - 227, 466, 290, 251, 974, 0, 211, 246, 230, 259, - 274, 277, 324, 391, 400, 429, 434, 296, 271, 244, - 459, 241, 485, 519, 520, 521, 523, 395, 266, 433, - 396, 0, 376, 576, 577, 316, 0, 0, 0, 528, - 0, 777, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 416, 0, 0, 0, 764, 0, 0, 0, 270, - 769, 0, 0, 0, 366, 267, 0, 0, 204, 505, - 0, 430, 0, 203, 0, 487, 252, 377, 374, 583, - 282, 273, 269, 250, 317, 385, 428, 518, 422, 776, - 370, 0, 0, 497, 401, 0, 0, 0, 0, 0, - 0, 0, 0, 771, 772, 0, 0, 0, 0, 0, - 0, 0, 0, 323, 248, 325, 202, 413, 498, 286, - 0, 95, 0, 0, 1023, 507, 959, 1090, 925, 963, - 1024, 976, 977, 978, 964, 0, 238, 965, 966, 245, - 967, 0, 924, 807, 809, 808, 874, 875, 876, 877, - 878, 879, 880, 810, 811, 805, 972, 609, 979, 980, - 0, 265, 321, 272, 264, 580, 0, 0, 0, 0, - 0, 0, 596, 0, 0, 229, 0, 0, 0, 0, - 0, 0, 0, 0, 761, 0, 775, 0, 0, 0, - 275, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 758, 759, 0, - 0, 0, 0, 919, 0, 760, 0, 0, 768, 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, 1008, 1009, 1010, 1011, - 1012, 1013, 1014, 1015, 1016, 1017, 1018, 1019, 1020, 1021, - 1022, 2121, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 297, 0, 402, 257, 0, 453, 918, 0, - 0, 629, 0, 0, 916, 0, 0, 0, 0, 365, - 0, 330, 197, 225, 0, 0, 412, 461, 473, 0, - 0, 0, 969, 0, 471, 426, 604, 233, 284, 458, - 432, 469, 440, 287, 0, 0, 470, 372, 585, 450, - 601, 630, 631, 263, 406, 615, 522, 623, 648, 226, - 260, 420, 506, 607, 494, 397, 581, 582, 329, 493, - 295, 201, 369, 636, 224, 479, 371, 242, 231, 587, - 612, 299, 289, 456, 643, 213, 517, 598, 239, 483, - 0, 0, 651, 247, 504, 610, 599, 215, 594, 503, - 393, 326, 327, 214, 0, 457, 268, 293, 0, 0, - 258, 415, 970, 971, 256, 652, 815, 622, 220, 0, - 621, 408, 584, 595, 394, 383, 219, 593, 392, 382, - 334, 823, 824, 280, 307, 900, 899, 898, 306, 308, - 896, 897, 895, 207, 608, 626, 0, 208, 0, 499, - 611, 653, 452, 212, 234, 235, 237, 0, 279, 283, - 291, 294, 303, 304, 313, 367, 419, 446, 442, 451, - 0, 579, 602, 616, 628, 634, 635, 637, 638, 639, - 640, 641, 644, 642, 407, 311, 495, 333, 373, 0, - 0, 425, 472, 240, 606, 496, 906, 928, 917, 781, - 782, 907, 908, 932, 909, 784, 785, 929, 930, 778, - 779, 783, 931, 933, 654, 655, 656, 657, 658, 659, + 1023, 771, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 298, 0, 403, 258, 0, 454, 919, 0, + 0, 630, 0, 0, 917, 0, 0, 0, 0, 366, + 0, 331, 197, 225, 0, 0, 413, 462, 474, 0, + 0, 0, 970, 0, 472, 427, 605, 233, 285, 459, + 433, 470, 441, 288, 0, 0, 471, 373, 586, 451, + 602, 631, 632, 264, 407, 616, 523, 624, 649, 226, + 261, 421, 507, 608, 495, 398, 582, 583, 330, 494, + 296, 201, 370, 637, 224, 480, 372, 242, 231, 588, + 613, 300, 252, 290, 457, 644, 213, 518, 599, 239, + 484, 0, 0, 652, 247, 505, 611, 600, 215, 595, + 504, 394, 327, 328, 214, 0, 458, 269, 294, 0, + 0, 259, 416, 971, 972, 257, 653, 816, 623, 220, + 0, 622, 409, 585, 596, 395, 384, 219, 594, 393, + 383, 335, 824, 825, 281, 308, 901, 900, 899, 307, + 309, 897, 898, 896, 207, 609, 627, 0, 208, 0, + 500, 612, 654, 453, 212, 234, 235, 237, 0, 280, + 284, 292, 295, 304, 305, 314, 368, 420, 447, 443, + 452, 0, 580, 603, 617, 629, 635, 636, 638, 639, + 640, 641, 642, 645, 643, 408, 312, 496, 334, 374, + 0, 0, 426, 473, 240, 607, 497, 907, 929, 918, + 782, 783, 908, 909, 933, 910, 785, 786, 930, 931, + 779, 780, 784, 932, 934, 655, 656, 657, 658, 659, + 660, 661, 662, 663, 664, 665, 666, 667, 668, 669, + 670, 671, 672, 650, 509, 515, 510, 511, 512, 513, + 514, 0, 516, 921, 768, 767, 0, 774, 775, 0, + 804, 805, 807, 813, 814, 815, 826, 873, 874, 882, + 884, 885, 883, 886, 887, 888, 891, 892, 893, 894, + 889, 890, 895, 787, 791, 788, 789, 790, 802, 792, + 793, 794, 795, 796, 797, 798, 799, 800, 801, 803, + 944, 945, 946, 947, 948, 949, 819, 823, 822, 820, + 821, 817, 818, 845, 844, 846, 847, 848, 849, 850, + 851, 853, 852, 854, 855, 856, 857, 858, 859, 827, + 828, 831, 832, 830, 829, 833, 842, 843, 834, 835, + 836, 837, 838, 839, 841, 840, 860, 861, 862, 863, + 864, 866, 865, 869, 870, 868, 867, 872, 871, 766, + 196, 221, 369, 0, 455, 289, 651, 619, 485, 614, + 206, 223, 935, 263, 936, 0, 0, 940, 0, 0, + 0, 942, 941, 0, 943, 905, 904, 0, 0, 937, + 938, 0, 939, 0, 0, 198, 200, 209, 222, 232, + 236, 243, 262, 277, 279, 286, 299, 311, 319, 320, + 323, 329, 381, 387, 388, 389, 390, 410, 411, 412, + 415, 418, 419, 422, 424, 425, 428, 432, 436, 437, + 438, 440, 442, 444, 456, 461, 475, 476, 477, 478, + 479, 482, 483, 489, 490, 491, 492, 493, 501, 502, + 517, 587, 589, 606, 626, 633, 481, 950, 951, 952, + 953, 954, 955, 956, 957, 301, 601, 634, 598, 646, + 628, 439, 379, 0, 0, 382, 282, 306, 321, 0, + 618, 503, 227, 467, 291, 251, 975, 0, 211, 246, + 230, 260, 275, 278, 325, 392, 401, 430, 435, 297, + 272, 244, 460, 241, 486, 520, 521, 522, 524, 396, + 267, 434, 397, 0, 377, 577, 578, 317, 0, 0, + 0, 529, 0, 778, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 417, 0, 0, 0, 765, 0, 0, + 0, 271, 770, 0, 0, 0, 367, 268, 0, 0, + 204, 506, 0, 431, 0, 203, 0, 488, 253, 378, + 375, 584, 283, 274, 270, 250, 318, 386, 429, 519, + 423, 777, 371, 0, 0, 498, 402, 0, 0, 0, + 0, 0, 0, 0, 0, 772, 773, 0, 0, 0, + 0, 0, 0, 0, 0, 324, 248, 326, 202, 414, + 499, 287, 0, 95, 0, 0, 1024, 508, 960, 1091, + 926, 964, 1025, 977, 978, 979, 965, 0, 238, 966, + 967, 245, 968, 0, 925, 808, 810, 809, 875, 876, + 877, 878, 879, 880, 881, 811, 812, 806, 973, 610, + 980, 981, 0, 266, 322, 273, 265, 581, 0, 0, + 0, 0, 0, 0, 597, 0, 0, 229, 0, 0, + 0, 0, 0, 0, 0, 0, 762, 0, 776, 0, + 0, 0, 276, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 759, + 760, 0, 0, 0, 0, 920, 0, 761, 0, 0, + 769, 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, 1008, 1009, 1010, + 1011, 1012, 1013, 1014, 1015, 1016, 1017, 1018, 1019, 1020, + 1021, 1022, 1023, 2124, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 298, 0, 403, 258, 0, 454, + 919, 0, 0, 630, 0, 0, 917, 0, 0, 0, + 0, 366, 0, 331, 197, 225, 0, 0, 413, 462, + 474, 0, 0, 0, 970, 0, 472, 427, 605, 233, + 285, 459, 433, 470, 441, 288, 0, 0, 471, 373, + 586, 451, 602, 631, 632, 264, 407, 616, 523, 624, + 649, 226, 261, 421, 507, 608, 495, 398, 582, 583, + 330, 494, 296, 201, 370, 637, 224, 480, 372, 242, + 231, 588, 613, 300, 252, 290, 457, 644, 213, 518, + 599, 239, 484, 0, 0, 652, 247, 505, 611, 600, + 215, 595, 504, 394, 327, 328, 214, 0, 458, 269, + 294, 0, 0, 259, 416, 971, 972, 257, 653, 816, + 623, 220, 0, 622, 409, 585, 596, 395, 384, 219, + 594, 393, 383, 335, 824, 825, 281, 308, 901, 900, + 899, 307, 309, 897, 898, 896, 207, 609, 627, 0, + 208, 0, 500, 612, 654, 453, 212, 234, 235, 237, + 0, 280, 284, 292, 295, 304, 305, 314, 368, 420, + 447, 443, 452, 0, 580, 603, 617, 629, 635, 636, + 638, 639, 640, 641, 642, 645, 643, 408, 312, 496, + 334, 374, 0, 0, 426, 473, 240, 607, 497, 907, + 929, 918, 782, 783, 908, 909, 933, 910, 785, 786, + 930, 931, 779, 780, 784, 932, 934, 655, 656, 657, + 658, 659, 660, 661, 662, 663, 664, 665, 666, 667, + 668, 669, 670, 671, 672, 650, 509, 515, 510, 511, + 512, 513, 514, 0, 516, 921, 768, 767, 0, 774, + 775, 0, 804, 805, 807, 813, 814, 815, 826, 873, + 874, 882, 884, 885, 883, 886, 887, 888, 891, 892, + 893, 894, 889, 890, 895, 787, 791, 788, 789, 790, + 802, 792, 793, 794, 795, 796, 797, 798, 799, 800, + 801, 803, 944, 945, 946, 947, 948, 949, 819, 823, + 822, 820, 821, 817, 818, 845, 844, 846, 847, 848, + 849, 850, 851, 853, 852, 854, 855, 856, 857, 858, + 859, 827, 828, 831, 832, 830, 829, 833, 842, 843, + 834, 835, 836, 837, 838, 839, 841, 840, 860, 861, + 862, 863, 864, 866, 865, 869, 870, 868, 867, 872, + 871, 766, 196, 221, 369, 0, 455, 289, 651, 619, + 485, 614, 206, 223, 935, 263, 936, 0, 0, 940, + 0, 0, 0, 942, 941, 0, 943, 905, 904, 0, + 0, 937, 938, 0, 939, 0, 0, 198, 200, 209, + 222, 232, 236, 243, 262, 277, 279, 286, 299, 311, + 319, 320, 323, 329, 381, 387, 388, 389, 390, 410, + 411, 412, 415, 418, 419, 422, 424, 425, 428, 432, + 436, 437, 438, 440, 442, 444, 456, 461, 475, 476, + 477, 478, 479, 482, 483, 489, 490, 491, 492, 493, + 501, 502, 517, 587, 589, 606, 626, 633, 481, 950, + 951, 952, 953, 954, 955, 956, 957, 301, 601, 634, + 598, 646, 628, 439, 379, 0, 0, 382, 282, 306, + 321, 0, 618, 503, 227, 467, 291, 251, 975, 0, + 211, 246, 230, 260, 275, 278, 325, 392, 401, 430, + 435, 297, 272, 244, 460, 241, 486, 520, 521, 522, + 524, 396, 267, 434, 397, 0, 377, 577, 578, 317, + 0, 0, 0, 529, 0, 778, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 417, 0, 0, 0, 765, + 0, 0, 0, 271, 770, 0, 0, 0, 367, 268, + 0, 0, 204, 506, 0, 431, 0, 203, 0, 488, + 253, 378, 375, 584, 283, 274, 270, 250, 318, 386, + 429, 519, 423, 777, 371, 0, 0, 498, 402, 0, + 0, 0, 0, 0, 0, 0, 0, 772, 773, 0, + 0, 0, 0, 0, 0, 0, 0, 324, 248, 326, + 202, 414, 499, 287, 0, 95, 0, 0, 1024, 508, + 960, 1091, 926, 964, 1025, 977, 978, 979, 965, 0, + 238, 966, 967, 245, 968, 0, 925, 808, 810, 809, + 875, 876, 877, 878, 879, 880, 881, 811, 812, 806, + 973, 610, 980, 981, 0, 266, 322, 273, 265, 581, + 0, 0, 0, 0, 0, 0, 597, 0, 0, 229, + 0, 0, 0, 0, 0, 0, 0, 0, 762, 0, + 776, 0, 0, 0, 276, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 759, 760, 0, 0, 0, 0, 920, 0, 761, + 0, 0, 769, 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, 1008, + 1009, 1010, 1011, 1012, 1013, 1014, 1015, 1016, 1017, 1018, + 1019, 1020, 1021, 1022, 1023, 2122, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 298, 0, 403, 258, + 0, 454, 919, 0, 0, 630, 0, 0, 917, 0, + 0, 0, 0, 366, 0, 331, 197, 225, 0, 0, + 413, 462, 474, 0, 0, 0, 970, 0, 472, 427, + 605, 233, 285, 459, 433, 470, 441, 288, 0, 0, + 471, 373, 586, 451, 602, 631, 632, 264, 407, 616, + 523, 624, 649, 226, 261, 421, 507, 608, 495, 398, + 582, 583, 330, 494, 296, 201, 370, 637, 224, 480, + 372, 242, 231, 588, 613, 300, 252, 290, 457, 644, + 213, 518, 599, 239, 484, 0, 0, 652, 247, 505, + 611, 600, 215, 595, 504, 394, 327, 328, 214, 0, + 458, 269, 294, 0, 0, 259, 416, 971, 972, 257, + 653, 816, 623, 220, 0, 622, 409, 585, 596, 395, + 384, 219, 594, 393, 383, 335, 824, 825, 281, 308, + 901, 900, 899, 307, 309, 897, 898, 896, 207, 609, + 627, 0, 208, 0, 500, 612, 654, 453, 212, 234, + 235, 237, 0, 280, 284, 292, 295, 304, 305, 314, + 368, 420, 447, 443, 452, 0, 580, 603, 617, 629, + 635, 636, 638, 639, 640, 641, 642, 645, 643, 408, + 312, 496, 334, 374, 0, 0, 426, 473, 240, 607, + 497, 907, 929, 918, 782, 783, 908, 909, 933, 910, + 785, 786, 930, 931, 779, 780, 784, 932, 934, 655, + 656, 657, 658, 659, 660, 661, 662, 663, 664, 665, + 666, 667, 668, 669, 670, 671, 672, 650, 509, 515, + 510, 511, 512, 513, 514, 0, 516, 921, 768, 767, + 0, 774, 775, 0, 804, 805, 807, 813, 814, 815, + 826, 873, 874, 882, 884, 885, 883, 886, 887, 888, + 891, 892, 893, 894, 889, 890, 895, 787, 791, 788, + 789, 790, 802, 792, 793, 794, 795, 796, 797, 798, + 799, 800, 801, 803, 944, 945, 946, 947, 948, 949, + 819, 823, 822, 820, 821, 817, 818, 845, 844, 846, + 847, 848, 849, 850, 851, 853, 852, 854, 855, 856, + 857, 858, 859, 827, 828, 831, 832, 830, 829, 833, + 842, 843, 834, 835, 836, 837, 838, 839, 841, 840, + 860, 861, 862, 863, 864, 866, 865, 869, 870, 868, + 867, 872, 871, 766, 196, 221, 369, 0, 455, 289, + 651, 619, 485, 614, 206, 223, 935, 263, 936, 0, + 0, 940, 0, 0, 0, 942, 941, 0, 943, 905, + 904, 0, 0, 937, 938, 0, 939, 0, 0, 198, + 200, 209, 222, 232, 236, 243, 262, 277, 279, 286, + 299, 311, 319, 320, 323, 329, 381, 387, 388, 389, + 390, 410, 411, 412, 415, 418, 419, 422, 424, 425, + 428, 432, 436, 437, 438, 440, 442, 444, 456, 461, + 475, 476, 477, 478, 479, 482, 483, 489, 490, 491, + 492, 493, 501, 502, 517, 587, 589, 606, 626, 633, + 481, 950, 951, 952, 953, 954, 955, 956, 957, 301, + 601, 634, 598, 646, 628, 439, 379, 0, 0, 382, + 282, 306, 321, 0, 618, 503, 227, 467, 291, 251, + 975, 0, 211, 246, 230, 260, 275, 278, 325, 392, + 401, 430, 435, 297, 272, 244, 460, 241, 486, 520, + 521, 522, 524, 396, 267, 434, 397, 0, 377, 577, + 578, 317, 0, 0, 0, 529, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 417, 0, 0, + 0, 0, 0, 0, 0, 271, 0, 0, 0, 0, + 367, 268, 0, 0, 204, 506, 0, 431, 0, 203, + 0, 488, 253, 378, 375, 584, 283, 274, 270, 250, + 318, 386, 429, 519, 423, 0, 371, 0, 0, 498, + 402, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 324, + 248, 326, 202, 414, 499, 287, 0, 0, 0, 0, + 0, 508, 725, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 238, 0, 0, 245, 0, 0, 0, 352, + 361, 360, 340, 341, 343, 345, 351, 358, 364, 337, + 346, 0, 0, 610, 0, 0, 0, 266, 322, 273, + 265, 581, 0, 0, 0, 0, 0, 0, 597, 0, + 0, 229, 0, 1142, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 276, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 298, 0, + 403, 258, 0, 454, 0, 0, 1141, 630, 0, 0, + 0, 0, 0, 1138, 1139, 366, 1099, 331, 197, 225, + 1132, 1136, 413, 462, 474, 0, 0, 0, 254, 0, + 472, 427, 605, 233, 285, 459, 433, 470, 441, 288, + 0, 0, 471, 373, 586, 451, 602, 631, 632, 264, + 407, 616, 523, 624, 649, 226, 261, 421, 507, 608, + 495, 398, 582, 583, 330, 494, 296, 201, 370, 637, + 224, 480, 372, 242, 231, 588, 613, 300, 252, 290, + 457, 644, 213, 518, 599, 239, 484, 0, 0, 652, + 247, 505, 611, 600, 215, 595, 504, 394, 327, 328, + 214, 0, 458, 269, 294, 0, 0, 259, 416, 590, + 591, 257, 653, 228, 623, 220, 0, 622, 409, 585, + 596, 395, 384, 219, 594, 393, 383, 335, 356, 357, + 281, 308, 448, 376, 449, 307, 309, 405, 404, 406, + 207, 609, 627, 0, 208, 0, 500, 612, 654, 453, + 212, 234, 235, 237, 0, 280, 284, 292, 295, 304, + 305, 314, 368, 420, 447, 443, 452, 0, 580, 603, + 617, 629, 635, 636, 638, 639, 640, 641, 642, 645, + 643, 408, 312, 496, 334, 374, 0, 0, 426, 473, + 240, 607, 497, 199, 0, 0, 0, 0, 255, 256, + 0, 576, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 655, 656, 657, 658, 659, 660, 661, 662, 663, + 664, 665, 666, 667, 668, 669, 670, 671, 672, 650, + 509, 515, 510, 511, 512, 513, 514, 0, 516, 0, + 0, 0, 0, 0, 399, 0, 592, 593, 673, 385, + 487, 604, 336, 350, 353, 342, 362, 0, 363, 338, + 339, 344, 347, 348, 349, 354, 355, 359, 365, 249, + 210, 391, 400, 579, 313, 216, 217, 218, 525, 526, + 527, 528, 620, 621, 625, 205, 463, 464, 465, 466, + 293, 615, 310, 469, 468, 332, 333, 380, 450, 541, + 543, 554, 558, 560, 562, 568, 571, 542, 544, 555, + 559, 561, 563, 569, 572, 531, 533, 535, 537, 550, + 549, 546, 574, 575, 552, 557, 536, 548, 553, 566, + 573, 570, 530, 534, 538, 547, 565, 564, 545, 556, + 567, 551, 539, 532, 540, 0, 196, 221, 369, 0, + 455, 289, 651, 619, 485, 614, 206, 223, 0, 263, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 198, 200, 209, 222, 232, 236, 243, 262, 277, + 279, 286, 299, 311, 319, 320, 323, 329, 381, 387, + 388, 389, 390, 410, 411, 412, 415, 418, 419, 422, + 424, 425, 428, 432, 436, 437, 438, 440, 442, 444, + 456, 461, 475, 476, 477, 478, 479, 482, 483, 489, + 490, 491, 492, 493, 501, 502, 517, 587, 589, 606, + 626, 633, 481, 302, 303, 445, 446, 315, 316, 647, + 648, 301, 601, 634, 598, 646, 628, 439, 379, 0, + 0, 382, 282, 306, 321, 0, 618, 503, 227, 467, + 291, 251, 0, 0, 211, 246, 230, 260, 275, 278, + 325, 392, 401, 430, 435, 297, 272, 244, 460, 241, + 486, 520, 521, 522, 524, 396, 267, 434, 397, 0, + 377, 577, 578, 317, 0, 0, 86, 529, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 417, + 0, 0, 0, 0, 0, 0, 0, 271, 0, 0, + 0, 0, 367, 268, 0, 0, 204, 506, 0, 431, + 0, 203, 0, 488, 253, 378, 375, 584, 283, 274, + 270, 250, 318, 386, 429, 519, 423, 0, 371, 0, + 0, 498, 402, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 324, 248, 326, 202, 414, 499, 287, 0, 95, + 0, 0, 0, 508, 194, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 238, 0, 0, 245, 0, 0, + 0, 352, 361, 360, 340, 341, 343, 345, 351, 358, + 364, 337, 346, 0, 0, 610, 0, 0, 0, 266, + 322, 273, 265, 581, 0, 0, 0, 0, 0, 0, + 597, 0, 0, 229, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 276, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 298, 0, 403, 258, 0, 454, 0, 0, 0, 630, + 0, 0, 0, 0, 0, 0, 0, 366, 0, 331, + 197, 225, 0, 0, 413, 462, 474, 0, 0, 0, + 254, 0, 472, 427, 605, 233, 285, 459, 433, 470, + 441, 288, 0, 0, 471, 373, 586, 451, 602, 631, + 632, 264, 407, 616, 523, 624, 649, 226, 261, 421, + 507, 608, 495, 398, 582, 583, 330, 494, 296, 201, + 370, 637, 224, 480, 372, 242, 231, 588, 613, 300, + 252, 290, 457, 644, 213, 518, 599, 239, 484, 0, + 0, 652, 247, 505, 611, 600, 215, 595, 504, 394, + 327, 328, 214, 0, 458, 269, 294, 0, 0, 259, + 416, 590, 591, 257, 653, 228, 623, 220, 0, 622, + 409, 585, 596, 395, 384, 219, 594, 393, 383, 335, + 356, 357, 281, 308, 448, 376, 449, 307, 309, 405, + 404, 406, 207, 609, 627, 0, 208, 0, 500, 612, + 654, 453, 212, 234, 235, 237, 0, 280, 284, 292, + 295, 304, 305, 314, 368, 420, 447, 443, 452, 0, + 580, 603, 617, 629, 635, 636, 638, 639, 640, 641, + 642, 645, 643, 408, 312, 496, 334, 374, 0, 0, + 426, 473, 240, 607, 497, 199, 0, 0, 0, 0, + 255, 256, 0, 576, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 655, 656, 657, 658, 659, 660, 661, + 662, 663, 664, 665, 666, 667, 668, 669, 670, 671, + 672, 650, 509, 515, 510, 511, 512, 513, 514, 0, + 516, 0, 0, 0, 0, 0, 399, 0, 592, 593, + 673, 385, 487, 604, 336, 350, 353, 342, 362, 0, + 363, 338, 339, 344, 347, 348, 349, 354, 355, 359, + 365, 249, 210, 391, 400, 579, 313, 216, 217, 218, + 525, 526, 527, 528, 620, 621, 625, 205, 463, 464, + 465, 466, 293, 615, 310, 469, 468, 332, 333, 380, + 450, 541, 543, 554, 558, 560, 562, 568, 571, 542, + 544, 555, 559, 561, 563, 569, 572, 531, 533, 535, + 537, 550, 549, 546, 574, 575, 552, 557, 536, 548, + 553, 566, 573, 570, 530, 534, 538, 547, 565, 564, + 545, 556, 567, 551, 539, 532, 540, 0, 196, 221, + 369, 94, 455, 289, 651, 619, 485, 614, 206, 223, + 0, 263, 0, 0, 0, 0, 0, 0, 2427, 0, + 0, 2426, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 198, 200, 209, 222, 232, 236, 243, + 262, 277, 279, 286, 299, 311, 319, 320, 323, 329, + 381, 387, 388, 389, 390, 410, 411, 412, 415, 418, + 419, 422, 424, 425, 428, 432, 436, 437, 438, 440, + 442, 444, 456, 461, 475, 476, 477, 478, 479, 482, + 483, 489, 490, 491, 492, 493, 501, 502, 517, 587, + 589, 606, 626, 633, 481, 302, 303, 445, 446, 315, + 316, 647, 648, 301, 601, 634, 598, 646, 628, 439, + 379, 0, 0, 382, 282, 306, 321, 0, 618, 503, + 227, 467, 291, 251, 0, 0, 211, 246, 230, 260, + 275, 278, 325, 392, 401, 430, 435, 297, 272, 244, + 460, 241, 486, 520, 521, 522, 524, 396, 267, 434, + 1777, 0, 377, 577, 578, 317, 0, 0, 0, 529, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 417, 0, 0, 1779, 0, 0, 0, 0, 271, + 0, 0, 0, 0, 367, 268, 0, 0, 204, 506, + 0, 431, 0, 203, 0, 488, 253, 378, 375, 584, + 283, 274, 270, 250, 318, 386, 429, 519, 423, 0, + 371, 0, 0, 498, 402, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 324, 248, 326, 202, 414, 499, 287, + 0, 0, 0, 0, 1781, 508, 725, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 238, 0, 0, 245, + 0, 0, 0, 352, 361, 360, 340, 341, 343, 345, + 351, 358, 364, 337, 346, 0, 0, 610, 0, 0, + 0, 266, 322, 273, 265, 581, 0, 0, 0, 0, + 0, 0, 597, 0, 0, 229, 0, 0, 0, 1480, + 0, 1481, 1482, 0, 0, 0, 0, 0, 0, 0, + 276, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 298, 0, 403, 258, 0, 454, 0, 0, + 0, 630, 0, 0, 0, 0, 0, 0, 0, 366, + 0, 331, 197, 225, 0, 0, 413, 462, 474, 0, + 0, 0, 254, 0, 472, 427, 605, 233, 285, 459, + 433, 470, 441, 288, 0, 0, 471, 373, 586, 451, + 602, 631, 632, 264, 407, 616, 523, 624, 649, 226, + 261, 421, 507, 608, 495, 398, 582, 583, 330, 494, + 296, 201, 370, 637, 224, 480, 372, 242, 231, 588, + 613, 300, 252, 290, 457, 644, 213, 518, 599, 239, + 484, 0, 0, 652, 247, 505, 611, 600, 215, 595, + 504, 394, 327, 328, 214, 0, 458, 269, 294, 0, + 0, 259, 416, 590, 591, 257, 653, 228, 623, 220, + 0, 622, 409, 585, 596, 395, 384, 219, 594, 393, + 383, 335, 356, 357, 281, 308, 448, 376, 449, 307, + 309, 405, 404, 406, 207, 609, 627, 0, 208, 0, + 500, 612, 654, 453, 212, 234, 235, 237, 0, 280, + 284, 292, 295, 304, 305, 314, 368, 420, 447, 443, + 452, 0, 580, 603, 617, 629, 635, 636, 638, 639, + 640, 641, 642, 645, 643, 408, 312, 496, 334, 374, + 0, 0, 426, 473, 240, 607, 497, 199, 0, 0, + 0, 0, 255, 256, 0, 576, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 655, 656, 657, 658, 659, 660, 661, 662, 663, 664, 665, 666, 667, 668, 669, - 670, 671, 649, 508, 514, 509, 510, 511, 512, 513, - 0, 515, 920, 767, 766, 0, 773, 774, 0, 803, - 804, 806, 812, 813, 814, 825, 872, 873, 881, 883, - 884, 882, 885, 886, 887, 890, 891, 892, 893, 888, - 889, 894, 786, 790, 787, 788, 789, 801, 791, 792, - 793, 794, 795, 796, 797, 798, 799, 800, 802, 943, - 944, 945, 946, 947, 948, 818, 822, 821, 819, 820, - 816, 817, 844, 843, 845, 846, 847, 848, 849, 850, - 852, 851, 853, 854, 855, 856, 857, 858, 826, 827, - 830, 831, 829, 828, 832, 841, 842, 833, 834, 835, - 836, 837, 838, 840, 839, 859, 860, 861, 862, 863, - 865, 864, 868, 869, 867, 866, 871, 870, 765, 196, - 221, 368, 0, 454, 288, 650, 618, 484, 613, 206, - 223, 934, 262, 935, 0, 0, 939, 0, 0, 0, - 941, 940, 0, 942, 904, 903, 0, 0, 936, 937, - 0, 938, 0, 0, 198, 200, 209, 222, 232, 236, - 243, 261, 276, 278, 285, 298, 310, 318, 319, 322, - 328, 380, 386, 387, 388, 389, 409, 410, 411, 414, - 417, 418, 421, 423, 424, 427, 431, 435, 436, 437, - 439, 441, 443, 455, 460, 474, 475, 476, 477, 478, - 481, 482, 488, 489, 490, 491, 492, 500, 501, 516, - 586, 588, 605, 625, 632, 480, 949, 950, 951, 952, - 953, 954, 955, 956, 300, 600, 633, 597, 645, 627, - 438, 378, 0, 0, 381, 281, 305, 320, 0, 617, - 502, 227, 466, 290, 251, 974, 0, 211, 246, 230, - 259, 274, 277, 324, 391, 400, 429, 434, 296, 271, - 244, 459, 241, 485, 519, 520, 521, 523, 395, 266, - 433, 396, 0, 376, 576, 577, 316, 0, 0, 0, - 528, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 416, 0, 0, 0, 0, 0, 0, 0, - 270, 0, 0, 0, 0, 366, 267, 0, 0, 204, - 505, 0, 430, 0, 203, 0, 487, 252, 377, 374, - 583, 282, 273, 269, 250, 317, 385, 428, 518, 422, - 0, 370, 0, 0, 497, 401, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 323, 248, 325, 202, 413, 498, - 286, 0, 0, 0, 0, 0, 507, 724, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 238, 0, 0, - 245, 0, 0, 0, 351, 360, 359, 339, 340, 342, - 344, 350, 357, 363, 336, 345, 0, 0, 609, 0, - 0, 0, 265, 321, 272, 264, 580, 0, 0, 0, - 0, 0, 0, 596, 0, 0, 229, 0, 1141, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 275, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 297, 0, 402, 257, 0, 453, 0, - 0, 1140, 629, 0, 0, 0, 0, 0, 1137, 1138, - 365, 1098, 330, 197, 225, 1131, 1135, 412, 461, 473, - 0, 0, 0, 253, 0, 471, 426, 604, 233, 284, - 458, 432, 469, 440, 287, 0, 0, 470, 372, 585, - 450, 601, 630, 631, 263, 406, 615, 522, 623, 648, - 226, 260, 420, 506, 607, 494, 397, 581, 582, 329, - 493, 295, 201, 369, 636, 224, 479, 371, 242, 231, - 587, 612, 299, 289, 456, 643, 213, 517, 598, 239, - 483, 0, 0, 651, 247, 504, 610, 599, 215, 594, - 503, 393, 326, 327, 214, 0, 457, 268, 293, 0, - 0, 258, 415, 589, 590, 256, 652, 228, 622, 220, - 0, 621, 408, 584, 595, 394, 383, 219, 593, 392, - 382, 334, 355, 356, 280, 307, 447, 375, 448, 306, - 308, 404, 403, 405, 207, 608, 626, 0, 208, 0, - 499, 611, 653, 452, 212, 234, 235, 237, 0, 279, - 283, 291, 294, 303, 304, 313, 367, 419, 446, 442, - 451, 0, 579, 602, 616, 628, 634, 635, 637, 638, - 639, 640, 641, 644, 642, 407, 311, 495, 333, 373, - 0, 0, 425, 472, 240, 606, 496, 199, 0, 0, - 0, 0, 254, 255, 0, 575, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 654, 655, 656, 657, 658, - 659, 660, 661, 662, 663, 664, 665, 666, 667, 668, - 669, 670, 671, 649, 508, 514, 509, 510, 511, 512, - 513, 0, 515, 0, 0, 0, 0, 0, 398, 0, - 591, 592, 672, 384, 486, 603, 335, 349, 352, 341, - 361, 0, 362, 337, 338, 343, 346, 347, 348, 353, - 354, 358, 364, 249, 210, 390, 399, 578, 312, 216, - 217, 218, 524, 525, 526, 527, 619, 620, 624, 205, - 462, 463, 464, 465, 292, 614, 309, 468, 467, 331, - 332, 379, 449, 540, 542, 553, 557, 559, 561, 567, - 570, 541, 543, 554, 558, 560, 562, 568, 571, 530, - 532, 534, 536, 549, 548, 545, 573, 574, 551, 556, - 535, 547, 552, 565, 572, 569, 529, 533, 537, 546, - 564, 563, 544, 555, 566, 550, 538, 531, 539, 0, - 196, 221, 368, 0, 454, 288, 650, 618, 484, 613, - 206, 223, 0, 262, 0, 0, 0, 0, 0, 0, + 670, 671, 672, 650, 509, 515, 510, 511, 512, 513, + 514, 0, 516, 0, 0, 0, 0, 0, 399, 0, + 592, 593, 673, 385, 487, 604, 336, 350, 353, 342, + 362, 0, 363, 338, 339, 344, 347, 348, 349, 354, + 355, 359, 365, 249, 210, 391, 400, 579, 313, 216, + 217, 218, 525, 526, 527, 528, 620, 621, 625, 205, + 463, 464, 465, 466, 293, 615, 310, 469, 468, 332, + 333, 380, 450, 541, 543, 554, 558, 560, 562, 568, + 571, 542, 544, 555, 559, 561, 563, 569, 572, 531, + 533, 535, 537, 550, 549, 546, 574, 575, 552, 557, + 536, 548, 553, 566, 573, 570, 530, 534, 538, 547, + 565, 564, 545, 556, 567, 551, 539, 532, 540, 0, + 196, 221, 369, 0, 455, 289, 651, 619, 485, 614, + 206, 223, 0, 263, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 198, 200, 209, 222, 232, - 236, 243, 261, 276, 278, 285, 298, 310, 318, 319, - 322, 328, 380, 386, 387, 388, 389, 409, 410, 411, - 414, 417, 418, 421, 423, 424, 427, 431, 435, 436, - 437, 439, 441, 443, 455, 460, 474, 475, 476, 477, - 478, 481, 482, 488, 489, 490, 491, 492, 500, 501, - 516, 586, 588, 605, 625, 632, 480, 301, 302, 444, - 445, 314, 315, 646, 647, 300, 600, 633, 597, 645, - 627, 438, 378, 0, 0, 381, 281, 305, 320, 0, - 617, 502, 227, 466, 290, 251, 0, 0, 211, 246, - 230, 259, 274, 277, 324, 391, 400, 429, 434, 296, - 271, 244, 459, 241, 485, 519, 520, 521, 523, 395, - 266, 433, 396, 0, 376, 576, 577, 316, 0, 0, - 86, 528, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 416, 0, 0, 0, 0, 0, 0, - 0, 270, 0, 0, 0, 0, 366, 267, 0, 0, - 204, 505, 0, 430, 0, 203, 0, 487, 252, 377, - 374, 583, 282, 273, 269, 250, 317, 385, 428, 518, - 422, 0, 370, 0, 0, 497, 401, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 323, 248, 325, 202, 413, - 498, 286, 0, 95, 0, 0, 0, 507, 194, 0, + 236, 243, 262, 277, 279, 286, 299, 311, 319, 320, + 323, 329, 381, 387, 388, 389, 390, 410, 411, 412, + 415, 418, 419, 422, 424, 425, 428, 432, 436, 437, + 438, 440, 442, 444, 456, 461, 475, 476, 477, 478, + 479, 482, 483, 489, 490, 491, 492, 493, 501, 502, + 517, 587, 589, 606, 626, 633, 481, 302, 303, 445, + 446, 315, 316, 647, 648, 301, 601, 634, 598, 646, + 628, 439, 379, 0, 0, 382, 282, 306, 321, 0, + 618, 503, 227, 467, 291, 251, 0, 0, 211, 246, + 230, 260, 275, 278, 325, 392, 401, 430, 435, 297, + 272, 244, 460, 241, 486, 520, 521, 522, 524, 396, + 267, 434, 397, 0, 377, 577, 578, 317, 0, 0, + 86, 529, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 417, 0, 0, 0, 0, 0, 0, + 0, 271, 0, 0, 0, 0, 367, 268, 0, 0, + 204, 506, 0, 431, 0, 203, 0, 488, 253, 378, + 375, 584, 283, 274, 270, 250, 318, 386, 429, 519, + 423, 0, 371, 0, 0, 498, 402, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 324, 248, 326, 202, 414, + 499, 287, 0, 95, 0, 1754, 0, 508, 725, 0, 0, 0, 0, 0, 0, 0, 0, 0, 238, 0, - 0, 245, 0, 0, 0, 351, 360, 359, 339, 340, - 342, 344, 350, 357, 363, 336, 345, 0, 0, 609, - 0, 0, 0, 265, 321, 272, 264, 580, 0, 0, - 0, 0, 0, 0, 596, 0, 0, 229, 0, 0, + 0, 245, 0, 0, 0, 352, 361, 360, 340, 341, + 343, 345, 351, 358, 364, 337, 346, 0, 0, 610, + 0, 0, 0, 266, 322, 273, 265, 581, 0, 0, + 0, 0, 0, 0, 597, 0, 0, 229, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 275, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 276, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -4491,1184 +4677,963 @@ 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, 297, 0, 402, 257, 0, 453, - 0, 0, 0, 629, 0, 0, 0, 0, 0, 0, - 0, 365, 0, 330, 197, 225, 0, 0, 412, 461, - 473, 0, 0, 0, 253, 0, 471, 426, 604, 233, - 284, 458, 432, 469, 440, 287, 0, 0, 470, 372, - 585, 450, 601, 630, 631, 263, 406, 615, 522, 623, - 648, 226, 260, 420, 506, 607, 494, 397, 581, 582, - 329, 493, 295, 201, 369, 636, 224, 479, 371, 242, - 231, 587, 612, 299, 289, 456, 643, 213, 517, 598, - 239, 483, 0, 0, 651, 247, 504, 610, 599, 215, - 594, 503, 393, 326, 327, 214, 0, 457, 268, 293, - 0, 0, 258, 415, 589, 590, 256, 652, 228, 622, - 220, 0, 621, 408, 584, 595, 394, 383, 219, 593, - 392, 382, 334, 355, 356, 280, 307, 447, 375, 448, - 306, 308, 404, 403, 405, 207, 608, 626, 0, 208, - 0, 499, 611, 653, 452, 212, 234, 235, 237, 0, - 279, 283, 291, 294, 303, 304, 313, 367, 419, 446, - 442, 451, 0, 579, 602, 616, 628, 634, 635, 637, - 638, 639, 640, 641, 644, 642, 407, 311, 495, 333, - 373, 0, 0, 425, 472, 240, 606, 496, 199, 0, - 0, 0, 0, 254, 255, 0, 575, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 654, 655, 656, 657, + 0, 0, 0, 0, 298, 0, 403, 258, 0, 454, + 0, 0, 0, 630, 0, 0, 0, 0, 0, 0, + 0, 366, 0, 331, 197, 225, 0, 0, 413, 462, + 474, 0, 0, 0, 254, 0, 472, 427, 605, 233, + 285, 459, 433, 470, 441, 288, 0, 0, 471, 373, + 586, 451, 602, 631, 632, 264, 407, 616, 523, 624, + 649, 226, 261, 421, 507, 608, 495, 398, 582, 583, + 330, 494, 296, 201, 370, 637, 224, 480, 372, 242, + 231, 588, 613, 300, 252, 290, 457, 644, 213, 518, + 599, 239, 484, 0, 0, 652, 247, 505, 611, 600, + 215, 595, 504, 394, 327, 328, 214, 0, 458, 269, + 294, 0, 0, 259, 416, 590, 591, 257, 653, 228, + 623, 220, 0, 622, 409, 585, 596, 395, 384, 219, + 594, 393, 383, 335, 356, 357, 281, 308, 448, 376, + 449, 307, 309, 405, 404, 406, 207, 609, 627, 0, + 208, 0, 500, 612, 654, 453, 212, 234, 235, 237, + 0, 280, 284, 292, 295, 304, 305, 314, 368, 420, + 447, 443, 452, 0, 580, 603, 617, 629, 635, 636, + 638, 639, 640, 641, 642, 645, 643, 408, 312, 496, + 334, 374, 0, 0, 426, 473, 240, 607, 497, 199, + 0, 0, 0, 0, 255, 256, 0, 576, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 655, 656, 657, 658, 659, 660, 661, 662, 663, 664, 665, 666, 667, - 668, 669, 670, 671, 649, 508, 514, 509, 510, 511, - 512, 513, 0, 515, 0, 0, 0, 0, 0, 398, - 0, 591, 592, 672, 384, 486, 603, 335, 349, 352, - 341, 361, 0, 362, 337, 338, 343, 346, 347, 348, - 353, 354, 358, 364, 249, 210, 390, 399, 578, 312, - 216, 217, 218, 524, 525, 526, 527, 619, 620, 624, - 205, 462, 463, 464, 465, 292, 614, 309, 468, 467, - 331, 332, 379, 449, 540, 542, 553, 557, 559, 561, - 567, 570, 541, 543, 554, 558, 560, 562, 568, 571, - 530, 532, 534, 536, 549, 548, 545, 573, 574, 551, - 556, 535, 547, 552, 565, 572, 569, 529, 533, 537, - 546, 564, 563, 544, 555, 566, 550, 538, 531, 539, - 0, 196, 221, 368, 94, 454, 288, 650, 618, 484, - 613, 206, 223, 0, 262, 0, 0, 0, 0, 0, - 0, 2426, 0, 0, 2425, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 198, 200, 209, 222, - 232, 236, 243, 261, 276, 278, 285, 298, 310, 318, - 319, 322, 328, 380, 386, 387, 388, 389, 409, 410, - 411, 414, 417, 418, 421, 423, 424, 427, 431, 435, - 436, 437, 439, 441, 443, 455, 460, 474, 475, 476, - 477, 478, 481, 482, 488, 489, 490, 491, 492, 500, - 501, 516, 586, 588, 605, 625, 632, 480, 301, 302, - 444, 445, 314, 315, 646, 647, 300, 600, 633, 597, - 645, 627, 438, 378, 0, 0, 381, 281, 305, 320, - 0, 617, 502, 227, 466, 290, 251, 0, 0, 211, - 246, 230, 259, 274, 277, 324, 391, 400, 429, 434, - 296, 271, 244, 459, 241, 485, 519, 520, 521, 523, - 395, 266, 433, 1776, 0, 376, 576, 577, 316, 0, - 0, 0, 528, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 416, 0, 0, 1778, 0, 0, - 0, 0, 270, 0, 0, 0, 0, 366, 267, 0, - 0, 204, 505, 0, 430, 0, 203, 0, 487, 252, - 377, 374, 583, 282, 273, 269, 250, 317, 385, 428, - 518, 422, 0, 370, 0, 0, 497, 401, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 323, 248, 325, 202, - 413, 498, 286, 0, 0, 0, 0, 1780, 507, 724, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 238, - 0, 0, 245, 0, 0, 0, 351, 360, 359, 339, - 340, 342, 344, 350, 357, 363, 336, 345, 0, 0, - 609, 0, 0, 0, 265, 321, 272, 264, 580, 0, - 0, 0, 0, 0, 0, 596, 0, 0, 229, 0, - 0, 0, 1479, 0, 1480, 1481, 0, 0, 0, 0, - 0, 0, 0, 275, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 297, 0, 402, 257, 0, - 453, 0, 0, 0, 629, 0, 0, 0, 0, 0, - 0, 0, 365, 0, 330, 197, 225, 0, 0, 412, - 461, 473, 0, 0, 0, 253, 0, 471, 426, 604, - 233, 284, 458, 432, 469, 440, 287, 0, 0, 470, - 372, 585, 450, 601, 630, 631, 263, 406, 615, 522, - 623, 648, 226, 260, 420, 506, 607, 494, 397, 581, - 582, 329, 493, 295, 201, 369, 636, 224, 479, 371, - 242, 231, 587, 612, 299, 289, 456, 643, 213, 517, - 598, 239, 483, 0, 0, 651, 247, 504, 610, 599, - 215, 594, 503, 393, 326, 327, 214, 0, 457, 268, - 293, 0, 0, 258, 415, 589, 590, 256, 652, 228, - 622, 220, 0, 621, 408, 584, 595, 394, 383, 219, - 593, 392, 382, 334, 355, 356, 280, 307, 447, 375, - 448, 306, 308, 404, 403, 405, 207, 608, 626, 0, - 208, 0, 499, 611, 653, 452, 212, 234, 235, 237, - 0, 279, 283, 291, 294, 303, 304, 313, 367, 419, - 446, 442, 451, 0, 579, 602, 616, 628, 634, 635, - 637, 638, 639, 640, 641, 644, 642, 407, 311, 495, - 333, 373, 0, 0, 425, 472, 240, 606, 496, 199, - 0, 0, 0, 0, 254, 255, 0, 575, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 654, 655, 656, - 657, 658, 659, 660, 661, 662, 663, 664, 665, 666, - 667, 668, 669, 670, 671, 649, 508, 514, 509, 510, - 511, 512, 513, 0, 515, 0, 0, 0, 0, 0, - 398, 0, 591, 592, 672, 384, 486, 603, 335, 349, - 352, 341, 361, 0, 362, 337, 338, 343, 346, 347, - 348, 353, 354, 358, 364, 249, 210, 390, 399, 578, - 312, 216, 217, 218, 524, 525, 526, 527, 619, 620, - 624, 205, 462, 463, 464, 465, 292, 614, 309, 468, - 467, 331, 332, 379, 449, 540, 542, 553, 557, 559, - 561, 567, 570, 541, 543, 554, 558, 560, 562, 568, - 571, 530, 532, 534, 536, 549, 548, 545, 573, 574, - 551, 556, 535, 547, 552, 565, 572, 569, 529, 533, - 537, 546, 564, 563, 544, 555, 566, 550, 538, 531, - 539, 0, 196, 221, 368, 0, 454, 288, 650, 618, - 484, 613, 206, 223, 0, 262, 0, 0, 0, 0, + 668, 669, 670, 671, 672, 650, 509, 515, 510, 511, + 512, 513, 514, 0, 516, 0, 0, 0, 0, 0, + 399, 0, 592, 593, 673, 385, 487, 604, 336, 350, + 353, 342, 362, 0, 363, 338, 339, 344, 347, 348, + 349, 354, 355, 359, 365, 249, 210, 391, 400, 579, + 313, 216, 217, 218, 525, 526, 527, 528, 620, 621, + 625, 205, 463, 464, 465, 466, 293, 615, 310, 469, + 468, 332, 333, 380, 450, 541, 543, 554, 558, 560, + 562, 568, 571, 542, 544, 555, 559, 561, 563, 569, + 572, 531, 533, 535, 537, 550, 549, 546, 574, 575, + 552, 557, 536, 548, 553, 566, 573, 570, 530, 534, + 538, 547, 565, 564, 545, 556, 567, 551, 539, 532, + 540, 0, 196, 221, 369, 94, 455, 289, 651, 619, + 485, 614, 206, 223, 0, 263, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 198, 200, 209, - 222, 232, 236, 243, 261, 276, 278, 285, 298, 310, - 318, 319, 322, 328, 380, 386, 387, 388, 389, 409, - 410, 411, 414, 417, 418, 421, 423, 424, 427, 431, - 435, 436, 437, 439, 441, 443, 455, 460, 474, 475, - 476, 477, 478, 481, 482, 488, 489, 490, 491, 492, - 500, 501, 516, 586, 588, 605, 625, 632, 480, 301, - 302, 444, 445, 314, 315, 646, 647, 300, 600, 633, - 597, 645, 627, 438, 378, 0, 0, 381, 281, 305, - 320, 0, 617, 502, 227, 466, 290, 251, 0, 0, - 211, 246, 230, 259, 274, 277, 324, 391, 400, 429, - 434, 296, 271, 244, 459, 241, 485, 519, 520, 521, - 523, 395, 266, 433, 396, 0, 376, 576, 577, 316, - 0, 0, 86, 528, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 416, 0, 0, 0, 0, - 0, 0, 0, 270, 0, 0, 0, 0, 366, 267, - 0, 0, 204, 505, 0, 430, 0, 203, 0, 487, - 252, 377, 374, 583, 282, 273, 269, 250, 317, 385, - 428, 518, 422, 0, 370, 0, 0, 497, 401, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 323, 248, 325, - 202, 413, 498, 286, 0, 95, 0, 1753, 0, 507, - 724, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 238, 0, 0, 245, 0, 0, 0, 351, 360, 359, - 339, 340, 342, 344, 350, 357, 363, 336, 345, 0, - 0, 609, 0, 0, 0, 265, 321, 272, 264, 580, - 0, 0, 0, 0, 0, 0, 596, 0, 0, 229, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 275, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 297, 0, 402, 257, - 0, 453, 0, 0, 0, 629, 0, 0, 0, 0, - 0, 0, 0, 365, 0, 330, 197, 225, 0, 0, - 412, 461, 473, 0, 0, 0, 253, 0, 471, 426, - 604, 233, 284, 458, 432, 469, 440, 287, 0, 0, - 470, 372, 585, 450, 601, 630, 631, 263, 406, 615, - 522, 623, 648, 226, 260, 420, 506, 607, 494, 397, - 581, 582, 329, 493, 295, 201, 369, 636, 224, 479, - 371, 242, 231, 587, 612, 299, 289, 456, 643, 213, - 517, 598, 239, 483, 0, 0, 651, 247, 504, 610, - 599, 215, 594, 503, 393, 326, 327, 214, 0, 457, - 268, 293, 0, 0, 258, 415, 589, 590, 256, 652, - 228, 622, 220, 0, 621, 408, 584, 595, 394, 383, - 219, 593, 392, 382, 334, 355, 356, 280, 307, 447, - 375, 448, 306, 308, 404, 403, 405, 207, 608, 626, - 0, 208, 0, 499, 611, 653, 452, 212, 234, 235, - 237, 0, 279, 283, 291, 294, 303, 304, 313, 367, - 419, 446, 442, 451, 0, 579, 602, 616, 628, 634, - 635, 637, 638, 639, 640, 641, 644, 642, 407, 311, - 495, 333, 373, 0, 0, 425, 472, 240, 606, 496, - 199, 0, 0, 0, 0, 254, 255, 0, 575, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 654, 655, + 222, 232, 236, 243, 262, 277, 279, 286, 299, 311, + 319, 320, 323, 329, 381, 387, 388, 389, 390, 410, + 411, 412, 415, 418, 419, 422, 424, 425, 428, 432, + 436, 437, 438, 440, 442, 444, 456, 461, 475, 476, + 477, 478, 479, 482, 483, 489, 490, 491, 492, 493, + 501, 502, 517, 587, 589, 606, 626, 633, 481, 302, + 303, 445, 446, 315, 316, 647, 648, 301, 601, 634, + 598, 646, 628, 439, 379, 0, 0, 382, 282, 306, + 321, 0, 618, 503, 227, 467, 291, 251, 0, 0, + 211, 246, 230, 260, 275, 278, 325, 392, 401, 430, + 435, 297, 272, 244, 460, 241, 486, 520, 521, 522, + 524, 396, 267, 434, 397, 0, 377, 577, 578, 317, + 0, 0, 0, 529, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 417, 0, 0, 0, 0, + 0, 0, 0, 271, 0, 0, 0, 0, 367, 268, + 0, 0, 204, 506, 0, 431, 0, 203, 0, 488, + 253, 378, 375, 584, 283, 274, 270, 250, 318, 386, + 429, 519, 423, 0, 371, 0, 0, 498, 402, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 324, 248, 326, + 202, 414, 499, 287, 0, 95, 0, 0, 0, 508, + 194, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 238, 0, 0, 245, 0, 0, 0, 352, 361, 360, + 340, 341, 343, 345, 351, 358, 364, 337, 346, 0, + 0, 610, 0, 0, 0, 266, 322, 273, 265, 581, + 0, 0, 0, 0, 0, 0, 597, 0, 0, 229, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 276, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 298, 0, 403, 258, + 0, 454, 0, 0, 0, 630, 0, 0, 0, 0, + 0, 0, 0, 366, 0, 331, 197, 225, 0, 0, + 413, 462, 474, 0, 0, 0, 254, 0, 472, 427, + 605, 233, 285, 459, 433, 470, 441, 288, 0, 0, + 471, 373, 586, 451, 602, 631, 632, 264, 407, 616, + 523, 624, 649, 226, 261, 421, 507, 608, 495, 398, + 582, 583, 330, 494, 296, 201, 370, 637, 224, 480, + 372, 242, 231, 588, 613, 300, 252, 290, 457, 644, + 213, 518, 599, 239, 484, 0, 0, 652, 247, 505, + 611, 600, 215, 595, 504, 394, 327, 328, 214, 0, + 458, 269, 294, 0, 0, 259, 416, 590, 591, 257, + 653, 228, 623, 220, 0, 622, 409, 585, 596, 395, + 384, 219, 594, 393, 383, 335, 356, 357, 281, 308, + 448, 376, 449, 307, 309, 405, 404, 406, 207, 609, + 627, 0, 208, 0, 500, 612, 654, 453, 212, 234, + 235, 237, 0, 280, 284, 292, 295, 304, 305, 314, + 368, 420, 447, 443, 452, 0, 580, 603, 617, 629, + 635, 636, 638, 639, 640, 641, 642, 645, 643, 408, + 312, 496, 334, 374, 0, 0, 426, 473, 240, 607, + 497, 199, 0, 0, 0, 0, 255, 256, 0, 576, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 655, 656, 657, 658, 659, 660, 661, 662, 663, 664, 665, - 666, 667, 668, 669, 670, 671, 649, 508, 514, 509, - 510, 511, 512, 513, 0, 515, 0, 0, 0, 0, - 0, 398, 0, 591, 592, 672, 384, 486, 603, 335, - 349, 352, 341, 361, 0, 362, 337, 338, 343, 346, - 347, 348, 353, 354, 358, 364, 249, 210, 390, 399, - 578, 312, 216, 217, 218, 524, 525, 526, 527, 619, - 620, 624, 205, 462, 463, 464, 465, 292, 614, 309, - 468, 467, 331, 332, 379, 449, 540, 542, 553, 557, - 559, 561, 567, 570, 541, 543, 554, 558, 560, 562, - 568, 571, 530, 532, 534, 536, 549, 548, 545, 573, - 574, 551, 556, 535, 547, 552, 565, 572, 569, 529, - 533, 537, 546, 564, 563, 544, 555, 566, 550, 538, - 531, 539, 0, 196, 221, 368, 94, 454, 288, 650, - 618, 484, 613, 206, 223, 0, 262, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 198, 200, - 209, 222, 232, 236, 243, 261, 276, 278, 285, 298, - 310, 318, 319, 322, 328, 380, 386, 387, 388, 389, - 409, 410, 411, 414, 417, 418, 421, 423, 424, 427, - 431, 435, 436, 437, 439, 441, 443, 455, 460, 474, - 475, 476, 477, 478, 481, 482, 488, 489, 490, 491, - 492, 500, 501, 516, 586, 588, 605, 625, 632, 480, - 301, 302, 444, 445, 314, 315, 646, 647, 300, 600, - 633, 597, 645, 627, 438, 378, 0, 0, 381, 281, - 305, 320, 0, 617, 502, 227, 466, 290, 251, 0, - 0, 211, 246, 230, 259, 274, 277, 324, 391, 400, - 429, 434, 296, 271, 244, 459, 241, 485, 519, 520, - 521, 523, 395, 266, 433, 396, 0, 376, 576, 577, - 316, 0, 0, 0, 528, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 416, 0, 0, 0, - 0, 0, 0, 0, 270, 0, 0, 0, 0, 366, - 267, 0, 0, 204, 505, 0, 430, 0, 203, 0, - 487, 252, 377, 374, 583, 282, 273, 269, 250, 317, - 385, 428, 518, 422, 0, 370, 0, 0, 497, 401, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 323, 248, - 325, 202, 413, 498, 286, 0, 95, 0, 0, 0, - 507, 194, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 238, 0, 0, 245, 0, 0, 0, 351, 360, - 359, 339, 340, 342, 344, 350, 357, 363, 336, 345, - 0, 0, 609, 0, 0, 0, 265, 321, 272, 264, - 580, 0, 0, 0, 0, 0, 0, 596, 0, 0, - 229, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 275, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 297, 0, 402, - 257, 0, 453, 0, 0, 0, 629, 0, 0, 0, - 0, 0, 0, 0, 365, 0, 330, 197, 225, 0, - 0, 412, 461, 473, 0, 0, 0, 253, 0, 471, - 426, 604, 233, 284, 458, 432, 469, 440, 287, 0, - 0, 470, 372, 585, 450, 601, 630, 631, 263, 406, - 615, 522, 623, 648, 226, 260, 420, 506, 607, 494, - 397, 581, 582, 329, 493, 295, 201, 369, 636, 224, - 479, 371, 242, 231, 587, 612, 299, 289, 456, 643, - 213, 517, 598, 239, 483, 0, 0, 651, 247, 504, - 610, 599, 215, 594, 503, 393, 326, 327, 214, 0, - 457, 268, 293, 0, 0, 258, 415, 589, 590, 256, - 652, 228, 622, 220, 0, 621, 408, 584, 595, 394, - 383, 219, 593, 392, 382, 334, 355, 356, 280, 307, - 447, 375, 448, 306, 308, 404, 403, 405, 207, 608, - 626, 0, 208, 0, 499, 611, 653, 452, 212, 234, - 235, 237, 0, 279, 283, 291, 294, 303, 304, 313, - 367, 419, 446, 442, 451, 0, 579, 602, 616, 628, - 634, 635, 637, 638, 639, 640, 641, 644, 642, 407, - 311, 495, 333, 373, 0, 0, 425, 472, 240, 606, - 496, 199, 0, 0, 0, 0, 254, 255, 0, 575, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 654, - 655, 656, 657, 658, 659, 660, 661, 662, 663, 664, - 665, 666, 667, 668, 669, 670, 671, 649, 508, 514, - 509, 510, 511, 512, 513, 0, 515, 0, 0, 0, - 0, 0, 398, 0, 591, 592, 672, 384, 486, 603, - 335, 349, 352, 341, 361, 0, 362, 337, 338, 343, - 346, 347, 348, 353, 354, 358, 364, 249, 210, 390, - 399, 578, 312, 216, 217, 218, 524, 525, 526, 527, - 619, 620, 624, 205, 462, 463, 464, 465, 292, 614, - 309, 468, 467, 331, 332, 379, 449, 540, 542, 553, - 557, 559, 561, 567, 570, 541, 543, 554, 558, 560, - 562, 568, 571, 530, 532, 534, 536, 549, 548, 545, - 573, 574, 551, 556, 535, 547, 552, 565, 572, 569, - 529, 533, 537, 546, 564, 563, 544, 555, 566, 550, - 538, 531, 539, 0, 196, 221, 368, 0, 454, 288, - 650, 618, 484, 613, 206, 223, 0, 262, 0, 0, - 0, 0, 0, 0, 2426, 0, 0, 2425, 0, 0, + 666, 667, 668, 669, 670, 671, 672, 650, 509, 515, + 510, 511, 512, 513, 514, 0, 516, 0, 0, 0, + 0, 0, 399, 0, 592, 593, 673, 385, 487, 604, + 336, 350, 353, 342, 362, 0, 363, 338, 339, 344, + 347, 348, 349, 354, 355, 359, 365, 249, 210, 391, + 400, 579, 313, 216, 217, 218, 525, 526, 527, 528, + 620, 621, 625, 205, 463, 464, 465, 466, 293, 615, + 310, 469, 468, 332, 333, 380, 450, 541, 543, 554, + 558, 560, 562, 568, 571, 542, 544, 555, 559, 561, + 563, 569, 572, 531, 533, 535, 537, 550, 549, 546, + 574, 575, 552, 557, 536, 548, 553, 566, 573, 570, + 530, 534, 538, 547, 565, 564, 545, 556, 567, 551, + 539, 532, 540, 0, 196, 221, 369, 0, 455, 289, + 651, 619, 485, 614, 206, 223, 0, 263, 0, 0, + 0, 0, 0, 0, 2427, 0, 0, 2426, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 198, - 200, 209, 222, 232, 236, 243, 261, 276, 278, 285, - 298, 310, 318, 319, 322, 328, 380, 386, 387, 388, - 389, 409, 410, 411, 414, 417, 418, 421, 423, 424, - 427, 431, 435, 436, 437, 439, 441, 443, 455, 460, - 474, 475, 476, 477, 478, 481, 482, 488, 489, 490, - 491, 492, 500, 501, 516, 586, 588, 605, 625, 632, - 480, 301, 302, 444, 445, 314, 315, 646, 647, 300, - 600, 633, 597, 645, 627, 438, 378, 0, 0, 381, - 281, 305, 320, 0, 617, 502, 227, 466, 290, 251, - 0, 0, 211, 246, 230, 259, 274, 277, 324, 391, - 400, 429, 434, 296, 271, 244, 459, 241, 485, 519, - 520, 521, 523, 395, 266, 433, 396, 0, 376, 576, - 577, 316, 0, 0, 0, 528, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 416, 0, 0, - 2373, 0, 0, 0, 0, 270, 0, 0, 0, 0, - 366, 267, 0, 0, 204, 505, 0, 430, 0, 203, - 0, 487, 252, 377, 374, 583, 282, 273, 269, 250, - 317, 385, 428, 518, 422, 0, 370, 0, 0, 497, - 401, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 323, - 248, 325, 202, 413, 498, 286, 0, 0, 0, 0, - 1961, 507, 194, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 238, 0, 0, 245, 0, 0, 0, 351, - 360, 359, 339, 340, 342, 344, 350, 357, 363, 336, - 345, 0, 0, 609, 0, 0, 0, 265, 321, 272, - 264, 580, 0, 0, 0, 0, 0, 0, 596, 0, + 200, 209, 222, 232, 236, 243, 262, 277, 279, 286, + 299, 311, 319, 320, 323, 329, 381, 387, 388, 389, + 390, 410, 411, 412, 415, 418, 419, 422, 424, 425, + 428, 432, 436, 437, 438, 440, 442, 444, 456, 461, + 475, 476, 477, 478, 479, 482, 483, 489, 490, 491, + 492, 493, 501, 502, 517, 587, 589, 606, 626, 633, + 481, 302, 303, 445, 446, 315, 316, 647, 648, 301, + 601, 634, 598, 646, 628, 439, 379, 0, 0, 382, + 282, 306, 321, 0, 618, 503, 227, 467, 291, 251, + 0, 0, 211, 246, 230, 260, 275, 278, 325, 392, + 401, 430, 435, 297, 272, 244, 460, 241, 486, 520, + 521, 522, 524, 396, 267, 434, 397, 0, 377, 577, + 578, 317, 0, 0, 0, 529, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 417, 0, 0, + 2374, 0, 0, 0, 0, 271, 0, 0, 0, 0, + 367, 268, 0, 0, 204, 506, 0, 431, 0, 203, + 0, 488, 253, 378, 375, 584, 283, 274, 270, 250, + 318, 386, 429, 519, 423, 0, 371, 0, 0, 498, + 402, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 324, + 248, 326, 202, 414, 499, 287, 0, 0, 0, 0, + 1962, 508, 194, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 238, 0, 0, 245, 0, 0, 0, 352, + 361, 360, 340, 341, 343, 345, 351, 358, 364, 337, + 346, 0, 0, 610, 0, 0, 0, 266, 322, 273, + 265, 581, 0, 0, 0, 0, 0, 0, 597, 0, 0, 229, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 275, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 297, 0, - 402, 257, 0, 453, 0, 0, 0, 629, 0, 0, - 0, 0, 0, 0, 0, 365, 0, 330, 197, 225, - 0, 0, 412, 461, 473, 0, 0, 0, 253, 0, - 471, 426, 604, 233, 284, 458, 432, 469, 440, 287, - 0, 2371, 470, 372, 585, 450, 601, 630, 631, 263, - 406, 615, 522, 623, 648, 226, 260, 420, 506, 607, - 494, 397, 581, 582, 329, 493, 295, 201, 369, 636, - 224, 479, 371, 242, 231, 587, 612, 299, 289, 456, - 643, 213, 517, 598, 239, 483, 0, 0, 651, 247, - 504, 610, 599, 215, 594, 503, 393, 326, 327, 214, - 0, 457, 268, 293, 0, 0, 258, 415, 589, 590, - 256, 652, 228, 622, 220, 0, 621, 408, 584, 595, - 394, 383, 219, 593, 392, 382, 334, 355, 356, 280, - 307, 447, 375, 448, 306, 308, 404, 403, 405, 207, - 608, 626, 0, 208, 0, 499, 611, 653, 452, 212, - 234, 235, 237, 0, 279, 283, 291, 294, 303, 304, - 313, 367, 419, 446, 442, 451, 0, 579, 602, 616, - 628, 634, 635, 637, 638, 639, 640, 641, 644, 642, - 407, 311, 495, 333, 373, 0, 0, 425, 472, 240, - 606, 496, 199, 0, 0, 0, 0, 254, 255, 0, - 575, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 654, 655, 656, 657, 658, 659, 660, 661, 662, 663, - 664, 665, 666, 667, 668, 669, 670, 671, 649, 508, - 514, 509, 510, 511, 512, 513, 0, 515, 0, 0, - 0, 0, 0, 398, 0, 591, 592, 672, 384, 486, - 603, 335, 349, 352, 341, 361, 0, 362, 337, 338, - 343, 346, 347, 348, 353, 354, 358, 364, 249, 210, - 390, 399, 578, 312, 216, 217, 218, 524, 525, 526, - 527, 619, 620, 624, 205, 462, 463, 464, 465, 292, - 614, 309, 468, 467, 331, 332, 379, 449, 540, 542, - 553, 557, 559, 561, 567, 570, 541, 543, 554, 558, - 560, 562, 568, 571, 530, 532, 534, 536, 549, 548, - 545, 573, 574, 551, 556, 535, 547, 552, 565, 572, - 569, 529, 533, 537, 546, 564, 563, 544, 555, 566, - 550, 538, 531, 539, 0, 196, 221, 368, 0, 454, - 288, 650, 618, 484, 613, 206, 223, 0, 262, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 198, 200, 209, 222, 232, 236, 243, 261, 276, 278, - 285, 298, 310, 318, 319, 322, 328, 380, 386, 387, - 388, 389, 409, 410, 411, 414, 417, 418, 421, 423, - 424, 427, 431, 435, 436, 437, 439, 441, 443, 455, - 460, 474, 475, 476, 477, 478, 481, 482, 488, 489, - 490, 491, 492, 500, 501, 516, 586, 588, 605, 625, - 632, 480, 301, 302, 444, 445, 314, 315, 646, 647, - 300, 600, 633, 597, 645, 627, 438, 378, 0, 0, - 381, 281, 305, 320, 0, 617, 502, 227, 466, 290, - 251, 0, 0, 211, 246, 230, 259, 274, 277, 324, - 391, 400, 429, 434, 296, 271, 244, 459, 241, 485, - 519, 520, 521, 523, 395, 266, 433, 396, 0, 376, - 576, 577, 316, 0, 0, 0, 528, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 416, 0, - 0, 0, 0, 0, 0, 0, 270, 0, 0, 0, - 0, 366, 267, 0, 0, 204, 505, 0, 430, 0, - 203, 0, 487, 252, 377, 374, 583, 282, 273, 269, - 250, 317, 385, 428, 518, 422, 0, 370, 0, 0, - 497, 401, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 323, 248, 325, 202, 413, 498, 286, 0, 0, 0, - 0, 0, 507, 724, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 238, 0, 0, 245, 0, 0, 0, - 351, 360, 359, 339, 340, 342, 344, 350, 357, 363, - 336, 345, 0, 0, 609, 0, 0, 0, 265, 321, - 272, 264, 580, 0, 0, 0, 0, 0, 0, 596, - 0, 0, 229, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 275, 0, 0, - 0, 0, 0, 0, 0, 0, 1092, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 297, - 0, 402, 257, 0, 453, 0, 0, 0, 629, 0, - 0, 0, 0, 0, 0, 0, 365, 1098, 330, 197, - 225, 1096, 0, 412, 461, 473, 0, 0, 0, 253, - 0, 471, 426, 604, 233, 284, 458, 432, 469, 440, - 287, 0, 0, 470, 372, 585, 450, 601, 630, 631, - 263, 406, 615, 522, 623, 648, 226, 260, 420, 506, - 607, 494, 397, 581, 582, 329, 493, 295, 201, 369, - 636, 224, 479, 371, 242, 231, 587, 612, 299, 289, - 456, 643, 213, 517, 598, 239, 483, 0, 0, 651, - 247, 504, 610, 599, 215, 594, 503, 393, 326, 327, - 214, 0, 457, 268, 293, 0, 0, 258, 415, 589, - 590, 256, 652, 228, 622, 220, 0, 621, 408, 584, - 595, 394, 383, 219, 593, 392, 382, 334, 355, 356, - 280, 307, 447, 375, 448, 306, 308, 404, 403, 405, - 207, 608, 626, 0, 208, 0, 499, 611, 653, 452, - 212, 234, 235, 237, 0, 279, 283, 291, 294, 303, - 304, 313, 367, 419, 446, 442, 451, 0, 579, 602, - 616, 628, 634, 635, 637, 638, 639, 640, 641, 644, - 642, 407, 311, 495, 333, 373, 0, 0, 425, 472, - 240, 606, 496, 199, 0, 0, 0, 0, 254, 255, - 0, 575, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 654, 655, 656, 657, 658, 659, 660, 661, 662, - 663, 664, 665, 666, 667, 668, 669, 670, 671, 649, - 508, 514, 509, 510, 511, 512, 513, 0, 515, 0, - 0, 0, 0, 0, 398, 0, 591, 592, 672, 384, - 486, 603, 335, 349, 352, 341, 361, 0, 362, 337, - 338, 343, 346, 347, 348, 353, 354, 358, 364, 249, - 210, 390, 399, 578, 312, 216, 217, 218, 524, 525, - 526, 527, 619, 620, 624, 205, 462, 463, 464, 465, - 292, 614, 309, 468, 467, 331, 332, 379, 449, 540, - 542, 553, 557, 559, 561, 567, 570, 541, 543, 554, - 558, 560, 562, 568, 571, 530, 532, 534, 536, 549, - 548, 545, 573, 574, 551, 556, 535, 547, 552, 565, - 572, 569, 529, 533, 537, 546, 564, 563, 544, 555, - 566, 550, 538, 531, 539, 0, 196, 221, 368, 0, - 454, 288, 650, 618, 484, 613, 206, 223, 0, 262, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 198, 200, 209, 222, 232, 236, 243, 261, 276, - 278, 285, 298, 310, 318, 319, 322, 328, 380, 386, - 387, 388, 389, 409, 410, 411, 414, 417, 418, 421, - 423, 424, 427, 431, 435, 436, 437, 439, 441, 443, - 455, 460, 474, 475, 476, 477, 478, 481, 482, 488, - 489, 490, 491, 492, 500, 501, 516, 586, 588, 605, - 625, 632, 480, 301, 302, 444, 445, 314, 315, 646, - 647, 300, 600, 633, 597, 645, 627, 438, 378, 0, - 0, 381, 281, 305, 320, 0, 617, 502, 227, 466, - 290, 251, 0, 0, 211, 246, 230, 259, 274, 277, - 324, 391, 400, 429, 434, 296, 271, 244, 459, 241, - 485, 519, 520, 521, 523, 395, 266, 433, 396, 0, - 376, 576, 577, 316, 0, 0, 0, 528, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 416, - 0, 0, 2373, 0, 0, 0, 0, 270, 0, 0, - 0, 0, 366, 267, 0, 0, 204, 505, 0, 430, - 0, 203, 0, 487, 252, 377, 374, 583, 282, 273, - 269, 250, 317, 385, 428, 518, 422, 0, 370, 0, - 0, 497, 401, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 323, 248, 325, 202, 413, 498, 286, 0, 0, - 0, 0, 1961, 507, 194, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 276, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 298, 0, + 403, 258, 0, 454, 0, 0, 0, 630, 0, 0, + 0, 0, 0, 0, 0, 366, 0, 331, 197, 225, + 0, 0, 413, 462, 474, 0, 0, 0, 254, 0, + 472, 427, 605, 233, 285, 459, 433, 470, 441, 288, + 0, 2372, 471, 373, 586, 451, 602, 631, 632, 264, + 407, 616, 523, 624, 649, 226, 261, 421, 507, 608, + 495, 398, 582, 583, 330, 494, 296, 201, 370, 637, + 224, 480, 372, 242, 231, 588, 613, 300, 252, 290, + 457, 644, 213, 518, 599, 239, 484, 0, 0, 652, + 247, 505, 611, 600, 215, 595, 504, 394, 327, 328, + 214, 0, 458, 269, 294, 0, 0, 259, 416, 590, + 591, 257, 653, 228, 623, 220, 0, 622, 409, 585, + 596, 395, 384, 219, 594, 393, 383, 335, 356, 357, + 281, 308, 448, 376, 449, 307, 309, 405, 404, 406, + 207, 609, 627, 0, 208, 0, 500, 612, 654, 453, + 212, 234, 235, 237, 0, 280, 284, 292, 295, 304, + 305, 314, 368, 420, 447, 443, 452, 0, 580, 603, + 617, 629, 635, 636, 638, 639, 640, 641, 642, 645, + 643, 408, 312, 496, 334, 374, 0, 0, 426, 473, + 240, 607, 497, 199, 0, 0, 0, 0, 255, 256, + 0, 576, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 655, 656, 657, 658, 659, 660, 661, 662, 663, + 664, 665, 666, 667, 668, 669, 670, 671, 672, 650, + 509, 515, 510, 511, 512, 513, 514, 0, 516, 0, + 0, 0, 0, 0, 399, 0, 592, 593, 673, 385, + 487, 604, 336, 350, 353, 342, 362, 0, 363, 338, + 339, 344, 347, 348, 349, 354, 355, 359, 365, 249, + 210, 391, 400, 579, 313, 216, 217, 218, 525, 526, + 527, 528, 620, 621, 625, 205, 463, 464, 465, 466, + 293, 615, 310, 469, 468, 332, 333, 380, 450, 541, + 543, 554, 558, 560, 562, 568, 571, 542, 544, 555, + 559, 561, 563, 569, 572, 531, 533, 535, 537, 550, + 549, 546, 574, 575, 552, 557, 536, 548, 553, 566, + 573, 570, 530, 534, 538, 547, 565, 564, 545, 556, + 567, 551, 539, 532, 540, 0, 196, 221, 369, 0, + 455, 289, 651, 619, 485, 614, 206, 223, 0, 263, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 198, 200, 209, 222, 232, 236, 243, 262, 277, + 279, 286, 299, 311, 319, 320, 323, 329, 381, 387, + 388, 389, 390, 410, 411, 412, 415, 418, 419, 422, + 424, 425, 428, 432, 436, 437, 438, 440, 442, 444, + 456, 461, 475, 476, 477, 478, 479, 482, 483, 489, + 490, 491, 492, 493, 501, 502, 517, 587, 589, 606, + 626, 633, 481, 302, 303, 445, 446, 315, 316, 647, + 648, 301, 601, 634, 598, 646, 628, 439, 379, 0, + 0, 382, 282, 306, 321, 0, 618, 503, 227, 467, + 291, 251, 0, 0, 211, 246, 230, 260, 275, 278, + 325, 392, 401, 430, 435, 297, 272, 244, 460, 241, + 486, 520, 521, 522, 524, 396, 267, 434, 397, 0, + 377, 577, 578, 317, 0, 0, 0, 529, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 417, + 0, 0, 0, 0, 0, 0, 0, 271, 0, 0, + 0, 0, 367, 268, 0, 0, 204, 506, 0, 431, + 0, 203, 0, 488, 253, 378, 375, 584, 283, 274, + 270, 250, 318, 386, 429, 519, 423, 0, 371, 0, + 0, 498, 402, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 324, 248, 326, 202, 414, 499, 287, 0, 0, + 0, 0, 0, 508, 725, 0, 0, 0, 0, 0, 0, 0, 0, 0, 238, 0, 0, 245, 0, 0, - 0, 351, 360, 359, 339, 340, 342, 344, 350, 357, - 363, 336, 345, 0, 0, 609, 0, 0, 0, 265, - 321, 272, 264, 580, 0, 0, 0, 0, 0, 0, - 596, 0, 0, 229, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 275, 0, + 0, 352, 361, 360, 340, 341, 343, 345, 351, 358, + 364, 337, 346, 0, 0, 610, 0, 0, 0, 266, + 322, 273, 265, 581, 0, 0, 0, 0, 0, 0, + 597, 0, 0, 229, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 276, 0, + 0, 0, 0, 0, 0, 0, 0, 1093, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 298, 0, 403, 258, 0, 454, 0, 0, 0, 630, + 0, 0, 0, 0, 0, 0, 0, 366, 1099, 331, + 197, 225, 1097, 0, 413, 462, 474, 0, 0, 0, + 254, 0, 472, 427, 605, 233, 285, 459, 433, 470, + 441, 288, 0, 0, 471, 373, 586, 451, 602, 631, + 632, 264, 407, 616, 523, 624, 649, 226, 261, 421, + 507, 608, 495, 398, 582, 583, 330, 494, 296, 201, + 370, 637, 224, 480, 372, 242, 231, 588, 613, 300, + 252, 290, 457, 644, 213, 518, 599, 239, 484, 0, + 0, 652, 247, 505, 611, 600, 215, 595, 504, 394, + 327, 328, 214, 0, 458, 269, 294, 0, 0, 259, + 416, 590, 591, 257, 653, 228, 623, 220, 0, 622, + 409, 585, 596, 395, 384, 219, 594, 393, 383, 335, + 356, 357, 281, 308, 448, 376, 449, 307, 309, 405, + 404, 406, 207, 609, 627, 0, 208, 0, 500, 612, + 654, 453, 212, 234, 235, 237, 0, 280, 284, 292, + 295, 304, 305, 314, 368, 420, 447, 443, 452, 0, + 580, 603, 617, 629, 635, 636, 638, 639, 640, 641, + 642, 645, 643, 408, 312, 496, 334, 374, 0, 0, + 426, 473, 240, 607, 497, 199, 0, 0, 0, 0, + 255, 256, 0, 576, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 655, 656, 657, 658, 659, 660, 661, + 662, 663, 664, 665, 666, 667, 668, 669, 670, 671, + 672, 650, 509, 515, 510, 511, 512, 513, 514, 0, + 516, 0, 0, 0, 0, 0, 399, 0, 592, 593, + 673, 385, 487, 604, 336, 350, 353, 342, 362, 0, + 363, 338, 339, 344, 347, 348, 349, 354, 355, 359, + 365, 249, 210, 391, 400, 579, 313, 216, 217, 218, + 525, 526, 527, 528, 620, 621, 625, 205, 463, 464, + 465, 466, 293, 615, 310, 469, 468, 332, 333, 380, + 450, 541, 543, 554, 558, 560, 562, 568, 571, 542, + 544, 555, 559, 561, 563, 569, 572, 531, 533, 535, + 537, 550, 549, 546, 574, 575, 552, 557, 536, 548, + 553, 566, 573, 570, 530, 534, 538, 547, 565, 564, + 545, 556, 567, 551, 539, 532, 540, 0, 196, 221, + 369, 0, 455, 289, 651, 619, 485, 614, 206, 223, + 0, 263, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 198, 200, 209, 222, 232, 236, 243, + 262, 277, 279, 286, 299, 311, 319, 320, 323, 329, + 381, 387, 388, 389, 390, 410, 411, 412, 415, 418, + 419, 422, 424, 425, 428, 432, 436, 437, 438, 440, + 442, 444, 456, 461, 475, 476, 477, 478, 479, 482, + 483, 489, 490, 491, 492, 493, 501, 502, 517, 587, + 589, 606, 626, 633, 481, 302, 303, 445, 446, 315, + 316, 647, 648, 301, 601, 634, 598, 646, 628, 439, + 379, 0, 0, 382, 282, 306, 321, 0, 618, 503, + 227, 467, 291, 251, 0, 0, 211, 246, 230, 260, + 275, 278, 325, 392, 401, 430, 435, 297, 272, 244, + 460, 241, 486, 520, 521, 522, 524, 396, 267, 434, + 397, 0, 377, 577, 578, 317, 0, 0, 0, 529, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 417, 0, 0, 2374, 0, 0, 0, 0, 271, + 0, 0, 0, 0, 367, 268, 0, 0, 204, 506, + 0, 431, 0, 203, 0, 488, 253, 378, 375, 584, + 283, 274, 270, 250, 318, 386, 429, 519, 423, 0, + 371, 0, 0, 498, 402, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 324, 248, 326, 202, 414, 499, 287, + 0, 0, 0, 0, 1962, 508, 194, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 238, 0, 0, 245, + 0, 0, 0, 352, 361, 360, 340, 341, 343, 345, + 351, 358, 364, 337, 346, 0, 0, 610, 0, 0, + 0, 266, 322, 273, 265, 581, 0, 0, 0, 0, + 0, 0, 597, 0, 0, 229, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 276, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 298, 0, 403, 258, 0, 454, 0, 0, + 0, 630, 0, 0, 0, 0, 0, 0, 0, 366, + 0, 331, 197, 225, 0, 0, 413, 462, 474, 0, + 0, 0, 254, 0, 472, 427, 605, 233, 285, 459, + 433, 470, 441, 288, 0, 0, 471, 373, 586, 451, + 602, 631, 632, 264, 407, 616, 523, 624, 649, 226, + 261, 421, 507, 608, 495, 398, 582, 583, 330, 494, + 296, 201, 370, 637, 224, 480, 372, 242, 231, 588, + 613, 300, 252, 290, 457, 644, 213, 518, 599, 239, + 484, 0, 0, 652, 247, 505, 611, 600, 215, 595, + 504, 394, 327, 328, 214, 0, 458, 269, 294, 0, + 0, 259, 416, 590, 591, 257, 653, 228, 623, 220, + 0, 622, 409, 585, 596, 395, 384, 219, 594, 393, + 383, 335, 356, 357, 281, 308, 448, 376, 449, 307, + 309, 405, 404, 406, 207, 609, 627, 0, 208, 0, + 500, 612, 654, 453, 212, 234, 235, 237, 0, 280, + 284, 292, 295, 304, 305, 314, 368, 420, 447, 443, + 452, 0, 580, 603, 617, 629, 635, 636, 638, 639, + 640, 641, 642, 645, 643, 408, 312, 496, 334, 374, + 0, 0, 426, 473, 240, 607, 497, 199, 0, 0, + 0, 0, 255, 256, 0, 576, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 655, 656, 657, 658, 659, + 660, 661, 662, 663, 664, 665, 666, 667, 668, 669, + 670, 671, 672, 650, 509, 515, 510, 511, 512, 513, + 514, 0, 516, 0, 0, 0, 0, 0, 399, 0, + 592, 593, 673, 385, 487, 604, 336, 350, 353, 342, + 362, 0, 363, 338, 339, 344, 347, 348, 349, 354, + 355, 359, 365, 249, 210, 391, 400, 579, 313, 216, + 217, 218, 525, 526, 527, 528, 620, 621, 625, 205, + 463, 464, 465, 466, 293, 615, 310, 469, 468, 332, + 333, 380, 450, 541, 543, 554, 558, 560, 562, 568, + 571, 542, 544, 555, 559, 561, 563, 569, 572, 531, + 533, 535, 537, 550, 549, 546, 574, 575, 552, 557, + 536, 548, 553, 566, 573, 570, 530, 534, 538, 547, + 565, 564, 545, 556, 567, 551, 539, 532, 540, 0, + 196, 221, 369, 0, 455, 289, 651, 619, 485, 614, + 206, 223, 0, 263, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 198, 200, 209, 222, 232, + 236, 243, 262, 277, 279, 286, 299, 311, 319, 320, + 323, 329, 381, 387, 388, 389, 390, 410, 411, 412, + 415, 418, 419, 422, 424, 425, 428, 432, 436, 437, + 438, 440, 442, 444, 456, 461, 475, 476, 477, 478, + 479, 482, 483, 489, 490, 491, 492, 493, 501, 502, + 517, 587, 589, 606, 626, 633, 481, 302, 303, 445, + 446, 315, 316, 647, 648, 301, 601, 634, 598, 646, + 628, 439, 379, 0, 0, 382, 282, 306, 321, 0, + 618, 503, 227, 467, 291, 251, 0, 0, 211, 246, + 230, 260, 275, 278, 325, 392, 401, 430, 435, 297, + 272, 244, 460, 241, 486, 520, 521, 522, 524, 396, + 267, 434, 397, 0, 377, 577, 578, 317, 0, 0, + 0, 529, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 417, 0, 0, 0, 0, 0, 0, + 0, 271, 0, 0, 0, 0, 367, 268, 0, 0, + 204, 506, 0, 431, 0, 203, 0, 488, 253, 378, + 375, 584, 283, 274, 270, 250, 318, 386, 429, 519, + 423, 0, 371, 0, 0, 498, 402, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 324, 248, 326, 202, 414, + 499, 287, 0, 0, 0, 1754, 0, 508, 725, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 238, 0, + 0, 245, 0, 0, 0, 352, 361, 360, 340, 341, + 343, 345, 351, 358, 364, 337, 346, 0, 0, 610, + 0, 0, 0, 266, 322, 273, 265, 581, 0, 0, + 0, 0, 0, 0, 597, 0, 0, 229, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 276, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 297, 0, 402, 257, 0, 453, 0, 0, 0, 629, - 0, 0, 0, 0, 0, 0, 0, 365, 0, 330, - 197, 225, 0, 0, 412, 461, 473, 0, 0, 0, - 253, 0, 471, 426, 604, 233, 284, 458, 432, 469, - 440, 287, 0, 0, 470, 372, 585, 450, 601, 630, - 631, 263, 406, 615, 522, 623, 648, 226, 260, 420, - 506, 607, 494, 397, 581, 582, 329, 493, 295, 201, - 369, 636, 224, 479, 371, 242, 231, 587, 612, 299, - 289, 456, 643, 213, 517, 598, 239, 483, 0, 0, - 651, 247, 504, 610, 599, 215, 594, 503, 393, 326, - 327, 214, 0, 457, 268, 293, 0, 0, 258, 415, - 589, 590, 256, 652, 228, 622, 220, 0, 621, 408, - 584, 595, 394, 383, 219, 593, 392, 382, 334, 355, - 356, 280, 307, 447, 375, 448, 306, 308, 404, 403, - 405, 207, 608, 626, 0, 208, 0, 499, 611, 653, - 452, 212, 234, 235, 237, 0, 279, 283, 291, 294, - 303, 304, 313, 367, 419, 446, 442, 451, 0, 579, - 602, 616, 628, 634, 635, 637, 638, 639, 640, 641, - 644, 642, 407, 311, 495, 333, 373, 0, 0, 425, - 472, 240, 606, 496, 199, 0, 0, 0, 0, 254, - 255, 0, 575, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 654, 655, 656, 657, 658, 659, 660, 661, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 298, 0, 403, 258, 0, 454, + 0, 0, 0, 630, 0, 0, 0, 3729, 0, 0, + 0, 366, 0, 331, 197, 225, 0, 0, 413, 462, + 474, 0, 0, 0, 254, 0, 472, 427, 605, 233, + 285, 459, 433, 470, 441, 288, 0, 0, 471, 373, + 586, 451, 602, 631, 632, 264, 407, 616, 523, 624, + 649, 226, 261, 421, 507, 608, 495, 398, 582, 583, + 330, 494, 296, 201, 370, 637, 224, 480, 372, 242, + 231, 588, 613, 300, 252, 290, 457, 644, 213, 518, + 599, 239, 484, 0, 0, 652, 247, 505, 611, 600, + 215, 595, 504, 394, 327, 328, 214, 0, 458, 269, + 294, 0, 0, 259, 416, 590, 591, 257, 653, 228, + 623, 220, 0, 622, 409, 585, 596, 395, 384, 219, + 594, 393, 383, 335, 356, 357, 281, 308, 448, 376, + 449, 307, 309, 405, 404, 406, 207, 609, 627, 0, + 208, 0, 500, 612, 654, 453, 212, 234, 235, 237, + 0, 280, 284, 292, 295, 304, 305, 314, 368, 420, + 447, 443, 452, 0, 580, 603, 617, 629, 635, 636, + 638, 639, 640, 641, 642, 645, 643, 408, 312, 496, + 334, 374, 0, 0, 426, 473, 240, 607, 497, 199, + 0, 0, 0, 0, 255, 256, 0, 576, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 655, 656, 657, + 658, 659, 660, 661, 662, 663, 664, 665, 666, 667, + 668, 669, 670, 671, 672, 650, 509, 515, 510, 511, + 512, 513, 514, 0, 516, 0, 0, 0, 0, 0, + 399, 0, 592, 593, 673, 385, 487, 604, 336, 350, + 353, 342, 362, 0, 363, 338, 339, 344, 347, 348, + 349, 354, 355, 359, 365, 249, 210, 391, 400, 579, + 313, 216, 217, 218, 525, 526, 527, 528, 620, 621, + 625, 205, 463, 464, 465, 466, 293, 615, 310, 469, + 468, 332, 333, 380, 450, 541, 543, 554, 558, 560, + 562, 568, 571, 542, 544, 555, 559, 561, 563, 569, + 572, 531, 533, 535, 537, 550, 549, 546, 574, 575, + 552, 557, 536, 548, 553, 566, 573, 570, 530, 534, + 538, 547, 565, 564, 545, 556, 567, 551, 539, 532, + 540, 0, 196, 221, 369, 0, 455, 289, 651, 619, + 485, 614, 206, 223, 0, 263, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 198, 200, 209, + 222, 232, 236, 243, 262, 277, 279, 286, 299, 311, + 319, 320, 323, 329, 381, 387, 388, 389, 390, 410, + 411, 412, 415, 418, 419, 422, 424, 425, 428, 432, + 436, 437, 438, 440, 442, 444, 456, 461, 475, 476, + 477, 478, 479, 482, 483, 489, 490, 491, 492, 493, + 501, 502, 517, 587, 589, 606, 626, 633, 481, 302, + 303, 445, 446, 315, 316, 647, 648, 301, 601, 634, + 598, 646, 628, 439, 379, 0, 0, 382, 282, 306, + 321, 0, 618, 503, 227, 467, 291, 251, 0, 0, + 211, 246, 230, 260, 275, 278, 325, 392, 401, 430, + 435, 297, 272, 244, 460, 241, 486, 520, 521, 522, + 524, 396, 267, 434, 397, 0, 377, 577, 578, 317, + 0, 0, 0, 529, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 417, 0, 0, 0, 0, + 0, 0, 0, 271, 0, 0, 0, 0, 367, 268, + 0, 0, 204, 506, 0, 431, 0, 203, 0, 488, + 253, 378, 375, 584, 283, 274, 270, 250, 318, 386, + 429, 519, 423, 0, 371, 0, 0, 498, 402, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 324, 248, 326, + 202, 414, 499, 287, 0, 0, 0, 0, 2133, 508, + 725, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 238, 0, 0, 245, 0, 0, 0, 352, 361, 360, + 340, 341, 343, 345, 351, 358, 364, 337, 346, 0, + 0, 610, 0, 0, 0, 266, 322, 273, 265, 581, + 0, 0, 0, 0, 0, 0, 597, 0, 0, 229, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 276, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 2134, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 298, 0, 403, 258, + 0, 454, 0, 0, 0, 630, 0, 0, 0, 0, + 0, 0, 0, 366, 0, 331, 197, 225, 0, 0, + 413, 462, 474, 0, 0, 0, 254, 0, 472, 427, + 605, 233, 285, 459, 433, 470, 441, 288, 0, 0, + 471, 373, 586, 451, 602, 631, 632, 264, 407, 616, + 523, 624, 649, 226, 261, 421, 507, 608, 495, 398, + 582, 583, 330, 494, 296, 201, 370, 637, 224, 480, + 372, 242, 231, 588, 613, 300, 252, 290, 457, 644, + 213, 518, 599, 239, 484, 0, 0, 652, 247, 505, + 611, 600, 215, 595, 504, 394, 327, 328, 214, 0, + 458, 269, 294, 0, 0, 259, 416, 590, 591, 257, + 653, 228, 623, 220, 0, 622, 409, 585, 596, 395, + 384, 219, 594, 393, 383, 335, 356, 357, 281, 308, + 448, 376, 449, 307, 309, 405, 404, 406, 207, 609, + 627, 0, 208, 0, 500, 612, 654, 453, 212, 234, + 235, 237, 0, 280, 284, 292, 295, 304, 305, 314, + 368, 420, 447, 443, 452, 0, 580, 603, 617, 629, + 635, 636, 638, 639, 640, 641, 642, 645, 643, 408, + 312, 496, 334, 374, 0, 0, 426, 473, 240, 607, + 497, 199, 0, 0, 0, 0, 255, 256, 0, 576, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 655, + 656, 657, 658, 659, 660, 661, 662, 663, 664, 665, + 666, 667, 668, 669, 670, 671, 672, 650, 509, 515, + 510, 511, 512, 513, 514, 0, 516, 0, 0, 0, + 0, 0, 399, 0, 592, 593, 673, 385, 487, 604, + 336, 350, 353, 342, 362, 0, 363, 338, 339, 344, + 347, 348, 349, 354, 355, 359, 365, 249, 210, 391, + 400, 579, 313, 216, 217, 218, 525, 526, 527, 528, + 620, 621, 625, 205, 463, 464, 465, 466, 293, 615, + 310, 469, 468, 332, 333, 380, 450, 541, 543, 554, + 558, 560, 562, 568, 571, 542, 544, 555, 559, 561, + 563, 569, 572, 531, 533, 535, 537, 550, 549, 546, + 574, 575, 552, 557, 536, 548, 553, 566, 573, 570, + 530, 534, 538, 547, 565, 564, 545, 556, 567, 551, + 539, 532, 540, 0, 196, 221, 369, 0, 455, 289, + 651, 619, 485, 614, 206, 223, 0, 263, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 198, + 200, 209, 222, 232, 236, 243, 262, 277, 279, 286, + 299, 311, 319, 320, 323, 329, 381, 387, 388, 389, + 390, 410, 411, 412, 415, 418, 419, 422, 424, 425, + 428, 432, 436, 437, 438, 440, 442, 444, 456, 461, + 475, 476, 477, 478, 479, 482, 483, 489, 490, 491, + 492, 493, 501, 502, 517, 587, 589, 606, 626, 633, + 481, 302, 303, 445, 446, 315, 316, 647, 648, 301, + 601, 634, 598, 646, 628, 439, 379, 0, 0, 382, + 282, 306, 321, 0, 618, 503, 227, 467, 291, 251, + 0, 0, 211, 246, 230, 260, 275, 278, 325, 392, + 401, 430, 435, 297, 272, 244, 460, 241, 486, 520, + 521, 522, 524, 396, 267, 434, 397, 0, 377, 577, + 578, 317, 0, 0, 0, 529, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 417, 0, 0, + 0, 0, 0, 0, 0, 271, 0, 0, 0, 0, + 367, 268, 0, 0, 204, 506, 0, 431, 0, 203, + 0, 488, 253, 378, 375, 584, 283, 274, 270, 250, + 318, 386, 429, 519, 423, 0, 371, 0, 0, 498, + 402, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 324, + 248, 326, 202, 414, 499, 287, 0, 0, 0, 0, + 2881, 508, 725, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 238, 0, 0, 245, 0, 0, 0, 352, + 361, 360, 340, 341, 343, 345, 351, 358, 364, 337, + 346, 0, 0, 610, 0, 0, 0, 266, 322, 273, + 265, 581, 0, 0, 0, 0, 0, 0, 597, 0, + 0, 229, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 276, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 2882, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 298, 0, + 403, 258, 0, 454, 0, 0, 0, 630, 0, 0, + 0, 0, 0, 0, 0, 366, 0, 331, 197, 225, + 0, 0, 413, 462, 474, 0, 0, 0, 254, 0, + 472, 427, 605, 233, 285, 459, 433, 470, 441, 288, + 0, 0, 471, 373, 586, 451, 602, 631, 632, 264, + 407, 616, 523, 624, 649, 226, 261, 421, 507, 608, + 495, 398, 582, 583, 330, 494, 296, 201, 370, 637, + 224, 480, 372, 242, 231, 588, 613, 300, 252, 290, + 457, 644, 213, 518, 599, 239, 484, 0, 0, 652, + 247, 505, 611, 600, 215, 595, 504, 394, 327, 328, + 214, 0, 458, 269, 294, 0, 0, 259, 416, 590, + 591, 257, 653, 228, 623, 220, 0, 622, 409, 585, + 596, 395, 384, 219, 594, 393, 383, 335, 356, 357, + 281, 308, 448, 376, 449, 307, 309, 405, 404, 406, + 207, 609, 627, 0, 208, 0, 500, 612, 654, 453, + 212, 234, 235, 237, 0, 280, 284, 292, 295, 304, + 305, 314, 368, 420, 447, 443, 452, 0, 580, 603, + 617, 629, 635, 636, 638, 639, 640, 641, 642, 645, + 643, 408, 312, 496, 334, 374, 0, 0, 426, 473, + 240, 607, 497, 199, 0, 0, 0, 0, 255, 256, + 0, 576, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 655, 656, 657, 658, 659, 660, 661, 662, 663, + 664, 665, 666, 667, 668, 669, 670, 671, 672, 650, + 509, 515, 510, 511, 512, 513, 514, 0, 516, 0, + 0, 0, 0, 0, 399, 0, 592, 593, 673, 385, + 487, 604, 336, 350, 353, 342, 362, 0, 363, 338, + 339, 344, 347, 348, 349, 354, 355, 359, 365, 249, + 210, 391, 400, 579, 313, 216, 217, 218, 525, 526, + 527, 528, 620, 621, 625, 205, 463, 464, 465, 466, + 293, 615, 310, 469, 468, 332, 333, 380, 450, 541, + 543, 554, 558, 560, 562, 568, 571, 542, 544, 555, + 559, 561, 563, 569, 572, 531, 533, 535, 537, 550, + 549, 546, 574, 575, 552, 557, 536, 548, 553, 566, + 573, 570, 530, 534, 538, 547, 565, 564, 545, 556, + 567, 551, 539, 532, 540, 0, 196, 221, 369, 0, + 455, 289, 651, 619, 485, 614, 206, 223, 0, 263, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 198, 200, 209, 222, 232, 236, 243, 262, 277, + 279, 286, 299, 311, 319, 320, 323, 329, 381, 387, + 388, 389, 390, 410, 411, 412, 415, 418, 419, 422, + 424, 425, 428, 432, 436, 437, 438, 440, 442, 444, + 456, 461, 475, 476, 477, 478, 479, 482, 483, 489, + 490, 491, 492, 493, 501, 502, 517, 587, 589, 606, + 626, 633, 481, 302, 303, 445, 446, 315, 316, 647, + 648, 301, 601, 634, 598, 646, 628, 439, 379, 0, + 0, 382, 282, 306, 321, 0, 618, 503, 227, 467, + 291, 251, 0, 0, 211, 246, 230, 260, 275, 278, + 325, 392, 401, 430, 435, 297, 272, 244, 460, 241, + 486, 520, 521, 522, 524, 396, 267, 434, 397, 0, + 377, 577, 578, 317, 0, 0, 0, 529, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 417, + 0, 0, 0, 0, 0, 0, 0, 271, 0, 0, + 0, 0, 367, 268, 0, 0, 204, 506, 0, 431, + 0, 203, 0, 488, 253, 378, 375, 584, 283, 274, + 270, 250, 318, 386, 429, 519, 423, 0, 371, 0, + 0, 498, 402, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 324, 248, 326, 202, 414, 499, 287, 0, 0, + 0, 0, 0, 508, 725, 0, 0, 0, 0, 2866, + 0, 0, 0, 0, 238, 0, 0, 245, 2867, 0, + 0, 352, 361, 360, 340, 341, 343, 345, 351, 358, + 364, 337, 346, 0, 0, 610, 0, 0, 0, 266, + 322, 273, 265, 581, 0, 0, 0, 0, 0, 0, + 597, 0, 0, 229, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 276, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 298, 0, 403, 258, 0, 454, 0, 0, 0, 630, + 0, 0, 0, 0, 0, 0, 0, 366, 0, 331, + 197, 225, 0, 0, 413, 462, 474, 0, 0, 0, + 254, 0, 472, 427, 605, 233, 285, 459, 433, 470, + 441, 288, 0, 0, 471, 373, 586, 451, 602, 631, + 632, 264, 407, 616, 523, 624, 649, 226, 261, 421, + 507, 608, 495, 398, 582, 583, 330, 494, 296, 201, + 370, 637, 224, 480, 372, 242, 231, 588, 613, 300, + 252, 290, 457, 644, 213, 518, 599, 239, 484, 0, + 0, 652, 247, 505, 611, 600, 215, 595, 504, 394, + 327, 328, 214, 0, 458, 269, 294, 0, 0, 259, + 416, 590, 591, 257, 653, 228, 623, 220, 0, 622, + 409, 585, 596, 395, 384, 219, 594, 393, 383, 335, + 356, 357, 281, 308, 448, 376, 449, 307, 309, 405, + 404, 406, 207, 609, 627, 0, 208, 0, 500, 612, + 654, 453, 212, 234, 235, 237, 0, 280, 284, 292, + 295, 304, 305, 314, 368, 420, 447, 443, 452, 0, + 580, 603, 617, 629, 635, 636, 638, 639, 640, 641, + 642, 645, 643, 408, 312, 496, 334, 374, 0, 0, + 426, 473, 240, 607, 497, 199, 0, 0, 0, 0, + 255, 256, 0, 576, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 655, 656, 657, 658, 659, 660, 661, 662, 663, 664, 665, 666, 667, 668, 669, 670, 671, - 649, 508, 514, 509, 510, 511, 512, 513, 0, 515, - 0, 0, 0, 0, 0, 398, 0, 591, 592, 672, - 384, 486, 603, 335, 349, 352, 341, 361, 0, 362, - 337, 338, 343, 346, 347, 348, 353, 354, 358, 364, - 249, 210, 390, 399, 578, 312, 216, 217, 218, 524, - 525, 526, 527, 619, 620, 624, 205, 462, 463, 464, - 465, 292, 614, 309, 468, 467, 331, 332, 379, 449, - 540, 542, 553, 557, 559, 561, 567, 570, 541, 543, - 554, 558, 560, 562, 568, 571, 530, 532, 534, 536, - 549, 548, 545, 573, 574, 551, 556, 535, 547, 552, - 565, 572, 569, 529, 533, 537, 546, 564, 563, 544, - 555, 566, 550, 538, 531, 539, 0, 196, 221, 368, - 0, 454, 288, 650, 618, 484, 613, 206, 223, 0, - 262, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 198, 200, 209, 222, 232, 236, 243, 261, - 276, 278, 285, 298, 310, 318, 319, 322, 328, 380, - 386, 387, 388, 389, 409, 410, 411, 414, 417, 418, - 421, 423, 424, 427, 431, 435, 436, 437, 439, 441, - 443, 455, 460, 474, 475, 476, 477, 478, 481, 482, - 488, 489, 490, 491, 492, 500, 501, 516, 586, 588, - 605, 625, 632, 480, 301, 302, 444, 445, 314, 315, - 646, 647, 300, 600, 633, 597, 645, 627, 438, 378, - 0, 0, 381, 281, 305, 320, 0, 617, 502, 227, - 466, 290, 251, 0, 0, 211, 246, 230, 259, 274, - 277, 324, 391, 400, 429, 434, 296, 271, 244, 459, - 241, 485, 519, 520, 521, 523, 395, 266, 433, 396, - 0, 376, 576, 577, 316, 0, 0, 0, 528, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 416, 0, 0, 0, 0, 0, 0, 0, 270, 0, - 0, 0, 0, 366, 267, 0, 0, 204, 505, 0, - 430, 0, 203, 0, 487, 252, 377, 374, 583, 282, - 273, 269, 250, 317, 385, 428, 518, 422, 0, 370, - 0, 0, 497, 401, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 323, 248, 325, 202, 413, 498, 286, 0, - 0, 0, 1753, 0, 507, 724, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 238, 0, 0, 245, 0, - 0, 0, 351, 360, 359, 339, 340, 342, 344, 350, - 357, 363, 336, 345, 0, 0, 609, 0, 0, 0, - 265, 321, 272, 264, 580, 0, 0, 0, 0, 0, - 0, 596, 0, 0, 229, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 275, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 297, 0, 402, 257, 0, 453, 0, 0, 0, - 629, 0, 0, 0, 3726, 0, 0, 0, 365, 0, - 330, 197, 225, 0, 0, 412, 461, 473, 0, 0, - 0, 253, 0, 471, 426, 604, 233, 284, 458, 432, - 469, 440, 287, 0, 0, 470, 372, 585, 450, 601, - 630, 631, 263, 406, 615, 522, 623, 648, 226, 260, - 420, 506, 607, 494, 397, 581, 582, 329, 493, 295, - 201, 369, 636, 224, 479, 371, 242, 231, 587, 612, - 299, 289, 456, 643, 213, 517, 598, 239, 483, 0, - 0, 651, 247, 504, 610, 599, 215, 594, 503, 393, - 326, 327, 214, 0, 457, 268, 293, 0, 0, 258, - 415, 589, 590, 256, 652, 228, 622, 220, 0, 621, - 408, 584, 595, 394, 383, 219, 593, 392, 382, 334, - 355, 356, 280, 307, 447, 375, 448, 306, 308, 404, - 403, 405, 207, 608, 626, 0, 208, 0, 499, 611, - 653, 452, 212, 234, 235, 237, 0, 279, 283, 291, - 294, 303, 304, 313, 367, 419, 446, 442, 451, 0, - 579, 602, 616, 628, 634, 635, 637, 638, 639, 640, - 641, 644, 642, 407, 311, 495, 333, 373, 0, 0, - 425, 472, 240, 606, 496, 199, 0, 0, 0, 0, - 254, 255, 0, 575, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 654, 655, 656, 657, 658, 659, 660, - 661, 662, 663, 664, 665, 666, 667, 668, 669, 670, - 671, 649, 508, 514, 509, 510, 511, 512, 513, 0, - 515, 0, 0, 0, 0, 0, 398, 0, 591, 592, - 672, 384, 486, 603, 335, 349, 352, 341, 361, 0, - 362, 337, 338, 343, 346, 347, 348, 353, 354, 358, - 364, 249, 210, 390, 399, 578, 312, 216, 217, 218, - 524, 525, 526, 527, 619, 620, 624, 205, 462, 463, - 464, 465, 292, 614, 309, 468, 467, 331, 332, 379, - 449, 540, 542, 553, 557, 559, 561, 567, 570, 541, - 543, 554, 558, 560, 562, 568, 571, 530, 532, 534, - 536, 549, 548, 545, 573, 574, 551, 556, 535, 547, - 552, 565, 572, 569, 529, 533, 537, 546, 564, 563, - 544, 555, 566, 550, 538, 531, 539, 0, 196, 221, - 368, 0, 454, 288, 650, 618, 484, 613, 206, 223, - 0, 262, 0, 0, 0, 0, 0, 0, 0, 0, + 672, 650, 509, 515, 510, 511, 512, 513, 514, 0, + 516, 0, 0, 0, 0, 0, 399, 0, 592, 593, + 673, 385, 487, 604, 336, 350, 353, 342, 362, 0, + 363, 338, 339, 344, 347, 348, 349, 354, 355, 359, + 365, 249, 210, 391, 400, 579, 313, 216, 217, 218, + 525, 526, 527, 528, 620, 621, 625, 205, 463, 464, + 465, 466, 293, 615, 310, 469, 468, 332, 333, 380, + 450, 541, 543, 554, 558, 560, 562, 568, 571, 542, + 544, 555, 559, 561, 563, 569, 572, 531, 533, 535, + 537, 550, 549, 546, 574, 575, 552, 557, 536, 548, + 553, 566, 573, 570, 530, 534, 538, 547, 565, 564, + 545, 556, 567, 551, 539, 532, 540, 0, 196, 221, + 369, 0, 455, 289, 651, 619, 485, 614, 206, 223, + 0, 263, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 198, 200, 209, 222, 232, 236, 243, - 261, 276, 278, 285, 298, 310, 318, 319, 322, 328, - 380, 386, 387, 388, 389, 409, 410, 411, 414, 417, - 418, 421, 423, 424, 427, 431, 435, 436, 437, 439, - 441, 443, 455, 460, 474, 475, 476, 477, 478, 481, - 482, 488, 489, 490, 491, 492, 500, 501, 516, 586, - 588, 605, 625, 632, 480, 301, 302, 444, 445, 314, - 315, 646, 647, 300, 600, 633, 597, 645, 627, 438, - 378, 0, 0, 381, 281, 305, 320, 0, 617, 502, - 227, 466, 290, 251, 0, 0, 211, 246, 230, 259, - 274, 277, 324, 391, 400, 429, 434, 296, 271, 244, - 459, 241, 485, 519, 520, 521, 523, 395, 266, 433, - 396, 0, 376, 576, 577, 316, 0, 0, 0, 528, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 416, 0, 0, 0, 0, 0, 0, 0, 270, - 0, 0, 0, 0, 366, 267, 0, 0, 204, 505, - 0, 430, 0, 203, 0, 487, 252, 377, 374, 583, - 282, 273, 269, 250, 317, 385, 428, 518, 422, 0, - 370, 0, 0, 497, 401, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 323, 248, 325, 202, 413, 498, 286, - 0, 0, 0, 0, 2132, 507, 724, 0, 0, 0, + 262, 277, 279, 286, 299, 311, 319, 320, 323, 329, + 381, 387, 388, 389, 390, 410, 411, 412, 415, 418, + 419, 422, 424, 425, 428, 432, 436, 437, 438, 440, + 442, 444, 456, 461, 475, 476, 477, 478, 479, 482, + 483, 489, 490, 491, 492, 493, 501, 502, 517, 587, + 589, 606, 626, 633, 481, 302, 303, 445, 446, 315, + 316, 647, 648, 301, 601, 634, 598, 646, 628, 439, + 379, 0, 0, 382, 282, 306, 321, 0, 618, 503, + 227, 467, 291, 251, 0, 0, 211, 246, 230, 260, + 275, 278, 325, 392, 401, 430, 435, 297, 272, 244, + 460, 241, 486, 520, 521, 522, 524, 396, 267, 434, + 397, 0, 377, 577, 578, 317, 0, 0, 0, 529, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 417, 0, 0, 0, 0, 0, 0, 0, 271, + 1800, 0, 0, 0, 367, 268, 0, 0, 204, 506, + 0, 431, 0, 203, 0, 488, 253, 378, 375, 584, + 283, 274, 270, 250, 318, 386, 429, 519, 423, 0, + 371, 0, 0, 498, 402, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 324, 248, 326, 202, 414, 499, 287, + 0, 0, 0, 0, 1799, 508, 725, 0, 0, 0, 0, 0, 0, 0, 0, 0, 238, 0, 0, 245, - 0, 0, 0, 351, 360, 359, 339, 340, 342, 344, - 350, 357, 363, 336, 345, 0, 0, 609, 0, 0, - 0, 265, 321, 272, 264, 580, 0, 0, 0, 0, - 0, 0, 596, 0, 0, 229, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 275, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 2133, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 297, 0, 402, 257, 0, 453, 0, 0, - 0, 629, 0, 0, 0, 0, 0, 0, 0, 365, - 0, 330, 197, 225, 0, 0, 412, 461, 473, 0, - 0, 0, 253, 0, 471, 426, 604, 233, 284, 458, - 432, 469, 440, 287, 0, 0, 470, 372, 585, 450, - 601, 630, 631, 263, 406, 615, 522, 623, 648, 226, - 260, 420, 506, 607, 494, 397, 581, 582, 329, 493, - 295, 201, 369, 636, 224, 479, 371, 242, 231, 587, - 612, 299, 289, 456, 643, 213, 517, 598, 239, 483, - 0, 0, 651, 247, 504, 610, 599, 215, 594, 503, - 393, 326, 327, 214, 0, 457, 268, 293, 0, 0, - 258, 415, 589, 590, 256, 652, 228, 622, 220, 0, - 621, 408, 584, 595, 394, 383, 219, 593, 392, 382, - 334, 355, 356, 280, 307, 447, 375, 448, 306, 308, - 404, 403, 405, 207, 608, 626, 0, 208, 0, 499, - 611, 653, 452, 212, 234, 235, 237, 0, 279, 283, - 291, 294, 303, 304, 313, 367, 419, 446, 442, 451, - 0, 579, 602, 616, 628, 634, 635, 637, 638, 639, - 640, 641, 644, 642, 407, 311, 495, 333, 373, 0, - 0, 425, 472, 240, 606, 496, 199, 0, 0, 0, - 0, 254, 255, 0, 575, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 654, 655, 656, 657, 658, 659, + 0, 0, 0, 352, 361, 360, 340, 341, 343, 345, + 351, 358, 364, 337, 346, 0, 0, 610, 0, 0, + 0, 266, 322, 273, 265, 581, 0, 0, 0, 0, + 0, 0, 597, 0, 0, 229, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 276, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 298, 0, 403, 258, 0, 454, 0, 0, + 0, 630, 0, 0, 0, 0, 0, 0, 0, 366, + 0, 331, 197, 225, 0, 0, 413, 462, 474, 0, + 0, 0, 254, 0, 472, 427, 605, 233, 285, 459, + 433, 470, 441, 288, 0, 0, 471, 373, 586, 451, + 602, 631, 632, 264, 407, 616, 523, 624, 649, 226, + 261, 421, 507, 608, 495, 398, 582, 583, 330, 494, + 296, 201, 370, 637, 224, 480, 372, 242, 231, 588, + 613, 300, 252, 290, 457, 644, 213, 518, 599, 239, + 484, 0, 0, 652, 247, 505, 611, 600, 215, 595, + 504, 394, 327, 328, 214, 0, 458, 269, 294, 0, + 0, 259, 416, 590, 591, 257, 653, 228, 623, 220, + 0, 622, 409, 585, 596, 395, 384, 219, 594, 393, + 383, 335, 356, 357, 281, 308, 448, 376, 449, 307, + 309, 405, 404, 406, 207, 609, 627, 0, 208, 0, + 500, 612, 654, 453, 212, 234, 235, 237, 0, 280, + 284, 292, 295, 304, 305, 314, 368, 420, 447, 443, + 452, 0, 580, 603, 617, 629, 635, 636, 638, 639, + 640, 641, 642, 645, 643, 408, 312, 496, 334, 374, + 0, 0, 426, 473, 240, 607, 497, 199, 0, 0, + 0, 0, 255, 256, 0, 576, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 655, 656, 657, 658, 659, 660, 661, 662, 663, 664, 665, 666, 667, 668, 669, - 670, 671, 649, 508, 514, 509, 510, 511, 512, 513, - 0, 515, 0, 0, 0, 0, 0, 398, 0, 591, - 592, 672, 384, 486, 603, 335, 349, 352, 341, 361, - 0, 362, 337, 338, 343, 346, 347, 348, 353, 354, - 358, 364, 249, 210, 390, 399, 578, 312, 216, 217, - 218, 524, 525, 526, 527, 619, 620, 624, 205, 462, - 463, 464, 465, 292, 614, 309, 468, 467, 331, 332, - 379, 449, 540, 542, 553, 557, 559, 561, 567, 570, - 541, 543, 554, 558, 560, 562, 568, 571, 530, 532, - 534, 536, 549, 548, 545, 573, 574, 551, 556, 535, - 547, 552, 565, 572, 569, 529, 533, 537, 546, 564, - 563, 544, 555, 566, 550, 538, 531, 539, 0, 196, - 221, 368, 0, 454, 288, 650, 618, 484, 613, 206, - 223, 0, 262, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 198, 200, 209, 222, 232, 236, - 243, 261, 276, 278, 285, 298, 310, 318, 319, 322, - 328, 380, 386, 387, 388, 389, 409, 410, 411, 414, - 417, 418, 421, 423, 424, 427, 431, 435, 436, 437, - 439, 441, 443, 455, 460, 474, 475, 476, 477, 478, - 481, 482, 488, 489, 490, 491, 492, 500, 501, 516, - 586, 588, 605, 625, 632, 480, 301, 302, 444, 445, - 314, 315, 646, 647, 300, 600, 633, 597, 645, 627, - 438, 378, 0, 0, 381, 281, 305, 320, 0, 617, - 502, 227, 466, 290, 251, 0, 0, 211, 246, 230, - 259, 274, 277, 324, 391, 400, 429, 434, 296, 271, - 244, 459, 241, 485, 519, 520, 521, 523, 395, 266, - 433, 396, 0, 376, 576, 577, 316, 0, 0, 0, - 528, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 416, 0, 0, 0, 0, 0, 0, 0, - 270, 0, 0, 0, 0, 366, 267, 0, 0, 204, - 505, 0, 430, 0, 203, 0, 487, 252, 377, 374, - 583, 282, 273, 269, 250, 317, 385, 428, 518, 422, - 0, 370, 0, 0, 497, 401, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 323, 248, 325, 202, 413, 498, - 286, 0, 0, 0, 0, 2879, 507, 724, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 238, 0, 0, - 245, 0, 0, 0, 351, 360, 359, 339, 340, 342, - 344, 350, 357, 363, 336, 345, 0, 0, 609, 0, - 0, 0, 265, 321, 272, 264, 580, 0, 0, 0, - 0, 0, 0, 596, 0, 0, 229, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 275, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 2880, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 297, 0, 402, 257, 0, 453, 0, - 0, 0, 629, 0, 0, 0, 0, 0, 0, 0, - 365, 0, 330, 197, 225, 0, 0, 412, 461, 473, - 0, 0, 0, 253, 0, 471, 426, 604, 233, 284, - 458, 432, 469, 440, 287, 0, 0, 470, 372, 585, - 450, 601, 630, 631, 263, 406, 615, 522, 623, 648, - 226, 260, 420, 506, 607, 494, 397, 581, 582, 329, - 493, 295, 201, 369, 636, 224, 479, 371, 242, 231, - 587, 612, 299, 289, 456, 643, 213, 517, 598, 239, - 483, 0, 0, 651, 247, 504, 610, 599, 215, 594, - 503, 393, 326, 327, 214, 0, 457, 268, 293, 0, - 0, 258, 415, 589, 590, 256, 652, 228, 622, 220, - 0, 621, 408, 584, 595, 394, 383, 219, 593, 392, - 382, 334, 355, 356, 280, 307, 447, 375, 448, 306, - 308, 404, 403, 405, 207, 608, 626, 0, 208, 0, - 499, 611, 653, 452, 212, 234, 235, 237, 0, 279, - 283, 291, 294, 303, 304, 313, 367, 419, 446, 442, - 451, 0, 579, 602, 616, 628, 634, 635, 637, 638, - 639, 640, 641, 644, 642, 407, 311, 495, 333, 373, - 0, 0, 425, 472, 240, 606, 496, 199, 0, 0, - 0, 0, 254, 255, 0, 575, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 654, 655, 656, 657, 658, - 659, 660, 661, 662, 663, 664, 665, 666, 667, 668, - 669, 670, 671, 649, 508, 514, 509, 510, 511, 512, - 513, 0, 515, 0, 0, 0, 0, 0, 398, 0, - 591, 592, 672, 384, 486, 603, 335, 349, 352, 341, - 361, 0, 362, 337, 338, 343, 346, 347, 348, 353, - 354, 358, 364, 249, 210, 390, 399, 578, 312, 216, - 217, 218, 524, 525, 526, 527, 619, 620, 624, 205, - 462, 463, 464, 465, 292, 614, 309, 468, 467, 331, - 332, 379, 449, 540, 542, 553, 557, 559, 561, 567, - 570, 541, 543, 554, 558, 560, 562, 568, 571, 530, - 532, 534, 536, 549, 548, 545, 573, 574, 551, 556, - 535, 547, 552, 565, 572, 569, 529, 533, 537, 546, - 564, 563, 544, 555, 566, 550, 538, 531, 539, 0, - 196, 221, 368, 0, 454, 288, 650, 618, 484, 613, - 206, 223, 0, 262, 0, 0, 0, 0, 0, 0, + 670, 671, 672, 650, 509, 515, 510, 511, 512, 513, + 514, 0, 516, 0, 0, 0, 0, 0, 399, 0, + 592, 593, 673, 385, 487, 604, 336, 350, 353, 342, + 362, 0, 363, 338, 339, 344, 347, 348, 349, 354, + 355, 359, 365, 249, 210, 391, 400, 579, 313, 216, + 217, 218, 525, 526, 527, 528, 620, 621, 625, 205, + 463, 464, 465, 466, 293, 615, 310, 469, 468, 332, + 333, 380, 450, 541, 543, 554, 558, 560, 562, 568, + 571, 542, 544, 555, 559, 561, 563, 569, 572, 531, + 533, 535, 537, 550, 549, 546, 574, 575, 552, 557, + 536, 548, 553, 566, 573, 570, 530, 534, 538, 547, + 565, 564, 545, 556, 567, 551, 539, 532, 540, 0, + 196, 221, 369, 0, 455, 289, 651, 619, 485, 614, + 206, 223, 0, 263, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 198, 200, 209, 222, 232, - 236, 243, 261, 276, 278, 285, 298, 310, 318, 319, - 322, 328, 380, 386, 387, 388, 389, 409, 410, 411, - 414, 417, 418, 421, 423, 424, 427, 431, 435, 436, - 437, 439, 441, 443, 455, 460, 474, 475, 476, 477, - 478, 481, 482, 488, 489, 490, 491, 492, 500, 501, - 516, 586, 588, 605, 625, 632, 480, 301, 302, 444, - 445, 314, 315, 646, 647, 300, 600, 633, 597, 645, - 627, 438, 378, 0, 0, 381, 281, 305, 320, 0, - 617, 502, 227, 466, 290, 251, 0, 0, 211, 246, - 230, 259, 274, 277, 324, 391, 400, 429, 434, 296, - 271, 244, 459, 241, 485, 519, 520, 521, 523, 395, - 266, 433, 396, 0, 376, 576, 577, 316, 0, 0, - 0, 528, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 416, 0, 0, 0, 0, 0, 0, - 0, 270, 0, 0, 0, 0, 366, 267, 0, 0, - 204, 505, 0, 430, 0, 203, 0, 487, 252, 377, - 374, 583, 282, 273, 269, 250, 317, 385, 428, 518, - 422, 0, 370, 0, 0, 497, 401, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 323, 248, 325, 202, 413, - 498, 286, 0, 0, 0, 0, 0, 507, 724, 0, - 0, 0, 0, 2864, 0, 0, 0, 0, 238, 0, - 0, 245, 2865, 0, 0, 351, 360, 359, 339, 340, - 342, 344, 350, 357, 363, 336, 345, 0, 0, 609, - 0, 0, 0, 265, 321, 272, 264, 580, 0, 0, - 0, 0, 0, 0, 596, 0, 0, 229, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 275, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 297, 0, 402, 257, 0, 453, - 0, 0, 0, 629, 0, 0, 0, 0, 0, 0, - 0, 365, 0, 330, 197, 225, 0, 0, 412, 461, - 473, 0, 0, 0, 253, 0, 471, 426, 604, 233, - 284, 458, 432, 469, 440, 287, 0, 0, 470, 372, - 585, 450, 601, 630, 631, 263, 406, 615, 522, 623, - 648, 226, 260, 420, 506, 607, 494, 397, 581, 582, - 329, 493, 295, 201, 369, 636, 224, 479, 371, 242, - 231, 587, 612, 299, 289, 456, 643, 213, 517, 598, - 239, 483, 0, 0, 651, 247, 504, 610, 599, 215, - 594, 503, 393, 326, 327, 214, 0, 457, 268, 293, - 0, 0, 258, 415, 589, 590, 256, 652, 228, 622, - 220, 0, 621, 408, 584, 595, 394, 383, 219, 593, - 392, 382, 334, 355, 356, 280, 307, 447, 375, 448, - 306, 308, 404, 403, 405, 207, 608, 626, 0, 208, - 0, 499, 611, 653, 452, 212, 234, 235, 237, 0, - 279, 283, 291, 294, 303, 304, 313, 367, 419, 446, - 442, 451, 0, 579, 602, 616, 628, 634, 635, 637, - 638, 639, 640, 641, 644, 642, 407, 311, 495, 333, - 373, 0, 0, 425, 472, 240, 606, 496, 199, 0, - 0, 0, 0, 254, 255, 0, 575, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 654, 655, 656, 657, + 236, 243, 262, 277, 279, 286, 299, 311, 319, 320, + 323, 329, 381, 387, 388, 389, 390, 410, 411, 412, + 415, 418, 419, 422, 424, 425, 428, 432, 436, 437, + 438, 440, 442, 444, 456, 461, 475, 476, 477, 478, + 479, 482, 483, 489, 490, 491, 492, 493, 501, 502, + 517, 587, 589, 606, 626, 633, 481, 302, 303, 445, + 446, 315, 316, 647, 648, 301, 601, 634, 598, 646, + 628, 439, 379, 0, 0, 382, 282, 306, 321, 0, + 618, 503, 227, 467, 291, 251, 0, 0, 211, 246, + 230, 260, 275, 278, 325, 392, 401, 430, 435, 297, + 272, 244, 460, 241, 486, 520, 521, 522, 524, 396, + 267, 434, 397, 0, 377, 577, 578, 317, 0, 0, + 0, 529, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 417, 0, 0, 0, 0, 0, 0, + 0, 271, 0, 0, 0, 0, 367, 268, 0, 0, + 204, 506, 0, 431, 0, 203, 0, 488, 253, 378, + 375, 584, 283, 274, 270, 250, 318, 386, 429, 519, + 423, 0, 371, 0, 0, 498, 402, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 324, 248, 326, 202, 414, + 499, 287, 0, 0, 0, 0, 0, 508, 727, 728, + 729, 0, 0, 0, 0, 0, 0, 0, 238, 0, + 0, 245, 0, 0, 0, 352, 361, 360, 340, 341, + 343, 345, 351, 358, 364, 337, 346, 0, 0, 610, + 0, 0, 0, 266, 322, 273, 265, 581, 0, 0, + 0, 0, 0, 0, 597, 0, 0, 229, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 276, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 298, 0, 403, 258, 0, 454, + 0, 0, 0, 630, 0, 0, 0, 0, 0, 0, + 0, 366, 0, 331, 197, 225, 0, 0, 413, 462, + 474, 0, 0, 0, 254, 0, 472, 427, 605, 233, + 285, 459, 433, 470, 441, 288, 0, 0, 471, 373, + 586, 451, 602, 631, 632, 264, 407, 616, 523, 624, + 649, 226, 261, 421, 507, 608, 495, 398, 582, 583, + 330, 494, 296, 201, 370, 637, 224, 480, 372, 242, + 231, 588, 613, 300, 252, 290, 457, 644, 213, 518, + 599, 239, 484, 0, 0, 652, 247, 505, 611, 600, + 215, 595, 504, 394, 327, 328, 214, 0, 458, 269, + 294, 0, 0, 259, 416, 590, 591, 257, 653, 228, + 623, 220, 0, 622, 409, 585, 596, 395, 384, 219, + 594, 393, 383, 335, 356, 357, 281, 308, 448, 376, + 449, 307, 309, 405, 404, 406, 207, 609, 627, 0, + 208, 0, 500, 612, 654, 453, 212, 234, 235, 237, + 0, 280, 284, 292, 295, 304, 305, 314, 368, 420, + 447, 443, 452, 0, 580, 603, 617, 629, 635, 636, + 638, 639, 640, 641, 642, 645, 643, 408, 312, 496, + 334, 374, 0, 0, 426, 473, 240, 607, 497, 199, + 0, 0, 0, 0, 255, 256, 0, 576, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 655, 656, 657, 658, 659, 660, 661, 662, 663, 664, 665, 666, 667, - 668, 669, 670, 671, 649, 508, 514, 509, 510, 511, - 512, 513, 0, 515, 0, 0, 0, 0, 0, 398, - 0, 591, 592, 672, 384, 486, 603, 335, 349, 352, - 341, 361, 0, 362, 337, 338, 343, 346, 347, 348, - 353, 354, 358, 364, 249, 210, 390, 399, 578, 312, - 216, 217, 218, 524, 525, 526, 527, 619, 620, 624, - 205, 462, 463, 464, 465, 292, 614, 309, 468, 467, - 331, 332, 379, 449, 540, 542, 553, 557, 559, 561, - 567, 570, 541, 543, 554, 558, 560, 562, 568, 571, - 530, 532, 534, 536, 549, 548, 545, 573, 574, 551, - 556, 535, 547, 552, 565, 572, 569, 529, 533, 537, - 546, 564, 563, 544, 555, 566, 550, 538, 531, 539, - 0, 196, 221, 368, 0, 454, 288, 650, 618, 484, - 613, 206, 223, 0, 262, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 198, 200, 209, 222, - 232, 236, 243, 261, 276, 278, 285, 298, 310, 318, - 319, 322, 328, 380, 386, 387, 388, 389, 409, 410, - 411, 414, 417, 418, 421, 423, 424, 427, 431, 435, - 436, 437, 439, 441, 443, 455, 460, 474, 475, 476, - 477, 478, 481, 482, 488, 489, 490, 491, 492, 500, - 501, 516, 586, 588, 605, 625, 632, 480, 301, 302, - 444, 445, 314, 315, 646, 647, 300, 600, 633, 597, - 645, 627, 438, 378, 0, 0, 381, 281, 305, 320, - 0, 617, 502, 227, 466, 290, 251, 0, 0, 211, - 246, 230, 259, 274, 277, 324, 391, 400, 429, 434, - 296, 271, 244, 459, 241, 485, 519, 520, 521, 523, - 395, 266, 433, 396, 0, 376, 576, 577, 316, 0, - 0, 0, 528, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 416, 0, 0, 0, 0, 0, - 0, 0, 270, 1799, 0, 0, 0, 366, 267, 0, - 0, 204, 505, 0, 430, 0, 203, 0, 487, 252, - 377, 374, 583, 282, 273, 269, 250, 317, 385, 428, - 518, 422, 0, 370, 0, 0, 497, 401, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 323, 248, 325, 202, - 413, 498, 286, 0, 0, 0, 0, 1798, 507, 724, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 238, - 0, 0, 245, 0, 0, 0, 351, 360, 359, 339, - 340, 342, 344, 350, 357, 363, 336, 345, 0, 0, - 609, 0, 0, 0, 265, 321, 272, 264, 580, 0, - 0, 0, 0, 0, 0, 596, 0, 0, 229, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 275, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 297, 0, 402, 257, 0, - 453, 0, 0, 0, 629, 0, 0, 0, 0, 0, - 0, 0, 365, 0, 330, 197, 225, 0, 0, 412, - 461, 473, 0, 0, 0, 253, 0, 471, 426, 604, - 233, 284, 458, 432, 469, 440, 287, 0, 0, 470, - 372, 585, 450, 601, 630, 631, 263, 406, 615, 522, - 623, 648, 226, 260, 420, 506, 607, 494, 397, 581, - 582, 329, 493, 295, 201, 369, 636, 224, 479, 371, - 242, 231, 587, 612, 299, 289, 456, 643, 213, 517, - 598, 239, 483, 0, 0, 651, 247, 504, 610, 599, - 215, 594, 503, 393, 326, 327, 214, 0, 457, 268, - 293, 0, 0, 258, 415, 589, 590, 256, 652, 228, - 622, 220, 0, 621, 408, 584, 595, 394, 383, 219, - 593, 392, 382, 334, 355, 356, 280, 307, 447, 375, - 448, 306, 308, 404, 403, 405, 207, 608, 626, 0, - 208, 0, 499, 611, 653, 452, 212, 234, 235, 237, - 0, 279, 283, 291, 294, 303, 304, 313, 367, 419, - 446, 442, 451, 0, 579, 602, 616, 628, 634, 635, - 637, 638, 639, 640, 641, 644, 642, 407, 311, 495, - 333, 373, 0, 0, 425, 472, 240, 606, 496, 199, - 0, 0, 0, 0, 254, 255, 0, 575, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 654, 655, 656, - 657, 658, 659, 660, 661, 662, 663, 664, 665, 666, - 667, 668, 669, 670, 671, 649, 508, 514, 509, 510, - 511, 512, 513, 0, 515, 0, 0, 0, 0, 0, - 398, 0, 591, 592, 672, 384, 486, 603, 335, 349, - 352, 341, 361, 0, 362, 337, 338, 343, 346, 347, - 348, 353, 354, 358, 364, 249, 210, 390, 399, 578, - 312, 216, 217, 218, 524, 525, 526, 527, 619, 620, - 624, 205, 462, 463, 464, 465, 292, 614, 309, 468, - 467, 331, 332, 379, 449, 540, 542, 553, 557, 559, - 561, 567, 570, 541, 543, 554, 558, 560, 562, 568, - 571, 530, 532, 534, 536, 549, 548, 545, 573, 574, - 551, 556, 535, 547, 552, 565, 572, 569, 529, 533, - 537, 546, 564, 563, 544, 555, 566, 550, 538, 531, - 539, 0, 196, 221, 368, 0, 454, 288, 650, 618, - 484, 613, 206, 223, 0, 262, 0, 0, 0, 0, + 668, 669, 670, 671, 672, 650, 509, 515, 510, 511, + 512, 513, 514, 0, 516, 0, 0, 0, 0, 0, + 399, 0, 592, 593, 673, 385, 487, 604, 336, 350, + 353, 342, 362, 0, 363, 338, 339, 344, 347, 348, + 349, 354, 355, 359, 365, 249, 210, 391, 400, 579, + 313, 216, 217, 218, 525, 526, 527, 528, 620, 621, + 625, 205, 463, 464, 465, 466, 293, 615, 310, 469, + 468, 332, 333, 380, 450, 541, 543, 554, 558, 560, + 562, 568, 571, 542, 544, 555, 559, 561, 563, 569, + 572, 531, 533, 535, 537, 550, 549, 546, 574, 575, + 552, 557, 536, 548, 553, 566, 573, 570, 530, 534, + 538, 547, 565, 564, 545, 556, 567, 551, 539, 532, + 540, 0, 196, 221, 369, 0, 455, 289, 651, 619, + 485, 614, 206, 223, 0, 263, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 198, 200, 209, - 222, 232, 236, 243, 261, 276, 278, 285, 298, 310, - 318, 319, 322, 328, 380, 386, 387, 388, 389, 409, - 410, 411, 414, 417, 418, 421, 423, 424, 427, 431, - 435, 436, 437, 439, 441, 443, 455, 460, 474, 475, - 476, 477, 478, 481, 482, 488, 489, 490, 491, 492, - 500, 501, 516, 586, 588, 605, 625, 632, 480, 301, - 302, 444, 445, 314, 315, 646, 647, 300, 600, 633, - 597, 645, 627, 438, 378, 0, 0, 381, 281, 305, - 320, 0, 617, 502, 227, 466, 290, 251, 0, 0, - 211, 246, 230, 259, 274, 277, 324, 391, 400, 429, - 434, 296, 271, 244, 459, 241, 485, 519, 520, 521, - 523, 395, 266, 433, 396, 0, 376, 576, 577, 316, - 0, 0, 0, 528, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 416, 0, 0, 0, 0, - 0, 0, 0, 270, 0, 0, 0, 0, 366, 267, - 0, 0, 204, 505, 0, 430, 0, 203, 0, 487, - 252, 377, 374, 583, 282, 273, 269, 250, 317, 385, - 428, 518, 422, 0, 370, 0, 0, 497, 401, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 323, 248, 325, - 202, 413, 498, 286, 0, 0, 0, 0, 0, 507, - 726, 727, 728, 0, 0, 0, 0, 0, 0, 0, - 238, 0, 0, 245, 0, 0, 0, 351, 360, 359, - 339, 340, 342, 344, 350, 357, 363, 336, 345, 0, - 0, 609, 0, 0, 0, 265, 321, 272, 264, 580, - 0, 0, 0, 0, 0, 0, 596, 0, 0, 229, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 275, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 297, 0, 402, 257, - 0, 453, 0, 0, 0, 629, 0, 0, 0, 0, - 0, 0, 0, 365, 0, 330, 197, 225, 0, 0, - 412, 461, 473, 0, 0, 0, 253, 0, 471, 426, - 604, 233, 284, 458, 432, 469, 440, 287, 0, 0, - 470, 372, 585, 450, 601, 630, 631, 263, 406, 615, - 522, 623, 648, 226, 260, 420, 506, 607, 494, 397, - 581, 582, 329, 493, 295, 201, 369, 636, 224, 479, - 371, 242, 231, 587, 612, 299, 289, 456, 643, 213, - 517, 598, 239, 483, 0, 0, 651, 247, 504, 610, - 599, 215, 594, 503, 393, 326, 327, 214, 0, 457, - 268, 293, 0, 0, 258, 415, 589, 590, 256, 652, - 228, 622, 220, 0, 621, 408, 584, 595, 394, 383, - 219, 593, 392, 382, 334, 355, 356, 280, 307, 447, - 375, 448, 306, 308, 404, 403, 405, 207, 608, 626, - 0, 208, 0, 499, 611, 653, 452, 212, 234, 235, - 237, 0, 279, 283, 291, 294, 303, 304, 313, 367, - 419, 446, 442, 451, 0, 579, 602, 616, 628, 634, - 635, 637, 638, 639, 640, 641, 644, 642, 407, 311, - 495, 333, 373, 0, 0, 425, 472, 240, 606, 496, - 199, 0, 0, 0, 0, 254, 255, 0, 575, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 654, 655, + 222, 232, 236, 243, 262, 277, 279, 286, 299, 311, + 319, 320, 323, 329, 381, 387, 388, 389, 390, 410, + 411, 412, 415, 418, 419, 422, 424, 425, 428, 432, + 436, 437, 438, 440, 442, 444, 456, 461, 475, 476, + 477, 478, 479, 482, 483, 489, 490, 491, 492, 493, + 501, 502, 517, 587, 589, 606, 626, 633, 481, 302, + 303, 445, 446, 315, 316, 647, 648, 301, 601, 634, + 598, 646, 628, 439, 379, 0, 0, 382, 282, 306, + 321, 0, 618, 503, 227, 467, 291, 251, 0, 0, + 211, 246, 230, 260, 275, 278, 325, 392, 401, 430, + 435, 297, 272, 244, 460, 241, 486, 520, 521, 522, + 524, 396, 267, 434, 397, 0, 377, 577, 578, 317, + 0, 0, 0, 529, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 417, 0, 0, 0, 0, + 0, 0, 0, 271, 0, 0, 0, 0, 367, 268, + 0, 0, 204, 506, 0, 431, 0, 203, 0, 488, + 253, 378, 375, 584, 283, 274, 270, 250, 318, 386, + 429, 519, 423, 0, 371, 0, 0, 498, 402, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 324, 248, 326, + 202, 414, 499, 287, 0, 0, 0, 0, 0, 508, + 725, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 238, 0, 0, 245, 0, 0, 0, 352, 361, 360, + 340, 341, 343, 345, 351, 358, 364, 337, 346, 0, + 0, 610, 0, 0, 0, 266, 322, 273, 265, 581, + 0, 0, 0, 0, 0, 0, 597, 0, 0, 229, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 276, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 298, 0, 403, 258, + 0, 454, 0, 0, 0, 630, 0, 0, 0, 4073, + 0, 0, 0, 366, 0, 331, 197, 225, 0, 0, + 413, 462, 474, 0, 0, 0, 254, 0, 472, 427, + 605, 233, 285, 459, 433, 470, 441, 288, 0, 0, + 471, 373, 586, 451, 602, 631, 632, 264, 407, 616, + 523, 624, 649, 226, 261, 421, 507, 608, 495, 398, + 582, 583, 330, 494, 296, 201, 370, 637, 224, 480, + 372, 242, 231, 588, 613, 300, 252, 290, 457, 644, + 213, 518, 599, 239, 484, 0, 0, 652, 247, 505, + 611, 600, 215, 595, 504, 394, 327, 328, 214, 0, + 458, 269, 294, 0, 0, 259, 416, 590, 591, 257, + 653, 228, 623, 220, 0, 622, 409, 585, 596, 395, + 384, 219, 594, 393, 383, 335, 356, 357, 281, 308, + 448, 376, 449, 307, 309, 405, 404, 406, 207, 609, + 627, 0, 208, 0, 500, 612, 654, 453, 212, 234, + 235, 237, 0, 280, 284, 292, 295, 304, 305, 314, + 368, 420, 447, 443, 452, 0, 580, 603, 617, 629, + 635, 636, 638, 639, 640, 641, 642, 645, 643, 408, + 312, 496, 334, 374, 0, 0, 426, 473, 240, 607, + 497, 199, 0, 0, 0, 0, 255, 256, 0, 576, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 655, 656, 657, 658, 659, 660, 661, 662, 663, 664, 665, - 666, 667, 668, 669, 670, 671, 649, 508, 514, 509, - 510, 511, 512, 513, 0, 515, 0, 0, 0, 0, - 0, 398, 0, 591, 592, 672, 384, 486, 603, 335, - 349, 352, 341, 361, 0, 362, 337, 338, 343, 346, - 347, 348, 353, 354, 358, 364, 249, 210, 390, 399, - 578, 312, 216, 217, 218, 524, 525, 526, 527, 619, - 620, 624, 205, 462, 463, 464, 465, 292, 614, 309, - 468, 467, 331, 332, 379, 449, 540, 542, 553, 557, - 559, 561, 567, 570, 541, 543, 554, 558, 560, 562, - 568, 571, 530, 532, 534, 536, 549, 548, 545, 573, - 574, 551, 556, 535, 547, 552, 565, 572, 569, 529, - 533, 537, 546, 564, 563, 544, 555, 566, 550, 538, - 531, 539, 0, 196, 221, 368, 0, 454, 288, 650, - 618, 484, 613, 206, 223, 0, 262, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 198, 200, - 209, 222, 232, 236, 243, 261, 276, 278, 285, 298, - 310, 318, 319, 322, 328, 380, 386, 387, 388, 389, - 409, 410, 411, 414, 417, 418, 421, 423, 424, 427, - 431, 435, 436, 437, 439, 441, 443, 455, 460, 474, - 475, 476, 477, 478, 481, 482, 488, 489, 490, 491, - 492, 500, 501, 516, 586, 588, 605, 625, 632, 480, - 301, 302, 444, 445, 314, 315, 646, 647, 300, 600, - 633, 597, 645, 627, 438, 378, 0, 0, 381, 281, - 305, 320, 0, 617, 502, 227, 466, 290, 251, 0, - 0, 211, 246, 230, 259, 274, 277, 324, 391, 400, - 429, 434, 296, 271, 244, 459, 241, 485, 519, 520, - 521, 523, 395, 266, 433, 396, 0, 376, 576, 577, - 316, 0, 0, 0, 528, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 416, 0, 0, 0, - 0, 0, 0, 0, 270, 0, 0, 0, 0, 366, - 267, 0, 0, 204, 505, 0, 430, 0, 203, 0, - 487, 252, 377, 374, 583, 282, 273, 269, 250, 317, - 385, 428, 518, 422, 0, 370, 0, 0, 497, 401, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 323, 248, - 325, 202, 413, 498, 286, 0, 0, 0, 0, 0, - 507, 724, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 238, 0, 0, 245, 0, 0, 0, 351, 360, - 359, 339, 340, 342, 344, 350, 357, 363, 336, 345, - 0, 0, 609, 0, 0, 0, 265, 321, 272, 264, - 580, 0, 0, 0, 0, 0, 0, 596, 0, 0, - 229, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 275, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 297, 0, 402, - 257, 0, 453, 0, 0, 0, 629, 0, 0, 0, - 4070, 0, 0, 0, 365, 0, 330, 197, 225, 0, - 0, 412, 461, 473, 0, 0, 0, 253, 0, 471, - 426, 604, 233, 284, 458, 432, 469, 440, 287, 0, - 0, 470, 372, 585, 450, 601, 630, 631, 263, 406, - 615, 522, 623, 648, 226, 260, 420, 506, 607, 494, - 397, 581, 582, 329, 493, 295, 201, 369, 636, 224, - 479, 371, 242, 231, 587, 612, 299, 289, 456, 643, - 213, 517, 598, 239, 483, 0, 0, 651, 247, 504, - 610, 599, 215, 594, 503, 393, 326, 327, 214, 0, - 457, 268, 293, 0, 0, 258, 415, 589, 590, 256, - 652, 228, 622, 220, 0, 621, 408, 584, 595, 394, - 383, 219, 593, 392, 382, 334, 355, 356, 280, 307, - 447, 375, 448, 306, 308, 404, 403, 405, 207, 608, - 626, 0, 208, 0, 499, 611, 653, 452, 212, 234, - 235, 237, 0, 279, 283, 291, 294, 303, 304, 313, - 367, 419, 446, 442, 451, 0, 579, 602, 616, 628, - 634, 635, 637, 638, 639, 640, 641, 644, 642, 407, - 311, 495, 333, 373, 0, 0, 425, 472, 240, 606, - 496, 199, 0, 0, 0, 0, 254, 255, 0, 575, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 654, - 655, 656, 657, 658, 659, 660, 661, 662, 663, 664, - 665, 666, 667, 668, 669, 670, 671, 649, 508, 514, - 509, 510, 511, 512, 513, 0, 515, 0, 0, 0, - 0, 0, 398, 0, 591, 592, 672, 384, 486, 603, - 335, 349, 352, 341, 361, 0, 362, 337, 338, 343, - 346, 347, 348, 353, 354, 358, 364, 249, 210, 390, - 399, 578, 312, 216, 217, 218, 524, 525, 526, 527, - 619, 620, 624, 205, 462, 463, 464, 465, 292, 614, - 309, 468, 467, 331, 332, 379, 449, 540, 542, 553, - 557, 559, 561, 567, 570, 541, 543, 554, 558, 560, - 562, 568, 571, 530, 532, 534, 536, 549, 548, 545, - 573, 574, 551, 556, 535, 547, 552, 565, 572, 569, - 529, 533, 537, 546, 564, 563, 544, 555, 566, 550, - 538, 531, 539, 0, 196, 221, 368, 0, 454, 288, - 650, 618, 484, 613, 206, 223, 0, 262, 0, 0, + 666, 667, 668, 669, 670, 671, 672, 650, 509, 515, + 510, 511, 512, 513, 514, 0, 516, 0, 0, 0, + 0, 0, 399, 0, 592, 593, 673, 385, 487, 604, + 336, 350, 353, 342, 362, 0, 363, 338, 339, 344, + 347, 348, 349, 354, 355, 359, 365, 249, 210, 391, + 400, 579, 313, 216, 217, 218, 525, 526, 527, 528, + 620, 621, 625, 205, 463, 464, 465, 466, 293, 615, + 310, 469, 468, 332, 333, 380, 450, 541, 543, 554, + 558, 560, 562, 568, 571, 542, 544, 555, 559, 561, + 563, 569, 572, 531, 533, 535, 537, 550, 549, 546, + 574, 575, 552, 557, 536, 548, 553, 566, 573, 570, + 530, 534, 538, 547, 565, 564, 545, 556, 567, 551, + 539, 532, 540, 0, 196, 221, 369, 0, 455, 289, + 651, 619, 485, 614, 206, 223, 0, 263, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 198, - 200, 209, 222, 232, 236, 243, 261, 276, 278, 285, - 298, 310, 318, 319, 322, 328, 380, 386, 387, 388, - 389, 409, 410, 411, 414, 417, 418, 421, 423, 424, - 427, 431, 435, 436, 437, 439, 441, 443, 455, 460, - 474, 475, 476, 477, 478, 481, 482, 488, 489, 490, - 491, 492, 500, 501, 516, 586, 588, 605, 625, 632, - 480, 301, 302, 444, 445, 314, 315, 646, 647, 300, - 600, 633, 597, 645, 627, 438, 378, 0, 0, 381, - 281, 305, 320, 0, 617, 502, 227, 466, 290, 251, - 0, 0, 211, 246, 230, 259, 274, 277, 324, 391, - 400, 429, 434, 296, 271, 244, 459, 241, 485, 519, - 520, 521, 523, 395, 266, 433, 396, 0, 376, 576, - 577, 316, 0, 0, 0, 528, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 416, 0, 0, - 0, 0, 0, 0, 0, 270, 0, 0, 0, 0, - 366, 267, 0, 0, 204, 505, 0, 430, 0, 203, - 0, 487, 252, 377, 374, 583, 282, 273, 269, 250, - 317, 385, 428, 518, 422, 0, 370, 0, 0, 497, - 401, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 323, - 248, 325, 202, 413, 498, 286, 0, 0, 0, 0, - 1961, 507, 194, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 238, 0, 0, 245, 0, 0, 0, 351, - 360, 359, 339, 340, 342, 344, 350, 357, 363, 336, - 345, 0, 0, 609, 0, 0, 0, 265, 321, 272, - 264, 580, 0, 0, 0, 0, 0, 0, 596, 0, + 200, 209, 222, 232, 236, 243, 262, 277, 279, 286, + 299, 311, 319, 320, 323, 329, 381, 387, 388, 389, + 390, 410, 411, 412, 415, 418, 419, 422, 424, 425, + 428, 432, 436, 437, 438, 440, 442, 444, 456, 461, + 475, 476, 477, 478, 479, 482, 483, 489, 490, 491, + 492, 493, 501, 502, 517, 587, 589, 606, 626, 633, + 481, 302, 303, 445, 446, 315, 316, 647, 648, 301, + 601, 634, 598, 646, 628, 439, 379, 0, 0, 382, + 282, 306, 321, 0, 618, 503, 227, 467, 291, 251, + 0, 0, 211, 246, 230, 260, 275, 278, 325, 392, + 401, 430, 435, 297, 272, 244, 460, 241, 486, 520, + 521, 522, 524, 396, 267, 434, 397, 0, 377, 577, + 578, 317, 0, 0, 0, 529, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 417, 0, 0, + 0, 0, 0, 0, 0, 271, 0, 0, 0, 0, + 367, 268, 0, 0, 204, 506, 0, 431, 0, 203, + 0, 488, 253, 378, 375, 584, 283, 274, 270, 250, + 318, 386, 429, 519, 423, 0, 371, 0, 0, 498, + 402, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 324, + 248, 326, 202, 414, 499, 287, 0, 0, 0, 0, + 1962, 508, 194, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 238, 0, 0, 245, 0, 0, 0, 352, + 361, 360, 340, 341, 343, 345, 351, 358, 364, 337, + 346, 0, 0, 610, 0, 0, 0, 266, 322, 273, + 265, 581, 0, 0, 0, 0, 0, 0, 597, 0, 0, 229, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 275, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 297, 0, - 402, 257, 0, 453, 0, 0, 0, 629, 0, 0, - 0, 0, 0, 0, 0, 365, 0, 330, 197, 225, - 0, 0, 412, 461, 473, 0, 0, 0, 253, 0, - 471, 426, 604, 233, 284, 458, 432, 469, 440, 287, - 0, 0, 470, 372, 585, 450, 601, 630, 631, 263, - 406, 615, 522, 623, 648, 226, 260, 420, 506, 607, - 494, 397, 581, 582, 329, 493, 295, 201, 369, 636, - 224, 479, 371, 242, 231, 587, 612, 299, 289, 456, - 643, 213, 517, 598, 239, 483, 0, 0, 651, 247, - 504, 610, 599, 215, 594, 503, 393, 326, 327, 214, - 0, 457, 268, 293, 0, 0, 258, 415, 589, 590, - 256, 652, 228, 622, 220, 0, 621, 408, 584, 595, - 394, 383, 219, 593, 392, 382, 334, 355, 356, 280, - 307, 447, 375, 448, 306, 308, 404, 403, 405, 207, - 608, 626, 0, 208, 0, 499, 611, 653, 452, 212, - 234, 235, 237, 0, 279, 283, 291, 294, 303, 304, - 313, 367, 419, 446, 442, 451, 0, 579, 602, 616, - 628, 634, 635, 637, 638, 639, 640, 641, 644, 642, - 407, 311, 495, 333, 373, 0, 0, 425, 472, 240, - 606, 496, 199, 0, 0, 0, 0, 254, 255, 0, - 575, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 654, 655, 656, 657, 658, 659, 660, 661, 662, 663, - 664, 665, 666, 667, 668, 669, 670, 671, 649, 508, - 514, 509, 510, 511, 512, 513, 0, 515, 0, 0, - 0, 0, 0, 398, 0, 591, 592, 672, 384, 486, - 603, 335, 349, 352, 341, 361, 0, 362, 337, 338, - 343, 346, 347, 348, 353, 354, 358, 364, 249, 210, - 390, 399, 578, 312, 216, 217, 218, 524, 525, 526, - 527, 619, 620, 624, 205, 462, 463, 464, 465, 292, - 614, 309, 468, 467, 331, 332, 379, 449, 540, 542, - 553, 557, 559, 561, 567, 570, 541, 543, 554, 558, - 560, 562, 568, 571, 530, 532, 534, 536, 549, 548, - 545, 573, 574, 551, 556, 535, 547, 552, 565, 572, - 569, 529, 533, 537, 546, 564, 563, 544, 555, 566, - 550, 538, 531, 539, 0, 196, 221, 368, 0, 454, - 288, 650, 618, 484, 613, 206, 223, 0, 262, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 198, 200, 209, 222, 232, 236, 243, 261, 276, 278, - 285, 298, 310, 318, 319, 322, 328, 380, 386, 387, - 388, 389, 409, 410, 411, 414, 417, 418, 421, 423, - 424, 427, 431, 435, 436, 437, 439, 441, 443, 455, - 460, 474, 475, 476, 477, 478, 481, 482, 488, 489, - 490, 491, 492, 500, 501, 516, 586, 588, 605, 625, - 632, 480, 301, 302, 444, 445, 314, 315, 646, 647, - 300, 600, 633, 597, 645, 627, 438, 378, 0, 0, - 381, 281, 305, 320, 0, 617, 502, 227, 466, 290, - 251, 0, 0, 211, 246, 230, 259, 274, 277, 324, - 391, 400, 429, 434, 296, 271, 244, 459, 241, 485, - 519, 520, 521, 523, 395, 266, 433, 396, 0, 376, - 576, 577, 316, 0, 0, 0, 528, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 416, 0, - 0, 0, 0, 0, 0, 0, 270, 0, 0, 0, - 0, 366, 267, 0, 0, 204, 505, 0, 430, 0, - 203, 0, 487, 252, 377, 374, 583, 282, 273, 269, - 250, 317, 385, 428, 518, 422, 0, 370, 0, 0, - 497, 401, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 323, 248, 325, 202, 413, 498, 286, 0, 0, 0, - 0, 0, 507, 724, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 238, 0, 0, 245, 0, 0, 0, - 351, 360, 359, 339, 340, 342, 344, 350, 357, 363, - 336, 345, 0, 0, 609, 0, 0, 0, 265, 321, - 272, 264, 580, 0, 0, 0, 0, 0, 0, 596, - 0, 0, 229, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 275, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 297, - 0, 402, 257, 0, 453, 0, 0, 0, 629, 0, - 0, 0, 3726, 0, 0, 0, 365, 0, 330, 197, - 225, 0, 0, 412, 461, 473, 0, 0, 0, 253, - 0, 471, 426, 604, 233, 284, 458, 432, 469, 440, - 287, 0, 0, 470, 372, 585, 450, 601, 630, 631, - 263, 406, 615, 522, 623, 648, 226, 260, 420, 506, - 607, 494, 397, 581, 582, 329, 493, 295, 201, 369, - 636, 224, 479, 371, 242, 231, 587, 612, 299, 289, - 456, 643, 213, 517, 598, 239, 483, 0, 0, 651, - 247, 504, 610, 599, 215, 594, 503, 393, 326, 327, - 214, 0, 457, 268, 293, 0, 0, 258, 415, 589, - 590, 256, 652, 228, 622, 220, 0, 621, 408, 584, - 595, 394, 383, 219, 593, 392, 382, 334, 355, 356, - 280, 307, 447, 375, 448, 306, 308, 404, 403, 405, - 207, 608, 626, 0, 208, 0, 499, 611, 653, 452, - 212, 234, 235, 237, 0, 279, 283, 291, 294, 303, - 304, 313, 367, 419, 446, 442, 451, 0, 579, 602, - 616, 628, 634, 635, 637, 638, 639, 640, 641, 644, - 642, 407, 311, 495, 333, 373, 0, 0, 425, 472, - 240, 606, 496, 199, 0, 0, 0, 0, 254, 255, - 0, 575, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 654, 655, 656, 657, 658, 659, 660, 661, 662, - 663, 664, 665, 666, 667, 668, 669, 670, 671, 649, - 508, 514, 509, 510, 511, 512, 513, 0, 515, 0, - 0, 0, 0, 0, 398, 0, 591, 592, 672, 384, - 486, 603, 335, 349, 352, 341, 361, 0, 362, 337, - 338, 343, 346, 347, 348, 353, 354, 358, 364, 249, - 210, 390, 399, 578, 312, 216, 217, 218, 524, 525, - 526, 527, 619, 620, 624, 205, 462, 463, 464, 465, - 292, 614, 309, 468, 467, 331, 332, 379, 449, 540, - 542, 553, 557, 559, 561, 567, 570, 541, 543, 554, - 558, 560, 562, 568, 571, 530, 532, 534, 536, 549, - 548, 545, 573, 574, 551, 556, 535, 547, 552, 565, - 572, 569, 529, 533, 537, 546, 564, 563, 544, 555, - 566, 550, 538, 531, 539, 0, 196, 221, 368, 0, - 454, 288, 650, 618, 484, 613, 206, 223, 0, 262, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 198, 200, 209, 222, 232, 236, 243, 261, 276, - 278, 285, 298, 310, 318, 319, 322, 328, 380, 386, - 387, 388, 389, 409, 410, 411, 414, 417, 418, 421, - 423, 424, 427, 431, 435, 436, 437, 439, 441, 443, - 455, 460, 474, 475, 476, 477, 478, 481, 482, 488, - 489, 490, 491, 492, 500, 501, 516, 586, 588, 605, - 625, 632, 480, 301, 302, 444, 445, 314, 315, 646, - 647, 300, 600, 633, 597, 645, 627, 438, 378, 0, - 0, 381, 281, 305, 320, 0, 617, 502, 227, 466, - 290, 251, 0, 0, 211, 246, 230, 259, 274, 277, - 324, 391, 400, 429, 434, 296, 271, 244, 459, 241, - 485, 519, 520, 521, 523, 395, 266, 433, 396, 0, - 376, 576, 577, 316, 0, 0, 0, 528, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 416, - 0, 0, 0, 0, 0, 0, 0, 270, 0, 0, - 0, 0, 366, 267, 0, 0, 204, 505, 0, 430, - 0, 203, 0, 487, 252, 377, 374, 583, 282, 273, - 269, 250, 317, 385, 428, 518, 422, 0, 370, 0, - 0, 497, 401, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 323, 248, 325, 202, 413, 498, 286, 0, 95, - 0, 0, 0, 507, 724, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 276, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 298, 0, + 403, 258, 0, 454, 0, 0, 0, 630, 0, 0, + 0, 0, 0, 0, 0, 366, 0, 331, 197, 225, + 0, 0, 413, 462, 474, 0, 0, 0, 254, 0, + 472, 427, 605, 233, 285, 459, 433, 470, 441, 288, + 0, 0, 471, 373, 586, 451, 602, 631, 632, 264, + 407, 616, 523, 624, 649, 226, 261, 421, 507, 608, + 495, 398, 582, 583, 330, 494, 296, 201, 370, 637, + 224, 480, 372, 242, 231, 588, 613, 300, 252, 290, + 457, 644, 213, 518, 599, 239, 484, 0, 0, 652, + 247, 505, 611, 600, 215, 595, 504, 394, 327, 328, + 214, 0, 458, 269, 294, 0, 0, 259, 416, 590, + 591, 257, 653, 228, 623, 220, 0, 622, 409, 585, + 596, 395, 384, 219, 594, 393, 383, 335, 356, 357, + 281, 308, 448, 376, 449, 307, 309, 405, 404, 406, + 207, 609, 627, 0, 208, 0, 500, 612, 654, 453, + 212, 234, 235, 237, 0, 280, 284, 292, 295, 304, + 305, 314, 368, 420, 447, 443, 452, 0, 580, 603, + 617, 629, 635, 636, 638, 639, 640, 641, 642, 645, + 643, 408, 312, 496, 334, 374, 0, 0, 426, 473, + 240, 607, 497, 199, 0, 0, 0, 0, 255, 256, + 0, 576, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 655, 656, 657, 658, 659, 660, 661, 662, 663, + 664, 665, 666, 667, 668, 669, 670, 671, 672, 650, + 509, 515, 510, 511, 512, 513, 514, 0, 516, 0, + 0, 0, 0, 0, 399, 0, 592, 593, 673, 385, + 487, 604, 336, 350, 353, 342, 362, 0, 363, 338, + 339, 344, 347, 348, 349, 354, 355, 359, 365, 249, + 210, 391, 400, 579, 313, 216, 217, 218, 525, 526, + 527, 528, 620, 621, 625, 205, 463, 464, 465, 466, + 293, 615, 310, 469, 468, 332, 333, 380, 450, 541, + 543, 554, 558, 560, 562, 568, 571, 542, 544, 555, + 559, 561, 563, 569, 572, 531, 533, 535, 537, 550, + 549, 546, 574, 575, 552, 557, 536, 548, 553, 566, + 573, 570, 530, 534, 538, 547, 565, 564, 545, 556, + 567, 551, 539, 532, 540, 0, 196, 221, 369, 0, + 455, 289, 651, 619, 485, 614, 206, 223, 0, 263, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 198, 200, 209, 222, 232, 236, 243, 262, 277, + 279, 286, 299, 311, 319, 320, 323, 329, 381, 387, + 388, 389, 390, 410, 411, 412, 415, 418, 419, 422, + 424, 425, 428, 432, 436, 437, 438, 440, 442, 444, + 456, 461, 475, 476, 477, 478, 479, 482, 483, 489, + 490, 491, 492, 493, 501, 502, 517, 587, 589, 606, + 626, 633, 481, 302, 303, 445, 446, 315, 316, 647, + 648, 301, 601, 634, 598, 646, 628, 439, 379, 0, + 0, 382, 282, 306, 321, 0, 618, 503, 227, 467, + 291, 251, 0, 0, 211, 246, 230, 260, 275, 278, + 325, 392, 401, 430, 435, 297, 272, 244, 460, 241, + 486, 520, 521, 522, 524, 396, 267, 434, 397, 0, + 377, 577, 578, 317, 0, 0, 0, 529, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 417, + 0, 0, 0, 0, 0, 0, 0, 271, 0, 0, + 0, 0, 367, 268, 0, 0, 204, 506, 0, 431, + 0, 203, 0, 488, 253, 378, 375, 584, 283, 274, + 270, 250, 318, 386, 429, 519, 423, 0, 371, 0, + 0, 498, 402, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 324, 248, 326, 202, 414, 499, 287, 0, 0, + 0, 0, 0, 508, 725, 0, 0, 0, 0, 0, 0, 0, 0, 0, 238, 0, 0, 245, 0, 0, - 0, 351, 360, 359, 339, 340, 342, 344, 350, 357, - 363, 336, 345, 0, 0, 609, 0, 0, 0, 265, - 321, 272, 264, 580, 0, 0, 0, 0, 0, 0, - 596, 0, 0, 229, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 275, 0, + 0, 352, 361, 360, 340, 341, 343, 345, 351, 358, + 364, 337, 346, 0, 0, 610, 0, 0, 0, 266, + 322, 273, 265, 581, 0, 0, 0, 0, 0, 0, + 597, 0, 0, 229, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 276, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -5677,147 +5642,73 @@ 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, - 297, 0, 402, 257, 0, 453, 0, 0, 0, 629, - 0, 0, 0, 0, 0, 0, 0, 365, 0, 330, - 197, 225, 0, 0, 412, 461, 473, 0, 0, 0, - 253, 0, 471, 426, 604, 233, 284, 458, 432, 469, - 440, 287, 0, 0, 470, 372, 585, 450, 601, 630, - 631, 263, 406, 615, 522, 623, 648, 226, 260, 420, - 506, 607, 494, 397, 581, 582, 329, 493, 295, 201, - 369, 636, 224, 479, 371, 242, 231, 587, 612, 299, - 289, 456, 643, 213, 517, 598, 239, 483, 0, 0, - 651, 247, 504, 610, 599, 215, 594, 503, 393, 326, - 327, 214, 0, 457, 268, 293, 0, 0, 258, 415, - 589, 590, 256, 652, 228, 622, 220, 0, 621, 408, - 584, 595, 394, 383, 219, 593, 392, 382, 334, 355, - 356, 280, 307, 447, 375, 448, 306, 308, 404, 403, - 405, 207, 608, 626, 0, 208, 0, 499, 611, 653, - 452, 212, 234, 235, 237, 0, 279, 283, 291, 294, - 303, 304, 313, 367, 419, 446, 442, 451, 0, 579, - 602, 616, 628, 634, 635, 637, 638, 639, 640, 641, - 644, 642, 407, 311, 495, 333, 373, 0, 0, 425, - 472, 240, 606, 496, 199, 0, 0, 0, 0, 254, - 255, 0, 575, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 654, 655, 656, 657, 658, 659, 660, 661, + 298, 0, 403, 258, 0, 454, 0, 0, 0, 630, + 0, 0, 0, 3729, 0, 0, 0, 366, 0, 331, + 197, 225, 0, 0, 413, 462, 474, 0, 0, 0, + 254, 0, 472, 427, 605, 233, 285, 459, 433, 470, + 441, 288, 0, 0, 471, 373, 586, 451, 602, 631, + 632, 264, 407, 616, 523, 624, 649, 226, 261, 421, + 507, 608, 495, 398, 582, 583, 330, 494, 296, 201, + 370, 637, 224, 480, 372, 242, 231, 588, 613, 300, + 252, 290, 457, 644, 213, 518, 599, 239, 484, 0, + 0, 652, 247, 505, 611, 600, 215, 595, 504, 394, + 327, 328, 214, 0, 458, 269, 294, 0, 0, 259, + 416, 590, 591, 257, 653, 228, 623, 220, 0, 622, + 409, 585, 596, 395, 384, 219, 594, 393, 383, 335, + 356, 357, 281, 308, 448, 376, 449, 307, 309, 405, + 404, 406, 207, 609, 627, 0, 208, 0, 500, 612, + 654, 453, 212, 234, 235, 237, 0, 280, 284, 292, + 295, 304, 305, 314, 368, 420, 447, 443, 452, 0, + 580, 603, 617, 629, 635, 636, 638, 639, 640, 641, + 642, 645, 643, 408, 312, 496, 334, 374, 0, 0, + 426, 473, 240, 607, 497, 199, 0, 0, 0, 0, + 255, 256, 0, 576, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 655, 656, 657, 658, 659, 660, 661, 662, 663, 664, 665, 666, 667, 668, 669, 670, 671, - 649, 508, 514, 509, 510, 511, 512, 513, 0, 515, - 0, 0, 0, 0, 0, 398, 0, 591, 592, 672, - 384, 486, 603, 335, 349, 352, 341, 361, 0, 362, - 337, 338, 343, 346, 347, 348, 353, 354, 358, 364, - 249, 210, 390, 399, 578, 312, 216, 217, 218, 524, - 525, 526, 527, 619, 620, 624, 205, 462, 463, 464, - 465, 292, 614, 309, 468, 467, 331, 332, 379, 449, - 540, 542, 553, 557, 559, 561, 567, 570, 541, 543, - 554, 558, 560, 562, 568, 571, 530, 532, 534, 536, - 549, 548, 545, 573, 574, 551, 556, 535, 547, 552, - 565, 572, 569, 529, 533, 537, 546, 564, 563, 544, - 555, 566, 550, 538, 531, 539, 0, 196, 221, 368, - 0, 454, 288, 650, 618, 484, 613, 206, 223, 0, - 262, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 198, 200, 209, 222, 232, 236, 243, 261, - 276, 278, 285, 298, 310, 318, 319, 322, 328, 380, - 386, 387, 388, 389, 409, 410, 411, 414, 417, 418, - 421, 423, 424, 427, 431, 435, 436, 437, 439, 441, - 443, 455, 460, 474, 475, 476, 477, 478, 481, 482, - 488, 489, 490, 491, 492, 500, 501, 516, 586, 588, - 605, 625, 632, 480, 301, 302, 444, 445, 314, 315, - 646, 647, 300, 600, 633, 597, 645, 627, 438, 378, - 0, 0, 381, 281, 305, 320, 0, 617, 502, 227, - 466, 290, 251, 0, 0, 211, 246, 230, 259, 274, - 277, 324, 391, 400, 429, 434, 296, 271, 244, 459, - 241, 485, 519, 520, 521, 523, 395, 266, 433, 396, - 0, 376, 576, 577, 316, 0, 0, 0, 528, 0, - 0, 0, 0, 2427, 0, 0, 0, 0, 0, 0, - 416, 0, 0, 0, 0, 0, 0, 0, 270, 0, - 0, 0, 0, 366, 267, 0, 0, 204, 505, 0, - 430, 0, 203, 0, 487, 252, 377, 374, 583, 282, - 273, 269, 250, 317, 385, 428, 518, 422, 0, 370, - 0, 0, 497, 401, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 323, 248, 325, 202, 413, 498, 286, 0, - 0, 0, 0, 0, 507, 194, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 238, 0, 0, 245, 0, - 0, 0, 351, 360, 359, 339, 340, 342, 344, 350, - 357, 363, 336, 345, 0, 0, 609, 0, 0, 0, - 265, 321, 272, 264, 580, 0, 0, 0, 0, 0, - 0, 596, 0, 0, 229, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 275, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 297, 0, 402, 257, 0, 453, 0, 0, 0, - 629, 0, 0, 0, 0, 0, 0, 0, 365, 0, - 330, 197, 225, 0, 0, 412, 461, 473, 0, 0, - 0, 253, 0, 471, 426, 604, 233, 284, 458, 432, - 469, 440, 287, 0, 0, 470, 372, 585, 450, 601, - 630, 631, 263, 406, 615, 522, 623, 648, 226, 260, - 420, 506, 607, 494, 397, 581, 582, 329, 493, 295, - 201, 369, 636, 224, 479, 371, 242, 231, 587, 612, - 299, 289, 456, 643, 213, 517, 598, 239, 483, 0, - 0, 651, 247, 504, 610, 599, 215, 594, 503, 393, - 326, 327, 214, 0, 457, 268, 293, 0, 0, 258, - 415, 589, 590, 256, 652, 228, 622, 220, 0, 621, - 408, 584, 595, 394, 383, 219, 593, 392, 382, 334, - 355, 356, 280, 307, 447, 375, 448, 306, 308, 404, - 403, 405, 207, 608, 626, 0, 208, 0, 499, 611, - 653, 452, 212, 234, 235, 237, 0, 279, 283, 291, - 294, 303, 304, 313, 367, 419, 446, 442, 451, 0, - 579, 602, 616, 628, 634, 635, 637, 638, 639, 640, - 641, 644, 642, 407, 311, 495, 333, 373, 0, 0, - 425, 472, 240, 606, 496, 199, 0, 0, 0, 0, - 254, 255, 0, 575, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 654, 655, 656, 657, 658, 659, 660, - 661, 662, 663, 664, 665, 666, 667, 668, 669, 670, - 671, 649, 508, 514, 509, 510, 511, 512, 513, 0, - 515, 0, 0, 0, 0, 0, 398, 0, 591, 592, - 672, 384, 486, 603, 335, 349, 352, 341, 361, 0, - 362, 337, 338, 343, 346, 347, 348, 353, 354, 358, - 364, 249, 210, 390, 399, 578, 312, 216, 217, 218, - 524, 525, 526, 527, 619, 620, 624, 205, 462, 463, - 464, 465, 292, 614, 309, 468, 467, 331, 332, 379, - 449, 540, 542, 553, 557, 559, 561, 567, 570, 541, - 543, 554, 558, 560, 562, 568, 571, 530, 532, 534, - 536, 549, 548, 545, 573, 574, 551, 556, 535, 547, - 552, 565, 572, 569, 529, 533, 537, 546, 564, 563, - 544, 555, 566, 550, 538, 531, 539, 0, 196, 221, - 368, 0, 454, 288, 650, 618, 484, 613, 206, 223, - 0, 262, 0, 0, 0, 0, 0, 0, 0, 0, + 672, 650, 509, 515, 510, 511, 512, 513, 514, 0, + 516, 0, 0, 0, 0, 0, 399, 0, 592, 593, + 673, 385, 487, 604, 336, 350, 353, 342, 362, 0, + 363, 338, 339, 344, 347, 348, 349, 354, 355, 359, + 365, 249, 210, 391, 400, 579, 313, 216, 217, 218, + 525, 526, 527, 528, 620, 621, 625, 205, 463, 464, + 465, 466, 293, 615, 310, 469, 468, 332, 333, 380, + 450, 541, 543, 554, 558, 560, 562, 568, 571, 542, + 544, 555, 559, 561, 563, 569, 572, 531, 533, 535, + 537, 550, 549, 546, 574, 575, 552, 557, 536, 548, + 553, 566, 573, 570, 530, 534, 538, 547, 565, 564, + 545, 556, 567, 551, 539, 532, 540, 0, 196, 221, + 369, 0, 455, 289, 651, 619, 485, 614, 206, 223, + 0, 263, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 198, 200, 209, 222, 232, 236, 243, - 261, 276, 278, 285, 298, 310, 318, 319, 322, 328, - 380, 386, 387, 388, 389, 409, 410, 411, 414, 417, - 418, 421, 423, 424, 427, 431, 435, 436, 437, 439, - 441, 443, 455, 460, 474, 475, 476, 477, 478, 481, - 482, 488, 489, 490, 491, 492, 500, 501, 516, 586, - 588, 605, 625, 632, 480, 301, 302, 444, 445, 314, - 315, 646, 647, 300, 600, 633, 597, 645, 627, 438, - 378, 0, 0, 381, 281, 305, 320, 0, 617, 502, - 227, 466, 290, 251, 0, 0, 211, 246, 230, 259, - 274, 277, 324, 391, 400, 429, 434, 296, 271, 244, - 459, 241, 485, 519, 520, 521, 523, 395, 266, 433, - 396, 0, 376, 576, 577, 316, 0, 0, 0, 528, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 416, 0, 0, 0, 0, 0, 0, 0, 270, - 0, 0, 0, 0, 366, 267, 0, 0, 204, 505, - 0, 430, 0, 203, 0, 487, 252, 377, 374, 583, - 282, 273, 269, 250, 317, 385, 428, 518, 422, 0, - 370, 0, 0, 497, 401, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 323, 248, 325, 202, 413, 498, 286, - 0, 0, 0, 0, 1780, 507, 724, 0, 0, 0, + 262, 277, 279, 286, 299, 311, 319, 320, 323, 329, + 381, 387, 388, 389, 390, 410, 411, 412, 415, 418, + 419, 422, 424, 425, 428, 432, 436, 437, 438, 440, + 442, 444, 456, 461, 475, 476, 477, 478, 479, 482, + 483, 489, 490, 491, 492, 493, 501, 502, 517, 587, + 589, 606, 626, 633, 481, 302, 303, 445, 446, 315, + 316, 647, 648, 301, 601, 634, 598, 646, 628, 439, + 379, 0, 0, 382, 282, 306, 321, 0, 618, 503, + 227, 467, 291, 251, 0, 0, 211, 246, 230, 260, + 275, 278, 325, 392, 401, 430, 435, 297, 272, 244, + 460, 241, 486, 520, 521, 522, 524, 396, 267, 434, + 397, 0, 377, 577, 578, 317, 0, 0, 0, 529, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 417, 0, 0, 0, 0, 0, 0, 0, 271, + 0, 0, 0, 0, 367, 268, 0, 0, 204, 506, + 0, 431, 0, 203, 0, 488, 253, 378, 375, 584, + 283, 274, 270, 250, 318, 386, 429, 519, 423, 0, + 371, 0, 0, 498, 402, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 324, 248, 326, 202, 414, 499, 287, + 0, 95, 0, 0, 0, 508, 725, 0, 0, 0, 0, 0, 0, 0, 0, 0, 238, 0, 0, 245, - 0, 0, 0, 351, 360, 359, 339, 340, 342, 344, - 350, 357, 363, 336, 345, 0, 0, 609, 0, 0, - 0, 265, 321, 272, 264, 580, 0, 0, 0, 0, - 0, 0, 596, 0, 0, 229, 0, 0, 0, 0, + 0, 0, 0, 352, 361, 360, 340, 341, 343, 345, + 351, 358, 364, 337, 346, 0, 0, 610, 0, 0, + 0, 266, 322, 273, 265, 581, 0, 0, 0, 0, + 0, 0, 597, 0, 0, 229, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 275, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 276, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -5825,147 +5716,73 @@ 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, 297, 0, 402, 257, 0, 453, 0, 0, - 0, 629, 0, 0, 0, 0, 0, 0, 0, 365, - 0, 330, 197, 225, 0, 0, 412, 461, 473, 0, - 0, 0, 253, 0, 471, 426, 604, 233, 284, 458, - 432, 469, 440, 287, 0, 0, 470, 372, 585, 450, - 601, 630, 631, 263, 406, 615, 522, 623, 648, 226, - 260, 420, 506, 607, 494, 397, 581, 582, 329, 493, - 295, 201, 369, 636, 224, 479, 371, 242, 231, 587, - 612, 299, 289, 456, 643, 213, 517, 598, 239, 483, - 0, 0, 651, 247, 504, 610, 599, 215, 594, 503, - 393, 326, 327, 214, 0, 457, 268, 293, 0, 0, - 258, 415, 589, 590, 256, 652, 228, 622, 220, 0, - 621, 408, 584, 595, 394, 383, 219, 593, 392, 382, - 334, 355, 356, 280, 307, 447, 375, 448, 306, 308, - 404, 403, 405, 207, 608, 626, 0, 208, 0, 499, - 611, 653, 452, 212, 234, 235, 237, 0, 279, 283, - 291, 294, 303, 304, 313, 367, 419, 446, 442, 451, - 0, 579, 602, 616, 628, 634, 635, 637, 638, 639, - 640, 641, 644, 642, 407, 311, 495, 333, 373, 0, - 0, 425, 472, 240, 606, 496, 199, 0, 0, 0, - 0, 254, 255, 0, 575, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 654, 655, 656, 657, 658, 659, + 0, 0, 298, 0, 403, 258, 0, 454, 0, 0, + 0, 630, 0, 0, 0, 0, 0, 0, 0, 366, + 0, 331, 197, 225, 0, 0, 413, 462, 474, 0, + 0, 0, 254, 0, 472, 427, 605, 233, 285, 459, + 433, 470, 441, 288, 0, 0, 471, 373, 586, 451, + 602, 631, 632, 264, 407, 616, 523, 624, 649, 226, + 261, 421, 507, 608, 495, 398, 582, 583, 330, 494, + 296, 201, 370, 637, 224, 480, 372, 242, 231, 588, + 613, 300, 252, 290, 457, 644, 213, 518, 599, 239, + 484, 0, 0, 652, 247, 505, 611, 600, 215, 595, + 504, 394, 327, 328, 214, 0, 458, 269, 294, 0, + 0, 259, 416, 590, 591, 257, 653, 228, 623, 220, + 0, 622, 409, 585, 596, 395, 384, 219, 594, 393, + 383, 335, 356, 357, 281, 308, 448, 376, 449, 307, + 309, 405, 404, 406, 207, 609, 627, 0, 208, 0, + 500, 612, 654, 453, 212, 234, 235, 237, 0, 280, + 284, 292, 295, 304, 305, 314, 368, 420, 447, 443, + 452, 0, 580, 603, 617, 629, 635, 636, 638, 639, + 640, 641, 642, 645, 643, 408, 312, 496, 334, 374, + 0, 0, 426, 473, 240, 607, 497, 199, 0, 0, + 0, 0, 255, 256, 0, 576, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 655, 656, 657, 658, 659, 660, 661, 662, 663, 664, 665, 666, 667, 668, 669, - 670, 671, 649, 508, 514, 509, 510, 511, 512, 513, - 0, 515, 0, 0, 0, 0, 0, 398, 0, 591, - 592, 672, 384, 486, 603, 335, 349, 352, 341, 361, - 0, 362, 337, 338, 343, 346, 347, 348, 353, 354, - 358, 364, 249, 210, 390, 399, 578, 312, 216, 217, - 218, 524, 525, 526, 527, 619, 620, 624, 205, 462, - 463, 464, 465, 292, 614, 309, 468, 467, 331, 332, - 379, 449, 540, 542, 553, 557, 559, 561, 567, 570, - 541, 543, 554, 558, 560, 562, 568, 571, 530, 532, - 534, 536, 549, 548, 545, 573, 574, 551, 556, 535, - 547, 552, 565, 572, 569, 529, 533, 537, 546, 564, - 563, 544, 555, 566, 550, 538, 531, 539, 0, 196, - 221, 368, 0, 454, 288, 650, 618, 484, 613, 206, - 223, 0, 262, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 198, 200, 209, 222, 232, 236, - 243, 261, 276, 278, 285, 298, 310, 318, 319, 322, - 328, 380, 386, 387, 388, 389, 409, 410, 411, 414, - 417, 418, 421, 423, 424, 427, 431, 435, 436, 437, - 439, 441, 443, 455, 460, 474, 475, 476, 477, 478, - 481, 482, 488, 489, 490, 491, 492, 500, 501, 516, - 586, 588, 605, 625, 632, 480, 301, 302, 444, 445, - 314, 315, 646, 647, 300, 600, 633, 597, 645, 627, - 438, 378, 0, 0, 381, 281, 305, 320, 0, 617, - 502, 227, 466, 290, 251, 0, 0, 211, 246, 230, - 259, 274, 277, 324, 391, 400, 429, 434, 296, 271, - 244, 459, 241, 485, 519, 520, 521, 523, 395, 266, - 433, 396, 0, 376, 576, 577, 316, 0, 0, 0, - 528, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 416, 0, 0, 0, 0, 0, 0, 0, - 270, 0, 0, 0, 0, 366, 267, 0, 0, 204, - 505, 0, 430, 0, 203, 0, 487, 252, 377, 374, - 583, 282, 273, 269, 250, 317, 385, 428, 518, 422, - 0, 370, 0, 0, 497, 401, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 323, 248, 325, 202, 413, 498, - 286, 0, 0, 0, 0, 0, 507, 194, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 238, 0, 0, - 245, 0, 0, 0, 351, 360, 359, 339, 340, 342, - 344, 350, 357, 363, 336, 345, 0, 0, 609, 0, - 0, 0, 265, 321, 272, 264, 580, 0, 0, 0, - 0, 0, 0, 596, 0, 0, 229, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 275, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 297, 0, 402, 257, 0, 453, 0, - 0, 0, 629, 0, 0, 0, 0, 0, 0, 0, - 365, 0, 330, 197, 225, 0, 0, 412, 461, 473, - 0, 0, 0, 253, 0, 471, 426, 604, 233, 284, - 458, 432, 469, 440, 287, 0, 0, 470, 372, 585, - 450, 601, 630, 631, 263, 406, 615, 522, 623, 648, - 226, 260, 420, 506, 607, 494, 397, 581, 582, 329, - 493, 295, 201, 369, 636, 224, 479, 371, 242, 231, - 587, 612, 299, 289, 456, 643, 213, 517, 598, 239, - 483, 0, 0, 651, 247, 504, 610, 599, 215, 594, - 503, 393, 326, 327, 214, 0, 457, 268, 293, 0, - 0, 258, 415, 589, 590, 256, 652, 228, 622, 220, - 0, 621, 408, 584, 595, 394, 383, 219, 593, 392, - 382, 334, 355, 356, 280, 307, 447, 375, 448, 306, - 308, 404, 403, 405, 207, 608, 626, 0, 208, 0, - 499, 611, 653, 452, 212, 234, 235, 237, 0, 279, - 283, 291, 294, 303, 304, 313, 367, 419, 446, 442, - 451, 0, 579, 602, 616, 628, 634, 635, 637, 638, - 639, 640, 641, 644, 642, 407, 311, 495, 333, 373, - 0, 0, 425, 472, 240, 606, 496, 199, 0, 0, - 0, 0, 254, 255, 0, 575, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 654, 655, 656, 657, 658, - 659, 660, 661, 662, 663, 664, 665, 666, 667, 668, - 669, 670, 671, 649, 508, 514, 509, 510, 511, 512, - 513, 0, 515, 0, 0, 0, 0, 0, 398, 0, - 591, 592, 672, 384, 486, 603, 335, 349, 352, 341, - 361, 0, 362, 337, 338, 343, 346, 347, 348, 353, - 354, 358, 364, 249, 210, 390, 399, 578, 312, 216, - 217, 218, 524, 525, 526, 527, 619, 620, 624, 205, - 462, 463, 464, 465, 292, 614, 309, 468, 467, 331, - 332, 379, 449, 540, 542, 553, 557, 559, 561, 567, - 570, 541, 543, 554, 558, 560, 562, 568, 571, 530, - 532, 534, 536, 549, 548, 545, 573, 574, 551, 556, - 535, 547, 552, 565, 572, 569, 529, 533, 537, 546, - 564, 563, 544, 555, 566, 550, 538, 531, 539, 0, - 196, 221, 368, 2079, 454, 288, 650, 618, 484, 613, - 206, 223, 0, 262, 0, 0, 0, 0, 0, 0, + 670, 671, 672, 650, 509, 515, 510, 511, 512, 513, + 514, 0, 516, 0, 0, 0, 0, 0, 399, 0, + 592, 593, 673, 385, 487, 604, 336, 350, 353, 342, + 362, 0, 363, 338, 339, 344, 347, 348, 349, 354, + 355, 359, 365, 249, 210, 391, 400, 579, 313, 216, + 217, 218, 525, 526, 527, 528, 620, 621, 625, 205, + 463, 464, 465, 466, 293, 615, 310, 469, 468, 332, + 333, 380, 450, 541, 543, 554, 558, 560, 562, 568, + 571, 542, 544, 555, 559, 561, 563, 569, 572, 531, + 533, 535, 537, 550, 549, 546, 574, 575, 552, 557, + 536, 548, 553, 566, 573, 570, 530, 534, 538, 547, + 565, 564, 545, 556, 567, 551, 539, 532, 540, 0, + 196, 221, 369, 0, 455, 289, 651, 619, 485, 614, + 206, 223, 0, 263, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 198, 200, 209, 222, 232, - 236, 243, 261, 276, 278, 285, 298, 310, 318, 319, - 322, 328, 380, 386, 387, 388, 389, 409, 410, 411, - 414, 417, 418, 421, 423, 424, 427, 431, 435, 436, - 437, 439, 441, 443, 455, 460, 474, 475, 476, 477, - 478, 481, 482, 488, 489, 490, 491, 492, 500, 501, - 516, 586, 588, 605, 625, 632, 480, 301, 302, 444, - 445, 314, 315, 646, 647, 300, 600, 633, 597, 645, - 627, 438, 378, 0, 0, 381, 281, 305, 320, 0, - 617, 502, 227, 466, 290, 251, 0, 0, 211, 246, - 230, 259, 274, 277, 324, 391, 400, 429, 434, 296, - 271, 244, 459, 241, 485, 519, 520, 521, 523, 395, - 266, 433, 396, 0, 376, 576, 577, 316, 0, 0, - 0, 528, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 416, 0, 0, 0, 0, 0, 0, - 0, 270, 0, 0, 0, 0, 366, 267, 0, 0, - 204, 505, 0, 430, 0, 203, 0, 487, 252, 377, - 374, 583, 282, 273, 269, 250, 317, 385, 428, 518, - 422, 0, 370, 0, 0, 497, 401, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 323, 248, 325, 202, 413, - 498, 286, 0, 0, 0, 0, 2070, 507, 724, 0, + 236, 243, 262, 277, 279, 286, 299, 311, 319, 320, + 323, 329, 381, 387, 388, 389, 390, 410, 411, 412, + 415, 418, 419, 422, 424, 425, 428, 432, 436, 437, + 438, 440, 442, 444, 456, 461, 475, 476, 477, 478, + 479, 482, 483, 489, 490, 491, 492, 493, 501, 502, + 517, 587, 589, 606, 626, 633, 481, 302, 303, 445, + 446, 315, 316, 647, 648, 301, 601, 634, 598, 646, + 628, 439, 379, 0, 0, 382, 282, 306, 321, 0, + 618, 503, 227, 467, 291, 251, 0, 0, 211, 246, + 230, 260, 275, 278, 325, 392, 401, 430, 435, 297, + 272, 244, 460, 241, 486, 520, 521, 522, 524, 396, + 267, 434, 397, 0, 377, 577, 578, 317, 0, 0, + 0, 529, 0, 0, 0, 0, 2428, 0, 0, 0, + 0, 0, 0, 417, 0, 0, 0, 0, 0, 0, + 0, 271, 0, 0, 0, 0, 367, 268, 0, 0, + 204, 506, 0, 431, 0, 203, 0, 488, 253, 378, + 375, 584, 283, 274, 270, 250, 318, 386, 429, 519, + 423, 0, 371, 0, 0, 498, 402, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 324, 248, 326, 202, 414, + 499, 287, 0, 0, 0, 0, 0, 508, 194, 0, 0, 0, 0, 0, 0, 0, 0, 0, 238, 0, - 0, 245, 0, 0, 0, 351, 360, 359, 339, 340, - 342, 344, 350, 357, 363, 336, 345, 0, 0, 609, - 0, 0, 0, 265, 321, 272, 264, 580, 0, 0, - 0, 0, 0, 0, 596, 0, 0, 229, 0, 0, + 0, 245, 0, 0, 0, 352, 361, 360, 340, 341, + 343, 345, 351, 358, 364, 337, 346, 0, 0, 610, + 0, 0, 0, 266, 322, 273, 265, 581, 0, 0, + 0, 0, 0, 0, 597, 0, 0, 229, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 275, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 276, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -5973,443 +5790,221 @@ 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, 297, 0, 402, 257, 0, 453, - 0, 0, 0, 629, 0, 0, 0, 0, 0, 0, - 0, 365, 0, 330, 197, 225, 0, 0, 412, 461, - 473, 0, 0, 0, 253, 0, 471, 426, 604, 233, - 284, 458, 432, 469, 440, 287, 0, 0, 470, 372, - 585, 450, 601, 630, 631, 263, 406, 615, 522, 623, - 648, 226, 260, 420, 506, 607, 494, 397, 581, 582, - 329, 493, 295, 201, 369, 636, 224, 479, 371, 242, - 231, 587, 612, 299, 289, 456, 643, 213, 517, 598, - 239, 483, 0, 0, 651, 247, 504, 610, 599, 215, - 594, 503, 393, 326, 327, 214, 0, 457, 268, 293, - 0, 0, 258, 415, 589, 590, 256, 652, 228, 622, - 220, 0, 621, 408, 584, 595, 394, 383, 219, 593, - 392, 382, 334, 355, 356, 280, 307, 447, 375, 448, - 306, 308, 404, 403, 405, 207, 608, 626, 0, 208, - 0, 499, 611, 653, 452, 212, 234, 235, 237, 0, - 279, 283, 291, 294, 303, 304, 313, 367, 419, 446, - 442, 451, 0, 579, 602, 616, 628, 634, 635, 637, - 638, 639, 640, 641, 644, 642, 407, 311, 495, 333, - 373, 0, 0, 425, 472, 240, 606, 496, 199, 0, - 0, 0, 0, 254, 255, 0, 575, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 654, 655, 656, 657, + 0, 0, 0, 0, 298, 0, 403, 258, 0, 454, + 0, 0, 0, 630, 0, 0, 0, 0, 0, 0, + 0, 366, 0, 331, 197, 225, 0, 0, 413, 462, + 474, 0, 0, 0, 254, 0, 472, 427, 605, 233, + 285, 459, 433, 470, 441, 288, 0, 0, 471, 373, + 586, 451, 602, 631, 632, 264, 407, 616, 523, 624, + 649, 226, 261, 421, 507, 608, 495, 398, 582, 583, + 330, 494, 296, 201, 370, 637, 224, 480, 372, 242, + 231, 588, 613, 300, 252, 290, 457, 644, 213, 518, + 599, 239, 484, 0, 0, 652, 247, 505, 611, 600, + 215, 595, 504, 394, 327, 328, 214, 0, 458, 269, + 294, 0, 0, 259, 416, 590, 591, 257, 653, 228, + 623, 220, 0, 622, 409, 585, 596, 395, 384, 219, + 594, 393, 383, 335, 356, 357, 281, 308, 448, 376, + 449, 307, 309, 405, 404, 406, 207, 609, 627, 0, + 208, 0, 500, 612, 654, 453, 212, 234, 235, 237, + 0, 280, 284, 292, 295, 304, 305, 314, 368, 420, + 447, 443, 452, 0, 580, 603, 617, 629, 635, 636, + 638, 639, 640, 641, 642, 645, 643, 408, 312, 496, + 334, 374, 0, 0, 426, 473, 240, 607, 497, 199, + 0, 0, 0, 0, 255, 256, 0, 576, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 655, 656, 657, 658, 659, 660, 661, 662, 663, 664, 665, 666, 667, - 668, 669, 670, 671, 649, 508, 514, 509, 510, 511, - 512, 513, 0, 515, 0, 0, 0, 0, 0, 398, - 0, 591, 592, 672, 384, 486, 603, 335, 349, 352, - 341, 361, 0, 362, 337, 338, 343, 346, 347, 348, - 353, 354, 358, 364, 249, 210, 390, 399, 578, 312, - 216, 217, 218, 524, 525, 526, 527, 619, 620, 624, - 205, 462, 463, 464, 465, 292, 614, 309, 468, 467, - 331, 332, 379, 449, 540, 542, 553, 557, 559, 561, - 567, 570, 541, 543, 554, 558, 560, 562, 568, 571, - 530, 532, 534, 536, 549, 548, 545, 573, 574, 551, - 556, 535, 547, 552, 565, 572, 569, 529, 533, 537, - 546, 564, 563, 544, 555, 566, 550, 538, 531, 539, - 0, 196, 221, 368, 0, 454, 288, 650, 618, 484, - 613, 206, 223, 0, 262, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 198, 200, 209, 222, - 232, 236, 243, 261, 276, 278, 285, 298, 310, 318, - 319, 322, 328, 380, 386, 387, 388, 389, 409, 410, - 411, 414, 417, 418, 421, 423, 424, 427, 431, 435, - 436, 437, 439, 441, 443, 455, 460, 474, 475, 476, - 477, 478, 481, 482, 488, 489, 490, 491, 492, 500, - 501, 516, 586, 588, 605, 625, 632, 480, 301, 302, - 444, 445, 314, 315, 646, 647, 300, 600, 633, 597, - 645, 627, 438, 378, 0, 0, 381, 281, 305, 320, - 0, 617, 502, 227, 466, 290, 251, 0, 0, 211, - 246, 230, 259, 274, 277, 324, 391, 400, 429, 434, - 296, 271, 244, 459, 241, 485, 519, 520, 521, 523, - 395, 266, 433, 396, 0, 376, 576, 577, 316, 0, - 0, 0, 528, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 416, 0, 0, 0, 0, 0, - 0, 0, 270, 0, 0, 0, 0, 366, 267, 0, - 1928, 204, 505, 0, 430, 0, 203, 0, 487, 252, - 377, 374, 583, 282, 273, 269, 250, 317, 385, 428, - 518, 422, 0, 370, 0, 0, 497, 401, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 323, 248, 325, 202, - 413, 498, 286, 0, 0, 0, 0, 0, 507, 724, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 238, - 0, 0, 245, 0, 0, 0, 351, 360, 359, 339, - 340, 342, 344, 350, 357, 363, 336, 345, 0, 0, - 609, 0, 0, 0, 265, 321, 272, 264, 580, 0, - 0, 0, 0, 0, 0, 596, 0, 0, 229, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 275, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 297, 0, 402, 257, 0, - 453, 0, 0, 0, 629, 0, 0, 0, 0, 0, - 0, 0, 365, 0, 330, 197, 225, 0, 0, 412, - 461, 473, 0, 0, 0, 253, 0, 471, 426, 604, - 233, 284, 458, 432, 469, 440, 287, 0, 0, 470, - 372, 585, 450, 601, 630, 631, 263, 406, 615, 522, - 623, 648, 226, 260, 420, 506, 607, 494, 397, 581, - 582, 329, 493, 295, 201, 369, 636, 224, 479, 371, - 242, 231, 587, 612, 299, 289, 456, 643, 213, 517, - 598, 239, 483, 0, 0, 651, 247, 504, 610, 599, - 215, 594, 503, 393, 326, 327, 214, 0, 457, 268, - 293, 0, 0, 258, 415, 589, 590, 256, 652, 228, - 622, 220, 0, 621, 408, 584, 595, 394, 383, 219, - 593, 392, 382, 334, 355, 356, 280, 307, 447, 375, - 448, 306, 308, 404, 403, 405, 207, 608, 626, 0, - 208, 0, 499, 611, 653, 452, 212, 234, 235, 237, - 0, 279, 283, 291, 294, 303, 304, 313, 367, 419, - 446, 442, 451, 0, 579, 602, 616, 628, 634, 635, - 637, 638, 639, 640, 641, 644, 642, 407, 311, 495, - 333, 373, 0, 0, 425, 472, 240, 606, 496, 199, - 0, 0, 0, 0, 254, 255, 0, 575, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 654, 655, 656, - 657, 658, 659, 660, 661, 662, 663, 664, 665, 666, - 667, 668, 669, 670, 671, 649, 508, 514, 509, 510, - 511, 512, 513, 0, 515, 0, 0, 0, 0, 0, - 398, 0, 591, 592, 672, 384, 486, 603, 335, 349, - 352, 341, 361, 0, 362, 337, 338, 343, 346, 347, - 348, 353, 354, 358, 364, 249, 210, 390, 399, 578, - 312, 216, 217, 218, 524, 525, 526, 527, 619, 620, - 624, 205, 462, 463, 464, 465, 292, 614, 309, 468, - 467, 331, 332, 379, 449, 540, 542, 553, 557, 559, - 561, 567, 570, 541, 543, 554, 558, 560, 562, 568, - 571, 530, 532, 534, 536, 549, 548, 545, 573, 574, - 551, 556, 535, 547, 552, 565, 572, 569, 529, 533, - 537, 546, 564, 563, 544, 555, 566, 550, 538, 531, - 539, 0, 196, 221, 368, 0, 454, 288, 650, 618, - 484, 613, 206, 223, 0, 262, 0, 0, 0, 0, + 668, 669, 670, 671, 672, 650, 509, 515, 510, 511, + 512, 513, 514, 0, 516, 0, 0, 0, 0, 0, + 399, 0, 592, 593, 673, 385, 487, 604, 336, 350, + 353, 342, 362, 0, 363, 338, 339, 344, 347, 348, + 349, 354, 355, 359, 365, 249, 210, 391, 400, 579, + 313, 216, 217, 218, 525, 526, 527, 528, 620, 621, + 625, 205, 463, 464, 465, 466, 293, 615, 310, 469, + 468, 332, 333, 380, 450, 541, 543, 554, 558, 560, + 562, 568, 571, 542, 544, 555, 559, 561, 563, 569, + 572, 531, 533, 535, 537, 550, 549, 546, 574, 575, + 552, 557, 536, 548, 553, 566, 573, 570, 530, 534, + 538, 547, 565, 564, 545, 556, 567, 551, 539, 532, + 540, 0, 196, 221, 369, 0, 455, 289, 651, 619, + 485, 614, 206, 223, 0, 263, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 198, 200, 209, - 222, 232, 236, 243, 261, 276, 278, 285, 298, 310, - 318, 319, 322, 328, 380, 386, 387, 388, 389, 409, - 410, 411, 414, 417, 418, 421, 423, 424, 427, 431, - 435, 436, 437, 439, 441, 443, 455, 460, 474, 475, - 476, 477, 478, 481, 482, 488, 489, 490, 491, 492, - 500, 501, 516, 586, 588, 605, 625, 632, 480, 301, - 302, 444, 445, 314, 315, 646, 647, 300, 600, 633, - 597, 645, 627, 438, 378, 0, 0, 381, 281, 305, - 320, 0, 617, 502, 227, 466, 290, 251, 0, 0, - 211, 246, 230, 259, 274, 277, 324, 391, 400, 429, - 434, 296, 271, 244, 459, 241, 485, 519, 520, 521, - 523, 395, 266, 433, 396, 0, 376, 576, 577, 316, - 0, 0, 0, 528, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 416, 0, 0, 0, 0, - 0, 0, 0, 270, 0, 0, 0, 0, 366, 267, - 0, 1926, 204, 505, 0, 430, 0, 203, 0, 487, - 252, 377, 374, 583, 282, 273, 269, 250, 317, 385, - 428, 518, 422, 0, 370, 0, 0, 497, 401, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 323, 248, 325, - 202, 413, 498, 286, 0, 0, 0, 0, 0, 507, - 724, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 238, 0, 0, 245, 0, 0, 0, 351, 360, 359, - 339, 340, 342, 344, 350, 357, 363, 336, 345, 0, - 0, 609, 0, 0, 0, 265, 321, 272, 264, 580, - 0, 0, 0, 0, 0, 0, 596, 0, 0, 229, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 275, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 297, 0, 402, 257, - 0, 453, 0, 0, 0, 629, 0, 0, 0, 0, - 0, 0, 0, 365, 0, 330, 197, 225, 0, 0, - 412, 461, 473, 0, 0, 0, 253, 0, 471, 426, - 604, 233, 284, 458, 432, 469, 440, 287, 0, 0, - 470, 372, 585, 450, 601, 630, 631, 263, 406, 615, - 522, 623, 648, 226, 260, 420, 506, 607, 494, 397, - 581, 582, 329, 493, 295, 201, 369, 636, 224, 479, - 371, 242, 231, 587, 612, 299, 289, 456, 643, 213, - 517, 598, 239, 483, 0, 0, 651, 247, 504, 610, - 599, 215, 594, 503, 393, 326, 327, 214, 0, 457, - 268, 293, 0, 0, 258, 415, 589, 590, 256, 652, - 228, 622, 220, 0, 621, 408, 584, 595, 394, 383, - 219, 593, 392, 382, 334, 355, 356, 280, 307, 447, - 375, 448, 306, 308, 404, 403, 405, 207, 608, 626, - 0, 208, 0, 499, 611, 653, 452, 212, 234, 235, - 237, 0, 279, 283, 291, 294, 303, 304, 313, 367, - 419, 446, 442, 451, 0, 579, 602, 616, 628, 634, - 635, 637, 638, 639, 640, 641, 644, 642, 407, 311, - 495, 333, 373, 0, 0, 425, 472, 240, 606, 496, - 199, 0, 0, 0, 0, 254, 255, 0, 575, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 654, 655, + 222, 232, 236, 243, 262, 277, 279, 286, 299, 311, + 319, 320, 323, 329, 381, 387, 388, 389, 390, 410, + 411, 412, 415, 418, 419, 422, 424, 425, 428, 432, + 436, 437, 438, 440, 442, 444, 456, 461, 475, 476, + 477, 478, 479, 482, 483, 489, 490, 491, 492, 493, + 501, 502, 517, 587, 589, 606, 626, 633, 481, 302, + 303, 445, 446, 315, 316, 647, 648, 301, 601, 634, + 598, 646, 628, 439, 379, 0, 0, 382, 282, 306, + 321, 0, 618, 503, 227, 467, 291, 251, 0, 0, + 211, 246, 230, 260, 275, 278, 325, 392, 401, 430, + 435, 297, 272, 244, 460, 241, 486, 520, 521, 522, + 524, 396, 267, 434, 397, 0, 377, 577, 578, 317, + 0, 0, 0, 529, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 417, 0, 0, 0, 0, + 0, 0, 0, 271, 0, 0, 0, 0, 367, 268, + 0, 0, 204, 506, 0, 431, 0, 203, 0, 488, + 253, 378, 375, 584, 283, 274, 270, 250, 318, 386, + 429, 519, 423, 0, 371, 0, 0, 498, 402, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 324, 248, 326, + 202, 414, 499, 287, 0, 0, 0, 0, 1781, 508, + 725, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 238, 0, 0, 245, 0, 0, 0, 352, 361, 360, + 340, 341, 343, 345, 351, 358, 364, 337, 346, 0, + 0, 610, 0, 0, 0, 266, 322, 273, 265, 581, + 0, 0, 0, 0, 0, 0, 597, 0, 0, 229, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 276, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 298, 0, 403, 258, + 0, 454, 0, 0, 0, 630, 0, 0, 0, 0, + 0, 0, 0, 366, 0, 331, 197, 225, 0, 0, + 413, 462, 474, 0, 0, 0, 254, 0, 472, 427, + 605, 233, 285, 459, 433, 470, 441, 288, 0, 0, + 471, 373, 586, 451, 602, 631, 632, 264, 407, 616, + 523, 624, 649, 226, 261, 421, 507, 608, 495, 398, + 582, 583, 330, 494, 296, 201, 370, 637, 224, 480, + 372, 242, 231, 588, 613, 300, 252, 290, 457, 644, + 213, 518, 599, 239, 484, 0, 0, 652, 247, 505, + 611, 600, 215, 595, 504, 394, 327, 328, 214, 0, + 458, 269, 294, 0, 0, 259, 416, 590, 591, 257, + 653, 228, 623, 220, 0, 622, 409, 585, 596, 395, + 384, 219, 594, 393, 383, 335, 356, 357, 281, 308, + 448, 376, 449, 307, 309, 405, 404, 406, 207, 609, + 627, 0, 208, 0, 500, 612, 654, 453, 212, 234, + 235, 237, 0, 280, 284, 292, 295, 304, 305, 314, + 368, 420, 447, 443, 452, 0, 580, 603, 617, 629, + 635, 636, 638, 639, 640, 641, 642, 645, 643, 408, + 312, 496, 334, 374, 0, 0, 426, 473, 240, 607, + 497, 199, 0, 0, 0, 0, 255, 256, 0, 576, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 655, 656, 657, 658, 659, 660, 661, 662, 663, 664, 665, - 666, 667, 668, 669, 670, 671, 649, 508, 514, 509, - 510, 511, 512, 513, 0, 515, 0, 0, 0, 0, - 0, 398, 0, 591, 592, 672, 384, 486, 603, 335, - 349, 352, 341, 361, 0, 362, 337, 338, 343, 346, - 347, 348, 353, 354, 358, 364, 249, 210, 390, 399, - 578, 312, 216, 217, 218, 524, 525, 526, 527, 619, - 620, 624, 205, 462, 463, 464, 465, 292, 614, 309, - 468, 467, 331, 332, 379, 449, 540, 542, 553, 557, - 559, 561, 567, 570, 541, 543, 554, 558, 560, 562, - 568, 571, 530, 532, 534, 536, 549, 548, 545, 573, - 574, 551, 556, 535, 547, 552, 565, 572, 569, 529, - 533, 537, 546, 564, 563, 544, 555, 566, 550, 538, - 531, 539, 0, 196, 221, 368, 0, 454, 288, 650, - 618, 484, 613, 206, 223, 0, 262, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 198, 200, - 209, 222, 232, 236, 243, 261, 276, 278, 285, 298, - 310, 318, 319, 322, 328, 380, 386, 387, 388, 389, - 409, 410, 411, 414, 417, 418, 421, 423, 424, 427, - 431, 435, 436, 437, 439, 441, 443, 455, 460, 474, - 475, 476, 477, 478, 481, 482, 488, 489, 490, 491, - 492, 500, 501, 516, 586, 588, 605, 625, 632, 480, - 301, 302, 444, 445, 314, 315, 646, 647, 300, 600, - 633, 597, 645, 627, 438, 378, 0, 0, 381, 281, - 305, 320, 0, 617, 502, 227, 466, 290, 251, 0, - 0, 211, 246, 230, 259, 274, 277, 324, 391, 400, - 429, 434, 296, 271, 244, 459, 241, 485, 519, 520, - 521, 523, 395, 266, 433, 396, 0, 376, 576, 577, - 316, 0, 0, 0, 528, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 416, 0, 0, 0, - 0, 0, 0, 0, 270, 0, 0, 0, 0, 366, - 267, 0, 1924, 204, 505, 0, 430, 0, 203, 0, - 487, 252, 377, 374, 583, 282, 273, 269, 250, 317, - 385, 428, 518, 422, 0, 370, 0, 0, 497, 401, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 323, 248, - 325, 202, 413, 498, 286, 0, 0, 0, 0, 0, - 507, 724, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 238, 0, 0, 245, 0, 0, 0, 351, 360, - 359, 339, 340, 342, 344, 350, 357, 363, 336, 345, - 0, 0, 609, 0, 0, 0, 265, 321, 272, 264, - 580, 0, 0, 0, 0, 0, 0, 596, 0, 0, - 229, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 275, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 297, 0, 402, - 257, 0, 453, 0, 0, 0, 629, 0, 0, 0, - 0, 0, 0, 0, 365, 0, 330, 197, 225, 0, - 0, 412, 461, 473, 0, 0, 0, 253, 0, 471, - 426, 604, 233, 284, 458, 432, 469, 440, 287, 0, - 0, 470, 372, 585, 450, 601, 630, 631, 263, 406, - 615, 522, 623, 648, 226, 260, 420, 506, 607, 494, - 397, 581, 582, 329, 493, 295, 201, 369, 636, 224, - 479, 371, 242, 231, 587, 612, 299, 289, 456, 643, - 213, 517, 598, 239, 483, 0, 0, 651, 247, 504, - 610, 599, 215, 594, 503, 393, 326, 327, 214, 0, - 457, 268, 293, 0, 0, 258, 415, 589, 590, 256, - 652, 228, 622, 220, 0, 621, 408, 584, 595, 394, - 383, 219, 593, 392, 382, 334, 355, 356, 280, 307, - 447, 375, 448, 306, 308, 404, 403, 405, 207, 608, - 626, 0, 208, 0, 499, 611, 653, 452, 212, 234, - 235, 237, 0, 279, 283, 291, 294, 303, 304, 313, - 367, 419, 446, 442, 451, 0, 579, 602, 616, 628, - 634, 635, 637, 638, 639, 640, 641, 644, 642, 407, - 311, 495, 333, 373, 0, 0, 425, 472, 240, 606, - 496, 199, 0, 0, 0, 0, 254, 255, 0, 575, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 654, - 655, 656, 657, 658, 659, 660, 661, 662, 663, 664, - 665, 666, 667, 668, 669, 670, 671, 649, 508, 514, - 509, 510, 511, 512, 513, 0, 515, 0, 0, 0, - 0, 0, 398, 0, 591, 592, 672, 384, 486, 603, - 335, 349, 352, 341, 361, 0, 362, 337, 338, 343, - 346, 347, 348, 353, 354, 358, 364, 249, 210, 390, - 399, 578, 312, 216, 217, 218, 524, 525, 526, 527, - 619, 620, 624, 205, 462, 463, 464, 465, 292, 614, - 309, 468, 467, 331, 332, 379, 449, 540, 542, 553, - 557, 559, 561, 567, 570, 541, 543, 554, 558, 560, - 562, 568, 571, 530, 532, 534, 536, 549, 548, 545, - 573, 574, 551, 556, 535, 547, 552, 565, 572, 569, - 529, 533, 537, 546, 564, 563, 544, 555, 566, 550, - 538, 531, 539, 0, 196, 221, 368, 0, 454, 288, - 650, 618, 484, 613, 206, 223, 0, 262, 0, 0, + 666, 667, 668, 669, 670, 671, 672, 650, 509, 515, + 510, 511, 512, 513, 514, 0, 516, 0, 0, 0, + 0, 0, 399, 0, 592, 593, 673, 385, 487, 604, + 336, 350, 353, 342, 362, 0, 363, 338, 339, 344, + 347, 348, 349, 354, 355, 359, 365, 249, 210, 391, + 400, 579, 313, 216, 217, 218, 525, 526, 527, 528, + 620, 621, 625, 205, 463, 464, 465, 466, 293, 615, + 310, 469, 468, 332, 333, 380, 450, 541, 543, 554, + 558, 560, 562, 568, 571, 542, 544, 555, 559, 561, + 563, 569, 572, 531, 533, 535, 537, 550, 549, 546, + 574, 575, 552, 557, 536, 548, 553, 566, 573, 570, + 530, 534, 538, 547, 565, 564, 545, 556, 567, 551, + 539, 532, 540, 0, 196, 221, 369, 0, 455, 289, + 651, 619, 485, 614, 206, 223, 0, 263, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 198, - 200, 209, 222, 232, 236, 243, 261, 276, 278, 285, - 298, 310, 318, 319, 322, 328, 380, 386, 387, 388, - 389, 409, 410, 411, 414, 417, 418, 421, 423, 424, - 427, 431, 435, 436, 437, 439, 441, 443, 455, 460, - 474, 475, 476, 477, 478, 481, 482, 488, 489, 490, - 491, 492, 500, 501, 516, 586, 588, 605, 625, 632, - 480, 301, 302, 444, 445, 314, 315, 646, 647, 300, - 600, 633, 597, 645, 627, 438, 378, 0, 0, 381, - 281, 305, 320, 0, 617, 502, 227, 466, 290, 251, - 0, 0, 211, 246, 230, 259, 274, 277, 324, 391, - 400, 429, 434, 296, 271, 244, 459, 241, 485, 519, - 520, 521, 523, 395, 266, 433, 396, 0, 376, 576, - 577, 316, 0, 0, 0, 528, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 416, 0, 0, - 0, 0, 0, 0, 0, 270, 0, 0, 0, 0, - 366, 267, 0, 1922, 204, 505, 0, 430, 0, 203, - 0, 487, 252, 377, 374, 583, 282, 273, 269, 250, - 317, 385, 428, 518, 422, 0, 370, 0, 0, 497, - 401, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 323, - 248, 325, 202, 413, 498, 286, 0, 0, 0, 0, - 0, 507, 724, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 238, 0, 0, 245, 0, 0, 0, 351, - 360, 359, 339, 340, 342, 344, 350, 357, 363, 336, - 345, 0, 0, 609, 0, 0, 0, 265, 321, 272, - 264, 580, 0, 0, 0, 0, 0, 0, 596, 0, + 200, 209, 222, 232, 236, 243, 262, 277, 279, 286, + 299, 311, 319, 320, 323, 329, 381, 387, 388, 389, + 390, 410, 411, 412, 415, 418, 419, 422, 424, 425, + 428, 432, 436, 437, 438, 440, 442, 444, 456, 461, + 475, 476, 477, 478, 479, 482, 483, 489, 490, 491, + 492, 493, 501, 502, 517, 587, 589, 606, 626, 633, + 481, 302, 303, 445, 446, 315, 316, 647, 648, 301, + 601, 634, 598, 646, 628, 439, 379, 0, 0, 382, + 282, 306, 321, 0, 618, 503, 227, 467, 291, 251, + 0, 0, 211, 246, 230, 260, 275, 278, 325, 392, + 401, 430, 435, 297, 272, 244, 460, 241, 486, 520, + 521, 522, 524, 396, 267, 434, 397, 0, 377, 577, + 578, 317, 0, 0, 0, 529, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 417, 0, 0, + 0, 0, 0, 0, 0, 271, 0, 0, 0, 0, + 367, 268, 0, 0, 204, 506, 0, 431, 0, 203, + 0, 488, 253, 378, 375, 584, 283, 274, 270, 250, + 318, 386, 429, 519, 423, 0, 371, 0, 0, 498, + 402, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 324, + 248, 326, 202, 414, 499, 287, 0, 0, 0, 0, + 0, 508, 194, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 238, 0, 0, 245, 0, 0, 0, 352, + 361, 360, 340, 341, 343, 345, 351, 358, 364, 337, + 346, 0, 0, 610, 0, 0, 0, 266, 322, 273, + 265, 581, 0, 0, 0, 0, 0, 0, 597, 0, 0, 229, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 275, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 297, 0, - 402, 257, 0, 453, 0, 0, 0, 629, 0, 0, - 0, 0, 0, 0, 0, 365, 0, 330, 197, 225, - 0, 0, 412, 461, 473, 0, 0, 0, 253, 0, - 471, 426, 604, 233, 284, 458, 432, 469, 440, 287, - 0, 0, 470, 372, 585, 450, 601, 630, 631, 263, - 406, 615, 522, 623, 648, 226, 260, 420, 506, 607, - 494, 397, 581, 582, 329, 493, 295, 201, 369, 636, - 224, 479, 371, 242, 231, 587, 612, 299, 289, 456, - 643, 213, 517, 598, 239, 483, 0, 0, 651, 247, - 504, 610, 599, 215, 594, 503, 393, 326, 327, 214, - 0, 457, 268, 293, 0, 0, 258, 415, 589, 590, - 256, 652, 228, 622, 220, 0, 621, 408, 584, 595, - 394, 383, 219, 593, 392, 382, 334, 355, 356, 280, - 307, 447, 375, 448, 306, 308, 404, 403, 405, 207, - 608, 626, 0, 208, 0, 499, 611, 653, 452, 212, - 234, 235, 237, 0, 279, 283, 291, 294, 303, 304, - 313, 367, 419, 446, 442, 451, 0, 579, 602, 616, - 628, 634, 635, 637, 638, 639, 640, 641, 644, 642, - 407, 311, 495, 333, 373, 0, 0, 425, 472, 240, - 606, 496, 199, 0, 0, 0, 0, 254, 255, 0, - 575, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 654, 655, 656, 657, 658, 659, 660, 661, 662, 663, - 664, 665, 666, 667, 668, 669, 670, 671, 649, 508, - 514, 509, 510, 511, 512, 513, 0, 515, 0, 0, - 0, 0, 0, 398, 0, 591, 592, 672, 384, 486, - 603, 335, 349, 352, 341, 361, 0, 362, 337, 338, - 343, 346, 347, 348, 353, 354, 358, 364, 249, 210, - 390, 399, 578, 312, 216, 217, 218, 524, 525, 526, - 527, 619, 620, 624, 205, 462, 463, 464, 465, 292, - 614, 309, 468, 467, 331, 332, 379, 449, 540, 542, - 553, 557, 559, 561, 567, 570, 541, 543, 554, 558, - 560, 562, 568, 571, 530, 532, 534, 536, 549, 548, - 545, 573, 574, 551, 556, 535, 547, 552, 565, 572, - 569, 529, 533, 537, 546, 564, 563, 544, 555, 566, - 550, 538, 531, 539, 0, 196, 221, 368, 0, 454, - 288, 650, 618, 484, 613, 206, 223, 0, 262, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 198, 200, 209, 222, 232, 236, 243, 261, 276, 278, - 285, 298, 310, 318, 319, 322, 328, 380, 386, 387, - 388, 389, 409, 410, 411, 414, 417, 418, 421, 423, - 424, 427, 431, 435, 436, 437, 439, 441, 443, 455, - 460, 474, 475, 476, 477, 478, 481, 482, 488, 489, - 490, 491, 492, 500, 501, 516, 586, 588, 605, 625, - 632, 480, 301, 302, 444, 445, 314, 315, 646, 647, - 300, 600, 633, 597, 645, 627, 438, 378, 0, 0, - 381, 281, 305, 320, 0, 617, 502, 227, 466, 290, - 251, 0, 0, 211, 246, 230, 259, 274, 277, 324, - 391, 400, 429, 434, 296, 271, 244, 459, 241, 485, - 519, 520, 521, 523, 395, 266, 433, 396, 0, 376, - 576, 577, 316, 0, 0, 0, 528, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 416, 0, - 0, 0, 0, 0, 0, 0, 270, 0, 0, 0, - 0, 366, 267, 0, 1920, 204, 505, 0, 430, 0, - 203, 0, 487, 252, 377, 374, 583, 282, 273, 269, - 250, 317, 385, 428, 518, 422, 0, 370, 0, 0, - 497, 401, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 323, 248, 325, 202, 413, 498, 286, 0, 0, 0, - 0, 0, 507, 724, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 238, 0, 0, 245, 0, 0, 0, - 351, 360, 359, 339, 340, 342, 344, 350, 357, 363, - 336, 345, 0, 0, 609, 0, 0, 0, 265, 321, - 272, 264, 580, 0, 0, 0, 0, 0, 0, 596, - 0, 0, 229, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 275, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 297, - 0, 402, 257, 0, 453, 0, 0, 0, 629, 0, - 0, 0, 0, 0, 0, 0, 365, 0, 330, 197, - 225, 0, 0, 412, 461, 473, 0, 0, 0, 253, - 0, 471, 426, 604, 233, 284, 458, 432, 469, 440, - 287, 0, 0, 470, 372, 585, 450, 601, 630, 631, - 263, 406, 615, 522, 623, 648, 226, 260, 420, 506, - 607, 494, 397, 581, 582, 329, 493, 295, 201, 369, - 636, 224, 479, 371, 242, 231, 587, 612, 299, 289, - 456, 643, 213, 517, 598, 239, 483, 0, 0, 651, - 247, 504, 610, 599, 215, 594, 503, 393, 326, 327, - 214, 0, 457, 268, 293, 0, 0, 258, 415, 589, - 590, 256, 652, 228, 622, 220, 0, 621, 408, 584, - 595, 394, 383, 219, 593, 392, 382, 334, 355, 356, - 280, 307, 447, 375, 448, 306, 308, 404, 403, 405, - 207, 608, 626, 0, 208, 0, 499, 611, 653, 452, - 212, 234, 235, 237, 0, 279, 283, 291, 294, 303, - 304, 313, 367, 419, 446, 442, 451, 0, 579, 602, - 616, 628, 634, 635, 637, 638, 639, 640, 641, 644, - 642, 407, 311, 495, 333, 373, 0, 0, 425, 472, - 240, 606, 496, 199, 0, 0, 0, 0, 254, 255, - 0, 575, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 654, 655, 656, 657, 658, 659, 660, 661, 662, - 663, 664, 665, 666, 667, 668, 669, 670, 671, 649, - 508, 514, 509, 510, 511, 512, 513, 0, 515, 0, - 0, 0, 0, 0, 398, 0, 591, 592, 672, 384, - 486, 603, 335, 349, 352, 341, 361, 0, 362, 337, - 338, 343, 346, 347, 348, 353, 354, 358, 364, 249, - 210, 390, 399, 578, 312, 216, 217, 218, 524, 525, - 526, 527, 619, 620, 624, 205, 462, 463, 464, 465, - 292, 614, 309, 468, 467, 331, 332, 379, 449, 540, - 542, 553, 557, 559, 561, 567, 570, 541, 543, 554, - 558, 560, 562, 568, 571, 530, 532, 534, 536, 549, - 548, 545, 573, 574, 551, 556, 535, 547, 552, 565, - 572, 569, 529, 533, 537, 546, 564, 563, 544, 555, - 566, 550, 538, 531, 539, 0, 196, 221, 368, 0, - 454, 288, 650, 618, 484, 613, 206, 223, 0, 262, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 198, 200, 209, 222, 232, 236, 243, 261, 276, - 278, 285, 298, 310, 318, 319, 322, 328, 380, 386, - 387, 388, 389, 409, 410, 411, 414, 417, 418, 421, - 423, 424, 427, 431, 435, 436, 437, 439, 441, 443, - 455, 460, 474, 475, 476, 477, 478, 481, 482, 488, - 489, 490, 491, 492, 500, 501, 516, 586, 588, 605, - 625, 632, 480, 301, 302, 444, 445, 314, 315, 646, - 647, 300, 600, 633, 597, 645, 627, 438, 378, 0, - 0, 381, 281, 305, 320, 0, 617, 502, 227, 466, - 290, 251, 0, 0, 211, 246, 230, 259, 274, 277, - 324, 391, 400, 429, 434, 296, 271, 244, 459, 241, - 485, 519, 520, 521, 523, 395, 266, 433, 396, 0, - 376, 576, 577, 316, 0, 0, 0, 528, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 416, - 0, 0, 0, 0, 0, 0, 0, 270, 0, 0, - 0, 0, 366, 267, 0, 1916, 204, 505, 0, 430, - 0, 203, 0, 487, 252, 377, 374, 583, 282, 273, - 269, 250, 317, 385, 428, 518, 422, 0, 370, 0, - 0, 497, 401, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 323, 248, 325, 202, 413, 498, 286, 0, 0, - 0, 0, 0, 507, 724, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 276, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 298, 0, + 403, 258, 0, 454, 0, 0, 0, 630, 0, 0, + 0, 0, 0, 0, 0, 366, 0, 331, 197, 225, + 0, 0, 413, 462, 474, 0, 0, 0, 254, 0, + 472, 427, 605, 233, 285, 459, 433, 470, 441, 288, + 0, 0, 471, 373, 586, 451, 602, 631, 632, 264, + 407, 616, 523, 624, 649, 226, 261, 421, 507, 608, + 495, 398, 582, 583, 330, 494, 296, 201, 370, 637, + 224, 480, 372, 242, 231, 588, 613, 300, 252, 290, + 457, 644, 213, 518, 599, 239, 484, 0, 0, 652, + 247, 505, 611, 600, 215, 595, 504, 394, 327, 328, + 214, 0, 458, 269, 294, 0, 0, 259, 416, 590, + 591, 257, 653, 228, 623, 220, 0, 622, 409, 585, + 596, 395, 384, 219, 594, 393, 383, 335, 356, 357, + 281, 308, 448, 376, 449, 307, 309, 405, 404, 406, + 207, 609, 627, 0, 208, 0, 500, 612, 654, 453, + 212, 234, 235, 237, 0, 280, 284, 292, 295, 304, + 305, 314, 368, 420, 447, 443, 452, 0, 580, 603, + 617, 629, 635, 636, 638, 639, 640, 641, 642, 645, + 643, 408, 312, 496, 334, 374, 0, 0, 426, 473, + 240, 607, 497, 199, 0, 0, 0, 0, 255, 256, + 0, 576, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 655, 656, 657, 658, 659, 660, 661, 662, 663, + 664, 665, 666, 667, 668, 669, 670, 671, 672, 650, + 509, 515, 510, 511, 512, 513, 514, 0, 516, 0, + 0, 0, 0, 0, 399, 0, 592, 593, 673, 385, + 487, 604, 336, 350, 353, 342, 362, 0, 363, 338, + 339, 344, 347, 348, 349, 354, 355, 359, 365, 249, + 210, 391, 400, 579, 313, 216, 217, 218, 525, 526, + 527, 528, 620, 621, 625, 205, 463, 464, 465, 466, + 293, 615, 310, 469, 468, 332, 333, 380, 450, 541, + 543, 554, 558, 560, 562, 568, 571, 542, 544, 555, + 559, 561, 563, 569, 572, 531, 533, 535, 537, 550, + 549, 546, 574, 575, 552, 557, 536, 548, 553, 566, + 573, 570, 530, 534, 538, 547, 565, 564, 545, 556, + 567, 551, 539, 532, 540, 0, 196, 221, 369, 2080, + 455, 289, 651, 619, 485, 614, 206, 223, 0, 263, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 198, 200, 209, 222, 232, 236, 243, 262, 277, + 279, 286, 299, 311, 319, 320, 323, 329, 381, 387, + 388, 389, 390, 410, 411, 412, 415, 418, 419, 422, + 424, 425, 428, 432, 436, 437, 438, 440, 442, 444, + 456, 461, 475, 476, 477, 478, 479, 482, 483, 489, + 490, 491, 492, 493, 501, 502, 517, 587, 589, 606, + 626, 633, 481, 302, 303, 445, 446, 315, 316, 647, + 648, 301, 601, 634, 598, 646, 628, 439, 379, 0, + 0, 382, 282, 306, 321, 0, 618, 503, 227, 467, + 291, 251, 0, 0, 211, 246, 230, 260, 275, 278, + 325, 392, 401, 430, 435, 297, 272, 244, 460, 241, + 486, 520, 521, 522, 524, 396, 267, 434, 397, 0, + 377, 577, 578, 317, 0, 0, 0, 529, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 417, + 0, 0, 0, 0, 0, 0, 0, 271, 0, 0, + 0, 0, 367, 268, 0, 0, 204, 506, 0, 431, + 0, 203, 0, 488, 253, 378, 375, 584, 283, 274, + 270, 250, 318, 386, 429, 519, 423, 0, 371, 0, + 0, 498, 402, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 324, 248, 326, 202, 414, 499, 287, 0, 0, + 0, 0, 2071, 508, 725, 0, 0, 0, 0, 0, 0, 0, 0, 0, 238, 0, 0, 245, 0, 0, - 0, 351, 360, 359, 339, 340, 342, 344, 350, 357, - 363, 336, 345, 0, 0, 609, 0, 0, 0, 265, - 321, 272, 264, 580, 0, 0, 0, 0, 0, 0, - 596, 0, 0, 229, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 275, 0, + 0, 352, 361, 360, 340, 341, 343, 345, 351, 358, + 364, 337, 346, 0, 0, 610, 0, 0, 0, 266, + 322, 273, 265, 581, 0, 0, 0, 0, 0, 0, + 597, 0, 0, 229, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 276, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -6418,147 +6013,73 @@ 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, - 297, 0, 402, 257, 0, 453, 0, 0, 0, 629, - 0, 0, 0, 0, 0, 0, 0, 365, 0, 330, - 197, 225, 0, 0, 412, 461, 473, 0, 0, 0, - 253, 0, 471, 426, 604, 233, 284, 458, 432, 469, - 440, 287, 0, 0, 470, 372, 585, 450, 601, 630, - 631, 263, 406, 615, 522, 623, 648, 226, 260, 420, - 506, 607, 494, 397, 581, 582, 329, 493, 295, 201, - 369, 636, 224, 479, 371, 242, 231, 587, 612, 299, - 289, 456, 643, 213, 517, 598, 239, 483, 0, 0, - 651, 247, 504, 610, 599, 215, 594, 503, 393, 326, - 327, 214, 0, 457, 268, 293, 0, 0, 258, 415, - 589, 590, 256, 652, 228, 622, 220, 0, 621, 408, - 584, 595, 394, 383, 219, 593, 392, 382, 334, 355, - 356, 280, 307, 447, 375, 448, 306, 308, 404, 403, - 405, 207, 608, 626, 0, 208, 0, 499, 611, 653, - 452, 212, 234, 235, 237, 0, 279, 283, 291, 294, - 303, 304, 313, 367, 419, 446, 442, 451, 0, 579, - 602, 616, 628, 634, 635, 637, 638, 639, 640, 641, - 644, 642, 407, 311, 495, 333, 373, 0, 0, 425, - 472, 240, 606, 496, 199, 0, 0, 0, 0, 254, - 255, 0, 575, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 654, 655, 656, 657, 658, 659, 660, 661, + 298, 0, 403, 258, 0, 454, 0, 0, 0, 630, + 0, 0, 0, 0, 0, 0, 0, 366, 0, 331, + 197, 225, 0, 0, 413, 462, 474, 0, 0, 0, + 254, 0, 472, 427, 605, 233, 285, 459, 433, 470, + 441, 288, 0, 0, 471, 373, 586, 451, 602, 631, + 632, 264, 407, 616, 523, 624, 649, 226, 261, 421, + 507, 608, 495, 398, 582, 583, 330, 494, 296, 201, + 370, 637, 224, 480, 372, 242, 231, 588, 613, 300, + 252, 290, 457, 644, 213, 518, 599, 239, 484, 0, + 0, 652, 247, 505, 611, 600, 215, 595, 504, 394, + 327, 328, 214, 0, 458, 269, 294, 0, 0, 259, + 416, 590, 591, 257, 653, 228, 623, 220, 0, 622, + 409, 585, 596, 395, 384, 219, 594, 393, 383, 335, + 356, 357, 281, 308, 448, 376, 449, 307, 309, 405, + 404, 406, 207, 609, 627, 0, 208, 0, 500, 612, + 654, 453, 212, 234, 235, 237, 0, 280, 284, 292, + 295, 304, 305, 314, 368, 420, 447, 443, 452, 0, + 580, 603, 617, 629, 635, 636, 638, 639, 640, 641, + 642, 645, 643, 408, 312, 496, 334, 374, 0, 0, + 426, 473, 240, 607, 497, 199, 0, 0, 0, 0, + 255, 256, 0, 576, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 655, 656, 657, 658, 659, 660, 661, 662, 663, 664, 665, 666, 667, 668, 669, 670, 671, - 649, 508, 514, 509, 510, 511, 512, 513, 0, 515, - 0, 0, 0, 0, 0, 398, 0, 591, 592, 672, - 384, 486, 603, 335, 349, 352, 341, 361, 0, 362, - 337, 338, 343, 346, 347, 348, 353, 354, 358, 364, - 249, 210, 390, 399, 578, 312, 216, 217, 218, 524, - 525, 526, 527, 619, 620, 624, 205, 462, 463, 464, - 465, 292, 614, 309, 468, 467, 331, 332, 379, 449, - 540, 542, 553, 557, 559, 561, 567, 570, 541, 543, - 554, 558, 560, 562, 568, 571, 530, 532, 534, 536, - 549, 548, 545, 573, 574, 551, 556, 535, 547, 552, - 565, 572, 569, 529, 533, 537, 546, 564, 563, 544, - 555, 566, 550, 538, 531, 539, 0, 196, 221, 368, - 0, 454, 288, 650, 618, 484, 613, 206, 223, 0, - 262, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 198, 200, 209, 222, 232, 236, 243, 261, - 276, 278, 285, 298, 310, 318, 319, 322, 328, 380, - 386, 387, 388, 389, 409, 410, 411, 414, 417, 418, - 421, 423, 424, 427, 431, 435, 436, 437, 439, 441, - 443, 455, 460, 474, 475, 476, 477, 478, 481, 482, - 488, 489, 490, 491, 492, 500, 501, 516, 586, 588, - 605, 625, 632, 480, 301, 302, 444, 445, 314, 315, - 646, 647, 300, 600, 633, 597, 645, 627, 438, 378, - 0, 0, 381, 281, 305, 320, 0, 617, 502, 227, - 466, 290, 251, 0, 0, 211, 246, 230, 259, 274, - 277, 324, 391, 400, 429, 434, 296, 271, 244, 459, - 241, 485, 519, 520, 521, 523, 395, 266, 433, 396, - 0, 376, 576, 577, 316, 0, 0, 0, 528, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 416, 0, 0, 0, 0, 0, 0, 0, 270, 0, - 0, 0, 0, 366, 267, 0, 1914, 204, 505, 0, - 430, 0, 203, 0, 487, 252, 377, 374, 583, 282, - 273, 269, 250, 317, 385, 428, 518, 422, 0, 370, - 0, 0, 497, 401, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 323, 248, 325, 202, 413, 498, 286, 0, - 0, 0, 0, 0, 507, 724, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 238, 0, 0, 245, 0, - 0, 0, 351, 360, 359, 339, 340, 342, 344, 350, - 357, 363, 336, 345, 0, 0, 609, 0, 0, 0, - 265, 321, 272, 264, 580, 0, 0, 0, 0, 0, - 0, 596, 0, 0, 229, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 275, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 297, 0, 402, 257, 0, 453, 0, 0, 0, - 629, 0, 0, 0, 0, 0, 0, 0, 365, 0, - 330, 197, 225, 0, 0, 412, 461, 473, 0, 0, - 0, 253, 0, 471, 426, 604, 233, 284, 458, 432, - 469, 440, 287, 0, 0, 470, 372, 585, 450, 601, - 630, 631, 263, 406, 615, 522, 623, 648, 226, 260, - 420, 506, 607, 494, 397, 581, 582, 329, 493, 295, - 201, 369, 636, 224, 479, 371, 242, 231, 587, 612, - 299, 289, 456, 643, 213, 517, 598, 239, 483, 0, - 0, 651, 247, 504, 610, 599, 215, 594, 503, 393, - 326, 327, 214, 0, 457, 268, 293, 0, 0, 258, - 415, 589, 590, 256, 652, 228, 622, 220, 0, 621, - 408, 584, 595, 394, 383, 219, 593, 392, 382, 334, - 355, 356, 280, 307, 447, 375, 448, 306, 308, 404, - 403, 405, 207, 608, 626, 0, 208, 0, 499, 611, - 653, 452, 212, 234, 235, 237, 0, 279, 283, 291, - 294, 303, 304, 313, 367, 419, 446, 442, 451, 0, - 579, 602, 616, 628, 634, 635, 637, 638, 639, 640, - 641, 644, 642, 407, 311, 495, 333, 373, 0, 0, - 425, 472, 240, 606, 496, 199, 0, 0, 0, 0, - 254, 255, 0, 575, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 654, 655, 656, 657, 658, 659, 660, - 661, 662, 663, 664, 665, 666, 667, 668, 669, 670, - 671, 649, 508, 514, 509, 510, 511, 512, 513, 0, - 515, 0, 0, 0, 0, 0, 398, 0, 591, 592, - 672, 384, 486, 603, 335, 349, 352, 341, 361, 0, - 362, 337, 338, 343, 346, 347, 348, 353, 354, 358, - 364, 249, 210, 390, 399, 578, 312, 216, 217, 218, - 524, 525, 526, 527, 619, 620, 624, 205, 462, 463, - 464, 465, 292, 614, 309, 468, 467, 331, 332, 379, - 449, 540, 542, 553, 557, 559, 561, 567, 570, 541, - 543, 554, 558, 560, 562, 568, 571, 530, 532, 534, - 536, 549, 548, 545, 573, 574, 551, 556, 535, 547, - 552, 565, 572, 569, 529, 533, 537, 546, 564, 563, - 544, 555, 566, 550, 538, 531, 539, 0, 196, 221, - 368, 0, 454, 288, 650, 618, 484, 613, 206, 223, - 0, 262, 0, 0, 0, 0, 0, 0, 0, 0, + 672, 650, 509, 515, 510, 511, 512, 513, 514, 0, + 516, 0, 0, 0, 0, 0, 399, 0, 592, 593, + 673, 385, 487, 604, 336, 350, 353, 342, 362, 0, + 363, 338, 339, 344, 347, 348, 349, 354, 355, 359, + 365, 249, 210, 391, 400, 579, 313, 216, 217, 218, + 525, 526, 527, 528, 620, 621, 625, 205, 463, 464, + 465, 466, 293, 615, 310, 469, 468, 332, 333, 380, + 450, 541, 543, 554, 558, 560, 562, 568, 571, 542, + 544, 555, 559, 561, 563, 569, 572, 531, 533, 535, + 537, 550, 549, 546, 574, 575, 552, 557, 536, 548, + 553, 566, 573, 570, 530, 534, 538, 547, 565, 564, + 545, 556, 567, 551, 539, 532, 540, 0, 196, 221, + 369, 0, 455, 289, 651, 619, 485, 614, 206, 223, + 0, 263, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 198, 200, 209, 222, 232, 236, 243, - 261, 276, 278, 285, 298, 310, 318, 319, 322, 328, - 380, 386, 387, 388, 389, 409, 410, 411, 414, 417, - 418, 421, 423, 424, 427, 431, 435, 436, 437, 439, - 441, 443, 455, 460, 474, 475, 476, 477, 478, 481, - 482, 488, 489, 490, 491, 492, 500, 501, 516, 586, - 588, 605, 625, 632, 480, 301, 302, 444, 445, 314, - 315, 646, 647, 300, 600, 633, 597, 645, 627, 438, - 378, 0, 0, 381, 281, 305, 320, 0, 617, 502, - 227, 466, 290, 251, 0, 0, 211, 246, 230, 259, - 274, 277, 324, 391, 400, 429, 434, 296, 271, 244, - 459, 241, 485, 519, 520, 521, 523, 395, 266, 433, - 396, 0, 376, 576, 577, 316, 0, 0, 0, 528, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 416, 0, 0, 0, 0, 0, 0, 0, 270, - 0, 0, 0, 0, 366, 267, 0, 1912, 204, 505, - 0, 430, 0, 203, 0, 487, 252, 377, 374, 583, - 282, 273, 269, 250, 317, 385, 428, 518, 422, 0, - 370, 0, 0, 497, 401, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 323, 248, 325, 202, 413, 498, 286, - 0, 0, 0, 0, 0, 507, 724, 0, 0, 0, + 262, 277, 279, 286, 299, 311, 319, 320, 323, 329, + 381, 387, 388, 389, 390, 410, 411, 412, 415, 418, + 419, 422, 424, 425, 428, 432, 436, 437, 438, 440, + 442, 444, 456, 461, 475, 476, 477, 478, 479, 482, + 483, 489, 490, 491, 492, 493, 501, 502, 517, 587, + 589, 606, 626, 633, 481, 302, 303, 445, 446, 315, + 316, 647, 648, 301, 601, 634, 598, 646, 628, 439, + 379, 0, 0, 382, 282, 306, 321, 0, 618, 503, + 227, 467, 291, 251, 0, 0, 211, 246, 230, 260, + 275, 278, 325, 392, 401, 430, 435, 297, 272, 244, + 460, 241, 486, 520, 521, 522, 524, 396, 267, 434, + 397, 0, 377, 577, 578, 317, 0, 0, 0, 529, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 417, 0, 0, 0, 0, 0, 0, 0, 271, + 0, 0, 0, 0, 367, 268, 0, 1929, 204, 506, + 0, 431, 0, 203, 0, 488, 253, 378, 375, 584, + 283, 274, 270, 250, 318, 386, 429, 519, 423, 0, + 371, 0, 0, 498, 402, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 324, 248, 326, 202, 414, 499, 287, + 0, 0, 0, 0, 0, 508, 725, 0, 0, 0, 0, 0, 0, 0, 0, 0, 238, 0, 0, 245, - 0, 0, 0, 351, 360, 359, 339, 340, 342, 344, - 350, 357, 363, 336, 345, 0, 0, 609, 0, 0, - 0, 265, 321, 272, 264, 580, 0, 0, 0, 0, - 0, 0, 596, 0, 0, 229, 0, 0, 0, 0, + 0, 0, 0, 352, 361, 360, 340, 341, 343, 345, + 351, 358, 364, 337, 346, 0, 0, 610, 0, 0, + 0, 266, 322, 273, 265, 581, 0, 0, 0, 0, + 0, 0, 597, 0, 0, 229, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 275, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 276, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -6566,147 +6087,73 @@ 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, 297, 0, 402, 257, 0, 453, 0, 0, - 0, 629, 0, 0, 0, 0, 0, 0, 0, 365, - 0, 330, 197, 225, 0, 0, 412, 461, 473, 0, - 0, 0, 253, 0, 471, 426, 604, 233, 284, 458, - 432, 469, 440, 287, 0, 0, 470, 372, 585, 450, - 601, 630, 631, 263, 406, 615, 522, 623, 648, 226, - 260, 420, 506, 607, 494, 397, 581, 582, 329, 493, - 295, 201, 369, 636, 224, 479, 371, 242, 231, 587, - 612, 299, 289, 456, 643, 213, 517, 598, 239, 483, - 0, 0, 651, 247, 504, 610, 599, 215, 594, 503, - 393, 326, 327, 214, 0, 457, 268, 293, 0, 0, - 258, 415, 589, 590, 256, 652, 228, 622, 220, 0, - 621, 408, 584, 595, 394, 383, 219, 593, 392, 382, - 334, 355, 356, 280, 307, 447, 375, 448, 306, 308, - 404, 403, 405, 207, 608, 626, 0, 208, 0, 499, - 611, 653, 452, 212, 234, 235, 237, 0, 279, 283, - 291, 294, 303, 304, 313, 367, 419, 446, 442, 451, - 0, 579, 602, 616, 628, 634, 635, 637, 638, 639, - 640, 641, 644, 642, 407, 311, 495, 333, 373, 0, - 0, 425, 472, 240, 606, 496, 199, 0, 0, 0, - 0, 254, 255, 0, 575, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 654, 655, 656, 657, 658, 659, + 0, 0, 298, 0, 403, 258, 0, 454, 0, 0, + 0, 630, 0, 0, 0, 0, 0, 0, 0, 366, + 0, 331, 197, 225, 0, 0, 413, 462, 474, 0, + 0, 0, 254, 0, 472, 427, 605, 233, 285, 459, + 433, 470, 441, 288, 0, 0, 471, 373, 586, 451, + 602, 631, 632, 264, 407, 616, 523, 624, 649, 226, + 261, 421, 507, 608, 495, 398, 582, 583, 330, 494, + 296, 201, 370, 637, 224, 480, 372, 242, 231, 588, + 613, 300, 252, 290, 457, 644, 213, 518, 599, 239, + 484, 0, 0, 652, 247, 505, 611, 600, 215, 595, + 504, 394, 327, 328, 214, 0, 458, 269, 294, 0, + 0, 259, 416, 590, 591, 257, 653, 228, 623, 220, + 0, 622, 409, 585, 596, 395, 384, 219, 594, 393, + 383, 335, 356, 357, 281, 308, 448, 376, 449, 307, + 309, 405, 404, 406, 207, 609, 627, 0, 208, 0, + 500, 612, 654, 453, 212, 234, 235, 237, 0, 280, + 284, 292, 295, 304, 305, 314, 368, 420, 447, 443, + 452, 0, 580, 603, 617, 629, 635, 636, 638, 639, + 640, 641, 642, 645, 643, 408, 312, 496, 334, 374, + 0, 0, 426, 473, 240, 607, 497, 199, 0, 0, + 0, 0, 255, 256, 0, 576, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 655, 656, 657, 658, 659, 660, 661, 662, 663, 664, 665, 666, 667, 668, 669, - 670, 671, 649, 508, 514, 509, 510, 511, 512, 513, - 0, 515, 0, 0, 0, 0, 0, 398, 0, 591, - 592, 672, 384, 486, 603, 335, 349, 352, 341, 361, - 0, 362, 337, 338, 343, 346, 347, 348, 353, 354, - 358, 364, 249, 210, 390, 399, 578, 312, 216, 217, - 218, 524, 525, 526, 527, 619, 620, 624, 205, 462, - 463, 464, 465, 292, 614, 309, 468, 467, 331, 332, - 379, 449, 540, 542, 553, 557, 559, 561, 567, 570, - 541, 543, 554, 558, 560, 562, 568, 571, 530, 532, - 534, 536, 549, 548, 545, 573, 574, 551, 556, 535, - 547, 552, 565, 572, 569, 529, 533, 537, 546, 564, - 563, 544, 555, 566, 550, 538, 531, 539, 0, 196, - 221, 368, 0, 454, 288, 650, 618, 484, 613, 206, - 223, 0, 262, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 198, 200, 209, 222, 232, 236, - 243, 261, 276, 278, 285, 298, 310, 318, 319, 322, - 328, 380, 386, 387, 388, 389, 409, 410, 411, 414, - 417, 418, 421, 423, 424, 427, 431, 435, 436, 437, - 439, 441, 443, 455, 460, 474, 475, 476, 477, 478, - 481, 482, 488, 489, 490, 491, 492, 500, 501, 516, - 586, 588, 605, 625, 632, 480, 301, 302, 444, 445, - 314, 315, 646, 647, 300, 600, 633, 597, 645, 627, - 438, 378, 0, 0, 381, 281, 305, 320, 0, 617, - 502, 227, 466, 290, 251, 0, 0, 211, 246, 230, - 259, 274, 277, 324, 391, 400, 429, 434, 296, 271, - 244, 459, 241, 485, 519, 520, 521, 523, 395, 266, - 433, 396, 0, 376, 576, 577, 316, 0, 0, 0, - 528, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 416, 0, 0, 0, 0, 0, 0, 0, - 270, 0, 0, 0, 0, 366, 267, 0, 0, 204, - 505, 0, 430, 0, 203, 0, 487, 252, 377, 374, - 583, 282, 273, 269, 250, 317, 385, 428, 518, 422, - 0, 370, 0, 0, 497, 401, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 323, 248, 325, 202, 413, 498, - 286, 0, 1887, 0, 0, 0, 507, 724, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 238, 0, 0, - 245, 0, 0, 0, 351, 360, 359, 339, 340, 342, - 344, 350, 357, 363, 336, 345, 0, 0, 609, 0, - 0, 0, 265, 321, 272, 264, 580, 0, 0, 0, - 0, 0, 0, 596, 0, 0, 229, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 275, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 297, 0, 402, 257, 0, 453, 0, - 0, 0, 629, 0, 0, 0, 0, 0, 0, 0, - 365, 0, 330, 197, 225, 0, 0, 412, 461, 473, - 0, 0, 0, 253, 0, 471, 426, 604, 233, 284, - 458, 432, 469, 440, 287, 0, 0, 470, 372, 585, - 450, 601, 630, 631, 263, 406, 615, 522, 623, 648, - 226, 260, 420, 506, 607, 494, 397, 581, 582, 329, - 493, 295, 201, 369, 636, 224, 479, 371, 242, 231, - 587, 612, 299, 289, 456, 643, 213, 517, 598, 239, - 483, 0, 0, 651, 247, 504, 610, 599, 215, 594, - 503, 393, 326, 327, 214, 0, 457, 268, 293, 0, - 0, 258, 415, 589, 590, 256, 652, 228, 622, 220, - 0, 621, 408, 584, 595, 394, 383, 219, 593, 392, - 382, 334, 355, 356, 280, 307, 447, 375, 448, 306, - 308, 404, 403, 405, 207, 608, 626, 0, 208, 0, - 499, 611, 653, 452, 212, 234, 235, 237, 0, 279, - 283, 291, 294, 303, 304, 313, 367, 419, 446, 442, - 451, 0, 579, 602, 616, 628, 634, 635, 637, 638, - 639, 640, 641, 644, 642, 407, 311, 495, 333, 373, - 0, 0, 425, 472, 240, 606, 496, 199, 0, 0, - 0, 0, 254, 255, 0, 575, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 654, 655, 656, 657, 658, - 659, 660, 661, 662, 663, 664, 665, 666, 667, 668, - 669, 670, 671, 649, 508, 514, 509, 510, 511, 512, - 513, 0, 515, 0, 0, 0, 0, 0, 398, 0, - 591, 592, 672, 384, 486, 603, 335, 349, 352, 341, - 361, 0, 362, 337, 338, 343, 346, 347, 348, 353, - 354, 358, 364, 249, 210, 390, 399, 578, 312, 216, - 217, 218, 524, 525, 526, 527, 619, 620, 624, 205, - 462, 463, 464, 465, 292, 614, 309, 468, 467, 331, - 332, 379, 449, 540, 542, 553, 557, 559, 561, 567, - 570, 541, 543, 554, 558, 560, 562, 568, 571, 530, - 532, 534, 536, 549, 548, 545, 573, 574, 551, 556, - 535, 547, 552, 565, 572, 569, 529, 533, 537, 546, - 564, 563, 544, 555, 566, 550, 538, 531, 539, 0, - 196, 221, 368, 0, 454, 288, 650, 618, 484, 613, - 206, 223, 0, 262, 0, 0, 0, 0, 0, 0, + 670, 671, 672, 650, 509, 515, 510, 511, 512, 513, + 514, 0, 516, 0, 0, 0, 0, 0, 399, 0, + 592, 593, 673, 385, 487, 604, 336, 350, 353, 342, + 362, 0, 363, 338, 339, 344, 347, 348, 349, 354, + 355, 359, 365, 249, 210, 391, 400, 579, 313, 216, + 217, 218, 525, 526, 527, 528, 620, 621, 625, 205, + 463, 464, 465, 466, 293, 615, 310, 469, 468, 332, + 333, 380, 450, 541, 543, 554, 558, 560, 562, 568, + 571, 542, 544, 555, 559, 561, 563, 569, 572, 531, + 533, 535, 537, 550, 549, 546, 574, 575, 552, 557, + 536, 548, 553, 566, 573, 570, 530, 534, 538, 547, + 565, 564, 545, 556, 567, 551, 539, 532, 540, 0, + 196, 221, 369, 0, 455, 289, 651, 619, 485, 614, + 206, 223, 0, 263, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 198, 200, 209, 222, 232, - 236, 243, 261, 276, 278, 285, 298, 310, 318, 319, - 322, 328, 380, 386, 387, 388, 389, 409, 410, 411, - 414, 417, 418, 421, 423, 424, 427, 431, 435, 436, - 437, 439, 441, 443, 455, 460, 474, 475, 476, 477, - 478, 481, 482, 488, 489, 490, 491, 492, 500, 501, - 516, 586, 588, 605, 625, 632, 480, 301, 302, 444, - 445, 314, 315, 646, 647, 300, 600, 633, 597, 645, - 627, 438, 378, 0, 0, 381, 281, 305, 320, 0, - 617, 502, 227, 466, 290, 251, 0, 0, 211, 246, - 230, 259, 274, 277, 324, 391, 400, 429, 434, 296, - 271, 244, 459, 241, 485, 519, 520, 521, 523, 395, - 266, 433, 396, 0, 376, 576, 577, 316, 0, 0, - 0, 528, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 416, 0, 0, 0, 0, 0, 0, - 1784, 270, 0, 0, 0, 0, 366, 267, 0, 0, - 204, 505, 0, 430, 0, 203, 0, 487, 252, 377, - 374, 583, 282, 273, 269, 250, 317, 385, 428, 518, - 422, 0, 370, 0, 0, 497, 401, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 323, 248, 325, 202, 413, - 498, 286, 0, 0, 0, 0, 0, 507, 194, 0, + 236, 243, 262, 277, 279, 286, 299, 311, 319, 320, + 323, 329, 381, 387, 388, 389, 390, 410, 411, 412, + 415, 418, 419, 422, 424, 425, 428, 432, 436, 437, + 438, 440, 442, 444, 456, 461, 475, 476, 477, 478, + 479, 482, 483, 489, 490, 491, 492, 493, 501, 502, + 517, 587, 589, 606, 626, 633, 481, 302, 303, 445, + 446, 315, 316, 647, 648, 301, 601, 634, 598, 646, + 628, 439, 379, 0, 0, 382, 282, 306, 321, 0, + 618, 503, 227, 467, 291, 251, 0, 0, 211, 246, + 230, 260, 275, 278, 325, 392, 401, 430, 435, 297, + 272, 244, 460, 241, 486, 520, 521, 522, 524, 396, + 267, 434, 397, 0, 377, 577, 578, 317, 0, 0, + 0, 529, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 417, 0, 0, 0, 0, 0, 0, + 0, 271, 0, 0, 0, 0, 367, 268, 0, 1927, + 204, 506, 0, 431, 0, 203, 0, 488, 253, 378, + 375, 584, 283, 274, 270, 250, 318, 386, 429, 519, + 423, 0, 371, 0, 0, 498, 402, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 324, 248, 326, 202, 414, + 499, 287, 0, 0, 0, 0, 0, 508, 725, 0, 0, 0, 0, 0, 0, 0, 0, 0, 238, 0, - 0, 245, 0, 0, 0, 351, 360, 359, 339, 340, - 342, 344, 350, 357, 363, 336, 345, 0, 0, 609, - 0, 0, 0, 265, 321, 272, 264, 580, 0, 0, - 0, 0, 0, 0, 596, 0, 0, 229, 0, 0, + 0, 245, 0, 0, 0, 352, 361, 360, 340, 341, + 343, 345, 351, 358, 364, 337, 346, 0, 0, 610, + 0, 0, 0, 266, 322, 273, 265, 581, 0, 0, + 0, 0, 0, 0, 597, 0, 0, 229, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 275, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 276, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -6714,147 +6161,221 @@ 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, 297, 0, 402, 257, 0, 453, - 0, 0, 0, 629, 0, 0, 0, 0, 0, 0, - 0, 365, 0, 330, 197, 225, 0, 0, 412, 461, - 473, 0, 0, 0, 253, 0, 471, 426, 604, 233, - 284, 458, 432, 469, 440, 287, 0, 0, 470, 372, - 585, 450, 601, 630, 631, 263, 406, 615, 522, 623, - 648, 226, 260, 420, 506, 607, 494, 397, 581, 582, - 329, 493, 295, 201, 369, 636, 224, 479, 371, 242, - 231, 587, 612, 299, 289, 456, 643, 213, 517, 598, - 239, 483, 0, 0, 651, 247, 504, 610, 599, 215, - 594, 503, 393, 326, 327, 214, 0, 457, 268, 293, - 0, 0, 258, 415, 589, 590, 256, 652, 228, 622, - 220, 0, 621, 408, 584, 595, 394, 383, 219, 593, - 392, 382, 334, 355, 356, 280, 307, 447, 375, 448, - 306, 308, 404, 403, 405, 207, 608, 626, 0, 208, - 0, 499, 611, 653, 452, 212, 234, 235, 237, 0, - 279, 283, 291, 294, 303, 304, 313, 367, 419, 446, - 442, 451, 0, 579, 602, 616, 628, 634, 635, 637, - 638, 639, 640, 641, 644, 642, 407, 311, 495, 333, - 373, 0, 0, 425, 472, 240, 606, 496, 199, 0, - 0, 0, 0, 254, 255, 0, 575, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 654, 655, 656, 657, + 0, 0, 0, 0, 298, 0, 403, 258, 0, 454, + 0, 0, 0, 630, 0, 0, 0, 0, 0, 0, + 0, 366, 0, 331, 197, 225, 0, 0, 413, 462, + 474, 0, 0, 0, 254, 0, 472, 427, 605, 233, + 285, 459, 433, 470, 441, 288, 0, 0, 471, 373, + 586, 451, 602, 631, 632, 264, 407, 616, 523, 624, + 649, 226, 261, 421, 507, 608, 495, 398, 582, 583, + 330, 494, 296, 201, 370, 637, 224, 480, 372, 242, + 231, 588, 613, 300, 252, 290, 457, 644, 213, 518, + 599, 239, 484, 0, 0, 652, 247, 505, 611, 600, + 215, 595, 504, 394, 327, 328, 214, 0, 458, 269, + 294, 0, 0, 259, 416, 590, 591, 257, 653, 228, + 623, 220, 0, 622, 409, 585, 596, 395, 384, 219, + 594, 393, 383, 335, 356, 357, 281, 308, 448, 376, + 449, 307, 309, 405, 404, 406, 207, 609, 627, 0, + 208, 0, 500, 612, 654, 453, 212, 234, 235, 237, + 0, 280, 284, 292, 295, 304, 305, 314, 368, 420, + 447, 443, 452, 0, 580, 603, 617, 629, 635, 636, + 638, 639, 640, 641, 642, 645, 643, 408, 312, 496, + 334, 374, 0, 0, 426, 473, 240, 607, 497, 199, + 0, 0, 0, 0, 255, 256, 0, 576, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 655, 656, 657, 658, 659, 660, 661, 662, 663, 664, 665, 666, 667, - 668, 669, 670, 671, 649, 508, 514, 509, 510, 511, - 512, 513, 0, 515, 0, 0, 0, 0, 0, 398, - 0, 591, 592, 672, 384, 486, 603, 335, 349, 352, - 341, 361, 0, 362, 337, 338, 343, 346, 347, 348, - 353, 354, 358, 364, 249, 210, 390, 399, 578, 312, - 216, 217, 218, 524, 525, 526, 527, 619, 620, 624, - 205, 462, 463, 464, 465, 292, 614, 309, 468, 467, - 331, 332, 379, 449, 540, 542, 553, 557, 559, 561, - 567, 570, 541, 543, 554, 558, 560, 562, 568, 571, - 530, 532, 534, 536, 549, 548, 545, 573, 574, 551, - 556, 535, 547, 552, 565, 572, 569, 529, 533, 537, - 546, 564, 563, 544, 555, 566, 550, 538, 531, 539, - 0, 196, 221, 368, 0, 454, 288, 650, 618, 484, - 613, 206, 223, 0, 262, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 198, 200, 209, 222, - 232, 236, 243, 261, 276, 278, 285, 298, 310, 318, - 319, 322, 328, 380, 386, 387, 388, 389, 409, 410, - 411, 414, 417, 418, 421, 423, 424, 427, 431, 435, - 436, 437, 439, 441, 443, 455, 460, 474, 475, 476, - 477, 478, 481, 482, 488, 489, 490, 491, 492, 500, - 501, 516, 586, 588, 605, 625, 632, 480, 301, 302, - 444, 445, 314, 315, 646, 647, 300, 600, 633, 597, - 645, 627, 438, 378, 0, 0, 381, 281, 305, 320, - 0, 617, 502, 227, 466, 290, 251, 0, 0, 211, - 246, 230, 259, 274, 277, 324, 391, 400, 429, 434, - 296, 271, 244, 459, 241, 485, 519, 520, 521, 523, - 395, 266, 433, 396, 0, 376, 576, 577, 316, 0, - 0, 0, 528, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 416, 0, 0, 0, 0, 0, - 0, 0, 270, 0, 0, 0, 0, 366, 267, 0, - 0, 204, 505, 0, 430, 0, 203, 0, 487, 252, - 377, 374, 583, 282, 273, 269, 250, 317, 385, 428, - 518, 422, 0, 370, 0, 0, 497, 401, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 323, 248, 325, 202, - 413, 498, 286, 0, 95, 0, 0, 0, 507, 959, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 238, - 0, 0, 245, 0, 0, 0, 351, 360, 359, 339, - 340, 342, 344, 350, 357, 363, 336, 345, 0, 0, - 609, 0, 0, 0, 265, 321, 272, 264, 580, 0, - 0, 0, 0, 0, 0, 596, 0, 0, 229, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 275, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 297, 0, 402, 257, 0, - 453, 0, 0, 0, 629, 0, 0, 0, 0, 0, - 0, 0, 365, 0, 330, 197, 225, 0, 0, 412, - 461, 473, 0, 0, 0, 253, 0, 471, 426, 604, - 233, 284, 458, 432, 469, 440, 287, 0, 0, 470, - 372, 585, 450, 601, 630, 631, 263, 406, 615, 522, - 623, 648, 226, 260, 420, 506, 607, 494, 397, 581, - 582, 329, 493, 295, 201, 369, 636, 224, 479, 371, - 242, 231, 587, 612, 299, 289, 456, 643, 213, 517, - 598, 239, 483, 0, 0, 651, 247, 504, 610, 599, - 215, 594, 503, 393, 326, 327, 214, 0, 457, 268, - 293, 0, 0, 258, 415, 589, 590, 256, 652, 228, - 622, 220, 0, 621, 408, 584, 595, 394, 383, 219, - 593, 392, 382, 334, 355, 356, 280, 307, 447, 375, - 448, 306, 308, 404, 403, 405, 207, 608, 626, 0, - 208, 0, 499, 611, 653, 452, 212, 234, 235, 237, - 0, 279, 283, 291, 294, 303, 304, 313, 367, 419, - 446, 442, 451, 0, 579, 602, 616, 628, 634, 635, - 637, 638, 639, 640, 641, 644, 642, 407, 311, 495, - 333, 373, 0, 0, 425, 472, 240, 606, 496, 199, - 0, 0, 0, 0, 254, 255, 0, 575, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 654, 655, 656, - 657, 658, 659, 660, 661, 662, 663, 664, 665, 666, - 667, 668, 669, 670, 671, 649, 508, 514, 509, 510, - 511, 512, 513, 0, 515, 0, 0, 0, 0, 0, - 398, 0, 591, 592, 672, 384, 486, 603, 335, 349, - 352, 341, 361, 0, 362, 337, 338, 343, 346, 347, - 348, 353, 354, 358, 364, 249, 210, 390, 399, 578, - 312, 216, 217, 218, 524, 525, 526, 527, 619, 620, - 624, 205, 462, 463, 464, 465, 292, 614, 309, 468, - 467, 331, 332, 379, 449, 540, 542, 553, 557, 559, - 561, 567, 570, 541, 543, 554, 558, 560, 562, 568, - 571, 530, 532, 534, 536, 549, 548, 545, 573, 574, - 551, 556, 535, 547, 552, 565, 572, 569, 529, 533, - 537, 546, 564, 563, 544, 555, 566, 550, 538, 531, - 539, 0, 196, 221, 368, 0, 454, 288, 650, 618, - 484, 613, 206, 223, 0, 262, 0, 0, 0, 0, + 668, 669, 670, 671, 672, 650, 509, 515, 510, 511, + 512, 513, 514, 0, 516, 0, 0, 0, 0, 0, + 399, 0, 592, 593, 673, 385, 487, 604, 336, 350, + 353, 342, 362, 0, 363, 338, 339, 344, 347, 348, + 349, 354, 355, 359, 365, 249, 210, 391, 400, 579, + 313, 216, 217, 218, 525, 526, 527, 528, 620, 621, + 625, 205, 463, 464, 465, 466, 293, 615, 310, 469, + 468, 332, 333, 380, 450, 541, 543, 554, 558, 560, + 562, 568, 571, 542, 544, 555, 559, 561, 563, 569, + 572, 531, 533, 535, 537, 550, 549, 546, 574, 575, + 552, 557, 536, 548, 553, 566, 573, 570, 530, 534, + 538, 547, 565, 564, 545, 556, 567, 551, 539, 532, + 540, 0, 196, 221, 369, 0, 455, 289, 651, 619, + 485, 614, 206, 223, 0, 263, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 198, 200, 209, - 222, 232, 236, 243, 261, 276, 278, 285, 298, 310, - 318, 319, 322, 328, 380, 386, 387, 388, 389, 409, - 410, 411, 414, 417, 418, 421, 423, 424, 427, 431, - 435, 436, 437, 439, 441, 443, 455, 460, 474, 475, - 476, 477, 478, 481, 482, 488, 489, 490, 491, 492, - 500, 501, 516, 586, 588, 605, 625, 632, 480, 301, - 302, 444, 445, 314, 315, 646, 647, 300, 600, 633, - 597, 645, 627, 438, 378, 0, 0, 381, 281, 305, - 320, 0, 617, 502, 227, 466, 290, 251, 0, 0, - 211, 246, 230, 259, 274, 277, 324, 391, 400, 429, - 434, 296, 271, 244, 459, 241, 485, 519, 520, 521, - 523, 395, 266, 433, 396, 0, 376, 576, 577, 316, - 0, 0, 0, 528, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 416, 0, 0, 0, 0, - 0, 0, 0, 270, 0, 0, 0, 0, 366, 267, - 0, 0, 204, 505, 0, 430, 0, 203, 0, 487, - 252, 377, 374, 583, 282, 273, 269, 250, 317, 385, - 428, 518, 422, 0, 370, 0, 0, 497, 401, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 323, 248, 325, - 202, 413, 498, 286, 0, 0, 0, 0, 0, 507, - 194, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 238, 0, 0, 245, 0, 0, 0, 351, 360, 359, - 339, 340, 342, 344, 350, 357, 363, 336, 345, 0, - 0, 609, 0, 0, 0, 265, 321, 272, 264, 580, - 0, 0, 0, 0, 0, 0, 596, 0, 0, 229, + 222, 232, 236, 243, 262, 277, 279, 286, 299, 311, + 319, 320, 323, 329, 381, 387, 388, 389, 390, 410, + 411, 412, 415, 418, 419, 422, 424, 425, 428, 432, + 436, 437, 438, 440, 442, 444, 456, 461, 475, 476, + 477, 478, 479, 482, 483, 489, 490, 491, 492, 493, + 501, 502, 517, 587, 589, 606, 626, 633, 481, 302, + 303, 445, 446, 315, 316, 647, 648, 301, 601, 634, + 598, 646, 628, 439, 379, 0, 0, 382, 282, 306, + 321, 0, 618, 503, 227, 467, 291, 251, 0, 0, + 211, 246, 230, 260, 275, 278, 325, 392, 401, 430, + 435, 297, 272, 244, 460, 241, 486, 520, 521, 522, + 524, 396, 267, 434, 397, 0, 377, 577, 578, 317, + 0, 0, 0, 529, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 417, 0, 0, 0, 0, + 0, 0, 0, 271, 0, 0, 0, 0, 367, 268, + 0, 1925, 204, 506, 0, 431, 0, 203, 0, 488, + 253, 378, 375, 584, 283, 274, 270, 250, 318, 386, + 429, 519, 423, 0, 371, 0, 0, 498, 402, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 324, 248, 326, + 202, 414, 499, 287, 0, 0, 0, 0, 0, 508, + 725, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 238, 0, 0, 245, 0, 0, 0, 352, 361, 360, + 340, 341, 343, 345, 351, 358, 364, 337, 346, 0, + 0, 610, 0, 0, 0, 266, 322, 273, 265, 581, + 0, 0, 0, 0, 0, 0, 597, 0, 0, 229, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 276, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 298, 0, 403, 258, + 0, 454, 0, 0, 0, 630, 0, 0, 0, 0, + 0, 0, 0, 366, 0, 331, 197, 225, 0, 0, + 413, 462, 474, 0, 0, 0, 254, 0, 472, 427, + 605, 233, 285, 459, 433, 470, 441, 288, 0, 0, + 471, 373, 586, 451, 602, 631, 632, 264, 407, 616, + 523, 624, 649, 226, 261, 421, 507, 608, 495, 398, + 582, 583, 330, 494, 296, 201, 370, 637, 224, 480, + 372, 242, 231, 588, 613, 300, 252, 290, 457, 644, + 213, 518, 599, 239, 484, 0, 0, 652, 247, 505, + 611, 600, 215, 595, 504, 394, 327, 328, 214, 0, + 458, 269, 294, 0, 0, 259, 416, 590, 591, 257, + 653, 228, 623, 220, 0, 622, 409, 585, 596, 395, + 384, 219, 594, 393, 383, 335, 356, 357, 281, 308, + 448, 376, 449, 307, 309, 405, 404, 406, 207, 609, + 627, 0, 208, 0, 500, 612, 654, 453, 212, 234, + 235, 237, 0, 280, 284, 292, 295, 304, 305, 314, + 368, 420, 447, 443, 452, 0, 580, 603, 617, 629, + 635, 636, 638, 639, 640, 641, 642, 645, 643, 408, + 312, 496, 334, 374, 0, 0, 426, 473, 240, 607, + 497, 199, 0, 0, 0, 0, 255, 256, 0, 576, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 655, + 656, 657, 658, 659, 660, 661, 662, 663, 664, 665, + 666, 667, 668, 669, 670, 671, 672, 650, 509, 515, + 510, 511, 512, 513, 514, 0, 516, 0, 0, 0, + 0, 0, 399, 0, 592, 593, 673, 385, 487, 604, + 336, 350, 353, 342, 362, 0, 363, 338, 339, 344, + 347, 348, 349, 354, 355, 359, 365, 249, 210, 391, + 400, 579, 313, 216, 217, 218, 525, 526, 527, 528, + 620, 621, 625, 205, 463, 464, 465, 466, 293, 615, + 310, 469, 468, 332, 333, 380, 450, 541, 543, 554, + 558, 560, 562, 568, 571, 542, 544, 555, 559, 561, + 563, 569, 572, 531, 533, 535, 537, 550, 549, 546, + 574, 575, 552, 557, 536, 548, 553, 566, 573, 570, + 530, 534, 538, 547, 565, 564, 545, 556, 567, 551, + 539, 532, 540, 0, 196, 221, 369, 0, 455, 289, + 651, 619, 485, 614, 206, 223, 0, 263, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 275, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 198, + 200, 209, 222, 232, 236, 243, 262, 277, 279, 286, + 299, 311, 319, 320, 323, 329, 381, 387, 388, 389, + 390, 410, 411, 412, 415, 418, 419, 422, 424, 425, + 428, 432, 436, 437, 438, 440, 442, 444, 456, 461, + 475, 476, 477, 478, 479, 482, 483, 489, 490, 491, + 492, 493, 501, 502, 517, 587, 589, 606, 626, 633, + 481, 302, 303, 445, 446, 315, 316, 647, 648, 301, + 601, 634, 598, 646, 628, 439, 379, 0, 0, 382, + 282, 306, 321, 0, 618, 503, 227, 467, 291, 251, + 0, 0, 211, 246, 230, 260, 275, 278, 325, 392, + 401, 430, 435, 297, 272, 244, 460, 241, 486, 520, + 521, 522, 524, 396, 267, 434, 397, 0, 377, 577, + 578, 317, 0, 0, 0, 529, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 417, 0, 0, + 0, 0, 0, 0, 0, 271, 0, 0, 0, 0, + 367, 268, 0, 1923, 204, 506, 0, 431, 0, 203, + 0, 488, 253, 378, 375, 584, 283, 274, 270, 250, + 318, 386, 429, 519, 423, 0, 371, 0, 0, 498, + 402, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 324, + 248, 326, 202, 414, 499, 287, 0, 0, 0, 0, + 0, 508, 725, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 238, 0, 0, 245, 0, 0, 0, 352, + 361, 360, 340, 341, 343, 345, 351, 358, 364, 337, + 346, 0, 0, 610, 0, 0, 0, 266, 322, 273, + 265, 581, 0, 0, 0, 0, 0, 0, 597, 0, + 0, 229, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 276, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 298, 0, + 403, 258, 0, 454, 0, 0, 0, 630, 0, 0, + 0, 0, 0, 0, 0, 366, 0, 331, 197, 225, + 0, 0, 413, 462, 474, 0, 0, 0, 254, 0, + 472, 427, 605, 233, 285, 459, 433, 470, 441, 288, + 0, 0, 471, 373, 586, 451, 602, 631, 632, 264, + 407, 616, 523, 624, 649, 226, 261, 421, 507, 608, + 495, 398, 582, 583, 330, 494, 296, 201, 370, 637, + 224, 480, 372, 242, 231, 588, 613, 300, 252, 290, + 457, 644, 213, 518, 599, 239, 484, 0, 0, 652, + 247, 505, 611, 600, 215, 595, 504, 394, 327, 328, + 214, 0, 458, 269, 294, 0, 0, 259, 416, 590, + 591, 257, 653, 228, 623, 220, 0, 622, 409, 585, + 596, 395, 384, 219, 594, 393, 383, 335, 356, 357, + 281, 308, 448, 376, 449, 307, 309, 405, 404, 406, + 207, 609, 627, 0, 208, 0, 500, 612, 654, 453, + 212, 234, 235, 237, 0, 280, 284, 292, 295, 304, + 305, 314, 368, 420, 447, 443, 452, 0, 580, 603, + 617, 629, 635, 636, 638, 639, 640, 641, 642, 645, + 643, 408, 312, 496, 334, 374, 0, 0, 426, 473, + 240, 607, 497, 199, 0, 0, 0, 0, 255, 256, + 0, 576, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 655, 656, 657, 658, 659, 660, 661, 662, 663, + 664, 665, 666, 667, 668, 669, 670, 671, 672, 650, + 509, 515, 510, 511, 512, 513, 514, 0, 516, 0, + 0, 0, 0, 0, 399, 0, 592, 593, 673, 385, + 487, 604, 336, 350, 353, 342, 362, 0, 363, 338, + 339, 344, 347, 348, 349, 354, 355, 359, 365, 249, + 210, 391, 400, 579, 313, 216, 217, 218, 525, 526, + 527, 528, 620, 621, 625, 205, 463, 464, 465, 466, + 293, 615, 310, 469, 468, 332, 333, 380, 450, 541, + 543, 554, 558, 560, 562, 568, 571, 542, 544, 555, + 559, 561, 563, 569, 572, 531, 533, 535, 537, 550, + 549, 546, 574, 575, 552, 557, 536, 548, 553, 566, + 573, 570, 530, 534, 538, 547, 565, 564, 545, 556, + 567, 551, 539, 532, 540, 0, 196, 221, 369, 0, + 455, 289, 651, 619, 485, 614, 206, 223, 0, 263, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 198, 200, 209, 222, 232, 236, 243, 262, 277, + 279, 286, 299, 311, 319, 320, 323, 329, 381, 387, + 388, 389, 390, 410, 411, 412, 415, 418, 419, 422, + 424, 425, 428, 432, 436, 437, 438, 440, 442, 444, + 456, 461, 475, 476, 477, 478, 479, 482, 483, 489, + 490, 491, 492, 493, 501, 502, 517, 587, 589, 606, + 626, 633, 481, 302, 303, 445, 446, 315, 316, 647, + 648, 301, 601, 634, 598, 646, 628, 439, 379, 0, + 0, 382, 282, 306, 321, 0, 618, 503, 227, 467, + 291, 251, 0, 0, 211, 246, 230, 260, 275, 278, + 325, 392, 401, 430, 435, 297, 272, 244, 460, 241, + 486, 520, 521, 522, 524, 396, 267, 434, 397, 0, + 377, 577, 578, 317, 0, 0, 0, 529, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 417, + 0, 0, 0, 0, 0, 0, 0, 271, 0, 0, + 0, 0, 367, 268, 0, 1921, 204, 506, 0, 431, + 0, 203, 0, 488, 253, 378, 375, 584, 283, 274, + 270, 250, 318, 386, 429, 519, 423, 0, 371, 0, + 0, 498, 402, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 324, 248, 326, 202, 414, 499, 287, 0, 0, + 0, 0, 0, 508, 725, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 238, 0, 0, 245, 0, 0, + 0, 352, 361, 360, 340, 341, 343, 345, 351, 358, + 364, 337, 346, 0, 0, 610, 0, 0, 0, 266, + 322, 273, 265, 581, 0, 0, 0, 0, 0, 0, + 597, 0, 0, 229, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 276, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -6862,525 +6383,1049 @@ 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, 1456, 0, 297, 0, 402, 257, - 0, 453, 0, 0, 0, 629, 0, 0, 0, 0, - 0, 0, 0, 365, 0, 330, 197, 225, 0, 0, - 412, 461, 473, 0, 0, 0, 253, 0, 471, 426, - 604, 233, 284, 458, 432, 469, 440, 287, 0, 0, - 470, 372, 585, 450, 601, 630, 631, 263, 406, 615, - 522, 623, 648, 226, 260, 420, 506, 607, 494, 397, - 581, 582, 329, 493, 295, 201, 369, 636, 224, 479, - 371, 242, 231, 587, 612, 299, 289, 456, 643, 213, - 517, 598, 239, 483, 0, 0, 651, 247, 504, 610, - 599, 215, 594, 503, 393, 326, 327, 214, 0, 457, - 268, 293, 0, 0, 258, 415, 589, 590, 256, 652, - 228, 622, 220, 0, 621, 408, 584, 595, 394, 383, - 219, 593, 392, 382, 334, 355, 356, 280, 307, 447, - 375, 448, 306, 308, 404, 403, 405, 207, 608, 626, - 0, 208, 0, 499, 611, 653, 452, 212, 234, 235, - 237, 0, 279, 283, 291, 294, 303, 304, 313, 367, - 419, 446, 442, 451, 0, 579, 602, 616, 628, 634, - 635, 637, 638, 639, 640, 641, 644, 642, 407, 311, - 495, 333, 373, 0, 0, 425, 472, 240, 606, 496, - 199, 0, 0, 0, 0, 254, 255, 0, 575, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 654, 655, - 656, 657, 658, 659, 660, 661, 662, 663, 664, 665, - 666, 667, 668, 669, 670, 671, 649, 508, 514, 509, - 510, 511, 512, 513, 0, 515, 0, 0, 0, 0, - 0, 398, 0, 591, 592, 672, 384, 486, 603, 335, - 349, 352, 341, 361, 0, 362, 337, 338, 343, 346, - 347, 348, 353, 354, 358, 364, 249, 210, 390, 399, - 578, 312, 216, 217, 218, 524, 525, 526, 527, 619, - 620, 624, 205, 462, 463, 464, 465, 292, 614, 309, - 468, 467, 331, 332, 379, 449, 540, 542, 553, 557, - 559, 561, 567, 570, 541, 543, 554, 558, 560, 562, - 568, 571, 530, 532, 534, 536, 549, 548, 545, 573, - 574, 551, 556, 535, 547, 552, 565, 572, 569, 529, - 533, 537, 546, 564, 563, 544, 555, 566, 550, 538, - 531, 539, 0, 196, 221, 368, 0, 454, 288, 650, - 618, 484, 613, 206, 223, 0, 262, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 198, 200, - 209, 222, 232, 236, 243, 261, 276, 278, 285, 298, - 310, 318, 319, 322, 328, 380, 386, 387, 388, 389, - 409, 410, 411, 414, 417, 418, 421, 423, 424, 427, - 431, 435, 436, 437, 439, 441, 443, 455, 460, 474, - 475, 476, 477, 478, 481, 482, 488, 489, 490, 491, - 492, 500, 501, 516, 586, 588, 605, 625, 632, 480, - 301, 302, 444, 445, 314, 315, 646, 647, 1455, 600, - 633, 597, 645, 627, 438, 378, 0, 0, 381, 281, - 305, 320, 0, 617, 502, 227, 466, 290, 251, 0, - 0, 211, 246, 230, 259, 274, 277, 324, 391, 400, - 429, 434, 296, 271, 244, 459, 241, 485, 519, 520, - 521, 523, 395, 266, 433, 396, 0, 376, 576, 577, - 316, 0, 0, 0, 528, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 416, 0, 0, 0, - 0, 0, 0, 0, 270, 0, 0, 0, 0, 366, - 267, 0, 0, 204, 505, 0, 430, 0, 203, 0, - 487, 252, 377, 374, 583, 282, 273, 269, 250, 317, - 385, 428, 518, 422, 0, 370, 0, 0, 497, 401, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 323, 248, - 325, 202, 413, 498, 286, 0, 0, 0, 0, 0, - 507, 194, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 238, 0, 0, 245, 0, 0, 0, 351, 360, - 359, 339, 340, 342, 344, 350, 357, 363, 336, 345, - 0, 0, 609, 0, 0, 0, 265, 321, 272, 264, - 580, 0, 0, 0, 0, 0, 0, 596, 0, 0, - 229, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 275, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 297, 0, 402, - 257, 0, 453, 0, 0, 0, 629, 0, 0, 0, - 0, 0, 0, 0, 365, 0, 330, 197, 225, 0, - 0, 412, 461, 473, 0, 0, 0, 253, 0, 471, - 426, 604, 233, 284, 458, 432, 469, 440, 287, 0, - 0, 470, 372, 585, 450, 601, 630, 631, 263, 406, - 615, 522, 623, 648, 226, 260, 420, 506, 607, 494, - 397, 581, 582, 329, 493, 295, 201, 369, 636, 224, - 479, 371, 242, 231, 587, 612, 299, 289, 456, 643, - 213, 517, 598, 239, 483, 0, 0, 651, 247, 504, - 610, 599, 215, 594, 503, 393, 326, 327, 214, 0, - 457, 268, 293, 0, 0, 258, 415, 589, 590, 256, - 652, 228, 622, 220, 0, 621, 408, 584, 595, 394, - 383, 219, 593, 392, 382, 334, 355, 356, 280, 307, - 447, 375, 448, 306, 308, 404, 403, 405, 207, 608, - 626, 0, 208, 0, 499, 611, 653, 452, 212, 234, - 235, 237, 0, 279, 283, 291, 294, 303, 304, 313, - 367, 419, 446, 442, 451, 0, 579, 602, 616, 628, - 634, 635, 637, 638, 639, 640, 641, 644, 642, 407, - 311, 495, 333, 373, 0, 0, 425, 472, 240, 606, - 496, 199, 0, 0, 0, 0, 254, 255, 0, 575, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 654, - 655, 656, 657, 658, 659, 660, 661, 662, 663, 664, - 665, 666, 667, 668, 669, 670, 671, 649, 508, 514, - 509, 510, 511, 512, 513, 0, 515, 0, 0, 0, - 0, 0, 398, 0, 591, 592, 672, 384, 486, 603, - 335, 349, 352, 341, 361, 0, 362, 337, 338, 343, - 346, 347, 348, 353, 354, 358, 364, 249, 210, 390, - 399, 578, 312, 216, 217, 218, 524, 525, 526, 527, - 619, 620, 624, 205, 462, 463, 464, 465, 292, 614, - 309, 468, 467, 331, 332, 379, 449, 540, 542, 553, - 557, 559, 561, 567, 570, 541, 543, 554, 558, 560, - 562, 568, 571, 530, 532, 534, 536, 549, 548, 545, - 573, 574, 551, 556, 535, 547, 552, 565, 572, 569, - 529, 533, 537, 546, 564, 563, 544, 555, 566, 550, - 538, 531, 539, 0, 196, 221, 368, 0, 454, 288, - 650, 618, 484, 613, 206, 223, 0, 262, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1049, 0, 0, 0, 198, - 200, 209, 222, 232, 236, 243, 261, 276, 278, 285, - 298, 310, 318, 319, 322, 328, 380, 386, 387, 388, - 389, 409, 410, 411, 414, 417, 418, 421, 423, 424, - 427, 431, 435, 436, 437, 439, 441, 443, 455, 460, - 474, 475, 476, 477, 478, 481, 482, 488, 489, 490, - 491, 492, 500, 501, 516, 586, 588, 605, 625, 632, - 480, 301, 302, 444, 445, 314, 315, 646, 647, 300, - 600, 633, 597, 645, 627, 438, 378, 0, 0, 381, - 281, 305, 320, 0, 617, 502, 227, 466, 290, 251, - 0, 0, 211, 246, 230, 259, 274, 277, 324, 391, - 400, 429, 434, 296, 271, 244, 459, 241, 485, 519, - 520, 521, 523, 395, 266, 433, 396, 0, 376, 576, - 577, 316, 0, 0, 0, 528, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 416, 0, 0, - 0, 0, 0, 0, 0, 270, 0, 0, 0, 0, - 366, 267, 0, 0, 204, 505, 0, 430, 0, 203, - 0, 487, 252, 377, 374, 583, 282, 273, 269, 250, - 317, 385, 428, 518, 422, 0, 370, 0, 0, 497, - 401, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 323, - 248, 325, 202, 413, 498, 286, 0, 0, 0, 0, - 0, 507, 194, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 238, 0, 0, 245, 0, 0, 0, 351, - 360, 359, 339, 340, 342, 344, 350, 357, 363, 336, - 345, 0, 0, 609, 0, 0, 0, 265, 321, 272, - 264, 580, 0, 0, 0, 0, 0, 0, 596, 0, - 0, 229, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 275, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 297, 0, - 402, 257, 0, 453, 0, 675, 0, 629, 0, 0, - 0, 0, 0, 0, 0, 365, 0, 330, 197, 225, - 0, 0, 412, 461, 473, 0, 0, 0, 253, 0, - 471, 426, 604, 233, 284, 458, 432, 469, 440, 287, - 0, 0, 470, 372, 585, 450, 601, 630, 631, 263, - 406, 615, 522, 623, 648, 226, 260, 420, 506, 607, - 494, 397, 581, 582, 329, 493, 295, 201, 369, 636, - 224, 479, 371, 242, 231, 587, 612, 299, 289, 456, - 643, 213, 517, 598, 239, 483, 0, 0, 651, 247, - 504, 610, 599, 215, 594, 503, 393, 326, 327, 214, - 0, 457, 268, 293, 0, 0, 258, 415, 589, 590, - 256, 652, 228, 622, 220, 0, 621, 408, 584, 595, - 394, 383, 219, 593, 392, 382, 334, 355, 356, 280, - 307, 447, 375, 448, 306, 308, 404, 403, 405, 207, - 608, 626, 0, 208, 0, 499, 611, 653, 452, 212, - 234, 235, 237, 0, 279, 283, 291, 294, 303, 304, - 313, 367, 419, 446, 442, 451, 0, 579, 602, 616, - 628, 634, 635, 637, 638, 639, 640, 641, 644, 642, - 407, 311, 495, 333, 373, 0, 0, 425, 472, 240, - 606, 496, 199, 0, 0, 0, 0, 254, 255, 0, - 575, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 654, 655, 656, 657, 658, 659, 660, 661, 662, 663, - 664, 665, 666, 667, 668, 669, 670, 671, 649, 508, - 514, 509, 510, 511, 512, 513, 0, 515, 0, 0, - 0, 0, 0, 398, 0, 591, 592, 672, 384, 486, - 603, 335, 349, 352, 341, 361, 0, 362, 337, 338, - 343, 346, 347, 348, 353, 354, 358, 364, 249, 210, - 390, 399, 578, 312, 216, 217, 218, 524, 525, 526, - 527, 619, 620, 624, 205, 462, 463, 464, 465, 292, - 614, 309, 468, 467, 331, 332, 379, 449, 540, 542, - 553, 557, 559, 561, 567, 570, 541, 543, 554, 558, - 560, 562, 568, 571, 530, 532, 534, 536, 549, 548, - 545, 573, 574, 551, 556, 535, 547, 552, 565, 572, - 569, 529, 533, 537, 546, 564, 563, 544, 555, 566, - 550, 538, 531, 539, 0, 196, 221, 368, 0, 454, - 288, 650, 618, 484, 613, 206, 223, 0, 262, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 198, 200, 209, 222, 232, 236, 243, 261, 276, 278, - 285, 298, 310, 318, 319, 322, 328, 380, 386, 387, - 388, 389, 409, 410, 411, 414, 417, 418, 421, 423, - 424, 427, 431, 435, 436, 437, 439, 441, 443, 455, - 460, 474, 475, 476, 477, 478, 481, 482, 488, 489, - 490, 491, 492, 500, 501, 516, 586, 588, 605, 625, - 632, 480, 301, 302, 444, 445, 314, 315, 646, 647, - 300, 600, 633, 597, 645, 627, 438, 378, 0, 0, - 381, 281, 305, 320, 0, 617, 502, 227, 466, 290, - 251, 0, 0, 211, 246, 230, 259, 274, 277, 324, - 391, 400, 429, 434, 296, 271, 244, 459, 241, 485, - 519, 520, 521, 523, 395, 266, 433, 396, 0, 376, - 576, 577, 316, 0, 0, 0, 528, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 416, 0, - 0, 0, 0, 0, 0, 0, 270, 0, 0, 0, - 0, 366, 267, 0, 0, 204, 505, 0, 430, 0, - 203, 0, 487, 252, 377, 374, 583, 282, 273, 269, - 250, 317, 385, 428, 518, 422, 0, 370, 0, 0, - 497, 401, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 323, 248, 325, 202, 413, 498, 286, 0, 0, 0, - 0, 0, 507, 724, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 238, 0, 0, 245, 0, 0, 0, - 351, 360, 359, 339, 340, 342, 344, 350, 357, 363, - 336, 345, 0, 0, 609, 0, 0, 0, 265, 321, - 272, 264, 580, 0, 0, 0, 0, 0, 0, 596, - 0, 0, 229, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 275, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 297, - 0, 402, 257, 0, 453, 0, 0, 0, 629, 0, - 0, 0, 0, 0, 0, 0, 365, 0, 330, 197, - 225, 0, 0, 412, 461, 473, 0, 0, 0, 253, - 0, 471, 426, 604, 233, 284, 458, 432, 469, 440, - 287, 0, 0, 470, 372, 585, 450, 601, 630, 631, - 263, 406, 615, 522, 623, 648, 226, 260, 420, 506, - 607, 494, 397, 581, 582, 329, 493, 295, 201, 369, - 636, 224, 479, 371, 242, 231, 587, 612, 299, 289, - 456, 643, 213, 517, 598, 239, 483, 0, 0, 651, - 247, 504, 610, 599, 215, 594, 503, 393, 326, 327, - 214, 0, 457, 268, 293, 0, 0, 258, 415, 589, - 590, 256, 652, 228, 622, 220, 0, 621, 408, 584, - 595, 394, 383, 219, 593, 392, 382, 334, 355, 356, - 280, 307, 447, 375, 448, 306, 308, 404, 403, 405, - 207, 608, 626, 0, 208, 0, 499, 611, 653, 452, - 212, 234, 235, 237, 0, 279, 283, 291, 294, 303, - 304, 313, 367, 419, 446, 442, 451, 0, 579, 602, - 616, 628, 634, 635, 637, 638, 639, 640, 641, 644, - 642, 407, 311, 495, 333, 373, 0, 0, 425, 472, - 240, 606, 496, 199, 0, 0, 0, 0, 254, 255, - 0, 575, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 654, 655, 656, 657, 658, 659, 660, 661, 662, - 663, 664, 665, 666, 667, 668, 669, 670, 671, 649, - 508, 514, 509, 510, 511, 512, 513, 0, 515, 0, - 0, 0, 0, 0, 398, 0, 591, 592, 672, 384, - 486, 603, 335, 349, 352, 341, 361, 0, 362, 337, - 338, 343, 346, 347, 348, 353, 354, 358, 364, 249, - 210, 390, 399, 578, 312, 216, 217, 218, 524, 525, - 526, 527, 619, 620, 624, 205, 462, 463, 464, 465, - 292, 614, 309, 468, 467, 331, 332, 379, 449, 540, - 542, 553, 557, 559, 561, 567, 570, 541, 543, 554, - 558, 560, 562, 568, 571, 530, 532, 534, 536, 549, - 548, 545, 573, 574, 551, 556, 535, 547, 552, 565, - 572, 569, 529, 533, 537, 546, 564, 563, 544, 555, - 566, 550, 538, 531, 539, 0, 196, 221, 368, 0, - 454, 288, 650, 618, 484, 613, 206, 223, 0, 262, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 198, 200, 209, 222, 232, 236, 243, 261, 276, - 278, 285, 298, 310, 318, 319, 322, 328, 380, 386, - 387, 388, 389, 4136, 410, 411, 414, 417, 418, 421, - 423, 424, 427, 431, 435, 436, 437, 439, 441, 443, - 455, 460, 474, 475, 476, 477, 478, 481, 482, 488, - 489, 490, 491, 492, 500, 501, 516, 586, 588, 605, - 625, 632, 480, 301, 302, 444, 445, 314, 315, 646, - 647, 300, 600, 633, 597, 645, 627, 438, 378, 0, - 0, 381, 281, 305, 320, 0, 617, 502, 227, 466, - 290, 251, 0, 0, 211, 246, 230, 259, 274, 277, - 324, 391, 400, 429, 434, 296, 271, 244, 459, 241, - 485, 519, 520, 521, 523, 395, 266, 433, 396, 0, - 376, 576, 577, 316, 0, 0, 0, 528, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 416, - 0, 0, 0, 0, 0, 0, 0, 270, 0, 0, - 0, 0, 366, 267, 0, 0, 204, 505, 0, 430, - 0, 203, 0, 487, 252, 377, 374, 583, 282, 273, - 269, 250, 317, 385, 428, 518, 422, 0, 370, 0, - 0, 497, 401, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 323, 248, 325, 202, 413, 498, 286, 0, 0, - 0, 0, 0, 507, 724, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 238, 0, 0, 245, 0, 0, - 0, 351, 360, 359, 339, 340, 342, 344, 350, 357, - 363, 336, 345, 0, 0, 609, 0, 0, 0, 265, - 321, 272, 264, 580, 0, 0, 0, 0, 0, 0, - 596, 0, 0, 229, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 275, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 298, 0, 403, 258, 0, 454, 0, 0, 0, 630, + 0, 0, 0, 0, 0, 0, 0, 366, 0, 331, + 197, 225, 0, 0, 413, 462, 474, 0, 0, 0, + 254, 0, 472, 427, 605, 233, 285, 459, 433, 470, + 441, 288, 0, 0, 471, 373, 586, 451, 602, 631, + 632, 264, 407, 616, 523, 624, 649, 226, 261, 421, + 507, 608, 495, 398, 582, 583, 330, 494, 296, 201, + 370, 637, 224, 480, 372, 242, 231, 588, 613, 300, + 252, 290, 457, 644, 213, 518, 599, 239, 484, 0, + 0, 652, 247, 505, 611, 600, 215, 595, 504, 394, + 327, 328, 214, 0, 458, 269, 294, 0, 0, 259, + 416, 590, 591, 257, 653, 228, 623, 220, 0, 622, + 409, 585, 596, 395, 384, 219, 594, 393, 383, 335, + 356, 357, 281, 308, 448, 376, 449, 307, 309, 405, + 404, 406, 207, 609, 627, 0, 208, 0, 500, 612, + 654, 453, 212, 234, 235, 237, 0, 280, 284, 292, + 295, 304, 305, 314, 368, 420, 447, 443, 452, 0, + 580, 603, 617, 629, 635, 636, 638, 639, 640, 641, + 642, 645, 643, 408, 312, 496, 334, 374, 0, 0, + 426, 473, 240, 607, 497, 199, 0, 0, 0, 0, + 255, 256, 0, 576, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 655, 656, 657, 658, 659, 660, 661, + 662, 663, 664, 665, 666, 667, 668, 669, 670, 671, + 672, 650, 509, 515, 510, 511, 512, 513, 514, 0, + 516, 0, 0, 0, 0, 0, 399, 0, 592, 593, + 673, 385, 487, 604, 336, 350, 353, 342, 362, 0, + 363, 338, 339, 344, 347, 348, 349, 354, 355, 359, + 365, 249, 210, 391, 400, 579, 313, 216, 217, 218, + 525, 526, 527, 528, 620, 621, 625, 205, 463, 464, + 465, 466, 293, 615, 310, 469, 468, 332, 333, 380, + 450, 541, 543, 554, 558, 560, 562, 568, 571, 542, + 544, 555, 559, 561, 563, 569, 572, 531, 533, 535, + 537, 550, 549, 546, 574, 575, 552, 557, 536, 548, + 553, 566, 573, 570, 530, 534, 538, 547, 565, 564, + 545, 556, 567, 551, 539, 532, 540, 0, 196, 221, + 369, 0, 455, 289, 651, 619, 485, 614, 206, 223, + 0, 263, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 198, 200, 209, 222, 232, 236, 243, + 262, 277, 279, 286, 299, 311, 319, 320, 323, 329, + 381, 387, 388, 389, 390, 410, 411, 412, 415, 418, + 419, 422, 424, 425, 428, 432, 436, 437, 438, 440, + 442, 444, 456, 461, 475, 476, 477, 478, 479, 482, + 483, 489, 490, 491, 492, 493, 501, 502, 517, 587, + 589, 606, 626, 633, 481, 302, 303, 445, 446, 315, + 316, 647, 648, 301, 601, 634, 598, 646, 628, 439, + 379, 0, 0, 382, 282, 306, 321, 0, 618, 503, + 227, 467, 291, 251, 0, 0, 211, 246, 230, 260, + 275, 278, 325, 392, 401, 430, 435, 297, 272, 244, + 460, 241, 486, 520, 521, 522, 524, 396, 267, 434, + 397, 0, 377, 577, 578, 317, 0, 0, 0, 529, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 417, 0, 0, 0, 0, 0, 0, 0, 271, + 0, 0, 0, 0, 367, 268, 0, 1917, 204, 506, + 0, 431, 0, 203, 0, 488, 253, 378, 375, 584, + 283, 274, 270, 250, 318, 386, 429, 519, 423, 0, + 371, 0, 0, 498, 402, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 324, 248, 326, 202, 414, 499, 287, + 0, 0, 0, 0, 0, 508, 725, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 238, 0, 0, 245, + 0, 0, 0, 352, 361, 360, 340, 341, 343, 345, + 351, 358, 364, 337, 346, 0, 0, 610, 0, 0, + 0, 266, 322, 273, 265, 581, 0, 0, 0, 0, + 0, 0, 597, 0, 0, 229, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 276, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 297, 0, 402, 257, 0, 453, 0, 0, 0, 629, - 0, 0, 0, 0, 0, 0, 0, 365, 0, 330, - 197, 225, 0, 0, 412, 461, 473, 0, 0, 0, - 253, 0, 471, 426, 604, 233, 284, 458, 432, 469, - 440, 287, 0, 0, 470, 372, 585, 450, 601, 630, - 631, 263, 406, 615, 522, 623, 648, 226, 260, 420, - 506, 607, 494, 397, 581, 582, 329, 493, 295, 201, - 369, 636, 224, 479, 371, 242, 231, 587, 612, 299, - 289, 456, 643, 213, 517, 598, 239, 483, 0, 0, - 651, 247, 504, 610, 599, 215, 594, 503, 393, 326, - 327, 214, 0, 457, 268, 293, 0, 0, 258, 415, - 589, 590, 256, 652, 228, 622, 220, 0, 621, 408, - 584, 595, 394, 383, 219, 593, 392, 382, 334, 355, - 356, 280, 307, 447, 375, 448, 306, 308, 404, 403, - 405, 207, 608, 626, 0, 208, 0, 499, 611, 653, - 452, 212, 234, 235, 237, 0, 279, 283, 291, 294, - 303, 304, 313, 367, 419, 446, 442, 451, 0, 579, - 602, 616, 628, 634, 635, 637, 638, 639, 640, 641, - 644, 642, 407, 311, 495, 333, 373, 0, 0, 425, - 472, 240, 606, 496, 199, 0, 0, 0, 0, 254, - 255, 0, 575, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 654, 655, 656, 657, 658, 659, 660, 661, - 662, 663, 664, 665, 666, 667, 668, 669, 670, 671, - 649, 508, 514, 509, 510, 511, 512, 513, 0, 515, - 0, 0, 0, 0, 0, 398, 0, 591, 592, 672, - 384, 486, 603, 335, 349, 352, 341, 361, 0, 362, - 337, 338, 343, 346, 347, 348, 353, 354, 358, 364, - 249, 210, 390, 399, 578, 312, 216, 217, 218, 524, - 525, 526, 527, 619, 620, 624, 205, 462, 463, 464, - 465, 292, 614, 309, 468, 467, 331, 332, 379, 449, - 540, 542, 553, 557, 559, 561, 567, 570, 541, 543, - 554, 558, 560, 562, 568, 571, 530, 532, 534, 536, - 549, 548, 545, 573, 574, 551, 556, 535, 547, 552, - 565, 572, 569, 529, 533, 537, 546, 564, 563, 544, - 555, 566, 550, 538, 531, 539, 0, 196, 221, 368, - 0, 454, 288, 650, 618, 484, 613, 206, 223, 0, - 262, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 198, 200, 209, 222, 232, 236, 243, 261, - 276, 278, 285, 298, 310, 318, 319, 322, 328, 380, - 386, 387, 388, 389, 409, 410, 411, 414, 417, 418, - 421, 423, 424, 427, 431, 435, 436, 437, 439, 441, - 443, 455, 460, 474, 475, 476, 477, 478, 481, 482, - 488, 489, 490, 491, 492, 500, 501, 516, 586, 588, - 605, 625, 632, 480, 301, 302, 444, 445, 314, 315, - 646, 647, 300, 600, 633, 597, 645, 627, 438, 378, - 0, 0, 381, 281, 305, 320, 0, 617, 502, 227, - 466, 290, 251, 0, 0, 211, 246, 230, 259, 274, - 277, 324, 391, 400, 429, 434, 296, 271, 244, 459, - 241, 485, 519, 520, 521, 523, 395, 266, 433, 396, - 0, 376, 576, 577, 316, 0, 0, 0, 528, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 416, 0, 0, 0, 0, 0, 0, 0, 270, 0, - 0, 0, 0, 366, 267, 0, 0, 204, 505, 0, - 430, 0, 203, 0, 487, 252, 377, 374, 583, 282, - 273, 269, 250, 317, 385, 428, 518, 422, 0, 370, - 0, 0, 497, 401, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 323, 248, 325, 202, 413, 498, 286, 0, - 0, 0, 0, 0, 507, 959, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 238, 0, 0, 245, 0, - 0, 0, 351, 360, 359, 339, 340, 342, 344, 350, - 357, 363, 336, 345, 0, 0, 609, 0, 0, 0, - 265, 321, 272, 264, 580, 0, 0, 0, 0, 0, - 0, 596, 0, 0, 229, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 275, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 297, 0, 402, 257, 0, 453, 0, 0, 0, - 629, 0, 0, 0, 0, 0, 0, 0, 365, 0, - 330, 197, 225, 0, 0, 412, 461, 473, 0, 0, - 0, 253, 0, 471, 426, 604, 233, 284, 458, 432, - 469, 440, 287, 0, 0, 470, 372, 585, 450, 601, - 630, 631, 263, 406, 615, 522, 623, 648, 226, 260, - 420, 506, 607, 494, 397, 581, 582, 329, 493, 295, - 201, 369, 636, 224, 479, 371, 242, 231, 587, 612, - 299, 289, 456, 643, 213, 517, 598, 239, 483, 0, - 0, 651, 247, 504, 610, 599, 215, 594, 503, 393, - 326, 327, 214, 0, 457, 268, 293, 0, 0, 258, - 415, 589, 590, 256, 652, 228, 622, 220, 0, 621, - 408, 584, 595, 394, 383, 219, 593, 392, 382, 334, - 355, 356, 280, 307, 447, 375, 448, 306, 308, 404, - 403, 405, 207, 608, 626, 0, 208, 0, 499, 611, - 653, 452, 212, 234, 235, 237, 0, 279, 283, 291, - 294, 303, 304, 313, 367, 419, 446, 442, 451, 0, - 579, 602, 616, 628, 634, 635, 637, 638, 639, 640, - 641, 644, 642, 407, 311, 495, 333, 373, 0, 0, - 425, 472, 240, 606, 496, 199, 0, 0, 0, 0, - 254, 255, 0, 575, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 654, 655, 656, 657, 658, 659, 660, - 661, 662, 663, 664, 665, 666, 667, 668, 669, 670, - 671, 649, 508, 514, 509, 510, 511, 512, 513, 0, - 515, 0, 0, 0, 0, 0, 398, 0, 591, 592, - 672, 384, 486, 603, 335, 349, 352, 341, 361, 0, - 362, 337, 338, 343, 346, 347, 348, 353, 354, 358, - 364, 249, 210, 390, 399, 578, 312, 216, 217, 218, - 524, 525, 526, 527, 619, 620, 624, 205, 462, 463, - 464, 465, 292, 614, 309, 468, 467, 331, 332, 379, - 449, 540, 542, 553, 557, 559, 561, 567, 570, 541, - 543, 554, 558, 560, 562, 568, 571, 530, 532, 534, - 536, 549, 548, 545, 573, 574, 551, 556, 535, 547, - 552, 565, 572, 569, 529, 533, 537, 546, 564, 563, - 544, 555, 566, 550, 538, 531, 539, 0, 196, 221, - 368, 0, 454, 288, 650, 618, 484, 613, 206, 223, - 0, 262, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 198, 200, 209, 222, 232, 236, 243, - 261, 276, 278, 285, 298, 310, 318, 319, 322, 328, - 380, 386, 387, 388, 389, 409, 410, 411, 414, 417, - 418, 421, 423, 424, 427, 431, 435, 436, 437, 439, - 441, 443, 455, 460, 474, 475, 476, 477, 478, 481, - 482, 488, 489, 490, 491, 492, 500, 501, 516, 586, - 588, 605, 625, 632, 480, 301, 302, 444, 445, 314, - 315, 646, 647, 300, 600, 633, 597, 645, 627, 438, - 378, 0, 0, 381, 281, 305, 320, 0, 617, 502, - 227, 466, 290, 251, 0, 0, 211, 246, 230, 259, - 274, 277, 324, 391, 400, 429, 434, 296, 271, 244, - 459, 241, 485, 519, 520, 521, 523, 395, 266, 433, - 396, 0, 376, 576, 577, 316, 0, 0, 0, 528, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 416, 0, 0, 0, 0, 0, 0, 0, 270, - 0, 0, 0, 0, 366, 267, 0, 0, 204, 505, - 0, 430, 0, 203, 0, 487, 252, 377, 374, 583, - 282, 273, 269, 250, 317, 385, 428, 518, 422, 0, - 370, 0, 0, 497, 401, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 323, 248, 325, 202, 413, 498, 286, - 0, 0, 0, 0, 0, 507, 194, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 238, 0, 0, 245, - 0, 0, 0, 351, 360, 359, 339, 340, 342, 344, - 350, 357, 363, 336, 345, 0, 0, 609, 0, 0, - 0, 265, 321, 272, 264, 580, 0, 0, 0, 0, - 0, 0, 596, 0, 0, 229, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 275, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 298, 0, 403, 258, 0, 454, 0, 0, + 0, 630, 0, 0, 0, 0, 0, 0, 0, 366, + 0, 331, 197, 225, 0, 0, 413, 462, 474, 0, + 0, 0, 254, 0, 472, 427, 605, 233, 285, 459, + 433, 470, 441, 288, 0, 0, 471, 373, 586, 451, + 602, 631, 632, 264, 407, 616, 523, 624, 649, 226, + 261, 421, 507, 608, 495, 398, 582, 583, 330, 494, + 296, 201, 370, 637, 224, 480, 372, 242, 231, 588, + 613, 300, 252, 290, 457, 644, 213, 518, 599, 239, + 484, 0, 0, 652, 247, 505, 611, 600, 215, 595, + 504, 394, 327, 328, 214, 0, 458, 269, 294, 0, + 0, 259, 416, 590, 591, 257, 653, 228, 623, 220, + 0, 622, 409, 585, 596, 395, 384, 219, 594, 393, + 383, 335, 356, 357, 281, 308, 448, 376, 449, 307, + 309, 405, 404, 406, 207, 609, 627, 0, 208, 0, + 500, 612, 654, 453, 212, 234, 235, 237, 0, 280, + 284, 292, 295, 304, 305, 314, 368, 420, 447, 443, + 452, 0, 580, 603, 617, 629, 635, 636, 638, 639, + 640, 641, 642, 645, 643, 408, 312, 496, 334, 374, + 0, 0, 426, 473, 240, 607, 497, 199, 0, 0, + 0, 0, 255, 256, 0, 576, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 655, 656, 657, 658, 659, + 660, 661, 662, 663, 664, 665, 666, 667, 668, 669, + 670, 671, 672, 650, 509, 515, 510, 511, 512, 513, + 514, 0, 516, 0, 0, 0, 0, 0, 399, 0, + 592, 593, 673, 385, 487, 604, 336, 350, 353, 342, + 362, 0, 363, 338, 339, 344, 347, 348, 349, 354, + 355, 359, 365, 249, 210, 391, 400, 579, 313, 216, + 217, 218, 525, 526, 527, 528, 620, 621, 625, 205, + 463, 464, 465, 466, 293, 615, 310, 469, 468, 332, + 333, 380, 450, 541, 543, 554, 558, 560, 562, 568, + 571, 542, 544, 555, 559, 561, 563, 569, 572, 531, + 533, 535, 537, 550, 549, 546, 574, 575, 552, 557, + 536, 548, 553, 566, 573, 570, 530, 534, 538, 547, + 565, 564, 545, 556, 567, 551, 539, 532, 540, 0, + 196, 221, 369, 0, 455, 289, 651, 619, 485, 614, + 206, 223, 0, 263, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 198, 200, 209, 222, 232, + 236, 243, 262, 277, 279, 286, 299, 311, 319, 320, + 323, 329, 381, 387, 388, 389, 390, 410, 411, 412, + 415, 418, 419, 422, 424, 425, 428, 432, 436, 437, + 438, 440, 442, 444, 456, 461, 475, 476, 477, 478, + 479, 482, 483, 489, 490, 491, 492, 493, 501, 502, + 517, 587, 589, 606, 626, 633, 481, 302, 303, 445, + 446, 315, 316, 647, 648, 301, 601, 634, 598, 646, + 628, 439, 379, 0, 0, 382, 282, 306, 321, 0, + 618, 503, 227, 467, 291, 251, 0, 0, 211, 246, + 230, 260, 275, 278, 325, 392, 401, 430, 435, 297, + 272, 244, 460, 241, 486, 520, 521, 522, 524, 396, + 267, 434, 397, 0, 377, 577, 578, 317, 0, 0, + 0, 529, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 417, 0, 0, 0, 0, 0, 0, + 0, 271, 0, 0, 0, 0, 367, 268, 0, 1915, + 204, 506, 0, 431, 0, 203, 0, 488, 253, 378, + 375, 584, 283, 274, 270, 250, 318, 386, 429, 519, + 423, 0, 371, 0, 0, 498, 402, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 324, 248, 326, 202, 414, + 499, 287, 0, 0, 0, 0, 0, 508, 725, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 238, 0, + 0, 245, 0, 0, 0, 352, 361, 360, 340, 341, + 343, 345, 351, 358, 364, 337, 346, 0, 0, 610, + 0, 0, 0, 266, 322, 273, 265, 581, 0, 0, + 0, 0, 0, 0, 597, 0, 0, 229, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 276, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 297, 0, 402, 257, 0, 453, 0, 0, - 0, 629, 0, 0, 0, 0, 0, 0, 0, 365, - 0, 330, 197, 225, 0, 0, 412, 461, 473, 0, - 0, 0, 253, 0, 471, 426, 604, 233, 284, 458, - 432, 469, 440, 287, 0, 0, 470, 372, 585, 450, - 601, 630, 631, 263, 406, 615, 522, 623, 648, 226, - 260, 420, 506, 607, 494, 397, 581, 582, 329, 493, - 295, 201, 369, 636, 224, 479, 371, 242, 231, 587, - 612, 299, 289, 456, 643, 213, 517, 598, 239, 483, - 0, 0, 651, 247, 504, 610, 599, 215, 594, 503, - 393, 326, 327, 214, 0, 457, 268, 293, 0, 0, - 258, 415, 589, 590, 256, 652, 228, 622, 220, 0, - 621, 408, 584, 595, 394, 383, 219, 593, 392, 382, - 334, 355, 356, 280, 307, 447, 375, 448, 306, 308, - 404, 403, 405, 207, 608, 626, 0, 208, 0, 499, - 611, 653, 452, 212, 234, 235, 237, 0, 279, 283, - 291, 294, 303, 304, 313, 367, 419, 446, 442, 451, - 0, 579, 602, 616, 628, 634, 635, 637, 638, 639, - 640, 641, 644, 642, 407, 311, 495, 333, 373, 0, - 0, 425, 472, 240, 606, 496, 199, 0, 0, 0, - 0, 254, 255, 0, 575, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 654, 655, 656, 657, 658, 659, - 660, 661, 662, 663, 664, 665, 666, 667, 668, 669, - 670, 671, 649, 508, 514, 509, 510, 511, 512, 513, - 0, 515, 0, 0, 0, 0, 0, 398, 0, 591, - 592, 672, 384, 486, 603, 335, 349, 352, 341, 361, - 0, 362, 337, 338, 343, 346, 347, 348, 353, 354, - 358, 364, 249, 210, 390, 399, 578, 312, 216, 217, - 218, 524, 525, 526, 527, 619, 620, 624, 205, 462, - 463, 464, 465, 292, 614, 309, 468, 467, 331, 332, - 379, 449, 540, 542, 553, 557, 559, 561, 567, 570, - 541, 543, 554, 558, 560, 562, 568, 571, 530, 532, - 534, 536, 549, 548, 545, 573, 574, 551, 556, 535, - 547, 552, 565, 572, 569, 529, 533, 537, 546, 564, - 563, 544, 555, 566, 550, 538, 531, 539, 0, 196, - 221, 368, 0, 454, 288, 650, 618, 484, 613, 206, - 223, 0, 262, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 198, 200, 209, 222, 232, 236, - 243, 261, 276, 278, 285, 298, 310, 318, 319, 322, - 328, 380, 386, 387, 388, 389, 409, 410, 411, 414, - 417, 418, 421, 423, 424, 427, 431, 435, 436, 437, - 439, 441, 443, 455, 460, 474, 475, 476, 477, 478, - 481, 482, 488, 489, 490, 491, 492, 500, 501, 516, - 586, 588, 605, 625, 632, 480, 301, 302, 444, 445, - 314, 315, 646, 647, 300, 600, 633, 597, 645, 627, - 438, 378, 0, 0, 381, 281, 305, 320, 0, 617, - 502, 227, 466, 290, 251, 0, 0, 211, 246, 230, - 259, 274, 277, 324, 391, 400, 429, 434, 296, 271, - 244, 459, 241, 485, 519, 520, 521, 523, 395, 266, - 433, 0, 0, 376, 576, 577, 316, -} - -var yyPact = [...]int{ - -1000, -1000, 1925, -1000, -552, -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, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 298, 0, 403, 258, 0, 454, + 0, 0, 0, 630, 0, 0, 0, 0, 0, 0, + 0, 366, 0, 331, 197, 225, 0, 0, 413, 462, + 474, 0, 0, 0, 254, 0, 472, 427, 605, 233, + 285, 459, 433, 470, 441, 288, 0, 0, 471, 373, + 586, 451, 602, 631, 632, 264, 407, 616, 523, 624, + 649, 226, 261, 421, 507, 608, 495, 398, 582, 583, + 330, 494, 296, 201, 370, 637, 224, 480, 372, 242, + 231, 588, 613, 300, 252, 290, 457, 644, 213, 518, + 599, 239, 484, 0, 0, 652, 247, 505, 611, 600, + 215, 595, 504, 394, 327, 328, 214, 0, 458, 269, + 294, 0, 0, 259, 416, 590, 591, 257, 653, 228, + 623, 220, 0, 622, 409, 585, 596, 395, 384, 219, + 594, 393, 383, 335, 356, 357, 281, 308, 448, 376, + 449, 307, 309, 405, 404, 406, 207, 609, 627, 0, + 208, 0, 500, 612, 654, 453, 212, 234, 235, 237, + 0, 280, 284, 292, 295, 304, 305, 314, 368, 420, + 447, 443, 452, 0, 580, 603, 617, 629, 635, 636, + 638, 639, 640, 641, 642, 645, 643, 408, 312, 496, + 334, 374, 0, 0, 426, 473, 240, 607, 497, 199, + 0, 0, 0, 0, 255, 256, 0, 576, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 655, 656, 657, + 658, 659, 660, 661, 662, 663, 664, 665, 666, 667, + 668, 669, 670, 671, 672, 650, 509, 515, 510, 511, + 512, 513, 514, 0, 516, 0, 0, 0, 0, 0, + 399, 0, 592, 593, 673, 385, 487, 604, 336, 350, + 353, 342, 362, 0, 363, 338, 339, 344, 347, 348, + 349, 354, 355, 359, 365, 249, 210, 391, 400, 579, + 313, 216, 217, 218, 525, 526, 527, 528, 620, 621, + 625, 205, 463, 464, 465, 466, 293, 615, 310, 469, + 468, 332, 333, 380, 450, 541, 543, 554, 558, 560, + 562, 568, 571, 542, 544, 555, 559, 561, 563, 569, + 572, 531, 533, 535, 537, 550, 549, 546, 574, 575, + 552, 557, 536, 548, 553, 566, 573, 570, 530, 534, + 538, 547, 565, 564, 545, 556, 567, 551, 539, 532, + 540, 0, 196, 221, 369, 0, 455, 289, 651, 619, + 485, 614, 206, 223, 0, 263, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 198, 200, 209, + 222, 232, 236, 243, 262, 277, 279, 286, 299, 311, + 319, 320, 323, 329, 381, 387, 388, 389, 390, 410, + 411, 412, 415, 418, 419, 422, 424, 425, 428, 432, + 436, 437, 438, 440, 442, 444, 456, 461, 475, 476, + 477, 478, 479, 482, 483, 489, 490, 491, 492, 493, + 501, 502, 517, 587, 589, 606, 626, 633, 481, 302, + 303, 445, 446, 315, 316, 647, 648, 301, 601, 634, + 598, 646, 628, 439, 379, 0, 0, 382, 282, 306, + 321, 0, 618, 503, 227, 467, 291, 251, 0, 0, + 211, 246, 230, 260, 275, 278, 325, 392, 401, 430, + 435, 297, 272, 244, 460, 241, 486, 520, 521, 522, + 524, 396, 267, 434, 397, 0, 377, 577, 578, 317, + 0, 0, 0, 529, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 417, 0, 0, 0, 0, + 0, 0, 0, 271, 0, 0, 0, 0, 367, 268, + 0, 1913, 204, 506, 0, 431, 0, 203, 0, 488, + 253, 378, 375, 584, 283, 274, 270, 250, 318, 386, + 429, 519, 423, 0, 371, 0, 0, 498, 402, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 324, 248, 326, + 202, 414, 499, 287, 0, 0, 0, 0, 0, 508, + 725, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 238, 0, 0, 245, 0, 0, 0, 352, 361, 360, + 340, 341, 343, 345, 351, 358, 364, 337, 346, 0, + 0, 610, 0, 0, 0, 266, 322, 273, 265, 581, + 0, 0, 0, 0, 0, 0, 597, 0, 0, 229, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 276, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 298, 0, 403, 258, + 0, 454, 0, 0, 0, 630, 0, 0, 0, 0, + 0, 0, 0, 366, 0, 331, 197, 225, 0, 0, + 413, 462, 474, 0, 0, 0, 254, 0, 472, 427, + 605, 233, 285, 459, 433, 470, 441, 288, 0, 0, + 471, 373, 586, 451, 602, 631, 632, 264, 407, 616, + 523, 624, 649, 226, 261, 421, 507, 608, 495, 398, + 582, 583, 330, 494, 296, 201, 370, 637, 224, 480, + 372, 242, 231, 588, 613, 300, 252, 290, 457, 644, + 213, 518, 599, 239, 484, 0, 0, 652, 247, 505, + 611, 600, 215, 595, 504, 394, 327, 328, 214, 0, + 458, 269, 294, 0, 0, 259, 416, 590, 591, 257, + 653, 228, 623, 220, 0, 622, 409, 585, 596, 395, + 384, 219, 594, 393, 383, 335, 356, 357, 281, 308, + 448, 376, 449, 307, 309, 405, 404, 406, 207, 609, + 627, 0, 208, 0, 500, 612, 654, 453, 212, 234, + 235, 237, 0, 280, 284, 292, 295, 304, 305, 314, + 368, 420, 447, 443, 452, 0, 580, 603, 617, 629, + 635, 636, 638, 639, 640, 641, 642, 645, 643, 408, + 312, 496, 334, 374, 0, 0, 426, 473, 240, 607, + 497, 199, 0, 0, 0, 0, 255, 256, 0, 576, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 655, + 656, 657, 658, 659, 660, 661, 662, 663, 664, 665, + 666, 667, 668, 669, 670, 671, 672, 650, 509, 515, + 510, 511, 512, 513, 514, 0, 516, 0, 0, 0, + 0, 0, 399, 0, 592, 593, 673, 385, 487, 604, + 336, 350, 353, 342, 362, 0, 363, 338, 339, 344, + 347, 348, 349, 354, 355, 359, 365, 249, 210, 391, + 400, 579, 313, 216, 217, 218, 525, 526, 527, 528, + 620, 621, 625, 205, 463, 464, 465, 466, 293, 615, + 310, 469, 468, 332, 333, 380, 450, 541, 543, 554, + 558, 560, 562, 568, 571, 542, 544, 555, 559, 561, + 563, 569, 572, 531, 533, 535, 537, 550, 549, 546, + 574, 575, 552, 557, 536, 548, 553, 566, 573, 570, + 530, 534, 538, 547, 565, 564, 545, 556, 567, 551, + 539, 532, 540, 0, 196, 221, 369, 0, 455, 289, + 651, 619, 485, 614, 206, 223, 0, 263, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 198, + 200, 209, 222, 232, 236, 243, 262, 277, 279, 286, + 299, 311, 319, 320, 323, 329, 381, 387, 388, 389, + 390, 410, 411, 412, 415, 418, 419, 422, 424, 425, + 428, 432, 436, 437, 438, 440, 442, 444, 456, 461, + 475, 476, 477, 478, 479, 482, 483, 489, 490, 491, + 492, 493, 501, 502, 517, 587, 589, 606, 626, 633, + 481, 302, 303, 445, 446, 315, 316, 647, 648, 301, + 601, 634, 598, 646, 628, 439, 379, 0, 0, 382, + 282, 306, 321, 0, 618, 503, 227, 467, 291, 251, + 0, 0, 211, 246, 230, 260, 275, 278, 325, 392, + 401, 430, 435, 297, 272, 244, 460, 241, 486, 520, + 521, 522, 524, 396, 267, 434, 397, 0, 377, 577, + 578, 317, 0, 0, 0, 529, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 417, 0, 0, + 0, 0, 0, 0, 0, 271, 0, 0, 0, 0, + 367, 268, 0, 0, 204, 506, 0, 431, 0, 203, + 0, 488, 253, 378, 375, 584, 283, 274, 270, 250, + 318, 386, 429, 519, 423, 0, 371, 0, 0, 498, + 402, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 324, + 248, 326, 202, 414, 499, 287, 0, 1888, 0, 0, + 0, 508, 725, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 238, 0, 0, 245, 0, 0, 0, 352, + 361, 360, 340, 341, 343, 345, 351, 358, 364, 337, + 346, 0, 0, 610, 0, 0, 0, 266, 322, 273, + 265, 581, 0, 0, 0, 0, 0, 0, 597, 0, + 0, 229, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 276, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 298, 0, + 403, 258, 0, 454, 0, 0, 0, 630, 0, 0, + 0, 0, 0, 0, 0, 366, 0, 331, 197, 225, + 0, 0, 413, 462, 474, 0, 0, 0, 254, 0, + 472, 427, 605, 233, 285, 459, 433, 470, 441, 288, + 0, 0, 471, 373, 586, 451, 602, 631, 632, 264, + 407, 616, 523, 624, 649, 226, 261, 421, 507, 608, + 495, 398, 582, 583, 330, 494, 296, 201, 370, 637, + 224, 480, 372, 242, 231, 588, 613, 300, 252, 290, + 457, 644, 213, 518, 599, 239, 484, 0, 0, 652, + 247, 505, 611, 600, 215, 595, 504, 394, 327, 328, + 214, 0, 458, 269, 294, 0, 0, 259, 416, 590, + 591, 257, 653, 228, 623, 220, 0, 622, 409, 585, + 596, 395, 384, 219, 594, 393, 383, 335, 356, 357, + 281, 308, 448, 376, 449, 307, 309, 405, 404, 406, + 207, 609, 627, 0, 208, 0, 500, 612, 654, 453, + 212, 234, 235, 237, 0, 280, 284, 292, 295, 304, + 305, 314, 368, 420, 447, 443, 452, 0, 580, 603, + 617, 629, 635, 636, 638, 639, 640, 641, 642, 645, + 643, 408, 312, 496, 334, 374, 0, 0, 426, 473, + 240, 607, 497, 199, 0, 0, 0, 0, 255, 256, + 0, 576, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 655, 656, 657, 658, 659, 660, 661, 662, 663, + 664, 665, 666, 667, 668, 669, 670, 671, 672, 650, + 509, 515, 510, 511, 512, 513, 514, 0, 516, 0, + 0, 0, 0, 0, 399, 0, 592, 593, 673, 385, + 487, 604, 336, 350, 353, 342, 362, 0, 363, 338, + 339, 344, 347, 348, 349, 354, 355, 359, 365, 249, + 210, 391, 400, 579, 313, 216, 217, 218, 525, 526, + 527, 528, 620, 621, 625, 205, 463, 464, 465, 466, + 293, 615, 310, 469, 468, 332, 333, 380, 450, 541, + 543, 554, 558, 560, 562, 568, 571, 542, 544, 555, + 559, 561, 563, 569, 572, 531, 533, 535, 537, 550, + 549, 546, 574, 575, 552, 557, 536, 548, 553, 566, + 573, 570, 530, 534, 538, 547, 565, 564, 545, 556, + 567, 551, 539, 532, 540, 0, 196, 221, 369, 0, + 455, 289, 651, 619, 485, 614, 206, 223, 0, 263, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 198, 200, 209, 222, 232, 236, 243, 262, 277, + 279, 286, 299, 311, 319, 320, 323, 329, 381, 387, + 388, 389, 390, 410, 411, 412, 415, 418, 419, 422, + 424, 425, 428, 432, 436, 437, 438, 440, 442, 444, + 456, 461, 475, 476, 477, 478, 479, 482, 483, 489, + 490, 491, 492, 493, 501, 502, 517, 587, 589, 606, + 626, 633, 481, 302, 303, 445, 446, 315, 316, 647, + 648, 301, 601, 634, 598, 646, 628, 439, 379, 0, + 0, 382, 282, 306, 321, 0, 618, 503, 227, 467, + 291, 251, 0, 0, 211, 246, 230, 260, 275, 278, + 325, 392, 401, 430, 435, 297, 272, 244, 460, 241, + 486, 520, 521, 522, 524, 396, 267, 434, 397, 0, + 377, 577, 578, 317, 0, 0, 0, 529, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 417, + 0, 0, 0, 0, 0, 0, 1785, 271, 0, 0, + 0, 0, 367, 268, 0, 0, 204, 506, 0, 431, + 0, 203, 0, 488, 253, 378, 375, 584, 283, 274, + 270, 250, 318, 386, 429, 519, 423, 0, 371, 0, + 0, 498, 402, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 324, 248, 326, 202, 414, 499, 287, 0, 0, + 0, 0, 0, 508, 194, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 238, 0, 0, 245, 0, 0, + 0, 352, 361, 360, 340, 341, 343, 345, 351, 358, + 364, 337, 346, 0, 0, 610, 0, 0, 0, 266, + 322, 273, 265, 581, 0, 0, 0, 0, 0, 0, + 597, 0, 0, 229, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 276, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 298, 0, 403, 258, 0, 454, 0, 0, 0, 630, + 0, 0, 0, 0, 0, 0, 0, 366, 0, 331, + 197, 225, 0, 0, 413, 462, 474, 0, 0, 0, + 254, 0, 472, 427, 605, 233, 285, 459, 433, 470, + 441, 288, 0, 0, 471, 373, 586, 451, 602, 631, + 632, 264, 407, 616, 523, 624, 649, 226, 261, 421, + 507, 608, 495, 398, 582, 583, 330, 494, 296, 201, + 370, 637, 224, 480, 372, 242, 231, 588, 613, 300, + 252, 290, 457, 644, 213, 518, 599, 239, 484, 0, + 0, 652, 247, 505, 611, 600, 215, 595, 504, 394, + 327, 328, 214, 0, 458, 269, 294, 0, 0, 259, + 416, 590, 591, 257, 653, 228, 623, 220, 0, 622, + 409, 585, 596, 395, 384, 219, 594, 393, 383, 335, + 356, 357, 281, 308, 448, 376, 449, 307, 309, 405, + 404, 406, 207, 609, 627, 0, 208, 0, 500, 612, + 654, 453, 212, 234, 235, 237, 0, 280, 284, 292, + 295, 304, 305, 314, 368, 420, 447, 443, 452, 0, + 580, 603, 617, 629, 635, 636, 638, 639, 640, 641, + 642, 645, 643, 408, 312, 496, 334, 374, 0, 0, + 426, 473, 240, 607, 497, 199, 0, 0, 0, 0, + 255, 256, 0, 576, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 655, 656, 657, 658, 659, 660, 661, + 662, 663, 664, 665, 666, 667, 668, 669, 670, 671, + 672, 650, 509, 515, 510, 511, 512, 513, 514, 0, + 516, 0, 0, 0, 0, 0, 399, 0, 592, 593, + 673, 385, 487, 604, 336, 350, 353, 342, 362, 0, + 363, 338, 339, 344, 347, 348, 349, 354, 355, 359, + 365, 249, 210, 391, 400, 579, 313, 216, 217, 218, + 525, 526, 527, 528, 620, 621, 625, 205, 463, 464, + 465, 466, 293, 615, 310, 469, 468, 332, 333, 380, + 450, 541, 543, 554, 558, 560, 562, 568, 571, 542, + 544, 555, 559, 561, 563, 569, 572, 531, 533, 535, + 537, 550, 549, 546, 574, 575, 552, 557, 536, 548, + 553, 566, 573, 570, 530, 534, 538, 547, 565, 564, + 545, 556, 567, 551, 539, 532, 540, 0, 196, 221, + 369, 0, 455, 289, 651, 619, 485, 614, 206, 223, + 0, 263, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 198, 200, 209, 222, 232, 236, 243, + 262, 277, 279, 286, 299, 311, 319, 320, 323, 329, + 381, 387, 388, 389, 390, 410, 411, 412, 415, 418, + 419, 422, 424, 425, 428, 432, 436, 437, 438, 440, + 442, 444, 456, 461, 475, 476, 477, 478, 479, 482, + 483, 489, 490, 491, 492, 493, 501, 502, 517, 587, + 589, 606, 626, 633, 481, 302, 303, 445, 446, 315, + 316, 647, 648, 301, 601, 634, 598, 646, 628, 439, + 379, 0, 0, 382, 282, 306, 321, 0, 618, 503, + 227, 467, 291, 251, 0, 0, 211, 246, 230, 260, + 275, 278, 325, 392, 401, 430, 435, 297, 272, 244, + 460, 241, 486, 520, 521, 522, 524, 396, 267, 434, + 397, 0, 377, 577, 578, 317, 0, 0, 0, 529, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 417, 0, 0, 0, 0, 0, 0, 0, 271, + 0, 0, 0, 0, 367, 268, 0, 0, 204, 506, + 0, 431, 0, 203, 0, 488, 253, 378, 375, 584, + 283, 274, 270, 250, 318, 386, 429, 519, 423, 0, + 371, 0, 0, 498, 402, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 324, 248, 326, 202, 414, 499, 287, + 0, 95, 0, 0, 0, 508, 960, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 238, 0, 0, 245, + 0, 0, 0, 352, 361, 360, 340, 341, 343, 345, + 351, 358, 364, 337, 346, 0, 0, 610, 0, 0, + 0, 266, 322, 273, 265, 581, 0, 0, 0, 0, + 0, 0, 597, 0, 0, 229, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 276, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 298, 0, 403, 258, 0, 454, 0, 0, + 0, 630, 0, 0, 0, 0, 0, 0, 0, 366, + 0, 331, 197, 225, 0, 0, 413, 462, 474, 0, + 0, 0, 254, 0, 472, 427, 605, 233, 285, 459, + 433, 470, 441, 288, 0, 0, 471, 373, 586, 451, + 602, 631, 632, 264, 407, 616, 523, 624, 649, 226, + 261, 421, 507, 608, 495, 398, 582, 583, 330, 494, + 296, 201, 370, 637, 224, 480, 372, 242, 231, 588, + 613, 300, 252, 290, 457, 644, 213, 518, 599, 239, + 484, 0, 0, 652, 247, 505, 611, 600, 215, 595, + 504, 394, 327, 328, 214, 0, 458, 269, 294, 0, + 0, 259, 416, 590, 591, 257, 653, 228, 623, 220, + 0, 622, 409, 585, 596, 395, 384, 219, 594, 393, + 383, 335, 356, 357, 281, 308, 448, 376, 449, 307, + 309, 405, 404, 406, 207, 609, 627, 0, 208, 0, + 500, 612, 654, 453, 212, 234, 235, 237, 0, 280, + 284, 292, 295, 304, 305, 314, 368, 420, 447, 443, + 452, 0, 580, 603, 617, 629, 635, 636, 638, 639, + 640, 641, 642, 645, 643, 408, 312, 496, 334, 374, + 0, 0, 426, 473, 240, 607, 497, 199, 0, 0, + 0, 0, 255, 256, 0, 576, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 655, 656, 657, 658, 659, + 660, 661, 662, 663, 664, 665, 666, 667, 668, 669, + 670, 671, 672, 650, 509, 515, 510, 511, 512, 513, + 514, 0, 516, 0, 0, 0, 0, 0, 399, 0, + 592, 593, 673, 385, 487, 604, 336, 350, 353, 342, + 362, 0, 363, 338, 339, 344, 347, 348, 349, 354, + 355, 359, 365, 249, 210, 391, 400, 579, 313, 216, + 217, 218, 525, 526, 527, 528, 620, 621, 625, 205, + 463, 464, 465, 466, 293, 615, 310, 469, 468, 332, + 333, 380, 450, 541, 543, 554, 558, 560, 562, 568, + 571, 542, 544, 555, 559, 561, 563, 569, 572, 531, + 533, 535, 537, 550, 549, 546, 574, 575, 552, 557, + 536, 548, 553, 566, 573, 570, 530, 534, 538, 547, + 565, 564, 545, 556, 567, 551, 539, 532, 540, 0, + 196, 221, 369, 0, 455, 289, 651, 619, 485, 614, + 206, 223, 0, 263, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 198, 200, 209, 222, 232, + 236, 243, 262, 277, 279, 286, 299, 311, 319, 320, + 323, 329, 381, 387, 388, 389, 390, 410, 411, 412, + 415, 418, 419, 422, 424, 425, 428, 432, 436, 437, + 438, 440, 442, 444, 456, 461, 475, 476, 477, 478, + 479, 482, 483, 489, 490, 491, 492, 493, 501, 502, + 517, 587, 589, 606, 626, 633, 481, 302, 303, 445, + 446, 315, 316, 647, 648, 301, 601, 634, 598, 646, + 628, 439, 379, 0, 0, 382, 282, 306, 321, 0, + 618, 503, 227, 467, 291, 251, 0, 0, 211, 246, + 230, 260, 275, 278, 325, 392, 401, 430, 435, 297, + 272, 244, 460, 241, 486, 520, 521, 522, 524, 396, + 267, 434, 397, 0, 377, 577, 578, 317, 0, 0, + 0, 529, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 417, 0, 0, 0, 0, 0, 0, + 0, 271, 0, 0, 0, 0, 367, 268, 0, 0, + 204, 506, 0, 431, 0, 203, 0, 488, 253, 378, + 375, 584, 283, 274, 270, 250, 318, 386, 429, 519, + 423, 0, 371, 0, 0, 498, 402, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 324, 248, 326, 202, 414, + 499, 287, 0, 0, 0, 0, 0, 508, 194, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 238, 0, + 0, 245, 0, 0, 0, 352, 361, 360, 340, 341, + 343, 345, 351, 358, 364, 337, 346, 0, 0, 610, + 0, 0, 0, 266, 322, 273, 265, 581, 0, 0, + 0, 0, 0, 0, 597, 0, 0, 229, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 276, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1457, 0, 298, 0, 403, 258, 0, 454, + 0, 0, 0, 630, 0, 0, 0, 0, 0, 0, + 0, 366, 0, 331, 197, 225, 0, 0, 413, 462, + 474, 0, 0, 0, 254, 0, 472, 427, 605, 233, + 285, 459, 433, 470, 441, 288, 0, 0, 471, 373, + 586, 451, 602, 631, 632, 264, 407, 616, 523, 624, + 649, 226, 261, 421, 507, 608, 495, 398, 582, 583, + 330, 494, 296, 201, 370, 637, 224, 480, 372, 242, + 231, 588, 613, 300, 252, 290, 457, 644, 213, 518, + 599, 239, 484, 0, 0, 652, 247, 505, 611, 600, + 215, 595, 504, 394, 327, 328, 214, 0, 458, 269, + 294, 0, 0, 259, 416, 590, 591, 257, 653, 228, + 623, 220, 0, 622, 409, 585, 596, 395, 384, 219, + 594, 393, 383, 335, 356, 357, 281, 308, 448, 376, + 449, 307, 309, 405, 404, 406, 207, 609, 627, 0, + 208, 0, 500, 612, 654, 453, 212, 234, 235, 237, + 0, 280, 284, 292, 295, 304, 305, 314, 368, 420, + 447, 443, 452, 0, 580, 603, 617, 629, 635, 636, + 638, 639, 640, 641, 642, 645, 643, 408, 312, 496, + 334, 374, 0, 0, 426, 473, 240, 607, 497, 199, + 0, 0, 0, 0, 255, 256, 0, 576, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 655, 656, 657, + 658, 659, 660, 661, 662, 663, 664, 665, 666, 667, + 668, 669, 670, 671, 672, 650, 509, 515, 510, 511, + 512, 513, 514, 0, 516, 0, 0, 0, 0, 0, + 399, 0, 592, 593, 673, 385, 487, 604, 336, 350, + 353, 342, 362, 0, 363, 338, 339, 344, 347, 348, + 349, 354, 355, 359, 365, 249, 210, 391, 400, 579, + 313, 216, 217, 218, 525, 526, 527, 528, 620, 621, + 625, 205, 463, 464, 465, 466, 293, 615, 310, 469, + 468, 332, 333, 380, 450, 541, 543, 554, 558, 560, + 562, 568, 571, 542, 544, 555, 559, 561, 563, 569, + 572, 531, 533, 535, 537, 550, 549, 546, 574, 575, + 552, 557, 536, 548, 553, 566, 573, 570, 530, 534, + 538, 547, 565, 564, 545, 556, 567, 551, 539, 532, + 540, 0, 196, 221, 369, 0, 455, 289, 651, 619, + 485, 614, 206, 223, 0, 263, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 198, 200, 209, + 222, 232, 236, 243, 262, 277, 279, 286, 299, 311, + 319, 320, 323, 329, 381, 387, 388, 389, 390, 410, + 411, 412, 415, 418, 419, 422, 424, 425, 428, 432, + 436, 437, 438, 440, 442, 444, 456, 461, 475, 476, + 477, 478, 479, 482, 483, 489, 490, 491, 492, 493, + 501, 502, 517, 587, 589, 606, 626, 633, 481, 302, + 303, 445, 446, 315, 316, 647, 648, 1456, 601, 634, + 598, 646, 628, 439, 379, 0, 0, 382, 282, 306, + 321, 0, 618, 503, 227, 467, 291, 251, 0, 0, + 211, 246, 230, 260, 275, 278, 325, 392, 401, 430, + 435, 297, 272, 244, 460, 241, 486, 520, 521, 522, + 524, 396, 267, 434, 397, 0, 377, 577, 578, 317, + 0, 0, 0, 529, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 417, 0, 0, 0, 0, + 0, 0, 0, 271, 0, 0, 0, 0, 367, 268, + 0, 0, 204, 506, 0, 431, 0, 203, 0, 488, + 253, 378, 375, 584, 283, 274, 270, 250, 318, 386, + 429, 519, 423, 0, 371, 0, 0, 498, 402, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 324, 248, 326, + 202, 414, 499, 287, 0, 0, 0, 0, 0, 508, + 194, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 238, 0, 0, 245, 0, 0, 0, 352, 361, 360, + 340, 341, 343, 345, 351, 358, 364, 337, 346, 0, + 0, 610, 0, 0, 0, 266, 322, 273, 265, 581, + 0, 0, 0, 0, 0, 0, 597, 0, 0, 229, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 276, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 298, 0, 403, 258, + 0, 454, 0, 0, 0, 630, 0, 0, 0, 0, + 0, 0, 0, 366, 0, 331, 197, 225, 0, 0, + 413, 462, 474, 0, 0, 0, 254, 0, 472, 427, + 605, 233, 285, 459, 433, 470, 441, 288, 0, 0, + 471, 373, 586, 451, 602, 631, 632, 264, 407, 616, + 523, 624, 649, 226, 261, 421, 507, 608, 495, 398, + 582, 583, 330, 494, 296, 201, 370, 637, 224, 480, + 372, 242, 231, 588, 613, 300, 252, 290, 457, 644, + 213, 518, 599, 239, 484, 0, 0, 652, 247, 505, + 611, 600, 215, 595, 504, 394, 327, 328, 214, 0, + 458, 269, 294, 0, 0, 259, 416, 590, 591, 257, + 653, 228, 623, 220, 0, 622, 409, 585, 596, 395, + 384, 219, 594, 393, 383, 335, 356, 357, 281, 308, + 448, 376, 449, 307, 309, 405, 404, 406, 207, 609, + 627, 0, 208, 0, 500, 612, 654, 453, 212, 234, + 235, 237, 0, 280, 284, 292, 295, 304, 305, 314, + 368, 420, 447, 443, 452, 0, 580, 603, 617, 629, + 635, 636, 638, 639, 640, 641, 642, 645, 643, 408, + 312, 496, 334, 374, 0, 0, 426, 473, 240, 607, + 497, 199, 0, 0, 0, 0, 255, 256, 0, 576, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 655, + 656, 657, 658, 659, 660, 661, 662, 663, 664, 665, + 666, 667, 668, 669, 670, 671, 672, 650, 509, 515, + 510, 511, 512, 513, 514, 0, 516, 0, 0, 0, + 0, 0, 399, 0, 592, 593, 673, 385, 487, 604, + 336, 350, 353, 342, 362, 0, 363, 338, 339, 344, + 347, 348, 349, 354, 355, 359, 365, 249, 210, 391, + 400, 579, 313, 216, 217, 218, 525, 526, 527, 528, + 620, 621, 625, 205, 463, 464, 465, 466, 293, 615, + 310, 469, 468, 332, 333, 380, 450, 541, 543, 554, + 558, 560, 562, 568, 571, 542, 544, 555, 559, 561, + 563, 569, 572, 531, 533, 535, 537, 550, 549, 546, + 574, 575, 552, 557, 536, 548, 553, 566, 573, 570, + 530, 534, 538, 547, 565, 564, 545, 556, 567, 551, + 539, 532, 540, 0, 196, 221, 369, 0, 455, 289, + 651, 619, 485, 614, 206, 223, 0, 263, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1050, 0, 0, 0, 198, + 200, 209, 222, 232, 236, 243, 262, 277, 279, 286, + 299, 311, 319, 320, 323, 329, 381, 387, 388, 389, + 390, 410, 411, 412, 415, 418, 419, 422, 424, 425, + 428, 432, 436, 437, 438, 440, 442, 444, 456, 461, + 475, 476, 477, 478, 479, 482, 483, 489, 490, 491, + 492, 493, 501, 502, 517, 587, 589, 606, 626, 633, + 481, 302, 303, 445, 446, 315, 316, 647, 648, 301, + 601, 634, 598, 646, 628, 439, 379, 0, 0, 382, + 282, 306, 321, 0, 618, 503, 227, 467, 291, 251, + 0, 0, 211, 246, 230, 260, 275, 278, 325, 392, + 401, 430, 435, 297, 272, 244, 460, 241, 486, 520, + 521, 522, 524, 396, 267, 434, 397, 0, 377, 577, + 578, 317, 0, 0, 0, 529, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 417, 0, 0, + 0, 0, 0, 0, 0, 271, 0, 0, 0, 0, + 367, 268, 0, 0, 204, 506, 0, 431, 0, 203, + 0, 488, 253, 378, 375, 584, 283, 274, 270, 250, + 318, 386, 429, 519, 423, 0, 371, 0, 0, 498, + 402, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 324, + 248, 326, 202, 414, 499, 287, 0, 0, 0, 0, + 0, 508, 194, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 238, 0, 0, 245, 0, 0, 0, 352, + 361, 360, 340, 341, 343, 345, 351, 358, 364, 337, + 346, 0, 0, 610, 0, 0, 0, 266, 322, 273, + 265, 581, 0, 0, 0, 0, 0, 0, 597, 0, + 0, 229, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 276, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 298, 0, + 403, 258, 0, 454, 0, 676, 0, 630, 0, 0, + 0, 0, 0, 0, 0, 366, 0, 331, 197, 225, + 0, 0, 413, 462, 474, 0, 0, 0, 254, 0, + 472, 427, 605, 233, 285, 459, 433, 470, 441, 288, + 0, 0, 471, 373, 586, 451, 602, 631, 632, 264, + 407, 616, 523, 624, 649, 226, 261, 421, 507, 608, + 495, 398, 582, 583, 330, 494, 296, 201, 370, 637, + 224, 480, 372, 242, 231, 588, 613, 300, 252, 290, + 457, 644, 213, 518, 599, 239, 484, 0, 0, 652, + 247, 505, 611, 600, 215, 595, 504, 394, 327, 328, + 214, 0, 458, 269, 294, 0, 0, 259, 416, 590, + 591, 257, 653, 228, 623, 220, 0, 622, 409, 585, + 596, 395, 384, 219, 594, 393, 383, 335, 356, 357, + 281, 308, 448, 376, 449, 307, 309, 405, 404, 406, + 207, 609, 627, 0, 208, 0, 500, 612, 654, 453, + 212, 234, 235, 237, 0, 280, 284, 292, 295, 304, + 305, 314, 368, 420, 447, 443, 452, 0, 580, 603, + 617, 629, 635, 636, 638, 639, 640, 641, 642, 645, + 643, 408, 312, 496, 334, 374, 0, 0, 426, 473, + 240, 607, 497, 199, 0, 0, 0, 0, 255, 256, + 0, 576, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 655, 656, 657, 658, 659, 660, 661, 662, 663, + 664, 665, 666, 667, 668, 669, 670, 671, 672, 650, + 509, 515, 510, 511, 512, 513, 514, 0, 516, 0, + 0, 0, 0, 0, 399, 0, 592, 593, 673, 385, + 487, 604, 336, 350, 353, 342, 362, 0, 363, 338, + 339, 344, 347, 348, 349, 354, 355, 359, 365, 249, + 210, 391, 400, 579, 313, 216, 217, 218, 525, 526, + 527, 528, 620, 621, 625, 205, 463, 464, 465, 466, + 293, 615, 310, 469, 468, 332, 333, 380, 450, 541, + 543, 554, 558, 560, 562, 568, 571, 542, 544, 555, + 559, 561, 563, 569, 572, 531, 533, 535, 537, 550, + 549, 546, 574, 575, 552, 557, 536, 548, 553, 566, + 573, 570, 530, 534, 538, 547, 565, 564, 545, 556, + 567, 551, 539, 532, 540, 0, 196, 221, 369, 0, + 455, 289, 651, 619, 485, 614, 206, 223, 0, 263, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 198, 200, 209, 222, 232, 236, 243, 262, 277, + 279, 286, 299, 311, 319, 320, 323, 329, 381, 387, + 388, 389, 390, 410, 411, 412, 415, 418, 419, 422, + 424, 425, 428, 432, 436, 437, 438, 440, 442, 444, + 456, 461, 475, 476, 477, 478, 479, 482, 483, 489, + 490, 491, 492, 493, 501, 502, 517, 587, 589, 606, + 626, 633, 481, 302, 303, 445, 446, 315, 316, 647, + 648, 301, 601, 634, 598, 646, 628, 439, 379, 0, + 0, 382, 282, 306, 321, 0, 618, 503, 227, 467, + 291, 251, 0, 0, 211, 246, 230, 260, 275, 278, + 325, 392, 401, 430, 435, 297, 272, 244, 460, 241, + 486, 520, 521, 522, 524, 396, 267, 434, 397, 0, + 377, 577, 578, 317, 0, 0, 0, 529, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 417, + 0, 0, 0, 0, 0, 0, 0, 271, 0, 0, + 0, 0, 367, 268, 0, 0, 204, 506, 0, 431, + 0, 203, 0, 488, 253, 378, 375, 584, 283, 274, + 270, 250, 318, 386, 429, 519, 423, 0, 371, 0, + 0, 498, 402, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 324, 248, 326, 202, 414, 499, 287, 0, 0, + 0, 0, 0, 508, 725, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 238, 0, 0, 245, 0, 0, + 0, 352, 361, 360, 340, 341, 343, 345, 351, 358, + 364, 337, 346, 0, 0, 610, 0, 0, 0, 266, + 322, 273, 265, 581, 0, 0, 0, 0, 0, 0, + 597, 0, 0, 229, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 276, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 298, 0, 403, 258, 0, 454, 0, 0, 0, 630, + 0, 0, 0, 0, 0, 0, 0, 366, 0, 331, + 197, 225, 0, 0, 413, 462, 474, 0, 0, 0, + 254, 0, 472, 427, 605, 233, 285, 459, 433, 470, + 441, 288, 0, 0, 471, 373, 586, 451, 602, 631, + 632, 264, 407, 616, 523, 624, 649, 226, 261, 421, + 507, 608, 495, 398, 582, 583, 330, 494, 296, 201, + 370, 637, 224, 480, 372, 242, 231, 588, 613, 300, + 252, 290, 457, 644, 213, 518, 599, 239, 484, 0, + 0, 652, 247, 505, 611, 600, 215, 595, 504, 394, + 327, 328, 214, 0, 458, 269, 294, 0, 0, 259, + 416, 590, 591, 257, 653, 228, 623, 220, 0, 622, + 409, 585, 596, 395, 384, 219, 594, 393, 383, 335, + 356, 357, 281, 308, 448, 376, 449, 307, 309, 405, + 404, 406, 207, 609, 627, 0, 208, 0, 500, 612, + 654, 453, 212, 234, 235, 237, 0, 280, 284, 292, + 295, 304, 305, 314, 368, 420, 447, 443, 452, 0, + 580, 603, 617, 629, 635, 636, 638, 639, 640, 641, + 642, 645, 643, 408, 312, 496, 334, 374, 0, 0, + 426, 473, 240, 607, 497, 199, 0, 0, 0, 0, + 255, 256, 0, 576, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 655, 656, 657, 658, 659, 660, 661, + 662, 663, 664, 665, 666, 667, 668, 669, 670, 671, + 672, 650, 509, 515, 510, 511, 512, 513, 514, 0, + 516, 0, 0, 0, 0, 0, 399, 0, 592, 593, + 673, 385, 487, 604, 336, 350, 353, 342, 362, 0, + 363, 338, 339, 344, 347, 348, 349, 354, 355, 359, + 365, 249, 210, 391, 400, 579, 313, 216, 217, 218, + 525, 526, 527, 528, 620, 621, 625, 205, 463, 464, + 465, 466, 293, 615, 310, 469, 468, 332, 333, 380, + 450, 541, 543, 554, 558, 560, 562, 568, 571, 542, + 544, 555, 559, 561, 563, 569, 572, 531, 533, 535, + 537, 550, 549, 546, 574, 575, 552, 557, 536, 548, + 553, 566, 573, 570, 530, 534, 538, 547, 565, 564, + 545, 556, 567, 551, 539, 532, 540, 0, 196, 221, + 369, 0, 455, 289, 651, 619, 485, 614, 206, 223, + 0, 263, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 198, 200, 209, 222, 232, 236, 243, + 262, 277, 279, 286, 299, 311, 319, 320, 323, 329, + 381, 387, 388, 389, 390, 4139, 411, 412, 415, 418, + 419, 422, 424, 425, 428, 432, 436, 437, 438, 440, + 442, 444, 456, 461, 475, 476, 477, 478, 479, 482, + 483, 489, 490, 491, 492, 493, 501, 502, 517, 587, + 589, 606, 626, 633, 481, 302, 303, 445, 446, 315, + 316, 647, 648, 301, 601, 634, 598, 646, 628, 439, + 379, 0, 0, 382, 282, 306, 321, 0, 618, 503, + 227, 467, 291, 251, 0, 0, 211, 246, 230, 260, + 275, 278, 325, 392, 401, 430, 435, 297, 272, 244, + 460, 241, 486, 520, 521, 522, 524, 396, 267, 434, + 397, 0, 377, 577, 578, 317, 0, 0, 0, 529, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 417, 0, 0, 0, 0, 0, 0, 0, 271, + 0, 0, 0, 0, 367, 268, 0, 0, 204, 506, + 0, 431, 0, 203, 0, 488, 253, 378, 375, 584, + 283, 274, 270, 250, 318, 386, 429, 519, 423, 0, + 371, 0, 0, 498, 402, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 324, 248, 326, 202, 414, 499, 287, + 0, 0, 0, 0, 0, 508, 725, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 238, 0, 0, 245, + 0, 0, 0, 352, 361, 360, 340, 341, 343, 345, + 351, 358, 364, 337, 346, 0, 0, 610, 0, 0, + 0, 266, 322, 273, 265, 581, 0, 0, 0, 0, + 0, 0, 597, 0, 0, 229, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 276, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 298, 0, 403, 258, 0, 454, 0, 0, + 0, 630, 0, 0, 0, 0, 0, 0, 0, 366, + 0, 331, 197, 225, 0, 0, 413, 462, 474, 0, + 0, 0, 254, 0, 472, 427, 605, 233, 285, 459, + 433, 470, 441, 288, 0, 0, 471, 373, 586, 451, + 602, 631, 632, 264, 407, 616, 523, 624, 649, 226, + 261, 421, 507, 608, 495, 398, 582, 583, 330, 494, + 296, 201, 370, 637, 224, 480, 372, 242, 231, 588, + 613, 300, 252, 290, 457, 644, 213, 518, 599, 239, + 484, 0, 0, 652, 247, 505, 611, 600, 215, 595, + 504, 394, 327, 328, 214, 0, 458, 269, 294, 0, + 0, 259, 416, 590, 591, 257, 653, 228, 623, 220, + 0, 622, 409, 585, 596, 395, 384, 219, 594, 393, + 383, 335, 356, 357, 281, 308, 448, 376, 449, 307, + 309, 405, 404, 406, 207, 609, 627, 0, 208, 0, + 500, 612, 654, 453, 212, 234, 235, 237, 0, 280, + 284, 292, 295, 304, 305, 314, 368, 420, 447, 443, + 452, 0, 580, 603, 617, 629, 635, 636, 638, 639, + 640, 641, 642, 645, 643, 408, 312, 496, 334, 374, + 0, 0, 426, 473, 240, 607, 497, 199, 0, 0, + 0, 0, 255, 256, 0, 576, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 655, 656, 657, 658, 659, + 660, 661, 662, 663, 664, 665, 666, 667, 668, 669, + 670, 671, 672, 650, 509, 515, 510, 511, 512, 513, + 514, 0, 516, 0, 0, 0, 0, 0, 399, 0, + 592, 593, 673, 385, 487, 604, 336, 350, 353, 342, + 362, 0, 363, 338, 339, 344, 347, 348, 349, 354, + 355, 359, 365, 249, 210, 391, 400, 579, 313, 216, + 217, 218, 525, 526, 527, 528, 620, 621, 625, 205, + 463, 464, 465, 466, 293, 615, 310, 469, 468, 332, + 333, 380, 450, 541, 543, 554, 558, 560, 562, 568, + 571, 542, 544, 555, 559, 561, 563, 569, 572, 531, + 533, 535, 537, 550, 549, 546, 574, 575, 552, 557, + 536, 548, 553, 566, 573, 570, 530, 534, 538, 547, + 565, 564, 545, 556, 567, 551, 539, 532, 540, 0, + 196, 221, 369, 0, 455, 289, 651, 619, 485, 614, + 206, 223, 0, 263, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 198, 200, 209, 222, 232, + 236, 243, 262, 277, 279, 286, 299, 311, 319, 320, + 323, 329, 381, 387, 388, 389, 390, 410, 411, 412, + 415, 418, 419, 422, 424, 425, 428, 432, 436, 437, + 438, 440, 442, 444, 456, 461, 475, 476, 477, 478, + 479, 482, 483, 489, 490, 491, 492, 493, 501, 502, + 517, 587, 589, 606, 626, 633, 481, 302, 303, 445, + 446, 315, 316, 647, 648, 301, 601, 634, 598, 646, + 628, 439, 379, 0, 0, 382, 282, 306, 321, 0, + 618, 503, 227, 467, 291, 251, 0, 0, 211, 246, + 230, 260, 275, 278, 325, 392, 401, 430, 435, 297, + 272, 244, 460, 241, 486, 520, 521, 522, 524, 396, + 267, 434, 397, 0, 377, 577, 578, 317, 0, 0, + 0, 529, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 417, 0, 0, 0, 0, 0, 0, + 0, 271, 0, 0, 0, 0, 367, 268, 0, 0, + 204, 506, 0, 431, 0, 203, 0, 488, 253, 378, + 375, 584, 283, 274, 270, 250, 318, 386, 429, 519, + 423, 0, 371, 0, 0, 498, 402, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 324, 248, 326, 202, 414, + 499, 287, 0, 0, 0, 0, 0, 508, 960, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 238, 0, + 0, 245, 0, 0, 0, 352, 361, 360, 340, 341, + 343, 345, 351, 358, 364, 337, 346, 0, 0, 610, + 0, 0, 0, 266, 322, 273, 265, 581, 0, 0, + 0, 0, 0, 0, 597, 0, 0, 229, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 276, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 298, 0, 403, 258, 0, 454, + 0, 0, 0, 630, 0, 0, 0, 0, 0, 0, + 0, 366, 0, 331, 197, 225, 0, 0, 413, 462, + 474, 0, 0, 0, 254, 0, 472, 427, 605, 233, + 285, 459, 433, 470, 441, 288, 0, 0, 471, 373, + 586, 451, 602, 631, 632, 264, 407, 616, 523, 624, + 649, 226, 261, 421, 507, 608, 495, 398, 582, 583, + 330, 494, 296, 201, 370, 637, 224, 480, 372, 242, + 231, 588, 613, 300, 252, 290, 457, 644, 213, 518, + 599, 239, 484, 0, 0, 652, 247, 505, 611, 600, + 215, 595, 504, 394, 327, 328, 214, 0, 458, 269, + 294, 0, 0, 259, 416, 590, 591, 257, 653, 228, + 623, 220, 0, 622, 409, 585, 596, 395, 384, 219, + 594, 393, 383, 335, 356, 357, 281, 308, 448, 376, + 449, 307, 309, 405, 404, 406, 207, 609, 627, 0, + 208, 0, 500, 612, 654, 453, 212, 234, 235, 237, + 0, 280, 284, 292, 295, 304, 305, 314, 368, 420, + 447, 443, 452, 0, 580, 603, 617, 629, 635, 636, + 638, 639, 640, 641, 642, 645, 643, 408, 312, 496, + 334, 374, 0, 0, 426, 473, 240, 607, 497, 199, + 0, 0, 0, 0, 255, 256, 0, 576, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 655, 656, 657, + 658, 659, 660, 661, 662, 663, 664, 665, 666, 667, + 668, 669, 670, 671, 672, 650, 509, 515, 510, 511, + 512, 513, 514, 0, 516, 0, 0, 0, 0, 0, + 399, 0, 592, 593, 673, 385, 487, 604, 336, 350, + 353, 342, 362, 0, 363, 338, 339, 344, 347, 348, + 349, 354, 355, 359, 365, 249, 210, 391, 400, 579, + 313, 216, 217, 218, 525, 526, 527, 528, 620, 621, + 625, 205, 463, 464, 465, 466, 293, 615, 310, 469, + 468, 332, 333, 380, 450, 541, 543, 554, 558, 560, + 562, 568, 571, 542, 544, 555, 559, 561, 563, 569, + 572, 531, 533, 535, 537, 550, 549, 546, 574, 575, + 552, 557, 536, 548, 553, 566, 573, 570, 530, 534, + 538, 547, 565, 564, 545, 556, 567, 551, 539, 532, + 540, 0, 196, 221, 369, 0, 455, 289, 651, 619, + 485, 614, 206, 223, 0, 263, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 198, 200, 209, + 222, 232, 236, 243, 262, 277, 279, 286, 299, 311, + 319, 320, 323, 329, 381, 387, 388, 389, 390, 410, + 411, 412, 415, 418, 419, 422, 424, 425, 428, 432, + 436, 437, 438, 440, 442, 444, 456, 461, 475, 476, + 477, 478, 479, 482, 483, 489, 490, 491, 492, 493, + 501, 502, 517, 587, 589, 606, 626, 633, 481, 302, + 303, 445, 446, 315, 316, 647, 648, 301, 601, 634, + 598, 646, 628, 439, 379, 0, 0, 382, 282, 306, + 321, 0, 618, 503, 227, 467, 291, 251, 0, 0, + 211, 246, 230, 260, 275, 278, 325, 392, 401, 430, + 435, 297, 272, 244, 460, 241, 486, 520, 521, 522, + 524, 396, 267, 434, 397, 0, 377, 577, 578, 317, + 0, 0, 0, 529, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 417, 0, 0, 0, 0, + 0, 0, 0, 271, 0, 0, 0, 0, 367, 268, + 0, 0, 204, 506, 0, 431, 0, 203, 0, 488, + 253, 378, 375, 584, 283, 274, 270, 250, 318, 386, + 429, 519, 423, 0, 371, 0, 0, 498, 402, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 324, 248, 326, + 202, 414, 499, 287, 0, 0, 0, 0, 0, 508, + 194, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 238, 0, 0, 245, 0, 0, 0, 352, 361, 360, + 340, 341, 343, 345, 351, 358, 364, 337, 346, 0, + 0, 610, 0, 0, 0, 266, 322, 273, 265, 581, + 0, 0, 0, 0, 0, 0, 597, 0, 0, 229, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 276, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 298, 0, 403, 258, + 0, 454, 0, 0, 0, 630, 0, 0, 0, 0, + 0, 0, 0, 366, 0, 331, 197, 225, 0, 0, + 413, 462, 474, 0, 0, 0, 254, 0, 472, 427, + 605, 233, 285, 459, 433, 470, 441, 288, 0, 0, + 471, 373, 586, 451, 602, 631, 632, 264, 407, 616, + 523, 624, 649, 226, 261, 421, 507, 608, 495, 398, + 582, 583, 330, 494, 296, 201, 370, 637, 224, 480, + 372, 242, 231, 588, 613, 300, 252, 290, 457, 644, + 213, 518, 599, 239, 484, 0, 0, 652, 247, 505, + 611, 600, 215, 595, 504, 394, 327, 328, 214, 0, + 458, 269, 294, 0, 0, 259, 416, 590, 591, 257, + 653, 228, 623, 220, 0, 622, 409, 585, 596, 395, + 384, 219, 594, 393, 383, 335, 356, 357, 281, 308, + 448, 376, 449, 307, 309, 405, 404, 406, 207, 609, + 627, 0, 208, 0, 500, 612, 654, 453, 212, 234, + 235, 237, 0, 280, 284, 292, 295, 304, 305, 314, + 368, 420, 447, 443, 452, 0, 580, 603, 617, 629, + 635, 636, 638, 639, 640, 641, 642, 645, 643, 408, + 312, 496, 334, 374, 0, 0, 426, 473, 240, 607, + 497, 199, 0, 0, 0, 0, 255, 256, 0, 576, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 655, + 656, 657, 658, 659, 660, 661, 662, 663, 664, 665, + 666, 667, 668, 669, 670, 671, 672, 650, 509, 515, + 510, 511, 512, 513, 514, 0, 516, 0, 0, 0, + 0, 0, 399, 0, 592, 593, 673, 385, 487, 604, + 336, 350, 353, 342, 362, 0, 363, 338, 339, 344, + 347, 348, 349, 354, 355, 359, 365, 249, 210, 391, + 400, 579, 313, 216, 217, 218, 525, 526, 527, 528, + 620, 621, 625, 205, 463, 464, 465, 466, 293, 615, + 310, 469, 468, 332, 333, 380, 450, 541, 543, 554, + 558, 560, 562, 568, 571, 542, 544, 555, 559, 561, + 563, 569, 572, 531, 533, 535, 537, 550, 549, 546, + 574, 575, 552, 557, 536, 548, 553, 566, 573, 570, + 530, 534, 538, 547, 565, 564, 545, 556, 567, 551, + 539, 532, 540, 0, 196, 221, 369, 0, 455, 289, + 651, 619, 485, 614, 206, 223, 0, 263, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 198, + 200, 209, 222, 232, 236, 243, 262, 277, 279, 286, + 299, 311, 319, 320, 323, 329, 381, 387, 388, 389, + 390, 410, 411, 412, 415, 418, 419, 422, 424, 425, + 428, 432, 436, 437, 438, 440, 442, 444, 456, 461, + 475, 476, 477, 478, 479, 482, 483, 489, 490, 491, + 492, 493, 501, 502, 517, 587, 589, 606, 626, 633, + 481, 302, 303, 445, 446, 315, 316, 647, 648, 301, + 601, 634, 598, 646, 628, 439, 379, 0, 0, 382, + 282, 306, 321, 0, 618, 503, 227, 467, 291, 251, + 0, 0, 211, 246, 230, 260, 275, 278, 325, 392, + 401, 430, 435, 297, 272, 244, 460, 241, 486, 520, + 521, 522, 524, 396, 267, 434, 0, 0, 377, 577, + 578, 317, +} + +var yyPact = [...]int{ + -1000, -1000, 4350, -1000, -549, -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, 2520, 2555, -1000, -1000, -1000, -1000, 2696, -1000, 1031, + 2117, -1000, 2474, 4989, -1000, 56250, 778, -1000, 53282, -420, + 897, 238, 36958, -1000, 193, -1000, 186, 54766, 189, -1000, + -1000, -1000, -1000, -420, 22860, 2334, 57, 55, 56250, -1000, + -1000, -1000, -1000, -373, 2641, 2087, -1000, 392, -1000, -1000, + -1000, -1000, -1000, -1000, 52540, -1000, 1119, -1000, -1000, 2482, + 2485, 2309, 941, 2399, -1000, 2569, 2087, -1000, 22860, 2632, + 2475, 22118, 22118, 456, -1000, -1000, 269, -1000, -1000, 31764, + 56250, 39926, 322, -1000, 2474, -1000, -1000, -1000, 211, -1000, + 342, 1987, -1000, 1971, -1000, 937, 914, 389, 473, 471, + 386, 385, 384, 368, 367, 363, 358, 355, 399, -1000, + 958, 958, -218, -220, 371, 769, 460, 460, 1035, 490, + 2427, 2379, -1000, -1000, 958, 958, 958, 339, 958, 958, + 958, 958, 311, 310, 958, 958, 958, 958, 958, 958, + 958, 958, 958, 958, 958, 958, 958, 958, 958, 958, + 958, 917, 2474, 270, -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, - -1000, 2636, 2900, -1000, -1000, -1000, -1000, 2720, -1000, 1025, - 2152, -1000, 2517, 5041, -1000, 55856, 753, -1000, 52892, -454, - 877, 238, 36590, -1000, 177, -1000, 165, 54374, 169, -1000, - -1000, -1000, -1000, -454, 22511, 2360, 38, 37, 55856, -1000, - -1000, -1000, -1000, -373, 2674, 2129, -1000, 380, -1000, -1000, - -1000, -1000, -1000, -1000, 52151, -1000, 1229, -1000, -1000, 2522, - 2491, 2370, 943, 2372, -1000, 2590, 2129, -1000, 22511, 2666, - 2439, 21770, 21770, 467, -1000, -1000, 273, -1000, -1000, 31403, - 55856, 39554, 897, -1000, 2517, -1000, -1000, -1000, 212, -1000, - 349, 2043, -1000, 2039, -1000, 942, 1017, 408, 473, 466, - 405, 404, 394, 391, 389, 388, 386, 381, 385, -1000, - 974, 974, -236, -241, 1021, 454, 461, 461, 1048, 496, - 2473, 2463, -1000, -1000, 974, 974, 974, 360, 974, 974, - 974, 974, 303, 302, 974, 974, 974, 974, 974, 974, - 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, - 974, 902, 2517, 266, -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, @@ -7425,70 +7470,70 @@ 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, -1000, -1000, 56250, 194, 56250, -1000, 828, 775, + -1000, -1000, -459, 1114, 1114, 101, 1114, 1114, 1114, 1114, + 183, 1043, 54, -1000, 182, 261, 168, 271, 1096, 304, + -1000, -1000, 257, 1096, 1834, -1000, 946, 266, 169, -1000, + 1114, 1114, -1000, 15416, 203, 15416, 15416, 265, 172, -1000, + 2464, -1000, -1000, -1000, -1000, -1000, 1344, -1000, -1000, -1000, + -1000, -37, 489, -1000, -1000, -1000, -1000, 54766, 51798, 283, + -1000, -1000, 780, 1851, 1393, 22860, 1315, 938, -1000, -1000, + 1434, 900, -1000, -1000, -1000, -1000, -1000, 809, -1000, 25086, + 25086, 25086, 25086, -1000, -1000, 1830, 51056, 1830, 1830, 25086, + 1830, 25086, 1830, 1830, 1830, 1830, 22860, 1830, 1830, 1830, + 1830, -1000, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, + 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, + 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, + 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, + 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, + 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, + 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, + 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, + 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, + 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, + 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, + 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, + 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, + 1830, -1000, -1000, -1000, -1000, 1830, 826, 1830, 1830, 1830, + 1830, 1830, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 1830, + 1830, 1830, 1830, 1830, 1830, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, 1830, 1830, 1830, 1830, 1830, 1830, + 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, -1000, -1000, + -1000, 1692, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 1693, + 1513, 1500, 1498, -1000, 19892, 1830, -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, -1000, 55856, 234, 55856, -1000, 821, 730, -1000, - -1000, -458, 1154, 1154, 80, 1154, 1154, 1154, 1154, 164, - 1008, 33, -1000, 160, 271, 149, 261, 1096, 798, -1000, - -1000, 252, 1096, 1852, -1000, 949, 259, 155, -1000, 1154, - 1154, -1000, 15077, 220, 15077, 15077, 256, 154, -1000, 2494, - -1000, -1000, -1000, -1000, -1000, 1381, -1000, -1000, -1000, -1000, - -36, 489, -1000, -1000, -1000, -1000, 54374, 51410, 290, -1000, - -1000, 30, 1958, 1395, 22511, 1420, 940, -1000, -1000, 1248, - 904, -1000, -1000, -1000, -1000, -1000, 783, -1000, 24734, 24734, - 24734, 24734, -1000, -1000, 2048, 50669, 2048, 2048, 24734, 2048, - 24734, 2048, 2048, 2048, 2048, 22511, 2048, 2048, 2048, 2048, - -1000, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, - 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, - 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, - 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, - 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, - 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, - 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, - 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, - 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, - 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, - 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, - 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, - 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, - -1000, -1000, -1000, -1000, 2048, 820, 2048, 2048, 2048, 2048, - 2048, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 2048, 2048, - 2048, 2048, 2048, 2048, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, 2048, 2048, 2048, 2048, 2048, 2048, 2048, - 2048, 2048, 2048, 2048, 2048, 2048, 2048, -1000, -1000, -1000, - 1591, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 1583, 1540, - 1534, 1525, -1000, 19547, 2048, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, 56250, -1000, 1830, + 216, 54766, 54766, 340, 1333, -1000, -1000, 2569, 2087, -1000, + 2641, 2620, 392, -1000, 3919, 2111, 1683, 1406, 2087, 1954, + 56250, -1000, 2005, -1000, -1000, -1000, -357, -364, 2232, 1455, + 1829, -1000, -1000, -1000, -1000, 1798, 22860, -1000, -1000, 2693, + -1000, 28796, 823, 2687, 50314, -1000, 456, 456, 1969, 421, + 13, -1000, -1000, -1000, -1000, 979, 36216, -1000, -1000, -1000, + -1000, -1000, 1884, 56250, -1000, -1000, 5043, 54766, -1000, 2107, + -1000, 1875, -1000, 2041, 22860, 2126, 768, 54766, 523, 516, + 510, 475, -62, -1000, -1000, -1000, -1000, -1000, -1000, 958, + 958, 958, -1000, 395, 2623, 4989, 5064, -1000, -1000, -1000, + 49572, 2102, 54766, -1000, 2088, -1000, 1039, 861, 864, 864, + 54766, -1000, -1000, 55508, 54766, 1037, 1033, 54766, 54766, 54766, + 54766, -1000, 48830, -1000, 48088, 47346, 1331, 54766, 46604, 45862, + 45120, 44378, 43636, -1000, 2609, -1000, 2204, -1000, -1000, -1000, + 55508, 54766, 54766, 55508, 54766, 55508, 56250, 54766, -1000, -1000, + 357, -1000, -1000, 1329, 1327, 1319, 958, 958, 1317, 1828, + 1823, 1816, 958, 958, 1305, 1800, 38442, 1780, 276, 1299, + 1296, 1294, 1293, 1775, 259, 1757, 1237, 1220, 1272, 54766, + 2083, 56250, -1000, 250, 1070, 434, 978, 2474, 2331, 1964, + 488, 767, 54766, 474, 474, 54766, -1000, 16164, 56250, 230, + -1000, 1744, 22860, -1000, 1117, 1096, 1096, -1000, -1000, -1000, + -1000, -1000, -1000, 1114, 56250, 1117, -1000, -1000, -1000, 1096, + 1114, 56250, 1114, 1114, 1114, 1114, 1096, 1096, 1096, 1114, + 56250, 56250, 56250, 56250, 56250, 56250, 56250, 56250, 56250, 15416, + 946, 1114, -460, -1000, 1742, -1000, -1000, -1000, 2230, -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, -1000, 55856, -1000, 2048, 196, - 54374, 54374, 317, 1361, -1000, -1000, 2590, 2129, -1000, 2674, - 2662, 380, -1000, 3907, 2056, 1627, 1538, 2129, 2019, 55856, - -1000, 2052, -1000, -1000, -1000, -343, -355, 2294, 1519, 1848, - -1000, -1000, -1000, -1000, 2065, 22511, -1000, -1000, 2717, -1000, - 28439, 816, 2706, 49928, -1000, 467, 467, 2036, 427, -4, - -1000, -1000, -1000, -1000, 1006, 35849, -1000, -1000, -1000, -1000, - -1000, 1946, 55856, -1000, -1000, 4251, 54374, -1000, 2150, -1000, - 1934, -1000, 2102, 22511, 2174, 724, 54374, 515, 510, 506, - 468, -71, -1000, -1000, -1000, -1000, -1000, -1000, 974, 974, - 974, -1000, 372, 2658, 5041, 5724, -1000, -1000, -1000, 49187, - 2148, 54374, -1000, 2147, -1000, 1136, 835, 847, 847, 54374, - -1000, -1000, 55115, 54374, 1113, 1093, 54374, 54374, 54374, 54374, - -1000, 48446, -1000, 47705, 46964, 1359, 54374, 46223, 45482, 44741, - 44000, 43259, -1000, 2471, -1000, 2096, -1000, -1000, -1000, 55115, - 54374, 54374, 55115, 54374, 55115, 55856, 54374, -1000, -1000, 321, - -1000, -1000, 1354, 1344, 1338, 974, 974, 1337, 1825, 1823, - 1807, 974, 974, 1336, 1799, 38072, 1791, 260, 1332, 1314, - 1312, 1340, 1772, 282, 1761, 1333, 1309, 1310, 54374, 2144, - 55856, -1000, 243, 1023, 407, 1005, 2517, 2353, 2033, 481, - 528, 54374, 464, 464, 54374, -1000, 15824, 55856, 217, -1000, - 1736, 22511, -1000, 1103, 1096, 1096, -1000, -1000, -1000, -1000, - -1000, -1000, 1154, 55856, 1103, -1000, -1000, -1000, 1096, 1154, - 55856, 1154, 1154, 1154, 1154, 1096, 1096, 1096, 1154, 55856, - 55856, 55856, 55856, 55856, 55856, 55856, 55856, 55856, 15077, 949, - 1154, -459, -1000, 1721, -1000, -1000, -1000, 2256, -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, @@ -7501,340 +7546,337 @@ 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, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 15416, + 15416, -1000, -1000, 2565, 2559, -1000, -1000, -1000, 1960, -1000, + 184, 17, 185, -1000, 42894, 535, 974, -1000, 535, -1000, + -1000, -1000, -1000, -1000, 1959, 42152, -1000, -461, -464, -467, + -469, -1000, -1000, -1000, -471, -473, -1000, -1000, -1000, 22860, + 22860, 22860, 22860, -257, -1000, 1217, 25086, 2491, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, 22860, 247, 1131, 25086, 25086, + 25086, 25086, 25086, 25086, 25086, 26570, 25828, 25086, 25086, 25086, + 25086, 25086, 25086, -1000, -1000, 33990, 6271, 6271, 900, 900, + 900, 900, -1000, -174, 1958, 55508, -1000, -1000, -1000, 822, + 22860, 22860, 900, -1000, 1238, 2007, 19892, 22860, 22860, 22860, + 22860, 960, 1393, 55508, 22860, -1000, 1406, -1000, -1000, -1000, + -1000, 1234, -1000, -1000, 1081, 2376, 2376, 2376, 2376, 22860, + 22860, 22860, 22860, 22860, 22860, 22860, 22860, 22860, 22860, 2376, + 22860, 208, 208, 919, 22860, 22860, 22860, 22860, 22860, 22860, + 22860, 22860, 18408, 22860, 22860, 25086, 22860, 22860, 22860, 1406, + 22860, 22860, 22860, 22860, 22860, 22860, 22860, 22860, 22860, 22860, + 22860, 22860, 22860, 22860, 22860, 22860, 22860, 22860, 22860, 22860, + 22860, 22860, 22860, 22860, 22860, 22860, 22860, 22860, 22860, 22860, + 22860, 22860, 22860, 22860, 22860, 22860, 22860, 22860, 22860, 22860, + 22860, 22860, 22860, 22860, 22860, 22860, 22860, 22860, 22860, 22860, + 22860, 22860, 22860, 22860, 22860, 22860, 22860, 22860, 22860, 22860, + 22860, 22860, 22860, 22860, 22860, 22860, 22860, 22860, 22860, 22860, + 22860, 22860, 22860, 22860, 22860, 22860, 22860, 22860, 22860, 1406, + 22860, 1221, 22860, 22860, 22860, 22860, 22860, 22860, 22860, 17660, + 22860, 22860, 22860, 22860, 22860, -1000, -1000, -1000, -1000, -1000, + -1000, 22860, 22860, 22860, 22860, 22860, 22860, 22860, 22860, 1406, + 22860, 22860, 22860, 22860, 22860, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, 1686, 1493, 1478, 22860, -1000, + 1957, -1000, -202, 31022, 22860, 1739, 2683, 2157, 54766, -1000, + -1000, -1000, -1000, 2569, -1000, 2569, 1686, 3881, 2277, 22118, + -1000, -1000, 3881, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, 1737, -1000, 56250, 1954, + 2545, 54766, -1000, -294, -1000, -295, 2272, 1730, 351, -1000, + 22860, 22860, 1953, -1000, 1399, 56250, -1000, -257, -1000, 41410, + -1000, -1000, 14668, 56250, 350, 56250, -1000, 30280, 40668, 306, + -1000, 13, 1933, -1000, 29, 1, 19150, 899, -1000, -1000, + -1000, 371, 27312, 1822, 899, 100, -1000, -1000, -1000, 2041, + -1000, 2041, 2041, 2041, 2041, 351, 351, 351, 351, -1000, + -1000, -1000, -1000, -1000, 2079, 2041, 2075, -1000, 2041, 2041, + 2041, 2041, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 2073, + 2073, 2073, 2061, 2061, 2042, 2042, 447, -1000, 22860, 486, + 39926, 2537, 1270, 1538, 250, 475, 2144, 54766, 54766, 54766, + 56250, 989, -1000, 1445, 1412, 1398, -1000, -532, 1952, -1000, + -1000, 2617, -1000, -1000, 956, 1088, 1084, 1064, 54766, 214, + 335, -1000, 433, -1000, 39926, 54766, 1029, 864, 54766, -1000, + 54766, -1000, -1000, -1000, -1000, -1000, 54766, -1000, -1000, 1950, + -1000, 1927, 1106, 1083, 1098, 1080, 1950, -1000, -1000, -183, + 1950, -1000, 1950, -1000, 1950, -1000, 1950, -1000, 1950, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 1001, 278, + -350, 54766, 214, 487, -1000, 485, 33990, -1000, -1000, -1000, + 33990, 33990, -1000, -1000, -1000, -1000, 1726, 1723, -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, -520, 56250, -1000, + 236, 971, 268, 376, 291, 56250, 405, 2423, 2413, 2403, + 2396, 2391, 2373, 2366, 242, 298, 56250, 56250, 474, 2206, + 56250, 2522, 56250, -1000, -1000, -1000, -1000, -1000, 1716, 1708, + -1000, 1393, 56250, -1000, -1000, 1114, 1114, -1000, -1000, 56250, + 1114, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 1114, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 15077, 15077, - -1000, -1000, 2584, 2575, -1000, -1000, -1000, 2032, -1000, 163, - 5, 168, -1000, 42518, 513, 1004, -1000, 513, -1000, -1000, - -1000, -1000, -1000, 2027, 41777, -1000, -460, -461, -462, -463, - -1000, -1000, -1000, -464, -473, -1000, -1000, -1000, 22511, 22511, - 22511, 22511, -276, -1000, 1199, 24734, 2504, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, 22511, 247, 991, 24734, 24734, 24734, - 24734, 24734, 24734, 24734, 26216, 25475, 24734, 24734, 24734, 24734, - 24734, 24734, -1000, -1000, 33626, 6276, 6276, 904, 904, 904, - 904, -1000, -186, 2022, 55115, -1000, -1000, -1000, 813, 22511, - 22511, 904, -1000, 1253, 1138, 19547, 22511, 22511, 22511, 22511, - 1011, 1395, 55115, 22511, -1000, 1538, -1000, -1000, -1000, -1000, - 1245, -1000, -1000, 1091, 2415, 2415, 2415, 2415, 22511, 22511, - 22511, 22511, 22511, 22511, 22511, 22511, 22511, 22511, 2415, 22511, - 148, 148, 719, 22511, 22511, 22511, 22511, 22511, 22511, 22511, - 22511, 18065, 22511, 22511, 24734, 22511, 22511, 22511, 1538, 22511, - 22511, 22511, 22511, 22511, 22511, 22511, 22511, 22511, 22511, 22511, - 22511, 22511, 22511, 22511, 22511, 22511, 22511, 22511, 22511, 22511, - 22511, 22511, 22511, 22511, 22511, 22511, 22511, 22511, 22511, 22511, - 22511, 22511, 22511, 22511, 22511, 22511, 22511, 22511, 22511, 22511, - 22511, 22511, 22511, 22511, 22511, 22511, 22511, 22511, 22511, 22511, - 22511, 22511, 22511, 22511, 22511, 22511, 22511, 22511, 22511, 22511, - 22511, 22511, 22511, 22511, 22511, 22511, 22511, 22511, 22511, 22511, - 22511, 22511, 22511, 22511, 22511, 22511, 22511, 22511, 1538, 22511, - 1351, 22511, 22511, 22511, 22511, 22511, 22511, 22511, 17318, 22511, - 22511, 22511, 22511, 22511, -1000, -1000, -1000, -1000, -1000, -1000, - 22511, 22511, 22511, 22511, 22511, 22511, 22511, 22511, 1538, 22511, - 22511, 22511, 22511, 22511, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, 1638, 1518, 1544, 22511, -1000, 2020, - -1000, -200, 30662, 22511, 1691, 2695, 2185, 54374, -1000, -1000, - -1000, -1000, 2590, -1000, 2590, 1638, 3897, 2266, 21770, -1000, - -1000, 3897, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, 1820, -1000, 55856, 2019, 2566, - 54374, -1000, -316, -1000, -321, 2296, 1687, 318, -1000, 22511, - 22511, 2016, -1000, 1698, 55856, -1000, -276, -1000, 41036, -1000, - -1000, 14330, 55856, 354, 55856, -1000, 29921, 40295, 319, -1000, - -4, 1996, -1000, 12, -2, 18806, 882, -1000, -1000, -1000, - 1021, 26957, 1950, 882, 83, -1000, -1000, -1000, 2102, -1000, - 2102, 2102, 2102, 2102, 318, 318, 318, 318, -1000, -1000, - -1000, -1000, -1000, 2142, 2102, 2137, -1000, 2102, 2102, 2102, - 2102, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 2136, 2136, - 2136, 2134, 2134, 2106, 2106, 453, -1000, 22511, 435, 39554, - 2546, 1308, 1193, 243, 468, 2183, 54374, 54374, 54374, 55856, - 1052, -1000, 1408, 1404, 1380, -1000, -532, 2015, -1000, -1000, - 2656, -1000, -1000, 964, 1175, 1172, 893, 54374, 205, 328, - -1000, 440, -1000, 39554, 54374, 1087, 847, 54374, -1000, 54374, - -1000, -1000, -1000, -1000, -1000, 54374, -1000, -1000, 2014, -1000, - 2005, 1201, 1166, 1195, 1161, 2014, -1000, -1000, -194, 2014, - -1000, 2014, -1000, 2014, -1000, 2014, -1000, 2014, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, -1000, 993, 286, -385, - 54374, 205, 480, -1000, 479, 33626, -1000, -1000, -1000, 33626, - 33626, -1000, -1000, -1000, -1000, 1656, 1643, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, 56250, -1000, -1000, -1000, -1000, + 1702, -1000, 56250, -37, 177, -1000, -1000, 54766, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -114, -1000, 854, + 26, 393, -1000, -1000, -1000, -1000, -1000, 2550, -1000, 1393, + 1021, 1010, -1000, 1830, -1000, -1000, 1233, -1000, -1000, -1000, + -1000, -1000, 1830, 1830, 1830, -1000, -1000, -1000, -1000, -1000, + 247, 25086, 25086, 25086, 1782, 777, 1891, 1812, 1474, 1231, + 1231, 976, 25086, 976, 25086, 918, 918, 918, 918, 918, + -1000, -1000, -1000, -1000, -1000, -1000, 1692, -1000, 1667, -1000, + 1830, 55508, 1827, 17660, 1747, 1599, 1406, 932, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 3935, 1406, + 1851, 1406, 1674, 3902, 1026, -1000, 22860, 1406, 3610, -1000, + -1000, 1406, 1406, 22860, -1000, -1000, 22860, 22860, 22860, 22860, + 1538, 1538, 1538, 1538, 1538, 1538, 1538, 1538, 1538, 1538, + 22860, 1538, 1947, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, -522, 55856, -1000, 228, - 998, 288, 315, 265, 55856, 367, 2469, 2438, 2433, 2432, - 2429, 2420, 2397, 241, 301, 55856, 55856, 464, 2240, 55856, - 2549, 55856, -1000, -1000, -1000, -1000, -1000, 1632, 1619, -1000, - 1395, 55856, -1000, -1000, 1154, 1154, -1000, -1000, 55856, 1154, - -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 1154, -1000, + -1000, 1938, 2669, 1558, 1538, 1538, 1538, 1538, 3587, 1538, + 1538, 22860, 1358, -1000, -1000, -1000, 1419, 3583, 1684, 3572, + 1538, 1538, -1000, 1538, 3541, 3536, 1406, 2928, 2820, 1538, + 1538, 1538, 1538, 1538, 2792, 2778, 1538, 1538, 2705, 1538, + 3522, 1538, 2698, 2681, 2677, 2633, 2611, 2603, 2594, 2570, + 2561, 2539, 2533, 2523, 2509, 2492, 2458, 2448, 2444, 2407, + 1538, 1538, 1538, 3508, 1538, 3489, 1538, 3485, 1538, 1538, + 3474, 2394, 2364, 1406, 1934, -1000, 3469, 1538, 3457, 3453, + 3446, 2354, 3427, 3422, 3418, 1538, 1538, 1538, 2339, 3413, + 3407, 3395, 3391, 3374, 3369, 3364, 3355, 3343, 1538, 1478, + 1478, 1478, 1478, 1478, 3253, -259, 1538, 1406, -1000, -1000, + -1000, -1000, -1000, 3092, 2333, 3042, 3030, 3011, 3003, 1406, + 1830, 821, -1000, -1000, 1478, 1406, 1406, 1478, 1478, 2995, + 2963, 2957, 2953, 2933, 2834, 1538, 1538, -1000, 1538, 2810, + 2800, 2303, 2290, 1406, -1000, 1478, 56250, -1000, -451, -1000, + -8, 954, 1830, -1000, 38442, 1406, -1000, 4349, -1000, 1168, + -1000, -1000, -1000, -1000, -1000, 35474, 1879, -1000, -1000, -1000, + -1000, 1830, 1762, -1000, -1000, -1000, -1000, 351, 73, 34732, + 890, 890, 123, 1393, 1393, 22860, -1000, -1000, -1000, -1000, + -1000, -1000, 820, 2642, 404, 1830, -1000, 1948, 3576, -1000, + -1000, -1000, 2544, 28054, -1000, -1000, 1830, 1830, 56250, 1939, + 1745, -1000, 819, -1000, 1320, 1933, 13, 24, -1000, -1000, + -1000, -1000, 1393, -1000, 1378, 353, 346, -1000, 437, -1000, + -1000, -1000, -1000, 2343, 81, -1000, -1000, -1000, 912, 351, + -1000, -1000, -1000, -1000, -1000, -1000, 1664, -1000, 1664, -1000, + -1000, -1000, -1000, -1000, 1268, -1000, -1000, -1000, -1000, 1266, + -1000, -1000, 1256, -1000, -1000, 2877, 2183, 486, -1000, -1000, + 958, 1655, -1000, -1000, 2356, 958, 958, 54766, -1000, -1000, + 1783, 2537, 236, 56250, 2203, -1000, 2144, 2144, 2144, -1000, + 2497, -1000, -1000, -1000, -1000, -1000, -1000, -522, 174, 609, + -1000, -1000, -1000, 1389, 54766, 1721, -1000, 224, -1000, 1779, + -1000, 54766, -1000, 1713, 2053, 54766, 54766, -1000, -1000, -1000, + 54766, 1830, -1000, -1000, -1000, -1000, 760, 2471, 319, -1000, + -1000, -283, -1000, -1000, 214, 224, 55508, 54766, 899, -1000, + -1000, -1000, -1000, -1000, -523, 1690, 506, 228, 328, 56250, + 56250, 56250, 56250, 56250, 56250, 800, -1000, -1000, 39, -1000, + -1000, 202, -1000, -1000, 1623, -1000, -1000, -1000, -1000, 202, + -1000, -1000, -1000, -1000, -1000, 275, 478, -1000, 56250, 56250, + 964, -1000, -1000, -1000, -1000, -1000, 1096, -1000, -1000, 1096, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, 55856, -1000, -1000, -1000, -1000, 1616, - -1000, 55856, -36, 161, -1000, -1000, 54374, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, -119, -1000, 351, 8, - 378, -1000, -1000, -1000, -1000, -1000, 2570, -1000, 1395, 1127, - 1057, -1000, 2048, -1000, -1000, 1239, -1000, -1000, -1000, -1000, - -1000, 2048, 2048, 2048, -1000, -1000, -1000, -1000, -1000, 247, - 24734, 24734, 24734, 1630, 777, 1347, 1801, 1204, 1334, 1334, - 919, 24734, 919, 24734, 922, 922, 922, 922, 922, -1000, - -1000, -1000, -1000, -1000, -1000, 1591, -1000, 1589, -1000, 2048, - 55115, 1932, 17318, 2072, 1735, 1538, 931, -1000, -1000, -1000, + -1000, -1000, 2457, 56250, 20, -490, -1000, -486, 22860, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, 1666, 749, 1891, 25086, + 25086, 2007, 2007, 25086, -1000, -1000, -1000, 1076, 1076, 33990, + -1000, 25086, 22860, -1000, -1000, 22860, 22860, 22860, 981, -1000, + 22860, 1181, -1000, 22860, -1000, -259, 1478, 1538, 1538, 1538, + 1538, -259, -259, -259, -259, -259, -259, -259, -259, -259, + -259, 1908, -1000, 22860, 22860, 22860, 1406, 334, -1000, -1000, + -1000, -259, 22860, -1000, -1000, 2666, -1000, 22860, -1000, 33990, + 22860, 22860, 22860, -1000, -1000, -1000, 22860, 22860, -1000, -1000, + 22860, -1000, 22860, -1000, -1000, -1000, -1000, -1000, -1000, 22860, + -1000, 22860, -1000, -1000, -1000, 22860, -1000, 22860, -1000, -1000, + 22860, -1000, 22860, -1000, 22860, -1000, 22860, -1000, 22860, -1000, + 22860, -1000, 22860, -1000, 22860, -1000, 22860, -1000, 22860, -1000, + 22860, -1000, 22860, -1000, 22860, -1000, 22860, -1000, 22860, -1000, + 22860, -1000, 22860, -1000, 22860, -1000, -1000, -1000, 22860, -1000, + 22860, -1000, 22860, -1000, -1000, 22860, -1000, 22860, -1000, 22860, + -1000, 22860, 22860, -1000, 22860, 22860, 22860, -1000, 22860, 22860, + 22860, 22860, -1000, -1000, -1000, -1000, 22860, 22860, 22860, 22860, + 22860, 22860, 22860, 22860, 22860, 22860, -1000, -1000, -1000, -1000, + -1000, -1000, 22860, -1000, 39926, 47, -259, 1221, 47, 1221, + 24344, 832, 507, 23602, -1000, 22860, 16912, -1000, -1000, -1000, + -1000, -1000, 22860, 22860, 22860, 22860, 22860, 22860, -1000, -1000, + -1000, 22860, 22860, -1000, 22860, -1000, 22860, -1000, -1000, -1000, + -1000, -1000, 954, -1000, 455, 453, 864, 54766, -1000, -1000, + -1000, -1000, 1930, -1000, 2564, -1000, 2285, 2284, 2659, 2642, + 22118, -1000, 30280, -1000, -1000, 54766, -419, -1000, 2326, 2476, + 890, 890, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 13920, + 2569, 22860, 2201, 55508, 223, -1000, 29538, 54766, 55508, 30280, + 30280, 30280, 30280, 30280, -1000, 2242, 2235, -1000, 2254, 2228, + 2313, 56250, -1000, 1686, 1688, -1000, 22860, 32506, 1935, 30280, + -1000, -1000, 30280, 56250, 13172, -1000, -1000, 6, -7, -1000, + -1000, -1000, -1000, 371, -1000, -1000, 1082, 2543, 2338, -1000, + -1000, -1000, -1000, -1000, 1682, -1000, 1676, 1928, 1671, 1662, + 278, -1000, 2067, 2451, 958, 958, -1000, 1254, -1000, 1238, + 1614, 1586, -1000, -1000, -1000, 496, -1000, 56250, 2200, 2195, + 2194, -1000, -540, 1253, 2052, 2012, 22860, 2051, 2615, 1913, + 54766, -1000, -1000, 55508, -1000, 197, -1000, 486, 54766, -1000, + -1000, -1000, 335, 56250, -1000, 7068, -1000, -1000, -1000, 224, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, 56250, 233, -1000, + 2047, 1343, -1000, -1000, 2140, -1000, -1000, -1000, -1000, -1000, + 213, 201, 1583, 199, 1547, -1000, 199, -1000, 56250, 928, + 2183, 56250, -1000, -1000, -1000, 1114, 1114, -1000, -1000, 2449, + -1000, 1238, 1538, 25086, 25086, -1000, 900, -1000, -1000, 435, + -229, 2041, 2041, -1000, 2041, 2042, -1000, 2041, 171, 2041, + 154, 2041, -1000, -1000, 1406, 1406, -1000, 1478, 2286, 1439, + 2796, -1000, 1393, 22860, 2715, -1000, -1000, -259, -259, -259, + -259, -259, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -69, 2689, 2637, 1538, -1000, 2039, 2036, -1000, + 1538, 22860, 1538, 1406, 2278, 1538, 1538, 1538, 1538, 1538, + 1538, 1538, 1538, 1538, 1538, 1538, 1538, 2266, 2257, 2249, + 2196, 2175, 2171, 2167, 2163, 2158, 2149, 2145, 2128, 2084, + 2080, 2076, 2062, 1538, 1538, 2058, 1538, 2054, 2048, -1000, + 1393, 1478, 2628, 1478, 1538, 1538, 2527, 337, 1538, 1651, + 1651, 1651, 1651, 1651, 1478, 1478, 1478, 1478, 1538, 54766, + -1000, -259, -1000, -1000, -307, -309, -1000, 1406, -259, 1926, + 25086, 1538, 25086, 25086, 25086, 1538, 1406, -1000, 2043, 2010, + 2270, 1940, 1538, 1832, 1538, 1538, 1538, 1821, -1000, 2535, + 1830, 2535, 1830, 2535, 1647, 1168, 56250, -1000, -1000, -1000, + -1000, 2642, 2639, -1000, 1914, -1000, 73, 639, -1000, 2311, + 2476, -1000, 2610, 2306, 2598, -1000, -1000, -1000, -1000, -1000, + 1393, -1000, 2479, 1916, -1000, 963, 1907, -1000, -1000, 21376, + 1649, 2281, 817, 1647, 1923, 3576, 2136, 2190, 3359, -1000, + -1000, -1000, -1000, 2231, -1000, 2063, -1000, -1000, 2005, -1000, + 1790, 350, 30280, 1877, 1877, -1000, 816, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, 1100, 7068, 2704, -1000, 1544, -1000, + 1366, 195, 1240, -1000, -1000, 958, 958, -1000, 1028, 1020, + -1000, 56250, 2031, -1000, 351, 1529, 351, 1225, -1000, -1000, + 1215, -1000, -1000, -1000, -1000, 1994, 2562, -1000, -1000, -1000, + -1000, 56250, -1000, 56250, 56250, 56250, 2029, 2595, -1000, 22860, + 2015, 962, 2253, 54766, 54766, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, 427, 958, -503, 297, + 294, 958, 958, 958, -541, -1000, -1000, 1639, 1637, -1000, + -205, -1000, 22860, -1000, -1000, -1000, -1000, -1000, 1249, 1249, + 1513, 1500, 1498, -1000, 2005, -1000, -1000, -1000, 1722, -1000, + -1000, -186, 54766, 54766, 54766, 54766, -1000, -1000, -1000, 1187, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, -1000, 3958, 1538, 1958, - 1538, 2351, 3939, 1053, -1000, 22511, 1538, 3935, -1000, -1000, - 1538, 1538, 22511, -1000, -1000, 22511, 22511, 22511, 22511, 1193, - 1193, 1193, 1193, 1193, 1193, 1193, 1193, 1193, 1193, 22511, - 1193, 2009, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, 900, 1406, 375, -188, 1406, -1000, -1000, 351, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - 2001, 2693, 1816, 1193, 1193, 1193, 1193, 3914, 1193, 1193, - 22511, 1839, -1000, -1000, -1000, 1460, 3905, 1586, 3606, 1193, - 1193, -1000, 1193, 3578, 3538, 1538, 2890, 2884, 1193, 1193, - 1193, 1193, 1193, 2869, 2859, 1193, 1193, 2823, 1193, 3523, - 1193, 2734, 2712, 2694, 2690, 2645, 2621, 2595, 2591, 2559, - 2527, 2488, 2483, 2478, 2456, 2452, 2447, 2412, 2343, 1193, - 1193, 1193, 3504, 1193, 3492, 1193, 3488, 1193, 1193, 3484, - 2338, 2333, 1538, 1999, -1000, 3480, 1193, 3476, 3472, 3465, - 2328, 3458, 3453, 3414, 1193, 1193, 1193, 2322, 3410, 3404, - 3395, 3390, 3385, 3378, 3367, 3348, 3281, 1193, 1544, 1544, - 1544, 1544, 1544, 3269, -280, 1193, 1538, -1000, -1000, -1000, - -1000, -1000, 3093, 2316, 3071, 3063, 3059, 3049, 1538, 2048, - 809, -1000, -1000, 1544, 1538, 1538, 1544, 1544, 3043, 3025, - 3009, 2993, 2956, 2948, 1193, 1193, -1000, 1193, 2939, 2927, - 2312, 2297, 1538, -1000, 1544, 55856, -1000, -453, -1000, -16, - 889, 2048, -1000, 38072, 1538, -1000, 5684, -1000, 1371, -1000, - -1000, -1000, -1000, -1000, 35108, 1911, -1000, -1000, -1000, -1000, - 2048, 1930, -1000, -1000, -1000, -1000, 318, 58, 34367, 876, - 876, 109, 1395, 1395, 22511, -1000, -1000, -1000, -1000, -1000, - -1000, 807, 2676, 371, 2048, -1000, 2011, 2757, -1000, -1000, - -1000, 2565, 27698, -1000, -1000, 2048, 2048, 55856, 1972, 1937, - -1000, 803, -1000, 1300, 1996, -4, -8, -1000, -1000, -1000, - -1000, 1395, -1000, 1376, 357, 359, -1000, 443, -1000, -1000, - -1000, -1000, 2387, 70, -1000, -1000, -1000, 332, 318, -1000, - -1000, -1000, -1000, -1000, -1000, 1587, -1000, 1587, -1000, -1000, - -1000, -1000, -1000, 1305, -1000, -1000, -1000, -1000, 1304, -1000, - -1000, 1303, -1000, -1000, 2379, 2227, 435, -1000, -1000, 974, - 1585, -1000, -1000, 2391, 974, 974, 54374, -1000, -1000, 1896, - 2546, 228, 55856, 2239, -1000, 2183, 2183, 2183, -1000, 2537, - -1000, -1000, -1000, -1000, -1000, -1000, -524, 175, 361, -1000, - -1000, -1000, 2041, 54374, 1900, -1000, 214, -1000, 1878, -1000, - 54374, -1000, 1877, 2128, 54374, 54374, -1000, -1000, -1000, 54374, - 2048, -1000, -1000, -1000, -1000, 523, 2516, 314, -1000, -1000, - -300, -1000, -1000, 205, 214, 55115, 54374, 882, -1000, -1000, - -1000, -1000, -1000, -525, 1862, 502, 225, 574, 55856, 55856, - 55856, 55856, 55856, 55856, 778, -1000, -1000, 23, -1000, -1000, - 191, -1000, -1000, -1000, -1000, -1000, -1000, 191, -1000, -1000, - -1000, -1000, -1000, 283, 478, -1000, 55856, 55856, 945, -1000, - -1000, -1000, -1000, -1000, 1096, -1000, -1000, 1096, -1000, -1000, + 22860, -1000, 22860, -1000, 22860, 1393, 22860, -1000, -1000, -1000, + -1000, -1000, 2569, 1489, 22860, 22860, -1000, 1211, 1209, -259, + 1538, -1000, -1000, -1000, 22860, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 22860, -1000, + 22860, -1000, 22860, -1000, 22860, -1000, 22860, -1000, 22860, -1000, + 22860, -1000, 22860, -1000, 22860, -1000, 22860, -1000, 22860, -1000, + 22860, -1000, 22860, -1000, 22860, -1000, 22860, -1000, 22860, -1000, + -1000, 22860, -1000, -1000, -1000, 22860, -1000, 22860, -1000, 22860, + -1000, -1000, -1000, 22860, 264, 1076, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, 1406, 347, -1000, + -1000, -1000, 2650, -1000, 1406, 22860, 2007, -1000, 2007, 2007, + 2007, -1000, -1000, -1000, 22860, -1000, 22860, 22860, -1000, 22860, + -1000, 22860, -1000, -1000, -1000, -1000, 22860, 1830, 2374, 39184, + 1830, 39184, 1830, 32506, -1000, -1000, 2639, 2596, 2593, 2300, + 2312, 2312, 2311, -1000, 2589, 2587, -1000, 1472, 2585, 1458, + 1006, -1000, 55508, 22860, -1000, 223, 38442, -1000, 403, 54766, + 223, 54766, -1000, 2634, -1000, -1000, 22860, 2014, -1000, 22860, + -1000, -1000, -1000, -1000, 6271, 2642, 1877, -1000, -1000, 922, + -1000, 22860, -1000, 11485, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, 1453, 1446, -1000, -1000, 2006, 22860, -1000, -1000, + -1000, 1663, 1652, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, 2005, -1000, -1000, -1000, -1000, 335, -527, 2224, 54766, + 1206, -1000, 1632, 1913, 288, 223, 1432, 958, 958, 958, + 1161, 1152, 38442, 1605, -1000, 54766, 364, -1000, 335, -1000, + -222, -223, 1538, -1000, -1000, 2530, -1000, -1000, 16912, -1000, + -1000, 2003, 2141, -1000, -1000, -1000, -1000, 2264, -179, -200, + -1000, -1000, 1538, 1538, 1538, 1405, 1406, -1000, 1538, 1538, + 1633, 1543, -1000, -1000, 1538, 1538, 1538, 1538, 1538, 1538, + 1538, 1538, 1538, 1538, 1538, 1538, 1538, 1538, 1538, 1538, + 1538, 1538, 1538, 1538, 1478, 1778, -1000, 264, 1406, 2184, + -1000, -1000, 6271, -1000, -1000, 2634, 2579, 47, -1000, -1000, + 222, 47, 1393, 1032, 1406, 1406, 1032, 1760, 1538, 1660, + 1642, 1538, 1538, 33248, -1000, 2572, 2571, 1542, -1000, -1000, + 39184, 1542, 39184, 954, 2596, -270, 22860, 22860, 2297, 1164, + -1000, -1000, -1000, -1000, 1402, 1390, -1000, 1377, -1000, 2699, + -1000, 1393, -1000, 1830, 223, -1000, 808, 1907, -1000, 2569, + 1393, 54766, 1393, 75, 2634, -1000, 1538, -1000, 1830, 1830, + 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, + 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, + 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, + 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, + 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, + 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, + 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, + 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, + 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, + 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, + 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, + 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, + 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, -1000, + -1000, 54766, 2180, -1000, -1000, 2529, 1554, 166, -1000, 1475, + 1913, -1000, -1000, 221, -1000, 22860, -1000, 38442, 1374, 1360, + -1000, -1000, -1000, -1000, -541, -1000, -1000, -1000, -1000, -1000, + -1000, 392, 1888, -1000, 957, 54766, 56250, -1000, 2188, -1000, + -1000, -1000, -1000, 22860, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - 2484, 55856, 4, -491, -1000, -488, 22511, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, 1307, 482, 1347, 24734, 24734, 1138, - 1138, 24734, -1000, -1000, -1000, 1149, 1149, 33626, -1000, 24734, - 22511, -1000, -1000, 22511, 22511, 22511, 1010, -1000, 22511, 1295, - -1000, 22511, -1000, -280, 1544, 1193, 1193, 1193, 1193, -280, - -280, -280, -280, -280, -280, -280, -280, -280, -280, 1976, - -1000, 22511, 22511, 22511, 1538, 295, -1000, -1000, -1000, -280, - 22511, -1000, -1000, 2692, -1000, 22511, -1000, 33626, 22511, 22511, - 22511, -1000, -1000, -1000, 22511, 22511, -1000, -1000, 22511, -1000, - 22511, -1000, -1000, -1000, -1000, -1000, -1000, 22511, -1000, 22511, - -1000, -1000, -1000, 22511, -1000, 22511, -1000, -1000, 22511, -1000, - 22511, -1000, 22511, -1000, 22511, -1000, 22511, -1000, 22511, -1000, - 22511, -1000, 22511, -1000, 22511, -1000, 22511, -1000, 22511, -1000, - 22511, -1000, 22511, -1000, 22511, -1000, 22511, -1000, 22511, -1000, - 22511, -1000, 22511, -1000, -1000, -1000, 22511, -1000, 22511, -1000, - 22511, -1000, -1000, 22511, -1000, 22511, -1000, 22511, -1000, 22511, - 22511, -1000, 22511, 22511, 22511, -1000, 22511, 22511, 22511, 22511, - -1000, -1000, -1000, -1000, 22511, 22511, 22511, 22511, 22511, 22511, - 22511, 22511, 22511, 22511, -1000, -1000, -1000, -1000, -1000, -1000, - 22511, -1000, 39554, 0, -280, 1351, 0, 1351, 23993, 823, - 784, 23252, -1000, 22511, 16571, -1000, -1000, -1000, -1000, -1000, - 22511, 22511, 22511, 22511, 22511, 22511, -1000, -1000, -1000, 22511, - 22511, -1000, 22511, -1000, 22511, -1000, -1000, -1000, -1000, -1000, - 889, -1000, 458, 456, 847, 54374, -1000, -1000, -1000, -1000, - 1989, -1000, 2609, -1000, 2317, 2313, 2684, 2676, 21770, -1000, - 29921, -1000, -1000, 54374, -442, -1000, 2354, 2401, 876, 876, - -1000, -1000, -1000, -1000, -1000, -1000, -1000, 13583, 2590, 22511, - 2221, 55115, 223, -1000, 29180, 54374, 55115, 29921, 29921, 29921, - 29921, 29921, -1000, 2255, 2252, -1000, 2264, 2263, 2582, 55856, - -1000, 1638, 1845, -1000, 22511, 32144, 1936, 29921, -1000, -1000, - 29921, 55856, 12836, -1000, -1000, 2, -21, -1000, -1000, -1000, - -1000, 1021, -1000, -1000, 1225, 2562, 2375, -1000, -1000, -1000, - -1000, -1000, 1813, -1000, 1777, 1986, 1769, 1753, 286, -1000, - 2172, 2479, 974, 974, -1000, 1301, -1000, 1253, 1584, 1567, - -1000, -1000, -1000, 499, -1000, 55856, 2220, 2217, 2213, -1000, - -539, 1299, 2124, 2171, 22511, 2120, 2652, 1968, 54374, -1000, - -1000, 55115, -1000, 272, -1000, 435, 54374, -1000, -1000, -1000, - 328, 55856, -1000, 5160, -1000, -1000, -1000, 214, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, 55856, 242, -1000, 2115, 1374, - -1000, -1000, 2179, -1000, -1000, -1000, -1000, -1000, 188, 187, - 1563, 184, 1557, 184, -1000, 55856, 873, 2227, 55856, -1000, - -1000, -1000, 1154, 1154, -1000, -1000, 2422, -1000, 1253, 1193, - 24734, 24734, -1000, 904, -1000, -1000, 409, -258, 2102, 2102, - -1000, 2102, 2106, -1000, 2102, 144, 2102, 138, 2102, -1000, - -1000, 1538, 1538, -1000, 1544, 2293, 1555, 2923, -1000, 1395, - 22511, 2819, -1000, -1000, -280, -280, -280, -280, -280, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -82, - 2719, 2699, 1193, -1000, 2101, 2100, -1000, 1193, 22511, 1193, - 1538, 2249, 1193, 1193, 1193, 1193, 1193, 1193, 1193, 1193, - 1193, 1193, 1193, 1193, 2232, 2226, 2222, 2218, 2203, 2180, - 2155, 2138, 2117, 2085, 2053, 2037, 2030, 2017, 2002, 1964, - 1193, 1193, 1953, 1193, 1928, 1918, -1000, 1395, 1544, 2551, - 1544, 1193, 1193, 2521, 306, 1193, 1749, 1749, 1749, 1749, - 1749, 1544, 1544, 1544, 1544, 1193, 54374, -1000, -280, -1000, - -1000, -332, -383, -1000, 1538, -280, 1970, 24734, 1193, 24734, - 24734, 24734, 1193, 1538, -1000, 1796, 1775, 2508, 1771, 1193, - 2460, 1193, 1193, 1193, 1759, -1000, 2561, 2048, 2561, 2048, - 2561, 1741, 1371, 55856, -1000, -1000, -1000, -1000, 2676, 2673, - -1000, 1969, -1000, 58, 622, -1000, 2331, 2401, -1000, 2647, - 2346, 2646, -1000, -1000, -1000, -1000, -1000, 1395, -1000, 2509, - 1915, -1000, 979, 1949, -1000, -1000, 21029, 1745, 2307, 800, - 1741, 1998, 2757, 2181, 2211, 3075, -1000, -1000, -1000, -1000, - 2237, -1000, 2228, -1000, -1000, 2052, -1000, 2417, 354, 29921, - 1944, 1944, -1000, 799, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, 1207, 5160, 2736, -1000, 1552, -1000, 1373, 194, 1293, - -1000, -1000, 974, 974, -1000, 1085, 1083, -1000, 55856, 2099, - -1000, 318, 1545, 318, 1290, -1000, -1000, 1276, -1000, -1000, - -1000, -1000, 2045, 2451, -1000, -1000, -1000, -1000, 55856, -1000, - 55856, 55856, 55856, 2069, 2644, -1000, 22511, 2068, 976, 2347, - 54374, 54374, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, 442, 974, -503, 289, 287, 974, 974, - 974, -543, -1000, -1000, 1728, 1726, -1000, -219, -1000, 22511, - -1000, -1000, -1000, -1000, -1000, 1272, 1272, 1540, 1534, 1525, - -1000, 2052, -1000, -1000, -1000, 1873, -1000, -1000, -198, 54374, - 54374, 54374, 54374, -1000, -1000, -1000, 1252, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 904, - 1538, 344, -203, 1538, -1000, -1000, 318, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, -1000, 22511, -1000, 22511, - -1000, 22511, 1395, 22511, -1000, -1000, -1000, -1000, -1000, 2590, - 1522, 22511, 22511, -1000, 1275, 1268, -280, 1193, -1000, -1000, - -1000, 22511, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, 22511, -1000, 22511, -1000, 22511, - -1000, 22511, -1000, 22511, -1000, 22511, -1000, 22511, -1000, 22511, - -1000, 22511, -1000, 22511, -1000, 22511, -1000, 22511, -1000, 22511, - -1000, 22511, -1000, 22511, -1000, 22511, -1000, -1000, 22511, -1000, - -1000, -1000, 22511, -1000, 22511, -1000, 22511, -1000, -1000, -1000, - 22511, 203, 1149, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, 1538, 346, -1000, -1000, -1000, 2679, - -1000, 1538, 22511, 1138, -1000, 1138, 1138, 1138, -1000, -1000, - -1000, 22511, -1000, 22511, 22511, -1000, 22511, -1000, 22511, -1000, - -1000, -1000, -1000, 22511, 2048, 2332, 38813, 2048, 38813, 2048, - 32144, -1000, -1000, 2673, 2669, 2643, 2337, 2340, 2340, 2331, - -1000, 2635, 2627, -1000, 1520, 2623, 1497, 1076, -1000, 55115, - 22511, -1000, 223, 38072, -1000, 384, 54374, 223, 54374, -1000, - 2610, -1000, -1000, 22511, 2061, -1000, 22511, -1000, -1000, -1000, - -1000, 6276, 2676, 1944, -1000, -1000, 928, -1000, 22511, -1000, - 10932, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 1495, - 1485, -1000, -1000, 2057, 22511, -1000, -1000, -1000, 1742, 1729, - -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 2052, -1000, - -1000, -1000, -1000, 328, -535, 2212, 54374, 1260, -1000, 1706, - 1968, 310, 223, 1467, 974, 974, 974, 1257, 1255, 38072, - 1699, -1000, 54374, 425, -1000, 328, -1000, -242, -244, 1193, - -1000, -1000, 2560, -1000, -1000, 16571, -1000, -1000, 2051, 2182, - -1000, -1000, -1000, -1000, 2292, -190, -227, -1000, -1000, 1193, - 1193, 1193, 1751, 1538, -1000, 1193, 1193, 1681, 1610, -1000, - -1000, 1193, 1193, 1193, 1193, 1193, 1193, 1193, 1193, 1193, - 1193, 1193, 1193, 1193, 1193, 1193, 1193, 1193, 1193, 1193, - 1193, 1544, 1708, -1000, 203, 1538, 2202, -1000, -1000, 6276, - -1000, -1000, 2610, 2622, 0, -1000, -1000, 221, 0, 1395, - 1019, 1538, 1538, 1019, 1680, 1193, 1635, 1560, 1193, 1193, - 32885, -1000, 2619, 2603, 1686, -1000, -1000, 38813, 1686, 38813, - 889, 2669, -290, 22511, 22511, 2335, 1269, -1000, -1000, -1000, - -1000, 1455, 1451, -1000, 1447, -1000, 2727, -1000, 1395, -1000, - 2048, 223, -1000, 794, 1949, -1000, 2590, 1395, 54374, 1395, - 54, 2610, -1000, 1193, -1000, 2048, 2048, 2048, 2048, 2048, - 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, - 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, - 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, - 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, - 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, - 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, - 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, - 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, - 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, - 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, - 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, - 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, - 2048, 2048, 2048, 2048, 2048, 2048, -1000, -1000, 54374, 1570, - -1000, -1000, 2554, 1696, 166, -1000, 1549, 1968, -1000, -1000, - 216, -1000, 22511, -1000, 38072, 1426, 1385, -1000, -1000, -1000, - -1000, -543, -1000, -1000, -1000, -1000, -1000, -1000, 380, 1966, - -1000, 959, 54374, 55856, -1000, 2282, -1000, -1000, -1000, -1000, - 22511, -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, 22511, -1000, - 1538, 2200, -1000, -377, -1000, -504, 22511, -280, -1000, -1000, - -280, -1000, -1000, -1000, -1000, -1000, 22511, -1000, -1000, 22511, - -1000, 22511, -1000, -1000, 1686, -1000, -1000, -1000, 37331, -1000, - 1686, -1000, 1686, -1000, -290, -1000, 1963, -1000, 54374, 1395, - 375, -1000, 1267, -1000, -1000, -1000, -1000, -1000, 55115, 54374, - 1949, 54374, -1000, -1000, 1684, 1538, 2048, 2590, -1000, 1660, - -1000, 380, -1000, 2050, 2171, -1000, -1000, -1000, 20288, -1000, - -1000, -1000, -1000, -1000, 262, -197, 16571, 12089, 1609, -1000, - -196, 1193, 1544, -1000, -477, -1000, -1000, -1000, -1000, 279, - -1000, -1000, 1958, -1000, -1000, 1548, 1537, 1475, -1000, -1000, - -1000, -1000, -1000, -1000, -290, -1000, -1000, 2553, -1000, -254, - -1000, -1000, 1952, 1529, -1000, -1000, -1000, 32144, 53633, -1000, - -184, 382, -197, 22511, 2049, 1538, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -39, -1000, -1000, 772, -1000, -1000, - -1000, 2179, -216, -1000, -1000, -1000, 307, -494, -288, -296, - 24734, -1000, 22511, -1000, 22511, -1000, 22511, -1000, 54374, 2048, - -1000, -1000, -1000, 1483, -1000, 4202, -401, 2195, -1000, -145, - -1000, -1000, -1000, 1203, 1364, -1000, -1000, -1000, -1000, -1000, - -1000, 1414, 54374, -1000, 436, -1000, -1000, 15824, -198, -233, - 1020, -1000, -1000, -1000, -1000, -1000, 1138, 1466, 1357, 1193, - -1000, 54374, -1000, 53633, -395, 882, 6276, -1000, 2193, 2190, - 2704, -1000, -1000, -1000, -1000, -1000, -1000, -549, 1464, 244, - -1000, -1000, -1000, 307, -314, -1000, 22511, -1000, 22511, -1000, - 1538, -1000, -1000, 2535, 54, -1000, 2724, -1000, 2691, 1031, - 1031, -1000, 1242, -549, -1000, -1000, -1000, -1000, 1193, 1193, - -1000, -403, -1000, -1000, -1000, -1000, -1000, 433, 1281, -1000, - -1000, -1000, -1000, -1000, 6276, -1000, -1000, -1000, 204, 204, - -1000, -1000, + -1000, 22860, -1000, 1406, 2181, -1000, -375, -1000, -504, 22860, + -259, -1000, -1000, -259, -1000, -1000, -1000, -1000, -1000, 22860, + -1000, -1000, 22860, -1000, 22860, -1000, -1000, 1542, -1000, -1000, + -1000, 37700, -1000, 1542, -1000, 1542, -1000, -270, -1000, 1882, + -1000, 54766, 1393, 345, -1000, 1162, -1000, -1000, -1000, -1000, + -1000, 55508, 54766, 1907, 54766, -1000, -1000, 1508, 1406, 1830, + 2569, -1000, 1482, -1000, 392, -1000, 2002, 2012, -1000, -1000, + -1000, 20634, -1000, -1000, -1000, -1000, -1000, 249, -185, 16912, + 12424, 1480, -1000, -184, 1538, 1478, -1000, -479, -1000, -1000, + -1000, -1000, 279, -1000, -1000, 1851, -1000, -1000, 1621, 1527, + 1444, -1000, -1000, -1000, -1000, -1000, -1000, -270, -1000, -1000, + 2525, -1000, -226, -1000, -1000, 1367, 1442, -1000, -1000, -1000, + 32506, 54024, -1000, -171, 293, -185, 22860, 1995, 1406, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -52, -1000, -1000, + 799, -1000, -1000, -1000, 2140, -189, -1000, -1000, -1000, 284, + -494, -291, -292, 25086, -1000, 22860, -1000, 22860, -1000, 22860, + -1000, 54766, 1830, -1000, -1000, -1000, 1426, -1000, 4978, -334, + 2177, -1000, -126, -1000, -1000, -1000, 1042, 1359, -1000, -1000, + -1000, -1000, -1000, -1000, 1936, 54766, -1000, 446, -1000, -1000, + 16164, -186, -201, 1000, -1000, -1000, -1000, -1000, -1000, 2007, + 1365, 1353, 1538, -1000, 54766, -1000, 54024, -314, 899, 6271, + -1000, 2164, 2159, 2656, -1000, -1000, -1000, -1000, -1000, -1000, + -544, 1356, 243, -1000, -1000, -1000, 284, -338, -1000, 22860, + -1000, 22860, -1000, 1406, -1000, -1000, 2494, 75, -1000, 2658, + -1000, 2643, 1016, 1016, -1000, 1136, -544, -1000, -1000, -1000, + -1000, 1538, 1538, -1000, -354, -1000, -1000, -1000, -1000, -1000, + 440, 1275, -1000, -1000, -1000, -1000, -1000, 6271, -1000, -1000, + -1000, 263, 263, -1000, -1000, } var yyPgo = [...]int{ - 0, 3435, 3430, 31, 7, 40, 39, 3428, 3426, 3423, - 171, 3421, 3420, 3419, 3411, 3410, 3407, 2789, 2782, 2755, - 3406, 3404, 3400, 3393, 3392, 3391, 3390, 3383, 3381, 27, - 114, 100, 101, 212, 192, 3380, 169, 161, 198, 3378, - 3373, 3372, 125, 182, 82, 81, 187, 3370, 3369, 68, - 3368, 3353, 3284, 180, 179, 177, 1100, 3282, 176, 111, - 47, 3279, 3277, 3275, 3274, 3268, 3261, 3260, 3258, 3257, - 3256, 3254, 3252, 3251, 3247, 3244, 3242, 3240, 3229, 308, - 3228, 3225, 16, 3223, 73, 3220, 3213, 3210, 3208, 3206, - 9, 3203, 3201, 32, 41, 55, 3197, 3195, 44, 3194, - 3193, 3192, 3191, 3187, 79, 3185, 14, 3184, 35, 3183, - 3179, 124, 3177, 3176, 3174, 42, 3173, 3171, 3170, 11, - 162, 3169, 3166, 138, 3163, 3158, 3147, 164, 221, 3144, - 2357, 3143, 93, 3142, 3141, 3140, 160, 191, 3138, 120, - 3137, 3136, 3135, 148, 3128, 3300, 3126, 3124, 67, 62, - 168, 3123, 3117, 201, 80, 8, 3116, 22, 23, 3115, - 3114, 72, 61, 3113, 98, 3110, 3108, 94, 99, 3107, - 109, 90, 3106, 3099, 10, 5, 3098, 1, 4, 2, - 115, 3091, 3088, 108, 3087, 3086, 3084, 97, 3081, 3077, - 4382, 3071, 83, 129, 96, 64, 3070, 170, 165, 3069, - 3068, 3065, 3064, 3059, 3058, 49, 3054, 3051, 3050, 136, - 1270, 116, 3049, 145, 352, 51, 146, 3048, 193, 75, - 3042, 163, 3041, 3038, 133, 132, 3031, 3030, 53, 166, - 197, 3028, 92, 128, 117, 183, 91, 130, 3027, 3026, - 59, 58, 3011, 3008, 2993, 2985, 167, 2983, 2982, 63, - 2977, 56, 2973, 195, 2967, 340, 77, 2964, 185, 158, - 2963, 66, 2949, 2948, 85, 95, 57, 29, 2946, 153, - 159, 126, 178, 2945, 2944, 52, 2941, 2939, 2938, 190, - 319, 2930, 2928, 323, 174, 140, 144, 87, 2925, 266, - 2923, 2917, 2913, 19, 5078, 7104, 181, 17, 155, 2912, - 2904, 8002, 48, 43, 13, 2897, 211, 2896, 194, 2892, - 2891, 2881, 203, 208, 105, 156, 54, 2880, 2879, 2878, - 2876, 71, 2870, 2869, 2866, 2862, 2860, 2859, 38, 37, - 36, 70, 196, 60, 25, 89, 154, 149, 65, 2858, - 2857, 2851, 121, 78, 2848, 152, 151, 123, 104, 2844, - 175, 141, 118, 2841, 102, 34, 2837, 2829, 2828, 2826, - 84, 2825, 2824, 2822, 2821, 150, 142, 119, 74, 2820, - 76, 113, 147, 143, 50, 2817, 45, 2814, 2812, 33, - 184, 28, 2811, 21, 106, 110, 2807, 6623, 2805, 12, - 265, 157, 2804, 2802, 15, 18, 6, 2799, 2797, 2794, - 2793, 131, 2788, 2784, 2783, 2775, 26, 46, 24, 20, - 112, 137, 69, 2773, 2761, 139, 2760, 2759, 2754, 0, - 1040, 127, 2722, 204, + 0, 3243, 3242, 31, 6, 37, 36, 3240, 3239, 3238, + 174, 3237, 3235, 3224, 3223, 3222, 3221, 2754, 2745, 2728, + 3220, 3219, 3218, 3215, 3213, 3211, 3210, 3209, 3208, 38, + 116, 91, 104, 201, 213, 3205, 170, 161, 197, 3203, + 3202, 3201, 115, 187, 84, 92, 195, 3200, 3199, 71, + 3196, 3194, 3192, 190, 189, 188, 1092, 3190, 184, 114, + 48, 3189, 3187, 3184, 3182, 3179, 3178, 3176, 3174, 3173, + 3172, 3170, 3169, 3168, 3165, 3163, 3162, 3161, 3160, 285, + 3157, 3155, 12, 3154, 73, 3153, 3152, 3149, 3147, 3146, + 8, 3140, 3139, 26, 45, 63, 3136, 3134, 44, 3132, + 3131, 3129, 3125, 3124, 72, 3123, 27, 3121, 40, 3120, + 3117, 124, 3115, 3111, 3105, 42, 3104, 3100, 3098, 28, + 169, 3097, 3094, 140, 3093, 3092, 3087, 167, 205, 3085, + 2265, 3082, 99, 3080, 3079, 3077, 166, 193, 3076, 125, + 3074, 3073, 3072, 152, 3068, 3263, 3066, 3065, 59, 80, + 164, 3064, 3062, 199, 79, 54, 3061, 17, 19, 3060, + 3058, 67, 70, 3057, 108, 3056, 3054, 103, 77, 3052, + 105, 102, 3051, 3050, 5, 7, 3048, 1, 4, 2, + 83, 3047, 3044, 119, 3041, 3040, 3039, 101, 3037, 3035, + 4407, 3031, 93, 130, 106, 76, 3028, 171, 132, 3027, + 3026, 3022, 3021, 3016, 2985, 49, 2984, 2983, 2982, 136, + 22, 110, 2981, 146, 346, 51, 147, 2978, 196, 78, + 2977, 172, 2971, 2969, 134, 133, 2968, 2962, 58, 168, + 194, 2960, 96, 129, 120, 176, 95, 135, 2954, 2946, + 56, 62, 2944, 2943, 2940, 2939, 181, 2935, 2929, 61, + 2927, 53, 2925, 182, 2924, 306, 69, 2922, 180, 162, + 2921, 60, 2919, 2918, 68, 100, 55, 39, 2914, 157, + 160, 127, 165, 2913, 2912, 52, 2905, 2899, 2894, 200, + 301, 2891, 2890, 329, 183, 143, 149, 85, 2887, 323, + 2886, 2885, 2884, 16, 5094, 7550, 186, 25, 159, 2883, + 2882, 8334, 20, 43, 24, 2872, 210, 2869, 185, 2867, + 2866, 2865, 211, 207, 112, 158, 57, 2864, 2859, 2857, + 2856, 64, 2855, 2850, 2849, 2848, 2847, 2846, 35, 34, + 33, 75, 218, 65, 21, 98, 156, 151, 66, 2843, + 2841, 2839, 123, 90, 2835, 155, 153, 126, 163, 2829, + 175, 142, 117, 2823, 87, 32, 2822, 2821, 2819, 2818, + 89, 2813, 2810, 2806, 2805, 150, 144, 121, 81, 2803, + 82, 118, 148, 145, 50, 2799, 46, 2798, 2797, 30, + 191, 29, 2787, 13, 111, 109, 2780, 6691, 2779, 9, + 305, 154, 2776, 2775, 10, 11, 14, 2772, 2771, 2770, + 2769, 131, 2768, 2765, 2763, 2761, 23, 47, 18, 15, + 113, 137, 74, 2760, 2746, 141, 2732, 2727, 2722, 0, + 1034, 128, 2721, 202, } -//line sql.y:8755 +//line sql.y:8764 type yySymType struct { union any empty struct{} @@ -8594,63 +8636,63 @@ var yyR1 = [...]int{ 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, 23, 23, 23, 23, 23, 111, 111, - 112, 112, 112, 112, 114, 114, 114, 375, 375, 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, 380, 380, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 111, + 111, 112, 112, 112, 112, 114, 114, 114, 375, 375, + 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, 380, 380, 380, 380, 380, 380, 380, 380, 380, 380, 380, 380, 380, 380, 380, 380, 380, 380, 380, 380, 380, - 341, 341, 341, 374, 374, 376, 113, 113, 113, 113, - 113, 113, 113, 113, 113, 113, 117, 116, 116, 115, - 118, 118, 118, 118, 118, 118, 118, 118, 378, 378, - 378, 63, 63, 379, 328, 329, 330, 5, 6, 355, - 377, 125, 125, 24, 39, 39, 25, 25, 25, 25, - 26, 26, 64, 67, 67, 65, 65, 65, 65, 65, + 380, 341, 341, 341, 374, 374, 376, 113, 113, 113, + 113, 113, 113, 113, 113, 113, 113, 117, 116, 116, + 115, 118, 118, 118, 118, 118, 118, 118, 118, 378, + 378, 378, 63, 63, 379, 328, 329, 330, 5, 6, + 355, 377, 125, 125, 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, 65, 65, 65, 65, 65, 65, - 65, 292, 292, 281, 281, 290, 290, 280, 280, 306, - 306, 306, 283, 283, 283, 284, 284, 403, 403, 403, - 277, 277, 66, 66, 66, 307, 307, 307, 307, 69, - 69, 413, 413, 414, 414, 415, 415, 415, 70, 71, - 71, 310, 310, 311, 311, 72, 73, 85, 85, 85, - 85, 85, 86, 86, 86, 86, 86, 86, 110, 110, - 110, 10, 10, 10, 10, 81, 81, 81, 9, 9, - 11, 68, 68, 75, 400, 400, 401, 402, 402, 402, - 402, 76, 78, 27, 27, 27, 27, 27, 27, 135, - 135, 123, 123, 123, 123, 123, 123, 123, 123, 123, - 123, 123, 123, 130, 130, 130, 124, 124, 422, 79, - 80, 80, 128, 128, 128, 121, 121, 121, 127, 127, - 127, 12, 12, 13, 263, 263, 14, 14, 134, 134, - 133, 133, 136, 136, 136, 136, 136, 136, 136, 136, - 136, 136, 136, 131, 131, 132, 132, 132, 132, 299, - 299, 299, 298, 298, 167, 167, 169, 168, 168, 170, - 170, 171, 171, 171, 171, 217, 217, 193, 193, 256, - 256, 257, 257, 255, 255, 262, 262, 258, 258, 258, - 258, 265, 265, 172, 172, 172, 172, 180, 180, 181, - 181, 182, 182, 309, 309, 304, 304, 304, 303, 303, - 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, 411, - 411, 159, 159, 159, 159, 159, 159, 159, 162, 162, - 162, 162, 162, 162, 162, 162, 162, 162, 162, 246, - 246, 150, 150, 150, 150, 150, 150, 150, 150, 150, - 150, 150, 150, 150, 150, 150, 153, 153, 153, 153, + 65, 65, 292, 292, 281, 281, 290, 290, 280, 280, + 306, 306, 306, 283, 283, 283, 284, 284, 403, 403, + 403, 277, 277, 66, 66, 66, 307, 307, 307, 307, + 69, 69, 413, 413, 414, 414, 415, 415, 415, 70, + 71, 71, 310, 310, 311, 311, 72, 73, 85, 85, + 85, 85, 85, 86, 86, 86, 86, 86, 86, 110, + 110, 110, 10, 10, 10, 10, 81, 81, 81, 9, + 9, 11, 68, 68, 75, 400, 400, 401, 402, 402, + 402, 402, 76, 78, 27, 27, 27, 27, 27, 27, + 135, 135, 123, 123, 123, 123, 123, 123, 123, 123, + 123, 123, 123, 123, 130, 130, 130, 124, 124, 422, + 79, 80, 80, 128, 128, 128, 121, 121, 121, 127, + 127, 127, 12, 12, 13, 263, 263, 14, 14, 134, + 134, 133, 133, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 131, 131, 132, 132, 132, 132, + 299, 299, 299, 298, 298, 167, 167, 169, 168, 168, + 170, 170, 171, 171, 171, 171, 217, 217, 193, 193, + 256, 256, 257, 257, 255, 255, 262, 262, 258, 258, + 258, 258, 265, 265, 172, 172, 172, 172, 180, 180, + 181, 181, 182, 182, 309, 309, 304, 304, 304, 303, + 303, 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, + 411, 411, 159, 159, 159, 159, 159, 159, 159, 162, + 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, + 246, 246, 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, 222, 222, 221, 221, 87, 87, 87, 88, 88, - 89, 89, 89, 89, 89, 90, 90, 90, 90, 90, - 90, 90, 92, 92, 91, 91, 212, 212, 296, 296, - 93, 94, 94, 95, 95, 98, 98, 97, 96, 96, - 102, 102, 99, 99, 101, 101, 100, 103, 103, 104, - 105, 105, 278, 278, 199, 199, 208, 208, 208, 208, - 200, 200, 201, 201, 201, 201, 201, 201, 209, 209, - 209, 216, 210, 210, 206, 206, 204, 204, 204, 204, - 204, 204, 204, 204, 204, 204, 204, 205, 205, 205, + 153, 153, 222, 222, 221, 221, 87, 87, 87, 88, + 88, 89, 89, 89, 89, 89, 90, 90, 90, 90, + 90, 90, 90, 92, 92, 91, 91, 212, 212, 296, + 296, 93, 94, 94, 95, 95, 98, 98, 97, 96, + 96, 102, 102, 99, 99, 101, 101, 100, 103, 103, + 104, 105, 105, 278, 278, 199, 199, 208, 208, 208, + 208, 200, 200, 201, 201, 201, 201, 201, 201, 209, + 209, 209, 216, 210, 210, 206, 206, 204, 204, 204, + 204, 204, 204, 204, 204, 204, 204, 204, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, @@ -8670,36 +8712,36 @@ var yyR1 = [...]int{ 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, - 164, 164, 164, 164, 227, 227, 151, 151, 151, 151, + 205, 164, 164, 164, 164, 227, 227, 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, 317, 317, 119, 119, 119, 119, + 151, 151, 152, 152, 165, 165, 165, 165, 166, 166, + 166, 166, 166, 166, 166, 317, 317, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, - 119, 119, 119, 119, 119, 119, 120, 120, 120, 120, + 119, 119, 119, 119, 119, 119, 119, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 423, 423, 332, 332, 332, 207, - 207, 207, 207, 207, 126, 126, 126, 126, 126, 314, - 314, 314, 318, 318, 318, 316, 316, 316, 316, 316, + 120, 120, 120, 120, 120, 423, 423, 332, 332, 332, + 207, 207, 207, 207, 207, 126, 126, 126, 126, 126, + 314, 314, 314, 318, 318, 318, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, - 319, 319, 225, 225, 122, 122, 223, 223, 224, 226, - 226, 218, 218, 218, 218, 220, 220, 203, 203, 203, - 228, 228, 320, 320, 229, 229, 106, 107, 107, 108, - 108, 230, 230, 232, 231, 231, 233, 234, 234, 234, - 235, 235, 236, 236, 236, 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, - 253, 253, 253, 253, 253, 253, 253, 237, 237, 237, - 244, 244, 244, 240, 240, 242, 242, 242, 243, 243, - 243, 241, 250, 250, 252, 252, 251, 251, 247, 247, - 248, 248, 249, 249, 249, 245, 245, 202, 202, 202, - 202, 202, 254, 254, 254, 254, 308, 308, 308, 266, - 266, 213, 213, 215, 215, 214, 214, 163, 267, 267, - 275, 272, 272, 273, 273, 300, 300, 300, 276, 276, - 289, 289, 285, 285, 286, 286, 279, 279, 291, 291, - 291, 77, 211, 211, 371, 371, 368, 295, 295, 297, - 297, 301, 301, 305, 305, 302, 302, 8, 416, 416, - 416, 293, 293, 293, 293, 293, 293, 293, 293, 293, + 316, 319, 319, 225, 225, 122, 122, 223, 223, 224, + 226, 226, 218, 218, 218, 218, 220, 220, 203, 203, + 203, 228, 228, 320, 320, 229, 229, 106, 107, 107, + 108, 108, 230, 230, 232, 231, 231, 233, 234, 234, + 234, 235, 235, 236, 236, 236, 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, 253, 253, 253, 253, 253, 253, 253, 237, 237, + 237, 244, 244, 244, 240, 240, 242, 242, 242, 243, + 243, 243, 241, 250, 250, 252, 252, 251, 251, 247, + 247, 248, 248, 249, 249, 249, 245, 245, 202, 202, + 202, 202, 202, 254, 254, 254, 254, 308, 308, 308, + 266, 266, 213, 213, 215, 215, 214, 214, 163, 267, + 267, 275, 272, 272, 273, 273, 300, 300, 300, 276, + 276, 289, 289, 285, 285, 286, 286, 279, 279, 291, + 291, 291, 77, 211, 211, 371, 371, 368, 295, 295, + 297, 297, 301, 301, 305, 305, 302, 302, 8, 416, + 416, 416, 293, 293, 293, 293, 293, 293, 293, 293, 293, 293, 293, 293, 293, 293, 293, 293, 293, 293, 293, 293, 293, 293, 293, 293, 293, 293, 293, 293, 293, 293, 293, 293, 293, 293, 293, 293, 293, 293, @@ -8714,7 +8756,7 @@ var yyR1 = [...]int{ 293, 293, 293, 293, 293, 293, 293, 293, 293, 293, 293, 293, 293, 293, 293, 293, 293, 293, 293, 293, 293, 293, 293, 293, 293, 293, 293, 293, 293, 293, - 293, 293, 293, 293, 294, 294, 294, 294, 294, 294, + 293, 293, 293, 293, 293, 294, 294, 294, 294, 294, 294, 294, 294, 294, 294, 294, 294, 294, 294, 294, 294, 294, 294, 294, 294, 294, 294, 294, 294, 294, 294, 294, 294, 294, 294, 294, 294, 294, 294, 294, @@ -8762,7 +8804,7 @@ var yyR1 = [...]int{ 294, 294, 294, 294, 294, 294, 294, 294, 294, 294, 294, 294, 294, 294, 294, 294, 294, 294, 294, 294, 294, 294, 294, 294, 294, 294, 294, 294, 294, 294, - 294, 419, 420, 312, 313, 313, 313, + 294, 294, 294, 419, 420, 312, 313, 313, 313, } var yyR2 = [...]int{ @@ -8826,111 +8868,111 @@ var yyR2 = [...]int{ 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, 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, 5, 3, - 5, 0, 1, 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, 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, - 1, 2, 1, 1, 1, 1, 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, 1, - 1, 3, 4, 3, 4, 4, 4, 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, 1, 0, 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, 1, 3, 4, 6, 4, 4, 8, 8, - 6, 8, 6, 5, 4, 10, 2, 2, 1, 2, - 2, 2, 2, 2, 5, 6, 6, 6, 6, 6, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 8, 4, 8, 8, 6, 5, 4, 4, 4, 5, - 7, 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, + 5, 5, 7, 7, 5, 5, 5, 5, 6, 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, 5, + 3, 5, 0, 1, 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, 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, 1, 2, 1, 1, 1, 1, 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, + 1, 1, 3, 4, 3, 4, 4, 4, 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, 1, 0, 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, 1, 3, 4, 6, 4, 4, 8, + 8, 6, 8, 6, 5, 4, 10, 2, 2, 1, + 2, 2, 2, 2, 2, 5, 6, 6, 6, 6, + 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 8, 4, 8, 8, 6, 5, 4, 4, 4, + 5, 7, 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, 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, + 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, - 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, 4, 0, 2, 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, 3, 1, 6, - 5, 4, 1, 3, 3, 5, 0, 2, 5, 0, - 5, 1, 3, 1, 2, 3, 4, 1, 1, 3, - 3, 1, 2, 1, 1, 1, 1, 1, 1, 1, - 0, 1, 0, 2, 0, 3, 0, 1, 0, 1, - 1, 5, 0, 1, 0, 1, 2, 1, 1, 1, - 1, 1, 1, 0, 1, 1, 1, 3, 0, 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, 4, 0, 2, 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, 3, 1, + 6, 5, 4, 1, 3, 3, 5, 0, 2, 5, + 0, 5, 1, 3, 1, 2, 3, 4, 1, 1, + 3, 3, 1, 2, 1, 1, 1, 1, 1, 1, + 1, 0, 1, 0, 2, 0, 3, 0, 1, 0, + 1, 1, 5, 0, 1, 0, 1, 2, 1, 1, + 1, 1, 1, 1, 0, 1, 1, 1, 3, 0, 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, @@ -8994,7 +9036,7 @@ 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, 0, 0, 1, 1, + 1, 1, 1, 1, 1, 0, 0, 1, 1, } var yyChk = [...]int{ @@ -9004,157 +9046,156 @@ var yyChk = [...]int{ -27, -28, -74, -75, -76, -77, -78, -12, -13, -14, -8, -32, -31, -30, 13, 14, -109, -35, 35, -40, -50, 240, -51, -41, 241, -52, 243, 242, 280, 244, - 396, 273, 83, 329, 330, 332, 333, 334, 335, -110, - 703, 278, 279, 246, 39, 51, 36, 37, 40, 250, - 286, 287, 249, 145, -33, -36, 12, -419, 15, 486, - 275, 274, 31, -34, 596, 95, -80, -418, 751, -253, + 397, 273, 83, 330, 331, 333, 334, 335, 336, -110, + 704, 278, 279, 246, 39, 51, 36, 37, 40, 250, + 286, 287, 249, 145, -33, -36, 12, -419, 15, 487, + 275, 274, 31, -34, 597, 95, -80, -418, 752, -253, -237, 26, 36, 32, -236, -232, -128, -237, 24, 22, 11, -79, -79, -79, 16, 17, -79, -356, -358, 95, 173, 95, -79, -57, -56, -54, -53, -55, -58, 34, -47, -48, -380, -46, -43, 245, 242, 290, 135, 136, 280, 281, 282, 244, 264, 279, 283, 278, 299, -42, - 90, 36, 596, 599, -363, 241, 247, 248, 243, 487, - 138, 137, 84, -360, 391, 630, 721, -58, 723, 110, - 113, 722, 50, 254, 724, 725, 726, 637, 727, 263, - 728, 729, 730, 731, 737, 678, 738, 739, 740, 139, - 11, -79, -305, -301, 100, -294, 593, 266, 628, 440, - 629, 315, 90, 47, 42, 532, 603, 387, 391, 630, - 517, 721, 397, 329, 347, 341, 522, 523, 524, 370, - 362, 594, 631, 604, 318, 267, 303, 715, 360, 149, - 723, 322, 632, 281, 398, 399, 633, 400, 110, 332, - 437, 736, 321, 634, 734, 113, 722, 337, 88, 516, - 57, 718, 50, 276, 445, 446, 358, 249, 354, 724, - 304, 635, 606, 297, 138, 135, 743, 39, 350, 56, - 33, 733, 137, 55, 725, 164, 636, 726, 637, 402, - 377, 709, 54, 403, 282, 638, 93, 287, 598, 326, - 717, 404, 537, 351, 405, 314, 732, 246, 639, 325, - 698, 690, 691, 406, 407, 710, 382, 378, 383, 539, - 640, 429, 521, 408, 694, 695, 750, 58, 641, 642, - 711, 136, 643, 87, 727, 89, 345, 346, 644, 312, - 265, 542, 543, 431, 374, 499, 127, 506, 507, 120, - 121, 502, 122, 508, 123, 128, 509, 510, 511, 500, - 124, 117, 501, 512, 513, 375, 376, 125, 514, 119, - 118, 503, 505, 126, 515, 263, 38, 409, 595, 316, - 64, 320, 291, 432, 52, 380, 747, 51, 705, 544, - 645, 708, 373, 369, 496, 59, 646, 647, 648, 649, - 518, 728, 372, 344, 368, 742, 4, 309, 491, 519, - 729, 68, 248, 385, 384, 386, 298, 428, 365, 650, - 651, 652, 270, 91, 653, 355, 25, 654, 655, 410, - 305, 656, 62, 657, 658, 435, 279, 659, 60, 730, - 45, 660, 284, 744, 731, 661, 662, 663, 704, 664, - 286, 665, 412, 666, 692, 693, 411, 379, 381, 545, - 293, 413, 396, 251, 597, 667, 327, 349, 283, 735, - 668, 271, 533, 534, 535, 536, 716, 541, 540, 285, - 290, 278, 436, 272, 669, 670, 671, 672, 673, 319, - 689, 674, 675, 333, 601, 737, 497, 49, 676, 677, - 678, 679, 680, 313, 308, 430, 439, 67, 92, 393, - 681, 682, 714, 343, 338, 43, 306, 99, 477, 479, - 480, 481, 482, 483, 478, 485, 683, 330, 61, 738, - 739, 740, 300, 741, 525, 526, 527, 528, 13, 579, - 562, 590, 563, 580, 564, 573, 565, 581, 589, 591, - 546, 554, 547, 555, 585, 568, 582, 574, 567, 566, - 588, 571, 575, 548, 556, 586, 572, 549, 557, 550, - 558, 551, 559, 584, 583, 576, 587, 552, 560, 578, - 553, 561, 577, 569, 570, 448, 748, 749, 520, 415, - 139, 310, 311, 53, 366, 292, 684, 323, 685, 356, - 357, 493, 494, 371, 342, 367, 146, 701, 331, 340, - 699, 294, 416, 498, 280, 686, 438, 307, 388, 131, - 339, 394, 324, 602, 538, 299, 417, 713, 600, 529, - 530, 364, 361, 301, 531, 687, 389, 703, 418, 255, - 295, 296, 688, 700, 419, 420, 317, 421, 422, 423, - 424, 425, 427, 328, 426, 702, 696, 697, 302, 476, - 599, 336, 359, 395, 458, 459, 460, 461, 462, 463, + 90, 36, 597, 600, -363, 241, 247, 248, 243, 488, + 138, 137, 84, -360, 392, 631, 722, -58, 724, 110, + 113, 723, 50, 254, 725, 726, 727, 638, 728, 263, + 729, 730, 731, 732, 738, 679, 739, 740, 741, 139, + 11, -79, -305, -301, 100, -294, 594, 266, 629, 441, + 630, 315, 90, 47, 42, 533, 604, 388, 392, 631, + 518, 722, 398, 330, 348, 342, 523, 524, 525, 371, + 363, 595, 632, 605, 318, 267, 303, 716, 361, 149, + 724, 322, 633, 281, 399, 400, 634, 401, 110, 333, + 438, 737, 321, 635, 735, 113, 723, 338, 88, 517, + 57, 719, 326, 50, 276, 446, 447, 359, 249, 355, + 725, 304, 636, 607, 297, 138, 135, 744, 39, 351, + 56, 33, 734, 137, 55, 726, 164, 637, 727, 638, + 403, 378, 710, 54, 404, 282, 639, 93, 287, 599, + 327, 718, 405, 538, 352, 406, 314, 733, 246, 640, + 325, 699, 691, 692, 407, 408, 711, 383, 379, 384, + 540, 641, 430, 522, 409, 695, 696, 751, 58, 642, + 643, 712, 136, 644, 87, 728, 89, 346, 347, 645, + 312, 265, 543, 544, 432, 375, 500, 127, 507, 508, + 120, 121, 503, 122, 509, 123, 128, 510, 511, 512, + 501, 124, 117, 502, 513, 514, 376, 377, 125, 515, + 119, 118, 504, 506, 126, 516, 263, 38, 410, 596, + 316, 64, 320, 291, 433, 52, 381, 748, 51, 706, + 545, 646, 709, 374, 370, 497, 59, 647, 648, 649, + 650, 519, 729, 373, 345, 369, 743, 4, 309, 492, + 520, 730, 68, 248, 386, 385, 387, 298, 429, 366, + 651, 652, 653, 270, 91, 654, 356, 25, 655, 656, + 411, 305, 657, 62, 658, 659, 436, 279, 660, 60, + 731, 45, 661, 284, 745, 732, 662, 663, 664, 705, + 665, 286, 666, 413, 667, 693, 694, 412, 380, 382, + 546, 293, 414, 397, 251, 598, 668, 328, 350, 283, + 736, 669, 271, 534, 535, 536, 537, 717, 542, 541, + 285, 290, 278, 437, 272, 670, 671, 672, 673, 674, + 319, 690, 675, 676, 334, 602, 738, 498, 49, 677, + 678, 679, 680, 681, 313, 308, 431, 440, 67, 92, + 394, 682, 683, 715, 344, 339, 43, 306, 99, 478, + 480, 481, 482, 483, 484, 479, 486, 684, 331, 61, + 739, 740, 741, 300, 742, 526, 527, 528, 529, 13, + 580, 563, 591, 564, 581, 565, 574, 566, 582, 590, + 592, 547, 555, 548, 556, 586, 569, 583, 575, 568, + 567, 589, 572, 576, 549, 557, 587, 573, 550, 558, + 551, 559, 552, 560, 585, 584, 577, 588, 553, 561, + 579, 554, 562, 578, 570, 571, 449, 749, 750, 521, + 416, 139, 310, 311, 53, 367, 292, 685, 323, 686, + 357, 358, 494, 495, 372, 343, 368, 146, 702, 332, + 341, 700, 294, 417, 499, 280, 687, 439, 307, 389, + 131, 340, 395, 324, 603, 539, 299, 418, 714, 601, + 530, 531, 365, 362, 301, 532, 688, 390, 704, 419, + 255, 295, 296, 689, 701, 420, 421, 317, 422, 423, + 424, 425, 426, 428, 329, 427, 703, 697, 698, 302, + 477, 600, 337, 360, 396, 459, 460, 461, 462, 463, 464, 465, 466, 467, 468, 469, 470, 471, 472, 473, - 474, 475, 495, 253, -79, 253, -190, -301, -130, 705, - 707, 192, -272, 399, -290, 401, 414, 409, 419, 407, - -281, 410, 412, 293, -403, 429, 253, 416, 240, 402, - 411, 420, 421, 317, 427, 422, 328, 426, 302, 423, - 424, 425, -387, 192, 726, 741, 331, 339, 149, 363, - 406, 404, 430, 705, 100, -307, 100, 101, 102, -294, - 331, -310, 336, -295, -387, -294, 334, -79, -79, -312, - -312, -130, -210, -145, 157, -159, -261, -162, 101, -150, - -153, -204, -205, -206, -207, -160, -220, -259, 181, 182, - 189, 158, -216, -163, 29, 592, 488, 487, 192, 34, - 235, 77, 78, 490, 491, 160, 63, 15, 453, 454, - -161, 443, 444, 455, 449, 450, 516, 518, 519, 520, - 517, 522, 523, 524, 525, 526, 527, 528, 529, 530, - 531, 521, 532, 493, 494, 129, 495, 117, 119, 118, - 127, 128, 496, 497, 498, 360, 544, 545, 539, 542, - 543, 541, 540, 375, 376, 499, 562, 563, 567, 566, - 564, 565, 568, 571, 572, 573, 574, 575, 576, 578, - 577, 569, 570, 547, 546, 548, 549, 550, 551, 552, - 553, 555, 554, 556, 557, 558, 559, 560, 561, 579, - 580, 581, 582, 583, 585, 584, 589, 588, 586, 587, - 591, 590, 500, 501, 120, 121, 122, 123, 124, 125, - 126, 502, 505, 503, 504, 506, 507, 508, 513, 514, - 509, 510, 511, 512, 515, 386, 384, 385, 381, 380, - 379, -89, -102, 619, 618, -103, 440, 445, 446, 448, - -151, -152, -165, -166, -295, -301, 258, 442, 252, 187, - 486, -154, -148, -218, 116, 102, -31, -214, 441, 451, - 452, 456, 447, 457, 605, 607, 622, 623, 625, 610, - 615, 614, 617, 533, 534, 535, 536, 537, 538, 690, - 691, 692, 693, 694, 695, 696, 697, -387, -294, 100, - -157, -155, -199, 103, 108, 111, 112, 114, -409, 276, - 356, 357, 130, -419, 719, -156, 105, 106, 107, 132, - 133, 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, 223, 224, 225, 226, 227, 228, 229, 230, 231, - 232, 233, 234, 98, 104, 50, 415, 415, -190, -79, - -79, -79, -79, -416, 722, 597, -230, -128, -232, -33, - -31, -419, 12, -79, -31, -32, -30, -36, -38, 624, - -37, -301, 109, -237, -253, 16, 67, 176, 48, 56, - -235, -236, -34, -31, -145, 23, 41, 27, -132, 183, - -145, -301, -132, -279, 257, -79, -79, -268, -315, 331, - -270, 430, 705, 429, -260, -273, 100, -259, -272, 428, - 101, -357, 173, -343, -347, -295, 268, -373, 264, -190, - -366, -365, -295, -419, -129, -289, 254, 262, 261, 150, - -390, 153, 310, 442, 252, -53, -54, -55, -272, 191, - 725, -111, 285, 289, 96, 96, -347, -346, -345, -391, - 289, 268, -372, -364, 260, 269, -353, 261, 262, -348, - 254, 151, -391, -348, 259, 269, 264, 268, 289, 289, - 139, 289, 139, 289, 289, 289, 289, 289, 289, 289, - 289, 289, 284, -354, 165, -354, 600, 600, -360, -391, - 264, 254, -391, -391, 260, -291, -348, 256, 28, 256, - 38, 38, -354, -354, -354, -272, 191, -354, -354, -354, - -354, 297, 297, -354, -354, -354, -354, -354, -354, -354, + 474, 475, 476, 496, 253, -79, 253, -190, -301, -130, + 706, 708, 192, -272, 400, -290, 402, 415, 410, 420, + 408, -281, 411, 413, 293, -403, 430, 253, 417, 240, + 403, 412, 421, 422, 317, 428, 423, 329, 427, 302, + 424, 425, 426, -387, 192, 727, 742, 332, 340, 149, + 364, 407, 405, 431, 706, 100, -307, 100, 101, 102, + -294, 332, -310, 337, -295, -387, -294, 335, -79, -79, + -312, -312, -130, -210, -145, 157, -159, -261, -162, 101, + -150, -153, -204, -205, -206, -207, -160, -220, -259, 181, + 182, 189, 158, -216, -163, 29, 593, 489, 488, 192, + 34, 235, 77, 78, 491, 492, 160, 63, 15, 454, + 455, -161, 444, 445, 456, 450, 451, 517, 519, 520, + 521, 518, 523, 524, 525, 526, 527, 528, 529, 530, + 531, 532, 522, 533, 494, 495, 129, 496, 117, 119, + 118, 127, 128, 497, 498, 499, 361, 545, 546, 540, + 543, 544, 542, 541, 376, 377, 500, 563, 564, 568, + 567, 565, 566, 569, 572, 573, 574, 575, 576, 577, + 579, 578, 570, 571, 548, 547, 549, 550, 551, 552, + 553, 554, 556, 555, 557, 558, 559, 560, 561, 562, + 580, 581, 582, 583, 584, 586, 585, 590, 589, 587, + 588, 592, 591, 501, 502, 120, 121, 122, 123, 124, + 125, 126, 503, 506, 504, 505, 507, 508, 509, 514, + 515, 510, 511, 512, 513, 516, 387, 385, 386, 382, + 381, 380, -89, -102, 620, 619, -103, 441, 446, 447, + 449, -151, -152, -165, -166, -295, -301, 258, 443, 252, + 187, 487, -154, -148, -218, 116, 102, -31, -214, 442, + 452, 453, 457, 448, 458, 606, 608, 623, 624, 626, + 611, 616, 615, 618, 534, 535, 536, 537, 538, 539, + 691, 692, 693, 694, 695, 696, 697, 698, -387, -294, + 100, -157, -155, -199, 103, 108, 111, 112, 114, -409, + 276, 357, 358, 130, -419, 720, -156, 105, 106, 107, + 132, 133, 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, 223, 224, 225, 226, 227, 228, 229, 230, + 231, 232, 233, 234, 98, 104, 50, 416, 416, -190, + -79, -79, -79, -79, -416, 723, 598, -230, -128, -232, + -33, -31, -419, 12, -79, -31, -32, -30, -36, -38, + 625, -37, -301, 109, -237, -253, 16, 67, 176, 48, + 56, -235, -236, -34, -31, -145, 23, 41, 27, -132, + 183, -145, -301, -132, -279, 257, -79, -79, -268, -315, + 332, -270, 431, 706, 430, -260, -273, 100, -259, -272, + 429, 101, -357, 173, -343, -347, -295, 268, -373, 264, + -190, -366, -365, -295, -419, -129, -289, 254, 262, 261, + 150, -390, 153, 310, 443, 252, -53, -54, -55, -272, + 191, 726, -111, 285, 289, 96, 96, -347, -346, -345, + -391, 289, 268, -372, -364, 260, 269, -353, 261, 262, + -348, 254, 151, -391, -348, 259, 269, 264, 268, 289, + 289, 139, 289, 139, 289, 289, 289, 289, 289, 289, + 289, 289, 289, 284, -354, 165, -354, 601, 601, -360, + -391, 264, 254, -391, -391, 260, -291, -348, 256, 28, + 256, 38, 38, -354, -354, -354, -272, 191, -354, -354, + -354, -354, 297, 297, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, - 253, -390, -137, 426, 317, 90, -56, 299, -39, -190, - -289, 254, 255, -390, 286, -190, 236, 253, 708, -283, - 173, 19, -283, -280, 415, 413, 400, 405, -283, -283, - -283, -283, 300, 398, -349, 254, 38, 265, 415, 300, - 398, 300, 301, 300, 301, 408, 418, 300, -306, 18, - 176, 442, 403, 407, 293, 253, 294, 255, 417, 301, - -306, 98, -284, 173, 300, 415, 409, 296, -283, -283, - -313, -419, -297, -295, -293, 245, 41, 156, 28, 30, - 159, 192, 142, 23, 160, 40, 247, 363, 264, 191, - 260, 487, 240, 81, 605, 443, 450, 441, 449, 453, - 489, 490, 442, 401, 34, 17, 607, 31, 274, 27, - 44, 185, 242, 163, 608, 277, 29, 275, 129, 133, - 610, 26, 84, 269, 18, 262, 46, 20, 611, 612, - 21, 74, 258, 257, 176, 254, 79, 15, 235, 32, - 172, 75, 613, 151, 145, 614, 615, 616, 617, 143, - 77, 173, 24, 745, 451, 452, 36, 706, 592, 288, - 187, 82, 65, 707, 157, 447, 618, 619, 130, 620, - 134, 85, 712, 153, 22, 80, 48, 621, 289, 622, - 259, 746, 623, 433, 624, 174, 243, 486, 78, 175, - 719, 625, 720, 252, 414, 12, 492, 35, 273, 261, - 73, 72, 141, 76, 457, 626, 253, 162, 256, 144, - 132, 11, 150, 37, 16, 83, 86, 454, 455, 456, - 63, 140, 596, 161, 19, 627, 434, 155, -387, 708, - -313, -313, 300, 340, 35, 101, -413, -414, -415, 596, - 433, 256, -295, -190, -85, 698, 244, -86, 704, 41, - 251, 146, 38, -135, 415, -123, 192, 726, 709, 710, - 711, 708, 412, 716, 714, 712, 300, 713, 96, 153, - 155, 156, 4, -145, 172, -200, -201, 171, 165, 166, - 167, 168, 169, 170, 177, 176, 157, 159, 173, -246, - 154, 178, 179, 180, 181, 182, 183, 184, 186, 185, - 187, 188, 174, 175, 191, 238, 239, -153, -153, -153, - -153, -216, -222, -221, -419, -218, -387, -294, -301, -419, - -419, -153, -278, -419, -150, -419, -419, -419, -419, -419, - -225, -145, -419, -419, -423, -419, -423, -423, -423, -332, - -419, -332, -332, -419, -419, -419, -419, -419, -419, -419, - -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, + -354, 253, -390, -137, 427, 317, 90, -56, 299, -39, + -190, -289, 254, 255, -390, 286, -190, 236, 253, 709, + -283, 173, 19, -283, -280, 416, 414, 401, 406, -283, + -283, -283, -283, 300, 399, -349, 254, 38, 265, 416, + 300, 399, 300, 301, 300, 301, 409, 419, 300, -306, + 18, 176, 443, 404, 408, 293, 253, 294, 255, 418, + 301, -306, 98, -284, 173, 300, 416, 410, 296, -283, + -283, -313, -419, -297, -295, -293, 245, 41, 156, 28, + 30, 159, 192, 142, 23, 160, 40, 247, 364, 264, + 191, 260, 488, 240, 81, 606, 444, 451, 442, 450, + 454, 490, 491, 443, 402, 34, 17, 608, 31, 274, + 27, 44, 185, 242, 163, 609, 277, 29, 275, 129, + 133, 611, 26, 84, 269, 18, 262, 46, 20, 612, + 613, 21, 74, 258, 257, 176, 254, 79, 15, 235, + 32, 172, 75, 614, 151, 145, 615, 616, 617, 618, + 143, 77, 173, 24, 746, 452, 453, 36, 707, 593, + 288, 187, 82, 65, 708, 157, 448, 619, 620, 130, + 621, 134, 85, 713, 153, 22, 80, 48, 622, 289, + 623, 259, 747, 624, 434, 625, 174, 243, 487, 78, + 175, 720, 626, 721, 252, 415, 12, 493, 35, 273, + 261, 73, 72, 141, 76, 458, 627, 253, 162, 256, + 144, 132, 11, 150, 37, 16, 83, 86, 455, 456, + 457, 63, 140, 597, 161, 19, 628, 435, 155, -387, + 709, -313, -313, 300, 341, 35, 101, -413, -414, -415, + 597, 434, 256, -295, -190, -85, 699, 244, -86, 705, + 41, 251, 146, 38, -135, 416, -123, 192, 727, 710, + 711, 712, 709, 413, 717, 715, 713, 300, 714, 96, + 153, 155, 156, 4, -145, 172, -200, -201, 171, 165, + 166, 167, 168, 169, 170, 177, 176, 157, 159, 173, + -246, 154, 178, 179, 180, 181, 182, 183, 184, 186, + 185, 187, 188, 174, 175, 191, 238, 239, -153, -153, + -153, -153, -216, -222, -221, -419, -218, -387, -294, -301, + -419, -419, -153, -278, -419, -150, -419, -419, -419, -419, + -419, -225, -145, -419, -419, -423, -419, -423, -423, -423, + -332, -419, -332, -332, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, @@ -9165,396 +9206,397 @@ var yyChk = [...]int{ -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, - -419, -419, -419, -419, -419, -419, -419, -419, 236, -419, - -419, -419, -419, -419, -332, -332, -332, -332, -332, -332, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, - -419, -419, -419, -419, 98, 112, 108, 111, 103, 114, - 98, 98, 98, 98, -31, -32, -210, -419, -312, -400, - -401, -193, -190, -419, 317, -295, -295, 286, 105, -235, - -34, -31, -230, -236, -232, -31, -79, -121, -134, 69, - 70, -133, -136, 27, 44, 74, 76, 99, 72, 73, - 71, 41, -420, 97, -420, -253, -420, 96, -38, -256, - 95, 652, 682, 652, 682, 67, 49, 98, 98, 96, - 25, -231, -233, -145, 18, -299, 4, -298, 28, -295, - 98, 236, 18, -191, 32, -190, -279, -279, 96, 100, - 331, -269, -271, 431, 433, 165, -300, -295, 98, 34, - 97, 96, -190, -321, -324, -326, -325, -327, -322, -323, - 360, 361, 192, 364, 366, 367, 368, 369, 370, 371, - 372, 373, 374, 377, 389, 35, 276, 356, 357, 358, - 359, 378, 379, 380, 381, 383, 384, 385, 386, 341, - 362, 594, 342, 343, 344, 345, 346, 347, 349, 350, - 353, 351, 352, 354, 355, -296, -295, 95, 97, 96, - -331, 95, -145, -137, 253, -295, 254, 254, 254, -286, - 258, 486, -354, -354, -354, 284, 23, -46, -43, -380, - 22, -42, -43, 245, 135, 136, 242, 95, -343, 95, - -352, -296, -295, 95, 151, 259, 150, -351, -348, -351, - -352, -295, -218, -295, 151, 151, -295, -295, -265, -295, - -265, -265, 41, -265, 41, -265, 41, 105, -295, -265, - 41, -265, 41, -265, 41, -265, 41, -265, 41, 34, - 87, 88, 89, 34, 91, 92, 93, -218, -295, -295, - -218, -343, -218, -190, -295, -272, 105, 105, 105, -354, - -354, 105, 98, 98, 98, -354, -354, 105, 98, -303, - -301, 98, 98, -392, 270, 314, 316, 105, 105, 105, - 105, 34, 98, -393, 34, 733, 732, 734, 735, 736, - 98, 105, 34, 105, 34, 105, -295, 95, -190, -143, - 304, 240, 242, 245, 85, 98, 322, 320, 321, 318, - 323, 324, 325, 165, 50, 96, 256, 253, -295, -285, - 258, -285, -295, -302, -301, -293, -190, 256, 397, 98, - -145, -350, 18, 176, -306, -306, -283, -190, -350, -306, - -283, -190, -283, -283, -283, -283, -306, -306, -306, -283, - -301, -301, -190, -190, -190, -190, -190, -190, -190, -313, - -284, -283, 708, 98, -277, 18, 85, -313, -313, -292, - 26, 26, 96, 337, 434, 435, -311, 334, -81, -295, - 98, -10, -29, -18, -17, -19, 165, -10, 96, 596, - -183, -190, 708, 708, 708, 708, 708, 708, -145, -145, - -145, -145, 620, -208, -411, 157, 132, 133, 130, 131, - -162, 42, 43, 41, -145, -209, -214, -216, 115, 176, - 159, 173, -246, -150, -153, -150, -150, -150, -150, -150, - -150, 235, -150, 235, -150, -150, -150, -150, -150, -150, - -314, -295, 98, 192, -158, -157, 114, -409, -158, 593, - 96, -221, 236, -145, -145, -387, -119, 459, 460, 461, - 462, 464, 465, 466, 469, 470, 474, 475, 458, 476, - 463, 468, 471, 472, 473, 467, 359, -145, -211, -210, - -211, -145, -145, -223, -224, 161, -218, -145, -420, -420, - 105, 183, -127, 27, 44, -127, -127, -127, -127, -145, - -145, -145, -145, -145, -145, -145, -145, -145, -145, -127, - -145, -120, 458, 476, 463, 468, 471, 472, 473, 467, - 359, 477, 478, 479, 480, 481, 482, 483, 484, 485, - -120, -119, -145, -145, -145, -145, -145, -145, -145, -145, - -87, -145, 142, 143, 144, -210, -145, -150, -145, -145, - -145, -420, -145, -145, -145, -211, -145, -145, -145, -145, + -419, -419, -419, -419, -419, -419, -419, -419, -419, 236, + -419, -419, -419, -419, -419, -332, -332, -332, -332, -332, + -332, -419, -419, -419, -419, -419, -419, -419, -419, -419, + -419, -419, -419, -419, -419, 98, 112, 108, 111, 103, + 114, 98, 98, 98, 98, -31, -32, -210, -419, -312, + -400, -401, -193, -190, -419, 317, -295, -295, 286, 105, + -235, -34, -31, -230, -236, -232, -31, -79, -121, -134, + 69, 70, -133, -136, 27, 44, 74, 76, 99, 72, + 73, 71, 41, -420, 97, -420, -253, -420, 96, -38, + -256, 95, 653, 683, 653, 683, 67, 49, 98, 98, + 96, 25, -231, -233, -145, 18, -299, 4, -298, 28, + -295, 98, 236, 18, -191, 32, -190, -279, -279, 96, + 100, 332, -269, -271, 432, 434, 165, -300, -295, 98, + 34, 97, 96, -190, -321, -324, -326, -325, -327, -322, + -323, 361, 362, 192, 365, 367, 368, 369, 370, 371, + 372, 373, 374, 375, 378, 390, 35, 276, 357, 358, + 359, 360, 379, 380, 381, 382, 384, 385, 386, 387, + 342, 363, 595, 343, 344, 345, 346, 347, 348, 350, + 351, 354, 352, 353, 355, 356, -296, -295, 95, 97, + 96, -331, 95, -145, -137, 253, -295, 254, 254, 254, + -286, 258, 487, -354, -354, -354, 284, 23, -46, -43, + -380, 22, -42, -43, 245, 135, 136, 242, 95, -343, + 95, -352, -296, -295, 95, 151, 259, 150, -351, -348, + -351, -352, -295, -218, -295, 151, 151, -295, -295, -265, + -295, -265, -265, 41, -265, 41, -265, 41, 105, -295, + -265, 41, -265, 41, -265, 41, -265, 41, -265, 41, + 34, 87, 88, 89, 34, 91, 92, 93, -218, -295, + -295, -218, -343, -218, -190, -295, -272, 105, 105, 105, + -354, -354, 105, 98, 98, 98, -354, -354, 105, 98, + -303, -301, 98, 98, -392, 270, 314, 316, 105, 105, + 105, 105, 34, 98, -393, 34, 734, 733, 735, 736, + 737, 98, 105, 34, 105, 34, 105, -295, 95, -190, + -143, 304, 240, 242, 245, 85, 98, 322, 320, 321, + 318, 323, 324, 325, 165, 50, 96, 256, 253, -295, + -285, 258, -285, -295, -302, -301, -293, -190, 256, 398, + 98, -145, -350, 18, 176, -306, -306, -283, -190, -350, + -306, -283, -190, -283, -283, -283, -283, -306, -306, -306, + -283, -301, -301, -190, -190, -190, -190, -190, -190, -190, + -313, -284, -283, 709, 98, -277, 18, 85, -313, -313, + -292, 26, 26, 96, 338, 435, 436, -311, 335, -81, + -295, 98, -10, -29, -18, -17, -19, 165, -10, 96, + 597, -183, -190, 709, 709, 709, 709, 709, 709, -145, + -145, -145, -145, 621, -208, -411, 157, 132, 133, 130, + 131, -162, 42, 43, 41, -145, -209, -214, -216, 115, + 176, 159, 173, -246, -150, -153, -150, -150, -150, -150, + -150, -150, 235, -150, 235, -150, -150, -150, -150, -150, + -150, -314, -295, 98, 192, -158, -157, 114, -409, -158, + 594, 96, -221, 236, -145, -145, -387, -119, 460, 461, + 462, 463, 465, 466, 467, 470, 471, 475, 476, 459, + 477, 464, 469, 472, 473, 474, 468, 360, -145, -211, + -210, -211, -145, -145, -223, -224, 161, -218, -145, -420, + -420, 105, 183, -127, 27, 44, -127, -127, -127, -127, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, + -127, -145, -120, 459, 477, 464, 469, 472, 473, 474, + 468, 360, 478, 479, 480, 481, 482, 483, 484, 485, + 486, -120, -119, -145, -145, -145, -145, -145, -145, -145, + -145, -87, -145, 142, 143, 144, -210, -145, -150, -145, + -145, -145, -420, -145, -145, -145, -211, -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, -386, -385, -384, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, - -145, -145, -145, -145, -145, -145, -145, -145, -210, -210, - -210, -210, -210, -145, -420, -145, -164, -148, 105, -261, - 114, 101, -145, -145, -145, -145, -145, -145, -211, -297, - -302, -293, -294, -210, -211, -211, -210, -210, -145, -145, - -145, -145, -145, -145, -145, -145, -420, -145, -145, -145, - -145, -145, -253, -420, -210, 96, -402, 433, 434, 706, - -304, 289, -303, 28, -211, 98, 18, -263, 86, -295, - -235, -235, 69, 70, 65, -131, -132, -136, -420, -37, - 28, -255, -295, 645, 645, 68, 98, -333, -272, 387, - 388, 192, -145, -145, 96, -234, 30, 31, -190, -298, - 183, -302, -190, -264, 289, -190, -168, -170, -171, -172, - -193, -217, -419, -173, -31, 616, 613, 18, -183, -184, - -192, -301, -270, -315, -269, 96, 432, 434, 435, 85, - 134, -145, -334, 191, -362, -361, -360, -343, -345, -346, - -347, 97, -334, -339, 394, 393, -331, -331, -331, -331, - -331, -333, -333, -333, -333, 95, -331, 95, -331, -331, - -331, -331, -336, 95, -336, -336, -337, -336, 95, -337, - -338, 95, -338, -373, -145, -370, -369, -367, -368, 263, - 110, 688, 644, 596, 637, 678, 86, -365, -234, 105, - -420, -143, -286, -371, -368, -295, -295, -295, -301, 157, - 100, 98, 100, 98, 100, 98, -112, -60, -1, 745, - 746, 747, 96, 23, -344, -343, -59, 314, -376, -377, - 289, -372, -366, -352, 151, -351, -352, -352, -295, 96, - 32, 139, 139, 139, 139, 596, 242, 35, -287, 636, - 157, 688, 644, -343, -59, 256, 256, -314, -314, -314, - 98, 98, -282, 741, -183, -139, 306, 165, 295, 295, - 253, 308, 253, 308, -190, 319, 322, 320, 321, 318, - 323, 324, 325, 41, 41, 41, 41, 41, 41, 41, - 307, 309, 311, 297, -190, -190, -285, 85, -185, -190, - 29, -301, 98, 98, -190, -283, -283, -190, -283, -283, - -190, 98, -301, -415, 338, -295, 374, 699, 701, -123, - 433, 96, 596, 26, -124, 26, -419, -411, 132, 133, - -216, -216, -216, -209, -150, -153, -150, 156, 277, -150, - -150, -419, -218, -420, -297, 28, 96, 86, -420, 181, - 96, -420, -420, 96, 18, 96, -226, -224, 163, -145, - -420, 96, -420, -420, -210, -145, -145, -145, -145, -420, - -420, -420, -420, -420, -420, -420, -420, -420, -420, -210, - -420, 96, 96, 18, -318, 28, -420, -420, -420, -420, - 96, -420, -420, -225, -420, 18, -420, 86, 96, 176, - 96, -420, -420, -420, 96, 96, -420, -420, 96, -420, - 96, -420, -420, -420, -420, -420, -420, 96, -420, 96, - -420, -420, -420, 96, -420, 96, -420, -420, 96, -420, + -145, -145, -145, -386, -385, -384, -145, -145, -145, -145, + -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, + -145, -145, -145, -145, -145, -145, -145, -145, -145, -210, + -210, -210, -210, -210, -145, -420, -145, -164, -148, 105, + -261, 114, 101, -145, -145, -145, -145, -145, -145, -211, + -297, -302, -293, -294, -210, -211, -211, -210, -210, -145, + -145, -145, -145, -145, -145, -145, -145, -420, -145, -145, + -145, -145, -145, -253, -420, -210, 96, -402, 434, 435, + 707, -304, 289, -303, 28, -211, 98, 18, -263, 86, + -295, -235, -235, 69, 70, 65, -131, -132, -136, -420, + -37, 28, -255, -295, 646, 646, 68, 98, -333, -272, + 388, 389, 192, -145, -145, 96, -234, 30, 31, -190, + -298, 183, -302, -190, -264, 289, -190, -168, -170, -171, + -172, -193, -217, -419, -173, -31, 617, 614, 18, -183, + -184, -192, -301, -270, -315, -269, 96, 433, 435, 436, + 85, 134, -145, -334, 191, -362, -361, -360, -343, -345, + -346, -347, 97, -334, -339, 395, 394, -331, -331, -331, + -331, -331, -333, -333, -333, -333, 95, -331, 95, -331, + -331, -331, -331, -336, 95, -336, -336, -337, -336, 95, + -337, -338, 95, -338, -373, -145, -370, -369, -367, -368, + 263, 110, 689, 645, 597, 638, 679, 86, -365, -234, + 105, -420, -143, -286, -371, -368, -295, -295, -295, -301, + 157, 100, 98, 100, 98, 100, 98, -112, -60, -1, + 746, 747, 748, 96, 23, -344, -343, -59, 314, -376, + -377, 289, -372, -366, -352, 151, -351, -352, -352, -295, + 96, 32, 139, 139, 139, 139, 597, 242, 35, -287, + 637, 157, 689, 645, -343, -59, 256, 256, -314, -314, + -314, 98, 98, -282, 742, -183, -139, 306, 165, 295, + 295, 253, 308, 253, 308, -190, 319, 322, 320, 321, + 318, 323, 324, 325, 326, 41, 41, 41, 41, 41, + 41, 41, 307, 309, 311, 297, -190, -190, -285, 85, + -185, -190, 29, -301, 98, 98, -190, -283, -283, -190, + -283, -283, -190, 98, -301, -415, 339, -295, 375, 700, + 702, -123, 434, 96, 597, 26, -124, 26, -419, -411, + 132, 133, -216, -216, -216, -209, -150, -153, -150, 156, + 277, -150, -150, -419, -218, -420, -297, 28, 96, 86, + -420, 181, 96, -420, -420, 96, 18, 96, -226, -224, + 163, -145, -420, 96, -420, -420, -210, -145, -145, -145, + -145, -420, -420, -420, -420, -420, -420, -420, -420, -420, + -420, -210, -420, 96, 96, 18, -318, 28, -420, -420, + -420, -420, 96, -420, -420, -225, -420, 18, -420, 86, + 96, 176, 96, -420, -420, -420, 96, 96, -420, -420, + 96, -420, 96, -420, -420, -420, -420, -420, -420, 96, + -420, 96, -420, -420, -420, 96, -420, 96, -420, -420, 96, -420, 96, -420, 96, -420, 96, -420, 96, -420, 96, -420, 96, -420, 96, -420, 96, -420, 96, -420, 96, -420, 96, -420, 96, -420, 96, -420, 96, -420, - 96, -420, 96, -420, -420, -420, 96, -420, 96, -420, - 96, -420, -420, 96, -420, 96, -420, 96, -420, 96, - 96, -420, 96, 96, 96, -420, 96, 96, 96, 96, - -420, -420, -420, -420, 96, 96, 96, 96, 96, 96, - 96, 96, 96, 96, -420, -420, -420, -420, -420, -420, - 96, -94, 621, -420, -420, 96, -420, 96, 96, 96, - 96, 96, -420, -419, 236, -420, -420, -420, -420, -420, - 96, 96, 96, 96, 96, 96, -420, -420, -420, 96, - 96, -420, 96, -420, 96, -420, -401, 705, 434, -197, - -196, -194, 83, 257, 84, -419, -303, -420, -158, -261, - -262, -261, -203, -295, 105, 114, -237, -167, 96, -169, - 18, -216, 97, 96, -333, -241, -247, -280, -295, 98, - 192, -335, 192, -335, 387, 388, -233, 236, -198, 19, - -202, 35, 63, -29, -419, -419, 35, 96, -186, -188, - -187, -189, 75, 79, 81, 76, 77, 78, 82, -309, - 28, -31, -168, -31, -419, -190, -183, -421, 18, 86, - -421, 96, 236, -271, -274, 436, 433, 439, -387, 98, - -111, 96, -360, -347, -238, -140, 46, -340, 395, -333, - 604, -333, -342, 98, -342, 105, 105, 105, 97, -49, - -44, -45, 36, 90, -367, -354, 98, 45, -354, -354, - -295, 97, -234, -139, -190, 85, -371, -371, -371, 29, - -2, 744, 750, 151, 95, 400, 22, -255, 96, 97, - -219, 315, 97, -113, -295, 97, 95, -352, -352, -295, - -419, 253, 34, 34, 688, 644, 636, -59, -219, -218, - -295, -334, 743, 742, 97, 255, 313, -144, 453, -141, - 98, 100, -190, -190, -190, -190, -190, -190, 245, 242, - 423, -410, 326, -410, 298, 256, -183, -190, 96, -84, - 272, 267, -306, -306, 36, -190, 433, 717, 715, -145, - 156, 277, -162, -153, -119, -119, -150, -316, 192, 360, - 276, 358, 354, 374, 365, 393, 356, 394, 351, 350, - 349, -316, -314, -150, -210, -145, -145, -145, 164, -145, - 162, -145, -95, -94, -420, -420, -420, -420, -420, -95, - -95, -95, -95, -95, -95, -95, -95, -95, -95, -230, - -145, -145, -145, -420, 192, 360, -95, -145, 18, -145, - -314, -145, -145, -145, -145, -145, -145, -145, -145, -145, + 96, -420, 96, -420, 96, -420, -420, -420, 96, -420, + 96, -420, 96, -420, -420, 96, -420, 96, -420, 96, + -420, 96, 96, -420, 96, 96, 96, -420, 96, 96, + 96, 96, -420, -420, -420, -420, 96, 96, 96, 96, + 96, 96, 96, 96, 96, 96, -420, -420, -420, -420, + -420, -420, 96, -94, 622, -420, -420, 96, -420, 96, + 96, 96, 96, 96, -420, -419, 236, -420, -420, -420, + -420, -420, 96, 96, 96, 96, 96, 96, -420, -420, + -420, 96, 96, -420, 96, -420, 96, -420, -401, 706, + 435, -197, -196, -194, 83, 257, 84, -419, -303, -420, + -158, -261, -262, -261, -203, -295, 105, 114, -237, -167, + 96, -169, 18, -216, 97, 96, -333, -241, -247, -280, + -295, 98, 192, -335, 192, -335, 388, 389, -233, 236, + -198, 19, -202, 35, 63, -29, -419, -419, 35, 96, + -186, -188, -187, -189, 75, 79, 81, 76, 77, 78, + 82, -309, 28, -31, -168, -31, -419, -190, -183, -421, + 18, 86, -421, 96, 236, -271, -274, 437, 434, 440, + -387, 98, -111, 96, -360, -347, -238, -140, 46, -340, + 396, -333, 605, -333, -342, 98, -342, 105, 105, 105, + 97, -49, -44, -45, 36, 90, -367, -354, 98, 45, + -354, -354, -295, 97, -234, -139, -190, 85, -371, -371, + -371, 29, -2, 745, 751, 151, 95, 401, 22, -255, + 96, 97, -219, 315, 97, -113, -295, 97, 95, -352, + -352, -295, -419, 253, 34, 34, 689, 645, 637, -59, + -219, -218, -295, -334, 744, 743, 97, 255, 313, -144, + 454, -141, 98, 100, -190, -190, -190, -190, -190, -190, + 245, 242, 424, -410, 327, 98, -410, 298, 256, -183, + -190, 96, -84, 272, 267, -306, -306, 36, -190, 434, + 718, 716, -145, 156, 277, -162, -153, -119, -119, -150, + -316, 192, 361, 276, 359, 355, 375, 366, 394, 357, + 395, 352, 351, 350, -316, -314, -150, -210, -145, -145, + -145, 164, -145, 162, -145, -95, -94, -420, -420, -420, + -420, -420, -95, -95, -95, -95, -95, -95, -95, -95, + -95, -95, -230, -145, -145, -145, -420, 192, 361, -95, + -145, 18, -145, -314, -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, -384, -145, -210, -145, - -210, -145, -145, -145, -145, -145, -385, -385, -385, -385, - -385, -210, -210, -210, -210, -145, -419, -295, -98, -97, - -96, 671, 257, -94, -164, -98, -164, 235, -145, 235, - 235, 235, -145, -211, -297, -145, -145, -145, -145, -145, - -145, -145, -145, -145, -145, -194, -348, 295, -348, 295, - -348, -265, 96, -276, 26, 18, 63, 63, -167, -198, - -132, -168, -295, -244, 698, -250, 52, -248, -249, 53, - -245, 54, 62, -335, -335, 183, -235, -145, -266, 85, - -267, -275, -218, -213, -215, -214, -419, -254, -420, -295, - -265, -267, -170, -171, -171, -170, -171, 75, 75, 75, - 80, 75, 80, 75, -187, -301, -420, -145, -304, 86, - -168, -168, -192, -301, 183, 433, 437, 438, -360, -408, - 130, 157, 34, 85, 391, 110, -406, 191, 633, 683, - 688, 644, 637, 678, -407, 259, 150, 151, 271, 28, - 47, 97, 96, 97, 96, 97, 97, 96, -288, -287, - -45, -44, -354, -354, 105, -387, 98, 98, 255, -190, - 85, 85, 85, -114, 748, 105, 95, -3, 90, -145, - 95, 23, -343, -218, -378, -328, -379, -329, -330, -5, - -6, -355, -117, 63, 110, -63, 50, 254, 728, 729, - 139, -419, 741, -370, -255, -374, -376, -190, -149, -419, - -161, -147, -146, -148, -154, 181, 182, 276, 356, 357, - -219, -190, -138, 304, 312, 95, -142, 101, -389, 86, - 295, 391, 295, 391, 98, -412, 327, 98, -412, -190, - -84, -49, -190, -283, -283, 36, -387, -420, -162, -153, - -126, 176, 596, -319, 603, -331, -331, -331, -338, -331, - 346, -331, 346, -331, -420, -420, -420, 96, -420, 26, - -420, 96, -145, 96, -95, -95, -95, -95, -95, -122, - 492, 96, 96, -420, 95, 95, -420, -145, -420, -420, - -420, 96, -420, -420, -420, -420, -420, -420, -420, -420, - -420, -420, -420, -420, -420, 96, -420, 96, -420, 96, - -420, 96, -420, 96, -420, 96, -420, 96, -420, 96, - -420, 96, -420, 96, -420, 96, -420, 96, -420, 96, - -420, 96, -420, 96, -420, 96, -420, -420, 96, -420, - -420, -420, 96, -420, 96, -420, 96, -420, -420, -420, - 96, -317, 689, -420, -420, -420, -420, -420, -420, -420, - -420, -420, -420, -420, -93, -296, -94, 653, 653, -420, - -94, -227, 96, -150, -420, -150, -150, -150, -420, -420, - -420, 96, -420, 96, 96, -420, 96, -420, 96, -420, - -420, -420, -420, 96, -195, 26, -419, -195, -419, -195, - -420, -261, -190, -198, -228, 20, -241, 57, 366, -252, - -251, 61, 53, -249, 23, 55, 23, 33, -266, 96, - 165, -308, 96, 28, -420, -420, 96, 63, 236, -420, - -198, -181, -180, 85, 86, -182, 85, -180, 75, 75, - -256, 96, -264, -168, -198, -198, 236, 130, -419, -149, - 16, 98, 98, -387, -405, 732, 733, 34, 105, -354, - -354, 151, 151, -190, 95, -333, 98, -333, 105, 105, - 34, 91, 92, 93, 34, 87, 88, 89, -190, -190, - -190, -190, -375, 95, 23, -145, 95, 165, 97, -255, - -255, 291, 176, -354, 726, 297, 297, -354, -354, -354, - -116, -115, 748, 97, -420, 96, -341, 596, 599, -145, - -155, -155, -256, 97, -383, 596, -388, -295, -295, -295, - -295, 105, 107, -420, 594, 82, 597, -420, -333, -145, - -145, -145, -145, -235, 98, -145, -145, 105, 105, -95, - -420, -145, -145, -145, -145, -145, -145, -145, -145, -145, + -145, -145, -145, -145, -145, -145, -145, -145, -145, -384, + -145, -210, -145, -210, -145, -145, -145, -145, -145, -385, + -385, -385, -385, -385, -210, -210, -210, -210, -145, -419, + -295, -98, -97, -96, 672, 257, -94, -164, -98, -164, + 235, -145, 235, 235, 235, -145, -211, -297, -145, -145, + -145, -145, -145, -145, -145, -145, -145, -145, -194, -348, + 295, -348, 295, -348, -265, 96, -276, 26, 18, 63, + 63, -167, -198, -132, -168, -295, -244, 699, -250, 52, + -248, -249, 53, -245, 54, 62, -335, -335, 183, -235, + -145, -266, 85, -267, -275, -218, -213, -215, -214, -419, + -254, -420, -295, -265, -267, -170, -171, -171, -170, -171, + 75, 75, 75, 80, 75, 80, 75, -187, -301, -420, + -145, -304, 86, -168, -168, -192, -301, 183, 434, 438, + 439, -360, -408, 130, 157, 34, 85, 392, 110, -406, + 191, 634, 684, 689, 645, 638, 679, -407, 259, 150, + 151, 271, 28, 47, 97, 96, 97, 96, 97, 97, + 96, -288, -287, -45, -44, -354, -354, 105, -387, 98, + 98, 255, -190, 85, 85, 85, -114, 749, 105, 95, + -3, 90, -145, 95, 23, -343, -218, -378, -328, -379, + -329, -330, -5, -6, -355, -117, 63, 110, -63, 50, + 254, 729, 730, 139, -419, 742, -370, -255, -374, -376, + -190, -149, -419, -161, -147, -146, -148, -154, 181, 182, + 276, 357, 358, -219, -190, -138, 304, 312, 95, -142, + 101, -389, 86, 295, 392, 295, 392, 98, -412, 328, + 98, -412, -190, -84, -49, -190, -283, -283, 36, -387, + -420, -162, -153, -126, 176, 597, -319, 604, -331, -331, + -331, -338, -331, 347, -331, 347, -331, -420, -420, -420, + 96, -420, 26, -420, 96, -145, 96, -95, -95, -95, + -95, -95, -122, 493, 96, 96, -420, 95, 95, -420, + -145, -420, -420, -420, 96, -420, -420, -420, -420, -420, + -420, -420, -420, -420, -420, -420, -420, -420, 96, -420, + 96, -420, 96, -420, 96, -420, 96, -420, 96, -420, + 96, -420, 96, -420, 96, -420, 96, -420, 96, -420, + 96, -420, 96, -420, 96, -420, 96, -420, 96, -420, + -420, 96, -420, -420, -420, 96, -420, 96, -420, 96, + -420, -420, -420, 96, -317, 690, -420, -420, -420, -420, + -420, -420, -420, -420, -420, -420, -420, -93, -296, -94, + 654, 654, -420, -94, -227, 96, -150, -420, -150, -150, + -150, -420, -420, -420, 96, -420, 96, 96, -420, 96, + -420, 96, -420, -420, -420, -420, 96, -195, 26, -419, + -195, -419, -195, -420, -261, -190, -198, -228, 20, -241, + 57, 367, -252, -251, 61, 53, -249, 23, 55, 23, + 33, -266, 96, 165, -308, 96, 28, -420, -420, 96, + 63, 236, -420, -198, -181, -180, 85, 86, -182, 85, + -180, 75, 75, -256, 96, -264, -168, -198, -198, 236, + 130, -419, -149, 16, 98, 98, -387, -405, 733, 734, + 34, 105, -354, -354, 151, 151, -190, 95, -333, 98, + -333, 105, 105, 34, 91, 92, 93, 34, 87, 88, + 89, -190, -190, -190, -190, -375, 95, 23, -145, 95, + 165, 97, -255, -255, 291, 176, -354, 727, 297, 297, + -354, -354, -354, -116, -115, 749, 97, -420, 96, -341, + 597, 600, -145, -155, -155, -256, 97, -383, 597, -388, + -295, -295, -295, -295, 105, 107, -420, 595, 82, 598, + -420, -333, -145, -145, -145, -145, -235, 98, -145, -145, + 105, 105, -95, -420, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, - -145, -210, -145, -420, -178, -177, -179, 709, 130, 34, - -316, -420, -212, 289, -101, -100, -99, 18, -420, -145, - -119, -119, -119, -119, -145, -145, -145, -145, -145, -145, - -419, 75, 22, 20, -258, -295, 259, -419, -258, -419, - -304, -228, -229, 21, 23, -242, 59, -240, 58, -240, - -251, 23, 23, 98, 23, 98, 151, -275, -145, -215, - -303, 63, -29, -295, -213, -295, -230, -145, 95, -145, - -158, -198, -198, -145, -205, 516, 518, 519, 520, 517, - 522, 523, 524, 525, 526, 527, 528, 529, 530, 531, - 521, 532, 493, 494, 495, 117, 119, 118, 127, 128, - 496, 497, 498, 360, 544, 545, 539, 542, 543, 541, - 540, 375, 376, 499, 562, 563, 567, 566, 564, 565, - 568, 571, 572, 573, 574, 575, 576, 578, 577, 569, - 570, 547, 546, 548, 549, 550, 551, 552, 553, 555, - 554, 556, 557, 558, 559, 560, 561, 579, 580, 581, - 582, 583, 585, 584, 589, 588, 586, 587, 591, 590, - 500, 501, 120, 121, 122, 123, 124, 125, 126, 502, - 505, 503, 506, 507, 508, 513, 514, 509, 510, 511, - 512, 515, 386, 384, 385, 381, 380, 379, 440, 445, - 446, 448, 533, 534, 535, 536, 537, 538, 690, 691, - 692, 693, 694, 695, 696, 697, 98, 98, 95, -145, - 97, 97, -256, -374, -60, 97, -257, -255, 105, 97, - 292, -214, -419, 98, -354, -354, -354, 105, 105, -303, - -420, 96, -295, -407, -376, 600, 600, -420, 28, -382, - -381, -297, 95, 86, 68, 595, 598, -420, -420, -420, - 96, -420, -420, -420, 97, 97, -420, -420, -420, -420, + -145, -145, -145, -145, -210, -145, -420, -178, -177, -179, + 710, 130, 34, -316, -420, -212, 289, -101, -100, -99, + 18, -420, -145, -119, -119, -119, -119, -145, -145, -145, + -145, -145, -145, -419, 75, 22, 20, -258, -295, 259, + -419, -258, -419, -304, -228, -229, 21, 23, -242, 59, + -240, 58, -240, -251, 23, 23, 98, 23, 98, 151, + -275, -145, -215, -303, 63, -29, -295, -213, -295, -230, + -145, 95, -145, -158, -198, -198, -145, -205, 517, 519, + 520, 521, 518, 523, 524, 525, 526, 527, 528, 529, + 530, 531, 532, 522, 533, 494, 495, 496, 117, 119, + 118, 127, 128, 497, 498, 499, 361, 545, 546, 540, + 543, 544, 542, 541, 376, 377, 500, 563, 564, 568, + 567, 565, 566, 569, 572, 573, 574, 575, 576, 577, + 579, 578, 570, 571, 548, 547, 549, 550, 551, 552, + 553, 554, 556, 555, 557, 558, 559, 560, 561, 562, + 580, 581, 582, 583, 584, 586, 585, 590, 589, 587, + 588, 592, 591, 501, 502, 120, 121, 122, 123, 124, + 125, 126, 503, 506, 504, 507, 508, 509, 514, 515, + 510, 511, 512, 513, 516, 387, 385, 386, 382, 381, + 380, 441, 446, 447, 449, 534, 535, 536, 537, 538, + 539, 691, 692, 693, 694, 695, 696, 697, 698, 98, + 98, 95, -145, 97, 97, -256, -374, -60, 97, -257, + -255, 105, 97, 292, -214, -419, 98, -354, -354, -354, + 105, 105, -303, -420, 96, -295, -407, -376, 601, 601, + -420, 28, -382, -381, -297, 95, 86, 68, 596, 599, + -420, -420, -420, 96, -420, -420, -420, 97, 97, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, - -420, -420, -420, -420, -420, -420, -420, -420, 96, -420, - -177, -179, -420, 85, -158, -230, 23, -98, 314, 316, - -98, -420, -420, -420, -420, -420, 96, -420, -420, 96, - -420, 96, -420, -420, -258, -420, 23, 23, 96, -420, - -258, -420, -258, -197, -229, -108, -107, -106, 627, -145, - -210, -243, 60, 85, 134, 98, 98, 98, 16, -419, - -213, 236, -308, -235, -255, -175, 400, -230, -420, -255, - 97, 28, 97, 750, 151, 97, -214, -125, -419, 288, - -303, 98, 98, -115, -118, -29, 96, 165, -255, -190, - 68, -145, -210, -420, 85, 608, 709, -92, -91, -88, - 720, 746, -210, -94, -94, -145, -145, -145, -420, -295, - 259, -420, -420, -108, 96, -105, -104, -295, -320, 596, - 85, 134, -267, -255, -308, -295, 97, -420, -419, -235, - 97, -239, -29, 95, -3, 288, -328, -379, -329, -330, - -5, -6, -355, -82, 596, -381, -359, -301, -297, 98, - 105, 97, 596, -420, -420, -90, 159, 718, 686, -155, - 235, -420, 96, -420, 96, -420, 96, -106, 96, 28, - 601, -420, -304, -176, -174, -295, 650, -398, -397, 592, - -408, -404, 130, 157, 110, -406, 688, 644, 140, 141, - -82, -145, 95, -420, -83, 303, 705, 236, -389, 597, - -90, 719, 664, 639, 664, 639, -150, -145, -145, -145, - -104, -419, -420, 96, 26, -321, -62, 661, -395, -396, - 85, -399, 406, 660, 681, 130, 98, 97, -255, 264, - -302, -383, 598, 156, -119, -420, 96, -420, 96, -420, - -93, -174, 657, -334, -158, -396, 85, -395, 85, 17, - 16, -4, 749, 97, 305, -90, 664, 639, -145, -145, - -420, -61, 29, -175, -394, 272, 267, 270, 35, -394, - 105, -4, -420, -420, 661, 266, 34, 130, -158, -178, - -177, -177, + -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, + -420, 96, -420, -177, -179, -420, 85, -158, -230, 23, + -98, 314, 316, -98, -420, -420, -420, -420, -420, 96, + -420, -420, 96, -420, 96, -420, -420, -258, -420, 23, + 23, 96, -420, -258, -420, -258, -197, -229, -108, -107, + -106, 628, -145, -210, -243, 60, 85, 134, 98, 98, + 98, 16, -419, -213, 236, -308, -235, -255, -175, 401, + -230, -420, -255, 97, 28, 97, 751, 151, 97, -214, + -125, -419, 288, -303, 98, 98, -115, -118, -29, 96, + 165, -255, -190, 68, -145, -210, -420, 85, 609, 710, + -92, -91, -88, 721, 747, -210, -94, -94, -145, -145, + -145, -420, -295, 259, -420, -420, -108, 96, -105, -104, + -295, -320, 597, 85, 134, -267, -255, -308, -295, 97, + -420, -419, -235, 97, -239, -29, 95, -3, 288, -328, + -379, -329, -330, -5, -6, -355, -82, 597, -381, -359, + -301, -297, 98, 105, 97, 597, -420, -420, -90, 159, + 719, 687, -155, 235, -420, 96, -420, 96, -420, 96, + -106, 96, 28, 602, -420, -304, -176, -174, -295, 651, + -398, -397, 593, -408, -404, 130, 157, 110, -406, 689, + 645, 140, 141, -82, -145, 95, -420, -83, 303, 706, + 236, -389, 598, -90, 720, 665, 640, 665, 640, -150, + -145, -145, -145, -104, -419, -420, 96, 26, -321, -62, + 662, -395, -396, 85, -399, 407, 661, 682, 130, 98, + 97, -255, 264, -302, -383, 599, 156, -119, -420, 96, + -420, 96, -420, -93, -174, 658, -334, -158, -396, 85, + -395, 85, 17, 16, -4, 750, 97, 305, -90, 665, + 640, -145, -145, -420, -61, 29, -175, -394, 272, 267, + 270, 35, -394, 105, -4, -420, -420, 662, 266, 34, + 130, -158, -178, -177, -177, } var yyDef = [...]int{ - 888, -2, -2, 890, 2, 4, 5, 6, 7, 8, + 889, -2, -2, 891, 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, 70, 72, 73, 888, 888, 888, 0, 888, 0, - 0, 888, -2, -2, 888, 1643, 0, 888, 0, 883, - 0, -2, 803, 809, 0, 818, -2, 0, 0, 888, - 888, 2283, 2283, 883, 0, 0, 0, 0, 0, 888, - 888, 888, 888, 1648, 1501, 50, 888, 0, 85, 86, - 838, 839, 840, 65, 0, 2281, 889, 1, 3, 71, - 75, 0, 0, 0, 58, 1510, 0, 78, 0, 0, - 892, 0, 0, 1626, 888, 888, 0, 126, 127, 0, + 39, 70, 72, 73, 889, 889, 889, 0, 889, 0, + 0, 889, -2, -2, 889, 1644, 0, 889, 0, 884, + 0, -2, 804, 810, 0, 819, -2, 0, 0, 889, + 889, 2285, 2285, 884, 0, 0, 0, 0, 0, 889, + 889, 889, 889, 1649, 1502, 50, 889, 0, 85, 86, + 839, 840, 841, 65, 0, 2283, 890, 1, 3, 71, + 75, 0, 0, 0, 58, 1511, 0, 78, 0, 0, + 893, 0, 0, 1627, 889, 889, 0, 126, 127, 0, 0, 0, -2, 130, -2, 159, 160, 161, 0, 166, - 608, 526, 578, 524, 563, -2, 512, 0, 0, 0, + 609, 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, 1628, 0, + 401, 401, 0, 0, -2, 512, 512, 512, 1629, 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, 1528, 165, 1644, 1641, 1642, 1804, 1805, 1806, 1807, - 1808, 1809, 1810, 1811, 1812, 1813, 1814, 1815, 1816, 1817, - 1818, 1819, 1820, 1821, 1822, 1823, 1824, 1825, 1826, 1827, - 1828, 1829, 1830, 1831, 1832, 1833, 1834, 1835, 1836, 1837, - 1838, 1839, 1840, 1841, 1842, 1843, 1844, 1845, 1846, 1847, - 1848, 1849, 1850, 1851, 1852, 1853, 1854, 1855, 1856, 1857, - 1858, 1859, 1860, 1861, 1862, 1863, 1864, 1865, 1866, 1867, - 1868, 1869, 1870, 1871, 1872, 1873, 1874, 1875, 1876, 1877, - 1878, 1879, 1880, 1881, 1882, 1883, 1884, 1885, 1886, 1887, - 1888, 1889, 1890, 1891, 1892, 1893, 1894, 1895, 1896, 1897, - 1898, 1899, 1900, 1901, 1902, 1903, 1904, 1905, 1906, 1907, - 1908, 1909, 1910, 1911, 1912, 1913, 1914, 1915, 1916, 1917, - 1918, 1919, 1920, 1921, 1922, 1923, 1924, 1925, 1926, 1927, - 1928, 1929, 1930, 1931, 1932, 1933, 1934, 1935, 1936, 1937, - 1938, 1939, 1940, 1941, 1942, 1943, 1944, 1945, 1946, 1947, - 1948, 1949, 1950, 1951, 1952, 1953, 1954, 1955, 1956, 1957, - 1958, 1959, 1960, 1961, 1962, 1963, 1964, 1965, 1966, 1967, - 1968, 1969, 1970, 1971, 1972, 1973, 1974, 1975, 1976, 1977, - 1978, 1979, 1980, 1981, 1982, 1983, 1984, 1985, 1986, 1987, - 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, - 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, - 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, - 2018, 2019, 2020, 2021, 2022, 2023, 2024, 2025, 2026, 2027, - 2028, 2029, 2030, 2031, 2032, 2033, 2034, 2035, 2036, 2037, - 2038, 2039, 2040, 2041, 2042, 2043, 2044, 2045, 2046, 2047, - 2048, 2049, 2050, 2051, 2052, 2053, 2054, 2055, 2056, 2057, - 2058, 2059, 2060, 2061, 2062, 2063, 2064, 2065, 2066, 2067, - 2068, 2069, 2070, 2071, 2072, 2073, 2074, 2075, 2076, 2077, - 2078, 2079, 2080, 2081, 2082, 2083, 2084, 2085, 2086, 2087, - 2088, 2089, 2090, 2091, 2092, 2093, 2094, 2095, 2096, 2097, - 2098, 2099, 2100, 2101, 2102, 2103, 2104, 2105, 2106, 2107, - 2108, 2109, 2110, 2111, 2112, 2113, 2114, 2115, 2116, 2117, - 2118, 2119, 2120, 2121, 2122, 2123, 2124, 2125, 2126, 2127, - 2128, 2129, 2130, 2131, 2132, 2133, 2134, 2135, 2136, 2137, - 2138, 2139, 2140, 2141, 2142, 2143, 2144, 2145, 2146, 2147, - 2148, 2149, 2150, 2151, 2152, 2153, 2154, 2155, 2156, 2157, - 2158, 2159, 2160, 2161, 2162, 2163, 2164, 2165, 2166, 2167, - 2168, 2169, 2170, 2171, 2172, 2173, 2174, 2175, 2176, 2177, - 2178, 2179, 2180, 2181, 2182, 2183, 2184, 2185, 2186, 2187, - 2188, 2189, 2190, 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, 2237, - 2238, 2239, 2240, 2241, 2242, 2243, 2244, 2245, 2246, 2247, - 2248, 2249, 2250, 2251, 2252, 2253, 2254, 2255, 2256, 2257, - 2258, 2259, 2260, 2261, 2262, 2263, 2264, 2265, 2266, 2267, - 2268, 2269, 2270, 2271, 2272, 2273, 2274, 2275, 2276, 2277, - 2278, 2279, 2280, 0, 1620, 0, 721, 992, 0, 884, - 885, 0, 792, 792, 0, 792, 792, 792, 792, 0, - 0, 0, 735, 0, 0, 0, 0, 789, 0, 751, - 752, 0, 789, 0, 758, 795, 0, 0, 765, 792, - 792, 768, 2284, 0, 2284, 2284, 0, 0, 1611, 0, - 786, 784, 798, 799, 42, 802, 805, 806, 807, 808, - 811, 0, 822, 825, 1637, 1638, 0, 827, 832, 851, - 852, 0, 45, 1152, 0, 1016, 0, 1027, -2, 1038, - 1055, 1056, 1057, 1058, 1059, 1061, 1062, 1063, 0, 0, - 0, 0, 1068, 1069, 0, 0, 0, 0, 0, 1132, - 0, 0, 0, 0, 2006, 1472, 0, 0, 1434, 1434, - 1168, 1434, 1434, 1436, 1436, 1436, 1857, 1998, 2007, 2186, - 1818, 1824, 1825, 1826, 2132, 2133, 2134, 2135, 2227, 2228, - 2232, 1920, 1813, 2199, 2200, 0, 2280, 1959, 1967, 1968, - 1944, 1953, 1992, 2094, 2211, 1836, 1987, 2057, 1917, 1939, - 1940, 2075, 2076, 1963, 1964, 1943, 2138, 2140, 2156, 2157, - 2142, 2144, 2153, 2159, 2164, 2143, 2155, 2160, 2173, 2177, - 2180, 2181, 2182, 2150, 2148, 2161, 2165, 2167, 2169, 2175, - 2178, 2151, 2149, 2162, 2166, 2168, 2170, 2176, 2179, 2137, - 2141, 2145, 2154, 2172, 2152, 2171, 2146, 2158, 2163, 2174, - 2147, 2139, 1957, 1960, 1947, 1948, 1950, 1952, 1958, 1965, - 1971, 1949, 1970, 1969, 0, 1945, 1946, 1951, 1962, 1966, - 1954, 1955, 1956, 1961, 1972, 2013, 2012, 2011, 2056, 1983, - 2055, 0, 0, 0, 0, 0, 1807, 1862, 1863, 2183, - 1356, 1357, 1358, 1359, 0, 0, 0, 0, 0, 0, - 0, 290, 291, 1485, 1486, 44, 1151, 1607, 1436, 1436, - 1436, 1436, 1436, 1436, 1090, 1091, 1092, 1093, 1094, 1120, - 1121, 1127, 1128, 2070, 2071, 2072, 2073, 1900, 2222, 1909, - 1910, 2052, 2053, 1922, 1923, 2254, 2255, -2, -2, -2, - 232, 233, 234, 235, 236, 237, 238, 239, 0, 1861, - 2197, 2198, 228, 0, 0, 295, 292, 293, 294, 1134, - 1135, 248, 249, 250, 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, 297, 298, 2283, 0, 861, 0, 0, - 0, 0, 0, 0, 1649, 1650, 1510, 0, 1502, 1501, - 63, 0, 888, -2, 0, 0, 0, 0, 47, 0, - 52, 949, 891, 77, 76, 1550, 1553, 0, 0, 0, - 59, 1511, 67, 69, 1512, 0, 893, 894, 0, 925, - 929, 0, 0, 0, 1627, 1626, 1626, 102, 0, 0, - 103, 123, 124, 125, 0, 0, 109, 110, 1613, 1614, - 43, 0, 0, 177, 178, 0, 1108, 428, 0, 173, - 0, 421, 360, 0, 1528, 0, 0, 0, 0, 0, - 1624, 0, 1621, 154, 155, 162, 163, 164, 401, 401, - 401, 575, 0, 0, 165, 165, 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, 669, 0, 402, 0, 573, 574, 464, 0, - 0, 0, 0, 0, 0, 0, 0, 1629, 1630, 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, 153, 1541, 0, 0, 0, -2, 0, 713, 0, - 0, 0, 1622, 1622, 0, 720, 0, 0, 0, 725, - 0, 0, 726, 0, 789, 789, 787, 788, 728, 729, - 730, 731, 792, 0, 0, 410, 411, 412, 789, 792, - 0, 792, 792, 792, 792, 789, 789, 789, 792, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 2284, 795, - 792, 0, 759, 0, 760, 761, 762, 763, 766, 767, - 769, 2285, 2286, 1639, 1640, 1651, 1652, 1653, 1654, 1655, + 401, 1529, 165, 1645, 1642, 1643, 1805, 1806, 1807, 1808, + 1809, 1810, 1811, 1812, 1813, 1814, 1815, 1816, 1817, 1818, + 1819, 1820, 1821, 1822, 1823, 1824, 1825, 1826, 1827, 1828, + 1829, 1830, 1831, 1832, 1833, 1834, 1835, 1836, 1837, 1838, + 1839, 1840, 1841, 1842, 1843, 1844, 1845, 1846, 1847, 1848, + 1849, 1850, 1851, 1852, 1853, 1854, 1855, 1856, 1857, 1858, + 1859, 1860, 1861, 1862, 1863, 1864, 1865, 1866, 1867, 1868, + 1869, 1870, 1871, 1872, 1873, 1874, 1875, 1876, 1877, 1878, + 1879, 1880, 1881, 1882, 1883, 1884, 1885, 1886, 1887, 1888, + 1889, 1890, 1891, 1892, 1893, 1894, 1895, 1896, 1897, 1898, + 1899, 1900, 1901, 1902, 1903, 1904, 1905, 1906, 1907, 1908, + 1909, 1910, 1911, 1912, 1913, 1914, 1915, 1916, 1917, 1918, + 1919, 1920, 1921, 1922, 1923, 1924, 1925, 1926, 1927, 1928, + 1929, 1930, 1931, 1932, 1933, 1934, 1935, 1936, 1937, 1938, + 1939, 1940, 1941, 1942, 1943, 1944, 1945, 1946, 1947, 1948, + 1949, 1950, 1951, 1952, 1953, 1954, 1955, 1956, 1957, 1958, + 1959, 1960, 1961, 1962, 1963, 1964, 1965, 1966, 1967, 1968, + 1969, 1970, 1971, 1972, 1973, 1974, 1975, 1976, 1977, 1978, + 1979, 1980, 1981, 1982, 1983, 1984, 1985, 1986, 1987, 1988, + 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, + 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, + 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, + 2019, 2020, 2021, 2022, 2023, 2024, 2025, 2026, 2027, 2028, + 2029, 2030, 2031, 2032, 2033, 2034, 2035, 2036, 2037, 2038, + 2039, 2040, 2041, 2042, 2043, 2044, 2045, 2046, 2047, 2048, + 2049, 2050, 2051, 2052, 2053, 2054, 2055, 2056, 2057, 2058, + 2059, 2060, 2061, 2062, 2063, 2064, 2065, 2066, 2067, 2068, + 2069, 2070, 2071, 2072, 2073, 2074, 2075, 2076, 2077, 2078, + 2079, 2080, 2081, 2082, 2083, 2084, 2085, 2086, 2087, 2088, + 2089, 2090, 2091, 2092, 2093, 2094, 2095, 2096, 2097, 2098, + 2099, 2100, 2101, 2102, 2103, 2104, 2105, 2106, 2107, 2108, + 2109, 2110, 2111, 2112, 2113, 2114, 2115, 2116, 2117, 2118, + 2119, 2120, 2121, 2122, 2123, 2124, 2125, 2126, 2127, 2128, + 2129, 2130, 2131, 2132, 2133, 2134, 2135, 2136, 2137, 2138, + 2139, 2140, 2141, 2142, 2143, 2144, 2145, 2146, 2147, 2148, + 2149, 2150, 2151, 2152, 2153, 2154, 2155, 2156, 2157, 2158, + 2159, 2160, 2161, 2162, 2163, 2164, 2165, 2166, 2167, 2168, + 2169, 2170, 2171, 2172, 2173, 2174, 2175, 2176, 2177, 2178, + 2179, 2180, 2181, 2182, 2183, 2184, 2185, 2186, 2187, 2188, + 2189, 2190, 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, 2237, 2238, + 2239, 2240, 2241, 2242, 2243, 2244, 2245, 2246, 2247, 2248, + 2249, 2250, 2251, 2252, 2253, 2254, 2255, 2256, 2257, 2258, + 2259, 2260, 2261, 2262, 2263, 2264, 2265, 2266, 2267, 2268, + 2269, 2270, 2271, 2272, 2273, 2274, 2275, 2276, 2277, 2278, + 2279, 2280, 2281, 2282, 0, 1621, 0, 722, 993, 0, + 885, 886, 0, 793, 793, 0, 793, 793, 793, 793, + 0, 0, 0, 736, 0, 0, 0, 0, 790, 0, + 752, 753, 0, 790, 0, 759, 796, 0, 0, 766, + 793, 793, 769, 2286, 0, 2286, 2286, 0, 0, 1612, + 0, 787, 785, 799, 800, 42, 803, 806, 807, 808, + 809, 812, 0, 823, 826, 1638, 1639, 0, 828, 833, + 852, 853, 0, 45, 1153, 0, 1017, 0, 1028, -2, + 1039, 1056, 1057, 1058, 1059, 1060, 1062, 1063, 1064, 0, + 0, 0, 0, 1069, 1070, 0, 0, 0, 0, 0, + 1133, 0, 0, 0, 0, 2008, 1473, 0, 0, 1435, + 1435, 1169, 1435, 1435, 1437, 1437, 1437, 1858, 2000, 2009, + 2188, 1819, 1825, 1826, 1827, 2134, 2135, 2136, 2137, 2229, + 2230, 2234, 1922, 1814, 2201, 2202, 0, 2282, 1961, 1969, + 1970, 1946, 1955, 1994, 2096, 2213, 1837, 1989, 2059, 1919, + 1941, 1942, 2077, 2078, 1965, 1966, 1945, 2140, 2142, 2158, + 2159, 2144, 2146, 2155, 2161, 2166, 2145, 2157, 2162, 2175, + 2179, 2182, 2183, 2184, 2152, 2150, 2163, 2167, 2169, 2171, + 2177, 2180, 2153, 2151, 2164, 2168, 2170, 2172, 2178, 2181, + 2139, 2143, 2147, 2156, 2174, 2154, 2173, 2148, 2160, 2165, + 2176, 2149, 2141, 1959, 1962, 1949, 1950, 1952, 1954, 1960, + 1967, 1973, 1951, 1972, 1971, 0, 1947, 1948, 1953, 1964, + 1968, 1956, 1957, 1958, 1963, 1974, 2015, 2014, 2013, 2058, + 1985, 2057, 0, 0, 0, 0, 0, 1808, 1864, 1865, + 2185, 1357, 1358, 1359, 1360, 0, 0, 0, 0, 0, + 0, 0, 290, 291, 1486, 1487, 44, 1152, 1608, 1437, + 1437, 1437, 1437, 1437, 1437, 1091, 1092, 1093, 1094, 1095, + 1121, 1122, 1128, 1129, 2072, 2073, 2074, 2075, 1902, 2224, + 1911, 1912, 2054, 2055, 1924, 1925, 2256, 2257, -2, -2, + -2, 232, 233, 234, 235, 236, 237, 238, 239, 0, + 1863, 2199, 2200, 228, 0, 0, 295, 292, 293, 294, + 1135, 1136, 248, 249, 250, 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, 297, 298, 2285, 0, 862, 0, + 0, 0, 0, 0, 0, 1650, 1651, 1511, 0, 1503, + 1502, 63, 0, 889, -2, 0, 0, 0, 0, 47, + 0, 52, 950, 892, 77, 76, 1551, 1554, 0, 0, + 0, 59, 1512, 67, 69, 1513, 0, 894, 895, 0, + 926, 930, 0, 0, 0, 1628, 1627, 1627, 102, 0, + 0, 103, 123, 124, 125, 0, 0, 109, 110, 1614, + 1615, 43, 0, 0, 177, 178, 0, 1109, 428, 0, + 173, 0, 421, 360, 0, 1529, 0, 0, 0, 0, + 0, 1625, 0, 1622, 154, 155, 162, 163, 164, 401, + 401, 401, 575, 0, 0, 165, 165, 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, 670, 0, 402, 0, 573, 574, 464, + 0, 0, 0, 0, 0, 0, 0, 0, 1630, 1631, + 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, 153, 1542, 0, 0, 0, -2, 0, 714, + 0, 0, 0, 1623, 1623, 0, 721, 0, 0, 0, + 726, 0, 0, 727, 0, 790, 790, 788, 789, 729, + 730, 731, 732, 793, 0, 0, 410, 411, 412, 790, + 793, 0, 793, 793, 793, 793, 790, 790, 790, 793, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 2286, + 796, 793, 0, 760, 0, 761, 762, 763, 764, 767, + 768, 770, 2287, 2288, 1640, 1641, 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, @@ -9569,288 +9611,288 @@ var yyDef = [...]int{ 1766, 1767, 1768, 1769, 1770, 1771, 1772, 1773, 1774, 1775, 1776, 1777, 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, 2284, 2284, - 773, 777, 781, 779, 1612, 804, 810, 812, 813, 0, - 0, 823, 826, 845, 49, 1908, 831, 49, 833, 834, - 835, 836, 837, 863, 864, 869, 0, 0, 0, 0, - 875, 876, 877, 0, 0, 880, 881, 882, 0, 0, - 0, 0, 0, 1014, 0, 0, 1140, 1141, 1142, 1143, - 1144, 1145, 1146, 1147, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1039, 1040, 0, 0, 0, 1064, 1065, 1066, - 1067, 1070, 0, 1081, 0, 1083, 1481, -2, 0, 0, - 0, 1075, 1076, 0, 0, 0, 1632, 1632, 0, 0, - 0, 1473, 0, 0, 1166, 0, 1167, 1169, 1170, 1171, - 0, 1172, 1173, 898, 898, 898, 898, 898, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 898, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1632, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 141, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 1632, 0, 0, - 1632, 1632, 0, 0, 220, 221, 222, 223, 224, 225, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 296, 240, 241, 242, 243, 244, - 299, 245, 246, 247, 1151, 0, 0, 0, 46, 853, - 854, 0, 975, 1632, 0, 0, 904, 0, 1647, 57, - 66, 68, 1510, 61, 1510, 0, 908, 0, 0, -2, - -2, 909, 910, 914, 915, 916, 917, 918, 919, 920, - 921, 922, 54, 2282, 55, 0, 74, 0, 48, 0, - 0, 1551, 0, 1554, 0, 0, 0, 374, 1558, 0, - 0, 1503, 1504, 1507, 0, 926, 2004, 930, 0, 932, - 933, 0, 0, 100, 0, 991, 0, 0, 0, 111, - 0, 113, 114, 0, 0, 0, 385, 1615, 1616, 1617, - -2, 408, 0, 385, 369, 307, 308, 309, 360, 311, - 360, 360, 360, 360, 374, 374, 374, 374, 342, 343, - 344, 345, 346, 0, 360, 0, 328, 360, 360, 360, - 360, 350, 351, 352, 353, 354, 355, 356, 357, 312, - 313, 314, 315, 316, 317, 318, 319, 320, 362, 362, - 362, 362, 362, 366, 366, 0, 1109, 0, 389, 0, - 1507, 0, 0, 1541, 1624, 1634, 0, 0, 0, 0, - 0, 132, 0, 0, 0, 576, 619, 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, 649, 961, - 0, 0, 0, 0, 0, 655, 656, 657, 0, 659, - 660, 661, 662, 663, 664, 665, 666, 667, 668, 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, - 978, 979, 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, 147, 1532, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 1622, 0, 0, - 0, 0, 907, 993, 1645, 1646, 722, 0, 0, 793, - 794, 0, 413, 414, 792, 792, 732, 774, 0, 792, - 736, 775, 737, 739, 738, 740, 753, 754, 792, 743, - 790, 791, 744, 745, 746, 747, 748, 749, 750, 770, - 755, 756, 757, 796, 0, 800, 801, 771, 772, 0, - 782, 0, 0, 0, 816, 817, 0, 824, 848, 846, - 847, 849, 841, 842, 843, 844, 0, 850, 0, 0, - 866, 96, 871, 872, 873, 874, 886, 879, 1153, 1011, - 1012, 1013, 0, 1015, 1021, 0, 1136, 1138, 1019, 1020, - 1023, 0, 0, 0, 1017, 1028, 1148, 1149, 1150, 0, - 0, 0, 0, 0, 1032, 1036, 1041, 1042, 1043, 1044, - 1045, 0, 1046, 0, 1049, 1050, 1051, 1052, 1053, 1054, - 1060, 1449, 1450, 1451, 1079, 300, 301, 0, 1080, 0, - 0, 0, 0, 0, 0, 0, 0, 1396, 1397, 1398, + 1796, 1797, 1798, 1799, 1800, 1801, 1802, 1803, 1804, 2286, + 2286, 774, 778, 782, 780, 1613, 805, 811, 813, 814, + 0, 0, 824, 827, 846, 49, 1910, 832, 49, 834, + 835, 836, 837, 838, 864, 865, 870, 0, 0, 0, + 0, 876, 877, 878, 0, 0, 881, 882, 883, 0, + 0, 0, 0, 0, 1015, 0, 0, 1141, 1142, 1143, + 1144, 1145, 1146, 1147, 1148, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1040, 1041, 0, 0, 0, 1065, 1066, + 1067, 1068, 1071, 0, 1082, 0, 1084, 1482, -2, 0, + 0, 0, 1076, 1077, 0, 0, 0, 1633, 1633, 0, + 0, 0, 1474, 0, 0, 1167, 0, 1168, 1170, 1171, + 1172, 0, 1173, 1174, 899, 899, 899, 899, 899, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 899, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1633, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 141, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1633, 0, + 0, 1633, 1633, 0, 0, 220, 221, 222, 223, 224, + 225, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 296, 240, 241, 242, 243, + 244, 299, 245, 246, 247, 1152, 0, 0, 0, 46, + 854, 855, 0, 976, 1633, 0, 0, 905, 0, 1648, + 57, 66, 68, 1511, 61, 1511, 0, 909, 0, 0, + -2, -2, 910, 911, 915, 916, 917, 918, 919, 920, + 921, 922, 923, 54, 2284, 55, 0, 74, 0, 48, + 0, 0, 1552, 0, 1555, 0, 0, 0, 374, 1559, + 0, 0, 1504, 1505, 1508, 0, 927, 2006, 931, 0, + 933, 934, 0, 0, 100, 0, 992, 0, 0, 0, + 111, 0, 113, 114, 0, 0, 0, 385, 1616, 1617, + 1618, -2, 408, 0, 385, 369, 307, 308, 309, 360, + 311, 360, 360, 360, 360, 374, 374, 374, 374, 342, + 343, 344, 345, 346, 0, 360, 0, 328, 360, 360, + 360, 360, 350, 351, 352, 353, 354, 355, 356, 357, + 312, 313, 314, 315, 316, 317, 318, 319, 320, 362, + 362, 362, 362, 362, 366, 366, 0, 1110, 0, 389, + 0, 1508, 0, 0, 1542, 1625, 1635, 0, 0, 0, + 0, 0, 132, 0, 0, 0, 576, 620, 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, 650, + 962, 0, 0, 0, 0, 0, 656, 657, 658, 0, + 660, 661, 662, 663, 664, 665, 666, 667, 668, 669, + 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, 979, 980, 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, 147, + 1533, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1623, 0, + 0, 0, 0, 908, 994, 1646, 1647, 723, 0, 0, + 794, 795, 0, 413, 414, 793, 793, 733, 775, 0, + 793, 737, 776, 738, 740, 739, 741, 754, 755, 793, + 744, 791, 792, 745, 746, 747, 748, 749, 750, 751, + 771, 756, 757, 758, 797, 0, 801, 802, 772, 773, + 0, 783, 0, 0, 0, 817, 818, 0, 825, 849, + 847, 848, 850, 842, 843, 844, 845, 0, 851, 0, + 0, 867, 96, 872, 873, 874, 875, 887, 880, 1154, + 1012, 1013, 1014, 0, 1016, 1022, 0, 1137, 1139, 1020, + 1021, 1024, 0, 0, 0, 1018, 1029, 1149, 1150, 1151, + 0, 0, 0, 0, 0, 1033, 1037, 1042, 1043, 1044, + 1045, 1046, 0, 1047, 0, 1050, 1051, 1052, 1053, 1054, + 1055, 1061, 1450, 1451, 1452, 1080, 300, 301, 0, 1081, + 0, 0, 0, 0, 0, 0, 0, 0, 1397, 1398, 1399, 1400, 1401, 1402, 1403, 1404, 1405, 1406, 1407, 1408, - 1409, 1410, 1411, 1412, 1413, 1414, 1415, 1152, 0, 1633, - 0, 0, 0, 1479, 1476, 0, 0, 0, 1435, 1437, - 0, 0, 0, 899, 900, 0, 0, 0, 0, 0, + 1409, 1410, 1411, 1412, 1413, 1414, 1415, 1416, 1153, 0, + 1634, 0, 0, 0, 1480, 1477, 0, 0, 0, 1436, + 1438, 0, 0, 0, 900, 901, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1416, 1417, 1418, 1419, 1420, 1421, 1422, 1423, + 0, 0, 0, 1417, 1418, 1419, 1420, 1421, 1422, 1423, 1424, 1425, 1426, 1427, 1428, 1429, 1430, 1431, 1432, 1433, - 0, 0, 1452, 0, 0, 0, 0, 0, 0, 0, - 1472, 0, 1085, 1086, 1087, 0, 0, 0, 0, 0, - 0, 1214, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 142, 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, 1360, 1361, 1362, - 1363, 41, 0, 0, 0, 0, 0, 0, 0, 1483, - 0, -2, -2, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 1385, 0, 0, 0, - 0, 0, 0, 1605, 0, 0, 856, 857, 859, 0, - 995, 0, 976, 0, 0, 862, 0, 903, 0, 906, - 60, 62, 912, 913, 0, 934, 923, 911, 56, 51, - 0, 0, 953, 1552, 1555, 1556, 374, 1578, 0, 383, - 383, 380, 1513, 1514, 0, 1506, 1508, 1509, 79, 931, - 927, 0, 1009, 0, 0, 990, 0, 937, 939, 940, - 941, 973, 0, 944, 945, 0, 0, 0, 0, 0, - 98, 992, 104, 0, 112, 0, 0, 117, 118, 105, - 106, 107, 108, 0, 608, -2, 460, 179, 181, 182, - 183, 174, -2, 372, 370, 371, 310, 374, 374, 336, - 337, 338, 339, 340, 341, 0, 348, 0, 329, 330, - 331, 332, 321, 0, 322, 323, 324, 364, 0, 325, - 326, 0, 327, 427, 0, 1515, 390, 391, 393, 401, - 0, 396, 397, 0, 401, 401, 0, 422, 423, 0, - 1507, 1532, 0, 0, 1635, 1634, 1634, 1634, 152, 0, - 167, 168, 169, 170, 171, 172, 644, 0, 0, 620, - 642, 643, 165, 0, 0, 175, 516, 515, 0, 676, - 0, 425, 0, 0, 419, 419, 404, 405, 557, 0, - 0, 651, 652, 653, 654, 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, 595, 598, 600, - 518, 604, 606, 594, 597, 599, 601, 518, 605, 607, - 1529, 1530, 1531, 0, 0, 714, 0, 0, 451, 94, - 1623, 719, 723, 724, 789, 742, 776, 789, 734, 741, - 764, 778, 780, 814, 815, 820, 828, 829, 830, 870, - 0, 0, 0, 0, 878, 0, 0, 1022, 1137, 1139, - 1024, 1025, 1026, 1029, 0, 1033, 1037, 0, 0, 0, - 0, 0, 1084, 1082, 1483, 0, 0, 0, 1133, 0, - 0, 1156, 1157, 0, 0, 0, 0, 1477, 0, 0, - 1164, 0, 1438, 1114, 0, 0, 0, 0, 0, 1114, - 1114, 1114, 1114, 1114, 1114, 1114, 1114, 1114, 1114, 1501, - 1191, 0, 0, 0, 0, 0, 1196, 1197, 1198, 1114, - 0, 1201, 1202, 0, 1204, 0, 1205, 0, 0, 0, - 0, 1212, 1213, 1215, 0, 0, 1218, 1219, 0, 1221, - 0, 1223, 1224, 1225, 1226, 1227, 1228, 0, 1230, 0, - 1232, 1233, 1234, 0, 1236, 0, 1238, 1239, 0, 1241, - 0, 1243, 0, 1246, 0, 1249, 0, 1252, 0, 1255, - 0, 1258, 0, 1261, 0, 1264, 0, 1267, 0, 1270, - 0, 1273, 0, 1276, 0, 1279, 0, 1282, 0, 1285, - 0, 1288, 0, 1291, 1292, 1293, 0, 1295, 0, 1297, - 0, 1300, 1301, 0, 1303, 0, 1306, 0, 1309, 0, - 0, 1310, 0, 0, 0, 1314, 0, 0, 0, 0, - 1323, 1324, 1325, 1326, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1337, 1338, 1339, 1340, 1341, 1342, - 0, 1344, 0, 1115, 0, 0, 1115, 0, 0, 0, - 0, 0, 1154, 1632, 0, 1439, 1440, 1441, 1442, 1443, - 0, 0, 0, 0, 0, 0, 1383, 1384, 1386, 0, - 0, 1389, 0, 1391, 0, 1606, 855, 858, 860, 947, - 996, 997, 0, 0, 0, 0, 977, 1631, 901, 902, - 905, 955, 0, 1487, 0, 0, 934, 1009, 0, 935, - 0, 53, 950, 0, 1560, 1559, 1572, 1585, 383, 383, - 377, 378, 384, 379, 381, 382, 1505, 0, 1510, 0, - 1599, 0, 0, 1588, 0, 0, 0, 0, 0, 0, - 0, 0, 980, 0, 0, 983, 0, 0, 0, 0, - 974, 945, 0, 946, 0, -2, 0, 0, 92, 93, - 0, 0, 0, 115, 116, 0, 0, 122, 386, 387, - 156, 165, 462, 180, 435, 0, 0, 306, 373, 333, - 334, 335, 0, 358, 0, 0, 0, 0, 456, 128, - 1519, 1518, 401, 401, 392, 0, 395, 0, 0, 0, - 1636, 361, 424, 0, 146, 0, 0, 0, 0, 1625, - 614, 0, 0, 621, 0, 0, 0, 525, 0, 536, - 537, 0, 648, -2, 710, 389, 0, 403, 406, 962, - 0, 0, 538, 0, 541, 542, 455, 516, 547, 548, - 562, 549, 497, 498, 495, 0, 0, 1542, 1543, 1548, - 1546, 1547, 133, 583, 585, 589, 584, 588, 0, 0, - 0, 520, 0, 520, 581, 0, 451, 1515, 0, 718, - 452, 453, 792, 792, 865, 97, 0, 868, 0, 0, - 0, 0, 1030, 1034, 1047, 1048, 1444, 1470, 360, 360, - 1457, 360, 366, 1460, 360, 1462, 360, 1465, 360, 1468, - 1469, 0, 0, 1077, 0, 0, 0, 0, 1163, 1480, - 0, 0, 1174, 1113, 1114, 1114, 1114, 1114, 1114, 1180, - 1181, 1182, 1183, 1184, 1185, 1186, 1187, 1188, 1189, 1474, - 0, 0, 0, 1195, 0, 0, 1199, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 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, 1394, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 1108, 1112, 0, 1116, - 1117, 0, 0, 1346, 0, 0, 1364, 0, 0, 0, - 0, 0, 0, 0, 1484, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 998, 1005, 0, 1005, 0, - 1005, 0, 0, 0, 1618, 1619, 1488, 1489, 1009, 1490, - 924, 936, 954, 1578, 0, 1571, 0, -2, 1580, 0, - 0, 0, 1586, 375, 376, 928, 80, 1010, 83, 0, - 1599, 1608, 0, 1596, 1601, 1603, 0, 0, 0, 1592, - 0, 1009, 938, 969, 971, 0, 966, 981, 982, 984, - 0, 986, 0, 988, 989, 949, 943, 0, 100, 0, - 1009, 1009, 99, 0, 994, 119, 120, 121, 461, 184, - 189, 0, 0, 0, 194, 0, 196, 0, 0, 0, - 201, 202, 401, 401, 436, 0, 303, 305, 0, 0, - 187, 374, 0, 374, 0, 365, 367, 0, 437, 457, - 1516, 1517, 0, 0, 394, 398, 399, 400, 0, 148, - 0, 0, 0, 617, 0, 645, 0, 0, 0, 0, - 0, 0, 176, 517, 677, 678, 679, 680, 681, 682, - 683, 684, 685, 0, 401, 0, 0, 0, 401, 401, - 401, 0, 702, 388, 0, 0, 673, 670, 539, 0, - 218, 219, 226, 227, 229, 0, 0, 0, 0, 0, - 546, 949, 1533, 1534, 1535, 0, 1545, 1549, 136, 0, - 0, 0, 0, 591, 596, 602, 0, 519, 603, 715, - 716, 717, 95, 727, 733, 867, 887, 1018, 1031, 1035, - 0, 0, 0, 0, 1471, 1455, 374, 1458, 1459, 1461, - 1463, 1464, 1466, 1467, 1073, 1074, 1078, 0, 1160, 0, - 1162, 0, 1478, 0, 1175, 1176, 1177, 1178, 1179, 1510, - 0, 0, 0, 1194, 0, 0, 1114, 0, 1207, 1206, - 1208, 0, 1210, 1211, 1216, 1217, 1220, 1222, 1229, 1231, - 1235, 1237, 1240, 1242, 1244, 0, 1247, 0, 1250, 0, - 1253, 0, 1256, 0, 1259, 0, 1262, 0, 1265, 0, - 1268, 0, 1271, 0, 1274, 0, 1277, 0, 1280, 0, - 1283, 0, 1286, 0, 1289, 0, 1294, 1296, 0, 1299, - 1302, 1304, 0, 1307, 0, 1311, 0, 1313, 1315, 1316, - 0, 0, 0, 1327, 1328, 1329, 1330, 1331, 1332, 1333, - 1334, 1335, 1336, 1343, 0, 1106, 1345, 1118, 1119, 1124, - 1348, 0, 0, 0, 1351, 0, 0, 0, 1355, 1155, - 1366, 0, 1371, 0, 0, 1377, 0, 1381, 0, 1387, - 1388, 1390, 1392, 0, 0, 0, 0, 0, 0, 0, - 975, 956, 64, 1490, 1494, 0, 1565, 1563, 1563, 1573, - 1574, 0, 0, 1581, 0, 0, 0, 0, 84, 0, - 0, 1587, 0, 0, 1604, 0, 0, 0, 0, 101, - 1501, 963, 970, 0, 0, 964, 0, 965, 985, 987, - 942, 0, 1009, 1009, 90, 91, 0, 190, 0, 192, - 0, 195, 197, 198, 199, 205, 206, 207, 200, 0, - 0, 302, 304, 0, 0, 347, 359, 349, 0, 0, - 1520, 1521, 1522, 1523, 1524, 1525, 1526, 1527, 949, 149, - 150, 151, 609, 0, 619, 0, 951, 0, 612, 0, - 528, 0, 0, 0, 401, 401, 401, 0, 0, 0, - 0, 687, 0, 0, 650, 0, 658, 0, 0, 0, - 230, 231, 0, 1544, 582, 0, 134, 135, 0, 0, - 587, 521, 522, 1071, 0, 0, 0, 1072, 1456, 0, - 0, 0, 0, 0, 1475, 0, 0, 0, 0, 1200, - 1203, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1319, 0, 0, 0, 639, 640, 0, - 1395, 1111, 1501, 0, 1115, 1125, 1126, 0, 1115, 1365, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1006, 0, 0, 0, 957, 958, 0, 0, 0, - 995, 1494, 1499, 0, 0, 1568, 0, 1561, 1564, 1562, - 1575, 0, 0, 1582, 0, 1584, 0, 1609, 1610, 1602, - 1597, 0, 1591, 1594, 1596, 1593, 1510, 967, 0, 972, - 0, 1501, 89, 0, 193, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 203, 204, 0, 0, - 363, 368, 0, 0, 0, 610, 0, 952, 622, 613, - 0, 700, 0, 704, 0, 0, 0, 707, 708, 709, - 686, 0, 690, 429, 674, 671, 672, 540, 0, 137, - 138, 0, 0, 0, 1445, 0, 1448, 1158, 1161, 1159, - 0, 1190, 1192, 1193, 1453, 1454, 1209, 1245, 1248, 1251, - 1254, 1257, 1260, 1263, 1266, 1269, 1272, 1275, 1278, 1281, - 1284, 1287, 1290, 1298, 1305, 1308, 1312, 1317, 0, 1320, - 0, 0, 1321, 0, 641, 1102, 0, 0, 1122, 1123, - 0, 1350, 1352, 1353, 1354, 1367, 0, 1372, 1373, 0, - 1378, 0, 1382, 1393, 0, 1000, 1007, 1008, 0, 1003, - 0, 1004, 0, 948, 1499, 82, 1500, 1497, 0, 1495, - 1492, 1557, 0, 1566, 1567, 1576, 1577, 1583, 0, 0, - 1596, 0, 1590, 87, 0, 0, 0, 1510, 191, 0, - 210, 0, 618, 0, 621, 611, 698, 699, 0, 711, - 703, 705, 706, 688, -2, 1536, 0, 0, 0, 590, - 1446, 0, 0, 1322, 0, 637, 638, 1110, 1103, 0, - 1088, 1089, 1107, 1347, 1349, 0, 0, 0, 999, 959, - 960, 1001, 1002, 81, 0, 1496, 1130, 0, 1491, 0, - 1569, 1570, 1600, 0, 1589, 1595, 968, 975, 0, 88, - 442, 435, 1536, 0, 0, 0, 691, 692, 693, 694, - 695, 696, 697, 579, 1538, 139, 140, 0, 509, 510, - 511, 133, 0, 1165, 1318, 1104, 0, 0, 0, 0, - 0, 1368, 0, 1374, 0, 1379, 0, 1498, 0, 0, - 1493, 1598, 623, 0, 625, 0, -2, 430, 443, 0, - 185, 211, 212, 0, 0, 215, 216, 217, 208, 209, - 129, 0, 0, 712, 0, 1539, 1540, 0, 136, 0, - 0, 1095, 1096, 1097, 1098, 1100, 0, 0, 0, 0, - 1131, 1108, 624, 0, 0, 385, 0, 634, 431, 432, - 0, 438, 439, 440, 441, 213, 214, 646, 0, 0, - 508, 586, 1447, 0, 0, 1369, 0, 1375, 0, 1380, - 0, 626, 627, 635, 0, 433, 0, 434, 0, 0, - 0, 615, 0, 646, 1537, 1105, 1099, 1101, 0, 0, - 1129, 0, 636, 632, 444, 446, 447, 0, 0, 445, - 647, 616, 1370, 1376, 0, 448, 449, 450, 628, 629, - 630, 631, + 1434, 0, 0, 1453, 0, 0, 0, 0, 0, 0, + 0, 1473, 0, 1086, 1087, 1088, 0, 0, 0, 0, + 0, 0, 1215, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 142, 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, 1361, 1362, + 1363, 1364, 41, 0, 0, 0, 0, 0, 0, 0, + 1484, 0, -2, -2, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1386, 0, 0, + 0, 0, 0, 0, 1606, 0, 0, 857, 858, 860, + 0, 996, 0, 977, 0, 0, 863, 0, 904, 0, + 907, 60, 62, 913, 914, 0, 935, 924, 912, 56, + 51, 0, 0, 954, 1553, 1556, 1557, 374, 1579, 0, + 383, 383, 380, 1514, 1515, 0, 1507, 1509, 1510, 79, + 932, 928, 0, 1010, 0, 0, 991, 0, 938, 940, + 941, 942, 974, 0, 945, 946, 0, 0, 0, 0, + 0, 98, 993, 104, 0, 112, 0, 0, 117, 118, + 105, 106, 107, 108, 0, 609, -2, 460, 179, 181, + 182, 183, 174, -2, 372, 370, 371, 310, 374, 374, + 336, 337, 338, 339, 340, 341, 0, 348, 0, 329, + 330, 331, 332, 321, 0, 322, 323, 324, 364, 0, + 325, 326, 0, 327, 427, 0, 1516, 390, 391, 393, + 401, 0, 396, 397, 0, 401, 401, 0, 422, 423, + 0, 1508, 1533, 0, 0, 1636, 1635, 1635, 1635, 152, + 0, 167, 168, 169, 170, 171, 172, 645, 0, 0, + 621, 643, 644, 165, 0, 0, 175, 516, 515, 0, + 677, 0, 425, 0, 0, 419, 419, 404, 405, 557, + 0, 0, 652, 653, 654, 655, 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, 595, 598, + 600, 518, 604, 606, 0, 594, 597, 599, 601, 518, + 605, 607, 1530, 1531, 1532, 0, 0, 715, 0, 0, + 451, 94, 1624, 720, 724, 725, 790, 743, 777, 790, + 735, 742, 765, 779, 781, 815, 816, 821, 829, 830, + 831, 871, 0, 0, 0, 0, 879, 0, 0, 1023, + 1138, 1140, 1025, 1026, 1027, 1030, 0, 1034, 1038, 0, + 0, 0, 0, 0, 1085, 1083, 1484, 0, 0, 0, + 1134, 0, 0, 1157, 1158, 0, 0, 0, 0, 1478, + 0, 0, 1165, 0, 1439, 1115, 0, 0, 0, 0, + 0, 1115, 1115, 1115, 1115, 1115, 1115, 1115, 1115, 1115, + 1115, 1502, 1192, 0, 0, 0, 0, 0, 1197, 1198, + 1199, 1115, 0, 1202, 1203, 0, 1205, 0, 1206, 0, + 0, 0, 0, 1213, 1214, 1216, 0, 0, 1219, 1220, + 0, 1222, 0, 1224, 1225, 1226, 1227, 1228, 1229, 0, + 1231, 0, 1233, 1234, 1235, 0, 1237, 0, 1239, 1240, + 0, 1242, 0, 1244, 0, 1247, 0, 1250, 0, 1253, + 0, 1256, 0, 1259, 0, 1262, 0, 1265, 0, 1268, + 0, 1271, 0, 1274, 0, 1277, 0, 1280, 0, 1283, + 0, 1286, 0, 1289, 0, 1292, 1293, 1294, 0, 1296, + 0, 1298, 0, 1301, 1302, 0, 1304, 0, 1307, 0, + 1310, 0, 0, 1311, 0, 0, 0, 1315, 0, 0, + 0, 0, 1324, 1325, 1326, 1327, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1338, 1339, 1340, 1341, + 1342, 1343, 0, 1345, 0, 1116, 0, 0, 1116, 0, + 0, 0, 0, 0, 1155, 1633, 0, 1440, 1441, 1442, + 1443, 1444, 0, 0, 0, 0, 0, 0, 1384, 1385, + 1387, 0, 0, 1390, 0, 1392, 0, 1607, 856, 859, + 861, 948, 997, 998, 0, 0, 0, 0, 978, 1632, + 902, 903, 906, 956, 0, 1488, 0, 0, 935, 1010, + 0, 936, 0, 53, 951, 0, 1561, 1560, 1573, 1586, + 383, 383, 377, 378, 384, 379, 381, 382, 1506, 0, + 1511, 0, 1600, 0, 0, 1589, 0, 0, 0, 0, + 0, 0, 0, 0, 981, 0, 0, 984, 0, 0, + 0, 0, 975, 946, 0, 947, 0, -2, 0, 0, + 92, 93, 0, 0, 0, 115, 116, 0, 0, 122, + 386, 387, 156, 165, 462, 180, 435, 0, 0, 306, + 373, 333, 334, 335, 0, 358, 0, 0, 0, 0, + 456, 128, 1520, 1519, 401, 401, 392, 0, 395, 0, + 0, 0, 1637, 361, 424, 0, 146, 0, 0, 0, + 0, 1626, 615, 0, 0, 622, 0, 0, 0, 525, + 0, 536, 537, 0, 649, -2, 711, 389, 0, 403, + 406, 963, 0, 0, 538, 0, 541, 542, 455, 516, + 547, 548, 562, 549, 497, 498, 495, 0, 0, 1543, + 1544, 1549, 1547, 1548, 133, 583, 585, 589, 584, 588, + 0, 0, 0, 520, 0, 608, 520, 581, 0, 451, + 1516, 0, 719, 452, 453, 793, 793, 866, 97, 0, + 869, 0, 0, 0, 0, 1031, 1035, 1048, 1049, 1445, + 1471, 360, 360, 1458, 360, 366, 1461, 360, 1463, 360, + 1466, 360, 1469, 1470, 0, 0, 1078, 0, 0, 0, + 0, 1164, 1481, 0, 0, 1175, 1114, 1115, 1115, 1115, + 1115, 1115, 1181, 1182, 1183, 1184, 1185, 1186, 1187, 1188, + 1189, 1190, 1475, 0, 0, 0, 1196, 0, 0, 1200, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 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, 1395, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1109, + 1113, 0, 1117, 1118, 0, 0, 1347, 0, 0, 1365, + 0, 0, 0, 0, 0, 0, 0, 1485, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 999, 1006, + 0, 1006, 0, 1006, 0, 0, 0, 1619, 1620, 1489, + 1490, 1010, 1491, 925, 937, 955, 1579, 0, 1572, 0, + -2, 1581, 0, 0, 0, 1587, 375, 376, 929, 80, + 1011, 83, 0, 1600, 1609, 0, 1597, 1602, 1604, 0, + 0, 0, 1593, 0, 1010, 939, 970, 972, 0, 967, + 982, 983, 985, 0, 987, 0, 989, 990, 950, 944, + 0, 100, 0, 1010, 1010, 99, 0, 995, 119, 120, + 121, 461, 184, 189, 0, 0, 0, 194, 0, 196, + 0, 0, 0, 201, 202, 401, 401, 436, 0, 303, + 305, 0, 0, 187, 374, 0, 374, 0, 365, 367, + 0, 437, 457, 1517, 1518, 0, 0, 394, 398, 399, + 400, 0, 148, 0, 0, 0, 618, 0, 646, 0, + 0, 0, 0, 0, 0, 176, 517, 678, 679, 680, + 681, 682, 683, 684, 685, 686, 0, 401, 0, 0, + 0, 401, 401, 401, 0, 703, 388, 0, 0, 674, + 671, 539, 0, 218, 219, 226, 227, 229, 0, 0, + 0, 0, 0, 546, 950, 1534, 1535, 1536, 0, 1546, + 1550, 136, 0, 0, 0, 0, 591, 596, 602, 0, + 519, 603, 716, 717, 718, 95, 728, 734, 868, 888, + 1019, 1032, 1036, 0, 0, 0, 0, 1472, 1456, 374, + 1459, 1460, 1462, 1464, 1465, 1467, 1468, 1074, 1075, 1079, + 0, 1161, 0, 1163, 0, 1479, 0, 1176, 1177, 1178, + 1179, 1180, 1511, 0, 0, 0, 1195, 0, 0, 1115, + 0, 1208, 1207, 1209, 0, 1211, 1212, 1217, 1218, 1221, + 1223, 1230, 1232, 1236, 1238, 1241, 1243, 1245, 0, 1248, + 0, 1251, 0, 1254, 0, 1257, 0, 1260, 0, 1263, + 0, 1266, 0, 1269, 0, 1272, 0, 1275, 0, 1278, + 0, 1281, 0, 1284, 0, 1287, 0, 1290, 0, 1295, + 1297, 0, 1300, 1303, 1305, 0, 1308, 0, 1312, 0, + 1314, 1316, 1317, 0, 0, 0, 1328, 1329, 1330, 1331, + 1332, 1333, 1334, 1335, 1336, 1337, 1344, 0, 1107, 1346, + 1119, 1120, 1125, 1349, 0, 0, 0, 1352, 0, 0, + 0, 1356, 1156, 1367, 0, 1372, 0, 0, 1378, 0, + 1382, 0, 1388, 1389, 1391, 1393, 0, 0, 0, 0, + 0, 0, 0, 976, 957, 64, 1491, 1495, 0, 1566, + 1564, 1564, 1574, 1575, 0, 0, 1582, 0, 0, 0, + 0, 84, 0, 0, 1588, 0, 0, 1605, 0, 0, + 0, 0, 101, 1502, 964, 971, 0, 0, 965, 0, + 966, 986, 988, 943, 0, 1010, 1010, 90, 91, 0, + 190, 0, 192, 0, 195, 197, 198, 199, 205, 206, + 207, 200, 0, 0, 302, 304, 0, 0, 347, 359, + 349, 0, 0, 1521, 1522, 1523, 1524, 1525, 1526, 1527, + 1528, 950, 149, 150, 151, 610, 0, 620, 0, 952, + 0, 613, 0, 528, 0, 0, 0, 401, 401, 401, + 0, 0, 0, 0, 688, 0, 0, 651, 0, 659, + 0, 0, 0, 230, 231, 0, 1545, 582, 0, 134, + 135, 0, 0, 587, 521, 522, 1072, 0, 0, 0, + 1073, 1457, 0, 0, 0, 0, 0, 1476, 0, 0, + 0, 0, 1201, 1204, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1320, 0, 0, 0, + 640, 641, 0, 1396, 1112, 1502, 0, 1116, 1126, 1127, + 0, 1116, 1366, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1007, 0, 0, 0, 958, 959, + 0, 0, 0, 996, 1495, 1500, 0, 0, 1569, 0, + 1562, 1565, 1563, 1576, 0, 0, 1583, 0, 1585, 0, + 1610, 1611, 1603, 1598, 0, 1592, 1595, 1597, 1594, 1511, + 968, 0, 973, 0, 1502, 89, 0, 193, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 203, + 204, 0, 0, 363, 368, 0, 0, 0, 611, 0, + 953, 623, 614, 0, 701, 0, 705, 0, 0, 0, + 708, 709, 710, 687, 0, 691, 429, 675, 672, 673, + 540, 0, 137, 138, 0, 0, 0, 1446, 0, 1449, + 1159, 1162, 1160, 0, 1191, 1193, 1194, 1454, 1455, 1210, + 1246, 1249, 1252, 1255, 1258, 1261, 1264, 1267, 1270, 1273, + 1276, 1279, 1282, 1285, 1288, 1291, 1299, 1306, 1309, 1313, + 1318, 0, 1321, 0, 0, 1322, 0, 642, 1103, 0, + 0, 1123, 1124, 0, 1351, 1353, 1354, 1355, 1368, 0, + 1373, 1374, 0, 1379, 0, 1383, 1394, 0, 1001, 1008, + 1009, 0, 1004, 0, 1005, 0, 949, 1500, 82, 1501, + 1498, 0, 1496, 1493, 1558, 0, 1567, 1568, 1577, 1578, + 1584, 0, 0, 1597, 0, 1591, 87, 0, 0, 0, + 1511, 191, 0, 210, 0, 619, 0, 622, 612, 699, + 700, 0, 712, 704, 706, 707, 689, -2, 1537, 0, + 0, 0, 590, 1447, 0, 0, 1323, 0, 638, 639, + 1111, 1104, 0, 1089, 1090, 1108, 1348, 1350, 0, 0, + 0, 1000, 960, 961, 1002, 1003, 81, 0, 1497, 1131, + 0, 1492, 0, 1570, 1571, 1601, 0, 1590, 1596, 969, + 976, 0, 88, 442, 435, 1537, 0, 0, 0, 692, + 693, 694, 695, 696, 697, 698, 579, 1539, 139, 140, + 0, 509, 510, 511, 133, 0, 1166, 1319, 1105, 0, + 0, 0, 0, 0, 1369, 0, 1375, 0, 1380, 0, + 1499, 0, 0, 1494, 1599, 624, 0, 626, 0, -2, + 430, 443, 0, 185, 211, 212, 0, 0, 215, 216, + 217, 208, 209, 129, 0, 0, 713, 0, 1540, 1541, + 0, 136, 0, 0, 1096, 1097, 1098, 1099, 1101, 0, + 0, 0, 0, 1132, 1109, 625, 0, 0, 385, 0, + 635, 431, 432, 0, 438, 439, 440, 441, 213, 214, + 647, 0, 0, 508, 586, 1448, 0, 0, 1370, 0, + 1376, 0, 1381, 0, 627, 628, 636, 0, 433, 0, + 434, 0, 0, 0, 616, 0, 647, 1538, 1106, 1100, + 1102, 0, 0, 1130, 0, 637, 633, 444, 446, 447, + 0, 0, 445, 648, 617, 1371, 1377, 0, 448, 449, + 450, 629, 630, 631, 632, } var yyTok1 = [...]int{ @@ -9859,7 +9901,7 @@ var yyTok1 = [...]int{ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 158, 3, 3, 3, 186, 178, 3, 95, 97, 183, 181, 96, 182, 236, 184, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 751, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 752, 166, 165, 167, 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, @@ -9994,7 +10036,7 @@ var yyTok3 = [...]int{ 58060, 735, 58061, 736, 58062, 737, 58063, 738, 58064, 739, 58065, 740, 58066, 741, 58067, 742, 58068, 743, 58069, 744, 58070, 745, 58071, 746, 58072, 747, 58073, 748, 58074, 749, - 58075, 750, 0, + 58075, 750, 58076, 751, 0, } var yyErrorMessages = [...]struct { @@ -14615,17 +14657,29 @@ yydefault: } yyVAL.union = yyLOCAL case 608: + yyDollar = yyS[yypt-6 : yypt+1] + var yyLOCAL Statement +//line sql.y:3411 + { + yyLOCAL = &AlterMigration{ + Type: SetCutOverThresholdMigrationType, + UUID: string(yyDollar[4].str), + Threshold: yyDollar[6].str, + } + } + yyVAL.union = yyLOCAL + case 609: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *PartitionOption -//line sql.y:3412 +//line sql.y:3420 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 609: + case 610: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *PartitionOption -//line sql.y:3416 +//line sql.y:3424 { yyDollar[3].partitionOptionUnion().Partitions = yyDollar[4].integerUnion() yyDollar[3].partitionOptionUnion().SubPartition = yyDollar[5].subPartitionUnion() @@ -14633,10 +14687,10 @@ yydefault: yyLOCAL = yyDollar[3].partitionOptionUnion() } yyVAL.union = yyLOCAL - case 610: + case 611: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *PartitionOption -//line sql.y:3425 +//line sql.y:3433 { yyLOCAL = &PartitionOption{ IsLinear: yyDollar[1].booleanUnion(), @@ -14645,10 +14699,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 611: + case 612: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *PartitionOption -//line sql.y:3433 +//line sql.y:3441 { yyLOCAL = &PartitionOption{ IsLinear: yyDollar[1].booleanUnion(), @@ -14658,10 +14712,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 612: + case 613: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *PartitionOption -//line sql.y:3442 +//line sql.y:3450 { yyLOCAL = &PartitionOption{ Type: yyDollar[1].partitionByTypeUnion(), @@ -14669,10 +14723,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 613: + case 614: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *PartitionOption -//line sql.y:3449 +//line sql.y:3457 { yyLOCAL = &PartitionOption{ Type: yyDollar[1].partitionByTypeUnion(), @@ -14680,18 +14734,18 @@ yydefault: } } yyVAL.union = yyLOCAL - case 614: + case 615: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *SubPartition -//line sql.y:3457 +//line sql.y:3465 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 615: + case 616: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL *SubPartition -//line sql.y:3461 +//line sql.y:3469 { yyLOCAL = &SubPartition{ IsLinear: yyDollar[3].booleanUnion(), @@ -14701,10 +14755,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 616: + case 617: yyDollar = yyS[yypt-9 : yypt+1] var yyLOCAL *SubPartition -//line sql.y:3470 +//line sql.y:3478 { yyLOCAL = &SubPartition{ IsLinear: yyDollar[3].booleanUnion(), @@ -14715,678 +14769,678 @@ yydefault: } } yyVAL.union = yyLOCAL - case 617: + case 618: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL []*PartitionDefinition -//line sql.y:3481 +//line sql.y:3489 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 618: + case 619: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []*PartitionDefinition -//line sql.y:3485 +//line sql.y:3493 { yyLOCAL = yyDollar[2].partDefsUnion() } yyVAL.union = yyLOCAL - case 619: + case 620: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL bool -//line sql.y:3490 +//line sql.y:3498 { yyLOCAL = false } yyVAL.union = yyLOCAL - case 620: + case 621: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL bool -//line sql.y:3494 +//line sql.y:3502 { yyLOCAL = true } yyVAL.union = yyLOCAL - case 621: + case 622: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL int -//line sql.y:3499 +//line sql.y:3507 { yyLOCAL = 0 } yyVAL.union = yyLOCAL - case 622: + case 623: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL int -//line sql.y:3503 +//line sql.y:3511 { yyLOCAL = convertStringToInt(yyDollar[3].str) } yyVAL.union = yyLOCAL - case 623: + case 624: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL TableExpr -//line sql.y:3509 +//line sql.y:3517 { yyLOCAL = &JSONTableExpr{Expr: yyDollar[3].exprUnion(), Filter: yyDollar[5].exprUnion(), Columns: yyDollar[6].jtColumnListUnion(), Alias: yyDollar[8].identifierCS} } yyVAL.union = yyLOCAL - case 624: + case 625: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL []*JtColumnDefinition -//line sql.y:3515 +//line sql.y:3523 { yyLOCAL = yyDollar[3].jtColumnListUnion() } yyVAL.union = yyLOCAL - case 625: + case 626: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []*JtColumnDefinition -//line sql.y:3521 +//line sql.y:3529 { yyLOCAL = []*JtColumnDefinition{yyDollar[1].jtColumnDefinitionUnion()} } yyVAL.union = yyLOCAL - case 626: + case 627: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:3525 +//line sql.y:3533 { yySLICE := (*[]*JtColumnDefinition)(yyIaddr(yyVAL.union)) *yySLICE = append(*yySLICE, yyDollar[3].jtColumnDefinitionUnion()) } - case 627: + case 628: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *JtColumnDefinition -//line sql.y:3531 +//line sql.y:3539 { yyLOCAL = &JtColumnDefinition{JtOrdinal: &JtOrdinalColDef{Name: yyDollar[1].identifierCI}} } yyVAL.union = yyLOCAL - case 628: + case 629: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *JtColumnDefinition -//line sql.y:3535 +//line sql.y:3543 { 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 629: + case 630: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL *JtColumnDefinition -//line sql.y:3541 +//line sql.y:3549 { 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 630: + case 631: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL *JtColumnDefinition -//line sql.y:3547 +//line sql.y:3555 { 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 631: + case 632: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL *JtColumnDefinition -//line sql.y:3553 +//line sql.y:3561 { 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 632: + case 633: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *JtColumnDefinition -//line sql.y:3559 +//line sql.y:3567 { jtNestedPath := &JtNestedPathColDef{Path: yyDollar[3].exprUnion(), Columns: yyDollar[4].jtColumnListUnion()} yyLOCAL = &JtColumnDefinition{JtNestedPath: jtNestedPath} } yyVAL.union = yyLOCAL - case 633: + case 634: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL bool -//line sql.y:3565 +//line sql.y:3573 { yyLOCAL = false } yyVAL.union = yyLOCAL - case 634: + case 635: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL bool -//line sql.y:3569 +//line sql.y:3577 { yyLOCAL = true } yyVAL.union = yyLOCAL - case 635: + case 636: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL bool -//line sql.y:3573 +//line sql.y:3581 { yyLOCAL = false } yyVAL.union = yyLOCAL - case 636: + case 637: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL bool -//line sql.y:3577 +//line sql.y:3585 { yyLOCAL = true } yyVAL.union = yyLOCAL - case 637: + case 638: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *JtOnResponse -//line sql.y:3583 +//line sql.y:3591 { yyLOCAL = yyDollar[1].jtOnResponseUnion() } yyVAL.union = yyLOCAL - case 638: + case 639: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *JtOnResponse -//line sql.y:3589 +//line sql.y:3597 { yyLOCAL = yyDollar[1].jtOnResponseUnion() } yyVAL.union = yyLOCAL - case 639: + case 640: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *JtOnResponse -//line sql.y:3595 +//line sql.y:3603 { yyLOCAL = &JtOnResponse{ResponseType: ErrorJSONType} } yyVAL.union = yyLOCAL - case 640: + case 641: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *JtOnResponse -//line sql.y:3599 +//line sql.y:3607 { yyLOCAL = &JtOnResponse{ResponseType: NullJSONType} } yyVAL.union = yyLOCAL - case 641: + case 642: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *JtOnResponse -//line sql.y:3603 +//line sql.y:3611 { yyLOCAL = &JtOnResponse{ResponseType: DefaultJSONType, Expr: yyDollar[2].exprUnion()} } yyVAL.union = yyLOCAL - case 642: + case 643: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL PartitionByType -//line sql.y:3609 +//line sql.y:3617 { yyLOCAL = RangeType } yyVAL.union = yyLOCAL - case 643: + case 644: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL PartitionByType -//line sql.y:3613 +//line sql.y:3621 { yyLOCAL = ListType } yyVAL.union = yyLOCAL - case 644: + case 645: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL int -//line sql.y:3618 +//line sql.y:3626 { yyLOCAL = -1 } yyVAL.union = yyLOCAL - case 645: + case 646: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL int -//line sql.y:3622 +//line sql.y:3630 { yyLOCAL = convertStringToInt(yyDollar[2].str) } yyVAL.union = yyLOCAL - case 646: + case 647: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL int -//line sql.y:3627 +//line sql.y:3635 { yyLOCAL = -1 } yyVAL.union = yyLOCAL - case 647: + case 648: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL int -//line sql.y:3631 +//line sql.y:3639 { yyLOCAL = convertStringToInt(yyDollar[2].str) } yyVAL.union = yyLOCAL - case 648: + case 649: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *PartitionSpec -//line sql.y:3637 +//line sql.y:3645 { yyLOCAL = &PartitionSpec{Action: AddAction, Definitions: []*PartitionDefinition{yyDollar[4].partDefUnion()}} } yyVAL.union = yyLOCAL - case 649: + case 650: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *PartitionSpec -//line sql.y:3641 +//line sql.y:3649 { yyLOCAL = &PartitionSpec{Action: DropAction, Names: yyDollar[3].partitionsUnion()} } yyVAL.union = yyLOCAL - case 650: + case 651: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL *PartitionSpec -//line sql.y:3645 +//line sql.y:3653 { yyLOCAL = &PartitionSpec{Action: ReorganizeAction, Names: yyDollar[3].partitionsUnion(), Definitions: yyDollar[6].partDefsUnion()} } yyVAL.union = yyLOCAL - case 651: + case 652: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *PartitionSpec -//line sql.y:3649 +//line sql.y:3657 { yyLOCAL = &PartitionSpec{Action: DiscardAction, Names: yyDollar[3].partitionsUnion()} } yyVAL.union = yyLOCAL - case 652: + case 653: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *PartitionSpec -//line sql.y:3653 +//line sql.y:3661 { yyLOCAL = &PartitionSpec{Action: DiscardAction, IsAll: true} } yyVAL.union = yyLOCAL - case 653: + case 654: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *PartitionSpec -//line sql.y:3657 +//line sql.y:3665 { yyLOCAL = &PartitionSpec{Action: ImportAction, Names: yyDollar[3].partitionsUnion()} } yyVAL.union = yyLOCAL - case 654: + case 655: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *PartitionSpec -//line sql.y:3661 +//line sql.y:3669 { yyLOCAL = &PartitionSpec{Action: ImportAction, IsAll: true} } yyVAL.union = yyLOCAL - case 655: + case 656: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *PartitionSpec -//line sql.y:3665 +//line sql.y:3673 { yyLOCAL = &PartitionSpec{Action: TruncateAction, Names: yyDollar[3].partitionsUnion()} } yyVAL.union = yyLOCAL - case 656: + case 657: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *PartitionSpec -//line sql.y:3669 +//line sql.y:3677 { yyLOCAL = &PartitionSpec{Action: TruncateAction, IsAll: true} } yyVAL.union = yyLOCAL - case 657: + case 658: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *PartitionSpec -//line sql.y:3673 +//line sql.y:3681 { yyLOCAL = &PartitionSpec{Action: CoalesceAction, Number: NewIntLiteral(yyDollar[3].str)} } yyVAL.union = yyLOCAL - case 658: + case 659: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL *PartitionSpec -//line sql.y:3677 +//line sql.y:3685 { yyLOCAL = &PartitionSpec{Action: ExchangeAction, Names: Partitions{yyDollar[3].identifierCI}, TableName: yyDollar[6].tableName, WithoutValidation: yyDollar[7].booleanUnion()} } yyVAL.union = yyLOCAL - case 659: + case 660: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *PartitionSpec -//line sql.y:3681 +//line sql.y:3689 { yyLOCAL = &PartitionSpec{Action: AnalyzeAction, Names: yyDollar[3].partitionsUnion()} } yyVAL.union = yyLOCAL - case 660: + case 661: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *PartitionSpec -//line sql.y:3685 +//line sql.y:3693 { yyLOCAL = &PartitionSpec{Action: AnalyzeAction, IsAll: true} } yyVAL.union = yyLOCAL - case 661: + case 662: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *PartitionSpec -//line sql.y:3689 +//line sql.y:3697 { yyLOCAL = &PartitionSpec{Action: CheckAction, Names: yyDollar[3].partitionsUnion()} } yyVAL.union = yyLOCAL - case 662: + case 663: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *PartitionSpec -//line sql.y:3693 +//line sql.y:3701 { yyLOCAL = &PartitionSpec{Action: CheckAction, IsAll: true} } yyVAL.union = yyLOCAL - case 663: + case 664: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *PartitionSpec -//line sql.y:3697 +//line sql.y:3705 { yyLOCAL = &PartitionSpec{Action: OptimizeAction, Names: yyDollar[3].partitionsUnion()} } yyVAL.union = yyLOCAL - case 664: + case 665: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *PartitionSpec -//line sql.y:3701 +//line sql.y:3709 { yyLOCAL = &PartitionSpec{Action: OptimizeAction, IsAll: true} } yyVAL.union = yyLOCAL - case 665: + case 666: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *PartitionSpec -//line sql.y:3705 +//line sql.y:3713 { yyLOCAL = &PartitionSpec{Action: RebuildAction, Names: yyDollar[3].partitionsUnion()} } yyVAL.union = yyLOCAL - case 666: + case 667: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *PartitionSpec -//line sql.y:3709 +//line sql.y:3717 { yyLOCAL = &PartitionSpec{Action: RebuildAction, IsAll: true} } yyVAL.union = yyLOCAL - case 667: + case 668: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *PartitionSpec -//line sql.y:3713 +//line sql.y:3721 { yyLOCAL = &PartitionSpec{Action: RepairAction, Names: yyDollar[3].partitionsUnion()} } yyVAL.union = yyLOCAL - case 668: + case 669: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *PartitionSpec -//line sql.y:3717 +//line sql.y:3725 { yyLOCAL = &PartitionSpec{Action: RepairAction, IsAll: true} } yyVAL.union = yyLOCAL - case 669: + case 670: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *PartitionSpec -//line sql.y:3721 +//line sql.y:3729 { yyLOCAL = &PartitionSpec{Action: UpgradeAction} } yyVAL.union = yyLOCAL - case 670: + case 671: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL bool -//line sql.y:3726 +//line sql.y:3734 { yyLOCAL = false } yyVAL.union = yyLOCAL - case 671: + case 672: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL bool -//line sql.y:3730 +//line sql.y:3738 { yyLOCAL = false } yyVAL.union = yyLOCAL - case 672: + case 673: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL bool -//line sql.y:3734 +//line sql.y:3742 { yyLOCAL = true } yyVAL.union = yyLOCAL - case 673: + case 674: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []*PartitionDefinition -//line sql.y:3740 +//line sql.y:3748 { yyLOCAL = []*PartitionDefinition{yyDollar[1].partDefUnion()} } yyVAL.union = yyLOCAL - case 674: + case 675: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:3744 +//line sql.y:3752 { yySLICE := (*[]*PartitionDefinition)(yyIaddr(yyVAL.union)) *yySLICE = append(*yySLICE, yyDollar[3].partDefUnion()) } - case 675: + case 676: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:3750 +//line sql.y:3758 { yyVAL.partDefUnion().Options = yyDollar[2].partitionDefinitionOptionsUnion() } - case 676: + case 677: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *PartitionDefinitionOptions -//line sql.y:3755 +//line sql.y:3763 { yyLOCAL = &PartitionDefinitionOptions{} } yyVAL.union = yyLOCAL - case 677: + case 678: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *PartitionDefinitionOptions -//line sql.y:3759 +//line sql.y:3767 { yyDollar[1].partitionDefinitionOptionsUnion().ValueRange = yyDollar[2].partitionValueRangeUnion() yyLOCAL = yyDollar[1].partitionDefinitionOptionsUnion() } yyVAL.union = yyLOCAL - case 678: + case 679: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *PartitionDefinitionOptions -//line sql.y:3764 +//line sql.y:3772 { yyDollar[1].partitionDefinitionOptionsUnion().Comment = yyDollar[2].literalUnion() yyLOCAL = yyDollar[1].partitionDefinitionOptionsUnion() } yyVAL.union = yyLOCAL - case 679: + case 680: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *PartitionDefinitionOptions -//line sql.y:3769 +//line sql.y:3777 { yyDollar[1].partitionDefinitionOptionsUnion().Engine = yyDollar[2].partitionEngineUnion() yyLOCAL = yyDollar[1].partitionDefinitionOptionsUnion() } yyVAL.union = yyLOCAL - case 680: + case 681: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *PartitionDefinitionOptions -//line sql.y:3774 +//line sql.y:3782 { yyDollar[1].partitionDefinitionOptionsUnion().DataDirectory = yyDollar[2].literalUnion() yyLOCAL = yyDollar[1].partitionDefinitionOptionsUnion() } yyVAL.union = yyLOCAL - case 681: + case 682: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *PartitionDefinitionOptions -//line sql.y:3779 +//line sql.y:3787 { yyDollar[1].partitionDefinitionOptionsUnion().IndexDirectory = yyDollar[2].literalUnion() yyLOCAL = yyDollar[1].partitionDefinitionOptionsUnion() } yyVAL.union = yyLOCAL - case 682: + case 683: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *PartitionDefinitionOptions -//line sql.y:3784 +//line sql.y:3792 { yyDollar[1].partitionDefinitionOptionsUnion().MaxRows = ptr.Of(yyDollar[2].integerUnion()) yyLOCAL = yyDollar[1].partitionDefinitionOptionsUnion() } yyVAL.union = yyLOCAL - case 683: + case 684: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *PartitionDefinitionOptions -//line sql.y:3789 +//line sql.y:3797 { yyDollar[1].partitionDefinitionOptionsUnion().MinRows = ptr.Of(yyDollar[2].integerUnion()) yyLOCAL = yyDollar[1].partitionDefinitionOptionsUnion() } yyVAL.union = yyLOCAL - case 684: + case 685: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *PartitionDefinitionOptions -//line sql.y:3794 +//line sql.y:3802 { yyDollar[1].partitionDefinitionOptionsUnion().TableSpace = yyDollar[2].str yyLOCAL = yyDollar[1].partitionDefinitionOptionsUnion() } yyVAL.union = yyLOCAL - case 685: + case 686: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *PartitionDefinitionOptions -//line sql.y:3799 +//line sql.y:3807 { yyDollar[1].partitionDefinitionOptionsUnion().SubPartitionDefinitions = yyDollar[2].subPartitionDefinitionsUnion() yyLOCAL = yyDollar[1].partitionDefinitionOptionsUnion() } yyVAL.union = yyLOCAL - case 686: + case 687: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL SubPartitionDefinitions -//line sql.y:3805 +//line sql.y:3813 { yyLOCAL = yyDollar[2].subPartitionDefinitionsUnion() } yyVAL.union = yyLOCAL - case 687: + case 688: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL SubPartitionDefinitions -//line sql.y:3811 +//line sql.y:3819 { yyLOCAL = SubPartitionDefinitions{yyDollar[1].subPartitionDefinitionUnion()} } yyVAL.union = yyLOCAL - case 688: + case 689: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:3815 +//line sql.y:3823 { yySLICE := (*SubPartitionDefinitions)(yyIaddr(yyVAL.union)) *yySLICE = append(*yySLICE, yyDollar[3].subPartitionDefinitionUnion()) } - case 689: + case 690: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *SubPartitionDefinition -//line sql.y:3821 +//line sql.y:3829 { yyLOCAL = &SubPartitionDefinition{Name: yyDollar[2].identifierCI, Options: yyDollar[3].subPartitionDefinitionOptionsUnion()} } yyVAL.union = yyLOCAL - case 690: + case 691: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *SubPartitionDefinitionOptions -//line sql.y:3826 +//line sql.y:3834 { yyLOCAL = &SubPartitionDefinitionOptions{} } yyVAL.union = yyLOCAL - case 691: + case 692: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *SubPartitionDefinitionOptions -//line sql.y:3830 +//line sql.y:3838 { yyDollar[1].subPartitionDefinitionOptionsUnion().Comment = yyDollar[2].literalUnion() yyLOCAL = yyDollar[1].subPartitionDefinitionOptionsUnion() } yyVAL.union = yyLOCAL - case 692: + case 693: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *SubPartitionDefinitionOptions -//line sql.y:3835 +//line sql.y:3843 { yyDollar[1].subPartitionDefinitionOptionsUnion().Engine = yyDollar[2].partitionEngineUnion() yyLOCAL = yyDollar[1].subPartitionDefinitionOptionsUnion() } yyVAL.union = yyLOCAL - case 693: + case 694: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *SubPartitionDefinitionOptions -//line sql.y:3840 +//line sql.y:3848 { yyDollar[1].subPartitionDefinitionOptionsUnion().DataDirectory = yyDollar[2].literalUnion() yyLOCAL = yyDollar[1].subPartitionDefinitionOptionsUnion() } yyVAL.union = yyLOCAL - case 694: + case 695: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *SubPartitionDefinitionOptions -//line sql.y:3845 +//line sql.y:3853 { yyDollar[1].subPartitionDefinitionOptionsUnion().IndexDirectory = yyDollar[2].literalUnion() yyLOCAL = yyDollar[1].subPartitionDefinitionOptionsUnion() } yyVAL.union = yyLOCAL - case 695: + case 696: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *SubPartitionDefinitionOptions -//line sql.y:3850 +//line sql.y:3858 { yyDollar[1].subPartitionDefinitionOptionsUnion().MaxRows = ptr.Of(yyDollar[2].integerUnion()) yyLOCAL = yyDollar[1].subPartitionDefinitionOptionsUnion() } yyVAL.union = yyLOCAL - case 696: + case 697: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *SubPartitionDefinitionOptions -//line sql.y:3855 +//line sql.y:3863 { yyDollar[1].subPartitionDefinitionOptionsUnion().MinRows = ptr.Of(yyDollar[2].integerUnion()) yyLOCAL = yyDollar[1].subPartitionDefinitionOptionsUnion() } yyVAL.union = yyLOCAL - case 697: + case 698: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *SubPartitionDefinitionOptions -//line sql.y:3860 +//line sql.y:3868 { yyDollar[1].subPartitionDefinitionOptionsUnion().TableSpace = yyDollar[2].str yyLOCAL = yyDollar[1].subPartitionDefinitionOptionsUnion() } yyVAL.union = yyLOCAL - case 698: + case 699: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *PartitionValueRange -//line sql.y:3867 +//line sql.y:3875 { yyLOCAL = &PartitionValueRange{ Type: LessThanType, @@ -15394,10 +15448,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 699: + case 700: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *PartitionValueRange -//line sql.y:3874 +//line sql.y:3882 { yyLOCAL = &PartitionValueRange{ Type: LessThanType, @@ -15405,10 +15459,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 700: + case 701: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *PartitionValueRange -//line sql.y:3881 +//line sql.y:3889 { yyLOCAL = &PartitionValueRange{ Type: InType, @@ -15416,131 +15470,131 @@ yydefault: } } yyVAL.union = yyLOCAL - case 701: + case 702: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL bool -//line sql.y:3889 +//line sql.y:3897 { yyLOCAL = false } yyVAL.union = yyLOCAL - case 702: + case 703: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL bool -//line sql.y:3893 +//line sql.y:3901 { yyLOCAL = true } yyVAL.union = yyLOCAL - case 703: + case 704: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *PartitionEngine -//line sql.y:3899 +//line sql.y:3907 { yyLOCAL = &PartitionEngine{Storage: yyDollar[1].booleanUnion(), Name: yyDollar[4].identifierCS.String()} } yyVAL.union = yyLOCAL - case 704: + case 705: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *Literal -//line sql.y:3905 +//line sql.y:3913 { yyLOCAL = NewStrLiteral(yyDollar[3].str) } yyVAL.union = yyLOCAL - case 705: + case 706: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *Literal -//line sql.y:3911 +//line sql.y:3919 { yyLOCAL = NewStrLiteral(yyDollar[4].str) } yyVAL.union = yyLOCAL - case 706: + case 707: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *Literal -//line sql.y:3917 +//line sql.y:3925 { yyLOCAL = NewStrLiteral(yyDollar[4].str) } yyVAL.union = yyLOCAL - case 707: + case 708: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL int -//line sql.y:3923 +//line sql.y:3931 { yyLOCAL = convertStringToInt(yyDollar[3].str) } yyVAL.union = yyLOCAL - case 708: + case 709: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL int -//line sql.y:3929 +//line sql.y:3937 { yyLOCAL = convertStringToInt(yyDollar[3].str) } yyVAL.union = yyLOCAL - case 709: + case 710: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:3935 +//line sql.y:3943 { yyVAL.str = yyDollar[3].identifierCS.String() } - case 710: + case 711: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *PartitionDefinition -//line sql.y:3941 +//line sql.y:3949 { yyLOCAL = &PartitionDefinition{Name: yyDollar[2].identifierCI} } yyVAL.union = yyLOCAL - case 711: + case 712: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3947 +//line sql.y:3955 { yyVAL.str = "" } - case 712: + case 713: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:3951 +//line sql.y:3959 { yyVAL.str = "" } - case 713: + case 714: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Statement -//line sql.y:3957 +//line sql.y:3965 { yyLOCAL = &RenameTable{TablePairs: yyDollar[3].renameTablePairsUnion()} } yyVAL.union = yyLOCAL - case 714: + case 715: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []*RenameTablePair -//line sql.y:3963 +//line sql.y:3971 { yyLOCAL = []*RenameTablePair{{FromTable: yyDollar[1].tableName, ToTable: yyDollar[3].tableName}} } yyVAL.union = yyLOCAL - case 715: + case 716: yyDollar = yyS[yypt-5 : yypt+1] -//line sql.y:3967 +//line sql.y:3975 { yySLICE := (*[]*RenameTablePair)(yyIaddr(yyVAL.union)) *yySLICE = append(*yySLICE, &RenameTablePair{FromTable: yyDollar[3].tableName, ToTable: yyDollar[5].tableName}) } - case 716: + case 717: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL Statement -//line sql.y:3973 +//line sql.y:3981 { yyLOCAL = &DropTable{FromTables: yyDollar[6].tableNamesUnion(), IfExists: yyDollar[5].booleanUnion(), Comments: Comments(yyDollar[2].strs).Parsed(), Temp: yyDollar[3].booleanUnion()} } yyVAL.union = yyLOCAL - case 717: + case 718: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL Statement -//line sql.y:3977 +//line sql.y:3985 { // Change this to an alter statement if yyDollar[4].identifierCI.Lowered() == "primary" { @@ -15550,1377 +15604,1377 @@ yydefault: } } yyVAL.union = yyLOCAL - case 718: + case 719: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Statement -//line sql.y:3986 +//line sql.y:3994 { yyLOCAL = &DropView{FromTables: yyDollar[5].tableNamesUnion(), Comments: Comments(yyDollar[2].strs).Parsed(), IfExists: yyDollar[4].booleanUnion()} } yyVAL.union = yyLOCAL - case 719: + case 720: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL Statement -//line sql.y:3990 +//line sql.y:3998 { yyLOCAL = &DropDatabase{Comments: Comments(yyDollar[2].strs).Parsed(), DBName: yyDollar[5].identifierCS, IfExists: yyDollar[4].booleanUnion()} } yyVAL.union = yyLOCAL - case 720: + case 721: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Statement -//line sql.y:3996 +//line sql.y:4004 { yyLOCAL = &TruncateTable{Table: yyDollar[3].tableName} } yyVAL.union = yyLOCAL - case 721: + case 722: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Statement -//line sql.y:4000 +//line sql.y:4008 { yyLOCAL = &TruncateTable{Table: yyDollar[2].tableName} } yyVAL.union = yyLOCAL - case 722: + case 723: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Statement -//line sql.y:4006 +//line sql.y:4014 { yyLOCAL = &Analyze{IsLocal: yyDollar[2].booleanUnion(), Table: yyDollar[4].tableName} } yyVAL.union = yyLOCAL - case 723: + case 724: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL Statement -//line sql.y:4012 +//line sql.y:4020 { yyLOCAL = &PurgeBinaryLogs{To: string(yyDollar[5].str)} } yyVAL.union = yyLOCAL - case 724: + case 725: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL Statement -//line sql.y:4016 +//line sql.y:4024 { yyLOCAL = &PurgeBinaryLogs{Before: string(yyDollar[5].str)} } yyVAL.union = yyLOCAL - case 725: + case 726: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Statement -//line sql.y:4022 +//line sql.y:4030 { yyLOCAL = &Show{&ShowBasic{Command: Charset, Filter: yyDollar[3].showFilterUnion()}} } yyVAL.union = yyLOCAL - case 726: + case 727: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Statement -//line sql.y:4026 +//line sql.y:4034 { yyLOCAL = &Show{&ShowBasic{Command: Collation, Filter: yyDollar[3].showFilterUnion()}} } yyVAL.union = yyLOCAL - case 727: + case 728: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL Statement -//line sql.y:4030 +//line sql.y:4038 { 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 728: + case 729: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Statement -//line sql.y:4034 +//line sql.y:4042 { yyLOCAL = &Show{&ShowBasic{Command: Database, Filter: yyDollar[3].showFilterUnion()}} } yyVAL.union = yyLOCAL - case 729: + case 730: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Statement -//line sql.y:4038 +//line sql.y:4046 { yyLOCAL = &Show{&ShowBasic{Command: Database, Filter: yyDollar[3].showFilterUnion()}} } yyVAL.union = yyLOCAL - case 730: + case 731: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Statement -//line sql.y:4042 +//line sql.y:4050 { yyLOCAL = &Show{&ShowBasic{Command: Keyspace, Filter: yyDollar[3].showFilterUnion()}} } yyVAL.union = yyLOCAL - case 731: + case 732: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Statement -//line sql.y:4046 +//line sql.y:4054 { yyLOCAL = &Show{&ShowBasic{Command: Keyspace, Filter: yyDollar[3].showFilterUnion()}} } yyVAL.union = yyLOCAL - case 732: + case 733: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Statement -//line sql.y:4050 +//line sql.y:4058 { yyLOCAL = &Show{&ShowBasic{Command: Function, Filter: yyDollar[4].showFilterUnion()}} } yyVAL.union = yyLOCAL - case 733: + case 734: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL Statement -//line sql.y:4054 +//line sql.y:4062 { yyLOCAL = &Show{&ShowBasic{Command: Index, Tbl: yyDollar[5].tableName, DbName: yyDollar[6].identifierCS, Filter: yyDollar[7].showFilterUnion()}} } yyVAL.union = yyLOCAL - case 734: + case 735: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL Statement -//line sql.y:4058 +//line sql.y:4066 { yyLOCAL = &Show{&ShowBasic{Command: OpenTable, DbName: yyDollar[4].identifierCS, Filter: yyDollar[5].showFilterUnion()}} } yyVAL.union = yyLOCAL - case 735: + case 736: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Statement -//line sql.y:4062 +//line sql.y:4070 { yyLOCAL = &Show{&ShowBasic{Command: Privilege}} } yyVAL.union = yyLOCAL - case 736: + case 737: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Statement -//line sql.y:4066 +//line sql.y:4074 { yyLOCAL = &Show{&ShowBasic{Command: Procedure, Filter: yyDollar[4].showFilterUnion()}} } yyVAL.union = yyLOCAL - case 737: + case 738: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Statement -//line sql.y:4070 +//line sql.y:4078 { yyLOCAL = &Show{&ShowBasic{Command: StatusSession, Filter: yyDollar[4].showFilterUnion()}} } yyVAL.union = yyLOCAL - case 738: + case 739: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Statement -//line sql.y:4074 +//line sql.y:4082 { yyLOCAL = &Show{&ShowBasic{Command: StatusGlobal, Filter: yyDollar[4].showFilterUnion()}} } yyVAL.union = yyLOCAL - case 739: + case 740: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Statement -//line sql.y:4078 +//line sql.y:4086 { yyLOCAL = &Show{&ShowBasic{Command: VariableSession, Filter: yyDollar[4].showFilterUnion()}} } yyVAL.union = yyLOCAL - case 740: + case 741: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Statement -//line sql.y:4082 +//line sql.y:4090 { yyLOCAL = &Show{&ShowBasic{Command: VariableGlobal, Filter: yyDollar[4].showFilterUnion()}} } yyVAL.union = yyLOCAL - case 741: + case 742: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL Statement -//line sql.y:4086 +//line sql.y:4094 { yyLOCAL = &Show{&ShowBasic{Command: TableStatus, DbName: yyDollar[4].identifierCS, Filter: yyDollar[5].showFilterUnion()}} } yyVAL.union = yyLOCAL - case 742: + case 743: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL Statement -//line sql.y:4090 +//line sql.y:4098 { yyLOCAL = &Show{&ShowBasic{Command: Table, Full: yyDollar[2].booleanUnion(), DbName: yyDollar[4].identifierCS, Filter: yyDollar[5].showFilterUnion()}} } yyVAL.union = yyLOCAL - case 743: + case 744: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Statement -//line sql.y:4094 +//line sql.y:4102 { yyLOCAL = &Show{&ShowBasic{Command: Trigger, DbName: yyDollar[3].identifierCS, Filter: yyDollar[4].showFilterUnion()}} } yyVAL.union = yyLOCAL - case 744: + case 745: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Statement -//line sql.y:4098 +//line sql.y:4106 { yyLOCAL = &Show{&ShowCreate{Command: CreateDb, Op: yyDollar[4].tableName}} } yyVAL.union = yyLOCAL - case 745: + case 746: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Statement -//line sql.y:4102 +//line sql.y:4110 { yyLOCAL = &Show{&ShowCreate{Command: CreateE, Op: yyDollar[4].tableName}} } yyVAL.union = yyLOCAL - case 746: + case 747: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Statement -//line sql.y:4106 +//line sql.y:4114 { yyLOCAL = &Show{&ShowCreate{Command: CreateF, Op: yyDollar[4].tableName}} } yyVAL.union = yyLOCAL - case 747: + case 748: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Statement -//line sql.y:4110 +//line sql.y:4118 { yyLOCAL = &Show{&ShowCreate{Command: CreateProc, Op: yyDollar[4].tableName}} } yyVAL.union = yyLOCAL - case 748: + case 749: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Statement -//line sql.y:4114 +//line sql.y:4122 { yyLOCAL = &Show{&ShowCreate{Command: CreateTbl, Op: yyDollar[4].tableName}} } yyVAL.union = yyLOCAL - case 749: + case 750: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Statement -//line sql.y:4118 +//line sql.y:4126 { yyLOCAL = &Show{&ShowCreate{Command: CreateTr, Op: yyDollar[4].tableName}} } yyVAL.union = yyLOCAL - case 750: + case 751: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Statement -//line sql.y:4122 +//line sql.y:4130 { yyLOCAL = &Show{&ShowCreate{Command: CreateV, Op: yyDollar[4].tableName}} } yyVAL.union = yyLOCAL - case 751: + case 752: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Statement -//line sql.y:4126 +//line sql.y:4134 { yyLOCAL = &Show{&ShowBasic{Command: Engines}} } yyVAL.union = yyLOCAL - case 752: + case 753: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Statement -//line sql.y:4130 +//line sql.y:4138 { yyLOCAL = &Show{&ShowBasic{Command: Plugins}} } yyVAL.union = yyLOCAL - case 753: + case 754: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Statement -//line sql.y:4134 +//line sql.y:4142 { yyLOCAL = &Show{&ShowBasic{Command: GtidExecGlobal, DbName: yyDollar[4].identifierCS}} } yyVAL.union = yyLOCAL - case 754: + case 755: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Statement -//line sql.y:4138 +//line sql.y:4146 { yyLOCAL = &Show{&ShowBasic{Command: VGtidExecGlobal, DbName: yyDollar[4].identifierCS}} } yyVAL.union = yyLOCAL - case 755: + case 756: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Statement -//line sql.y:4142 +//line sql.y:4150 { yyLOCAL = &Show{&ShowBasic{Command: VitessVariables, Filter: yyDollar[4].showFilterUnion()}} } yyVAL.union = yyLOCAL - case 756: + case 757: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Statement -//line sql.y:4146 +//line sql.y:4154 { yyLOCAL = &Show{&ShowBasic{Command: VitessMigrations, Filter: yyDollar[4].showFilterUnion(), DbName: yyDollar[3].identifierCS}} } yyVAL.union = yyLOCAL - case 757: + case 758: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Statement -//line sql.y:4150 +//line sql.y:4158 { yyLOCAL = &ShowMigrationLogs{UUID: string(yyDollar[3].str)} } yyVAL.union = yyLOCAL - case 758: + case 759: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Statement -//line sql.y:4154 +//line sql.y:4162 { yyLOCAL = &ShowThrottledApps{} } yyVAL.union = yyLOCAL - case 759: + case 760: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Statement -//line sql.y:4158 +//line sql.y:4166 { yyLOCAL = &Show{&ShowBasic{Command: VitessReplicationStatus, Filter: yyDollar[3].showFilterUnion()}} } yyVAL.union = yyLOCAL - case 760: + case 761: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Statement -//line sql.y:4162 +//line sql.y:4170 { yyLOCAL = &ShowThrottlerStatus{} } yyVAL.union = yyLOCAL - case 761: + case 762: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Statement -//line sql.y:4166 +//line sql.y:4174 { yyLOCAL = &Show{&ShowBasic{Command: VschemaTables}} } yyVAL.union = yyLOCAL - case 762: + case 763: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Statement -//line sql.y:4170 +//line sql.y:4178 { yyLOCAL = &Show{&ShowBasic{Command: VschemaKeyspaces}} } yyVAL.union = yyLOCAL - case 763: + case 764: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Statement -//line sql.y:4174 +//line sql.y:4182 { yyLOCAL = &Show{&ShowBasic{Command: VschemaVindexes}} } yyVAL.union = yyLOCAL - case 764: + case 765: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL Statement -//line sql.y:4178 +//line sql.y:4186 { yyLOCAL = &Show{&ShowBasic{Command: VschemaVindexes, Tbl: yyDollar[5].tableName}} } yyVAL.union = yyLOCAL - case 765: + case 766: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Statement -//line sql.y:4182 +//line sql.y:4190 { yyLOCAL = &Show{&ShowBasic{Command: Warnings}} } yyVAL.union = yyLOCAL - case 766: + case 767: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Statement -//line sql.y:4186 +//line sql.y:4194 { yyLOCAL = &Show{&ShowBasic{Command: VitessShards, Filter: yyDollar[3].showFilterUnion()}} } yyVAL.union = yyLOCAL - case 767: + case 768: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Statement -//line sql.y:4190 +//line sql.y:4198 { yyLOCAL = &Show{&ShowBasic{Command: VitessTablets, Filter: yyDollar[3].showFilterUnion()}} } yyVAL.union = yyLOCAL - case 768: + case 769: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Statement -//line sql.y:4194 +//line sql.y:4202 { yyLOCAL = &Show{&ShowBasic{Command: VitessTarget}} } yyVAL.union = yyLOCAL - case 769: + case 770: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Statement -//line sql.y:4201 +//line sql.y:4209 { yyLOCAL = &Show{&ShowOther{Command: string(yyDollar[2].identifierCI.String())}} } yyVAL.union = yyLOCAL - case 770: + case 771: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Statement -//line sql.y:4205 +//line sql.y:4213 { yyLOCAL = &Show{&ShowOther{Command: string(yyDollar[2].str) + " " + string(yyDollar[3].str)}} } yyVAL.union = yyLOCAL - case 771: + case 772: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Statement -//line sql.y:4209 +//line sql.y:4217 { yyLOCAL = &Show{&ShowOther{Command: string(yyDollar[2].str) + " " + yyDollar[3].identifierCI.String()}} } yyVAL.union = yyLOCAL - case 772: + case 773: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Statement -//line sql.y:4213 +//line sql.y:4221 { yyLOCAL = &Show{&ShowOther{Command: string(yyDollar[2].str) + " " + string(yyDollar[3].str)}} } yyVAL.union = yyLOCAL - case 773: + case 774: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Statement -//line sql.y:4217 +//line sql.y:4225 { yyLOCAL = &Show{&ShowOther{Command: string(yyDollar[2].str)}} } yyVAL.union = yyLOCAL - case 774: + case 775: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Statement -//line sql.y:4221 +//line sql.y:4229 { yyLOCAL = &Show{&ShowOther{Command: string(yyDollar[2].str) + " " + string(yyDollar[3].str) + " " + String(yyDollar[4].tableName)}} } yyVAL.union = yyLOCAL - case 775: + case 776: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Statement -//line sql.y:4225 +//line sql.y:4233 { yyLOCAL = &Show{&ShowOther{Command: string(yyDollar[2].str) + " " + string(yyDollar[3].str) + " " + String(yyDollar[4].tableName)}} } yyVAL.union = yyLOCAL - case 776: + case 777: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL Statement -//line sql.y:4229 +//line sql.y:4237 { yyLOCAL = &Show{&ShowOther{Command: string(yyDollar[3].str)}} } yyVAL.union = yyLOCAL - case 777: + case 778: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Statement -//line sql.y:4233 +//line sql.y:4241 { yyLOCAL = &Show{&ShowOther{Command: string(yyDollar[2].str)}} } yyVAL.union = yyLOCAL - case 778: + case 779: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL Statement -//line sql.y:4237 +//line sql.y:4245 { yyLOCAL = &Show{&ShowTransactionStatus{TransactionID: string(yyDollar[5].str)}} } yyVAL.union = yyLOCAL - case 779: + case 780: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Statement -//line sql.y:4241 +//line sql.y:4249 { yyLOCAL = &Show{&ShowTransactionStatus{}} } yyVAL.union = yyLOCAL - case 780: + case 781: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL Statement -//line sql.y:4245 +//line sql.y:4253 { yyLOCAL = &Show{&ShowTransactionStatus{Keyspace: yyDollar[5].identifierCS.String()}} } yyVAL.union = yyLOCAL - case 781: + case 782: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:4250 +//line sql.y:4258 { } - case 782: + case 783: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:4252 +//line sql.y:4260 { } - case 783: + case 784: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:4256 +//line sql.y:4264 { yyVAL.str = "" } - case 784: + case 785: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:4260 +//line sql.y:4268 { yyVAL.str = "extended " } - case 785: + case 786: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL bool -//line sql.y:4266 +//line sql.y:4274 { yyLOCAL = false } yyVAL.union = yyLOCAL - case 786: + case 787: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL bool -//line sql.y:4270 +//line sql.y:4278 { yyLOCAL = true } yyVAL.union = yyLOCAL - case 787: + case 788: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:4276 +//line sql.y:4284 { yyVAL.str = string(yyDollar[1].str) } - case 788: + case 789: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:4280 +//line sql.y:4288 { yyVAL.str = string(yyDollar[1].str) } - case 789: + case 790: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:4286 +//line sql.y:4294 { yyVAL.identifierCS = NewIdentifierCS("") } - case 790: + case 791: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:4290 +//line sql.y:4298 { yyVAL.identifierCS = yyDollar[2].identifierCS } - case 791: + case 792: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:4294 +//line sql.y:4302 { yyVAL.identifierCS = yyDollar[2].identifierCS } - case 792: + case 793: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *ShowFilter -//line sql.y:4300 +//line sql.y:4308 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 793: + case 794: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *ShowFilter -//line sql.y:4304 +//line sql.y:4312 { yyLOCAL = &ShowFilter{Like: string(yyDollar[2].str)} } yyVAL.union = yyLOCAL - case 794: + case 795: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *ShowFilter -//line sql.y:4308 +//line sql.y:4316 { yyLOCAL = &ShowFilter{Filter: yyDollar[2].exprUnion()} } yyVAL.union = yyLOCAL - case 795: + case 796: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *ShowFilter -//line sql.y:4314 +//line sql.y:4322 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 796: + case 797: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *ShowFilter -//line sql.y:4318 +//line sql.y:4326 { yyLOCAL = &ShowFilter{Like: string(yyDollar[2].str)} } yyVAL.union = yyLOCAL - case 797: + case 798: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:4324 +//line sql.y:4332 { yyVAL.empty = struct{}{} } - case 798: + case 799: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:4328 +//line sql.y:4336 { yyVAL.empty = struct{}{} } - case 799: + case 800: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:4332 +//line sql.y:4340 { yyVAL.empty = struct{}{} } - case 800: + case 801: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:4338 +//line sql.y:4346 { yyVAL.str = string(yyDollar[1].str) } - case 801: + case 802: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:4342 +//line sql.y:4350 { yyVAL.str = string(yyDollar[1].str) } - case 802: + case 803: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Statement -//line sql.y:4348 +//line sql.y:4356 { yyLOCAL = &Use{DBName: yyDollar[2].identifierCS} } yyVAL.union = yyLOCAL - case 803: + case 804: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Statement -//line sql.y:4352 +//line sql.y:4360 { yyLOCAL = &Use{DBName: IdentifierCS{v: ""}} } yyVAL.union = yyLOCAL - case 804: + case 805: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Statement -//line sql.y:4356 +//line sql.y:4364 { yyLOCAL = &Use{DBName: NewIdentifierCS(yyDollar[2].identifierCS.String() + "@" + string(yyDollar[3].str))} } yyVAL.union = yyLOCAL - case 805: + case 806: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:4363 +//line sql.y:4371 { yyVAL.identifierCS = NewIdentifierCS(string(yyDollar[1].str)) } - case 806: + case 807: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:4367 +//line sql.y:4375 { yyVAL.identifierCS = NewIdentifierCS("@" + string(yyDollar[1].str)) } - case 807: + case 808: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:4371 +//line sql.y:4379 { yyVAL.identifierCS = NewIdentifierCS("@@" + string(yyDollar[1].str)) } - case 808: + case 809: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:4375 +//line sql.y:4383 { yyVAL.identifierCS = NewIdentifierCS(string(yyDollar[1].str)) } - case 809: + case 810: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Statement -//line sql.y:4382 +//line sql.y:4390 { yyLOCAL = &Begin{} } yyVAL.union = yyLOCAL - case 810: + case 811: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Statement -//line sql.y:4386 +//line sql.y:4394 { yyLOCAL = &Begin{TxAccessModes: yyDollar[3].txAccessModesUnion()} } yyVAL.union = yyLOCAL - case 811: + case 812: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL []TxAccessMode -//line sql.y:4391 +//line sql.y:4399 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 812: + case 813: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []TxAccessMode -//line sql.y:4395 +//line sql.y:4403 { yyLOCAL = yyDollar[1].txAccessModesUnion() } yyVAL.union = yyLOCAL - case 813: + case 814: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []TxAccessMode -//line sql.y:4401 +//line sql.y:4409 { yyLOCAL = []TxAccessMode{yyDollar[1].txAccessModeUnion()} } yyVAL.union = yyLOCAL - case 814: + case 815: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:4405 +//line sql.y:4413 { yySLICE := (*[]TxAccessMode)(yyIaddr(yyVAL.union)) *yySLICE = append(*yySLICE, yyDollar[3].txAccessModeUnion()) } - case 815: + case 816: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL TxAccessMode -//line sql.y:4411 +//line sql.y:4419 { yyLOCAL = WithConsistentSnapshot } yyVAL.union = yyLOCAL - case 816: + case 817: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL TxAccessMode -//line sql.y:4415 +//line sql.y:4423 { yyLOCAL = ReadWrite } yyVAL.union = yyLOCAL - case 817: + case 818: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL TxAccessMode -//line sql.y:4419 +//line sql.y:4427 { yyLOCAL = ReadOnly } yyVAL.union = yyLOCAL - case 818: + case 819: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Statement -//line sql.y:4426 +//line sql.y:4434 { yyLOCAL = &Commit{} } yyVAL.union = yyLOCAL - case 819: + case 820: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Statement -//line sql.y:4432 +//line sql.y:4440 { yyLOCAL = &Rollback{} } yyVAL.union = yyLOCAL - case 820: + case 821: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL Statement -//line sql.y:4436 +//line sql.y:4444 { yyLOCAL = &SRollback{Name: yyDollar[5].identifierCI} } yyVAL.union = yyLOCAL - case 821: + case 822: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:4441 +//line sql.y:4449 { yyVAL.empty = struct{}{} } - case 822: + case 823: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:4443 +//line sql.y:4451 { yyVAL.empty = struct{}{} } - case 823: + case 824: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:4446 +//line sql.y:4454 { yyVAL.empty = struct{}{} } - case 824: + case 825: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:4448 +//line sql.y:4456 { yyVAL.empty = struct{}{} } - case 825: + case 826: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Statement -//line sql.y:4452 +//line sql.y:4460 { yyLOCAL = &Savepoint{Name: yyDollar[2].identifierCI} } yyVAL.union = yyLOCAL - case 826: + case 827: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Statement -//line sql.y:4458 +//line sql.y:4466 { yyLOCAL = &Release{Name: yyDollar[3].identifierCI} } yyVAL.union = yyLOCAL - case 827: + case 828: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL ExplainType -//line sql.y:4463 +//line sql.y:4471 { yyLOCAL = EmptyType } yyVAL.union = yyLOCAL - case 828: + case 829: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL ExplainType -//line sql.y:4467 +//line sql.y:4475 { yyLOCAL = JSONType } yyVAL.union = yyLOCAL - case 829: + case 830: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL ExplainType -//line sql.y:4471 +//line sql.y:4479 { yyLOCAL = TreeType } yyVAL.union = yyLOCAL - case 830: + case 831: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL ExplainType -//line sql.y:4475 +//line sql.y:4483 { yyLOCAL = TraditionalType } yyVAL.union = yyLOCAL - case 831: + case 832: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL ExplainType -//line sql.y:4479 +//line sql.y:4487 { yyLOCAL = AnalyzeType } yyVAL.union = yyLOCAL - case 832: + case 833: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL VExplainType -//line sql.y:4484 +//line sql.y:4492 { yyLOCAL = PlanVExplainType } yyVAL.union = yyLOCAL - case 833: + case 834: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL VExplainType -//line sql.y:4488 +//line sql.y:4496 { yyLOCAL = PlanVExplainType } yyVAL.union = yyLOCAL - case 834: + case 835: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL VExplainType -//line sql.y:4492 +//line sql.y:4500 { yyLOCAL = AllVExplainType } yyVAL.union = yyLOCAL - case 835: + case 836: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL VExplainType -//line sql.y:4496 +//line sql.y:4504 { yyLOCAL = QueriesVExplainType } yyVAL.union = yyLOCAL - case 836: + case 837: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL VExplainType -//line sql.y:4500 +//line sql.y:4508 { yyLOCAL = TraceVExplainType } yyVAL.union = yyLOCAL - case 837: + case 838: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL VExplainType -//line sql.y:4504 +//line sql.y:4512 { yyLOCAL = KeysVExplainType } yyVAL.union = yyLOCAL - case 838: + case 839: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:4510 +//line sql.y:4518 { yyVAL.str = yyDollar[1].str } - case 839: + case 840: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:4514 +//line sql.y:4522 { yyVAL.str = yyDollar[1].str } - case 840: + case 841: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:4518 +//line sql.y:4526 { yyVAL.str = yyDollar[1].str } - case 841: + case 842: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Statement -//line sql.y:4524 +//line sql.y:4532 { yyLOCAL = yyDollar[1].selStmtUnion() } yyVAL.union = yyLOCAL - case 842: + case 843: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Statement -//line sql.y:4528 +//line sql.y:4536 { yyLOCAL = yyDollar[1].statementUnion() } yyVAL.union = yyLOCAL - case 843: + case 844: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Statement -//line sql.y:4532 +//line sql.y:4540 { yyLOCAL = yyDollar[1].statementUnion() } yyVAL.union = yyLOCAL - case 844: + case 845: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Statement -//line sql.y:4536 +//line sql.y:4544 { yyLOCAL = yyDollar[1].statementUnion() } yyVAL.union = yyLOCAL - case 845: + case 846: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:4541 +//line sql.y:4549 { yyVAL.str = "" } - case 846: + case 847: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:4545 +//line sql.y:4553 { yyVAL.str = yyDollar[1].identifierCI.val } - case 847: + case 848: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:4549 +//line sql.y:4557 { yyVAL.str = encodeSQLString(yyDollar[1].str) } - case 848: + case 849: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Statement -//line sql.y:4555 +//line sql.y:4563 { yyLOCAL = &ExplainTab{Table: yyDollar[3].tableName, Wild: yyDollar[4].str} } yyVAL.union = yyLOCAL - case 849: + case 850: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Statement -//line sql.y:4559 +//line sql.y:4567 { yyLOCAL = &ExplainStmt{Type: yyDollar[3].explainTypeUnion(), Statement: yyDollar[4].statementUnion(), Comments: Comments(yyDollar[2].strs).Parsed()} } yyVAL.union = yyLOCAL - case 850: + case 851: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Statement -//line sql.y:4565 +//line sql.y:4573 { yyLOCAL = &VExplainStmt{Type: yyDollar[3].vexplainTypeUnion(), Statement: yyDollar[4].statementUnion(), Comments: Comments(yyDollar[2].strs).Parsed()} } yyVAL.union = yyLOCAL - case 851: + case 852: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Statement -//line sql.y:4571 +//line sql.y:4579 { yyLOCAL = &OtherAdmin{} } yyVAL.union = yyLOCAL - case 852: + case 853: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Statement -//line sql.y:4575 +//line sql.y:4583 { yyLOCAL = &OtherAdmin{} } yyVAL.union = yyLOCAL - case 853: + case 854: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Statement -//line sql.y:4581 +//line sql.y:4589 { yyLOCAL = &LockTables{Tables: yyDollar[3].tableAndLockTypesUnion()} } yyVAL.union = yyLOCAL - case 854: + case 855: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL TableAndLockTypes -//line sql.y:4587 +//line sql.y:4595 { yyLOCAL = TableAndLockTypes{yyDollar[1].tableAndLockTypeUnion()} } yyVAL.union = yyLOCAL - case 855: + case 856: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:4591 +//line sql.y:4599 { yySLICE := (*TableAndLockTypes)(yyIaddr(yyVAL.union)) *yySLICE = append(*yySLICE, yyDollar[3].tableAndLockTypeUnion()) } - case 856: + case 857: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *TableAndLockType -//line sql.y:4597 +//line sql.y:4605 { yyLOCAL = &TableAndLockType{Table: yyDollar[1].aliasedTableNameUnion(), Lock: yyDollar[2].lockTypeUnion()} } yyVAL.union = yyLOCAL - case 857: + case 858: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL LockType -//line sql.y:4603 +//line sql.y:4611 { yyLOCAL = Read } yyVAL.union = yyLOCAL - case 858: + case 859: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL LockType -//line sql.y:4607 +//line sql.y:4615 { yyLOCAL = ReadLocal } yyVAL.union = yyLOCAL - case 859: + case 860: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL LockType -//line sql.y:4611 +//line sql.y:4619 { yyLOCAL = Write } yyVAL.union = yyLOCAL - case 860: + case 861: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL LockType -//line sql.y:4615 +//line sql.y:4623 { yyLOCAL = LowPriorityWrite } yyVAL.union = yyLOCAL - case 861: + case 862: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Statement -//line sql.y:4621 +//line sql.y:4629 { yyLOCAL = &UnlockTables{} } yyVAL.union = yyLOCAL - case 862: + case 863: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Statement -//line sql.y:4627 +//line sql.y:4635 { yyLOCAL = &RevertMigration{Comments: Comments(yyDollar[2].strs).Parsed(), UUID: string(yyDollar[4].str)} } yyVAL.union = yyLOCAL - case 863: + case 864: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Statement -//line sql.y:4633 +//line sql.y:4641 { yyLOCAL = &Flush{IsLocal: yyDollar[2].booleanUnion(), FlushOptions: yyDollar[3].strs} } yyVAL.union = yyLOCAL - case 864: + case 865: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Statement -//line sql.y:4637 +//line sql.y:4645 { yyLOCAL = &Flush{IsLocal: yyDollar[2].booleanUnion()} } yyVAL.union = yyLOCAL - case 865: + case 866: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Statement -//line sql.y:4641 +//line sql.y:4649 { yyLOCAL = &Flush{IsLocal: yyDollar[2].booleanUnion(), WithLock: true} } yyVAL.union = yyLOCAL - case 866: + case 867: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Statement -//line sql.y:4645 +//line sql.y:4653 { yyLOCAL = &Flush{IsLocal: yyDollar[2].booleanUnion(), TableNames: yyDollar[4].tableNamesUnion()} } yyVAL.union = yyLOCAL - case 867: + case 868: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL Statement -//line sql.y:4649 +//line sql.y:4657 { yyLOCAL = &Flush{IsLocal: yyDollar[2].booleanUnion(), TableNames: yyDollar[4].tableNamesUnion(), WithLock: true} } yyVAL.union = yyLOCAL - case 868: + case 869: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Statement -//line sql.y:4653 +//line sql.y:4661 { yyLOCAL = &Flush{IsLocal: yyDollar[2].booleanUnion(), TableNames: yyDollar[4].tableNamesUnion(), ForExport: true} } yyVAL.union = yyLOCAL - case 869: + case 870: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:4659 +//line sql.y:4667 { yyVAL.strs = []string{yyDollar[1].str} } - case 870: + case 871: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:4663 +//line sql.y:4671 { yyVAL.strs = append(yyDollar[1].strs, yyDollar[3].str) } - case 871: - yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:4669 - { - yyVAL.str = string(yyDollar[1].str) + " " + string(yyDollar[2].str) - } case 872: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:4673 +//line sql.y:4677 { yyVAL.str = string(yyDollar[1].str) + " " + string(yyDollar[2].str) } case 873: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:4677 +//line sql.y:4681 { yyVAL.str = string(yyDollar[1].str) + " " + string(yyDollar[2].str) } case 874: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:4681 +//line sql.y:4685 { yyVAL.str = string(yyDollar[1].str) + " " + string(yyDollar[2].str) } case 875: - yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:4685 + yyDollar = yyS[yypt-2 : yypt+1] +//line sql.y:4689 { - yyVAL.str = string(yyDollar[1].str) + yyVAL.str = string(yyDollar[1].str) + " " + string(yyDollar[2].str) } case 876: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:4689 +//line sql.y:4693 { yyVAL.str = string(yyDollar[1].str) } case 877: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:4693 +//line sql.y:4697 { yyVAL.str = string(yyDollar[1].str) } case 878: + yyDollar = yyS[yypt-1 : yypt+1] +//line sql.y:4701 + { + yyVAL.str = string(yyDollar[1].str) + } + case 879: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:4697 +//line sql.y:4705 { yyVAL.str = string(yyDollar[1].str) + " " + string(yyDollar[2].str) + yyDollar[3].str } - case 879: + case 880: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:4701 +//line sql.y:4709 { yyVAL.str = string(yyDollar[1].str) + " " + string(yyDollar[2].str) } - case 880: + case 881: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:4705 +//line sql.y:4713 { yyVAL.str = string(yyDollar[1].str) } - case 881: + case 882: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:4709 +//line sql.y:4717 { yyVAL.str = string(yyDollar[1].str) } - case 882: + case 883: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:4713 +//line sql.y:4721 { yyVAL.str = string(yyDollar[1].str) } - case 883: + case 884: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL bool -//line sql.y:4718 +//line sql.y:4726 { yyLOCAL = false } yyVAL.union = yyLOCAL - case 884: + case 885: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL bool -//line sql.y:4722 +//line sql.y:4730 { yyLOCAL = true } yyVAL.union = yyLOCAL - case 885: + case 886: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL bool -//line sql.y:4726 +//line sql.y:4734 { yyLOCAL = true } yyVAL.union = yyLOCAL - case 886: + case 887: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:4731 +//line sql.y:4739 { yyVAL.str = "" } - case 887: + case 888: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:4735 +//line sql.y:4743 { yyVAL.str = " " + string(yyDollar[1].str) + " " + string(yyDollar[2].str) + " " + yyDollar[3].identifierCI.String() } - case 888: + case 889: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:4740 +//line sql.y:4748 { setAllowComments(yylex, true) } - case 889: + case 890: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:4744 +//line sql.y:4752 { yyVAL.strs = yyDollar[2].strs setAllowComments(yylex, false) } - case 890: + case 891: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:4750 +//line sql.y:4758 { yyVAL.strs = nil } - case 891: + case 892: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:4754 +//line sql.y:4762 { yyVAL.strs = append(yyDollar[1].strs, yyDollar[2].str) } - case 892: + case 893: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL bool -//line sql.y:4760 +//line sql.y:4768 { yyLOCAL = true } yyVAL.union = yyLOCAL - case 893: + case 894: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL bool -//line sql.y:4764 +//line sql.y:4772 { yyLOCAL = false } yyVAL.union = yyLOCAL - case 894: + case 895: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL bool -//line sql.y:4768 +//line sql.y:4776 { yyLOCAL = true } yyVAL.union = yyLOCAL - case 895: + case 896: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:4773 +//line sql.y:4781 { yyVAL.str = "" } - case 896: + case 897: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:4777 +//line sql.y:4785 { yyVAL.str = SQLNoCacheStr } - case 897: + case 898: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:4781 +//line sql.y:4789 { yyVAL.str = SQLCacheStr } - case 898: + case 899: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL bool -//line sql.y:4786 +//line sql.y:4794 { yyLOCAL = false } yyVAL.union = yyLOCAL - case 899: + case 900: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL bool -//line sql.y:4790 +//line sql.y:4798 { yyLOCAL = true } yyVAL.union = yyLOCAL - case 900: + case 901: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL bool -//line sql.y:4794 +//line sql.y:4802 { yyLOCAL = true } yyVAL.union = yyLOCAL - case 901: + case 902: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL Statement -//line sql.y:4800 +//line sql.y:4808 { yyLOCAL = &PrepareStmt{Name: yyDollar[3].identifierCI, Comments: Comments(yyDollar[2].strs).Parsed(), Statement: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 902: + case 903: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL Statement -//line sql.y:4804 +//line sql.y:4812 { yyLOCAL = &PrepareStmt{ Name: yyDollar[3].identifierCI, @@ -16929,597 +16983,597 @@ yydefault: } } yyVAL.union = yyLOCAL - case 903: + case 904: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Statement -//line sql.y:4814 +//line sql.y:4822 { yyLOCAL = &ExecuteStmt{Name: yyDollar[3].identifierCI, Comments: Comments(yyDollar[2].strs).Parsed(), Arguments: yyDollar[4].variablesUnion()} } yyVAL.union = yyLOCAL - case 904: + case 905: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL []*Variable -//line sql.y:4819 +//line sql.y:4827 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 905: + case 906: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL []*Variable -//line sql.y:4823 +//line sql.y:4831 { yyLOCAL = yyDollar[2].variablesUnion() } yyVAL.union = yyLOCAL - case 906: + case 907: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Statement -//line sql.y:4829 +//line sql.y:4837 { yyLOCAL = &DeallocateStmt{Comments: Comments(yyDollar[2].strs).Parsed(), Name: yyDollar[4].identifierCI} } yyVAL.union = yyLOCAL - case 907: + case 908: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Statement -//line sql.y:4833 +//line sql.y:4841 { yyLOCAL = &DeallocateStmt{Comments: Comments(yyDollar[2].strs).Parsed(), Name: yyDollar[4].identifierCI} } yyVAL.union = yyLOCAL - case 908: + case 909: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:4838 +//line sql.y:4846 { yyVAL.strs = nil } - case 909: + case 910: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:4842 +//line sql.y:4850 { yyVAL.strs = yyDollar[1].strs } - case 910: + case 911: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:4848 +//line sql.y:4856 { yyVAL.strs = []string{yyDollar[1].str} } - case 911: + case 912: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:4852 +//line sql.y:4860 { yyVAL.strs = append(yyDollar[1].strs, yyDollar[2].str) } - case 912: + case 913: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:4858 +//line sql.y:4866 { yyVAL.str = SQLNoCacheStr } - case 913: + case 914: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:4862 +//line sql.y:4870 { yyVAL.str = SQLCacheStr } - case 914: + case 915: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:4866 +//line sql.y:4874 { yyVAL.str = DistinctStr } - case 915: + case 916: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:4870 +//line sql.y:4878 { yyVAL.str = DistinctStr } - case 916: + case 917: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:4874 +//line sql.y:4882 { yyVAL.str = HighPriorityStr } - case 917: + case 918: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:4878 +//line sql.y:4886 { yyVAL.str = StraightJoinHint } - case 918: + case 919: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:4882 +//line sql.y:4890 { yyVAL.str = SQLBufferResultStr } - case 919: + case 920: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:4886 +//line sql.y:4894 { yyVAL.str = SQLSmallResultStr } - case 920: + case 921: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:4890 +//line sql.y:4898 { yyVAL.str = SQLBigResultStr } - case 921: + case 922: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:4894 +//line sql.y:4902 { yyVAL.str = SQLCalcFoundRowsStr } - case 922: + case 923: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:4898 +//line sql.y:4906 { 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 923: + case 924: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL SelectExprs -//line sql.y:4904 +//line sql.y:4912 { yyLOCAL = SelectExprs{yyDollar[1].selectExprUnion()} } yyVAL.union = yyLOCAL - case 924: + case 925: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:4908 +//line sql.y:4916 { yySLICE := (*SelectExprs)(yyIaddr(yyVAL.union)) *yySLICE = append(*yySLICE, yyDollar[3].selectExprUnion()) } - case 925: + case 926: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL SelectExpr -//line sql.y:4914 +//line sql.y:4922 { yyLOCAL = &StarExpr{} } yyVAL.union = yyLOCAL - case 926: + case 927: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL SelectExpr -//line sql.y:4918 +//line sql.y:4926 { yyLOCAL = &AliasedExpr{Expr: yyDollar[1].exprUnion(), As: yyDollar[2].identifierCI} } yyVAL.union = yyLOCAL - case 927: + case 928: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL SelectExpr -//line sql.y:4922 +//line sql.y:4930 { yyLOCAL = &StarExpr{TableName: TableName{Name: yyDollar[1].identifierCS}} } yyVAL.union = yyLOCAL - case 928: + case 929: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL SelectExpr -//line sql.y:4926 +//line sql.y:4934 { yyLOCAL = &StarExpr{TableName: TableName{Qualifier: yyDollar[1].identifierCS, Name: yyDollar[3].identifierCS}} } yyVAL.union = yyLOCAL - case 929: + case 930: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:4931 +//line sql.y:4939 { yyVAL.identifierCI = IdentifierCI{} } - case 930: + case 931: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:4935 +//line sql.y:4943 { yyVAL.identifierCI = yyDollar[1].identifierCI } - case 931: + case 932: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:4939 +//line sql.y:4947 { yyVAL.identifierCI = yyDollar[2].identifierCI } - case 933: + case 934: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:4946 +//line sql.y:4954 { yyVAL.identifierCI = NewIdentifierCI(string(yyDollar[1].str)) } - case 934: + case 935: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL TableExprs -//line sql.y:4951 +//line sql.y:4959 { yyLOCAL = TableExprs{&AliasedTableExpr{Expr: TableName{Name: NewIdentifierCS("dual")}}} } yyVAL.union = yyLOCAL - case 935: + case 936: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL TableExprs -//line sql.y:4955 +//line sql.y:4963 { yyLOCAL = yyDollar[1].tableExprsUnion() } yyVAL.union = yyLOCAL - case 936: + case 937: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL TableExprs -//line sql.y:4961 +//line sql.y:4969 { yyLOCAL = yyDollar[2].tableExprsUnion() } yyVAL.union = yyLOCAL - case 937: + case 938: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL TableExprs -//line sql.y:4967 +//line sql.y:4975 { yyLOCAL = TableExprs{yyDollar[1].tableExprUnion()} } yyVAL.union = yyLOCAL - case 938: + case 939: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:4971 +//line sql.y:4979 { yySLICE := (*TableExprs)(yyIaddr(yyVAL.union)) *yySLICE = append(*yySLICE, yyDollar[3].tableExprUnion()) } - case 941: + case 942: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL TableExpr -//line sql.y:4981 +//line sql.y:4989 { yyLOCAL = yyDollar[1].aliasedTableNameUnion() } yyVAL.union = yyLOCAL - case 942: + case 943: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL TableExpr -//line sql.y:4985 +//line sql.y:4993 { yyLOCAL = &AliasedTableExpr{Expr: yyDollar[1].derivedTableUnion(), As: yyDollar[3].identifierCS, Columns: yyDollar[4].columnsUnion()} } yyVAL.union = yyLOCAL - case 943: + case 944: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL TableExpr -//line sql.y:4989 +//line sql.y:4997 { yyLOCAL = &ParenTableExpr{Exprs: yyDollar[2].tableExprsUnion()} } yyVAL.union = yyLOCAL - case 944: + case 945: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL TableExpr -//line sql.y:4993 +//line sql.y:5001 { yyLOCAL = yyDollar[1].tableExprUnion() } yyVAL.union = yyLOCAL - case 945: + case 946: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *DerivedTable -//line sql.y:4999 +//line sql.y:5007 { yyLOCAL = &DerivedTable{Lateral: false, Select: yyDollar[1].selStmtUnion()} } yyVAL.union = yyLOCAL - case 946: + case 947: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *DerivedTable -//line sql.y:5003 +//line sql.y:5011 { yyLOCAL = &DerivedTable{Lateral: true, Select: yyDollar[2].selStmtUnion()} } yyVAL.union = yyLOCAL - case 947: + case 948: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *AliasedTableExpr -//line sql.y:5009 +//line sql.y:5017 { yyLOCAL = &AliasedTableExpr{Expr: yyDollar[1].tableName, As: yyDollar[2].identifierCS, Hints: yyDollar[3].indexHintsUnion()} } yyVAL.union = yyLOCAL - case 948: + case 949: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL *AliasedTableExpr -//line sql.y:5013 +//line sql.y:5021 { yyLOCAL = &AliasedTableExpr{Expr: yyDollar[1].tableName, Partitions: yyDollar[4].partitionsUnion(), As: yyDollar[6].identifierCS, Hints: yyDollar[7].indexHintsUnion()} } yyVAL.union = yyLOCAL - case 949: + case 950: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL Columns -//line sql.y:5018 +//line sql.y:5026 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 950: + case 951: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Columns -//line sql.y:5022 +//line sql.y:5030 { yyLOCAL = yyDollar[2].columnsUnion() } yyVAL.union = yyLOCAL - case 951: + case 952: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL Columns -//line sql.y:5027 +//line sql.y:5035 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 952: + case 953: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Columns -//line sql.y:5031 +//line sql.y:5039 { yyLOCAL = yyDollar[1].columnsUnion() } yyVAL.union = yyLOCAL - case 953: + case 954: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Columns -//line sql.y:5037 +//line sql.y:5045 { yyLOCAL = Columns{yyDollar[1].identifierCI} } yyVAL.union = yyLOCAL - case 954: + case 955: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:5041 +//line sql.y:5049 { yySLICE := (*Columns)(yyIaddr(yyVAL.union)) *yySLICE = append(*yySLICE, yyDollar[3].identifierCI) } - case 955: + case 956: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []*Variable -//line sql.y:5047 +//line sql.y:5055 { yyLOCAL = []*Variable{yyDollar[1].variableUnion()} } yyVAL.union = yyLOCAL - case 956: + case 957: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:5051 +//line sql.y:5059 { yySLICE := (*[]*Variable)(yyIaddr(yyVAL.union)) *yySLICE = append(*yySLICE, yyDollar[3].variableUnion()) } - case 957: + case 958: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Columns -//line sql.y:5057 +//line sql.y:5065 { yyLOCAL = Columns{yyDollar[1].identifierCI} } yyVAL.union = yyLOCAL - case 958: + case 959: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Columns -//line sql.y:5061 +//line sql.y:5069 { yyLOCAL = Columns{NewIdentifierCI(string(yyDollar[1].str))} } yyVAL.union = yyLOCAL - case 959: + case 960: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:5065 +//line sql.y:5073 { yySLICE := (*Columns)(yyIaddr(yyVAL.union)) *yySLICE = append(*yySLICE, yyDollar[3].identifierCI) } - case 960: + case 961: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:5069 +//line sql.y:5077 { yySLICE := (*Columns)(yyIaddr(yyVAL.union)) *yySLICE = append(*yySLICE, NewIdentifierCI(string(yyDollar[3].str))) } - case 961: + case 962: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Partitions -//line sql.y:5075 +//line sql.y:5083 { yyLOCAL = Partitions{yyDollar[1].identifierCI} } yyVAL.union = yyLOCAL - case 962: + case 963: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:5079 +//line sql.y:5087 { yySLICE := (*Partitions)(yyIaddr(yyVAL.union)) *yySLICE = append(*yySLICE, yyDollar[3].identifierCI) } - case 963: + case 964: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL TableExpr -//line sql.y:5092 +//line sql.y:5100 { yyLOCAL = &JoinTableExpr{LeftExpr: yyDollar[1].tableExprUnion(), Join: yyDollar[2].joinTypeUnion(), RightExpr: yyDollar[3].tableExprUnion(), Condition: yyDollar[4].joinCondition} } yyVAL.union = yyLOCAL - case 964: + case 965: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL TableExpr -//line sql.y:5096 +//line sql.y:5104 { yyLOCAL = &JoinTableExpr{LeftExpr: yyDollar[1].tableExprUnion(), Join: yyDollar[2].joinTypeUnion(), RightExpr: yyDollar[3].tableExprUnion(), Condition: yyDollar[4].joinCondition} } yyVAL.union = yyLOCAL - case 965: + case 966: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL TableExpr -//line sql.y:5100 +//line sql.y:5108 { yyLOCAL = &JoinTableExpr{LeftExpr: yyDollar[1].tableExprUnion(), Join: yyDollar[2].joinTypeUnion(), RightExpr: yyDollar[3].tableExprUnion(), Condition: yyDollar[4].joinCondition} } yyVAL.union = yyLOCAL - case 966: + case 967: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL TableExpr -//line sql.y:5104 +//line sql.y:5112 { yyLOCAL = &JoinTableExpr{LeftExpr: yyDollar[1].tableExprUnion(), Join: yyDollar[2].joinTypeUnion(), RightExpr: yyDollar[3].tableExprUnion()} } yyVAL.union = yyLOCAL - case 967: + case 968: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:5110 +//line sql.y:5118 { yyVAL.joinCondition = &JoinCondition{On: yyDollar[2].exprUnion()} } - case 968: + case 969: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:5112 +//line sql.y:5120 { yyVAL.joinCondition = &JoinCondition{Using: yyDollar[3].columnsUnion()} } - case 969: + case 970: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:5116 +//line sql.y:5124 { yyVAL.joinCondition = &JoinCondition{} } - case 970: + case 971: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:5118 +//line sql.y:5126 { yyVAL.joinCondition = yyDollar[1].joinCondition } - case 971: + case 972: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:5122 +//line sql.y:5130 { yyVAL.joinCondition = &JoinCondition{} } - case 972: + case 973: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:5124 +//line sql.y:5132 { yyVAL.joinCondition = &JoinCondition{On: yyDollar[2].exprUnion()} } - case 973: + case 974: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:5127 +//line sql.y:5135 { yyVAL.empty = struct{}{} } - case 974: + case 975: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:5129 +//line sql.y:5137 { yyVAL.empty = struct{}{} } - case 975: + case 976: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:5132 +//line sql.y:5140 { yyVAL.identifierCS = NewIdentifierCS("") } - case 976: + case 977: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:5136 +//line sql.y:5144 { yyVAL.identifierCS = yyDollar[1].identifierCS } - case 977: + case 978: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:5140 +//line sql.y:5148 { yyVAL.identifierCS = yyDollar[2].identifierCS } - case 979: + case 980: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:5147 +//line sql.y:5155 { yyVAL.identifierCS = NewIdentifierCS(string(yyDollar[1].str)) } - case 980: + case 981: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL JoinType -//line sql.y:5153 +//line sql.y:5161 { yyLOCAL = NormalJoinType } yyVAL.union = yyLOCAL - case 981: + case 982: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL JoinType -//line sql.y:5157 +//line sql.y:5165 { yyLOCAL = NormalJoinType } yyVAL.union = yyLOCAL - case 982: + case 983: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL JoinType -//line sql.y:5161 +//line sql.y:5169 { yyLOCAL = NormalJoinType } yyVAL.union = yyLOCAL - case 983: + case 984: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL JoinType -//line sql.y:5167 +//line sql.y:5175 { yyLOCAL = StraightJoinType } yyVAL.union = yyLOCAL - case 984: + case 985: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL JoinType -//line sql.y:5173 +//line sql.y:5181 { yyLOCAL = LeftJoinType } yyVAL.union = yyLOCAL - case 985: + case 986: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL JoinType -//line sql.y:5177 +//line sql.y:5185 { yyLOCAL = LeftJoinType } yyVAL.union = yyLOCAL - case 986: + case 987: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL JoinType -//line sql.y:5181 +//line sql.y:5189 { yyLOCAL = RightJoinType } yyVAL.union = yyLOCAL - case 987: + case 988: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL JoinType -//line sql.y:5185 +//line sql.y:5193 { yyLOCAL = RightJoinType } yyVAL.union = yyLOCAL - case 988: + case 989: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL JoinType -//line sql.y:5191 +//line sql.y:5199 { yyLOCAL = NaturalJoinType } yyVAL.union = yyLOCAL - case 989: + case 990: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL JoinType -//line sql.y:5195 +//line sql.y:5203 { if yyDollar[2].joinTypeUnion() == LeftJoinType { yyLOCAL = NaturalLeftJoinType @@ -17528,667 +17582,667 @@ yydefault: } } yyVAL.union = yyLOCAL - case 990: + case 991: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:5205 +//line sql.y:5213 { yyVAL.tableName = yyDollar[2].tableName } - case 991: + case 992: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:5209 +//line sql.y:5217 { yyVAL.tableName = yyDollar[1].tableName } - case 992: + case 993: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:5215 +//line sql.y:5223 { yyVAL.tableName = TableName{Name: yyDollar[1].identifierCS} } - case 993: + case 994: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:5219 +//line sql.y:5227 { yyVAL.tableName = TableName{Qualifier: yyDollar[1].identifierCS, Name: yyDollar[3].identifierCS} } - case 994: + case 995: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:5225 +//line sql.y:5233 { yyVAL.tableName = TableName{Name: yyDollar[1].identifierCS} } - case 995: + case 996: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL IndexHints -//line sql.y:5230 +//line sql.y:5238 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 996: + case 997: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL IndexHints -//line sql.y:5234 +//line sql.y:5242 { yyLOCAL = yyDollar[1].indexHintsUnion() } yyVAL.union = yyLOCAL - case 997: + case 998: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL IndexHints -//line sql.y:5240 +//line sql.y:5248 { yyLOCAL = IndexHints{yyDollar[1].indexHintUnion()} } yyVAL.union = yyLOCAL - case 998: + case 999: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:5244 +//line sql.y:5252 { yySLICE := (*IndexHints)(yyIaddr(yyVAL.union)) *yySLICE = append(*yySLICE, yyDollar[2].indexHintUnion()) } - case 999: + case 1000: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *IndexHint -//line sql.y:5250 +//line sql.y:5258 { yyLOCAL = &IndexHint{Type: UseOp, ForType: yyDollar[3].indexHintForTypeUnion(), Indexes: yyDollar[5].columnsUnion()} } yyVAL.union = yyLOCAL - case 1000: + case 1001: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *IndexHint -//line sql.y:5254 +//line sql.y:5262 { yyLOCAL = &IndexHint{Type: UseOp, ForType: yyDollar[3].indexHintForTypeUnion()} } yyVAL.union = yyLOCAL - case 1001: + case 1002: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *IndexHint -//line sql.y:5258 +//line sql.y:5266 { yyLOCAL = &IndexHint{Type: IgnoreOp, ForType: yyDollar[3].indexHintForTypeUnion(), Indexes: yyDollar[5].columnsUnion()} } yyVAL.union = yyLOCAL - case 1002: + case 1003: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *IndexHint -//line sql.y:5262 +//line sql.y:5270 { yyLOCAL = &IndexHint{Type: ForceOp, ForType: yyDollar[3].indexHintForTypeUnion(), Indexes: yyDollar[5].columnsUnion()} } yyVAL.union = yyLOCAL - case 1003: + case 1004: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *IndexHint -//line sql.y:5266 +//line sql.y:5274 { yyLOCAL = &IndexHint{Type: UseVindexOp, Indexes: yyDollar[4].columnsUnion()} } yyVAL.union = yyLOCAL - case 1004: + case 1005: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *IndexHint -//line sql.y:5270 +//line sql.y:5278 { yyLOCAL = &IndexHint{Type: IgnoreVindexOp, Indexes: yyDollar[4].columnsUnion()} } yyVAL.union = yyLOCAL - case 1005: + case 1006: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL IndexHintForType -//line sql.y:5275 +//line sql.y:5283 { yyLOCAL = NoForType } yyVAL.union = yyLOCAL - case 1006: + case 1007: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL IndexHintForType -//line sql.y:5279 +//line sql.y:5287 { yyLOCAL = JoinForType } yyVAL.union = yyLOCAL - case 1007: + case 1008: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL IndexHintForType -//line sql.y:5283 +//line sql.y:5291 { yyLOCAL = OrderByForType } yyVAL.union = yyLOCAL - case 1008: + case 1009: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL IndexHintForType -//line sql.y:5287 +//line sql.y:5295 { yyLOCAL = GroupByForType } yyVAL.union = yyLOCAL - case 1009: + case 1010: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL Expr -//line sql.y:5293 +//line sql.y:5301 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1010: + case 1011: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Expr -//line sql.y:5297 +//line sql.y:5305 { yyLOCAL = yyDollar[2].exprUnion() } yyVAL.union = yyLOCAL - case 1011: + case 1012: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Expr -//line sql.y:5304 +//line sql.y:5312 { yyLOCAL = &OrExpr{Left: yyDollar[1].exprUnion(), Right: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1012: + case 1013: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Expr -//line sql.y:5308 +//line sql.y:5316 { yyLOCAL = &XorExpr{Left: yyDollar[1].exprUnion(), Right: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1013: + case 1014: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Expr -//line sql.y:5312 +//line sql.y:5320 { yyLOCAL = &AndExpr{Left: yyDollar[1].exprUnion(), Right: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1014: + case 1015: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Expr -//line sql.y:5316 +//line sql.y:5324 { yyLOCAL = &NotExpr{Expr: yyDollar[2].exprUnion()} } yyVAL.union = yyLOCAL - case 1015: + case 1016: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Expr -//line sql.y:5320 +//line sql.y:5328 { yyLOCAL = &IsExpr{Left: yyDollar[1].exprUnion(), Right: yyDollar[3].isExprOperatorUnion()} } yyVAL.union = yyLOCAL - case 1016: + case 1017: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Expr -//line sql.y:5324 +//line sql.y:5332 { yyLOCAL = yyDollar[1].exprUnion() } yyVAL.union = yyLOCAL - case 1017: + case 1018: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Expr -//line sql.y:5328 +//line sql.y:5336 { yyLOCAL = &AssignmentExpr{Left: yyDollar[1].variableUnion(), Right: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1018: + case 1019: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:5332 +//line sql.y:5340 { yyLOCAL = &MemberOfExpr{Value: yyDollar[1].exprUnion(), JSONArr: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1019: + case 1020: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:5338 +//line sql.y:5346 { } - case 1020: + case 1021: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:5341 +//line sql.y:5349 { } - case 1021: + case 1022: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Expr -//line sql.y:5346 +//line sql.y:5354 { yyLOCAL = &IsExpr{Left: yyDollar[1].exprUnion(), Right: IsNullOp} } yyVAL.union = yyLOCAL - case 1022: + case 1023: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:5350 +//line sql.y:5358 { yyLOCAL = &IsExpr{Left: yyDollar[1].exprUnion(), Right: IsNotNullOp} } yyVAL.union = yyLOCAL - case 1023: + case 1024: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Expr -//line sql.y:5354 +//line sql.y:5362 { yyLOCAL = &ComparisonExpr{Left: yyDollar[1].exprUnion(), Operator: yyDollar[2].comparisonExprOperatorUnion(), Right: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1024: + case 1025: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:5358 +//line sql.y:5366 { yyLOCAL = &ComparisonExpr{Left: yyDollar[1].exprUnion(), Operator: yyDollar[2].comparisonExprOperatorUnion(), Modifier: Any, Right: yyDollar[4].subqueryUnion()} } yyVAL.union = yyLOCAL - case 1025: + case 1026: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:5362 +//line sql.y:5370 { yyLOCAL = &ComparisonExpr{Left: yyDollar[1].exprUnion(), Operator: yyDollar[2].comparisonExprOperatorUnion(), Modifier: Any, Right: yyDollar[4].subqueryUnion()} } yyVAL.union = yyLOCAL - case 1026: + case 1027: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:5366 +//line sql.y:5374 { yyLOCAL = &ComparisonExpr{Left: yyDollar[1].exprUnion(), Operator: yyDollar[2].comparisonExprOperatorUnion(), Modifier: All, Right: yyDollar[4].subqueryUnion()} } yyVAL.union = yyLOCAL - case 1027: + case 1028: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Expr -//line sql.y:5370 +//line sql.y:5378 { yyLOCAL = yyDollar[1].exprUnion() } yyVAL.union = yyLOCAL - case 1028: + case 1029: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Expr -//line sql.y:5376 +//line sql.y:5384 { yyLOCAL = &ComparisonExpr{Left: yyDollar[1].exprUnion(), Operator: InOp, Right: yyDollar[3].colTupleUnion()} } yyVAL.union = yyLOCAL - case 1029: + case 1030: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:5380 +//line sql.y:5388 { yyLOCAL = &ComparisonExpr{Left: yyDollar[1].exprUnion(), Operator: NotInOp, Right: yyDollar[4].colTupleUnion()} } yyVAL.union = yyLOCAL - case 1030: + case 1031: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL Expr -//line sql.y:5384 +//line sql.y:5392 { yyLOCAL = &BetweenExpr{Left: yyDollar[1].exprUnion(), IsBetween: true, From: yyDollar[3].exprUnion(), To: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1031: + case 1032: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:5388 +//line sql.y:5396 { yyLOCAL = &BetweenExpr{Left: yyDollar[1].exprUnion(), IsBetween: false, From: yyDollar[4].exprUnion(), To: yyDollar[6].exprUnion()} } yyVAL.union = yyLOCAL - case 1032: + case 1033: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Expr -//line sql.y:5392 +//line sql.y:5400 { yyLOCAL = &ComparisonExpr{Left: yyDollar[1].exprUnion(), Operator: LikeOp, Right: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1033: + case 1034: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:5396 +//line sql.y:5404 { yyLOCAL = &ComparisonExpr{Left: yyDollar[1].exprUnion(), Operator: NotLikeOp, Right: yyDollar[4].exprUnion()} } yyVAL.union = yyLOCAL - case 1034: + case 1035: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL Expr -//line sql.y:5400 +//line sql.y:5408 { yyLOCAL = &ComparisonExpr{Left: yyDollar[1].exprUnion(), Operator: LikeOp, Right: yyDollar[3].exprUnion(), Escape: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1035: + case 1036: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:5404 +//line sql.y:5412 { yyLOCAL = &ComparisonExpr{Left: yyDollar[1].exprUnion(), Operator: NotLikeOp, Right: yyDollar[4].exprUnion(), Escape: yyDollar[6].exprUnion()} } yyVAL.union = yyLOCAL - case 1036: + case 1037: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Expr -//line sql.y:5408 +//line sql.y:5416 { yyLOCAL = &ComparisonExpr{Left: yyDollar[1].exprUnion(), Operator: RegexpOp, Right: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1037: + case 1038: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:5412 +//line sql.y:5420 { yyLOCAL = &ComparisonExpr{Left: yyDollar[1].exprUnion(), Operator: NotRegexpOp, Right: yyDollar[4].exprUnion()} } yyVAL.union = yyLOCAL - case 1038: + case 1039: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Expr -//line sql.y:5416 +//line sql.y:5424 { yyLOCAL = yyDollar[1].exprUnion() } yyVAL.union = yyLOCAL - case 1039: + case 1040: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:5422 +//line sql.y:5430 { } - case 1040: + case 1041: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:5425 +//line sql.y:5433 { } - case 1041: + case 1042: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Expr -//line sql.y:5431 +//line sql.y:5439 { yyLOCAL = &BinaryExpr{Left: yyDollar[1].exprUnion(), Operator: BitOrOp, Right: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1042: + case 1043: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Expr -//line sql.y:5435 +//line sql.y:5443 { yyLOCAL = &BinaryExpr{Left: yyDollar[1].exprUnion(), Operator: BitAndOp, Right: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1043: + case 1044: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Expr -//line sql.y:5439 +//line sql.y:5447 { yyLOCAL = &BinaryExpr{Left: yyDollar[1].exprUnion(), Operator: ShiftLeftOp, Right: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1044: + case 1045: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Expr -//line sql.y:5443 +//line sql.y:5451 { yyLOCAL = &BinaryExpr{Left: yyDollar[1].exprUnion(), Operator: ShiftRightOp, Right: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1045: + case 1046: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Expr -//line sql.y:5447 +//line sql.y:5455 { yyLOCAL = &BinaryExpr{Left: yyDollar[1].exprUnion(), Operator: PlusOp, Right: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1046: + case 1047: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Expr -//line sql.y:5451 +//line sql.y:5459 { yyLOCAL = &BinaryExpr{Left: yyDollar[1].exprUnion(), Operator: MinusOp, Right: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1047: + case 1048: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL Expr -//line sql.y:5455 +//line sql.y:5463 { yyLOCAL = &IntervalDateExpr{Syntax: IntervalDateExprBinaryAdd, Date: yyDollar[1].exprUnion(), Unit: yyDollar[5].intervalTypeUnion(), Interval: yyDollar[4].exprUnion()} } yyVAL.union = yyLOCAL - case 1048: + case 1049: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL Expr -//line sql.y:5459 +//line sql.y:5467 { yyLOCAL = &IntervalDateExpr{Syntax: IntervalDateExprBinarySub, Date: yyDollar[1].exprUnion(), Unit: yyDollar[5].intervalTypeUnion(), Interval: yyDollar[4].exprUnion()} } yyVAL.union = yyLOCAL - case 1049: + case 1050: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Expr -//line sql.y:5463 +//line sql.y:5471 { yyLOCAL = &BinaryExpr{Left: yyDollar[1].exprUnion(), Operator: MultOp, Right: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1050: + case 1051: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Expr -//line sql.y:5467 +//line sql.y:5475 { yyLOCAL = &BinaryExpr{Left: yyDollar[1].exprUnion(), Operator: DivOp, Right: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1051: + case 1052: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Expr -//line sql.y:5471 +//line sql.y:5479 { yyLOCAL = &BinaryExpr{Left: yyDollar[1].exprUnion(), Operator: ModOp, Right: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1052: + case 1053: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Expr -//line sql.y:5475 +//line sql.y:5483 { yyLOCAL = &BinaryExpr{Left: yyDollar[1].exprUnion(), Operator: IntDivOp, Right: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1053: + case 1054: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Expr -//line sql.y:5479 +//line sql.y:5487 { yyLOCAL = &BinaryExpr{Left: yyDollar[1].exprUnion(), Operator: ModOp, Right: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1054: + case 1055: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Expr -//line sql.y:5483 +//line sql.y:5491 { yyLOCAL = &BinaryExpr{Left: yyDollar[1].exprUnion(), Operator: BitXorOp, Right: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1055: + case 1056: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Expr -//line sql.y:5487 +//line sql.y:5495 { yyLOCAL = yyDollar[1].exprUnion() } yyVAL.union = yyLOCAL - case 1056: + case 1057: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Expr -//line sql.y:5493 +//line sql.y:5501 { yyLOCAL = yyDollar[1].exprUnion() } yyVAL.union = yyLOCAL - case 1057: + case 1058: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Expr -//line sql.y:5497 +//line sql.y:5505 { yyLOCAL = yyDollar[1].exprUnion() } yyVAL.union = yyLOCAL - case 1058: + case 1059: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Expr -//line sql.y:5501 +//line sql.y:5509 { yyLOCAL = yyDollar[1].exprUnion() } yyVAL.union = yyLOCAL - case 1059: + case 1060: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Expr -//line sql.y:5505 +//line sql.y:5513 { yyLOCAL = yyDollar[1].exprUnion() } yyVAL.union = yyLOCAL - case 1060: + case 1061: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Expr -//line sql.y:5509 +//line sql.y:5517 { yyLOCAL = &CollateExpr{Expr: yyDollar[1].exprUnion(), Collation: yyDollar[3].str} } yyVAL.union = yyLOCAL - case 1061: + case 1062: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Expr -//line sql.y:5513 +//line sql.y:5521 { yyLOCAL = yyDollar[1].exprUnion() } yyVAL.union = yyLOCAL - case 1062: + case 1063: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Expr -//line sql.y:5517 +//line sql.y:5525 { yyLOCAL = yyDollar[1].exprUnion() } yyVAL.union = yyLOCAL - case 1063: + case 1064: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Expr -//line sql.y:5521 +//line sql.y:5529 { yyLOCAL = yyDollar[1].variableUnion() } yyVAL.union = yyLOCAL - case 1064: + case 1065: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Expr -//line sql.y:5525 +//line sql.y:5533 { yyLOCAL = yyDollar[2].exprUnion() // TODO: do we really want to ignore unary '+' before any kind of literals? } yyVAL.union = yyLOCAL - case 1065: + case 1066: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Expr -//line sql.y:5529 +//line sql.y:5537 { yyLOCAL = &UnaryExpr{Operator: UMinusOp, Expr: yyDollar[2].exprUnion()} } yyVAL.union = yyLOCAL - case 1066: + case 1067: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Expr -//line sql.y:5533 +//line sql.y:5541 { yyLOCAL = &UnaryExpr{Operator: TildaOp, Expr: yyDollar[2].exprUnion()} } yyVAL.union = yyLOCAL - case 1067: + case 1068: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Expr -//line sql.y:5537 +//line sql.y:5545 { yyLOCAL = &UnaryExpr{Operator: BangOp, Expr: yyDollar[2].exprUnion()} } yyVAL.union = yyLOCAL - case 1068: + case 1069: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Expr -//line sql.y:5541 +//line sql.y:5549 { yyLOCAL = yyDollar[1].subqueryUnion() } yyVAL.union = yyLOCAL - case 1069: + case 1070: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Expr -//line sql.y:5545 +//line sql.y:5553 { yyLOCAL = yyDollar[1].exprUnion() } yyVAL.union = yyLOCAL - case 1070: + case 1071: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Expr -//line sql.y:5549 +//line sql.y:5557 { yyLOCAL = &ExistsExpr{Subquery: yyDollar[2].subqueryUnion()} } yyVAL.union = yyLOCAL - case 1071: + case 1072: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL Expr -//line sql.y:5553 +//line sql.y:5561 { yyLOCAL = &MatchExpr{Columns: yyDollar[2].colNamesUnion(), Expr: yyDollar[5].exprUnion(), Option: yyDollar[6].matchExprOptionUnion()} } yyVAL.union = yyLOCAL - case 1072: + case 1073: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL Expr -//line sql.y:5557 +//line sql.y:5565 { yyLOCAL = &CastExpr{Expr: yyDollar[3].exprUnion(), Type: yyDollar[5].convertTypeUnion(), Array: yyDollar[6].booleanUnion()} } yyVAL.union = yyLOCAL - case 1073: + case 1074: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:5561 +//line sql.y:5569 { yyLOCAL = &ConvertExpr{Expr: yyDollar[3].exprUnion(), Type: yyDollar[5].convertTypeUnion()} } yyVAL.union = yyLOCAL - case 1074: + case 1075: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:5565 +//line sql.y:5573 { yyLOCAL = &ConvertUsingExpr{Expr: yyDollar[3].exprUnion(), Type: yyDollar[5].str} } yyVAL.union = yyLOCAL - case 1075: + case 1076: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Expr -//line sql.y:5569 +//line sql.y:5577 { // 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: @@ -18197,3218 +18251,3218 @@ yydefault: yyLOCAL = &ConvertExpr{Expr: yyDollar[2].exprUnion(), Type: &ConvertType{Type: yyDollar[1].str}} } yyVAL.union = yyLOCAL - case 1076: + case 1077: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Expr -//line sql.y:5577 +//line sql.y:5585 { yyLOCAL = &Default{ColName: yyDollar[2].str} } yyVAL.union = yyLOCAL - case 1077: + case 1078: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL Expr -//line sql.y:5581 +//line sql.y:5589 { yyLOCAL = &IntervalDateExpr{Syntax: IntervalDateExprBinaryAddLeft, Date: yyDollar[5].exprUnion(), Unit: yyDollar[3].intervalTypeUnion(), Interval: yyDollar[2].exprUnion()} } yyVAL.union = yyLOCAL - case 1078: + case 1079: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:5585 +//line sql.y:5593 { yyLOCAL = &IntervalFuncExpr{Expr: yyDollar[3].exprUnion(), Exprs: yyDollar[5].exprsUnion()} } yyVAL.union = yyLOCAL - case 1079: + case 1080: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Expr -//line sql.y:5589 +//line sql.y:5597 { yyLOCAL = &JSONExtractExpr{JSONDoc: yyDollar[1].exprUnion(), PathList: []Expr{yyDollar[3].exprUnion()}} } yyVAL.union = yyLOCAL - case 1080: + case 1081: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Expr -//line sql.y:5593 +//line sql.y:5601 { yyLOCAL = &JSONUnquoteExpr{JSONValue: &JSONExtractExpr{JSONDoc: yyDollar[1].exprUnion(), PathList: []Expr{yyDollar[3].exprUnion()}}} } yyVAL.union = yyLOCAL - case 1081: + case 1082: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []*ColName -//line sql.y:5599 +//line sql.y:5607 { yyLOCAL = yyDollar[1].colNamesUnion() } yyVAL.union = yyLOCAL - case 1082: + case 1083: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []*ColName -//line sql.y:5603 +//line sql.y:5611 { yyLOCAL = yyDollar[2].colNamesUnion() } yyVAL.union = yyLOCAL - case 1083: + case 1084: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []*ColName -//line sql.y:5609 +//line sql.y:5617 { yyLOCAL = []*ColName{yyDollar[1].colNameUnion()} } yyVAL.union = yyLOCAL - case 1084: + case 1085: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:5613 +//line sql.y:5621 { yySLICE := (*[]*ColName)(yyIaddr(yyVAL.union)) *yySLICE = append(*yySLICE, yyDollar[3].colNameUnion()) } - case 1085: + case 1086: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL TrimType -//line sql.y:5619 +//line sql.y:5627 { yyLOCAL = BothTrimType } yyVAL.union = yyLOCAL - case 1086: + case 1087: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL TrimType -//line sql.y:5623 +//line sql.y:5631 { yyLOCAL = LeadingTrimType } yyVAL.union = yyLOCAL - case 1087: + case 1088: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL TrimType -//line sql.y:5627 +//line sql.y:5635 { yyLOCAL = TrailingTrimType } yyVAL.union = yyLOCAL - case 1088: + case 1089: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL FrameUnitType -//line sql.y:5633 +//line sql.y:5641 { yyLOCAL = FrameRowsType } yyVAL.union = yyLOCAL - case 1089: + case 1090: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL FrameUnitType -//line sql.y:5637 +//line sql.y:5645 { yyLOCAL = FrameRangeType } yyVAL.union = yyLOCAL - case 1090: + case 1091: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL ArgumentLessWindowExprType -//line sql.y:5644 +//line sql.y:5652 { yyLOCAL = CumeDistExprType } yyVAL.union = yyLOCAL - case 1091: + case 1092: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL ArgumentLessWindowExprType -//line sql.y:5648 +//line sql.y:5656 { yyLOCAL = DenseRankExprType } yyVAL.union = yyLOCAL - case 1092: + case 1093: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL ArgumentLessWindowExprType -//line sql.y:5652 +//line sql.y:5660 { yyLOCAL = PercentRankExprType } yyVAL.union = yyLOCAL - case 1093: + case 1094: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL ArgumentLessWindowExprType -//line sql.y:5656 +//line sql.y:5664 { yyLOCAL = RankExprType } yyVAL.union = yyLOCAL - case 1094: + case 1095: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL ArgumentLessWindowExprType -//line sql.y:5660 +//line sql.y:5668 { yyLOCAL = RowNumberExprType } yyVAL.union = yyLOCAL - case 1095: + case 1096: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *FramePoint -//line sql.y:5666 +//line sql.y:5674 { yyLOCAL = &FramePoint{Type: CurrentRowType} } yyVAL.union = yyLOCAL - case 1096: + case 1097: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *FramePoint -//line sql.y:5670 +//line sql.y:5678 { yyLOCAL = &FramePoint{Type: UnboundedPrecedingType} } yyVAL.union = yyLOCAL - case 1097: + case 1098: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *FramePoint -//line sql.y:5674 +//line sql.y:5682 { yyLOCAL = &FramePoint{Type: UnboundedFollowingType} } yyVAL.union = yyLOCAL - case 1098: + case 1099: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *FramePoint -//line sql.y:5678 +//line sql.y:5686 { yyLOCAL = &FramePoint{Type: ExprPrecedingType, Expr: yyDollar[1].exprUnion()} } yyVAL.union = yyLOCAL - case 1099: + case 1100: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *FramePoint -//line sql.y:5682 +//line sql.y:5690 { yyLOCAL = &FramePoint{Type: ExprPrecedingType, Expr: yyDollar[2].exprUnion(), Unit: yyDollar[3].intervalTypeUnion()} } yyVAL.union = yyLOCAL - case 1100: + case 1101: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *FramePoint -//line sql.y:5686 +//line sql.y:5694 { yyLOCAL = &FramePoint{Type: ExprFollowingType, Expr: yyDollar[1].exprUnion()} } yyVAL.union = yyLOCAL - case 1101: + case 1102: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *FramePoint -//line sql.y:5690 +//line sql.y:5698 { yyLOCAL = &FramePoint{Type: ExprFollowingType, Expr: yyDollar[2].exprUnion(), Unit: yyDollar[3].intervalTypeUnion()} } yyVAL.union = yyLOCAL - case 1102: + case 1103: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *FrameClause -//line sql.y:5695 +//line sql.y:5703 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1103: + case 1104: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *FrameClause -//line sql.y:5699 +//line sql.y:5707 { yyLOCAL = yyDollar[1].frameClauseUnion() } yyVAL.union = yyLOCAL - case 1104: + case 1105: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *FrameClause -//line sql.y:5705 +//line sql.y:5713 { yyLOCAL = &FrameClause{Unit: yyDollar[1].frameUnitTypeUnion(), Start: yyDollar[2].framePointUnion()} } yyVAL.union = yyLOCAL - case 1105: + case 1106: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *FrameClause -//line sql.y:5709 +//line sql.y:5717 { yyLOCAL = &FrameClause{Unit: yyDollar[1].frameUnitTypeUnion(), Start: yyDollar[3].framePointUnion(), End: yyDollar[5].framePointUnion()} } yyVAL.union = yyLOCAL - case 1106: + case 1107: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL Exprs -//line sql.y:5714 +//line sql.y:5722 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1107: + case 1108: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Exprs -//line sql.y:5718 +//line sql.y:5726 { yyLOCAL = yyDollar[3].exprsUnion() } yyVAL.union = yyLOCAL - case 1108: + case 1109: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:5723 +//line sql.y:5731 { yyVAL.identifierCI = IdentifierCI{} } - case 1109: + case 1110: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:5727 +//line sql.y:5735 { yyVAL.identifierCI = yyDollar[1].identifierCI } - case 1110: + case 1111: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *WindowSpecification -//line sql.y:5733 +//line sql.y:5741 { yyLOCAL = &WindowSpecification{Name: yyDollar[1].identifierCI, PartitionClause: yyDollar[2].exprsUnion(), OrderClause: yyDollar[3].orderByUnion(), FrameClause: yyDollar[4].frameClauseUnion()} } yyVAL.union = yyLOCAL - case 1111: + case 1112: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *OverClause -//line sql.y:5739 +//line sql.y:5747 { yyLOCAL = &OverClause{WindowSpec: yyDollar[3].windowSpecificationUnion()} } yyVAL.union = yyLOCAL - case 1112: + case 1113: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *OverClause -//line sql.y:5743 +//line sql.y:5751 { yyLOCAL = &OverClause{WindowName: yyDollar[2].identifierCI} } yyVAL.union = yyLOCAL - case 1113: + case 1114: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *OverClause -//line sql.y:5749 +//line sql.y:5757 { yyLOCAL = yyDollar[1].overClauseUnion() } yyVAL.union = yyLOCAL - case 1114: + case 1115: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *OverClause -//line sql.y:5753 +//line sql.y:5761 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1115: + case 1116: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *NullTreatmentClause -//line sql.y:5758 +//line sql.y:5766 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1117: + case 1118: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *NullTreatmentClause -//line sql.y:5765 +//line sql.y:5773 { yyLOCAL = &NullTreatmentClause{yyDollar[1].nullTreatmentTypeUnion()} } yyVAL.union = yyLOCAL - case 1118: + case 1119: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL NullTreatmentType -//line sql.y:5771 +//line sql.y:5779 { yyLOCAL = RespectNullsType } yyVAL.union = yyLOCAL - case 1119: + case 1120: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL NullTreatmentType -//line sql.y:5775 +//line sql.y:5783 { yyLOCAL = IgnoreNullsType } yyVAL.union = yyLOCAL - case 1120: + case 1121: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL FirstOrLastValueExprType -//line sql.y:5781 +//line sql.y:5789 { yyLOCAL = FirstValueExprType } yyVAL.union = yyLOCAL - case 1121: + case 1122: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL FirstOrLastValueExprType -//line sql.y:5785 +//line sql.y:5793 { yyLOCAL = LastValueExprType } yyVAL.union = yyLOCAL - case 1122: + case 1123: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL FromFirstLastType -//line sql.y:5791 +//line sql.y:5799 { yyLOCAL = FromFirstType } yyVAL.union = yyLOCAL - case 1123: + case 1124: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL FromFirstLastType -//line sql.y:5795 +//line sql.y:5803 { yyLOCAL = FromLastType } yyVAL.union = yyLOCAL - case 1124: + case 1125: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *FromFirstLastClause -//line sql.y:5800 +//line sql.y:5808 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1126: + case 1127: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *FromFirstLastClause -//line sql.y:5807 +//line sql.y:5815 { yyLOCAL = &FromFirstLastClause{yyDollar[1].fromFirstLastTypeUnion()} } yyVAL.union = yyLOCAL - case 1127: + case 1128: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL LagLeadExprType -//line sql.y:5813 +//line sql.y:5821 { yyLOCAL = LagExprType } yyVAL.union = yyLOCAL - case 1128: + case 1129: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL LagLeadExprType -//line sql.y:5817 +//line sql.y:5825 { yyLOCAL = LeadExprType } yyVAL.union = yyLOCAL - case 1129: + case 1130: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *WindowDefinition -//line sql.y:5823 +//line sql.y:5831 { yyLOCAL = &WindowDefinition{Name: yyDollar[1].identifierCI, WindowSpec: yyDollar[4].windowSpecificationUnion()} } yyVAL.union = yyLOCAL - case 1130: + case 1131: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL WindowDefinitions -//line sql.y:5829 +//line sql.y:5837 { yyLOCAL = WindowDefinitions{yyDollar[1].windowDefinitionUnion()} } yyVAL.union = yyLOCAL - case 1131: + case 1132: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:5833 +//line sql.y:5841 { yySLICE := (*WindowDefinitions)(yyIaddr(yyVAL.union)) *yySLICE = append(*yySLICE, yyDollar[3].windowDefinitionUnion()) } - case 1132: + case 1133: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:5839 +//line sql.y:5847 { yyVAL.str = "" } - case 1133: + case 1134: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:5843 +//line sql.y:5851 { yyVAL.str = string(yyDollar[2].identifierCI.String()) } - case 1134: + case 1135: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL BoolVal -//line sql.y:5849 +//line sql.y:5857 { yyLOCAL = BoolVal(true) } yyVAL.union = yyLOCAL - case 1135: + case 1136: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL BoolVal -//line sql.y:5853 +//line sql.y:5861 { yyLOCAL = BoolVal(false) } yyVAL.union = yyLOCAL - case 1136: + case 1137: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL IsExprOperator -//line sql.y:5860 +//line sql.y:5868 { yyLOCAL = IsTrueOp } yyVAL.union = yyLOCAL - case 1137: + case 1138: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL IsExprOperator -//line sql.y:5864 +//line sql.y:5872 { yyLOCAL = IsNotTrueOp } yyVAL.union = yyLOCAL - case 1138: + case 1139: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL IsExprOperator -//line sql.y:5868 +//line sql.y:5876 { yyLOCAL = IsFalseOp } yyVAL.union = yyLOCAL - case 1139: + case 1140: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL IsExprOperator -//line sql.y:5872 +//line sql.y:5880 { yyLOCAL = IsNotFalseOp } yyVAL.union = yyLOCAL - case 1140: + case 1141: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL ComparisonExprOperator -//line sql.y:5878 +//line sql.y:5886 { yyLOCAL = yyDollar[1].comparisonExprOperatorUnion() } yyVAL.union = yyLOCAL - case 1141: + case 1142: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL ComparisonExprOperator -//line sql.y:5882 +//line sql.y:5890 { yyLOCAL = NullSafeEqualOp } yyVAL.union = yyLOCAL - case 1142: + case 1143: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL ComparisonExprOperator -//line sql.y:5888 +//line sql.y:5896 { yyLOCAL = EqualOp } yyVAL.union = yyLOCAL - case 1143: + case 1144: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL ComparisonExprOperator -//line sql.y:5892 +//line sql.y:5900 { yyLOCAL = LessThanOp } yyVAL.union = yyLOCAL - case 1144: + case 1145: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL ComparisonExprOperator -//line sql.y:5896 +//line sql.y:5904 { yyLOCAL = GreaterThanOp } yyVAL.union = yyLOCAL - case 1145: + case 1146: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL ComparisonExprOperator -//line sql.y:5900 +//line sql.y:5908 { yyLOCAL = LessEqualOp } yyVAL.union = yyLOCAL - case 1146: + case 1147: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL ComparisonExprOperator -//line sql.y:5904 +//line sql.y:5912 { yyLOCAL = GreaterEqualOp } yyVAL.union = yyLOCAL - case 1147: + case 1148: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL ComparisonExprOperator -//line sql.y:5908 +//line sql.y:5916 { yyLOCAL = NotEqualOp } yyVAL.union = yyLOCAL - case 1148: + case 1149: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL ColTuple -//line sql.y:5914 +//line sql.y:5922 { yyLOCAL = yyDollar[1].valTupleUnion() } yyVAL.union = yyLOCAL - case 1149: + case 1150: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL ColTuple -//line sql.y:5918 +//line sql.y:5926 { yyLOCAL = yyDollar[1].subqueryUnion() } yyVAL.union = yyLOCAL - case 1150: + case 1151: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL ColTuple -//line sql.y:5922 +//line sql.y:5930 { yyLOCAL = ListArg(yyDollar[1].str[2:]) markBindVariable(yylex, yyDollar[1].str[2:]) } yyVAL.union = yyLOCAL - case 1151: + case 1152: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *Subquery -//line sql.y:5929 +//line sql.y:5937 { yyLOCAL = &Subquery{yyDollar[1].selStmtUnion()} } yyVAL.union = yyLOCAL - case 1152: + case 1153: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Exprs -//line sql.y:5935 +//line sql.y:5943 { yyLOCAL = Exprs{yyDollar[1].exprUnion()} } yyVAL.union = yyLOCAL - case 1153: + case 1154: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:5939 +//line sql.y:5947 { yySLICE := (*Exprs)(yyIaddr(yyVAL.union)) *yySLICE = append(*yySLICE, yyDollar[3].exprUnion()) } - case 1154: + case 1155: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:5949 +//line sql.y:5957 { yyLOCAL = &FuncExpr{Name: yyDollar[1].identifierCI, Exprs: yyDollar[3].exprsUnion()} } yyVAL.union = yyLOCAL - case 1155: + case 1156: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:5953 +//line sql.y:5961 { yyLOCAL = &FuncExpr{Qualifier: yyDollar[1].identifierCS, Name: yyDollar[3].identifierCI, Exprs: yyDollar[5].exprsUnion()} } yyVAL.union = yyLOCAL - case 1156: + case 1157: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:5963 +//line sql.y:5971 { yyLOCAL = &FuncExpr{Name: NewIdentifierCI("left"), Exprs: yyDollar[3].exprsUnion()} } yyVAL.union = yyLOCAL - case 1157: + case 1158: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:5967 +//line sql.y:5975 { yyLOCAL = &FuncExpr{Name: NewIdentifierCI("right"), Exprs: yyDollar[3].exprsUnion()} } yyVAL.union = yyLOCAL - case 1158: + case 1159: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL Expr -//line sql.y:5971 +//line sql.y:5979 { yyLOCAL = &SubstrExpr{Name: yyDollar[3].exprUnion(), From: yyDollar[5].exprUnion(), To: yyDollar[7].exprUnion()} } yyVAL.union = yyLOCAL - case 1159: + case 1160: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL Expr -//line sql.y:5975 +//line sql.y:5983 { yyLOCAL = &SubstrExpr{Name: yyDollar[3].exprUnion(), From: yyDollar[5].exprUnion(), To: yyDollar[7].exprUnion()} } yyVAL.union = yyLOCAL - case 1160: + case 1161: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:5979 +//line sql.y:5987 { yyLOCAL = &SubstrExpr{Name: yyDollar[3].exprUnion(), From: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1161: + case 1162: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL Expr -//line sql.y:5983 +//line sql.y:5991 { yyLOCAL = &SubstrExpr{Name: yyDollar[3].exprUnion(), From: yyDollar[5].exprUnion(), To: yyDollar[7].exprUnion()} } yyVAL.union = yyLOCAL - case 1162: + case 1163: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:5987 +//line sql.y:5995 { yyLOCAL = &SubstrExpr{Name: yyDollar[3].exprUnion(), From: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1163: + case 1164: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL Expr -//line sql.y:5991 +//line sql.y:5999 { yyLOCAL = &CaseExpr{Expr: yyDollar[2].exprUnion(), Whens: yyDollar[3].whensUnion(), Else: yyDollar[4].exprUnion()} } yyVAL.union = yyLOCAL - case 1164: + case 1165: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:5995 +//line sql.y:6003 { yyLOCAL = &ValuesFuncExpr{Name: yyDollar[3].colNameUnion()} } yyVAL.union = yyLOCAL - case 1165: + case 1166: yyDollar = yyS[yypt-10 : yypt+1] var yyLOCAL Expr -//line sql.y:5999 +//line sql.y:6007 { yyLOCAL = &InsertExpr{Str: yyDollar[3].exprUnion(), Pos: yyDollar[5].exprUnion(), Len: yyDollar[7].exprUnion(), NewStr: yyDollar[9].exprUnion()} } yyVAL.union = yyLOCAL - case 1166: + case 1167: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Expr -//line sql.y:6003 +//line sql.y:6011 { yyLOCAL = &FuncExpr{Name: NewIdentifierCI(yyDollar[1].str)} } yyVAL.union = yyLOCAL - case 1167: + case 1168: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Expr -//line sql.y:6014 +//line sql.y:6022 { yyLOCAL = &FuncExpr{Name: NewIdentifierCI("utc_date")} } yyVAL.union = yyLOCAL - case 1168: + case 1169: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Expr -//line sql.y:6018 +//line sql.y:6026 { yyLOCAL = yyDollar[1].exprUnion() } yyVAL.union = yyLOCAL - case 1169: + case 1170: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Expr -//line sql.y:6024 +//line sql.y:6032 { yyLOCAL = &FuncExpr{Name: NewIdentifierCI("current_date")} } yyVAL.union = yyLOCAL - case 1170: + case 1171: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Expr -//line sql.y:6028 +//line sql.y:6036 { yyLOCAL = &FuncExpr{Name: NewIdentifierCI("curdate")} } yyVAL.union = yyLOCAL - case 1171: + case 1172: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Expr -//line sql.y:6032 +//line sql.y:6040 { yyLOCAL = &CurTimeFuncExpr{Name: NewIdentifierCI("utc_time"), Fsp: yyDollar[2].integerUnion()} } yyVAL.union = yyLOCAL - case 1172: + case 1173: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Expr -//line sql.y:6037 +//line sql.y:6045 { yyLOCAL = &CurTimeFuncExpr{Name: NewIdentifierCI("curtime"), Fsp: yyDollar[2].integerUnion()} } yyVAL.union = yyLOCAL - case 1173: + case 1174: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Expr -//line sql.y:6042 +//line sql.y:6050 { yyLOCAL = &CurTimeFuncExpr{Name: NewIdentifierCI("current_time"), Fsp: yyDollar[2].integerUnion()} } yyVAL.union = yyLOCAL - case 1174: + case 1175: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL Expr -//line sql.y:6046 +//line sql.y:6054 { yyLOCAL = &CountStar{OverClause: yyDollar[5].overClauseUnion()} } yyVAL.union = yyLOCAL - case 1175: + case 1176: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6050 +//line sql.y:6058 { yyLOCAL = &Count{Distinct: yyDollar[3].booleanUnion(), Args: yyDollar[4].exprsUnion(), OverClause: yyDollar[6].overClauseUnion()} } yyVAL.union = yyLOCAL - case 1176: + case 1177: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6054 +//line sql.y:6062 { yyLOCAL = &Max{Distinct: yyDollar[3].booleanUnion(), Arg: yyDollar[4].exprUnion(), OverClause: yyDollar[6].overClauseUnion()} } yyVAL.union = yyLOCAL - case 1177: + case 1178: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6058 +//line sql.y:6066 { yyLOCAL = &Min{Distinct: yyDollar[3].booleanUnion(), Arg: yyDollar[4].exprUnion(), OverClause: yyDollar[6].overClauseUnion()} } yyVAL.union = yyLOCAL - case 1178: + case 1179: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6062 +//line sql.y:6070 { yyLOCAL = &Sum{Distinct: yyDollar[3].booleanUnion(), Arg: yyDollar[4].exprUnion(), OverClause: yyDollar[6].overClauseUnion()} } yyVAL.union = yyLOCAL - case 1179: + case 1180: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6066 +//line sql.y:6074 { yyLOCAL = &Avg{Distinct: yyDollar[3].booleanUnion(), Arg: yyDollar[4].exprUnion(), OverClause: yyDollar[6].overClauseUnion()} } yyVAL.union = yyLOCAL - case 1180: + case 1181: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL Expr -//line sql.y:6070 +//line sql.y:6078 { yyLOCAL = &BitAnd{Arg: yyDollar[3].exprUnion(), OverClause: yyDollar[5].overClauseUnion()} } yyVAL.union = yyLOCAL - case 1181: + case 1182: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL Expr -//line sql.y:6074 +//line sql.y:6082 { yyLOCAL = &BitOr{Arg: yyDollar[3].exprUnion(), OverClause: yyDollar[5].overClauseUnion()} } yyVAL.union = yyLOCAL - case 1182: + case 1183: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL Expr -//line sql.y:6078 +//line sql.y:6086 { yyLOCAL = &BitXor{Arg: yyDollar[3].exprUnion(), OverClause: yyDollar[5].overClauseUnion()} } yyVAL.union = yyLOCAL - case 1183: + case 1184: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL Expr -//line sql.y:6082 +//line sql.y:6090 { yyLOCAL = &Std{Arg: yyDollar[3].exprUnion(), OverClause: yyDollar[5].overClauseUnion()} } yyVAL.union = yyLOCAL - case 1184: + case 1185: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL Expr -//line sql.y:6086 +//line sql.y:6094 { yyLOCAL = &StdDev{Arg: yyDollar[3].exprUnion(), OverClause: yyDollar[5].overClauseUnion()} } yyVAL.union = yyLOCAL - case 1185: + case 1186: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL Expr -//line sql.y:6090 +//line sql.y:6098 { yyLOCAL = &StdPop{Arg: yyDollar[3].exprUnion(), OverClause: yyDollar[5].overClauseUnion()} } yyVAL.union = yyLOCAL - case 1186: + case 1187: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL Expr -//line sql.y:6094 +//line sql.y:6102 { yyLOCAL = &StdSamp{Arg: yyDollar[3].exprUnion(), OverClause: yyDollar[5].overClauseUnion()} } yyVAL.union = yyLOCAL - case 1187: + case 1188: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL Expr -//line sql.y:6098 +//line sql.y:6106 { yyLOCAL = &VarPop{Arg: yyDollar[3].exprUnion(), OverClause: yyDollar[5].overClauseUnion()} } yyVAL.union = yyLOCAL - case 1188: + case 1189: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL Expr -//line sql.y:6102 +//line sql.y:6110 { yyLOCAL = &VarSamp{Arg: yyDollar[3].exprUnion(), OverClause: yyDollar[5].overClauseUnion()} } yyVAL.union = yyLOCAL - case 1189: + case 1190: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL Expr -//line sql.y:6106 +//line sql.y:6114 { yyLOCAL = &Variance{Arg: yyDollar[3].exprUnion(), OverClause: yyDollar[5].overClauseUnion()} } yyVAL.union = yyLOCAL - case 1190: + case 1191: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL Expr -//line sql.y:6110 +//line sql.y:6118 { 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 1191: + case 1192: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6114 +//line sql.y:6122 { yyLOCAL = &AnyValue{Arg: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1192: + case 1193: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL Expr -//line sql.y:6118 +//line sql.y:6126 { yyLOCAL = &IntervalDateExpr{Syntax: IntervalDateExprTimestampadd, Date: yyDollar[7].exprUnion(), Interval: yyDollar[5].exprUnion(), Unit: yyDollar[3].intervalTypeUnion()} } yyVAL.union = yyLOCAL - case 1193: + case 1194: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL Expr -//line sql.y:6122 +//line sql.y:6130 { yyLOCAL = &TimestampDiffExpr{Unit: yyDollar[3].intervalTypeUnion(), Expr1: yyDollar[5].exprUnion(), Expr2: yyDollar[7].exprUnion()} } yyVAL.union = yyLOCAL - case 1194: + case 1195: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6126 +//line sql.y:6134 { yyLOCAL = &ExtractFuncExpr{IntervalType: yyDollar[3].intervalTypeUnion(), Expr: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1195: + case 1196: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL Expr -//line sql.y:6130 +//line sql.y:6138 { yyLOCAL = &WeightStringFuncExpr{Expr: yyDollar[3].exprUnion(), As: yyDollar[4].convertTypeUnion()} } yyVAL.union = yyLOCAL - case 1196: + case 1197: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6134 +//line sql.y:6142 { yyLOCAL = &JSONPrettyExpr{JSONVal: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1197: + case 1198: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6138 +//line sql.y:6146 { yyLOCAL = &JSONStorageFreeExpr{JSONVal: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1198: + case 1199: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6142 +//line sql.y:6150 { yyLOCAL = &JSONStorageSizeExpr{JSONVal: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1199: + case 1200: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL Expr -//line sql.y:6146 +//line sql.y:6154 { yyLOCAL = &JSONArrayAgg{Expr: yyDollar[3].exprUnion(), OverClause: yyDollar[5].overClauseUnion()} } yyVAL.union = yyLOCAL - case 1200: + case 1201: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL Expr -//line sql.y:6150 +//line sql.y:6158 { yyLOCAL = &JSONObjectAgg{Key: yyDollar[3].exprUnion(), Value: yyDollar[5].exprUnion(), OverClause: yyDollar[7].overClauseUnion()} } yyVAL.union = yyLOCAL - case 1201: + case 1202: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6154 +//line sql.y:6162 { yyLOCAL = &TrimFuncExpr{TrimFuncType: LTrimType, Type: LeadingTrimType, StringArg: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1202: + case 1203: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6158 +//line sql.y:6166 { yyLOCAL = &TrimFuncExpr{TrimFuncType: RTrimType, Type: TrailingTrimType, StringArg: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1203: + case 1204: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL Expr -//line sql.y:6162 +//line sql.y:6170 { yyLOCAL = &TrimFuncExpr{Type: yyDollar[3].trimTypeUnion(), TrimArg: yyDollar[4].exprUnion(), StringArg: yyDollar[6].exprUnion()} } yyVAL.union = yyLOCAL - case 1204: + case 1205: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6166 +//line sql.y:6174 { yyLOCAL = &TrimFuncExpr{StringArg: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1205: + case 1206: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6170 +//line sql.y:6178 { yyLOCAL = &CharExpr{Exprs: yyDollar[3].exprsUnion()} } yyVAL.union = yyLOCAL - case 1206: + case 1207: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6174 +//line sql.y:6182 { yyLOCAL = &CharExpr{Exprs: yyDollar[3].exprsUnion(), Charset: yyDollar[5].str} } yyVAL.union = yyLOCAL - case 1207: + case 1208: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6178 +//line sql.y:6186 { yyLOCAL = &TrimFuncExpr{TrimArg: yyDollar[3].exprUnion(), StringArg: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1208: + case 1209: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6182 +//line sql.y:6190 { yyLOCAL = &LocateExpr{SubStr: yyDollar[3].exprUnion(), Str: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1209: + case 1210: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL Expr -//line sql.y:6186 +//line sql.y:6194 { yyLOCAL = &LocateExpr{SubStr: yyDollar[3].exprUnion(), Str: yyDollar[5].exprUnion(), Pos: yyDollar[7].exprUnion()} } yyVAL.union = yyLOCAL - case 1210: + case 1211: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6190 +//line sql.y:6198 { yyLOCAL = &LocateExpr{SubStr: yyDollar[3].exprUnion(), Str: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1211: + case 1212: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6194 +//line sql.y:6202 { yyLOCAL = &LockingFunc{Type: GetLock, Name: yyDollar[3].exprUnion(), Timeout: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1212: + case 1213: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6198 +//line sql.y:6206 { yyLOCAL = &LockingFunc{Type: IsFreeLock, Name: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1213: + case 1214: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6202 +//line sql.y:6210 { yyLOCAL = &LockingFunc{Type: IsUsedLock, Name: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1214: + case 1215: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Expr -//line sql.y:6206 +//line sql.y:6214 { yyLOCAL = &LockingFunc{Type: ReleaseAllLocks} } yyVAL.union = yyLOCAL - case 1215: + case 1216: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6210 +//line sql.y:6218 { yyLOCAL = &LockingFunc{Type: ReleaseLock, Name: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1216: + case 1217: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6214 +//line sql.y:6222 { yyLOCAL = &JSONSchemaValidFuncExpr{Schema: yyDollar[3].exprUnion(), Document: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1217: + case 1218: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6218 +//line sql.y:6226 { yyLOCAL = &JSONSchemaValidationReportFuncExpr{Schema: yyDollar[3].exprUnion(), Document: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1218: + case 1219: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6222 +//line sql.y:6230 { yyLOCAL = &JSONArrayExpr{Params: yyDollar[3].exprsUnion()} } yyVAL.union = yyLOCAL - case 1219: + case 1220: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6226 +//line sql.y:6234 { yyLOCAL = &GeomFormatExpr{FormatType: BinaryFormat, Geom: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1220: + case 1221: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6230 +//line sql.y:6238 { yyLOCAL = &GeomFormatExpr{FormatType: BinaryFormat, Geom: yyDollar[3].exprUnion(), AxisOrderOpt: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1221: + case 1222: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6234 +//line sql.y:6242 { yyLOCAL = &GeomFormatExpr{FormatType: TextFormat, Geom: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1222: + case 1223: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6238 +//line sql.y:6246 { yyLOCAL = &GeomFormatExpr{FormatType: TextFormat, Geom: yyDollar[3].exprUnion(), AxisOrderOpt: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1223: + case 1224: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6242 +//line sql.y:6250 { yyLOCAL = &GeomPropertyFuncExpr{Property: IsEmpty, Geom: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1224: + case 1225: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6246 +//line sql.y:6254 { yyLOCAL = &GeomPropertyFuncExpr{Property: IsSimple, Geom: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1225: + case 1226: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6250 +//line sql.y:6258 { yyLOCAL = &GeomPropertyFuncExpr{Property: Dimension, Geom: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1226: + case 1227: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6254 +//line sql.y:6262 { yyLOCAL = &GeomPropertyFuncExpr{Property: Envelope, Geom: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1227: + case 1228: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6258 +//line sql.y:6266 { yyLOCAL = &GeomPropertyFuncExpr{Property: GeometryType, Geom: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1228: + case 1229: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6262 +//line sql.y:6270 { yyLOCAL = &PointPropertyFuncExpr{Property: Latitude, Point: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1229: + case 1230: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6266 +//line sql.y:6274 { yyLOCAL = &PointPropertyFuncExpr{Property: Latitude, Point: yyDollar[3].exprUnion(), ValueToSet: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1230: + case 1231: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6270 +//line sql.y:6278 { yyLOCAL = &PointPropertyFuncExpr{Property: Longitude, Point: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1231: + case 1232: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6274 +//line sql.y:6282 { yyLOCAL = &PointPropertyFuncExpr{Property: Longitude, Point: yyDollar[3].exprUnion(), ValueToSet: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1232: + case 1233: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6278 +//line sql.y:6286 { yyLOCAL = &LinestrPropertyFuncExpr{Property: EndPoint, Linestring: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1233: + case 1234: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6282 +//line sql.y:6290 { yyLOCAL = &LinestrPropertyFuncExpr{Property: IsClosed, Linestring: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1234: + case 1235: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6286 +//line sql.y:6294 { yyLOCAL = &LinestrPropertyFuncExpr{Property: Length, Linestring: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1235: + case 1236: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6290 +//line sql.y:6298 { yyLOCAL = &LinestrPropertyFuncExpr{Property: Length, Linestring: yyDollar[3].exprUnion(), PropertyDefArg: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1236: + case 1237: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6294 +//line sql.y:6302 { yyLOCAL = &LinestrPropertyFuncExpr{Property: NumPoints, Linestring: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1237: + case 1238: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6298 +//line sql.y:6306 { yyLOCAL = &LinestrPropertyFuncExpr{Property: PointN, Linestring: yyDollar[3].exprUnion(), PropertyDefArg: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1238: + case 1239: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6302 +//line sql.y:6310 { yyLOCAL = &LinestrPropertyFuncExpr{Property: StartPoint, Linestring: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1239: + case 1240: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6306 +//line sql.y:6314 { yyLOCAL = &PointPropertyFuncExpr{Property: XCordinate, Point: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1240: + case 1241: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6310 +//line sql.y:6318 { yyLOCAL = &PointPropertyFuncExpr{Property: XCordinate, Point: yyDollar[3].exprUnion(), ValueToSet: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1241: + case 1242: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6314 +//line sql.y:6322 { yyLOCAL = &PointPropertyFuncExpr{Property: YCordinate, Point: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1242: + case 1243: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6318 +//line sql.y:6326 { yyLOCAL = &PointPropertyFuncExpr{Property: YCordinate, Point: yyDollar[3].exprUnion(), ValueToSet: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1243: + case 1244: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6322 +//line sql.y:6330 { yyLOCAL = &GeomFromTextExpr{Type: GeometryFromText, WktText: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1244: + case 1245: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6326 +//line sql.y:6334 { yyLOCAL = &GeomFromTextExpr{Type: GeometryFromText, WktText: yyDollar[3].exprUnion(), Srid: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1245: + case 1246: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL Expr -//line sql.y:6330 +//line sql.y:6338 { yyLOCAL = &GeomFromTextExpr{Type: GeometryFromText, WktText: yyDollar[3].exprUnion(), Srid: yyDollar[5].exprUnion(), AxisOrderOpt: yyDollar[7].exprUnion()} } yyVAL.union = yyLOCAL - case 1246: + case 1247: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6334 +//line sql.y:6342 { yyLOCAL = &GeomFromTextExpr{Type: GeometryCollectionFromText, WktText: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1247: + case 1248: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6338 +//line sql.y:6346 { yyLOCAL = &GeomFromTextExpr{Type: GeometryCollectionFromText, WktText: yyDollar[3].exprUnion(), Srid: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1248: + case 1249: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL Expr -//line sql.y:6342 +//line sql.y:6350 { yyLOCAL = &GeomFromTextExpr{Type: GeometryCollectionFromText, WktText: yyDollar[3].exprUnion(), Srid: yyDollar[5].exprUnion(), AxisOrderOpt: yyDollar[7].exprUnion()} } yyVAL.union = yyLOCAL - case 1249: + case 1250: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6346 +//line sql.y:6354 { yyLOCAL = &GeomFromTextExpr{Type: LineStringFromText, WktText: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1250: + case 1251: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6350 +//line sql.y:6358 { yyLOCAL = &GeomFromTextExpr{Type: LineStringFromText, WktText: yyDollar[3].exprUnion(), Srid: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1251: + case 1252: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL Expr -//line sql.y:6354 +//line sql.y:6362 { yyLOCAL = &GeomFromTextExpr{Type: LineStringFromText, WktText: yyDollar[3].exprUnion(), Srid: yyDollar[5].exprUnion(), AxisOrderOpt: yyDollar[7].exprUnion()} } yyVAL.union = yyLOCAL - case 1252: + case 1253: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6358 +//line sql.y:6366 { yyLOCAL = &GeomFromTextExpr{Type: MultiLinestringFromText, WktText: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1253: + case 1254: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6362 +//line sql.y:6370 { yyLOCAL = &GeomFromTextExpr{Type: MultiLinestringFromText, WktText: yyDollar[3].exprUnion(), Srid: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1254: + case 1255: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL Expr -//line sql.y:6366 +//line sql.y:6374 { yyLOCAL = &GeomFromTextExpr{Type: MultiLinestringFromText, WktText: yyDollar[3].exprUnion(), Srid: yyDollar[5].exprUnion(), AxisOrderOpt: yyDollar[7].exprUnion()} } yyVAL.union = yyLOCAL - case 1255: + case 1256: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6370 +//line sql.y:6378 { yyLOCAL = &GeomFromTextExpr{Type: MultiPointFromText, WktText: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1256: + case 1257: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6374 +//line sql.y:6382 { yyLOCAL = &GeomFromTextExpr{Type: MultiPointFromText, WktText: yyDollar[3].exprUnion(), Srid: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1257: + case 1258: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL Expr -//line sql.y:6378 +//line sql.y:6386 { yyLOCAL = &GeomFromTextExpr{Type: MultiPointFromText, WktText: yyDollar[3].exprUnion(), Srid: yyDollar[5].exprUnion(), AxisOrderOpt: yyDollar[7].exprUnion()} } yyVAL.union = yyLOCAL - case 1258: + case 1259: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6382 +//line sql.y:6390 { yyLOCAL = &GeomFromTextExpr{Type: MultiPolygonFromText, WktText: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1259: + case 1260: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6386 +//line sql.y:6394 { yyLOCAL = &GeomFromTextExpr{Type: MultiPolygonFromText, WktText: yyDollar[3].exprUnion(), Srid: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1260: + case 1261: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL Expr -//line sql.y:6390 +//line sql.y:6398 { yyLOCAL = &GeomFromTextExpr{Type: MultiPolygonFromText, WktText: yyDollar[3].exprUnion(), Srid: yyDollar[5].exprUnion(), AxisOrderOpt: yyDollar[7].exprUnion()} } yyVAL.union = yyLOCAL - case 1261: + case 1262: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6394 +//line sql.y:6402 { yyLOCAL = &GeomFromTextExpr{Type: PointFromText, WktText: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1262: + case 1263: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6398 +//line sql.y:6406 { yyLOCAL = &GeomFromTextExpr{Type: PointFromText, WktText: yyDollar[3].exprUnion(), Srid: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1263: + case 1264: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL Expr -//line sql.y:6402 +//line sql.y:6410 { yyLOCAL = &GeomFromTextExpr{Type: PointFromText, WktText: yyDollar[3].exprUnion(), Srid: yyDollar[5].exprUnion(), AxisOrderOpt: yyDollar[7].exprUnion()} } yyVAL.union = yyLOCAL - case 1264: + case 1265: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6406 +//line sql.y:6414 { yyLOCAL = &GeomFromTextExpr{Type: PolygonFromText, WktText: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1265: + case 1266: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6410 +//line sql.y:6418 { yyLOCAL = &GeomFromTextExpr{Type: PolygonFromText, WktText: yyDollar[3].exprUnion(), Srid: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1266: + case 1267: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL Expr -//line sql.y:6414 +//line sql.y:6422 { yyLOCAL = &GeomFromTextExpr{Type: PolygonFromText, WktText: yyDollar[3].exprUnion(), Srid: yyDollar[5].exprUnion(), AxisOrderOpt: yyDollar[7].exprUnion()} } yyVAL.union = yyLOCAL - case 1267: + case 1268: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6418 +//line sql.y:6426 { yyLOCAL = &GeomFromWKBExpr{Type: GeometryFromWKB, WkbBlob: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1268: + case 1269: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6422 +//line sql.y:6430 { yyLOCAL = &GeomFromWKBExpr{Type: GeometryFromWKB, WkbBlob: yyDollar[3].exprUnion(), Srid: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1269: + case 1270: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL Expr -//line sql.y:6426 +//line sql.y:6434 { yyLOCAL = &GeomFromWKBExpr{Type: GeometryFromWKB, WkbBlob: yyDollar[3].exprUnion(), Srid: yyDollar[5].exprUnion(), AxisOrderOpt: yyDollar[7].exprUnion()} } yyVAL.union = yyLOCAL - case 1270: + case 1271: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6430 +//line sql.y:6438 { yyLOCAL = &GeomFromWKBExpr{Type: GeometryCollectionFromWKB, WkbBlob: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1271: + case 1272: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6434 +//line sql.y:6442 { yyLOCAL = &GeomFromWKBExpr{Type: GeometryCollectionFromWKB, WkbBlob: yyDollar[3].exprUnion(), Srid: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1272: + case 1273: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL Expr -//line sql.y:6438 +//line sql.y:6446 { yyLOCAL = &GeomFromWKBExpr{Type: GeometryCollectionFromWKB, WkbBlob: yyDollar[3].exprUnion(), Srid: yyDollar[5].exprUnion(), AxisOrderOpt: yyDollar[7].exprUnion()} } yyVAL.union = yyLOCAL - case 1273: + case 1274: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6442 +//line sql.y:6450 { yyLOCAL = &GeomFromWKBExpr{Type: LineStringFromWKB, WkbBlob: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1274: + case 1275: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6446 +//line sql.y:6454 { yyLOCAL = &GeomFromWKBExpr{Type: LineStringFromWKB, WkbBlob: yyDollar[3].exprUnion(), Srid: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1275: + case 1276: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL Expr -//line sql.y:6450 +//line sql.y:6458 { yyLOCAL = &GeomFromWKBExpr{Type: LineStringFromWKB, WkbBlob: yyDollar[3].exprUnion(), Srid: yyDollar[5].exprUnion(), AxisOrderOpt: yyDollar[7].exprUnion()} } yyVAL.union = yyLOCAL - case 1276: + case 1277: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6454 +//line sql.y:6462 { yyLOCAL = &GeomFromWKBExpr{Type: MultiLinestringFromWKB, WkbBlob: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1277: + case 1278: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6458 +//line sql.y:6466 { yyLOCAL = &GeomFromWKBExpr{Type: MultiLinestringFromWKB, WkbBlob: yyDollar[3].exprUnion(), Srid: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1278: + case 1279: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL Expr -//line sql.y:6462 +//line sql.y:6470 { yyLOCAL = &GeomFromWKBExpr{Type: MultiLinestringFromWKB, WkbBlob: yyDollar[3].exprUnion(), Srid: yyDollar[5].exprUnion(), AxisOrderOpt: yyDollar[7].exprUnion()} } yyVAL.union = yyLOCAL - case 1279: + case 1280: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6466 +//line sql.y:6474 { yyLOCAL = &GeomFromWKBExpr{Type: MultiPointFromWKB, WkbBlob: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1280: + case 1281: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6470 +//line sql.y:6478 { yyLOCAL = &GeomFromWKBExpr{Type: MultiPointFromWKB, WkbBlob: yyDollar[3].exprUnion(), Srid: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1281: + case 1282: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL Expr -//line sql.y:6474 +//line sql.y:6482 { yyLOCAL = &GeomFromWKBExpr{Type: MultiPointFromWKB, WkbBlob: yyDollar[3].exprUnion(), Srid: yyDollar[5].exprUnion(), AxisOrderOpt: yyDollar[7].exprUnion()} } yyVAL.union = yyLOCAL - case 1282: + case 1283: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6478 +//line sql.y:6486 { yyLOCAL = &GeomFromWKBExpr{Type: MultiPolygonFromWKB, WkbBlob: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1283: + case 1284: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6482 +//line sql.y:6490 { yyLOCAL = &GeomFromWKBExpr{Type: MultiPolygonFromWKB, WkbBlob: yyDollar[3].exprUnion(), Srid: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1284: + case 1285: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL Expr -//line sql.y:6486 +//line sql.y:6494 { yyLOCAL = &GeomFromWKBExpr{Type: MultiPolygonFromWKB, WkbBlob: yyDollar[3].exprUnion(), Srid: yyDollar[5].exprUnion(), AxisOrderOpt: yyDollar[7].exprUnion()} } yyVAL.union = yyLOCAL - case 1285: + case 1286: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6490 +//line sql.y:6498 { yyLOCAL = &GeomFromWKBExpr{Type: PointFromWKB, WkbBlob: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1286: + case 1287: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6494 +//line sql.y:6502 { yyLOCAL = &GeomFromWKBExpr{Type: PointFromWKB, WkbBlob: yyDollar[3].exprUnion(), Srid: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1287: + case 1288: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL Expr -//line sql.y:6498 +//line sql.y:6506 { yyLOCAL = &GeomFromWKBExpr{Type: PointFromWKB, WkbBlob: yyDollar[3].exprUnion(), Srid: yyDollar[5].exprUnion(), AxisOrderOpt: yyDollar[7].exprUnion()} } yyVAL.union = yyLOCAL - case 1288: + case 1289: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6502 +//line sql.y:6510 { yyLOCAL = &GeomFromWKBExpr{Type: PolygonFromWKB, WkbBlob: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1289: + case 1290: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6506 +//line sql.y:6514 { yyLOCAL = &GeomFromWKBExpr{Type: PolygonFromWKB, WkbBlob: yyDollar[3].exprUnion(), Srid: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1290: + case 1291: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL Expr -//line sql.y:6510 +//line sql.y:6518 { yyLOCAL = &GeomFromWKBExpr{Type: PolygonFromWKB, WkbBlob: yyDollar[3].exprUnion(), Srid: yyDollar[5].exprUnion(), AxisOrderOpt: yyDollar[7].exprUnion()} } yyVAL.union = yyLOCAL - case 1291: + case 1292: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6514 +//line sql.y:6522 { yyLOCAL = &PolygonPropertyFuncExpr{Property: Area, Polygon: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1292: + case 1293: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6518 +//line sql.y:6526 { yyLOCAL = &PolygonPropertyFuncExpr{Property: Centroid, Polygon: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1293: + case 1294: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6522 +//line sql.y:6530 { yyLOCAL = &PolygonPropertyFuncExpr{Property: ExteriorRing, Polygon: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1294: + case 1295: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6526 +//line sql.y:6534 { yyLOCAL = &PolygonPropertyFuncExpr{Property: InteriorRingN, Polygon: yyDollar[3].exprUnion(), PropertyDefArg: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1295: + case 1296: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6530 +//line sql.y:6538 { yyLOCAL = &PolygonPropertyFuncExpr{Property: NumInteriorRings, Polygon: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1296: + case 1297: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6534 +//line sql.y:6542 { yyLOCAL = &GeomCollPropertyFuncExpr{Property: GeometryN, GeomColl: yyDollar[3].exprUnion(), PropertyDefArg: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1297: + case 1298: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6538 +//line sql.y:6546 { yyLOCAL = &GeomCollPropertyFuncExpr{Property: NumGeometries, GeomColl: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1298: + case 1299: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL Expr -//line sql.y:6542 +//line sql.y:6550 { yyLOCAL = &GeoHashFromLatLongExpr{Longitude: yyDollar[3].exprUnion(), Latitude: yyDollar[5].exprUnion(), MaxLength: yyDollar[7].exprUnion()} } yyVAL.union = yyLOCAL - case 1299: + case 1300: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6546 +//line sql.y:6554 { yyLOCAL = &GeoHashFromPointExpr{Point: yyDollar[3].exprUnion(), MaxLength: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1300: + case 1301: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6550 +//line sql.y:6558 { yyLOCAL = &GeomFromGeoHashExpr{GeomType: LatitudeFromHash, GeoHash: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1301: + case 1302: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6554 +//line sql.y:6562 { yyLOCAL = &GeomFromGeoHashExpr{GeomType: LongitudeFromHash, GeoHash: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1302: + case 1303: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6558 +//line sql.y:6566 { yyLOCAL = &GeomFromGeoHashExpr{GeomType: PointFromHash, GeoHash: yyDollar[3].exprUnion(), SridOpt: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1303: + case 1304: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6562 +//line sql.y:6570 { yyLOCAL = &GeomFromGeoJSONExpr{GeoJSON: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1304: + case 1305: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6566 +//line sql.y:6574 { yyLOCAL = &GeomFromGeoJSONExpr{GeoJSON: yyDollar[3].exprUnion(), HigherDimHandlerOpt: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1305: + case 1306: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL Expr -//line sql.y:6570 +//line sql.y:6578 { yyLOCAL = &GeomFromGeoJSONExpr{GeoJSON: yyDollar[3].exprUnion(), HigherDimHandlerOpt: yyDollar[5].exprUnion(), Srid: yyDollar[7].exprUnion()} } yyVAL.union = yyLOCAL - case 1306: + case 1307: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6574 +//line sql.y:6582 { yyLOCAL = &GeoJSONFromGeomExpr{Geom: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1307: + case 1308: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6578 +//line sql.y:6586 { yyLOCAL = &GeoJSONFromGeomExpr{Geom: yyDollar[3].exprUnion(), MaxDecimalDigits: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1308: + case 1309: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL Expr -//line sql.y:6582 +//line sql.y:6590 { yyLOCAL = &GeoJSONFromGeomExpr{Geom: yyDollar[3].exprUnion(), MaxDecimalDigits: yyDollar[5].exprUnion(), Bitmask: yyDollar[7].exprUnion()} } yyVAL.union = yyLOCAL - case 1309: + case 1310: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6586 +//line sql.y:6594 { yyLOCAL = &JSONObjectExpr{Params: yyDollar[3].jsonObjectParamsUnion()} } yyVAL.union = yyLOCAL - case 1310: + case 1311: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6590 +//line sql.y:6598 { yyLOCAL = &JSONQuoteExpr{StringArg: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1311: + case 1312: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6594 +//line sql.y:6602 { yyLOCAL = &JSONContainsExpr{Target: yyDollar[3].exprUnion(), Candidate: yyDollar[5].exprsUnion()[0], PathList: yyDollar[5].exprsUnion()[1:]} } yyVAL.union = yyLOCAL - case 1312: + case 1313: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL Expr -//line sql.y:6598 +//line sql.y:6606 { yyLOCAL = &JSONContainsPathExpr{JSONDoc: yyDollar[3].exprUnion(), OneOrAll: yyDollar[5].exprUnion(), PathList: yyDollar[7].exprsUnion()} } yyVAL.union = yyLOCAL - case 1313: + case 1314: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6602 +//line sql.y:6610 { yyLOCAL = &JSONExtractExpr{JSONDoc: yyDollar[3].exprUnion(), PathList: yyDollar[5].exprsUnion()} } yyVAL.union = yyLOCAL - case 1314: + case 1315: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6606 +//line sql.y:6614 { yyLOCAL = &JSONKeysExpr{JSONDoc: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1315: + case 1316: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6610 +//line sql.y:6618 { yyLOCAL = &JSONKeysExpr{JSONDoc: yyDollar[3].exprUnion(), Path: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1316: + case 1317: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6614 +//line sql.y:6622 { yyLOCAL = &JSONOverlapsExpr{JSONDoc1: yyDollar[3].exprUnion(), JSONDoc2: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1317: + case 1318: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL Expr -//line sql.y:6618 +//line sql.y:6626 { yyLOCAL = &JSONSearchExpr{JSONDoc: yyDollar[3].exprUnion(), OneOrAll: yyDollar[5].exprUnion(), SearchStr: yyDollar[7].exprUnion()} } yyVAL.union = yyLOCAL - case 1318: + case 1319: yyDollar = yyS[yypt-10 : yypt+1] var yyLOCAL Expr -//line sql.y:6622 +//line sql.y:6630 { 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 1319: + case 1320: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL Expr -//line sql.y:6626 +//line sql.y:6634 { yyLOCAL = &JSONValueExpr{JSONDoc: yyDollar[3].exprUnion(), Path: yyDollar[5].exprUnion(), ReturningType: yyDollar[6].convertTypeUnion()} } yyVAL.union = yyLOCAL - case 1320: + case 1321: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL Expr -//line sql.y:6630 +//line sql.y:6638 { yyLOCAL = &JSONValueExpr{JSONDoc: yyDollar[3].exprUnion(), Path: yyDollar[5].exprUnion(), ReturningType: yyDollar[6].convertTypeUnion(), EmptyOnResponse: yyDollar[7].jtOnResponseUnion()} } yyVAL.union = yyLOCAL - case 1321: + case 1322: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL Expr -//line sql.y:6634 +//line sql.y:6642 { yyLOCAL = &JSONValueExpr{JSONDoc: yyDollar[3].exprUnion(), Path: yyDollar[5].exprUnion(), ReturningType: yyDollar[6].convertTypeUnion(), ErrorOnResponse: yyDollar[7].jtOnResponseUnion()} } yyVAL.union = yyLOCAL - case 1322: + case 1323: yyDollar = yyS[yypt-9 : yypt+1] var yyLOCAL Expr -//line sql.y:6638 +//line sql.y:6646 { 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 1323: + case 1324: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6642 +//line sql.y:6650 { yyLOCAL = &JSONAttributesExpr{Type: DepthAttributeType, JSONDoc: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1324: + case 1325: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6646 +//line sql.y:6654 { yyLOCAL = &JSONAttributesExpr{Type: ValidAttributeType, JSONDoc: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1325: + case 1326: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6650 +//line sql.y:6658 { yyLOCAL = &JSONAttributesExpr{Type: TypeAttributeType, JSONDoc: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1326: + case 1327: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6654 +//line sql.y:6662 { yyLOCAL = &JSONAttributesExpr{Type: LengthAttributeType, JSONDoc: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1327: + case 1328: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6658 +//line sql.y:6666 { yyLOCAL = &JSONAttributesExpr{Type: LengthAttributeType, JSONDoc: yyDollar[3].exprUnion(), Path: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1328: + case 1329: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6662 +//line sql.y:6670 { yyLOCAL = &JSONValueModifierExpr{Type: JSONArrayAppendType, JSONDoc: yyDollar[3].exprUnion(), Params: yyDollar[5].jsonObjectParamsUnion()} } yyVAL.union = yyLOCAL - case 1329: + case 1330: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6666 +//line sql.y:6674 { yyLOCAL = &JSONValueModifierExpr{Type: JSONArrayInsertType, JSONDoc: yyDollar[3].exprUnion(), Params: yyDollar[5].jsonObjectParamsUnion()} } yyVAL.union = yyLOCAL - case 1330: + case 1331: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6670 +//line sql.y:6678 { yyLOCAL = &JSONValueModifierExpr{Type: JSONInsertType, JSONDoc: yyDollar[3].exprUnion(), Params: yyDollar[5].jsonObjectParamsUnion()} } yyVAL.union = yyLOCAL - case 1331: + case 1332: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6674 +//line sql.y:6682 { yyLOCAL = &JSONValueModifierExpr{Type: JSONReplaceType, JSONDoc: yyDollar[3].exprUnion(), Params: yyDollar[5].jsonObjectParamsUnion()} } yyVAL.union = yyLOCAL - case 1332: + case 1333: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6678 +//line sql.y:6686 { yyLOCAL = &JSONValueModifierExpr{Type: JSONSetType, JSONDoc: yyDollar[3].exprUnion(), Params: yyDollar[5].jsonObjectParamsUnion()} } yyVAL.union = yyLOCAL - case 1333: + case 1334: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6682 +//line sql.y:6690 { yyLOCAL = &JSONValueMergeExpr{Type: JSONMergeType, JSONDoc: yyDollar[3].exprUnion(), JSONDocList: yyDollar[5].exprsUnion()} } yyVAL.union = yyLOCAL - case 1334: + case 1335: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6686 +//line sql.y:6694 { yyLOCAL = &JSONValueMergeExpr{Type: JSONMergePatchType, JSONDoc: yyDollar[3].exprUnion(), JSONDocList: yyDollar[5].exprsUnion()} } yyVAL.union = yyLOCAL - case 1335: + case 1336: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6690 +//line sql.y:6698 { yyLOCAL = &JSONValueMergeExpr{Type: JSONMergePreserveType, JSONDoc: yyDollar[3].exprUnion(), JSONDocList: yyDollar[5].exprsUnion()} } yyVAL.union = yyLOCAL - case 1336: + case 1337: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6694 +//line sql.y:6702 { yyLOCAL = &JSONRemoveExpr{JSONDoc: yyDollar[3].exprUnion(), PathList: yyDollar[5].exprsUnion()} } yyVAL.union = yyLOCAL - case 1337: + case 1338: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6698 +//line sql.y:6706 { yyLOCAL = &JSONUnquoteExpr{JSONValue: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1338: + case 1339: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6702 +//line sql.y:6710 { yyLOCAL = &MultiPolygonExpr{PolygonParams: yyDollar[3].exprsUnion()} } yyVAL.union = yyLOCAL - case 1339: + case 1340: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6706 +//line sql.y:6714 { yyLOCAL = &MultiPointExpr{PointParams: yyDollar[3].exprsUnion()} } yyVAL.union = yyLOCAL - case 1340: + case 1341: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6710 +//line sql.y:6718 { yyLOCAL = &MultiLinestringExpr{LinestringParams: yyDollar[3].exprsUnion()} } yyVAL.union = yyLOCAL - case 1341: + case 1342: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6714 +//line sql.y:6722 { yyLOCAL = &PolygonExpr{LinestringParams: yyDollar[3].exprsUnion()} } yyVAL.union = yyLOCAL - case 1342: + case 1343: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6718 +//line sql.y:6726 { yyLOCAL = &LineStringExpr{PointParams: yyDollar[3].exprsUnion()} } yyVAL.union = yyLOCAL - case 1343: + case 1344: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6722 +//line sql.y:6730 { yyLOCAL = &PointExpr{XCordinate: yyDollar[3].exprUnion(), YCordinate: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1344: + case 1345: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6726 +//line sql.y:6734 { yyLOCAL = &ArgumentLessWindowExpr{Type: yyDollar[1].argumentLessWindowExprTypeUnion(), OverClause: yyDollar[4].overClauseUnion()} } yyVAL.union = yyLOCAL - case 1345: + case 1346: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6730 +//line sql.y:6738 { yyLOCAL = &FirstOrLastValueExpr{Type: yyDollar[1].firstOrLastValueExprTypeUnion(), Expr: yyDollar[3].exprUnion(), NullTreatmentClause: yyDollar[5].nullTreatmentClauseUnion(), OverClause: yyDollar[6].overClauseUnion()} } yyVAL.union = yyLOCAL - case 1346: + case 1347: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL Expr -//line sql.y:6734 +//line sql.y:6742 { yyLOCAL = &NtileExpr{N: yyDollar[3].exprUnion(), OverClause: yyDollar[5].overClauseUnion()} } yyVAL.union = yyLOCAL - case 1347: + case 1348: yyDollar = yyS[yypt-9 : yypt+1] var yyLOCAL Expr -//line sql.y:6738 +//line sql.y:6746 { 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 1348: + case 1349: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6742 +//line sql.y:6750 { yyLOCAL = &LagLeadExpr{Type: yyDollar[1].lagLeadExprTypeUnion(), Expr: yyDollar[3].exprUnion(), NullTreatmentClause: yyDollar[5].nullTreatmentClauseUnion(), OverClause: yyDollar[6].overClauseUnion()} } yyVAL.union = yyLOCAL - case 1349: + case 1350: yyDollar = yyS[yypt-9 : yypt+1] var yyLOCAL Expr -//line sql.y:6746 +//line sql.y:6754 { 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 1350: + case 1351: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL Expr -//line sql.y:6750 +//line sql.y:6758 { yyLOCAL = &IntervalDateExpr{Syntax: IntervalDateExprAdddate, Date: yyDollar[3].exprUnion(), Interval: yyDollar[6].exprUnion(), Unit: yyDollar[7].intervalTypeUnion()} } yyVAL.union = yyLOCAL - case 1351: + case 1352: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6754 +//line sql.y:6762 { yyLOCAL = &IntervalDateExpr{Syntax: IntervalDateExprAdddate, Date: yyDollar[3].exprUnion(), Interval: yyDollar[5].exprUnion(), Unit: IntervalNone} } yyVAL.union = yyLOCAL - case 1352: + case 1353: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL Expr -//line sql.y:6758 +//line sql.y:6766 { yyLOCAL = &IntervalDateExpr{Syntax: IntervalDateExprDateAdd, Date: yyDollar[3].exprUnion(), Interval: yyDollar[6].exprUnion(), Unit: yyDollar[7].intervalTypeUnion()} } yyVAL.union = yyLOCAL - case 1353: + case 1354: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL Expr -//line sql.y:6762 +//line sql.y:6770 { yyLOCAL = &IntervalDateExpr{Syntax: IntervalDateExprDateSub, Date: yyDollar[3].exprUnion(), Interval: yyDollar[6].exprUnion(), Unit: yyDollar[7].intervalTypeUnion()} } yyVAL.union = yyLOCAL - case 1354: + case 1355: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL Expr -//line sql.y:6766 +//line sql.y:6774 { yyLOCAL = &IntervalDateExpr{Syntax: IntervalDateExprSubdate, Date: yyDollar[3].exprUnion(), Interval: yyDollar[6].exprUnion(), Unit: yyDollar[7].intervalTypeUnion()} } yyVAL.union = yyLOCAL - case 1355: + case 1356: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6770 +//line sql.y:6778 { yyLOCAL = &IntervalDateExpr{Syntax: IntervalDateExprSubdate, Date: yyDollar[3].exprUnion(), Interval: yyDollar[5].exprUnion(), Unit: IntervalNone} } yyVAL.union = yyLOCAL - case 1360: + case 1361: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Expr -//line sql.y:6780 +//line sql.y:6788 { yyLOCAL = yyDollar[1].exprUnion() } yyVAL.union = yyLOCAL - case 1361: + case 1362: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Expr -//line sql.y:6784 +//line sql.y:6792 { yyLOCAL = NewIntLiteral(yyDollar[1].str) } yyVAL.union = yyLOCAL - case 1362: + case 1363: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Expr -//line sql.y:6788 +//line sql.y:6796 { yyLOCAL = yyDollar[1].variableUnion() } yyVAL.union = yyLOCAL - case 1363: + case 1364: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Expr -//line sql.y:6792 +//line sql.y:6800 { yyLOCAL = parseBindVariable(yylex, yyDollar[1].str[1:]) } yyVAL.union = yyLOCAL - case 1364: + case 1365: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL Expr -//line sql.y:6797 +//line sql.y:6805 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1365: + case 1366: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Expr -//line sql.y:6801 +//line sql.y:6809 { yyLOCAL = yyDollar[2].exprUnion() } yyVAL.union = yyLOCAL - case 1366: + case 1367: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6807 +//line sql.y:6815 { yyLOCAL = &RegexpInstrExpr{Expr: yyDollar[3].exprUnion(), Pattern: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1367: + case 1368: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL Expr -//line sql.y:6811 +//line sql.y:6819 { yyLOCAL = &RegexpInstrExpr{Expr: yyDollar[3].exprUnion(), Pattern: yyDollar[5].exprUnion(), Position: yyDollar[7].exprUnion()} } yyVAL.union = yyLOCAL - case 1368: + case 1369: yyDollar = yyS[yypt-10 : yypt+1] var yyLOCAL Expr -//line sql.y:6815 +//line sql.y:6823 { yyLOCAL = &RegexpInstrExpr{Expr: yyDollar[3].exprUnion(), Pattern: yyDollar[5].exprUnion(), Position: yyDollar[7].exprUnion(), Occurrence: yyDollar[9].exprUnion()} } yyVAL.union = yyLOCAL - case 1369: + case 1370: yyDollar = yyS[yypt-12 : yypt+1] var yyLOCAL Expr -//line sql.y:6819 +//line sql.y:6827 { 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 1370: + case 1371: yyDollar = yyS[yypt-14 : yypt+1] var yyLOCAL Expr -//line sql.y:6823 +//line sql.y:6831 { // 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 1371: + case 1372: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6828 +//line sql.y:6836 { yyLOCAL = &RegexpLikeExpr{Expr: yyDollar[3].exprUnion(), Pattern: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1372: + case 1373: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL Expr -//line sql.y:6832 +//line sql.y:6840 { yyLOCAL = &RegexpLikeExpr{Expr: yyDollar[3].exprUnion(), Pattern: yyDollar[5].exprUnion(), MatchType: yyDollar[7].exprUnion()} } yyVAL.union = yyLOCAL - case 1373: + case 1374: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL Expr -//line sql.y:6836 +//line sql.y:6844 { yyLOCAL = &RegexpReplaceExpr{Expr: yyDollar[3].exprUnion(), Pattern: yyDollar[5].exprUnion(), Repl: yyDollar[7].exprUnion()} } yyVAL.union = yyLOCAL - case 1374: + case 1375: yyDollar = yyS[yypt-10 : yypt+1] var yyLOCAL Expr -//line sql.y:6840 +//line sql.y:6848 { yyLOCAL = &RegexpReplaceExpr{Expr: yyDollar[3].exprUnion(), Pattern: yyDollar[5].exprUnion(), Repl: yyDollar[7].exprUnion(), Position: yyDollar[9].exprUnion()} } yyVAL.union = yyLOCAL - case 1375: + case 1376: yyDollar = yyS[yypt-12 : yypt+1] var yyLOCAL Expr -//line sql.y:6844 +//line sql.y:6852 { 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 1376: + case 1377: yyDollar = yyS[yypt-14 : yypt+1] var yyLOCAL Expr -//line sql.y:6848 +//line sql.y:6856 { // 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 1377: + case 1378: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6853 +//line sql.y:6861 { yyLOCAL = &RegexpSubstrExpr{Expr: yyDollar[3].exprUnion(), Pattern: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1378: + case 1379: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL Expr -//line sql.y:6857 +//line sql.y:6865 { yyLOCAL = &RegexpSubstrExpr{Expr: yyDollar[3].exprUnion(), Pattern: yyDollar[5].exprUnion(), Position: yyDollar[7].exprUnion()} } yyVAL.union = yyLOCAL - case 1379: + case 1380: yyDollar = yyS[yypt-10 : yypt+1] var yyLOCAL Expr -//line sql.y:6861 +//line sql.y:6869 { yyLOCAL = &RegexpSubstrExpr{Expr: yyDollar[3].exprUnion(), Pattern: yyDollar[5].exprUnion(), Position: yyDollar[7].exprUnion(), Occurrence: yyDollar[9].exprUnion()} } yyVAL.union = yyLOCAL - case 1380: + case 1381: yyDollar = yyS[yypt-12 : yypt+1] var yyLOCAL Expr -//line sql.y:6865 +//line sql.y:6873 { // 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 1381: + case 1382: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6872 +//line sql.y:6880 { yyLOCAL = &ExtractValueExpr{Fragment: yyDollar[3].exprUnion(), XPathExpr: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1382: + case 1383: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL Expr -//line sql.y:6876 +//line sql.y:6884 { yyLOCAL = &UpdateXMLExpr{Target: yyDollar[3].exprUnion(), XPathExpr: yyDollar[5].exprUnion(), NewXML: yyDollar[7].exprUnion()} } yyVAL.union = yyLOCAL - case 1383: + case 1384: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6882 +//line sql.y:6890 { yyLOCAL = &PerformanceSchemaFuncExpr{Type: FormatBytesType, Argument: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1384: + case 1385: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6886 +//line sql.y:6894 { yyLOCAL = &PerformanceSchemaFuncExpr{Type: FormatPicoTimeType, Argument: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1385: + case 1386: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Expr -//line sql.y:6890 +//line sql.y:6898 { yyLOCAL = &PerformanceSchemaFuncExpr{Type: PsCurrentThreadIDType} } yyVAL.union = yyLOCAL - case 1386: + case 1387: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6894 +//line sql.y:6902 { yyLOCAL = &PerformanceSchemaFuncExpr{Type: PsThreadIDType, Argument: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1387: + case 1388: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6900 +//line sql.y:6908 { yyLOCAL = >IDFuncExpr{Type: GTIDSubsetType, Set1: yyDollar[3].exprUnion(), Set2: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1388: + case 1389: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6904 +//line sql.y:6912 { yyLOCAL = >IDFuncExpr{Type: GTIDSubtractType, Set1: yyDollar[3].exprUnion(), Set2: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1389: + case 1390: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6908 +//line sql.y:6916 { yyLOCAL = >IDFuncExpr{Type: WaitForExecutedGTIDSetType, Set1: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1390: + case 1391: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6912 +//line sql.y:6920 { yyLOCAL = >IDFuncExpr{Type: WaitForExecutedGTIDSetType, Set1: yyDollar[3].exprUnion(), Timeout: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1391: + case 1392: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6916 +//line sql.y:6924 { yyLOCAL = >IDFuncExpr{Type: WaitUntilSQLThreadAfterGTIDSType, Set1: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1392: + case 1393: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6920 +//line sql.y:6928 { yyLOCAL = >IDFuncExpr{Type: WaitUntilSQLThreadAfterGTIDSType, Set1: yyDollar[3].exprUnion(), Timeout: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1393: + case 1394: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL Expr -//line sql.y:6924 +//line sql.y:6932 { yyLOCAL = >IDFuncExpr{Type: WaitUntilSQLThreadAfterGTIDSType, Set1: yyDollar[3].exprUnion(), Timeout: yyDollar[5].exprUnion(), Channel: yyDollar[7].exprUnion()} } yyVAL.union = yyLOCAL - case 1394: + case 1395: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *ConvertType -//line sql.y:6929 +//line sql.y:6937 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1395: + case 1396: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *ConvertType -//line sql.y:6933 +//line sql.y:6941 { yyLOCAL = yyDollar[2].convertTypeUnion() } yyVAL.union = yyLOCAL - case 1396: + case 1397: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL IntervalType -//line sql.y:6939 +//line sql.y:6947 { yyLOCAL = IntervalDayHour } yyVAL.union = yyLOCAL - case 1397: + case 1398: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL IntervalType -//line sql.y:6943 +//line sql.y:6951 { yyLOCAL = IntervalDayMicrosecond } yyVAL.union = yyLOCAL - case 1398: + case 1399: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL IntervalType -//line sql.y:6947 +//line sql.y:6955 { yyLOCAL = IntervalDayMinute } yyVAL.union = yyLOCAL - case 1399: + case 1400: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL IntervalType -//line sql.y:6951 +//line sql.y:6959 { yyLOCAL = IntervalDaySecond } yyVAL.union = yyLOCAL - case 1400: + case 1401: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL IntervalType -//line sql.y:6955 +//line sql.y:6963 { yyLOCAL = IntervalHourMicrosecond } yyVAL.union = yyLOCAL - case 1401: + case 1402: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL IntervalType -//line sql.y:6959 +//line sql.y:6967 { yyLOCAL = IntervalHourMinute } yyVAL.union = yyLOCAL - case 1402: + case 1403: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL IntervalType -//line sql.y:6963 +//line sql.y:6971 { yyLOCAL = IntervalHourSecond } yyVAL.union = yyLOCAL - case 1403: + case 1404: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL IntervalType -//line sql.y:6967 +//line sql.y:6975 { yyLOCAL = IntervalMinuteMicrosecond } yyVAL.union = yyLOCAL - case 1404: + case 1405: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL IntervalType -//line sql.y:6971 +//line sql.y:6979 { yyLOCAL = IntervalMinuteSecond } yyVAL.union = yyLOCAL - case 1405: + case 1406: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL IntervalType -//line sql.y:6975 +//line sql.y:6983 { yyLOCAL = IntervalSecondMicrosecond } yyVAL.union = yyLOCAL - case 1406: + case 1407: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL IntervalType -//line sql.y:6979 +//line sql.y:6987 { yyLOCAL = IntervalYearMonth } yyVAL.union = yyLOCAL - case 1407: + case 1408: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL IntervalType -//line sql.y:6983 +//line sql.y:6991 { yyLOCAL = IntervalDay } yyVAL.union = yyLOCAL - case 1408: + case 1409: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL IntervalType -//line sql.y:6987 +//line sql.y:6995 { yyLOCAL = IntervalWeek } yyVAL.union = yyLOCAL - case 1409: + case 1410: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL IntervalType -//line sql.y:6991 +//line sql.y:6999 { yyLOCAL = IntervalHour } yyVAL.union = yyLOCAL - case 1410: + case 1411: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL IntervalType -//line sql.y:6995 +//line sql.y:7003 { yyLOCAL = IntervalMinute } yyVAL.union = yyLOCAL - case 1411: + case 1412: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL IntervalType -//line sql.y:6999 +//line sql.y:7007 { yyLOCAL = IntervalMonth } yyVAL.union = yyLOCAL - case 1412: + case 1413: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL IntervalType -//line sql.y:7003 +//line sql.y:7011 { yyLOCAL = IntervalQuarter } yyVAL.union = yyLOCAL - case 1413: + case 1414: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL IntervalType -//line sql.y:7007 +//line sql.y:7015 { yyLOCAL = IntervalSecond } yyVAL.union = yyLOCAL - case 1414: + case 1415: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL IntervalType -//line sql.y:7011 +//line sql.y:7019 { yyLOCAL = IntervalMicrosecond } yyVAL.union = yyLOCAL - case 1415: + case 1416: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL IntervalType -//line sql.y:7015 +//line sql.y:7023 { yyLOCAL = IntervalYear } yyVAL.union = yyLOCAL - case 1416: + case 1417: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL IntervalType -//line sql.y:7021 +//line sql.y:7029 { yyLOCAL = IntervalDay } yyVAL.union = yyLOCAL - case 1417: + case 1418: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL IntervalType -//line sql.y:7025 +//line sql.y:7033 { yyLOCAL = IntervalWeek } yyVAL.union = yyLOCAL - case 1418: + case 1419: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL IntervalType -//line sql.y:7029 +//line sql.y:7037 { yyLOCAL = IntervalHour } yyVAL.union = yyLOCAL - case 1419: + case 1420: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL IntervalType -//line sql.y:7033 +//line sql.y:7041 { yyLOCAL = IntervalMinute } yyVAL.union = yyLOCAL - case 1420: + case 1421: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL IntervalType -//line sql.y:7037 +//line sql.y:7045 { yyLOCAL = IntervalMonth } yyVAL.union = yyLOCAL - case 1421: + case 1422: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL IntervalType -//line sql.y:7041 +//line sql.y:7049 { yyLOCAL = IntervalQuarter } yyVAL.union = yyLOCAL - case 1422: + case 1423: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL IntervalType -//line sql.y:7045 +//line sql.y:7053 { yyLOCAL = IntervalSecond } yyVAL.union = yyLOCAL - case 1423: + case 1424: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL IntervalType -//line sql.y:7049 +//line sql.y:7057 { yyLOCAL = IntervalMicrosecond } yyVAL.union = yyLOCAL - case 1424: + case 1425: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL IntervalType -//line sql.y:7053 +//line sql.y:7061 { yyLOCAL = IntervalYear } yyVAL.union = yyLOCAL - case 1425: + case 1426: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL IntervalType -//line sql.y:7057 +//line sql.y:7065 { yyLOCAL = IntervalDay } yyVAL.union = yyLOCAL - case 1426: + case 1427: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL IntervalType -//line sql.y:7061 +//line sql.y:7069 { yyLOCAL = IntervalWeek } yyVAL.union = yyLOCAL - case 1427: + case 1428: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL IntervalType -//line sql.y:7065 +//line sql.y:7073 { yyLOCAL = IntervalHour } yyVAL.union = yyLOCAL - case 1428: + case 1429: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL IntervalType -//line sql.y:7069 +//line sql.y:7077 { yyLOCAL = IntervalMinute } yyVAL.union = yyLOCAL - case 1429: + case 1430: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL IntervalType -//line sql.y:7073 +//line sql.y:7081 { yyLOCAL = IntervalMonth } yyVAL.union = yyLOCAL - case 1430: + case 1431: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL IntervalType -//line sql.y:7077 +//line sql.y:7085 { yyLOCAL = IntervalQuarter } yyVAL.union = yyLOCAL - case 1431: + case 1432: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL IntervalType -//line sql.y:7081 +//line sql.y:7089 { yyLOCAL = IntervalSecond } yyVAL.union = yyLOCAL - case 1432: + case 1433: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL IntervalType -//line sql.y:7085 +//line sql.y:7093 { yyLOCAL = IntervalMicrosecond } yyVAL.union = yyLOCAL - case 1433: + case 1434: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL IntervalType -//line sql.y:7089 +//line sql.y:7097 { yyLOCAL = IntervalYear } yyVAL.union = yyLOCAL - case 1436: + case 1437: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL int -//line sql.y:7099 +//line sql.y:7107 { yyLOCAL = 0 } yyVAL.union = yyLOCAL - case 1437: + case 1438: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL int -//line sql.y:7103 +//line sql.y:7111 { yyLOCAL = 0 } yyVAL.union = yyLOCAL - case 1438: + case 1439: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL int -//line sql.y:7107 +//line sql.y:7115 { yyLOCAL = convertStringToInt(yyDollar[2].str) } yyVAL.union = yyLOCAL - case 1439: + case 1440: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:7117 +//line sql.y:7125 { yyLOCAL = &FuncExpr{Name: NewIdentifierCI("if"), Exprs: yyDollar[3].exprsUnion()} } yyVAL.union = yyLOCAL - case 1440: + case 1441: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:7121 +//line sql.y:7129 { yyLOCAL = &FuncExpr{Name: NewIdentifierCI("database"), Exprs: yyDollar[3].exprsUnion()} } yyVAL.union = yyLOCAL - case 1441: + case 1442: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:7125 +//line sql.y:7133 { yyLOCAL = &FuncExpr{Name: NewIdentifierCI("schema"), Exprs: yyDollar[3].exprsUnion()} } yyVAL.union = yyLOCAL - case 1442: + case 1443: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:7129 +//line sql.y:7137 { yyLOCAL = &FuncExpr{Name: NewIdentifierCI("mod"), Exprs: yyDollar[3].exprsUnion()} } yyVAL.union = yyLOCAL - case 1443: + case 1444: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:7133 +//line sql.y:7141 { yyLOCAL = &FuncExpr{Name: NewIdentifierCI("replace"), Exprs: yyDollar[3].exprsUnion()} } yyVAL.union = yyLOCAL - case 1444: + case 1445: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL MatchExprOption -//line sql.y:7139 +//line sql.y:7147 { yyLOCAL = NoOption } yyVAL.union = yyLOCAL - case 1445: + case 1446: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL MatchExprOption -//line sql.y:7143 +//line sql.y:7151 { yyLOCAL = BooleanModeOpt } yyVAL.union = yyLOCAL - case 1446: + case 1447: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL MatchExprOption -//line sql.y:7147 +//line sql.y:7155 { yyLOCAL = NaturalLanguageModeOpt } yyVAL.union = yyLOCAL - case 1447: + case 1448: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL MatchExprOption -//line sql.y:7151 +//line sql.y:7159 { yyLOCAL = NaturalLanguageModeWithQueryExpansionOpt } yyVAL.union = yyLOCAL - case 1448: + case 1449: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL MatchExprOption -//line sql.y:7155 +//line sql.y:7163 { yyLOCAL = QueryExpansionOpt } yyVAL.union = yyLOCAL - case 1449: + case 1450: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:7161 +//line sql.y:7169 { yyVAL.str = string(yyDollar[1].identifierCI.String()) } - case 1450: + case 1451: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:7165 +//line sql.y:7173 { yyVAL.str = string(yyDollar[1].str) } - case 1451: + case 1452: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:7169 +//line sql.y:7177 { yyVAL.str = string(yyDollar[1].str) } - case 1452: + case 1453: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *ConvertType -//line sql.y:7175 +//line sql.y:7183 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1453: + case 1454: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *ConvertType -//line sql.y:7179 +//line sql.y:7187 { yyLOCAL = &ConvertType{Type: string(yyDollar[2].str), Length: ptr.Of(convertStringToInt(yyDollar[4].str))} } yyVAL.union = yyLOCAL - case 1454: + case 1455: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *ConvertType -//line sql.y:7183 +//line sql.y:7191 { yyLOCAL = &ConvertType{Type: string(yyDollar[2].str), Length: ptr.Of(convertStringToInt(yyDollar[4].str))} } yyVAL.union = yyLOCAL - case 1455: + case 1456: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *ConvertType -//line sql.y:7189 +//line sql.y:7197 { yyLOCAL = &ConvertType{Type: string(yyDollar[1].str), Length: yyDollar[2].intPtrUnion()} } yyVAL.union = yyLOCAL - case 1456: + case 1457: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *ConvertType -//line sql.y:7193 +//line sql.y:7201 { yyLOCAL = &ConvertType{Type: string(yyDollar[1].str), Length: yyDollar[2].intPtrUnion(), Charset: yyDollar[3].columnCharset} } yyVAL.union = yyLOCAL - case 1457: + case 1458: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *ConvertType -//line sql.y:7197 +//line sql.y:7205 { yyLOCAL = &ConvertType{Type: string(yyDollar[1].str)} } yyVAL.union = yyLOCAL - case 1458: + case 1459: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *ConvertType -//line sql.y:7201 +//line sql.y:7209 { yyLOCAL = &ConvertType{Type: string(yyDollar[1].str), Length: yyDollar[2].intPtrUnion()} } yyVAL.union = yyLOCAL - case 1459: + case 1460: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *ConvertType -//line sql.y:7205 +//line sql.y:7213 { yyLOCAL = &ConvertType{Type: string(yyDollar[1].str)} yyLOCAL.Length = yyDollar[2].LengthScaleOption.Length yyLOCAL.Scale = yyDollar[2].LengthScaleOption.Scale } yyVAL.union = yyLOCAL - case 1460: + case 1461: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *ConvertType -//line sql.y:7211 +//line sql.y:7219 { yyLOCAL = &ConvertType{Type: string(yyDollar[1].str)} } yyVAL.union = yyLOCAL - case 1461: + case 1462: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *ConvertType -//line sql.y:7215 +//line sql.y:7223 { yyLOCAL = &ConvertType{Type: string(yyDollar[1].str), Length: yyDollar[2].intPtrUnion()} } yyVAL.union = yyLOCAL - case 1462: + case 1463: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *ConvertType -//line sql.y:7219 +//line sql.y:7227 { yyLOCAL = &ConvertType{Type: string(yyDollar[1].str)} } yyVAL.union = yyLOCAL - case 1463: + case 1464: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *ConvertType -//line sql.y:7223 +//line sql.y:7231 { yyLOCAL = &ConvertType{Type: string(yyDollar[1].str)} } yyVAL.union = yyLOCAL - case 1464: + case 1465: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *ConvertType -//line sql.y:7227 +//line sql.y:7235 { yyLOCAL = &ConvertType{Type: string(yyDollar[1].str), Length: yyDollar[2].intPtrUnion()} } yyVAL.union = yyLOCAL - case 1465: + case 1466: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *ConvertType -//line sql.y:7231 +//line sql.y:7239 { yyLOCAL = &ConvertType{Type: string(yyDollar[1].str)} } yyVAL.union = yyLOCAL - case 1466: + case 1467: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *ConvertType -//line sql.y:7235 +//line sql.y:7243 { yyLOCAL = &ConvertType{Type: string(yyDollar[1].str)} } yyVAL.union = yyLOCAL - case 1467: + case 1468: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *ConvertType -//line sql.y:7239 +//line sql.y:7247 { yyLOCAL = &ConvertType{Type: string(yyDollar[1].str), Length: yyDollar[2].intPtrUnion()} } yyVAL.union = yyLOCAL - case 1468: + case 1469: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *ConvertType -//line sql.y:7243 +//line sql.y:7251 { yyLOCAL = &ConvertType{Type: string(yyDollar[1].str)} } yyVAL.union = yyLOCAL - case 1469: + case 1470: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *ConvertType -//line sql.y:7247 +//line sql.y:7255 { yyLOCAL = &ConvertType{Type: string(yyDollar[1].str)} } yyVAL.union = yyLOCAL - case 1470: + case 1471: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL bool -//line sql.y:7253 +//line sql.y:7261 { yyLOCAL = false } yyVAL.union = yyLOCAL - case 1471: + case 1472: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL bool -//line sql.y:7257 +//line sql.y:7265 { yyLOCAL = true } yyVAL.union = yyLOCAL - case 1472: + case 1473: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL Expr -//line sql.y:7262 +//line sql.y:7270 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1473: + case 1474: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Expr -//line sql.y:7266 +//line sql.y:7274 { yyLOCAL = yyDollar[1].exprUnion() } yyVAL.union = yyLOCAL - case 1474: + case 1475: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:7271 +//line sql.y:7279 { yyVAL.str = string("") } - case 1475: + case 1476: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:7275 +//line sql.y:7283 { yyVAL.str = encodeSQLString(yyDollar[2].str) } - case 1476: + case 1477: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []*When -//line sql.y:7281 +//line sql.y:7289 { yyLOCAL = []*When{yyDollar[1].whenUnion()} } yyVAL.union = yyLOCAL - case 1477: + case 1478: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:7285 +//line sql.y:7293 { yySLICE := (*[]*When)(yyIaddr(yyVAL.union)) *yySLICE = append(*yySLICE, yyDollar[2].whenUnion()) } - case 1478: + case 1479: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *When -//line sql.y:7291 +//line sql.y:7299 { yyLOCAL = &When{Cond: yyDollar[2].exprUnion(), Val: yyDollar[4].exprUnion()} } yyVAL.union = yyLOCAL - case 1479: + case 1480: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL Expr -//line sql.y:7296 +//line sql.y:7304 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1480: + case 1481: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Expr -//line sql.y:7300 +//line sql.y:7308 { yyLOCAL = yyDollar[2].exprUnion() } yyVAL.union = yyLOCAL - case 1481: + case 1482: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *ColName -//line sql.y:7306 +//line sql.y:7314 { yyLOCAL = &ColName{Name: yyDollar[1].identifierCI} } yyVAL.union = yyLOCAL - case 1482: + case 1483: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *ColName -//line sql.y:7310 +//line sql.y:7318 { yyLOCAL = &ColName{Name: NewIdentifierCI(string(yyDollar[1].str))} } yyVAL.union = yyLOCAL - case 1483: + case 1484: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *ColName -//line sql.y:7314 +//line sql.y:7322 { yyLOCAL = &ColName{Qualifier: TableName{Name: yyDollar[1].identifierCS}, Name: yyDollar[3].identifierCI} } yyVAL.union = yyLOCAL - case 1484: + case 1485: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *ColName -//line sql.y:7318 +//line sql.y:7326 { yyLOCAL = &ColName{Qualifier: TableName{Qualifier: yyDollar[1].identifierCS, Name: yyDollar[3].identifierCS}, Name: yyDollar[5].identifierCI} } yyVAL.union = yyLOCAL - case 1485: + case 1486: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Expr -//line sql.y:7324 +//line sql.y:7332 { yyLOCAL = yyDollar[1].colNameUnion() } yyVAL.union = yyLOCAL - case 1486: + case 1487: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Expr -//line sql.y:7328 +//line sql.y:7336 { yyLOCAL = &Offset{V: convertStringToInt(yyDollar[1].str)} } yyVAL.union = yyLOCAL - case 1487: + case 1488: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Expr -//line sql.y:7334 +//line sql.y:7342 { // TODO(sougou): Deprecate this construct. if yyDollar[1].identifierCI.Lowered() != "value" { @@ -21418,442 +21472,442 @@ yydefault: yyLOCAL = NewIntLiteral("1") } yyVAL.union = yyLOCAL - case 1488: + case 1489: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Expr -//line sql.y:7343 +//line sql.y:7351 { yyLOCAL = NewIntLiteral(yyDollar[1].str) } yyVAL.union = yyLOCAL - case 1489: + case 1490: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Expr -//line sql.y:7347 +//line sql.y:7355 { yyLOCAL = parseBindVariable(yylex, yyDollar[1].str[1:]) } yyVAL.union = yyLOCAL - case 1490: + case 1491: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *GroupBy -//line sql.y:7352 +//line sql.y:7360 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1491: + case 1492: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *GroupBy -//line sql.y:7356 +//line sql.y:7364 { yyLOCAL = &GroupBy{Exprs: yyDollar[3].exprsUnion(), WithRollup: yyDollar[4].booleanUnion()} } yyVAL.union = yyLOCAL - case 1492: + case 1493: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL bool -//line sql.y:7361 +//line sql.y:7369 { yyLOCAL = false } yyVAL.union = yyLOCAL - case 1493: + case 1494: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL bool -//line sql.y:7365 +//line sql.y:7373 { yyLOCAL = true } yyVAL.union = yyLOCAL - case 1494: + case 1495: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL Expr -//line sql.y:7371 +//line sql.y:7379 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1495: + case 1496: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Expr -//line sql.y:7375 +//line sql.y:7383 { yyLOCAL = yyDollar[2].exprUnion() } yyVAL.union = yyLOCAL - case 1496: + case 1497: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *NamedWindow -//line sql.y:7381 +//line sql.y:7389 { yyLOCAL = &NamedWindow{yyDollar[2].windowDefinitionsUnion()} } yyVAL.union = yyLOCAL - case 1497: + case 1498: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL NamedWindows -//line sql.y:7387 +//line sql.y:7395 { yyLOCAL = NamedWindows{yyDollar[1].namedWindowUnion()} } yyVAL.union = yyLOCAL - case 1498: + case 1499: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:7391 +//line sql.y:7399 { yySLICE := (*NamedWindows)(yyIaddr(yyVAL.union)) *yySLICE = append(*yySLICE, yyDollar[3].namedWindowUnion()) } - case 1499: + case 1500: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL NamedWindows -//line sql.y:7396 +//line sql.y:7404 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1500: + case 1501: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL NamedWindows -//line sql.y:7400 +//line sql.y:7408 { yyLOCAL = yyDollar[1].namedWindowsUnion() } yyVAL.union = yyLOCAL - case 1501: + case 1502: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL OrderBy -//line sql.y:7405 +//line sql.y:7413 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1502: + case 1503: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL OrderBy -//line sql.y:7409 +//line sql.y:7417 { yyLOCAL = yyDollar[1].orderByUnion() } yyVAL.union = yyLOCAL - case 1503: + case 1504: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL OrderBy -//line sql.y:7415 +//line sql.y:7423 { yyLOCAL = yyDollar[3].orderByUnion() } yyVAL.union = yyLOCAL - case 1504: + case 1505: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL OrderBy -//line sql.y:7421 +//line sql.y:7429 { yyLOCAL = OrderBy{yyDollar[1].orderUnion()} } yyVAL.union = yyLOCAL - case 1505: + case 1506: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:7425 +//line sql.y:7433 { yySLICE := (*OrderBy)(yyIaddr(yyVAL.union)) *yySLICE = append(*yySLICE, yyDollar[3].orderUnion()) } - case 1506: + case 1507: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *Order -//line sql.y:7431 +//line sql.y:7439 { yyLOCAL = &Order{Expr: yyDollar[1].exprUnion(), Direction: yyDollar[2].orderDirectionUnion()} } yyVAL.union = yyLOCAL - case 1507: + case 1508: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL OrderDirection -//line sql.y:7436 +//line sql.y:7444 { yyLOCAL = AscOrder } yyVAL.union = yyLOCAL - case 1508: + case 1509: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL OrderDirection -//line sql.y:7440 +//line sql.y:7448 { yyLOCAL = AscOrder } yyVAL.union = yyLOCAL - case 1509: + case 1510: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL OrderDirection -//line sql.y:7444 +//line sql.y:7452 { yyLOCAL = DescOrder } yyVAL.union = yyLOCAL - case 1510: + case 1511: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *Limit -//line sql.y:7449 +//line sql.y:7457 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1511: + case 1512: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *Limit -//line sql.y:7453 +//line sql.y:7461 { yyLOCAL = yyDollar[1].limitUnion() } yyVAL.union = yyLOCAL - case 1512: + case 1513: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *Limit -//line sql.y:7459 +//line sql.y:7467 { yyLOCAL = &Limit{Rowcount: yyDollar[2].exprUnion()} } yyVAL.union = yyLOCAL - case 1513: + case 1514: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *Limit -//line sql.y:7463 +//line sql.y:7471 { yyLOCAL = &Limit{Offset: yyDollar[2].exprUnion(), Rowcount: yyDollar[4].exprUnion()} } yyVAL.union = yyLOCAL - case 1514: + case 1515: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *Limit -//line sql.y:7467 +//line sql.y:7475 { yyLOCAL = &Limit{Offset: yyDollar[4].exprUnion(), Rowcount: yyDollar[2].exprUnion()} } yyVAL.union = yyLOCAL - case 1515: + case 1516: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL []AlterOption -//line sql.y:7472 +//line sql.y:7480 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1516: + case 1517: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL []AlterOption -//line sql.y:7476 +//line sql.y:7484 { yyLOCAL = []AlterOption{yyDollar[1].alterOptionUnion(), yyDollar[2].alterOptionUnion()} } yyVAL.union = yyLOCAL - case 1517: + case 1518: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL []AlterOption -//line sql.y:7480 +//line sql.y:7488 { yyLOCAL = []AlterOption{yyDollar[1].alterOptionUnion(), yyDollar[2].alterOptionUnion()} } yyVAL.union = yyLOCAL - case 1518: + case 1519: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []AlterOption -//line sql.y:7484 +//line sql.y:7492 { yyLOCAL = []AlterOption{yyDollar[1].alterOptionUnion()} } yyVAL.union = yyLOCAL - case 1519: + case 1520: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []AlterOption -//line sql.y:7488 +//line sql.y:7496 { yyLOCAL = []AlterOption{yyDollar[1].alterOptionUnion()} } yyVAL.union = yyLOCAL - case 1520: + case 1521: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL AlterOption -//line sql.y:7495 +//line sql.y:7503 { yyLOCAL = &LockOption{Type: DefaultType} } yyVAL.union = yyLOCAL - case 1521: + case 1522: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL AlterOption -//line sql.y:7499 +//line sql.y:7507 { yyLOCAL = &LockOption{Type: NoneType} } yyVAL.union = yyLOCAL - case 1522: + case 1523: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL AlterOption -//line sql.y:7503 +//line sql.y:7511 { yyLOCAL = &LockOption{Type: SharedType} } yyVAL.union = yyLOCAL - case 1523: + case 1524: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL AlterOption -//line sql.y:7507 +//line sql.y:7515 { yyLOCAL = &LockOption{Type: ExclusiveType} } yyVAL.union = yyLOCAL - case 1524: + case 1525: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL AlterOption -//line sql.y:7513 +//line sql.y:7521 { yyLOCAL = AlgorithmValue(yyDollar[3].str) } yyVAL.union = yyLOCAL - case 1525: + case 1526: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL AlterOption -//line sql.y:7517 +//line sql.y:7525 { yyLOCAL = AlgorithmValue(yyDollar[3].str) } yyVAL.union = yyLOCAL - case 1526: + case 1527: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL AlterOption -//line sql.y:7521 +//line sql.y:7529 { yyLOCAL = AlgorithmValue(yyDollar[3].str) } yyVAL.union = yyLOCAL - case 1527: + case 1528: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL AlterOption -//line sql.y:7525 +//line sql.y:7533 { yyLOCAL = AlgorithmValue(yyDollar[3].str) } yyVAL.union = yyLOCAL - case 1528: + case 1529: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:7530 +//line sql.y:7538 { yyVAL.str = "" } - case 1529: + case 1530: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:7534 +//line sql.y:7542 { yyVAL.str = string(yyDollar[3].str) } - case 1530: + case 1531: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:7538 +//line sql.y:7546 { yyVAL.str = string(yyDollar[3].str) } - case 1531: + case 1532: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:7542 +//line sql.y:7550 { yyVAL.str = string(yyDollar[3].str) } - case 1532: + case 1533: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:7547 +//line sql.y:7555 { yyVAL.str = "" } - case 1533: + case 1534: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:7551 +//line sql.y:7559 { yyVAL.str = yyDollar[3].str } - case 1534: + case 1535: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:7557 +//line sql.y:7565 { yyVAL.str = string(yyDollar[1].str) } - case 1535: + case 1536: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:7561 +//line sql.y:7569 { yyVAL.str = string(yyDollar[1].str) } - case 1536: + case 1537: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:7566 +//line sql.y:7574 { yyVAL.str = "" } - case 1537: + case 1538: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:7570 +//line sql.y:7578 { yyVAL.str = yyDollar[2].str } - case 1538: + case 1539: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:7575 +//line sql.y:7583 { yyVAL.str = "cascaded" } - case 1539: + case 1540: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:7579 +//line sql.y:7587 { yyVAL.str = string(yyDollar[1].str) } - case 1540: + case 1541: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:7583 +//line sql.y:7591 { yyVAL.str = string(yyDollar[1].str) } - case 1541: + case 1542: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *Definer -//line sql.y:7588 +//line sql.y:7596 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1542: + case 1543: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *Definer -//line sql.y:7592 +//line sql.y:7600 { yyLOCAL = yyDollar[3].definerUnion() } yyVAL.union = yyLOCAL - case 1543: + case 1544: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *Definer -//line sql.y:7598 +//line sql.y:7606 { yyLOCAL = &Definer{ Name: string(yyDollar[1].str), } } yyVAL.union = yyLOCAL - case 1544: + case 1545: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *Definer -//line sql.y:7604 +//line sql.y:7612 { yyLOCAL = &Definer{ Name: string(yyDollar[1].str), } } yyVAL.union = yyLOCAL - case 1545: + case 1546: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *Definer -//line sql.y:7610 +//line sql.y:7618 { yyLOCAL = &Definer{ Name: yyDollar[1].str, @@ -21861,433 +21915,433 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1546: + case 1547: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:7619 +//line sql.y:7627 { yyVAL.str = encodeSQLString(yyDollar[1].str) } - case 1547: + case 1548: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:7623 +//line sql.y:7631 { yyVAL.str = formatIdentifier(yyDollar[1].str) } - case 1548: + case 1549: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:7628 +//line sql.y:7636 { yyVAL.str = "" } - case 1549: + case 1550: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:7632 +//line sql.y:7640 { yyVAL.str = formatAddress(yyDollar[1].str) } - case 1550: + case 1551: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Lock -//line sql.y:7638 +//line sql.y:7646 { yyLOCAL = ForUpdateLock } yyVAL.union = yyLOCAL - case 1551: + case 1552: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Lock -//line sql.y:7642 +//line sql.y:7650 { yyLOCAL = ForUpdateLockNoWait } yyVAL.union = yyLOCAL - case 1552: + case 1553: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Lock -//line sql.y:7646 +//line sql.y:7654 { yyLOCAL = ForUpdateLockSkipLocked } yyVAL.union = yyLOCAL - case 1553: + case 1554: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Lock -//line sql.y:7650 +//line sql.y:7658 { yyLOCAL = ForShareLock } yyVAL.union = yyLOCAL - case 1554: + case 1555: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Lock -//line sql.y:7654 +//line sql.y:7662 { yyLOCAL = ForShareLockNoWait } yyVAL.union = yyLOCAL - case 1555: + case 1556: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Lock -//line sql.y:7658 +//line sql.y:7666 { yyLOCAL = ForShareLockSkipLocked } yyVAL.union = yyLOCAL - case 1556: + case 1557: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Lock -//line sql.y:7662 +//line sql.y:7670 { yyLOCAL = ShareModeLock } yyVAL.union = yyLOCAL - case 1557: + case 1558: yyDollar = yyS[yypt-9 : yypt+1] var yyLOCAL *SelectInto -//line sql.y:7668 +//line sql.y:7676 { 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 1558: + case 1559: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *SelectInto -//line sql.y:7672 +//line sql.y:7680 { yyLOCAL = &SelectInto{Type: IntoDumpfile, FileName: encodeSQLString(yyDollar[3].str), Charset: ColumnCharset{}, FormatOption: "", ExportOption: "", Manifest: "", Overwrite: ""} } yyVAL.union = yyLOCAL - case 1559: + case 1560: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *SelectInto -//line sql.y:7676 +//line sql.y:7684 { yyLOCAL = &SelectInto{Type: IntoOutfile, FileName: encodeSQLString(yyDollar[3].str), Charset: yyDollar[4].columnCharset, FormatOption: "", ExportOption: yyDollar[5].str, Manifest: "", Overwrite: ""} } yyVAL.union = yyLOCAL - case 1560: + case 1561: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:7681 +//line sql.y:7689 { yyVAL.str = "" } - case 1561: + case 1562: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:7685 +//line sql.y:7693 { yyVAL.str = " format csv" + yyDollar[3].str } - case 1562: + case 1563: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:7689 +//line sql.y:7697 { yyVAL.str = " format text" + yyDollar[3].str } - case 1563: + case 1564: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:7694 +//line sql.y:7702 { yyVAL.str = "" } - case 1564: + case 1565: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:7698 +//line sql.y:7706 { yyVAL.str = " header" } - case 1565: + case 1566: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:7703 +//line sql.y:7711 { yyVAL.str = "" } - case 1566: + case 1567: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:7707 +//line sql.y:7715 { yyVAL.str = " manifest on" } - case 1567: + case 1568: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:7711 +//line sql.y:7719 { yyVAL.str = " manifest off" } - case 1568: + case 1569: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:7716 +//line sql.y:7724 { yyVAL.str = "" } - case 1569: + case 1570: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:7720 +//line sql.y:7728 { yyVAL.str = " overwrite on" } - case 1570: + case 1571: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:7724 +//line sql.y:7732 { yyVAL.str = " overwrite off" } - case 1571: + case 1572: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:7730 +//line sql.y:7738 { yyVAL.str = yyDollar[1].str + yyDollar[2].str } - case 1572: + case 1573: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:7735 +//line sql.y:7743 { yyVAL.str = "" } - case 1573: + case 1574: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:7739 +//line sql.y:7747 { yyVAL.str = " lines" + yyDollar[2].str } - case 1574: + case 1575: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:7745 +//line sql.y:7753 { yyVAL.str = yyDollar[1].str } - case 1575: + case 1576: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:7749 +//line sql.y:7757 { yyVAL.str = yyDollar[1].str + yyDollar[2].str } - case 1576: + case 1577: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:7755 +//line sql.y:7763 { yyVAL.str = " starting by " + encodeSQLString(yyDollar[3].str) } - case 1577: + case 1578: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:7759 +//line sql.y:7767 { yyVAL.str = " terminated by " + encodeSQLString(yyDollar[3].str) } - case 1578: + case 1579: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:7764 +//line sql.y:7772 { yyVAL.str = "" } - case 1579: + case 1580: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:7768 +//line sql.y:7776 { yyVAL.str = " " + yyDollar[1].str + yyDollar[2].str } - case 1580: + case 1581: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:7774 +//line sql.y:7782 { yyVAL.str = yyDollar[1].str } - case 1581: + case 1582: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:7778 +//line sql.y:7786 { yyVAL.str = yyDollar[1].str + yyDollar[2].str } - case 1582: + case 1583: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:7784 +//line sql.y:7792 { yyVAL.str = " terminated by " + encodeSQLString(yyDollar[3].str) } - case 1583: + case 1584: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:7788 +//line sql.y:7796 { yyVAL.str = yyDollar[1].str + " enclosed by " + encodeSQLString(yyDollar[4].str) } - case 1584: + case 1585: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:7792 +//line sql.y:7800 { yyVAL.str = " escaped by " + encodeSQLString(yyDollar[3].str) } - case 1585: + case 1586: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:7797 +//line sql.y:7805 { yyVAL.str = "" } - case 1586: + case 1587: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:7801 +//line sql.y:7809 { yyVAL.str = " optionally" } - case 1587: + case 1588: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *Insert -//line sql.y:7814 +//line sql.y:7822 { yyLOCAL = &Insert{Rows: yyDollar[2].valuesUnion(), RowAlias: yyDollar[3].rowAliasUnion()} } yyVAL.union = yyLOCAL - case 1588: + case 1589: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *Insert -//line sql.y:7818 +//line sql.y:7826 { yyLOCAL = &Insert{Rows: yyDollar[1].selStmtUnion()} } yyVAL.union = yyLOCAL - case 1589: + case 1590: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *Insert -//line sql.y:7822 +//line sql.y:7830 { yyLOCAL = &Insert{Columns: yyDollar[2].columnsUnion(), Rows: yyDollar[5].valuesUnion(), RowAlias: yyDollar[6].rowAliasUnion()} } yyVAL.union = yyLOCAL - case 1590: + case 1591: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *Insert -//line sql.y:7826 +//line sql.y:7834 { yyLOCAL = &Insert{Columns: []IdentifierCI{}, Rows: yyDollar[4].valuesUnion(), RowAlias: yyDollar[5].rowAliasUnion()} } yyVAL.union = yyLOCAL - case 1591: + case 1592: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *Insert -//line sql.y:7830 +//line sql.y:7838 { yyLOCAL = &Insert{Columns: yyDollar[2].columnsUnion(), Rows: yyDollar[4].selStmtUnion()} } yyVAL.union = yyLOCAL - case 1592: + case 1593: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Columns -//line sql.y:7836 +//line sql.y:7844 { yyLOCAL = Columns{yyDollar[1].identifierCI} } yyVAL.union = yyLOCAL - case 1593: + case 1594: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Columns -//line sql.y:7840 +//line sql.y:7848 { yyLOCAL = Columns{yyDollar[3].identifierCI} } yyVAL.union = yyLOCAL - case 1594: + case 1595: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:7844 +//line sql.y:7852 { yySLICE := (*Columns)(yyIaddr(yyVAL.union)) *yySLICE = append(*yySLICE, yyDollar[3].identifierCI) } - case 1595: + case 1596: yyDollar = yyS[yypt-5 : yypt+1] -//line sql.y:7848 +//line sql.y:7856 { yySLICE := (*Columns)(yyIaddr(yyVAL.union)) *yySLICE = append(*yySLICE, yyDollar[5].identifierCI) } - case 1596: + case 1597: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *RowAlias -//line sql.y:7853 +//line sql.y:7861 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1597: + case 1598: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *RowAlias -//line sql.y:7857 +//line sql.y:7865 { yyLOCAL = &RowAlias{TableName: yyDollar[2].identifierCS} } yyVAL.union = yyLOCAL - case 1598: + case 1599: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *RowAlias -//line sql.y:7861 +//line sql.y:7869 { yyLOCAL = &RowAlias{TableName: yyDollar[2].identifierCS, Columns: yyDollar[4].columnsUnion()} } yyVAL.union = yyLOCAL - case 1599: + case 1600: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL UpdateExprs -//line sql.y:7866 +//line sql.y:7874 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1600: + case 1601: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL UpdateExprs -//line sql.y:7870 +//line sql.y:7878 { yyLOCAL = yyDollar[5].updateExprsUnion() } yyVAL.union = yyLOCAL - case 1601: + case 1602: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Values -//line sql.y:7876 +//line sql.y:7884 { yyLOCAL = Values{yyDollar[1].valTupleUnion()} } yyVAL.union = yyLOCAL - case 1602: + case 1603: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:7880 +//line sql.y:7888 { yySLICE := (*Values)(yyIaddr(yyVAL.union)) *yySLICE = append(*yySLICE, yyDollar[3].valTupleUnion()) } - case 1603: + case 1604: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL ValTuple -//line sql.y:7886 +//line sql.y:7894 { yyLOCAL = yyDollar[1].valTupleUnion() } yyVAL.union = yyLOCAL - case 1604: + case 1605: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL ValTuple -//line sql.y:7890 +//line sql.y:7898 { yyLOCAL = ValTuple{} } yyVAL.union = yyLOCAL - case 1605: + case 1606: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL ValTuple -//line sql.y:7896 +//line sql.y:7904 { yyLOCAL = ValTuple(yyDollar[2].exprsUnion()) } yyVAL.union = yyLOCAL - case 1606: + case 1607: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL ValTuple -//line sql.y:7900 +//line sql.y:7908 { yyLOCAL = ValTuple(yyDollar[3].exprsUnion()) } yyVAL.union = yyLOCAL - case 1607: + case 1608: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Expr -//line sql.y:7905 +//line sql.y:7913 { if len(yyDollar[1].valTupleUnion()) == 1 { yyLOCAL = yyDollar[1].valTupleUnion()[0] @@ -22296,300 +22350,300 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1608: + case 1609: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL UpdateExprs -//line sql.y:7915 +//line sql.y:7923 { yyLOCAL = UpdateExprs{yyDollar[1].updateExprUnion()} } yyVAL.union = yyLOCAL - case 1609: + case 1610: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:7919 +//line sql.y:7927 { yySLICE := (*UpdateExprs)(yyIaddr(yyVAL.union)) *yySLICE = append(*yySLICE, yyDollar[3].updateExprUnion()) } - case 1610: + case 1611: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *UpdateExpr -//line sql.y:7925 +//line sql.y:7933 { yyLOCAL = &UpdateExpr{Name: yyDollar[1].colNameUnion(), Expr: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1612: + case 1613: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:7932 +//line sql.y:7940 { yyVAL.str = "charset" } - case 1615: + case 1616: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Expr -//line sql.y:7942 +//line sql.y:7950 { yyLOCAL = NewStrLiteral(yyDollar[1].identifierCI.String()) } yyVAL.union = yyLOCAL - case 1616: + case 1617: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Expr -//line sql.y:7946 +//line sql.y:7954 { yyLOCAL = NewStrLiteral(yyDollar[1].str) } yyVAL.union = yyLOCAL - case 1617: + case 1618: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Expr -//line sql.y:7950 +//line sql.y:7958 { yyLOCAL = &Default{} } yyVAL.union = yyLOCAL - case 1620: + case 1621: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL bool -//line sql.y:7959 +//line sql.y:7967 { yyLOCAL = false } yyVAL.union = yyLOCAL - case 1621: + case 1622: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL bool -//line sql.y:7961 +//line sql.y:7969 { yyLOCAL = true } yyVAL.union = yyLOCAL - case 1622: + case 1623: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL bool -//line sql.y:7964 +//line sql.y:7972 { yyLOCAL = false } yyVAL.union = yyLOCAL - case 1623: + case 1624: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL bool -//line sql.y:7966 +//line sql.y:7974 { yyLOCAL = true } yyVAL.union = yyLOCAL - case 1624: + case 1625: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL bool -//line sql.y:7969 +//line sql.y:7977 { yyLOCAL = false } yyVAL.union = yyLOCAL - case 1625: + case 1626: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL bool -//line sql.y:7971 +//line sql.y:7979 { yyLOCAL = true } yyVAL.union = yyLOCAL - case 1626: + case 1627: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL Ignore -//line sql.y:7974 +//line sql.y:7982 { yyLOCAL = false } yyVAL.union = yyLOCAL - case 1627: + case 1628: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Ignore -//line sql.y:7976 +//line sql.y:7984 { yyLOCAL = true } yyVAL.union = yyLOCAL - case 1628: + case 1629: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:7979 +//line sql.y:7987 { yyVAL.empty = struct{}{} } - case 1629: + case 1630: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:7981 +//line sql.y:7989 { yyVAL.empty = struct{}{} } - case 1630: + case 1631: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:7983 +//line sql.y:7991 { yyVAL.empty = struct{}{} } - case 1631: + case 1632: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL Statement -//line sql.y:7987 +//line sql.y:7995 { yyLOCAL = &CallProc{Name: yyDollar[2].tableName, Params: yyDollar[4].exprsUnion()} } yyVAL.union = yyLOCAL - case 1632: + case 1633: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL Exprs -//line sql.y:7992 +//line sql.y:8000 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1633: + case 1634: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Exprs -//line sql.y:7996 +//line sql.y:8004 { yyLOCAL = yyDollar[1].exprsUnion() } yyVAL.union = yyLOCAL - case 1634: + case 1635: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL []*IndexOption -//line sql.y:8001 +//line sql.y:8009 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1635: + case 1636: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []*IndexOption -//line sql.y:8003 +//line sql.y:8011 { yyLOCAL = []*IndexOption{yyDollar[1].indexOptionUnion()} } yyVAL.union = yyLOCAL - case 1636: + case 1637: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *IndexOption -//line sql.y:8007 +//line sql.y:8015 { yyLOCAL = &IndexOption{Name: string(yyDollar[1].str), String: string(yyDollar[2].identifierCI.String())} } yyVAL.union = yyLOCAL - case 1637: + case 1638: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:8013 +//line sql.y:8021 { yyVAL.identifierCI = yyDollar[1].identifierCI } - case 1638: + case 1639: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:8017 +//line sql.y:8025 { yyVAL.identifierCI = NewIdentifierCI(string(yyDollar[1].str)) } - case 1640: + case 1641: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:8024 +//line sql.y:8032 { yyVAL.identifierCI = NewIdentifierCI(string(yyDollar[1].str)) } - case 1641: + case 1642: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:8030 +//line sql.y:8038 { yyVAL.identifierCS = NewIdentifierCS(string(yyDollar[1].str)) } - case 1642: + case 1643: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:8034 +//line sql.y:8042 { yyVAL.identifierCS = NewIdentifierCS(string(yyDollar[1].str)) } - case 1643: + case 1644: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:8040 +//line sql.y:8048 { yyVAL.identifierCS = NewIdentifierCS("") } - case 1644: + case 1645: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:8044 +//line sql.y:8052 { yyVAL.identifierCS = yyDollar[1].identifierCS } - case 1646: + case 1647: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:8051 +//line sql.y:8059 { yyVAL.identifierCS = NewIdentifierCS(string(yyDollar[1].str)) } - case 1647: + case 1648: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Statement -//line sql.y:8057 +//line sql.y:8065 { yyLOCAL = &Kill{Type: yyDollar[2].killTypeUnion(), ProcesslistID: convertStringToUInt64(yyDollar[3].str)} } yyVAL.union = yyLOCAL - case 1648: + case 1649: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL KillType -//line sql.y:8063 +//line sql.y:8071 { yyLOCAL = ConnectionType } yyVAL.union = yyLOCAL - case 1649: + case 1650: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL KillType -//line sql.y:8067 +//line sql.y:8075 { yyLOCAL = ConnectionType } yyVAL.union = yyLOCAL - case 1650: + case 1651: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL KillType -//line sql.y:8071 +//line sql.y:8079 { yyLOCAL = QueryType } yyVAL.union = yyLOCAL - case 2281: + case 2283: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:8730 +//line sql.y:8739 { } - case 2282: + case 2284: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:8735 +//line sql.y:8744 { } - case 2283: + case 2285: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:8739 +//line sql.y:8748 { skipToEnd(yylex) } - case 2284: + case 2286: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:8744 +//line sql.y:8753 { skipToEnd(yylex) } - case 2285: + case 2287: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:8748 +//line sql.y:8757 { skipToEnd(yylex) } - case 2286: + case 2288: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:8752 +//line sql.y:8761 { skipToEnd(yylex) } diff --git a/go/vt/sqlparser/sql.y b/go/vt/sqlparser/sql.y index 18db4d2eb08..bf1a793e7e8 100644 --- a/go/vt/sqlparser/sql.y +++ b/go/vt/sqlparser/sql.y @@ -329,7 +329,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 FORCE_CUTOVER EXPIRE RATIO +%token VITESS_MIGRATION CANCEL RETRY LAUNCH COMPLETE CLEANUP THROTTLE UNTHROTTLE FORCE_CUTOVER CUTOVER_THRESHOLD EXPIRE RATIO // Throttler tokens %token VITESS_THROTTLER @@ -3407,6 +3407,14 @@ alter_statement: Type: ForceCutOverAllMigrationType, } } +| ALTER comment_opt VITESS_MIGRATION STRING CUTOVER_THRESHOLD STRING + { + $$ = &AlterMigration{ + Type: SetCutOverThresholdMigrationType, + UUID: string($4), + Threshold: $6, + } + } partitions_options_opt: { @@ -8301,6 +8309,7 @@ non_reserved_keyword: | COUNT %prec FUNCTION_CALL_NON_KEYWORD | CSV | CURRENT +| CUTOVER_THRESHOLD | DATA | DATE %prec STRING_TYPE_PREFIX_NON_KEYWORD | DATE_ADD %prec FUNCTION_CALL_NON_KEYWORD diff --git a/go/vt/vitessdriver/convert.go b/go/vt/vitessdriver/convert.go index 7ba95db4147..aa8bcedc7ee 100644 --- a/go/vt/vitessdriver/convert.go +++ b/go/vt/vitessdriver/convert.go @@ -43,10 +43,8 @@ func (cv *converter) ToNative(v sqltypes.Value) (any, error) { return v.ToUint64() case v.IsFloat(): return v.ToFloat64() - case v.Type() == sqltypes.Datetime, v.Type() == sqltypes.Timestamp: - return datetimeToNative(v, cv.location) - case v.Type() == sqltypes.Date: - return dateToNative(v, cv.location) + case v.Type() == sqltypes.Datetime, v.Type() == sqltypes.Timestamp, v.Type() == sqltypes.Date: + return v.ToTimeInLocation(cv.location) case v.IsQuoted() || v.Type() == sqltypes.Bit || v.Type() == sqltypes.Decimal: out, err = v.ToBytes() case v.Type() == sqltypes.Expression: diff --git a/go/vt/vitessdriver/time.go b/go/vt/vitessdriver/time.go index 70ec2d679ae..c6526197d9d 100644 --- a/go/vt/vitessdriver/time.go +++ b/go/vt/vitessdriver/time.go @@ -17,83 +17,12 @@ limitations under the License. package vitessdriver import ( - "errors" "time" "vitess.io/vitess/go/sqltypes" ) -// ErrInvalidTime is returned when we fail to parse a datetime -// string from MySQL. This should never happen unless things are -// seriously messed up. -var ErrInvalidTime = errors.New("invalid MySQL time string") - var isoTimeFormat = "2006-01-02 15:04:05.999999" -var isoNullTime = "0000-00-00 00:00:00.000000" -var isoTimeLength = len(isoTimeFormat) - -// parseISOTime pases a time string in MySQL's textual datetime format. -// This is very similar to ISO8601, with some differences: -// -// - There is no T separator between the date and time sections; -// a space is used instead. -// - There is never a timezone section in the string, as these datetimes -// are not timezone-aware. There isn't a Z value for UTC times for -// the same reason. -// -// Note that this function can handle both DATE (which should _always_ have -// a length of 10) and DATETIME strings (which have a variable length, 18+ -// depending on the number of decimal sub-second places). -// -// Also note that this function handles the case where MySQL returns a NULL -// time (with a string where all sections are zeroes) by returning a zeroed -// out time.Time object. NULL time strings are not considered a parsing error. -// -// See: isoTimeFormat -func parseISOTime(tstr string, loc *time.Location, minLen, maxLen int) (t time.Time, err error) { - tlen := len(tstr) - if tlen < minLen || tlen > maxLen { - err = ErrInvalidTime - return - } - - if tstr == isoNullTime[:tlen] { - // This is what MySQL would send when the date is NULL, - // so return an empty time.Time instead. - // This is not a parsing error - return - } - - if loc == nil { - loc = time.UTC - } - - // Since the time format returned from MySQL never has a Timezone - // section, ParseInLocation will initialize the time.Time struct - // with the default `loc` we're passing here. - return time.ParseInLocation(isoTimeFormat[:tlen], tstr, loc) -} - -// datetimeToNative converts a Datetime Value into a time.Time -func datetimeToNative(v sqltypes.Value, loc *time.Location) (time.Time, error) { - // Valid format string offsets for a DATETIME - // |DATETIME |19+ - // |------------------|------| - // "2006-01-02 15:04:05.999999" - return parseISOTime(v.ToString(), loc, 19, isoTimeLength) -} - -// dateToNative converts a Date Value into a time.Time. -// Note that there's no specific type in the Go stdlib to represent -// dates without time components, so the returned Time will have -// their hours/mins/seconds zeroed out. -func dateToNative(v sqltypes.Value, loc *time.Location) (time.Time, error) { - // Valid format string offsets for a DATE - // |DATE |10 - // |---------| - // "2006-01-02 00:00:00.000000" - return parseISOTime(v.ToString(), loc, 10, 10) -} // NewDatetime builds a Datetime Value func NewDatetime(t time.Time, defaultLoc *time.Location) sqltypes.Value { diff --git a/go/vt/vitessdriver/time_test.go b/go/vt/vitessdriver/time_test.go deleted file mode 100644 index 949d8f43354..00000000000 --- a/go/vt/vitessdriver/time_test.go +++ /dev/null @@ -1,175 +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 vitessdriver - -import ( - "reflect" - "testing" - "time" - - "vitess.io/vitess/go/sqltypes" -) - -var randomLocation = time.FixedZone("Nowhere", 3*60*60) - -func DatetimeValue(str string) sqltypes.Value { - return sqltypes.TestValue(sqltypes.Datetime, str) -} - -func DateValue(str string) sqltypes.Value { - return sqltypes.TestValue(sqltypes.Date, str) -} - -func TestDatetimeToNative(t *testing.T) { - - tcases := []struct { - val sqltypes.Value - loc *time.Location - out time.Time - err bool - }{{ - val: DatetimeValue("1899-08-24 17:20:00"), - out: time.Date(1899, 8, 24, 17, 20, 0, 0, time.UTC), - }, { - val: DatetimeValue("1952-03-11 01:02:03"), - loc: time.Local, - out: time.Date(1952, 3, 11, 1, 2, 3, 0, time.Local), - }, { - val: DatetimeValue("1952-03-11 01:02:03"), - loc: randomLocation, - out: time.Date(1952, 3, 11, 1, 2, 3, 0, randomLocation), - }, { - val: DatetimeValue("1952-03-11 01:02:03"), - loc: time.UTC, - out: time.Date(1952, 3, 11, 1, 2, 3, 0, time.UTC), - }, { - val: DatetimeValue("1899-08-24 17:20:00.000000"), - out: time.Date(1899, 8, 24, 17, 20, 0, 0, time.UTC), - }, { - val: DatetimeValue("1899-08-24 17:20:00.000001"), - out: time.Date(1899, 8, 24, 17, 20, 0, int(1*time.Microsecond), time.UTC), - }, { - val: DatetimeValue("1899-08-24 17:20:00.123456"), - out: time.Date(1899, 8, 24, 17, 20, 0, int(123456*time.Microsecond), time.UTC), - }, { - val: DatetimeValue("1899-08-24 17:20:00.222"), - out: time.Date(1899, 8, 24, 17, 20, 0, int(222*time.Millisecond), time.UTC), - }, { - val: DatetimeValue("1899-08-24 17:20:00.1234567"), - err: true, - }, { - val: DatetimeValue("1899-08-24 17:20:00.1"), - out: time.Date(1899, 8, 24, 17, 20, 0, int(100*time.Millisecond), time.UTC), - }, { - val: DatetimeValue("0000-00-00 00:00:00"), - out: time.Time{}, - }, { - val: DatetimeValue("0000-00-00 00:00:00.0"), - out: time.Time{}, - }, { - val: DatetimeValue("0000-00-00 00:00:00.000"), - out: time.Time{}, - }, { - val: DatetimeValue("0000-00-00 00:00:00.000000"), - out: time.Time{}, - }, { - val: DatetimeValue("0000-00-00 00:00:00.0000000"), - err: true, - }, { - val: DatetimeValue("1899-08-24T17:20:00.000000"), - err: true, - }, { - val: DatetimeValue("1899-02-31 17:20:00.000000"), - err: true, - }, { - val: DatetimeValue("1899-08-24 17:20:00."), - out: time.Date(1899, 8, 24, 17, 20, 0, 0, time.UTC), - }, { - val: DatetimeValue("0000-00-00 00:00:00.000001"), - err: true, - }, { - val: DatetimeValue("1899-08-24 17:20:00 +02:00"), - err: true, - }, { - val: DatetimeValue("1899-08-24"), - err: true, - }, { - val: DatetimeValue("This is not a valid timestamp"), - err: true, - }} - - for _, tcase := range tcases { - got, err := datetimeToNative(tcase.val, tcase.loc) - if tcase.err && err == nil { - t.Errorf("datetimeToNative(%v, %#v) succeeded; expected error", tcase.val, tcase.loc) - } - if !tcase.err && err != nil { - t.Errorf("datetimeToNative(%v, %#v) failed: %v", tcase.val, tcase.loc, err) - } - if !reflect.DeepEqual(got, tcase.out) { - t.Errorf("datetimeToNative(%v, %#v): %v, want %v", tcase.val, tcase.loc, got, tcase.out) - } - } -} - -func TestDateToNative(t *testing.T) { - tcases := []struct { - val sqltypes.Value - loc *time.Location - out time.Time - err bool - }{{ - val: DateValue("1899-08-24"), - out: time.Date(1899, 8, 24, 0, 0, 0, 0, time.UTC), - }, { - val: DateValue("1952-03-11"), - loc: time.Local, - out: time.Date(1952, 3, 11, 0, 0, 0, 0, time.Local), - }, { - val: DateValue("1952-03-11"), - loc: randomLocation, - out: time.Date(1952, 3, 11, 0, 0, 0, 0, randomLocation), - }, { - val: DateValue("0000-00-00"), - out: time.Time{}, - }, { - val: DateValue("1899-02-31"), - err: true, - }, { - val: DateValue("1899-08-24 17:20:00"), - err: true, - }, { - val: DateValue("0000-00-00 00:00:00"), - err: true, - }, { - val: DateValue("This is not a valid timestamp"), - err: true, - }} - - for _, tcase := range tcases { - got, err := dateToNative(tcase.val, tcase.loc) - if tcase.err && err == nil { - t.Errorf("dateToNative(%v, %#v) succeeded; expected error", tcase.val, tcase.loc) - } - if !tcase.err && err != nil { - t.Errorf("dateToNative(%v, %#v) failed: %v", tcase.val, tcase.loc, err) - } - if !reflect.DeepEqual(got, tcase.out) { - t.Errorf("dateToNative(%v, %#v): %v, want %v", tcase.val, tcase.loc, got, tcase.out) - } - } -} diff --git a/go/vt/vtadmin/api.go b/go/vt/vtadmin/api.go index cef8816504a..4f91459d9ed 100644 --- a/go/vt/vtadmin/api.go +++ b/go/vt/vtadmin/api.go @@ -59,6 +59,7 @@ import ( "vitess.io/vitess/go/vt/vtadmin/rbac" "vitess.io/vitess/go/vt/vtadmin/sort" "vitess.io/vitess/go/vt/vtadmin/vtadminproto" + "vitess.io/vitess/go/vt/vtctl/grpcvtctldserver" "vitess.io/vitess/go/vt/vtctl/workflow" "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vterrors" @@ -488,6 +489,31 @@ func (api *API) ApplySchema(ctx context.Context, req *vtadminpb.ApplySchemaReque return nil, err } + // Parser with default options. New() itself initializes with default MySQL version. + parser, err := sqlparser.New(sqlparser.Options{ + TruncateUILen: 512, + TruncateErrLen: 0, + }) + if err != nil { + return nil, err + } + + // Split the sql statement received from request. + sqlParts, err := parser.SplitStatementToPieces(req.Sql) + if err != nil { + return nil, err + } + + req.Request.Sql = sqlParts + + // Set the callerID if not empty. + if req.CallerId != "" { + req.Request.CallerId = &vtrpcpb.CallerID{Principal: req.CallerId} + } + + // Set the default wait replicas timeout. + req.Request.WaitReplicasTimeout = protoutil.DurationToProto(grpcvtctldserver.DefaultWaitReplicasTimeout) + return c.ApplySchema(ctx, req.Request) } diff --git a/go/vt/vtadmin/http/schema_migrations.go b/go/vt/vtadmin/http/schema_migrations.go index e0207989648..3da6026fe9f 100644 --- a/go/vt/vtadmin/http/schema_migrations.go +++ b/go/vt/vtadmin/http/schema_migrations.go @@ -34,19 +34,26 @@ 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 { + var body struct { + Sql string `json:"sql"` + CallerId string `json:"caller_id"` + Request vtctldatapb.ApplySchemaRequest `json:"request"` + } + + if err := decoder.Decode(&body); err != nil { return NewJSONResponse(nil, &errors.BadRequest{ Err: err, }) } vars := mux.Vars(r.Request) - req.Keyspace = vars["keyspace"] + body.Request.Keyspace = vars["keyspace"] resp, err := api.server.ApplySchema(ctx, &vtadminpb.ApplySchemaRequest{ ClusterId: vars["cluster_id"], - Request: &req, + Sql: body.Sql, + CallerId: body.CallerId, + Request: &body.Request, }) return NewJSONResponse(resp, err) diff --git a/go/vt/vtctl/grpcvtctldserver/server.go b/go/vt/vtctl/grpcvtctldserver/server.go index e280a410e02..c3dc22d21b4 100644 --- a/go/vt/vtctl/grpcvtctldserver/server.go +++ b/go/vt/vtctl/grpcvtctldserver/server.go @@ -303,7 +303,9 @@ func (s *VtctldServer) ApplySchema(ctx context.Context, req *vtctldatapb.ApplySc } for _, shard := range execResult.SuccessShards { - resp.RowsAffectedByShard[shard.Shard] = shard.Result.RowsAffected + for _, result := range shard.Results { + resp.RowsAffectedByShard[shard.Shard] += result.RowsAffected + } } return resp, err @@ -2096,14 +2098,12 @@ func (s *VtctldServer) UpdateThrottlerConfig(ctx context.Context, req *vtctldata throttlerConfig.CheckAsCheckSelf = false } if req.ThrottledApp != nil && req.ThrottledApp.Name != "" { - // TODO(shlomi) in v22: replace the following line with the commented out block - throttlerConfig.ThrottledApps[req.ThrottledApp.Name] = req.ThrottledApp - // timeNow := time.Now() - // if protoutil.TimeFromProto(req.ThrottledApp.ExpiresAt).After(timeNow) { - // throttlerConfig.ThrottledApps[req.ThrottledApp.Name] = req.ThrottledApp - // } else { - // delete(throttlerConfig.ThrottledApps, req.ThrottledApp.Name) - // } + timeNow := time.Now() + if protoutil.TimeFromProto(req.ThrottledApp.ExpiresAt).After(timeNow) { + throttlerConfig.ThrottledApps[req.ThrottledApp.Name] = req.ThrottledApp + } else { + delete(throttlerConfig.ThrottledApps, req.ThrottledApp.Name) + } } return throttlerConfig } diff --git a/go/vt/vtctl/grpcvtctldserver/testutil/test_tmclient.go b/go/vt/vtctl/grpcvtctldserver/testutil/test_tmclient.go index b9c83c3658d..768fae5bff4 100644 --- a/go/vt/vtctl/grpcvtctldserver/testutil/test_tmclient.go +++ b/go/vt/vtctl/grpcvtctldserver/testutil/test_tmclient.go @@ -188,6 +188,8 @@ type TabletManagerClient struct { EventJitter time.Duration ErrorAfter time.Duration } + // Backing Up - keyed by tablet alias. + TabletsBackupState map[string]bool // keyed by tablet alias. ChangeTagsResult map[string]struct { Response *tabletmanagerdatapb.ChangeTagsResponse @@ -1080,6 +1082,9 @@ func (fake *TabletManagerClient) ReplicationStatus(ctx context.Context, tablet * } if result, ok := fake.ReplicationStatusResults[key]; ok { + if _, ok = fake.TabletsBackupState[key]; ok { + result.Position.BackupRunning = fake.TabletsBackupState[key] + } return result.Position, result.Error } diff --git a/go/vt/vtctl/reparentutil/emergency_reparenter.go b/go/vt/vtctl/reparentutil/emergency_reparenter.go index ef30f48e8ac..70faf8958c7 100644 --- a/go/vt/vtctl/reparentutil/emergency_reparenter.go +++ b/go/vt/vtctl/reparentutil/emergency_reparenter.go @@ -258,7 +258,7 @@ func (erp *EmergencyReparenter) reparentShardLocked(ctx context.Context, ev *eve // 2. Remove the tablets with the Must_not promote rule // 3. Remove cross-cell tablets if PreventCrossCellPromotion is specified // Our final primary candidate MUST belong to this list of valid candidates - validCandidateTablets, err = erp.filterValidCandidates(validCandidateTablets, stoppedReplicationSnapshot.reachableTablets, prevPrimary, opts) + validCandidateTablets, err = erp.filterValidCandidates(validCandidateTablets, stoppedReplicationSnapshot.reachableTablets, stoppedReplicationSnapshot.tabletsBackupState, prevPrimary, opts) if err != nil { return err } @@ -737,9 +737,12 @@ func (erp *EmergencyReparenter) identifyPrimaryCandidate( return nil, vterrors.Errorf(vtrpc.Code_INTERNAL, "unreachable - did not find a valid primary candidate even though the valid candidate list was non-empty") } -// filterValidCandidates filters valid tablets, keeping only the ones which can successfully be promoted without any constraint failures and can make forward progress on being promoted -func (erp *EmergencyReparenter) filterValidCandidates(validTablets []*topodatapb.Tablet, tabletsReachable []*topodatapb.Tablet, prevPrimary *topodatapb.Tablet, opts EmergencyReparentOptions) ([]*topodatapb.Tablet, error) { +// filterValidCandidates filters valid tablets, keeping only the ones which can successfully be promoted without any +// constraint failures and can make forward progress on being promoted. It will filter out candidates taking backups +// if possible. +func (erp *EmergencyReparenter) filterValidCandidates(validTablets []*topodatapb.Tablet, tabletsReachable []*topodatapb.Tablet, tabletsBackupState map[string]bool, prevPrimary *topodatapb.Tablet, opts EmergencyReparentOptions) ([]*topodatapb.Tablet, error) { var restrictedValidTablets []*topodatapb.Tablet + var notPreferredValidTablets []*topodatapb.Tablet for _, tablet := range validTablets { tabletAliasStr := topoproto.TabletAliasString(tablet.Alias) // Remove tablets which have MustNot promote rule since they must never be promoted @@ -766,9 +769,20 @@ func (erp *EmergencyReparenter) filterValidCandidates(validTablets []*topodatapb } continue } - restrictedValidTablets = append(restrictedValidTablets, tablet) + // Put candidates that are running a backup in a separate list + backingUp, ok := tabletsBackupState[tabletAliasStr] + if ok && backingUp { + erp.logger.Infof("Setting %s in list of valid candidates taking a backup", tabletAliasStr) + notPreferredValidTablets = append(notPreferredValidTablets, tablet) + } else { + restrictedValidTablets = append(restrictedValidTablets, tablet) + } + } + if len(restrictedValidTablets) > 0 { + return restrictedValidTablets, nil } - return restrictedValidTablets, nil + + return notPreferredValidTablets, nil } // findErrantGTIDs tries to find errant GTIDs for the valid candidates and returns the updated list of valid candidates. diff --git a/go/vt/vtctl/reparentutil/emergency_reparenter_test.go b/go/vt/vtctl/reparentutil/emergency_reparenter_test.go index ea6e768d036..3669c34dc11 100644 --- a/go/vt/vtctl/reparentutil/emergency_reparenter_test.go +++ b/go/vt/vtctl/reparentutil/emergency_reparenter_test.go @@ -4463,27 +4463,55 @@ func TestEmergencyReparenter_filterValidCandidates(t *testing.T) { } ) allTablets := []*topodatapb.Tablet{primaryTablet, replicaTablet, rdonlyTablet, replicaCrossCellTablet, rdonlyCrossCellTablet} + noTabletsTakingBackup := map[string]bool{ + topoproto.TabletAliasString(primaryTablet.Alias): false, topoproto.TabletAliasString(replicaTablet.Alias): false, + topoproto.TabletAliasString(rdonlyTablet.Alias): false, topoproto.TabletAliasString(replicaCrossCellTablet.Alias): false, + topoproto.TabletAliasString(rdonlyCrossCellTablet.Alias): false, + } + replicaTakingBackup := map[string]bool{ + topoproto.TabletAliasString(primaryTablet.Alias): false, topoproto.TabletAliasString(replicaTablet.Alias): true, + topoproto.TabletAliasString(rdonlyTablet.Alias): false, topoproto.TabletAliasString(replicaCrossCellTablet.Alias): false, + topoproto.TabletAliasString(rdonlyCrossCellTablet.Alias): false, + } tests := []struct { - name string - durability string - validTablets []*topodatapb.Tablet - tabletsReachable []*topodatapb.Tablet - prevPrimary *topodatapb.Tablet - opts EmergencyReparentOptions - filteredTablets []*topodatapb.Tablet - errShouldContain string + name string + durability string + validTablets []*topodatapb.Tablet + tabletsReachable []*topodatapb.Tablet + tabletsTakingBackup map[string]bool + prevPrimary *topodatapb.Tablet + opts EmergencyReparentOptions + filteredTablets []*topodatapb.Tablet + errShouldContain string }{ { - name: "filter must not", - durability: "none", - validTablets: allTablets, - tabletsReachable: allTablets, - filteredTablets: []*topodatapb.Tablet{primaryTablet, replicaTablet, replicaCrossCellTablet}, + name: "filter must not", + durability: "none", + validTablets: allTablets, + tabletsReachable: allTablets, + tabletsTakingBackup: noTabletsTakingBackup, + filteredTablets: []*topodatapb.Tablet{primaryTablet, replicaTablet, replicaCrossCellTablet}, }, { - name: "filter cross cell", - durability: "none", - validTablets: allTablets, - tabletsReachable: allTablets, + name: "host taking backup must not be on the list when there are other candidates", + durability: "none", + validTablets: allTablets, + tabletsReachable: []*topodatapb.Tablet{replicaTablet, replicaCrossCellTablet, rdonlyTablet, rdonlyCrossCellTablet}, + tabletsTakingBackup: replicaTakingBackup, + filteredTablets: []*topodatapb.Tablet{replicaCrossCellTablet}, + }, { + name: "host taking backup must be the only one on the list when there are no other candidates", + durability: "none", + validTablets: allTablets, + tabletsReachable: []*topodatapb.Tablet{replicaTablet, rdonlyTablet, rdonlyCrossCellTablet}, + tabletsTakingBackup: replicaTakingBackup, + filteredTablets: []*topodatapb.Tablet{replicaTablet}, + }, { + name: "filter cross cell", + durability: "none", + validTablets: allTablets, + tabletsReachable: allTablets, + tabletsTakingBackup: noTabletsTakingBackup, + prevPrimary: &topodatapb.Tablet{ Alias: &topodatapb.TabletAlias{ Cell: "zone-1", @@ -4494,11 +4522,12 @@ func TestEmergencyReparenter_filterValidCandidates(t *testing.T) { }, filteredTablets: []*topodatapb.Tablet{primaryTablet, replicaTablet}, }, { - name: "filter establish", - durability: "cross_cell", - validTablets: []*topodatapb.Tablet{primaryTablet, replicaTablet}, - tabletsReachable: []*topodatapb.Tablet{primaryTablet, replicaTablet, rdonlyTablet, rdonlyCrossCellTablet}, - filteredTablets: nil, + name: "filter establish", + durability: "cross_cell", + validTablets: []*topodatapb.Tablet{primaryTablet, replicaTablet}, + tabletsReachable: []*topodatapb.Tablet{primaryTablet, replicaTablet, rdonlyTablet, rdonlyCrossCellTablet}, + tabletsTakingBackup: noTabletsTakingBackup, + filteredTablets: nil, }, { name: "filter mixed", durability: "cross_cell", @@ -4510,34 +4539,38 @@ func TestEmergencyReparenter_filterValidCandidates(t *testing.T) { opts: EmergencyReparentOptions{ PreventCrossCellPromotion: true, }, - validTablets: allTablets, - tabletsReachable: allTablets, - filteredTablets: []*topodatapb.Tablet{replicaCrossCellTablet}, + validTablets: allTablets, + tabletsReachable: allTablets, + tabletsTakingBackup: noTabletsTakingBackup, + filteredTablets: []*topodatapb.Tablet{replicaCrossCellTablet}, }, { - name: "error - requested primary must not", - durability: "none", - validTablets: allTablets, - tabletsReachable: allTablets, + name: "error - requested primary must not", + durability: "none", + validTablets: allTablets, + tabletsReachable: allTablets, + tabletsTakingBackup: noTabletsTakingBackup, opts: EmergencyReparentOptions{ NewPrimaryAlias: rdonlyTablet.Alias, }, errShouldContain: "proposed primary zone-1-0000000003 has a must not promotion rule", }, { - name: "error - requested primary not in same cell", - durability: "none", - validTablets: allTablets, - tabletsReachable: allTablets, - prevPrimary: primaryTablet, + name: "error - requested primary not in same cell", + durability: "none", + validTablets: allTablets, + tabletsReachable: allTablets, + tabletsTakingBackup: noTabletsTakingBackup, + prevPrimary: primaryTablet, opts: EmergencyReparentOptions{ PreventCrossCellPromotion: true, NewPrimaryAlias: replicaCrossCellTablet.Alias, }, errShouldContain: "proposed primary zone-2-0000000002 is is a different cell as the previous primary", }, { - name: "error - requested primary cannot establish", - durability: "cross_cell", - validTablets: allTablets, - tabletsReachable: []*topodatapb.Tablet{primaryTablet, replicaTablet, rdonlyTablet, rdonlyCrossCellTablet}, + name: "error - requested primary cannot establish", + durability: "cross_cell", + validTablets: allTablets, + tabletsTakingBackup: noTabletsTakingBackup, + tabletsReachable: []*topodatapb.Tablet{primaryTablet, replicaTablet, rdonlyTablet, rdonlyCrossCellTablet}, opts: EmergencyReparentOptions{ NewPrimaryAlias: primaryTablet.Alias, }, @@ -4551,7 +4584,7 @@ func TestEmergencyReparenter_filterValidCandidates(t *testing.T) { tt.opts.durability = durability logger := logutil.NewMemoryLogger() erp := NewEmergencyReparenter(nil, nil, logger) - tabletList, err := erp.filterValidCandidates(tt.validTablets, tt.tabletsReachable, tt.prevPrimary, tt.opts) + tabletList, err := erp.filterValidCandidates(tt.validTablets, tt.tabletsReachable, tt.tabletsTakingBackup, tt.prevPrimary, tt.opts) if tt.errShouldContain != "" { require.Error(t, err) require.Contains(t, err.Error(), tt.errShouldContain) diff --git a/go/vt/vtctl/reparentutil/reparent_sorter_test.go b/go/vt/vtctl/reparentutil/reparent_sorter_test.go index ae5d56e884e..87e7b253d54 100644 --- a/go/vt/vtctl/reparentutil/reparent_sorter_test.go +++ b/go/vt/vtctl/reparentutil/reparent_sorter_test.go @@ -22,7 +22,6 @@ import ( "github.com/stretchr/testify/require" "vitess.io/vitess/go/mysql/replication" - topodatapb "vitess.io/vitess/go/vt/proto/topodata" ) diff --git a/go/vt/vtctl/reparentutil/replication.go b/go/vt/vtctl/reparentutil/replication.go index 8642de84fc7..17dbaeae015 100644 --- a/go/vt/vtctl/reparentutil/replication.go +++ b/go/vt/vtctl/reparentutil/replication.go @@ -165,9 +165,10 @@ func SetReplicationSource(ctx context.Context, ts *topo.Server, tmc tmclient.Tab // replicationSnapshot stores the status maps and the tablets that were reachable // when trying to stopReplicationAndBuildStatusMaps. type replicationSnapshot struct { - statusMap map[string]*replicationdatapb.StopReplicationStatus - primaryStatusMap map[string]*replicationdatapb.PrimaryStatus - reachableTablets []*topodatapb.Tablet + statusMap map[string]*replicationdatapb.StopReplicationStatus + primaryStatusMap map[string]*replicationdatapb.PrimaryStatus + reachableTablets []*topodatapb.Tablet + tabletsBackupState map[string]bool } // stopReplicationAndBuildStatusMaps stops replication on all replicas, then @@ -193,9 +194,10 @@ func stopReplicationAndBuildStatusMaps( errChan = make(chan concurrency.Error) allTablets []*topodatapb.Tablet res = &replicationSnapshot{ - statusMap: map[string]*replicationdatapb.StopReplicationStatus{}, - primaryStatusMap: map[string]*replicationdatapb.PrimaryStatus{}, - reachableTablets: []*topodatapb.Tablet{}, + statusMap: map[string]*replicationdatapb.StopReplicationStatus{}, + primaryStatusMap: map[string]*replicationdatapb.PrimaryStatus{}, + reachableTablets: []*topodatapb.Tablet{}, + tabletsBackupState: map[string]bool{}, } ) @@ -237,6 +239,20 @@ func stopReplicationAndBuildStatusMaps( err = vterrors.Wrapf(err, "error when getting replication status for alias %v: %v", alias, err) } } else { + isTakingBackup := false + + // Prefer the most up-to-date information regarding whether the tablet is taking a backup from the After + // replication status, but fall back to the Before status if After is nil. + if stopReplicationStatus.After != nil { + isTakingBackup = stopReplicationStatus.After.BackupRunning + } else if stopReplicationStatus.Before != nil { + isTakingBackup = stopReplicationStatus.Before.BackupRunning + } + + m.Lock() + res.tabletsBackupState[alias] = isTakingBackup + m.Unlock() + var sqlThreadRunning bool // Check if the sql thread was running for the tablet sqlThreadRunning, err = SQLThreadWasRunning(stopReplicationStatus) diff --git a/go/vt/vtctl/reparentutil/replication_test.go b/go/vt/vtctl/reparentutil/replication_test.go index 4a449b1189c..1b36186efb8 100644 --- a/go/vt/vtctl/reparentutil/replication_test.go +++ b/go/vt/vtctl/reparentutil/replication_test.go @@ -283,6 +283,7 @@ func Test_stopReplicationAndBuildStatusMaps(t *testing.T) { waitForAllTablets bool expectedStatusMap map[string]*replicationdatapb.StopReplicationStatus expectedPrimaryStatusMap map[string]*replicationdatapb.PrimaryStatus + expectedTakingBackup map[string]bool expectedTabletsReachable []*topodatapb.Tablet shouldErr bool }{ @@ -339,6 +340,7 @@ func Test_stopReplicationAndBuildStatusMaps(t *testing.T) { After: &replicationdatapb.Status{Position: "MySQL56/3E11FA47-71CA-11E1-9E33-C80AA9429101:1-9"}, }, }, + expectedTakingBackup: map[string]bool{"zone1-0000000100": false, "zone1-0000000101": false}, expectedPrimaryStatusMap: map[string]*replicationdatapb.PrimaryStatus{}, expectedTabletsReachable: []*topodatapb.Tablet{{ Type: topodatapb.TabletType_REPLICA, @@ -407,6 +409,7 @@ func Test_stopReplicationAndBuildStatusMaps(t *testing.T) { After: &replicationdatapb.Status{Position: "MySQL56/3E11FA47-71CA-11E1-9E33-C80AA9429101:1-9"}, }, }, + expectedTakingBackup: map[string]bool{"zone1-0000000100": false, "zone1-0000000101": false}, expectedPrimaryStatusMap: map[string]*replicationdatapb.PrimaryStatus{}, expectedTabletsReachable: []*topodatapb.Tablet{{ Type: topodatapb.TabletType_REPLICA, @@ -491,6 +494,7 @@ func Test_stopReplicationAndBuildStatusMaps(t *testing.T) { After: &replicationdatapb.Status{Position: "MySQL56/3E11FA47-71CA-11E1-9E33-C80AA9429101:1-9"}, }, }, + expectedTakingBackup: map[string]bool{"zone1-0000000100": false, "zone1-0000000101": false}, expectedPrimaryStatusMap: map[string]*replicationdatapb.PrimaryStatus{}, expectedTabletsReachable: []*topodatapb.Tablet{{ Type: topodatapb.TabletType_REPLICA, @@ -585,6 +589,7 @@ func Test_stopReplicationAndBuildStatusMaps(t *testing.T) { After: &replicationdatapb.Status{Position: "MySQL56/3E11FA47-71CA-11E1-9E33-C80AA9429101:1-9"}, }, }, + expectedTakingBackup: map[string]bool{"zone1-0000000100": false, "zone1-0000000101": false}, expectedPrimaryStatusMap: map[string]*replicationdatapb.PrimaryStatus{}, expectedTabletsReachable: []*topodatapb.Tablet{{ Type: topodatapb.TabletType_REPLICA, @@ -678,6 +683,7 @@ func Test_stopReplicationAndBuildStatusMaps(t *testing.T) { After: &replicationdatapb.Status{Position: "MySQL56/3E11FA47-71CA-11E1-9E33-C80AA9429101:1-9"}, }, }, + expectedTakingBackup: map[string]bool{"zone1-0000000100": false, "zone1-0000000101": false}, expectedPrimaryStatusMap: map[string]*replicationdatapb.PrimaryStatus{}, expectedTabletsReachable: []*topodatapb.Tablet{{ Type: topodatapb.TabletType_REPLICA, @@ -750,6 +756,7 @@ func Test_stopReplicationAndBuildStatusMaps(t *testing.T) { Uid: 101, }, }}, + expectedTakingBackup: map[string]bool{"zone1-0000000101": false}, expectedPrimaryStatusMap: map[string]*replicationdatapb.PrimaryStatus{}, shouldErr: false, }, @@ -811,6 +818,7 @@ func Test_stopReplicationAndBuildStatusMaps(t *testing.T) { After: &replicationdatapb.Status{Position: "MySQL56/3E11FA47-71CA-11E1-9E33-C80AA9429101:1-9"}, }, }, + expectedTakingBackup: map[string]bool{"zone1-0000000101": false}, expectedPrimaryStatusMap: map[string]*replicationdatapb.PrimaryStatus{ "zone1-0000000100": { Position: "primary-position-100", @@ -885,6 +893,7 @@ func Test_stopReplicationAndBuildStatusMaps(t *testing.T) { After: &replicationdatapb.Status{Position: "MySQL56/3E11FA47-71CA-11E1-9E33-C80AA9429101:1-9"}, }, }, + expectedTakingBackup: map[string]bool{"zone1-0000000101": false}, expectedPrimaryStatusMap: map[string]*replicationdatapb.PrimaryStatus{}, // zone1-0000000100 fails to demote, so does not appear expectedTabletsReachable: []*topodatapb.Tablet{{ Type: topodatapb.TabletType_REPLICA, @@ -1008,6 +1017,7 @@ func Test_stopReplicationAndBuildStatusMaps(t *testing.T) { Uid: 101, }, }}, + expectedTakingBackup: map[string]bool{"zone1-0000000101": false}, expectedPrimaryStatusMap: map[string]*replicationdatapb.PrimaryStatus{}, shouldErr: false, }, @@ -1057,6 +1067,7 @@ func Test_stopReplicationAndBuildStatusMaps(t *testing.T) { After: &replicationdatapb.Status{Position: "MySQL56/3E11FA47-71CA-11E1-9E33-C80AA9429101:1-9"}, }, }, + expectedTakingBackup: map[string]bool{"zone1-0000000101": false}, expectedPrimaryStatusMap: map[string]*replicationdatapb.PrimaryStatus{}, expectedTabletsReachable: []*topodatapb.Tablet{{ Type: topodatapb.TabletType_REPLICA, @@ -1252,8 +1263,78 @@ func Test_stopReplicationAndBuildStatusMaps(t *testing.T) { }, }}, stopReplicasTimeout: time.Minute, + expectedTakingBackup: map[string]bool{"zone1-0000000100": false, "zone1-0000000101": false, "zone1-0000000102": false}, expectedPrimaryStatusMap: map[string]*replicationdatapb.PrimaryStatus{}, shouldErr: false, + }, { + name: "Handle nil replication status After. No segfaulting when determining backup status, and fall back to Before status", + durability: "none", + tmc: &stopReplicationAndBuildStatusMapsTestTMClient{ + stopReplicationAndGetStatusResults: map[string]*struct { + StopStatus *replicationdatapb.StopReplicationStatus + Err error + }{ + "zone1-0000000100": { + StopStatus: &replicationdatapb.StopReplicationStatus{ + Before: &replicationdatapb.Status{Position: "MySQL56/3E11FA47-71CA-11E1-9E33-C80AA9429100:1-5", IoState: int32(replication.ReplicationStateRunning), SqlState: int32(replication.ReplicationStateRunning), BackupRunning: true}, + After: nil, + }, + }, + "zone1-0000000101": { + StopStatus: &replicationdatapb.StopReplicationStatus{ + Before: &replicationdatapb.Status{Position: "MySQL56/3E11FA47-71CA-11E1-9E33-C80AA9429101:1-5", IoState: int32(replication.ReplicationStateRunning), SqlState: int32(replication.ReplicationStateRunning), BackupRunning: true}, + After: nil, + }, + }, + }, + }, + tabletMap: map[string]*topo.TabletInfo{ + "zone1-0000000100": { + Tablet: &topodatapb.Tablet{ + Type: topodatapb.TabletType_REPLICA, + Alias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 100, + }, + }, + }, + "zone1-0000000101": { + Tablet: &topodatapb.Tablet{ + Type: topodatapb.TabletType_REPLICA, + Alias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 101, + }, + }, + }, + }, + ignoredTablets: sets.New[string](), + expectedStatusMap: map[string]*replicationdatapb.StopReplicationStatus{ + "zone1-0000000100": { + Before: &replicationdatapb.Status{Position: "MySQL56/3E11FA47-71CA-11E1-9E33-C80AA9429100:1-5", IoState: int32(replication.ReplicationStateRunning), SqlState: int32(replication.ReplicationStateRunning), BackupRunning: true}, + After: nil, + }, + "zone1-0000000101": { + Before: &replicationdatapb.Status{Position: "MySQL56/3E11FA47-71CA-11E1-9E33-C80AA9429101:1-5", IoState: int32(replication.ReplicationStateRunning), SqlState: int32(replication.ReplicationStateRunning), BackupRunning: true}, + After: nil, + }, + }, + expectedTakingBackup: map[string]bool{"zone1-0000000100": true, "zone1-0000000101": true}, + expectedPrimaryStatusMap: map[string]*replicationdatapb.PrimaryStatus{}, + expectedTabletsReachable: []*topodatapb.Tablet{{ + Type: topodatapb.TabletType_REPLICA, + Alias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 100, + }, + }, { + Type: topodatapb.TabletType_REPLICA, + Alias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 101, + }, + }}, + shouldErr: false, }, } @@ -1279,6 +1360,7 @@ func Test_stopReplicationAndBuildStatusMaps(t *testing.T) { for idx, tablet := range res.reachableTablets { assert.True(t, topoproto.IsTabletInList(tablet, tt.expectedTabletsReachable), "TabletsReached[%d] not found - %s", idx, topoproto.TabletAliasString(tablet.Alias)) } + assert.Equal(t, tt.expectedTakingBackup, res.tabletsBackupState) }) } } diff --git a/go/vt/vtctl/reparentutil/util.go b/go/vt/vtctl/reparentutil/util.go index fd701f8c69b..c4c23e65c7e 100644 --- a/go/vt/vtctl/reparentutil/util.go +++ b/go/vt/vtctl/reparentutil/util.go @@ -58,7 +58,8 @@ const ( // cell as the current primary, and to be different from avoidPrimaryAlias. The // tablet with the most advanced replication position is chosen to minimize the // amount of time spent catching up with the current primary. Further ties are -// broken by the durability rules. +// broken by the durability rules. Tablets taking backups are excluded from +// consideration. // Note that the search for the most advanced replication position will race // 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 @@ -126,13 +127,17 @@ func ElectNewPrimary( tb := tablet errorGroup.Go(func() error { // find and store the positions for the tablet - pos, replLag, err := findPositionAndLagForTablet(groupCtx, tb, logger, tmc, opts.WaitReplicasTimeout) + pos, replLag, takingBackup, err := findTabletPositionLagBackupStatus(groupCtx, tb, logger, tmc, opts.WaitReplicasTimeout) mu.Lock() defer mu.Unlock() if err == nil && (opts.TolerableReplLag == 0 || opts.TolerableReplLag >= replLag) { - validTablets = append(validTablets, tb) - tabletPositions = append(tabletPositions, pos) - innodbBufferPool = append(innodbBufferPool, innodbBufferPoolData[topoproto.TabletAliasString(tb.Alias)]) + if takingBackup { + reasonsToInvalidate.WriteString(fmt.Sprintf("\n%v is taking a backup", topoproto.TabletAliasString(tablet.Alias))) + } else { + validTablets = append(validTablets, tb) + tabletPositions = append(tabletPositions, pos) + innodbBufferPool = append(innodbBufferPool, innodbBufferPoolData[topoproto.TabletAliasString(tb.Alias)]) + } } else { reasonsToInvalidate.WriteString(fmt.Sprintf("\n%v has %v replication lag which is more than the tolerable amount", topoproto.TabletAliasString(tablet.Alias), replLag)) } @@ -150,7 +155,7 @@ func ElectNewPrimary( return nil, vterrors.Errorf(vtrpc.Code_INTERNAL, "cannot find a tablet to reparent to%v", reasonsToInvalidate.String()) } - // sort the tablets for finding the best primary + // sort preferred tablets for finding the best primary err = sortTabletsForReparent(validTablets, tabletPositions, innodbBufferPool, opts.durability) if err != nil { return nil, err @@ -159,9 +164,9 @@ func ElectNewPrimary( return validTablets[0].Alias, nil } -// findPositionAndLagForTablet processes the replication position and lag for a single tablet and +// findTabletPositionLagBackupStatus processes the replication position and lag for a single tablet and // returns it. It is safe to call from multiple goroutines. -func findPositionAndLagForTablet(ctx context.Context, tablet *topodatapb.Tablet, logger logutil.Logger, tmc tmclient.TabletManagerClient, waitTimeout time.Duration) (replication.Position, time.Duration, error) { +func findTabletPositionLagBackupStatus(ctx context.Context, tablet *topodatapb.Tablet, logger logutil.Logger, tmc tmclient.TabletManagerClient, waitTimeout time.Duration) (replication.Position, time.Duration, bool, error) { logger.Infof("getting replication position from %v", topoproto.TabletAliasString(tablet.Alias)) ctx, cancel := context.WithTimeout(ctx, waitTimeout) @@ -172,10 +177,10 @@ func findPositionAndLagForTablet(ctx context.Context, tablet *topodatapb.Tablet, 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{}, 0, nil + return replication.Position{}, 0, false, nil } logger.Warningf("failed to get replication status from %v, ignoring tablet: %v", topoproto.TabletAliasString(tablet.Alias), err) - return replication.Position{}, 0, err + return replication.Position{}, 0, false, err } // Use the relay log position if available, otherwise use the executed GTID set (binary log position). @@ -186,10 +191,10 @@ func findPositionAndLagForTablet(ctx context.Context, tablet *topodatapb.Tablet, 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{}, 0, err + return replication.Position{}, 0, status.BackupRunning, err } - return pos, time.Second * time.Duration(status.ReplicationLagSeconds), nil + return pos, time.Second * time.Duration(status.ReplicationLagSeconds), status.BackupRunning, nil } // FindCurrentPrimary returns the current primary tablet of a shard, if any. The diff --git a/go/vt/vtctl/reparentutil/util_test.go b/go/vt/vtctl/reparentutil/util_test.go index f4e9092fc3f..ac44da8175a 100644 --- a/go/vt/vtctl/reparentutil/util_test.go +++ b/go/vt/vtctl/reparentutil/util_test.go @@ -139,6 +139,112 @@ func TestElectNewPrimary(t *testing.T) { }, errContains: nil, }, + { + name: "Two good replicas, but one of them is taking a backup so we pick the other one", + tmc: &chooseNewPrimaryTestTMClient{ + // both zone1-101 and zone1-102 are equivalent from a replicaiton PoV, but zone1-102 is taking a backup + replicationStatuses: map[string]*replicationdatapb.Status{ + "zone1-0000000101": { + Position: "MySQL56/3E11FA47-71CA-11E1-9E33-C80AA9429562:1-5", + BackupRunning: true, + }, + "zone1-0000000102": { + Position: "MySQL56/3E11FA47-71CA-11E1-9E33-C80AA9429562:1-5", + BackupRunning: false, + }, + }, + }, + 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, + }, + errContains: nil, + }, + { + name: "Only one replica, but it's taking a backup. We don't elect it.", + tmc: &chooseNewPrimaryTestTMClient{ + // both zone1-101 and zone1-102 are equivalent from a replicaiton PoV, but zone1-102 is taking a backup + replicationStatuses: map[string]*replicationdatapb.Status{ + "zone1-0000000101": { + Position: "MySQL56/3E11FA47-71CA-11E1-9E33-C80AA9429562:1-5", + BackupRunning: true, + }, + }, + }, + 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, + }, + }, + }, + avoidPrimaryAlias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 0, + }, + expected: nil, + errContains: []string{"zone1-0000000101 is taking a backup"}, + }, { name: "new primary alias provided - no tolerable replication lag", tolerableReplLag: 0, @@ -414,6 +520,67 @@ func TestElectNewPrimary(t *testing.T) { }, errContains: nil, }, + { + name: "Two replicas, first one with too much lag, another one taking a backup - none is a good candidate", + tmc: &chooseNewPrimaryTestTMClient{ + // zone1-101 is behind zone1-102 + replicationStatuses: map[string]*replicationdatapb.Status{ + "zone1-0000000101": { + Position: "MySQL56/3E11FA47-71CA-11E1-9E33-C80AA9429562:1", + ReplicationLagSeconds: 55, + }, + "zone1-0000000102": { + Position: "MySQL56/3E11FA47-71CA-11E1-9E33-C80AA9429562:1-5", + BackupRunning: true, + }, + }, + }, + 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: nil, + errContains: []string{ + "zone1-0000000101 has 55s replication lag which is more than the tolerable amount", + "zone1-0000000102 is taking a backup", + }, + }, { name: "found a replica - more advanced relay log position", tmc: &chooseNewPrimaryTestTMClient{ @@ -881,12 +1048,13 @@ func TestFindPositionForTablet(t *testing.T) { ctx := context.Background() logger := logutil.NewMemoryLogger() tests := []struct { - name string - tmc *testutil.TabletManagerClient - tablet *topodatapb.Tablet - expectedPosition string - expectedLag time.Duration - expectedErr string + name string + tmc *testutil.TabletManagerClient + tablet *topodatapb.Tablet + expectedPosition string + expectedLag time.Duration + expectedErr string + expectedTakingBackup bool }{ { name: "executed gtid set", @@ -911,6 +1079,31 @@ func TestFindPositionForTablet(t *testing.T) { }, expectedLag: 201 * time.Second, expectedPosition: "MySQL56/3e11fa47-71ca-11e1-9e33-c80aa9429562:1-5", + }, { + name: "Host is taking a backup", + 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-5", + ReplicationLagSeconds: 201, + }, + }, + }, + TabletsBackupState: map[string]bool{"zone1-0000000100": true}, + }, + tablet: &topodatapb.Tablet{ + Alias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 100, + }, + }, + expectedLag: 201 * time.Second, + expectedTakingBackup: true, + expectedPosition: "MySQL56/3e11fa47-71ca-11e1-9e33-c80aa9429562:1-5", }, { name: "no replication status", tmc: &testutil.TabletManagerClient{ @@ -981,7 +1174,7 @@ func TestFindPositionForTablet(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { - pos, lag, err := findPositionAndLagForTablet(ctx, test.tablet, logger, test.tmc, 10*time.Second) + pos, lag, takingBackup, err := findTabletPositionLagBackupStatus(ctx, test.tablet, logger, test.tmc, 10*time.Second) if test.expectedErr != "" { require.EqualError(t, err, test.expectedErr) return @@ -990,6 +1183,7 @@ func TestFindPositionForTablet(t *testing.T) { posString := replication.EncodePosition(pos) require.Equal(t, test.expectedPosition, posString) require.Equal(t, test.expectedLag, lag) + require.Equal(t, test.expectedTakingBackup, takingBackup) }) } } diff --git a/go/vt/vtctl/workflow/framework_test.go b/go/vt/vtctl/workflow/framework_test.go index 249ff07cf41..a2c1b2ef8e3 100644 --- a/go/vt/vtctl/workflow/framework_test.go +++ b/go/vt/vtctl/workflow/framework_test.go @@ -271,6 +271,7 @@ type testTMClient struct { vrQueries map[int][]*queryResult createVReplicationWorkflowRequests map[uint32]*createVReplicationWorkflowRequestResponse readVReplicationWorkflowRequests map[uint32]*readVReplicationWorkflowRequestResponse + updateVReplicationWorklowsRequests map[uint32]*tabletmanagerdatapb.UpdateVReplicationWorkflowsRequest applySchemaRequests map[uint32]*applySchemaRequestResponse primaryPositions map[uint32]string vdiffRequests map[uint32]*vdiffRequestResponse @@ -294,6 +295,7 @@ func newTestTMClient(env *testEnv) *testTMClient { vrQueries: make(map[int][]*queryResult), createVReplicationWorkflowRequests: make(map[uint32]*createVReplicationWorkflowRequestResponse), readVReplicationWorkflowRequests: make(map[uint32]*readVReplicationWorkflowRequestResponse), + updateVReplicationWorklowsRequests: make(map[uint32]*tabletmanagerdatapb.UpdateVReplicationWorkflowsRequest), applySchemaRequests: make(map[uint32]*applySchemaRequestResponse), readVReplicationWorkflowsResponses: make(map[string][]*tabletmanagerdatapb.ReadVReplicationWorkflowsResponse), primaryPositions: make(map[uint32]string), @@ -496,6 +498,11 @@ func (tmc *testTMClient) ExecuteFetchAsAllPrivs(ctx context.Context, tablet *top return nil, nil } +func (tmc *testTMClient) ExecuteFetchAsApp(ctx context.Context, tablet *topodatapb.Tablet, usePool bool, req *tabletmanagerdatapb.ExecuteFetchAsAppRequest) (*querypb.QueryResult, error) { + // Reuse VReplicationExec. + return tmc.VReplicationExec(ctx, tablet, string(req.Query)) +} + func (tmc *testTMClient) expectApplySchemaRequest(tabletID uint32, req *applySchemaRequestResponse) { tmc.mu.Lock() defer tmc.mu.Unlock() @@ -617,6 +624,10 @@ func (tmc *testTMClient) HasVReplicationWorkflows(ctx context.Context, tablet *t }, nil } +func (tmc *testTMClient) ResetSequences(ctx context.Context, tablet *topodatapb.Tablet, tables []string) error { + return nil +} + func (tmc *testTMClient) ReadVReplicationWorkflows(ctx context.Context, tablet *topodatapb.Tablet, req *tabletmanagerdatapb.ReadVReplicationWorkflowsRequest) (*tabletmanagerdatapb.ReadVReplicationWorkflowsResponse, error) { tmc.mu.Lock() defer tmc.mu.Unlock() @@ -677,6 +688,19 @@ func (tmc *testTMClient) UpdateVReplicationWorkflow(ctx context.Context, tablet }, nil } +func (tmc *testTMClient) UpdateVReplicationWorkflows(ctx context.Context, tablet *topodatapb.Tablet, req *tabletmanagerdatapb.UpdateVReplicationWorkflowsRequest) (*tabletmanagerdatapb.UpdateVReplicationWorkflowsResponse, error) { + tmc.mu.Lock() + defer tmc.mu.Unlock() + if expect := tmc.updateVReplicationWorklowsRequests[tablet.Alias.Uid]; expect != nil { + if !proto.Equal(expect, req) { + return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "unexpected ReadVReplicationWorkflow request on tablet %s: got %+v, want %+v", + topoproto.TabletAliasString(tablet.Alias), req, expect) + } + } + delete(tmc.updateVReplicationWorklowsRequests, tablet.Alias.Uid) + return nil, nil +} + func (tmc *testTMClient) ValidateVReplicationPermissions(ctx context.Context, tablet *topodatapb.Tablet, req *tabletmanagerdatapb.ValidateVReplicationPermissionsRequest) (*tabletmanagerdatapb.ValidateVReplicationPermissionsResponse, error) { return &tabletmanagerdatapb.ValidateVReplicationPermissionsResponse{ User: "vt_filtered", @@ -736,6 +760,12 @@ func (tmc *testTMClient) AddVReplicationWorkflowsResponse(key string, resp *tabl tmc.readVReplicationWorkflowsResponses[key] = append(tmc.readVReplicationWorkflowsResponses[key], resp) } +func (tmc *testTMClient) AddUpdateVReplicationRequests(tabletUID uint32, req *tabletmanagerdatapb.UpdateVReplicationWorkflowsRequest) { + tmc.mu.Lock() + defer tmc.mu.Unlock() + tmc.updateVReplicationWorklowsRequests[tabletUID] = req +} + func (tmc *testTMClient) getVReplicationWorkflowsResponse(key string) *tabletmanagerdatapb.ReadVReplicationWorkflowsResponse { if len(tmc.readVReplicationWorkflowsResponses) == 0 { return nil diff --git a/go/vt/vtctl/workflow/materializer_test.go b/go/vt/vtctl/workflow/materializer_test.go index 746c5fe2bae..a583a101186 100644 --- a/go/vt/vtctl/workflow/materializer_test.go +++ b/go/vt/vtctl/workflow/materializer_test.go @@ -2588,7 +2588,7 @@ func TestCreateLookupVindexFailures(t *testing.T) { err: "unique vindex 'from' should have only one column", }, { - description: "non-unique lookup should have more than one column", + description: "non-unique lookup can have only one column", input: &vschemapb.Keyspace{ Vindexes: map[string]*vschemapb.Vindex{ "v": { @@ -2601,7 +2601,7 @@ func TestCreateLookupVindexFailures(t *testing.T) { }, }, }, - err: "non-unique vindex 'from' should have more than one column", + err: "", }, { description: "vindex not found", diff --git a/go/vt/vtctl/workflow/resharder_test.go b/go/vt/vtctl/workflow/resharder_test.go index 6353f36db9f..6fe1afb0c70 100644 --- a/go/vt/vtctl/workflow/resharder_test.go +++ b/go/vt/vtctl/workflow/resharder_test.go @@ -22,14 +22,20 @@ import ( "testing" "time" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "golang.org/x/exp/maps" + "vitess.io/vitess/go/ptr" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/topo" "vitess.io/vitess/go/vt/topo/topoproto" + "vitess.io/vitess/go/vt/vtgate/vindexes" + 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" ) @@ -65,6 +71,8 @@ func TestReshardCreate(t *testing.T) { sourceKeyspace, targetKeyspace *testKeyspace preFunc func(env *testEnv) want *vtctldatapb.WorkflowStatusResponse + updateVReplicationRequest *tabletmanagerdatapb.UpdateVReplicationWorkflowsRequest + autoStart bool wantErr string }{ { @@ -77,6 +85,11 @@ func TestReshardCreate(t *testing.T) { KeyspaceName: targetKeyspaceName, ShardNames: []string{"-80", "80-"}, }, + autoStart: true, + updateVReplicationRequest: &tabletmanagerdatapb.UpdateVReplicationWorkflowsRequest{ + AllWorkflows: true, + State: ptr.Of(binlogdatapb.VReplicationWorkflowState_Running), + }, want: &vtctldatapb.WorkflowStatusResponse{ ShardStreams: map[string]*vtctldatapb.WorkflowStatusResponse_ShardStreams{ "targetks/-80": { @@ -137,6 +150,7 @@ func TestReshardCreate(t *testing.T) { SourceShards: tc.sourceKeyspace.ShardNames, TargetShards: tc.targetKeyspace.ShardNames, Cells: []string{env.cell}, + AutoStart: tc.autoStart, } for i := range tc.sourceKeyspace.ShardNames { @@ -172,6 +186,9 @@ func TestReshardCreate(t *testing.T) { "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)", &sqltypes.Result{}, ) + if tc.updateVReplicationRequest != nil { + env.tmc.AddUpdateVReplicationRequests(uint32(tabletUID), tc.updateVReplicationRequest) + } } if tc.preFunc != nil { @@ -187,6 +204,300 @@ func TestReshardCreate(t *testing.T) { if tc.want != nil { require.Equal(t, tc.want, res) } + + // Expect updateVReplicationWorklowsRequests to be empty, + // if AutoStart is enabled. This is because we delete the specific + // key from the map in the testTMC, once updateVReplicationWorklows() + // with the expected request is called. + if tc.autoStart { + assert.Len(t, env.tmc.updateVReplicationWorklowsRequests, 0) + } + }) + } +} + +func TestReadRefStreams(t *testing.T) { + ctx := context.Background() + + sourceKeyspace := &testKeyspace{ + KeyspaceName: "sourceKeyspace", + ShardNames: []string{"-"}, + } + targetKeyspace := &testKeyspace{ + KeyspaceName: "targetKeyspace", + ShardNames: []string{"-"}, + } + + env := newTestEnv(t, ctx, defaultCellName, sourceKeyspace, targetKeyspace) + defer env.close() + + s1, err := env.ts.UpdateShardFields(ctx, targetKeyspace.KeyspaceName, "-", func(si *topo.ShardInfo) error { + return nil + }) + require.NoError(t, err) + + sourceTablet, ok := env.tablets[sourceKeyspace.KeyspaceName][100] + require.True(t, ok) + + env.tmc.schema = map[string]*tabletmanagerdatapb.SchemaDefinition{ + "t1": {}, + } + + rules := make([]*binlogdatapb.Rule, len(env.tmc.schema)) + for i, table := range maps.Keys(env.tmc.schema) { + rules[i] = &binlogdatapb.Rule{ + Match: table, + Filter: fmt.Sprintf("select * from %s", table), + } + } + + refKey := fmt.Sprintf("wf:%s:-", sourceKeyspace.KeyspaceName) + + testCases := []struct { + name string + addVReplicationWorkflowsResponse *tabletmanagerdatapb.ReadVReplicationWorkflowsResponse + preRefStreams map[string]*refStream + wantRefStreamKeys []string + wantErr bool + errContains string + }{ + { + name: "error for unnamed workflow", + addVReplicationWorkflowsResponse: &tabletmanagerdatapb.ReadVReplicationWorkflowsResponse{ + Workflows: []*tabletmanagerdatapb.ReadVReplicationWorkflowResponse{ + { + Workflow: "", + WorkflowType: binlogdatapb.VReplicationWorkflowType_Reshard, + }, + }, + }, + wantErr: true, + }, + { + name: "populate ref streams", + addVReplicationWorkflowsResponse: &tabletmanagerdatapb.ReadVReplicationWorkflowsResponse{ + Workflows: []*tabletmanagerdatapb.ReadVReplicationWorkflowResponse{ + { + Workflow: "wf", + WorkflowType: binlogdatapb.VReplicationWorkflowType_Reshard, + Streams: []*tabletmanagerdatapb.ReadVReplicationWorkflowResponse_Stream{ + { + + Bls: &binlogdatapb.BinlogSource{ + Keyspace: sourceKeyspace.KeyspaceName, + Shard: "-", + Tables: maps.Keys(env.tmc.schema), + Filter: &binlogdatapb.Filter{ + Rules: rules, + }, + }, + }, + }, + }, + }, + }, + wantRefStreamKeys: []string{refKey}, + }, + { + name: "mismatched streams with empty map", + preRefStreams: map[string]*refStream{}, + addVReplicationWorkflowsResponse: &tabletmanagerdatapb.ReadVReplicationWorkflowsResponse{ + Workflows: []*tabletmanagerdatapb.ReadVReplicationWorkflowResponse{ + { + Workflow: "wf", + WorkflowType: binlogdatapb.VReplicationWorkflowType_Reshard, + Streams: []*tabletmanagerdatapb.ReadVReplicationWorkflowResponse_Stream{ + { + + Bls: &binlogdatapb.BinlogSource{ + Keyspace: sourceKeyspace.KeyspaceName, + Shard: "-", + Tables: maps.Keys(env.tmc.schema), + Filter: &binlogdatapb.Filter{ + Rules: rules, + }, + }, + }, + }, + }, + }, + }, + wantErr: true, + errContains: "mismatch", + }, + { + name: "mismatched streams", + preRefStreams: map[string]*refStream{ + refKey: nil, + "nonexisting": nil, + }, + addVReplicationWorkflowsResponse: &tabletmanagerdatapb.ReadVReplicationWorkflowsResponse{ + Workflows: []*tabletmanagerdatapb.ReadVReplicationWorkflowResponse{ + { + Workflow: "wf", + WorkflowType: binlogdatapb.VReplicationWorkflowType_Reshard, + Streams: []*tabletmanagerdatapb.ReadVReplicationWorkflowResponse_Stream{ + { + + Bls: &binlogdatapb.BinlogSource{ + Keyspace: sourceKeyspace.KeyspaceName, + Shard: "-", + Tables: maps.Keys(env.tmc.schema), + Filter: &binlogdatapb.Filter{ + Rules: rules, + }, + }, + }, + }, + }, + }, + }, + wantErr: true, + errContains: "mismatch", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + rs := &resharder{ + s: env.ws, + keyspace: targetKeyspace.KeyspaceName, + sourceShards: []*topo.ShardInfo{s1}, + sourcePrimaries: map[string]*topo.TabletInfo{ + "-": { + Tablet: sourceTablet, + }, + }, + workflow: "wf", + vschema: &vschemapb.Keyspace{ + Tables: map[string]*vschemapb.Table{ + "t1": { + Type: vindexes.TypeReference, + }, + }, + }, + refStreams: tc.preRefStreams, + } + + workflowKey := env.tmc.GetWorkflowKey(sourceKeyspace.KeyspaceName, "-") + + env.tmc.AddVReplicationWorkflowsResponse(workflowKey, tc.addVReplicationWorkflowsResponse) + + err := rs.readRefStreams(ctx) + if !tc.wantErr { + assert.NoError(t, err) + for _, rk := range tc.wantRefStreamKeys { + assert.Contains(t, rs.refStreams, rk) + } + return + } + + assert.Error(t, err) + assert.ErrorContains(t, err, tc.errContains) + }) + } +} + +func TestBlsIsReference(t *testing.T) { + testCases := []struct { + name string + bls *binlogdatapb.BinlogSource + tables map[string]*vschemapb.Table + expected bool + wantErr bool + errContains string + }{ + { + name: "all references", + bls: &binlogdatapb.BinlogSource{ + Filter: &binlogdatapb.Filter{ + Rules: []*binlogdatapb.Rule{ + {Match: "ref_table1"}, + {Match: "ref_table2"}, + }, + }, + }, + tables: map[string]*vschemapb.Table{ + "ref_table1": {Type: vindexes.TypeReference}, + "ref_table2": {Type: vindexes.TypeReference}, + }, + expected: true, + }, + { + name: "all sharded", + bls: &binlogdatapb.BinlogSource{ + Filter: &binlogdatapb.Filter{ + Rules: []*binlogdatapb.Rule{ + {Match: "sharded_table1"}, + {Match: "sharded_table2"}, + }, + }, + }, + tables: map[string]*vschemapb.Table{ + "sharded_table1": {Type: vindexes.TypeTable}, + "sharded_table2": {Type: vindexes.TypeTable}, + }, + expected: false, + }, + { + name: "mixed reference and sharded tables", + bls: &binlogdatapb.BinlogSource{ + Filter: &binlogdatapb.Filter{ + Rules: []*binlogdatapb.Rule{ + {Match: "ref_table"}, + {Match: "sharded_table"}, + }, + }, + }, + tables: map[string]*vschemapb.Table{ + "ref_table": {Type: vindexes.TypeReference}, + "sharded_table": {Type: vindexes.TypeTable}, + }, + wantErr: true, + }, + { + name: "rule table not found in vschema", + bls: &binlogdatapb.BinlogSource{ + Filter: &binlogdatapb.Filter{ + Rules: []*binlogdatapb.Rule{ + {Match: "unknown_table"}, + }, + }, + }, + tables: map[string]*vschemapb.Table{}, + wantErr: true, + errContains: "unknown_table", + }, + { + name: "internal operation table ignored", + bls: &binlogdatapb.BinlogSource{ + Filter: &binlogdatapb.Filter{ + Rules: []*binlogdatapb.Rule{ + {Match: "_vt_hld_6ace8bcef73211ea87e9f875a4d24e90_20200915120410_"}, + }, + }, + }, + tables: map[string]*vschemapb.Table{}, + expected: false, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + rs := &resharder{ + vschema: &vschemapb.Keyspace{ + Tables: tc.tables, + }, + } + + result, err := rs.blsIsReference(tc.bls) + + if tc.wantErr { + assert.ErrorContains(t, err, tc.errContains) + } else { + assert.NoError(t, err) + assert.Equal(t, tc.expected, result) + } }) } } diff --git a/go/vt/vtctl/workflow/server.go b/go/vt/vtctl/workflow/server.go index 7c49de58c9b..baea602b7a4 100644 --- a/go/vt/vtctl/workflow/server.go +++ b/go/vt/vtctl/workflow/server.go @@ -18,7 +18,6 @@ package workflow import ( "context" - "encoding/json" "errors" "fmt" "math" @@ -41,11 +40,9 @@ import ( "vitess.io/vitess/go/constants/sidecar" "vitess.io/vitess/go/protoutil" "vitess.io/vitess/go/ptr" - "vitess.io/vitess/go/sets" "vitess.io/vitess/go/sqlescape" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/trace" - "vitess.io/vitess/go/vt/binlog/binlogplayer" "vitess.io/vitess/go/vt/concurrency" "vitess.io/vitess/go/vt/discovery" "vitess.io/vitess/go/vt/key" @@ -58,7 +55,6 @@ import ( "vitess.io/vitess/go/vt/topo/topoproto" "vitess.io/vitess/go/vt/topotools" "vitess.io/vitess/go/vt/vtctl/schematools" - "vitess.io/vitess/go/vt/vtctl/workflow/common" "vitess.io/vitess/go/vt/vtctl/workflow/vexec" "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vterrors" @@ -406,546 +402,28 @@ func (s *Server) GetWorkflows(ctx context.Context, req *vtctldatapb.GetWorkflows span.Annotate("include_logs", req.IncludeLogs) span.Annotate("shards", req.Shards) - readReq := &tabletmanagerdatapb.ReadVReplicationWorkflowsRequest{} - if req.Workflow != "" { - readReq.IncludeWorkflows = []string{req.Workflow} + w := &workflowFetcher{ + ts: s.ts, + tmc: s.tmc, + parser: s.SQLParser(), + logger: s.Logger(), } - if req.ActiveOnly { - readReq.ExcludeStates = []binlogdatapb.VReplicationWorkflowState{binlogdatapb.VReplicationWorkflowState_Stopped} - } - - // Guards access to the maps used throughout. - m := sync.Mutex{} - shards, err := common.GetShards(ctx, s.ts, req.Keyspace, req.Shards) + workflowsByShard, err := w.fetchWorkflowsByShard(ctx, req) if err != nil { return nil, err } - results := make(map[*topo.TabletInfo]*tabletmanagerdatapb.ReadVReplicationWorkflowsResponse, len(shards)) - readWorkflowsEg, readWorkflowsCtx := errgroup.WithContext(ctx) - for _, shard := range shards { - readWorkflowsEg.Go(func() error { - si, err := s.ts.GetShard(readWorkflowsCtx, req.Keyspace, shard) - if err != nil { - return err - } - if si.PrimaryAlias == nil { - return fmt.Errorf("%w %s/%s", vexec.ErrNoShardPrimary, req.Keyspace, shard) - } - primary, err := s.ts.GetTablet(readWorkflowsCtx, si.PrimaryAlias) - if err != nil { - return err - } - if primary == nil { - return fmt.Errorf("%w %s/%s: tablet %v not found", vexec.ErrNoShardPrimary, req.Keyspace, shard, topoproto.TabletAliasString(si.PrimaryAlias)) - } - // Clone the request so that we can set the correct DB name for tablet. - req := readReq.CloneVT() - wres, err := s.tmc.ReadVReplicationWorkflows(readWorkflowsCtx, primary.Tablet, req) - if err != nil { - return err - } - m.Lock() - defer m.Unlock() - results[primary] = wres - return nil - }) - } - if readWorkflowsEg.Wait() != nil { - return nil, err - } - - copyStatesByShardStreamId := make(map[string][]*vtctldatapb.Workflow_Stream_CopyState, len(results)) - - fetchCopyStates := func(ctx context.Context, tablet *topo.TabletInfo, streamIds []int32) 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 { - tablet := tablet // loop closure - - streamIds := make([]int32, 0, len(result.Workflows)) - for _, wf := range result.Workflows { - for _, stream := range wf.Streams { - streamIds = append(streamIds, stream.Id) - } - } - - if len(streamIds) == 0 { - continue - } - - fetchCopyStatesEg.Go(func() error { - return fetchCopyStates(fetchCopyStatesCtx, tablet, streamIds) - }) - } - - if err := fetchCopyStatesEg.Wait(); err != nil { + copyStatesByShardStreamId, err := w.fetchCopyStatesByShardStream(ctx, workflowsByShard) + if 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)) - targetKeyspaceByWorkflow := make(map[string]string, len(results)) - targetShardsByWorkflow := make(map[string]sets.Set[string], len(results)) - maxVReplicationLagByWorkflow := make(map[string]float64, len(results)) - maxVReplicationTransactionLagByWorkflow := make(map[string]float64, len(results)) - - // We guarantee the following invariants when this function is called for a - // given workflow: - // - workflow.Name != "" (more precisely, ".Name is set 'properly'") - // - workflowsMap[workflow.Name] == workflow - // - sourceShardsByWorkflow[workflow.Name] != nil - // - targetShardsByWorkflow[workflow.Name] != nil - // - workflow.ShardStatuses != nil - scanWorkflow := func(ctx context.Context, workflow *vtctldatapb.Workflow, res *tabletmanagerdatapb.ReadVReplicationWorkflowResponse, tablet *topo.TabletInfo) error { - // This is not called concurrently, but we still protect the maps to ensure - // that we're concurrency-safe in the face of future changes (e.g. where other - // things are running concurrently with this which also access these maps). - m.Lock() - defer m.Unlock() - for _, rstream := range res.Streams { - // 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 - // when needed. - pos := rstream.Pos - if pos != "" { - mpos, err := binlogplayer.DecodePosition(pos) - if err != nil { - return err - } - pos = mpos.String() - } - - cells := strings.Split(res.Cells, ",") - for i := range cells { - cells[i] = strings.TrimSpace(cells[i]) - } - options := res.Options - if options != "" { - if err := json.Unmarshal([]byte(options), &workflow.Options); err != nil { - return err - } - } - stream := &vtctldatapb.Workflow_Stream{ - Id: int64(rstream.Id), - Shard: tablet.Shard, - Tablet: tablet.Alias, - BinlogSource: rstream.Bls, - Position: pos, - StopPosition: rstream.StopPos, - State: rstream.State.String(), - DbName: tablet.DbName(), - TabletTypes: res.TabletTypes, - TabletSelectionPreference: res.TabletSelectionPreference, - Cells: cells, - TransactionTimestamp: rstream.TransactionTimestamp, - TimeUpdated: rstream.TimeUpdated, - Message: rstream.Message, - Tags: strings.Split(res.Tags, ","), - RowsCopied: rstream.RowsCopied, - ThrottlerStatus: &vtctldatapb.Workflow_Stream_ThrottlerStatus{ - ComponentThrottled: rstream.ComponentThrottled, - TimeThrottled: rstream.TimeThrottled, - }, - } - - // Merge in copy states, which we've already fetched. - shardStreamId := fmt.Sprintf("%s/%d", tablet.Shard, stream.Id) - if copyState, ok := copyStatesByShardStreamId[shardStreamId]; ok { - stream.CopyStates = copyState - } - - if rstream.TimeUpdated == nil { - rstream.TimeUpdated = &vttimepb.Time{} - } - - switch { - case strings.Contains(strings.ToLower(stream.Message), "error"): - stream.State = binlogdatapb.VReplicationWorkflowState_Error.String() - case stream.State == binlogdatapb.VReplicationWorkflowState_Running.String() && len(stream.CopyStates) > 0: - stream.State = binlogdatapb.VReplicationWorkflowState_Copying.String() - case stream.State == binlogdatapb.VReplicationWorkflowState_Running.String() && int64(time.Now().Second())-rstream.TimeUpdated.Seconds > 10: - stream.State = binlogdatapb.VReplicationWorkflowState_Lagging.String() - } - - shardStreamKey := fmt.Sprintf("%s/%s", tablet.Shard, tablet.AliasString()) - shardStream, ok := workflow.ShardStreams[shardStreamKey] - if !ok { - ctx, cancel := context.WithTimeout(ctx, topo.RemoteOperationTimeout) - defer cancel() - - si, err := s.ts.GetShard(ctx, req.Keyspace, tablet.Shard) - if err != nil { - return err - } - - shardStream = &vtctldatapb.Workflow_ShardStream{ - Streams: nil, - TabletControls: si.TabletControls, - IsPrimaryServing: si.IsPrimaryServing, - } - - workflow.ShardStreams[shardStreamKey] = shardStream - } - - shardStream.Streams = append(shardStream.Streams, stream) - sourceShardsByWorkflow[workflow.Name].Insert(stream.BinlogSource.Shard) - targetShardsByWorkflow[workflow.Name].Insert(tablet.Shard) - - if ks, ok := sourceKeyspaceByWorkflow[workflow.Name]; ok && ks != stream.BinlogSource.Keyspace { - return vterrors.Wrapf(ErrMultipleSourceKeyspaces, "workflow = %v, ks1 = %v, ks2 = %v", workflow.Name, ks, stream.BinlogSource.Keyspace) - } - - sourceKeyspaceByWorkflow[workflow.Name] = stream.BinlogSource.Keyspace - - if ks, ok := targetKeyspaceByWorkflow[workflow.Name]; ok && ks != tablet.Keyspace { - return vterrors.Wrapf(ErrMultipleTargetKeyspaces, "workflow = %v, ks1 = %v, ks2 = %v", workflow.Name, ks, tablet.Keyspace) - } - - targetKeyspaceByWorkflow[workflow.Name] = tablet.Keyspace - - if stream.TimeUpdated == nil { - stream.TimeUpdated = &vttimepb.Time{} - } - timeUpdated := time.Unix(stream.TimeUpdated.Seconds, 0) - vreplicationLag := time.Since(timeUpdated) - - // MaxVReplicationLag represents the time since we last processed any event - // in the workflow. - if currentMaxLag, ok := maxVReplicationLagByWorkflow[workflow.Name]; ok { - if vreplicationLag.Seconds() > currentMaxLag { - maxVReplicationLagByWorkflow[workflow.Name] = vreplicationLag.Seconds() - } - } else { - maxVReplicationLagByWorkflow[workflow.Name] = vreplicationLag.Seconds() - } - - workflow.WorkflowType = res.WorkflowType.String() - workflow.WorkflowSubType = res.WorkflowSubType.String() - workflow.DeferSecondaryKeys = res.DeferSecondaryKeys - - // MaxVReplicationTransactionLag estimates the actual statement processing lag - // between the source and the target. If we are still processing source events it - // is the difference b/w current time and the timestamp of the last event. If - // heartbeats are more recent than the last event, then the lag is the time since - // the last heartbeat as there can be an actual event immediately after the - // heartbeat, but which has not yet been processed on the target. - // We don't allow switching during the copy phase, so in that case we just return - // a large lag. All timestamps are in seconds since epoch. - if _, ok := maxVReplicationTransactionLagByWorkflow[workflow.Name]; !ok { - maxVReplicationTransactionLagByWorkflow[workflow.Name] = 0 - } - if rstream.TransactionTimestamp == nil { - rstream.TransactionTimestamp = &vttimepb.Time{} - } - lastTransactionTime := rstream.TransactionTimestamp.Seconds - if rstream.TimeHeartbeat == nil { - rstream.TimeHeartbeat = &vttimepb.Time{} - } - lastHeartbeatTime := rstream.TimeHeartbeat.Seconds - if stream.State == binlogdatapb.VReplicationWorkflowState_Copying.String() { - maxVReplicationTransactionLagByWorkflow[workflow.Name] = math.MaxInt64 - } else { - if lastTransactionTime == 0 /* no new events after copy */ || - lastHeartbeatTime > lastTransactionTime /* no recent transactions, so all caught up */ { - - lastTransactionTime = lastHeartbeatTime - } - now := time.Now().Unix() /* seconds since epoch */ - transactionReplicationLag := float64(now - lastTransactionTime) - if transactionReplicationLag > maxVReplicationTransactionLagByWorkflow[workflow.Name] { - maxVReplicationTransactionLagByWorkflow[workflow.Name] = transactionReplicationLag - } - } - } - - return nil - } - - for tablet, result := range results { - // In the old implementation, we knew we had at most one (0 <= N <= 1) - // workflow for each shard primary we queried. There might be multiple - // rows (streams) comprising that workflow, so we would aggregate the - // rows for a given primary into a single value ("the workflow", - // ReplicationStatusResult in the old types). - // - // In this version, we have many (N >= 0) workflows for each shard - // primary we queried, so we need to determine if each row corresponds - // to a workflow we're already aggregating, or if it's a workflow we - // haven't seen yet for that shard primary. We use the workflow name to - // dedupe for this. - for _, wfres := range result.Workflows { - workflowName := wfres.Workflow - workflow, ok := workflowsMap[workflowName] - if !ok { - workflow = &vtctldatapb.Workflow{ - Name: workflowName, - ShardStreams: map[string]*vtctldatapb.Workflow_ShardStream{}, - } - - workflowsMap[workflowName] = workflow - sourceShardsByWorkflow[workflowName] = sets.New[string]() - targetShardsByWorkflow[workflowName] = sets.New[string]() - } - - if err := scanWorkflow(ctx, workflow, wfres, tablet); err != nil { - return nil, err - } - } - } - - var ( - fetchLogsWG sync.WaitGroup - vrepLogQuery = strings.TrimSpace(` -SELECT - id, - vrepl_id, - type, - state, - message, - created_at, - updated_at, - count -FROM - _vt.vreplication_log -WHERE vrepl_id IN %a -ORDER BY - vrepl_id ASC, - id ASC -`) - ) - - fetchStreamLogs := func(ctx context.Context, workflow *vtctldatapb.Workflow) { - span, ctx := trace.NewSpan(ctx, "workflow.Server.fetchStreamLogs") - defer span.Finish() - - span.Annotate("keyspace", req.Keyspace) - span.Annotate("workflow", workflow.Name) - - vreplIDs := make([]int64, 0, len(workflow.ShardStreams)) - for _, shardStream := range maps.Values(workflow.ShardStreams) { - for _, stream := range shardStream.Streams { - vreplIDs = append(vreplIDs, stream.Id) - } - } - idsBV, err := sqltypes.BuildBindVariable(vreplIDs) - if err != nil { - return - } - - query, err := sqlparser.ParseAndBind(vrepLogQuery, idsBV) - if err != nil { - return - } - - vx := vexec.NewVExec(req.Keyspace, workflow.Name, s.ts, s.tmc, s.SQLParser()) - results, err := vx.QueryContext(ctx, query) - if err != nil { - // Note that we do not return here. If there are any query results - // in the map (i.e. some tablets returned successfully), we will - // still try to read log rows from them on a best-effort basis. But, - // we will also pre-emptively record the top-level fetch error on - // every stream in every shard in the workflow. Further processing - // below may override the error message for certain streams. - for _, streams := range workflow.ShardStreams { - for _, stream := range streams.Streams { - stream.LogFetchError = err.Error() - } - } - } - - for target, p3qr := range results { - qr := sqltypes.Proto3ToResult(p3qr) - shardStreamKey := fmt.Sprintf("%s/%s", target.Shard, target.AliasString()) - - ss, ok := workflow.ShardStreams[shardStreamKey] - if !ok || ss == nil { - continue - } - - streams := ss.Streams - streamIdx := 0 - markErrors := func(err error) { - if streamIdx >= len(streams) { - return - } - - streams[streamIdx].LogFetchError = err.Error() - } - - for _, row := range qr.Rows { - id, err := row[0].ToCastInt64() - if err != nil { - markErrors(err) - continue - } - - streamID, err := row[1].ToCastInt64() - if err != nil { - markErrors(err) - continue - } - - typ := row[2].ToString() - state := row[3].ToString() - message := row[4].ToString() - - createdAt, err := time.Parse("2006-01-02 15:04:05", row[5].ToString()) - if err != nil { - markErrors(err) - continue - } - - updatedAt, err := time.Parse("2006-01-02 15:04:05", row[6].ToString()) - if err != nil { - markErrors(err) - continue - } - - count, err := row[7].ToCastInt64() - if err != nil { - markErrors(err) - continue - } - - streamLog := &vtctldatapb.Workflow_Stream_Log{ - Id: id, - StreamId: streamID, - Type: typ, - State: state, - CreatedAt: &vttimepb.Time{ - Seconds: createdAt.Unix(), - }, - UpdatedAt: &vttimepb.Time{ - Seconds: updatedAt.Unix(), - }, - Message: message, - Count: count, - } - - // Earlier, in the main loop where we called scanWorkflow for - // each _vt.vreplication row, we also sorted each ShardStreams - // slice by ascending id, and our _vt.vreplication_log query - // ordered by (stream_id ASC, id ASC), so we can walk the - // streams in index order in O(n) amortized over all the rows - // for this tablet. - for streamIdx < len(streams) { - stream := streams[streamIdx] - if stream.Id < streamLog.StreamId { - streamIdx++ - continue - } - - if stream.Id > streamLog.StreamId { - s.Logger().Warningf("Found stream log for nonexistent stream: %+v", streamLog) - // This can happen on manual/failed workflow cleanup so move to the next log. - break - } - - // stream.Id == streamLog.StreamId - stream.Logs = append(stream.Logs, streamLog) - break - } - } - } - } - - workflows := make([]*vtctldatapb.Workflow, 0, len(workflowsMap)) - - for name, workflow := range workflowsMap { - sourceShards, ok := sourceShardsByWorkflow[name] - if !ok { - return nil, vterrors.Wrapf(ErrInvalidWorkflow, "%s has no source shards", name) - } - - sourceKeyspace, ok := sourceKeyspaceByWorkflow[name] - if !ok { - return nil, vterrors.Wrapf(ErrInvalidWorkflow, "%s has no source keyspace", name) - } - - targetShards, ok := targetShardsByWorkflow[name] - if !ok { - return nil, vterrors.Wrapf(ErrInvalidWorkflow, "%s has no target shards", name) - } - - targetKeyspace, ok := targetKeyspaceByWorkflow[name] - if !ok { - return nil, vterrors.Wrapf(ErrInvalidWorkflow, "%s has no target keyspace", name) - } - - maxVReplicationLag, ok := maxVReplicationLagByWorkflow[name] - if !ok { - return nil, vterrors.Wrapf(ErrInvalidWorkflow, "%s has no tracked vreplication lag", name) - } - - maxVReplicationTransactionLag, ok := maxVReplicationTransactionLagByWorkflow[name] - if !ok { - return nil, vterrors.Wrapf(ErrInvalidWorkflow, "%s has no tracked vreplication transaction lag", name) - } - - workflow.Source = &vtctldatapb.Workflow_ReplicationLocation{ - Keyspace: sourceKeyspace, - Shards: sets.List(sourceShards), - } - - workflow.Target = &vtctldatapb.Workflow_ReplicationLocation{ - Keyspace: targetKeyspace, - Shards: sets.List(targetShards), - } - - workflow.MaxVReplicationLag = int64(maxVReplicationLag) - workflow.MaxVReplicationTransactionLag = int64(maxVReplicationTransactionLag) - - // Sort shard streams by stream_id ASC, to support an optimization - // in fetchStreamLogs below. - for _, shardStreams := range workflow.ShardStreams { - sort.Slice(shardStreams.Streams, func(i, j int) bool { - return shardStreams.Streams[i].Id < shardStreams.Streams[j].Id - }) - } - - workflows = append(workflows, workflow) - - if req.IncludeLogs { - // Fetch logs for all streams associated with this workflow in the background. - fetchLogsWG.Add(1) - go func(ctx context.Context, workflow *vtctldatapb.Workflow) { - defer fetchLogsWG.Done() - fetchStreamLogs(ctx, workflow) - }(ctx, workflow) - } + workflows, err := w.buildWorkflows(ctx, workflowsByShard, copyStatesByShardStreamId, req) + if err != nil { + return nil, err } - // Wait for all the log fetchers to finish. - fetchLogsWG.Wait() - return &vtctldatapb.GetWorkflowsResponse{ Workflows: workflows, }, nil @@ -1080,51 +558,6 @@ func (s *Server) getWorkflowState(ctx context.Context, targetKeyspace, workflowN return ts, state, nil } -func (s *Server) getWorkflowCopyStates(ctx context.Context, tablet *topo.TabletInfo, streamIds []int32) ([]*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("stream_ids", fmt.Sprintf("%#v", streamIds)) - - 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 - } - - result := sqltypes.Proto3ToResult(qr) - if result == nil { - return nil, nil - } - - copyStates := make([]*vtctldatapb.Workflow_Stream_CopyState, len(result.Rows)) - for i, row := range result.Rows { - 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{ - StreamId: streamId, - Table: row[1].ToString(), - LastPk: row[2].ToString(), - } - } - - return copyStates, nil -} - // LookupVindexCreate creates the lookup vindex in the specified // keyspace and creates a VReplication workflow to backfill that // vindex from the keyspace to the target/lookup table specified. @@ -1545,7 +978,7 @@ func (s *Server) moveTablesCreate(ctx context.Context, req *vtctldatapb.MoveTabl } } if isStandardMoveTables() { // Non-standard ones do not use shard scoped mechanisms - if err := s.setupInitialDeniedTables(ctx, ts); err != nil { + if err := setupInitialDeniedTables(ctx, ts); err != nil { return nil, vterrors.Wrapf(err, "failed to put initial denied tables entries in place on the target shards") } } @@ -1600,7 +1033,7 @@ func (s *Server) moveTablesCreate(ctx context.Context, req *vtctldatapb.MoveTabl }) } -func (s *Server) validateRoutingRuleFlags(req *vtctldatapb.MoveTablesCreateRequest, mz *materializer) error { +func validateRoutingRuleFlags(req *vtctldatapb.MoveTablesCreateRequest, mz *materializer) error { if mz.IsMultiTenantMigration() { switch { case req.NoRoutingRules: @@ -1612,7 +1045,7 @@ func (s *Server) validateRoutingRuleFlags(req *vtctldatapb.MoveTablesCreateReque return nil } -func (s *Server) setupInitialDeniedTables(ctx context.Context, ts *trafficSwitcher) error { +func setupInitialDeniedTables(ctx context.Context, ts *trafficSwitcher) error { if ts.MigrationType() != binlogdatapb.MigrationType_TABLES { return nil } @@ -1630,7 +1063,7 @@ func (s *Server) setupInitialDeniedTables(ctx context.Context, ts *trafficSwitch } func (s *Server) setupInitialRoutingRules(ctx context.Context, req *vtctldatapb.MoveTablesCreateRequest, mz *materializer, tables []string) error { - if err := s.validateRoutingRuleFlags(req, mz); err != nil { + if err := validateRoutingRuleFlags(req, mz); err != nil { return err } @@ -4026,10 +3459,6 @@ func (s *Server) prepareCreateLookup(ctx context.Context, workflow, keyspace str if len(vindexFromCols) != 1 { return nil, nil, nil, nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "unique vindex 'from' should have only one column") } - } else { - if len(vindexFromCols) < 2 { - return nil, nil, nil, nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "non-unique vindex 'from' should have more than one column") - } } vindexToCol = vindex.Params["to"] // Make the vindex write_only. If one exists already in the vschema, diff --git a/go/vt/vtctl/workflow/server_test.go b/go/vt/vtctl/workflow/server_test.go index dbe06ab1a47..26d722f1de0 100644 --- a/go/vt/vtctl/workflow/server_test.go +++ b/go/vt/vtctl/workflow/server_test.go @@ -2470,7 +2470,7 @@ func TestGetWorkflowsStreamLogs(t *testing.T) { }, sourceShards, targetShards) logResult := sqltypes.MakeTestResult( - sqltypes.MakeTestFields("id|vrepl_id|type|state|message|created_at|updated_at|`count`", "int64|int64|varchar|varchar|varchar|varchar|varchar|int64"), + sqltypes.MakeTestFields("id|vrepl_id|type|state|message|created_at|updated_at|count", "int64|int64|varchar|varchar|varchar|varchar|varchar|int64"), "1|0|State Change|Running|test message for non-existent 1|2006-01-02 15:04:05|2006-01-02 15:04:05|1", "2|0|State Change|Stopped|test message for non-existent 2|2006-01-02 15:04:06|2006-01-02 15:04:06|1", "3|1|State Change|Running|log message|2006-01-02 15:04:07|2006-01-02 15:04:07|1", @@ -2499,3 +2499,63 @@ func TestGetWorkflowsStreamLogs(t *testing.T) { assert.Equal(t, gotLogs[0].State, "Running") assert.Equal(t, gotLogs[0].Id, int64(3)) } + +func TestWorkflowStatus(t *testing.T) { + ctx := context.Background() + + sourceKeyspace := "source_keyspace" + targetKeyspace := "target_keyspace" + workflow := "test_workflow" + + sourceShards := []string{"-"} + targetShards := []string{"-"} + + te := newTestMaterializerEnv(t, ctx, &vtctldatapb.MaterializeSettings{ + SourceKeyspace: sourceKeyspace, + TargetKeyspace: targetKeyspace, + Workflow: workflow, + TableSettings: []*vtctldatapb.TableMaterializeSettings{ + { + TargetTable: "table1", + SourceExpression: fmt.Sprintf("select * from %s", "table1"), + }, + { + TargetTable: "table2", + SourceExpression: fmt.Sprintf("select * from %s", "table2"), + }, + }, + }, sourceShards, targetShards) + + tablesResult := sqltypes.MakeTestResult(sqltypes.MakeTestFields("table_name", "varchar"), "table1", "table2") + te.tmc.expectVRQuery(200, "select distinct table_name from _vt.copy_state cs, _vt.vreplication vr where vr.id = cs.vrepl_id and vr.id = 1", tablesResult) + + tablesTargetCopyResult := sqltypes.MakeTestResult(sqltypes.MakeTestFields("table_name|table_rows|data_length", "varchar|int64|int64"), "table1|50|500", "table2|100|250") + te.tmc.expectVRQuery(200, "select table_name, table_rows, data_length from information_schema.tables where table_schema = 'vt_target_keyspace' and table_name in ('table1','table2')", tablesTargetCopyResult) + + tablesSourceCopyResult := sqltypes.MakeTestResult(sqltypes.MakeTestFields("table_name|table_rows|data_length", "varchar|int64|int64"), "table1|100|1000", "table2|200|500") + te.tmc.expectVRQuery(100, "select table_name, table_rows, data_length from information_schema.tables where table_schema = 'vt_source_keyspace' and table_name in ('table1','table2')", tablesSourceCopyResult) + + te.tmc.expectVRQuery(200, "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)", &sqltypes.Result{}) + + res, err := te.ws.WorkflowStatus(ctx, &vtctldatapb.WorkflowStatusRequest{ + Keyspace: targetKeyspace, + Workflow: workflow, + Shards: targetShards, + }) + + assert.NoError(t, err) + + require.NotNil(t, res.TableCopyState) + + stateTable1 := res.TableCopyState["table1"] + stateTable2 := res.TableCopyState["table2"] + require.NotNil(t, stateTable1) + require.NotNil(t, stateTable2) + + assert.Equal(t, int64(100), stateTable1.RowsTotal) + assert.Equal(t, int64(200), stateTable2.RowsTotal) + assert.Equal(t, int64(50), stateTable1.RowsCopied) + assert.Equal(t, int64(100), stateTable2.RowsCopied) + assert.Equal(t, float32(50), stateTable1.RowsPercentage) + assert.Equal(t, float32(50), stateTable2.RowsPercentage) +} diff --git a/go/vt/vtctl/workflow/traffic_switcher.go b/go/vt/vtctl/workflow/traffic_switcher.go index 937dffe70b3..4fc34992b0f 100644 --- a/go/vt/vtctl/workflow/traffic_switcher.go +++ b/go/vt/vtctl/workflow/traffic_switcher.go @@ -1135,30 +1135,45 @@ func (ts *trafficSwitcher) switchDeniedTables(ctx context.Context) error { return nil } +// cancelMigration attempts to revert all changes made during the migration so that we can get back to the +// state when traffic switching (or reversing) was initiated. func (ts *trafficSwitcher) cancelMigration(ctx context.Context, sm *StreamMigrator) { var err error + + if ctx.Err() != nil { + // Even though we create a new context later on we still record any context error: + // for forensics in case of failures. + ts.Logger().Infof("In Cancel migration: original context invalid: %s", ctx.Err()) + } + + // We create a new context while canceling the migration, so that we are independent of the original + // context being cancelled prior to or during the cancel operation. + cmTimeout := 60 * time.Second + cmCtx, cmCancel := context.WithTimeout(context.Background(), cmTimeout) + defer cmCancel() + if ts.MigrationType() == binlogdatapb.MigrationType_TABLES { - err = ts.switchDeniedTables(ctx) + err = ts.switchDeniedTables(cmCtx) } else { - err = ts.changeShardsAccess(ctx, ts.SourceKeyspaceName(), ts.SourceShards(), allowWrites) + err = ts.changeShardsAccess(cmCtx, ts.SourceKeyspaceName(), ts.SourceShards(), allowWrites) } if err != nil { - ts.Logger().Errorf("Cancel migration failed: %v", err) + ts.Logger().Errorf("Cancel migration failed: could not revert denied tables / shard access: %v", err) } - sm.CancelStreamMigrations(ctx) + sm.CancelStreamMigrations(cmCtx) err = ts.ForAllTargets(func(target *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())) - _, err := ts.TabletManagerClient().VReplicationExec(ctx, target.GetPrimary().Tablet, query) + _, err := ts.TabletManagerClient().VReplicationExec(cmCtx, target.GetPrimary().Tablet, query) return err }) if err != nil { ts.Logger().Errorf("Cancel migration failed: could not restart vreplication: %v", err) } - err = ts.deleteReverseVReplication(ctx) + err = ts.deleteReverseVReplication(cmCtx) if err != nil { ts.Logger().Errorf("Cancel migration failed: could not delete reverse vreplication streams: %v", err) } diff --git a/go/vt/vtctl/workflow/traffic_switcher_test.go b/go/vt/vtctl/workflow/traffic_switcher_test.go index 325b405b6f0..b06c95b6c16 100644 --- a/go/vt/vtctl/workflow/traffic_switcher_test.go +++ b/go/vt/vtctl/workflow/traffic_switcher_test.go @@ -28,6 +28,7 @@ import ( "github.com/stretchr/testify/require" "vitess.io/vitess/go/sqlescape" + "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/mysqlctl/tmutils" "vitess.io/vitess/go/vt/proto/vschema" "vitess.io/vitess/go/vt/sqlparser" @@ -36,6 +37,7 @@ import ( "vitess.io/vitess/go/vt/vttablet/tabletmanager/vreplication" tabletmanagerdatapb "vitess.io/vitess/go/vt/proto/tabletmanagerdata" + topodatapb "vitess.io/vitess/go/vt/proto/topodata" vtctldatapb "vitess.io/vitess/go/vt/proto/vtctldata" ) @@ -649,3 +651,255 @@ func TestTrafficSwitchPositionHandling(t *testing.T) { }) require.NoError(t, err) } + +func TestInitializeTargetSequences(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) + defer cancel() + + workflowName := "wf1" + tableName := "t1" + sourceKeyspaceName := "sourceks" + targetKeyspaceName := "targetks" + + schema := map[string]*tabletmanagerdatapb.SchemaDefinition{ + tableName: { + TableDefinitions: []*tabletmanagerdatapb.TableDefinition{ + { + Name: tableName, + Schema: fmt.Sprintf("CREATE TABLE %s (id BIGINT, name VARCHAR(64), PRIMARY KEY (id))", tableName), + }, + }, + }, + } + + sourceKeyspace := &testKeyspace{ + KeyspaceName: sourceKeyspaceName, + ShardNames: []string{"0"}, + } + targetKeyspace := &testKeyspace{ + KeyspaceName: targetKeyspaceName, + ShardNames: []string{"0"}, + } + + env := newTestEnv(t, ctx, defaultCellName, sourceKeyspace, targetKeyspace) + defer env.close() + env.tmc.schema = schema + + ts, _, err := env.ws.getWorkflowState(ctx, targetKeyspaceName, workflowName) + require.NoError(t, err) + sw := &switcher{ts: ts, s: env.ws} + + sequencesByBackingTable := map[string]*sequenceMetadata{ + "my-seq1": { + backingTableName: "my-seq1", + backingTableKeyspace: sourceKeyspaceName, + backingTableDBName: fmt.Sprintf("vt_%s", sourceKeyspaceName), + usingTableName: tableName, + usingTableDBName: "vt_targetks", + usingTableDefinition: &vschema.Table{ + AutoIncrement: &vschema.AutoIncrement{ + Column: "my-col", + Sequence: fmt.Sprintf("%s.my-seq1", sourceKeyspace.KeyspaceName), + }, + }, + }, + } + + env.tmc.expectVRQuery(200, "/select max.*", sqltypes.MakeTestResult(sqltypes.MakeTestFields("maxval", "int64"), "34")) + // Expect the insert query to be executed with 35 as a params, since we provide a maxID of 34 in the last query + env.tmc.expectVRQuery(100, "/insert into.*35.*", &sqltypes.Result{RowsAffected: 1}) + + err = sw.initializeTargetSequences(ctx, sequencesByBackingTable) + assert.NoError(t, err) + + // Expect the queries to be cleared + assert.Empty(t, env.tmc.vrQueries[100]) + assert.Empty(t, env.tmc.vrQueries[200]) +} + +func TestAddTenantFilter(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) + defer cancel() + + workflowName := "wf1" + tableName := "t1" + sourceKeyspaceName := "sourceks" + targetKeyspaceName := "targetks" + + sourceKeyspace := &testKeyspace{ + KeyspaceName: sourceKeyspaceName, + ShardNames: []string{"0"}, + } + targetKeyspace := &testKeyspace{ + KeyspaceName: targetKeyspaceName, + ShardNames: []string{"0"}, + } + + schema := map[string]*tabletmanagerdatapb.SchemaDefinition{ + tableName: { + TableDefinitions: []*tabletmanagerdatapb.TableDefinition{ + { + Name: tableName, + Schema: fmt.Sprintf("CREATE TABLE %s (id BIGINT, name VARCHAR(64), PRIMARY KEY (id))", tableName), + }, + }, + }, + } + + env := newTestEnv(t, ctx, defaultCellName, sourceKeyspace, targetKeyspace) + defer env.close() + env.tmc.schema = schema + + err := env.ts.SaveVSchema(ctx, targetKeyspaceName, &vschema.Keyspace{ + MultiTenantSpec: &vschema.MultiTenantSpec{ + TenantIdColumnName: "tenant_id", + TenantIdColumnType: sqltypes.Int64, + }, + }) + require.NoError(t, err) + + ts, _, err := env.ws.getWorkflowState(ctx, targetKeyspaceName, workflowName) + require.NoError(t, err) + + ts.options.TenantId = "123" + + filter, err := ts.addTenantFilter(ctx, fmt.Sprintf("select * from %s where id < 5", tableName)) + assert.NoError(t, err) + assert.Equal(t, "select * from t1 where tenant_id = 123 and id < 5", filter) +} + +func TestChangeShardRouting(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) + defer cancel() + + workflowName := "wf1" + tableName := "t1" + sourceKeyspaceName := "sourceks" + targetKeyspaceName := "targetks" + + sourceKeyspace := &testKeyspace{ + KeyspaceName: sourceKeyspaceName, + ShardNames: []string{"0"}, + } + targetKeyspace := &testKeyspace{ + KeyspaceName: targetKeyspaceName, + ShardNames: []string{"0"}, + } + + schema := map[string]*tabletmanagerdatapb.SchemaDefinition{ + tableName: { + TableDefinitions: []*tabletmanagerdatapb.TableDefinition{ + { + Name: tableName, + Schema: fmt.Sprintf("CREATE TABLE %s (id BIGINT, name VARCHAR(64), PRIMARY KEY (id))", tableName), + }, + }, + }, + } + + env := newTestEnv(t, ctx, defaultCellName, sourceKeyspace, targetKeyspace) + defer env.close() + env.tmc.schema = schema + + ts, _, err := env.ws.getWorkflowState(ctx, targetKeyspaceName, workflowName) + require.NoError(t, err) + + err = env.ws.ts.UpdateSrvKeyspace(ctx, "cell", targetKeyspaceName, &topodatapb.SrvKeyspace{ + Partitions: []*topodatapb.SrvKeyspace_KeyspacePartition{ + { + ShardReferences: []*topodatapb.ShardReference{ + { + Name: "0", + }, + }, + }, + }, + }) + require.NoError(t, err) + + err = env.ws.ts.UpdateSrvKeyspace(ctx, "cell", sourceKeyspaceName, &topodatapb.SrvKeyspace{ + Partitions: []*topodatapb.SrvKeyspace_KeyspacePartition{ + { + ShardReferences: []*topodatapb.ShardReference{ + { + Name: "0", + }, + }, + }, + }, + }) + require.NoError(t, err) + + ctx, _, err = env.ws.ts.LockShard(ctx, targetKeyspaceName, "0", "targetks0") + require.NoError(t, err) + + ctx, _, err = env.ws.ts.LockKeyspace(ctx, targetKeyspaceName, "targetks0") + require.NoError(t, err) + + err = ts.changeShardRouting(ctx) + assert.NoError(t, err) + + sourceShardInfo, err := env.ws.ts.GetShard(ctx, sourceKeyspaceName, "0") + assert.NoError(t, err) + assert.False(t, sourceShardInfo.IsPrimaryServing, "source shard shouldn't have it's primary serving after changeShardRouting() is called.") + + targetShardInfo, err := env.ws.ts.GetShard(ctx, targetKeyspaceName, "0") + assert.NoError(t, err) + assert.True(t, targetShardInfo.IsPrimaryServing, "target shard should have it's primary serving after changeShardRouting() is called.") +} + +func TestAddParticipatingTablesToKeyspace(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) + defer cancel() + + workflowName := "wf1" + tableName := "t1" + sourceKeyspaceName := "sourceks" + targetKeyspaceName := "targetks" + + sourceKeyspace := &testKeyspace{ + KeyspaceName: sourceKeyspaceName, + ShardNames: []string{"0"}, + } + targetKeyspace := &testKeyspace{ + KeyspaceName: targetKeyspaceName, + ShardNames: []string{"0"}, + } + + schema := map[string]*tabletmanagerdatapb.SchemaDefinition{ + tableName: { + TableDefinitions: []*tabletmanagerdatapb.TableDefinition{ + { + Name: tableName, + Schema: fmt.Sprintf("CREATE TABLE %s (id BIGINT, name VARCHAR(64), PRIMARY KEY (id))", tableName), + }, + }, + }, + } + + env := newTestEnv(t, ctx, defaultCellName, sourceKeyspace, targetKeyspace) + defer env.close() + env.tmc.schema = schema + + ts, _, err := env.ws.getWorkflowState(ctx, targetKeyspaceName, workflowName) + require.NoError(t, err) + + err = ts.addParticipatingTablesToKeyspace(ctx, sourceKeyspaceName, "") + assert.NoError(t, err) + + vs, err := env.ts.GetVSchema(ctx, sourceKeyspaceName) + assert.NoError(t, err) + assert.NotNil(t, vs.Tables["t1"]) + assert.Empty(t, vs.Tables["t1"]) + + specs := `{"t1":{"column_vindexes":[{"column":"col1","name":"v1"}, {"column":"col2","name":"v2"}]},"t2":{"column_vindexes":[{"column":"col2","name":"v2"}]}}` + err = ts.addParticipatingTablesToKeyspace(ctx, sourceKeyspaceName, specs) + assert.NoError(t, err) + + vs, err = env.ts.GetVSchema(ctx, sourceKeyspaceName) + assert.NoError(t, err) + require.NotNil(t, vs.Tables["t1"]) + require.NotNil(t, vs.Tables["t2"]) + assert.Len(t, vs.Tables["t1"].ColumnVindexes, 2) + assert.Len(t, vs.Tables["t2"].ColumnVindexes, 1) +} diff --git a/go/vt/vtctl/workflow/workflows.go b/go/vt/vtctl/workflow/workflows.go new file mode 100644 index 00000000000..da0ee5dfec7 --- /dev/null +++ b/go/vt/vtctl/workflow/workflows.go @@ -0,0 +1,672 @@ +/* +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. +*/ + +/* +This file provides functions for fetching and retrieving information about VReplication workflows + +At the moment it is used by the `GetWorkflows` function in `server.go and includes functionality to +get the following: +- Fetch workflows by shard +- Fetch copy states by shard stream +- Build workflows with metadata +- Fetch stream logs +*/ + +package workflow + +import ( + "context" + "encoding/json" + "fmt" + "math" + "sort" + "strings" + "sync" + "time" + + "golang.org/x/exp/maps" + "golang.org/x/sync/errgroup" + + "vitess.io/vitess/go/sets" + "vitess.io/vitess/go/sqltypes" + "vitess.io/vitess/go/trace" + "vitess.io/vitess/go/vt/binlog/binlogplayer" + "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/vtctl/workflow/common" + "vitess.io/vitess/go/vt/vtctl/workflow/vexec" + "vitess.io/vitess/go/vt/vterrors" + "vitess.io/vitess/go/vt/vttablet/tmclient" + + binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" + tabletmanagerdatapb "vitess.io/vitess/go/vt/proto/tabletmanagerdata" + vtctldatapb "vitess.io/vitess/go/vt/proto/vtctldata" + vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" + vttimepb "vitess.io/vitess/go/vt/proto/vttime" +) + +// workflowFetcher is responsible for fetching and retrieving information +// about VReplication workflows. +type workflowFetcher struct { + ts *topo.Server + tmc tmclient.TabletManagerClient + + logger logutil.Logger + parser *sqlparser.Parser +} + +type workflowMetadata struct { + sourceKeyspace string + sourceShards sets.Set[string] + targetKeyspace string + targetShards sets.Set[string] + maxVReplicationLag float64 + maxVReplicationTransactionLag float64 +} + +var vrepLogQuery = strings.TrimSpace(` +SELECT + id, + vrepl_id, + type, + state, + message, + created_at, + updated_at, + count +FROM + _vt.vreplication_log +WHERE vrepl_id IN %a +ORDER BY + vrepl_id ASC, + id ASC +`) + +func (wf *workflowFetcher) fetchWorkflowsByShard( + ctx context.Context, + req *vtctldatapb.GetWorkflowsRequest, +) (map[*topo.TabletInfo]*tabletmanagerdatapb.ReadVReplicationWorkflowsResponse, error) { + readReq := &tabletmanagerdatapb.ReadVReplicationWorkflowsRequest{} + if req.Workflow != "" { + readReq.IncludeWorkflows = []string{req.Workflow} + } + if req.ActiveOnly { + readReq.ExcludeStates = []binlogdatapb.VReplicationWorkflowState{binlogdatapb.VReplicationWorkflowState_Stopped} + } + + m := sync.Mutex{} + + shards, err := common.GetShards(ctx, wf.ts, req.Keyspace, req.Shards) + if err != nil { + return nil, err + } + + results := make(map[*topo.TabletInfo]*tabletmanagerdatapb.ReadVReplicationWorkflowsResponse, len(shards)) + + err = wf.forAllShards(ctx, req.Keyspace, shards, func(ctx context.Context, si *topo.ShardInfo) error { + primary, err := wf.ts.GetTablet(ctx, si.PrimaryAlias) + if err != nil { + return err + } + if primary == nil { + return fmt.Errorf("%w %s/%s: tablet %v not found", vexec.ErrNoShardPrimary, req.Keyspace, si.ShardName(), topoproto.TabletAliasString(si.PrimaryAlias)) + } + // Clone the request so that we can set the correct DB name for tablet. + req := readReq.CloneVT() + wres, err := wf.tmc.ReadVReplicationWorkflows(ctx, primary.Tablet, req) + if err != nil { + return err + } + m.Lock() + defer m.Unlock() + results[primary] = wres + return nil + }) + if err != nil { + return nil, err + } + + return results, nil +} + +func (wf *workflowFetcher) fetchCopyStatesByShardStream( + ctx context.Context, + workflowsByShard map[*topo.TabletInfo]*tabletmanagerdatapb.ReadVReplicationWorkflowsResponse, +) (map[string][]*vtctldatapb.Workflow_Stream_CopyState, error) { + m := sync.Mutex{} + + copyStatesByShardStreamId := make(map[string][]*vtctldatapb.Workflow_Stream_CopyState, len(workflowsByShard)) + + fetchCopyStates := func(ctx context.Context, tablet *topo.TabletInfo, streamIds []int32) error { + span, ctx := trace.NewSpan(ctx, "workflowFetcher.workflow.fetchCopyStates") + defer span.Finish() + + span.Annotate("shard", tablet.Shard) + span.Annotate("tablet_alias", tablet.AliasString()) + + copyStates, err := wf.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 workflowsByShard { + streamIds := make([]int32, 0, len(result.Workflows)) + for _, wf := range result.Workflows { + for _, stream := range wf.Streams { + streamIds = append(streamIds, stream.Id) + } + } + + if len(streamIds) == 0 { + continue + } + + fetchCopyStatesEg.Go(func() error { + return fetchCopyStates(fetchCopyStatesCtx, tablet, streamIds) + }) + } + if err := fetchCopyStatesEg.Wait(); err != nil { + return nil, err + } + + return copyStatesByShardStreamId, nil +} + +func (wf *workflowFetcher) getWorkflowCopyStates(ctx context.Context, tablet *topo.TabletInfo, streamIds []int32) ([]*vtctldatapb.Workflow_Stream_CopyState, error) { + span, ctx := trace.NewSpan(ctx, "workflowFetcher.workflow.getWorkflowCopyStates") + defer span.Finish() + + span.Annotate("keyspace", tablet.Keyspace) + span.Annotate("shard", tablet.Shard) + span.Annotate("tablet_alias", tablet.AliasString()) + span.Annotate("stream_ids", fmt.Sprintf("%#v", streamIds)) + + 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 := wf.tmc.VReplicationExec(ctx, tablet.Tablet, query) + if err != nil { + return nil, err + } + + result := sqltypes.Proto3ToResult(qr) + if result == nil { + return nil, nil + } + + copyStates := make([]*vtctldatapb.Workflow_Stream_CopyState, len(result.Rows)) + for i, row := range result.Named().Rows { + streamId, err := row["vrepl_id"].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{ + StreamId: streamId, + Table: row["table_name"].ToString(), + LastPk: row["lastpk"].ToString(), + } + } + + return copyStates, nil +} + +func (wf *workflowFetcher) buildWorkflows( + ctx context.Context, + results map[*topo.TabletInfo]*tabletmanagerdatapb.ReadVReplicationWorkflowsResponse, + copyStatesByShardStreamId map[string][]*vtctldatapb.Workflow_Stream_CopyState, + req *vtctldatapb.GetWorkflowsRequest, +) ([]*vtctldatapb.Workflow, error) { + workflowsMap := make(map[string]*vtctldatapb.Workflow, len(results)) + workflowMetadataMap := make(map[string]*workflowMetadata, len(results)) + + for tablet, result := range results { + // In the old implementation, we knew we had at most one (0 <= N <= 1) + // workflow for each shard primary we queried. There might be multiple + // rows (streams) comprising that workflow, so we would aggregate the + // rows for a given primary into a single value ("the workflow", + // ReplicationStatusResult in the old types). + // + // In this version, we have many (N >= 0) workflows for each shard + // primary we queried, so we need to determine if each row corresponds + // to a workflow we're already aggregating, or if it's a workflow we + // haven't seen yet for that shard primary. We use the workflow name to + // dedupe for this. + for _, wfres := range result.Workflows { + workflowName := wfres.Workflow + workflow, ok := workflowsMap[workflowName] + if !ok { + workflow = &vtctldatapb.Workflow{ + Name: workflowName, + ShardStreams: map[string]*vtctldatapb.Workflow_ShardStream{}, + } + + workflowsMap[workflowName] = workflow + workflowMetadataMap[workflowName] = &workflowMetadata{ + sourceShards: sets.New[string](), + targetShards: sets.New[string](), + } + } + + metadata := workflowMetadataMap[workflowName] + err := wf.scanWorkflow(ctx, workflow, wfres, tablet, metadata, copyStatesByShardStreamId, req.Keyspace) + if err != nil { + return nil, err + } + } + } + + for name, workflow := range workflowsMap { + meta := workflowMetadataMap[name] + updateWorkflowWithMetadata(workflow, meta) + + // Sort shard streams by stream_id ASC, to support an optimization + // in fetchStreamLogs below. + for _, shardStreams := range workflow.ShardStreams { + sort.Slice(shardStreams.Streams, func(i, j int) bool { + return shardStreams.Streams[i].Id < shardStreams.Streams[j].Id + }) + } + } + + if req.IncludeLogs { + var fetchLogsWG sync.WaitGroup + + for _, workflow := range workflowsMap { + // Fetch logs for all streams associated with this workflow in the background. + fetchLogsWG.Add(1) + go func(ctx context.Context, workflow *vtctldatapb.Workflow) { + defer fetchLogsWG.Done() + wf.fetchStreamLogs(ctx, req.Keyspace, workflow) + }(ctx, workflow) + } + + // Wait for all the log fetchers to finish. + fetchLogsWG.Wait() + } + + return maps.Values(workflowsMap), nil +} + +func (wf *workflowFetcher) scanWorkflow( + ctx context.Context, + workflow *vtctldatapb.Workflow, + res *tabletmanagerdatapb.ReadVReplicationWorkflowResponse, + tablet *topo.TabletInfo, + meta *workflowMetadata, + copyStatesByShardStreamId map[string][]*vtctldatapb.Workflow_Stream_CopyState, + keyspace string, +) error { + shardStreamKey := fmt.Sprintf("%s/%s", tablet.Shard, tablet.AliasString()) + shardStream, ok := workflow.ShardStreams[shardStreamKey] + if !ok { + ctx, cancel := context.WithTimeout(ctx, topo.RemoteOperationTimeout) + defer cancel() + + si, err := wf.ts.GetShard(ctx, keyspace, tablet.Shard) + if err != nil { + return err + } + + shardStream = &vtctldatapb.Workflow_ShardStream{ + Streams: nil, + TabletControls: si.TabletControls, + IsPrimaryServing: si.IsPrimaryServing, + } + + workflow.ShardStreams[shardStreamKey] = shardStream + } + + for _, rstream := range res.Streams { + // 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 + // when needed. + pos := rstream.Pos + if pos != "" { + mpos, err := binlogplayer.DecodePosition(pos) + if err != nil { + return err + } + pos = mpos.String() + } + + cells := strings.Split(res.Cells, ",") + for i := range cells { + cells[i] = strings.TrimSpace(cells[i]) + } + options := res.Options + if options != "" { + if err := json.Unmarshal([]byte(options), &workflow.Options); err != nil { + return err + } + } + + stream := &vtctldatapb.Workflow_Stream{ + Id: int64(rstream.Id), + Shard: tablet.Shard, + Tablet: tablet.Alias, + BinlogSource: rstream.Bls, + Position: pos, + StopPosition: rstream.StopPos, + State: rstream.State.String(), + DbName: tablet.DbName(), + TabletTypes: res.TabletTypes, + TabletSelectionPreference: res.TabletSelectionPreference, + Cells: cells, + TransactionTimestamp: rstream.TransactionTimestamp, + TimeUpdated: rstream.TimeUpdated, + Message: rstream.Message, + Tags: strings.Split(res.Tags, ","), + RowsCopied: rstream.RowsCopied, + ThrottlerStatus: &vtctldatapb.Workflow_Stream_ThrottlerStatus{ + ComponentThrottled: rstream.ComponentThrottled, + TimeThrottled: rstream.TimeThrottled, + }, + } + + // Merge in copy states, which we've already fetched. + shardStreamId := fmt.Sprintf("%s/%d", tablet.Shard, stream.Id) + if copyStates, ok := copyStatesByShardStreamId[shardStreamId]; ok { + stream.CopyStates = copyStates + } + + if rstream.TimeUpdated == nil { + rstream.TimeUpdated = &vttimepb.Time{} + } + + stream.State = getStreamState(stream, rstream) + + shardStream.Streams = append(shardStream.Streams, stream) + + meta.sourceShards.Insert(stream.BinlogSource.Shard) + meta.targetShards.Insert(tablet.Shard) + + if meta.sourceKeyspace != "" && meta.sourceKeyspace != stream.BinlogSource.Keyspace { + return vterrors.Wrapf(ErrMultipleSourceKeyspaces, "workflow = %v, ks1 = %v, ks2 = %v", workflow.Name, meta.sourceKeyspace, stream.BinlogSource.Keyspace) + } + + meta.sourceKeyspace = stream.BinlogSource.Keyspace + + if meta.targetKeyspace != "" && meta.targetKeyspace != tablet.Keyspace { + return vterrors.Wrapf(ErrMultipleTargetKeyspaces, "workflow = %v, ks1 = %v, ks2 = %v", workflow.Name, meta.targetKeyspace, tablet.Keyspace) + } + + meta.targetKeyspace = tablet.Keyspace + + if stream.TimeUpdated == nil { + stream.TimeUpdated = &vttimepb.Time{} + } + timeUpdated := time.Unix(stream.TimeUpdated.Seconds, 0) + vreplicationLag := time.Since(timeUpdated) + + // MaxVReplicationLag represents the time since we last processed any event + // in the workflow. + if vreplicationLag.Seconds() > meta.maxVReplicationLag { + meta.maxVReplicationLag = vreplicationLag.Seconds() + } + + workflow.WorkflowType = res.WorkflowType.String() + workflow.WorkflowSubType = res.WorkflowSubType.String() + workflow.DeferSecondaryKeys = res.DeferSecondaryKeys + + // MaxVReplicationTransactionLag estimates the actual statement processing lag + // between the source and the target. If we are still processing source events it + // is the difference b/w current time and the timestamp of the last event. If + // heartbeats are more recent than the last event, then the lag is the time since + // the last heartbeat as there can be an actual event immediately after the + // heartbeat, but which has not yet been processed on the target. + // We don't allow switching during the copy phase, so in that case we just return + // a large lag. All timestamps are in seconds since epoch. + if rstream.TransactionTimestamp == nil { + rstream.TransactionTimestamp = &vttimepb.Time{} + } + lastTransactionTime := rstream.TransactionTimestamp.Seconds + if rstream.TimeHeartbeat == nil { + rstream.TimeHeartbeat = &vttimepb.Time{} + } + lastHeartbeatTime := rstream.TimeHeartbeat.Seconds + if stream.State == binlogdatapb.VReplicationWorkflowState_Copying.String() { + meta.maxVReplicationTransactionLag = math.MaxInt64 + } else { + if lastTransactionTime == 0 /* no new events after copy */ || + lastHeartbeatTime > lastTransactionTime /* no recent transactions, so all caught up */ { + + lastTransactionTime = lastHeartbeatTime + } + now := time.Now().Unix() /* seconds since epoch */ + transactionReplicationLag := float64(now - lastTransactionTime) + if transactionReplicationLag > meta.maxVReplicationTransactionLag { + meta.maxVReplicationTransactionLag = transactionReplicationLag + } + } + } + + return nil +} + +func updateWorkflowWithMetadata(workflow *vtctldatapb.Workflow, meta *workflowMetadata) { + workflow.Source = &vtctldatapb.Workflow_ReplicationLocation{ + Keyspace: meta.sourceKeyspace, + Shards: sets.List(meta.sourceShards), + } + + workflow.Target = &vtctldatapb.Workflow_ReplicationLocation{ + Keyspace: meta.targetKeyspace, + Shards: sets.List(meta.targetShards), + } + + workflow.MaxVReplicationLag = int64(meta.maxVReplicationLag) + workflow.MaxVReplicationTransactionLag = int64(meta.maxVReplicationTransactionLag) +} + +func (wf *workflowFetcher) fetchStreamLogs(ctx context.Context, keyspace string, workflow *vtctldatapb.Workflow) { + span, ctx := trace.NewSpan(ctx, "workflowFetcher.workflow.fetchStreamLogs") + defer span.Finish() + + span.Annotate("keyspace", keyspace) + span.Annotate("workflow", workflow.Name) + + vreplIDs := make([]int64, 0, len(workflow.ShardStreams)) + for _, shardStream := range maps.Values(workflow.ShardStreams) { + for _, stream := range shardStream.Streams { + vreplIDs = append(vreplIDs, stream.Id) + } + } + idsBV, err := sqltypes.BuildBindVariable(vreplIDs) + if err != nil { + return + } + + query, err := sqlparser.ParseAndBind(vrepLogQuery, idsBV) + if err != nil { + return + } + + vx := vexec.NewVExec(keyspace, workflow.Name, wf.ts, wf.tmc, wf.parser) + results, err := vx.QueryContext(ctx, query) + if err != nil { + // Note that we do not return here. If there are any query results + // in the map (i.e. some tablets returned successfully), we will + // still try to read log rows from them on a best-effort basis. But, + // we will also pre-emptively record the top-level fetch error on + // every stream in every shard in the workflow. Further processing + // below may override the error message for certain streams. + for _, streams := range workflow.ShardStreams { + for _, stream := range streams.Streams { + stream.LogFetchError = err.Error() + } + } + } + + for target, p3qr := range results { + qr := sqltypes.Proto3ToResult(p3qr) + shardStreamKey := fmt.Sprintf("%s/%s", target.Shard, target.AliasString()) + + ss, ok := workflow.ShardStreams[shardStreamKey] + if !ok || ss == nil { + continue + } + + streams := ss.Streams + streamIdx := 0 + markErrors := func(err error) { + if streamIdx >= len(streams) { + return + } + + streams[streamIdx].LogFetchError = err.Error() + } + + for _, row := range qr.Named().Rows { + id, err := row["id"].ToCastInt64() + if err != nil { + markErrors(err) + continue + } + + streamID, err := row["vrepl_id"].ToCastInt64() + if err != nil { + markErrors(err) + continue + } + + typ := row["type"].ToString() + state := row["state"].ToString() + message := row["message"].ToString() + + createdAt, err := time.Parse("2006-01-02 15:04:05", row["created_at"].ToString()) + if err != nil { + markErrors(err) + continue + } + + updatedAt, err := time.Parse("2006-01-02 15:04:05", row["updated_at"].ToString()) + if err != nil { + markErrors(err) + continue + } + + count, err := row["count"].ToCastInt64() + if err != nil { + markErrors(err) + continue + } + + streamLog := &vtctldatapb.Workflow_Stream_Log{ + Id: id, + StreamId: streamID, + Type: typ, + State: state, + CreatedAt: &vttimepb.Time{ + Seconds: createdAt.Unix(), + }, + UpdatedAt: &vttimepb.Time{ + Seconds: updatedAt.Unix(), + }, + Message: message, + Count: count, + } + + // Earlier, in buildWorkflows, we sorted each ShardStreams + // slice by ascending id, and our _vt.vreplication_log query + // ordered by (stream_id ASC, id ASC), so we can walk the + // streams in index order in O(n) amortized over all the rows + // for this tablet. + for streamIdx < len(streams) { + stream := streams[streamIdx] + if stream.Id < streamLog.StreamId { + streamIdx++ + continue + } + + if stream.Id > streamLog.StreamId { + wf.logger.Warningf("Found stream log for nonexistent stream: %+v", streamLog) + // This can happen on manual/failed workflow cleanup so move to the next log. + break + } + + // stream.Id == streamLog.StreamId + stream.Logs = append(stream.Logs, streamLog) + break + } + } + } +} + +func (wf *workflowFetcher) forAllShards( + ctx context.Context, + keyspace string, + shards []string, + f func(ctx context.Context, shard *topo.ShardInfo) error, +) error { + eg, egCtx := errgroup.WithContext(ctx) + for _, shard := range shards { + eg.Go(func() error { + si, err := wf.ts.GetShard(ctx, keyspace, shard) + if err != nil { + return err + } + if si.PrimaryAlias == nil { + return fmt.Errorf("%w %s/%s", vexec.ErrNoShardPrimary, keyspace, shard) + } + + if err := f(egCtx, si); err != nil { + return err + } + return nil + }) + } + if err := eg.Wait(); err != nil { + return err + } + return nil +} + +func getStreamState(stream *vtctldatapb.Workflow_Stream, rstream *tabletmanagerdatapb.ReadVReplicationWorkflowResponse_Stream) string { + switch { + case strings.Contains(strings.ToLower(stream.Message), "error"): + return binlogdatapb.VReplicationWorkflowState_Error.String() + case stream.State == binlogdatapb.VReplicationWorkflowState_Running.String() && len(stream.CopyStates) > 0: + return binlogdatapb.VReplicationWorkflowState_Copying.String() + case stream.State == binlogdatapb.VReplicationWorkflowState_Running.String() && int64(time.Now().Second())-rstream.TimeUpdated.Seconds > 10: + return binlogdatapb.VReplicationWorkflowState_Lagging.String() + } + return rstream.State.String() +} diff --git a/go/vt/vtctl/workflow/workflows_test.go b/go/vt/vtctl/workflow/workflows_test.go new file mode 100644 index 00000000000..2015c8d1b7c --- /dev/null +++ b/go/vt/vtctl/workflow/workflows_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 workflow + +import ( + "context" + "fmt" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "vitess.io/vitess/go/sqltypes" + "vitess.io/vitess/go/vt/proto/binlogdata" + "vitess.io/vitess/go/vt/proto/vttime" + "vitess.io/vitess/go/vt/topo" + + tabletmanagerdatapb "vitess.io/vitess/go/vt/proto/tabletmanagerdata" + topodatapb "vitess.io/vitess/go/vt/proto/topodata" + vtctldatapb "vitess.io/vitess/go/vt/proto/vtctldata" +) + +func TestGetStreamState(t *testing.T) { + testCases := []struct { + name string + stream *vtctldatapb.Workflow_Stream + rstream *tabletmanagerdatapb.ReadVReplicationWorkflowResponse_Stream + want string + }{ + { + name: "error state", + stream: &vtctldatapb.Workflow_Stream{ + Message: "test error", + }, + want: "Error", + }, + { + name: "copying state", + stream: &vtctldatapb.Workflow_Stream{ + State: "Running", + CopyStates: []*vtctldatapb.Workflow_Stream_CopyState{ + { + Table: "table1", + }, + }, + }, + want: "Copying", + }, + { + name: "lagging state", + stream: &vtctldatapb.Workflow_Stream{ + State: "Running", + }, + rstream: &tabletmanagerdatapb.ReadVReplicationWorkflowResponse_Stream{ + TimeUpdated: &vttime.Time{ + Seconds: int64(time.Now().Second()) - 11, + }, + }, + want: "Lagging", + }, + { + name: "non-running and error free", + stream: &vtctldatapb.Workflow_Stream{ + State: "Stopped", + }, + rstream: &tabletmanagerdatapb.ReadVReplicationWorkflowResponse_Stream{ + State: binlogdata.VReplicationWorkflowState_Stopped, + }, + want: "Stopped", + }, + } + + for _, tt := range testCases { + t.Run(tt.name, func(t *testing.T) { + state := getStreamState(tt.stream, tt.rstream) + assert.Equal(t, tt.want, state) + }) + } +} + +func TestGetWorkflowCopyStates(t *testing.T) { + ctx := context.Background() + + sourceShards := []string{"-"} + targetShards := []string{"-"} + + te := newTestMaterializerEnv(t, ctx, &vtctldatapb.MaterializeSettings{ + SourceKeyspace: "source_keyspace", + TargetKeyspace: "target_keyspace", + Workflow: "test_workflow", + TableSettings: []*vtctldatapb.TableMaterializeSettings{ + { + TargetTable: "table1", + SourceExpression: fmt.Sprintf("select * from %s", "table1"), + }, + { + TargetTable: "table2", + SourceExpression: fmt.Sprintf("select * from %s", "table2"), + }, + }, + }, sourceShards, targetShards) + + wf := workflowFetcher{ + ts: te.ws.ts, + tmc: te.tmc, + } + + tablet := &topodatapb.Tablet{ + Alias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 100, + }, + } + + query := "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)" + te.tmc.expectVRQuery(100, query, sqltypes.MakeTestResult( + sqltypes.MakeTestFields("vrepl_id|table_name|lastpk", "int64|varchar|varchar"), + "1|table1|2", "1|table2|1", + )) + + copyStates, err := wf.getWorkflowCopyStates(ctx, &topo.TabletInfo{ + Tablet: tablet, + }, []int32{1}) + assert.NoError(t, err) + assert.Len(t, copyStates, 2) + + state1 := &vtctldatapb.Workflow_Stream_CopyState{ + Table: "table1", + LastPk: "2", + StreamId: 1, + } + state2 := &vtctldatapb.Workflow_Stream_CopyState{ + Table: "table2", + LastPk: "1", + StreamId: 1, + } + assert.Contains(t, copyStates, state1) + assert.Contains(t, copyStates, state2) +} + +func TestFetchCopyStatesByShardStream(t *testing.T) { + ctx := context.Background() + + sourceShards := []string{"-"} + targetShards := []string{"-"} + + te := newTestMaterializerEnv(t, ctx, &vtctldatapb.MaterializeSettings{ + SourceKeyspace: "source_keyspace", + TargetKeyspace: "target_keyspace", + Workflow: "test_workflow", + TableSettings: []*vtctldatapb.TableMaterializeSettings{ + { + TargetTable: "table1", + SourceExpression: fmt.Sprintf("select * from %s", "table1"), + }, + { + TargetTable: "table2", + SourceExpression: fmt.Sprintf("select * from %s", "table2"), + }, + }, + }, sourceShards, targetShards) + + wf := workflowFetcher{ + ts: te.ws.ts, + tmc: te.tmc, + } + + tablet := &topodatapb.Tablet{ + Shard: "-80", + Alias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 100, + }, + } + tablet2 := &topodatapb.Tablet{ + Shard: "80-", + Alias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 101, + }, + } + + query := "select vrepl_id, table_name, lastpk from _vt.copy_state where vrepl_id in (1, 2) and id in (select max(id) from _vt.copy_state where vrepl_id in (1, 2) group by vrepl_id, table_name)" + te.tmc.expectVRQuery(100, query, sqltypes.MakeTestResult( + sqltypes.MakeTestFields("vrepl_id|table_name|lastpk", "int64|varchar|varchar"), + "1|table1|2", "2|table2|1", "2|table1|1", + )) + + te.tmc.expectVRQuery(101, query, sqltypes.MakeTestResult( + sqltypes.MakeTestFields("vrepl_id|table_name|lastpk", "int64|varchar|varchar"), + "1|table1|2", "1|table2|1", + )) + + ti := &topo.TabletInfo{ + Tablet: tablet, + } + ti2 := &topo.TabletInfo{ + Tablet: tablet2, + } + + readVReplicationResponse := map[*topo.TabletInfo]*tabletmanagerdatapb.ReadVReplicationWorkflowsResponse{ + ti: { + Workflows: []*tabletmanagerdatapb.ReadVReplicationWorkflowResponse{ + { + Streams: []*tabletmanagerdatapb.ReadVReplicationWorkflowResponse_Stream{ + { + Id: 1, + }, { + Id: 2, + }, + }, + }, + }, + }, + ti2: { + Workflows: []*tabletmanagerdatapb.ReadVReplicationWorkflowResponse{ + { + Streams: []*tabletmanagerdatapb.ReadVReplicationWorkflowResponse_Stream{ + { + Id: 1, + }, { + Id: 2, + }, + }, + }, + }, + }, + } + copyStatesByStreamId, err := wf.fetchCopyStatesByShardStream(ctx, readVReplicationResponse) + assert.NoError(t, err) + + copyStates1 := copyStatesByStreamId["-80/1"] + copyStates2 := copyStatesByStreamId["-80/2"] + copyStates3 := copyStatesByStreamId["80-/1"] + + require.NotNil(t, copyStates1) + require.NotNil(t, copyStates2) + require.NotNil(t, copyStates3) + + assert.Len(t, copyStates1, 1) + assert.Len(t, copyStates2, 2) + assert.Len(t, copyStates3, 2) + + assert.Nil(t, copyStatesByStreamId["80-/2"]) +} diff --git a/go/vt/vtexplain/vtexplain_vtgate.go b/go/vt/vtexplain/vtexplain_vtgate.go index d511e2d2ea0..f9ae8be3820 100644 --- a/go/vt/vtexplain/vtexplain_vtgate.go +++ b/go/vt/vtexplain/vtexplain_vtgate.go @@ -38,6 +38,7 @@ import ( "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vtgate" "vitess.io/vitess/go/vt/vtgate/engine" + econtext "vitess.io/vitess/go/vt/vtgate/executorcontext" "vitess.io/vitess/go/vt/vtgate/logstats" "vitess.io/vitess/go/vt/vtgate/vindexes" "vitess.io/vitess/go/vt/vttablet/queryservice" @@ -235,7 +236,7 @@ func (vte *VTExplain) vtgateExecute(sql string) ([]*engine.Plan, map[string]*Tab // This will ensure that the commit/rollback order is predictable. vte.sortShardSession() - _, err := vte.vtgateExecutor.Execute(context.Background(), nil, "VtexplainExecute", vtgate.NewSafeSession(vte.vtgateSession), sql, nil) + _, err := vte.vtgateExecutor.Execute(context.Background(), nil, "VtexplainExecute", econtext.NewSafeSession(vte.vtgateSession), sql, nil) if err != nil { for _, tc := range vte.explainTopo.TabletConns { tc.tabletQueries = nil diff --git a/go/vt/vtexplain/vtexplain_vttablet.go b/go/vt/vtexplain/vtexplain_vttablet.go index 38a3ca7bbb3..65cd1a96181 100644 --- a/go/vt/vtexplain/vtexplain_vttablet.go +++ b/go/vt/vtexplain/vtexplain_vttablet.go @@ -22,6 +22,7 @@ import ( "reflect" "strings" "sync" + "time" "vitess.io/vitess/go/stats" "vitess.io/vitess/go/vt/sidecardb" @@ -113,8 +114,7 @@ func (vte *VTExplain) newTablet(ctx context.Context, env *vtenv.Environment, opt config := tabletenv.NewCurrentConfig() config.TrackSchemaVersions = false if opts.ExecutionMode == ModeTwoPC { - config.TwoPCAbandonAge = 1.0 - config.TwoPCEnable = true + config.TwoPCAbandonAge = 1 * time.Second } config.EnableOnlineDDL = false config.EnableTableGC = false diff --git a/go/vt/vtgate/autocommit_test.go b/go/vt/vtgate/autocommit_test.go index 1ba99c01ef2..2e65cefbabe 100644 --- a/go/vt/vtgate/autocommit_test.go +++ b/go/vt/vtgate/autocommit_test.go @@ -23,10 +23,10 @@ import ( "github.com/stretchr/testify/require" "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" + econtext "vitess.io/vitess/go/vt/vtgate/executorcontext" ) // This file contains tests for all the autocommit code paths @@ -382,7 +382,7 @@ func TestAutocommitTransactionStarted(t *testing.T) { // single shard query - no savepoint needed sql := "update `user` set a = 2 where id = 1" - _, err := executor.Execute(context.Background(), nil, "TestExecute", NewSafeSession(session), sql, map[string]*querypb.BindVariable{}) + _, err := executor.Execute(context.Background(), nil, "TestExecute", econtext.NewSafeSession(session), sql, map[string]*querypb.BindVariable{}) require.NoError(t, err) require.Len(t, sbc1.Queries, 1) require.Equal(t, sql, sbc1.Queries[0].Sql) @@ -394,7 +394,7 @@ func TestAutocommitTransactionStarted(t *testing.T) { // multi shard query - savepoint needed sql = "update `user` set a = 2 where id in (1, 4)" expectedSql := "update `user` set a = 2 where id in ::__vals" - _, err = executor.Execute(context.Background(), nil, "TestExecute", NewSafeSession(session), sql, map[string]*querypb.BindVariable{}) + _, err = executor.Execute(context.Background(), nil, "TestExecute", econtext.NewSafeSession(session), sql, map[string]*querypb.BindVariable{}) require.NoError(t, err) require.Len(t, sbc1.Queries, 2) require.Contains(t, sbc1.Queries[0].Sql, "savepoint") @@ -413,7 +413,7 @@ func TestAutocommitDirectTarget(t *testing.T) { } sql := "insert into `simple`(val) values ('val')" - _, err := executor.Execute(context.Background(), nil, "TestExecute", NewSafeSession(session), sql, map[string]*querypb.BindVariable{}) + _, err := executor.Execute(context.Background(), nil, "TestExecute", econtext.NewSafeSession(session), sql, map[string]*querypb.BindVariable{}) require.NoError(t, err) assertQueries(t, sbclookup, []*querypb.BoundQuery{{ @@ -434,7 +434,7 @@ func TestAutocommitDirectRangeTarget(t *testing.T) { } sql := "delete from sharded_user_msgs limit 1000" - _, err := executor.Execute(context.Background(), nil, "TestExecute", NewSafeSession(session), sql, map[string]*querypb.BindVariable{}) + _, err := executor.Execute(context.Background(), nil, "TestExecute", econtext.NewSafeSession(session), sql, map[string]*querypb.BindVariable{}) require.NoError(t, err) assertQueries(t, sbc1, []*querypb.BoundQuery{{ @@ -451,5 +451,5 @@ func autocommitExec(executor *Executor, sql string) (*sqltypes.Result, error) { TransactionMode: vtgatepb.TransactionMode_MULTI, } - return executor.Execute(context.Background(), nil, "TestExecute", NewSafeSession(session), sql, map[string]*querypb.BindVariable{}) + return executor.Execute(context.Background(), nil, "TestExecute", econtext.NewSafeSession(session), sql, map[string]*querypb.BindVariable{}) } diff --git a/go/vt/vtgate/buffer/buffer.go b/go/vt/vtgate/buffer/buffer.go index dec83e2c78c..eb937a6361c 100644 --- a/go/vt/vtgate/buffer/buffer.go +++ b/go/vt/vtgate/buffer/buffer.go @@ -208,6 +208,7 @@ func (b *Buffer) WaitForFailoverEnd(ctx context.Context, keyspace, shard string, } func (b *Buffer) HandleKeyspaceEvent(ksevent *discovery.KeyspaceEvent) { + log.Infof("Keyspace Event received for keyspace %v", ksevent.Keyspace) for _, shard := range ksevent.Shards { sb := b.getOrCreateBuffer(shard.Target.Keyspace, shard.Target.Shard) if sb != nil { diff --git a/go/vt/vtgate/buffer/shard_buffer.go b/go/vt/vtgate/buffer/shard_buffer.go index e1f02bb7f0e..66c6ee702e6 100644 --- a/go/vt/vtgate/buffer/shard_buffer.go +++ b/go/vt/vtgate/buffer/shard_buffer.go @@ -286,7 +286,7 @@ func (sb *shardBuffer) startBufferingLocked(ctx context.Context, kev *discovery. msg = "Dry-run: Would have started buffering" } starts.Add(sb.statsKey, 1) - log.Infof("%v for shard: %s (window: %v, size: %v, max failover duration: %v) (A failover was detected by this seen error: %v.)", + log.V(2).Infof("%v for shard: %s (window: %v, size: %v, max failover duration: %v) (A failover was detected by this seen error: %v.)", msg, topoproto.KeyspaceShardString(sb.keyspace, sb.shard), sb.buf.config.Window, @@ -488,7 +488,7 @@ func (sb *shardBuffer) recordKeyspaceEvent(alias *topodatapb.TabletAlias, stillS sb.mu.Lock() defer sb.mu.Unlock() - log.Infof("disruption in shard %s/%s resolved (serving: %v), movetable state %#v", + log.V(2).Infof("disruption in shard %s/%s resolved (serving: %v), movetable state %#v", sb.keyspace, sb.shard, stillServing, keyspaceEvent.MoveTablesState) if !topoproto.TabletAliasEqual(alias, sb.currentPrimary) { @@ -562,7 +562,7 @@ func (sb *shardBuffer) stopBufferingLocked(reason stopReason, details string) { if sb.mode == bufferModeDryRun { msg = "Dry-run: Would have stopped buffering" } - log.Infof("%v for shard: %s after: %.1f seconds due to: %v. Draining %d buffered requests now.", + log.V(2).Infof("%v for shard: %s after: %.1f seconds due to: %v. Draining %d buffered requests now.", msg, topoproto.KeyspaceShardString(sb.keyspace, sb.shard), d.Seconds(), details, len(q)) var clientEntryError error @@ -622,7 +622,7 @@ func (sb *shardBuffer) drain(q []*entry, err error) { 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)) + log.V(2).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))) // Draining is done. Change state from "draining" to "idle". diff --git a/go/vt/vtgate/debugenv.go b/go/vt/vtgate/debugenv.go index 4fa989c69a3..7213353432d 100644 --- a/go/vt/vtgate/debugenv.go +++ b/go/vt/vtgate/debugenv.go @@ -22,9 +22,10 @@ import ( "html" "net/http" "strconv" - "text/template" "time" + "github.com/google/safehtml/template" + "vitess.io/vitess/go/acl" "vitess.io/vitess/go/vt/discovery" "vitess.io/vitess/go/vt/log" diff --git a/go/vt/vtgate/dynamicconfig/config.go b/go/vt/vtgate/dynamicconfig/config.go new file mode 100644 index 00000000000..5bb1d991eae --- /dev/null +++ b/go/vt/vtgate/dynamicconfig/config.go @@ -0,0 +1,6 @@ +package dynamicconfig + +type DDL interface { + OnlineEnabled() bool + DirectEnabled() bool +} diff --git a/go/vt/vtgate/engine/cached_size.go b/go/vt/vtgate/engine/cached_size.go index 4c0d1009bd1..c764a6aab08 100644 --- a/go/vt/vtgate/engine/cached_size.go +++ b/go/vt/vtgate/engine/cached_size.go @@ -131,7 +131,7 @@ func (cached *DDL) CachedSize(alloc bool) int64 { } size := int64(0) if alloc { - size += int64(64) + size += int64(80) } // field Keyspace *vitess.io/vitess/go/vt/vtgate/vindexes.Keyspace size += cached.Keyspace.CachedSize(true) @@ -145,6 +145,10 @@ func (cached *DDL) CachedSize(alloc bool) int64 { size += cached.NormalDDL.CachedSize(true) // field OnlineDDL *vitess.io/vitess/go/vt/vtgate/engine.OnlineDDL size += cached.OnlineDDL.CachedSize(true) + // field Config vitess.io/vitess/go/vt/vtgate/dynamicconfig.DDL + if cc, ok := cached.Config.(cachedObject); ok { + size += cc.CachedSize(true) + } return size } func (cached *DML) CachedSize(alloc bool) int64 { diff --git a/go/vt/vtgate/engine/ddl.go b/go/vt/vtgate/engine/ddl.go index cfdaa5866dc..d7e17eb4f4f 100644 --- a/go/vt/vtgate/engine/ddl.go +++ b/go/vt/vtgate/engine/ddl.go @@ -25,6 +25,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/vtgate/dynamicconfig" "vitess.io/vitess/go/vt/vtgate/vindexes" ) @@ -42,8 +43,7 @@ type DDL struct { NormalDDL *Send OnlineDDL *OnlineDDL - DirectDDLEnabled bool - OnlineDDLEnabled bool + Config dynamicconfig.DDL CreateTempTable bool } @@ -107,12 +107,12 @@ func (ddl *DDL) TryExecute(ctx context.Context, vcursor VCursor, bindVars map[st switch { case ddl.isOnlineSchemaDDL(): - if !ddl.OnlineDDLEnabled { + if !ddl.Config.OnlineEnabled() { return nil, schema.ErrOnlineDDLDisabled } return vcursor.ExecutePrimitive(ctx, ddl.OnlineDDL, bindVars, wantfields) default: // non online-ddl - if !ddl.DirectDDLEnabled { + if !ddl.Config.DirectEnabled() { return nil, schema.ErrDirectDDLDisabled } return vcursor.ExecutePrimitive(ctx, ddl.NormalDDL, bindVars, wantfields) diff --git a/go/vt/vtgate/engine/ddl_test.go b/go/vt/vtgate/engine/ddl_test.go index 3f7ccb75f70..1d52089bf39 100644 --- a/go/vt/vtgate/engine/ddl_test.go +++ b/go/vt/vtgate/engine/ddl_test.go @@ -27,13 +27,23 @@ import ( "vitess.io/vitess/go/vt/vtgate/vindexes" ) +type ddlConfig struct{} + +func (ddlConfig) DirectEnabled() bool { + return true +} + +func (ddlConfig) OnlineEnabled() bool { + return true +} + func TestDDL(t *testing.T) { ddl := &DDL{ DDL: &sqlparser.CreateTable{ Table: sqlparser.NewTableName("a"), }, - DirectDDLEnabled: true, - OnlineDDL: &OnlineDDL{}, + Config: ddlConfig{}, + OnlineDDL: &OnlineDDL{}, NormalDDL: &Send{ Keyspace: &vindexes.Keyspace{ Name: "ks", diff --git a/go/vt/vtgate/engine/plan_description.go b/go/vt/vtgate/engine/plan_description.go index dfcad4e5e6b..e8e763c1ee1 100644 --- a/go/vt/vtgate/engine/plan_description.go +++ b/go/vt/vtgate/engine/plan_description.go @@ -126,6 +126,133 @@ func (pd PrimitiveDescription) MarshalJSON() ([]byte, error) { return buf.Bytes(), nil } +// PrimitiveDescriptionFromString creates primitive description out of a data string. +func PrimitiveDescriptionFromString(data string) (pd PrimitiveDescription, err error) { + resultMap := make(map[string]any) + err = json.Unmarshal([]byte(data), &resultMap) + if err != nil { + return PrimitiveDescription{}, err + } + return PrimitiveDescriptionFromMap(resultMap) +} + +// PrimitiveDescriptionFromMap populates the fields of a PrimitiveDescription from a map representation. +func PrimitiveDescriptionFromMap(data map[string]any) (pd PrimitiveDescription, err error) { + if opType, isPresent := data["OperatorType"]; isPresent { + pd.OperatorType = opType.(string) + } + if variant, isPresent := data["Variant"]; isPresent { + pd.Variant = variant.(string) + } + if ksMap, isPresent := data["Keyspace"]; isPresent { + ksMap := ksMap.(map[string]any) + pd.Keyspace = &vindexes.Keyspace{ + Name: ksMap["Name"].(string), + Sharded: ksMap["Sharded"].(bool), + } + } + if ttt, isPresent := data["TargetTabletType"]; isPresent { + pd.TargetTabletType = topodatapb.TabletType(ttt.(int)) + } + if other, isPresent := data["Other"]; isPresent { + pd.Other = other.(map[string]any) + } + if inpName, isPresent := data["InputName"]; isPresent { + pd.InputName = inpName.(string) + } + if avgRows, isPresent := data["AvgNumberOfRows"]; isPresent { + pd.RowsReceived = RowsReceived{ + int(avgRows.(float64)), + } + } + if sq, isPresent := data["ShardsQueried"]; isPresent { + sq := int(sq.(float64)) + pd.ShardsQueried = (*ShardsQueried)(&sq) + } + if inputs, isPresent := data["Inputs"]; isPresent { + inputs := inputs.([]any) + for _, input := range inputs { + inputMap := input.(map[string]any) + inp, err := PrimitiveDescriptionFromMap(inputMap) + if err != nil { + return PrimitiveDescription{}, err + } + pd.Inputs = append(pd.Inputs, inp) + } + } + return pd, nil +} + +// WalkPrimitiveDescription walks the primitive description. +func WalkPrimitiveDescription(pd PrimitiveDescription, f func(PrimitiveDescription)) { + f(pd) + for _, child := range pd.Inputs { + WalkPrimitiveDescription(child, f) + } +} + +func (pd PrimitiveDescription) Equals(other PrimitiveDescription) string { + if pd.Variant != other.Variant { + return fmt.Sprintf("Variant: %v != %v", pd.Variant, other.Variant) + } + + if pd.OperatorType != other.OperatorType { + return fmt.Sprintf("OperatorType: %v != %v", pd.OperatorType, other.OperatorType) + } + + // TODO (harshit): enable this to compare keyspace as well + // switch { + // case pd.Keyspace == nil && other.Keyspace == nil: + // // do nothing + // case pd.Keyspace != nil && other.Keyspace != nil: + // if pd.Keyspace.Name != other.Keyspace.Name { + // return fmt.Sprintf("Keyspace.Name: %v != %v", pd.Keyspace.Name, other.Keyspace.Name) + // } + // default: + // return "Keyspace is nil in one of the descriptions" + // } + + switch { + case pd.TargetDestination == nil && other.TargetDestination == nil: + // do nothing + case pd.TargetDestination != nil && other.TargetDestination != nil: + if pd.TargetDestination.String() != other.TargetDestination.String() { + return fmt.Sprintf("TargetDestination: %v != %v", pd.TargetDestination, other.TargetDestination) + } + default: + return "TargetDestination is nil in one of the descriptions" + } + + if pd.TargetTabletType != other.TargetTabletType { + return fmt.Sprintf("TargetTabletType: %v != %v", pd.TargetTabletType, other.TargetTabletType) + } + + switch { + case pd.Other == nil && other.Other == nil: + // do nothing + case pd.Other != nil && other.Other != nil: + if len(pd.Other) != len(other.Other) { + return fmt.Sprintf("Other length did not match: %v != %v", pd.Other, other.Other) + } + for ky, val := range pd.Other { + if other.Other[ky] != val { + return fmt.Sprintf("Other[%v]: %v != %v", ky, val, other.Other[ky]) + } + } + default: + return "Other is nil in one of the descriptions" + } + if len(pd.Inputs) != len(other.Inputs) { + return fmt.Sprintf("Inputs length did not match: %v != %v", len(pd.Inputs), len(other.Inputs)) + } + for idx, input := range pd.Inputs { + if diff := input.Equals(other.Inputs[idx]); diff != "" { + return diff + } + } + return "" +} + func average(nums []int) float64 { total := 0 for _, num := range nums { diff --git a/go/vt/vtgate/evalengine/eval_result.go b/go/vt/vtgate/evalengine/eval_result.go index d9916af03be..5c1973d8eb1 100644 --- a/go/vt/vtgate/evalengine/eval_result.go +++ b/go/vt/vtgate/evalengine/eval_result.go @@ -62,6 +62,7 @@ func (er EvalResult) String() string { // TupleValues allows for retrieval of the value we expose for public consumption func (er EvalResult) TupleValues() []sqltypes.Value { + // TODO: Make this collation-aware switch v := er.v.(type) { case *evalTuple: result := make([]sqltypes.Value, 0, len(v.t)) diff --git a/go/vt/vtgate/executor.go b/go/vt/vtgate/executor.go index 928f42fca30..0bb47361f55 100644 --- a/go/vt/vtgate/executor.go +++ b/go/vt/vtgate/executor.go @@ -30,6 +30,8 @@ import ( "github.com/spf13/pflag" + vschemapb "vitess.io/vitess/go/vt/proto/vschema" + "vitess.io/vitess/go/acl" "vitess.io/vitess/go/cache/theine" "vitess.io/vitess/go/mysql/capabilities" @@ -57,6 +59,7 @@ import ( "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vtgate/engine" "vitess.io/vitess/go/vt/vtgate/evalengine" + econtext "vitess.io/vitess/go/vt/vtgate/executorcontext" "vitess.io/vitess/go/vt/vtgate/logstats" "vitess.io/vitess/go/vt/vtgate/planbuilder" "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" @@ -67,7 +70,6 @@ import ( ) var ( - errNoKeyspace = vterrors.VT09005() defaultTabletType = topodatapb.TabletType_PRIMARY // TODO: @rafael - These two counters should be deprecated in favor of the ByTable ones in v17+. They are kept for now for backwards compatibility. @@ -111,7 +113,6 @@ type Executor struct { resolver *Resolver scatterConn *ScatterConn txConn *TxConn - pv plancontext.PlannerVersion mu sync.Mutex vschema *vindexes.VSchema @@ -121,8 +122,7 @@ type Executor struct { plans *PlanCache epoch atomic.Uint32 - normalize bool - warnShardedOnly bool + normalize bool vm *VSchemaManager schemaTracker SchemaInfo @@ -135,6 +135,8 @@ type Executor struct { warmingReadsPercent int warmingReadsChannel chan bool + + vConfig econtext.VCursorConfig } var executorOnce sync.Once @@ -175,17 +177,16 @@ func NewExecutor( scatterConn: resolver.scatterConn, txConn: resolver.scatterConn.txConn, normalize: normalize, - warnShardedOnly: warnOnShardedOnly, streamSize: streamSize, schemaTracker: schemaTracker, allowScatter: !noScatter, - pv: pv, plans: plans, warmingReadsPercent: warmingReadsPercent, warmingReadsChannel: make(chan bool, warmingReadsConcurrency), } + // setting the vcursor config. + e.initVConfig(warnOnShardedOnly, pv) - vschemaacl.Init() // we subscribe to update from the VSchemaManager e.vm = &VSchemaManager{ subscriber: e.SaveVSchema, @@ -223,7 +224,7 @@ func NewExecutor( } // Execute executes a non-streaming query. -func (e *Executor) Execute(ctx context.Context, mysqlCtx vtgateservice.MySQLConnection, method string, safeSession *SafeSession, sql string, bindVars map[string]*querypb.BindVariable) (result *sqltypes.Result, err error) { +func (e *Executor) Execute(ctx context.Context, mysqlCtx vtgateservice.MySQLConnection, method string, safeSession *econtext.SafeSession, sql string, bindVars map[string]*querypb.BindVariable) (result *sqltypes.Result, err error) { span, ctx := trace.NewSpan(ctx, "executor.Execute") span.Annotate("method", method) trace.AnnotateSQL(span, sqlparser.Preview(sql)) @@ -286,7 +287,7 @@ func (e *Executor) StreamExecute( ctx context.Context, mysqlCtx vtgateservice.MySQLConnection, method string, - safeSession *SafeSession, + safeSession *econtext.SafeSession, sql string, bindVars map[string]*querypb.BindVariable, callback func(*sqltypes.Result) error, @@ -300,7 +301,7 @@ func (e *Executor) StreamExecute( srr := &streaminResultReceiver{callback: callback} var err error - resultHandler := func(ctx context.Context, plan *engine.Plan, vc *vcursorImpl, bindVars map[string]*querypb.BindVariable, execStart time.Time) error { + resultHandler := func(ctx context.Context, plan *engine.Plan, vc *econtext.VCursorImpl, bindVars map[string]*querypb.BindVariable, execStart time.Time) error { var seenResults atomic.Bool var resultMu sync.Mutex result := &sqltypes.Result{} @@ -368,7 +369,7 @@ func (e *Executor) StreamExecute( logStats.TablesUsed = plan.TablesUsed logStats.TabletType = vc.TabletType().String() logStats.ExecuteTime = time.Since(execStart) - logStats.ActiveKeyspace = vc.keyspace + logStats.ActiveKeyspace = vc.GetKeyspace() e.updateQueryCounts(plan.Instructions.RouteType(), plan.Instructions.GetKeyspaceName(), plan.Instructions.GetTableName(), int64(logStats.ShardQueries)) @@ -411,12 +412,12 @@ func canReturnRows(stmtType sqlparser.StatementType) bool { } } -func saveSessionStats(safeSession *SafeSession, stmtType sqlparser.StatementType, rowsAffected, insertID uint64, rowsReturned int, err error) { +func saveSessionStats(safeSession *econtext.SafeSession, stmtType sqlparser.StatementType, rowsAffected, insertID uint64, rowsReturned int, err error) { safeSession.RowCount = -1 if err != nil { return } - if !safeSession.foundRowsHandled { + if !safeSession.IsFoundRowsHandled() { safeSession.FoundRows = uint64(rowsReturned) } if insertID > 0 { @@ -430,11 +431,11 @@ func saveSessionStats(safeSession *SafeSession, stmtType sqlparser.StatementType } } -func (e *Executor) execute(ctx context.Context, mysqlCtx vtgateservice.MySQLConnection, safeSession *SafeSession, sql string, bindVars map[string]*querypb.BindVariable, logStats *logstats.LogStats) (sqlparser.StatementType, *sqltypes.Result, error) { +func (e *Executor) execute(ctx context.Context, mysqlCtx vtgateservice.MySQLConnection, safeSession *econtext.SafeSession, sql string, bindVars map[string]*querypb.BindVariable, logStats *logstats.LogStats) (sqlparser.StatementType, *sqltypes.Result, error) { var err error var qr *sqltypes.Result var stmtType sqlparser.StatementType - err = e.newExecute(ctx, mysqlCtx, safeSession, sql, bindVars, logStats, func(ctx context.Context, plan *engine.Plan, vc *vcursorImpl, bindVars map[string]*querypb.BindVariable, time time.Time) error { + err = e.newExecute(ctx, mysqlCtx, safeSession, sql, bindVars, logStats, func(ctx context.Context, plan *engine.Plan, vc *econtext.VCursorImpl, bindVars map[string]*querypb.BindVariable, time time.Time) error { stmtType = plan.Type qr, err = e.executePlan(ctx, safeSession, plan, vc, bindVars, logStats, time) return err @@ -448,7 +449,7 @@ func (e *Executor) execute(ctx context.Context, mysqlCtx vtgateservice.MySQLConn } // addNeededBindVars adds bind vars that are needed by the plan -func (e *Executor) addNeededBindVars(vcursor *vcursorImpl, bindVarNeeds *sqlparser.BindVarNeeds, bindVars map[string]*querypb.BindVariable, session *SafeSession) error { +func (e *Executor) addNeededBindVars(vcursor *econtext.VCursorImpl, bindVarNeeds *sqlparser.BindVarNeeds, bindVars map[string]*querypb.BindVariable, session *econtext.SafeSession) error { for _, funcName := range bindVarNeeds.NeedFunctionResult { switch funcName { case sqlparser.DBVarName: @@ -541,7 +542,7 @@ func (e *Executor) addNeededBindVars(vcursor *vcursorImpl, bindVarNeeds *sqlpars } evalExpr, err := evalengine.Translate(expr, &evalengine.Config{ - Collation: vcursor.collation, + Collation: vcursor.ConnCollation(), Environment: e.env, SQLMode: evalengine.ParseSQLMode(vcursor.SQLMode()), }) @@ -552,7 +553,7 @@ func (e *Executor) addNeededBindVars(vcursor *vcursorImpl, bindVarNeeds *sqlpars if err != nil { return err } - bindVars[key] = sqltypes.ValueBindVariable(evaluated.Value(vcursor.collation)) + bindVars[key] = sqltypes.ValueBindVariable(evaluated.Value(vcursor.ConnCollation())) } } } @@ -572,21 +573,21 @@ func (e *Executor) addNeededBindVars(vcursor *vcursorImpl, bindVarNeeds *sqlpars return nil } -func ifOptionsExist(session *SafeSession, f func(*querypb.ExecuteOptions)) { +func ifOptionsExist(session *econtext.SafeSession, f func(*querypb.ExecuteOptions)) { options := session.GetOptions() if options != nil { f(options) } } -func ifReadAfterWriteExist(session *SafeSession, f func(*vtgatepb.ReadAfterWrite)) { +func ifReadAfterWriteExist(session *econtext.SafeSession, f func(*vtgatepb.ReadAfterWrite)) { raw := session.ReadAfterWrite if raw != nil { f(raw) } } -func (e *Executor) handleBegin(ctx context.Context, safeSession *SafeSession, logStats *logstats.LogStats, stmt sqlparser.Statement) (*sqltypes.Result, error) { +func (e *Executor) handleBegin(ctx context.Context, safeSession *econtext.SafeSession, logStats *logstats.LogStats, stmt sqlparser.Statement) (*sqltypes.Result, error) { execStart := time.Now() logStats.PlanTime = execStart.Sub(logStats.StartTime) @@ -599,7 +600,7 @@ func (e *Executor) handleBegin(ctx context.Context, safeSession *SafeSession, lo return &sqltypes.Result{}, err } -func (e *Executor) handleCommit(ctx context.Context, safeSession *SafeSession, logStats *logstats.LogStats) (*sqltypes.Result, error) { +func (e *Executor) handleCommit(ctx context.Context, safeSession *econtext.SafeSession, logStats *logstats.LogStats) (*sqltypes.Result, error) { execStart := time.Now() logStats.PlanTime = execStart.Sub(logStats.StartTime) logStats.ShardQueries = uint64(len(safeSession.ShardSessions)) @@ -611,11 +612,11 @@ func (e *Executor) handleCommit(ctx context.Context, safeSession *SafeSession, l } // Commit commits the existing transactions -func (e *Executor) Commit(ctx context.Context, safeSession *SafeSession) error { +func (e *Executor) Commit(ctx context.Context, safeSession *econtext.SafeSession) error { return e.txConn.Commit(ctx, safeSession) } -func (e *Executor) handleRollback(ctx context.Context, safeSession *SafeSession, logStats *logstats.LogStats) (*sqltypes.Result, error) { +func (e *Executor) handleRollback(ctx context.Context, safeSession *econtext.SafeSession, logStats *logstats.LogStats) (*sqltypes.Result, error) { execStart := time.Now() logStats.PlanTime = execStart.Sub(logStats.StartTime) logStats.ShardQueries = uint64(len(safeSession.ShardSessions)) @@ -625,7 +626,7 @@ func (e *Executor) handleRollback(ctx context.Context, safeSession *SafeSession, return &sqltypes.Result{}, err } -func (e *Executor) handleSavepoint(ctx context.Context, safeSession *SafeSession, sql string, planType string, logStats *logstats.LogStats, nonTxResponse func(query string) (*sqltypes.Result, error), ignoreMaxMemoryRows bool) (*sqltypes.Result, error) { +func (e *Executor) handleSavepoint(ctx context.Context, safeSession *econtext.SafeSession, sql string, planType string, logStats *logstats.LogStats, nonTxResponse func(query string) (*sqltypes.Result, error), ignoreMaxMemoryRows bool) (*sqltypes.Result, error) { execStart := time.Now() logStats.PlanTime = execStart.Sub(logStats.StartTime) logStats.ShardQueries = uint64(len(safeSession.ShardSessions)) @@ -637,7 +638,7 @@ func (e *Executor) handleSavepoint(ctx context.Context, safeSession *SafeSession // If no transaction exists on any of the shard sessions, // then savepoint does not need to be executed, it will be only stored in the session // and later will be executed when a transaction is started. - if !safeSession.isTxOpen() { + if !safeSession.IsTxOpen() { if safeSession.InTransaction() { // Storing, as this needs to be executed just after starting transaction on the shard. safeSession.StoreSavepoint(sql) @@ -645,7 +646,7 @@ func (e *Executor) handleSavepoint(ctx context.Context, safeSession *SafeSession } return nonTxResponse(sql) } - orig := safeSession.commitOrder + orig := safeSession.GetCommitOrder() qr, err := e.executeSPInAllSessions(ctx, safeSession, sql, ignoreMaxMemoryRows) safeSession.SetCommitOrder(orig) if err != nil { @@ -657,7 +658,7 @@ func (e *Executor) handleSavepoint(ctx context.Context, safeSession *SafeSession // executeSPInAllSessions function executes the savepoint query in all open shard sessions (pre, normal and post) // which has non-zero transaction id (i.e. an open transaction on the shard connection). -func (e *Executor) executeSPInAllSessions(ctx context.Context, safeSession *SafeSession, sql string, ignoreMaxMemoryRows bool) (*sqltypes.Result, error) { +func (e *Executor) executeSPInAllSessions(ctx context.Context, safeSession *econtext.SafeSession, sql string, ignoreMaxMemoryRows bool) (*sqltypes.Result, error) { var qr *sqltypes.Result var errs []error for _, co := range []vtgatepb.CommitOrder{vtgatepb.CommitOrder_PRE, vtgatepb.CommitOrder_NORMAL, vtgatepb.CommitOrder_POST} { @@ -665,7 +666,7 @@ func (e *Executor) executeSPInAllSessions(ctx context.Context, safeSession *Safe var rss []*srvtopo.ResolvedShard var queries []*querypb.BoundQuery - for _, shardSession := range safeSession.getSessions() { + for _, shardSession := range safeSession.GetSessions() { // This will avoid executing savepoint on reserved connections // which has no open transaction. if shardSession.TransactionId == 0 { @@ -718,11 +719,11 @@ func (e *Executor) handleKill(ctx context.Context, mysqlCtx vtgateservice.MySQLC // CloseSession releases the current connection, which rollbacks open transactions and closes reserved connections. // It is called then the MySQL servers closes the connection to its client. -func (e *Executor) CloseSession(ctx context.Context, safeSession *SafeSession) error { +func (e *Executor) CloseSession(ctx context.Context, safeSession *econtext.SafeSession) error { return e.txConn.ReleaseAll(ctx, safeSession) } -func (e *Executor) setVitessMetadata(ctx context.Context, name, value string) error { +func (e *Executor) SetVitessMetadata(ctx context.Context, name, value string) error { // TODO(kalfonso): move to its own acl check and consolidate into an acl component that can handle multiple operations (vschema, metadata) user := callerid.ImmediateCallerIDFromContext(ctx) allowed := vschemaacl.Authorized(user) @@ -741,7 +742,7 @@ func (e *Executor) setVitessMetadata(ctx context.Context, name, value string) er return ts.UpsertMetadata(ctx, name, value) } -func (e *Executor) showVitessMetadata(ctx context.Context, filter *sqlparser.ShowFilter) (*sqltypes.Result, error) { +func (e *Executor) ShowVitessMetadata(ctx context.Context, filter *sqlparser.ShowFilter) (*sqltypes.Result, error) { ts, err := e.serv.GetTopoServer() if err != nil { return nil, err @@ -774,7 +775,7 @@ func (e *Executor) showVitessMetadata(ctx context.Context, filter *sqlparser.Sho type tabletFilter func(tablet *topodatapb.Tablet, servingState string, primaryTermStartTime int64) bool -func (e *Executor) showShards(ctx context.Context, filter *sqlparser.ShowFilter, destTabletType topodatapb.TabletType) (*sqltypes.Result, error) { +func (e *Executor) ShowShards(ctx context.Context, filter *sqlparser.ShowFilter, destTabletType topodatapb.TabletType) (*sqltypes.Result, error) { showVitessShardsFilters := func(filter *sqlparser.ShowFilter) ([]func(string) bool, []func(string, *topodatapb.ShardReference) bool) { keyspaceFilters := []func(string) bool{} shardFilters := []func(string, *topodatapb.ShardReference) bool{} @@ -858,7 +859,7 @@ func (e *Executor) showShards(ctx context.Context, filter *sqlparser.ShowFilter, }, nil } -func (e *Executor) showTablets(filter *sqlparser.ShowFilter) (*sqltypes.Result, error) { +func (e *Executor) ShowTablets(filter *sqlparser.ShowFilter) (*sqltypes.Result, error) { getTabletFilters := func(filter *sqlparser.ShowFilter) []tabletFilter { var filters []tabletFilter @@ -931,7 +932,7 @@ func (e *Executor) showTablets(filter *sqlparser.ShowFilter) (*sqltypes.Result, }, nil } -func (e *Executor) showVitessReplicationStatus(ctx context.Context, filter *sqlparser.ShowFilter) (*sqltypes.Result, error) { +func (e *Executor) ShowVitessReplicationStatus(ctx context.Context, filter *sqlparser.ShowFilter) (*sqltypes.Result, error) { ctx, cancel := context.WithTimeout(ctx, healthCheckTimeout) defer cancel() rows := [][]sqltypes.Value{} @@ -1078,26 +1079,14 @@ func (e *Executor) SaveVSchema(vschema *vindexes.VSchema, stats *VSchemaStats) { // ParseDestinationTarget parses destination target string and sets default keyspace if possible. func (e *Executor) ParseDestinationTarget(targetString string) (string, topodatapb.TabletType, key.Destination, error) { - destKeyspace, destTabletType, dest, err := topoproto.ParseDestination(targetString, defaultTabletType) - // Set default keyspace - if destKeyspace == "" && len(e.VSchema().Keyspaces) == 1 { - for k := range e.VSchema().Keyspaces { - destKeyspace = k - } - } - return destKeyspace, destTabletType, dest, err -} - -type iQueryOption interface { - cachePlan() bool - getSelectLimit() int + return econtext.ParseDestinationTarget(targetString, defaultTabletType, e.VSchema()) } // getPlan computes the plan for the given query. If one is in // the cache, it reuses it. func (e *Executor) getPlan( ctx context.Context, - vcursor *vcursorImpl, + vcursor *econtext.VCursorImpl, query string, stmt sqlparser.Statement, comments sqlparser.MarginComments, @@ -1135,10 +1124,10 @@ func (e *Executor) getPlan( reservedVars, bindVars, parameterize, - vcursor.keyspace, - vcursor.safeSession.getSelectLimit(), + vcursor.GetKeyspace(), + vcursor.SafeSession.GetSelectLimit(), setVarComment, - vcursor.safeSession.SystemVariables, + vcursor.GetSystemVariablesCopy(), vcursor.GetForeignKeyChecksState(), vcursor, ) @@ -1157,9 +1146,9 @@ func (e *Executor) getPlan( return e.cacheAndBuildStatement(ctx, vcursor, query, stmt, reservedVars, bindVarNeeds, logStats) } -func (e *Executor) hashPlan(ctx context.Context, vcursor *vcursorImpl, query string) PlanCacheKey { +func (e *Executor) hashPlan(ctx context.Context, vcursor *econtext.VCursorImpl, query string) PlanCacheKey { hasher := vthash.New256() - vcursor.keyForPlan(ctx, query, hasher) + vcursor.KeyForPlan(ctx, query, hasher) var planKey PlanCacheKey hasher.Sum(planKey[:0]) @@ -1168,19 +1157,22 @@ func (e *Executor) hashPlan(ctx context.Context, vcursor *vcursorImpl, query str func (e *Executor) buildStatement( ctx context.Context, - vcursor *vcursorImpl, + vcursor *econtext.VCursorImpl, query string, stmt sqlparser.Statement, reservedVars *sqlparser.ReservedVars, bindVarNeeds *sqlparser.BindVarNeeds, ) (*engine.Plan, error) { - plan, err := planbuilder.BuildFromStmt(ctx, query, stmt, reservedVars, vcursor, bindVarNeeds, enableOnlineDDL, enableDirectDDL) + cfg := &dynamicViperConfig{ + onlineDDL: enableOnlineDDL, + directDDL: enableDirectDDL, + } + plan, err := planbuilder.BuildFromStmt(ctx, query, stmt, reservedVars, vcursor, bindVarNeeds, cfg) if err != nil { return nil, err } - plan.Warnings = vcursor.warnings - vcursor.warnings = nil + plan.Warnings = vcursor.GetAndEmptyWarnings() err = e.checkThatPlanIsValid(stmt, plan) return plan, err @@ -1188,14 +1180,14 @@ func (e *Executor) buildStatement( func (e *Executor) cacheAndBuildStatement( ctx context.Context, - vcursor *vcursorImpl, + vcursor *econtext.VCursorImpl, query string, stmt sqlparser.Statement, reservedVars *sqlparser.ReservedVars, bindVarNeeds *sqlparser.BindVarNeeds, logStats *logstats.LogStats, ) (*engine.Plan, error) { - planCachable := sqlparser.CachePlan(stmt) && vcursor.safeSession.cachePlan() + planCachable := sqlparser.CachePlan(stmt) && vcursor.CachePlan() if planCachable { planKey := e.hashPlan(ctx, vcursor, query) @@ -1213,7 +1205,7 @@ func (e *Executor) canNormalizeStatement(stmt sqlparser.Statement, setVarComment return sqlparser.CanNormalize(stmt) || setVarComment != "" } -func prepareSetVarComment(vcursor *vcursorImpl, stmt sqlparser.Statement) (string, error) { +func prepareSetVarComment(vcursor *econtext.VCursorImpl, stmt sqlparser.Statement) (string, error) { if vcursor == nil || vcursor.Session().InReservedConn() { return "", nil } @@ -1354,7 +1346,7 @@ func isValidPayloadSize(query string) bool { } // Prepare executes a prepare statements. -func (e *Executor) Prepare(ctx context.Context, method string, safeSession *SafeSession, sql string, bindVars map[string]*querypb.BindVariable) (fld []*querypb.Field, err error) { +func (e *Executor) Prepare(ctx context.Context, method string, safeSession *econtext.SafeSession, sql string, bindVars map[string]*querypb.BindVariable) (fld []*querypb.Field, err error) { logStats := logstats.NewLogStats(ctx, method, sql, safeSession.GetSessionUUID(), bindVars) fld, err = e.prepare(ctx, safeSession, sql, bindVars, logStats) logStats.Error = err @@ -1373,7 +1365,7 @@ func (e *Executor) Prepare(ctx context.Context, method string, safeSession *Safe return fld, err } -func (e *Executor) prepare(ctx context.Context, safeSession *SafeSession, sql string, bindVars map[string]*querypb.BindVariable, logStats *logstats.LogStats) ([]*querypb.Field, error) { +func (e *Executor) prepare(ctx context.Context, safeSession *econtext.SafeSession, sql string, bindVars map[string]*querypb.BindVariable, logStats *logstats.LogStats) ([]*querypb.Field, error) { // Start an implicit transaction if necessary. if !safeSession.Autocommit && !safeSession.InTransaction() { if err := e.txConn.Begin(ctx, safeSession, nil); err != nil { @@ -1409,9 +1401,41 @@ func (e *Executor) prepare(ctx context.Context, safeSession *SafeSession, sql st return nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "[BUG] unrecognized prepare statement: %s", sql) } -func (e *Executor) handlePrepare(ctx context.Context, safeSession *SafeSession, sql string, bindVars map[string]*querypb.BindVariable, logStats *logstats.LogStats) ([]*querypb.Field, error) { +func (e *Executor) initVConfig(warnOnShardedOnly bool, pv plancontext.PlannerVersion) { + connCollation := collations.Unknown + if gw, isTabletGw := e.resolver.resolver.GetGateway().(*TabletGateway); isTabletGw { + connCollation = gw.DefaultConnCollation() + } + if connCollation == collations.Unknown { + connCollation = e.env.CollationEnv().DefaultConnectionCharset() + } + + e.vConfig = econtext.VCursorConfig{ + Collation: connCollation, + DefaultTabletType: defaultTabletType, + PlannerVersion: pv, + + QueryTimeout: queryTimeout, + MaxMemoryRows: maxMemoryRows, + + SetVarEnabled: sysVarSetEnabled, + EnableViews: enableViews, + ForeignKeyMode: fkMode(foreignKeyMode), + EnableShardRouting: enableShardRouting, + WarnShardedOnly: warnOnShardedOnly, + + DBDDLPlugin: dbDDLPlugin, + + WarmingReadsPercent: e.warmingReadsPercent, + WarmingReadsTimeout: warmingReadsQueryTimeout, + WarmingReadsChannel: e.warmingReadsChannel, + } +} + +func (e *Executor) handlePrepare(ctx context.Context, safeSession *econtext.SafeSession, sql string, bindVars map[string]*querypb.BindVariable, logStats *logstats.LogStats) ([]*querypb.Field, error) { query, comments := sqlparser.SplitMarginComments(sql) - vcursor, _ := newVCursorImpl(safeSession, comments, e, logStats, e.vm, e.VSchema(), e.resolver.resolver, e.serv, e.warnShardedOnly, e.pv) + + vcursor, _ := econtext.NewVCursorImpl(safeSession, comments, e, logStats, e.vm, e.VSchema(), e.resolver.resolver, e.serv, nullResultsObserver{}, e.vConfig) stmt, reservedVars, err := parseAndValidateQuery(query, e.env.Parser()) if err != nil { @@ -1460,17 +1484,17 @@ func parseAndValidateQuery(query string, parser *sqlparser.Parser) (sqlparser.St } // ExecuteMultiShard implements the IExecutor interface -func (e *Executor) ExecuteMultiShard(ctx context.Context, primitive engine.Primitive, rss []*srvtopo.ResolvedShard, queries []*querypb.BoundQuery, session *SafeSession, autocommit bool, ignoreMaxMemoryRows bool, resultsObserver resultsObserver) (qr *sqltypes.Result, errs []error) { +func (e *Executor) ExecuteMultiShard(ctx context.Context, primitive engine.Primitive, rss []*srvtopo.ResolvedShard, queries []*querypb.BoundQuery, session *econtext.SafeSession, autocommit bool, ignoreMaxMemoryRows bool, resultsObserver econtext.ResultsObserver) (qr *sqltypes.Result, errs []error) { return e.scatterConn.ExecuteMultiShard(ctx, primitive, rss, queries, session, autocommit, ignoreMaxMemoryRows, resultsObserver) } // StreamExecuteMulti implements the IExecutor interface -func (e *Executor) StreamExecuteMulti(ctx context.Context, primitive engine.Primitive, query string, rss []*srvtopo.ResolvedShard, vars []map[string]*querypb.BindVariable, session *SafeSession, autocommit bool, callback func(reply *sqltypes.Result) error, resultsObserver resultsObserver) []error { +func (e *Executor) StreamExecuteMulti(ctx context.Context, primitive engine.Primitive, query string, rss []*srvtopo.ResolvedShard, vars []map[string]*querypb.BindVariable, session *econtext.SafeSession, autocommit bool, callback func(reply *sqltypes.Result) error, resultsObserver econtext.ResultsObserver) []error { return e.scatterConn.StreamExecuteMulti(ctx, primitive, query, rss, vars, session, autocommit, callback, resultsObserver) } // ExecuteLock implements the IExecutor interface -func (e *Executor) ExecuteLock(ctx context.Context, rs *srvtopo.ResolvedShard, query *querypb.BoundQuery, session *SafeSession, lockFuncType sqlparser.LockingFuncType) (*sqltypes.Result, error) { +func (e *Executor) ExecuteLock(ctx context.Context, rs *srvtopo.ResolvedShard, query *querypb.BoundQuery, session *econtext.SafeSession, lockFuncType sqlparser.LockingFuncType) (*sqltypes.Result, error) { return e.scatterConn.ExecuteLock(ctx, rs, query, session, lockFuncType) } @@ -1581,25 +1605,25 @@ func getTabletThrottlerStatus(tabletHostPort string) (string, error) { } // ReleaseLock implements the IExecutor interface -func (e *Executor) ReleaseLock(ctx context.Context, session *SafeSession) error { +func (e *Executor) ReleaseLock(ctx context.Context, session *econtext.SafeSession) error { return e.txConn.ReleaseLock(ctx, session) } -// planPrepareStmt implements the IExecutor interface -func (e *Executor) planPrepareStmt(ctx context.Context, vcursor *vcursorImpl, query string) (*engine.Plan, sqlparser.Statement, error) { +// PlanPrepareStmt implements the IExecutor interface +func (e *Executor) PlanPrepareStmt(ctx context.Context, vcursor *econtext.VCursorImpl, query string) (*engine.Plan, sqlparser.Statement, error) { stmt, reservedVars, err := parseAndValidateQuery(query, e.env.Parser()) if err != nil { return nil, nil, err } // creating this log stats to not interfere with the original log stats. - lStats := logstats.NewLogStats(ctx, "prepare", query, vcursor.safeSession.SessionUUID, nil) + lStats := logstats.NewLogStats(ctx, "prepare", query, vcursor.Session().GetSessionUUID(), nil) plan, err := e.getPlan( ctx, vcursor, query, sqlparser.Clone(stmt), - vcursor.marginComments, + vcursor.GetMarginComments(), map[string]*querypb.BindVariable{}, reservedVars, /* normalize */ false, @@ -1621,7 +1645,7 @@ func (e *Executor) Close() { e.plans.Close() } -func (e *Executor) environment() *vtenv.Environment { +func (e *Executor) Environment() *vtenv.Environment { return e.env } @@ -1633,6 +1657,10 @@ func (e *Executor) UnresolvedTransactions(ctx context.Context, targets []*queryp return e.txConn.UnresolvedTransactions(ctx, targets) } +func (e *Executor) AddWarningCount(name string, count int64) { + warnings.Add(name, count) +} + type ( errorTransformer interface { TransformError(err error) error @@ -1643,3 +1671,16 @@ type ( func (nullErrorTransformer) TransformError(err error) error { return err } + +func fkMode(foreignkey string) vschemapb.Keyspace_ForeignKeyMode { + switch foreignkey { + case "disallow": + return vschemapb.Keyspace_disallow + case "managed": + return vschemapb.Keyspace_managed + case "unmanaged": + return vschemapb.Keyspace_unmanaged + + } + return vschemapb.Keyspace_unspecified +} diff --git a/go/vt/vtgate/executor_ddl_test.go b/go/vt/vtgate/executor_ddl_test.go index 3274fd94475..bf117856e08 100644 --- a/go/vt/vtgate/executor_ddl_test.go +++ b/go/vt/vtgate/executor_ddl_test.go @@ -21,14 +21,15 @@ import ( "testing" vtgatepb "vitess.io/vitess/go/vt/proto/vtgate" + econtext "vitess.io/vitess/go/vt/vtgate/executorcontext" "github.com/stretchr/testify/require" ) func TestDDLFlags(t *testing.T) { defer func() { - enableOnlineDDL = true - enableDirectDDL = true + enableOnlineDDL.Set(true) + enableDirectDDL.Set(true) }() testcases := []struct { enableDirectDDL bool @@ -56,9 +57,9 @@ func TestDDLFlags(t *testing.T) { for _, testcase := range testcases { t.Run(fmt.Sprintf("%s-%v-%v", testcase.sql, testcase.enableDirectDDL, testcase.enableOnlineDDL), func(t *testing.T) { executor, _, _, _, ctx := createExecutorEnv(t) - session := NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded}) - enableDirectDDL = testcase.enableDirectDDL - enableOnlineDDL = testcase.enableOnlineDDL + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded}) + enableDirectDDL.Set(testcase.enableDirectDDL) + enableOnlineDDL.Set(testcase.enableOnlineDDL) _, err := executor.Execute(ctx, nil, "TestDDLFlags", session, testcase.sql, nil) if testcase.wantErr { require.EqualError(t, err, testcase.err) diff --git a/go/vt/vtgate/executor_dml_test.go b/go/vt/vtgate/executor_dml_test.go index 3dce4e212ef..792e197f48d 100644 --- a/go/vt/vtgate/executor_dml_test.go +++ b/go/vt/vtgate/executor_dml_test.go @@ -25,6 +25,8 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + econtext "vitess.io/vitess/go/vt/vtgate/executorcontext" + "vitess.io/vitess/go/mysql/config" "vitess.io/vitess/go/mysql/sqlerror" "vitess.io/vitess/go/sqltypes" @@ -135,7 +137,6 @@ func TestUpdateEqual(t *testing.T) { func TestUpdateFromSubQuery(t *testing.T) { executor, sbc1, sbc2, _, ctx := createExecutorEnv(t) - executor.pv = querypb.ExecuteOptions_Gen4 logChan := executor.queryLogger.Subscribe("Test") defer executor.queryLogger.Unsubscribe(logChan) @@ -234,7 +235,7 @@ func TestUpdateInTransactionLookupDefaultReadLock(t *testing.T) { )} executor, sbc1, sbc2, sbcLookup, ctx := createCustomExecutorSetValues(t, executorVSchema, res) - safeSession := NewSafeSession(&vtgatepb.Session{InTransaction: true}) + safeSession := econtext.NewSafeSession(&vtgatepb.Session{InTransaction: true}) _, err := executorExecSession(ctx, executor, "update t2_lookup set lu_col = 5 where nv_lu_col = 2", @@ -296,7 +297,7 @@ func TestUpdateInTransactionLookupExclusiveReadLock(t *testing.T) { )} executor, sbc1, sbc2, sbcLookup, ctx := createCustomExecutorSetValues(t, executorVSchema, res) - safeSession := NewSafeSession(&vtgatepb.Session{InTransaction: true}) + safeSession := econtext.NewSafeSession(&vtgatepb.Session{InTransaction: true}) _, err := executorExecSession(ctx, executor, "update t2_lookup set lu_col = 5 where erl_lu_col = 2", @@ -358,7 +359,7 @@ func TestUpdateInTransactionLookupSharedReadLock(t *testing.T) { )} executor, sbc1, sbc2, sbcLookup, ctx := createCustomExecutorSetValues(t, executorVSchema, res) - safeSession := NewSafeSession(&vtgatepb.Session{InTransaction: true}) + safeSession := econtext.NewSafeSession(&vtgatepb.Session{InTransaction: true}) _, err := executorExecSession(ctx, executor, "update t2_lookup set lu_col = 5 where srl_lu_col = 2", @@ -420,7 +421,7 @@ func TestUpdateInTransactionLookupNoReadLock(t *testing.T) { )} executor, sbc1, sbc2, sbcLookup, ctx := createCustomExecutorSetValues(t, executorVSchema, res) - safeSession := NewSafeSession(&vtgatepb.Session{InTransaction: true}) + safeSession := econtext.NewSafeSession(&vtgatepb.Session{InTransaction: true}) _, err := executorExecSession(ctx, executor, "update t2_lookup set lu_col = 5 where nrl_lu_col = 2", @@ -2066,7 +2067,7 @@ func TestInsertPartialFail1(t *testing.T) { context.Background(), nil, "TestExecute", - NewSafeSession(&vtgatepb.Session{InTransaction: true}), + econtext.NewSafeSession(&vtgatepb.Session{InTransaction: true}), "insert into user(id, v, name) values (1, 2, 'myname')", nil, ) @@ -2082,7 +2083,7 @@ func TestInsertPartialFail2(t *testing.T) { // Make the second DML fail, it should result in a rollback. sbc1.MustFailExecute[sqlparser.StmtInsert] = 1 - safeSession := NewSafeSession(&vtgatepb.Session{InTransaction: true}) + safeSession := econtext.NewSafeSession(&vtgatepb.Session{InTransaction: true}) _, err := executor.Execute( context.Background(), nil, @@ -2656,7 +2657,7 @@ func TestReservedConnDML(t *testing.T) { logChan := executor.queryLogger.Subscribe("TestReservedConnDML") defer executor.queryLogger.Unsubscribe(logChan) - session := NewAutocommitSession(&vtgatepb.Session{EnableSystemSettings: true}) + session := econtext.NewAutocommitSession(&vtgatepb.Session{EnableSystemSettings: true}) _, err := executor.Execute(ctx, nil, "TestReservedConnDML", session, "use "+KsTestUnsharded, nil) require.NoError(t, err) @@ -2708,7 +2709,7 @@ func TestStreamingDML(t *testing.T) { logChan := executor.queryLogger.Subscribe(method) defer executor.queryLogger.Unsubscribe(logChan) - session := NewAutocommitSession(&vtgatepb.Session{}) + session := econtext.NewAutocommitSession(&vtgatepb.Session{}) tcases := []struct { query string @@ -2792,7 +2793,7 @@ func TestPartialVindexInsertQueryFailure(t *testing.T) { logChan := executor.queryLogger.Subscribe("Test") defer executor.queryLogger.Unsubscribe(logChan) - session := NewAutocommitSession(&vtgatepb.Session{}) + session := econtext.NewAutocommitSession(&vtgatepb.Session{}) require.True(t, session.GetAutocommit()) require.False(t, session.InTransaction()) @@ -2845,7 +2846,7 @@ func TestPartialVindexInsertQueryFailureAutoCommit(t *testing.T) { logChan := executor.queryLogger.Subscribe("Test") defer executor.queryLogger.Unsubscribe(logChan) - session := NewAutocommitSession(&vtgatepb.Session{}) + session := econtext.NewAutocommitSession(&vtgatepb.Session{}) require.True(t, session.GetAutocommit()) require.False(t, session.InTransaction()) @@ -2886,7 +2887,7 @@ func TestPartialVindexInsertQueryFailureAutoCommit(t *testing.T) { func TestMultiInternalSavepoint(t *testing.T) { executor, sbc1, sbc2, _, ctx := createExecutorEnv(t) - session := NewAutocommitSession(&vtgatepb.Session{}) + session := econtext.NewAutocommitSession(&vtgatepb.Session{}) _, err := executorExecSession(ctx, executor, "begin", nil, session.Session) require.NoError(t, err) @@ -2935,7 +2936,7 @@ func TestInsertSelectFromDual(t *testing.T) { logChan := executor.queryLogger.Subscribe("TestInsertSelect") defer executor.queryLogger.Unsubscribe(logChan) - session := NewAutocommitSession(&vtgatepb.Session{}) + session := econtext.NewAutocommitSession(&vtgatepb.Session{}) query := "insert into user(id, v, name) select 1, 2, 'myname' from dual" wantQueries := []*querypb.BoundQuery{{ @@ -2990,7 +2991,7 @@ func TestInsertSelectFromTable(t *testing.T) { logChan := executor.queryLogger.Subscribe("TestInsertSelect") defer executor.queryLogger.Unsubscribe(logChan) - session := NewAutocommitSession(&vtgatepb.Session{}) + session := econtext.NewAutocommitSession(&vtgatepb.Session{}) query := "insert into user(id, name) select c1, c2 from music" wantQueries := []*querypb.BoundQuery{{ @@ -3140,3 +3141,62 @@ func TestDeleteMultiTable(t *testing.T) { // 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) } + +// TestSessionRowsAffected test that rowsAffected is set correctly for each shard session. +func TestSessionRowsAffected(t *testing.T) { + method := t.Name() + executor, _, sbc4060, _, ctx := createExecutorEnv(t) + + session := econtext.NewAutocommitSession(&vtgatepb.Session{}) + + // start the transaction + _, err := executor.Execute(ctx, nil, method, session, "begin", nil) + require.NoError(t, err) + + // -20 - select query + _, err = executor.Execute(ctx, nil, method, session, "select * from user where id = 1", nil) + require.NoError(t, err) + require.Len(t, session.ShardSessions, 1) + require.False(t, session.ShardSessions[0].RowsAffected) + + // -20 - update query (rows affected) + _, err = executor.Execute(ctx, nil, method, session, "update user set foo = 41 where id = 1", nil) + require.NoError(t, err) + require.True(t, session.ShardSessions[0].RowsAffected) + + // e0- - select query + _, err = executor.Execute(ctx, nil, method, session, "select * from user where id = 7", nil) + require.NoError(t, err) + assert.Len(t, session.ShardSessions, 2) + require.False(t, session.ShardSessions[1].RowsAffected) + + // c0-e0 - update query (rows affected) + _, err = executor.Execute(ctx, nil, method, session, "update user set foo = 42 where id = 5", nil) + require.NoError(t, err) + require.Len(t, session.ShardSessions, 3) + require.True(t, session.ShardSessions[2].RowsAffected) + + // 40-60 - update query (no rows affected) + sbc4060.SetResults([]*sqltypes.Result{{RowsAffected: 0}}) + _, err = executor.Execute(ctx, nil, method, session, "update user set foo = 42 where id = 3", nil) + require.NoError(t, err) + assert.Len(t, session.ShardSessions, 4) + require.False(t, session.ShardSessions[3].RowsAffected) + + // 40-60 - select query + _, err = executor.Execute(ctx, nil, method, session, "select * from user where id = 3", nil) + require.NoError(t, err) + require.False(t, session.ShardSessions[3].RowsAffected) + + // 40-60 - delete query (rows affected) + _, err = executor.Execute(ctx, nil, method, session, "delete from user where id = 3", nil) + require.NoError(t, err) + require.True(t, session.ShardSessions[0].RowsAffected) + require.False(t, session.ShardSessions[1].RowsAffected) + require.True(t, session.ShardSessions[2].RowsAffected) + require.True(t, session.ShardSessions[3].RowsAffected) + + _, err = executor.Execute(ctx, nil, method, session, "commit", nil) + require.NoError(t, err) + require.Zero(t, session.ShardSessions) +} diff --git a/go/vt/vtgate/executor_framework_test.go b/go/vt/vtgate/executor_framework_test.go index 332139c4a78..2ee3425209f 100644 --- a/go/vt/vtgate/executor_framework_test.go +++ b/go/vt/vtgate/executor_framework_test.go @@ -28,6 +28,8 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + econtext "vitess.io/vitess/go/vt/vtgate/executorcontext" + "vitess.io/vitess/go/cache/theine" "vitess.io/vitess/go/constants/sidecar" "vitess.io/vitess/go/sqltypes" @@ -307,7 +309,7 @@ func executorExecSession(ctx context.Context, executor *Executor, sql string, bv ctx, nil, "TestExecute", - NewSafeSession(session), + econtext.NewSafeSession(session), sql, bv) } @@ -320,7 +322,7 @@ func executorPrepare(ctx context.Context, executor *Executor, session *vtgatepb. return executor.Prepare( ctx, "TestExecute", - NewSafeSession(session), + econtext.NewSafeSession(session), sql, bv) } @@ -331,7 +333,7 @@ func executorStream(ctx context.Context, executor *Executor, sql string) (qr *sq ctx, nil, "TestExecuteStream", - NewSafeSession(nil), + econtext.NewSafeSession(nil), sql, nil, func(qr *sqltypes.Result) error { diff --git a/go/vt/vtgate/executor_scatter_stats_test.go b/go/vt/vtgate/executor_scatter_stats_test.go index 84dd2744e8b..b665f850a23 100644 --- a/go/vt/vtgate/executor_scatter_stats_test.go +++ b/go/vt/vtgate/executor_scatter_stats_test.go @@ -24,12 +24,13 @@ import ( "github.com/stretchr/testify/require" vtgatepb "vitess.io/vitess/go/vt/proto/vtgate" + econtext "vitess.io/vitess/go/vt/vtgate/executorcontext" ) func TestScatterStatsWithNoScatterQuery(t *testing.T) { executor, _, _, _, ctx := createExecutorEnv(t) - session := NewSafeSession(&vtgatepb.Session{TargetString: "@primary"}) + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: "@primary"}) _, err := executor.Execute(ctx, nil, "TestExecutorResultsExceeded", session, "select * from main1", nil) require.NoError(t, err) @@ -41,7 +42,7 @@ func TestScatterStatsWithNoScatterQuery(t *testing.T) { func TestScatterStatsWithSingleScatterQuery(t *testing.T) { executor, _, _, _, ctx := createExecutorEnv(t) - session := NewSafeSession(&vtgatepb.Session{TargetString: "@primary"}) + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: "@primary"}) _, err := executor.Execute(ctx, nil, "TestExecutorResultsExceeded", session, "select * from user", nil) require.NoError(t, err) @@ -53,7 +54,7 @@ func TestScatterStatsWithSingleScatterQuery(t *testing.T) { func TestScatterStatsHttpWriting(t *testing.T) { executor, _, _, _, ctx := createExecutorEnv(t) - session := NewSafeSession(&vtgatepb.Session{TargetString: "@primary"}) + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: "@primary"}) _, err := executor.Execute(ctx, nil, "TestExecutorResultsExceeded", session, "select * from user", nil) require.NoError(t, err) diff --git a/go/vt/vtgate/executor_select_test.go b/go/vt/vtgate/executor_select_test.go index 8ba89d25daf..86aafaefba4 100644 --- a/go/vt/vtgate/executor_select_test.go +++ b/go/vt/vtgate/executor_select_test.go @@ -30,6 +30,8 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + econtext "vitess.io/vitess/go/vt/vtgate/executorcontext" + _flag "vitess.io/vitess/go/internal/flag" "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/sqltypes" @@ -59,7 +61,7 @@ func TestSelectNext(t *testing.T) { }} // Autocommit - session := NewAutocommitSession(&vtgatepb.Session{}) + session := econtext.NewAutocommitSession(&vtgatepb.Session{}) _, err := executor.Execute(context.Background(), nil, "TestSelectNext", session, query, bv) require.NoError(t, err) @@ -69,7 +71,7 @@ func TestSelectNext(t *testing.T) { sbclookup.Queries = nil // Txn - session = NewAutocommitSession(&vtgatepb.Session{}) + session = econtext.NewAutocommitSession(&vtgatepb.Session{}) session.Session.InTransaction = true _, err = executor.Execute(context.Background(), nil, "TestSelectNext", session, query, bv) require.NoError(t, err) @@ -80,7 +82,7 @@ func TestSelectNext(t *testing.T) { sbclookup.Queries = nil // Reserve - session = NewAutocommitSession(&vtgatepb.Session{}) + session = econtext.NewAutocommitSession(&vtgatepb.Session{}) session.Session.InReservedConn = true _, err = executor.Execute(context.Background(), nil, "TestSelectNext", session, query, bv) require.NoError(t, err) @@ -91,7 +93,7 @@ func TestSelectNext(t *testing.T) { sbclookup.Queries = nil // Reserve and Txn - session = NewAutocommitSession(&vtgatepb.Session{}) + session = econtext.NewAutocommitSession(&vtgatepb.Session{}) session.Session.InReservedConn = true session.Session.InTransaction = true _, err = executor.Execute(context.Background(), nil, "TestSelectNext", session, query, bv) @@ -107,7 +109,7 @@ func TestSelectDBA(t *testing.T) { query := "select * from INFORMATION_SCHEMA.foo" _, err := executor.Execute(context.Background(), nil, "TestSelectDBA", - NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}), + econtext.NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}), query, map[string]*querypb.BindVariable{}, ) require.NoError(t, err) @@ -117,7 +119,7 @@ func TestSelectDBA(t *testing.T) { sbc1.Queries = nil query = "SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES ist WHERE ist.table_schema = 'performance_schema' AND ist.table_name = 'foo'" _, err = executor.Execute(context.Background(), nil, "TestSelectDBA", - NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}), + econtext.NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}), query, map[string]*querypb.BindVariable{}, ) require.NoError(t, err) @@ -133,7 +135,7 @@ func TestSelectDBA(t *testing.T) { sbc1.Queries = nil query = "select 1 from information_schema.table_constraints where constraint_schema = 'vt_ks' and table_name = 'user'" _, err = executor.Execute(context.Background(), nil, "TestSelectDBA", - NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}), + econtext.NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}), query, map[string]*querypb.BindVariable{}, ) require.NoError(t, err) @@ -149,7 +151,7 @@ func TestSelectDBA(t *testing.T) { sbc1.Queries = nil query = "select 1 from information_schema.table_constraints where constraint_schema = 'vt_ks'" _, err = executor.Execute(context.Background(), nil, "TestSelectDBA", - NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}), + econtext.NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}), query, map[string]*querypb.BindVariable{}, ) require.NoError(t, err) @@ -167,7 +169,7 @@ func TestSystemVariablesMySQLBelow80(t *testing.T) { executor.normalize = true setVarEnabled = true - session := NewAutocommitSession(&vtgatepb.Session{EnableSystemSettings: true, TargetString: "TestExecutor"}) + session := econtext.NewAutocommitSession(&vtgatepb.Session{EnableSystemSettings: true, TargetString: "TestExecutor"}) sbc1.SetResults([]*sqltypes.Result{{ Fields: []*querypb.Field{ @@ -199,12 +201,8 @@ func TestSystemVariablesMySQLBelow80(t *testing.T) { func TestSystemVariablesWithSetVarDisabled(t *testing.T) { executor, sbc1, _, _, _ := createCustomExecutor(t, "{}", "8.0.0") executor.normalize = true - - setVarEnabled = false - defer func() { - setVarEnabled = true - }() - session := NewAutocommitSession(&vtgatepb.Session{EnableSystemSettings: true, TargetString: "TestExecutor"}) + executor.vConfig.SetVarEnabled = false + session := econtext.NewAutocommitSession(&vtgatepb.Session{EnableSystemSettings: true, TargetString: "TestExecutor"}) sbc1.SetResults([]*sqltypes.Result{{ Fields: []*querypb.Field{ @@ -237,7 +235,7 @@ func TestSetSystemVariablesTx(t *testing.T) { executor, sbc1, _, _, _ := createCustomExecutor(t, "{}", "8.0.1") executor.normalize = true - session := NewAutocommitSession(&vtgatepb.Session{EnableSystemSettings: true, TargetString: "TestExecutor"}) + session := econtext.NewAutocommitSession(&vtgatepb.Session{EnableSystemSettings: true, TargetString: "TestExecutor"}) _, err := executor.Execute(context.Background(), nil, "TestBegin", session, "begin", map[string]*querypb.BindVariable{}) require.NoError(t, err) @@ -283,7 +281,7 @@ func TestSetSystemVariables(t *testing.T) { executor, _, _, lookup, _ := createExecutorEnv(t) executor.normalize = true - session := NewAutocommitSession(&vtgatepb.Session{EnableSystemSettings: true, TargetString: KsTestUnsharded, SystemVariables: map[string]string{}}) + session := econtext.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 @@ -394,7 +392,7 @@ func TestSetSystemVariablesWithReservedConnection(t *testing.T) { executor, sbc1, _, _, _ := createExecutorEnv(t) executor.normalize = true - session := NewAutocommitSession(&vtgatepb.Session{EnableSystemSettings: true, SystemVariables: map[string]string{}}) + session := econtext.NewAutocommitSession(&vtgatepb.Session{EnableSystemSettings: true, SystemVariables: map[string]string{}}) sbc1.SetResults([]*sqltypes.Result{{ Fields: []*querypb.Field{ @@ -437,7 +435,7 @@ func TestSelectVindexFunc(t *testing.T) { executor, _, _, _, _ := createExecutorEnv(t) query := "select * from hash_index where id = 1" - session := NewAutocommitSession(&vtgatepb.Session{}) + session := econtext.NewAutocommitSession(&vtgatepb.Session{}) _, err := executor.Execute(context.Background(), nil, "TestSelectVindexFunc", session, query, nil) require.ErrorContains(t, err, "VT09005: no database selected") @@ -450,7 +448,7 @@ func TestCreateTableValidTimestamp(t *testing.T) { executor, sbc1, _, _, _ := createExecutorEnv(t) executor.normalize = true - session := NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor", SystemVariables: map[string]string{"sql_mode": "ALLOW_INVALID_DATES"}}) + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor", SystemVariables: map[string]string{"sql_mode": "ALLOW_INVALID_DATES"}}) query := "create table aa(t timestamp default 0)" _, err := executor.Execute(context.Background(), nil, "TestSelect", session, query, map[string]*querypb.BindVariable{}) @@ -468,11 +466,10 @@ func TestCreateTableValidTimestamp(t *testing.T) { func TestGen4SelectDBA(t *testing.T) { executor, sbc1, _, _, _ := createExecutorEnv(t) executor.normalize = true - executor.pv = querypb.ExecuteOptions_Gen4 query := "select * from INFORMATION_SCHEMA.TABLE_CONSTRAINTS" _, err := executor.Execute(context.Background(), nil, "TestSelectDBA", - NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}), + econtext.NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}), query, map[string]*querypb.BindVariable{}, ) require.NoError(t, err) @@ -483,7 +480,7 @@ func TestGen4SelectDBA(t *testing.T) { sbc1.Queries = nil query = "SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES ist WHERE ist.table_schema = 'performance_schema' AND ist.table_name = 'foo'" _, err = executor.Execute(context.Background(), nil, "TestSelectDBA", - NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}), + econtext.NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}), query, map[string]*querypb.BindVariable{}, ) require.NoError(t, err) @@ -501,7 +498,7 @@ func TestGen4SelectDBA(t *testing.T) { sbc1.Queries = nil query = "select 1 from information_schema.table_constraints where constraint_schema = 'vt_ks' and table_name = 'user'" _, err = executor.Execute(context.Background(), nil, "TestSelectDBA", - NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}), + econtext.NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}), query, map[string]*querypb.BindVariable{}, ) require.NoError(t, err) @@ -519,7 +516,7 @@ func TestGen4SelectDBA(t *testing.T) { sbc1.Queries = nil query = "select 1 from information_schema.table_constraints where constraint_schema = 'vt_ks'" - _, err = executor.Execute(context.Background(), nil, "TestSelectDBA", NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}), query, map[string]*querypb.BindVariable{}) + _, err = executor.Execute(context.Background(), nil, "TestSelectDBA", econtext.NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}), query, map[string]*querypb.BindVariable{}) require.NoError(t, err) wantQueries = []*querypb.BoundQuery{{ Sql: "select :vtg1 /* INT64 */ from information_schema.table_constraints where constraint_schema = :__vtschemaname /* VARCHAR */", @@ -534,7 +531,7 @@ func TestGen4SelectDBA(t *testing.T) { sbc1.Queries = nil query = "select t.table_schema,t.table_name,c.column_name,c.column_type from tables t join columns c on c.table_schema = t.table_schema and c.table_name = t.table_name where t.table_schema = 'TestExecutor' and c.table_schema = 'TestExecutor' order by t.table_schema,t.table_name,c.column_name" _, err = executor.Execute(context.Background(), nil, "TestSelectDBA", - NewSafeSession(&vtgatepb.Session{TargetString: "information_schema"}), + econtext.NewSafeSession(&vtgatepb.Session{TargetString: "information_schema"}), query, map[string]*querypb.BindVariable{}, ) require.NoError(t, err) @@ -651,7 +648,7 @@ func TestStreamBuffering(t *testing.T) { context.Background(), nil, "TestStreamBuffering", - NewSafeSession(session), + econtext.NewSafeSession(session), "select id from music_user_map where id = 1", nil, func(qr *sqltypes.Result) error { @@ -723,7 +720,7 @@ func TestStreamLimitOffset(t *testing.T) { context.Background(), nil, "TestStreamLimitOffset", - NewSafeSession(session), + econtext.NewSafeSession(session), "select id, textcol from user order by id limit 2 offset 2", nil, func(qr *sqltypes.Result) error { @@ -1083,7 +1080,7 @@ func TestSelectDatabase(t *testing.T) { newSession := &vtgatepb.Session{ TargetString: "@primary", } - session := NewSafeSession(newSession) + session := econtext.NewSafeSession(newSession) session.TargetString = "TestExecutor@primary" result, err := executor.Execute( context.Background(), @@ -1283,7 +1280,6 @@ func TestSelectEqual(t *testing.T) { func TestSelectINFromOR(t *testing.T) { executor, sbc1, _, _, ctx := createExecutorEnv(t) - executor.pv = querypb.ExecuteOptions_Gen4 session := &vtgatepb.Session{ TargetString: "@primary", @@ -2951,7 +2947,7 @@ func TestSubQueryAndQueryWithLimit(t *testing.T) { sbc1.SetResults(result1) sbc2.SetResults(result2) - exec(executor, NewSafeSession(&vtgatepb.Session{ + exec(executor, econtext.NewSafeSession(&vtgatepb.Session{ TargetString: "@primary", }), "select id1, id2 from t1 where id1 >= ( select id1 from t1 order by id1 asc limit 1) limit 100") require.Equal(t, 2, len(sbc1.Queries)) @@ -3000,7 +2996,7 @@ func TestSelectUsingMultiEqualOnLookupColumn(t *testing.T) { }}, }}) - result, err := exec(executor, NewSafeSession(&vtgatepb.Session{ + result, err := exec(executor, econtext.NewSafeSession(&vtgatepb.Session{ TargetString: KsTestSharded, }), "select nv_lu_col, other from t2_lookup WHERE (nv_lu_col = 1 AND other = 'bar') OR (nv_lu_col = 2 AND other = 'baz') OR (nv_lu_col = 3 AND other = 'qux') OR (nv_lu_col = 4 AND other = 'brz') OR (nv_lu_col = 5 AND other = 'brz')") @@ -3197,7 +3193,7 @@ func TestSelectWithUnionAll(t *testing.T) { func TestSelectLock(t *testing.T) { executor, sbc1, _, _, _ := createExecutorEnv(t) - session := NewSafeSession(nil) + session := econtext.NewSafeSession(nil) session.Session.InTransaction = true session.ShardSessions = []*vtgatepb.Session_ShardSession{{ Target: &querypb.Target{ @@ -3265,7 +3261,7 @@ func TestLockReserve(t *testing.T) { "select release_lock('lock name') from dual", } - session := NewAutocommitSession(&vtgatepb.Session{}) + session := econtext.NewAutocommitSession(&vtgatepb.Session{}) for _, sql := range tcases { t.Run(sql, func(t *testing.T) { @@ -3283,7 +3279,7 @@ func TestLockReserve(t *testing.T) { func TestSelectFromInformationSchema(t *testing.T) { executor, sbc1, _, _, _ := createExecutorEnv(t) - session := NewSafeSession(nil) + session := econtext.NewSafeSession(nil) // check failure when trying to query two keyspaces _, err := exec(executor, session, "SELECT B.TABLE_NAME FROM INFORMATION_SCHEMA.TABLES AS A, INFORMATION_SCHEMA.COLUMNS AS B WHERE A.TABLE_SCHEMA = 'TestExecutor' AND A.TABLE_SCHEMA = 'TestXBadSharding'") @@ -3410,8 +3406,8 @@ func TestSelectScatterFails(t *testing.T) { func TestGen4SelectStraightJoin(t *testing.T) { executor, sbc1, _, _, _ := createExecutorEnv(t) executor.normalize = true - executor.pv = querypb.ExecuteOptions_Gen4 - session := NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}) + + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}) query := "select u.id from user u straight_join user2 u2 on u.id = u2.id" _, err := executor.Execute(context.Background(), nil, "TestGen4SelectStraightJoin", @@ -3432,9 +3428,8 @@ func TestGen4SelectStraightJoin(t *testing.T) { func TestGen4MultiColumnVindexEqual(t *testing.T) { executor, sbc1, sbc2, _, _ := createExecutorEnv(t) executor.normalize = true - executor.pv = querypb.ExecuteOptions_Gen4 - session := NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}) + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}) query := "select * from user_region where cola = 1 and colb = 2" _, err := executor.Execute(context.Background(), nil, "TestGen4MultiColumnVindex", session, query, map[string]*querypb.BindVariable{}) require.NoError(t, err) @@ -3471,9 +3466,8 @@ func TestGen4MultiColumnVindexEqual(t *testing.T) { func TestGen4MultiColumnVindexIn(t *testing.T) { executor, sbc1, sbc2, _, _ := createExecutorEnv(t) executor.normalize = true - executor.pv = querypb.ExecuteOptions_Gen4 - session := NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}) + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}) query := "select * from user_region where cola IN (1,17984) and colb IN (2,3,4)" _, err := executor.Execute(context.Background(), nil, "TestGen4MultiColumnVindex", session, query, map[string]*querypb.BindVariable{}) require.NoError(t, err) @@ -3510,9 +3504,8 @@ func TestGen4MultiColumnVindexIn(t *testing.T) { func TestGen4MultiColMixedColComparision(t *testing.T) { executor, sbc1, sbc2, _, _ := createExecutorEnv(t) executor.normalize = true - executor.pv = querypb.ExecuteOptions_Gen4 - session := NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}) + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}) query := "select * from user_region where colb = 2 and cola IN (1,17984)" _, err := executor.Execute(context.Background(), nil, "TestGen4MultiColMixedColComparision", session, query, map[string]*querypb.BindVariable{}) require.NoError(t, err) @@ -3547,9 +3540,8 @@ func TestGen4MultiColMixedColComparision(t *testing.T) { func TestGen4MultiColBestVindexSel(t *testing.T) { executor, sbc1, sbc2, _, _ := createExecutorEnv(t) executor.normalize = true - executor.pv = querypb.ExecuteOptions_Gen4 - session := NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}) + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}) query := "select * from user_region where colb = 2 and cola IN (1,17984) and cola = 1" _, err := executor.Execute(context.Background(), nil, "TestGen4MultiColBestVindexSel", session, query, map[string]*querypb.BindVariable{}) require.NoError(t, err) @@ -3593,9 +3585,8 @@ func TestGen4MultiColBestVindexSel(t *testing.T) { func TestGen4MultiColMultiEqual(t *testing.T) { executor, sbc1, sbc2, _, _ := createExecutorEnv(t) executor.normalize = true - executor.pv = querypb.ExecuteOptions_Gen4 - session := NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}) + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}) query := "select * from user_region where (cola,colb) in ((17984,2),(17984,3))" _, err := executor.Execute(context.Background(), nil, "TestGen4MultiColMultiEqual", session, query, map[string]*querypb.BindVariable{}) require.NoError(t, err) @@ -3615,7 +3606,6 @@ func TestGen4MultiColMultiEqual(t *testing.T) { func TestGen4SelectUnqualifiedReferenceTable(t *testing.T) { executor, sbc1, sbc2, sbclookup, ctx := createExecutorEnv(t) - executor.pv = querypb.ExecuteOptions_Gen4 query := "select * from zip_detail" session := &vtgatepb.Session{ @@ -3636,7 +3626,6 @@ func TestGen4SelectUnqualifiedReferenceTable(t *testing.T) { func TestGen4SelectQualifiedReferenceTable(t *testing.T) { executor, sbc1, sbc2, sbclookup, ctx := createExecutorEnv(t) - executor.pv = querypb.ExecuteOptions_Gen4 query := fmt.Sprintf("select * from %s.zip_detail", KsTestSharded) session := &vtgatepb.Session{ @@ -3657,7 +3646,6 @@ func TestGen4SelectQualifiedReferenceTable(t *testing.T) { func TestGen4JoinUnqualifiedReferenceTable(t *testing.T) { executor, sbc1, sbc2, sbclookup, ctx := createExecutorEnv(t) - executor.pv = querypb.ExecuteOptions_Gen4 query := "select * from user join zip_detail on user.zip_detail_id = zip_detail.id" session := &vtgatepb.Session{ @@ -3694,7 +3682,6 @@ func TestGen4JoinUnqualifiedReferenceTable(t *testing.T) { func TestGen4CrossShardJoinQualifiedReferenceTable(t *testing.T) { executor, sbc1, sbc2, sbclookup, ctx := createExecutorEnv(t) - executor.pv = querypb.ExecuteOptions_Gen4 query := "select user.id from user join TestUnsharded.zip_detail on user.zip_detail_id = TestUnsharded.zip_detail.id" session := &vtgatepb.Session{ @@ -3751,7 +3738,6 @@ func TestRegionRange(t *testing.T) { } executor := createExecutor(ctx, serv, cell, resolver) defer executor.Close() - executor.pv = querypb.ExecuteOptions_Gen4 tcases := []struct { regionID int @@ -3769,7 +3755,7 @@ func TestRegionRange(t *testing.T) { for _, tcase := range tcases { t.Run(strconv.Itoa(tcase.regionID), func(t *testing.T) { sql := fmt.Sprintf("select * from user_region where cola = %d", tcase.regionID) - _, err := executor.Execute(context.Background(), nil, "TestRegionRange", NewAutocommitSession(&vtgatepb.Session{}), sql, nil) + _, err := executor.Execute(context.Background(), nil, "TestRegionRange", econtext.NewAutocommitSession(&vtgatepb.Session{}), sql, nil) require.NoError(t, err) count := 0 for _, sbc := range conns { @@ -3801,7 +3787,6 @@ func TestMultiCol(t *testing.T) { } executor := createExecutor(ctx, serv, cell, resolver) defer executor.Close() - executor.pv = querypb.ExecuteOptions_Gen4 tcases := []struct { cola, colb, colc int @@ -3817,7 +3802,7 @@ func TestMultiCol(t *testing.T) { shards: []string{"20a0-"}, }} - session := NewAutocommitSession(&vtgatepb.Session{}) + session := econtext.NewAutocommitSession(&vtgatepb.Session{}) for _, tcase := range tcases { t.Run(fmt.Sprintf("%d_%d_%d", tcase.cola, tcase.colb, tcase.colc), func(t *testing.T) { @@ -3882,7 +3867,6 @@ func TestMultiColPartial(t *testing.T) { } executor := createExecutor(ctx, serv, cell, resolver) defer executor.Close() - executor.pv = querypb.ExecuteOptions_Gen4 tcases := []struct { where string @@ -3907,7 +3891,7 @@ func TestMultiColPartial(t *testing.T) { shards: []string{"20a0c0-"}, }} - session := NewAutocommitSession(&vtgatepb.Session{}) + session := econtext.NewAutocommitSession(&vtgatepb.Session{}) for _, tcase := range tcases { t.Run(tcase.where, func(t *testing.T) { @@ -3946,7 +3930,6 @@ func TestSelectAggregationNoData(t *testing.T) { } executor := createExecutor(ctx, serv, cell, resolver) defer executor.Close() - executor.pv = querypb.ExecuteOptions_Gen4 tcases := []struct { sql string @@ -4038,7 +4021,6 @@ func TestSelectAggregationData(t *testing.T) { } executor := createExecutor(ctx, serv, cell, resolver) defer executor.Close() - executor.pv = querypb.ExecuteOptions_Gen4 tcases := []struct { sql string @@ -4196,8 +4178,7 @@ func TestSelectAggregationRandom(t *testing.T) { executor := createExecutor(ctx, serv, cell, resolver) defer executor.Close() - executor.pv = querypb.ExecuteOptions_Gen4 - session := NewAutocommitSession(&vtgatepb.Session{}) + session := econtext.NewAutocommitSession(&vtgatepb.Session{}) rs, err := executor.Execute(context.Background(), nil, "TestSelectCFC", session, "select /*vt+ PLANNER=gen4 */ A.a, A.b, (A.a / A.b) as c from (select sum(a) as a, sum(b) as b from user) A", nil) require.NoError(t, err) @@ -4207,7 +4188,7 @@ func TestSelectAggregationRandom(t *testing.T) { func TestSelectDateTypes(t *testing.T) { executor, _, _, _, _ := createExecutorEnv(t) executor.normalize = true - session := NewAutocommitSession(&vtgatepb.Session{}) + session := econtext.NewAutocommitSession(&vtgatepb.Session{}) qr, err := executor.Execute(context.Background(), nil, "TestSelectDateTypes", session, "select '2020-01-01' + interval month(date_sub(FROM_UNIXTIME(1234), interval 1 month))-1 month", nil) require.NoError(t, err) @@ -4218,7 +4199,7 @@ func TestSelectDateTypes(t *testing.T) { func TestSelectHexAndBit(t *testing.T) { executor, _, _, _, _ := createExecutorEnv(t) executor.normalize = true - session := NewAutocommitSession(&vtgatepb.Session{}) + session := econtext.NewAutocommitSession(&vtgatepb.Session{}) qr, err := executor.Execute(context.Background(), nil, "TestSelectHexAndBit", session, "select 0b1001, b'1001', 0x9, x'09'", nil) require.NoError(t, err) @@ -4234,7 +4215,7 @@ func TestSelectHexAndBit(t *testing.T) { func TestSelectCFC(t *testing.T) { executor, _, _, _, _ := createExecutorEnv(t) executor.normalize = true - session := NewAutocommitSession(&vtgatepb.Session{}) + session := econtext.NewAutocommitSession(&vtgatepb.Session{}) _, err := executor.Execute(context.Background(), nil, "TestSelectCFC", session, "select /*vt+ PLANNER=gen4 */ c2 from tbl_cfc where c1 like 'A%'", nil) require.NoError(t, err) @@ -4263,7 +4244,7 @@ func TestSelectView(t *testing.T) { require.NoError(t, err) executor.normalize = true - session := NewAutocommitSession(&vtgatepb.Session{}) + session := econtext.NewAutocommitSession(&vtgatepb.Session{}) _, err = executor.Execute(context.Background(), nil, "TestSelectView", session, "select * from user_details_view", nil) require.NoError(t, err) @@ -4304,7 +4285,7 @@ func TestWarmingReads(t *testing.T) { executor, primary, replica := createExecutorEnvWithPrimaryReplicaConn(t, ctx, 100) executor.normalize = true - session := NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded}) + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded}) // Since queries on the replica will run in a separate go-routine, we need synchronization for the Queries field in the sandboxconn. replica.RequireQueriesLocking() @@ -4368,6 +4349,7 @@ func TestWarmingReads(t *testing.T) { // waitUntilQueryCount waits until the number of queries run on the tablet reach the specified count. func waitUntilQueryCount(t *testing.T, tab *sandboxconn.SandboxConn, count int) { + t.Helper() timeout := time.After(1 * time.Second) for { select { @@ -4428,7 +4410,7 @@ func TestStreamJoinQuery(t *testing.T) { func TestSysVarGlobalAndSession(t *testing.T) { executor, sbc1, _, _, _ := createExecutorEnv(t) executor.normalize = true - session := NewAutocommitSession(&vtgatepb.Session{EnableSystemSettings: true, SystemVariables: map[string]string{}}) + session := econtext.NewAutocommitSession(&vtgatepb.Session{EnableSystemSettings: true, SystemVariables: map[string]string{}}) sbc1.SetResults([]*sqltypes.Result{ sqltypes.MakeTestResult(sqltypes.MakeTestFields("innodb_lock_wait_timeout", "uint64"), "20"), diff --git a/go/vt/vtgate/executor_set_test.go b/go/vt/vtgate/executor_set_test.go index 12e8e272bd7..f8ed0b558c3 100644 --- a/go/vt/vtgate/executor_set_test.go +++ b/go/vt/vtgate/executor_set_test.go @@ -22,6 +22,7 @@ import ( "vitess.io/vitess/go/mysql/sqlerror" querypb "vitess.io/vitess/go/vt/proto/query" + econtext "vitess.io/vitess/go/vt/vtgate/executorcontext" "vitess.io/vitess/go/test/utils" @@ -266,7 +267,7 @@ func TestExecutorSet(t *testing.T) { }} for i, tcase := range testcases { t.Run(fmt.Sprintf("%d-%s", i, tcase.in), func(t *testing.T) { - session := NewSafeSession(&vtgatepb.Session{Autocommit: true}) + session := econtext.NewSafeSession(&vtgatepb.Session{Autocommit: true}) _, err := executorEnv.Execute(ctx, nil, "TestExecute", session, tcase.in, nil) if tcase.err == "" { require.NoError(t, err) @@ -374,7 +375,7 @@ func TestExecutorSetOp(t *testing.T) { }} for _, tcase := range testcases { t.Run(tcase.in, func(t *testing.T) { - session := NewAutocommitSession(&vtgatepb.Session{ + session := econtext.NewAutocommitSession(&vtgatepb.Session{ TargetString: "@primary", }) session.TargetString = KsTestUnsharded @@ -392,7 +393,7 @@ func TestExecutorSetMetadata(t *testing.T) { t.Run("Session 1", func(t *testing.T) { executor, _, _, _, ctx := createExecutorEnv(t) - session := NewSafeSession(&vtgatepb.Session{TargetString: "@primary", Autocommit: true}) + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: "@primary", Autocommit: true}) set := "set @@vitess_metadata.app_keyspace_v1= '1'" _, err := executor.Execute(ctx, nil, "TestExecute", session, set, nil) @@ -400,21 +401,21 @@ func TestExecutorSetMetadata(t *testing.T) { }) t.Run("Session 2", func(t *testing.T) { - vschemaacl.AuthorizedDDLUsers = "%" + vschemaacl.AuthorizedDDLUsers.Set(vschemaacl.NewAuthorizedDDLUsers("%")) defer func() { - vschemaacl.AuthorizedDDLUsers = "" + vschemaacl.AuthorizedDDLUsers.Set(vschemaacl.NewAuthorizedDDLUsers("")) }() executor, _, _, _, ctx := createExecutorEnv(t) - session := NewSafeSession(&vtgatepb.Session{TargetString: "@primary", Autocommit: true}) + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: "@primary", Autocommit: true}) set := "set @@vitess_metadata.app_keyspace_v1= '1'" _, err := executor.Execute(ctx, nil, "TestExecute", session, set, nil) - assert.NoError(t, err, "%s error: %v", set, err) + require.NoError(t, err, "%s error: %v", set, err) show := `show vitess_metadata variables like 'app\\_keyspace\\_v_'` result, err := executor.Execute(ctx, nil, "TestExecute", session, show, nil) - assert.NoError(t, err) + require.NoError(t, err) want := "1" got := result.Rows[0][1].ToString() @@ -423,11 +424,11 @@ func TestExecutorSetMetadata(t *testing.T) { // Update metadata set = "set @@vitess_metadata.app_keyspace_v2='2'" _, err = executor.Execute(ctx, nil, "TestExecute", session, set, nil) - assert.NoError(t, err, "%s error: %v", set, err) + require.NoError(t, err, "%s error: %v", set, err) show = `show vitess_metadata variables like 'app\\_keyspace\\_v%'` gotqr, err := executor.Execute(ctx, nil, "TestExecute", session, show, nil) - assert.NoError(t, err) + require.NoError(t, err) wantqr := &sqltypes.Result{ Fields: buildVarCharFields("Key", "Value"), @@ -469,7 +470,7 @@ func TestPlanExecutorSetUDV(t *testing.T) { }} for _, tcase := range testcases { t.Run(tcase.in, func(t *testing.T) { - session := NewSafeSession(&vtgatepb.Session{Autocommit: true}) + session := econtext.NewSafeSession(&vtgatepb.Session{Autocommit: true}) _, err := executor.Execute(ctx, nil, "TestExecute", session, tcase.in, nil) if err != nil { require.EqualError(t, err, tcase.err) @@ -515,7 +516,7 @@ func TestSetVar(t *testing.T) { executor, _, _, sbc, ctx := createCustomExecutor(t, "{}", "8.0.0") executor.normalize = true - session := NewAutocommitSession(&vtgatepb.Session{EnableSystemSettings: true, TargetString: KsTestUnsharded}) + session := econtext.NewAutocommitSession(&vtgatepb.Session{EnableSystemSettings: true, TargetString: KsTestUnsharded}) sbc.SetResults([]*sqltypes.Result{sqltypes.MakeTestResult( sqltypes.MakeTestFields("orig|new", "varchar|varchar"), @@ -554,7 +555,7 @@ func TestSetVarShowVariables(t *testing.T) { executor, _, _, sbc, ctx := createCustomExecutor(t, "{}", "8.0.0") executor.normalize = true - session := NewAutocommitSession(&vtgatepb.Session{EnableSystemSettings: true, TargetString: KsTestUnsharded}) + session := econtext.NewAutocommitSession(&vtgatepb.Session{EnableSystemSettings: true, TargetString: KsTestUnsharded}) sbc.SetResults([]*sqltypes.Result{ // select query result for checking any change in system settings @@ -597,7 +598,7 @@ func TestExecutorSetAndSelect(t *testing.T) { sysVar: "tx_isolation", exp: `[[VARCHAR("READ-UNCOMMITTED")]]`, // this returns the value set in previous query. }} - session := NewAutocommitSession(&vtgatepb.Session{TargetString: KsTestUnsharded, EnableSystemSettings: true}) + session := econtext.NewAutocommitSession(&vtgatepb.Session{TargetString: KsTestUnsharded, EnableSystemSettings: true}) for _, tcase := range testcases { t.Run(fmt.Sprintf("%s-%s", tcase.sysVar, tcase.val), func(t *testing.T) { sbc.ExecCount.Store(0) // reset the value @@ -631,7 +632,7 @@ func TestExecutorSetAndSelect(t *testing.T) { func TestExecutorTimeZone(t *testing.T) { e, _, _, _, ctx := createExecutorEnv(t) - session := NewAutocommitSession(&vtgatepb.Session{TargetString: KsTestUnsharded, EnableSystemSettings: true}) + session := econtext.NewAutocommitSession(&vtgatepb.Session{TargetString: KsTestUnsharded, EnableSystemSettings: true}) session.SetSystemVariable("time_zone", "'+08:00'") qr, err := e.Execute(ctx, nil, "TestExecutorSetAndSelect", session, "select now()", nil) diff --git a/go/vt/vtgate/executor_stream_test.go b/go/vt/vtgate/executor_stream_test.go index b8cfeaf3cd5..a8500dd59c4 100644 --- a/go/vt/vtgate/executor_stream_test.go +++ b/go/vt/vtgate/executor_stream_test.go @@ -31,6 +31,7 @@ import ( topodatapb "vitess.io/vitess/go/vt/proto/topodata" vtgatepb "vitess.io/vitess/go/vt/proto/vtgate" "vitess.io/vitess/go/vt/vtenv" + econtext "vitess.io/vitess/go/vt/vtgate/executorcontext" "vitess.io/vitess/go/vt/vtgate/logstats" _ "vitess.io/vitess/go/vt/vtgate/vindexes" "vitess.io/vitess/go/vt/vttablet/sandboxconn" @@ -102,7 +103,7 @@ func executorStreamMessages(executor *Executor, sql string) (qr *sqltypes.Result ctx, nil, "TestExecuteStream", - NewSafeSession(session), + econtext.NewSafeSession(session), sql, nil, func(qr *sqltypes.Result) error { diff --git a/go/vt/vtgate/executor_test.go b/go/vt/vtgate/executor_test.go index 3732a37d1d1..d3ab28d6600 100644 --- a/go/vt/vtgate/executor_test.go +++ b/go/vt/vtgate/executor_test.go @@ -36,6 +36,8 @@ import ( "github.com/stretchr/testify/require" "google.golang.org/protobuf/proto" + econtext "vitess.io/vitess/go/vt/vtgate/executorcontext" + "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/mysql/sqlerror" "vitess.io/vitess/go/sqltypes" @@ -64,7 +66,7 @@ func TestExecutorResultsExceeded(t *testing.T) { warnMemoryRows = 3 defer func() { warnMemoryRows = save }() - session := NewSafeSession(&vtgatepb.Session{TargetString: "@primary"}) + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: "@primary"}) initial := warnings.Counts()["ResultsExceeded"] @@ -88,7 +90,7 @@ func TestExecutorMaxMemoryRowsExceeded(t *testing.T) { maxMemoryRows = 3 defer func() { maxMemoryRows = save }() - session := NewSafeSession(&vtgatepb.Session{TargetString: "@primary"}) + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: "@primary"}) result := sqltypes.MakeTestResult(sqltypes.MakeTestFields("col", "int64"), "1", "2", "3", "4") fn := func(r *sqltypes.Result) error { return nil @@ -122,7 +124,7 @@ func TestExecutorMaxMemoryRowsExceeded(t *testing.T) { func TestExecutorTransactionsNoAutoCommit(t *testing.T) { executor, _, _, sbclookup, ctx := createExecutorEnv(t) - session := NewSafeSession(&vtgatepb.Session{TargetString: "@primary", SessionUUID: "suuid"}) + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: "@primary", SessionUUID: "suuid"}) logChan := executor.queryLogger.Subscribe("Test") defer executor.queryLogger.Unsubscribe(logChan) @@ -188,7 +190,7 @@ func TestExecutorTransactionsNoAutoCommit(t *testing.T) { } // Prevent use of non-primary if in_transaction is on. - session = NewSafeSession(&vtgatepb.Session{TargetString: "@primary", InTransaction: true}) + session = econtext.NewSafeSession(&vtgatepb.Session{TargetString: "@primary", InTransaction: true}) _, err = executor.Execute(ctx, nil, "TestExecute", session, "use @replica", nil) require.EqualError(t, err, `can't execute the given command because you have an active transaction`) } @@ -205,7 +207,7 @@ func TestDirectTargetRewrites(t *testing.T) { } sql := "select database()" - _, err := executor.Execute(ctx, nil, "TestExecute", NewSafeSession(session), sql, map[string]*querypb.BindVariable{}) + _, err := executor.Execute(ctx, nil, "TestExecute", econtext.NewSafeSession(session), sql, map[string]*querypb.BindVariable{}) require.NoError(t, err) assertQueries(t, sbclookup, []*querypb.BoundQuery{{ Sql: "select :__vtdbname as `database()` from dual", @@ -216,7 +218,7 @@ func TestDirectTargetRewrites(t *testing.T) { func TestExecutorTransactionsAutoCommit(t *testing.T) { executor, _, _, sbclookup, ctx := createExecutorEnv(t) - session := NewSafeSession(&vtgatepb.Session{TargetString: "@primary", Autocommit: true, SessionUUID: "suuid"}) + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: "@primary", Autocommit: true, SessionUUID: "suuid"}) logChan := executor.queryLogger.Subscribe("Test") defer executor.queryLogger.Unsubscribe(logChan) @@ -270,7 +272,7 @@ func TestExecutorTransactionsAutoCommitStreaming(t *testing.T) { executor, _, _, sbclookup, ctx := createExecutorEnv(t) oltpOptions := &querypb.ExecuteOptions{Workload: querypb.ExecuteOptions_OLTP} - session := NewSafeSession(&vtgatepb.Session{ + session := econtext.NewSafeSession(&vtgatepb.Session{ TargetString: "@primary", Autocommit: true, Options: oltpOptions, @@ -333,13 +335,13 @@ func TestExecutorTransactionsAutoCommitStreaming(t *testing.T) { } func TestExecutorDeleteMetadata(t *testing.T) { - vschemaacl.AuthorizedDDLUsers = "%" + vschemaacl.AuthorizedDDLUsers.Set(vschemaacl.NewAuthorizedDDLUsers("%")) defer func() { - vschemaacl.AuthorizedDDLUsers = "" + vschemaacl.AuthorizedDDLUsers.Set(vschemaacl.NewAuthorizedDDLUsers("")) }() executor, _, _, _, ctx := createExecutorEnv(t) - session := NewSafeSession(&vtgatepb.Session{TargetString: "@primary", Autocommit: true}) + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: "@primary", Autocommit: true}) set := "set @@vitess_metadata.app_v1= '1'" _, err := executor.Execute(ctx, nil, "TestExecute", session, set, nil) @@ -367,7 +369,7 @@ func TestExecutorDeleteMetadata(t *testing.T) { func TestExecutorAutocommit(t *testing.T) { executor, _, _, sbclookup, ctx := createExecutorEnv(t) - session := NewSafeSession(&vtgatepb.Session{TargetString: "@primary"}) + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: "@primary"}) logChan := executor.queryLogger.Subscribe("Test") defer executor.queryLogger.Unsubscribe(logChan) @@ -446,7 +448,7 @@ func TestExecutorAutocommit(t *testing.T) { // transition autocommit from 0 to 1 in the middle of a transaction. startCount = sbclookup.CommitCount.Load() - session = NewSafeSession(&vtgatepb.Session{TargetString: "@primary"}) + session = econtext.NewSafeSession(&vtgatepb.Session{TargetString: "@primary"}) _, err = executor.Execute(ctx, nil, "TestExecute", session, "begin", nil) require.NoError(t, err) _, err = executor.Execute(ctx, nil, "TestExecute", session, "update main1 set id=1", nil) @@ -468,7 +470,7 @@ func TestExecutorAutocommit(t *testing.T) { func TestExecutorShowColumns(t *testing.T) { executor, sbc1, sbc2, sbclookup, ctx := createExecutorEnv(t) - session := NewSafeSession(&vtgatepb.Session{TargetString: ""}) + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: ""}) queries := []string{ "SHOW COLUMNS FROM `user` in `TestExecutor`", @@ -520,7 +522,7 @@ func assertMatchesNoOrder(t *testing.T, expected, got string) { func TestExecutorShow(t *testing.T) { executor, _, _, sbclookup, ctx := createExecutorEnv(t) - session := NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}) + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}) for _, query := range []string{"show vitess_keyspaces", "show keyspaces"} { qr, err := executor.Execute(ctx, nil, "TestExecute", session, query, nil) @@ -545,7 +547,7 @@ func TestExecutorShow(t *testing.T) { _, err = executor.Execute(ctx, nil, "TestExecute", session, "use @primary", nil) require.NoError(t, err) _, err = executor.Execute(ctx, nil, "TestExecute", session, "show tables", nil) - assert.EqualError(t, err, errNoKeyspace.Error(), "'show tables' should fail without a keyspace") + assert.EqualError(t, err, econtext.ErrNoKeyspace.Error(), "'show tables' should fail without a keyspace") assert.Empty(t, sbclookup.Queries, "sbclookup unexpectedly has queries already") showResults := &sqltypes.Result{ @@ -920,7 +922,7 @@ func TestExecutorShow(t *testing.T) { query = "show vschema vindexes on user" _, err = executor.Execute(ctx, nil, "TestExecute", session, query, nil) - wantErr := errNoKeyspace.Error() + wantErr := econtext.ErrNoKeyspace.Error() assert.EqualError(t, err, wantErr, query) query = "show vschema vindexes on TestExecutor.garbage" @@ -1024,7 +1026,7 @@ func TestExecutorShow(t *testing.T) { utils.MustMatch(t, wantqr, qr, fmt.Sprintf("%s, with a bad keyspace", query)) query = "show vschema tables" - session = NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded}) + session = econtext.NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded}) qr, err = executor.Execute(ctx, nil, "TestExecute", session, query, nil) require.NoError(t, err) wantqr = &sqltypes.Result{ @@ -1050,9 +1052,9 @@ func TestExecutorShow(t *testing.T) { utils.MustMatch(t, wantqr, qr, query) query = "show vschema tables" - session = NewSafeSession(&vtgatepb.Session{}) + session = econtext.NewSafeSession(&vtgatepb.Session{}) _, err = executor.Execute(ctx, nil, "TestExecute", session, query, nil) - want = errNoKeyspace.Error() + want = econtext.ErrNoKeyspace.Error() assert.EqualError(t, err, want, query) query = "show 10" @@ -1061,7 +1063,7 @@ func TestExecutorShow(t *testing.T) { assert.EqualError(t, err, want, query) query = "show vschema tables" - session = NewSafeSession(&vtgatepb.Session{TargetString: "no_such_keyspace"}) + session = econtext.NewSafeSession(&vtgatepb.Session{TargetString: "no_such_keyspace"}) _, err = executor.Execute(ctx, nil, "TestExecute", session, query, nil) want = "VT05003: unknown database 'no_such_keyspace' in vschema" assert.EqualError(t, err, want, query) @@ -1080,7 +1082,7 @@ func TestExecutorShow(t *testing.T) { func TestExecutorShowTargeted(t *testing.T) { executor, _, sbc2, _, ctx := createExecutorEnv(t) - session := NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor/40-60"}) + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor/40-60"}) queries := []string{ "show databases", @@ -1107,7 +1109,7 @@ func TestExecutorShowTargeted(t *testing.T) { func TestExecutorShowFromSystemSchema(t *testing.T) { executor, _, _, _, ctx := createExecutorEnv(t) - session := NewSafeSession(&vtgatepb.Session{TargetString: "mysql"}) + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: "mysql"}) _, err := executor.Execute(ctx, nil, "TestExecutorShowFromSystemSchema", session, "show tables", nil) require.NoError(t, err) @@ -1116,7 +1118,7 @@ func TestExecutorShowFromSystemSchema(t *testing.T) { func TestExecutorUse(t *testing.T) { executor, _, _, _, ctx := createExecutorEnv(t) - session := NewSafeSession(&vtgatepb.Session{Autocommit: true, TargetString: "@primary"}) + session := econtext.NewSafeSession(&vtgatepb.Session{Autocommit: true, TargetString: "@primary"}) stmts := []string{ "use TestExecutor", @@ -1135,13 +1137,13 @@ func TestExecutorUse(t *testing.T) { utils.MustMatch(t, wantSession, session.Session, "session does not match") } - _, err := executor.Execute(ctx, nil, "TestExecute", NewSafeSession(&vtgatepb.Session{}), "use 1", nil) + _, err := executor.Execute(ctx, nil, "TestExecute", econtext.NewSafeSession(&vtgatepb.Session{}), "use 1", nil) wantErr := "syntax error at position 6 near '1'" if err == nil || err.Error() != wantErr { t.Errorf("got: %v, want %v", err, wantErr) } - _, err = executor.Execute(ctx, nil, "TestExecute", NewSafeSession(&vtgatepb.Session{}), "use UnexistentKeyspace", nil) + _, err = executor.Execute(ctx, nil, "TestExecute", econtext.NewSafeSession(&vtgatepb.Session{}), "use UnexistentKeyspace", nil) require.EqualError(t, err, "VT05003: unknown database 'UnexistentKeyspace' in vschema") } @@ -1155,7 +1157,7 @@ func TestExecutorComment(t *testing.T) { wantResult := &sqltypes.Result{} for _, stmt := range stmts { - gotResult, err := executor.Execute(ctx, nil, "TestExecute", NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded}), stmt, nil) + gotResult, err := executor.Execute(ctx, nil, "TestExecute", econtext.NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded}), stmt, nil) if err != nil { t.Error(err) } @@ -1240,9 +1242,9 @@ func TestExecutorDDL(t *testing.T) { sbc2.ExecCount.Store(0) sbclookup.ExecCount.Store(0) stmtType := "DDL" - _, err := executor.Execute(ctx, nil, "TestExecute", NewSafeSession(&vtgatepb.Session{TargetString: tc.targetStr}), stmt, nil) + _, err := executor.Execute(ctx, nil, "TestExecute", econtext.NewSafeSession(&vtgatepb.Session{TargetString: tc.targetStr}), stmt, nil) if tc.hasNoKeyspaceErr { - require.EqualError(t, err, errNoKeyspace.Error(), "expect query to fail: %q", stmt) + require.EqualError(t, err, econtext.ErrNoKeyspace.Error(), "expect query to fail: %q", stmt) stmtType = "" // For error case, plan is not generated to query log will not contain any stmtType. } else { require.NoError(t, err, "did not expect error for query: %q", stmt) @@ -1278,9 +1280,9 @@ func TestExecutorDDL(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: ""}), stmt.input, nil) + _, err := executor.Execute(ctx, nil, "TestExecute", econtext.NewSafeSession(&vtgatepb.Session{TargetString: ""}), stmt.input, nil) if stmt.hasErr { - require.EqualError(t, err, errNoKeyspace.Error(), "expect query to fail") + require.EqualError(t, err, econtext.ErrNoKeyspace.Error(), "expect query to fail") testQueryLog(t, executor, logChan, "TestExecute", "", stmt.input, 0) } else { require.NoError(t, err) @@ -1297,13 +1299,13 @@ func TestExecutorDDLFk(t *testing.T) { } for _, stmt := range stmts { - for _, fkMode := range []string{"allow", "disallow"} { - t.Run(stmt+fkMode, func(t *testing.T) { + for _, mode := range []string{"allow", "disallow"} { + t.Run(stmt+mode, func(t *testing.T) { executor, _, _, sbc, ctx := createExecutorEnv(t) sbc.ExecCount.Store(0) - foreignKeyMode = fkMode - _, err := executor.Execute(ctx, nil, mName, NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded}), stmt, nil) - if fkMode == "allow" { + executor.vConfig.ForeignKeyMode = fkMode(mode) + _, err := executor.Execute(ctx, nil, mName, econtext.NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded}), stmt, nil) + if mode == "allow" { require.NoError(t, err) require.EqualValues(t, 1, sbc.ExecCount.Load()) } else { @@ -1316,13 +1318,13 @@ func TestExecutorDDLFk(t *testing.T) { } func TestExecutorAlterVSchemaKeyspace(t *testing.T) { - vschemaacl.AuthorizedDDLUsers = "%" + vschemaacl.AuthorizedDDLUsers.Set(vschemaacl.NewAuthorizedDDLUsers("%")) defer func() { - vschemaacl.AuthorizedDDLUsers = "" + vschemaacl.AuthorizedDDLUsers.Set(vschemaacl.NewAuthorizedDDLUsers("")) }() executor, _, _, _, ctx := createExecutorEnv(t) - session := NewSafeSession(&vtgatepb.Session{TargetString: "@primary", Autocommit: true}) + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: "@primary", Autocommit: true}) vschemaUpdates := make(chan *vschemapb.SrvVSchema, 2) executor.serv.WatchSrvVSchema(ctx, executor.cell, func(vschema *vschemapb.SrvVSchema, err error) bool { @@ -1345,9 +1347,9 @@ func TestExecutorAlterVSchemaKeyspace(t *testing.T) { } func TestExecutorCreateVindexDDL(t *testing.T) { - vschemaacl.AuthorizedDDLUsers = "%" + vschemaacl.AuthorizedDDLUsers.Set(vschemaacl.NewAuthorizedDDLUsers("%")) defer func() { - vschemaacl.AuthorizedDDLUsers = "" + vschemaacl.AuthorizedDDLUsers.Set(vschemaacl.NewAuthorizedDDLUsers("")) }() executor, sbc1, sbc2, sbclookup, ctx := createExecutorEnv(t) ks := "TestExecutor" @@ -1364,7 +1366,7 @@ func TestExecutorCreateVindexDDL(t *testing.T) { t.Fatalf("test_vindex should not exist in original vschema") } - session := NewSafeSession(&vtgatepb.Session{TargetString: ks}) + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: ks}) stmt := "alter vschema create vindex test_vindex using hash" _, err := executor.Execute(ctx, nil, "TestExecute", session, stmt, nil) require.NoError(t, err) @@ -1388,7 +1390,7 @@ func TestExecutorCreateVindexDDL(t *testing.T) { // Create a new vschema keyspace implicitly by creating a vindex with a different // target in the session // ksNew := "test_new_keyspace" - session = NewSafeSession(&vtgatepb.Session{TargetString: ks}) + session = econtext.NewSafeSession(&vtgatepb.Session{TargetString: ks}) stmt = "alter vschema create vindex test_vindex2 using hash" _, err = executor.Execute(ctx, nil, "TestExecute", session, stmt, nil) if err != nil { @@ -1415,9 +1417,9 @@ func TestExecutorCreateVindexDDL(t *testing.T) { } func TestExecutorAddDropVschemaTableDDL(t *testing.T) { - vschemaacl.AuthorizedDDLUsers = "%" + vschemaacl.AuthorizedDDLUsers.Set(vschemaacl.NewAuthorizedDDLUsers("%")) defer func() { - vschemaacl.AuthorizedDDLUsers = "" + vschemaacl.AuthorizedDDLUsers.Set(vschemaacl.NewAuthorizedDDLUsers("")) }() executor, sbc1, sbc2, sbclookup, ctx := createExecutorEnv(t) ks := KsTestUnsharded @@ -1439,7 +1441,7 @@ func TestExecutorAddDropVschemaTableDDL(t *testing.T) { vschemaTables = append(vschemaTables, t) } - session := NewSafeSession(&vtgatepb.Session{TargetString: ks}) + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: ks}) stmt := "alter vschema add table test_table" _, err := executor.Execute(ctx, nil, "TestExecute", session, stmt, nil) require.NoError(t, err) @@ -1451,7 +1453,7 @@ func TestExecutorAddDropVschemaTableDDL(t *testing.T) { _ = waitForVschemaTables(t, ks, append([]string{"test_table", "test_table2"}, vschemaTables...), executor) // Should fail adding a table on a sharded keyspace - session = NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}) + session = econtext.NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}) stmt = "alter vschema add table test_table" _, err = executor.Execute(ctx, nil, "TestExecute", session, stmt, nil) require.EqualError(t, err, "add vschema table: unsupported on sharded keyspace TestExecutor") @@ -1470,7 +1472,7 @@ func TestExecutorVindexDDLACL(t *testing.T) { executor, _, _, _, ctx := createExecutorEnv(t) ks := "TestExecutor" - session := NewSafeSession(&vtgatepb.Session{TargetString: ks}) + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: ks}) ctxRedUser := callerid.NewContext(ctx, &vtrpcpb.CallerID{}, &querypb.VTGateCallerID{Username: "redUser"}) ctxBlueUser := callerid.NewContext(ctx, &vtrpcpb.CallerID{}, &querypb.VTGateCallerID{Username: "blueUser"}) @@ -1484,8 +1486,7 @@ func TestExecutorVindexDDLACL(t *testing.T) { require.EqualError(t, err, `User 'blueUser' is not authorized to perform vschema operations`) // test when all users are enabled - vschemaacl.AuthorizedDDLUsers = "%" - vschemaacl.Init() + vschemaacl.AuthorizedDDLUsers.Set(vschemaacl.NewAuthorizedDDLUsers("%")) _, err = executor.Execute(ctxRedUser, nil, "TestExecute", session, stmt, nil) if err != nil { t.Errorf("unexpected error '%v'", err) @@ -1497,8 +1498,7 @@ func TestExecutorVindexDDLACL(t *testing.T) { } // test when only one user is enabled - vschemaacl.AuthorizedDDLUsers = "orangeUser, blueUser, greenUser" - vschemaacl.Init() + vschemaacl.AuthorizedDDLUsers.Set(vschemaacl.NewAuthorizedDDLUsers("orangeUser, blueUser, greenUser")) _, err = executor.Execute(ctxRedUser, nil, "TestExecute", session, stmt, nil) require.EqualError(t, err, `User 'redUser' is not authorized to perform vschema operations`) @@ -1509,13 +1509,13 @@ func TestExecutorVindexDDLACL(t *testing.T) { } // restore the disallowed state - vschemaacl.AuthorizedDDLUsers = "" + vschemaacl.AuthorizedDDLUsers.Set(vschemaacl.NewAuthorizedDDLUsers("")) } func TestExecutorUnrecognized(t *testing.T) { executor, _, _, _, ctx := createExecutorEnv(t) - _, err := executor.Execute(ctx, nil, "TestExecute", NewSafeSession(&vtgatepb.Session{}), "invalid statement", nil) + _, err := executor.Execute(ctx, nil, "TestExecute", econtext.NewSafeSession(&vtgatepb.Session{}), "invalid statement", nil) require.Error(t, err, "unrecognized statement: invalid statement'") } @@ -1525,7 +1525,7 @@ func TestExecutorDeniedErrorNoBuffer(t *testing.T) { vschemaWaitTimeout = 500 * time.Millisecond - session := NewAutocommitSession(&vtgatepb.Session{TargetString: "@primary"}) + session := econtext.NewAutocommitSession(&vtgatepb.Session{TargetString: "@primary"}) startExec := time.Now() _, err := executor.Execute(ctx, nil, "TestExecutorDeniedErrorNoBuffer", session, "select * from user", nil) require.NoError(t, err, "enforce denied tables not buffered") @@ -1559,9 +1559,8 @@ var pv = querypb.ExecuteOptions_Gen4 func TestGetPlanUnnormalized(t *testing.T) { r, _, _, _, ctx := createExecutorEnv(t) - - emptyvc, _ := newVCursorImpl(NewSafeSession(&vtgatepb.Session{TargetString: "@unknown"}), makeComments(""), r, nil, r.vm, r.VSchema(), r.resolver.resolver, nil, false, pv) - unshardedvc, _ := newVCursorImpl(NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded + "@unknown"}), makeComments(""), r, nil, r.vm, r.VSchema(), r.resolver.resolver, nil, false, pv) + emptyvc, _ := econtext.NewVCursorImpl(econtext.NewSafeSession(&vtgatepb.Session{TargetString: "@unknown"}), makeComments(""), r, nil, r.vm, r.VSchema(), r.resolver.resolver, nil, nullResultsObserver{}, econtext.VCursorConfig{}) + unshardedvc, _ := econtext.NewVCursorImpl(econtext.NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded + "@unknown"}), makeComments(""), r, nil, r.vm, r.VSchema(), r.resolver.resolver, nil, nullResultsObserver{}, econtext.VCursorConfig{}) query1 := "select * from music_user_map where id = 1" plan1, logStats1 := getPlanCached(t, ctx, r, emptyvc, query1, makeComments(" /* comment */"), map[string]*querypb.BindVariable{}, false) @@ -1604,7 +1603,7 @@ func assertCacheSize(t *testing.T, c *PlanCache, expected int) { } } -func assertCacheContains(t *testing.T, e *Executor, vc *vcursorImpl, sql string) *engine.Plan { +func assertCacheContains(t *testing.T, e *Executor, vc *econtext.VCursorImpl, sql string) *engine.Plan { t.Helper() var plan *engine.Plan @@ -1623,9 +1622,9 @@ func assertCacheContains(t *testing.T, e *Executor, vc *vcursorImpl, sql string) return plan } -func getPlanCached(t *testing.T, ctx context.Context, e *Executor, vcursor *vcursorImpl, sql string, comments sqlparser.MarginComments, bindVars map[string]*querypb.BindVariable, skipQueryPlanCache bool) (*engine.Plan, *logstats.LogStats) { +func getPlanCached(t *testing.T, ctx context.Context, e *Executor, vcursor *econtext.VCursorImpl, sql string, comments sqlparser.MarginComments, bindVars map[string]*querypb.BindVariable, skipQueryPlanCache bool) (*engine.Plan, *logstats.LogStats) { logStats := logstats.NewLogStats(ctx, "Test", "", "", nil) - vcursor.safeSession = &SafeSession{ + vcursor.SafeSession = &econtext.SafeSession{ Session: &vtgatepb.Session{ Options: &querypb.ExecuteOptions{SkipQueryPlanCache: skipQueryPlanCache}}, } @@ -1644,7 +1643,7 @@ func TestGetPlanCacheUnnormalized(t *testing.T) { t.Run("Cache", func(t *testing.T) { r, _, _, _, ctx := createExecutorEnv(t) - emptyvc, _ := newVCursorImpl(NewSafeSession(&vtgatepb.Session{TargetString: "@unknown"}), makeComments(""), r, nil, r.vm, r.VSchema(), r.resolver.resolver, nil, false, pv) + emptyvc, _ := econtext.NewVCursorImpl(econtext.NewSafeSession(&vtgatepb.Session{TargetString: "@unknown"}), makeComments(""), r, nil, r.vm, r.VSchema(), r.resolver.resolver, nil, nullResultsObserver{}, econtext.VCursorConfig{}) query1 := "select * from music_user_map where id = 1" _, logStats1 := getPlanCached(t, ctx, r, emptyvc, query1, makeComments(" /* comment */"), map[string]*querypb.BindVariable{}, true) @@ -1668,7 +1667,7 @@ func TestGetPlanCacheUnnormalized(t *testing.T) { // Skip cache using directive r, _, _, _, ctx := createExecutorEnv(t) - unshardedvc, _ := newVCursorImpl(NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded + "@unknown"}), makeComments(""), r, nil, r.vm, r.VSchema(), r.resolver.resolver, nil, false, pv) + unshardedvc, _ := econtext.NewVCursorImpl(econtext.NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded + "@unknown"}), makeComments(""), r, nil, r.vm, r.VSchema(), r.resolver.resolver, nil, nullResultsObserver{}, r.vConfig) query1 := "insert /*vt+ SKIP_QUERY_PLAN_CACHE=1 */ into user(id) values (1), (2)" getPlanCached(t, ctx, r, unshardedvc, query1, makeComments(" /* comment */"), map[string]*querypb.BindVariable{}, false) @@ -1679,12 +1678,12 @@ func TestGetPlanCacheUnnormalized(t *testing.T) { assertCacheSize(t, r.plans, 1) // the target string will be resolved and become part of the plan cache key, which adds a new entry - ksIDVc1, _ := newVCursorImpl(NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded + "[deadbeef]"}), makeComments(""), r, nil, r.vm, r.VSchema(), r.resolver.resolver, nil, false, pv) + ksIDVc1, _ := econtext.NewVCursorImpl(econtext.NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded + "[deadbeef]"}), makeComments(""), r, nil, r.vm, r.VSchema(), r.resolver.resolver, nil, nullResultsObserver{}, r.vConfig) getPlanCached(t, ctx, r, ksIDVc1, query1, makeComments(" /* comment */"), map[string]*querypb.BindVariable{}, false) assertCacheSize(t, r.plans, 2) // the target string will be resolved and become part of the plan cache key, as it's an unsharded ks, it will be the same entry as above - ksIDVc2, _ := newVCursorImpl(NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded + "[beefdead]"}), makeComments(""), r, nil, r.vm, r.VSchema(), r.resolver.resolver, nil, false, pv) + ksIDVc2, _ := econtext.NewVCursorImpl(econtext.NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded + "[beefdead]"}), makeComments(""), r, nil, r.vm, r.VSchema(), r.resolver.resolver, nil, nullResultsObserver{}, r.vConfig) getPlanCached(t, ctx, r, ksIDVc2, query1, makeComments(" /* comment */"), map[string]*querypb.BindVariable{}, false) assertCacheSize(t, r.plans, 2) }) @@ -1694,7 +1693,7 @@ func TestGetPlanCacheNormalized(t *testing.T) { t.Run("Cache", func(t *testing.T) { r, _, _, _, ctx := createExecutorEnv(t) r.normalize = true - emptyvc, _ := newVCursorImpl(NewSafeSession(&vtgatepb.Session{TargetString: "@unknown"}), makeComments(""), r, nil, r.vm, r.VSchema(), r.resolver.resolver, nil, false, pv) + emptyvc, _ := econtext.NewVCursorImpl(econtext.NewSafeSession(&vtgatepb.Session{TargetString: "@unknown"}), makeComments(""), r, nil, r.vm, r.VSchema(), r.resolver.resolver, nil, nullResultsObserver{}, r.vConfig) query1 := "select * from music_user_map where id = 1" _, logStats1 := getPlanCached(t, ctx, r, emptyvc, query1, makeComments(" /* comment */"), map[string]*querypb.BindVariable{}, true /* skipQueryPlanCache */) @@ -1711,7 +1710,7 @@ func TestGetPlanCacheNormalized(t *testing.T) { // Skip cache using directive r, _, _, _, ctx := createExecutorEnv(t) r.normalize = true - unshardedvc, _ := newVCursorImpl(NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded + "@unknown"}), makeComments(""), r, nil, r.vm, r.VSchema(), r.resolver.resolver, nil, false, pv) + unshardedvc, _ := econtext.NewVCursorImpl(econtext.NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded + "@unknown"}), makeComments(""), r, nil, r.vm, r.VSchema(), r.resolver.resolver, nil, nullResultsObserver{}, r.vConfig) query1 := "insert /*vt+ SKIP_QUERY_PLAN_CACHE=1 */ into user(id) values (1), (2)" getPlanCached(t, ctx, r, unshardedvc, query1, makeComments(" /* comment */"), map[string]*querypb.BindVariable{}, false) @@ -1722,12 +1721,12 @@ func TestGetPlanCacheNormalized(t *testing.T) { assertCacheSize(t, r.plans, 1) // the target string will be resolved and become part of the plan cache key, which adds a new entry - ksIDVc1, _ := newVCursorImpl(NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded + "[deadbeef]"}), makeComments(""), r, nil, r.vm, r.VSchema(), r.resolver.resolver, nil, false, pv) + ksIDVc1, _ := econtext.NewVCursorImpl(econtext.NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded + "[deadbeef]"}), makeComments(""), r, nil, r.vm, r.VSchema(), r.resolver.resolver, nil, nullResultsObserver{}, r.vConfig) getPlanCached(t, ctx, r, ksIDVc1, query1, makeComments(" /* comment */"), map[string]*querypb.BindVariable{}, false) assertCacheSize(t, r.plans, 2) // the target string will be resolved and become part of the plan cache key, as it's an unsharded ks, it will be the same entry as above - ksIDVc2, _ := newVCursorImpl(NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded + "[beefdead]"}), makeComments(""), r, nil, r.vm, r.VSchema(), r.resolver.resolver, nil, false, pv) + ksIDVc2, _ := econtext.NewVCursorImpl(econtext.NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded + "[beefdead]"}), makeComments(""), r, nil, r.vm, r.VSchema(), r.resolver.resolver, nil, nullResultsObserver{}, r.vConfig) getPlanCached(t, ctx, r, ksIDVc2, query1, makeComments(" /* comment */"), map[string]*querypb.BindVariable{}, false) assertCacheSize(t, r.plans, 2) }) @@ -1737,8 +1736,8 @@ func TestGetPlanNormalized(t *testing.T) { r, _, _, _, ctx := createExecutorEnv(t) r.normalize = true - emptyvc, _ := newVCursorImpl(NewSafeSession(&vtgatepb.Session{TargetString: "@unknown"}), makeComments(""), r, nil, r.vm, r.VSchema(), r.resolver.resolver, nil, false, pv) - unshardedvc, _ := newVCursorImpl(NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded + "@unknown"}), makeComments(""), r, nil, r.vm, r.VSchema(), r.resolver.resolver, nil, false, pv) + emptyvc, _ := econtext.NewVCursorImpl(econtext.NewSafeSession(&vtgatepb.Session{TargetString: "@unknown"}), makeComments(""), r, nil, r.vm, r.VSchema(), r.resolver.resolver, nil, nullResultsObserver{}, econtext.VCursorConfig{}) + unshardedvc, _ := econtext.NewVCursorImpl(econtext.NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded + "@unknown"}), makeComments(""), r, nil, r.vm, r.VSchema(), r.resolver.resolver, nil, nullResultsObserver{}, econtext.VCursorConfig{}) query1 := "select * from music_user_map where id = 1" query2 := "select * from music_user_map where id = 2" @@ -1785,7 +1784,7 @@ func TestGetPlanPriority(t *testing.T) { {name: "empty priority", sql: "select * from music_user_map", expectedPriority: "", expectedError: nil}, } - session := NewSafeSession(&vtgatepb.Session{TargetString: "@unknown", Options: &querypb.ExecuteOptions{}}) + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: "@unknown", Options: &querypb.ExecuteOptions{}}) for _, aTestCase := range testCases { testCase := aTestCase @@ -1795,7 +1794,7 @@ func TestGetPlanPriority(t *testing.T) { r.normalize = true logStats := logstats.NewLogStats(ctx, "Test", "", "", nil) - vCursor, err := newVCursorImpl(session, makeComments(""), r, nil, r.vm, r.VSchema(), r.resolver.resolver, nil, false, pv) + vCursor, err := econtext.NewVCursorImpl(session, makeComments(""), r, nil, r.vm, r.VSchema(), r.resolver.resolver, nil, nullResultsObserver{}, econtext.VCursorConfig{}) assert.NoError(t, err) stmt, err := sqlparser.NewTestParser().Parse(testCase.sql) @@ -1809,7 +1808,7 @@ func TestGetPlanPriority(t *testing.T) { } else { assert.NoError(t, err) assert.Equal(t, testCase.expectedPriority, priorityFromStatement) - assert.Equal(t, testCase.expectedPriority, vCursor.safeSession.Options.Priority) + assert.Equal(t, testCase.expectedPriority, vCursor.SafeSession.Options.Priority) } }) } @@ -1966,7 +1965,7 @@ func TestExecutorMaxPayloadSizeExceeded(t *testing.T) { executor, _, _, _, _ := createExecutorEnv(t) - session := NewSafeSession(&vtgatepb.Session{TargetString: "@primary"}) + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: "@primary"}) warningCount := warnings.Counts()["WarnPayloadSizeExceeded"] testMaxPayloadSizeExceeded := []string{ "select * from main1", @@ -2014,7 +2013,7 @@ func TestOlapSelectDatabase(t *testing.T) { cbInvoked = true return nil } - err := executor.StreamExecute(context.Background(), nil, "TestExecute", NewSafeSession(session), sql, nil, cb) + err := executor.StreamExecute(context.Background(), nil, "TestExecute", econtext.NewSafeSession(session), sql, nil, cb) assert.NoError(t, err) assert.True(t, cbInvoked) } @@ -2022,7 +2021,7 @@ func TestOlapSelectDatabase(t *testing.T) { func TestExecutorClearsWarnings(t *testing.T) { executor, _, _, _, _ := createExecutorEnv(t) - session := NewSafeSession(&vtgatepb.Session{ + session := econtext.NewSafeSession(&vtgatepb.Session{ Warnings: []*querypb.QueryWarning{{Code: 234, Message: "oh noes"}}, }) _, err := executor.Execute(context.Background(), nil, "TestExecute", session, "select 42", nil) @@ -2039,7 +2038,6 @@ func TestServingKeyspaces(t *testing.T) { executor, sbc1, _, sbclookup, ctx := createExecutorEnv(t) - executor.pv = querypb.ExecuteOptions_Gen4 gw, ok := executor.resolver.resolver.GetGateway().(*TabletGateway) require.True(t, ok) hc := gw.hc.(*discovery.FakeHealthCheck) @@ -2058,7 +2056,7 @@ func TestServingKeyspaces(t *testing.T) { }) require.ElementsMatch(t, []string{"TestExecutor", "TestUnsharded"}, gw.GetServingKeyspaces()) - result, err := executor.Execute(ctx, nil, "TestServingKeyspaces", NewSafeSession(&vtgatepb.Session{}), "select keyspace_name from dual", nil) + result, err := executor.Execute(ctx, nil, "TestServingKeyspaces", econtext.NewSafeSession(&vtgatepb.Session{}), "select keyspace_name from dual", nil) require.NoError(t, err) require.Equal(t, `[[VARCHAR("TestExecutor")]]`, fmt.Sprintf("%v", result.Rows)) @@ -2074,7 +2072,7 @@ func TestServingKeyspaces(t *testing.T) { // Clear plan cache, to force re-planning of the query. executor.ClearPlans() require.ElementsMatch(t, []string{"TestUnsharded"}, gw.GetServingKeyspaces()) - result, err = executor.Execute(ctx, nil, "TestServingKeyspaces", NewSafeSession(&vtgatepb.Session{}), "select keyspace_name from dual", nil) + result, err = executor.Execute(ctx, nil, "TestServingKeyspaces", econtext.NewSafeSession(&vtgatepb.Session{}), "select keyspace_name from dual", nil) require.NoError(t, err) require.Equal(t, `[[VARCHAR("TestUnsharded")]]`, fmt.Sprintf("%v", result.Rows)) } @@ -2150,9 +2148,9 @@ func TestExecutorOther(t *testing.T) { sbc2.ExecCount.Store(0) sbclookup.ExecCount.Store(0) - _, err := executor.Execute(ctx, nil, "TestExecute", NewSafeSession(&vtgatepb.Session{TargetString: tc.targetStr}), stmt, nil) + _, err := executor.Execute(ctx, nil, "TestExecute", econtext.NewSafeSession(&vtgatepb.Session{TargetString: tc.targetStr}), stmt, nil) if tc.hasNoKeyspaceErr { - assert.Error(t, err, errNoKeyspace) + assert.Error(t, err, econtext.ErrNoKeyspace.Error()) } else if tc.hasDestinationShardErr { assert.Errorf(t, err, "Destination can only be a single shard for statement: %s", stmt) } else { @@ -2206,7 +2204,7 @@ func TestExecutorAnalyze(t *testing.T) { 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(context.Background(), nil, "TestExecute", econtext.NewSafeSession(&vtgatepb.Session{TargetString: tc.targetStr}), stmt, nil) require.NoError(t, err) utils.MustMatch(t, tc.wantCnts, cnts{ @@ -2270,7 +2268,7 @@ func TestExecutorExplainStmt(t *testing.T) { sbc2.ExecCount.Store(0) sbclookup.ExecCount.Store(0) - _, err := executor.Execute(ctx, nil, "TestExecute", NewSafeSession(&vtgatepb.Session{TargetString: tc.targetStr}), stmt, nil) + _, err := executor.Execute(ctx, nil, "TestExecute", econtext.NewSafeSession(&vtgatepb.Session{TargetString: tc.targetStr}), stmt, nil) assert.NoError(t, err) utils.MustMatch(t, tc.wantCnts, cnts{ @@ -2360,9 +2358,9 @@ func TestExecutorOtherAdmin(t *testing.T) { 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(context.Background(), nil, "TestExecute", econtext.NewSafeSession(&vtgatepb.Session{TargetString: tc.targetStr}), stmt, nil) if tc.hasNoKeyspaceErr { - assert.Error(t, err, errNoKeyspace) + assert.Error(t, err, econtext.ErrNoKeyspace.Error()) } else if tc.hasDestinationShardErr { assert.Errorf(t, err, "Destination can only be a single shard for statement: %s, got: DestinationExactKeyRange(-)", stmt) } else { @@ -2387,7 +2385,7 @@ func TestExecutorSavepointInTx(t *testing.T) { logChan := executor.queryLogger.Subscribe("TestExecutorSavepoint") defer executor.queryLogger.Unsubscribe(logChan) - session := NewSafeSession(&vtgatepb.Session{Autocommit: false, TargetString: "@primary"}) + session := econtext.NewSafeSession(&vtgatepb.Session{Autocommit: false, TargetString: "@primary"}) _, err := exec(executor, session, "savepoint a") require.NoError(t, err) _, err = exec(executor, session, "rollback to a") @@ -2470,7 +2468,7 @@ func TestExecutorSavepointInTxWithReservedConn(t *testing.T) { logChan := executor.queryLogger.Subscribe("TestExecutorSavepoint") defer executor.queryLogger.Unsubscribe(logChan) - session := NewSafeSession(&vtgatepb.Session{Autocommit: true, TargetString: "TestExecutor", EnableSystemSettings: true}) + session := econtext.NewSafeSession(&vtgatepb.Session{Autocommit: true, TargetString: "TestExecutor", EnableSystemSettings: true}) sbc1.SetResults([]*sqltypes.Result{ sqltypes.MakeTestResult(sqltypes.MakeTestFields("orig|new", "varchar|varchar"), "a|"), }) @@ -2537,7 +2535,7 @@ func TestExecutorSavepointWithoutTx(t *testing.T) { logChan := executor.queryLogger.Subscribe("TestExecutorSavepoint") defer executor.queryLogger.Unsubscribe(logChan) - session := NewSafeSession(&vtgatepb.Session{Autocommit: true, TargetString: "@primary", InTransaction: false}) + session := econtext.NewSafeSession(&vtgatepb.Session{Autocommit: true, TargetString: "@primary", InTransaction: false}) _, err := exec(executor, session, "savepoint a") require.NoError(t, err) _, err = exec(executor, session, "rollback to a") @@ -2622,9 +2620,9 @@ func TestExecutorCallProc(t *testing.T) { sbc2.ExecCount.Store(0) sbcUnsharded.ExecCount.Store(0) - _, err := executor.Execute(context.Background(), nil, "TestExecute", NewSafeSession(&vtgatepb.Session{TargetString: tc.targetStr}), "CALL proc()", nil) + _, err := executor.Execute(context.Background(), nil, "TestExecute", econtext.NewSafeSession(&vtgatepb.Session{TargetString: tc.targetStr}), "CALL proc()", nil) if tc.hasNoKeyspaceErr { - assert.EqualError(t, err, errNoKeyspace.Error()) + assert.EqualError(t, err, econtext.ErrNoKeyspace.Error()) } else if tc.unshardedOnlyErr { require.EqualError(t, err, "CALL is not supported for sharded keyspace") } else { @@ -2644,9 +2642,9 @@ func TestExecutorTempTable(t *testing.T) { executor, _, _, sbcUnsharded, ctx := createExecutorEnv(t) initialWarningsCount := warnings.Counts()["WarnUnshardedOnly"] - executor.warnShardedOnly = true + executor.vConfig.WarnShardedOnly = true creatQuery := "create temporary table temp_t(id bigint primary key)" - session := NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded}) + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded}) _, err := executor.Execute(ctx, nil, "TestExecutorTempTable", session, creatQuery, nil) require.NoError(t, err) assert.EqualValues(t, 1, sbcUnsharded.ExecCount.Load()) @@ -2665,7 +2663,7 @@ func TestExecutorShowVitessMigrations(t *testing.T) { executor, sbc1, sbc2, _, ctx := createExecutorEnv(t) showQuery := "show vitess_migrations" - session := NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}) + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}) _, err := executor.Execute(ctx, nil, "", session, showQuery, nil) require.NoError(t, err) assert.Contains(t, sbc1.StringQueries(), "show vitess_migrations") @@ -2676,7 +2674,7 @@ func TestExecutorDescHash(t *testing.T) { executor, _, _, _, ctx := createExecutorEnv(t) showQuery := "desc hash_index" - session := NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}) + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}) _, err := executor.Execute(ctx, nil, "", session, showQuery, nil) require.NoError(t, err) } @@ -2684,7 +2682,7 @@ func TestExecutorDescHash(t *testing.T) { func TestExecutorVExplainQueries(t *testing.T) { executor, _, _, sbclookup, ctx := createExecutorEnv(t) - session := NewAutocommitSession(&vtgatepb.Session{}) + session := econtext.NewAutocommitSession(&vtgatepb.Session{}) sbclookup.SetResults([]*sqltypes.Result{ sqltypes.MakeTestResult(sqltypes.MakeTestFields("name|user_id", "varchar|int64"), "apa|1", "apa|2"), @@ -2697,7 +2695,7 @@ func TestExecutorVExplainQueries(t *testing.T) { // Test the streaming side as well var results []sqltypes.Row - session = NewAutocommitSession(&vtgatepb.Session{}) + session = econtext.NewAutocommitSession(&vtgatepb.Session{}) err = executor.StreamExecute(ctx, nil, "TestExecutorVExplainQueries", session, "vexplain queries select * from user where name = 'apa'", nil, func(result *sqltypes.Result) error { results = append(results, result.Rows...) return nil @@ -2710,7 +2708,7 @@ func TestExecutorVExplainQueries(t *testing.T) { func TestExecutorStartTxnStmt(t *testing.T) { executor, _, _, _, ctx := createExecutorEnv(t) - session := NewAutocommitSession(&vtgatepb.Session{}) + session := econtext.NewAutocommitSession(&vtgatepb.Session{}) tcases := []struct { beginSQL string @@ -2757,7 +2755,7 @@ func TestExecutorPrepareExecute(t *testing.T) { executor, _, _, _, _ := createExecutorEnv(t) executor.normalize = true - session := NewAutocommitSession(&vtgatepb.Session{}) + session := econtext.NewAutocommitSession(&vtgatepb.Session{}) // prepare statement. _, err := executor.Execute(context.Background(), nil, "TestExecutorPrepareExecute", session, "prepare prep_user from 'select * from user where id = ?'", nil) @@ -2834,7 +2832,7 @@ func TestExecutorSettingsInTwoPC(t *testing.T) { sbc2.SetResults(tcase.testRes) // create a new session - session := NewSafeSession(&vtgatepb.Session{ + session := econtext.NewSafeSession(&vtgatepb.Session{ TargetString: KsTestSharded, TransactionMode: vtgatepb.TransactionMode_TWOPC, EnableSystemSettings: true, @@ -2892,7 +2890,7 @@ func TestExecutorRejectTwoPC(t *testing.T) { sbc2.SetResults(tcase.testRes) // create a new session - session := NewSafeSession(&vtgatepb.Session{ + session := econtext.NewSafeSession(&vtgatepb.Session{ TargetString: KsTestSharded, TransactionMode: vtgatepb.TransactionMode_TWOPC, EnableSystemSettings: true, @@ -2922,7 +2920,7 @@ func TestExecutorTruncateErrors(t *testing.T) { truncateErrorLen = 32 defer func() { truncateErrorLen = save }() - session := NewSafeSession(&vtgatepb.Session{}) + session := econtext.NewSafeSession(&vtgatepb.Session{}) fn := func(r *sqltypes.Result) error { return nil } @@ -2982,7 +2980,7 @@ func TestExecutorFlushStmt(t *testing.T) { for _, tc := range tcs { t.Run(tc.query+tc.targetStr, func(t *testing.T) { - _, err := executor.Execute(context.Background(), nil, "TestExecutorFlushStmt", NewSafeSession(&vtgatepb.Session{TargetString: tc.targetStr}), tc.query, nil) + _, err := executor.Execute(context.Background(), nil, "TestExecutorFlushStmt", econtext.NewSafeSession(&vtgatepb.Session{TargetString: tc.targetStr}), tc.query, nil) if tc.expectedErr == "" { require.NoError(t, err) } else { @@ -3029,7 +3027,7 @@ func TestExecutorKillStmt(t *testing.T) { allowKillStmt = !tc.disallow t.Run("execute:"+tc.query+tc.errStr, func(t *testing.T) { mysqlCtx := &fakeMysqlConnection{ErrMsg: tc.errStr} - _, err := executor.Execute(context.Background(), mysqlCtx, "TestExecutorKillStmt", NewAutocommitSession(&vtgatepb.Session{}), tc.query, nil) + _, err := executor.Execute(context.Background(), mysqlCtx, "TestExecutorKillStmt", econtext.NewAutocommitSession(&vtgatepb.Session{}), tc.query, nil) if tc.errStr != "" { require.ErrorContains(t, err, tc.errStr) } else { @@ -3039,7 +3037,7 @@ func TestExecutorKillStmt(t *testing.T) { }) t.Run("stream:"+tc.query+tc.errStr, func(t *testing.T) { mysqlCtx := &fakeMysqlConnection{ErrMsg: tc.errStr} - err := executor.StreamExecute(context.Background(), mysqlCtx, "TestExecutorKillStmt", NewAutocommitSession(&vtgatepb.Session{}), tc.query, nil, func(result *sqltypes.Result) error { + err := executor.StreamExecute(context.Background(), mysqlCtx, "TestExecutorKillStmt", econtext.NewAutocommitSession(&vtgatepb.Session{}), tc.query, nil, func(result *sqltypes.Result) error { return nil }) if tc.errStr != "" { @@ -3075,7 +3073,7 @@ func (f *fakeMysqlConnection) KillConnection(ctx context.Context, connID uint32) var _ vtgateservice.MySQLConnection = (*fakeMysqlConnection)(nil) -func exec(executor *Executor, session *SafeSession, sql string) (*sqltypes.Result, error) { +func exec(executor *Executor, session *econtext.SafeSession, sql string) (*sqltypes.Result, error) { return executor.Execute(context.Background(), nil, "TestExecute", session, sql, nil) } diff --git a/go/vt/vtgate/executor_vexplain_test.go b/go/vt/vtgate/executor_vexplain_test.go index 99eb77c7ed4..a9516492f1b 100644 --- a/go/vt/vtgate/executor_vexplain_test.go +++ b/go/vt/vtgate/executor_vexplain_test.go @@ -26,6 +26,8 @@ import ( "github.com/stretchr/testify/assert" + econtext "vitess.io/vitess/go/vt/vtgate/executorcontext" + "github.com/stretchr/testify/require" "vitess.io/vitess/go/sqltypes" @@ -135,7 +137,7 @@ func TestVExplainKeys(t *testing.T) { for _, tt := range tests { t.Run(tt.Query, func(t *testing.T) { executor, _, _, _, _ := createExecutorEnv(t) - session := NewSafeSession(&vtgatepb.Session{TargetString: "@primary"}) + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: "@primary"}) gotResult, err := executor.Execute(context.Background(), nil, "Execute", session, "vexplain keys "+tt.Query, nil) require.NoError(t, err) diff --git a/go/vt/vtgate/executor_vschema_ddl_test.go b/go/vt/vtgate/executor_vschema_ddl_test.go index 1c912ed0d62..1acc1ba2362 100644 --- a/go/vt/vtgate/executor_vschema_ddl_test.go +++ b/go/vt/vtgate/executor_vschema_ddl_test.go @@ -25,6 +25,8 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + econtext "vitess.io/vitess/go/vt/vtgate/executorcontext" + "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/test/utils" "vitess.io/vitess/go/vt/callerid" @@ -133,12 +135,12 @@ func waitForColVindexes(t *testing.T, ks, table string, names []string, executor } func TestPlanExecutorAlterVSchemaKeyspace(t *testing.T) { - vschemaacl.AuthorizedDDLUsers = "%" + vschemaacl.AuthorizedDDLUsers.Set(vschemaacl.NewAuthorizedDDLUsers("%")) defer func() { - vschemaacl.AuthorizedDDLUsers = "" + vschemaacl.AuthorizedDDLUsers.Set(vschemaacl.NewAuthorizedDDLUsers("")) }() executor, _, _, _, ctx := createExecutorEnv(t) - session := NewSafeSession(&vtgatepb.Session{TargetString: "@primary", Autocommit: true}) + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: "@primary", Autocommit: true}) vschemaUpdates := make(chan *vschemapb.SrvVSchema, 2) executor.serv.WatchSrvVSchema(ctx, "aa", func(vschema *vschemapb.SrvVSchema, err error) bool { @@ -161,9 +163,9 @@ func TestPlanExecutorAlterVSchemaKeyspace(t *testing.T) { } func TestPlanExecutorCreateVindexDDL(t *testing.T) { - vschemaacl.AuthorizedDDLUsers = "%" + vschemaacl.AuthorizedDDLUsers.Set(vschemaacl.NewAuthorizedDDLUsers("%")) defer func() { - vschemaacl.AuthorizedDDLUsers = "" + vschemaacl.AuthorizedDDLUsers.Set(vschemaacl.NewAuthorizedDDLUsers("")) }() executor, _, _, _, ctx := createExecutorEnv(t) ks := "TestExecutor" @@ -180,7 +182,7 @@ func TestPlanExecutorCreateVindexDDL(t *testing.T) { t.Fatalf("test_vindex should not exist in original vschema") } - session := NewSafeSession(&vtgatepb.Session{TargetString: ks}) + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: ks}) stmt := "alter vschema create vindex test_vindex using hash" _, err := executor.Execute(ctx, nil, "TestExecute", session, stmt, nil) require.NoError(t, err) @@ -203,9 +205,9 @@ func TestPlanExecutorCreateVindexDDL(t *testing.T) { } func TestPlanExecutorDropVindexDDL(t *testing.T) { - vschemaacl.AuthorizedDDLUsers = "%" + vschemaacl.AuthorizedDDLUsers.Set(vschemaacl.NewAuthorizedDDLUsers("%")) defer func() { - vschemaacl.AuthorizedDDLUsers = "" + vschemaacl.AuthorizedDDLUsers.Set(vschemaacl.NewAuthorizedDDLUsers("")) }() executor, _, _, _, ctx := createExecutorEnv(t) ks := "TestExecutor" @@ -222,7 +224,7 @@ func TestPlanExecutorDropVindexDDL(t *testing.T) { t.Fatalf("test_vindex should not exist in original vschema") } - session := NewSafeSession(&vtgatepb.Session{TargetString: ks}) + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: ks}) stmt := "alter vschema drop vindex test_vindex" _, err := executor.Execute(ctx, nil, "TestExecute", session, stmt, nil) wantErr := "vindex test_vindex does not exists in keyspace TestExecutor" @@ -272,9 +274,9 @@ func TestPlanExecutorDropVindexDDL(t *testing.T) { } func TestPlanExecutorAddDropVschemaTableDDL(t *testing.T) { - vschemaacl.AuthorizedDDLUsers = "%" + vschemaacl.AuthorizedDDLUsers.Set(vschemaacl.NewAuthorizedDDLUsers("%")) defer func() { - vschemaacl.AuthorizedDDLUsers = "" + vschemaacl.AuthorizedDDLUsers.Set(vschemaacl.NewAuthorizedDDLUsers("")) }() executor, sbc1, sbc2, sbclookup, ctx := createExecutorEnv(t) ks := KsTestUnsharded @@ -296,7 +298,7 @@ func TestPlanExecutorAddDropVschemaTableDDL(t *testing.T) { vschemaTables = append(vschemaTables, t) } - session := NewSafeSession(&vtgatepb.Session{TargetString: ks}) + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: ks}) stmt := "alter vschema add table test_table" _, err := executor.Execute(ctx, nil, "TestExecute", session, stmt, nil) require.NoError(t, err) @@ -308,7 +310,7 @@ func TestPlanExecutorAddDropVschemaTableDDL(t *testing.T) { _ = waitForVschemaTables(t, ks, append([]string{"test_table", "test_table2"}, vschemaTables...), executor) // Should fail adding a table on a sharded keyspace - session = NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}) + session = econtext.NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}) stmt = "alter vschema add table test_table" _, err = executor.Execute(ctx, nil, "TestExecute", session, stmt, nil) wantErr := "add vschema table: unsupported on sharded keyspace TestExecutor" @@ -329,9 +331,9 @@ func TestPlanExecutorAddDropVschemaTableDDL(t *testing.T) { } func TestExecutorAddSequenceDDL(t *testing.T) { - vschemaacl.AuthorizedDDLUsers = "%" + vschemaacl.AuthorizedDDLUsers.Set(vschemaacl.NewAuthorizedDDLUsers("%")) defer func() { - vschemaacl.AuthorizedDDLUsers = "" + vschemaacl.AuthorizedDDLUsers.Set(vschemaacl.NewAuthorizedDDLUsers("")) }() executor, _, _, _, ctx := createExecutorEnv(t) ks := KsTestUnsharded @@ -343,7 +345,7 @@ func TestExecutorAddSequenceDDL(t *testing.T) { vschemaTables = append(vschemaTables, t) } - session := NewSafeSession(&vtgatepb.Session{TargetString: ks}) + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: ks}) stmt := "alter vschema add sequence test_seq" _, err := executor.Execute(ctx, nil, "TestExecute", session, stmt, nil) require.NoError(t, err) @@ -357,7 +359,7 @@ func TestExecutorAddSequenceDDL(t *testing.T) { // Should fail adding a table on a sharded keyspace ksSharded := "TestExecutor" - session = NewSafeSession(&vtgatepb.Session{TargetString: ksSharded}) + session = econtext.NewSafeSession(&vtgatepb.Session{TargetString: ksSharded}) stmt = "alter vschema add sequence sequence_table" _, err = executor.Execute(ctx, nil, "TestExecute", session, stmt, nil) @@ -389,9 +391,9 @@ func TestExecutorAddSequenceDDL(t *testing.T) { } func TestExecutorDropSequenceDDL(t *testing.T) { - vschemaacl.AuthorizedDDLUsers = "%" + vschemaacl.AuthorizedDDLUsers.Set(vschemaacl.NewAuthorizedDDLUsers("%")) defer func() { - vschemaacl.AuthorizedDDLUsers = "" + vschemaacl.AuthorizedDDLUsers.Set(vschemaacl.NewAuthorizedDDLUsers("")) }() executor, _, _, _, ctx := createExecutorEnv(t) ks := KsTestUnsharded @@ -403,7 +405,7 @@ func TestExecutorDropSequenceDDL(t *testing.T) { t.Fatalf("test_seq should not exist in original vschema") } - session := NewSafeSession(&vtgatepb.Session{TargetString: ks}) + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: ks}) // add test sequence stmt := "alter vschema add sequence test_seq" @@ -428,7 +430,7 @@ func TestExecutorDropSequenceDDL(t *testing.T) { } // Should fail dropping a non-existing test sequence - session = NewSafeSession(&vtgatepb.Session{TargetString: ks}) + session = econtext.NewSafeSession(&vtgatepb.Session{TargetString: ks}) stmt = "alter vschema drop sequence test_seq" _, err = executor.Execute(ctx, nil, "TestExecute", session, stmt, nil) @@ -440,14 +442,14 @@ func TestExecutorDropSequenceDDL(t *testing.T) { } func TestExecutorDropAutoIncDDL(t *testing.T) { - vschemaacl.AuthorizedDDLUsers = "%" + vschemaacl.AuthorizedDDLUsers.Set(vschemaacl.NewAuthorizedDDLUsers("%")) defer func() { - vschemaacl.AuthorizedDDLUsers = "" + vschemaacl.AuthorizedDDLUsers.Set(vschemaacl.NewAuthorizedDDLUsers("")) }() executor, _, _, _, ctx := createExecutorEnv(t) ks := KsTestUnsharded - session := NewSafeSession(&vtgatepb.Session{TargetString: ks}) + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: ks}) stmt := "alter vschema add table test_table" _, err := executor.Execute(ctx, nil, "TestExecute", session, stmt, nil) @@ -482,13 +484,13 @@ func TestExecutorDropAutoIncDDL(t *testing.T) { } func TestExecutorAddDropVindexDDL(t *testing.T) { - vschemaacl.AuthorizedDDLUsers = "%" + vschemaacl.AuthorizedDDLUsers.Set(vschemaacl.NewAuthorizedDDLUsers("%")) defer func() { - vschemaacl.AuthorizedDDLUsers = "" + vschemaacl.AuthorizedDDLUsers.Set(vschemaacl.NewAuthorizedDDLUsers("")) }() executor, sbc1, sbc2, sbclookup, ctx := createExecutorEnv(t) ks := "TestExecutor" - session := NewSafeSession(&vtgatepb.Session{TargetString: ks}) + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: ks}) vschemaUpdates := make(chan *vschemapb.SrvVSchema, 4) executor.serv.WatchSrvVSchema(ctx, "aa", func(vschema *vschemapb.SrvVSchema, err error) bool { vschemaUpdates <- vschema @@ -706,7 +708,7 @@ func TestExecutorAddDropVindexDDL(t *testing.T) { require.EqualError(t, err, "table TestExecutor.nonexistent not defined in vschema") stmt = "alter vschema on nonexistent drop vindex test_lookup" - _, err = executor.Execute(ctx, nil, "TestExecute", NewSafeSession(&vtgatepb.Session{TargetString: "InvalidKeyspace"}), stmt, nil) + _, err = executor.Execute(ctx, nil, "TestExecute", econtext.NewSafeSession(&vtgatepb.Session{TargetString: "InvalidKeyspace"}), stmt, nil) require.EqualError(t, err, "VT05003: unknown database 'InvalidKeyspace' in vschema") stmt = "alter vschema on nowhere.nohow drop vindex test_lookup" @@ -731,7 +733,7 @@ func TestPlanExecutorVindexDDLACL(t *testing.T) { // t.Skip("not yet planned") executor, _, _, _, ctx := createExecutorEnv(t) ks := "TestExecutor" - session := NewSafeSession(&vtgatepb.Session{TargetString: ks}) + session := econtext.NewSafeSession(&vtgatepb.Session{TargetString: ks}) ctxRedUser := callerid.NewContext(ctx, &vtrpcpb.CallerID{}, &querypb.VTGateCallerID{Username: "redUser"}) ctxBlueUser := callerid.NewContext(ctx, &vtrpcpb.CallerID{}, &querypb.VTGateCallerID{Username: "blueUser"}) @@ -745,8 +747,7 @@ func TestPlanExecutorVindexDDLACL(t *testing.T) { require.EqualError(t, err, `User 'blueUser' is not authorized to perform vschema operations`) // test when all users are enabled - vschemaacl.AuthorizedDDLUsers = "%" - vschemaacl.Init() + vschemaacl.AuthorizedDDLUsers.Set(vschemaacl.NewAuthorizedDDLUsers("%")) _, err = executor.Execute(ctxRedUser, nil, "TestExecute", session, stmt, nil) if err != nil { t.Errorf("unexpected error '%v'", err) @@ -758,8 +759,7 @@ func TestPlanExecutorVindexDDLACL(t *testing.T) { } // test when only one user is enabled - vschemaacl.AuthorizedDDLUsers = "orangeUser, blueUser, greenUser" - vschemaacl.Init() + vschemaacl.AuthorizedDDLUsers.Set(vschemaacl.NewAuthorizedDDLUsers("orangeUser, blueUser, greenUser")) _, err = executor.Execute(ctxRedUser, nil, "TestExecute", session, stmt, nil) require.EqualError(t, err, `User 'redUser' is not authorized to perform vschema operations`) @@ -770,5 +770,5 @@ func TestPlanExecutorVindexDDLACL(t *testing.T) { } // restore the disallowed state - vschemaacl.AuthorizedDDLUsers = "" + vschemaacl.AuthorizedDDLUsers.Set(vschemaacl.NewAuthorizedDDLUsers("")) } diff --git a/go/vt/vtgate/executor_vstream_test.go b/go/vt/vtgate/executor_vstream_test.go index 5466e9e8f3f..22fb7ee1034 100644 --- a/go/vt/vtgate/executor_vstream_test.go +++ b/go/vt/vtgate/executor_vstream_test.go @@ -21,6 +21,7 @@ import ( "time" "vitess.io/vitess/go/vt/vtgate/engine" + econtext "vitess.io/vitess/go/vt/vtgate/executorcontext" querypb "vitess.io/vitess/go/vt/proto/query" @@ -76,7 +77,7 @@ func TestVStreamSQLUnsharded(t *testing.T) { results := make(chan *sqltypes.Result, 20) go func() { - err := executor.StreamExecute(ctx, nil, "TestExecuteStream", NewAutocommitSession(&vtgatepb.Session{TargetString: KsTestUnsharded}), sql, nil, func(qr *sqltypes.Result) error { + err := executor.StreamExecute(ctx, nil, "TestExecuteStream", econtext.NewAutocommitSession(&vtgatepb.Session{TargetString: KsTestUnsharded}), sql, nil, func(qr *sqltypes.Result) error { results <- qr return nil }) diff --git a/go/vt/vtgate/executorcontext/faketopo.go b/go/vt/vtgate/executorcontext/faketopo.go new file mode 100644 index 00000000000..f61119dce15 --- /dev/null +++ b/go/vt/vtgate/executorcontext/faketopo.go @@ -0,0 +1,68 @@ +/* +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 executorcontext + +import ( + "context" + "encoding/hex" + + topodatapb "vitess.io/vitess/go/vt/proto/topodata" + vschemapb "vitess.io/vitess/go/vt/proto/vschema" + "vitess.io/vitess/go/vt/topo" +) + +type FakeTopoServer struct{} + +// GetTopoServer returns the full topo.Server instance. +func (f *FakeTopoServer) GetTopoServer() (*topo.Server, error) { + return nil, nil +} + +// GetSrvKeyspaceNames returns the list of keyspaces served in +// the provided cell. +func (f *FakeTopoServer) GetSrvKeyspaceNames(ctx context.Context, cell string, staleOK bool) ([]string, error) { + return []string{"ks1"}, nil +} + +// GetSrvKeyspace returns the SrvKeyspace for a cell/keyspace. +func (f *FakeTopoServer) GetSrvKeyspace(ctx context.Context, cell, keyspace string) (*topodatapb.SrvKeyspace, error) { + zeroHexBytes, _ := hex.DecodeString("") + eightyHexBytes, _ := hex.DecodeString("80") + ks := &topodatapb.SrvKeyspace{ + Partitions: []*topodatapb.SrvKeyspace_KeyspacePartition{ + { + ServedType: topodatapb.TabletType_PRIMARY, + ShardReferences: []*topodatapb.ShardReference{ + {Name: "-80", KeyRange: &topodatapb.KeyRange{Start: zeroHexBytes, End: eightyHexBytes}}, + {Name: "80-", KeyRange: &topodatapb.KeyRange{Start: eightyHexBytes, End: zeroHexBytes}}, + }, + }, + }, + } + return ks, nil +} + +func (f *FakeTopoServer) WatchSrvKeyspace(ctx context.Context, cell, keyspace string, callback func(*topodatapb.SrvKeyspace, error) bool) { + ks, err := f.GetSrvKeyspace(ctx, cell, keyspace) + callback(ks, err) +} + +// WatchSrvVSchema starts watching the SrvVSchema object for +// the provided cell. It will call the callback when +// a new value or an error occurs. +func (f *FakeTopoServer) WatchSrvVSchema(ctx context.Context, cell string, callback func(*vschemapb.SrvVSchema, error) bool) { +} diff --git a/go/vt/vtgate/safe_session.go b/go/vt/vtgate/executorcontext/safe_session.go similarity index 78% rename from go/vt/vtgate/safe_session.go rename to go/vt/vtgate/executorcontext/safe_session.go index 1d57c63ef35..c77bba76ff8 100644 --- a/go/vt/vtgate/safe_session.go +++ b/go/vt/vtgate/executorcontext/safe_session.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package vtgate +package executorcontext import ( "fmt" @@ -23,22 +23,19 @@ import ( "sync" "time" - "vitess.io/vitess/go/sqltypes" - "google.golang.org/protobuf/proto" "vitess.io/vitess/go/mysql/datetime" - + "vitess.io/vitess/go/sqltypes" + 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/sqlparser" "vitess.io/vitess/go/vt/srvtopo" "vitess.io/vitess/go/vt/sysvars" "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vtgate/engine" - - 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" ) type ( @@ -58,7 +55,7 @@ type ( rollbackOnPartialExec string savepointName string - // this is a signal that found_rows has already been handles by the primitives, + // this is a signal that found_rows has already been handled by the primitives, // and doesn't have to be updated by the executor foundRowsHandled bool @@ -66,12 +63,12 @@ type ( // as the query that started a new transaction on the shard belong to a vindex. queryFromVindex bool - logging *executeLogger + logging *ExecuteLogger *vtgatepb.Session } - executeLogger struct { + ExecuteLogger struct { mu sync.Mutex entries []engine.ExecuteEntry lastID int @@ -127,6 +124,8 @@ const ( savepointRollback ) +const TxRollback = "Rollback Transaction" + // NewSafeSession returns a new SafeSession based on the Session func NewSafeSession(sessn *vtgatepb.Session) *SafeSession { if sessn == nil { @@ -205,6 +204,50 @@ func (session *SafeSession) resetCommonLocked() { } } +// NewAutocommitSession returns a SafeSession based on the original +// session, but with autocommit enabled. +func (session *SafeSession) NewAutocommitSession() *SafeSession { + ss := NewAutocommitSession(session.Session) + ss.logging = session.logging + return ss +} + +// IsFoundRowsHandled returns the foundRowsHandled. +func (session *SafeSession) IsFoundRowsHandled() bool { + session.mu.Lock() + defer session.mu.Unlock() + return session.foundRowsHandled +} + +// SetFoundRows set the found rows value. +func (session *SafeSession) SetFoundRows(value uint64) { + session.mu.Lock() + defer session.mu.Unlock() + session.FoundRows = value + session.foundRowsHandled = true +} + +// GetRollbackOnPartialExec returns the rollbackOnPartialExec value. +func (session *SafeSession) GetRollbackOnPartialExec() string { + session.mu.Lock() + defer session.mu.Unlock() + return session.rollbackOnPartialExec +} + +// SetQueryFromVindex set the queryFromVindex value. +func (session *SafeSession) SetQueryFromVindex(value bool) { + session.mu.Lock() + defer session.mu.Unlock() + session.queryFromVindex = value +} + +// GetQueryFromVindex returns the queryFromVindex value. +func (session *SafeSession) GetQueryFromVindex() bool { + session.mu.Lock() + defer session.mu.Unlock() + return session.queryFromVindex +} + // SetQueryTimeout sets the query timeout func (session *SafeSession) SetQueryTimeout(queryTimeout int64) { session.mu.Lock() @@ -312,7 +355,7 @@ func (session *SafeSession) SetRollbackCommand() { if session.savepointState == savepointSet { session.rollbackOnPartialExec = fmt.Sprintf("rollback to %s", session.savepointName) } else { - session.rollbackOnPartialExec = txRollback + session.rollbackOnPartialExec = TxRollback } session.savepointState = savepointRollbackSet } @@ -340,6 +383,18 @@ func (session *SafeSession) SetCommitOrder(co vtgatepb.CommitOrder) { session.commitOrder = co } +// GetCommitOrder returns the commit order. +func (session *SafeSession) GetCommitOrder() vtgatepb.CommitOrder { + session.mu.Lock() + defer session.mu.Unlock() + return session.commitOrder +} + +// GetLogger returns executor logger. +func (session *SafeSession) GetLogger() *ExecuteLogger { + return session.logging +} + // InTransaction returns true if we are in a transaction func (session *SafeSession) InTransaction() bool { session.mu.Lock() @@ -347,73 +402,88 @@ func (session *SafeSession) InTransaction() bool { return session.Session.InTransaction } -// FindAndChangeSessionIfInSingleTxMode returns the transactionId and tabletAlias, if any, for a session -// modifies the shard session in a specific case for single mode transaction. -func (session *SafeSession) FindAndChangeSessionIfInSingleTxMode(keyspace, shard string, tabletType topodatapb.TabletType, txMode vtgatepb.TransactionMode) (int64, int64, *topodatapb.TabletAlias, error) { +// FindAndChangeSessionIfInSingleTxMode retrieves the ShardSession matching the given keyspace, shard, and tablet type. +// It performs additional checks and may modify the ShardSession in specific cases for single-mode transactions. +// +// Key behavior: +// 1. Retrieves the appropriate list of sessions (PreSessions, PostSessions, or default ShardSessions) based on the commit order. +// 2. Identifies a matching session by keyspace, shard, and tablet type. +// 3. If the session meets specific conditions (e.g., non-vindex-only, single transaction mode), it updates the session state: +// - Converts a vindex-only session to a standard session if required by the transaction type. +// - If a multi-shard transaction is detected in Single mode, marks the session for rollback and returns an error. +// +// Parameters: +// - keyspace: The keyspace of the target shard. +// - shard: The shard name of the target. +// - tabletType: The type of the tablet for the shard session. +// - txMode: The transaction mode (e.g., Single, Multi). +// +// Returns: +// - The matching ShardSession, if found and valid for the operation. +// - An error if a Single-mode transaction attempts to span multiple shards. +func (session *SafeSession) FindAndChangeSessionIfInSingleTxMode(keyspace, shard string, tabletType topodatapb.TabletType, txMode vtgatepb.TransactionMode) (*vtgatepb.Session_ShardSession, error) { session.mu.Lock() defer session.mu.Unlock() - sessions := session.ShardSessions + + shardSession := session.findSessionLocked(keyspace, shard, tabletType) + + if shardSession == nil { + return nil, nil + } + + if !shardSession.VindexOnly { + return shardSession, nil + } + + if err := session.singleModeErrorOnCrossShard(txMode, 0); err != nil { + return nil, err + } + + // the shard session is now used by non-vindex query as well, + // so it is not an exclusive vindex only shard session anymore. + shardSession.VindexOnly = false + return shardSession, nil +} + +func (session *SafeSession) findSessionLocked(keyspace, shard string, tabletType topodatapb.TabletType) *vtgatepb.Session_ShardSession { + // Select the appropriate session list based on the commit order. + var sessions []*vtgatepb.Session_ShardSession switch session.commitOrder { case vtgatepb.CommitOrder_PRE: sessions = session.PreSessions case vtgatepb.CommitOrder_POST: sessions = session.PostSessions + default: + sessions = session.ShardSessions } + + // Find and return the matching shard session. for _, shardSession := range sessions { - if keyspace == shardSession.Target.Keyspace && tabletType == shardSession.Target.TabletType && shard == shardSession.Target.Shard { - if txMode != vtgatepb.TransactionMode_SINGLE || !shardSession.VindexOnly || session.queryFromVindex { - return shardSession.TransactionId, shardSession.ReservedId, shardSession.TabletAlias, nil - } - count := actualNoOfShardSession(session.ShardSessions) - // If the count of shard session which are non vindex only is greater than 0, then it is a - if count > 0 { - session.mustRollback = true - return 0, 0, nil, vterrors.Errorf(vtrpcpb.Code_ABORTED, "multi-db transaction attempted: %v", session.ShardSessions) - } - // the shard session is now used by non-vindex query as well, - // so it is not an exclusive vindex only shard session anymore. - shardSession.VindexOnly = false - return shardSession.TransactionId, shardSession.ReservedId, shardSession.TabletAlias, nil + if shardSession.Target.Keyspace == keyspace && + shardSession.Target.Shard == shard && + shardSession.Target.TabletType == tabletType { + return shardSession } } - return 0, 0, nil, nil -} - -func addOrUpdate(shardSession *vtgatepb.Session_ShardSession, sessions []*vtgatepb.Session_ShardSession) ([]*vtgatepb.Session_ShardSession, error) { - appendSession := true - for i, sess := range sessions { - targetedAtSameTablet := sess.Target.Keyspace == shardSession.Target.Keyspace && - sess.Target.TabletType == shardSession.Target.TabletType && - sess.Target.Shard == shardSession.Target.Shard - if targetedAtSameTablet { - if !proto.Equal(sess.TabletAlias, shardSession.TabletAlias) { - errorDetails := fmt.Sprintf("got non-matching aliases (%v vs %v) for the same target (keyspace: %v, tabletType: %v, shard: %v)", - sess.TabletAlias, shardSession.TabletAlias, - sess.Target.Keyspace, sess.Target.TabletType, sess.Target.Shard) - return nil, vterrors.New(vtrpcpb.Code_FAILED_PRECONDITION, errorDetails) - } - // replace the old info with the new one - sessions[i] = shardSession - appendSession = false - break - } - } - if appendSession { - sessions = append(sessions, shardSession) - } + return nil +} - return sessions, nil +type ShardActionInfo interface { + TransactionID() int64 + ReservedID() int64 + RowsAffected() bool + Alias() *topodatapb.TabletAlias } // AppendOrUpdate adds a new ShardSession, or updates an existing one if one already exists for the given shard session -func (session *SafeSession) AppendOrUpdate(shardSession *vtgatepb.Session_ShardSession, txMode vtgatepb.TransactionMode) error { +func (session *SafeSession) AppendOrUpdate(target *querypb.Target, info ShardActionInfo, existingSession *vtgatepb.Session_ShardSession, txMode vtgatepb.TransactionMode) error { session.mu.Lock() defer session.mu.Unlock() // additional check of transaction id is required // as now in autocommit mode there can be session due to reserved connection // that needs to be stored as shard session. - if session.autocommitState == autocommitted && shardSession.TransactionId != 0 { + if session.autocommitState == autocommitted && info.TransactionID() != 0 { // Should be unreachable return vterrors.VT13001("unexpected 'autocommitted' state in transaction") } @@ -423,45 +493,62 @@ func (session *SafeSession) AppendOrUpdate(shardSession *vtgatepb.Session_ShardS } session.autocommitState = notAutocommittable - // Always append, in order for rollback to succeed. - switch session.commitOrder { - case vtgatepb.CommitOrder_NORMAL: - if session.queryFromVindex { - shardSession.VindexOnly = true + if existingSession != nil { + existingSession.TransactionId = info.TransactionID() + existingSession.ReservedId = info.ReservedID() + if !existingSession.RowsAffected { + existingSession.RowsAffected = info.RowsAffected() } - newSessions, err := addOrUpdate(shardSession, session.ShardSessions) - if err != nil { + if existingSession.VindexOnly { + existingSession.VindexOnly = session.queryFromVindex + } + if err := session.singleModeErrorOnCrossShard(txMode, 1); err != nil { return err } - session.ShardSessions = newSessions + return nil + } + newSession := &vtgatepb.Session_ShardSession{ + Target: target, + TabletAlias: info.Alias(), + TransactionId: info.TransactionID(), + ReservedId: info.ReservedID(), + RowsAffected: info.RowsAffected(), + VindexOnly: session.queryFromVindex, + } - if session.queryFromVindex { - break - } - // isSingle is enforced only for normal commit order operations. - if session.isSingleDB(txMode) && len(session.ShardSessions) > 1 { - count := actualNoOfShardSession(session.ShardSessions) - if count <= 1 { - break - } - session.mustRollback = true - return vterrors.Errorf(vtrpcpb.Code_ABORTED, "multi-db transaction attempted: %v", session.ShardSessions) - } - case vtgatepb.CommitOrder_PRE: - newSessions, err := addOrUpdate(shardSession, session.PreSessions) - if err != nil { + // Always append, in order for rollback to succeed. + switch session.commitOrder { + case vtgatepb.CommitOrder_NORMAL: + session.ShardSessions = append(session.ShardSessions, newSession) + if err := session.singleModeErrorOnCrossShard(txMode, 1); err != nil { return err } - session.PreSessions = newSessions + case vtgatepb.CommitOrder_PRE: + session.PreSessions = append(session.PreSessions, newSession) case vtgatepb.CommitOrder_POST: - newSessions, err := addOrUpdate(shardSession, session.PostSessions) - if err != nil { - return err - } - session.PostSessions = newSessions + session.PostSessions = append(session.PostSessions, newSession) default: // Should be unreachable - return vterrors.Errorf(vtrpcpb.Code_INTERNAL, "[BUG] SafeSession.AppendOrUpdate: unexpected commitOrder") + return vterrors.VT13001(fmt.Sprintf("unexpected commitOrder to append shard session: %v", session.commitOrder)) + } + + return nil +} + +// singleModeErrorOnCrossShard checks if a transaction violates the Single mode constraint by spanning multiple shards. +func (session *SafeSession) singleModeErrorOnCrossShard(txMode vtgatepb.TransactionMode, exceedsCrossShard int) error { + // Skip the check if: + // 1. The query comes from a lookup vindex. + // 2. The transaction mode is not Single. + // 3. The transaction is not in the normal shard session. + if session.queryFromVindex || session.commitOrder != vtgatepb.CommitOrder_NORMAL || !session.isSingleDB(txMode) { + return nil + } + + // If the transaction spans multiple shards, abort it. + if actualNoOfShardSession(session.ShardSessions) > exceedsCrossShard { + session.mustRollback = true // Mark the session for rollback. + return vterrors.Errorf(vtrpcpb.Code_ABORTED, "multi-db transaction attempted: %v", session.ShardSessions) } return nil @@ -678,12 +765,11 @@ func (session *SafeSession) UpdateLockHeartbeat() { session.LastLockHeartbeat = time.Now().Unix() } -// TriggerLockHeartBeat returns if it time to trigger next lock heartbeat -func (session *SafeSession) TriggerLockHeartBeat() bool { +// GetLockHeartbeat returns last time the lock heartbeat was sent. +func (session *SafeSession) GetLockHeartbeat() int64 { session.mu.Lock() defer session.mu.Unlock() - now := time.Now().Unix() - return now-session.LastLockHeartbeat >= int64(lockHeartbeatTime.Seconds()) + return session.LastLockHeartbeat } // InLockSession returns whether locking is used on this session. @@ -836,9 +922,7 @@ func (session *SafeSession) GetOrCreateOptions() *querypb.ExecuteOptions { return session.Session.Options } -var _ iQueryOption = (*SafeSession)(nil) - -func (session *SafeSession) cachePlan() bool { +func (session *SafeSession) CachePlan() bool { if session == nil || session.Options == nil { return true } @@ -849,7 +933,7 @@ func (session *SafeSession) cachePlan() bool { return !(session.Options.SkipQueryPlanCache || session.Options.HasCreatedTempTables) } -func (session *SafeSession) getSelectLimit() int { +func (session *SafeSession) GetSelectLimit() int { if session == nil || session.Options == nil { return -1 } @@ -860,16 +944,16 @@ func (session *SafeSession) getSelectLimit() int { return int(session.Options.SqlSelectLimit) } -// isTxOpen returns true if there is open connection to any of the shard. -func (session *SafeSession) isTxOpen() bool { +// IsTxOpen returns true if there is open connection to any of the shard. +func (session *SafeSession) IsTxOpen() bool { session.mu.Lock() defer session.mu.Unlock() return len(session.ShardSessions) > 0 || len(session.PreSessions) > 0 || len(session.PostSessions) > 0 } -// getSessions returns the shard session for the current commit order. -func (session *SafeSession) getSessions() []*vtgatepb.Session_ShardSession { +// GetSessions returns the shard session for the current commit order. +func (session *SafeSession) GetSessions() []*vtgatepb.Session_ShardSession { session.mu.Lock() defer session.mu.Unlock() @@ -956,7 +1040,7 @@ func (session *SafeSession) EnableLogging(parser *sqlparser.Parser) { session.mu.Lock() defer session.mu.Unlock() - session.logging = &executeLogger{ + session.logging = &ExecuteLogger{ parser: parser, } } @@ -994,7 +1078,15 @@ func (session *SafeSession) GetPrepareData(name string) *vtgatepb.PrepareData { return session.PrepareStatement[name] } -func (l *executeLogger) log(primitive engine.Primitive, target *querypb.Target, gateway srvtopo.Gateway, query string, begin bool, bv map[string]*querypb.BindVariable) { +func (session *SafeSession) Log(primitive engine.Primitive, target *querypb.Target, gateway srvtopo.Gateway, query string, begin bool, bv map[string]*querypb.BindVariable) { + session.logging.Log(primitive, target, gateway, query, begin, bv) +} + +func (session *SafeSession) GetLogs() []engine.ExecuteEntry { + return session.logging.GetLogs() +} + +func (l *ExecuteLogger) Log(primitive engine.Primitive, target *querypb.Target, gateway srvtopo.Gateway, query string, begin bool, bv map[string]*querypb.BindVariable) { if l == nil { return } @@ -1033,7 +1125,10 @@ func (l *executeLogger) log(primitive engine.Primitive, target *querypb.Target, }) } -func (l *executeLogger) GetLogs() []engine.ExecuteEntry { +func (l *ExecuteLogger) GetLogs() []engine.ExecuteEntry { + if l == nil { + return nil + } l.mu.Lock() defer l.mu.Unlock() result := make([]engine.ExecuteEntry, len(l.entries)) diff --git a/go/vt/vtgate/executorcontext/safe_session_test.go b/go/vt/vtgate/executorcontext/safe_session_test.go new file mode 100644 index 00000000000..14ea2ad9dac --- /dev/null +++ b/go/vt/vtgate/executorcontext/safe_session_test.go @@ -0,0 +1,201 @@ +/* +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 executorcontext + +import ( + "reflect" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + querypb "vitess.io/vitess/go/vt/proto/query" + topodatapb "vitess.io/vitess/go/vt/proto/topodata" + vtgatepb "vitess.io/vitess/go/vt/proto/vtgate" +) + +type fakeInfo struct { + transactionID int64 + alias *topodatapb.TabletAlias +} + +func (s *fakeInfo) TransactionID() int64 { + return s.transactionID +} + +func (s *fakeInfo) ReservedID() int64 { + return 0 +} + +func (s *fakeInfo) RowsAffected() bool { + return false +} + +func (s *fakeInfo) Alias() *topodatapb.TabletAlias { + return s.alias +} + +func info(txId, uid int) ShardActionInfo { + return &fakeInfo{transactionID: int64(txId), alias: &topodatapb.TabletAlias{Cell: "cell", Uid: uint32(uid)}} +} + +// TestFailToMultiShardWhenSetToSingleDb tests that single db transactions fails on going multi shard. +func TestFailToMultiShardWhenSetToSingleDb(t *testing.T) { + session := NewSafeSession(&vtgatepb.Session{ + InTransaction: true, TransactionMode: vtgatepb.TransactionMode_SINGLE, + }) + + err := session.AppendOrUpdate( + &querypb.Target{Keyspace: "keyspace", Shard: "0"}, + info(1, 0), + nil, + vtgatepb.TransactionMode_SINGLE) + require.NoError(t, err) + err = session.AppendOrUpdate( + &querypb.Target{Keyspace: "keyspace", Shard: "1"}, + info(1, 1), + nil, + vtgatepb.TransactionMode_SINGLE) + require.Error(t, err) +} + +// TestSingleDbUpdateToMultiShard tests that a single db transaction cannot be updated to multi shard. +func TestSingleDbUpdateToMultiShard(t *testing.T) { + session := NewSafeSession(&vtgatepb.Session{ + InTransaction: true, TransactionMode: vtgatepb.TransactionMode_SINGLE, + }) + + // shard session s0 due to a vindex query + session.queryFromVindex = true + err := session.AppendOrUpdate( + &querypb.Target{Keyspace: "keyspace", Shard: "0"}, + info(1, 0), + nil, + vtgatepb.TransactionMode_SINGLE) + require.NoError(t, err) + session.queryFromVindex = false + + // shard session s1 + err = session.AppendOrUpdate( + &querypb.Target{Keyspace: "keyspace", Shard: "1"}, + info(1, 1), + nil, + vtgatepb.TransactionMode_SINGLE) + require.NoError(t, err) + + // shard session s0 with normal query + err = session.AppendOrUpdate( + &querypb.Target{Keyspace: "keyspace", Shard: "0"}, + info(1, 1), + session.ShardSessions[0], + vtgatepb.TransactionMode_SINGLE) + require.Error(t, err) +} + +// TestSingleDbPreFailOnFind tests that finding a shard session fails +// if already shard session exists on another shard and the query is not from vindex. +func TestSingleDbPreFailOnFind(t *testing.T) { + session := NewSafeSession(&vtgatepb.Session{ + InTransaction: true, TransactionMode: vtgatepb.TransactionMode_SINGLE, + }) + + // shard session s0 due to a vindex query + session.queryFromVindex = true + err := session.AppendOrUpdate( + &querypb.Target{Keyspace: "keyspace", Shard: "0"}, + info(1, 0), + nil, + vtgatepb.TransactionMode_SINGLE) + require.NoError(t, err) + session.queryFromVindex = false + + // shard session s1 + err = session.AppendOrUpdate( + &querypb.Target{Keyspace: "keyspace", Shard: "1"}, + info(1, 1), + nil, + vtgatepb.TransactionMode_SINGLE) + require.NoError(t, err) + + // shard session s1 for normal query again - should not fail as already part of the session. + ss, err := session.FindAndChangeSessionIfInSingleTxMode( + "keyspace", + "1", + topodatapb.TabletType_UNKNOWN, + vtgatepb.TransactionMode_SINGLE) + require.NoError(t, err) + require.NotNil(t, ss) + require.False(t, ss.VindexOnly) + require.EqualValues(t, 1, ss.TabletAlias.Uid) + + // shard session s0 for normal query + _, err = session.FindAndChangeSessionIfInSingleTxMode( + "keyspace", + "0", + topodatapb.TabletType_UNKNOWN, + vtgatepb.TransactionMode_SINGLE) + require.Error(t, err) +} + +func TestPrequeries(t *testing.T) { + session := NewSafeSession(&vtgatepb.Session{ + SystemVariables: map[string]string{ + "s1": "'apa'", + "s2": "42", + }, + }) + + want := []string{"set s1 = 'apa', s2 = 42"} + preQueries := session.SetPreQueries() + + if !reflect.DeepEqual(want, preQueries) { + t.Errorf("got %v but wanted %v", preQueries, want) + } +} + +func TestTimeZone(t *testing.T) { + testCases := []struct { + tz string + want string + }{ + { + tz: "'Europe/Amsterdam'", + want: "Europe/Amsterdam", + }, + { + tz: "'+02:00'", + want: "UTC+02:00", + }, + { + tz: "foo", + want: (*time.Location)(nil).String(), + }, + } + + for _, tc := range testCases { + t.Run(tc.tz, func(t *testing.T) { + session := NewSafeSession(&vtgatepb.Session{ + SystemVariables: map[string]string{ + "time_zone": tc.tz, + }, + }) + + assert.Equal(t, tc.want, session.TimeZone().String()) + }) + } +} diff --git a/go/vt/vtgate/vcursor_impl.go b/go/vt/vtgate/executorcontext/vcursor_impl.go similarity index 65% rename from go/vt/vtgate/vcursor_impl.go rename to go/vt/vtgate/executorcontext/vcursor_impl.go index e9b1d3d7712..c1f341b38cf 100644 --- a/go/vt/vtgate/vcursor_impl.go +++ b/go/vt/vtgate/executorcontext/vcursor_impl.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package vtgate +package executorcontext import ( "context" @@ -26,10 +26,12 @@ import ( "time" "github.com/google/uuid" + "golang.org/x/exp/maps" "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/mysql/config" "vitess.io/vitess/go/mysql/sqlerror" + "vitess.io/vitess/go/protoutil" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/callerid" "vitess.io/vitess/go/vt/discovery" @@ -59,38 +61,62 @@ import ( ) var ( - _ engine.VCursor = (*vcursorImpl)(nil) - _ plancontext.VSchema = (*vcursorImpl)(nil) - _ iExecute = (*Executor)(nil) - _ vindexes.VCursor = (*vcursorImpl)(nil) + _ engine.VCursor = (*VCursorImpl)(nil) + _ plancontext.VSchema = (*VCursorImpl)(nil) + _ vindexes.VCursor = (*VCursorImpl)(nil) ) +var ErrNoKeyspace = vterrors.VT09005() + type ( + ResultsObserver interface { + Observe(*sqltypes.Result) + } + + VCursorConfig struct { + Collation collations.ID + + MaxMemoryRows int + EnableShardRouting bool + DefaultTabletType topodatapb.TabletType + QueryTimeout int + DBDDLPlugin string + ForeignKeyMode vschemapb.Keyspace_ForeignKeyMode + SetVarEnabled bool + EnableViews bool + WarnShardedOnly bool + PlannerVersion plancontext.PlannerVersion + + WarmingReadsPercent int + WarmingReadsTimeout time.Duration + WarmingReadsChannel chan bool + } + // vcursor_impl needs these facilities to be able to be able to execute queries for vindexes iExecute interface { Execute(ctx context.Context, mysqlCtx vtgateservice.MySQLConnection, method string, session *SafeSession, s string, vars map[string]*querypb.BindVariable) (*sqltypes.Result, error) - ExecuteMultiShard(ctx context.Context, primitive engine.Primitive, rss []*srvtopo.ResolvedShard, queries []*querypb.BoundQuery, session *SafeSession, autocommit bool, ignoreMaxMemoryRows bool, resultsObserver resultsObserver) (qr *sqltypes.Result, errs []error) - StreamExecuteMulti(ctx context.Context, primitive engine.Primitive, query string, rss []*srvtopo.ResolvedShard, vars []map[string]*querypb.BindVariable, session *SafeSession, autocommit bool, callback func(reply *sqltypes.Result) error, observer resultsObserver) []error + ExecuteMultiShard(ctx context.Context, primitive engine.Primitive, rss []*srvtopo.ResolvedShard, queries []*querypb.BoundQuery, session *SafeSession, autocommit bool, ignoreMaxMemoryRows bool, resultsObserver ResultsObserver) (qr *sqltypes.Result, errs []error) + StreamExecuteMulti(ctx context.Context, primitive engine.Primitive, query string, rss []*srvtopo.ResolvedShard, vars []map[string]*querypb.BindVariable, session *SafeSession, autocommit bool, callback func(reply *sqltypes.Result) error, observer ResultsObserver) []error ExecuteLock(ctx context.Context, rs *srvtopo.ResolvedShard, query *querypb.BoundQuery, session *SafeSession, lockFuncType sqlparser.LockingFuncType) (*sqltypes.Result, error) Commit(ctx context.Context, safeSession *SafeSession) error ExecuteMessageStream(ctx context.Context, rss []*srvtopo.ResolvedShard, name string, callback func(*sqltypes.Result) error) error ExecuteVStream(ctx context.Context, rss []*srvtopo.ResolvedShard, filter *binlogdatapb.Filter, gtid string, callback func(evs []*binlogdatapb.VEvent) error) error ReleaseLock(ctx context.Context, session *SafeSession) error - showVitessReplicationStatus(ctx context.Context, filter *sqlparser.ShowFilter) (*sqltypes.Result, error) - showShards(ctx context.Context, filter *sqlparser.ShowFilter, destTabletType topodatapb.TabletType) (*sqltypes.Result, error) - showTablets(filter *sqlparser.ShowFilter) (*sqltypes.Result, error) - showVitessMetadata(ctx context.Context, filter *sqlparser.ShowFilter) (*sqltypes.Result, error) - setVitessMetadata(ctx context.Context, name, value string) error + ShowVitessReplicationStatus(ctx context.Context, filter *sqlparser.ShowFilter) (*sqltypes.Result, error) + ShowShards(ctx context.Context, filter *sqlparser.ShowFilter, destTabletType topodatapb.TabletType) (*sqltypes.Result, error) + ShowTablets(filter *sqlparser.ShowFilter) (*sqltypes.Result, error) + ShowVitessMetadata(ctx context.Context, filter *sqlparser.ShowFilter) (*sqltypes.Result, error) + SetVitessMetadata(ctx context.Context, name, value string) error // TODO: remove when resolver is gone - 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) + PlanPrepareStmt(ctx context.Context, vcursor *VCursorImpl, query string) (*engine.Plan, sqlparser.Statement, error) - environment() *vtenv.Environment + Environment() *vtenv.Environment ReadTransaction(ctx context.Context, transactionID string) (*querypb.TransactionMetadata, error) UnresolvedTransactions(ctx context.Context, targets []*querypb.Target) ([]*querypb.TransactionMetadata, error) + AddWarningCount(name string, value int64) } // VSchemaOperator is an interface to Vschema Operations @@ -99,10 +125,11 @@ type ( UpdateVSchema(ctx context.Context, ksName string, vschema *vschemapb.SrvVSchema) error } - // vcursorImpl implements the VCursor functionality used by dependent + // VCursorImpl implements the VCursor functionality used by dependent // packages to call back into VTGate. - vcursorImpl struct { - safeSession *SafeSession + VCursorImpl struct { + config VCursorConfig + SafeSession *SafeSession keyspace string tabletType topodatapb.TabletType destination key.Destination @@ -111,7 +138,6 @@ type ( resolver *srvtopo.Resolver topoServer *topo.Server 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 @@ -122,16 +148,11 @@ type ( vschema *vindexes.VSchema vm VSchemaOperator semTable *semantics.SemTable - warnShardedOnly bool // when using sharded only features, a warning will be warnings field queryTimeout time.Duration warnings []*querypb.QueryWarning // any warnings that are accumulated during the planning phase are stored here - pv plancontext.PlannerVersion - warmingReadsPercent int - warmingReadsChannel chan bool - - resultsObserver resultsObserver + observer ResultsObserver // this is a map of the number of rows that every primitive has returned // if this field is nil, it means that we are not logging operator traffic @@ -140,23 +161,23 @@ type ( } ) -// newVcursorImpl creates a vcursorImpl. Before creating this object, you have to separate out any marginComments that came with +// NewVCursorImpl creates a VCursorImpl. Before creating this object, you have to separate out any marginComments that came with // the query and supply it here. Trailing comments are typically sent by the application for various reasons, // including as identifying markers. So, they have to be added back to all queries that are executed // on behalf of the original query. -func newVCursorImpl( +func NewVCursorImpl( safeSession *SafeSession, marginComments sqlparser.MarginComments, - executor *Executor, + executor iExecute, logStats *logstats.LogStats, vm VSchemaOperator, vschema *vindexes.VSchema, resolver *srvtopo.Resolver, serv srvtopo.Server, - warnShardedOnly bool, - pv plancontext.PlannerVersion, -) (*vcursorImpl, error) { - keyspace, tabletType, destination, err := parseDestinationTarget(safeSession.TargetString, vschema) + observer ResultsObserver, + cfg VCursorConfig, +) (*VCursorImpl, error) { + keyspace, tabletType, destination, err := ParseDestinationTarget(safeSession.TargetString, cfg.DefaultTabletType, vschema) if err != nil { return nil, err } @@ -171,107 +192,175 @@ func newVCursorImpl( } } - // we only support collations for the new TabletGateway implementation - var connCollation collations.ID - if executor != nil { - if gw, isTabletGw := executor.resolver.resolver.GetGateway().(*TabletGateway); isTabletGw { - connCollation = gw.DefaultConnCollation() - } - } - if connCollation == collations.Unknown { - connCollation = executor.env.CollationEnv().DefaultConnectionCharset() - } - - warmingReadsPct := 0 - var warmingReadsChan chan bool - if executor != nil { - warmingReadsPct = executor.warmingReadsPercent - warmingReadsChan = executor.warmingReadsChannel - } - return &vcursorImpl{ - safeSession: safeSession, - keyspace: keyspace, - tabletType: tabletType, - destination: destination, - marginComments: marginComments, - executor: executor, - logStats: logStats, - collation: connCollation, - resolver: resolver, - vschema: vschema, - vm: vm, - topoServer: ts, - warnShardedOnly: warnShardedOnly, - pv: pv, - warmingReadsPercent: warmingReadsPct, - warmingReadsChannel: warmingReadsChan, - resultsObserver: nullResultsObserver{}, + return &VCursorImpl{ + config: cfg, + SafeSession: safeSession, + keyspace: keyspace, + tabletType: tabletType, + destination: destination, + marginComments: marginComments, + executor: executor, + logStats: logStats, + resolver: resolver, + vschema: vschema, + vm: vm, + topoServer: ts, + + observer: observer, }, nil } +func (vc *VCursorImpl) CloneForMirroring(ctx context.Context) engine.VCursor { + callerId := callerid.EffectiveCallerIDFromContext(ctx) + immediateCallerId := callerid.ImmediateCallerIDFromContext(ctx) + + clonedCtx := callerid.NewContext(ctx, callerId, immediateCallerId) + + v := &VCursorImpl{ + config: vc.config, + SafeSession: NewAutocommitSession(vc.SafeSession.Session), + keyspace: vc.keyspace, + tabletType: vc.tabletType, + destination: vc.destination, + marginComments: vc.marginComments, + executor: vc.executor, + resolver: vc.resolver, + topoServer: vc.topoServer, + logStats: &logstats.LogStats{Ctx: clonedCtx}, + ignoreMaxMemoryRows: vc.ignoreMaxMemoryRows, + vschema: vc.vschema, + vm: vc.vm, + semTable: vc.semTable, + warnings: vc.warnings, + observer: vc.observer, + } + + v.marginComments.Trailing += "/* mirror query */" + + return v +} + +func (vc *VCursorImpl) CloneForReplicaWarming(ctx context.Context) engine.VCursor { + callerId := callerid.EffectiveCallerIDFromContext(ctx) + immediateCallerId := callerid.ImmediateCallerIDFromContext(ctx) + + timedCtx, _ := context.WithTimeout(context.Background(), vc.config.WarmingReadsTimeout) // nolint + clonedCtx := callerid.NewContext(timedCtx, callerId, immediateCallerId) + + v := &VCursorImpl{ + config: vc.config, + SafeSession: NewAutocommitSession(vc.SafeSession.Session), + keyspace: vc.keyspace, + tabletType: topodatapb.TabletType_REPLICA, + destination: vc.destination, + marginComments: vc.marginComments, + executor: vc.executor, + resolver: vc.resolver, + topoServer: vc.topoServer, + logStats: &logstats.LogStats{Ctx: clonedCtx}, + + ignoreMaxMemoryRows: vc.ignoreMaxMemoryRows, + vschema: vc.vschema, + vm: vc.vm, + semTable: vc.semTable, + warnings: vc.warnings, + observer: vc.observer, + } + + v.marginComments.Trailing += "/* warming read */" + + return v +} + +func (vc *VCursorImpl) cloneWithAutocommitSession() *VCursorImpl { + safeSession := vc.SafeSession.NewAutocommitSession() + return &VCursorImpl{ + config: vc.config, + SafeSession: safeSession, + keyspace: vc.keyspace, + tabletType: vc.tabletType, + destination: vc.destination, + marginComments: vc.marginComments, + executor: vc.executor, + logStats: vc.logStats, + resolver: vc.resolver, + vschema: vc.vschema, + vm: vc.vm, + topoServer: vc.topoServer, + observer: vc.observer, + } +} + // HasSystemVariables returns whether the session has set system variables or not -func (vc *vcursorImpl) HasSystemVariables() bool { - return vc.safeSession.HasSystemVariables() +func (vc *VCursorImpl) HasSystemVariables() bool { + return vc.SafeSession.HasSystemVariables() } // GetSystemVariables takes a visitor function that will save each system variables of the session -func (vc *vcursorImpl) GetSystemVariables(f func(k string, v string)) { - vc.safeSession.GetSystemVariables(f) +func (vc *VCursorImpl) GetSystemVariables(f func(k string, v string)) { + vc.SafeSession.GetSystemVariables(f) +} + +// GetSystemVariablesCopy returns a copy of the system variables of the session. Changes to the original map will not affect the session. +func (vc *VCursorImpl) GetSystemVariablesCopy() map[string]string { + vc.SafeSession.mu.Lock() + defer vc.SafeSession.mu.Unlock() + return maps.Clone(vc.SafeSession.SystemVariables) } // ConnCollation returns the collation of this session -func (vc *vcursorImpl) ConnCollation() collations.ID { - return vc.collation +func (vc *VCursorImpl) ConnCollation() collations.ID { + return vc.config.Collation } // Environment returns the vtenv associated with this session -func (vc *vcursorImpl) Environment() *vtenv.Environment { - return vc.executor.environment() +func (vc *VCursorImpl) Environment() *vtenv.Environment { + return vc.executor.Environment() } -func (vc *vcursorImpl) TimeZone() *time.Location { - return vc.safeSession.TimeZone() +func (vc *VCursorImpl) TimeZone() *time.Location { + return vc.SafeSession.TimeZone() } -func (vc *vcursorImpl) SQLMode() string { +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 +func (vc *VCursorImpl) MaxMemoryRows() int { + return vc.config.MaxMemoryRows } // ExceedsMaxMemoryRows returns a boolean indicating whether the maxMemoryRows value has been exceeded. // Returns false if the max memory rows override directive is set to true. -func (vc *vcursorImpl) ExceedsMaxMemoryRows(numRows int) bool { - return !vc.ignoreMaxMemoryRows && numRows > maxMemoryRows +func (vc *VCursorImpl) ExceedsMaxMemoryRows(numRows int) bool { + return !vc.ignoreMaxMemoryRows && numRows > vc.config.MaxMemoryRows } // SetIgnoreMaxMemoryRows sets the ignoreMaxMemoryRows value. -func (vc *vcursorImpl) SetIgnoreMaxMemoryRows(ignoreMaxMemoryRows bool) { +func (vc *VCursorImpl) SetIgnoreMaxMemoryRows(ignoreMaxMemoryRows bool) { vc.ignoreMaxMemoryRows = ignoreMaxMemoryRows } // RecordWarning stores the given warning in the current session -func (vc *vcursorImpl) RecordWarning(warning *querypb.QueryWarning) { - vc.safeSession.RecordWarning(warning) +func (vc *VCursorImpl) RecordWarning(warning *querypb.QueryWarning) { + vc.SafeSession.RecordWarning(warning) } // IsShardRoutingEnabled implements the VCursor interface. -func (vc *vcursorImpl) IsShardRoutingEnabled() bool { - return enableShardRouting +func (vc *VCursorImpl) IsShardRoutingEnabled() bool { + return vc.config.EnableShardRouting } -func (vc *vcursorImpl) ReadTransaction(ctx context.Context, transactionID string) (*querypb.TransactionMetadata, error) { +func (vc *VCursorImpl) ReadTransaction(ctx context.Context, transactionID string) (*querypb.TransactionMetadata, error) { return vc.executor.ReadTransaction(ctx, transactionID) } // UnresolvedTransactions gets the unresolved transactions for the given keyspace. If the keyspace is not given, // then we use the default keyspace. -func (vc *vcursorImpl) UnresolvedTransactions(ctx context.Context, keyspace string) ([]*querypb.TransactionMetadata, error) { +func (vc *VCursorImpl) UnresolvedTransactions(ctx context.Context, keyspace string) ([]*querypb.TransactionMetadata, error) { if keyspace == "" { keyspace = vc.GetKeyspace() } @@ -286,7 +375,7 @@ func (vc *vcursorImpl) UnresolvedTransactions(ctx context.Context, keyspace stri return vc.executor.UnresolvedTransactions(ctx, targets) } -func (vc *vcursorImpl) StartPrimitiveTrace() func() engine.Stats { +func (vc *VCursorImpl) StartPrimitiveTrace() func() engine.Stats { vc.interOpStats = make(map[engine.Primitive]engine.RowsReceived) vc.shardsStats = make(map[engine.Primitive]engine.ShardsQueried) return func() engine.Stats { @@ -299,8 +388,8 @@ func (vc *vcursorImpl) StartPrimitiveTrace() func() engine.Stats { // FindTable finds the specified table. If the keyspace what specified in the input, it gets used as qualifier. // Otherwise, the keyspace from the request is used, if one was provided. -func (vc *vcursorImpl) FindTable(name sqlparser.TableName) (*vindexes.Table, string, topodatapb.TabletType, key.Destination, error) { - destKeyspace, destTabletType, dest, err := vc.executor.ParseDestinationTarget(name.Qualifier.String()) +func (vc *VCursorImpl) FindTable(name sqlparser.TableName) (*vindexes.Table, string, topodatapb.TabletType, key.Destination, error) { + destKeyspace, destTabletType, dest, err := vc.ParseDestinationTarget(name.Qualifier.String()) if err != nil { return nil, "", destTabletType, nil, err } @@ -314,8 +403,8 @@ func (vc *vcursorImpl) FindTable(name sqlparser.TableName) (*vindexes.Table, str return table, destKeyspace, destTabletType, dest, err } -func (vc *vcursorImpl) FindView(name sqlparser.TableName) sqlparser.SelectStatement { - ks, _, _, err := vc.executor.ParseDestinationTarget(name.Qualifier.String()) +func (vc *VCursorImpl) FindView(name sqlparser.TableName) sqlparser.SelectStatement { + ks, _, _, err := vc.ParseDestinationTarget(name.Qualifier.String()) if err != nil { return nil } @@ -325,8 +414,8 @@ func (vc *vcursorImpl) FindView(name sqlparser.TableName) sqlparser.SelectStatem return vc.vschema.FindView(ks, name.Name.String()) } -func (vc *vcursorImpl) FindRoutedTable(name sqlparser.TableName) (*vindexes.Table, error) { - destKeyspace, destTabletType, _, err := vc.executor.ParseDestinationTarget(name.Qualifier.String()) +func (vc *VCursorImpl) FindRoutedTable(name sqlparser.TableName) (*vindexes.Table, error) { + destKeyspace, destTabletType, _, err := vc.ParseDestinationTarget(name.Qualifier.String()) if err != nil { return nil, err } @@ -343,14 +432,14 @@ func (vc *vcursorImpl) FindRoutedTable(name sqlparser.TableName) (*vindexes.Tabl } // FindTableOrVindex finds the specified table or vindex. -func (vc *vcursorImpl) FindTableOrVindex(name sqlparser.TableName) (*vindexes.Table, vindexes.Vindex, string, topodatapb.TabletType, key.Destination, error) { +func (vc *VCursorImpl) FindTableOrVindex(name sqlparser.TableName) (*vindexes.Table, vindexes.Vindex, string, topodatapb.TabletType, key.Destination, error) { if name.Qualifier.IsEmpty() && name.Name.String() == "dual" { // The magical MySQL dual table should only be resolved // when it is not qualified by a database name. return vc.getDualTable() } - destKeyspace, destTabletType, dest, err := vc.executor.ParseDestinationTarget(name.Qualifier.String()) + destKeyspace, destTabletType, dest, err := ParseDestinationTarget(name.Qualifier.String(), vc.tabletType, vc.vschema) if err != nil { return nil, nil, "", destTabletType, nil, err } @@ -364,7 +453,23 @@ func (vc *vcursorImpl) FindTableOrVindex(name sqlparser.TableName) (*vindexes.Ta return table, vindex, destKeyspace, destTabletType, dest, nil } -func (vc *vcursorImpl) getDualTable() (*vindexes.Table, vindexes.Vindex, string, topodatapb.TabletType, key.Destination, error) { +func (vc *VCursorImpl) ParseDestinationTarget(targetString string) (string, topodatapb.TabletType, key.Destination, error) { + return ParseDestinationTarget(targetString, vc.tabletType, vc.vschema) +} + +// ParseDestinationTarget parses destination target string and provides a keyspace if possible. +func ParseDestinationTarget(targetString string, tablet topodatapb.TabletType, vschema *vindexes.VSchema) (string, topodatapb.TabletType, key.Destination, error) { + destKeyspace, destTabletType, dest, err := topoprotopb.ParseDestination(targetString, tablet) + // If the keyspace is not specified, and there is only one keyspace in the VSchema, use that. + if destKeyspace == "" && len(vschema.Keyspaces) == 1 { + for k := range vschema.Keyspaces { + destKeyspace = k + } + } + return destKeyspace, destTabletType, dest, err +} + +func (vc *VCursorImpl) getDualTable() (*vindexes.Table, vindexes.Vindex, string, topodatapb.TabletType, key.Destination, error) { ksName := vc.getActualKeyspace() var ks *vindexes.Keyspace if ksName == "" { @@ -381,7 +486,7 @@ func (vc *vcursorImpl) getDualTable() (*vindexes.Table, vindexes.Vindex, string, return tbl, nil, ksName, topodatapb.TabletType_PRIMARY, nil, nil } -func (vc *vcursorImpl) getActualKeyspace() string { +func (vc *VCursorImpl) getActualKeyspace() string { if !sqlparser.SystemSchema(vc.keyspace) { return vc.keyspace } @@ -392,12 +497,12 @@ func (vc *vcursorImpl) getActualKeyspace() string { return ks.Name } -// DefaultKeyspace returns the default keyspace of the current request +// SelectedKeyspace returns the selected keyspace of the current request // if there is one. If the keyspace specified in the target cannot be // identified, it returns an error. -func (vc *vcursorImpl) DefaultKeyspace() (*vindexes.Keyspace, error) { +func (vc *VCursorImpl) SelectedKeyspace() (*vindexes.Keyspace, error) { if ignoreKeyspace(vc.keyspace) { - return nil, errNoKeyspace + return nil, ErrNoKeyspace } ks, ok := vc.vschema.Keyspaces[vc.keyspace] if !ok { @@ -408,12 +513,12 @@ func (vc *vcursorImpl) DefaultKeyspace() (*vindexes.Keyspace, error) { var errNoDbAvailable = vterrors.NewErrorf(vtrpcpb.Code_FAILED_PRECONDITION, vterrors.NoDB, "no database available") -func (vc *vcursorImpl) AnyKeyspace() (*vindexes.Keyspace, error) { - keyspace, err := vc.DefaultKeyspace() +func (vc *VCursorImpl) AnyKeyspace() (*vindexes.Keyspace, error) { + keyspace, err := vc.SelectedKeyspace() if err == nil { return keyspace, nil } - if err != errNoKeyspace { + if err != ErrNoKeyspace { return nil, err } @@ -434,7 +539,7 @@ func (vc *vcursorImpl) AnyKeyspace() (*vindexes.Keyspace, error) { } // getSortedServingKeyspaces gets the sorted serving keyspaces -func (vc *vcursorImpl) getSortedServingKeyspaces() []*vindexes.Keyspace { +func (vc *VCursorImpl) getSortedServingKeyspaces() []*vindexes.Keyspace { var keyspaces []*vindexes.Keyspace if vc.resolver != nil && vc.resolver.GetGateway() != nil { @@ -458,7 +563,7 @@ func (vc *vcursorImpl) getSortedServingKeyspaces() []*vindexes.Keyspace { return keyspaces } -func (vc *vcursorImpl) FirstSortedKeyspace() (*vindexes.Keyspace, error) { +func (vc *VCursorImpl) FirstSortedKeyspace() (*vindexes.Keyspace, error) { if len(vc.vschema.Keyspaces) == 0 { return nil, errNoDbAvailable } @@ -468,17 +573,17 @@ func (vc *vcursorImpl) FirstSortedKeyspace() (*vindexes.Keyspace, error) { } // SysVarSetEnabled implements the ContextVSchema interface -func (vc *vcursorImpl) SysVarSetEnabled() bool { +func (vc *VCursorImpl) SysVarSetEnabled() bool { return vc.GetSessionEnableSystemSettings() } // KeyspaceExists provides whether the keyspace exists or not. -func (vc *vcursorImpl) KeyspaceExists(ks string) bool { +func (vc *VCursorImpl) KeyspaceExists(ks string) bool { return vc.vschema.Keyspaces[ks] != nil } // AllKeyspace implements the ContextVSchema interface -func (vc *vcursorImpl) AllKeyspace() ([]*vindexes.Keyspace, error) { +func (vc *VCursorImpl) AllKeyspace() ([]*vindexes.Keyspace, error) { if len(vc.vschema.Keyspaces) == 0 { return nil, errNoDbAvailable } @@ -490,7 +595,7 @@ func (vc *vcursorImpl) AllKeyspace() ([]*vindexes.Keyspace, error) { } // FindKeyspace implements the VSchema interface -func (vc *vcursorImpl) FindKeyspace(keyspace string) (*vindexes.Keyspace, error) { +func (vc *VCursorImpl) FindKeyspace(keyspace string) (*vindexes.Keyspace, error) { if len(vc.vschema.Keyspaces) == 0 { return nil, errNoDbAvailable } @@ -503,28 +608,28 @@ func (vc *vcursorImpl) FindKeyspace(keyspace string) (*vindexes.Keyspace, error) } // Planner implements the ContextVSchema interface -func (vc *vcursorImpl) Planner() plancontext.PlannerVersion { - if vc.safeSession.Options != nil && - vc.safeSession.Options.PlannerVersion != querypb.ExecuteOptions_DEFAULT_PLANNER { - return vc.safeSession.Options.PlannerVersion +func (vc *VCursorImpl) Planner() plancontext.PlannerVersion { + if vc.SafeSession.Options != nil && + vc.SafeSession.Options.PlannerVersion != querypb.ExecuteOptions_DEFAULT_PLANNER { + return vc.SafeSession.Options.PlannerVersion } - return vc.pv + return vc.config.PlannerVersion } // GetSemTable implements the ContextVSchema interface -func (vc *vcursorImpl) GetSemTable() *semantics.SemTable { +func (vc *VCursorImpl) GetSemTable() *semantics.SemTable { return vc.semTable } // TargetString returns the current TargetString of the session. -func (vc *vcursorImpl) TargetString() string { - return vc.safeSession.TargetString +func (vc *VCursorImpl) TargetString() string { + return vc.SafeSession.TargetString } // MaxBufferingRetries is to represent max retries on buffering. const MaxBufferingRetries = 3 -func (vc *vcursorImpl) ExecutePrimitive(ctx context.Context, primitive engine.Primitive, bindVars map[string]*querypb.BindVariable, wantfields bool) (*sqltypes.Result, error) { +func (vc *VCursorImpl) ExecutePrimitive(ctx context.Context, primitive engine.Primitive, bindVars map[string]*querypb.BindVariable, wantfields bool) (*sqltypes.Result, error) { for try := 0; try < MaxBufferingRetries; try++ { res, err := primitive.TryExecute(ctx, vc, bindVars, wantfields) if err != nil && vterrors.RootCause(err) == buffer.ShardMissingError { @@ -536,7 +641,7 @@ func (vc *vcursorImpl) ExecutePrimitive(ctx context.Context, primitive engine.Pr return nil, vterrors.New(vtrpcpb.Code_UNAVAILABLE, "upstream shards are not available") } -func (vc *vcursorImpl) logOpTraffic(primitive engine.Primitive, res *sqltypes.Result) { +func (vc *VCursorImpl) logOpTraffic(primitive engine.Primitive, res *sqltypes.Result) { if vc.interOpStats != nil { rows := vc.interOpStats[primitive] if res == nil { @@ -548,14 +653,14 @@ func (vc *vcursorImpl) logOpTraffic(primitive engine.Primitive, res *sqltypes.Re } } -func (vc *vcursorImpl) logShardsQueried(primitive engine.Primitive, shardsNb int) { +func (vc *VCursorImpl) logShardsQueried(primitive engine.Primitive, shardsNb int) { if vc.shardsStats != nil { vc.shardsStats[primitive] += engine.ShardsQueried(shardsNb) } } -func (vc *vcursorImpl) ExecutePrimitiveStandalone(ctx context.Context, primitive engine.Primitive, bindVars map[string]*querypb.BindVariable, wantfields bool) (*sqltypes.Result, error) { - // clone the vcursorImpl with a new session. +func (vc *VCursorImpl) ExecutePrimitiveStandalone(ctx context.Context, primitive engine.Primitive, bindVars map[string]*querypb.BindVariable, wantfields bool) (*sqltypes.Result, error) { + // clone the VCursorImpl with a new session. newVC := vc.cloneWithAutocommitSession() for try := 0; try < MaxBufferingRetries; try++ { res, err := primitive.TryExecute(ctx, newVC, bindVars, wantfields) @@ -568,7 +673,7 @@ func (vc *vcursorImpl) ExecutePrimitiveStandalone(ctx context.Context, primitive return nil, vterrors.New(vtrpcpb.Code_UNAVAILABLE, "upstream shards are not available") } -func (vc *vcursorImpl) wrapCallback(callback func(*sqltypes.Result) error, primitive engine.Primitive) func(*sqltypes.Result) error { +func (vc *VCursorImpl) wrapCallback(callback func(*sqltypes.Result) error, primitive engine.Primitive) func(*sqltypes.Result) error { if vc.interOpStats == nil { return callback } @@ -579,7 +684,7 @@ func (vc *vcursorImpl) wrapCallback(callback func(*sqltypes.Result) error, primi } } -func (vc *vcursorImpl) StreamExecutePrimitive(ctx context.Context, primitive engine.Primitive, bindVars map[string]*querypb.BindVariable, wantfields bool, callback func(*sqltypes.Result) error) error { +func (vc *VCursorImpl) StreamExecutePrimitive(ctx context.Context, primitive engine.Primitive, bindVars map[string]*querypb.BindVariable, wantfields bool, callback func(*sqltypes.Result) error) error { callback = vc.wrapCallback(callback, primitive) for try := 0; try < MaxBufferingRetries; try++ { @@ -592,10 +697,10 @@ func (vc *vcursorImpl) StreamExecutePrimitive(ctx context.Context, primitive eng return vterrors.New(vtrpcpb.Code_UNAVAILABLE, "upstream shards are not available") } -func (vc *vcursorImpl) StreamExecutePrimitiveStandalone(ctx context.Context, primitive engine.Primitive, bindVars map[string]*querypb.BindVariable, wantfields bool, callback func(result *sqltypes.Result) error) error { +func (vc *VCursorImpl) StreamExecutePrimitiveStandalone(ctx context.Context, primitive engine.Primitive, bindVars map[string]*querypb.BindVariable, wantfields bool, callback func(result *sqltypes.Result) error) error { callback = vc.wrapCallback(callback, primitive) - // clone the vcursorImpl with a new session. + // clone the VCursorImpl with a new session. newVC := vc.cloneWithAutocommitSession() for try := 0; try < MaxBufferingRetries; try++ { err := primitive.TryStreamExecute(ctx, newVC, bindVars, wantfields, callback) @@ -608,12 +713,11 @@ func (vc *vcursorImpl) StreamExecutePrimitiveStandalone(ctx context.Context, pri } // Execute is part of the engine.VCursor interface. -func (vc *vcursorImpl) Execute(ctx context.Context, method string, query string, bindVars map[string]*querypb.BindVariable, rollbackOnError bool, co vtgatepb.CommitOrder) (*sqltypes.Result, error) { - session := vc.safeSession +func (vc *VCursorImpl) Execute(ctx context.Context, method string, query string, bindVars map[string]*querypb.BindVariable, rollbackOnError bool, co vtgatepb.CommitOrder) (*sqltypes.Result, error) { + session := vc.SafeSession if co == vtgatepb.CommitOrder_AUTOCOMMIT { // For autocommit, we have to create an independent session. - session = NewAutocommitSession(vc.safeSession.Session) - session.logging = vc.safeSession.logging + session = vc.SafeSession.NewAutocommitSession() rollbackOnError = false } else { session.SetCommitOrder(co) @@ -634,24 +738,22 @@ func (vc *vcursorImpl) Execute(ctx context.Context, method string, query string, // markSavepoint opens an internal savepoint before executing the original query. // This happens only when rollback is allowed and no other savepoint was executed // and the query is executed in an explicit transaction (i.e. started by the client). -func (vc *vcursorImpl) markSavepoint(ctx context.Context, needsRollbackOnParialExec bool, bindVars map[string]*querypb.BindVariable) error { - if !needsRollbackOnParialExec || !vc.safeSession.CanAddSavepoint() { +func (vc *VCursorImpl) markSavepoint(ctx context.Context, needsRollbackOnParialExec bool, bindVars map[string]*querypb.BindVariable) error { + if !needsRollbackOnParialExec || !vc.SafeSession.CanAddSavepoint() { return nil } uID := fmt.Sprintf("_vt%s", strings.ReplaceAll(uuid.NewString(), "-", "_")) spQuery := fmt.Sprintf("%ssavepoint %s%s", vc.marginComments.Leading, uID, vc.marginComments.Trailing) - _, err := vc.executor.Execute(ctx, nil, "MarkSavepoint", vc.safeSession, spQuery, bindVars) + _, err := vc.executor.Execute(ctx, nil, "MarkSavepoint", vc.SafeSession, spQuery, bindVars) if err != nil { return err } - vc.safeSession.SetSavepoint(uID) + vc.SafeSession.SetSavepoint(uID) return nil } -const txRollback = "Rollback Transaction" - // ExecuteMultiShard is part of the engine.VCursor interface. -func (vc *vcursorImpl) ExecuteMultiShard(ctx context.Context, primitive engine.Primitive, rss []*srvtopo.ResolvedShard, queries []*querypb.BoundQuery, rollbackOnError, canAutocommit bool) (*sqltypes.Result, []error) { +func (vc *VCursorImpl) ExecuteMultiShard(ctx context.Context, primitive engine.Primitive, rss []*srvtopo.ResolvedShard, queries []*querypb.BoundQuery, rollbackOnError, canAutocommit bool) (*sqltypes.Result, []error) { noOfShards := len(rss) atomic.AddUint64(&vc.logStats.ShardQueries, uint64(noOfShards)) err := vc.markSavepoint(ctx, rollbackOnError && (noOfShards > 1), map[string]*querypb.BindVariable{}) @@ -659,14 +761,14 @@ func (vc *vcursorImpl) ExecuteMultiShard(ctx context.Context, primitive engine.P return nil, []error{err} } - qr, errs := vc.executor.ExecuteMultiShard(ctx, primitive, rss, commentedShardQueries(queries, vc.marginComments), vc.safeSession, canAutocommit, vc.ignoreMaxMemoryRows, vc.resultsObserver) + qr, errs := vc.executor.ExecuteMultiShard(ctx, primitive, rss, commentedShardQueries(queries, vc.marginComments), vc.SafeSession, canAutocommit, vc.ignoreMaxMemoryRows, vc.observer) vc.setRollbackOnPartialExecIfRequired(len(errs) != len(rss), rollbackOnError) vc.logShardsQueried(primitive, len(rss)) return qr, errs } // StreamExecuteMulti is the streaming version of ExecuteMultiShard. -func (vc *vcursorImpl) StreamExecuteMulti(ctx context.Context, primitive engine.Primitive, query string, rss []*srvtopo.ResolvedShard, bindVars []map[string]*querypb.BindVariable, rollbackOnError bool, autocommit bool, callback func(reply *sqltypes.Result) error) []error { +func (vc *VCursorImpl) StreamExecuteMulti(ctx context.Context, primitive engine.Primitive, query string, rss []*srvtopo.ResolvedShard, bindVars []map[string]*querypb.BindVariable, rollbackOnError bool, autocommit bool, callback func(reply *sqltypes.Result) error) []error { callback = vc.wrapCallback(callback, primitive) noOfShards := len(rss) @@ -676,20 +778,20 @@ func (vc *vcursorImpl) StreamExecuteMulti(ctx context.Context, primitive engine. return []error{err} } - errs := vc.executor.StreamExecuteMulti(ctx, primitive, vc.marginComments.Leading+query+vc.marginComments.Trailing, rss, bindVars, vc.safeSession, autocommit, callback, vc.resultsObserver) + errs := vc.executor.StreamExecuteMulti(ctx, primitive, vc.marginComments.Leading+query+vc.marginComments.Trailing, rss, bindVars, vc.SafeSession, autocommit, callback, vc.observer) vc.setRollbackOnPartialExecIfRequired(len(errs) != len(rss), rollbackOnError) return errs } // ExecuteLock is for executing advisory lock statements. -func (vc *vcursorImpl) ExecuteLock(ctx context.Context, rs *srvtopo.ResolvedShard, query *querypb.BoundQuery, lockFuncType sqlparser.LockingFuncType) (*sqltypes.Result, error) { +func (vc *VCursorImpl) ExecuteLock(ctx context.Context, rs *srvtopo.ResolvedShard, query *querypb.BoundQuery, lockFuncType sqlparser.LockingFuncType) (*sqltypes.Result, error) { query.Sql = vc.marginComments.Leading + query.Sql + vc.marginComments.Trailing - return vc.executor.ExecuteLock(ctx, rs, query, vc.safeSession, lockFuncType) + return vc.executor.ExecuteLock(ctx, rs, query, vc.SafeSession, lockFuncType) } // ExecuteStandalone is part of the engine.VCursor interface. -func (vc *vcursorImpl) ExecuteStandalone(ctx context.Context, primitive engine.Primitive, query string, bindVars map[string]*querypb.BindVariable, rs *srvtopo.ResolvedShard) (*sqltypes.Result, error) { +func (vc *VCursorImpl) ExecuteStandalone(ctx context.Context, primitive engine.Primitive, query string, bindVars map[string]*querypb.BindVariable, rs *srvtopo.ResolvedShard) (*sqltypes.Result, error) { rss := []*srvtopo.ResolvedShard{rs} bqs := []*querypb.BoundQuery{ { @@ -699,13 +801,13 @@ func (vc *vcursorImpl) ExecuteStandalone(ctx context.Context, primitive engine.P } // The autocommit flag is always set to false because we currently don't // execute DMLs through ExecuteStandalone. - qr, errs := vc.executor.ExecuteMultiShard(ctx, primitive, rss, bqs, NewAutocommitSession(vc.safeSession.Session), false /* autocommit */, vc.ignoreMaxMemoryRows, vc.resultsObserver) + qr, errs := vc.executor.ExecuteMultiShard(ctx, primitive, rss, bqs, NewAutocommitSession(vc.SafeSession.Session), false /* autocommit */, vc.ignoreMaxMemoryRows, vc.observer) vc.logShardsQueried(primitive, len(rss)) return qr, vterrors.Aggregate(errs) } // ExecuteKeyspaceID is part of the engine.VCursor interface. -func (vc *vcursorImpl) ExecuteKeyspaceID(ctx context.Context, keyspace string, ksid []byte, query string, bindVars map[string]*querypb.BindVariable, rollbackOnError, autocommit bool) (*sqltypes.Result, error) { +func (vc *VCursorImpl) ExecuteKeyspaceID(ctx context.Context, keyspace string, ksid []byte, query string, bindVars map[string]*querypb.BindVariable, rollbackOnError, autocommit bool) (*sqltypes.Result, error) { atomic.AddUint64(&vc.logStats.ShardQueries, 1) rss, _, err := vc.ResolveDestinations(ctx, keyspace, nil, []key.Destination{key.DestinationKeyspaceID(ksid)}) if err != nil { @@ -722,17 +824,17 @@ func (vc *vcursorImpl) ExecuteKeyspaceID(ctx context.Context, keyspace string, k // This creates a transaction but that transaction is for locking purpose only and should not cause multi-db transaction error. // This fields helps in to ignore multi-db transaction error when it states `queryFromVindex`. if !rollbackOnError { - vc.safeSession.queryFromVindex = true + vc.SafeSession.SetQueryFromVindex(true) defer func() { - vc.safeSession.queryFromVindex = false + vc.SafeSession.SetQueryFromVindex(false) }() } qr, errs := vc.ExecuteMultiShard(ctx, nil, rss, queries, rollbackOnError, autocommit) return qr, vterrors.Aggregate(errs) } -func (vc *vcursorImpl) InTransactionAndIsDML() bool { - if !vc.safeSession.InTransaction() { +func (vc *VCursorImpl) InTransactionAndIsDML() bool { + if !vc.SafeSession.InTransaction() { return false } switch vc.logStats.StmtType { @@ -742,7 +844,7 @@ func (vc *vcursorImpl) InTransactionAndIsDML() bool { return false } -func (vc *vcursorImpl) LookupRowLockShardSession() vtgatepb.CommitOrder { +func (vc *VCursorImpl) LookupRowLockShardSession() vtgatepb.CommitOrder { switch vc.logStats.StmtType { case "DELETE", "UPDATE": return vtgatepb.CommitOrder_POST @@ -751,23 +853,23 @@ func (vc *vcursorImpl) LookupRowLockShardSession() vtgatepb.CommitOrder { } // AutocommitApproval is part of the engine.VCursor interface. -func (vc *vcursorImpl) AutocommitApproval() bool { - return vc.safeSession.AutocommitApproval() +func (vc *VCursorImpl) AutocommitApproval() bool { + return vc.SafeSession.AutocommitApproval() } // setRollbackOnPartialExecIfRequired sets the value on SafeSession.rollbackOnPartialExec // when the query gets successfully executed on at least one shard, // there does not exist any old savepoint for which rollback is already set // and rollback on error is allowed. -func (vc *vcursorImpl) setRollbackOnPartialExecIfRequired(atleastOneSuccess bool, rollbackOnError bool) { - if atleastOneSuccess && rollbackOnError && !vc.safeSession.IsRollbackSet() { - vc.safeSession.SetRollbackCommand() +func (vc *VCursorImpl) setRollbackOnPartialExecIfRequired(atleastOneSuccess bool, rollbackOnError bool) { + if atleastOneSuccess && rollbackOnError && !vc.SafeSession.IsRollbackSet() { + vc.SafeSession.SetRollbackCommand() } } // fixupPartiallyMovedShards checks if any of the shards in the route has a ShardRoutingRule (true when a keyspace // is in the middle of being moved to another keyspace using MoveTables moving a subset of shards at a time -func (vc *vcursorImpl) fixupPartiallyMovedShards(rss []*srvtopo.ResolvedShard) ([]*srvtopo.ResolvedShard, error) { +func (vc *VCursorImpl) fixupPartiallyMovedShards(rss []*srvtopo.ResolvedShard) ([]*srvtopo.ResolvedShard, error) { if vc.vschema.ShardRoutingRules == nil { return rss, nil } @@ -784,12 +886,12 @@ func (vc *vcursorImpl) fixupPartiallyMovedShards(rss []*srvtopo.ResolvedShard) ( return rss, nil } -func (vc *vcursorImpl) ResolveDestinations(ctx context.Context, keyspace string, ids []*querypb.Value, destinations []key.Destination) ([]*srvtopo.ResolvedShard, [][]*querypb.Value, error) { +func (vc *VCursorImpl) ResolveDestinations(ctx context.Context, keyspace string, ids []*querypb.Value, destinations []key.Destination) ([]*srvtopo.ResolvedShard, [][]*querypb.Value, error) { rss, values, err := vc.resolver.ResolveDestinations(ctx, keyspace, vc.tabletType, ids, destinations) if err != nil { return nil, nil, err } - if enableShardRouting { + if vc.config.EnableShardRouting { rss, err = vc.fixupPartiallyMovedShards(rss) if err != nil { return nil, nil, err @@ -798,12 +900,12 @@ func (vc *vcursorImpl) ResolveDestinations(ctx context.Context, keyspace string, return rss, values, err } -func (vc *vcursorImpl) ResolveDestinationsMultiCol(ctx context.Context, keyspace string, ids [][]sqltypes.Value, destinations []key.Destination) ([]*srvtopo.ResolvedShard, [][][]sqltypes.Value, error) { +func (vc *VCursorImpl) ResolveDestinationsMultiCol(ctx context.Context, keyspace string, ids [][]sqltypes.Value, destinations []key.Destination) ([]*srvtopo.ResolvedShard, [][][]sqltypes.Value, error) { rss, values, err := vc.resolver.ResolveDestinationsMultiCol(ctx, keyspace, vc.tabletType, ids, destinations) if err != nil { return nil, nil, err } - if enableShardRouting { + if vc.config.EnableShardRouting { rss, err = vc.fixupPartiallyMovedShards(rss) if err != nil { return nil, nil, err @@ -812,12 +914,12 @@ func (vc *vcursorImpl) ResolveDestinationsMultiCol(ctx context.Context, keyspace return rss, values, err } -func (vc *vcursorImpl) Session() engine.SessionActions { +func (vc *VCursorImpl) Session() engine.SessionActions { return vc } -func (vc *vcursorImpl) SetTarget(target string) error { - keyspace, tabletType, _, err := topoprotopb.ParseDestination(target, defaultTabletType) +func (vc *VCursorImpl) SetTarget(target string) error { + keyspace, tabletType, _, err := topoprotopb.ParseDestination(target, vc.config.DefaultTabletType) if err != nil { return err } @@ -825,10 +927,12 @@ func (vc *vcursorImpl) SetTarget(target string) error { return vterrors.VT05003(keyspace) } - if vc.safeSession.InTransaction() && tabletType != topodatapb.TabletType_PRIMARY { + if vc.SafeSession.InTransaction() && tabletType != topodatapb.TabletType_PRIMARY { return vterrors.NewErrorf(vtrpcpb.Code_INVALID_ARGUMENT, vterrors.LockOrActiveTransaction, "can't execute the given command because you have an active transaction") } - vc.safeSession.SetTargetString(target) + vc.SafeSession.SetTargetString(target) + vc.keyspace = keyspace + vc.tabletType = tabletType return nil } @@ -836,30 +940,30 @@ func ignoreKeyspace(keyspace string) bool { return keyspace == "" || sqlparser.SystemSchema(keyspace) } -func (vc *vcursorImpl) SetUDV(key string, value any) error { +func (vc *VCursorImpl) SetUDV(key string, value any) error { bindValue, err := sqltypes.BuildBindVariable(value) if err != nil { return err } - vc.safeSession.SetUserDefinedVariable(key, bindValue) + vc.SafeSession.SetUserDefinedVariable(key, bindValue) return nil } -func (vc *vcursorImpl) SetSysVar(name string, expr string) { - vc.safeSession.SetSystemVariable(name, expr) +func (vc *VCursorImpl) SetSysVar(name string, expr string) { + vc.SafeSession.SetSystemVariable(name, expr) } // NeedsReservedConn implements the SessionActions interface -func (vc *vcursorImpl) NeedsReservedConn() { - vc.safeSession.SetReservedConn(true) +func (vc *VCursorImpl) NeedsReservedConn() { + vc.SafeSession.SetReservedConn(true) } -func (vc *vcursorImpl) InReservedConn() bool { - return vc.safeSession.InReservedConn() +func (vc *VCursorImpl) InReservedConn() bool { + return vc.SafeSession.InReservedConn() } -func (vc *vcursorImpl) ShardSession() []*srvtopo.ResolvedShard { - ss := vc.safeSession.GetShardSessions() +func (vc *VCursorImpl) ShardSession() []*srvtopo.ResolvedShard { + ss := vc.SafeSession.GetShardSessions() if len(ss) == 0 { return nil } @@ -874,12 +978,12 @@ func (vc *vcursorImpl) ShardSession() []*srvtopo.ResolvedShard { } // Destination implements the ContextVSchema interface -func (vc *vcursorImpl) Destination() key.Destination { +func (vc *VCursorImpl) Destination() key.Destination { return vc.destination } // TabletType implements the ContextVSchema interface -func (vc *vcursorImpl) TabletType() topodatapb.TabletType { +func (vc *VCursorImpl) TabletType() topodatapb.TabletType { return vc.tabletType } @@ -898,13 +1002,13 @@ func commentedShardQueries(shardQueries []*querypb.BoundQuery, marginComments sq } // TargetDestination implements the ContextVSchema interface -func (vc *vcursorImpl) TargetDestination(qualifier string) (key.Destination, *vindexes.Keyspace, topodatapb.TabletType, error) { +func (vc *VCursorImpl) TargetDestination(qualifier string) (key.Destination, *vindexes.Keyspace, topodatapb.TabletType, error) { keyspaceName := vc.getActualKeyspace() if vc.destination == nil && qualifier != "" { keyspaceName = qualifier } if keyspaceName == "" { - return nil, nil, 0, errNoKeyspace + return nil, nil, 0, ErrNoKeyspace } keyspace := vc.vschema.Keyspaces[keyspaceName] if keyspace == nil { @@ -914,63 +1018,63 @@ func (vc *vcursorImpl) TargetDestination(qualifier string) (key.Destination, *vi } // SetAutocommit implements the SessionActions interface -func (vc *vcursorImpl) SetAutocommit(ctx context.Context, autocommit bool) error { - if autocommit && vc.safeSession.InTransaction() { - if err := vc.executor.Commit(ctx, vc.safeSession); err != nil { +func (vc *VCursorImpl) SetAutocommit(ctx context.Context, autocommit bool) error { + if autocommit && vc.SafeSession.InTransaction() { + if err := vc.executor.Commit(ctx, vc.SafeSession); err != nil { return err } } - vc.safeSession.Autocommit = autocommit + vc.SafeSession.Autocommit = autocommit return nil } // SetQueryTimeout implements the SessionActions interface -func (vc *vcursorImpl) SetQueryTimeout(maxExecutionTime int64) { - vc.safeSession.QueryTimeout = maxExecutionTime +func (vc *VCursorImpl) SetQueryTimeout(maxExecutionTime int64) { + vc.SafeSession.QueryTimeout = maxExecutionTime } // SetClientFoundRows implements the SessionActions interface -func (vc *vcursorImpl) SetClientFoundRows(_ context.Context, clientFoundRows bool) error { - vc.safeSession.GetOrCreateOptions().ClientFoundRows = clientFoundRows +func (vc *VCursorImpl) SetClientFoundRows(_ context.Context, clientFoundRows bool) error { + vc.SafeSession.GetOrCreateOptions().ClientFoundRows = clientFoundRows return nil } // SetSkipQueryPlanCache implements the SessionActions interface -func (vc *vcursorImpl) SetSkipQueryPlanCache(_ context.Context, skipQueryPlanCache bool) error { - vc.safeSession.GetOrCreateOptions().SkipQueryPlanCache = skipQueryPlanCache +func (vc *VCursorImpl) SetSkipQueryPlanCache(_ context.Context, skipQueryPlanCache bool) error { + vc.SafeSession.GetOrCreateOptions().SkipQueryPlanCache = skipQueryPlanCache return nil } // SetSQLSelectLimit implements the SessionActions interface -func (vc *vcursorImpl) SetSQLSelectLimit(limit int64) error { - vc.safeSession.GetOrCreateOptions().SqlSelectLimit = limit +func (vc *VCursorImpl) SetSQLSelectLimit(limit int64) error { + vc.SafeSession.GetOrCreateOptions().SqlSelectLimit = limit return nil } // SetTransactionMode implements the SessionActions interface -func (vc *vcursorImpl) SetTransactionMode(mode vtgatepb.TransactionMode) { - vc.safeSession.TransactionMode = mode +func (vc *VCursorImpl) SetTransactionMode(mode vtgatepb.TransactionMode) { + vc.SafeSession.TransactionMode = mode } // SetWorkload implements the SessionActions interface -func (vc *vcursorImpl) SetWorkload(workload querypb.ExecuteOptions_Workload) { - vc.safeSession.GetOrCreateOptions().Workload = workload +func (vc *VCursorImpl) SetWorkload(workload querypb.ExecuteOptions_Workload) { + vc.SafeSession.GetOrCreateOptions().Workload = workload } // SetPlannerVersion implements the SessionActions interface -func (vc *vcursorImpl) SetPlannerVersion(v plancontext.PlannerVersion) { - vc.safeSession.GetOrCreateOptions().PlannerVersion = v +func (vc *VCursorImpl) SetPlannerVersion(v plancontext.PlannerVersion) { + vc.SafeSession.GetOrCreateOptions().PlannerVersion = v } -func (vc *vcursorImpl) SetPriority(priority string) { +func (vc *VCursorImpl) SetPriority(priority string) { if priority != "" { - vc.safeSession.GetOrCreateOptions().Priority = priority - } else if vc.safeSession.Options != nil && vc.safeSession.Options.Priority != "" { - vc.safeSession.Options.Priority = "" + vc.SafeSession.GetOrCreateOptions().Priority = priority + } else if vc.SafeSession.Options != nil && vc.SafeSession.Options.Priority != "" { + vc.SafeSession.Options.Priority = "" } } -func (vc *vcursorImpl) SetExecQueryTimeout(timeout *int) { +func (vc *VCursorImpl) SetExecQueryTimeout(timeout *int) { // Determine the effective timeout: use passed timeout if non-nil, otherwise use session's query timeout if available var execTimeout *int if timeout != nil { @@ -981,153 +1085,152 @@ func (vc *vcursorImpl) SetExecQueryTimeout(timeout *int) { // If no effective timeout and no session options, return early if execTimeout == nil { - if vc.safeSession.GetOptions() == nil { + if vc.SafeSession.GetOptions() == nil { return } - vc.safeSession.GetOrCreateOptions().Timeout = nil + vc.SafeSession.GetOrCreateOptions().Timeout = nil return } vc.queryTimeout = time.Duration(*execTimeout) * time.Millisecond // Set the authoritative timeout using the determined execTimeout - vc.safeSession.GetOrCreateOptions().Timeout = &querypb.ExecuteOptions_AuthoritativeTimeout{ + vc.SafeSession.GetOrCreateOptions().Timeout = &querypb.ExecuteOptions_AuthoritativeTimeout{ AuthoritativeTimeout: int64(*execTimeout), } } // getQueryTimeout returns timeout based on the priority // session setting > global default specified by a flag. -func (vc *vcursorImpl) getQueryTimeout() int { - sessionQueryTimeout := int(vc.safeSession.GetQueryTimeout()) +func (vc *VCursorImpl) getQueryTimeout() int { + sessionQueryTimeout := int(vc.SafeSession.GetQueryTimeout()) if sessionQueryTimeout != 0 { return sessionQueryTimeout } - return queryTimeout + return vc.config.QueryTimeout } // SetConsolidator implements the SessionActions interface -func (vc *vcursorImpl) SetConsolidator(consolidator querypb.ExecuteOptions_Consolidator) { +func (vc *VCursorImpl) SetConsolidator(consolidator querypb.ExecuteOptions_Consolidator) { // Avoid creating session Options when they do not yet exist and the // consolidator is unspecified. - if consolidator == querypb.ExecuteOptions_CONSOLIDATOR_UNSPECIFIED && vc.safeSession.GetOptions() == nil { + if consolidator == querypb.ExecuteOptions_CONSOLIDATOR_UNSPECIFIED && vc.SafeSession.GetOptions() == nil { return } - vc.safeSession.GetOrCreateOptions().Consolidator = consolidator + vc.SafeSession.GetOrCreateOptions().Consolidator = consolidator } -func (vc *vcursorImpl) SetWorkloadName(workloadName string) { +func (vc *VCursorImpl) SetWorkloadName(workloadName string) { if workloadName != "" { - vc.safeSession.GetOrCreateOptions().WorkloadName = workloadName + vc.SafeSession.GetOrCreateOptions().WorkloadName = workloadName } } // SetFoundRows implements the SessionActions interface -func (vc *vcursorImpl) SetFoundRows(foundRows uint64) { - vc.safeSession.FoundRows = foundRows - vc.safeSession.foundRowsHandled = true +func (vc *VCursorImpl) SetFoundRows(foundRows uint64) { + vc.SafeSession.SetFoundRows(foundRows) } // SetDDLStrategy implements the SessionActions interface -func (vc *vcursorImpl) SetDDLStrategy(strategy string) { - vc.safeSession.SetDDLStrategy(strategy) +func (vc *VCursorImpl) SetDDLStrategy(strategy string) { + vc.SafeSession.SetDDLStrategy(strategy) } // GetDDLStrategy implements the SessionActions interface -func (vc *vcursorImpl) GetDDLStrategy() string { - return vc.safeSession.GetDDLStrategy() +func (vc *VCursorImpl) GetDDLStrategy() string { + return vc.SafeSession.GetDDLStrategy() } // SetMigrationContext implements the SessionActions interface -func (vc *vcursorImpl) SetMigrationContext(migrationContext string) { - vc.safeSession.SetMigrationContext(migrationContext) +func (vc *VCursorImpl) SetMigrationContext(migrationContext string) { + vc.SafeSession.SetMigrationContext(migrationContext) } // GetMigrationContext implements the SessionActions interface -func (vc *vcursorImpl) GetMigrationContext() string { - return vc.safeSession.GetMigrationContext() +func (vc *VCursorImpl) GetMigrationContext() string { + return vc.SafeSession.GetMigrationContext() } // GetSessionUUID implements the SessionActions interface -func (vc *vcursorImpl) GetSessionUUID() string { - return vc.safeSession.GetSessionUUID() +func (vc *VCursorImpl) GetSessionUUID() string { + return vc.SafeSession.GetSessionUUID() } // SetSessionEnableSystemSettings implements the SessionActions interface -func (vc *vcursorImpl) SetSessionEnableSystemSettings(_ context.Context, allow bool) error { - vc.safeSession.SetSessionEnableSystemSettings(allow) +func (vc *VCursorImpl) SetSessionEnableSystemSettings(_ context.Context, allow bool) error { + vc.SafeSession.SetSessionEnableSystemSettings(allow) return nil } // GetSessionEnableSystemSettings implements the SessionActions interface -func (vc *vcursorImpl) GetSessionEnableSystemSettings() bool { - return vc.safeSession.GetSessionEnableSystemSettings() +func (vc *VCursorImpl) GetSessionEnableSystemSettings() bool { + return vc.SafeSession.GetSessionEnableSystemSettings() } // SetReadAfterWriteGTID implements the SessionActions interface -func (vc *vcursorImpl) SetReadAfterWriteGTID(vtgtid string) { - vc.safeSession.SetReadAfterWriteGTID(vtgtid) +func (vc *VCursorImpl) SetReadAfterWriteGTID(vtgtid string) { + vc.SafeSession.SetReadAfterWriteGTID(vtgtid) } // SetReadAfterWriteTimeout implements the SessionActions interface -func (vc *vcursorImpl) SetReadAfterWriteTimeout(timeout float64) { - vc.safeSession.SetReadAfterWriteTimeout(timeout) +func (vc *VCursorImpl) SetReadAfterWriteTimeout(timeout float64) { + vc.SafeSession.SetReadAfterWriteTimeout(timeout) } // SetSessionTrackGTIDs implements the SessionActions interface -func (vc *vcursorImpl) SetSessionTrackGTIDs(enable bool) { - vc.safeSession.SetSessionTrackGtids(enable) +func (vc *VCursorImpl) SetSessionTrackGTIDs(enable bool) { + vc.SafeSession.SetSessionTrackGtids(enable) } // HasCreatedTempTable implements the SessionActions interface -func (vc *vcursorImpl) HasCreatedTempTable() { - vc.safeSession.GetOrCreateOptions().HasCreatedTempTables = true +func (vc *VCursorImpl) HasCreatedTempTable() { + vc.SafeSession.GetOrCreateOptions().HasCreatedTempTables = true } // GetWarnings implements the SessionActions interface -func (vc *vcursorImpl) GetWarnings() []*querypb.QueryWarning { - return vc.safeSession.GetWarnings() +func (vc *VCursorImpl) GetWarnings() []*querypb.QueryWarning { + return vc.SafeSession.GetWarnings() } // AnyAdvisoryLockTaken implements the SessionActions interface -func (vc *vcursorImpl) AnyAdvisoryLockTaken() bool { - return vc.safeSession.HasAdvisoryLock() +func (vc *VCursorImpl) AnyAdvisoryLockTaken() bool { + return vc.SafeSession.HasAdvisoryLock() } // AddAdvisoryLock implements the SessionActions interface -func (vc *vcursorImpl) AddAdvisoryLock(name string) { - vc.safeSession.AddAdvisoryLock(name) +func (vc *VCursorImpl) AddAdvisoryLock(name string) { + vc.SafeSession.AddAdvisoryLock(name) } // RemoveAdvisoryLock implements the SessionActions interface -func (vc *vcursorImpl) RemoveAdvisoryLock(name string) { - vc.safeSession.RemoveAdvisoryLock(name) +func (vc *VCursorImpl) RemoveAdvisoryLock(name string) { + vc.SafeSession.RemoveAdvisoryLock(name) } -func (vc *vcursorImpl) SetCommitOrder(co vtgatepb.CommitOrder) { - vc.safeSession.SetCommitOrder(co) +func (vc *VCursorImpl) SetCommitOrder(co vtgatepb.CommitOrder) { + vc.SafeSession.SetCommitOrder(co) } -func (vc *vcursorImpl) InTransaction() bool { - return vc.safeSession.InTransaction() +func (vc *VCursorImpl) InTransaction() bool { + return vc.SafeSession.InTransaction() } -func (vc *vcursorImpl) Commit(ctx context.Context) error { - return vc.executor.Commit(ctx, vc.safeSession) +func (vc *VCursorImpl) Commit(ctx context.Context) error { + return vc.executor.Commit(ctx, vc.SafeSession) } // GetDBDDLPluginName implements the VCursor interface -func (vc *vcursorImpl) GetDBDDLPluginName() string { - return dbDDLPlugin +func (vc *VCursorImpl) GetDBDDLPluginName() string { + return vc.config.DBDDLPlugin } // KeyspaceAvailable implements the VCursor interface -func (vc *vcursorImpl) KeyspaceAvailable(ks string) bool { +func (vc *VCursorImpl) KeyspaceAvailable(ks string) bool { _, exists := vc.executor.VSchema().Keyspaces[ks] return exists } // ErrorIfShardedF implements the VCursor interface -func (vc *vcursorImpl) ErrorIfShardedF(ks *vindexes.Keyspace, warn, errFormat string, params ...any) error { +func (vc *VCursorImpl) ErrorIfShardedF(ks *vindexes.Keyspace, warn, errFormat string, params ...any) error { if ks.Sharded { return vterrors.Errorf(vtrpcpb.Code_UNIMPLEMENTED, errFormat, params...) } @@ -1136,19 +1239,25 @@ func (vc *vcursorImpl) ErrorIfShardedF(ks *vindexes.Keyspace, warn, errFormat st return nil } +func (vc *VCursorImpl) GetAndEmptyWarnings() []*querypb.QueryWarning { + w := vc.warnings + vc.warnings = nil + return w +} + // WarnUnshardedOnly implements the VCursor interface -func (vc *vcursorImpl) WarnUnshardedOnly(format string, params ...any) { - if vc.warnShardedOnly { +func (vc *VCursorImpl) WarnUnshardedOnly(format string, params ...any) { + if vc.config.WarnShardedOnly { vc.warnings = append(vc.warnings, &querypb.QueryWarning{ Code: uint32(sqlerror.ERNotSupportedYet), Message: fmt.Sprintf(format, params...), }) - warnings.Add("WarnUnshardedOnly", 1) + vc.executor.AddWarningCount("WarnUnshardedOnly", 1) } } // PlannerWarning implements the VCursor interface -func (vc *vcursorImpl) PlannerWarning(message string) { +func (vc *VCursorImpl) PlannerWarning(message string) { if message == "" { return } @@ -1159,8 +1268,8 @@ func (vc *vcursorImpl) PlannerWarning(message string) { } // ForeignKeyMode implements the VCursor interface -func (vc *vcursorImpl) ForeignKeyMode(keyspace string) (vschemapb.Keyspace_ForeignKeyMode, error) { - if strings.ToLower(foreignKeyMode) == "disallow" { +func (vc *VCursorImpl) ForeignKeyMode(keyspace string) (vschemapb.Keyspace_ForeignKeyMode, error) { + if vc.config.ForeignKeyMode == vschemapb.Keyspace_disallow { return vschemapb.Keyspace_disallow, nil } ks := vc.vschema.Keyspaces[keyspace] @@ -1170,7 +1279,7 @@ func (vc *vcursorImpl) ForeignKeyMode(keyspace string) (vschemapb.Keyspace_Forei return ks.ForeignKeyMode, nil } -func (vc *vcursorImpl) KeyspaceError(keyspace string) error { +func (vc *VCursorImpl) KeyspaceError(keyspace string) error { ks := vc.vschema.Keyspaces[keyspace] if ks == nil { return vterrors.VT14004(keyspace) @@ -1178,14 +1287,14 @@ func (vc *vcursorImpl) KeyspaceError(keyspace string) error { return ks.Error } -func (vc *vcursorImpl) GetAggregateUDFs() []string { +func (vc *VCursorImpl) GetAggregateUDFs() []string { return vc.vschema.GetAggregateUDFs() } // FindMirrorRule finds the mirror rule for the requested table name and // VSchema tablet type. -func (vc *vcursorImpl) FindMirrorRule(name sqlparser.TableName) (*vindexes.MirrorRule, error) { - destKeyspace, destTabletType, _, err := vc.executor.ParseDestinationTarget(name.Qualifier.String()) +func (vc *VCursorImpl) FindMirrorRule(name sqlparser.TableName) (*vindexes.MirrorRule, error) { + destKeyspace, destTabletType, _, err := vc.ParseDestinationTarget(name.Qualifier.String()) if err != nil { return nil, err } @@ -1199,23 +1308,11 @@ func (vc *vcursorImpl) FindMirrorRule(name sqlparser.TableName) (*vindexes.Mirro return mirrorRule, err } -// ParseDestinationTarget parses destination target string and sets default keyspace if possible. -func parseDestinationTarget(targetString string, vschema *vindexes.VSchema) (string, topodatapb.TabletType, key.Destination, error) { - destKeyspace, destTabletType, dest, err := topoprotopb.ParseDestination(targetString, defaultTabletType) - // Set default keyspace - if destKeyspace == "" && len(vschema.Keyspaces) == 1 { - for k := range vschema.Keyspaces { - destKeyspace = k - } - } - return destKeyspace, destTabletType, dest, err -} - -func (vc *vcursorImpl) keyForPlan(ctx context.Context, query string, buf io.StringWriter) { +func (vc *VCursorImpl) KeyForPlan(ctx context.Context, query string, buf io.StringWriter) { _, _ = buf.WriteString(vc.keyspace) _, _ = buf.WriteString(vindexes.TabletTypeSuffix[vc.tabletType]) _, _ = buf.WriteString("+Collate:") - _, _ = buf.WriteString(vc.Environment().CollationEnv().LookupName(vc.collation)) + _, _ = buf.WriteString(vc.Environment().CollationEnv().LookupName(vc.config.Collation)) if vc.destination != nil { switch vc.destination.(type) { @@ -1245,11 +1342,11 @@ func (vc *vcursorImpl) keyForPlan(ctx context.Context, query string, buf io.Stri _, _ = buf.WriteString(query) } -func (vc *vcursorImpl) GetKeyspace() string { +func (vc *VCursorImpl) GetKeyspace() string { return vc.keyspace } -func (vc *vcursorImpl) ExecuteVSchema(ctx context.Context, keyspace string, vschemaDDL *sqlparser.AlterVschema) error { +func (vc *VCursorImpl) ExecuteVSchema(ctx context.Context, keyspace string, vschemaDDL *sqlparser.AlterVschema) error { srvVschema := vc.vm.GetCurrentSrvVschema() if srvVschema == nil { return vterrors.Errorf(vtrpcpb.Code_INTERNAL, "vschema not loaded") @@ -1270,7 +1367,7 @@ func (vc *vcursorImpl) ExecuteVSchema(ctx context.Context, keyspace string, vsch ksName = keyspace } if ksName == "" { - return errNoKeyspace + return ErrNoKeyspace } ks := srvVschema.Keyspaces[ksName] @@ -1284,43 +1381,43 @@ func (vc *vcursorImpl) ExecuteVSchema(ctx context.Context, keyspace string, vsch return vc.vm.UpdateVSchema(ctx, ksName, srvVschema) } -func (vc *vcursorImpl) MessageStream(ctx context.Context, rss []*srvtopo.ResolvedShard, tableName string, callback func(*sqltypes.Result) error) error { +func (vc *VCursorImpl) MessageStream(ctx context.Context, rss []*srvtopo.ResolvedShard, tableName string, callback func(*sqltypes.Result) error) error { atomic.AddUint64(&vc.logStats.ShardQueries, uint64(len(rss))) return vc.executor.ExecuteMessageStream(ctx, rss, tableName, callback) } -func (vc *vcursorImpl) VStream(ctx context.Context, rss []*srvtopo.ResolvedShard, filter *binlogdatapb.Filter, gtid string, callback func(evs []*binlogdatapb.VEvent) error) error { +func (vc *VCursorImpl) VStream(ctx context.Context, rss []*srvtopo.ResolvedShard, filter *binlogdatapb.Filter, gtid string, callback func(evs []*binlogdatapb.VEvent) error) error { return vc.executor.ExecuteVStream(ctx, rss, filter, gtid, callback) } -func (vc *vcursorImpl) ShowExec(ctx context.Context, command sqlparser.ShowCommandType, filter *sqlparser.ShowFilter) (*sqltypes.Result, error) { +func (vc *VCursorImpl) ShowExec(ctx context.Context, command sqlparser.ShowCommandType, filter *sqlparser.ShowFilter) (*sqltypes.Result, error) { switch command { case sqlparser.VitessReplicationStatus: - return vc.executor.showVitessReplicationStatus(ctx, filter) + return vc.executor.ShowVitessReplicationStatus(ctx, filter) case sqlparser.VitessShards: - return vc.executor.showShards(ctx, filter, vc.tabletType) + return vc.executor.ShowShards(ctx, filter, vc.tabletType) case sqlparser.VitessTablets: - return vc.executor.showTablets(filter) + return vc.executor.ShowTablets(filter) case sqlparser.VitessVariables: - return vc.executor.showVitessMetadata(ctx, filter) + return vc.executor.ShowVitessMetadata(ctx, filter) default: return nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "bug: unexpected show command: %v", command) } } -func (vc *vcursorImpl) GetVSchema() *vindexes.VSchema { +func (vc *VCursorImpl) GetVSchema() *vindexes.VSchema { return vc.vschema } -func (vc *vcursorImpl) GetSrvVschema() *vschemapb.SrvVSchema { +func (vc *VCursorImpl) GetSrvVschema() *vschemapb.SrvVSchema { return vc.vm.GetCurrentSrvVschema() } -func (vc *vcursorImpl) SetExec(ctx context.Context, name string, value string) error { - return vc.executor.setVitessMetadata(ctx, name, value) +func (vc *VCursorImpl) SetExec(ctx context.Context, name string, value string) error { + return vc.executor.SetVitessMetadata(ctx, name, value) } -func (vc *vcursorImpl) ThrottleApp(ctx context.Context, throttledAppRule *topodatapb.ThrottledAppRule) (err error) { +func (vc *VCursorImpl) ThrottleApp(ctx context.Context, throttledAppRule *topodatapb.ThrottledAppRule) (err error) { if throttledAppRule == nil { return vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "ThrottleApp: nil rule") } @@ -1343,14 +1440,12 @@ func (vc *vcursorImpl) ThrottleApp(ctx context.Context, throttledAppRule *topoda throttlerConfig.ThrottledApps = make(map[string]*topodatapb.ThrottledAppRule) } if req.ThrottledApp != nil && req.ThrottledApp.Name != "" { - // TODO(shlomi) in v22: replace the following line with the commented out block - throttlerConfig.ThrottledApps[req.ThrottledApp.Name] = req.ThrottledApp - // timeNow := time.Now() - // if protoutil.TimeFromProto(req.ThrottledApp.ExpiresAt).After(timeNow) { - // throttlerConfig.ThrottledApps[req.ThrottledApp.Name] = req.ThrottledApp - // } else { - // delete(throttlerConfig.ThrottledApps, req.ThrottledApp.Name) - // } + timeNow := time.Now() + if protoutil.TimeFromProto(req.ThrottledApp.ExpiresAt).After(timeNow) { + throttlerConfig.ThrottledApps[req.ThrottledApp.Name] = req.ThrottledApp + } else { + delete(throttlerConfig.ThrottledApps, req.ThrottledApp.Name) + } } return throttlerConfig } @@ -1378,147 +1473,60 @@ func (vc *vcursorImpl) ThrottleApp(ctx context.Context, throttledAppRule *topoda return err } -func (vc *vcursorImpl) CanUseSetVar() bool { - return vc.Environment().Parser().IsMySQL80AndAbove() && setVarEnabled +func (vc *VCursorImpl) CanUseSetVar() bool { + return vc.Environment().Parser().IsMySQL80AndAbove() && vc.config.SetVarEnabled } -func (vc *vcursorImpl) ReleaseLock(ctx context.Context) error { - return vc.executor.ReleaseLock(ctx, vc.safeSession) +func (vc *VCursorImpl) ReleaseLock(ctx context.Context) error { + return vc.executor.ReleaseLock(ctx, vc.SafeSession) } -func (vc *vcursorImpl) cloneWithAutocommitSession() *vcursorImpl { - safeSession := NewAutocommitSession(vc.safeSession.Session) - safeSession.logging = vc.safeSession.logging - return &vcursorImpl{ - safeSession: safeSession, - keyspace: vc.keyspace, - tabletType: vc.tabletType, - destination: vc.destination, - marginComments: vc.marginComments, - executor: vc.executor, - logStats: vc.logStats, - collation: vc.collation, - resolver: vc.resolver, - vschema: vc.vschema, - vm: vc.vm, - topoServer: vc.topoServer, - warnShardedOnly: vc.warnShardedOnly, - pv: vc.pv, - resultsObserver: vc.resultsObserver, - } -} - -func (vc *vcursorImpl) VExplainLogging() { - vc.safeSession.EnableLogging(vc.Environment().Parser()) +func (vc *VCursorImpl) VExplainLogging() { + vc.SafeSession.EnableLogging(vc.Environment().Parser()) } -func (vc *vcursorImpl) GetVExplainLogs() []engine.ExecuteEntry { - return vc.safeSession.logging.GetLogs() +func (vc *VCursorImpl) GetVExplainLogs() []engine.ExecuteEntry { + return vc.SafeSession.GetLogs() } -func (vc *vcursorImpl) FindRoutedShard(keyspace, shard string) (keyspaceName string, err error) { +func (vc *VCursorImpl) FindRoutedShard(keyspace, shard string) (keyspaceName string, err error) { return vc.vschema.FindRoutedShard(keyspace, shard) } -func (vc *vcursorImpl) IsViewsEnabled() bool { - return enableViews -} - -func (vc *vcursorImpl) GetUDV(name string) *querypb.BindVariable { - return vc.safeSession.GetUDV(name) +func (vc *VCursorImpl) IsViewsEnabled() bool { + return vc.config.EnableViews } -func (vc *vcursorImpl) PlanPrepareStatement(ctx context.Context, query string) (*engine.Plan, sqlparser.Statement, error) { - return vc.executor.planPrepareStmt(ctx, vc, query) +func (vc *VCursorImpl) GetUDV(name string) *querypb.BindVariable { + return vc.SafeSession.GetUDV(name) } -func (vc *vcursorImpl) ClearPrepareData(name string) { - delete(vc.safeSession.PrepareStatement, name) +func (vc *VCursorImpl) PlanPrepareStatement(ctx context.Context, query string) (*engine.Plan, sqlparser.Statement, error) { + return vc.executor.PlanPrepareStmt(ctx, vc, query) } -func (vc *vcursorImpl) StorePrepareData(stmtName string, prepareData *vtgatepb.PrepareData) { - vc.safeSession.StorePrepareData(stmtName, prepareData) +func (vc *VCursorImpl) ClearPrepareData(name string) { + delete(vc.SafeSession.PrepareStatement, name) } -func (vc *vcursorImpl) GetPrepareData(stmtName string) *vtgatepb.PrepareData { - return vc.safeSession.GetPrepareData(stmtName) +func (vc *VCursorImpl) StorePrepareData(stmtName string, prepareData *vtgatepb.PrepareData) { + vc.SafeSession.StorePrepareData(stmtName, prepareData) } -func (vc *vcursorImpl) GetWarmingReadsPercent() int { - return vc.warmingReadsPercent +func (vc *VCursorImpl) GetPrepareData(stmtName string) *vtgatepb.PrepareData { + return vc.SafeSession.GetPrepareData(stmtName) } -func (vc *vcursorImpl) GetWarmingReadsChannel() chan bool { - return vc.warmingReadsChannel -} - -func (vc *vcursorImpl) CloneForReplicaWarming(ctx context.Context) engine.VCursor { - callerId := callerid.EffectiveCallerIDFromContext(ctx) - immediateCallerId := callerid.ImmediateCallerIDFromContext(ctx) - - timedCtx, _ := context.WithTimeout(context.Background(), warmingReadsQueryTimeout) // nolint - clonedCtx := callerid.NewContext(timedCtx, callerId, immediateCallerId) - - v := &vcursorImpl{ - safeSession: NewAutocommitSession(vc.safeSession.Session), - keyspace: vc.keyspace, - tabletType: topodatapb.TabletType_REPLICA, - destination: vc.destination, - marginComments: vc.marginComments, - executor: vc.executor, - resolver: vc.resolver, - topoServer: vc.topoServer, - logStats: &logstats.LogStats{Ctx: clonedCtx}, - collation: vc.collation, - ignoreMaxMemoryRows: vc.ignoreMaxMemoryRows, - vschema: vc.vschema, - vm: vc.vm, - semTable: vc.semTable, - warnShardedOnly: vc.warnShardedOnly, - warnings: vc.warnings, - pv: vc.pv, - resultsObserver: nullResultsObserver{}, - } - - v.marginComments.Trailing += "/* warming read */" - - return v +func (vc *VCursorImpl) GetWarmingReadsPercent() int { + return vc.config.WarmingReadsPercent } -func (vc *vcursorImpl) CloneForMirroring(ctx context.Context) engine.VCursor { - callerId := callerid.EffectiveCallerIDFromContext(ctx) - immediateCallerId := callerid.ImmediateCallerIDFromContext(ctx) - - clonedCtx := callerid.NewContext(ctx, callerId, immediateCallerId) - - v := &vcursorImpl{ - safeSession: NewAutocommitSession(vc.safeSession.Session), - keyspace: vc.keyspace, - tabletType: vc.tabletType, - destination: vc.destination, - marginComments: vc.marginComments, - executor: vc.executor, - resolver: vc.resolver, - topoServer: vc.topoServer, - logStats: &logstats.LogStats{Ctx: clonedCtx}, - collation: vc.collation, - ignoreMaxMemoryRows: vc.ignoreMaxMemoryRows, - vschema: vc.vschema, - vm: vc.vm, - semTable: vc.semTable, - warnShardedOnly: vc.warnShardedOnly, - warnings: vc.warnings, - pv: vc.pv, - resultsObserver: nullResultsObserver{}, - } - - v.marginComments.Trailing += "/* mirror query */" - - return v +func (vc *VCursorImpl) GetWarmingReadsChannel() chan bool { + return vc.config.WarmingReadsChannel } // UpdateForeignKeyChecksState updates the foreign key checks state of the vcursor. -func (vc *vcursorImpl) UpdateForeignKeyChecksState(fkStateFromQuery *bool) { +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, @@ -1528,17 +1536,36 @@ func (vc *vcursorImpl) UpdateForeignKeyChecksState(fkStateFromQuery *bool) { return } // If the query doesn't have anything, then we consult the session state. - vc.fkChecksState = vc.safeSession.ForeignKeyChecks() + vc.fkChecksState = vc.SafeSession.ForeignKeyChecks() } // GetForeignKeyChecksState gets the stored foreign key checks state in the vcursor. -func (vc *vcursorImpl) GetForeignKeyChecksState() *bool { +func (vc *VCursorImpl) GetForeignKeyChecksState() *bool { return vc.fkChecksState } // RecordMirrorStats is used to record stats about a mirror query. -func (vc *vcursorImpl) RecordMirrorStats(sourceExecTime, targetExecTime time.Duration, targetErr error) { +func (vc *VCursorImpl) RecordMirrorStats(sourceExecTime, targetExecTime time.Duration, targetErr error) { vc.logStats.MirrorSourceExecuteTime = sourceExecTime vc.logStats.MirrorTargetExecuteTime = targetExecTime vc.logStats.MirrorTargetError = targetErr } + +func (vc *VCursorImpl) GetMarginComments() sqlparser.MarginComments { + return vc.marginComments +} + +func (vc *VCursorImpl) CachePlan() bool { + return vc.SafeSession.CachePlan() +} + +func (vc *VCursorImpl) GetContextWithTimeOut(ctx context.Context) (context.Context, context.CancelFunc) { + if vc.queryTimeout == 0 { + return ctx, func() {} + } + return context.WithTimeout(ctx, vc.queryTimeout) +} + +func (vc *VCursorImpl) IgnoreMaxMemoryRows() bool { + return vc.ignoreMaxMemoryRows +} diff --git a/go/vt/vtgate/vcursor_impl_test.go b/go/vt/vtgate/executorcontext/vcursor_impl_test.go similarity index 60% rename from go/vt/vtgate/vcursor_impl_test.go rename to go/vt/vtgate/executorcontext/vcursor_impl_test.go index 95d9a18078d..16d2c03bf1c 100644 --- a/go/vt/vtgate/vcursor_impl_test.go +++ b/go/vt/vtgate/executorcontext/vcursor_impl_test.go @@ -1,8 +1,23 @@ -package vtgate +/* +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 executorcontext import ( "context" - "encoding/hex" "errors" "fmt" "strconv" @@ -12,10 +27,16 @@ import ( "github.com/stretchr/testify/require" + "vitess.io/vitess/go/mysql/collations" + "vitess.io/vitess/go/sqltypes" + binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" + "vitess.io/vitess/go/vt/vtenv" + "vitess.io/vitess/go/vt/vtgate/engine" + "vitess.io/vitess/go/vt/vtgate/vtgateservice" + "vitess.io/vitess/go/vt/key" "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/srvtopo" - "vitess.io/vitess/go/vt/topo" "vitess.io/vitess/go/vt/vtgate/logstats" "vitess.io/vitess/go/vt/vtgate/vindexes" @@ -39,48 +60,6 @@ func (f fakeVSchemaOperator) UpdateVSchema(ctx context.Context, ksName string, v panic("implement me") } -type fakeTopoServer struct{} - -// GetTopoServer returns the full topo.Server instance. -func (f *fakeTopoServer) GetTopoServer() (*topo.Server, error) { - return nil, nil -} - -// GetSrvKeyspaceNames returns the list of keyspaces served in -// the provided cell. -func (f *fakeTopoServer) GetSrvKeyspaceNames(ctx context.Context, cell string, staleOK bool) ([]string, error) { - return []string{"ks1"}, nil -} - -// GetSrvKeyspace returns the SrvKeyspace for a cell/keyspace. -func (f *fakeTopoServer) GetSrvKeyspace(ctx context.Context, cell, keyspace string) (*topodatapb.SrvKeyspace, error) { - zeroHexBytes, _ := hex.DecodeString("") - eightyHexBytes, _ := hex.DecodeString("80") - ks := &topodatapb.SrvKeyspace{ - Partitions: []*topodatapb.SrvKeyspace_KeyspacePartition{ - { - ServedType: topodatapb.TabletType_PRIMARY, - ShardReferences: []*topodatapb.ShardReference{ - {Name: "-80", KeyRange: &topodatapb.KeyRange{Start: zeroHexBytes, End: eightyHexBytes}}, - {Name: "80-", KeyRange: &topodatapb.KeyRange{Start: eightyHexBytes, End: zeroHexBytes}}, - }, - }, - }, - } - return ks, nil -} - -func (f *fakeTopoServer) WatchSrvKeyspace(ctx context.Context, cell, keyspace string, callback func(*topodatapb.SrvKeyspace, error) bool) { - ks, err := f.GetSrvKeyspace(ctx, cell, keyspace) - callback(ks, err) -} - -// WatchSrvVSchema starts watching the SrvVSchema object for -// the provided cell. It will call the callback when -// a new value or an error occurs. -func (f *fakeTopoServer) WatchSrvVSchema(ctx context.Context, cell string, callback func(*vschemapb.SrvVSchema, error) bool) { -} - func TestDestinationKeyspace(t *testing.T) { ks1 := &vindexes.Keyspace{ Name: "ks1", @@ -184,13 +163,17 @@ func TestDestinationKeyspace(t *testing.T) { }, { vschema: vschemaWith2KS, targetString: "", - expectedError: errNoKeyspace.Error(), + 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{}, r, nil, &fakeVSchemaOperator{vschema: tc.vschema}, tc.vschema, nil, nil, false, querypb.ExecuteOptions_Gen4) + session := NewSafeSession(&vtgatepb.Session{TargetString: tc.targetString}) + impl, _ := NewVCursorImpl(session, sqlparser.MarginComments{}, nil, nil, + &fakeVSchemaOperator{vschema: tc.vschema}, tc.vschema, nil, nil, + fakeObserver{}, VCursorConfig{ + DefaultTabletType: topodatapb.TabletType_PRIMARY, + }) impl.vschema = tc.vschema dest, keyspace, tabletType, err := impl.TargetDestination(tc.qualifier) if tc.expectedError == "" { @@ -250,15 +233,15 @@ 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{}, r, nil, &fakeVSchemaOperator{vschema: tc.vschema}, tc.vschema, nil, nil, false, querypb.ExecuteOptions_Gen4) + cfg := VCursorConfig{DefaultTabletType: topodatapb.TabletType_PRIMARY} + vc, _ := NewVCursorImpl(NewSafeSession(&vtgatepb.Session{InTransaction: true}), sqlparser.MarginComments{}, nil, nil, &fakeVSchemaOperator{vschema: tc.vschema}, tc.vschema, nil, nil, fakeObserver{}, cfg) vc.vschema = tc.vschema err := vc.SetTarget(tc.targetString) if tc.expectedError == "" { require.NoError(t, err) - require.Equal(t, vc.safeSession.TargetString, tc.targetString) + require.Equal(t, vc.SafeSession.TargetString, tc.targetString) } else { require.EqualError(t, err, tc.expectedError) } @@ -299,17 +282,20 @@ func TestKeyForPlan(t *testing.T) { 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{}, r, nil, &fakeVSchemaOperator{vschema: tc.vschema}, tc.vschema, srvtopo.NewResolver(&fakeTopoServer{}, nil, ""), nil, false, querypb.ExecuteOptions_Gen4) + cfg := VCursorConfig{ + Collation: collations.CollationUtf8mb4ID, + DefaultTabletType: topodatapb.TabletType_PRIMARY, + } + vc, err := NewVCursorImpl(ss, sqlparser.MarginComments{}, &fakeExecutor{}, nil, &fakeVSchemaOperator{vschema: tc.vschema}, tc.vschema, srvtopo.NewResolver(&FakeTopoServer{}, nil, ""), nil, fakeObserver{}, cfg) require.NoError(t, err) vc.vschema = tc.vschema var buf strings.Builder - vc.keyForPlan(context.Background(), "SELECT 1", &buf) + vc.KeyForPlan(context.Background(), "SELECT 1", &buf) require.Equal(t, tc.expectedPlanPrefixKey, buf.String()) }) } @@ -327,8 +313,7 @@ func TestFirstSortedKeyspace(t *testing.T) { }, } - 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) + vc, err := NewVCursorImpl(NewSafeSession(nil), sqlparser.MarginComments{}, nil, nil, &fakeVSchemaOperator{vschema: vschemaWith2KS}, vschemaWith2KS, srvtopo.NewResolver(&FakeTopoServer{}, nil, ""), nil, fakeObserver{}, VCursorConfig{}) require.NoError(t, err) ks, err := vc.FirstSortedKeyspace() require.NoError(t, err) @@ -338,13 +323,13 @@ func TestFirstSortedKeyspace(t *testing.T) { // TestSetExecQueryTimeout tests the SetExecQueryTimeout method. // Validates the timeout value is set based on override rule. func TestSetExecQueryTimeout(t *testing.T) { - executor, _, _, _, _ := createExecutorEnv(t) safeSession := NewSafeSession(nil) - vc, err := newVCursorImpl(safeSession, sqlparser.MarginComments{}, executor, nil, nil, &vindexes.VSchema{}, nil, nil, false, querypb.ExecuteOptions_Gen4) + vc, err := NewVCursorImpl(safeSession, sqlparser.MarginComments{}, nil, nil, nil, &vindexes.VSchema{}, nil, nil, fakeObserver{}, VCursorConfig{ + // flag timeout + QueryTimeout: 20, + }) require.NoError(t, err) - // flag timeout - queryTimeout = 20 vc.SetExecQueryTimeout(nil) require.Equal(t, 20*time.Millisecond, vc.queryTimeout) require.NotNil(t, safeSession.Options.Timeout) @@ -371,8 +356,8 @@ func TestSetExecQueryTimeout(t *testing.T) { require.NotNil(t, safeSession.Options.Timeout) require.EqualValues(t, 0, safeSession.Options.GetAuthoritativeTimeout()) - // reset - queryTimeout = 0 + // reset flag timeout + vc.config.QueryTimeout = 0 safeSession.SetQueryTimeout(0) vc.SetExecQueryTimeout(nil) require.Equal(t, 0*time.Millisecond, vc.queryTimeout) @@ -381,10 +366,9 @@ func TestSetExecQueryTimeout(t *testing.T) { } func TestRecordMirrorStats(t *testing.T) { - executor, _, _, _, _ := createExecutorEnv(t) safeSession := NewSafeSession(nil) logStats := logstats.NewLogStats(context.Background(), t.Name(), "select 1", "", nil) - vc, err := newVCursorImpl(safeSession, sqlparser.MarginComments{}, executor, logStats, nil, &vindexes.VSchema{}, nil, nil, false, querypb.ExecuteOptions_Gen4) + vc, err := NewVCursorImpl(safeSession, sqlparser.MarginComments{}, nil, logStats, nil, &vindexes.VSchema{}, nil, nil, fakeObserver{}, VCursorConfig{}) require.NoError(t, err) require.Zero(t, logStats.MirrorSourceExecuteTime) @@ -397,3 +381,113 @@ func TestRecordMirrorStats(t *testing.T) { require.Equal(t, 20*time.Millisecond, logStats.MirrorTargetExecuteTime) require.ErrorContains(t, logStats.MirrorTargetError, "test error") } + +type fakeExecutor struct{} + +func (f fakeExecutor) Execute(ctx context.Context, mysqlCtx vtgateservice.MySQLConnection, method string, session *SafeSession, s string, vars map[string]*querypb.BindVariable) (*sqltypes.Result, error) { + // TODO implement me + panic("implement me") +} + +func (f fakeExecutor) ExecuteMultiShard(ctx context.Context, primitive engine.Primitive, rss []*srvtopo.ResolvedShard, queries []*querypb.BoundQuery, session *SafeSession, autocommit bool, ignoreMaxMemoryRows bool, resultsObserver ResultsObserver) (qr *sqltypes.Result, errs []error) { + // TODO implement me + panic("implement me") +} + +func (f fakeExecutor) StreamExecuteMulti(ctx context.Context, primitive engine.Primitive, query string, rss []*srvtopo.ResolvedShard, vars []map[string]*querypb.BindVariable, session *SafeSession, autocommit bool, callback func(reply *sqltypes.Result) error, observer ResultsObserver) []error { + // TODO implement me + panic("implement me") +} + +func (f fakeExecutor) ExecuteLock(ctx context.Context, rs *srvtopo.ResolvedShard, query *querypb.BoundQuery, session *SafeSession, lockFuncType sqlparser.LockingFuncType) (*sqltypes.Result, error) { + // TODO implement me + panic("implement me") +} + +func (f fakeExecutor) Commit(ctx context.Context, safeSession *SafeSession) error { + // TODO implement me + panic("implement me") +} + +func (f fakeExecutor) ExecuteMessageStream(ctx context.Context, rss []*srvtopo.ResolvedShard, name string, callback func(*sqltypes.Result) error) error { + // TODO implement me + panic("implement me") +} + +func (f fakeExecutor) ExecuteVStream(ctx context.Context, rss []*srvtopo.ResolvedShard, filter *binlogdatapb.Filter, gtid string, callback func(evs []*binlogdatapb.VEvent) error) error { + // TODO implement me + panic("implement me") +} + +func (f fakeExecutor) ReleaseLock(ctx context.Context, session *SafeSession) error { + // TODO implement me + panic("implement me") +} + +func (f fakeExecutor) ShowVitessReplicationStatus(ctx context.Context, filter *sqlparser.ShowFilter) (*sqltypes.Result, error) { + // TODO implement me + panic("implement me") +} + +func (f fakeExecutor) ShowShards(ctx context.Context, filter *sqlparser.ShowFilter, destTabletType topodatapb.TabletType) (*sqltypes.Result, error) { + // TODO implement me + panic("implement me") +} + +func (f fakeExecutor) ShowTablets(filter *sqlparser.ShowFilter) (*sqltypes.Result, error) { + // TODO implement me + panic("implement me") +} + +func (f fakeExecutor) ShowVitessMetadata(ctx context.Context, filter *sqlparser.ShowFilter) (*sqltypes.Result, error) { + // TODO implement me + panic("implement me") +} + +func (f fakeExecutor) SetVitessMetadata(ctx context.Context, name, value string) error { + // TODO implement me + panic("implement me") +} + +func (f fakeExecutor) ParseDestinationTarget(targetString string) (string, topodatapb.TabletType, key.Destination, error) { + // TODO implement me + panic("implement me") +} + +func (f fakeExecutor) VSchema() *vindexes.VSchema { + // TODO implement me + panic("implement me") +} + +func (f fakeExecutor) PlanPrepareStmt(ctx context.Context, vcursor *VCursorImpl, query string) (*engine.Plan, sqlparser.Statement, error) { + // TODO implement me + panic("implement me") +} + +func (f fakeExecutor) Environment() *vtenv.Environment { + return vtenv.NewTestEnv() +} + +func (f fakeExecutor) ReadTransaction(ctx context.Context, transactionID string) (*querypb.TransactionMetadata, error) { + // TODO implement me + panic("implement me") +} + +func (f fakeExecutor) UnresolvedTransactions(ctx context.Context, targets []*querypb.Target) ([]*querypb.TransactionMetadata, error) { + // TODO implement me + panic("implement me") +} + +func (f fakeExecutor) AddWarningCount(name string, value int64) { + // TODO implement me + panic("implement me") +} + +var _ iExecute = (*fakeExecutor)(nil) + +type fakeObserver struct{} + +func (f fakeObserver) Observe(*sqltypes.Result) { +} + +var _ ResultsObserver = (*fakeObserver)(nil) diff --git a/go/vt/vtgate/legacy_scatter_conn_test.go b/go/vt/vtgate/legacy_scatter_conn_test.go index 4512fc0724e..0d49e7b7bd9 100644 --- a/go/vt/vtgate/legacy_scatter_conn_test.go +++ b/go/vt/vtgate/legacy_scatter_conn_test.go @@ -26,6 +26,8 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + econtext "vitess.io/vitess/go/vt/vtgate/executorcontext" + "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/sqltypes" @@ -99,7 +101,7 @@ func TestLegacyExecuteFailOnAutocommit(t *testing.T) { }, Autocommit: false, } - _, errs := sc.ExecuteMultiShard(ctx, nil, rss, queries, NewSafeSession(session), true /*autocommit*/, false, nullResultsObserver{}) + _, errs := sc.ExecuteMultiShard(ctx, nil, rss, queries, econtext.NewSafeSession(session), true /*autocommit*/, false, nullResultsObserver{}) err := vterrors.Aggregate(errs) require.Error(t, err) require.Contains(t, err.Error(), "in autocommit mode, transactionID should be zero but was: 123") @@ -123,7 +125,7 @@ func TestScatterConnExecuteMulti(t *testing.T) { } } - qr, errs := sc.ExecuteMultiShard(ctx, nil, rss, queries, NewSafeSession(nil), false /*autocommit*/, false, nullResultsObserver{}) + qr, errs := sc.ExecuteMultiShard(ctx, nil, rss, queries, econtext.NewSafeSession(nil), false /*autocommit*/, false, nullResultsObserver{}) return qr, vterrors.Aggregate(errs) }) } @@ -138,7 +140,7 @@ func TestScatterConnStreamExecuteMulti(t *testing.T) { bvs := make([]map[string]*querypb.BindVariable, len(rss)) qr := new(sqltypes.Result) var mu sync.Mutex - errors := sc.StreamExecuteMulti(ctx, nil, "query", rss, bvs, NewSafeSession(&vtgatepb.Session{InTransaction: true}), true /* autocommit */, func(r *sqltypes.Result) error { + errors := sc.StreamExecuteMulti(ctx, nil, "query", rss, bvs, econtext.NewSafeSession(&vtgatepb.Session{InTransaction: true}), true /* autocommit */, func(r *sqltypes.Result) error { mu.Lock() defer mu.Unlock() qr.AppendResult(r) @@ -280,7 +282,7 @@ func TestMaxMemoryRows(t *testing.T) { []key.Destination{key.DestinationShard("0"), key.DestinationShard("1")}) require.NoError(t, err) - session := NewSafeSession(&vtgatepb.Session{InTransaction: true}) + session := econtext.NewSafeSession(&vtgatepb.Session{InTransaction: true}) queries := []*querypb.BoundQuery{{ Sql: "query1", BindVariables: map[string]*querypb.BindVariable{}, @@ -328,7 +330,7 @@ func TestLegaceHealthCheckFailsOnReservedConnections(t *testing.T) { res := srvtopo.NewResolver(newSandboxForCells(ctx, []string{"aa"}), sc.gateway, "aa") - session := NewSafeSession(&vtgatepb.Session{InTransaction: false, InReservedConn: true}) + session := econtext.NewSafeSession(&vtgatepb.Session{InTransaction: false, InReservedConn: true}) destinations := []key.Destination{key.DestinationShard("0")} rss, _, err := res.ResolveDestinations(ctx, keyspace, topodatapb.TabletType_REPLICA, nil, destinations) require.NoError(t, err) @@ -346,12 +348,12 @@ func TestLegaceHealthCheckFailsOnReservedConnections(t *testing.T) { require.Error(t, vterrors.Aggregate(errs)) } -func executeOnShards(t *testing.T, ctx context.Context, res *srvtopo.Resolver, keyspace string, sc *ScatterConn, session *SafeSession, destinations []key.Destination) { +func executeOnShards(t *testing.T, ctx context.Context, res *srvtopo.Resolver, keyspace string, sc *ScatterConn, session *econtext.SafeSession, destinations []key.Destination) { t.Helper() require.Empty(t, executeOnShardsReturnsErr(t, ctx, res, keyspace, sc, session, destinations)) } -func executeOnShardsReturnsErr(t *testing.T, ctx context.Context, res *srvtopo.Resolver, keyspace string, sc *ScatterConn, session *SafeSession, destinations []key.Destination) error { +func executeOnShardsReturnsErr(t *testing.T, ctx context.Context, res *srvtopo.Resolver, keyspace string, sc *ScatterConn, session *econtext.SafeSession, destinations []key.Destination) error { t.Helper() rss, _, err := res.ResolveDestinations(ctx, keyspace, topodatapb.TabletType_REPLICA, nil, destinations) require.NoError(t, err) @@ -374,7 +376,7 @@ type recordingResultsObserver struct { recorded []*sqltypes.Result } -func (o *recordingResultsObserver) observe(result *sqltypes.Result) { +func (o *recordingResultsObserver) Observe(result *sqltypes.Result) { mu.Lock() o.recorded = append(o.recorded, result) mu.Unlock() @@ -429,7 +431,7 @@ func TestMultiExecs(t *testing.T) { observer := recordingResultsObserver{} - session := NewSafeSession(&vtgatepb.Session{}) + session := econtext.NewSafeSession(&vtgatepb.Session{}) _, err := sc.ExecuteMultiShard(ctx, nil, rss, queries, session, false, false, &observer) require.NoError(t, vterrors.Aggregate(err)) if len(sbc0.Queries) == 0 || len(sbc1.Queries) == 0 { @@ -511,7 +513,7 @@ func TestScatterConnSingleDB(t *testing.T) { want := "multi-db transaction attempted" // TransactionMode_SINGLE in session - session := NewSafeSession(&vtgatepb.Session{InTransaction: true, TransactionMode: vtgatepb.TransactionMode_SINGLE}) + session := econtext.NewSafeSession(&vtgatepb.Session{InTransaction: true, TransactionMode: vtgatepb.TransactionMode_SINGLE}) queries := []*querypb.BoundQuery{{Sql: "query1"}} _, errors := sc.ExecuteMultiShard(ctx, nil, rss0, queries, session, false, false, nullResultsObserver{}) require.Empty(t, errors) @@ -521,7 +523,7 @@ func TestScatterConnSingleDB(t *testing.T) { // TransactionMode_SINGLE in txconn sc.txConn.mode = vtgatepb.TransactionMode_SINGLE - session = NewSafeSession(&vtgatepb.Session{InTransaction: true}) + session = econtext.NewSafeSession(&vtgatepb.Session{InTransaction: true}) _, errors = sc.ExecuteMultiShard(ctx, nil, rss0, queries, session, false, false, nullResultsObserver{}) require.Empty(t, errors) _, errors = sc.ExecuteMultiShard(ctx, nil, rss1, queries, session, false, false, nullResultsObserver{}) @@ -530,7 +532,7 @@ func TestScatterConnSingleDB(t *testing.T) { // TransactionMode_MULTI in txconn. Should not fail. sc.txConn.mode = vtgatepb.TransactionMode_MULTI - session = NewSafeSession(&vtgatepb.Session{InTransaction: true}) + session = econtext.NewSafeSession(&vtgatepb.Session{InTransaction: true}) _, errors = sc.ExecuteMultiShard(ctx, nil, rss0, queries, session, false, false, nullResultsObserver{}) require.Empty(t, errors) _, errors = sc.ExecuteMultiShard(ctx, nil, rss1, queries, session, false, false, nullResultsObserver{}) @@ -601,7 +603,7 @@ func TestReservePrequeries(t *testing.T) { res := srvtopo.NewResolver(newSandboxForCells(ctx, []string{"aa"}), sc.gateway, "aa") - session := NewSafeSession(&vtgatepb.Session{ + session := econtext.NewSafeSession(&vtgatepb.Session{ InTransaction: false, InReservedConn: true, SystemVariables: map[string]string{ diff --git a/go/vt/vtgate/plan_execute.go b/go/vt/vtgate/plan_execute.go index 1c0915470ef..db7923c09f0 100644 --- a/go/vt/vtgate/plan_execute.go +++ b/go/vt/vtgate/plan_execute.go @@ -29,11 +29,12 @@ import ( "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vtgate/engine" + econtext "vitess.io/vitess/go/vt/vtgate/executorcontext" "vitess.io/vitess/go/vt/vtgate/logstats" "vitess.io/vitess/go/vt/vtgate/vtgateservice" ) -type planExec func(ctx context.Context, plan *engine.Plan, vc *vcursorImpl, bindVars map[string]*querypb.BindVariable, startTime time.Time) error +type planExec func(ctx context.Context, plan *engine.Plan, vc *econtext.VCursorImpl, bindVars map[string]*querypb.BindVariable, startTime time.Time) error type txResult func(sqlparser.StatementType, *sqltypes.Result) error var vschemaWaitTimeout = 30 * time.Second @@ -56,10 +57,12 @@ func waitForNewerVSchema(ctx context.Context, e *Executor, lastVSchemaCreated ti } } +const MaxBufferingRetries = 3 + func (e *Executor) newExecute( ctx context.Context, mysqlCtx vtgateservice.MySQLConnection, - safeSession *SafeSession, + safeSession *econtext.SafeSession, sql string, bindVars map[string]*querypb.BindVariable, logStats *logstats.LogStats, @@ -116,7 +119,7 @@ func (e *Executor) newExecute( } } - vcursor, err := newVCursorImpl(safeSession, comments, e, logStats, e.vm, vs, e.resolver.resolver, e.serv, e.warnShardedOnly, e.pv) + vcursor, err := econtext.NewVCursorImpl(safeSession, comments, e, logStats, e.vm, vs, e.resolver.resolver, e.serv, nullResultsObserver{}, e.vConfig) if err != nil { return err } @@ -146,10 +149,8 @@ func (e *Executor) newExecute( } // set the overall query timeout if it is not already set - if vcursor.queryTimeout > 0 && cancel == nil { - ctx, cancel = context.WithTimeout(ctx, vcursor.queryTimeout) - defer cancel() - } + ctx, cancel = vcursor.GetContextWithTimeOut(ctx) + defer cancel() result, err = e.handleTransactions(ctx, mysqlCtx, safeSession, plan, logStats, vcursor, stmt) if err != nil { @@ -225,10 +226,10 @@ func (e *Executor) newExecute( func (e *Executor) handleTransactions( ctx context.Context, mysqlCtx vtgateservice.MySQLConnection, - safeSession *SafeSession, + safeSession *econtext.SafeSession, plan *engine.Plan, logStats *logstats.LogStats, - vcursor *vcursorImpl, + vcursor *econtext.VCursorImpl, stmt sqlparser.Statement, ) (*sqltypes.Result, error) { // We need to explicitly handle errors, and begin/commit/rollback, since these control transactions. Everything else @@ -247,19 +248,19 @@ func (e *Executor) handleTransactions( qr, err := e.handleSavepoint(ctx, safeSession, plan.Original, "Savepoint", logStats, func(_ string) (*sqltypes.Result, error) { // Safely to ignore as there is no transaction. return &sqltypes.Result{}, nil - }, vcursor.ignoreMaxMemoryRows) + }, vcursor.IgnoreMaxMemoryRows()) return qr, err case sqlparser.StmtSRollback: qr, err := e.handleSavepoint(ctx, safeSession, plan.Original, "Rollback Savepoint", logStats, func(query string) (*sqltypes.Result, error) { // Error as there is no transaction, so there is no savepoint that exists. return nil, vterrors.NewErrorf(vtrpcpb.Code_NOT_FOUND, vterrors.SPDoesNotExist, "SAVEPOINT does not exist: %s", query) - }, vcursor.ignoreMaxMemoryRows) + }, vcursor.IgnoreMaxMemoryRows()) return qr, err case sqlparser.StmtRelease: qr, err := e.handleSavepoint(ctx, safeSession, plan.Original, "Release Savepoint", logStats, func(query string) (*sqltypes.Result, error) { // Error as there is no transaction, so there is no savepoint that exists. return nil, vterrors.NewErrorf(vtrpcpb.Code_NOT_FOUND, vterrors.SPDoesNotExist, "SAVEPOINT does not exist: %s", query) - }, vcursor.ignoreMaxMemoryRows) + }, vcursor.IgnoreMaxMemoryRows()) return qr, err case sqlparser.StmtKill: return e.handleKill(ctx, mysqlCtx, stmt, logStats) @@ -267,7 +268,7 @@ func (e *Executor) handleTransactions( return nil, nil } -func (e *Executor) startTxIfNecessary(ctx context.Context, safeSession *SafeSession) error { +func (e *Executor) startTxIfNecessary(ctx context.Context, safeSession *econtext.SafeSession) error { if !safeSession.Autocommit && !safeSession.InTransaction() { if err := e.txConn.Begin(ctx, safeSession, nil); err != nil { return err @@ -276,7 +277,7 @@ func (e *Executor) startTxIfNecessary(ctx context.Context, safeSession *SafeSess return nil } -func (e *Executor) insideTransaction(ctx context.Context, safeSession *SafeSession, logStats *logstats.LogStats, execPlan func() error) error { +func (e *Executor) insideTransaction(ctx context.Context, safeSession *econtext.SafeSession, logStats *logstats.LogStats, execPlan func() error) error { mustCommit := false if safeSession.Autocommit && !safeSession.InTransaction() { mustCommit = true @@ -320,9 +321,9 @@ func (e *Executor) insideTransaction(ctx context.Context, safeSession *SafeSessi func (e *Executor) executePlan( ctx context.Context, - safeSession *SafeSession, + safeSession *econtext.SafeSession, plan *engine.Plan, - vcursor *vcursorImpl, + vcursor *econtext.VCursorImpl, bindVars map[string]*querypb.BindVariable, logStats *logstats.LogStats, execStart time.Time, @@ -342,7 +343,7 @@ func (e *Executor) executePlan( } // rollbackExecIfNeeded rollbacks the partial execution if earlier it was detected that it needs partial query execution to be rolled back. -func (e *Executor) rollbackExecIfNeeded(ctx context.Context, safeSession *SafeSession, bindVars map[string]*querypb.BindVariable, logStats *logstats.LogStats, err error) error { +func (e *Executor) rollbackExecIfNeeded(ctx context.Context, safeSession *econtext.SafeSession, bindVars map[string]*querypb.BindVariable, logStats *logstats.LogStats, err error) error { if safeSession.InTransaction() && safeSession.IsRollbackSet() { rErr := e.rollbackPartialExec(ctx, safeSession, bindVars, logStats) return vterrors.Wrap(err, rErr.Error()) @@ -353,7 +354,7 @@ func (e *Executor) rollbackExecIfNeeded(ctx context.Context, safeSession *SafeSe // rollbackPartialExec rollbacks to the savepoint or rollbacks transaction based on the value set on SafeSession.rollbackOnPartialExec. // Once, it is used the variable is reset. // If it fails to rollback to the previous savepoint then, the transaction is forced to be rolled back. -func (e *Executor) rollbackPartialExec(ctx context.Context, safeSession *SafeSession, bindVars map[string]*querypb.BindVariable, logStats *logstats.LogStats) error { +func (e *Executor) rollbackPartialExec(ctx context.Context, safeSession *econtext.SafeSession, bindVars map[string]*querypb.BindVariable, logStats *logstats.LogStats) error { var err error var errMsg strings.Builder @@ -367,8 +368,8 @@ func (e *Executor) rollbackPartialExec(ctx context.Context, safeSession *SafeSes } // needs to rollback only once. - rQuery := safeSession.rollbackOnPartialExec - if rQuery != txRollback { + rQuery := safeSession.GetRollbackOnPartialExec() + if rQuery != econtext.TxRollback { safeSession.SavepointRollback() _, _, err = e.execute(ctx, nil, safeSession, rQuery, bindVars, logStats) // If no error, the revert is successful with the savepoint. Notify the reason as error to the client. @@ -388,9 +389,9 @@ func (e *Executor) rollbackPartialExec(ctx context.Context, safeSession *SafeSes return vterrors.New(vtrpcpb.Code_ABORTED, errMsg.String()) } -func (e *Executor) setLogStats(logStats *logstats.LogStats, plan *engine.Plan, vcursor *vcursorImpl, execStart time.Time, err error, qr *sqltypes.Result) { +func (e *Executor) setLogStats(logStats *logstats.LogStats, plan *engine.Plan, vcursor *econtext.VCursorImpl, execStart time.Time, err error, qr *sqltypes.Result) { logStats.StmtType = plan.Type.String() - logStats.ActiveKeyspace = vcursor.keyspace + logStats.ActiveKeyspace = vcursor.GetKeyspace() logStats.TablesUsed = plan.TablesUsed logStats.TabletType = vcursor.TabletType().String() errCount := e.logExecutionEnd(logStats, execStart, plan, err, qr) diff --git a/go/vt/vtgate/planbuilder/builder.go b/go/vt/vtgate/planbuilder/builder.go index 27b994b1730..ca4ccb7ac5a 100644 --- a/go/vt/vtgate/planbuilder/builder.go +++ b/go/vt/vtgate/planbuilder/builder.go @@ -28,6 +28,7 @@ import ( 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/dynamicconfig" "vitess.io/vitess/go/vt/vtgate/engine" "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" "vitess.io/vitess/go/vt/vtgate/vindexes" @@ -63,6 +64,16 @@ func singleTable(ks, tbl string) string { return fmt.Sprintf("%s.%s", ks, tbl) } +type staticConfig struct{} + +func (staticConfig) OnlineEnabled() bool { + return true +} + +func (staticConfig) DirectEnabled() bool { + return true +} + // 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) { @@ -92,12 +103,12 @@ func TestBuilder(query string, vschema plancontext.VSchema, keyspace string) (*e } reservedVars := sqlparser.NewReservedVars("vtg", reserved) - return BuildFromStmt(context.Background(), query, result.AST, reservedVars, vschema, result.BindVarNeeds, true, true) + return BuildFromStmt(context.Background(), query, result.AST, reservedVars, vschema, result.BindVarNeeds, staticConfig{}) } // BuildFromStmt builds a plan based on the AST provided. -func BuildFromStmt(ctx context.Context, query string, stmt sqlparser.Statement, reservedVars *sqlparser.ReservedVars, vschema plancontext.VSchema, bindVarNeeds *sqlparser.BindVarNeeds, enableOnlineDDL, enableDirectDDL bool) (*engine.Plan, error) { - planResult, err := createInstructionFor(ctx, query, stmt, reservedVars, vschema, enableOnlineDDL, enableDirectDDL) +func BuildFromStmt(ctx context.Context, query string, stmt sqlparser.Statement, reservedVars *sqlparser.ReservedVars, vschema plancontext.VSchema, bindVarNeeds *sqlparser.BindVarNeeds, cfg dynamicconfig.DDL) (*engine.Plan, error) { + planResult, err := createInstructionFor(ctx, query, stmt, reservedVars, vschema, cfg) if err != nil { return nil, err } @@ -154,7 +165,7 @@ func buildRoutePlan(stmt sqlparser.Statement, reservedVars *sqlparser.ReservedVa return f(stmt, reservedVars, vschema) } -func createInstructionFor(ctx context.Context, query string, stmt sqlparser.Statement, reservedVars *sqlparser.ReservedVars, vschema plancontext.VSchema, enableOnlineDDL, enableDirectDDL bool) (*planResult, error) { +func createInstructionFor(ctx context.Context, query string, stmt sqlparser.Statement, reservedVars *sqlparser.ReservedVars, vschema plancontext.VSchema, cfg dynamicconfig.DDL) (*planResult, error) { switch stmt := stmt.(type) { case *sqlparser.Select, *sqlparser.Insert, *sqlparser.Update, *sqlparser.Delete: configuredPlanner, err := getConfiguredPlanner(vschema, stmt, query) @@ -169,13 +180,13 @@ func createInstructionFor(ctx context.Context, query string, stmt sqlparser.Stat } return buildRoutePlan(stmt, reservedVars, vschema, configuredPlanner) case sqlparser.DDLStatement: - return buildGeneralDDLPlan(ctx, query, stmt, reservedVars, vschema, enableOnlineDDL, enableDirectDDL) + return buildGeneralDDLPlan(ctx, query, stmt, reservedVars, vschema, cfg) case *sqlparser.AlterMigration: - return buildAlterMigrationPlan(query, stmt, vschema, enableOnlineDDL) + return buildAlterMigrationPlan(query, stmt, vschema, cfg) case *sqlparser.RevertMigration: - return buildRevertMigrationPlan(query, stmt, vschema, enableOnlineDDL) + return buildRevertMigrationPlan(query, stmt, vschema, cfg) case *sqlparser.ShowMigrationLogs: - return buildShowMigrationLogsPlan(query, vschema, enableOnlineDDL) + return buildShowMigrationLogsPlan(query, vschema, cfg) case *sqlparser.ShowThrottledApps: return buildShowThrottledAppsPlan(query, vschema) case *sqlparser.ShowThrottlerStatus: @@ -189,7 +200,7 @@ func createInstructionFor(ctx context.Context, query string, stmt sqlparser.Stat case *sqlparser.ExplainStmt: return buildRoutePlan(stmt, reservedVars, vschema, buildExplainStmtPlan) case *sqlparser.VExplainStmt: - return buildVExplainPlan(ctx, stmt, reservedVars, vschema, enableOnlineDDL, enableDirectDDL) + return buildVExplainPlan(ctx, stmt, reservedVars, vschema, cfg) case *sqlparser.OtherAdmin: return buildOtherReadAndAdmin(query, vschema) case *sqlparser.Analyze: @@ -275,7 +286,7 @@ func buildDBDDLPlan(stmt sqlparser.Statement, _ *sqlparser.ReservedVars, vschema dbDDLstmt := stmt.(sqlparser.DBDDLStatement) ksName := dbDDLstmt.GetDatabaseName() if ksName == "" { - ks, err := vschema.DefaultKeyspace() + ks, err := vschema.SelectedKeyspace() if err != nil { return nil, err } @@ -310,7 +321,7 @@ func buildDBDDLPlan(stmt sqlparser.Statement, _ *sqlparser.ReservedVars, vschema } func buildLoadPlan(query string, vschema plancontext.VSchema) (*planResult, error) { - keyspace, err := vschema.DefaultKeyspace() + keyspace, err := vschema.SelectedKeyspace() if err != nil { return nil, err } @@ -355,7 +366,7 @@ func buildFlushOptions(stmt *sqlparser.Flush, vschema plancontext.VSchema) (*pla return nil, vterrors.VT09012("FLUSH", vschema.TabletType().String()) } - keyspace, err := vschema.DefaultKeyspace() + keyspace, err := vschema.SelectedKeyspace() if err != nil { return nil, err } diff --git a/go/vt/vtgate/planbuilder/bypass.go b/go/vt/vtgate/planbuilder/bypass.go index 62cae9655b1..d3384d509c1 100644 --- a/go/vt/vtgate/planbuilder/bypass.go +++ b/go/vt/vtgate/planbuilder/bypass.go @@ -26,7 +26,7 @@ import ( ) func buildPlanForBypass(stmt sqlparser.Statement, _ *sqlparser.ReservedVars, vschema plancontext.VSchema) (*planResult, error) { - keyspace, err := vschema.DefaultKeyspace() + keyspace, err := vschema.SelectedKeyspace() if err != nil { return nil, err } diff --git a/go/vt/vtgate/planbuilder/collations_test.go b/go/vt/vtgate/planbuilder/collations_test.go index b393e186679..0595039e673 100644 --- a/go/vt/vtgate/planbuilder/collations_test.go +++ b/go/vt/vtgate/planbuilder/collations_test.go @@ -41,15 +41,13 @@ type collationTestCase struct { } func (tc *collationTestCase) run(t *testing.T) { - vschemaWrapper := &vschemawrapper.VSchemaWrapper{ - V: loadSchema(t, "vschemas/schema.json", false), - SysVarEnabled: true, - Version: Gen4, - Env: vtenv.NewTestEnv(), - } + env := vtenv.NewTestEnv() + vschema := loadSchema(t, "vschemas/schema.json", true) + vw, err := vschemawrapper.NewVschemaWrapper(env, vschema, TestBuilder) + require.NoError(t, err) - tc.addCollationsToSchema(vschemaWrapper) - plan, err := TestBuilder(tc.query, vschemaWrapper, vschemaWrapper.CurrentDb()) + tc.addCollationsToSchema(vw) + plan, err := TestBuilder(tc.query, vw, vw.CurrentDb()) require.NoError(t, err) tc.check(t, tc.collations, plan.Instructions) } diff --git a/go/vt/vtgate/planbuilder/ddl.go b/go/vt/vtgate/planbuilder/ddl.go index f4b8ab6976f..a0045cec060 100644 --- a/go/vt/vtgate/planbuilder/ddl.go +++ b/go/vt/vtgate/planbuilder/ddl.go @@ -9,6 +9,7 @@ import ( vschemapb "vitess.io/vitess/go/vt/proto/vschema" "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vterrors" + "vitess.io/vitess/go/vt/vtgate/dynamicconfig" "vitess.io/vitess/go/vt/vtgate/engine" "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" "vitess.io/vitess/go/vt/vtgate/vindexes" @@ -43,11 +44,11 @@ func (fk *fkContraint) FkWalk(node sqlparser.SQLNode) (kontinue bool, err error) // a session context. It's only when we Execute() the primitive that we have that context. // This is why we return a compound primitive (DDL) which contains fully populated primitives (Send & OnlineDDL), // and which chooses which of the two to invoke at runtime. -func buildGeneralDDLPlan(ctx context.Context, sql string, ddlStatement sqlparser.DDLStatement, reservedVars *sqlparser.ReservedVars, vschema plancontext.VSchema, enableOnlineDDL, enableDirectDDL bool) (*planResult, error) { +func buildGeneralDDLPlan(ctx context.Context, sql string, ddlStatement sqlparser.DDLStatement, reservedVars *sqlparser.ReservedVars, vschema plancontext.VSchema, cfg dynamicconfig.DDL) (*planResult, error) { if vschema.Destination() != nil { return buildByPassPlan(sql, vschema, true) } - normalDDLPlan, onlineDDLPlan, err := buildDDLPlans(ctx, sql, ddlStatement, reservedVars, vschema, enableOnlineDDL, enableDirectDDL) + normalDDLPlan, onlineDDLPlan, err := buildDDLPlans(ctx, sql, ddlStatement, reservedVars, vschema, cfg) if err != nil { return nil, err } @@ -61,15 +62,12 @@ func buildGeneralDDLPlan(ctx context.Context, sql string, ddlStatement sqlparser } eddl := &engine.DDL{ - Keyspace: normalDDLPlan.Keyspace, - SQL: normalDDLPlan.Query, - DDL: ddlStatement, - NormalDDL: normalDDLPlan, - OnlineDDL: onlineDDLPlan, - - DirectDDLEnabled: enableDirectDDL, - OnlineDDLEnabled: enableOnlineDDL, - + Keyspace: normalDDLPlan.Keyspace, + SQL: normalDDLPlan.Query, + DDL: ddlStatement, + NormalDDL: normalDDLPlan, + OnlineDDL: onlineDDLPlan, + Config: cfg, CreateTempTable: ddlStatement.IsTemporary(), } tc := &tableCollector{} @@ -81,7 +79,7 @@ func buildGeneralDDLPlan(ctx context.Context, sql string, ddlStatement sqlparser } func buildByPassPlan(sql string, vschema plancontext.VSchema, isDDL bool) (*planResult, error) { - keyspace, err := vschema.DefaultKeyspace() + keyspace, err := vschema.SelectedKeyspace() if err != nil { return nil, err } @@ -94,7 +92,7 @@ func buildByPassPlan(sql string, vschema plancontext.VSchema, isDDL bool) (*plan return newPlanResult(send), nil } -func buildDDLPlans(ctx context.Context, sql string, ddlStatement sqlparser.DDLStatement, reservedVars *sqlparser.ReservedVars, vschema plancontext.VSchema, enableOnlineDDL, enableDirectDDL bool) (*engine.Send, *engine.OnlineDDL, error) { +func buildDDLPlans(ctx context.Context, sql string, ddlStatement sqlparser.DDLStatement, reservedVars *sqlparser.ReservedVars, vschema plancontext.VSchema, cfg dynamicconfig.DDL) (*engine.Send, *engine.OnlineDDL, error) { var destination key.Destination var keyspace *vindexes.Keyspace var err error @@ -113,9 +111,9 @@ func buildDDLPlans(ctx context.Context, sql string, ddlStatement sqlparser.DDLSt } err = checkFKError(vschema, ddlStatement, keyspace) case *sqlparser.CreateView: - destination, keyspace, err = buildCreateViewCommon(ctx, vschema, reservedVars, enableOnlineDDL, enableDirectDDL, ddl.Select, ddl) + destination, keyspace, err = buildCreateViewCommon(ctx, vschema, reservedVars, cfg, ddl.Select, ddl) case *sqlparser.AlterView: - destination, keyspace, err = buildCreateViewCommon(ctx, vschema, reservedVars, enableOnlineDDL, enableDirectDDL, ddl.Select, ddl) + destination, keyspace, err = buildCreateViewCommon(ctx, vschema, reservedVars, cfg, ddl.Select, ddl) case *sqlparser.DropView: destination, keyspace, err = buildDropView(vschema, ddlStatement) case *sqlparser.DropTable: @@ -197,7 +195,7 @@ func buildCreateViewCommon( ctx context.Context, vschema plancontext.VSchema, reservedVars *sqlparser.ReservedVars, - enableOnlineDDL, enableDirectDDL bool, + cfg dynamicconfig.DDL, ddlSelect sqlparser.SelectStatement, ddl sqlparser.DDLStatement, ) (key.Destination, *vindexes.Keyspace, error) { @@ -214,7 +212,7 @@ func buildCreateViewCommon( expressions = append(expressions, sqlparser.Clone(p.SelectExprs)) return nil }) - selectPlan, err := createInstructionFor(ctx, sqlparser.String(ddlSelect), ddlSelect, reservedVars, vschema, enableOnlineDDL, enableDirectDDL) + selectPlan, err := createInstructionFor(ctx, sqlparser.String(ddlSelect), ddlSelect, reservedVars, vschema, cfg) if err != nil { return nil, nil, err } diff --git a/go/vt/vtgate/planbuilder/migration.go b/go/vt/vtgate/planbuilder/migration.go index 6fb73a9039d..e64b990aa6b 100644 --- a/go/vt/vtgate/planbuilder/migration.go +++ b/go/vt/vtgate/planbuilder/migration.go @@ -27,6 +27,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/vtgate/dynamicconfig" "vitess.io/vitess/go/vt/vtgate/engine" "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" "vitess.io/vitess/go/vt/vtgate/vindexes" @@ -80,8 +81,8 @@ func buildAlterMigrationThrottleAppPlan(query string, alterMigration *sqlparser. }), nil } -func buildAlterMigrationPlan(query string, alterMigration *sqlparser.AlterMigration, vschema plancontext.VSchema, enableOnlineDDL bool) (*planResult, error) { - if !enableOnlineDDL { +func buildAlterMigrationPlan(query string, alterMigration *sqlparser.AlterMigration, vschema plancontext.VSchema, cfg dynamicconfig.DDL) (*planResult, error) { + if !cfg.OnlineEnabled() { return nil, schema.ErrOnlineDDLDisabled } @@ -118,8 +119,8 @@ func buildAlterMigrationPlan(query string, alterMigration *sqlparser.AlterMigrat return newPlanResult(send), nil } -func buildRevertMigrationPlan(query string, stmt *sqlparser.RevertMigration, vschema plancontext.VSchema, enableOnlineDDL bool) (*planResult, error) { - if !enableOnlineDDL { +func buildRevertMigrationPlan(query string, stmt *sqlparser.RevertMigration, vschema plancontext.VSchema, cfg dynamicconfig.DDL) (*planResult, error) { + if !cfg.OnlineEnabled() { return nil, schema.ErrOnlineDDLDisabled } dest, ks, tabletType, err := vschema.TargetDestination("") @@ -147,8 +148,8 @@ func buildRevertMigrationPlan(query string, stmt *sqlparser.RevertMigration, vsc return newPlanResult(emig), nil } -func buildShowMigrationLogsPlan(query string, vschema plancontext.VSchema, enableOnlineDDL bool) (*planResult, error) { - if !enableOnlineDDL { +func buildShowMigrationLogsPlan(query string, vschema plancontext.VSchema, cfg dynamicconfig.DDL) (*planResult, error) { + if !cfg.OnlineEnabled() { return nil, schema.ErrOnlineDDLDisabled } dest, ks, tabletType, err := vschema.TargetDestination("") diff --git a/go/vt/vtgate/planbuilder/operator_transformers.go b/go/vt/vtgate/planbuilder/operator_transformers.go index a22719b4489..df14745e6b2 100644 --- a/go/vt/vtgate/planbuilder/operator_transformers.go +++ b/go/vt/vtgate/planbuilder/operator_transformers.go @@ -545,7 +545,7 @@ func routeToEngineRoute(ctx *plancontext.PlanningContext, op *operators.Route, h } func newRoutingParams(ctx *plancontext.PlanningContext, opCode engine.Opcode) *engine.RoutingParameters { - ks, _ := ctx.VSchema.DefaultKeyspace() + ks, _ := ctx.VSchema.SelectedKeyspace() if ks == nil { // if we don't have a selected keyspace, any keyspace will do // this is used by operators that do not set the keyspace diff --git a/go/vt/vtgate/planbuilder/operators/delete.go b/go/vt/vtgate/planbuilder/operators/delete.go index 4d30d9b9cc1..81e36d54315 100644 --- a/go/vt/vtgate/planbuilder/operators/delete.go +++ b/go/vt/vtgate/planbuilder/operators/delete.go @@ -328,7 +328,7 @@ func updateQueryGraphWithSource(ctx *plancontext.PlanningContext, input Operator if tbl.ID != tblID { continue } - tbl.Alias = sqlparser.NewAliasedTableExpr(sqlparser.NewTableName(vTbl.Name.String()), tbl.Alias.As.String()) + tbl.Alias = sqlparser.NewAliasedTableExpr(sqlparser.NewTableNameWithQualifier(vTbl.Name.String(), vTbl.Keyspace.Name), tbl.Alias.As.String()) tbl.Table, _ = tbl.Alias.TableName() } return op, Rewrote("change query table point to source table") diff --git a/go/vt/vtgate/planbuilder/plan_test.go b/go/vt/vtgate/planbuilder/plan_test.go index 9cf92a91ddf..7135f4dff29 100644 --- a/go/vt/vtgate/planbuilder/plan_test.go +++ b/go/vt/vtgate/planbuilder/plan_test.go @@ -74,17 +74,16 @@ func TestPlanTestSuite(t *testing.T) { func (s *planTestSuite) TestPlan() { defer utils.EnsureNoLeaks(s.T()) - vschemaWrapper := &vschemawrapper.VSchemaWrapper{ - V: loadSchema(s.T(), "vschemas/schema.json", true), - TabletType_: topodatapb.TabletType_PRIMARY, - SysVarEnabled: true, - TestBuilder: TestBuilder, - Env: vtenv.NewTestEnv(), - } - s.addPKs(vschemaWrapper.V, "user", []string{"user", "music"}) - s.addPKsProvided(vschemaWrapper.V, "user", []string{"user_extra"}, []string{"id", "user_id"}) - s.addPKsProvided(vschemaWrapper.V, "ordering", []string{"order"}, []string{"oid", "region_id"}) - s.addPKsProvided(vschemaWrapper.V, "ordering", []string{"order_event"}, []string{"oid", "ename"}) + + env := vtenv.NewTestEnv() + vschema := loadSchema(s.T(), "vschemas/schema.json", true) + vw, err := vschemawrapper.NewVschemaWrapper(env, vschema, TestBuilder) + require.NoError(s.T(), err) + + s.addPKs(vschema, "user", []string{"user", "music"}) + s.addPKsProvided(vschema, "user", []string{"user_extra"}, []string{"id", "user_id"}) + s.addPKsProvided(vschema, "ordering", []string{"order"}, []string{"oid", "region_id"}) + s.addPKsProvided(vschema, "ordering", []string{"order_event"}, []string{"oid", "ename"}) // 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 @@ -92,77 +91,73 @@ func (s *planTestSuite) TestPlan() { // the column is named as Id. This is to make sure that // column names are case-preserved, but treated as // case-insensitive even if they come from the vschema. - s.testFile("aggr_cases.json", vschemaWrapper, false) - s.testFile("dml_cases.json", vschemaWrapper, false) - s.testFile("from_cases.json", vschemaWrapper, false) - s.testFile("filter_cases.json", vschemaWrapper, false) - s.testFile("postprocess_cases.json", vschemaWrapper, false) - s.testFile("select_cases.json", vschemaWrapper, false) - s.testFile("symtab_cases.json", vschemaWrapper, false) - s.testFile("unsupported_cases.json", vschemaWrapper, false) - s.testFile("unknown_schema_cases.json", vschemaWrapper, false) - s.testFile("vindex_func_cases.json", vschemaWrapper, false) - s.testFile("wireup_cases.json", vschemaWrapper, false) - s.testFile("memory_sort_cases.json", vschemaWrapper, false) - s.testFile("use_cases.json", vschemaWrapper, false) - s.testFile("set_cases.json", vschemaWrapper, false) - s.testFile("union_cases.json", vschemaWrapper, false) - s.testFile("large_union_cases.json", vschemaWrapper, false) - s.testFile("transaction_cases.json", vschemaWrapper, false) - s.testFile("lock_cases.json", vschemaWrapper, false) - s.testFile("large_cases.json", vschemaWrapper, false) - s.testFile("ddl_cases_no_default_keyspace.json", vschemaWrapper, false) - s.testFile("flush_cases_no_default_keyspace.json", vschemaWrapper, false) - s.testFile("show_cases_no_default_keyspace.json", vschemaWrapper, false) - s.testFile("stream_cases.json", vschemaWrapper, false) - s.testFile("info_schema80_cases.json", vschemaWrapper, false) - s.testFile("reference_cases.json", vschemaWrapper, false) - s.testFile("vexplain_cases.json", vschemaWrapper, false) - s.testFile("misc_cases.json", vschemaWrapper, false) - s.testFile("cte_cases.json", vschemaWrapper, false) + s.testFile("aggr_cases.json", vw, false) + s.testFile("dml_cases.json", vw, false) + s.testFile("from_cases.json", vw, false) + s.testFile("filter_cases.json", vw, false) + s.testFile("postprocess_cases.json", vw, false) + s.testFile("select_cases.json", vw, false) + s.testFile("symtab_cases.json", vw, false) + s.testFile("unsupported_cases.json", vw, false) + s.testFile("unknown_schema_cases.json", vw, false) + s.testFile("vindex_func_cases.json", vw, false) + s.testFile("wireup_cases.json", vw, false) + s.testFile("memory_sort_cases.json", vw, false) + s.testFile("use_cases.json", vw, false) + s.testFile("set_cases.json", vw, false) + s.testFile("union_cases.json", vw, false) + s.testFile("large_union_cases.json", vw, false) + s.testFile("transaction_cases.json", vw, false) + s.testFile("lock_cases.json", vw, false) + s.testFile("large_cases.json", vw, false) + s.testFile("ddl_cases_no_default_keyspace.json", vw, false) + s.testFile("flush_cases_no_default_keyspace.json", vw, false) + s.testFile("show_cases_no_default_keyspace.json", vw, false) + s.testFile("stream_cases.json", vw, false) + s.testFile("info_schema80_cases.json", vw, false) + s.testFile("reference_cases.json", vw, false) + s.testFile("vexplain_cases.json", vw, false) + s.testFile("misc_cases.json", vw, false) + s.testFile("cte_cases.json", vw, false) } // TestForeignKeyPlanning tests the planning of foreign keys in a managed mode by Vitess. func (s *planTestSuite) TestForeignKeyPlanning() { + env := vtenv.NewTestEnv() vschema := loadSchema(s.T(), "vschemas/schema.json", true) - s.setFks(vschema) - vschemaWrapper := &vschemawrapper.VSchemaWrapper{ - V: vschema, - TestBuilder: TestBuilder, - Env: vtenv.NewTestEnv(), - } + vw, err := vschemawrapper.NewVschemaWrapper(env, vschema, TestBuilder) + require.NoError(s.T(), err) - s.testFile("foreignkey_cases.json", vschemaWrapper, false) + s.setFks(vschema) + s.testFile("foreignkey_cases.json", vw, false) } // TestForeignKeyChecksOn tests the planning when the session variable for foreign_key_checks is set to ON. func (s *planTestSuite) TestForeignKeyChecksOn() { + env := vtenv.NewTestEnv() vschema := loadSchema(s.T(), "vschemas/schema.json", true) - s.setFks(vschema) + vw, err := vschemawrapper.NewVschemaWrapper(env, vschema, TestBuilder) + require.NoError(s.T(), err) + fkChecksState := true - vschemaWrapper := &vschemawrapper.VSchemaWrapper{ - V: vschema, - TestBuilder: TestBuilder, - ForeignKeyChecksState: &fkChecksState, - Env: vtenv.NewTestEnv(), - } + vw.ForeignKeyChecksState = &fkChecksState - s.testFile("foreignkey_checks_on_cases.json", vschemaWrapper, false) + s.setFks(vschema) + s.testFile("foreignkey_checks_on_cases.json", vw, false) } // TestForeignKeyChecksOff tests the planning when the session variable for foreign_key_checks is set to OFF. func (s *planTestSuite) TestForeignKeyChecksOff() { + env := vtenv.NewTestEnv() vschema := loadSchema(s.T(), "vschemas/schema.json", true) - s.setFks(vschema) + vw, err := vschemawrapper.NewVschemaWrapper(env, vschema, TestBuilder) + require.NoError(s.T(), err) + fkChecksState := false - vschemaWrapper := &vschemawrapper.VSchemaWrapper{ - V: vschema, - TestBuilder: TestBuilder, - ForeignKeyChecksState: &fkChecksState, - Env: vtenv.NewTestEnv(), - } + vw.ForeignKeyChecksState = &fkChecksState - s.testFile("foreignkey_checks_off_cases.json", vschemaWrapper, false) + s.setFks(vschema) + s.testFile("foreignkey_checks_off_cases.json", vw, false) } func (s *planTestSuite) setFks(vschema *vindexes.VSchema) { @@ -266,120 +261,127 @@ func (s *planTestSuite) TestSystemTables57() { MySQLServerVersion: "5.7.9", }) require.NoError(s.T(), err) - vschemaWrapper := &vschemawrapper.VSchemaWrapper{ - V: loadSchema(s.T(), "vschemas/schema.json", true), - Env: env, - } - s.testFile("info_schema57_cases.json", vschemaWrapper, false) + vschema := loadSchema(s.T(), "vschemas/schema.json", true) + vw, err := vschemawrapper.NewVschemaWrapper(env, vschema, TestBuilder) + require.NoError(s.T(), err) + + s.testFile("info_schema57_cases.json", vw, false) } func (s *planTestSuite) TestSysVarSetDisabled() { - vschemaWrapper := &vschemawrapper.VSchemaWrapper{ - V: loadSchema(s.T(), "vschemas/schema.json", true), - SysVarEnabled: false, - Env: vtenv.NewTestEnv(), - } + env := vtenv.NewTestEnv() + vschema := loadSchema(s.T(), "vschemas/schema.json", true) + vw, err := vschemawrapper.NewVschemaWrapper(env, vschema, TestBuilder) + require.NoError(s.T(), err) + + vw.SysVarEnabled = false - s.testFile("set_sysvar_disabled_cases.json", vschemaWrapper, false) + s.testFile("set_sysvar_disabled_cases.json", vw, false) } func (s *planTestSuite) TestViews() { - vschemaWrapper := &vschemawrapper.VSchemaWrapper{ - V: loadSchema(s.T(), "vschemas/schema.json", true), - EnableViews: true, - Env: vtenv.NewTestEnv(), - } + env := vtenv.NewTestEnv() + vschema := loadSchema(s.T(), "vschemas/schema.json", true) + vw, err := vschemawrapper.NewVschemaWrapper(env, vschema, TestBuilder) + require.NoError(s.T(), err) + + vw.EnableViews = true - s.testFile("view_cases.json", vschemaWrapper, false) + s.testFile("view_cases.json", vw, false) } func (s *planTestSuite) TestOne() { reset := operators.EnableDebugPrinting() defer reset() - lv := loadSchema(s.T(), "vschemas/schema.json", true) - s.setFks(lv) - s.addPKs(lv, "user", []string{"user", "music"}) - s.addPKs(lv, "main", []string{"unsharded"}) - s.addPKsProvided(lv, "user", []string{"user_extra"}, []string{"id", "user_id"}) - s.addPKsProvided(lv, "ordering", []string{"order"}, []string{"oid", "region_id"}) - s.addPKsProvided(lv, "ordering", []string{"order_event"}, []string{"oid", "ename"}) - vschema := &vschemawrapper.VSchemaWrapper{ - V: lv, - TestBuilder: TestBuilder, - Env: vtenv.NewTestEnv(), - } + env := vtenv.NewTestEnv() + vschema := loadSchema(s.T(), "vschemas/schema.json", true) + vw, err := vschemawrapper.NewVschemaWrapper(env, vschema, TestBuilder) + require.NoError(s.T(), err) - s.testFile("onecase.json", vschema, false) + s.setFks(vschema) + s.addPKs(vschema, "user", []string{"user", "music"}) + s.addPKs(vschema, "main", []string{"unsharded"}) + s.addPKsProvided(vschema, "user", []string{"user_extra"}, []string{"id", "user_id"}) + s.addPKsProvided(vschema, "ordering", []string{"order"}, []string{"oid", "region_id"}) + s.addPKsProvided(vschema, "ordering", []string{"order_event"}, []string{"oid", "ename"}) + + s.testFile("onecase.json", vw, false) } func (s *planTestSuite) TestOneTPCC() { reset := operators.EnableDebugPrinting() defer reset() - vschema := &vschemawrapper.VSchemaWrapper{ - V: loadSchema(s.T(), "vschemas/tpcc_schema.json", true), - Env: vtenv.NewTestEnv(), - } + env := vtenv.NewTestEnv() + vschema := loadSchema(s.T(), "vschemas/tpcc_schema.json", true) + vw, err := vschemawrapper.NewVschemaWrapper(env, vschema, TestBuilder) + require.NoError(s.T(), err) - s.testFile("onecase.json", vschema, false) + s.testFile("onecase.json", vw, false) } func (s *planTestSuite) TestOneWithMainAsDefault() { reset := operators.EnableDebugPrinting() defer reset() - vschema := &vschemawrapper.VSchemaWrapper{ - V: loadSchema(s.T(), "vschemas/schema.json", true), - Keyspace: &vindexes.Keyspace{ - Name: "main", - Sharded: false, - }, - Env: vtenv.NewTestEnv(), - } - s.testFile("onecase.json", vschema, false) + env := vtenv.NewTestEnv() + vschema := loadSchema(s.T(), "vschemas/schema.json", true) + vw, err := vschemawrapper.NewVschemaWrapper(env, vschema, TestBuilder) + require.NoError(s.T(), err) + + vw.Vcursor.SetTarget("main") + vw.Keyspace = &vindexes.Keyspace{Name: "main"} + + s.testFile("onecase.json", vw, false) } func (s *planTestSuite) TestOneWithSecondUserAsDefault() { reset := operators.EnableDebugPrinting() defer reset() - vschema := &vschemawrapper.VSchemaWrapper{ - V: loadSchema(s.T(), "vschemas/schema.json", true), - Keyspace: &vindexes.Keyspace{ - Name: "second_user", - Sharded: true, - }, - Env: vtenv.NewTestEnv(), + + env := vtenv.NewTestEnv() + vschema := loadSchema(s.T(), "vschemas/schema.json", true) + vw, err := vschemawrapper.NewVschemaWrapper(env, vschema, TestBuilder) + require.NoError(s.T(), err) + + vw.Vcursor.SetTarget("second_user") + vw.Keyspace = &vindexes.Keyspace{ + Name: "second_user", + Sharded: true, } - s.testFile("onecase.json", vschema, false) + s.testFile("onecase.json", vw, false) } func (s *planTestSuite) TestOneWithUserAsDefault() { reset := operators.EnableDebugPrinting() defer reset() - vschema := &vschemawrapper.VSchemaWrapper{ - V: loadSchema(s.T(), "vschemas/schema.json", true), - Keyspace: &vindexes.Keyspace{ - Name: "user", - Sharded: true, - }, - Env: vtenv.NewTestEnv(), + + env := vtenv.NewTestEnv() + vschema := loadSchema(s.T(), "vschemas/schema.json", true) + vw, err := vschemawrapper.NewVschemaWrapper(env, vschema, TestBuilder) + require.NoError(s.T(), err) + + vw.Vcursor.SetTarget("user") + vw.Keyspace = &vindexes.Keyspace{ + Name: "user", + Sharded: true, } - s.testFile("onecase.json", vschema, false) + s.testFile("onecase.json", vw, false) } func (s *planTestSuite) TestOneWithTPCHVSchema() { reset := operators.EnableDebugPrinting() defer reset() - vschema := &vschemawrapper.VSchemaWrapper{ - V: loadSchema(s.T(), "vschemas/tpch_schema.json", true), - SysVarEnabled: true, - Env: vtenv.NewTestEnv(), - } - s.testFile("onecase.json", vschema, false) + env := vtenv.NewTestEnv() + vschema := loadSchema(s.T(), "vschemas/schema.json", true) + vw, err := vschemawrapper.NewVschemaWrapper(env, vschema, TestBuilder) + require.NoError(s.T(), err) + + s.testFile("onecase.json", vw, false) } func (s *planTestSuite) TestOneWith57Version() { @@ -390,52 +392,47 @@ func (s *planTestSuite) TestOneWith57Version() { MySQLServerVersion: "5.7.9", }) require.NoError(s.T(), err) - vschema := &vschemawrapper.VSchemaWrapper{ - V: loadSchema(s.T(), "vschemas/schema.json", true), - Env: env, - } + vschema := loadSchema(s.T(), "vschemas/schema.json", true) + vw, err := vschemawrapper.NewVschemaWrapper(env, vschema, TestBuilder) + require.NoError(s.T(), err) - s.testFile("onecase.json", vschema, false) + s.testFile("onecase.json", vw, false) } func (s *planTestSuite) TestRubyOnRailsQueries() { - vschemaWrapper := &vschemawrapper.VSchemaWrapper{ - V: loadSchema(s.T(), "vschemas/rails_schema.json", true), - SysVarEnabled: true, - Env: vtenv.NewTestEnv(), - } + env := vtenv.NewTestEnv() + vschema := loadSchema(s.T(), "vschemas/rails_schema.json", true) + vw, err := vschemawrapper.NewVschemaWrapper(env, vschema, TestBuilder) + require.NoError(s.T(), err) - s.testFile("rails_cases.json", vschemaWrapper, false) + s.testFile("rails_cases.json", vw, false) } func (s *planTestSuite) TestOLTP() { - vschemaWrapper := &vschemawrapper.VSchemaWrapper{ - V: loadSchema(s.T(), "vschemas/oltp_schema.json", true), - SysVarEnabled: true, - Env: vtenv.NewTestEnv(), - } + env := vtenv.NewTestEnv() + vschema := loadSchema(s.T(), "vschemas/oltp_schema.json", true) + vw, err := vschemawrapper.NewVschemaWrapper(env, vschema, TestBuilder) + require.NoError(s.T(), err) - s.testFile("oltp_cases.json", vschemaWrapper, false) + s.testFile("oltp_cases.json", vw, false) } func (s *planTestSuite) TestTPCC() { - vschemaWrapper := &vschemawrapper.VSchemaWrapper{ - V: loadSchema(s.T(), "vschemas/tpcc_schema.json", true), - SysVarEnabled: true, - Env: vtenv.NewTestEnv(), - } + env := vtenv.NewTestEnv() + vschema := loadSchema(s.T(), "vschemas/tpcc_schema.json", true) + vw, err := vschemawrapper.NewVschemaWrapper(env, vschema, TestBuilder) + require.NoError(s.T(), err) - s.testFile("tpcc_cases.json", vschemaWrapper, false) + s.testFile("tpcc_cases.json", vw, false) } func (s *planTestSuite) TestTPCH() { - vschemaWrapper := &vschemawrapper.VSchemaWrapper{ - V: loadSchema(s.T(), "vschemas/tpch_schema.json", true), - SysVarEnabled: true, - Env: vtenv.NewTestEnv(), - } + env := vtenv.NewTestEnv() + vschema := loadSchema(s.T(), "vschemas/tpch_schema.json", true) + vw, err := vschemawrapper.NewVschemaWrapper(env, vschema, TestBuilder) + require.NoError(s.T(), err) - s.testFile("tpch_cases.json", vschemaWrapper, false) + s.testFile("tpch_cases.json", vw, false) } func BenchmarkOLTP(b *testing.B) { @@ -451,15 +448,14 @@ func BenchmarkTPCH(b *testing.B) { } func benchmarkWorkload(b *testing.B, name string) { - vschemaWrapper := &vschemawrapper.VSchemaWrapper{ - V: loadSchema(b, "vschemas/"+name+"_schema.json", true), - SysVarEnabled: true, - Env: vtenv.NewTestEnv(), - } + env := vtenv.NewTestEnv() + vschema := loadSchema(b, "vschemas/"+name+"_schema.json", true) + vw, err := vschemawrapper.NewVschemaWrapper(env, vschema, TestBuilder) + require.NoError(b, err) testCases := readJSONTests(name + "_cases.json") b.ResetTimer() - benchmarkPlanner(b, Gen4, testCases, vschemaWrapper) + benchmarkPlanner(b, Gen4, testCases, vw) } func (s *planTestSuite) TestBypassPlanningShardTargetFromFile() { @@ -478,35 +474,33 @@ func (s *planTestSuite) TestBypassPlanningShardTargetFromFile() { } func (s *planTestSuite) TestBypassPlanningKeyrangeTargetFromFile() { + env := vtenv.NewTestEnv() + vschema := loadSchema(s.T(), "vschemas/schema.json", true) + vw, err := vschemawrapper.NewVschemaWrapper(env, vschema, TestBuilder) + require.NoError(s.T(), err) + keyRange, _ := key.ParseShardingSpec("-") + vw.Dest = key.DestinationExactKeyRange{KeyRange: keyRange[0]} - vschema := &vschemawrapper.VSchemaWrapper{ - V: loadSchema(s.T(), "vschemas/schema.json", true), - Keyspace: &vindexes.Keyspace{ - Name: "main", - Sharded: false, - }, - TabletType_: topodatapb.TabletType_PRIMARY, - Dest: key.DestinationExactKeyRange{KeyRange: keyRange[0]}, - Env: vtenv.NewTestEnv(), - } + vw.Vcursor.SetTarget("main") + vw.Keyspace = &vindexes.Keyspace{Name: "main"} - s.testFile("bypass_keyrange_cases.json", vschema, false) + s.testFile("bypass_keyrange_cases.json", vw, false) } func (s *planTestSuite) TestWithDefaultKeyspaceFromFile() { ctx, cancel := context.WithCancel(context.Background()) defer cancel() + // We are testing this separately so we can set a default keyspace - vschema := &vschemawrapper.VSchemaWrapper{ - V: loadSchema(s.T(), "vschemas/schema.json", true), - Keyspace: &vindexes.Keyspace{ - Name: "main", - Sharded: false, - }, - TabletType_: topodatapb.TabletType_PRIMARY, - Env: vtenv.NewTestEnv(), - } + env := vtenv.NewTestEnv() + vschema := loadSchema(s.T(), "vschemas/schema.json", true) + vw, err := vschemawrapper.NewVschemaWrapper(env, vschema, TestBuilder) + require.NoError(s.T(), err) + + vw.Vcursor.SetTarget("main") + vw.Keyspace = &vindexes.Keyspace{Name: "main"} + ts := memorytopo.NewServer(ctx, "cell1") ts.CreateKeyspace(ctx, "main", &topodatapb.Keyspace{}) ts.CreateKeyspace(ctx, "user", &topodatapb.Keyspace{}) @@ -521,96 +515,92 @@ func (s *planTestSuite) TestWithDefaultKeyspaceFromFile() { }) require.True(s.T(), created) - s.testFile("alterVschema_cases.json", vschema, false) - s.testFile("ddl_cases.json", vschema, false) - s.testFile("migration_cases.json", vschema, false) - s.testFile("flush_cases.json", vschema, false) - s.testFile("show_cases.json", vschema, false) - s.testFile("call_cases.json", vschema, false) + s.testFile("alterVschema_cases.json", vw, false) + s.testFile("ddl_cases.json", vw, false) + s.testFile("migration_cases.json", vw, false) + s.testFile("flush_cases.json", vw, false) + s.testFile("show_cases.json", vw, false) + s.testFile("call_cases.json", vw, false) } func (s *planTestSuite) TestWithDefaultKeyspaceFromFileSharded() { // We are testing this separately so we can set a default keyspace - vschema := &vschemawrapper.VSchemaWrapper{ - V: loadSchema(s.T(), "vschemas/schema.json", true), - Keyspace: &vindexes.Keyspace{ - Name: "second_user", - Sharded: true, - }, - TabletType_: topodatapb.TabletType_PRIMARY, - Env: vtenv.NewTestEnv(), + env := vtenv.NewTestEnv() + vschema := loadSchema(s.T(), "vschemas/schema.json", true) + vw, err := vschemawrapper.NewVschemaWrapper(env, vschema, TestBuilder) + require.NoError(s.T(), err) + + vw.Vcursor.SetTarget("second_user") + vw.Keyspace = &vindexes.Keyspace{ + Name: "second_user", + Sharded: true, } - s.testFile("select_cases_with_default.json", vschema, false) + s.testFile("select_cases_with_default.json", vw, false) } func (s *planTestSuite) TestWithUserDefaultKeyspaceFromFileSharded() { // We are testing this separately so we can set a default keyspace - vschema := &vschemawrapper.VSchemaWrapper{ - V: loadSchema(s.T(), "vschemas/schema.json", true), - Keyspace: &vindexes.Keyspace{ - Name: "user", - Sharded: true, - }, - TabletType_: topodatapb.TabletType_PRIMARY, - Env: vtenv.NewTestEnv(), + env := vtenv.NewTestEnv() + vschema := loadSchema(s.T(), "vschemas/schema.json", true) + vw, err := vschemawrapper.NewVschemaWrapper(env, vschema, TestBuilder) + require.NoError(s.T(), err) + + vw.Vcursor.SetTarget("user") + vw.Keyspace = &vindexes.Keyspace{ + Name: "user", + Sharded: true, } - s.testFile("select_cases_with_user_as_default.json", vschema, false) + s.testFile("select_cases_with_user_as_default.json", vw, false) + s.testFile("dml_cases_with_user_as_default.json", vw, false) } func (s *planTestSuite) TestWithSystemSchemaAsDefaultKeyspace() { // We are testing this separately so we can set a default keyspace - vschema := &vschemawrapper.VSchemaWrapper{ - V: loadSchema(s.T(), "vschemas/schema.json", true), - Keyspace: &vindexes.Keyspace{Name: "information_schema"}, - TabletType_: topodatapb.TabletType_PRIMARY, - Env: vtenv.NewTestEnv(), - } + env := vtenv.NewTestEnv() + vschema := loadSchema(s.T(), "vschemas/schema.json", true) + vw, err := vschemawrapper.NewVschemaWrapper(env, vschema, TestBuilder) + require.NoError(s.T(), err) - s.testFile("sysschema_default.json", vschema, false) + vw.Keyspace = &vindexes.Keyspace{Name: "information_schema"} + + s.testFile("sysschema_default.json", vw, false) } func (s *planTestSuite) TestOtherPlanningFromFile() { // We are testing this separately so we can set a default keyspace - vschema := &vschemawrapper.VSchemaWrapper{ - V: loadSchema(s.T(), "vschemas/schema.json", true), - Keyspace: &vindexes.Keyspace{ - Name: "main", - Sharded: false, - }, - TabletType_: topodatapb.TabletType_PRIMARY, - Env: vtenv.NewTestEnv(), - } + env := vtenv.NewTestEnv() + vschema := loadSchema(s.T(), "vschemas/schema.json", true) + vw, err := vschemawrapper.NewVschemaWrapper(env, vschema, TestBuilder) + require.NoError(s.T(), err) - s.testFile("other_read_cases.json", vschema, false) - s.testFile("other_admin_cases.json", vschema, false) + vw.Vcursor.SetTarget("main") + vw.Keyspace = &vindexes.Keyspace{Name: "main"} + + s.testFile("other_read_cases.json", vw, false) + s.testFile("other_admin_cases.json", vw, false) } func (s *planTestSuite) TestMirrorPlanning() { - vschema := &vschemawrapper.VSchemaWrapper{ - V: loadSchema(s.T(), "vschemas/mirror_schema.json", true), - TabletType_: topodatapb.TabletType_PRIMARY, - SysVarEnabled: true, - TestBuilder: TestBuilder, - Env: vtenv.NewTestEnv(), - } + env := vtenv.NewTestEnv() + vschema := loadSchema(s.T(), "vschemas/mirror_schema.json", true) + vw, err := vschemawrapper.NewVschemaWrapper(env, vschema, TestBuilder) + require.NoError(s.T(), err) - s.testFile("mirror_cases.json", vschema, false) + s.testFile("mirror_cases.json", vw, false) } func (s *planTestSuite) TestOneMirror() { reset := operators.EnableDebugPrinting() defer reset() - vschema := &vschemawrapper.VSchemaWrapper{ - V: loadSchema(s.T(), "vschemas/mirror_schema.json", true), - TabletType_: topodatapb.TabletType_PRIMARY, - SysVarEnabled: true, - TestBuilder: TestBuilder, - Env: vtenv.NewTestEnv(), - } - s.testFile("onecase.json", vschema, false) + env := vtenv.NewTestEnv() + vschema := loadSchema(s.T(), "vschemas/schema.json", true) + vw, err := vschemawrapper.NewVschemaWrapper(env, vschema, TestBuilder) + require.NoError(s.T(), err) + + s.testFile("onecase.json", vw, false) } func loadSchema(t testing.TB, filename string, setCollation bool) *vindexes.VSchema { @@ -659,21 +649,12 @@ func createFkDefinition(childCols []string, parentTableName string, parentCols [ } } -type ( - planTest struct { - Comment string `json:"comment,omitempty"` - Query string `json:"query,omitempty"` - Plan json.RawMessage `json:"plan,omitempty"` - Skip bool `json:"skip,omitempty"` - } -) - func (s *planTestSuite) testFile(filename string, vschema *vschemawrapper.VSchemaWrapper, render bool) { opts := jsondiff.DefaultConsoleOptions() s.T().Run(filename, func(t *testing.T) { failed := false - var expected []planTest + var expected []PlanTest for _, tcase := range readJSONTests(filename) { testName := tcase.Comment if testName == "" { @@ -682,9 +663,10 @@ func (s *planTestSuite) testFile(filename string, vschema *vschemawrapper.VSchem if tcase.Query == "" { continue } - current := planTest{ - Comment: testName, + current := PlanTest{ + Comment: tcase.Comment, Query: tcase.Query, + SkipE2E: tcase.SkipE2E, } vschema.Version = Gen4 out := getPlanOutput(tcase, vschema, render) @@ -730,8 +712,8 @@ func (s *planTestSuite) testFile(filename string, vschema *vschemawrapper.VSchem }) } -func readJSONTests(filename string) []planTest { - var output []planTest +func readJSONTests(filename string) []PlanTest { + var output []PlanTest file, err := os.Open(locateFile(filename)) if err != nil { panic(err) @@ -745,7 +727,7 @@ func readJSONTests(filename string) []planTest { return output } -func getPlanOutput(tcase planTest, vschema *vschemawrapper.VSchemaWrapper, render bool) (out string) { +func getPlanOutput(tcase PlanTest, vschema *vschemawrapper.VSchemaWrapper, render bool) (out string) { defer func() { if r := recover(); r != nil { out = fmt.Sprintf("panicked: %v\n%s", r, string(debug.Stack())) @@ -783,30 +765,29 @@ func locateFile(name string) string { var benchMarkFiles = []string{"from_cases.json", "filter_cases.json", "large_cases.json", "aggr_cases.json", "select_cases.json", "union_cases.json"} func BenchmarkPlanner(b *testing.B) { - vschema := &vschemawrapper.VSchemaWrapper{ - V: loadSchema(b, "vschemas/schema.json", true), - SysVarEnabled: true, - Env: vtenv.NewTestEnv(), - } + env := vtenv.NewTestEnv() + vschema := loadSchema(b, "vschemas/schema.json", true) + vw, err := vschemawrapper.NewVschemaWrapper(env, vschema, TestBuilder) + require.NoError(b, err) + for _, filename := range benchMarkFiles { testCases := readJSONTests(filename) b.Run(filename+"-gen4", func(b *testing.B) { - benchmarkPlanner(b, Gen4, testCases, vschema) + benchmarkPlanner(b, Gen4, testCases, vw) }) } } func BenchmarkSemAnalysis(b *testing.B) { - vschema := &vschemawrapper.VSchemaWrapper{ - V: loadSchema(b, "vschemas/schema.json", true), - SysVarEnabled: true, - Env: vtenv.NewTestEnv(), - } + env := vtenv.NewTestEnv() + vschema := loadSchema(b, "vschemas/schema.json", true) + vw, err := vschemawrapper.NewVschemaWrapper(env, vschema, TestBuilder) + require.NoError(b, err) for i := 0; i < b.N; i++ { for _, filename := range benchMarkFiles { for _, tc := range readJSONTests(filename) { - exerciseAnalyzer(tc.Query, vschema.CurrentDb(), vschema) + exerciseAnalyzer(tc.Query, vw.CurrentDb(), vw) } } } @@ -831,12 +812,10 @@ func exerciseAnalyzer(query, database string, s semantics.SchemaInformation) { } func BenchmarkSelectVsDML(b *testing.B) { - vschema := &vschemawrapper.VSchemaWrapper{ - V: loadSchema(b, "vschemas/schema.json", true), - SysVarEnabled: true, - Version: Gen4, - Env: vtenv.NewTestEnv(), - } + env := vtenv.NewTestEnv() + vschema := loadSchema(b, "vschemas/schema.json", true) + vw, err := vschemawrapper.NewVschemaWrapper(env, vschema, TestBuilder) + require.NoError(b, err) dmlCases := readJSONTests("dml_cases.json") selectCases := readJSONTests("select_cases.json") @@ -850,44 +829,37 @@ func BenchmarkSelectVsDML(b *testing.B) { }) b.Run("DML (random sample, N=32)", func(b *testing.B) { - benchmarkPlanner(b, Gen4, dmlCases[:32], vschema) + benchmarkPlanner(b, Gen4, dmlCases[:32], vw) }) b.Run("Select (random sample, N=32)", func(b *testing.B) { - benchmarkPlanner(b, Gen4, selectCases[:32], vschema) + benchmarkPlanner(b, Gen4, selectCases[:32], vw) }) } func BenchmarkBaselineVsMirrored(b *testing.B) { + env := vtenv.NewTestEnv() baseline := loadSchema(b, "vschemas/mirror_schema.json", true) baseline.MirrorRules = map[string]*vindexes.MirrorRule{} - baselineVschema := &vschemawrapper.VSchemaWrapper{ - V: baseline, - SysVarEnabled: true, - Version: Gen4, - Env: vtenv.NewTestEnv(), - } + bvw, err := vschemawrapper.NewVschemaWrapper(env, baseline, TestBuilder) + require.NoError(b, err) mirroredSchema := loadSchema(b, "vschemas/mirror_schema.json", true) - mirroredVschema := &vschemawrapper.VSchemaWrapper{ - V: mirroredSchema, - SysVarEnabled: true, - Version: Gen4, - Env: vtenv.NewTestEnv(), - } + mvw, err := vschemawrapper.NewVschemaWrapper(env, mirroredSchema, TestBuilder) + require.NoError(b, err) cases := readJSONTests("mirror_cases.json") b.Run("Baseline", func(b *testing.B) { - benchmarkPlanner(b, Gen4, cases, baselineVschema) + benchmarkPlanner(b, Gen4, cases, bvw) }) b.Run("Mirrored", func(b *testing.B) { - benchmarkPlanner(b, Gen4, cases, mirroredVschema) + benchmarkPlanner(b, Gen4, cases, mvw) }) } -func benchmarkPlanner(b *testing.B, version plancontext.PlannerVersion, testCases []planTest, vschema *vschemawrapper.VSchemaWrapper) { +func benchmarkPlanner(b *testing.B, version plancontext.PlannerVersion, testCases []PlanTest, vschema *vschemawrapper.VSchemaWrapper) { b.ReportAllocs() for n := 0; n < b.N; n++ { for _, tcase := range testCases { diff --git a/go/vt/vtgate/planbuilder/plancontext/planning_context.go b/go/vt/vtgate/planbuilder/plancontext/planning_context.go index 607ca83aa31..016f5c877cf 100644 --- a/go/vt/vtgate/planbuilder/plancontext/planning_context.go +++ b/go/vt/vtgate/planbuilder/plancontext/planning_context.go @@ -91,7 +91,7 @@ func CreatePlanningContext(stmt sqlparser.Statement, version querypb.ExecuteOptions_PlannerVersion, ) (*PlanningContext, error) { ksName := "" - if ks, _ := vschema.DefaultKeyspace(); ks != nil { + if ks, _ := vschema.SelectedKeyspace(); ks != nil { ksName = ks.Name } diff --git a/go/vt/vtgate/planbuilder/plancontext/planning_context_test.go b/go/vt/vtgate/planbuilder/plancontext/planning_context_test.go index d7315f376b6..e5e96b0a4be 100644 --- a/go/vt/vtgate/planbuilder/plancontext/planning_context_test.go +++ b/go/vt/vtgate/planbuilder/plancontext/planning_context_test.go @@ -201,7 +201,7 @@ func (v *vschema) FindTableOrVindex(tablename sqlparser.TableName) (*vindexes.Ta panic("implement me") } -func (v *vschema) DefaultKeyspace() (*vindexes.Keyspace, error) { +func (v *vschema) SelectedKeyspace() (*vindexes.Keyspace, error) { // TODO implement me panic("implement me") } diff --git a/go/vt/vtgate/planbuilder/plancontext/vschema.go b/go/vt/vtgate/planbuilder/plancontext/vschema.go index 6e92ad0d83b..b4560424718 100644 --- a/go/vt/vtgate/planbuilder/plancontext/vschema.go +++ b/go/vt/vtgate/planbuilder/plancontext/vschema.go @@ -27,7 +27,9 @@ type VSchema interface { FindTable(tablename sqlparser.TableName) (*vindexes.Table, string, topodatapb.TabletType, key.Destination, error) FindView(name sqlparser.TableName) sqlparser.SelectStatement FindTableOrVindex(tablename sqlparser.TableName) (*vindexes.Table, vindexes.Vindex, string, topodatapb.TabletType, key.Destination, error) - DefaultKeyspace() (*vindexes.Keyspace, error) + + // SelectedKeyspace returns the current keyspace if set, otherwise returns an error + SelectedKeyspace() (*vindexes.Keyspace, error) TargetString() string Destination() key.Destination TabletType() topodatapb.TabletType diff --git a/go/vt/vtgate/planbuilder/select.go b/go/vt/vtgate/planbuilder/select.go index 9cc1c8efe06..409343f2760 100644 --- a/go/vt/vtgate/planbuilder/select.go +++ b/go/vt/vtgate/planbuilder/select.go @@ -46,7 +46,7 @@ func gen4SelectStmtPlanner( } if p != nil { used := "dual" - keyspace, ksErr := vschema.DefaultKeyspace() + keyspace, ksErr := vschema.SelectedKeyspace() if ksErr == nil { // we are just getting the ks to log the correct table use. // no need to fail this if we can't find the default keyspace @@ -101,7 +101,7 @@ func gen4SelectStmtPlanner( func gen4planSQLCalcFoundRows(vschema plancontext.VSchema, sel *sqlparser.Select, query string, reservedVars *sqlparser.ReservedVars) (*planResult, error) { ksName := "" - if ks, _ := vschema.DefaultKeyspace(); ks != nil { + if ks, _ := vschema.SelectedKeyspace(); ks != nil { ksName = ks.Name } semTable, err := semantics.Analyze(sel, ksName, vschema) diff --git a/go/vt/vtgate/planbuilder/show.go b/go/vt/vtgate/planbuilder/show.go index 82035adaa87..40cf7b2411f 100644 --- a/go/vt/vtgate/planbuilder/show.go +++ b/go/vt/vtgate/planbuilder/show.go @@ -676,7 +676,7 @@ func buildVschemaKeyspacesPlan(vschema plancontext.VSchema) (engine.Primitive, e func buildVschemaTablesPlan(vschema plancontext.VSchema) (engine.Primitive, error) { vs := vschema.GetVSchema() - ks, err := vschema.DefaultKeyspace() + ks, err := vschema.SelectedKeyspace() if err != nil { return nil, err } diff --git a/go/vt/vtgate/planbuilder/show_test.go b/go/vt/vtgate/planbuilder/show_test.go index bfdb9a623a0..c3651aaa1cd 100644 --- a/go/vt/vtgate/planbuilder/show_test.go +++ b/go/vt/vtgate/planbuilder/show_test.go @@ -32,10 +32,13 @@ import ( ) func TestBuildDBPlan(t *testing.T) { - vschema := &vschemawrapper.VSchemaWrapper{ - Keyspace: &vindexes.Keyspace{Name: "main"}, - Env: vtenv.NewTestEnv(), - } + env := vtenv.NewTestEnv() + vschema := loadSchema(t, "vschemas/schema.json", true) + vw, err := vschemawrapper.NewVschemaWrapper(env, vschema, TestBuilder) + require.NoError(t, err) + + vw.Vcursor.SetTarget("main") + vw.Keyspace = &vindexes.Keyspace{Name: "main"} testCases := []struct { query string @@ -54,7 +57,7 @@ func TestBuildDBPlan(t *testing.T) { require.NoError(t, err) show := parserOut.(*sqlparser.Show) - primitive, err := buildDBPlan(show.Internal.(*sqlparser.ShowBasic), vschema) + primitive, err := buildDBPlan(show.Internal.(*sqlparser.ShowBasic), vw) require.NoError(t, err) result, err := primitive.TryExecute(context.Background(), nil, nil, false) diff --git a/go/vt/vtgate/planbuilder/simplifier_test.go b/go/vt/vtgate/planbuilder/simplifier_test.go index 305c18896e3..dce21b3e175 100644 --- a/go/vt/vtgate/planbuilder/simplifier_test.go +++ b/go/vt/vtgate/planbuilder/simplifier_test.go @@ -38,21 +38,21 @@ func TestSimplifyBuggyQuery(t *testing.T) { query := "select distinct count(distinct a), count(distinct 4) from user left join unsharded on 0 limit 5" // select 0 from unsharded union select 0 from `user` union select 0 from unsharded // select 0 from unsharded union (select 0 from `user` union select 0 from unsharded) - vschema := &vschemawrapper.VSchemaWrapper{ - V: loadSchema(t, "vschemas/schema.json", true), - Version: Gen4, - Env: vtenv.NewTestEnv(), - } + env := vtenv.NewTestEnv() + vschema := loadSchema(t, "vschemas/schema.json", true) + vw, err := vschemawrapper.NewVschemaWrapper(env, vschema, TestBuilder) + require.NoError(t, err) + stmt, reserved, err := sqlparser.NewTestParser().Parse2(query) require.NoError(t, err) - rewritten, _ := sqlparser.RewriteAST(sqlparser.Clone(stmt), vschema.CurrentDb(), sqlparser.SQLSelectLimitUnset, "", nil, nil, nil) + rewritten, _ := sqlparser.RewriteAST(sqlparser.Clone(stmt), vw.CurrentDb(), sqlparser.SQLSelectLimitUnset, "", nil, nil, nil) reservedVars := sqlparser.NewReservedVars("vtg", reserved) simplified := simplifier.SimplifyStatement( stmt.(sqlparser.SelectStatement), - vschema.CurrentDb(), - vschema, - keepSameError(query, reservedVars, vschema, rewritten.BindVarNeeds), + vw.CurrentDb(), + vw, + keepSameError(query, reservedVars, vw, rewritten.BindVarNeeds), ) fmt.Println(sqlparser.String(simplified)) @@ -61,21 +61,22 @@ func TestSimplifyBuggyQuery(t *testing.T) { func TestSimplifyPanic(t *testing.T) { t.Skip("not needed to run") query := "(select id from unsharded union select id from unsharded_auto) union (select id from unsharded_auto union select name from unsharded)" - vschema := &vschemawrapper.VSchemaWrapper{ - V: loadSchema(t, "vschemas/schema.json", true), - Version: Gen4, - Env: vtenv.NewTestEnv(), - } + + env := vtenv.NewTestEnv() + vschema := loadSchema(t, "vschemas/schema.json", true) + vw, err := vschemawrapper.NewVschemaWrapper(env, vschema, TestBuilder) + require.NoError(t, err) + stmt, reserved, err := sqlparser.NewTestParser().Parse2(query) require.NoError(t, err) - rewritten, _ := sqlparser.RewriteAST(sqlparser.Clone(stmt), vschema.CurrentDb(), sqlparser.SQLSelectLimitUnset, "", nil, nil, nil) + rewritten, _ := sqlparser.RewriteAST(sqlparser.Clone(stmt), vw.CurrentDb(), sqlparser.SQLSelectLimitUnset, "", nil, nil, nil) reservedVars := sqlparser.NewReservedVars("vtg", reserved) simplified := simplifier.SimplifyStatement( stmt.(sqlparser.SelectStatement), - vschema.CurrentDb(), - vschema, - keepPanicking(query, reservedVars, vschema, rewritten.BindVarNeeds), + vw.CurrentDb(), + vw, + keepPanicking(query, reservedVars, vw, rewritten.BindVarNeeds), ) fmt.Println(sqlparser.String(simplified)) @@ -83,11 +84,11 @@ func TestSimplifyPanic(t *testing.T) { func TestUnsupportedFile(t *testing.T) { t.Skip("run manually to see if any queries can be simplified") - vschema := &vschemawrapper.VSchemaWrapper{ - V: loadSchema(t, "vschemas/schema.json", true), - Version: Gen4, - Env: vtenv.NewTestEnv(), - } + env := vtenv.NewTestEnv() + vschema := loadSchema(t, "vschemas/schema.json", true) + vw, err := vschemawrapper.NewVschemaWrapper(env, vschema, TestBuilder) + require.NoError(t, err) + fmt.Println(vschema) for _, tcase := range readJSONTests("unsupported_cases.txt") { t.Run(tcase.Query, func(t *testing.T) { @@ -99,11 +100,10 @@ func TestUnsupportedFile(t *testing.T) { t.Skip() return } - rewritten, err := sqlparser.RewriteAST(stmt, vschema.CurrentDb(), sqlparser.SQLSelectLimitUnset, "", nil, nil, nil) + rewritten, err := sqlparser.RewriteAST(stmt, vw.CurrentDb(), sqlparser.SQLSelectLimitUnset, "", nil, nil, nil) if err != nil { t.Skip() } - vschema.CurrentDb() reservedVars := sqlparser.NewReservedVars("vtg", reserved) ast := rewritten.AST @@ -111,9 +111,9 @@ func TestUnsupportedFile(t *testing.T) { stmt, _, _ = sqlparser.NewTestParser().Parse2(tcase.Query) simplified := simplifier.SimplifyStatement( stmt.(sqlparser.SelectStatement), - vschema.CurrentDb(), - vschema, - keepSameError(tcase.Query, reservedVars, vschema, rewritten.BindVarNeeds), + vw.CurrentDb(), + vw, + keepSameError(tcase.Query, reservedVars, vw, rewritten.BindVarNeeds), ) if simplified == nil { @@ -135,12 +135,12 @@ func keepSameError(query string, reservedVars *sqlparser.ReservedVars, vschema * } 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) + _, expected := BuildFromStmt(context.Background(), query, ast, reservedVars, vschema, rewritten.BindVarNeeds, staticConfig{}) if expected == nil { panic("query does not fail to plan") } return func(statement sqlparser.SelectStatement) bool { - _, myErr := BuildFromStmt(context.Background(), query, statement, reservedVars, vschema, needs, true, true) + _, myErr := BuildFromStmt(context.Background(), query, statement, reservedVars, vschema, needs, staticConfig{}) if myErr == nil { return false } @@ -162,7 +162,7 @@ func keepPanicking(query string, reservedVars *sqlparser.ReservedVars, vschema * } }() log.Errorf("trying %s", sqlparser.String(statement)) - _, _ = BuildFromStmt(context.Background(), query, statement, reservedVars, vschema, needs, true, true) + _, _ = BuildFromStmt(context.Background(), query, statement, reservedVars, vschema, needs, staticConfig{}) log.Errorf("did not panic") return false diff --git a/go/vt/vtgate/planbuilder/test_helper.go b/go/vt/vtgate/planbuilder/test_helper.go new file mode 100644 index 00000000000..25d6b7306d1 --- /dev/null +++ b/go/vt/vtgate/planbuilder/test_helper.go @@ -0,0 +1,27 @@ +/* +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 planbuilder + +import "encoding/json" + +type PlanTest struct { + Comment string `json:"comment,omitempty"` + Query string `json:"query,omitempty"` + Plan json.RawMessage `json:"plan,omitempty"` + Skip bool `json:"skip,omitempty"` + SkipE2E bool `json:"skip_e2e,omitempty"` +} diff --git a/go/vt/vtgate/planbuilder/testdata/aggr_cases.json b/go/vt/vtgate/planbuilder/testdata/aggr_cases.json index 8b268e367dd..49a03a8f05a 100644 --- a/go/vt/vtgate/planbuilder/testdata/aggr_cases.json +++ b/go/vt/vtgate/planbuilder/testdata/aggr_cases.json @@ -940,19 +940,44 @@ "Table": "`user`, user_extra" }, { - "OperatorType": "Route", + "OperatorType": "VindexLookup", "Variant": "EqualUnique", "Keyspace": { "Name": "user", "Sharded": true }, - "FieldQuery": "select music.`name` from music where 1 != 1", - "Query": "select music.`name` from music where music.id = :user_id", - "Table": "music", "Values": [ ":user_id" ], - "Vindex": "music_user_map" + "Vindex": "music_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 music.`name` from music where 1 != 1", + "Query": "select music.`name` from music where music.id = :user_id", + "Table": "music" + } + ] } ] } @@ -2992,19 +3017,44 @@ ] }, { - "OperatorType": "Route", + "OperatorType": "VindexLookup", "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.id = :u2_val2", - "Table": "music", "Values": [ ":u2_val2" ], - "Vindex": "music_user_map" + "Vindex": "music_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 1 from music as m where 1 != 1", + "Query": "select 1 from music as m where m.id = :u2_val2", + "Table": "music" + } + ] } ] } diff --git a/go/vt/vtgate/planbuilder/testdata/dml_cases_with_user_as_default.json b/go/vt/vtgate/planbuilder/testdata/dml_cases_with_user_as_default.json new file mode 100644 index 00000000000..ff66967c2ce --- /dev/null +++ b/go/vt/vtgate/planbuilder/testdata/dml_cases_with_user_as_default.json @@ -0,0 +1,24 @@ +[ + { + "comment": "Update reference table from sharded keyspace to unsharded keyspace", + "query": "update ambiguous_ref_with_source set done = true where id = 1;", + "plan": { + "QueryType": "UPDATE", + "Original": "update ambiguous_ref_with_source set done = true where id = 1;", + "Instructions": { + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "Query": "update ambiguous_ref_with_source set done = true where id = 1", + "Table": "ambiguous_ref_with_source" + }, + "TablesUsed": [ + "main.ambiguous_ref_with_source" + ] + } + } +] \ No newline at end of file diff --git a/go/vt/vtgate/planbuilder/testdata/filter_cases.json b/go/vt/vtgate/planbuilder/testdata/filter_cases.json index 4194a369bd6..edce4ebd0cb 100644 --- a/go/vt/vtgate/planbuilder/testdata/filter_cases.json +++ b/go/vt/vtgate/planbuilder/testdata/filter_cases.json @@ -3404,19 +3404,44 @@ "QueryType": "SELECT", "Original": "select * from multicolvin where column_b = 1", "Instructions": { - "OperatorType": "Route", + "OperatorType": "VindexLookup", "Variant": "EqualUnique", "Keyspace": { "Name": "user", "Sharded": true }, - "FieldQuery": "select * from multicolvin where 1 != 1", - "Query": "select * from multicolvin where column_b = 1", - "Table": "multicolvin", "Values": [ "1" ], - "Vindex": "colb_colc_map" + "Vindex": "colb_colc_map", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "IN", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select colb, keyspace_id from colb_colc_map where 1 != 1", + "Query": "select colb, keyspace_id from colb_colc_map where colb in ::__vals", + "Table": "colb_colc_map", + "Values": [ + "::colb" + ], + "Vindex": "hash" + }, + { + "OperatorType": "Route", + "Variant": "ByDestination", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select * from multicolvin where 1 != 1", + "Query": "select * from multicolvin where column_b = 1", + "Table": "multicolvin" + } + ] }, "TablesUsed": [ "user.multicolvin" @@ -3430,19 +3455,44 @@ "QueryType": "SELECT", "Original": "select * from multicolvin where column_b = 1 and column_c = 2", "Instructions": { - "OperatorType": "Route", + "OperatorType": "VindexLookup", "Variant": "EqualUnique", "Keyspace": { "Name": "user", "Sharded": true }, - "FieldQuery": "select * from multicolvin where 1 != 1", - "Query": "select * from multicolvin where column_b = 1 and column_c = 2", - "Table": "multicolvin", "Values": [ "1" ], - "Vindex": "colb_colc_map" + "Vindex": "colb_colc_map", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "IN", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select colb, keyspace_id from colb_colc_map where 1 != 1", + "Query": "select colb, keyspace_id from colb_colc_map where colb in ::__vals", + "Table": "colb_colc_map", + "Values": [ + "::colb" + ], + "Vindex": "hash" + }, + { + "OperatorType": "Route", + "Variant": "ByDestination", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select * from multicolvin where 1 != 1", + "Query": "select * from multicolvin where column_b = 1 and column_c = 2", + "Table": "multicolvin" + } + ] }, "TablesUsed": [ "user.multicolvin" @@ -3456,19 +3506,44 @@ "QueryType": "SELECT", "Original": "select * from multicolvin where column_b = 1 and column_c = 2 and column_a = 3", "Instructions": { - "OperatorType": "Route", + "OperatorType": "VindexLookup", "Variant": "EqualUnique", "Keyspace": { "Name": "user", "Sharded": true }, - "FieldQuery": "select * from multicolvin where 1 != 1", - "Query": "select * from multicolvin where column_b = 1 and column_c = 2 and column_a = 3", - "Table": "multicolvin", "Values": [ "1" ], - "Vindex": "colb_colc_map" + "Vindex": "colb_colc_map", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "IN", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select colb, keyspace_id from colb_colc_map where 1 != 1", + "Query": "select colb, keyspace_id from colb_colc_map where colb in ::__vals", + "Table": "colb_colc_map", + "Values": [ + "::colb" + ], + "Vindex": "hash" + }, + { + "OperatorType": "Route", + "Variant": "ByDestination", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select * from multicolvin where 1 != 1", + "Query": "select * from multicolvin where column_b = 1 and column_c = 2 and column_a = 3", + "Table": "multicolvin" + } + ] }, "TablesUsed": [ "user.multicolvin" @@ -3482,19 +3557,44 @@ "QueryType": "SELECT", "Original": "select * from multicolvin where column_a = 3 and column_b = 1", "Instructions": { - "OperatorType": "Route", + "OperatorType": "VindexLookup", "Variant": "EqualUnique", "Keyspace": { "Name": "user", "Sharded": true }, - "FieldQuery": "select * from multicolvin where 1 != 1", - "Query": "select * from multicolvin where column_a = 3 and column_b = 1", - "Table": "multicolvin", "Values": [ "1" ], - "Vindex": "colb_colc_map" + "Vindex": "colb_colc_map", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "IN", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select colb, keyspace_id from colb_colc_map where 1 != 1", + "Query": "select colb, keyspace_id from colb_colc_map where colb in ::__vals", + "Table": "colb_colc_map", + "Values": [ + "::colb" + ], + "Vindex": "hash" + }, + { + "OperatorType": "Route", + "Variant": "ByDestination", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select * from multicolvin where 1 != 1", + "Query": "select * from multicolvin where column_a = 3 and column_b = 1", + "Table": "multicolvin" + } + ] }, "TablesUsed": [ "user.multicolvin" diff --git a/go/vt/vtgate/planbuilder/testdata/from_cases.json b/go/vt/vtgate/planbuilder/testdata/from_cases.json index 2e0fe429c1f..bec64fd7b1e 100644 --- a/go/vt/vtgate/planbuilder/testdata/from_cases.json +++ b/go/vt/vtgate/planbuilder/testdata/from_cases.json @@ -4709,19 +4709,44 @@ ] }, { - "OperatorType": "Route", + "OperatorType": "VindexLookup", "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 /* INT16 */", - "Table": "music", "Values": [ "20" ], - "Vindex": "music_user_map" + "Vindex": "music_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 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 /* INT16 */", + "Table": "music" + } + ] } ] }, diff --git a/go/vt/vtgate/planbuilder/testdata/memory_sort_cases.json b/go/vt/vtgate/planbuilder/testdata/memory_sort_cases.json index 060f073a366..a35949cd4c1 100644 --- a/go/vt/vtgate/planbuilder/testdata/memory_sort_cases.json +++ b/go/vt/vtgate/planbuilder/testdata/memory_sort_cases.json @@ -318,19 +318,44 @@ "Vindex": "user_index" }, { - "OperatorType": "Route", + "OperatorType": "VindexLookup", "Variant": "EqualUnique", "Keyspace": { "Name": "user", "Sharded": true }, - "FieldQuery": "select music.col3 as c, weight_string(music.col3) from music where 1 != 1", - "Query": "select music.col3 as c, weight_string(music.col3) from music where music.id = :user_id", - "Table": "music", "Values": [ ":user_id" ], - "Vindex": "music_user_map" + "Vindex": "music_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 music.col3 as c, weight_string(music.col3) from music where 1 != 1", + "Query": "select music.col3 as c, weight_string(music.col3) from music where music.id = :user_id", + "Table": "music" + } + ] } ] } @@ -379,19 +404,44 @@ "Vindex": "user_index" }, { - "OperatorType": "Route", + "OperatorType": "VindexLookup", "Variant": "EqualUnique", "Keyspace": { "Name": "user", "Sharded": true }, - "FieldQuery": "select music.col3, weight_string(music.col3) from music where 1 != 1", - "Query": "select music.col3, weight_string(music.col3) from music where music.id = :user_id", - "Table": "music", "Values": [ ":user_id" ], - "Vindex": "music_user_map" + "Vindex": "music_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 music.col3, weight_string(music.col3) from music where 1 != 1", + "Query": "select music.col3, weight_string(music.col3) from music where music.id = :user_id", + "Table": "music" + } + ] } ] } diff --git a/go/vt/vtgate/planbuilder/testdata/postprocess_cases.json b/go/vt/vtgate/planbuilder/testdata/postprocess_cases.json index 36f1472007d..6a8e94c0241 100644 --- a/go/vt/vtgate/planbuilder/testdata/postprocess_cases.json +++ b/go/vt/vtgate/planbuilder/testdata/postprocess_cases.json @@ -544,19 +544,44 @@ "Vindex": "user_index" }, { - "OperatorType": "Route", + "OperatorType": "VindexLookup", "Variant": "EqualUnique", "Keyspace": { "Name": "user", "Sharded": true }, - "FieldQuery": "select music.col3 from music where 1 != 1", - "Query": "select music.col3 from music where music.id = :user_id", - "Table": "music", "Values": [ ":user_id" ], - "Vindex": "music_user_map" + "Vindex": "music_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 music.col3 from music where 1 != 1", + "Query": "select music.col3 from music where music.id = :user_id", + "Table": "music" + } + ] } ] }, @@ -597,19 +622,44 @@ "Vindex": "user_index" }, { - "OperatorType": "Route", + "OperatorType": "VindexLookup", "Variant": "EqualUnique", "Keyspace": { "Name": "user", "Sharded": true }, - "FieldQuery": "select music.col3 from music where 1 != 1", - "Query": "select music.col3 from music where music.id = :user_id", - "Table": "music", "Values": [ ":user_id" ], - "Vindex": "music_user_map" + "Vindex": "music_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 music.col3 from music where 1 != 1", + "Query": "select music.col3 from music where music.id = :user_id", + "Table": "music" + } + ] } ] }, @@ -650,19 +700,44 @@ "Vindex": "user_index" }, { - "OperatorType": "Route", + "OperatorType": "VindexLookup", "Variant": "EqualUnique", "Keyspace": { "Name": "user", "Sharded": true }, - "FieldQuery": "select music.col3 from music where 1 != 1", - "Query": "select music.col3 from music where music.id = :user_id", - "Table": "music", "Values": [ ":user_id" ], - "Vindex": "music_user_map" + "Vindex": "music_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 music.col3 from music where 1 != 1", + "Query": "select music.col3 from music where music.id = :user_id", + "Table": "music" + } + ] } ] }, @@ -770,19 +845,44 @@ "Vindex": "user_index" }, { - "OperatorType": "Route", + "OperatorType": "VindexLookup", "Variant": "EqualUnique", "Keyspace": { "Name": "user", "Sharded": true }, - "FieldQuery": "select music.col3 from music where 1 != 1", - "Query": "select music.col3 from music where music.id = :user_id", - "Table": "music", "Values": [ ":user_id" ], - "Vindex": "music_user_map" + "Vindex": "music_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 music.col3 from music where 1 != 1", + "Query": "select music.col3 from music where music.id = :user_id", + "Table": "music" + } + ] } ] }, diff --git a/go/vt/vtgate/planbuilder/testdata/schemas/main.sql b/go/vt/vtgate/planbuilder/testdata/schemas/main.sql new file mode 100644 index 00000000000..8c15b99218c --- /dev/null +++ b/go/vt/vtgate/planbuilder/testdata/schemas/main.sql @@ -0,0 +1,12 @@ +CREATE TABLE `unsharded` ( + `id` INT NOT NULL PRIMARY KEY, + `col1` VARCHAR(255) DEFAULT NULL, + `col2` VARCHAR(255) DEFAULT NULL, + `name` VARCHAR(255) DEFAULT NULL +); + +CREATE TABLE `unsharded_auto` ( + `id` INT NOT NULL PRIMARY KEY, + `col1` VARCHAR(255) DEFAULT NULL, + `col2` VARCHAR(255) DEFAULT NULL +); \ No newline at end of file diff --git a/go/vt/vtgate/planbuilder/testdata/schemas/user.sql b/go/vt/vtgate/planbuilder/testdata/schemas/user.sql new file mode 100644 index 00000000000..55f4078557a --- /dev/null +++ b/go/vt/vtgate/planbuilder/testdata/schemas/user.sql @@ -0,0 +1,100 @@ +CREATE TABLE user +( + id INT PRIMARY KEY, + col BIGINT, + predef1 VARCHAR(255), + predef2 VARCHAR(255), + textcol1 VARCHAR(255), + intcol BIGINT, + textcol2 VARCHAR(255) +); + +CREATE TABLE user_metadata +( + user_id INT, + email VARCHAR(255), + address VARCHAR(255), + md5 VARCHAR(255), + non_planable VARCHAR(255), + PRIMARY KEY (user_id) +); + +CREATE TABLE music +( + user_id INT, + id INT, + PRIMARY KEY (user_id) +); + +CREATE TABLE samecolvin +( + col VARCHAR(255), + PRIMARY KEY (col) +); + +CREATE TABLE multicolvin +( + kid INT, + column_a VARCHAR(255), + column_b VARCHAR(255), + column_c VARCHAR(255), + PRIMARY KEY (kid) +); + +CREATE TABLE customer +( + id INT, + email VARCHAR(255), + phone VARCHAR(255), + PRIMARY KEY (id) +); + +CREATE TABLE multicol_tbl +( + cola VARCHAR(255), + colb VARCHAR(255), + colc VARCHAR(255), + name VARCHAR(255), + PRIMARY KEY (cola, colb) +); + +CREATE TABLE mixed_tbl +( + shard_key VARCHAR(255), + lkp_key VARCHAR(255), + PRIMARY KEY (shard_key) +); + +CREATE TABLE pin_test +( + id INT PRIMARY KEY +); + +CREATE TABLE cfc_vindex_col +( + c1 VARCHAR(255), + c2 VARCHAR(255), + PRIMARY KEY (c1) +); + +CREATE TABLE unq_lkp_idx +( + unq_key INT PRIMARY KEY, + keyspace_id VARCHAR(255) +); + +CREATE TABLE t1 +( + c1 INT, + c2 INT, + c3 INT, + PRIMARY KEY (c1) +); + +CREATE TABLE authoritative +( + user_id bigint NOT NULL, + col1 VARCHAR(255), + col2 bigint, + PRIMARY KEY (user_id) +) ENGINE=InnoDB; \ No newline at end of file diff --git a/go/vt/vtgate/planbuilder/testdata/select_cases.json b/go/vt/vtgate/planbuilder/testdata/select_cases.json index ab69df2cc47..eac13216380 100644 --- a/go/vt/vtgate/planbuilder/testdata/select_cases.json +++ b/go/vt/vtgate/planbuilder/testdata/select_cases.json @@ -92,7 +92,8 @@ "user.user", "user.user_metadata" ] - } + }, + "skip_e2e": true }, { "comment": "select with timeout directive sets QueryTimeout in the route", @@ -197,7 +198,8 @@ "TablesUsed": [ "main.unsharded" ] - } + }, + "skip_e2e": true }, { "comment": "select with partial scatter directive", @@ -402,7 +404,8 @@ { "comment": "test table lookup failure for authoritative code path", "query": "select a.* from authoritative", - "plan": "Unknown table 'a'" + "plan": "Unknown table 'a'", + "skip_e2e": true }, { "comment": "select * from qualified authoritative table", @@ -470,7 +473,8 @@ "user.authoritative", "user.user" ] - } + }, + "skip_e2e": true }, { "comment": "auto-resolve anonymous columns for simple route", @@ -493,7 +497,8 @@ "user.user", "user.user_extra" ] - } + }, + "skip_e2e": true }, { "comment": "json_arrayagg in single sharded query", @@ -519,7 +524,8 @@ "TablesUsed": [ "user.user" ] - } + }, + "skip_e2e": true }, { "comment": "json_objectagg in single sharded query", @@ -545,17 +551,20 @@ "TablesUsed": [ "user.user" ] - } + }, + "skip_e2e": true }, { "comment": "unsupported json aggregation expressions in scatter query", "query": "select count(1) from user where cola = 'abc' group by n_id having json_arrayagg(a_id) = '[]'", - "plan": "VT12001: unsupported: in scatter query: aggregation function 'json_arrayagg(a_id)'" + "plan": "VT12001: unsupported: in scatter query: aggregation function 'json_arrayagg(a_id)'", + "skip_e2e": true }, { "comment": "Cannot auto-resolve for cross-shard joins", "query": "select col from user join user_extra", - "plan": "Column 'col' in field list is ambiguous" + "plan": "Column 'col' in field list is ambiguous", + "skip_e2e": true }, { "comment": "Auto-resolve should work if unique vindex columns are referenced", @@ -597,7 +606,8 @@ "user.user", "user.user_extra" ] - } + }, + "skip_e2e": true }, { "comment": "database calls should be substituted", @@ -619,7 +629,8 @@ "TablesUsed": [ "main.dual" ] - } + }, + "skip_e2e": true }, { "comment": "last_insert_id for unsharded route", @@ -641,7 +652,8 @@ "TablesUsed": [ "main.unsharded" ] - } + }, + "skip_e2e": true }, { "comment": "select from dual on unqualified keyspace", @@ -694,7 +706,8 @@ { "comment": "prefixing dual with a keyspace should not work", "query": "select 1 from user.dual", - "plan": "table dual not found" + "plan": "table dual not found", + "skip_e2e": true }, { "comment": "RHS route referenced", @@ -736,7 +749,8 @@ "user.user", "user.user_extra" ] - } + }, + "skip_e2e": true }, { "comment": "Both routes referenced", @@ -778,7 +792,8 @@ "user.user", "user.user_extra" ] - } + }, + "skip_e2e": true }, { "comment": "Expression with single-route reference", @@ -820,7 +835,8 @@ "user.user", "user.user_extra" ] - } + }, + "skip_e2e": true }, { "comment": "subquery with an aggregation in order by that can be merged into a single route", @@ -847,7 +863,8 @@ "user.user", "user.user_extra" ] - } + }, + "skip_e2e": true }, { "comment": "Jumbled references", @@ -889,7 +906,8 @@ "user.user", "user.user_extra" ] - } + }, + "skip_e2e": true }, { "comment": "Comments", @@ -931,7 +949,8 @@ "user.user", "user.user_extra" ] - } + }, + "skip_e2e": true }, { "comment": "for update", @@ -973,7 +992,8 @@ "user.user", "user.user_extra" ] - } + }, + "skip_e2e": true }, { "comment": "Field query should work for joins select bind vars", @@ -1018,7 +1038,8 @@ "main.unsharded", "user.user" ] - } + }, + "skip_e2e": true }, { "comment": "Case preservation", @@ -1060,12 +1081,14 @@ "user.user", "user.user_extra" ] - } + }, + "skip_e2e": true }, { "comment": "syntax error", "query": "the quick brown fox", - "plan": "syntax error at position 4 near 'the'" + "plan": "syntax error at position 4 near 'the'", + "skip_e2e": true }, { "comment": "Hex number is not treated as a simple value", @@ -1113,7 +1136,8 @@ "TablesUsed": [ "user.user" ] - } + }, + "skip_e2e": true }, { "comment": "Selection but make the planner explicitly use a vindex", @@ -1164,12 +1188,14 @@ "TablesUsed": [ "user.user" ] - } + }, + "skip_e2e": true }, { "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'" + "plan": "VT09021: Vindex 'does_not_exist' does not exist in table 'user.user'", + "skip_e2e": true }, { "comment": "sharded limit offset", @@ -1231,7 +1257,8 @@ "TablesUsed": [ "user.music" ] - } + }, + "skip_e2e": true }, { "comment": "Sharding Key Condition in Parenthesis", @@ -1257,7 +1284,8 @@ "TablesUsed": [ "user.user" ] - } + }, + "skip_e2e": true }, { "comment": "Multiple parenthesized expressions", @@ -1283,7 +1311,8 @@ "TablesUsed": [ "user.user" ] - } + }, + "skip_e2e": true }, { "comment": "Multiple parenthesized expressions", @@ -1309,7 +1338,8 @@ "TablesUsed": [ "user.user" ] - } + }, + "skip_e2e": true }, { "comment": "Column Aliasing with Table.Column", @@ -1387,7 +1417,8 @@ "TablesUsed": [ "user.user" ] - } + }, + "skip_e2e": true }, { "comment": "Column as boolean-ish", @@ -1413,7 +1444,8 @@ "TablesUsed": [ "user.user" ] - } + }, + "skip_e2e": true }, { "comment": "PK as fake boolean, and column as boolean-ish", @@ -1439,7 +1471,8 @@ "TablesUsed": [ "user.user" ] - } + }, + "skip_e2e": true }, { "comment": "top level subquery in select", @@ -1484,7 +1517,8 @@ "main.unsharded", "user.user" ] - } + }, + "skip_e2e": true }, { "comment": "sub-expression subquery in select", @@ -1529,7 +1563,8 @@ "main.unsharded", "user.user" ] - } + }, + "skip_e2e": true }, { "comment": "select * from derived table expands specific columns", @@ -1571,17 +1606,20 @@ "user.user", "user.user_extra" ] - } + }, + "skip_e2e": true }, { "comment": "duplicate columns not allowed in derived table", "query": "select * from (select user.id, user_extra.id from user join user_extra) as t", - "plan": "Duplicate column name 'id'" + "plan": "Duplicate column name 'id'", + "skip_e2e": true }, { "comment": "non-existent symbol in cross-shard derived table", "query": "select t.col from (select user.id from user join user_extra) as t", - "plan": "column 't.col' not found" + "plan": "column 't.col' not found", + "skip_e2e": true }, { "comment": "union with the same target shard", @@ -1608,7 +1646,8 @@ "user.music", "user.user" ] - } + }, + "skip_e2e": true }, { "comment": "union with the same target shard last_insert_id", @@ -1635,7 +1674,8 @@ "user.music", "user.user" ] - } + }, + "skip_e2e": true }, { "comment": "unsharded union in derived table", @@ -1793,7 +1833,8 @@ "TablesUsed": [ "main.unsharded" ] - } + }, + "skip_e2e": true }, { "comment": "routing table on music", @@ -1815,7 +1856,8 @@ "TablesUsed": [ "user.music" ] - } + }, + "skip_e2e": true }, { "comment": "testing SingleRow Projection", @@ -1962,7 +2004,8 @@ "main.unsharded_a", "user.user" ] - } + }, + "skip_e2e": true }, { "comment": "Complex expression in a subquery used in NOT IN clause of an aggregate query", @@ -2015,7 +2058,8 @@ "main.unsharded_a", "user.user" ] - } + }, + "skip_e2e": true }, { "comment": "testing SingleRow Projection with arithmetics", @@ -2218,12 +2262,14 @@ { "comment": "sql_calc_found_rows in sub queries", "query": "select * from music where user_id IN (select sql_calc_found_rows * from music limit 10)", - "plan": "Incorrect usage/placement of 'SQL_CALC_FOUND_ROWS'" + "plan": "Incorrect usage/placement of 'SQL_CALC_FOUND_ROWS'", + "skip_e2e": true }, { "comment": "sql_calc_found_rows in derived table", "query": "select sql_calc_found_rows * from (select sql_calc_found_rows * from music limit 10) t limit 1", - "plan": "Incorrect usage/placement of 'SQL_CALC_FOUND_ROWS'" + "plan": "Incorrect usage/placement of 'SQL_CALC_FOUND_ROWS'", + "skip_e2e": true }, { "comment": "select from unsharded keyspace into dumpfile", @@ -2245,7 +2291,8 @@ "TablesUsed": [ "main.unsharded" ] - } + }, + "skip_e2e": true }, { "comment": "select from unsharded keyspace into outfile", @@ -2267,7 +2314,8 @@ "TablesUsed": [ "main.unsharded" ] - } + }, + "skip_e2e": true }, { "comment": "select from unsharded keyspace into outfile s3", @@ -2289,7 +2337,8 @@ "TablesUsed": [ "main.unsharded" ] - } + }, + "skip_e2e": true }, { "comment": "left join with a dual table on left - merge-able", @@ -2500,17 +2549,20 @@ { "comment": "Union after into outfile is incorrect", "query": "select id from user into outfile 'out_file_name' union all select id from music", - "plan": "syntax error at position 55 near 'union'" + "plan": "syntax error at position 55 near 'union'", + "skip_e2e": true }, { "comment": "Into outfile s3 in derived table is incorrect", "query": "select id from (select id from user into outfile s3 'inner_outfile') as t2", - "plan": "syntax error at position 41 near 'into'" + "plan": "syntax error at position 41 near 'into'", + "skip_e2e": true }, { "comment": "Into outfile s3 in derived table with union incorrect", "query": "select id from (select id from user into outfile s3 'inner_outfile' union select 1) as t2", - "plan": "syntax error at position 41 near 'into'" + "plan": "syntax error at position 41 near 'into'", + "skip_e2e": true }, { "comment": "select (select u.id from user as u where u.id = 1), a.id from user as a where a.id = 1", @@ -2579,7 +2631,8 @@ "user.user", "user.user_extra" ] - } + }, + "skip_e2e": true }, { "comment": "((((select 1))))", @@ -2624,7 +2677,8 @@ "main.dual", "user.user" ] - } + }, + "skip_e2e": true }, { "comment": "subquery in select expression of derived table", @@ -2694,7 +2748,8 @@ "user.user", "user.user_extra" ] - } + }, + "skip_e2e": true }, { "comment": "ORDER BY subquery", @@ -2764,7 +2819,8 @@ "user.user", "user.user_extra" ] - } + }, + "skip_e2e": true }, { "comment": "plan test for a natural character set string", @@ -2831,7 +2887,8 @@ "user.user", "user.user_extra" ] - } + }, + "skip_e2e": true }, { "comment": "Straight Join ensures specific ordering of joins", @@ -2876,7 +2933,8 @@ "user.user", "user.user_extra" ] - } + }, + "skip_e2e": true }, { "comment": "Dual query should be handled on the vtgate even with a LIMIT", @@ -2950,7 +3008,8 @@ "user.user", "user.user_extra" ] - } + }, + "skip_e2e": true }, { "comment": "Straight Join preserved in MySQL query", @@ -2973,7 +3032,8 @@ "user.user", "user.user_extra" ] - } + }, + "skip_e2e": true }, { "comment": "correlated subquery in exists clause", @@ -3031,7 +3091,8 @@ "user.user", "user.user_extra" ] - } + }, + "skip_e2e": true }, { "comment": "correlated subquery in exists clause with an order by", @@ -3090,7 +3151,8 @@ "user.user", "user.user_extra" ] - } + }, + "skip_e2e": true }, { "comment": "correlated subquery having dependencies on two tables", @@ -3163,7 +3225,8 @@ "user.user", "user.user_extra" ] - } + }, + "skip_e2e": true }, { "comment": "correlated subquery using a column twice", @@ -3220,7 +3283,8 @@ "user.user", "user.user_extra" ] - } + }, + "skip_e2e": true }, { "comment": "correlated subquery that is dependent on one side of a join, fully mergeable", @@ -3271,7 +3335,8 @@ "user.user", "user.user_extra" ] - } + }, + "skip_e2e": true }, { "comment": "union as a derived table", @@ -3360,7 +3425,8 @@ "user.user", "user.user_extra" ] - } + }, + "skip_e2e": true }, { "comment": "mergeable derived table with order by and limit", @@ -3382,7 +3448,8 @@ "TablesUsed": [ "main.unsharded" ] - } + }, + "skip_e2e": true }, { "comment": "mergeable derived table with group by and limit", @@ -3404,7 +3471,8 @@ "TablesUsed": [ "main.unsharded" ] - } + }, + "skip_e2e": true }, { "comment": "select user.id, trim(leading 'x' from user.name) from user", @@ -3426,7 +3494,8 @@ "TablesUsed": [ "user.user" ] - } + }, + "skip_e2e": true }, { "comment": "json utility functions", @@ -3448,7 +3517,8 @@ "TablesUsed": [ "user.user" ] - } + }, + "skip_e2e": true }, { "comment": "dual query with exists clause", @@ -3546,7 +3616,8 @@ "user.user", "user.user_extra" ] - } + }, + "skip_e2e": true }, { "comment": "yeah, it does not make sense, but it's valid", @@ -3639,7 +3710,8 @@ "TablesUsed": [ "user.user" ] - } + }, + "skip_e2e": true }, { "comment": "groupe by with non aggregated columns and table alias", @@ -3661,7 +3733,8 @@ "TablesUsed": [ "user.user" ] - } + }, + "skip_e2e": true }, { "comment": "Functions that return JSON value attributes", @@ -3866,7 +3939,8 @@ "TablesUsed": [ "user.user" ] - } + }, + "skip_e2e": true }, { "comment": "gtid functions", @@ -3934,7 +4008,8 @@ "user.user_extra", "user.user_metadata" ] - } + }, + "skip_e2e": true }, { "comment": "Join across multiple tables, with conditions on different vindexes, but mergeable through join predicates", @@ -3962,7 +4037,8 @@ "user.music_extra", "user.user" ] - } + }, + "skip_e2e": true }, { "comment": "SQL_CALC_FOUND_ROWS with vindex lookup", @@ -4073,7 +4149,8 @@ "TablesUsed": [ "user.user" ] - } + }, + "skip_e2e": true }, { "comment": "`None` route being merged with another route via join predicate on Vindex columns", @@ -4122,7 +4199,8 @@ "TablesUsed": [ "user.music" ] - } + }, + "skip_e2e": true }, { "comment": "Subquery with `IN` condition using columns with matching lookup vindexes", @@ -4200,7 +4278,8 @@ "TablesUsed": [ "user.music" ] - } + }, + "skip_e2e": true }, { "comment": "Subquery with `IN` condition using columns with matching lookup vindexes", @@ -4314,7 +4393,8 @@ "TablesUsed": [ "user.music" ] - } + }, + "skip_e2e": true }, { "comment": "Mergeable scatter subquery with `GROUP BY` on unique vindex column", @@ -4336,7 +4416,8 @@ "TablesUsed": [ "user.music" ] - } + }, + "skip_e2e": true }, { "comment": "Unmergeable scatter subquery with `GROUP BY` on-non vindex column", @@ -4375,26 +4456,52 @@ }, { "InputName": "Outer", - "OperatorType": "Route", + "OperatorType": "VindexLookup", "Variant": "IN", "Keyspace": { "Name": "user", "Sharded": true }, - "FieldQuery": "select music.id from music where 1 != 1", - "Query": "select music.id from music where :__sq_has_values and music.id in ::__vals", - "Table": "music", "Values": [ "::__sq1" ], - "Vindex": "music_user_map" + "Vindex": "music_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 music.id from music where 1 != 1", + "Query": "select music.id from music where :__sq_has_values and music.id in ::__vals", + "Table": "music" + } + ] } ] }, "TablesUsed": [ "user.music" ] - } + }, + "skip_e2e": true }, { "comment": "Unmergeable scatter subquery with LIMIT", @@ -4430,26 +4537,52 @@ }, { "InputName": "Outer", - "OperatorType": "Route", + "OperatorType": "VindexLookup", "Variant": "IN", "Keyspace": { "Name": "user", "Sharded": true }, - "FieldQuery": "select music.id from music where 1 != 1", - "Query": "select music.id from music where :__sq_has_values and music.id in ::__vals", - "Table": "music", "Values": [ "::__sq1" ], - "Vindex": "music_user_map" + "Vindex": "music_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 music.id from music where 1 != 1", + "Query": "select music.id from music where :__sq_has_values and music.id in ::__vals", + "Table": "music" + } + ] } ] }, "TablesUsed": [ "user.music" ] - } + }, + "skip_e2e": true }, { "comment": "Mergeable subquery with `MAX` aggregate and grouped by unique vindex", @@ -4483,26 +4616,52 @@ }, { "InputName": "Outer", - "OperatorType": "Route", + "OperatorType": "VindexLookup", "Variant": "IN", "Keyspace": { "Name": "user", "Sharded": true }, - "FieldQuery": "select music.id from music where 1 != 1", - "Query": "select music.id from music where :__sq_has_values and music.id in ::__vals", - "Table": "music", "Values": [ "::__sq1" ], - "Vindex": "music_user_map" + "Vindex": "music_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 music.id from music where 1 != 1", + "Query": "select music.id from music where :__sq_has_values and music.id in ::__vals", + "Table": "music" + } + ] } ] }, "TablesUsed": [ "user.music" ] - } + }, + "skip_e2e": true }, { "comment": "Unmergeable subquery with `MAX` aggregate", @@ -4543,19 +4702,44 @@ }, { "InputName": "Outer", - "OperatorType": "Route", + "OperatorType": "VindexLookup", "Variant": "IN", "Keyspace": { "Name": "user", "Sharded": true }, - "FieldQuery": "select music.id from music where 1 != 1", - "Query": "select music.id from music where :__sq_has_values and music.id in ::__vals", - "Table": "music", "Values": [ "::__sq1" ], - "Vindex": "music_user_map" + "Vindex": "music_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 music.id from music where 1 != 1", + "Query": "select music.id from music where :__sq_has_values and music.id in ::__vals", + "Table": "music" + } + ] } ] }, @@ -4596,19 +4780,44 @@ }, { "InputName": "Outer", - "OperatorType": "Route", + "OperatorType": "VindexLookup", "Variant": "IN", "Keyspace": { "Name": "user", "Sharded": true }, - "FieldQuery": "select music.id from music where 1 != 1", - "Query": "select music.id from music where :__sq_has_values and music.id in ::__vals", - "Table": "music", "Values": [ "::__sq1" ], - "Vindex": "music_user_map" + "Vindex": "music_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 music.id from music where 1 != 1", + "Query": "select music.id from music where :__sq_has_values and music.id in ::__vals", + "Table": "music" + } + ] } ] }, @@ -4649,26 +4858,52 @@ }, { "InputName": "Outer", - "OperatorType": "Route", + "OperatorType": "VindexLookup", "Variant": "IN", "Keyspace": { "Name": "user", "Sharded": true }, - "FieldQuery": "select music.id from music where 1 != 1", - "Query": "select music.id from music where :__sq_has_values and music.id in ::__vals", - "Table": "music", "Values": [ "::__sq1" ], - "Vindex": "music_user_map" + "Vindex": "music_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 music.id from music where 1 != 1", + "Query": "select music.id from music where :__sq_has_values and music.id in ::__vals", + "Table": "music" + } + ] } ] }, "TablesUsed": [ "user.music" ] - } + }, + "skip_e2e": true }, { "comment": "Mergeable subquery with multiple levels of derived statements", @@ -4760,26 +4995,52 @@ }, { "InputName": "Outer", - "OperatorType": "Route", + "OperatorType": "VindexLookup", "Variant": "IN", "Keyspace": { "Name": "user", "Sharded": true }, - "FieldQuery": "select music.id from music where 1 != 1", - "Query": "select music.id from music where :__sq_has_values and music.id in ::__vals", - "Table": "music", "Values": [ "::__sq1" ], - "Vindex": "music_user_map" + "Vindex": "music_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 music.id from music where 1 != 1", + "Query": "select music.id from music where :__sq_has_values and music.id in ::__vals", + "Table": "music" + } + ] } ] }, "TablesUsed": [ "user.music" ] - } + }, + "skip_e2e": true }, { "comment": "Unmergeable subquery with multiple levels of derived statements", @@ -4815,26 +5076,52 @@ }, { "InputName": "Outer", - "OperatorType": "Route", + "OperatorType": "VindexLookup", "Variant": "IN", "Keyspace": { "Name": "user", "Sharded": true }, - "FieldQuery": "select music.id from music where 1 != 1", - "Query": "select music.id from music where :__sq_has_values and music.id in ::__vals", - "Table": "music", "Values": [ "::__sq1" ], - "Vindex": "music_user_map" + "Vindex": "music_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 music.id from music where 1 != 1", + "Query": "select music.id from music where :__sq_has_values and music.id in ::__vals", + "Table": "music" + } + ] } ] }, "TablesUsed": [ "user.music" ] - } + }, + "skip_e2e": true }, { "comment": "`None` subquery as top level predicate - outer query changes from `Scatter` to `None` on merge", @@ -5033,7 +5320,8 @@ "user.user", "user.user_extra" ] - } + }, + "skip_e2e": true }, { "comment": "select user.a, t.b from user join (select id, count(*) b, req from user_extra group by req, id) as t on user.id = t.id", @@ -5097,7 +5385,8 @@ "user.user", "user.user_extra" ] - } + }, + "skip_e2e": true }, { "comment": "SELECT music.id FROM (SELECT MAX(id) as maxt FROM music WHERE music.user_id = 5) other JOIN music ON other.maxt = music.id", @@ -5220,7 +5509,8 @@ "main.dual", "main.unsharded_a" ] - } + }, + "skip_e2e": true }, { "comment": "subquery having join table on clause, using column reference of outer select table", @@ -5269,7 +5559,8 @@ "main.unsharded", "main.unsharded_a" ] - } + }, + "skip_e2e": true }, { "comment": "ALL modifier on unsharded table works well", @@ -5292,7 +5583,8 @@ "main.unsharded", "main.unsharded_a" ] - } + }, + "skip_e2e": true }, { "comment": "allow last_insert_id with argument", @@ -5337,7 +5629,8 @@ "user.music_extra", "user.user" ] - } + }, + "skip_e2e": true }, { "comment": "Query with non-plannable lookup vindex", @@ -5363,7 +5656,8 @@ "TablesUsed": [ "user.user_metadata" ] - } + }, + "skip_e2e": true }, { "comment": "join query with lookup and join on different vindex column", @@ -5415,7 +5709,8 @@ "user.user", "user.user_metadata" ] - } + }, + "skip_e2e": true }, { "comment": "pick email as vindex lookup", @@ -5425,7 +5720,7 @@ "Original": "select * from customer where email = 'a@mail.com'", "Instructions": { "OperatorType": "VindexLookup", - "Variant": "Equal", + "Variant": "EqualUnique", "Keyspace": { "Name": "user", "Sharded": true @@ -5510,7 +5805,8 @@ "TablesUsed": [ "user.customer" ] - } + }, + "skip_e2e": true }, { "comment": "email vindex is costly than phone vindex - but phone vindex is backfiling hence ignored", @@ -5520,7 +5816,7 @@ "Original": "select * from customer where email = 'a@mail.com' and phone = 123456", "Instructions": { "OperatorType": "VindexLookup", - "Variant": "Equal", + "Variant": "EqualUnique", "Keyspace": { "Name": "user", "Sharded": true @@ -5571,7 +5867,7 @@ "Original": "select * from customer where phone = 123456 and email = 'a@mail.com'", "Instructions": { "OperatorType": "VindexLookup", - "Variant": "Equal", + "Variant": "EqualUnique", "Keyspace": { "Name": "user", "Sharded": true @@ -5634,7 +5930,8 @@ "TablesUsed": [ "user.samecolvin" ] - } + }, + "skip_e2e": true }, { "comment": "column with qualifier is correctly used", @@ -5677,7 +5974,8 @@ "user.user", "user.user_extra" ] - } + }, + "skip_e2e": true }, { "comment": "Derived tables going to a single shard still need to expand derived table columns", @@ -5722,7 +6020,8 @@ "main.unsharded", "user.user" ] - } + }, + "skip_e2e": true }, { "comment": "column name aliases in outer join queries", @@ -5777,7 +6076,8 @@ "user.user", "user.user_extra" ] - } + }, + "skip_e2e": true }, { "comment": "Over clause works for unsharded tables", @@ -5799,7 +6099,8 @@ "TablesUsed": [ "main.unsharded_a" ] - } + }, + "skip_e2e": true }, { "comment": "join with derived table with alias and join condition - merge into route", diff --git a/go/vt/vtgate/planbuilder/testdata/union_cases.json b/go/vt/vtgate/planbuilder/testdata/union_cases.json index 7feabb0a698..2927c1c6093 100644 --- a/go/vt/vtgate/planbuilder/testdata/union_cases.json +++ b/go/vt/vtgate/planbuilder/testdata/union_cases.json @@ -447,34 +447,84 @@ "OperatorType": "Concatenate", "Inputs": [ { - "OperatorType": "Route", + "OperatorType": "VindexLookup", "Variant": "EqualUnique", "Keyspace": { "Name": "user", "Sharded": true }, - "FieldQuery": "select 1 from music where 1 != 1", - "Query": "select distinct 1 from music where id = 1", - "Table": "music", "Values": [ "1" ], - "Vindex": "music_user_map" + "Vindex": "music_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 1 from music where 1 != 1", + "Query": "select distinct 1 from music where id = 1", + "Table": "music" + } + ] }, { - "OperatorType": "Route", + "OperatorType": "VindexLookup", "Variant": "EqualUnique", "Keyspace": { "Name": "user", "Sharded": true }, - "FieldQuery": "select 1 from music where 1 != 1", - "Query": "select distinct 1 from music where id = 2", - "Table": "music", "Values": [ "2" ], - "Vindex": "music_user_map" + "Vindex": "music_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 1 from music where 1 != 1", + "Query": "select distinct 1 from music where id = 2", + "Table": "music" + } + ] } ] } diff --git a/go/vt/vtgate/planbuilder/testdata/vschemas/schema.json b/go/vt/vtgate/planbuilder/testdata/vschemas/schema.json index 4fe275f2398..a5de9d3697e 100644 --- a/go/vt/vtgate/planbuilder/testdata/vschemas/schema.json +++ b/go/vt/vtgate/planbuilder/testdata/vschemas/schema.json @@ -58,34 +58,52 @@ "sharded": true, "vindexes": { "user_index": { - "type": "hash_test", + "type": "hash", "owner": "user" }, "kid_index": { - "type": "hash_test", + "type": "hash", "owner": "multicolvin" }, + "hash": { + "type": "hash" + }, "user_md5_index": { "type": "unicode_loose_md5" }, "music_user_map": { - "type": "lookup_test", - "owner": "music" + "type": "lookup_unique", + "owner": "music", + "params": { + "table": "name_user_vdx", + "from": "name", + "to": "keyspace_id" + } }, "cola_map": { - "type": "lookup_test", - "owner": "multicolvin" + "type": "lookup_unique", + "owner": "multicolvin", + "params": { + "table": "cola_map", + "from": "cola", + "to": "keyspace_id" + } }, "colb_colc_map": { - "type": "lookup_test", - "owner": "multicolvin" + "type": "lookup_unique", + "owner": "multicolvin", + "params": { + "table": "colb_colc_map", + "from": "colb,colc", + "to": "keyspace_id" + } }, "cola_kid_map": { - "type": "lookup_test", + "type": "lookup_unique", "owner": "overlap_vindex" }, "name_user_map": { - "type": "name_lkp_test", + "type": "lookup", "owner": "user", "params": { "table": "name_user_vdx", @@ -94,42 +112,56 @@ } }, "email_user_map": { - "type": "lookup_test", + "type": "lookup_unique", "owner": "user_metadata" }, "address_user_map": { - "type": "lookup_test", + "type": "lookup_unique", "owner": "user_metadata" }, "costly_map": { - "type": "costly", - "owner": "user" + "type": "lookup_cost", + "owner": "user", + "params": { + "table": "costly_map", + "from": "costly", + "to": "keyspace_id", + "cost": "100" + } }, "hash_dup": { - "type": "hash_test", + "type": "hash", "owner": "user" }, "vindex1": { - "type": "hash_test", + "type": "hash", "owner": "samecolvin" }, "vindex2": { - "type": "lookup_test", + "type": "lookup_unique", "owner": "samecolvin" }, "cfc": { "type": "cfc" }, "multicolIdx": { - "type": "multiCol_test" + "type": "multicol", + "params": { + "column_count": "2" + } }, "colc_map": { - "type": "lookup_test", + "type": "lookup_unique", "owner": "multicol_tbl" }, "name_muticoltbl_map": { - "type": "name_lkp_test", - "owner": "multicol_tbl" + "type": "lookup", + "owner": "multicol_tbl", + "params": { + "table": "name_user_vdx", + "from": "name", + "to": "keyspace_id" + } }, "non_planable_user_map": { "type": "lookup_unicodeloosemd5_hash", @@ -141,7 +173,7 @@ "owner": "user_metadata" }, "lkp_shard_map": { - "type": "name_lkp_test", + "type": "lookup_unique", "owner": "mixed_tbl", "params": { "table": "lkp_shard_vdx", @@ -153,18 +185,18 @@ "type": "xxhash" }, "unq_lkp_bf_vdx": { - "type": "unq_lkp_test", + "type": "lookup_unique", "owner": "customer", "params": { "table": "unq_lkp_idx", - "from": " ", + "from": "unq_key", "to": "keyspace_id", "cost": "100", "write_only": "true" } }, "unq_lkp_vdx": { - "type": "unq_lkp_test", + "type": "lookup_unique", "owner": "customer", "params": { "table": "unq_lkp_idx", @@ -174,11 +206,11 @@ } }, "lkp_bf_vdx": { - "type": "name_lkp_test", + "type": "lookup_unique", "owner": "customer", "params": { "table": "lkp_shard_vdx", - "from": " ", + "from": "unq_key", "to": "keyspace_id", "write_only": "true" } @@ -352,6 +384,22 @@ } ] }, + "cola_map": { + "column_vindexes": [ + { + "column": "cola", + "name": "hash" + } + ] + }, + "colb_colc_map": { + "column_vindexes": [ + { + "column": "colb", + "name": "hash" + } + ] + }, "overlap_vindex": { "column_vindexes": [ { @@ -462,6 +510,14 @@ } ] }, + "costly_map": { + "column_vindexes": [ + { + "column": "name", + "name": "user_md5_index" + } + ] + }, "mixed_tbl": { "column_vindexes": [ { @@ -641,7 +697,10 @@ "type": "hash_test" }, "multicolIdx": { - "type": "multiCol_test" + "type": "multicol", + "params": { + "column_count": "3" + } } }, "tables": { diff --git a/go/vt/vtgate/planbuilder/vexplain.go b/go/vt/vtgate/planbuilder/vexplain.go index f66af7bfc33..7aed1e48884 100644 --- a/go/vt/vtgate/planbuilder/vexplain.go +++ b/go/vt/vtgate/planbuilder/vexplain.go @@ -26,6 +26,7 @@ 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/dynamicconfig" "vitess.io/vitess/go/vt/vtgate/engine" "vitess.io/vitess/go/vt/vtgate/planbuilder/operators" "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" @@ -37,15 +38,15 @@ func buildVExplainPlan( vexplainStmt *sqlparser.VExplainStmt, reservedVars *sqlparser.ReservedVars, vschema plancontext.VSchema, - enableOnlineDDL, enableDirectDDL bool, + cfg dynamicconfig.DDL, ) (*planResult, error) { switch vexplainStmt.Type { case sqlparser.QueriesVExplainType, sqlparser.AllVExplainType: - return buildVExplainLoggingPlan(ctx, vexplainStmt, reservedVars, vschema, enableOnlineDDL, enableDirectDDL) + return buildVExplainLoggingPlan(ctx, vexplainStmt, reservedVars, vschema, cfg) case sqlparser.PlanVExplainType: - return buildVExplainVtgatePlan(ctx, vexplainStmt.Statement, reservedVars, vschema, enableOnlineDDL, enableDirectDDL) + return buildVExplainVtgatePlan(ctx, vexplainStmt.Statement, reservedVars, vschema, cfg) case sqlparser.TraceVExplainType: - return buildVExplainTracePlan(ctx, vexplainStmt.Statement, reservedVars, vschema, enableOnlineDDL, enableDirectDDL) + return buildVExplainTracePlan(ctx, vexplainStmt.Statement, reservedVars, vschema, cfg) case sqlparser.KeysVExplainType: return buildVExplainKeysPlan(vexplainStmt.Statement, vschema) } @@ -92,8 +93,8 @@ func explainTabPlan(explain *sqlparser.ExplainTab, vschema plancontext.VSchema) }, singleTable(keyspace.Name, explain.Table.Name.String())), nil } -func buildVExplainVtgatePlan(ctx context.Context, explainStatement sqlparser.Statement, reservedVars *sqlparser.ReservedVars, vschema plancontext.VSchema, enableOnlineDDL, enableDirectDDL bool) (*planResult, error) { - innerInstruction, err := createInstructionFor(ctx, sqlparser.String(explainStatement), explainStatement, reservedVars, vschema, enableOnlineDDL, enableDirectDDL) +func buildVExplainVtgatePlan(ctx context.Context, explainStatement sqlparser.Statement, reservedVars *sqlparser.ReservedVars, vschema plancontext.VSchema, cfg dynamicconfig.DDL) (*planResult, error) { + innerInstruction, err := createInstructionFor(ctx, sqlparser.String(explainStatement), explainStatement, reservedVars, vschema, cfg) if err != nil { return nil, err } @@ -124,8 +125,8 @@ func buildVExplainKeysPlan(statement sqlparser.Statement, vschema plancontext.VS return getJsonResultPlan(result, "ColumnUsage") } -func buildVExplainLoggingPlan(ctx context.Context, explain *sqlparser.VExplainStmt, reservedVars *sqlparser.ReservedVars, vschema plancontext.VSchema, enableOnlineDDL, enableDirectDDL bool) (*planResult, error) { - input, err := createInstructionFor(ctx, sqlparser.String(explain.Statement), explain.Statement, reservedVars, vschema, enableOnlineDDL, enableDirectDDL) +func buildVExplainLoggingPlan(ctx context.Context, explain *sqlparser.VExplainStmt, reservedVars *sqlparser.ReservedVars, vschema plancontext.VSchema, cfg dynamicconfig.DDL) (*planResult, error) { + input, err := createInstructionFor(ctx, sqlparser.String(explain.Statement), explain.Statement, reservedVars, vschema, cfg) if err != nil { return nil, err } @@ -188,8 +189,8 @@ func explainPlan(explain *sqlparser.ExplainStmt, reservedVars *sqlparser.Reserve }, tables...), nil } -func buildVExplainTracePlan(ctx context.Context, explainStatement sqlparser.Statement, reservedVars *sqlparser.ReservedVars, vschema plancontext.VSchema, enableOnlineDDL, enableDirectDDL bool) (*planResult, error) { - innerInstruction, err := createInstructionFor(ctx, sqlparser.String(explainStatement), explainStatement, reservedVars, vschema, enableOnlineDDL, enableDirectDDL) +func buildVExplainTracePlan(ctx context.Context, explainStatement sqlparser.Statement, reservedVars *sqlparser.ReservedVars, vschema plancontext.VSchema, cfg dynamicconfig.DDL) (*planResult, error) { + innerInstruction, err := createInstructionFor(ctx, sqlparser.String(explainStatement), explainStatement, reservedVars, vschema, cfg) if err != nil { return nil, err } diff --git a/go/vt/vtgate/querylogz.go b/go/vt/vtgate/querylogz.go index 7c72e950d4a..05d301f28be 100644 --- a/go/vt/vtgate/querylogz.go +++ b/go/vt/vtgate/querylogz.go @@ -20,15 +20,15 @@ import ( "net/http" "strconv" "strings" - "text/template" "time" - "vitess.io/vitess/go/vt/vtgate/logstats" + "github.com/google/safehtml/template" "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/logstats" ) var ( diff --git a/go/vt/vtgate/querylogz_test.go b/go/vt/vtgate/querylogz_test.go index 3cecb983b3f..9236b2ac840 100644 --- a/go/vt/vtgate/querylogz_test.go +++ b/go/vt/vtgate/querylogz_test.go @@ -35,7 +35,7 @@ import ( func TestQuerylogzHandlerFormatting(t *testing.T) { req, _ := http.NewRequest("GET", "/querylogz?timeout=10&limit=1", nil) - logStats := logstats.NewLogStats(context.Background(), "Execute", "select name from test_table limit 1000", "suuid", nil) + logStats := logstats.NewLogStats(context.Background(), "Execute", "select name, 'inject ' from test_table limit 1000", "suuid", nil) logStats.StmtType = "select" logStats.RowsAffected = 1000 logStats.ShardQueries = 1 @@ -64,7 +64,7 @@ func TestQuerylogzHandlerFormatting(t *testing.T) { `0.002`, `0.003`, `select`, - `select name from test_table limit 1000`, + regexp.QuoteMeta(`select name,​ 'inject <script>alert()​;</script>' from test_table limit 1000`), `1`, `1000`, ``, @@ -94,7 +94,7 @@ func TestQuerylogzHandlerFormatting(t *testing.T) { `0.002`, `0.003`, `select`, - `select name from test_table limit 1000`, + regexp.QuoteMeta(`select name,​ 'inject <script>alert()​;</script>' from test_table limit 1000`), `1`, `1000`, ``, @@ -124,7 +124,7 @@ func TestQuerylogzHandlerFormatting(t *testing.T) { `0.002`, `0.003`, `select`, - `select name from test_table limit 1000`, + regexp.QuoteMeta(`select name,​ 'inject <script>alert()​;</script>' from test_table limit 1000`), `1`, `1000`, ``, diff --git a/go/vt/vtgate/safe_session_test.go b/go/vt/vtgate/safe_session_test.go deleted file mode 100644 index ce681fe7fd3..00000000000 --- a/go/vt/vtgate/safe_session_test.go +++ /dev/null @@ -1,100 +0,0 @@ -/* -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 vtgate - -import ( - "reflect" - "testing" - "time" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - querypb "vitess.io/vitess/go/vt/proto/query" - topodatapb "vitess.io/vitess/go/vt/proto/topodata" - vtgatepb "vitess.io/vitess/go/vt/proto/vtgate" -) - -func TestFailToMultiShardWhenSetToSingleDb(t *testing.T) { - session := NewSafeSession(&vtgatepb.Session{ - InTransaction: true, TransactionMode: vtgatepb.TransactionMode_SINGLE, - }) - - sess0 := &vtgatepb.Session_ShardSession{ - Target: &querypb.Target{Keyspace: "keyspace", Shard: "0"}, - TabletAlias: &topodatapb.TabletAlias{Cell: "cell", Uid: 0}, - TransactionId: 1, - } - sess1 := &vtgatepb.Session_ShardSession{ - Target: &querypb.Target{Keyspace: "keyspace", Shard: "1"}, - TabletAlias: &topodatapb.TabletAlias{Cell: "cell", Uid: 1}, - TransactionId: 1, - } - - err := session.AppendOrUpdate(sess0, vtgatepb.TransactionMode_SINGLE) - require.NoError(t, err) - err = session.AppendOrUpdate(sess1, vtgatepb.TransactionMode_SINGLE) - require.Error(t, err) -} - -func TestPrequeries(t *testing.T) { - session := NewSafeSession(&vtgatepb.Session{ - SystemVariables: map[string]string{ - "s1": "'apa'", - "s2": "42", - }, - }) - - want := []string{"set s1 = 'apa', s2 = 42"} - preQueries := session.SetPreQueries() - - if !reflect.DeepEqual(want, preQueries) { - t.Errorf("got %v but wanted %v", preQueries, want) - } -} - -func TestTimeZone(t *testing.T) { - testCases := []struct { - tz string - want string - }{ - { - tz: "'Europe/Amsterdam'", - want: "Europe/Amsterdam", - }, - { - tz: "'+02:00'", - want: "UTC+02:00", - }, - { - tz: "foo", - want: (*time.Location)(nil).String(), - }, - } - - for _, tc := range testCases { - t.Run(tc.tz, func(t *testing.T) { - session := NewSafeSession(&vtgatepb.Session{ - SystemVariables: map[string]string{ - "time_zone": tc.tz, - }, - }) - - assert.Equal(t, tc.want, session.TimeZone().String()) - }) - } -} diff --git a/go/vt/vtgate/scatter_conn.go b/go/vt/vtgate/scatter_conn.go index f7db598127e..6e2cf9ad8ba 100644 --- a/go/vt/vtgate/scatter_conn.go +++ b/go/vt/vtgate/scatter_conn.go @@ -24,26 +24,25 @@ import ( "sync/atomic" "time" - "vitess.io/vitess/go/mysql/sqlerror" - "vitess.io/vitess/go/vt/sqlparser" - "google.golang.org/protobuf/proto" + "vitess.io/vitess/go/mysql/sqlerror" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/stats" "vitess.io/vitess/go/vt/concurrency" "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" + 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/srvtopo" "vitess.io/vitess/go/vt/topo/topoproto" "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vtgate/engine" + econtext "vitess.io/vitess/go/vt/vtgate/executorcontext" "vitess.io/vitess/go/vt/vttablet/queryservice" - - 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" ) // ScatterConn is used for executing queries across @@ -73,13 +72,10 @@ type shardActionFunc func(rs *srvtopo.ResolvedShard, i int) error type shardActionTransactionFunc func(rs *srvtopo.ResolvedShard, i int, shardActionInfo *shardActionInfo) (*shardActionInfo, error) type ( - resultsObserver interface { - observe(*sqltypes.Result) - } nullResultsObserver struct{} ) -func (nullResultsObserver) observe(*sqltypes.Result) {} +func (nullResultsObserver) Observe(*sqltypes.Result) {} // NewScatterConn creates a new ScatterConn. func NewScatterConn(statsName string, txConn *TxConn, gw *TabletGateway) *ScatterConn { @@ -108,7 +104,7 @@ func (stc *ScatterConn) startAction(name string, target *querypb.Target) (time.T return startTime, statsKey } -func (stc *ScatterConn) endAction(startTime time.Time, allErrors *concurrency.AllErrorRecorder, statsKey []string, err *error, session *SafeSession) { +func (stc *ScatterConn) endAction(startTime time.Time, allErrors *concurrency.AllErrorRecorder, statsKey []string, err *error, session *econtext.SafeSession) { if *err != nil { allErrors.RecordError(*err) // Don't increment the error counter for duplicate @@ -152,10 +148,10 @@ func (stc *ScatterConn) ExecuteMultiShard( primitive engine.Primitive, rss []*srvtopo.ResolvedShard, queries []*querypb.BoundQuery, - session *SafeSession, + session *econtext.SafeSession, autocommit bool, ignoreMaxMemoryRows bool, - resultsObserver resultsObserver, + resultsObserver econtext.ResultsObserver, ) (qr *sqltypes.Result, errs []error) { if len(rss) != len(queries) { @@ -166,7 +162,7 @@ func (stc *ScatterConn) ExecuteMultiShard( var mu sync.Mutex qr = new(sqltypes.Result) - if session.InLockSession() && session.TriggerLockHeartBeat() { + if session.InLockSession() && triggerLockHeartBeat(session) { go stc.runLockQuery(ctx, session) } @@ -224,6 +220,7 @@ func (stc *ScatterConn) ExecuteMultiShard( retryRequest(func() { // we seem to have lost our connection. it was a reserved connection, let's try to recreate it info.actionNeeded = reserve + info.ignoreOldSession = true var state queryservice.ReservedState state, innerqr, err = qs.ReserveExecute(ctx, rs.Target, session.SetPreQueries(), queries[i].Sql, queries[i].BindVariables, 0 /*transactionId*/, opts) reservedID = state.ReservedID @@ -239,6 +236,7 @@ func (stc *ScatterConn) ExecuteMultiShard( retryRequest(func() { // we seem to have lost our connection. it was a reserved connection, let's try to recreate it info.actionNeeded = reserveBegin + info.ignoreOldSession = true var state queryservice.ReservedTransactionState state, innerqr, err = qs.ReserveBeginExecute(ctx, rs.Target, session.SetPreQueries(), session.SavePoints(), queries[i].Sql, queries[i].BindVariables, opts) transactionID = state.TransactionID @@ -260,10 +258,10 @@ func (stc *ScatterConn) ExecuteMultiShard( default: return nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "[BUG] unexpected actionNeeded on query execution: %v", info.actionNeeded) } - session.logging.log(primitive, rs.Target, rs.Gateway, queries[i].Sql, info.actionNeeded == begin || info.actionNeeded == reserveBegin, queries[i].BindVariables) + session.Log(primitive, rs.Target, rs.Gateway, queries[i].Sql, info.actionNeeded == begin || info.actionNeeded == reserveBegin, queries[i].BindVariables) // We need to new shard info irrespective of the error. - newInfo := info.updateTransactionAndReservedID(transactionID, reservedID, alias) + newInfo := info.updateTransactionAndReservedID(transactionID, reservedID, alias, innerqr) if err != nil { return newInfo, err } @@ -271,7 +269,7 @@ func (stc *ScatterConn) ExecuteMultiShard( defer mu.Unlock() if innerqr != nil { - resultsObserver.observe(innerqr) + resultsObserver.Observe(innerqr) } // Don't append more rows if row count is exceeded. @@ -289,7 +287,13 @@ func (stc *ScatterConn) ExecuteMultiShard( return qr, allErrors.GetErrors() } -func (stc *ScatterConn) runLockQuery(ctx context.Context, session *SafeSession) { +func triggerLockHeartBeat(session *econtext.SafeSession) bool { + now := time.Now().Unix() + lastHeartbeat := session.GetLockHeartbeat() + return now-lastHeartbeat >= int64(lockHeartbeatTime.Seconds()) +} + +func (stc *ScatterConn) runLockQuery(ctx context.Context, session *econtext.SafeSession) { rs := &srvtopo.ResolvedShard{Target: session.LockSession.Target, Gateway: stc.gateway} query := &querypb.BoundQuery{Sql: "select 1", BindVariables: nil} _, lockErr := stc.ExecuteLock(ctx, rs, query, session, sqlparser.IsUsedLock) @@ -298,7 +302,7 @@ func (stc *ScatterConn) runLockQuery(ctx context.Context, session *SafeSession) } } -func checkAndResetShardSession(info *shardActionInfo, err error, session *SafeSession, target *querypb.Target) reset { +func checkAndResetShardSession(info *shardActionInfo, err error, session *econtext.SafeSession, target *querypb.Target) reset { retry := none if info.reservedID != 0 && info.transactionID == 0 { if wasConnectionClosed(err) { @@ -314,7 +318,7 @@ func checkAndResetShardSession(info *shardActionInfo, err error, session *SafeSe return retry } -func getQueryService(ctx context.Context, rs *srvtopo.ResolvedShard, info *shardActionInfo, session *SafeSession, skipReset bool) (queryservice.QueryService, error) { +func getQueryService(ctx context.Context, rs *srvtopo.ResolvedShard, info *shardActionInfo, session *econtext.SafeSession, skipReset bool) (queryservice.QueryService, error) { if info.alias == nil { return rs.Gateway, nil } @@ -365,18 +369,18 @@ func (stc *ScatterConn) StreamExecuteMulti( query string, rss []*srvtopo.ResolvedShard, bindVars []map[string]*querypb.BindVariable, - session *SafeSession, + session *econtext.SafeSession, autocommit bool, callback func(reply *sqltypes.Result) error, - resultsObserver resultsObserver, + resultsObserver econtext.ResultsObserver, ) []error { - if session.InLockSession() && session.TriggerLockHeartBeat() { + if session.InLockSession() && triggerLockHeartBeat(session) { go stc.runLockQuery(ctx, session) } observedCallback := func(reply *sqltypes.Result) error { if reply != nil { - resultsObserver.observe(reply) + resultsObserver.Observe(reply) } return callback(reply) } @@ -469,10 +473,10 @@ func (stc *ScatterConn) StreamExecuteMulti( default: return nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "[BUG] unexpected actionNeeded on query execution: %v", info.actionNeeded) } - session.logging.log(primitive, rs.Target, rs.Gateway, query, info.actionNeeded == begin || info.actionNeeded == reserveBegin, bindVars[i]) + session.Log(primitive, rs.Target, rs.Gateway, query, info.actionNeeded == begin || info.actionNeeded == reserveBegin, bindVars[i]) - // We need to new shard info irrespective of the error. - newInfo := info.updateTransactionAndReservedID(transactionID, reservedID, alias) + // We need the new shard info irrespective of the error. + newInfo := info.updateTransactionAndReservedID(transactionID, reservedID, alias, nil) if err != nil { return newInfo, err } @@ -604,7 +608,7 @@ func (stc *ScatterConn) multiGo( startTime, statsKey := stc.startAction(name, rs.Target) // Send a dummy session. // TODO(sougou): plumb a real session through this call. - defer stc.endAction(startTime, allErrors, statsKey, &err, NewSafeSession(nil)) + defer stc.endAction(startTime, allErrors, statsKey, &err, econtext.NewSafeSession(nil)) err = action(rs, i) } @@ -646,7 +650,7 @@ func (stc *ScatterConn) multiGoTransaction( ctx context.Context, name string, rss []*srvtopo.ResolvedShard, - session *SafeSession, + session *econtext.SafeSession, autocommit bool, action shardActionTransactionFunc, ) (allErrors *concurrency.AllErrorRecorder) { @@ -662,21 +666,24 @@ func (stc *ScatterConn) multiGoTransaction( startTime, statsKey := stc.startAction(name, rs.Target) defer stc.endAction(startTime, allErrors, statsKey, &err, session) - shardActionInfo, err := actionInfo(ctx, rs.Target, session, autocommit, stc.txConn.mode) + info, shardSession, err := actionInfo(ctx, rs.Target, session, autocommit, stc.txConn.mode) if err != nil { return } - updated, err := action(rs, i, shardActionInfo) - if updated == nil { + info, err = action(rs, i, info) + if info == nil { return } - if updated.actionNeeded != nothing && (updated.transactionID != 0 || updated.reservedID != 0) { - appendErr := session.AppendOrUpdate(&vtgatepb.Session_ShardSession{ - Target: rs.Target, - TransactionId: updated.transactionID, - ReservedId: updated.reservedID, - TabletAlias: updated.alias, - }, stc.txConn.mode) + if info.ignoreOldSession { + shardSession = nil + } + if shardSession != nil && info.rowsAffected { + // We might not always update or append in the session. + // We need to track if rows were affected in the transaction. + shardSession.RowsAffected = info.rowsAffected + } + if info.actionNeeded != nothing && (info.transactionID != 0 || info.reservedID != 0) { + appendErr := session.AppendOrUpdate(rs.Target, info, shardSession, stc.txConn.mode) if appendErr != nil { err = appendErr } @@ -727,7 +734,7 @@ func (stc *ScatterConn) multiGoTransaction( // It returns an error recorder in which each shard error is recorded positionally, // i.e. if rss[2] had an error, then the error recorder will store that error // in the second position. -func (stc *ScatterConn) ExecuteLock(ctx context.Context, rs *srvtopo.ResolvedShard, query *querypb.BoundQuery, session *SafeSession, lockFuncType sqlparser.LockingFuncType) (*sqltypes.Result, error) { +func (stc *ScatterConn) ExecuteLock(ctx context.Context, rs *srvtopo.ResolvedShard, query *querypb.BoundQuery, session *econtext.SafeSession, lockFuncType sqlparser.LockingFuncType) (*sqltypes.Result, error) { var ( qr *sqltypes.Result @@ -830,25 +837,25 @@ func requireNewQS(err error, target *querypb.Target) bool { } // actionInfo looks at the current session, and returns information about what needs to be done for this tablet -func actionInfo(ctx context.Context, target *querypb.Target, session *SafeSession, autocommit bool, txMode vtgatepb.TransactionMode) (*shardActionInfo, error) { +func actionInfo(ctx context.Context, target *querypb.Target, session *econtext.SafeSession, autocommit bool, txMode vtgatepb.TransactionMode) (*shardActionInfo, *vtgatepb.Session_ShardSession, error) { if !(session.InTransaction() || session.InReservedConn()) { - return &shardActionInfo{}, nil + return &shardActionInfo{}, nil, nil } ignoreSession := ctx.Value(engine.IgnoreReserveTxn) if ignoreSession != nil { - return &shardActionInfo{}, nil + return &shardActionInfo{}, nil, nil } // No need to protect ourselves from the race condition between // Find and AppendOrUpdate. The higher level functions ensure that no // duplicate (target) tuples can execute // this at the same time. - transactionID, reservedID, alias, err := session.FindAndChangeSessionIfInSingleTxMode(target.Keyspace, target.Shard, target.TabletType, txMode) + shardSession, err := session.FindAndChangeSessionIfInSingleTxMode(target.Keyspace, target.Shard, target.TabletType, txMode) if err != nil { - return nil, err + return nil, nil, err } - shouldReserve := session.InReservedConn() && reservedID == 0 - shouldBegin := session.InTransaction() && transactionID == 0 && !autocommit + shouldReserve := session.InReservedConn() && (shardSession == nil || shardSession.ReservedId == 0) + shouldBegin := session.InTransaction() && (shardSession == nil || shardSession.TransactionId == 0) && !autocommit var act = nothing switch { @@ -860,16 +867,20 @@ func actionInfo(ctx context.Context, target *querypb.Target, session *SafeSessio act = begin } - return &shardActionInfo{ - actionNeeded: act, - transactionID: transactionID, - reservedID: reservedID, - alias: alias, - }, nil + info := &shardActionInfo{ + actionNeeded: act, + } + if shardSession != nil { + info.transactionID = shardSession.TransactionId + info.reservedID = shardSession.ReservedId + info.alias = shardSession.TabletAlias + info.rowsAffected = shardSession.RowsAffected + } + return info, shardSession, nil } // lockInfo looks at the current session, and returns information about what needs to be done for this tablet -func lockInfo(target *querypb.Target, session *SafeSession, lockFuncType sqlparser.LockingFuncType) (*shardActionInfo, error) { +func lockInfo(target *querypb.Target, session *econtext.SafeSession, lockFuncType sqlparser.LockingFuncType) (*shardActionInfo, error) { info := &shardActionInfo{actionNeeded: nothing} if session.LockSession != nil { if !proto.Equal(target, session.LockSession.Target) { @@ -894,10 +905,35 @@ type shardActionInfo struct { actionNeeded actionNeeded reservedID, transactionID int64 alias *topodatapb.TabletAlias + + // ignoreOldSession is used when there is a retry on the same shard due to connection loss for a reserved connection. + // The old reference should be ignored and new shard session should be added to the session. + ignoreOldSession bool + rowsAffected bool +} + +func (sai *shardActionInfo) TransactionID() int64 { + return sai.transactionID +} + +func (sai *shardActionInfo) ReservedID() int64 { + return sai.reservedID +} + +func (sai *shardActionInfo) RowsAffected() bool { + return sai.rowsAffected +} + +func (sai *shardActionInfo) Alias() *topodatapb.TabletAlias { + return sai.alias } -func (sai *shardActionInfo) updateTransactionAndReservedID(txID int64, rID int64, alias *topodatapb.TabletAlias) *shardActionInfo { - if txID == sai.transactionID && rID == sai.reservedID { +func (sai *shardActionInfo) updateTransactionAndReservedID(txID int64, rID int64, alias *topodatapb.TabletAlias, qr *sqltypes.Result) *shardActionInfo { + firstTimeRowsAffected := false + if txID != 0 && qr != nil && !sai.rowsAffected { + firstTimeRowsAffected = qr.RowsAffected > 0 + } + if txID == sai.transactionID && rID == sai.reservedID && !firstTimeRowsAffected { // As transaction id and reserved id have not changed, there is nothing to update in session shard sessions. return nil } @@ -905,6 +941,7 @@ func (sai *shardActionInfo) updateTransactionAndReservedID(txID int64, rID int64 newInfo.reservedID = rID newInfo.transactionID = txID newInfo.alias = alias + newInfo.rowsAffected = firstTimeRowsAffected return &newInfo } diff --git a/go/vt/vtgate/scatter_conn_test.go b/go/vt/vtgate/scatter_conn_test.go index c5d4f350433..ab8680ca5e6 100644 --- a/go/vt/vtgate/scatter_conn_test.go +++ b/go/vt/vtgate/scatter_conn_test.go @@ -21,6 +21,7 @@ import ( "testing" "vitess.io/vitess/go/vt/log" + econtext "vitess.io/vitess/go/vt/vtgate/executorcontext" "vitess.io/vitess/go/mysql/sqlerror" vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" @@ -100,7 +101,7 @@ func TestExecuteFailOnAutocommit(t *testing.T) { }, Autocommit: false, } - _, errs := sc.ExecuteMultiShard(ctx, nil, rss, queries, NewSafeSession(session), true /*autocommit*/, false, nullResultsObserver{}) + _, errs := sc.ExecuteMultiShard(ctx, nil, rss, queries, econtext.NewSafeSession(session), true /*autocommit*/, false, nullResultsObserver{}) err := vterrors.Aggregate(errs) require.Error(t, err) require.Contains(t, err.Error(), "in autocommit mode, transactionID should be zero but was: 123") @@ -183,7 +184,7 @@ func TestExecutePanic(t *testing.T) { require.Contains(t, logMessage, "(*ScatterConn).multiGoTransaction") }() - _, _ = sc.ExecuteMultiShard(ctx, nil, rss, queries, NewSafeSession(session), true /*autocommit*/, false, nullResultsObserver{}) + _, _ = sc.ExecuteMultiShard(ctx, nil, rss, queries, econtext.NewSafeSession(session), true /*autocommit*/, false, nullResultsObserver{}) } @@ -204,7 +205,7 @@ func TestReservedOnMultiReplica(t *testing.T) { res := srvtopo.NewResolver(newSandboxForCells(ctx, []string{"aa"}), sc.gateway, "aa") - session := NewSafeSession(&vtgatepb.Session{InTransaction: false, InReservedConn: true}) + session := econtext.NewSafeSession(&vtgatepb.Session{InTransaction: false, InReservedConn: true}) destinations := []key.Destination{key.DestinationShard("0")} for i := 0; i < 10; i++ { executeOnShards(t, ctx, res, keyspace, sc, session, destinations) @@ -351,7 +352,7 @@ func TestReservedBeginTableDriven(t *testing.T) { res := srvtopo.NewResolver(newSandboxForCells(ctx, []string{"aa"}), sc.gateway, "aa") t.Run(test.name, func(t *testing.T) { - session := NewSafeSession(&vtgatepb.Session{}) + session := econtext.NewSafeSession(&vtgatepb.Session{}) for _, action := range test.actions { session.Session.InTransaction = action.transaction session.Session.InReservedConn = action.reserved @@ -384,7 +385,7 @@ func TestReservedConnFail(t *testing.T) { _ = hc.AddTestTablet("aa", "1", 1, keyspace, "1", topodatapb.TabletType_REPLICA, true, 1, nil) res := srvtopo.NewResolver(newSandboxForCells(ctx, []string{"aa"}), sc.gateway, "aa") - session := NewSafeSession(&vtgatepb.Session{InTransaction: false, InReservedConn: true}) + session := econtext.NewSafeSession(&vtgatepb.Session{InTransaction: false, InReservedConn: true}) destinations := []key.Destination{key.DestinationShard("0")} executeOnShards(t, ctx, res, keyspace, sc, session, destinations) diff --git a/go/vt/vtgate/tabletgateway_flaky_test.go b/go/vt/vtgate/tabletgateway_flaky_test.go index d136542d176..124997bea9e 100644 --- a/go/vt/vtgate/tabletgateway_flaky_test.go +++ b/go/vt/vtgate/tabletgateway_flaky_test.go @@ -20,6 +20,8 @@ import ( "testing" "time" + econtext "vitess.io/vitess/go/vt/vtgate/executorcontext" + "github.com/stretchr/testify/require" "vitess.io/vitess/go/mysql/collations" @@ -53,7 +55,7 @@ func TestGatewayBufferingWhenPrimarySwitchesServingState(t *testing.T) { TabletType: tabletType, } - ts := &fakeTopoServer{} + ts := &econtext.FakeTopoServer{} // create a new fake health check. We want to check the buffering code which uses Subscribe, so we must also pass a channel hc := discovery.NewFakeHealthCheck(make(chan *discovery.TabletHealth)) // create a new tablet gateway @@ -156,7 +158,7 @@ func TestGatewayBufferingWhileReparenting(t *testing.T) { TabletType: tabletType, } - ts := &fakeTopoServer{} + ts := &econtext.FakeTopoServer{} // create a new fake health check. We want to check the buffering code which uses Subscribe, so we must also pass a channel hc := discovery.NewFakeHealthCheck(make(chan *discovery.TabletHealth)) // create a new tablet gateway @@ -286,7 +288,7 @@ func TestInconsistentStateDetectedBuffering(t *testing.T) { TabletType: tabletType, } - ts := &fakeTopoServer{} + ts := &econtext.FakeTopoServer{} // create a new fake health check. We want to check the buffering code which uses Subscribe, so we must also pass a channel hc := discovery.NewFakeHealthCheck(make(chan *discovery.TabletHealth)) // create a new tablet gateway diff --git a/go/vt/vtgate/tabletgateway_test.go b/go/vt/vtgate/tabletgateway_test.go index 2aafb78af99..b318cb84981 100644 --- a/go/vt/vtgate/tabletgateway_test.go +++ b/go/vt/vtgate/tabletgateway_test.go @@ -22,6 +22,8 @@ import ( "strings" "testing" + econtext "vitess.io/vitess/go/vt/vtgate/executorcontext" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -109,7 +111,7 @@ func TestTabletGatewayShuffleTablets(t *testing.T) { ctx := utils.LeakCheckContext(t) hc := discovery.NewFakeHealthCheck(nil) - ts := &fakeTopoServer{} + ts := &econtext.FakeTopoServer{} tg := NewTabletGateway(ctx, hc, ts, "local") defer tg.Close(ctx) @@ -183,7 +185,7 @@ func TestTabletGatewayReplicaTransactionError(t *testing.T) { TabletType: tabletType, } hc := discovery.NewFakeHealthCheck(nil) - ts := &fakeTopoServer{} + ts := &econtext.FakeTopoServer{} tg := NewTabletGateway(ctx, hc, ts, "cell") defer tg.Close(ctx) @@ -218,7 +220,7 @@ func testTabletGatewayGenericHelper(t *testing.T, ctx context.Context, f func(ct TabletType: tabletType, } hc := discovery.NewFakeHealthCheck(nil) - ts := &fakeTopoServer{} + ts := &econtext.FakeTopoServer{} tg := NewTabletGateway(ctx, hc, ts, "cell") defer tg.Close(ctx) // no tablet @@ -306,7 +308,7 @@ func testTabletGatewayTransact(t *testing.T, ctx context.Context, f func(ctx con TabletType: tabletType, } hc := discovery.NewFakeHealthCheck(nil) - ts := &fakeTopoServer{} + ts := &econtext.FakeTopoServer{} tg := NewTabletGateway(ctx, hc, ts, "cell") defer tg.Close(ctx) @@ -348,7 +350,7 @@ func verifyShardErrors(t *testing.T, err error, wantErrors []string, wantCode vt // TestWithRetry tests the functionality of withRetry function in different circumstances. func TestWithRetry(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) - tg := NewTabletGateway(ctx, discovery.NewFakeHealthCheck(nil), &fakeTopoServer{}, "cell") + tg := NewTabletGateway(ctx, discovery.NewFakeHealthCheck(nil), &econtext.FakeTopoServer{}, "cell") tg.kev = discovery.NewKeyspaceEventWatcher(ctx, tg.srvTopoServer, tg.hc, tg.localCell) defer func() { cancel() diff --git a/go/vt/vtgate/tx_conn.go b/go/vt/vtgate/tx_conn.go index 315484ea499..3ce138bc0e4 100644 --- a/go/vt/vtgate/tx_conn.go +++ b/go/vt/vtgate/tx_conn.go @@ -33,6 +33,7 @@ import ( vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vterrors" + econtext "vitess.io/vitess/go/vt/vtgate/executorcontext" "vitess.io/vitess/go/vt/vttablet/queryservice" ) @@ -80,7 +81,7 @@ var phaseMessage = map[commitPhase]string{ // Begin begins a new transaction. If one is already in progress, it commits it // and starts a new one. -func (txc *TxConn) Begin(ctx context.Context, session *SafeSession, txAccessModes []sqlparser.TxAccessMode) error { +func (txc *TxConn) Begin(ctx context.Context, session *econtext.SafeSession, txAccessModes []sqlparser.TxAccessMode) error { if session.InTransaction() { if err := txc.Commit(ctx, session); err != nil { return err @@ -102,7 +103,7 @@ func (txc *TxConn) Begin(ctx context.Context, session *SafeSession, txAccessMode // Commit commits the current transaction. The type of commit can be // best effort or 2pc depending on the session setting. -func (txc *TxConn) Commit(ctx context.Context, session *SafeSession) error { +func (txc *TxConn) Commit(ctx context.Context, session *econtext.SafeSession) error { defer session.ResetTx() if !session.InTransaction() { return nil @@ -123,7 +124,7 @@ func (txc *TxConn) Commit(ctx context.Context, session *SafeSession) error { return txc.commitNormal(ctx, session) } -func recordCommitTime(session *SafeSession, twopc bool, startTime time.Time) { +func recordCommitTime(session *econtext.SafeSession, twopc bool, startTime time.Time) { switch { case len(session.ShardSessions) == 0: // No-op @@ -143,7 +144,7 @@ func (txc *TxConn) queryService(ctx context.Context, alias *topodatapb.TabletAli return txc.tabletGateway.QueryServiceByAlias(ctx, alias, nil) } -func (txc *TxConn) commitShard(ctx context.Context, s *vtgatepb.Session_ShardSession, logging *executeLogger) error { +func (txc *TxConn) commitShard(ctx context.Context, s *vtgatepb.Session_ShardSession, logging *econtext.ExecuteLogger) error { if s.TransactionId == 0 { return nil } @@ -159,19 +160,19 @@ func (txc *TxConn) commitShard(ctx context.Context, s *vtgatepb.Session_ShardSes } s.TransactionId = 0 s.ReservedId = reservedID - logging.log(nil, s.Target, nil, "commit", false, nil) + logging.Log(nil, s.Target, nil, "commit", false, nil) return nil } -func (txc *TxConn) commitNormal(ctx context.Context, session *SafeSession) error { - if err := txc.runSessions(ctx, session.PreSessions, session.logging, txc.commitShard); err != nil { +func (txc *TxConn) commitNormal(ctx context.Context, session *econtext.SafeSession) error { + if err := txc.runSessions(ctx, session.PreSessions, session.GetLogger(), txc.commitShard); err != nil { _ = txc.Release(ctx, session) return err } // Retain backward compatibility on commit order for the normal session. for i, shardSession := range session.ShardSessions { - if err := txc.commitShard(ctx, shardSession, session.logging); err != nil { + if err := txc.commitShard(ctx, shardSession, session.GetLogger()); err != nil { if i > 0 { nShards := i elipsis := false @@ -197,7 +198,7 @@ func (txc *TxConn) commitNormal(ctx context.Context, session *SafeSession) error } } - if err := txc.runSessions(ctx, session.PostSessions, session.logging, txc.commitShard); err != nil { + if err := txc.runSessions(ctx, session.PostSessions, session.GetLogger(), txc.commitShard); err != nil { // If last commit fails, there will be nothing to rollback. session.RecordWarning(&querypb.QueryWarning{Message: fmt.Sprintf("post-operation transaction had an error: %v", err)}) // With reserved connection we should release them. @@ -209,7 +210,7 @@ func (txc *TxConn) commitNormal(ctx context.Context, session *SafeSession) error } // commit2PC will not used the pinned tablets - to make sure we use the current source, we need to use the gateway's queryservice -func (txc *TxConn) commit2PC(ctx context.Context, session *SafeSession) (err error) { +func (txc *TxConn) commit2PC(ctx context.Context, session *econtext.SafeSession) (err error) { // If the number of participants is one or less, then it's a normal commit. if len(session.ShardSessions) <= 1 { return txc.commitNormal(ctx, session) @@ -249,7 +250,7 @@ func (txc *TxConn) commit2PC(ctx context.Context, session *SafeSession) (err err } txPhase = Commit2pcPrepare - prepareAction := func(ctx context.Context, s *vtgatepb.Session_ShardSession, logging *executeLogger) error { + prepareAction := func(ctx context.Context, s *vtgatepb.Session_ShardSession, logging *econtext.ExecuteLogger) error { if DebugTwoPc { // Test code to simulate a failure during RM prepare if terr := checkTestFailure(ctx, "RMPrepare_-40_FailNow", s.Target); terr != nil { return terr @@ -257,7 +258,7 @@ func (txc *TxConn) commit2PC(ctx context.Context, session *SafeSession) (err err } return txc.tabletGateway.Prepare(ctx, s.Target, s.TransactionId, dtid) } - if err = txc.runSessions(ctx, rmShards, session.logging, prepareAction); err != nil { + if err = txc.runSessions(ctx, rmShards, session.GetLogger(), prepareAction); err != nil { return err } @@ -280,7 +281,7 @@ func (txc *TxConn) commit2PC(ctx context.Context, session *SafeSession) (err err } txPhase = Commit2pcPrepareCommit - prepareCommitAction := func(ctx context.Context, s *vtgatepb.Session_ShardSession, logging *executeLogger) error { + prepareCommitAction := func(ctx context.Context, s *vtgatepb.Session_ShardSession, logging *econtext.ExecuteLogger) error { if DebugTwoPc { // Test code to simulate a failure during RM prepare if terr := checkTestFailure(ctx, "RMCommit_-40_FailNow", s.Target); terr != nil { return terr @@ -288,7 +289,7 @@ func (txc *TxConn) commit2PC(ctx context.Context, session *SafeSession) (err err } return txc.tabletGateway.CommitPrepared(ctx, s.Target, dtid) } - if err = txc.runSessions(ctx, rmShards, session.logging, prepareCommitAction); err != nil { + if err = txc.runSessions(ctx, rmShards, session.GetLogger(), prepareCommitAction); err != nil { return err } @@ -300,7 +301,7 @@ func (txc *TxConn) commit2PC(ctx context.Context, session *SafeSession) (err err return nil } -func (txc *TxConn) checkValidCondition(session *SafeSession) error { +func (txc *TxConn) checkValidCondition(session *econtext.SafeSession) error { if len(session.PreSessions) != 0 || len(session.PostSessions) != 0 { return vterrors.VT12001("atomic distributed transaction commit with consistent lookup vindex") } @@ -309,7 +310,7 @@ func (txc *TxConn) checkValidCondition(session *SafeSession) error { func (txc *TxConn) errActionAndLogWarn( ctx context.Context, - session *SafeSession, + session *econtext.SafeSession, txPhase commitPhase, startCommitState querypb.StartCommitState, dtid string, @@ -323,12 +324,12 @@ func (txc *TxConn) errActionAndLogWarn( rollbackErr = txc.Rollback(ctx, session) case Commit2pcPrepare: // Rollback the prepared and unprepared transactions. - rollbackErr = txc.rollbackTx(ctx, dtid, mmShard, rmShards, session.logging) + rollbackErr = txc.rollbackTx(ctx, dtid, mmShard, rmShards, session.GetLogger()) case Commit2pcStartCommit: // Failed to store the commit decision on MM. // If the failure state is certain, then the only option is to rollback the prepared transactions on the RMs. if startCommitState == querypb.StartCommitState_Fail { - rollbackErr = txc.rollbackTx(ctx, dtid, mmShard, rmShards, session.logging) + rollbackErr = txc.rollbackTx(ctx, dtid, mmShard, rmShards, session.GetLogger()) } fallthrough case Commit2pcPrepareCommit: @@ -362,7 +363,7 @@ func createWarningMessage(dtid string, txPhase commitPhase) string { } // Rollback rolls back the current transaction. There are no retries on this operation. -func (txc *TxConn) Rollback(ctx context.Context, session *SafeSession) error { +func (txc *TxConn) Rollback(ctx context.Context, session *econtext.SafeSession) error { if !session.InTransaction() { return nil } @@ -371,7 +372,7 @@ func (txc *TxConn) Rollback(ctx context.Context, session *SafeSession) error { allsessions := append(session.PreSessions, session.ShardSessions...) allsessions = append(allsessions, session.PostSessions...) - err := txc.runSessions(ctx, allsessions, session.logging, func(ctx context.Context, s *vtgatepb.Session_ShardSession, logging *executeLogger) error { + err := txc.runSessions(ctx, allsessions, session.GetLogger(), func(ctx context.Context, s *vtgatepb.Session_ShardSession, logging *econtext.ExecuteLogger) error { if s.TransactionId == 0 { return nil } @@ -385,7 +386,7 @@ func (txc *TxConn) Rollback(ctx context.Context, session *SafeSession) error { } s.TransactionId = 0 s.ReservedId = reservedID - logging.log(nil, s.Target, nil, "rollback", false, nil) + logging.Log(nil, s.Target, nil, "rollback", false, nil) return nil }) if err != nil { @@ -398,7 +399,7 @@ func (txc *TxConn) Rollback(ctx context.Context, session *SafeSession) error { } // Release releases the reserved connection and/or rollbacks the transaction -func (txc *TxConn) Release(ctx context.Context, session *SafeSession) error { +func (txc *TxConn) Release(ctx context.Context, session *econtext.SafeSession) error { if !session.InTransaction() && !session.InReservedConn() { return nil } @@ -407,7 +408,7 @@ func (txc *TxConn) Release(ctx context.Context, session *SafeSession) error { allsessions := append(session.PreSessions, session.ShardSessions...) allsessions = append(allsessions, session.PostSessions...) - return txc.runSessions(ctx, allsessions, session.logging, func(ctx context.Context, s *vtgatepb.Session_ShardSession, logging *executeLogger) error { + return txc.runSessions(ctx, allsessions, session.GetLogger(), func(ctx context.Context, s *vtgatepb.Session_ShardSession, logging *econtext.ExecuteLogger) error { if s.ReservedId == 0 && s.TransactionId == 0 { return nil } @@ -426,7 +427,7 @@ func (txc *TxConn) Release(ctx context.Context, session *SafeSession) error { } // ReleaseLock releases the reserved connection used for locking. -func (txc *TxConn) ReleaseLock(ctx context.Context, session *SafeSession) error { +func (txc *TxConn) ReleaseLock(ctx context.Context, session *econtext.SafeSession) error { if !session.InLockSession() { return nil } @@ -445,7 +446,7 @@ func (txc *TxConn) ReleaseLock(ctx context.Context, session *SafeSession) error } // ReleaseAll releases all the shard sessions and lock session. -func (txc *TxConn) ReleaseAll(ctx context.Context, session *SafeSession) error { +func (txc *TxConn) ReleaseAll(ctx context.Context, session *econtext.SafeSession) error { if !session.InTransaction() && !session.InReservedConn() && !session.InLockSession() { return nil } @@ -457,7 +458,7 @@ func (txc *TxConn) ReleaseAll(ctx context.Context, session *SafeSession) error { allsessions = append(allsessions, session.LockSession) } - return txc.runSessions(ctx, allsessions, session.logging, func(ctx context.Context, s *vtgatepb.Session_ShardSession, loggging *executeLogger) error { + return txc.runSessions(ctx, allsessions, session.GetLogger(), func(ctx context.Context, s *vtgatepb.Session_ShardSession, loggging *econtext.ExecuteLogger) error { if s.ReservedId == 0 && s.TransactionId == 0 { return nil } @@ -529,12 +530,12 @@ func (txc *TxConn) resolveTx(ctx context.Context, target *querypb.Target, transa // rollbackTx rollbacks the specified distributed transaction. // Rollbacks happens on the metadata manager and all participants irrespective of the failure. -func (txc *TxConn) rollbackTx(ctx context.Context, dtid string, mmShard *vtgatepb.Session_ShardSession, participants []*vtgatepb.Session_ShardSession, logging *executeLogger) error { +func (txc *TxConn) rollbackTx(ctx context.Context, dtid string, mmShard *vtgatepb.Session_ShardSession, participants []*vtgatepb.Session_ShardSession, logging *econtext.ExecuteLogger) error { var errs []error if mmErr := txc.rollbackMM(ctx, dtid, mmShard); mmErr != nil { errs = append(errs, mmErr) } - if rmErr := txc.runSessions(ctx, participants, logging, func(ctx context.Context, session *vtgatepb.Session_ShardSession, logger *executeLogger) error { + if rmErr := txc.runSessions(ctx, participants, logging, func(ctx context.Context, session *vtgatepb.Session_ShardSession, logger *econtext.ExecuteLogger) error { return txc.tabletGateway.RollbackPrepared(ctx, session.Target, dtid, session.TransactionId) }); rmErr != nil { errs = append(errs, rmErr) @@ -575,7 +576,7 @@ func (txc *TxConn) resumeCommit(ctx context.Context, target *querypb.Target, tra } // runSessions executes the action for all shardSessions in parallel and returns a consolidated error. -func (txc *TxConn) runSessions(ctx context.Context, shardSessions []*vtgatepb.Session_ShardSession, logging *executeLogger, action func(context.Context, *vtgatepb.Session_ShardSession, *executeLogger) error) error { +func (txc *TxConn) runSessions(ctx context.Context, shardSessions []*vtgatepb.Session_ShardSession, logging *econtext.ExecuteLogger, action func(context.Context, *vtgatepb.Session_ShardSession, *econtext.ExecuteLogger) error) error { // Fastpath. if len(shardSessions) == 1 { return action(ctx, shardSessions[0], logging) diff --git a/go/vt/vtgate/tx_conn_test.go b/go/vt/vtgate/tx_conn_test.go index 9d49626f6f1..333094569c8 100644 --- a/go/vt/vtgate/tx_conn_test.go +++ b/go/vt/vtgate/tx_conn_test.go @@ -26,6 +26,8 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + econtext "vitess.io/vitess/go/vt/vtgate/executorcontext" + "vitess.io/vitess/go/event/syslogger" "vitess.io/vitess/go/mysql/sqlerror" "vitess.io/vitess/go/test/utils" @@ -51,7 +53,7 @@ func TestTxConnBegin(t *testing.T) { session := &vtgatepb.Session{} // begin - safeSession := NewSafeSession(session) + safeSession := econtext.NewSafeSession(session) err := sc.txConn.Begin(ctx, safeSession, nil) require.NoError(t, err) wantSession := vtgatepb.Session{InTransaction: true} @@ -75,7 +77,7 @@ func TestTxConnCommitFailure(t *testing.T) { // Sequence the executes to ensure commit order - session := NewSafeSession(&vtgatepb.Session{InTransaction: true}) + session := econtext.NewSafeSession(&vtgatepb.Session{InTransaction: true}) sc.ExecuteMultiShard(ctx, nil, rssm[0], queries, session, false, false, nullResultsObserver{}) wantSession := vtgatepb.Session{ InTransaction: true, @@ -176,7 +178,7 @@ func TestTxConnCommitFailureAfterNonAtomicCommitMaxShards(t *testing.T) { // Sequence the executes to ensure commit order - session := NewSafeSession(&vtgatepb.Session{InTransaction: true}) + session := econtext.NewSafeSession(&vtgatepb.Session{InTransaction: true}) wantSession := vtgatepb.Session{ InTransaction: true, ShardSessions: []*vtgatepb.Session_ShardSession{}, @@ -230,7 +232,7 @@ func TestTxConnCommitFailureBeforeNonAtomicCommitMaxShards(t *testing.T) { // Sequence the executes to ensure commit order - session := NewSafeSession(&vtgatepb.Session{InTransaction: true}) + session := econtext.NewSafeSession(&vtgatepb.Session{InTransaction: true}) wantSession := vtgatepb.Session{ InTransaction: true, ShardSessions: []*vtgatepb.Session_ShardSession{}, @@ -282,7 +284,7 @@ func TestTxConnCommitSuccess(t *testing.T) { sc.txConn.mode = vtgatepb.TransactionMode_MULTI // Sequence the executes to ensure commit order - session := NewSafeSession(&vtgatepb.Session{InTransaction: true}) + session := econtext.NewSafeSession(&vtgatepb.Session{InTransaction: true}) sc.ExecuteMultiShard(ctx, nil, rss0, queries, session, false, false, nullResultsObserver{}) wantSession := vtgatepb.Session{ InTransaction: true, @@ -335,7 +337,7 @@ func TestTxConnReservedCommitSuccess(t *testing.T) { sc.txConn.mode = vtgatepb.TransactionMode_MULTI // Sequence the executes to ensure commit order - session := NewSafeSession(&vtgatepb.Session{InTransaction: true, InReservedConn: true}) + session := econtext.NewSafeSession(&vtgatepb.Session{InTransaction: true, InReservedConn: true}) sc.ExecuteMultiShard(ctx, nil, rss0, queries, session, false, false, nullResultsObserver{}) wantSession := vtgatepb.Session{ InTransaction: true, @@ -420,7 +422,7 @@ func TestTxConnReservedOn2ShardTxOn1ShardAndCommit(t *testing.T) { sc.txConn.mode = vtgatepb.TransactionMode_MULTI // Sequence the executes to ensure shard session order - session := NewSafeSession(&vtgatepb.Session{InReservedConn: true}) + session := econtext.NewSafeSession(&vtgatepb.Session{InReservedConn: true}) // this will create reserved connections against all tablets _, errs := sc.ExecuteMultiShard(ctx, nil, rss1, queries, session, false, false, nullResultsObserver{}) @@ -515,7 +517,7 @@ func TestTxConnReservedOn2ShardTxOn1ShardAndRollback(t *testing.T) { sc.txConn.mode = vtgatepb.TransactionMode_MULTI // Sequence the executes to ensure shard session order - session := NewSafeSession(&vtgatepb.Session{InReservedConn: true}) + session := econtext.NewSafeSession(&vtgatepb.Session{InReservedConn: true}) // this will create reserved connections against all tablets _, errs := sc.ExecuteMultiShard(ctx, nil, rss1, queries, session, false, false, nullResultsObserver{}) @@ -611,7 +613,7 @@ func TestTxConnCommitOrderFailure1(t *testing.T) { queries := []*querypb.BoundQuery{{Sql: "query1"}} // Sequence the executes to ensure commit order - session := NewSafeSession(&vtgatepb.Session{InTransaction: true}) + session := econtext.NewSafeSession(&vtgatepb.Session{InTransaction: true}) sc.ExecuteMultiShard(ctx, nil, rss0, queries, session, false, false, nullResultsObserver{}) session.SetCommitOrder(vtgatepb.CommitOrder_PRE) @@ -646,7 +648,7 @@ func TestTxConnCommitOrderFailure2(t *testing.T) { }} // Sequence the executes to ensure commit order - session := NewSafeSession(&vtgatepb.Session{InTransaction: true}) + session := econtext.NewSafeSession(&vtgatepb.Session{InTransaction: true}) sc.ExecuteMultiShard(context.Background(), nil, rss1, queries, session, false, false, nullResultsObserver{}) session.SetCommitOrder(vtgatepb.CommitOrder_PRE) @@ -680,7 +682,7 @@ func TestTxConnCommitOrderFailure3(t *testing.T) { }} // Sequence the executes to ensure commit order - session := NewSafeSession(&vtgatepb.Session{InTransaction: true}) + session := econtext.NewSafeSession(&vtgatepb.Session{InTransaction: true}) sc.ExecuteMultiShard(ctx, nil, rss0, queries, session, false, false, nullResultsObserver{}) session.SetCommitOrder(vtgatepb.CommitOrder_PRE) @@ -722,7 +724,7 @@ func TestTxConnCommitOrderSuccess(t *testing.T) { }} // Sequence the executes to ensure commit order - session := NewSafeSession(&vtgatepb.Session{InTransaction: true}) + session := econtext.NewSafeSession(&vtgatepb.Session{InTransaction: true}) sc.ExecuteMultiShard(ctx, nil, rss0, queries, session, false, false, nullResultsObserver{}) wantSession := vtgatepb.Session{ InTransaction: true, @@ -820,7 +822,7 @@ func TestTxConnReservedCommitOrderSuccess(t *testing.T) { }} // Sequence the executes to ensure commit order - session := NewSafeSession(&vtgatepb.Session{InTransaction: true, InReservedConn: true}) + session := econtext.NewSafeSession(&vtgatepb.Session{InTransaction: true, InReservedConn: true}) sc.ExecuteMultiShard(ctx, nil, rss0, queries, session, false, false, nullResultsObserver{}) wantSession := vtgatepb.Session{ InTransaction: true, @@ -957,7 +959,7 @@ func TestTxConnCommit2PC(t *testing.T) { sc, sbc0, sbc1, rss0, _, rss01 := newTestTxConnEnv(t, ctx, "TestTxConnCommit2PC") - session := NewSafeSession(&vtgatepb.Session{InTransaction: true}) + session := econtext.NewSafeSession(&vtgatepb.Session{InTransaction: true}) sc.ExecuteMultiShard(ctx, nil, rss0, queries, session, false, false, nullResultsObserver{}) sc.ExecuteMultiShard(ctx, nil, rss01, twoQueries, session, false, false, nullResultsObserver{}) session.TransactionMode = vtgatepb.TransactionMode_TWOPC @@ -974,7 +976,7 @@ func TestTxConnCommit2PCOneParticipant(t *testing.T) { ctx := utils.LeakCheckContext(t) sc, sbc0, _, rss0, _, _ := newTestTxConnEnv(t, ctx, "TestTxConnCommit2PCOneParticipant") - session := NewSafeSession(&vtgatepb.Session{InTransaction: true}) + session := econtext.NewSafeSession(&vtgatepb.Session{InTransaction: true}) sc.ExecuteMultiShard(ctx, nil, rss0, queries, session, false, false, nullResultsObserver{}) session.TransactionMode = vtgatepb.TransactionMode_TWOPC require.NoError(t, @@ -987,7 +989,7 @@ func TestTxConnCommit2PCCreateTransactionFail(t *testing.T) { sc, sbc0, sbc1, rss0, rss1, _ := newTestTxConnEnv(t, ctx, "TestTxConnCommit2PCCreateTransactionFail") - session := NewSafeSession(&vtgatepb.Session{InTransaction: true}) + session := econtext.NewSafeSession(&vtgatepb.Session{InTransaction: true}) sc.ExecuteMultiShard(ctx, nil, rss0, queries, session, false, false, nullResultsObserver{}) sc.ExecuteMultiShard(ctx, nil, rss1, queries, session, false, false, nullResultsObserver{}) @@ -1009,7 +1011,7 @@ func TestTxConnCommit2PCPrepareFail(t *testing.T) { sc, sbc0, sbc1, rss0, _, rss01 := newTestTxConnEnv(t, ctx, "TestTxConnCommit2PCPrepareFail") - session := NewSafeSession(&vtgatepb.Session{InTransaction: true}) + session := econtext.NewSafeSession(&vtgatepb.Session{InTransaction: true}) sc.ExecuteMultiShard(ctx, nil, rss0, queries, session, false, false, nullResultsObserver{}) sc.ExecuteMultiShard(ctx, nil, rss01, twoQueries, session, false, false, nullResultsObserver{}) @@ -1035,7 +1037,7 @@ func TestTxConnCommit2PCStartCommitFail(t *testing.T) { sc, sbc0, sbc1, rss0, _, rss01 := newTestTxConnEnv(t, ctx, "TestTxConnCommit2PCStartCommitFail") - session := NewSafeSession(&vtgatepb.Session{InTransaction: true}) + session := econtext.NewSafeSession(&vtgatepb.Session{InTransaction: true}) sc.ExecuteMultiShard(ctx, nil, rss0, queries, session, false, false, nullResultsObserver{}) sc.ExecuteMultiShard(ctx, nil, rss01, twoQueries, session, false, false, nullResultsObserver{}) @@ -1054,7 +1056,7 @@ func TestTxConnCommit2PCStartCommitFail(t *testing.T) { sbc0.ResetCounter() sbc1.ResetCounter() - session = NewSafeSession(&vtgatepb.Session{InTransaction: true}) + session = econtext.NewSafeSession(&vtgatepb.Session{InTransaction: true}) sc.ExecuteMultiShard(ctx, nil, rss0, queries, session, false, false, nullResultsObserver{}) sc.ExecuteMultiShard(ctx, nil, rss01, twoQueries, session, false, false, nullResultsObserver{}) @@ -1077,7 +1079,7 @@ func TestTxConnCommit2PCCommitPreparedFail(t *testing.T) { sc, sbc0, sbc1, rss0, _, rss01 := newTestTxConnEnv(t, ctx, "TestTxConnCommit2PCCommitPreparedFail") - session := NewSafeSession(&vtgatepb.Session{InTransaction: true}) + session := econtext.NewSafeSession(&vtgatepb.Session{InTransaction: true}) sc.ExecuteMultiShard(ctx, nil, rss0, queries, session, false, false, nullResultsObserver{}) sc.ExecuteMultiShard(ctx, nil, rss01, twoQueries, session, false, false, nullResultsObserver{}) @@ -1097,7 +1099,7 @@ func TestTxConnCommit2PCConcludeTransactionFail(t *testing.T) { sc, sbc0, sbc1, rss0, _, rss01 := newTestTxConnEnv(t, ctx, "TestTxConnCommit2PCConcludeTransactionFail") - session := NewSafeSession(&vtgatepb.Session{InTransaction: true}) + session := econtext.NewSafeSession(&vtgatepb.Session{InTransaction: true}) sc.ExecuteMultiShard(ctx, nil, rss0, queries, session, false, false, nullResultsObserver{}) sc.ExecuteMultiShard(ctx, nil, rss01, twoQueries, session, false, false, nullResultsObserver{}) @@ -1117,7 +1119,7 @@ func TestTxConnRollback(t *testing.T) { sc, sbc0, sbc1, rss0, _, rss01 := newTestTxConnEnv(t, ctx, "TxConnRollback") - session := NewSafeSession(&vtgatepb.Session{InTransaction: true}) + session := econtext.NewSafeSession(&vtgatepb.Session{InTransaction: true}) sc.ExecuteMultiShard(ctx, nil, rss0, queries, session, false, false, nullResultsObserver{}) sc.ExecuteMultiShard(ctx, nil, rss01, twoQueries, session, false, false, nullResultsObserver{}) require.NoError(t, @@ -1133,7 +1135,7 @@ func TestTxConnReservedRollback(t *testing.T) { sc, sbc0, sbc1, rss0, _, rss01 := newTestTxConnEnv(t, ctx, "TxConnReservedRollback") - session := NewSafeSession(&vtgatepb.Session{InTransaction: true, InReservedConn: true}) + session := econtext.NewSafeSession(&vtgatepb.Session{InTransaction: true, InReservedConn: true}) sc.ExecuteMultiShard(ctx, nil, rss0, queries, session, false, false, nullResultsObserver{}) sc.ExecuteMultiShard(ctx, nil, rss01, twoQueries, session, false, false, nullResultsObserver{}) require.NoError(t, @@ -1170,7 +1172,7 @@ func TestTxConnReservedRollbackFailure(t *testing.T) { sc, sbc0, sbc1, rss0, rss1, rss01 := newTestTxConnEnv(t, ctx, "TxConnReservedRollback") - session := NewSafeSession(&vtgatepb.Session{InTransaction: true, InReservedConn: true}) + session := econtext.NewSafeSession(&vtgatepb.Session{InTransaction: true, InReservedConn: true}) sc.ExecuteMultiShard(ctx, nil, rss0, queries, session, false, false, nullResultsObserver{}) sc.ExecuteMultiShard(ctx, nil, rss01, twoQueries, session, false, false, nullResultsObserver{}) @@ -1449,7 +1451,7 @@ func TestTxConnMultiGoSessions(t *testing.T) { Keyspace: "0", }, }} - err := txc.runSessions(ctx, input, nil, func(ctx context.Context, s *vtgatepb.Session_ShardSession, logger *executeLogger) error { + err := txc.runSessions(ctx, input, nil, func(ctx context.Context, s *vtgatepb.Session_ShardSession, logger *econtext.ExecuteLogger) error { return vterrors.Errorf(vtrpcpb.Code_INTERNAL, "err %s", s.Target.Keyspace) }) want := "err 0" @@ -1464,7 +1466,7 @@ func TestTxConnMultiGoSessions(t *testing.T) { Keyspace: "1", }, }} - err = txc.runSessions(ctx, input, nil, func(ctx context.Context, s *vtgatepb.Session_ShardSession, logger *executeLogger) error { + err = txc.runSessions(ctx, input, nil, func(ctx context.Context, s *vtgatepb.Session_ShardSession, logger *econtext.ExecuteLogger) error { return vterrors.Errorf(vtrpcpb.Code_INTERNAL, "err %s", s.Target.Keyspace) }) want = "err 0\nerr 1" @@ -1472,7 +1474,7 @@ func TestTxConnMultiGoSessions(t *testing.T) { wantCode := vtrpcpb.Code_INTERNAL assert.Equal(t, wantCode, vterrors.Code(err), "error code") - err = txc.runSessions(ctx, input, nil, func(ctx context.Context, s *vtgatepb.Session_ShardSession, logger *executeLogger) error { + err = txc.runSessions(ctx, input, nil, func(ctx context.Context, s *vtgatepb.Session_ShardSession, logger *econtext.ExecuteLogger) error { return nil }) require.NoError(t, err) @@ -1515,7 +1517,7 @@ func TestTxConnAccessModeReset(t *testing.T) { tcases := []struct { name string - f func(ctx context.Context, session *SafeSession) error + f func(ctx context.Context, session *econtext.SafeSession) error }{{ name: "begin-commit", f: sc.txConn.Commit, @@ -1532,7 +1534,7 @@ func TestTxConnAccessModeReset(t *testing.T) { for _, tcase := range tcases { t.Run(tcase.name, func(t *testing.T) { - safeSession := NewSafeSession(&vtgatepb.Session{ + safeSession := econtext.NewSafeSession(&vtgatepb.Session{ Options: &querypb.ExecuteOptions{ TransactionAccessMode: []querypb.ExecuteOptions_TransactionAccessMode{querypb.ExecuteOptions_READ_ONLY}, }, diff --git a/go/vt/vtgate/vindexes/cached_size.go b/go/vt/vtgate/vindexes/cached_size.go index a97411a6ac8..eeadb69b532 100644 --- a/go/vt/vtgate/vindexes/cached_size.go +++ b/go/vt/vtgate/vindexes/cached_size.go @@ -175,6 +175,18 @@ func (cached *Keyspace) CachedSize(alloc bool) int64 { size += hack.RuntimeAllocSize(int64(len(cached.Name))) return size } +func (cached *LookupCost) CachedSize(alloc bool) int64 { + if cached == nil { + return int64(0) + } + size := int64(0) + if alloc { + size += int64(16) + } + // field LookupNonUnique *vitess.io/vitess/go/vt/vtgate/vindexes.LookupNonUnique + size += cached.LookupNonUnique.CachedSize(true) + return size +} func (cached *LookupHash) CachedSize(alloc bool) int64 { if cached == nil { return int64(0) diff --git a/go/vt/vtgate/vindexes/lookup_cost.go b/go/vt/vtgate/vindexes/lookup_cost.go new file mode 100644 index 00000000000..6556032cea5 --- /dev/null +++ b/go/vt/vtgate/vindexes/lookup_cost.go @@ -0,0 +1,70 @@ +/* +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 vindexes + +import ( + "strconv" +) + +var ( + _ SingleColumn = (*LookupCost)(nil) + _ Lookup = (*LookupCost)(nil) + _ LookupPlanable = (*LookupCost)(nil) +) + +func init() { + Register("lookup_cost", newLookupCost) +} + +const defaultCost = 5 + +// LookupCost defines a test vindex that uses the cost provided by the user. +// This is a test vindex. +type LookupCost struct { + *LookupNonUnique + cost int +} + +// Cost returns the cost of this vindex as provided. +func (lc *LookupCost) Cost() int { + return lc.cost +} + +func newLookupCost(name string, m map[string]string) (Vindex, error) { + lookup, err := newLookup(name, m) + if err != nil { + return nil, err + } + cost := getInt(m, "cost", defaultCost) + return &LookupCost{ + LookupNonUnique: lookup.(*LookupNonUnique), + cost: cost, + }, nil + +} + +func getInt(m map[string]string, key string, defaultVal int) int { + val, ok := m[key] + if !ok { + return defaultVal + } + intVal, err := strconv.Atoi(val) + if err != nil { + return defaultVal + } + return intVal +} diff --git a/go/vt/vtgate/vindexes/vschema_test.go b/go/vt/vtgate/vindexes/vschema_test.go index 25f8e135698..f9bcf43ddaa 100644 --- a/go/vt/vtgate/vindexes/vschema_test.go +++ b/go/vt/vtgate/vindexes/vschema_test.go @@ -21,6 +21,7 @@ import ( "encoding/json" "errors" "fmt" + "os" "reflect" "strings" "testing" @@ -3551,6 +3552,20 @@ func TestFindTableWithSequences(t *testing.T) { } } +func TestGlobalTables(t *testing.T) { + input, err := os.ReadFile("../planbuilder/testdata/vschemas/schema.json") + require.NoError(t, err) + + var vs vschemapb.SrvVSchema + err = json2.UnmarshalPB(input, &vs) + require.NoError(t, err) + + got := BuildVSchema(&vs, sqlparser.NewTestParser()) + tbl, err := got.findGlobalTable("user", false) + require.NoError(t, err) + assert.NotNil(t, tbl) +} + func vindexNames(vindexes []*ColumnVindex) (result []string) { for _, vindex := range vindexes { result = append(result, vindex.Name) diff --git a/go/vt/vtgate/viperconfig.go b/go/vt/vtgate/viperconfig.go new file mode 100644 index 00000000000..ec77ff62d4f --- /dev/null +++ b/go/vt/vtgate/viperconfig.go @@ -0,0 +1,16 @@ +package vtgate + +import "vitess.io/vitess/go/viperutil" + +type dynamicViperConfig struct { + onlineDDL viperutil.Value[bool] + directDDL viperutil.Value[bool] +} + +func (d *dynamicViperConfig) OnlineEnabled() bool { + return d.onlineDDL.Get() +} + +func (d *dynamicViperConfig) DirectEnabled() bool { + return d.directDDL.Get() +} diff --git a/go/vt/vtgate/vschema_manager.go b/go/vt/vtgate/vschema_manager.go index 2b6761f4a8e..62ea2cd3455 100644 --- a/go/vt/vtgate/vschema_manager.go +++ b/go/vt/vtgate/vschema_manager.go @@ -33,8 +33,6 @@ import ( vschemapb "vitess.io/vitess/go/vt/proto/vschema" ) -var _ VSchemaOperator = (*VSchemaManager)(nil) - // VSchemaManager is used to watch for updates to the vschema and to implement // the DDL commands to add / remove vindexes type VSchemaManager struct { diff --git a/go/vt/vtgate/vschemaacl/vschemaacl.go b/go/vt/vtgate/vschemaacl/vschemaacl.go index 5345d1437fc..08f6c2b0cd4 100644 --- a/go/vt/vtgate/vschemaacl/vschemaacl.go +++ b/go/vt/vtgate/vschemaacl/vschemaacl.go @@ -18,26 +18,67 @@ package vschemaacl import ( "strings" - "sync" "github.com/spf13/pflag" + "github.com/spf13/viper" - "vitess.io/vitess/go/vt/servenv" - + "vitess.io/vitess/go/viperutil" querypb "vitess.io/vitess/go/vt/proto/query" + "vitess.io/vitess/go/vt/servenv" ) -var ( - // AuthorizedDDLUsers specifies the users that can perform ddl operations - AuthorizedDDLUsers string - - // ddlAllowAll is true if the special value of "*" was specified +type authorizedDDLUsers struct { allowAll bool + acl map[string]struct{} + source string +} + +func NewAuthorizedDDLUsers(users string) *authorizedDDLUsers { + acl := make(map[string]struct{}) + allowAll := false + + switch users { + case "": + case "%": + allowAll = true + default: + for _, user := range strings.Split(users, ",") { + user = strings.TrimSpace(user) + acl[user] = struct{}{} + } + } + + return &authorizedDDLUsers{ + allowAll: allowAll, + acl: acl, + source: users, + } +} - // ddlACL contains a set of allowed usernames - acl map[string]struct{} +func (a *authorizedDDLUsers) String() string { + return a.source +} - initMu sync.Mutex +var ( + // AuthorizedDDLUsers specifies the users that can perform ddl operations + AuthorizedDDLUsers = viperutil.Configure( + "vschema_ddl_authorized_users", + viperutil.Options[*authorizedDDLUsers]{ + FlagName: "vschema_ddl_authorized_users", + Default: &authorizedDDLUsers{}, + Dynamic: true, + GetFunc: func(v *viper.Viper) func(key string) *authorizedDDLUsers { + return func(key string) *authorizedDDLUsers { + newVal := v.GetString(key) + curVal, ok := v.Get(key).(*authorizedDDLUsers) + if ok && newVal == curVal.source { + return curVal + } + return NewAuthorizedDDLUsers(newVal) + } + }, + }, + ) ) // RegisterSchemaACLFlags installs log flags on the given FlagSet. @@ -46,7 +87,8 @@ var ( // calls this function, or call this function directly before parsing // command-line arguments. func RegisterSchemaACLFlags(fs *pflag.FlagSet) { - fs.StringVar(&AuthorizedDDLUsers, "vschema_ddl_authorized_users", AuthorizedDDLUsers, "List of users authorized to execute vschema ddl operations, or '%' to allow all users.") + fs.String("vschema_ddl_authorized_users", "", "List of users authorized to execute vschema ddl operations, or '%' to allow all users.") + viperutil.BindFlags(fs, AuthorizedDDLUsers) } func init() { @@ -55,33 +97,14 @@ func init() { } } -// Init parses the users option and sets allowAll / acl accordingly -func Init() { - initMu.Lock() - defer initMu.Unlock() - acl = make(map[string]struct{}) - allowAll = false - - if AuthorizedDDLUsers == "%" { - allowAll = true - return - } else if AuthorizedDDLUsers == "" { - return - } - - for _, user := range strings.Split(AuthorizedDDLUsers, ",") { - user = strings.TrimSpace(user) - acl[user] = struct{}{} - } -} - // Authorized returns true if the given caller is allowed to execute vschema operations func Authorized(caller *querypb.VTGateCallerID) bool { - if allowAll { + users := AuthorizedDDLUsers.Get() + if users.allowAll { return true } user := caller.GetUsername() - _, ok := acl[user] + _, ok := users.acl[user] return ok } diff --git a/go/vt/vtgate/vschemaacl/vschemaacl_test.go b/go/vt/vtgate/vschemaacl/vschemaacl_test.go index faa2dbfc294..cfd1de705af 100644 --- a/go/vt/vtgate/vschemaacl/vschemaacl_test.go +++ b/go/vt/vtgate/vschemaacl/vschemaacl_test.go @@ -35,8 +35,7 @@ func TestVschemaAcl(t *testing.T) { } // Test wildcard - AuthorizedDDLUsers = "%" - Init() + AuthorizedDDLUsers.Set(NewAuthorizedDDLUsers("%")) if !Authorized(&redUser) { t.Errorf("user should be authorized") @@ -46,8 +45,7 @@ func TestVschemaAcl(t *testing.T) { } // Test user list - AuthorizedDDLUsers = "oneUser, twoUser, redUser, blueUser" - Init() + AuthorizedDDLUsers.Set(NewAuthorizedDDLUsers("oneUser, twoUser, redUser, blueUser")) if !Authorized(&redUser) { t.Errorf("user should be authorized") @@ -57,8 +55,7 @@ func TestVschemaAcl(t *testing.T) { } // Revert to baseline state for other tests - AuthorizedDDLUsers = "" - Init() + AuthorizedDDLUsers.Set(NewAuthorizedDDLUsers("")) // By default no users are allowed in if Authorized(&redUser) { diff --git a/go/vt/vtgate/vtgate.go b/go/vt/vtgate/vtgate.go index e9e7cd65011..8bab05479dd 100644 --- a/go/vt/vtgate/vtgate.go +++ b/go/vt/vtgate/vtgate.go @@ -34,6 +34,7 @@ import ( "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/stats" "vitess.io/vitess/go/tb" + "vitess.io/vitess/go/viperutil" "vitess.io/vitess/go/vt/discovery" "vitess.io/vitess/go/vt/key" "vitess.io/vitess/go/vt/log" @@ -51,6 +52,7 @@ import ( "vitess.io/vitess/go/vt/topo/topoproto" "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vterrors" + econtext "vitess.io/vitess/go/vt/vtgate/executorcontext" "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" vtschema "vitess.io/vitess/go/vt/vtgate/schema" "vitess.io/vitess/go/vt/vtgate/txresolver" @@ -93,8 +95,24 @@ var ( foreignKeyMode = "allow" dbDDLPlugin = "fail" defaultDDLStrategy = string(schema.DDLStrategyDirect) - enableOnlineDDL = true - enableDirectDDL = true + + enableOnlineDDL = viperutil.Configure( + "enable_online_ddl", + viperutil.Options[bool]{ + FlagName: "enable_online_ddl", + Default: true, + Dynamic: true, + }, + ) + + enableDirectDDL = viperutil.Configure( + "enable_direct_ddl", + viperutil.Options[bool]{ + FlagName: "enable_direct_ddl", + Default: true, + Dynamic: true, + }, + ) // schema tracking flags enableSchemaChangeSignal = true @@ -141,8 +159,8 @@ func registerFlags(fs *pflag.FlagSet) { fs.DurationVar(&lockHeartbeatTime, "lock_heartbeat_time", lockHeartbeatTime, "If there is lock function used. This will keep the lock connection active by using this heartbeat") fs.BoolVar(&warnShardedOnly, "warn_sharded_only", warnShardedOnly, "If any features that are only available in unsharded mode are used, query execution warnings will be added to the session") fs.StringVar(&foreignKeyMode, "foreign_key_mode", foreignKeyMode, "This is to provide how to handle foreign key constraint in create/alter table. Valid values are: allow, disallow") - fs.BoolVar(&enableOnlineDDL, "enable_online_ddl", enableOnlineDDL, "Allow users to submit, review and control Online DDL") - fs.BoolVar(&enableDirectDDL, "enable_direct_ddl", enableDirectDDL, "Allow users to submit direct DDL statements") + fs.Bool("enable_online_ddl", enableOnlineDDL.Default(), "Allow users to submit, review and control Online DDL") + fs.Bool("enable_direct_ddl", enableDirectDDL.Default(), "Allow users to submit direct DDL statements") fs.BoolVar(&enableSchemaChangeSignal, "schema_change_signal", enableSchemaChangeSignal, "Enable the schema tracker; requires queryserver-config-schema-change-signal to be enabled on the underlying vttablets for this to work") fs.IntVar(&queryTimeout, "query-timeout", queryTimeout, "Sets the default query timeout (in ms). Can be overridden by session variable (query_timeout) or comment directive (QUERY_TIMEOUT_MS)") fs.StringVar(&queryLogToFile, "log_queries_to_file", queryLogToFile, "Enable query logging to the specified file") @@ -154,6 +172,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") + + viperutil.BindFlags(fs, enableOnlineDDL, enableDirectDDL) } func init() { @@ -469,7 +489,7 @@ func (vtg *VTGate) Execute(ctx context.Context, mysqlCtx vtgateservice.MySQLConn if bvErr := sqltypes.ValidateBindVariables(bindVariables); bvErr != nil { err = vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "%v", bvErr) } else { - safeSession := NewSafeSession(session) + safeSession := econtext.NewSafeSession(session) qr, err = vtg.executor.Execute(ctx, mysqlCtx, "Execute", safeSession, sql, bindVariables) safeSession.RemoveInternalSavepoint() } @@ -526,7 +546,7 @@ func (vtg *VTGate) StreamExecute(ctx context.Context, mysqlCtx vtgateservice.MyS defer vtg.timings.Record(statsKey, time.Now()) - safeSession := NewSafeSession(session) + safeSession := econtext.NewSafeSession(session) var err error if bvErr := sqltypes.ValidateBindVariables(bindVariables); bvErr != nil { err = vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "%v", bvErr) @@ -560,7 +580,7 @@ func (vtg *VTGate) StreamExecute(ctx context.Context, mysqlCtx vtgateservice.MyS // same effect as if a "rollback" statement was executed, but does not affect the query // statistics. func (vtg *VTGate) CloseSession(ctx context.Context, session *vtgatepb.Session) error { - return vtg.executor.CloseSession(ctx, NewSafeSession(session)) + return vtg.executor.CloseSession(ctx, econtext.NewSafeSession(session)) } // Prepare supports non-streaming prepare statement query with multi shards @@ -575,7 +595,7 @@ func (vtg *VTGate) Prepare(ctx context.Context, session *vtgatepb.Session, sql s goto handleError } - fld, err = vtg.executor.Prepare(ctx, "Prepare", NewSafeSession(session), sql, bindVariables) + fld, err = vtg.executor.Prepare(ctx, "Prepare", econtext.NewSafeSession(session), sql, bindVariables) if err == nil { return session, fld, nil } diff --git a/go/vt/vtorc/config/config.go b/go/vt/vtorc/config/config.go index 2d21e377cb6..cafff5acce8 100644 --- a/go/vt/vtorc/config/config.go +++ b/go/vt/vtorc/config/config.go @@ -17,14 +17,12 @@ package config import ( - "encoding/json" - "fmt" - "os" "time" "github.com/spf13/pflag" - "vitess.io/vitess/go/vt/log" + "vitess.io/vitess/go/viperutil" + "vitess.io/vitess/go/vt/servenv" ) var configurationLoaded = make(chan bool) @@ -42,200 +40,296 @@ const ( ) var ( - sqliteDataFile = "file::memory:?mode=memory&cache=shared" - instancePollTime = 5 * time.Second - snapshotTopologyInterval = 0 * time.Hour - reasonableReplicationLag = 10 * time.Second - auditFileLocation = "" - auditToBackend = false - auditToSyslog = false - auditPurgeDuration = 7 * 24 * time.Hour // Equivalent of 7 days - recoveryPeriodBlockDuration = 30 * time.Second - preventCrossCellFailover = false - waitReplicasTimeout = 30 * time.Second - tolerableReplicationLag = 0 * time.Second - topoInformationRefreshDuration = 15 * time.Second - recoveryPollDuration = 1 * time.Second - ersEnabled = true - convertTabletsWithErrantGTIDs = false + instancePollTime = viperutil.Configure( + "instance-poll-time", + viperutil.Options[time.Duration]{ + FlagName: "instance-poll-time", + Default: 5 * time.Second, + Dynamic: true, + }, + ) + + preventCrossCellFailover = viperutil.Configure( + "prevent-cross-cell-failover", + viperutil.Options[bool]{ + FlagName: "prevent-cross-cell-failover", + Default: false, + Dynamic: true, + }, + ) + + sqliteDataFile = viperutil.Configure( + "sqlite-data-file", + viperutil.Options[string]{ + FlagName: "sqlite-data-file", + Default: "file::memory:?mode=memory&cache=shared", + Dynamic: false, + }, + ) + + snapshotTopologyInterval = viperutil.Configure( + "snapshot-topology-interval", + viperutil.Options[time.Duration]{ + FlagName: "snapshot-topology-interval", + Default: 0 * time.Hour, + Dynamic: true, + }, + ) + + reasonableReplicationLag = viperutil.Configure( + "reasonable-replication-lag", + viperutil.Options[time.Duration]{ + FlagName: "reasonable-replication-lag", + Default: 10 * time.Second, + Dynamic: true, + }, + ) + + auditFileLocation = viperutil.Configure( + "audit-file-location", + viperutil.Options[string]{ + FlagName: "audit-file-location", + Default: "", + Dynamic: false, + }, + ) + + auditToBackend = viperutil.Configure( + "audit-to-backend", + viperutil.Options[bool]{ + FlagName: "audit-to-backend", + Default: false, + Dynamic: true, + }, + ) + + auditToSyslog = viperutil.Configure( + "audit-to-syslog", + viperutil.Options[bool]{ + FlagName: "audit-to-syslog", + Default: false, + Dynamic: true, + }, + ) + + auditPurgeDuration = viperutil.Configure( + "audit-purge-duration", + viperutil.Options[time.Duration]{ + FlagName: "audit-purge-duration", + Default: 7 * 24 * time.Hour, + Dynamic: true, + }, + ) + + waitReplicasTimeout = viperutil.Configure( + "wait-replicas-timeout", + viperutil.Options[time.Duration]{ + FlagName: "wait-replicas-timeout", + Default: 30 * time.Second, + Dynamic: true, + }, + ) + + tolerableReplicationLag = viperutil.Configure( + "tolerable-replication-lag", + viperutil.Options[time.Duration]{ + FlagName: "tolerable-replication-lag", + Default: 0 * time.Second, + Dynamic: true, + }, + ) + + topoInformationRefreshDuration = viperutil.Configure( + "topo-information-refresh-duration", + viperutil.Options[time.Duration]{ + FlagName: "topo-information-refresh-duration", + Default: 15 * time.Second, + Dynamic: true, + }, + ) + + recoveryPollDuration = viperutil.Configure( + "recovery-poll-duration", + viperutil.Options[time.Duration]{ + FlagName: "recovery-poll-duration", + Default: 1 * time.Second, + Dynamic: true, + }, + ) + + ersEnabled = viperutil.Configure( + "allow-emergency-reparent", + viperutil.Options[bool]{ + FlagName: "allow-emergency-reparent", + Default: true, + Dynamic: true, + }, + ) + + convertTabletsWithErrantGTIDs = viperutil.Configure( + "change-tablets-with-errant-gtid-to-drained", + viperutil.Options[bool]{ + FlagName: "change-tablets-with-errant-gtid-to-drained", + Default: false, + Dynamic: true, + }, + ) ) -// RegisterFlags registers the flags required by VTOrc -func RegisterFlags(fs *pflag.FlagSet) { - fs.StringVar(&sqliteDataFile, "sqlite-data-file", sqliteDataFile, "SQLite Datafile to use as VTOrc's database") - fs.DurationVar(&instancePollTime, "instance-poll-time", instancePollTime, "Timer duration on which VTOrc refreshes MySQL information") - fs.DurationVar(&snapshotTopologyInterval, "snapshot-topology-interval", snapshotTopologyInterval, "Timer duration on which VTOrc takes a snapshot of the current MySQL information it has in the database. Should be in multiple of hours") - fs.DurationVar(&reasonableReplicationLag, "reasonable-replication-lag", reasonableReplicationLag, "Maximum replication lag on replicas which is deemed to be acceptable") - fs.StringVar(&auditFileLocation, "audit-file-location", auditFileLocation, "File location where the audit logs are to be stored") - fs.BoolVar(&auditToBackend, "audit-to-backend", auditToBackend, "Whether to store the audit log in the VTOrc database") - fs.BoolVar(&auditToSyslog, "audit-to-syslog", auditToSyslog, "Whether to store the audit log in the syslog") - fs.DurationVar(&auditPurgeDuration, "audit-purge-duration", auditPurgeDuration, "Duration for which audit logs are held before being purged. Should be in multiples of days") - fs.DurationVar(&recoveryPeriodBlockDuration, "recovery-period-block-duration", recoveryPeriodBlockDuration, "Duration for which a new recovery is blocked on an instance after running a recovery") - fs.MarkDeprecated("recovery-period-block-duration", "As of v20 this is ignored and will be removed in a future release.") - 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") - fs.BoolVar(&convertTabletsWithErrantGTIDs, "change-tablets-with-errant-gtid-to-drained", convertTabletsWithErrantGTIDs, "Whether VTOrc should be changing the type of tablets with errant GTIDs to DRAINED") +func init() { + servenv.OnParseFor("vtorc", registerFlags) } -// Configuration makes for vtorc configuration input, which can be provided by user via JSON formatted file. -// 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 { - SQLite3DataFile string // full path to sqlite3 datafile - InstancePollSeconds uint // Number of seconds between instance reads - SnapshotTopologiesIntervalHours uint // Interval in hour between snapshot-topologies invocation. Default: 0 (disabled) - ReasonableReplicationLagSeconds int // Above this value is considered a problem - AuditLogFile string // Name of log file for audit operations. Disabled when empty. - AuditToSyslog bool // If true, audit messages are written to syslog - AuditToBackendDB bool // If true, audit messages are written to the backend DB's `audit` table (default: true) - AuditPurgeDays uint // Days after which audit entries are purged from the database - 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 +// registerFlags registers the flags required by VTOrc +func registerFlags(fs *pflag.FlagSet) { + fs.String("sqlite-data-file", sqliteDataFile.Default(), "SQLite Datafile to use as VTOrc's database") + fs.Duration("instance-poll-time", instancePollTime.Default(), "Timer duration on which VTOrc refreshes MySQL information") + fs.Duration("snapshot-topology-interval", snapshotTopologyInterval.Default(), "Timer duration on which VTOrc takes a snapshot of the current MySQL information it has in the database. Should be in multiple of hours") + fs.Duration("reasonable-replication-lag", reasonableReplicationLag.Default(), "Maximum replication lag on replicas which is deemed to be acceptable") + fs.String("audit-file-location", auditFileLocation.Default(), "File location where the audit logs are to be stored") + fs.Bool("audit-to-backend", auditToBackend.Default(), "Whether to store the audit log in the VTOrc database") + fs.Bool("audit-to-syslog", auditToSyslog.Default(), "Whether to store the audit log in the syslog") + fs.Duration("audit-purge-duration", auditPurgeDuration.Default(), "Duration for which audit logs are held before being purged. Should be in multiples of days") + fs.Bool("prevent-cross-cell-failover", preventCrossCellFailover.Default(), "Prevent VTOrc from promoting a primary in a different cell than the current primary in case of a failover") + fs.Duration("wait-replicas-timeout", waitReplicasTimeout.Default(), "Duration for which to wait for replica's to respond when issuing RPCs") + fs.Duration("tolerable-replication-lag", tolerableReplicationLag.Default(), "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.Duration("topo-information-refresh-duration", topoInformationRefreshDuration.Default(), "Timer duration on which VTOrc refreshes the keyspace and vttablet records from the topology server") + fs.Duration("recovery-poll-duration", recoveryPollDuration.Default(), "Timer duration on which VTOrc polls its database to run a recovery") + fs.Bool("allow-emergency-reparent", ersEnabled.Default(), "Whether VTOrc should be allowed to run emergency reparent operation when it detects a dead primary") + fs.Bool("change-tablets-with-errant-gtid-to-drained", convertTabletsWithErrantGTIDs.Default(), "Whether VTOrc should be changing the type of tablets with errant GTIDs to DRAINED") + + viperutil.BindFlags(fs, + instancePollTime, + preventCrossCellFailover, + sqliteDataFile, + snapshotTopologyInterval, + reasonableReplicationLag, + auditFileLocation, + auditToBackend, + auditToSyslog, + auditPurgeDuration, + waitReplicasTimeout, + tolerableReplicationLag, + topoInformationRefreshDuration, + recoveryPollDuration, + ersEnabled, + convertTabletsWithErrantGTIDs, + ) } -// ToJSONString will marshal this configuration as JSON -func (config *Configuration) ToJSONString() string { - b, _ := json.Marshal(config) - return string(b) +// GetInstancePollTime is a getter function. +func GetInstancePollTime() time.Duration { + return instancePollTime.Get() } -// Config is *the* configuration instance, used globally to get configuration data -var Config = newConfiguration() -var readFileNames []string - -// UpdateConfigValuesFromFlags is used to update the config values from the flags defined. -// This is done before we read any configuration files from the user. So the config files take precedence. -func UpdateConfigValuesFromFlags() { - Config.SQLite3DataFile = sqliteDataFile - Config.InstancePollSeconds = uint(instancePollTime / time.Second) - Config.InstancePollSeconds = uint(instancePollTime / time.Second) - Config.SnapshotTopologiesIntervalHours = uint(snapshotTopologyInterval / time.Hour) - Config.ReasonableReplicationLagSeconds = int(reasonableReplicationLag / time.Second) - Config.AuditLogFile = auditFileLocation - Config.AuditToBackendDB = auditToBackend - Config.AuditToSyslog = auditToSyslog - Config.AuditPurgeDays = uint(auditPurgeDuration / (time.Hour * 24)) - 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) +// SetInstancePollTime is a setter function. +func SetInstancePollTime(v time.Duration) { + instancePollTime.Set(v) } -// ERSEnabled reports whether VTOrc is allowed to run ERS or not. -func ERSEnabled() bool { - return ersEnabled +// GetInstancePollSeconds gets the instance poll time but in seconds. +func GetInstancePollSeconds() uint { + return uint(instancePollTime.Get() / time.Second) } -// SetERSEnabled sets the value for the ersEnabled variable. This should only be used from tests. -func SetERSEnabled(val bool) { - ersEnabled = val +// GetPreventCrossCellFailover is a getter function. +func GetPreventCrossCellFailover() bool { + return preventCrossCellFailover.Get() } -// ConvertTabletWithErrantGTIDs reports whether VTOrc is allowed to change the tablet type of tablets with errant GTIDs to DRAINED. -func ConvertTabletWithErrantGTIDs() bool { - return convertTabletsWithErrantGTIDs +// GetSQLiteDataFile is a getter function. +func GetSQLiteDataFile() string { + return sqliteDataFile.Get() } -// SetConvertTabletWithErrantGTIDs sets the value for the convertTabletWithErrantGTIDs variable. This should only be used from tests. -func SetConvertTabletWithErrantGTIDs(val bool) { - convertTabletsWithErrantGTIDs = val +// GetReasonableReplicationLagSeconds gets the reasonable replication lag but in seconds. +func GetReasonableReplicationLagSeconds() int64 { + return int64(reasonableReplicationLag.Get() / time.Second) +} + +// GetSnapshotTopologyInterval is a getter function. +func GetSnapshotTopologyInterval() time.Duration { + return snapshotTopologyInterval.Get() } -// LogConfigValues is used to log the config values. -func LogConfigValues() { - b, _ := json.MarshalIndent(Config, "", "\t") - log.Infof("Running with Configuration - %v", string(b)) +// GetAuditFileLocation is a getter function. +func GetAuditFileLocation() string { + return auditFileLocation.Get() } -func newConfiguration() *Configuration { - return &Configuration{ - SQLite3DataFile: "file::memory:?mode=memory&cache=shared", - InstancePollSeconds: 5, - SnapshotTopologiesIntervalHours: 0, - ReasonableReplicationLagSeconds: 10, - AuditLogFile: "", - AuditToSyslog: false, - AuditToBackendDB: false, - AuditPurgeDays: 7, - RecoveryPeriodBlockSeconds: 30, - PreventCrossDataCenterPrimaryFailover: false, - WaitReplicasTimeoutSeconds: 30, - TopoInformationRefreshSeconds: 15, - RecoveryPollSeconds: 1, - } +// SetAuditFileLocation is a setter function. +func SetAuditFileLocation(v string) { + auditFileLocation.Set(v) } -func (config *Configuration) postReadAdjustments() error { - if config.SQLite3DataFile == "" { - return fmt.Errorf("SQLite3DataFile must be set") - } +// GetAuditToSyslog is a getter function. +func GetAuditToSyslog() bool { + return auditToSyslog.Get() +} + +// SetAuditToSyslog is a setter function. +func SetAuditToSyslog(v bool) { + auditToSyslog.Set(v) +} + +// GetAuditToBackend is a getter function. +func GetAuditToBackend() bool { + return auditToBackend.Get() +} + +// SetAuditToBackend is a setter function. +func SetAuditToBackend(v bool) { + auditToBackend.Set(v) +} - return nil +// GetAuditPurgeDays gets the audit purge duration but in days. +func GetAuditPurgeDays() int64 { + return int64(auditPurgeDuration.Get() / (24 * time.Hour)) } -// read reads configuration from given file, or silently skips if the file does not exist. -// If the file does exist, then it is expected to be in valid JSON format or the function bails out. -func read(fileName string) (*Configuration, error) { - if fileName == "" { - return Config, fmt.Errorf("Empty file name") - } - file, err := os.Open(fileName) - if err != nil { - return Config, err - } - decoder := json.NewDecoder(file) - err = decoder.Decode(Config) - if err == nil { - log.Infof("Read config: %s", fileName) - } else { - log.Fatal("Cannot read config file:", fileName, err) - } - if err := Config.postReadAdjustments(); err != nil { - log.Fatal(err) - } - return Config, err +// SetAuditPurgeDays sets the audit purge duration. +func SetAuditPurgeDays(days int64) { + auditPurgeDuration.Set(time.Duration(days) * 24 * time.Hour) } -// Read reads configuration from zero, either, some or all given files, in order of input. -// A file can override configuration provided in previous file. -func Read(fileNames ...string) *Configuration { - for _, fileName := range fileNames { - _, _ = read(fileName) - } - readFileNames = fileNames - return Config +// GetWaitReplicasTimeout is a getter function. +func GetWaitReplicasTimeout() time.Duration { + return waitReplicasTimeout.Get() } -// ForceRead reads configuration from given file name or bails out if it fails -func ForceRead(fileName string) *Configuration { - _, err := read(fileName) - if err != nil { - log.Fatal("Cannot read config file:", fileName, err) - } - readFileNames = []string{fileName} - return Config +// GetTolerableReplicationLag is a getter function. +func GetTolerableReplicationLag() time.Duration { + return tolerableReplicationLag.Get() } -// Reload re-reads configuration from last used files -func Reload(extraFileNames ...string) *Configuration { - for _, fileName := range readFileNames { - _, _ = read(fileName) - } - for _, fileName := range extraFileNames { - _, _ = read(fileName) - } - return Config +// GetTopoInformationRefreshDuration is a getter function. +func GetTopoInformationRefreshDuration() time.Duration { + return topoInformationRefreshDuration.Get() +} + +// GetRecoveryPollDuration is a getter function. +func GetRecoveryPollDuration() time.Duration { + return recoveryPollDuration.Get() +} + +// ERSEnabled reports whether VTOrc is allowed to run ERS or not. +func ERSEnabled() bool { + return ersEnabled.Get() +} + +// SetERSEnabled sets the value for the ersEnabled variable. This should only be used from tests. +func SetERSEnabled(val bool) { + ersEnabled.Set(val) +} + +// ConvertTabletWithErrantGTIDs reports whether VTOrc is allowed to change the tablet type of tablets with errant GTIDs to DRAINED. +func ConvertTabletWithErrantGTIDs() bool { + return convertTabletsWithErrantGTIDs.Get() +} + +// SetConvertTabletWithErrantGTIDs sets the value for the convertTabletWithErrantGTIDs variable. This should only be used from tests. +func SetConvertTabletWithErrantGTIDs(val bool) { + convertTabletsWithErrantGTIDs.Set(val) } // MarkConfigurationLoaded is called once configuration has first been loaded. diff --git a/go/vt/vtorc/config/config_test.go b/go/vt/vtorc/config/config_test.go deleted file mode 100644 index 2009b476f1d..00000000000 --- a/go/vt/vtorc/config/config_test.go +++ /dev/null @@ -1,234 +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 config - -import ( - "testing" - "time" - - "github.com/stretchr/testify/require" -) - -func TestUpdateConfigValuesFromFlags(t *testing.T) { - t.Run("defaults", func(t *testing.T) { - // Restore the changes we make to the Config parameter - defer func() { - Config = newConfiguration() - }() - defaultConfig := newConfiguration() - UpdateConfigValuesFromFlags() - require.Equal(t, defaultConfig, Config) - }) - - t.Run("override auditPurgeDuration", func(t *testing.T) { - oldAuditPurgeDuration := auditPurgeDuration - auditPurgeDuration = 8 * time.Hour * 24 - auditPurgeDuration += time.Second + 4*time.Minute - // Restore the changes we make - defer func() { - Config = newConfiguration() - auditPurgeDuration = oldAuditPurgeDuration - }() - - testConfig := newConfiguration() - // auditPurgeDuration is supposed to be in multiples of days. - // If it is not, then we round down to the nearest number of days. - testConfig.AuditPurgeDays = 8 - UpdateConfigValuesFromFlags() - require.Equal(t, testConfig, Config) - }) - - t.Run("override sqliteDataFile", func(t *testing.T) { - oldSqliteDataFile := sqliteDataFile - sqliteDataFile = "newVal" - // Restore the changes we make - defer func() { - Config = newConfiguration() - sqliteDataFile = oldSqliteDataFile - }() - - testConfig := newConfiguration() - testConfig.SQLite3DataFile = "newVal" - UpdateConfigValuesFromFlags() - require.Equal(t, testConfig, Config) - }) - - t.Run("override instancePollTime", func(t *testing.T) { - oldInstancePollTime := instancePollTime - instancePollTime = 7 * time.Second - // Restore the changes we make - defer func() { - Config = newConfiguration() - instancePollTime = oldInstancePollTime - }() - - testConfig := newConfiguration() - testConfig.InstancePollSeconds = 7 - UpdateConfigValuesFromFlags() - require.Equal(t, testConfig, Config) - }) - - t.Run("override snapshotTopologyInterval", func(t *testing.T) { - oldSnapshotTopologyInterval := snapshotTopologyInterval - snapshotTopologyInterval = 1 * time.Hour - // Restore the changes we make - defer func() { - Config = newConfiguration() - snapshotTopologyInterval = oldSnapshotTopologyInterval - }() - - testConfig := newConfiguration() - testConfig.SnapshotTopologiesIntervalHours = 1 - UpdateConfigValuesFromFlags() - require.Equal(t, testConfig, Config) - }) - - t.Run("override reasonableReplicationLag", func(t *testing.T) { - oldReasonableReplicationLag := reasonableReplicationLag - reasonableReplicationLag = 15 * time.Second - // Restore the changes we make - defer func() { - Config = newConfiguration() - reasonableReplicationLag = oldReasonableReplicationLag - }() - - testConfig := newConfiguration() - testConfig.ReasonableReplicationLagSeconds = 15 - UpdateConfigValuesFromFlags() - require.Equal(t, testConfig, Config) - }) - - t.Run("override auditFileLocation", func(t *testing.T) { - oldAuditFileLocation := auditFileLocation - auditFileLocation = "newFile" - // Restore the changes we make - defer func() { - Config = newConfiguration() - auditFileLocation = oldAuditFileLocation - }() - - testConfig := newConfiguration() - testConfig.AuditLogFile = "newFile" - UpdateConfigValuesFromFlags() - require.Equal(t, testConfig, Config) - }) - - t.Run("override auditToBackend", func(t *testing.T) { - oldAuditToBackend := auditToBackend - auditToBackend = true - // Restore the changes we make - defer func() { - Config = newConfiguration() - auditToBackend = oldAuditToBackend - }() - - testConfig := newConfiguration() - testConfig.AuditToBackendDB = true - UpdateConfigValuesFromFlags() - require.Equal(t, testConfig, Config) - }) - - t.Run("override auditToSyslog", func(t *testing.T) { - oldAuditToSyslog := auditToSyslog - auditToSyslog = true - // Restore the changes we make - defer func() { - Config = newConfiguration() - auditToSyslog = oldAuditToSyslog - }() - - testConfig := newConfiguration() - testConfig.AuditToSyslog = true - UpdateConfigValuesFromFlags() - require.Equal(t, testConfig, Config) - }) - - t.Run("override recoveryPeriodBlockDuration", func(t *testing.T) { - oldRecoveryPeriodBlockDuration := recoveryPeriodBlockDuration - recoveryPeriodBlockDuration = 5 * time.Minute - // Restore the changes we make - defer func() { - Config = newConfiguration() - recoveryPeriodBlockDuration = oldRecoveryPeriodBlockDuration - }() - - testConfig := newConfiguration() - testConfig.RecoveryPeriodBlockSeconds = 300 - UpdateConfigValuesFromFlags() - require.Equal(t, testConfig, Config) - }) - - t.Run("override preventCrossCellFailover", func(t *testing.T) { - oldPreventCrossCellFailover := preventCrossCellFailover - preventCrossCellFailover = true - // Restore the changes we make - defer func() { - Config = newConfiguration() - preventCrossCellFailover = oldPreventCrossCellFailover - }() - - testConfig := newConfiguration() - testConfig.PreventCrossDataCenterPrimaryFailover = true - UpdateConfigValuesFromFlags() - require.Equal(t, testConfig, Config) - }) - - t.Run("override waitReplicasTimeout", func(t *testing.T) { - oldWaitReplicasTimeout := waitReplicasTimeout - waitReplicasTimeout = 3*time.Minute + 4*time.Second - // Restore the changes we make - defer func() { - Config = newConfiguration() - waitReplicasTimeout = oldWaitReplicasTimeout - }() - - testConfig := newConfiguration() - testConfig.WaitReplicasTimeoutSeconds = 184 - UpdateConfigValuesFromFlags() - require.Equal(t, testConfig, Config) - }) - - t.Run("override topoInformationRefreshDuration", func(t *testing.T) { - oldTopoInformationRefreshDuration := topoInformationRefreshDuration - topoInformationRefreshDuration = 1 * time.Second - // Restore the changes we make - defer func() { - Config = newConfiguration() - topoInformationRefreshDuration = oldTopoInformationRefreshDuration - }() - - testConfig := newConfiguration() - testConfig.TopoInformationRefreshSeconds = 1 - UpdateConfigValuesFromFlags() - require.Equal(t, testConfig, Config) - }) - - t.Run("override recoveryPollDuration", func(t *testing.T) { - oldRecoveryPollDuration := recoveryPollDuration - recoveryPollDuration = 15 * time.Second - // Restore the changes we make - defer func() { - Config = newConfiguration() - recoveryPollDuration = oldRecoveryPollDuration - }() - - testConfig := newConfiguration() - testConfig.RecoveryPollSeconds = 15 - UpdateConfigValuesFromFlags() - require.Equal(t, testConfig, Config) - }) -} diff --git a/go/vt/vtorc/db/db.go b/go/vt/vtorc/db/db.go index 64143477645..870a3d15949 100644 --- a/go/vt/vtorc/db/db.go +++ b/go/vt/vtorc/db/db.go @@ -44,10 +44,12 @@ func (m *vtorcDB) QueryVTOrc(query string, argsArray []any, onRow func(sqlutils. // OpenTopology returns the DB instance for the vtorc backed database func OpenVTOrc() (db *sql.DB, err error) { var fromCache bool - db, fromCache, err = sqlutils.GetSQLiteDB(config.Config.SQLite3DataFile) + db, fromCache, err = sqlutils.GetSQLiteDB(config.GetSQLiteDataFile()) if err == nil && !fromCache { - log.Infof("Connected to vtorc backend: sqlite on %v", config.Config.SQLite3DataFile) - _ = initVTOrcDB(db) + log.Infof("Connected to vtorc backend: sqlite on %v", config.GetSQLiteDataFile()) + if err := initVTOrcDB(db); err != nil { + log.Fatalf("Cannot initiate vtorc: %+v", err) + } } if db != nil { db.SetMaxOpenConns(1) @@ -58,13 +60,13 @@ func OpenVTOrc() (db *sql.DB, err error) { // registerVTOrcDeployment updates the vtorc_db_deployments table upon successful deployment func registerVTOrcDeployment(db *sql.DB) error { - query := ` - replace into vtorc_db_deployments ( - deployed_version, deployed_timestamp - ) values ( - ?, datetime('now') - ) - ` + query := `REPLACE INTO vtorc_db_deployments ( + deployed_version, + deployed_timestamp + ) VALUES ( + ?, + DATETIME('now') + )` if _, err := execInternal(db, query, ""); err != nil { log.Fatalf("Unable to write to vtorc_db_deployments: %+v", err) } @@ -76,27 +78,24 @@ func registerVTOrcDeployment(db *sql.DB) error { func deployStatements(db *sql.DB, queries []string) error { tx, err := db.Begin() if err != nil { - log.Fatal(err.Error()) return err } for _, query := range queries { if _, err := tx.Exec(query); err != nil { - log.Fatalf("Cannot initiate vtorc: %+v; query=%+v", err, query) return err } } - if err := tx.Commit(); err != nil { - log.Fatal(err.Error()) - } - return nil + return tx.Commit() } // ClearVTOrcDatabase is used to clear the VTOrc database. This function is meant to be used by tests to clear the // database to get a clean slate without starting a new one. func ClearVTOrcDatabase() { - db, _, _ := sqlutils.GetSQLiteDB(config.Config.SQLite3DataFile) + db, _, _ := sqlutils.GetSQLiteDB(config.GetSQLiteDataFile()) if db != nil { - _ = initVTOrcDB(db) + if err := initVTOrcDB(db); err != nil { + log.Fatalf("Cannot re-initiate vtorc: %+v", err) + } } } @@ -105,20 +104,24 @@ func ClearVTOrcDatabase() { func initVTOrcDB(db *sql.DB) error { log.Info("Initializing vtorc") log.Info("Migrating database schema") - _ = deployStatements(db, vtorcBackend) - _ = registerVTOrcDeployment(db) - - _, _ = ExecVTOrc(`PRAGMA journal_mode = WAL`) - _, _ = ExecVTOrc(`PRAGMA synchronous = NORMAL`) - + if err := deployStatements(db, vtorcBackend); err != nil { + return err + } + if err := registerVTOrcDeployment(db); err != nil { + return err + } + if _, err := ExecVTOrc(`PRAGMA journal_mode = WAL`); err != nil { + return err + } + if _, err := ExecVTOrc(`PRAGMA synchronous = NORMAL`); err != nil { + return err + } return nil } // execInternal func execInternal(db *sql.DB, query string, args ...any) (sql.Result, error) { - var err error - res, err := sqlutils.ExecNoPrepare(db, query, args...) - return res, err + return sqlutils.ExecNoPrepare(db, query, args...) } // ExecVTOrc will execute given query on the vtorc backend database. diff --git a/go/vt/vtorc/discovery/queue.go b/go/vt/vtorc/discovery/queue.go index 95751c6ae25..4b18303959b 100644 --- a/go/vt/vtorc/discovery/queue.go +++ b/go/vt/vtorc/discovery/queue.go @@ -153,7 +153,7 @@ func (q *Queue) Consume() string { // alarm if have been waiting for too long timeOnQueue := time.Since(q.queuedKeys[key]) - if timeOnQueue > time.Duration(config.Config.InstancePollSeconds)*time.Second { + if timeOnQueue > config.GetInstancePollTime() { log.Warningf("key %v spent %.4fs waiting on a discoveryQueue", key, timeOnQueue.Seconds()) } diff --git a/go/vt/vtorc/inst/analysis.go b/go/vt/vtorc/inst/analysis.go index 66d6c6dd9ce..3e9e81c5c9f 100644 --- a/go/vt/vtorc/inst/analysis.go +++ b/go/vt/vtorc/inst/analysis.go @@ -144,5 +144,5 @@ func (replicationAnalysis *ReplicationAnalysis) MarshalJSON() ([]byte, error) { // ValidSecondsFromSeenToLastAttemptedCheck returns the maximum allowed elapsed time // between last_attempted_check to last_checked before we consider the instance as invalid. func ValidSecondsFromSeenToLastAttemptedCheck() uint { - return config.Config.InstancePollSeconds + 1 + return config.GetInstancePollSeconds() } diff --git a/go/vt/vtorc/inst/analysis_dao.go b/go/vt/vtorc/inst/analysis_dao.go index 25d93a6864b..07830bf7dda 100644 --- a/go/vt/vtorc/inst/analysis_dao.go +++ b/go/vt/vtorc/inst/analysis_dao.go @@ -47,7 +47,7 @@ func init() { func initializeAnalysisDaoPostConfiguration() { config.WaitForConfigurationToBeLoaded() - recentInstantAnalysis = cache.New(time.Duration(config.Config.RecoveryPollSeconds*2)*time.Second, time.Second) + recentInstantAnalysis = cache.New(config.GetRecoveryPollDuration()*2, time.Second) } type clusterAnalysis struct { @@ -68,9 +68,8 @@ func GetReplicationAnalysis(keyspace string, shard string, hints *ReplicationAna } // TODO(sougou); deprecate ReduceReplicationAnalysisCount - args := sqlutils.Args(config.Config.ReasonableReplicationLagSeconds, ValidSecondsFromSeenToLastAttemptedCheck(), config.Config.ReasonableReplicationLagSeconds, keyspace, shard) - query := ` - SELECT + args := sqlutils.Args(config.GetReasonableReplicationLagSeconds(), ValidSecondsFromSeenToLastAttemptedCheck(), config.GetReasonableReplicationLagSeconds(), keyspace, shard) + query := `SELECT vitess_tablet.info AS tablet_info, vitess_tablet.tablet_type, vitess_tablet.primary_timestamp, @@ -91,13 +90,13 @@ func GetReplicationAnalysis(keyspace string, shard string, hints *ReplicationAna IFNULL( primary_instance.binary_log_file = database_instance_stale_binlog_coordinates.binary_log_file AND primary_instance.binary_log_pos = database_instance_stale_binlog_coordinates.binary_log_pos - AND database_instance_stale_binlog_coordinates.first_seen < datetime('now', printf('-%d second', ?)), + AND database_instance_stale_binlog_coordinates.first_seen < DATETIME('now', PRINTF('-%d SECOND', ?)), 0 ) ) AS is_stale_binlog_coordinates, MIN( primary_instance.last_checked <= primary_instance.last_seen - and primary_instance.last_attempted_check <= datetime(primary_instance.last_seen, printf('+%d second', ?)) + and primary_instance.last_attempted_check <= DATETIME(primary_instance.last_seen, PRINTF('+%d SECOND', ?)) ) = 1 AS is_last_check_valid, /* To be considered a primary, traditional async replication must not be present/valid AND the host should either */ /* not be a replication group member OR be the primary of the replication group */ @@ -655,13 +654,13 @@ func auditInstanceAnalysisInChangelog(tabletAlias string, analysisCode AnalysisC // Find if the lastAnalysisHasChanged or not while updating the row if it has. lastAnalysisChanged := false { - sqlResult, err := db.ExecVTOrc(` - update database_instance_last_analysis set + sqlResult, err := db.ExecVTOrc(`UPDATE database_instance_last_analysis + SET analysis = ?, - analysis_timestamp = datetime('now') - where + analysis_timestamp = DATETIME('now') + WHERE alias = ? - and analysis != ? + AND analysis != ? `, string(analysisCode), tabletAlias, string(analysisCode), ) @@ -682,13 +681,16 @@ func auditInstanceAnalysisInChangelog(tabletAlias string, analysisCode AnalysisC firstInsertion := false if !lastAnalysisChanged { // The insert only returns more than 1 row changed if this is the first insertion. - sqlResult, err := db.ExecVTOrc(` - insert or ignore into database_instance_last_analysis ( - alias, analysis_timestamp, analysis - ) values ( - ?, datetime('now'), ? - ) - `, + sqlResult, err := db.ExecVTOrc(`INSERT OR IGNORE + INTO database_instance_last_analysis ( + alias, + analysis_timestamp, + analysis + ) VALUES ( + ?, + DATETIME('now'), + ? + )`, tabletAlias, string(analysisCode), ) if err != nil { @@ -708,13 +710,16 @@ func auditInstanceAnalysisInChangelog(tabletAlias string, analysisCode AnalysisC return nil } - _, err := db.ExecVTOrc(` - insert into database_instance_analysis_changelog ( - alias, analysis_timestamp, analysis - ) values ( - ?, datetime('now'), ? - ) - `, + _, err := db.ExecVTOrc(`INSERT + INTO database_instance_analysis_changelog ( + alias, + analysis_timestamp, + analysis + ) VALUES ( + ?, + DATETIME('now'), + ? + )`, tabletAlias, string(analysisCode), ) if err == nil { @@ -727,12 +732,11 @@ func auditInstanceAnalysisInChangelog(tabletAlias string, analysisCode AnalysisC // ExpireInstanceAnalysisChangelog removes old-enough analysis entries from the changelog func ExpireInstanceAnalysisChangelog() error { - _, err := db.ExecVTOrc(` - delete - from database_instance_analysis_changelog - where - analysis_timestamp < datetime('now', printf('-%d hour', ?)) - `, + _, err := db.ExecVTOrc(`DELETE + FROM database_instance_analysis_changelog + WHERE + analysis_timestamp < DATETIME('now', PRINTF('-%d HOUR', ?)) + `, config.UnseenInstanceForgetHours, ) if err != nil { diff --git a/go/vt/vtorc/inst/audit_dao.go b/go/vt/vtorc/inst/audit_dao.go index 642fb187509..7ae60fba927 100644 --- a/go/vt/vtorc/inst/audit_dao.go +++ b/go/vt/vtorc/inst/audit_dao.go @@ -38,10 +38,10 @@ func AuditOperation(auditType string, tabletAlias string, message string) error } auditWrittenToFile := false - if config.Config.AuditLogFile != "" { + if config.GetAuditFileLocation() != "" { auditWrittenToFile = true go func() { - f, err := os.OpenFile(config.Config.AuditLogFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0640) + f, err := os.OpenFile(config.GetAuditFileLocation(), os.O_RDWR|os.O_CREATE|os.O_APPEND, 0640) if err != nil { log.Error(err) return @@ -54,15 +54,23 @@ func AuditOperation(auditType string, tabletAlias string, message string) error } }() } - if config.Config.AuditToBackendDB { - _, err := db.ExecVTOrc(` - insert - into audit ( - audit_timestamp, audit_type, alias, keyspace, shard, message - ) VALUES ( - datetime('now'), ?, ?, ?, ?, ? - ) - `, + if config.GetAuditToBackend() { + _, err := db.ExecVTOrc(`INSERT + INTO audit ( + audit_timestamp, + audit_type, + alias, + keyspace, + shard, + message + ) VALUES ( + DATETIME('now'), + ?, + ?, + ?, + ?, + ? + )`, auditType, tabletAlias, keyspace, diff --git a/go/vt/vtorc/inst/audit_dao_test.go b/go/vt/vtorc/inst/audit_dao_test.go index 1d50de4c146..d22e9177dc3 100644 --- a/go/vt/vtorc/inst/audit_dao_test.go +++ b/go/vt/vtorc/inst/audit_dao_test.go @@ -35,13 +35,13 @@ import ( // This test also verifies that we are able to read the recent audits that are written to the databaes. func TestAuditOperation(t *testing.T) { // Restore original configurations - originalAuditSysLog := config.Config.AuditToSyslog - originalAuditLogFile := config.Config.AuditLogFile - originalAuditBackend := config.Config.AuditToBackendDB + originalAuditSysLog := config.GetAuditToSyslog() + originalAuditLogFile := config.GetAuditFileLocation() + originalAuditBackend := config.GetAuditToBackend() defer func() { - config.Config.AuditToSyslog = originalAuditSysLog - config.Config.AuditLogFile = originalAuditLogFile - config.Config.AuditToBackendDB = originalAuditBackend + config.SetAuditToSyslog(originalAuditSysLog) + config.SetAuditFileLocation(originalAuditLogFile) + config.SetAuditToBackend(originalAuditBackend) }() orcDb, err := db.OpenVTOrc() @@ -78,9 +78,9 @@ func TestAuditOperation(t *testing.T) { message := "test-message" t.Run("audit to backend", func(t *testing.T) { - config.Config.AuditLogFile = "" - config.Config.AuditToSyslog = false - config.Config.AuditToBackendDB = true + config.SetAuditFileLocation("") + config.SetAuditToSyslog(false) + config.SetAuditToBackend(true) // Auditing should succeed as expected err = AuditOperation(auditType, tab100Alias, message) @@ -106,13 +106,13 @@ func TestAuditOperation(t *testing.T) { }) t.Run("audit to File", func(t *testing.T) { - config.Config.AuditToBackendDB = false - config.Config.AuditToSyslog = false + config.SetAuditToBackend(false) + config.SetAuditToSyslog(false) file, err := os.CreateTemp("", "test-auditing-*") require.NoError(t, err) defer os.Remove(file.Name()) - config.Config.AuditLogFile = file.Name() + config.SetAuditFileLocation(file.Name()) err = AuditOperation(auditType, tab100Alias, message) require.NoError(t, err) diff --git a/go/vt/vtorc/inst/instance_dao.go b/go/vt/vtorc/inst/instance_dao.go index bd4438dd05f..d1421dbc91d 100644 --- a/go/vt/vtorc/inst/instance_dao.go +++ b/go/vt/vtorc/inst/instance_dao.go @@ -80,7 +80,7 @@ func init() { func initializeInstanceDao() { config.WaitForConfigurationToBeLoaded() - forgetAliases = cache.New(time.Duration(config.Config.InstancePollSeconds*3)*time.Second, time.Second) + forgetAliases = cache.New(config.GetInstancePollTime()*3, time.Second) cacheInitializationCompleted.Store(true) } @@ -114,10 +114,15 @@ func ExecDBWriteFunc(f func() error) error { func ExpireTableData(tableName string, timestampColumn string) error { writeFunc := func() error { - _, err := db.ExecVTOrc( - fmt.Sprintf("delete from %s where %s < datetime('now', printf('-%%d DAY', ?))", tableName, timestampColumn), - config.Config.AuditPurgeDays, + query := fmt.Sprintf(`DELETE + FROM %s + WHERE + %s < DATETIME('now', PRINTF('-%%d DAY', ?)) + `, + tableName, + timestampColumn, ) + _, err := db.ExecVTOrc(query, config.GetAuditPurgeDays()) return err } return ExecDBWriteFunc(writeFunc) @@ -357,35 +362,7 @@ Cleanup: // Add replication group ancestry UUID as well. Otherwise, VTOrc thinks there are errant GTIDs in group // members and its replicas, even though they are not. instance.AncestryUUID = strings.Trim(instance.AncestryUUID, ",") - if instance.ExecutedGtidSet != "" && instance.primaryExecutedGtidSet != "" { - // Compare primary & replica GTID sets, but ignore the sets that present the primary's UUID. - // This is because vtorc may pool primary and replica at an inconvenient timing, - // such that the replica may _seems_ to have more entries than the primary, when in fact - // it's just that the primary's probing is stale. - redactedExecutedGtidSet, _ := NewOracleGtidSet(instance.ExecutedGtidSet) - for _, uuid := range strings.Split(instance.AncestryUUID, ",") { - if uuid != instance.ServerUUID { - redactedExecutedGtidSet.RemoveUUID(uuid) - } - if instance.IsCoPrimary && uuid == instance.ServerUUID { - // If this is a co-primary, then this server is likely to show its own generated GTIDs as errant, - // because its co-primary has not applied them yet - redactedExecutedGtidSet.RemoveUUID(uuid) - } - } - // Avoid querying the database if there's no point: - if !redactedExecutedGtidSet.IsEmpty() { - redactedPrimaryExecutedGtidSet, _ := NewOracleGtidSet(instance.primaryExecutedGtidSet) - redactedPrimaryExecutedGtidSet.RemoveUUID(instance.SourceUUID) - - instance.GtidErrant, err = replication.Subtract(redactedExecutedGtidSet.String(), redactedPrimaryExecutedGtidSet.String()) - if err == nil { - var gtidCount int64 - gtidCount, err = replication.GTIDCount(instance.GtidErrant) - currentErrantGTIDCount.Set(tabletAlias, gtidCount) - } - } - } + err = detectErrantGTIDs(instance, tablet) } latency.Stop("instance") @@ -412,6 +389,63 @@ Cleanup: return nil, err } +// detectErrantGTIDs detects the errant GTIDs on an instance. +func detectErrantGTIDs(instance *Instance, tablet *topodatapb.Tablet) (err error) { + // If the tablet is not replicating from anyone, then it could be the previous primary. + // We should check for errant GTIDs by finding the difference with the shard's current primary. + if instance.primaryExecutedGtidSet == "" && instance.SourceHost == "" { + var primaryInstance *Instance + primaryAlias, _, _ := ReadShardPrimaryInformation(tablet.Keyspace, tablet.Shard) + if primaryAlias != "" { + // Check if the current tablet is the primary. + // If it is, then we don't need to run errant gtid detection on it. + if primaryAlias == instance.InstanceAlias { + return nil + } + primaryInstance, _, _ = ReadInstance(primaryAlias) + } + // Only run errant GTID detection, if we are sure that the data read of the current primary + // is up-to-date enough to reflect that it has been promoted. This is needed to prevent + // flagging incorrect errant GTIDs. If we were to use old data, we could have some GTIDs + // accepted by the old primary (this tablet) that don't show in the new primary's set. + if primaryInstance != nil { + if primaryInstance.SourceHost == "" { + instance.primaryExecutedGtidSet = primaryInstance.ExecutedGtidSet + } + } + } + if instance.ExecutedGtidSet != "" && instance.primaryExecutedGtidSet != "" { + // Compare primary & replica GTID sets, but ignore the sets that present the primary's UUID. + // This is because vtorc may pool primary and replica at an inconvenient timing, + // such that the replica may _seems_ to have more entries than the primary, when in fact + // it's just that the primary's probing is stale. + redactedExecutedGtidSet, _ := NewOracleGtidSet(instance.ExecutedGtidSet) + for _, uuid := range strings.Split(instance.AncestryUUID, ",") { + if uuid != instance.ServerUUID { + redactedExecutedGtidSet.RemoveUUID(uuid) + } + if instance.IsCoPrimary && uuid == instance.ServerUUID { + // If this is a co-primary, then this server is likely to show its own generated GTIDs as errant, + // because its co-primary has not applied them yet + redactedExecutedGtidSet.RemoveUUID(uuid) + } + } + // Avoid querying the database if there's no point: + if !redactedExecutedGtidSet.IsEmpty() { + redactedPrimaryExecutedGtidSet, _ := NewOracleGtidSet(instance.primaryExecutedGtidSet) + redactedPrimaryExecutedGtidSet.RemoveUUID(instance.SourceUUID) + + instance.GtidErrant, err = replication.Subtract(redactedExecutedGtidSet.String(), redactedPrimaryExecutedGtidSet.String()) + if err == nil { + var gtidCount int64 + gtidCount, err = replication.GTIDCount(instance.GtidErrant) + currentErrantGTIDCount.Set(instance.InstanceAlias, gtidCount) + } + } + } + return err +} + // getKeyspaceShardName returns a single string having both the keyspace and shard func getKeyspaceShardName(keyspace, shard string) string { return fmt.Sprintf("%v:%v", keyspace, shard) @@ -439,16 +473,16 @@ func ReadInstanceClusterAttributes(instance *Instance) (err error) { var primaryExecutedGtidSet string primaryDataFound := false - query := ` - select - replication_depth, - source_host, - source_port, - ancestry_uuid, - executed_gtid_set - from database_instance - where hostname=? and port=? - ` + query := `SELECT + replication_depth, + source_host, + source_port, + ancestry_uuid, + executed_gtid_set + FROM database_instance + WHERE + hostname = ? + AND port = ?` primaryHostname := instance.SourceHost primaryPort := instance.SourcePort args := sqlutils.Args(primaryHostname, primaryPort) @@ -544,8 +578,8 @@ func readInstanceRow(m sqlutils.RowMap) *Instance { instance.ReplicationDepth = m.GetUint("replication_depth") instance.IsCoPrimary = m.GetBool("is_co_primary") instance.HasReplicationCredentials = m.GetBool("has_replication_credentials") - instance.IsUpToDate = (m.GetUint("seconds_since_last_checked") <= config.Config.InstancePollSeconds) - instance.IsRecentlyChecked = (m.GetUint("seconds_since_last_checked") <= config.Config.InstancePollSeconds*5) + instance.IsUpToDate = m.GetUint("seconds_since_last_checked") <= config.GetInstancePollSeconds() + instance.IsRecentlyChecked = m.GetUint("seconds_since_last_checked") <= config.GetInstancePollSeconds()*5 instance.LastSeenTimestamp = m.GetString("last_seen") instance.IsLastCheckValid = m.GetBool("is_last_check_valid") instance.SecondsSinceLastSeen = m.GetNullInt64("seconds_since_last_seen") @@ -562,7 +596,7 @@ func readInstanceRow(m sqlutils.RowMap) *Instance { instance.Problems = append(instance.Problems, "not_recently_checked") } else if instance.ReplicationThreadsExist() && !instance.ReplicaRunning() { instance.Problems = append(instance.Problems, "not_replicating") - } else if instance.ReplicationLagSeconds.Valid && util.AbsInt64(instance.ReplicationLagSeconds.Int64-int64(instance.SQLDelay)) > int64(config.Config.ReasonableReplicationLagSeconds) { + } else if instance.ReplicationLagSeconds.Valid && util.AbsInt64(instance.ReplicationLagSeconds.Int64-int64(instance.SQLDelay)) > int64(config.GetReasonableReplicationLagSeconds()) { instance.Problems = append(instance.Problems, "replication_lag") } if instance.GtidErrant != "" { @@ -580,20 +614,22 @@ func readInstancesByCondition(condition string, args []any, sort string) ([](*In if sort == "" { sort = `alias` } - query := fmt.Sprintf(` - select - *, - strftime('%%s', 'now') - strftime('%%s', last_checked) as seconds_since_last_checked, - ifnull(last_checked <= last_seen, 0) as is_last_check_valid, - strftime('%%s', 'now') - strftime('%%s', last_seen) as seconds_since_last_seen - from - vitess_tablet - left join database_instance using (alias, hostname, port) - where - %s - order by - %s - `, condition, sort) + query := fmt.Sprintf(`SELECT + *, + STRFTIME('%%s', 'now') - STRFTIME('%%s', last_checked) AS seconds_since_last_checked, + IFNULL(last_checked <= last_seen, 0) AS is_last_check_valid, + STRFTIME('%%s', 'now') - STRFTIME('%%s', last_seen) AS seconds_since_last_seen + FROM + vitess_tablet + LEFT JOIN database_instance USING (alias, hostname, port) + WHERE + %s + ORDER BY + %s + `, + condition, + sort, + ) err := db.QueryVTOrc(query, args, func(m sqlutils.RowMap) error { instance := readInstanceRow(m) @@ -614,9 +650,7 @@ func readInstancesByCondition(condition string, args []any, sort string) ([](*In // ReadInstance reads an instance from the vtorc backend database func ReadInstance(tabletAlias string) (*Instance, bool, error) { - condition := ` - alias = ? - ` + condition := `alias = ?` instances, err := readInstancesByCondition(condition, sqlutils.Args(tabletAlias), "") // We know there will be at most one (alias is the PK). // And we expect to find one. @@ -633,30 +667,28 @@ func ReadInstance(tabletAlias string) (*Instance, bool, error) { // ReadProblemInstances reads all instances with problems func ReadProblemInstances(keyspace string, shard string) ([](*Instance), error) { condition := ` - keyspace LIKE (CASE WHEN ? = '' THEN '%' ELSE ? END) - and shard LIKE (CASE WHEN ? = '' THEN '%' ELSE ? END) - and ( - (last_seen < last_checked) - or (strftime('%%s', 'now') - strftime('%%s', last_checked) > ?) - or (replication_sql_thread_state not in (-1 ,1)) - or (replication_io_thread_state not in (-1 ,1)) - or (abs(cast(replication_lag_seconds as integer) - cast(sql_delay as integer)) > ?) - or (abs(cast(replica_lag_seconds as integer) - cast(sql_delay as integer)) > ?) - or (gtid_errant != '') - ) - ` - - args := sqlutils.Args(keyspace, keyspace, shard, shard, config.Config.InstancePollSeconds*5, config.Config.ReasonableReplicationLagSeconds, config.Config.ReasonableReplicationLagSeconds) + keyspace LIKE (CASE WHEN ? = '' THEN '%' ELSE ? END) + AND shard LIKE (CASE WHEN ? = '' THEN '%' ELSE ? END) + AND ( + (last_seen < last_checked) + OR (STRFTIME('%%s', 'now') - STRFTIME('%%s', last_checked) > ?) + OR (replication_sql_thread_state NOT IN (-1 ,1)) + OR (replication_io_thread_state NOT IN (-1 ,1)) + OR (ABS(CAST(replication_lag_seconds AS integer) - CAST(sql_delay AS integer)) > ?) + OR (ABS(CAST(replica_lag_seconds AS integer) - CAST(sql_delay AS integer)) > ?) + OR (gtid_errant != '') + )` + + args := sqlutils.Args(keyspace, keyspace, shard, shard, config.GetInstancePollSeconds()*5, config.GetReasonableReplicationLagSeconds(), config.GetReasonableReplicationLagSeconds()) return readInstancesByCondition(condition, args, "") } // ReadInstancesWithErrantGTIds reads all instances with errant GTIDs func ReadInstancesWithErrantGTIds(keyspace string, shard string) ([]*Instance, error) { condition := ` - keyspace LIKE (CASE WHEN ? = '' THEN '%' ELSE ? END) - and shard LIKE (CASE WHEN ? = '' THEN '%' ELSE ? END) - and gtid_errant != '' - ` + keyspace LIKE (CASE WHEN ? = '' THEN '%' ELSE ? END) + AND shard LIKE (CASE WHEN ? = '' THEN '%' ELSE ? END) + AND gtid_errant != ''` args := sqlutils.Args(keyspace, keyspace, shard, shard) return readInstancesByCondition(condition, args, "") @@ -664,15 +696,14 @@ func ReadInstancesWithErrantGTIds(keyspace string, shard string) ([]*Instance, e // GetKeyspaceShardName gets the keyspace shard name for the given instance key func GetKeyspaceShardName(tabletAlias string) (keyspace string, shard string, err error) { - query := ` - select - keyspace, - shard - from - vitess_tablet - where - alias = ? - ` + query := `SELECT + keyspace, + shard + FROM + vitess_tablet + WHERE + alias = ? + ` err = db.QueryVTOrc(query, sqlutils.Args(tabletAlias), func(m sqlutils.RowMap) error { keyspace = m.GetString("keyspace") shard = m.GetString("shard") @@ -695,28 +726,27 @@ func GetKeyspaceShardName(tabletAlias string) (keyspace string, shard string, er // the instance. func ReadOutdatedInstanceKeys() ([]string, error) { var res []string - query := ` - SELECT - alias - FROM - database_instance - WHERE - CASE - WHEN last_attempted_check <= last_checked - THEN last_checked < datetime('now', printf('-%d second', ?)) - ELSE last_checked < datetime('now', printf('-%d second', ?)) - END - UNION - SELECT - vitess_tablet.alias - FROM - vitess_tablet LEFT JOIN database_instance ON ( - vitess_tablet.alias = database_instance.alias - ) - WHERE - database_instance.alias IS NULL - ` - args := sqlutils.Args(config.Config.InstancePollSeconds, 2*config.Config.InstancePollSeconds) + query := `SELECT + alias + FROM + database_instance + WHERE + CASE + WHEN last_attempted_check <= last_checked + THEN last_checked < DATETIME('now', PRINTF('-%d SECOND', ?)) + ELSE last_checked < DATETIME('now', PRINTF('-%d SECOND', ?)) + END + UNION + SELECT + vitess_tablet.alias + FROM + vitess_tablet LEFT JOIN database_instance ON ( + vitess_tablet.alias = database_instance.alias + ) + WHERE + database_instance.alias IS NULL + ` + args := sqlutils.Args(config.GetInstancePollSeconds(), 2*config.GetInstancePollSeconds()) err := db.QueryVTOrc(query, args, func(m sqlutils.RowMap) error { tabletAlias := m.GetString("alias") @@ -758,12 +788,17 @@ func mkInsert(table string, columns []string, values []string, nrRows int, inser } col := strings.Join(columns, ", ") - q.WriteString(fmt.Sprintf(`%s %s - (%s) - VALUES - %s - `, - insertStr, table, col, val.String())) + query := fmt.Sprintf(`%s %s + (%s) + VALUES + %s + `, + insertStr, + table, + col, + val.String(), + ) + q.WriteString(query) return q.String(), nil } @@ -849,13 +884,13 @@ func mkInsertForInstances(instances []*Instance, instanceWasActuallyFound bool, for i := range columns { values[i] = "?" } - values[3] = "datetime('now')" // last_checked - values[4] = "datetime('now')" // last_attempted_check + values[3] = "DATETIME('now')" // last_checked + values[4] = "DATETIME('now')" // last_attempted_check values[5] = "1" // last_check_partial_success if updateLastSeen { columns = append(columns, "last_seen") - values = append(values, "datetime('now')") + values = append(values, "DATETIME('now')") } var args []any @@ -971,14 +1006,13 @@ func WriteInstance(instance *Instance, instanceWasActuallyFound bool, lastError // for a given instance func UpdateInstanceLastChecked(tabletAlias string, partialSuccess bool) error { writeFunc := func() error { - _, err := db.ExecVTOrc(` - update - database_instance - set - last_checked = datetime('now'), - last_check_partial_success = ? - where - alias = ?`, + _, err := db.ExecVTOrc(`UPDATE database_instance + SET + last_checked = DATETIME('now'), + last_check_partial_success = ? + WHERE + alias = ? + `, partialSuccess, tabletAlias, ) @@ -1000,13 +1034,12 @@ func UpdateInstanceLastChecked(tabletAlias string, partialSuccess bool) error { // we have a "hanging" issue. func UpdateInstanceLastAttemptedCheck(tabletAlias string) error { writeFunc := func() error { - _, err := db.ExecVTOrc(` - update - database_instance - set - last_attempted_check = datetime('now') - where - alias = ?`, + _, err := db.ExecVTOrc(`UPDATE database_instance + SET + last_attempted_check = DATETIME('now') + WHERE + alias = ? + `, tabletAlias, ) if err != nil { @@ -1037,11 +1070,11 @@ func ForgetInstance(tabletAlias string) error { currentErrantGTIDCount.Reset(tabletAlias) // Delete from the 'vitess_tablet' table. - _, err := db.ExecVTOrc(` - delete - from vitess_tablet - where - alias = ?`, + _, err := db.ExecVTOrc(`DELETE + FROM vitess_tablet + WHERE + alias = ? + `, tabletAlias, ) if err != nil { @@ -1050,11 +1083,11 @@ func ForgetInstance(tabletAlias string) error { } // Also delete from the 'database_instance' table. - sqlResult, err := db.ExecVTOrc(` - delete - from database_instance - where - alias = ?`, + sqlResult, err := db.ExecVTOrc(`DELETE + FROM database_instance + WHERE + alias = ? + `, tabletAlias, ) if err != nil { @@ -1078,11 +1111,11 @@ func ForgetInstance(tabletAlias string) error { // ForgetLongUnseenInstances will remove entries of all instances that have long since been last seen. func ForgetLongUnseenInstances() error { - sqlResult, err := db.ExecVTOrc(` - delete - from database_instance - where - last_seen < datetime('now', printf('-%d hour', ?))`, + sqlResult, err := db.ExecVTOrc(`DELETE + FROM database_instance + WHERE + last_seen < DATETIME('now', PRINTF('-%d HOUR', ?)) + `, config.UnseenInstanceForgetHours, ) if err != nil { @@ -1103,18 +1136,26 @@ func ForgetLongUnseenInstances() error { // SnapshotTopologies records topology graph for all existing topologies func SnapshotTopologies() error { writeFunc := func() error { - _, err := db.ExecVTOrc(` - insert or ignore into - database_instance_topology_history (snapshot_unix_timestamp, - alias, hostname, port, source_host, source_port, keyspace, shard, version) - select - strftime('%s', 'now'), - vitess_tablet.alias, vitess_tablet.hostname, vitess_tablet.port, - database_instance.source_host, database_instance.source_port, + _, err := db.ExecVTOrc(`INSERT OR IGNORE + INTO database_instance_topology_history ( + snapshot_unix_timestamp, + alias, + hostname, + port, + source_host, + source_port, + keyspace, + shard, + version + ) + SELECT + STRFTIME('%s', 'now'), + vitess_tablet.alias, vitess_tablet.hostname, vitess_tablet.port, + database_instance.source_host, database_instance.source_port, vitess_tablet.keyspace, vitess_tablet.shard, database_instance.version - from - vitess_tablet left join database_instance using (alias, hostname, port) - `, + FROM + vitess_tablet LEFT JOIN database_instance USING (alias, hostname, port) + `, ) if err != nil { log.Error(err) @@ -1127,15 +1168,17 @@ func SnapshotTopologies() error { } func ExpireStaleInstanceBinlogCoordinates() error { - expireSeconds := config.Config.ReasonableReplicationLagSeconds * 2 + expireSeconds := config.GetReasonableReplicationLagSeconds() * 2 if expireSeconds < config.StaleInstanceCoordinatesExpireSeconds { expireSeconds = config.StaleInstanceCoordinatesExpireSeconds } writeFunc := func() error { - _, err := db.ExecVTOrc(` - delete from database_instance_stale_binlog_coordinates - where first_seen < datetime('now', printf('-%d second', ?)) - `, expireSeconds, + _, err := db.ExecVTOrc(`DELETE + FROM database_instance_stale_binlog_coordinates + WHERE + first_seen < DATETIME('now', PRINTF('-%d SECOND', ?)) + `, + expireSeconds, ) if err != nil { log.Error(err) @@ -1157,7 +1200,7 @@ func GetDatabaseState() (string, error) { ts := tableState{ TableName: tableName, } - err := db.QueryVTOrc("select * from "+tableName, nil, func(rowMap sqlutils.RowMap) error { + err := db.QueryVTOrc("SELECT * FROM "+tableName, nil, func(rowMap sqlutils.RowMap) error { ts.Rows = append(ts.Rows, rowMap) return nil }) diff --git a/go/vt/vtorc/inst/instance_dao_test.go b/go/vt/vtorc/inst/instance_dao_test.go index 2416c1abb90..cc3217442ed 100644 --- a/go/vt/vtorc/inst/instance_dao_test.go +++ b/go/vt/vtorc/inst/instance_dao_test.go @@ -14,6 +14,7 @@ import ( "vitess.io/vitess/go/vt/external/golib/sqlutils" "vitess.io/vitess/go/vt/log" topodatapb "vitess.io/vitess/go/vt/proto/topodata" + "vitess.io/vitess/go/vt/topo" "vitess.io/vitess/go/vt/topo/topoproto" "vitess.io/vitess/go/vt/vtorc/config" "vitess.io/vitess/go/vt/vtorc/db" @@ -65,7 +66,7 @@ func TestMkInsertSingle(t *testing.T) { replica_sql_running, replica_io_running, replication_sql_thread_state, replication_io_thread_state, has_replication_filters, supports_oracle_gtid, oracle_gtid, source_uuid, ancestry_uuid, executed_gtid_set, gtid_mode, gtid_purged, gtid_errant, mariadb_gtid, pseudo_gtid, source_log_file, read_source_log_pos, relay_source_log_file, exec_source_log_pos, relay_log_file, relay_log_pos, last_sql_error, last_io_error, replication_lag_seconds, replica_lag_seconds, sql_delay, data_center, region, physical_environment, replication_depth, is_co_primary, has_replication_credentials, allow_tls, semi_sync_enforced, semi_sync_primary_enabled, semi_sync_primary_timeout, semi_sync_primary_wait_for_replica_count, semi_sync_replica_enabled, semi_sync_primary_status, semi_sync_primary_clients, semi_sync_replica_status, last_discovery_latency, last_seen) VALUES - (?, ?, ?, datetime('now'), datetime('now'), 1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, datetime('now')) + (?, ?, ?, DATETIME('now'), DATETIME('now'), 1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, DATETIME('now')) ` a1 := `zone1-i710, i710, 3306, 710, , 5.6.7, 5.6, MySQL, false, false, STATEMENT, FULL, false, false, , 0, , 0, 0, 0, @@ -88,9 +89,9 @@ func TestMkInsertThree(t *testing.T) { replica_sql_running, replica_io_running, replication_sql_thread_state, replication_io_thread_state, has_replication_filters, supports_oracle_gtid, oracle_gtid, source_uuid, ancestry_uuid, executed_gtid_set, gtid_mode, gtid_purged, gtid_errant, mariadb_gtid, pseudo_gtid, source_log_file, read_source_log_pos, relay_source_log_file, exec_source_log_pos, relay_log_file, relay_log_pos, last_sql_error, last_io_error, replication_lag_seconds, replica_lag_seconds, sql_delay, data_center, region, physical_environment, replication_depth, is_co_primary, has_replication_credentials, allow_tls, semi_sync_enforced, semi_sync_primary_enabled, semi_sync_primary_timeout, semi_sync_primary_wait_for_replica_count, semi_sync_replica_enabled, semi_sync_primary_status, semi_sync_primary_clients, semi_sync_replica_status, last_discovery_latency, last_seen) VALUES - (?, ?, ?, datetime('now'), datetime('now'), 1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, datetime('now')), - (?, ?, ?, datetime('now'), datetime('now'), 1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, datetime('now')), - (?, ?, ?, datetime('now'), datetime('now'), 1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, datetime('now')) + (?, ?, ?, DATETIME('now'), DATETIME('now'), 1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, DATETIME('now')), + (?, ?, ?, DATETIME('now'), DATETIME('now'), 1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, DATETIME('now')), + (?, ?, ?, DATETIME('now'), DATETIME('now'), 1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, DATETIME('now')) ` a3 := ` zone1-i710, i710, 3306, 710, , 5.6.7, 5.6, MySQL, false, false, STATEMENT, FULL, false, false, , 0, , 0, 0, 0, false, false, 0, 0, false, false, false, , , , , , , false, false, , 0, mysql.000007, 10, , 0, , , {0 false}, {0 false}, 0, , , , 0, false, false, false, false, false, 0, 0, false, false, 0, false, 0, @@ -241,11 +242,11 @@ func TestReadProblemInstances(t *testing.T) { // We need to set InstancePollSeconds to a large value otherwise all the instances are reported as having problems since their last_checked is very old. // Setting this value to a hundred years, we ensure that this test doesn't fail with this issue for the next hundred years. - oldVal := config.Config.InstancePollSeconds + oldVal := config.GetInstancePollTime() defer func() { - config.Config.InstancePollSeconds = oldVal + config.SetInstancePollTime(oldVal) }() - config.Config.InstancePollSeconds = 60 * 60 * 24 * 365 * 100 + config.SetInstancePollTime(60 * 60 * 24 * 365 * 100 * time.Second) for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -325,11 +326,11 @@ func TestReadInstancesWithErrantGTIds(t *testing.T) { // We need to set InstancePollSeconds to a large value otherwise all the instances are reported as having problems since their last_checked is very old. // Setting this value to a hundred years, we ensure that this test doesn't fail with this issue for the next hundred years. - oldVal := config.Config.InstancePollSeconds + oldVal := config.GetInstancePollTime() defer func() { - config.Config.InstancePollSeconds = oldVal + config.SetInstancePollTime(oldVal) }() - config.Config.InstancePollSeconds = 60 * 60 * 24 * 365 * 100 + config.SetInstancePollTime(60 * 60 * 24 * 365 * 100 * time.Second) for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -428,27 +429,27 @@ func TestReadOutdatedInstanceKeys(t *testing.T) { }{ { name: "No problems", - sql: []string{"update database_instance set last_checked = datetime('now')"}, + sql: []string{"update database_instance set last_checked = DATETIME('now')"}, instancesRequired: nil, }, { name: "One instance is outdated", sql: []string{ - "update database_instance set last_checked = datetime('now')", - "update database_instance set last_checked = datetime('now', '-1 hour') where alias = 'zone1-0000000100'", + "update database_instance set last_checked = DATETIME('now')", + "update database_instance set last_checked = DATETIME('now', '-1 hour') where alias = 'zone1-0000000100'", }, instancesRequired: []string{"zone1-0000000100"}, }, { name: "One instance doesn't have myql data", sql: []string{ - "update database_instance set last_checked = datetime('now')", + "update database_instance set last_checked = DATETIME('now')", `INSERT INTO vitess_tablet VALUES('zone1-0000000103','localhost',7706,'ks','0','zone1',2,'0001-01-01 00:00:00+00:00','');`, }, instancesRequired: []string{"zone1-0000000103"}, }, { name: "One instance doesn't have myql data and one is outdated", sql: []string{ - "update database_instance set last_checked = datetime('now')", - "update database_instance set last_checked = datetime('now', '-1 hour') where alias = 'zone1-0000000100'", + "update database_instance set last_checked = DATETIME('now')", + "update database_instance set last_checked = DATETIME('now', '-1 hour') where alias = 'zone1-0000000100'", `INSERT INTO vitess_tablet VALUES('zone1-0000000103','localhost',7706,'ks','0','zone1',2,'0001-01-01 00:00:00+00:00','');`, }, instancesRequired: []string{"zone1-0000000103", "zone1-0000000100"}, @@ -459,13 +460,13 @@ func TestReadOutdatedInstanceKeys(t *testing.T) { waitForCacheInitialization() // We are setting InstancePollSeconds to 59 minutes, just for the test. - oldVal := config.Config.InstancePollSeconds + oldVal := config.GetInstancePollTime() oldCache := forgetAliases defer func() { forgetAliases = oldCache - config.Config.InstancePollSeconds = oldVal + config.SetInstancePollTime(oldVal) }() - config.Config.InstancePollSeconds = 60 * 25 + config.SetInstancePollTime(60 * 25 * time.Second) forgetAliases = cache.New(time.Minute, time.Minute) for _, tt := range tests { @@ -485,10 +486,10 @@ func TestReadOutdatedInstanceKeys(t *testing.T) { errInDataCollection := db.QueryVTOrcRowsMap(`select alias, last_checked, last_attempted_check, -ROUND((JULIANDAY(datetime('now')) - JULIANDAY(last_checked)) * 86400) AS difference, +ROUND((JULIANDAY(DATETIME('now')) - JULIANDAY(last_checked)) * 86400) AS difference, last_attempted_check <= last_checked as use1, -last_checked < datetime('now', '-1500 second') as is_outdated1, -last_checked < datetime('now', '-3000 second') as is_outdated2 +last_checked < DATETIME('now', '-1500 second') as is_outdated1, +last_checked < DATETIME('now', '-3000 second') as is_outdated2 from database_instance`, func(rowMap sqlutils.RowMap) error { log.Errorf("Row in database_instance - %+v", rowMap) return nil @@ -512,12 +513,12 @@ func TestUpdateInstanceLastChecked(t *testing.T) { name: "Verify updated last checked", tabletAlias: "zone1-0000000100", partialSuccess: false, - conditionToCheck: "last_checked >= datetime('now', '-30 second') and last_check_partial_success = false", + conditionToCheck: "last_checked >= DATETIME('now', '-30 second') and last_check_partial_success = false", }, { name: "Verify partial success", tabletAlias: "zone1-0000000100", partialSuccess: true, - conditionToCheck: "last_checked >= datetime('now', '-30 second') and last_check_partial_success = true", + conditionToCheck: "last_checked >= DATETIME('now', '-30 second') and last_check_partial_success = true", }, { name: "Verify no error on unknown tablet", tabletAlias: "unknown tablet", @@ -563,7 +564,7 @@ func TestUpdateInstanceLastAttemptedCheck(t *testing.T) { { name: "Verify updated last checked", tabletAlias: "zone1-0000000100", - conditionToCheck: "last_attempted_check >= datetime('now', '-30 second')", + conditionToCheck: "last_attempted_check >= DATETIME('now', '-30 second')", }, { name: "Verify no error on unknown tablet", tabletAlias: "unknown tablet", @@ -718,10 +719,10 @@ func TestGetDatabaseState(t *testing.T) { } func TestExpireTableData(t *testing.T) { - oldVal := config.Config.AuditPurgeDays - config.Config.AuditPurgeDays = 10 + oldVal := config.GetAuditPurgeDays() + config.SetAuditPurgeDays(10) defer func() { - config.Config.AuditPurgeDays = oldVal + config.SetAuditPurgeDays(oldVal) }() tests := []struct { @@ -736,19 +737,19 @@ func TestExpireTableData(t *testing.T) { tableName: "audit", timestampColumn: "audit_timestamp", expectedRowCount: 1, - insertQuery: `insert into audit (audit_id, audit_timestamp, audit_type, alias, message, keyspace, shard) values -(1, datetime('now', '-50 DAY'), 'a','a','a','a','a'), -(2, datetime('now', '-5 DAY'), 'a','a','a','a','a')`, + insertQuery: `INSERT INTO audit (audit_id, audit_timestamp, audit_type, alias, message, keyspace, shard) VALUES +(1, DATETIME('now', '-50 DAY'), 'a','a','a','a','a'), +(2, DATETIME('now', '-5 DAY'), 'a','a','a','a','a')`, }, { name: "ExpireRecoveryDetectionHistory", tableName: "recovery_detection", timestampColumn: "detection_timestamp", expectedRowCount: 2, - insertQuery: `insert into recovery_detection (detection_id, detection_timestamp, alias, analysis, keyspace, shard) values -(1, datetime('now', '-3 DAY'),'a','a','a','a'), -(2, datetime('now', '-5 DAY'),'a','a','a','a'), -(3, datetime('now', '-15 DAY'),'a','a','a','a')`, + insertQuery: `INSERT INTO recovery_detection (detection_id, detection_timestamp, alias, analysis, keyspace, shard) VALUES +(1, DATETIME('now', '-3 DAY'),'a','a','a','a'), +(2, DATETIME('now', '-5 DAY'),'a','a','a','a'), +(3, DATETIME('now', '-15 DAY'),'a','a','a','a')`, }, } for _, tt := range tests { @@ -773,3 +774,165 @@ func TestExpireTableData(t *testing.T) { }) } } + +func TestDetectErrantGTIDs(t *testing.T) { + tests := []struct { + name string + instance *Instance + primaryInstance *Instance + wantErr bool + wantErrantGTID string + }{ + { + name: "No errant GTIDs", + instance: &Instance{ + ExecutedGtidSet: "230ea8ea-81e3-11e4-972a-e25ec4bd140a:1-10539,8bc65c84-3fe4-11ed-a912-257f0fcdd6c9:1-34", + primaryExecutedGtidSet: "230ea8ea-81e3-11e4-972a-e25ec4bd140a:1-10591,8bc65c84-3fe4-11ed-a912-257f0fcdd6c9:1-34", + AncestryUUID: "316d193c-70e5-11e5-adb2-ecf4bb2262ff,230ea8ea-81e3-11e4-972a-e25ec4bd140a", + ServerUUID: "316d193c-70e5-11e5-adb2-ecf4bb2262ff", + SourceUUID: "230ea8ea-81e3-11e4-972a-e25ec4bd140a", + }, + }, { + name: "Errant GTIDs on replica", + instance: &Instance{ + ExecutedGtidSet: "230ea8ea-81e3-11e4-972a-e25ec4bd140a:1-10539,8bc65c84-3fe4-11ed-a912-257f0fcdd6c9:1-34,316d193c-70e5-11e5-adb2-ecf4bb2262ff:34", + primaryExecutedGtidSet: "230ea8ea-81e3-11e4-972a-e25ec4bd140a:1-10591,8bc65c84-3fe4-11ed-a912-257f0fcdd6c9:1-34", + AncestryUUID: "316d193c-70e5-11e5-adb2-ecf4bb2262ff,230ea8ea-81e3-11e4-972a-e25ec4bd140a", + ServerUUID: "316d193c-70e5-11e5-adb2-ecf4bb2262ff", + SourceUUID: "230ea8ea-81e3-11e4-972a-e25ec4bd140a", + }, + wantErrantGTID: "316d193c-70e5-11e5-adb2-ecf4bb2262ff:34", + }, + { + name: "No errant GTIDs on old primary", + instance: &Instance{ + ExecutedGtidSet: "230ea8ea-81e3-11e4-972a-e25ec4bd140a:1-10539,8bc65c84-3fe4-11ed-a912-257f0fcdd6c9:1-34,316d193c-70e5-11e5-adb2-ecf4bb2262ff:1-341", + AncestryUUID: "316d193c-70e5-11e5-adb2-ecf4bb2262ff", + ServerUUID: "316d193c-70e5-11e5-adb2-ecf4bb2262ff", + }, + primaryInstance: &Instance{ + SourceHost: "", + ExecutedGtidSet: "230ea8ea-81e3-11e4-972a-e25ec4bd140a:1-10589,8bc65c84-3fe4-11ed-a912-257f0fcdd6c9:1-34,316d193c-70e5-11e5-adb2-ecf4bb2262ff:1-341", + }, + }, + { + name: "Errant GTIDs on old primary", + instance: &Instance{ + ExecutedGtidSet: "230ea8ea-81e3-11e4-972a-e25ec4bd140a:1-10539,8bc65c84-3fe4-11ed-a912-257f0fcdd6c9:1-34,316d193c-70e5-11e5-adb2-ecf4bb2262ff:1-342", + AncestryUUID: "316d193c-70e5-11e5-adb2-ecf4bb2262ff", + ServerUUID: "316d193c-70e5-11e5-adb2-ecf4bb2262ff", + }, + primaryInstance: &Instance{ + SourceHost: "", + ExecutedGtidSet: "230ea8ea-81e3-11e4-972a-e25ec4bd140a:1-10589,8bc65c84-3fe4-11ed-a912-257f0fcdd6c9:1-34,316d193c-70e5-11e5-adb2-ecf4bb2262ff:1-341", + }, + wantErrantGTID: "316d193c-70e5-11e5-adb2-ecf4bb2262ff:342", + }, { + name: "Old information for new primary", + instance: &Instance{ + ExecutedGtidSet: "230ea8ea-81e3-11e4-972a-e25ec4bd140a:1-10539,8bc65c84-3fe4-11ed-a912-257f0fcdd6c9:1-34,316d193c-70e5-11e5-adb2-ecf4bb2262ff:1-342", + AncestryUUID: "316d193c-70e5-11e5-adb2-ecf4bb2262ff", + ServerUUID: "316d193c-70e5-11e5-adb2-ecf4bb2262ff", + }, + primaryInstance: &Instance{ + SourceHost: "localhost", + ExecutedGtidSet: "230ea8ea-81e3-11e4-972a-e25ec4bd140a:1-10539,8bc65c84-3fe4-11ed-a912-257f0fcdd6c9:1-34,316d193c-70e5-11e5-adb2-ecf4bb2262ff:1-311", + }, + }, + } + + keyspaceName := "ks" + shardName := "0" + tablet := &topodatapb.Tablet{ + Alias: &topodatapb.TabletAlias{ + Cell: "zone-1", + Uid: 100, + }, + Keyspace: keyspaceName, + Shard: shardName, + } + primaryTablet := &topodatapb.Tablet{ + Alias: &topodatapb.TabletAlias{ + Cell: "zone-1", + Uid: 101, + }, + Keyspace: keyspaceName, + Shard: shardName, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Clear the database after the test. The easiest way to do that is to run all the initialization commands again. + defer func() { + db.ClearVTOrcDatabase() + }() + db.ClearVTOrcDatabase() + + // Save shard record for the primary tablet. + err := SaveShard(topo.NewShardInfo(keyspaceName, shardName, &topodatapb.Shard{ + PrimaryAlias: primaryTablet.Alias, + }, nil)) + require.NoError(t, err) + + if tt.primaryInstance != nil { + tt.primaryInstance.InstanceAlias = topoproto.TabletAliasString(primaryTablet.Alias) + err = SaveTablet(primaryTablet) + require.NoError(t, err) + err = WriteInstance(tt.primaryInstance, true, nil) + require.NoError(t, err) + } + + tt.instance.InstanceAlias = topoproto.TabletAliasString(tablet.Alias) + err = detectErrantGTIDs(tt.instance, tablet) + if tt.wantErr { + require.Error(t, err) + return + } + require.NoError(t, err) + require.EqualValues(t, tt.wantErrantGTID, tt.instance.GtidErrant) + }) + } +} + +// TestPrimaryErrantGTIDs tests that we don't run Errant GTID detection on the primary tablet itself! +func TestPrimaryErrantGTIDs(t *testing.T) { + // Clear the database after the test. The easiest way to do that is to run all the initialization commands again. + defer func() { + db.ClearVTOrcDatabase() + }() + db.ClearVTOrcDatabase() + keyspaceName := "ks" + shardName := "0" + tablet := &topodatapb.Tablet{ + Alias: &topodatapb.TabletAlias{ + Cell: "zone-1", + Uid: 100, + }, + Keyspace: keyspaceName, + Shard: shardName, + } + instance := &Instance{ + SourceHost: "", + ExecutedGtidSet: "230ea8ea-81e3-11e4-972a-e25ec4bd140a:1-10589,8bc65c84-3fe4-11ed-a912-257f0fcdd6c9:1-34,316d193c-70e5-11e5-adb2-ecf4bb2262ff:1-341", + InstanceAlias: topoproto.TabletAliasString(tablet.Alias), + } + + // Save shard record for the primary tablet. + err := SaveShard(topo.NewShardInfo(keyspaceName, shardName, &topodatapb.Shard{ + PrimaryAlias: tablet.Alias, + }, nil)) + require.NoError(t, err) + + // Store the tablet record and the instance. + err = SaveTablet(tablet) + require.NoError(t, err) + err = WriteInstance(instance, true, nil) + require.NoError(t, err) + + // After this if we read a new information for the record that updates its + // gtid set further, we shouldn't be detecting errant GTIDs on it since it is the primary! + // We shouldn't be comparing it with a previous version of itself! + instance.ExecutedGtidSet = "230ea8ea-81e3-11e4-972a-e25ec4bd140a:1-10589,8bc65c84-3fe4-11ed-a912-257f0fcdd6c9:1-34,316d193c-70e5-11e5-adb2-ecf4bb2262ff:1-351" + err = detectErrantGTIDs(instance, tablet) + require.NoError(t, err) + require.EqualValues(t, "", instance.GtidErrant) +} diff --git a/go/vt/vtorc/inst/tablet_dao.go b/go/vt/vtorc/inst/tablet_dao.go index af304292a70..f48f2b97370 100644 --- a/go/vt/vtorc/inst/tablet_dao.go +++ b/go/vt/vtorc/inst/tablet_dao.go @@ -56,13 +56,13 @@ func fullStatus(tabletAlias string) (*replicationdatapb.FullStatus, error) { // ReadTablet reads the vitess tablet record. func ReadTablet(tabletAlias string) (*topodatapb.Tablet, error) { - query := ` - select - info - from - vitess_tablet - where alias = ? - ` + query := `SELECT + info + FROM + vitess_tablet + WHERE + alias = ? + ` args := sqlutils.Args(tabletAlias) tablet := &topodatapb.Tablet{} opts := prototext.UnmarshalOptions{DiscardUnknown: true} @@ -84,14 +84,28 @@ func SaveTablet(tablet *topodatapb.Tablet) error { if err != nil { return err } - _, err = db.ExecVTOrc(` - replace - into vitess_tablet ( - alias, hostname, port, cell, keyspace, shard, tablet_type, primary_timestamp, info - ) values ( - ?, ?, ?, ?, ?, ?, ?, ?, ? - ) - `, + _, err = db.ExecVTOrc(`REPLACE + INTO vitess_tablet ( + alias, + hostname, + port, + cell, + keyspace, + shard, + tablet_type, + primary_timestamp, + info + ) VALUES ( + ?, + ?, + ?, + ?, + ?, + ?, + ?, + ?, + ? + )`, topoproto.TabletAliasString(tablet.Alias), tablet.MysqlHostname, int(tablet.MysqlPort), diff --git a/go/vt/vtorc/logic/disable_recovery.go b/go/vt/vtorc/logic/disable_recovery.go index 60650798876..c5446eeb9ff 100644 --- a/go/vt/vtorc/logic/disable_recovery.go +++ b/go/vt/vtorc/logic/disable_recovery.go @@ -40,14 +40,13 @@ import ( // IsRecoveryDisabled returns true if Recoveries are disabled globally func IsRecoveryDisabled() (disabled bool, err error) { - query := ` - SELECT - COUNT(*) as mycount - FROM - global_recovery_disable - WHERE - disable_recovery=? - ` + query := `SELECT + COUNT(*) AS mycount + FROM + global_recovery_disable + WHERE + disable_recovery = ? + ` err = db.QueryVTOrc(query, sqlutils.Args(1), func(m sqlutils.RowMap) error { mycount := m.GetInt("mycount") disabled = (mycount > 0) @@ -63,21 +62,19 @@ func IsRecoveryDisabled() (disabled bool, err error) { // DisableRecovery ensures recoveries are disabled globally func DisableRecovery() error { - _, err := db.ExecVTOrc(` - INSERT OR IGNORE INTO global_recovery_disable - (disable_recovery) - VALUES (1) - `, - ) + _, err := db.ExecVTOrc(`INSERT OR IGNORE + INTO global_recovery_disable ( + disable_recovery + ) VALUES (1)`) return err } // EnableRecovery ensures recoveries are enabled globally func EnableRecovery() error { // The "WHERE" clause is just to avoid full-scan reports by monitoring tools - _, err := db.ExecVTOrc(` - DELETE FROM global_recovery_disable WHERE disable_recovery >= 0 - `, - ) + _, err := db.ExecVTOrc(`DELETE + FROM global_recovery_disable + WHERE + disable_recovery >= 0`) return err } diff --git a/go/vt/vtorc/logic/keyspace_shard_discovery.go b/go/vt/vtorc/logic/keyspace_shard_discovery.go index b1e93fe2a01..0dd17cb65fd 100644 --- a/go/vt/vtorc/logic/keyspace_shard_discovery.go +++ b/go/vt/vtorc/logic/keyspace_shard_discovery.go @@ -29,17 +29,16 @@ import ( ) // RefreshAllKeyspacesAndShards reloads the keyspace and shard information for the keyspaces that vtorc is concerned with. -func RefreshAllKeyspacesAndShards() { +func RefreshAllKeyspacesAndShards(ctx context.Context) error { var keyspaces []string if len(clustersToWatch) == 0 { // all known keyspaces - ctx, cancel := context.WithTimeout(context.Background(), topo.RemoteOperationTimeout) + ctx, cancel := context.WithTimeout(ctx, topo.RemoteOperationTimeout) defer cancel() var err error // Get all the keyspaces keyspaces, err = ts.GetKeyspaces(ctx) if err != nil { - log.Error(err) - return + return err } } else { // Parse input and build list of keyspaces @@ -55,14 +54,14 @@ func RefreshAllKeyspacesAndShards() { } if len(keyspaces) == 0 { log.Errorf("Found no keyspaces for input: %+v", clustersToWatch) - return + return nil } } // Sort the list of keyspaces. // The list can have duplicates because the input to clusters to watch may have multiple shards of the same keyspace sort.Strings(keyspaces) - refreshCtx, refreshCancel := context.WithTimeout(context.Background(), topo.RemoteOperationTimeout) + refreshCtx, refreshCancel := context.WithTimeout(ctx, topo.RemoteOperationTimeout) defer refreshCancel() var wg sync.WaitGroup for idx, keyspace := range keyspaces { @@ -83,6 +82,8 @@ func RefreshAllKeyspacesAndShards() { }(keyspace) } wg.Wait() + + return nil } // RefreshKeyspaceAndShard refreshes the keyspace record and shard record for the given keyspace and shard. diff --git a/go/vt/vtorc/logic/keyspace_shard_discovery_test.go b/go/vt/vtorc/logic/keyspace_shard_discovery_test.go index 097865db84a..5cbe139728b 100644 --- a/go/vt/vtorc/logic/keyspace_shard_discovery_test.go +++ b/go/vt/vtorc/logic/keyspace_shard_discovery_test.go @@ -92,7 +92,7 @@ func TestRefreshAllKeyspaces(t *testing.T) { // Set clusters to watch to only watch ks1 and ks3 onlyKs1and3 := []string{"ks1/-80", "ks3/-80", "ks3/80-"} clustersToWatch = onlyKs1and3 - RefreshAllKeyspacesAndShards() + require.NoError(t, RefreshAllKeyspacesAndShards(context.Background())) // Verify that we only have ks1 and ks3 in vtorc's db. verifyKeyspaceInfo(t, "ks1", keyspaceDurabilityNone, "") @@ -107,7 +107,7 @@ func TestRefreshAllKeyspaces(t *testing.T) { clustersToWatch = nil // Change the durability policy of ks1 reparenttestutil.SetKeyspaceDurability(ctx, t, ts, "ks1", "semi_sync") - RefreshAllKeyspacesAndShards() + require.NoError(t, RefreshAllKeyspacesAndShards(context.Background())) // Verify that all the keyspaces are correctly reloaded verifyKeyspaceInfo(t, "ks1", keyspaceDurabilitySemiSync, "") diff --git a/go/vt/vtorc/logic/tablet_discovery.go b/go/vt/vtorc/logic/tablet_discovery.go index e9bbcee35cb..990192a23f7 100644 --- a/go/vt/vtorc/logic/tablet_discovery.go +++ b/go/vt/vtorc/logic/tablet_discovery.go @@ -27,7 +27,6 @@ import ( "time" "github.com/spf13/pflag" - "google.golang.org/protobuf/encoding/prototext" "google.golang.org/protobuf/proto" @@ -38,7 +37,6 @@ import ( "vitess.io/vitess/go/vt/vtorc/config" "vitess.io/vitess/go/vt/vtorc/db" "vitess.io/vitess/go/vt/vtorc/inst" - "vitess.io/vitess/go/vt/vtorc/process" "vitess.io/vitess/go/vt/vttablet/tmclient" topodatapb "vitess.io/vitess/go/vt/proto/topodata" @@ -66,39 +64,36 @@ func OpenTabletDiscovery() <-chan time.Time { ts = topo.Open() tmc = inst.InitializeTMC() // Clear existing cache and perform a new refresh. - if _, err := db.ExecVTOrc("delete from vitess_tablet"); err != nil { + if _, err := db.ExecVTOrc("DELETE FROM vitess_tablet"); err != nil { log.Error(err) } - // We refresh all information from the topo once before we start the ticks to do it on a timer. - populateAllInformation() - return time.Tick(time.Second * time.Duration(config.Config.TopoInformationRefreshSeconds)) //nolint SA1015: using time.Tick leaks the underlying ticker -} - -// populateAllInformation initializes all the information for VTOrc to function. -func populateAllInformation() { - refreshAllInformation() - // We have completed one full discovery cycle. We should update the process health. - process.FirstDiscoveryCycleComplete.Store(true) + // We refresh all information from the topo once before we start the ticks to do + // it on a timer. + ctx, cancel := context.WithTimeout(context.Background(), topo.RemoteOperationTimeout) + defer cancel() + if err := refreshAllInformation(ctx); err != nil { + log.Errorf("failed to initialize topo information: %+v", err) + } + return time.Tick(config.GetTopoInformationRefreshDuration()) //nolint SA1015: using time.Tick leaks the underlying ticker } // refreshAllTablets reloads the tablets from topo and discovers the ones which haven't been refreshed in a while -func refreshAllTablets() { - refreshTabletsUsing(func(tabletAlias string) { +func refreshAllTablets(ctx context.Context) error { + return refreshTabletsUsing(ctx, func(tabletAlias string) { DiscoverInstance(tabletAlias, false /* forceDiscovery */) }, false /* forceRefresh */) } -func refreshTabletsUsing(loader func(tabletAlias string), forceRefresh bool) { +func refreshTabletsUsing(ctx context.Context, loader func(tabletAlias string), forceRefresh bool) error { if len(clustersToWatch) == 0 { // all known clusters - ctx, cancel := context.WithTimeout(context.Background(), topo.RemoteOperationTimeout) + ctx, cancel := context.WithTimeout(ctx, topo.RemoteOperationTimeout) defer cancel() cells, err := ts.GetKnownCells(ctx) if err != nil { - log.Error(err) - return + return err } - refreshCtx, refreshCancel := context.WithTimeout(context.Background(), topo.RemoteOperationTimeout) + refreshCtx, refreshCancel := context.WithTimeout(ctx, topo.RemoteOperationTimeout) defer refreshCancel() var wg sync.WaitGroup for _, cell := range cells { @@ -119,7 +114,7 @@ func refreshTabletsUsing(loader func(tabletAlias string), forceRefresh bool) { keyspaceShards = append(keyspaceShards, &topo.KeyspaceShard{Keyspace: input[0], Shard: input[1]}) } else { // Assume this is a keyspace and find all shards in keyspace - ctx, cancel := context.WithTimeout(context.Background(), topo.RemoteOperationTimeout) + ctx, cancel := context.WithTimeout(ctx, topo.RemoteOperationTimeout) defer cancel() shards, err := ts.GetShardNames(ctx, ks) if err != nil { @@ -138,9 +133,9 @@ func refreshTabletsUsing(loader func(tabletAlias string), forceRefresh bool) { } if len(keyspaceShards) == 0 { log.Errorf("Found no keyspaceShards for input: %+v", clustersToWatch) - return + return nil } - refreshCtx, refreshCancel := context.WithTimeout(context.Background(), topo.RemoteOperationTimeout) + refreshCtx, refreshCancel := context.WithTimeout(ctx, topo.RemoteOperationTimeout) defer refreshCancel() var wg sync.WaitGroup for _, ks := range keyspaceShards { @@ -152,6 +147,7 @@ func refreshTabletsUsing(loader func(tabletAlias string), forceRefresh bool) { } wg.Wait() } + return nil } func refreshTabletsInCell(ctx context.Context, cell string, loader func(tabletAlias string), forceRefresh bool) { diff --git a/go/vt/vtorc/logic/tablet_discovery_test.go b/go/vt/vtorc/logic/tablet_discovery_test.go index bc9eeba1fb7..f6a7af38382 100644 --- a/go/vt/vtorc/logic/tablet_discovery_test.go +++ b/go/vt/vtorc/logic/tablet_discovery_test.go @@ -37,7 +37,6 @@ import ( "vitess.io/vitess/go/vt/vtctl/grpcvtctldserver/testutil" "vitess.io/vitess/go/vt/vtorc/db" "vitess.io/vitess/go/vt/vtorc/inst" - "vitess.io/vitess/go/vt/vtorc/process" ) var ( @@ -369,25 +368,6 @@ func TestGetLockAction(t *testing.T) { } } -// TestProcessHealth tests that the health of the process reflects that we have run the first discovery once correctly. -func TestProcessHealth(t *testing.T) { - require.False(t, process.FirstDiscoveryCycleComplete.Load()) - originalTs := ts - defer func() { - ts = originalTs - process.FirstDiscoveryCycleComplete.Store(false) - }() - // Verify in the beginning, we have the first DiscoveredOnce field false. - _, discoveredOnce := process.HealthTest() - require.False(t, discoveredOnce) - ts = memorytopo.NewServer(context.Background(), cell1) - populateAllInformation() - require.True(t, process.FirstDiscoveryCycleComplete.Load()) - // Verify after we populate all information, we have the first DiscoveredOnce field true. - _, discoveredOnce = process.HealthTest() - require.True(t, discoveredOnce) -} - func TestSetReadOnly(t *testing.T) { tests := []struct { name string diff --git a/go/vt/vtorc/logic/topology_recovery.go b/go/vt/vtorc/logic/topology_recovery.go index aec137a45b4..f14eca624c9 100644 --- a/go/vt/vtorc/logic/topology_recovery.go +++ b/go/vt/vtorc/logic/topology_recovery.go @@ -21,7 +21,6 @@ import ( "encoding/json" "fmt" "math/rand/v2" - "time" "vitess.io/vitess/go/stats" "vitess.io/vitess/go/vt/log" @@ -235,8 +234,8 @@ func runEmergencyReparentOp(ctx context.Context, analysisEntry *inst.Replication tablet.Shard, reparentutil.EmergencyReparentOptions{ IgnoreReplicas: nil, - WaitReplicasTimeout: time.Duration(config.Config.WaitReplicasTimeoutSeconds) * time.Second, - PreventCrossCellPromotion: config.Config.PreventCrossDataCenterPrimaryFailover, + WaitReplicasTimeout: config.GetWaitReplicasTimeout(), + PreventCrossCellPromotion: config.GetPreventCrossCellFailover(), WaitAllTablets: waitForAllTablets, }, ) @@ -703,8 +702,8 @@ func electNewPrimary(ctx context.Context, analysisEntry *inst.ReplicationAnalysi analyzedTablet.Keyspace, analyzedTablet.Shard, reparentutil.PlannedReparentOptions{ - WaitReplicasTimeout: time.Duration(config.Config.WaitReplicasTimeoutSeconds) * time.Second, - TolerableReplLag: time.Duration(config.Config.TolerableReplicationLagSeconds) * time.Second, + WaitReplicasTimeout: config.GetWaitReplicasTimeout(), + TolerableReplLag: config.GetTolerableReplicationLag(), }, ) diff --git a/go/vt/vtorc/logic/topology_recovery_dao.go b/go/vt/vtorc/logic/topology_recovery_dao.go index 730e6b2a158..137251c4fc8 100644 --- a/go/vt/vtorc/logic/topology_recovery_dao.go +++ b/go/vt/vtorc/logic/topology_recovery_dao.go @@ -30,21 +30,20 @@ import ( // InsertRecoveryDetection inserts the recovery analysis that has been detected. func InsertRecoveryDetection(analysisEntry *inst.ReplicationAnalysis) error { - sqlResult, err := db.ExecVTOrc(` - insert or ignore - into recovery_detection ( - alias, - analysis, - keyspace, - shard, - detection_timestamp - ) values ( - ?, - ?, - ?, - ?, - datetime('now') - )`, + sqlResult, err := db.ExecVTOrc(`INSERT OR IGNORE + INTO recovery_detection ( + alias, + analysis, + keyspace, + shard, + detection_timestamp + ) VALUES ( + ?, + ?, + ?, + ?, + DATETIME('now') + )`, analysisEntry.AnalyzedInstanceAlias, string(analysisEntry.Analysis), analysisEntry.ClusterDetails.Keyspace, @@ -65,26 +64,24 @@ func InsertRecoveryDetection(analysisEntry *inst.ReplicationAnalysis) error { func writeTopologyRecovery(topologyRecovery *TopologyRecovery) (*TopologyRecovery, error) { analysisEntry := topologyRecovery.AnalysisEntry - sqlResult, err := db.ExecVTOrc(` - insert or ignore - into topology_recovery ( - recovery_id, - alias, - start_recovery, - analysis, - keyspace, - shard, - detection_id - ) values ( - ?, - ?, - datetime('now'), - ?, - ?, - ?, - ? - ) - `, + sqlResult, err := db.ExecVTOrc(`INSERT OR IGNORE + INTO topology_recovery ( + recovery_id, + alias, + start_recovery, + analysis, + keyspace, + shard, + detection_id + ) VALUES ( + ?, + ?, + DATETIME('now'), + ?, + ?, + ?, + ? + )`, sqlutils.NilIfZero(topologyRecovery.ID), analysisEntry.AnalyzedInstanceAlias, string(analysisEntry.Analysis), @@ -138,15 +135,16 @@ func AttemptRecoveryRegistration(analysisEntry *inst.ReplicationAnalysis) (*Topo // ResolveRecovery is called on completion of a recovery process and updates the recovery status. // It does not clear the "active period" as this still takes place in order to avoid flapping. func writeResolveRecovery(topologyRecovery *TopologyRecovery) error { - _, err := db.ExecVTOrc(` - update topology_recovery set - is_successful = ?, - successor_alias = ?, - all_errors = ?, - end_recovery = datetime('now') - where - recovery_id = ? - `, topologyRecovery.IsSuccessful, + _, err := db.ExecVTOrc(`UPDATE topology_recovery + SET + is_successful = ?, + successor_alias = ?, + all_errors = ?, + end_recovery = DATETIME('now') + WHERE + recovery_id = ? + `, + topologyRecovery.IsSuccessful, topologyRecovery.SuccessorAlias, strings.Join(topologyRecovery.AllErrors, "\n"), topologyRecovery.ID, @@ -160,26 +158,27 @@ func writeResolveRecovery(topologyRecovery *TopologyRecovery) error { // readRecoveries reads recovery entry/audit entries from topology_recovery func readRecoveries(whereCondition string, limit string, args []any) ([]*TopologyRecovery, error) { res := []*TopologyRecovery{} - query := fmt.Sprintf(` - select - recovery_id, - alias, - start_recovery, - IFNULL(end_recovery, '') AS end_recovery, - is_successful, - ifnull(successor_alias, '') as successor_alias, - analysis, - keyspace, - shard, - all_errors, - detection_id - from + query := fmt.Sprintf(`SELECT + recovery_id, + alias, + start_recovery, + IFNULL(end_recovery, '') AS end_recovery, + is_successful, + IFNULL(successor_alias, '') AS successor_alias, + analysis, + keyspace, + shard, + all_errors, + detection_id + FROM topology_recovery %s - order by - recovery_id desc + ORDER BY recovery_id DESC %s - `, whereCondition, limit) + `, + whereCondition, + limit, + ) err := db.QueryVTOrc(query, args, func(m sqlutils.RowMap) error { topologyRecovery := *NewTopologyRecovery(inst.ReplicationAnalysis{}) topologyRecovery.ID = m.GetInt64("recovery_id") @@ -211,11 +210,10 @@ func readRecoveries(whereCondition string, limit string, args []any) ([]*Topolog // ReadActiveClusterRecoveries reads recoveries that are ongoing for the given cluster. func ReadActiveClusterRecoveries(keyspace string, shard string) ([]*TopologyRecovery, error) { - whereClause := ` - where - end_recovery IS NULL - and keyspace=? - and shard=?` + whereClause := `WHERE + end_recovery IS NULL + AND keyspace = ? + AND shard = ?` return readRecoveries(whereClause, ``, sqlutils.Args(keyspace, shard)) } @@ -225,23 +223,30 @@ func ReadRecentRecoveries(page int) ([]*TopologyRecovery, error) { whereClause := "" var args []any if len(whereConditions) > 0 { - whereClause = fmt.Sprintf("where %s", strings.Join(whereConditions, " and ")) + whereClause = fmt.Sprintf("WHERE %s", strings.Join(whereConditions, " AND ")) } - limit := ` - limit ? - offset ?` + limit := `LIMIT ? OFFSET ?` args = append(args, config.AuditPageSize, page*config.AuditPageSize) return readRecoveries(whereClause, limit, args) } // writeTopologyRecoveryStep writes down a single step in a recovery process func writeTopologyRecoveryStep(topologyRecoveryStep *TopologyRecoveryStep) error { - sqlResult, err := db.ExecVTOrc(` - insert or ignore - into topology_recovery_steps ( - recovery_step_id, recovery_id, audit_at, message - ) values (?, ?, datetime('now'), ?) - `, sqlutils.NilIfZero(topologyRecoveryStep.ID), topologyRecoveryStep.RecoveryID, topologyRecoveryStep.Message, + sqlResult, err := db.ExecVTOrc(`INSERT OR IGNORE + INTO topology_recovery_steps ( + recovery_step_id, + recovery_id, + audit_at, + message + ) VALUES ( + ?, + ?, + DATETIME('now'), + ? + )`, + sqlutils.NilIfZero(topologyRecoveryStep.ID), + topologyRecoveryStep.RecoveryID, + topologyRecoveryStep.Message, ) if err != nil { log.Error(err) diff --git a/go/vt/vtorc/logic/topology_recovery_dao_test.go b/go/vt/vtorc/logic/topology_recovery_dao_test.go index 20dfb7e91e2..6a1d7c4c48f 100644 --- a/go/vt/vtorc/logic/topology_recovery_dao_test.go +++ b/go/vt/vtorc/logic/topology_recovery_dao_test.go @@ -70,10 +70,10 @@ func TestTopologyRecovery(t *testing.T) { } func TestExpireTableData(t *testing.T) { - oldVal := config.Config.AuditPurgeDays - config.Config.AuditPurgeDays = 10 + oldVal := config.GetAuditPurgeDays() + config.SetAuditPurgeDays(10) defer func() { - config.Config.AuditPurgeDays = oldVal + config.SetAuditPurgeDays(oldVal) }() tests := []struct { diff --git a/go/vt/vtorc/logic/vtorc.go b/go/vt/vtorc/logic/vtorc.go index 9a468d1508a..39326525ce2 100644 --- a/go/vt/vtorc/logic/vtorc.go +++ b/go/vt/vtorc/logic/vtorc.go @@ -17,15 +17,14 @@ package logic import ( - "os" - "os/signal" + "context" "sync" "sync/atomic" - "syscall" "time" "github.com/patrickmn/go-cache" "github.com/sjmudd/stopwatch" + "golang.org/x/sync/errgroup" "vitess.io/vitess/go/stats" "vitess.io/vitess/go/vt/log" @@ -35,6 +34,7 @@ import ( "vitess.io/vitess/go/vt/vtorc/discovery" "vitess.io/vitess/go/vt/vtorc/inst" ometrics "vitess.io/vitess/go/vt/vtorc/metrics" + "vitess.io/vitess/go/vt/vtorc/process" "vitess.io/vitess/go/vt/vtorc/util" ) @@ -73,26 +73,6 @@ func init() { }) } -// used in several places -func instancePollSecondsDuration() time.Duration { - return time.Duration(config.Config.InstancePollSeconds) * time.Second -} - -// acceptSighupSignal registers for SIGHUP signal from the OS to reload the configuration files. -func acceptSighupSignal() { - c := make(chan os.Signal, 1) - - signal.Notify(c, syscall.SIGHUP) - go func() { - for range c { - log.Infof("Received SIGHUP. Reloading configuration") - _ = inst.AuditOperation("reload-configuration", "", "Triggered via SIGHUP") - config.Reload() - discoveryMetrics.SetExpirePeriod(time.Duration(config.DiscoveryCollectionRetentionSeconds) * time.Second) - } - }() -} - // closeVTOrc runs all the operations required to cleanly shutdown VTOrc func closeVTOrc() { log.Infof("Starting VTOrc shutdown") @@ -161,7 +141,7 @@ func DiscoverInstance(tabletAlias string, forceDiscovery bool) { defer func() { latency.Stop("total") discoveryTime := latency.Elapsed("total") - if discoveryTime > instancePollSecondsDuration() { + if discoveryTime > config.GetInstancePollTime() { instancePollSecondsExceededCounter.Add(1) log.Warningf("discoverInstance exceeded InstancePollSeconds for %+v, took %.4fs", tabletAlias, discoveryTime.Seconds()) if metric != nil { @@ -177,7 +157,7 @@ func DiscoverInstance(tabletAlias string, forceDiscovery bool) { // Calculate the expiry period each time as InstancePollSeconds // _may_ change during the run of the process (via SIGHUP) and // it is not possible to change the cache's default expiry.. - if existsInCacheError := recentDiscoveryOperationKeys.Add(tabletAlias, true, instancePollSecondsDuration()); existsInCacheError != nil && !forceDiscovery { + if existsInCacheError := recentDiscoveryOperationKeys.Add(tabletAlias, true, config.GetInstancePollTime()); existsInCacheError != nil && !forceDiscovery { // Just recently attempted return } @@ -271,24 +251,23 @@ func onHealthTick() { // nolint SA1015: using time.Tick leaks the underlying ticker func ContinuousDiscovery() { log.Infof("continuous discovery: setting up") - recentDiscoveryOperationKeys = cache.New(instancePollSecondsDuration(), time.Second) + recentDiscoveryOperationKeys = cache.New(config.GetInstancePollTime(), time.Second) go handleDiscoveryRequests() healthTick := time.Tick(config.HealthPollSeconds * time.Second) caretakingTick := time.Tick(time.Minute) - recoveryTick := time.Tick(time.Duration(config.Config.RecoveryPollSeconds) * time.Second) + recoveryTick := time.Tick(config.GetRecoveryPollDuration()) tabletTopoTick := OpenTabletDiscovery() var recoveryEntrance int64 var snapshotTopologiesTick <-chan time.Time - if config.Config.SnapshotTopologiesIntervalHours > 0 { - snapshotTopologiesTick = time.Tick(time.Duration(config.Config.SnapshotTopologiesIntervalHours) * time.Hour) + if config.GetSnapshotTopologyInterval() > 0 { + snapshotTopologiesTick = time.Tick(config.GetSnapshotTopologyInterval()) } go func() { _ = ometrics.InitMetrics() }() - go acceptSighupSignal() // On termination of the server, we should close VTOrc cleanly servenv.OnTermSync(closeVTOrc) @@ -328,30 +307,34 @@ func ContinuousDiscovery() { go inst.SnapshotTopologies() }() case <-tabletTopoTick: - refreshAllInformation() + ctx, cancel := context.WithTimeout(context.Background(), config.GetTopoInformationRefreshDuration()) + if err := refreshAllInformation(ctx); err != nil { + log.Errorf("failed to refresh topo information: %+v", err) + } + cancel() } } } // refreshAllInformation refreshes both shard and tablet information. This is meant to be run on tablet topo ticks. -func refreshAllInformation() { - // Create a wait group - var wg sync.WaitGroup +func refreshAllInformation(ctx context.Context) error { + // Create an errgroup + eg, ctx := errgroup.WithContext(ctx) // Refresh all keyspace information. - wg.Add(1) - go func() { - defer wg.Done() - RefreshAllKeyspacesAndShards() - }() + eg.Go(func() error { + return RefreshAllKeyspacesAndShards(ctx) + }) // Refresh all tablets. - wg.Add(1) - go func() { - defer wg.Done() - refreshAllTablets() - }() + eg.Go(func() error { + return refreshAllTablets(ctx) + }) // Wait for both the refreshes to complete - wg.Wait() + err := eg.Wait() + if err == nil { + process.FirstDiscoveryCycleComplete.Store(true) + } + return err } diff --git a/go/vt/vtorc/logic/vtorc_test.go b/go/vt/vtorc/logic/vtorc_test.go index c8f2ac3bfdc..edd8141e8b7 100644 --- a/go/vt/vtorc/logic/vtorc_test.go +++ b/go/vt/vtorc/logic/vtorc_test.go @@ -1,11 +1,17 @@ package logic import ( + "context" "sync/atomic" "testing" "time" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "vitess.io/vitess/go/vt/topo/memorytopo" + "vitess.io/vitess/go/vt/vtorc/db" + "vitess.io/vitess/go/vt/vtorc/process" ) func TestWaitForLocksRelease(t *testing.T) { @@ -54,3 +60,41 @@ func waitForLocksReleaseAndGetTimeWaitedFor() time.Duration { waitForLocksRelease() return time.Since(start) } + +func TestRefreshAllInformation(t *testing.T) { + // Store the old flags and restore on test completion + oldTs := ts + defer func() { + ts = oldTs + }() + + // Clear the database after the test. The easiest way to do that is to run all the initialization commands again. + defer func() { + db.ClearVTOrcDatabase() + }() + + // Verify in the beginning, we have the first DiscoveredOnce field false. + _, discoveredOnce := process.HealthTest() + require.False(t, discoveredOnce) + + // Create a memory topo-server and create the keyspace and shard records + ts = memorytopo.NewServer(context.Background(), cell1) + _, err := ts.GetOrCreateShard(context.Background(), keyspace, shard) + require.NoError(t, err) + + // Test error + ctx, cancel := context.WithCancel(context.Background()) + cancel() // cancel context to simulate timeout + require.Error(t, refreshAllInformation(ctx)) + require.False(t, process.FirstDiscoveryCycleComplete.Load()) + _, discoveredOnce = process.HealthTest() + require.False(t, discoveredOnce) + + // Test success + ctx2, cancel2 := context.WithCancel(context.Background()) + defer cancel2() + require.NoError(t, refreshAllInformation(ctx2)) + require.True(t, process.FirstDiscoveryCycleComplete.Load()) + _, discoveredOnce = process.HealthTest() + require.True(t, discoveredOnce) +} diff --git a/go/vt/vtorc/process/health.go b/go/vt/vtorc/process/health.go index 87a11733f66..d448f03bb83 100644 --- a/go/vt/vtorc/process/health.go +++ b/go/vt/vtorc/process/health.go @@ -35,12 +35,17 @@ var ThisNodeHealth = &NodeHealth{} // writeHealthToDatabase writes to the database and returns if it was successful. func writeHealthToDatabase() bool { - _, err := db.ExecVTOrc("delete from node_health") + _, err := db.ExecVTOrc("DELETE FROM node_health") if err != nil { log.Error(err) return false } - sqlResult, err := db.ExecVTOrc(`insert into node_health (last_seen_active) values (datetime('now'))`) + sqlResult, err := db.ExecVTOrc(`INSERT + INTO node_health ( + last_seen_active + ) VALUES ( + DATETIME('now') + )`) if err != nil { log.Error(err) return false @@ -57,7 +62,7 @@ func writeHealthToDatabase() bool { func HealthTest() (health *NodeHealth, discoveredOnce bool) { ThisNodeHealth.LastReported = time.Now() discoveredOnce = FirstDiscoveryCycleComplete.Load() - ThisNodeHealth.Healthy = writeHealthToDatabase() + ThisNodeHealth.Healthy = discoveredOnce && writeHealthToDatabase() return ThisNodeHealth, discoveredOnce } diff --git a/go/vt/vtorc/process/health_test.go b/go/vt/vtorc/process/health_test.go new file mode 100644 index 00000000000..c198deda4e4 --- /dev/null +++ b/go/vt/vtorc/process/health_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 process + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestHealthTest(t *testing.T) { + defer func() { + FirstDiscoveryCycleComplete.Store(false) + ThisNodeHealth = &NodeHealth{} + }() + + require.Zero(t, ThisNodeHealth.LastReported) + require.False(t, ThisNodeHealth.Healthy) + + ThisNodeHealth = &NodeHealth{} + health, discoveredOnce := HealthTest() + require.False(t, health.Healthy) + require.False(t, discoveredOnce) + require.NotZero(t, ThisNodeHealth.LastReported) + + ThisNodeHealth = &NodeHealth{} + FirstDiscoveryCycleComplete.Store(true) + health, discoveredOnce = HealthTest() + require.True(t, health.Healthy) + require.True(t, discoveredOnce) + require.NotZero(t, ThisNodeHealth.LastReported) +} diff --git a/go/vt/vtorc/server/api.go b/go/vt/vtorc/server/api.go index 5e9a84c0a29..177f2c80333 100644 --- a/go/vt/vtorc/server/api.go +++ b/go/vt/vtorc/server/api.go @@ -25,6 +25,7 @@ import ( "time" "vitess.io/vitess/go/acl" + "vitess.io/vitess/go/viperutil/debug" "vitess.io/vitess/go/vt/servenv" "vitess.io/vitess/go/vt/vtorc/collection" "vitess.io/vitess/go/vt/vtorc/discovery" @@ -46,6 +47,7 @@ const ( enableGlobalRecoveriesAPI = "/api/enable-global-recoveries" replicationAnalysisAPI = "/api/replication-analysis" databaseStateAPI = "/api/database-state" + configAPI = "/api/config" healthAPI = "/debug/health" AggregatedDiscoveryMetricsAPI = "/api/aggregated-discovery-metrics" @@ -62,6 +64,7 @@ var ( enableGlobalRecoveriesAPI, replicationAnalysisAPI, databaseStateAPI, + configAPI, healthAPI, AggregatedDiscoveryMetricsAPI, } @@ -90,6 +93,8 @@ func (v *vtorcAPI) ServeHTTP(response http.ResponseWriter, request *http.Request replicationAnalysisAPIHandler(response, request) case databaseStateAPI: databaseStateAPIHandler(response) + case configAPI: + configAPIHandler(response) case AggregatedDiscoveryMetricsAPI: AggregatedDiscoveryMetricsAPIHandler(response, request) default: @@ -106,7 +111,7 @@ func getACLPermissionLevelForAPI(apiEndpoint string) string { return acl.MONITORING case disableGlobalRecoveriesAPI, enableGlobalRecoveriesAPI: return acl.ADMIN - case replicationAnalysisAPI: + case replicationAnalysisAPI, configAPI: return acl.MONITORING case healthAPI, databaseStateAPI: return acl.MONITORING @@ -180,6 +185,17 @@ func databaseStateAPIHandler(response http.ResponseWriter) { writePlainTextResponse(response, ds, http.StatusOK) } +// configAPIHandler is the handler for the configAPI endpoint +func configAPIHandler(response http.ResponseWriter) { + settingsMap := debug.AllSettings() + jsonOut, err := json.MarshalIndent(settingsMap, "", "\t") + if err != nil { + http.Error(response, err.Error(), http.StatusInternalServerError) + return + } + writePlainTextResponse(response, string(jsonOut), http.StatusOK) +} + // AggregatedDiscoveryMetricsAPIHandler is the handler for the discovery metrics endpoint func AggregatedDiscoveryMetricsAPIHandler(response http.ResponseWriter, request *http.Request) { // return metrics for last x seconds diff --git a/go/vt/vtorc/server/api_test.go b/go/vt/vtorc/server/api_test.go index c352d1e600f..ab6b9eed9af 100644 --- a/go/vt/vtorc/server/api_test.go +++ b/go/vt/vtorc/server/api_test.go @@ -31,6 +31,9 @@ func TestGetACLPermissionLevelForAPI(t *testing.T) { }, { apiEndpoint: healthAPI, want: acl.MONITORING, + }, { + apiEndpoint: configAPI, + want: acl.MONITORING, }, { apiEndpoint: "gibberish", want: acl.ADMIN, diff --git a/go/vt/vttablet/common/flags.go b/go/vt/vttablet/common/flags.go index 3c6141d62eb..75e8e58982f 100644 --- a/go/vt/vttablet/common/flags.go +++ b/go/vt/vttablet/common/flags.go @@ -33,8 +33,7 @@ const ( ) var ( - // Default flags: currently VReplicationExperimentalFlagVPlayerBatching is not enabled by default. - vreplicationExperimentalFlags = VReplicationExperimentalFlagOptimizeInserts | VReplicationExperimentalFlagAllowNoBlobBinlogRowImage + vreplicationExperimentalFlags = VReplicationExperimentalFlagOptimizeInserts | VReplicationExperimentalFlagAllowNoBlobBinlogRowImage | VReplicationExperimentalFlagVPlayerBatching vreplicationNetReadTimeout = 300 vreplicationNetWriteTimeout = 600 vreplicationCopyPhaseDuration = 1 * time.Hour diff --git a/go/vt/vttablet/endtoend/config_test.go b/go/vt/vttablet/endtoend/config_test.go index 4abf5b36c21..c3ad5f8a9db 100644 --- a/go/vt/vttablet/endtoend/config_test.go +++ b/go/vt/vttablet/endtoend/config_test.go @@ -36,7 +36,7 @@ import ( ) func TestPoolSize(t *testing.T) { - revert := changeVar(t, "PoolSize", "1") + revert := changeVar(t, "ReadPoolSize", "1") defer revert() vstart := framework.DebugVars() @@ -92,7 +92,7 @@ func TestTxPoolSize(t *testing.T) { defer client2.Rollback() verifyIntValue(t, framework.DebugVars(), "FoundRowsPoolAvailable", framework.FetchInt(vstart, "FoundRowsPoolAvailable")-1) - revert := changeVar(t, "TxPoolSize", "1") + revert := changeVar(t, "TransactionPoolSize", "1") defer revert() vend := framework.DebugVars() verifyIntValue(t, vend, "TransactionPoolAvailable", 0) diff --git a/go/vt/vttablet/endtoend/connecttcp/main_test.go b/go/vt/vttablet/endtoend/connecttcp/main_test.go index 9d52b1287a1..43be05893cc 100644 --- a/go/vt/vttablet/endtoend/connecttcp/main_test.go +++ b/go/vt/vttablet/endtoend/connecttcp/main_test.go @@ -22,6 +22,7 @@ import ( "fmt" "os" "testing" + "time" "vitess.io/vitess/go/mysql" vttestpb "vitess.io/vitess/go/vt/proto/vttest" @@ -86,8 +87,7 @@ func TestMain(m *testing.M) { defer cancel() config := tabletenv.NewDefaultConfig() - config.TwoPCEnable = true - config.TwoPCAbandonAge = 1 + config.TwoPCAbandonAge = 1 * time.Second if err := framework.StartCustomServer(ctx, connParams, connAppDebugParams, cluster.DbName(), config); err != nil { fmt.Fprintf(os.Stderr, "%v", err) diff --git a/go/vt/vttablet/endtoend/framework/server.go b/go/vt/vttablet/endtoend/framework/server.go index 0124bb992ba..3374aadb450 100644 --- a/go/vt/vttablet/endtoend/framework/server.go +++ b/go/vt/vttablet/endtoend/framework/server.go @@ -108,8 +108,7 @@ func StartCustomServer(ctx context.Context, connParams, connAppDebugParams mysql func StartServer(ctx context.Context, connParams, connAppDebugParams mysql.ConnParams, dbName string) error { config := tabletenv.NewDefaultConfig() config.StrictTableACL = true - config.TwoPCEnable = true - config.TwoPCAbandonAge = 1 + config.TwoPCAbandonAge = 1 * time.Second config.HotRowProtection.Mode = tabletenv.Enable config.TrackSchemaVersions = true config.GracePeriods.Shutdown = 2 * time.Second diff --git a/go/vt/vttablet/endtoend/twopc/main_test.go b/go/vt/vttablet/endtoend/twopc/main_test.go index 090751503d4..3b68ce273e1 100644 --- a/go/vt/vttablet/endtoend/twopc/main_test.go +++ b/go/vt/vttablet/endtoend/twopc/main_test.go @@ -22,6 +22,7 @@ import ( "fmt" "os" "testing" + "time" "vitess.io/vitess/go/mysql" "vitess.io/vitess/go/vt/vttablet/endtoend/framework" @@ -83,8 +84,7 @@ func TestMain(m *testing.M) { defer cancel() config := tabletenv.NewDefaultConfig() - config.TwoPCEnable = true - config.TwoPCAbandonAge = 1 + config.TwoPCAbandonAge = 1 * time.Second err := framework.StartCustomServer(ctx, connParams, connAppDebugParams, cluster.DbName(), config) if err != nil { fmt.Fprintf(os.Stderr, "%v", err) diff --git a/go/vt/vttablet/grpctmserver/server.go b/go/vt/vttablet/grpctmserver/server.go index 777f641b1fc..6f0fd2aa4dc 100644 --- a/go/vt/vttablet/grpctmserver/server.go +++ b/go/vt/vttablet/grpctmserver/server.go @@ -637,6 +637,7 @@ func (s *server) StopReplicationAndGetStatus(ctx context.Context, request *table if err == nil { response.Status = statusResponse.Status } + return response, err } diff --git a/go/vt/vttablet/onlineddl/executor.go b/go/vt/vttablet/onlineddl/executor.go index 555cadd53ea..f8b5cfd9b8d 100644 --- a/go/vt/vttablet/onlineddl/executor.go +++ b/go/vt/vttablet/onlineddl/executor.go @@ -94,13 +94,18 @@ var ( ptOSCBinaryPath = "/usr/bin/pt-online-schema-change" migrationCheckInterval = 1 * time.Minute 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} cutoverIntervals = []time.Duration{0, 1 * time.Minute, 5 * time.Minute, 10 * time.Minute, 30 * time.Minute} ) +const ( + defaultCutOverThreshold = 10 * time.Second + minCutOverThreshold = 5 * time.Second + maxCutOverThreshold = 30 * time.Second +) + func init() { servenv.OnParseFor("vtcombo", registerOnlineDDLFlags) servenv.OnParseFor("vttablet", registerOnlineDDLFlags) @@ -199,13 +204,19 @@ func newGCTableRetainTime() time.Time { return time.Now().UTC().Add(retainOnlineDDLTables) } -// getMigrationCutOverThreshold returns the cut-over threshold for the given migration. The migration's -// 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 +// safeMigrationCutOverThreshold receives a desired threshold, and returns a cut-over threshold that +// is reasonable to use +func safeMigrationCutOverThreshold(threshold time.Duration) (time.Duration, error) { + switch { + case threshold == 0: + return defaultCutOverThreshold, nil + case threshold < minCutOverThreshold: + return defaultCutOverThreshold, vterrors.Errorf(vtrpcpb.Code_FAILED_PRECONDITION, "cut-over min value is %v", minCutOverThreshold) + case threshold > maxCutOverThreshold: + return defaultCutOverThreshold, vterrors.Errorf(vtrpcpb.Code_FAILED_PRECONDITION, "cut-over max value is %v", maxCutOverThreshold) + default: + return threshold, nil } - return defaultCutOverThreshold } // NewExecutor creates a new gh-ost executor. @@ -890,8 +901,6 @@ func (e *Executor) cutOverVReplMigration(ctx context.Context, s *VReplStream, sh var sentryTableName string - migrationCutOverThreshold := getMigrationCutOverThreshold(onlineDDL) - waitForPos := func(s *VReplStream, pos replication.Position, timeout time.Duration) error { ctx, cancel := context.WithTimeout(ctx, timeout) defer cancel() @@ -951,8 +960,8 @@ func (e *Executor) cutOverVReplMigration(ctx context.Context, s *VReplStream, sh } defer preparationsConn.Recycle() // Set large enough `@@lock_wait_timeout` so that it does not interfere with the cut-over operation. - // The code will ensure everything that needs to be terminated by `migrationCutOverThreshold` will be terminated. - preparationConnRestoreLockWaitTimeout, err := e.initConnectionLockWaitTimeout(ctx, preparationsConn.Conn, 3*migrationCutOverThreshold) + // The code will ensure everything that needs to be terminated by `onlineDDL.CutOverThreshold` will be terminated. + preparationConnRestoreLockWaitTimeout, err := e.initConnectionLockWaitTimeout(ctx, preparationsConn.Conn, 3*onlineDDL.CutOverThreshold) if err != nil { return vterrors.Wrap(err, "failed setting lock_wait_timeout on locking connection") } @@ -989,7 +998,7 @@ func (e *Executor) cutOverVReplMigration(ctx context.Context, s *VReplStream, sh // impacts query serving so we wait for a multiple of the cutover threshold here, with // that variable primarily serving to limit the max time we later spend waiting for // a position again AFTER we've taken the locks and table access is blocked. - if err := waitForPos(s, postSentryPos, migrationCutOverThreshold*3); err != nil { + if err := waitForPos(s, postSentryPos, onlineDDL.CutOverThreshold*3); err != nil { return vterrors.Wrapf(err, "failed waiting for pos after sentry creation") } e.updateMigrationStage(ctx, onlineDDL.UUID, "post-sentry pos reached") @@ -1001,8 +1010,8 @@ func (e *Executor) cutOverVReplMigration(ctx context.Context, s *VReplStream, sh } defer lockConn.Recycle() // Set large enough `@@lock_wait_timeout` so that it does not interfere with the cut-over operation. - // The code will ensure everything that needs to be terminated by `migrationCutOverThreshold` will be terminated. - lockConnRestoreLockWaitTimeout, err := e.initConnectionLockWaitTimeout(ctx, lockConn.Conn, 5*migrationCutOverThreshold) + // The code will ensure everything that needs to be terminated by `onlineDDL.CutOverThreshold` will be terminated. + lockConnRestoreLockWaitTimeout, err := e.initConnectionLockWaitTimeout(ctx, lockConn.Conn, 5*onlineDDL.CutOverThreshold) if err != nil { return vterrors.Wrapf(err, "failed setting lock_wait_timeout on locking connection") } @@ -1016,8 +1025,8 @@ func (e *Executor) cutOverVReplMigration(ctx context.Context, s *VReplStream, sh return vterrors.Wrapf(err, "failed getting rename connection") } // Set large enough `@@lock_wait_timeout` so that it does not interfere with the cut-over operation. - // The code will ensure everything that needs to be terminated by `migrationCutOverThreshold` will be terminated. - renameConnRestoreLockWaitTimeout, err := e.initConnectionLockWaitTimeout(ctx, renameConn.Conn, 5*migrationCutOverThreshold*4) + // The code will ensure everything that needs to be terminated by `onlineDDL.CutOverThreshold` will be terminated. + renameConnRestoreLockWaitTimeout, err := e.initConnectionLockWaitTimeout(ctx, renameConn.Conn, 5*onlineDDL.CutOverThreshold*4) if err != nil { return vterrors.Wrapf(err, "failed setting lock_wait_timeout on rename connection") } @@ -1052,7 +1061,7 @@ func (e *Executor) cutOverVReplMigration(ctx context.Context, s *VReplStream, sh // 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-instantaneous and it's unlikely that the function will find it. - renameWaitCtx, cancel := context.WithTimeout(ctx, migrationCutOverThreshold) + renameWaitCtx, cancel := context.WithTimeout(ctx, onlineDDL.CutOverThreshold) defer cancel() for { @@ -1081,7 +1090,7 @@ func (e *Executor) cutOverVReplMigration(ctx context.Context, s *VReplStream, sh // Preparation is complete. We proceed to cut-over. toggleBuffering := func(bufferQueries bool) error { log.Infof("toggling buffering: %t in migration %v", bufferQueries, onlineDDL.UUID) - timeout := migrationCutOverThreshold + qrBufferExtraTimeout + timeout := onlineDDL.CutOverThreshold + qrBufferExtraTimeout e.toggleBufferTableFunc(bufferingCtx, onlineDDL.Table, timeout, bufferQueries) if !bufferQueries { @@ -1147,7 +1156,7 @@ func (e *Executor) cutOverVReplMigration(ctx context.Context, s *VReplStream, sh // real production e.updateMigrationStage(ctx, onlineDDL.UUID, "locking tables") - lockCtx, cancel := context.WithTimeout(ctx, migrationCutOverThreshold) + lockCtx, cancel := context.WithTimeout(ctx, onlineDDL.CutOverThreshold) defer cancel() lockTableQuery := sqlparser.BuildParsedQuery(sqlLockTwoTablesWrite, sentryTableName, onlineDDL.Table) if _, err := lockConn.Conn.Exec(lockCtx, lockTableQuery.Query, 1, false); err != nil { @@ -1187,7 +1196,7 @@ func (e *Executor) cutOverVReplMigration(ctx context.Context, s *VReplStream, sh } e.updateMigrationStage(ctx, onlineDDL.UUID, "waiting for post-lock pos: %v", replication.EncodePosition(postWritesPos)) - if err := waitForPos(s, postWritesPos, migrationCutOverThreshold); err != nil { + if err := waitForPos(s, postWritesPos, onlineDDL.CutOverThreshold); err != nil { e.updateMigrationStage(ctx, onlineDDL.UUID, "timeout while waiting for post-lock pos: %v", err) return vterrors.Wrapf(err, "failed waiting for pos after locking") } @@ -1220,14 +1229,14 @@ func (e *Executor) cutOverVReplMigration(ctx context.Context, s *VReplStream, sh { dropTableQuery := sqlparser.BuildParsedQuery(sqlDropTable, sentryTableName) - lockCtx, cancel := context.WithTimeout(ctx, migrationCutOverThreshold) + lockCtx, cancel := context.WithTimeout(ctx, onlineDDL.CutOverThreshold) defer cancel() if _, err := lockConn.Conn.Exec(lockCtx, dropTableQuery.Query, 1, false); err != nil { return vterrors.Wrapf(err, "failed dropping sentry table") } } { - lockCtx, cancel := context.WithTimeout(ctx, migrationCutOverThreshold) + lockCtx, cancel := context.WithTimeout(ctx, onlineDDL.CutOverThreshold) defer cancel() e.updateMigrationStage(ctx, onlineDDL.UUID, "unlocking tables") if _, err := lockConn.Conn.Exec(lockCtx, sqlUnlockTables, 1, false); err != nil { @@ -1235,7 +1244,7 @@ func (e *Executor) cutOverVReplMigration(ctx context.Context, s *VReplStream, sh } } { - lockCtx, cancel := context.WithTimeout(ctx, migrationCutOverThreshold) + lockCtx, cancel := context.WithTimeout(ctx, onlineDDL.CutOverThreshold) defer cancel() e.updateMigrationStage(lockCtx, onlineDDL.UUID, "waiting for RENAME to complete") if err := <-renameCompleteChan; err != nil { @@ -2034,7 +2043,9 @@ func (e *Executor) readMigration(ctx context.Context, uuid string) (onlineDDL *s WasReadyToComplete: row.AsInt64("was_ready_to_complete", 0), TabletAlias: row["tablet"].ToString(), MigrationContext: row["migration_context"].ToString(), + CutOverThreshold: time.Second * time.Duration(row.AsInt64("cutover_threshold_seconds", 0)), } + onlineDDL.CutOverThreshold, _ = safeMigrationCutOverThreshold(onlineDDL.CutOverThreshold) return onlineDDL, row, nil } @@ -3572,55 +3583,36 @@ func (e *Executor) isPreserveForeignKeySupported(ctx context.Context) (isSupport // and is up to date with the binlogs. func (e *Executor) isVReplMigrationReadyToCutOver(ctx context.Context, onlineDDL *schema.OnlineDDL, s *VReplStream) (isReady bool, err error) { // Check all the cases where migration is still running: - { - // when ready to cut-over, pos must have some value - if s.pos == "" { - return false, nil - } + // when ready to cut-over, pos must have some value + if s.pos == "" { + return false, nil } - { - // 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 { - return t1.Sub(t2).Abs() - } - migrationCutOverThreshold := getMigrationCutOverThreshold(onlineDDL) - - timeNow := time.Now() - timeUpdated := time.Unix(s.timeUpdated, 0) - if durationDiff(timeNow, timeUpdated) > migrationCutOverThreshold { - return false, nil - } - // Let's look at transaction timestamp. This gets written by any ongoing - // writes on the server (whether on this table or any other table) - transactionTimestamp := time.Unix(s.transactionTimestamp, 0) - if durationDiff(timeNow, transactionTimestamp) > migrationCutOverThreshold { - return false, nil - } + // 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 + if s.Lag() > onlineDDL.CutOverThreshold { + return false, nil } - { - // copy_state must have no entries for this vreplication id: if entries are - // present that means copy is still in progress - query, err := sqlparser.ParseAndBind(sqlReadCountCopyState, - sqltypes.Int32BindVariable(s.id), - ) - if err != nil { - return false, err - } - r, err := e.execQuery(ctx, query) - if err != nil { - return false, err - } - csRow := r.Named().Row() - if csRow == nil { - return false, err - } - count := csRow.AsInt64("cnt", 0) - if count > 0 { - // Still copying - return false, nil - } + // copy_state must have no entries for this vreplication id: if entries are + // present that means copy is still in progress + query, err := sqlparser.ParseAndBind(sqlReadCountCopyState, + sqltypes.Int32BindVariable(s.id), + ) + if err != nil { + return false, err + } + r, err := e.execQuery(ctx, query) + if err != nil { + return false, err + } + csRow := r.Named().Row() + if csRow == nil { + return false, err + } + count := csRow.AsInt64("cnt", 0) + if count > 0 { + // Still copying + return false, nil } return true, nil @@ -3767,6 +3759,7 @@ func (e *Executor) reviewRunningMigrations(ctx context.Context) (countRunnning i } _ = e.updateRowsCopied(ctx, uuid, s.rowsCopied) _ = e.updateMigrationProgressByRowsCopied(ctx, uuid, s.rowsCopied) + _ = e.updateMigrationVreplicationLagSeconds(ctx, uuid, int64(s.Lag().Seconds())) _ = e.updateMigrationETASecondsByProgress(ctx, uuid) if s.timeThrottled != 0 { // Avoid creating a 0000-00-00 00:00:00 timestamp @@ -4525,6 +4518,18 @@ func (e *Executor) updateRowsCopied(ctx context.Context, uuid string, rowsCopied return err } +func (e *Executor) updateMigrationVreplicationLagSeconds(ctx context.Context, uuid string, vreplicationLagSeconds int64) error { + query, err := sqlparser.ParseAndBind(sqlUpdateMigrationVreplicationLagSeconds, + sqltypes.Int64BindVariable(vreplicationLagSeconds), + sqltypes.StringBindVariable(uuid), + ) + if err != nil { + return err + } + _, err = e.execQuery(ctx, query) + return err +} + func (e *Executor) updateVitessLivenessIndicator(ctx context.Context, uuid string, livenessIndicator int64) error { query, err := sqlparser.ParseAndBind(sqlUpdateMigrationVitessLivenessIndicator, sqltypes.Int64BindVariable(livenessIndicator), @@ -4747,6 +4752,42 @@ func (e *Executor) ForceCutOverPendingMigrations(ctx context.Context) (result *s return result, nil } +func (e *Executor) SetMigrationCutOverThreshold(ctx context.Context, uuid string, thresholdString 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) + } + threshold, err := time.ParseDuration(thresholdString) + if err != nil { + return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "invalid cut-over threshold value: %s. Try '5s' to '30s'", thresholdString) + } + + log.Infof("SetMigrationCutOverThreshold: request to set cut-over threshold to %v on migration %s", threshold, uuid) + e.migrationMutex.Lock() + defer e.migrationMutex.Unlock() + + threshold, err = safeMigrationCutOverThreshold(threshold) + if err != nil { + return nil, err + } + query, err := sqlparser.ParseAndBind(sqlUpdateCutOverThresholdSeconds, + sqltypes.Int64BindVariable(int64(threshold.Seconds())), + 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("SetMigrationCutOverThreshold: migration %s cut-over threshold was set to", uuid, threshold) + return rs, 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 { @@ -5032,7 +5073,14 @@ func (e *Executor) SubmitMigration( // Explicit retention indicated by `--retain-artifact` DDL strategy flag for this migration. Override! retainArtifactsSeconds = int64((retainArtifacts).Seconds()) } - + cutoverThreshold, err := onlineDDL.StrategySetting().CutOverThreshold() + if err != nil { + return nil, vterrors.Wrapf(err, "parsing cut-over threshold in migration %v", onlineDDL.UUID) + } + cutoverThreshold, err = safeMigrationCutOverThreshold(cutoverThreshold) + if err != nil { + return nil, vterrors.Wrapf(err, "validating cut-over threshold in migration %v", onlineDDL.UUID) + } _, allowConcurrentMigration := e.allowConcurrentMigration(onlineDDL) submitQuery, err := sqlparser.ParseAndBind(sqlInsertMigration, sqltypes.StringBindVariable(onlineDDL.UUID), @@ -5048,6 +5096,7 @@ func (e *Executor) SubmitMigration( sqltypes.StringBindVariable(string(schema.OnlineDDLStatusQueued)), sqltypes.StringBindVariable(e.TabletAliasString()), sqltypes.Int64BindVariable(retainArtifactsSeconds), + sqltypes.Int64BindVariable(int64(cutoverThreshold.Seconds())), sqltypes.BoolBindVariable(onlineDDL.StrategySetting().IsPostponeLaunch()), sqltypes.BoolBindVariable(onlineDDL.StrategySetting().IsPostponeCompletion()), sqltypes.BoolBindVariable(allowConcurrentMigration), diff --git a/go/vt/vttablet/onlineddl/executor_test.go b/go/vt/vttablet/onlineddl/executor_test.go index 2533f3a4b48..105da7fc1e3 100644 --- a/go/vt/vttablet/onlineddl/executor_test.go +++ b/go/vt/vttablet/onlineddl/executor_test.go @@ -25,6 +25,7 @@ import ( "time" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestShouldCutOverAccordingToBackoff(t *testing.T) { @@ -164,3 +165,59 @@ func TestShouldCutOverAccordingToBackoff(t *testing.T) { }) } } + +func TestSafeMigrationCutOverThreshold(t *testing.T) { + require.NotZero(t, defaultCutOverThreshold) + require.GreaterOrEqual(t, defaultCutOverThreshold, minCutOverThreshold) + require.LessOrEqual(t, defaultCutOverThreshold, maxCutOverThreshold) + + tcases := []struct { + threshold time.Duration + expect time.Duration + isErr bool + }{ + { + threshold: 0, + expect: defaultCutOverThreshold, + }, + { + threshold: 2 * time.Second, + expect: defaultCutOverThreshold, + isErr: true, + }, + { + threshold: 75 * time.Second, + expect: defaultCutOverThreshold, + isErr: true, + }, + { + threshold: defaultCutOverThreshold, + expect: defaultCutOverThreshold, + }, + { + threshold: 5 * time.Second, + expect: 5 * time.Second, + }, + { + threshold: 15 * time.Second, + expect: 15 * time.Second, + }, + { + threshold: 25 * time.Second, + expect: 25 * time.Second, + }, + } + for _, tcase := range tcases { + t.Run(tcase.threshold.String(), func(t *testing.T) { + threshold, err := safeMigrationCutOverThreshold(tcase.threshold) + if tcase.isErr { + assert.Error(t, err) + require.Equal(t, tcase.expect, defaultCutOverThreshold) + // And keep testing, because we then also expect the threshold to be the default + } else { + assert.NoError(t, err) + } + assert.Equal(t, tcase.expect, threshold) + }) + } +} diff --git a/go/vt/vttablet/onlineddl/schema.go b/go/vt/vttablet/onlineddl/schema.go index 1b120dfa58c..943a3b1df07 100644 --- a/go/vt/vttablet/onlineddl/schema.go +++ b/go/vt/vttablet/onlineddl/schema.go @@ -32,13 +32,14 @@ const ( migration_status, tablet, retain_artifacts_seconds, + cutover_threshold_seconds, postpone_launch, postpone_completion, allow_concurrent, reverted_uuid, is_view ) VALUES ( - %a, %a, %a, %a, %a, %a, %a, %a, %a, NOW(6), %a, %a, %a, %a, %a, %a, %a, %a, %a + %a, %a, %a, %a, %a, %a, %a, %a, %a, NOW(6), %a, %a, %a, %a, %a, %a, %a, %a, %a, %a )` sqlSelectQueuedMigrations = `SELECT @@ -86,6 +87,11 @@ const ( WHERE migration_uuid=%a ` + sqlUpdateMigrationVreplicationLagSeconds = `UPDATE _vt.schema_migrations + SET vreplication_lag_seconds=%a + WHERE + migration_uuid=%a + ` sqlUpdateMigrationIsView = `UPDATE _vt.schema_migrations SET is_view=%a WHERE @@ -181,6 +187,11 @@ const ( WHERE migration_uuid=%a ` + sqlUpdateCutOverThresholdSeconds = `UPDATE _vt.schema_migrations + SET cutover_threshold_seconds=%a + WHERE + migration_uuid=%a + ` sqlUpdateLaunchMigration = `UPDATE _vt.schema_migrations SET postpone_launch=0 WHERE @@ -429,6 +440,7 @@ const ( removed_unique_keys, migration_context, retain_artifacts_seconds, + cutover_threshold_seconds, is_view, ready_to_complete, ready_to_complete_timestamp is not null as was_ready_to_complete, diff --git a/go/vt/vttablet/onlineddl/vrepl.go b/go/vt/vttablet/onlineddl/vrepl.go index 26eb614e95a..2761c27c801 100644 --- a/go/vt/vttablet/onlineddl/vrepl.go +++ b/go/vt/vttablet/onlineddl/vrepl.go @@ -30,6 +30,7 @@ import ( "net/url" "strconv" "strings" + "time" "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/mysql/collations/charset" @@ -96,6 +97,19 @@ func (v *VReplStream) hasError() (isTerminal bool, vreplError error) { return false, nil } +// Lag returns the vreplication lag, as determined by the higher of the transaction timestamp and the time updated. +func (s *VReplStream) Lag() time.Duration { + durationDiff := func(t1, t2 time.Time) time.Duration { + return t1.Sub(t2).Abs() + } + timeNow := time.Now() + timeUpdated := time.Unix(s.timeUpdated, 0) + // Let's look at transaction timestamp. This gets written by any ongoing + // writes on the server (whether on this table or any other table) + transactionTimestamp := time.Unix(s.transactionTimestamp, 0) + return max(durationDiff(timeNow, timeUpdated), durationDiff(timeNow, transactionTimestamp)) +} + // VRepl is an online DDL helper for VReplication based migrations (ddl_strategy="online") type VRepl struct { workflow string diff --git a/go/vt/vttablet/tabletmanager/rpc_agent.go b/go/vt/vttablet/tabletmanager/rpc_agent.go index 203e1d006ab..445d74cb930 100644 --- a/go/vt/vttablet/tabletmanager/rpc_agent.go +++ b/go/vt/vttablet/tabletmanager/rpc_agent.go @@ -166,6 +166,8 @@ type RPCTM interface { RestoreFromBackup(ctx context.Context, logger logutil.Logger, request *tabletmanagerdatapb.RestoreFromBackupRequest) error + IsBackupRunning() bool + // HandleRPCPanic is to be called in a defer statement in each // RPC input point. HandleRPCPanic(ctx context.Context, name string, args, reply any, verbose bool, err *error) diff --git a/go/vt/vttablet/tabletmanager/rpc_backup.go b/go/vt/vttablet/tabletmanager/rpc_backup.go index a66264d98af..22fe72716dd 100644 --- a/go/vt/vttablet/tabletmanager/rpc_backup.go +++ b/go/vt/vttablet/tabletmanager/rpc_backup.go @@ -205,6 +205,10 @@ func (tm *TabletManager) RestoreFromBackup(ctx context.Context, logger logutil.L return err } +func (tm *TabletManager) IsBackupRunning() bool { + return tm._isBackupRunning +} + func (tm *TabletManager) beginBackup(backupMode string) error { tm.mutex.Lock() defer tm.mutex.Unlock() diff --git a/go/vt/vttablet/tabletmanager/rpc_replication.go b/go/vt/vttablet/tabletmanager/rpc_replication.go index 90e4d835a79..f13efa66124 100644 --- a/go/vt/vttablet/tabletmanager/rpc_replication.go +++ b/go/vt/vttablet/tabletmanager/rpc_replication.go @@ -46,7 +46,11 @@ func (tm *TabletManager) ReplicationStatus(ctx context.Context) (*replicationdat if err != nil { return nil, err } - return replication.ReplicationStatusToProto(status), nil + + protoStatus := replication.ReplicationStatusToProto(status) + protoStatus.BackupRunning = tm.IsBackupRunning() + + return protoStatus, nil } // FullStatus returns the full status of MySQL including the replication information, semi-sync information, GTID information among others @@ -893,6 +897,7 @@ func (tm *TabletManager) StopReplicationAndGetStatus(ctx context.Context, stopRe return StopReplicationAndGetStatusResponse{}, vterrors.Wrap(err, "before status failed") } before := replication.ReplicationStatusToProto(rs) + before.BackupRunning = tm.IsBackupRunning() if stopReplicationMode == replicationdatapb.StopReplicationMode_IOTHREADONLY { if !rs.IOHealthy() { @@ -939,6 +944,7 @@ func (tm *TabletManager) StopReplicationAndGetStatus(ctx context.Context, stopRe }, vterrors.Wrap(err, "acquiring replication status failed") } after := replication.ReplicationStatusToProto(rsAfter) + after.BackupRunning = tm.IsBackupRunning() rs.Position = rsAfter.Position rs.RelayLogPosition = rsAfter.RelayLogPosition diff --git a/go/vt/vttablet/tabletmanager/rpc_vreplication_test.go b/go/vt/vttablet/tabletmanager/rpc_vreplication_test.go index 0a5bd9f26fd..3f8bc85ac7f 100644 --- a/go/vt/vttablet/tabletmanager/rpc_vreplication_test.go +++ b/go/vt/vttablet/tabletmanager/rpc_vreplication_test.go @@ -305,7 +305,6 @@ func TestCreateVReplicationWorkflow(t *testing.T) { // results returned. Followed by ensuring that SwitchTraffic // and ReverseTraffic also work as expected. func TestMoveTablesUnsharded(t *testing.T) { - t.Skip("Skipping test temporarily as it is flaky on CI, pending investigation") ctx, cancel := context.WithCancel(context.Background()) defer cancel() sourceKs := "sourceks" @@ -403,6 +402,9 @@ func TestMoveTablesUnsharded(t *testing.T) { ftc.vrdbClient.AddInvariant(getCopyStateQuery, &sqltypes.Result{}) tenv.tmc.setVReplicationExecResults(ftc.tablet, getCopyState, &sqltypes.Result{}) ftc.vrdbClient.ExpectRequest(fmt.Sprintf(readAllWorkflows, tenv.dbName, ""), &sqltypes.Result{}, nil) + for _, table := range defaultSchema.TableDefinitions { + tenv.db.AddQuery(fmt.Sprintf(getNonEmptyTableQuery, table.Name), &sqltypes.Result{}) + } insert := fmt.Sprintf(`%s values ('%s', 'keyspace:"%s" shard:"%s" filter:{rules:{match:"t1" filter:"select * from t1"}}', '', 0, 0, '%s', 'primary,replica,rdonly', now(), 0, 'Stopped', '%s', %d, 0, 0, '{}')`, insertVReplicationPrefix, wf, sourceKs, sourceShard, tenv.cells[0], tenv.dbName, vreplID) ftc.vrdbClient.ExpectRequest(insert, &sqltypes.Result{InsertID: 1}, nil) @@ -1780,7 +1782,7 @@ func addInvariants(dbClient *binlogplayer.MockDBClient, vreplID, sourceTabletUID "0", )) dbClient.AddInvariant(fmt.Sprintf(updatePickedSourceTablet, cell, sourceTabletUID, vreplID), &sqltypes.Result{}) - + dbClient.AddInvariant("update _vt.vreplication set state='Running', message='' where id=1", &sqltypes.Result{}) } func addMaterializeSettingsTablesToSchema(ms *vtctldatapb.MaterializeSettings, tenv *testEnv, venv *vtenv.Environment) { diff --git a/go/vt/vttablet/tabletmanager/vdiff/framework_test.go b/go/vt/vttablet/tabletmanager/vdiff/framework_test.go index 33a0da8e23f..7d4cdb78c20 100644 --- a/go/vt/vttablet/tabletmanager/vdiff/framework_test.go +++ b/go/vt/vttablet/tabletmanager/vdiff/framework_test.go @@ -396,6 +396,10 @@ func (dbc *realDBClient) Close() { dbc.conn.Close() } +func (dbc *realDBClient) IsClosed() bool { + return dbc.conn.IsClosed() +} + func (dbc *realDBClient) ExecuteFetch(query string, maxrows int) (*sqltypes.Result, error) { // Use Clone() because the contents of memory region referenced by // string can change when clients (e.g. vcopier) use unsafe string methods. diff --git a/go/vt/vttablet/tabletmanager/vreplication/external_connector_test.go b/go/vt/vttablet/tabletmanager/vreplication/external_connector_test.go index e00a5578171..c671d2a086d 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/external_connector_test.go +++ b/go/vt/vttablet/tabletmanager/vreplication/external_connector_test.go @@ -163,8 +163,7 @@ func TestExternalConnectorPlay(t *testing.T) { expectDBClientAndVreplicationQueries(t, []string{ "begin", - "insert into tab1(id,val) values (1,_binary'a')", - "insert into tab1(id,val) values (2,_binary'b')", + "insert into tab1(id,val) values (1,_binary'a'), (2,_binary'b')", "/update _vt.vreplication set pos=", "commit", }, pos) diff --git a/go/vt/vttablet/tabletmanager/vreplication/framework_test.go b/go/vt/vttablet/tabletmanager/vreplication/framework_test.go index 12d20e3a867..12a05a69dbc 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/framework_test.go +++ b/go/vt/vttablet/tabletmanager/vreplication/framework_test.go @@ -141,7 +141,6 @@ func setup(ctx context.Context) (func(), int) { resetBinlogClient() vttablet.InitVReplicationConfigDefaults() - vttablet.DefaultVReplicationConfig.ExperimentalFlags = 0 // Engines cannot be initialized in testenv because it introduces circular dependencies. streamerEngine = vstreamer.NewEngine(env.TabletEnv, env.SrvTopo, env.SchemaEngine, nil, env.Cells[0]) @@ -479,6 +478,10 @@ func (dbc *realDBClient) Close() { dbc.conn.Close() } +func (dbc *realDBClient) IsClosed() bool { + return dbc.conn.IsClosed() +} + func (dbc *realDBClient) ExecuteFetch(query string, maxrows int) (*sqltypes.Result, error) { // Use Clone() because the contents of memory region referenced by // string can change when clients (e.g. vcopier) use unsafe string methods. diff --git a/go/vt/vttablet/tabletmanager/vreplication/replicator_plan.go b/go/vt/vttablet/tabletmanager/vreplication/replicator_plan.go index 6a416cb4414..62d6166b5ca 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/replicator_plan.go +++ b/go/vt/vttablet/tabletmanager/vreplication/replicator_plan.go @@ -618,49 +618,40 @@ func valsEqual(v1, v2 sqltypes.Value) bool { func (tp *TablePlan) appendFromRow(buf *bytes2.Buffer, row *querypb.Row) error { bindLocations := tp.BulkInsertValues.BindLocations() if len(tp.Fields) < len(bindLocations) { - return vterrors.Errorf(vtrpcpb.Code_INTERNAL, "wrong number of fields: got %d fields for %d bind locations ", + return vterrors.Errorf(vtrpcpb.Code_INTERNAL, "wrong number of fields: got %d fields for %d bind locations", len(tp.Fields), len(bindLocations)) } - type colInfo struct { - typ querypb.Type - length int64 - offset int64 - field *querypb.Field - } - rowInfo := make([]*colInfo, 0) - - offset := int64(0) - for i, field := range tp.Fields { // collect info required for fields to be bound - length := row.Lengths[i] - if !tp.FieldsToSkip[strings.ToLower(field.Name)] { - rowInfo = append(rowInfo, &colInfo{ - typ: field.Type, - length: length, - offset: offset, - field: field, - }) - } - if length > 0 { - offset += row.Lengths[i] + // Bind field values to locations. + var ( + offset int64 + offsetQuery int + fieldsIndex int + field *querypb.Field + ) + for i, loc := range bindLocations { + field = tp.Fields[fieldsIndex] + length := row.Lengths[fieldsIndex] + for tp.FieldsToSkip[strings.ToLower(field.Name)] { + if length > 0 { + offset += length + } + fieldsIndex++ + field = tp.Fields[fieldsIndex] + length = row.Lengths[fieldsIndex] } - } - // bind field values to locations - var offsetQuery int - for i, loc := range bindLocations { - col := rowInfo[i] buf.WriteString(tp.BulkInsertValues.Query[offsetQuery:loc.Offset]) - typ := col.typ + typ := field.Type 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 + if 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] + buf2 := row.Values[offset : offset+length] vv, err := vjson.MarshalSQLValue(buf2) if err != nil { return err @@ -668,16 +659,16 @@ func (tp *TablePlan) appendFromRow(buf *bytes2.Buffer, row *querypb.Row) error { buf.WriteString(vv.RawStr()) } default: - if col.length < 0 { + if length < 0 { // -1 means a null variable; serialize it directly buf.WriteString(sqltypes.NullStr) } else { - raw := row.Values[col.offset : col.offset+col.length] + raw := row.Values[offset : offset+length] var vv sqltypes.Value - if conversion, ok := tp.ConvertCharset[col.field.Name]; ok && col.length > 0 { + if conversion, ok := tp.ConvertCharset[field.Name]; ok && length > 0 { // Non-null string value, for which we have a charset conversion instruction - out, err := tp.convertStringCharset(raw, conversion, col.field.Name) + out, err := tp.convertStringCharset(raw, conversion, field.Name) if err != nil { return err } @@ -690,6 +681,10 @@ func (tp *TablePlan) appendFromRow(buf *bytes2.Buffer, row *querypb.Row) error { } } offsetQuery = loc.Offset + loc.Length + if length > 0 { + offset += length + } + fieldsIndex++ } buf.WriteString(tp.BulkInsertValues.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 644b4585914..09ace916f11 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/replicator_plan_test.go +++ b/go/vt/vttablet/tabletmanager/vreplication/replicator_plan_test.go @@ -21,17 +21,18 @@ import ( "strings" "testing" - vttablet "vitess.io/vitess/go/vt/vttablet/common" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "vitess.io/vitess/go/bytes2" "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" + querypb "vitess.io/vitess/go/vt/proto/query" + vttablet "vitess.io/vitess/go/vt/vttablet/common" ) type TestReplicatorPlan struct { @@ -829,3 +830,137 @@ func TestBuildPlayerPlanExclude(t *testing.T) { wantPlan, _ := json.Marshal(want) assert.Equal(t, string(gotPlan), string(wantPlan)) } + +func TestAppendFromRow(t *testing.T) { + testCases := []struct { + name string + tp *TablePlan + row *querypb.Row + want string + wantErr string + }{ + { + name: "simple", + tp: &TablePlan{ + BulkInsertValues: sqlparser.BuildParsedQuery("values (%a, %a, %a)", + ":c1", ":c2", ":c3", + ), + Fields: []*querypb.Field{ + {Name: "c1", Type: querypb.Type_INT32}, + {Name: "c2", Type: querypb.Type_INT32}, + {Name: "c3", Type: querypb.Type_INT32}, + }, + }, + row: sqltypes.RowToProto3( + []sqltypes.Value{ + sqltypes.NewInt64(1), + sqltypes.NewInt64(2), + sqltypes.NewInt64(3), + }, + ), + want: "values (1, 2, 3)", + }, + { + name: "too few fields", + tp: &TablePlan{ + BulkInsertValues: sqlparser.BuildParsedQuery("values (%a, %a, %a)", + ":c1", ":c2", ":c3", + ), + Fields: []*querypb.Field{ + {Name: "c1", Type: querypb.Type_INT32}, + {Name: "c2", Type: querypb.Type_INT32}, + }, + }, + wantErr: "wrong number of fields: got 2 fields for 3 bind locations", + }, + { + name: "skip half", + tp: &TablePlan{ + BulkInsertValues: sqlparser.BuildParsedQuery("values (%a, %a, %a, %a)", + ":c1", ":c2", ":c4", ":c8", + ), + Fields: []*querypb.Field{ + {Name: "c1", Type: querypb.Type_INT32}, + {Name: "c2", Type: querypb.Type_INT32}, + {Name: "c3", Type: querypb.Type_INT32}, + {Name: "c4", Type: querypb.Type_INT32}, + {Name: "c5", Type: querypb.Type_INT32}, + {Name: "c6", Type: querypb.Type_INT32}, + {Name: "c7", Type: querypb.Type_INT32}, + {Name: "c8", Type: querypb.Type_INT32}, + }, + FieldsToSkip: map[string]bool{ + "c3": true, + "c5": true, + "c6": true, + "c7": true, + }, + }, + row: sqltypes.RowToProto3( + []sqltypes.Value{ + sqltypes.NewInt64(1), + sqltypes.NewInt64(2), + sqltypes.NewInt64(3), + sqltypes.NewInt64(4), + sqltypes.NewInt64(5), + sqltypes.NewInt64(6), + sqltypes.NewInt64(7), + sqltypes.NewInt64(8), + }, + ), + want: "values (1, 2, 4, 8)", + }, + { + name: "skip all but one", + tp: &TablePlan{ + BulkInsertValues: sqlparser.BuildParsedQuery("values (%a)", + ":c4", + ), + Fields: []*querypb.Field{ + {Name: "c1", Type: querypb.Type_INT32}, + {Name: "c2", Type: querypb.Type_INT32}, + {Name: "c3", Type: querypb.Type_INT32}, + {Name: "c4", Type: querypb.Type_INT32}, + {Name: "c5", Type: querypb.Type_INT32}, + {Name: "c6", Type: querypb.Type_INT32}, + {Name: "c7", Type: querypb.Type_INT32}, + {Name: "c8", Type: querypb.Type_INT32}, + }, + FieldsToSkip: map[string]bool{ + "c1": true, + "c2": true, + "c3": true, + "c5": true, + "c6": true, + "c7": true, + "c8": true, + }, + }, + row: sqltypes.RowToProto3( + []sqltypes.Value{ + sqltypes.NewInt64(1), + sqltypes.NewInt64(2), + sqltypes.NewInt64(3), + sqltypes.NewInt64(4), + sqltypes.NewInt64(5), + sqltypes.NewInt64(6), + sqltypes.NewInt64(7), + sqltypes.NewInt64(8), + }, + ), + want: "values (4)", + }, + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + bb := &bytes2.Buffer{} + err := tc.tp.appendFromRow(bb, tc.row) + if tc.wantErr != "" { + require.EqualError(t, err, tc.wantErr) + } else { + require.NoError(t, err) + require.Equal(t, tc.want, bb.String()) + } + }) + } +} diff --git a/go/vt/vttablet/tabletmanager/vreplication/utils.go b/go/vt/vttablet/tabletmanager/vreplication/utils.go index bb1c469cc93..67b52c56261 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/utils.go +++ b/go/vt/vttablet/tabletmanager/vreplication/utils.go @@ -232,6 +232,24 @@ func isUnrecoverableError(err error) bool { sqlerror.ERWrongValueCountOnRow: log.Errorf("Got unrecoverable error: %v", sqlErr) return true + case sqlerror.ERErrorDuringCommit: + switch sqlErr.HaErrorCode() { + case + 0, // Not really a HA error. + sqlerror.HaErrLockDeadlock, + sqlerror.HaErrLockTableFull, + sqlerror.HaErrLockWaitTimeout, + sqlerror.HaErrNotInLockPartitions, + sqlerror.HaErrQueryInterrupted, + sqlerror.HaErrRolledBack, + sqlerror.HaErrTooManyConcurrentTrxs, + sqlerror.HaErrUndoRecTooBig: + // These are recoverable errors. + return false + default: + log.Errorf("Got unrecoverable error: %v", sqlErr) + return true + } } return false } diff --git a/go/vt/vttablet/tabletmanager/vreplication/utils_test.go b/go/vt/vttablet/tabletmanager/vreplication/utils_test.go index 69a57c34341..15093e299fc 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/utils_test.go +++ b/go/vt/vttablet/tabletmanager/vreplication/utils_test.go @@ -152,6 +152,16 @@ func TestIsUnrecoverableError(t *testing.T) { err: sqlerror.NewSQLError(sqlerror.ERDataOutOfRange, "data out of range", "test"), expected: true, }, + { + name: "SQL error with HaErrDiskFullNowait error", + err: sqlerror.NewSQLError(sqlerror.ERErrorDuringCommit, "unknown", "ERROR HY000: Got error 204 - 'No more room in disk' during COMMIT"), + expected: true, + }, + { + name: "SQL error with HaErrLockDeadlock error", + err: sqlerror.NewSQLError(sqlerror.ERErrorDuringCommit, "unknown", "ERROR HY000: Got error 149 - 'Lock deadlock; Retry transaction' during COMMIT"), + expected: false, + }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { diff --git a/go/vt/vttablet/tabletmanager/vreplication/vcopier_test.go b/go/vt/vttablet/tabletmanager/vreplication/vcopier_test.go index a95e0bf17c5..a7e4794ba76 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/vcopier_test.go +++ b/go/vt/vttablet/tabletmanager/vreplication/vcopier_test.go @@ -684,6 +684,14 @@ func testPlayerCopyBigTable(t *testing.T) { reset := vstreamer.AdjustPacketSize(1) defer reset() + // The test is written to match the behavior w/o + // VReplicationExperimentalFlagOptimizeInserts enabled. + origExperimentalFlags := vttablet.DefaultVReplicationConfig.ExperimentalFlags + vttablet.DefaultVReplicationConfig.ExperimentalFlags = 0 + defer func() { + vttablet.DefaultVReplicationConfig.ExperimentalFlags = origExperimentalFlags + }() + savedCopyPhaseDuration := vttablet.DefaultVReplicationConfig.CopyPhaseDuration // copyPhaseDuration should be low enough to have time to send one row. vttablet.DefaultVReplicationConfig.CopyPhaseDuration = 500 * time.Millisecond @@ -814,6 +822,14 @@ func testPlayerCopyWildcardRule(t *testing.T) { reset := vstreamer.AdjustPacketSize(1) defer reset() + // The test is written to match the behavior w/o + // VReplicationExperimentalFlagOptimizeInserts enabled. + origExperimentalFlags := vttablet.DefaultVReplicationConfig.ExperimentalFlags + vttablet.DefaultVReplicationConfig.ExperimentalFlags = 0 + defer func() { + vttablet.DefaultVReplicationConfig.ExperimentalFlags = origExperimentalFlags + }() + savedCopyPhaseDuration := vttablet.DefaultVReplicationConfig.CopyPhaseDuration // copyPhaseDuration should be low enough to have time to send one row. vttablet.DefaultVReplicationConfig.CopyPhaseDuration = 500 * time.Millisecond diff --git a/go/vt/vttablet/tabletmanager/vreplication/vdbclient.go b/go/vt/vttablet/tabletmanager/vreplication/vdbclient.go index b8339cdf874..63538be881d 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/vdbclient.go +++ b/go/vt/vttablet/tabletmanager/vreplication/vdbclient.go @@ -30,6 +30,8 @@ import ( "vitess.io/vitess/go/vt/vterrors" ) +const beginStmtLen = int64(len("begin;")) + // vdbClient is a wrapper on binlogplayer.DBClient. // It allows us to retry a failed transactions on lock errors. type vdbClient struct { @@ -56,16 +58,19 @@ func (vc *vdbClient) Begin() error { if vc.InTransaction { return nil } - if err := vc.DBClient.Begin(); err != nil { - return err + if vc.maxBatchSize > 0 { + // We are batching the contents of the transaction, which + // starts with the BEGIN and ends with the COMMIT, so we + // do not send a BEGIN down the wire ahead of time. + vc.queriesPos = int64(len(vc.queries)) + vc.batchSize = beginStmtLen + } else { + // We're not batching so we start the transaction here + // by sending the BEGIN down the wire. + 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() @@ -171,7 +176,7 @@ func (vc *vdbClient) Execute(query string) (*sqltypes.Result, error) { func (vc *vdbClient) ExecuteWithRetry(ctx context.Context, query string) (*sqltypes.Result, error) { qr, err := vc.Execute(query) for err != nil { - if sqlErr, ok := err.(*sqlerror.SQLError); ok && sqlErr.Number() == sqlerror.ERLockDeadlock || sqlErr.Number() == sqlerror.ERLockWaitTimeout { + if sqlErr, ok := err.(*sqlerror.SQLError); ok && (sqlErr.Number() == sqlerror.ERLockDeadlock || sqlErr.Number() == sqlerror.ERLockWaitTimeout) { log.Infof("retryable error: %v, waiting for %v and retrying", sqlErr, dbLockRetryDelay) if err := vc.Rollback(); err != nil { return nil, err diff --git a/go/vt/vttablet/tabletmanager/vreplication/vplayer.go b/go/vt/vttablet/tabletmanager/vreplication/vplayer.go index db2f3f341ac..98e36119622 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/vplayer.go +++ b/go/vt/vttablet/tabletmanager/vreplication/vplayer.go @@ -133,7 +133,8 @@ func newVPlayer(vr *vreplicator, settings binlogplayer.VRSettings, copyState map return vr.dbClient.Commit() } batchMode := false - if vr.workflowConfig.ExperimentalFlags&vttablet.VReplicationExperimentalFlagVPlayerBatching != 0 { + // We only do batching in the running/replicating phase. + if len(copyState) == 0 && vr.workflowConfig.ExperimentalFlags&vttablet.VReplicationExperimentalFlagVPlayerBatching != 0 { batchMode = true } if batchMode { diff --git a/go/vt/vttablet/tabletmanager/vreplication/vplayer_flaky_test.go b/go/vt/vttablet/tabletmanager/vreplication/vplayer_flaky_test.go index 0cc568c1cf1..50d93e60e5a 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/vplayer_flaky_test.go +++ b/go/vt/vttablet/tabletmanager/vreplication/vplayer_flaky_test.go @@ -628,7 +628,6 @@ func TestPlayerStatementModeWithFilterAndErrorHandling(t *testing.T) { // It does not work when filter is enabled output := qh.Expect( - "begin", "rollback", fmt.Sprintf("/update _vt.vreplication set message='%s", expectedMsg), ) @@ -975,8 +974,7 @@ func TestPlayerFilters(t *testing.T) { input: "insert into src4 values (1,100,'aaa'),(2,200,'bbb'),(3,100,'ccc')", output: qh.Expect( "begin", - "insert into dst4(id1,val) values (1,_binary'aaa')", - "insert into dst4(id1,val) values (3,_binary'ccc')", + "insert into dst4(id1,val) values (1,_binary'aaa'), (3,_binary'ccc')", "/update _vt.vreplication set pos=", "commit", ), @@ -987,8 +985,7 @@ func TestPlayerFilters(t *testing.T) { input: "insert into src5 values (1,100,'abc'),(2,200,'xyz'),(3,100,'xyz'),(4,300,'abc'),(5,200,'xyz')", output: qh.Expect( "begin", - "insert into dst5(id1,val) values (1,_binary'abc')", - "insert into dst5(id1,val) values (4,_binary'abc')", + "insert into dst5(id1,val) values (1,_binary'abc'), (4,_binary'abc')", "/update _vt.vreplication set pos=", "commit", ), @@ -1495,9 +1492,7 @@ func TestPlayerRowMove(t *testing.T) { }) expectDBClientQueries(t, qh.Expect( "begin", - "insert into dst(val1,sval2,rcount) values (1,ifnull(1, 0),1) on duplicate key update sval2=sval2+ifnull(values(sval2), 0), rcount=rcount+1", - "insert into dst(val1,sval2,rcount) values (2,ifnull(2, 0),1) on duplicate key update sval2=sval2+ifnull(values(sval2), 0), rcount=rcount+1", - "insert into dst(val1,sval2,rcount) values (2,ifnull(3, 0),1) on duplicate key update sval2=sval2+ifnull(values(sval2), 0), rcount=rcount+1", + "insert into dst(val1,sval2,rcount) values (1,ifnull(1, 0),1), (2,ifnull(2, 0),1), (2,ifnull(3, 0),1) on duplicate key update sval2=sval2+ifnull(values(sval2), 0), rcount=rcount+1", "/update _vt.vreplication set pos=", "commit", )) @@ -1505,7 +1500,7 @@ func TestPlayerRowMove(t *testing.T) { {"1", "1", "1"}, {"2", "5", "2"}, }) - validateQueryCountStat(t, "replicate", 3) + validateQueryCountStat(t, "replicate", 1) execStatements(t, []string{ "update src set val1=1, val2=4 where id=3", @@ -1521,7 +1516,7 @@ func TestPlayerRowMove(t *testing.T) { {"1", "5", "2"}, {"2", "2", "1"}, }) - validateQueryCountStat(t, "replicate", 5) + validateQueryCountStat(t, "replicate", 3) } func TestPlayerTypes(t *testing.T) { @@ -2179,6 +2174,14 @@ func TestPlayerSplitTransaction(t *testing.T) { func TestPlayerLockErrors(t *testing.T) { defer deleteTablet(addTablet(100)) + // The immediate retry behavior does not apply when doing + // VPlayer Batching. + origExperimentalFlags := vttablet.DefaultVReplicationConfig.ExperimentalFlags + vttablet.DefaultVReplicationConfig.ExperimentalFlags = 0 + defer func() { + vttablet.DefaultVReplicationConfig.ExperimentalFlags = origExperimentalFlags + }() + execStatements(t, []string{ "create table t1(id int, val varchar(128), primary key(id))", fmt.Sprintf("create table %s.t1(id int, val varchar(128), primary key(id))", vrepldb), @@ -2258,6 +2261,14 @@ func TestPlayerLockErrors(t *testing.T) { func TestPlayerCancelOnLock(t *testing.T) { defer deleteTablet(addTablet(100)) + // The immediate retry behavior does not apply when doing + // VPlayer Batching. + origExperimentalFlags := vttablet.DefaultVReplicationConfig.ExperimentalFlags + vttablet.DefaultVReplicationConfig.ExperimentalFlags = 0 + defer func() { + vttablet.DefaultVReplicationConfig.ExperimentalFlags = origExperimentalFlags + }() + execStatements(t, []string{ "create table t1(id int, val varchar(128), primary key(id))", fmt.Sprintf("create table %s.t1(id int, val varchar(128), primary key(id))", vrepldb), diff --git a/go/vt/vttablet/tabletmanager/vreplication/vreplicator.go b/go/vt/vttablet/tabletmanager/vreplication/vreplicator.go index 0c5c0b5b334..42701288a44 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/vreplicator.go +++ b/go/vt/vttablet/tabletmanager/vreplication/vreplicator.go @@ -186,11 +186,18 @@ func newVReplicator(id int32, source *binlogdatapb.BinlogSource, sourceVStreamer // code. func (vr *vreplicator) Replicate(ctx context.Context) error { err := vr.replicate(ctx) - if err != nil { - if err := vr.setMessage(err.Error()); err != nil { - binlogplayer.LogError("Failed to set error state", err) + if err == nil { + return nil + } + if vr.dbClient.IsClosed() { + // Connection was possible terminated by the server. We should renew it. + if cerr := vr.dbClient.Connect(); cerr != nil { + return vterrors.Wrapf(err, "failed to reconnect to the database: %v", cerr) } } + if err := vr.setMessage(err.Error()); err != nil { + binlogplayer.LogError("Failed to set error state", err) + } return err } @@ -501,8 +508,14 @@ func (vr *vreplicator) setState(state binlogdatapb.VReplicationWorkflowState, me } vr.stats.State.Store(state.String()) query := fmt.Sprintf("update _vt.vreplication set state='%v', message=%v where id=%v", state, encodeString(binlogplayer.MessageTruncate(message)), vr.id) - if _, err := vr.dbClient.ExecuteFetch(query, 1); err != nil { - return fmt.Errorf("could not set state: %v: %v", query, err) + // If we're batching a transaction, then include the state update + // in the current transaction batch. + if vr.dbClient.InTransaction && vr.dbClient.maxBatchSize > 0 { + vr.dbClient.AddQueryToTrxBatch(query) + } else { // Otherwise, send it down the wire + if _, err := vr.dbClient.ExecuteFetch(query, 1); err != nil { + return fmt.Errorf("could not set state: %v: %v", query, err) + } } if state == vr.state { return nil diff --git a/go/vt/vttablet/tabletserver/debugenv.go b/go/vt/vttablet/tabletserver/debugenv.go index 54cf09db7d6..6f1ea854ea9 100644 --- a/go/vt/vttablet/tabletserver/debugenv.go +++ b/go/vt/vttablet/tabletserver/debugenv.go @@ -23,9 +23,10 @@ import ( "html" "net/http" "strconv" - "text/template" "time" + "github.com/google/safehtml/template" + "vitess.io/vitess/go/acl" "vitess.io/vitess/go/vt/log" ) @@ -70,90 +71,131 @@ func debugEnvHandler(tsv *TabletServer, w http.ResponseWriter, r *http.Request) return } + switch r.Method { + case http.MethodPost: + handlePost(tsv, w, r) + case http.MethodGet: + handleGet(tsv, w, r) + default: + http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) + } +} + +func handlePost(tsv *TabletServer, w http.ResponseWriter, r *http.Request) { + varname := r.FormValue("varname") + value := r.FormValue("value") + var msg string - if r.Method == "POST" { - varname := r.FormValue("varname") - value := r.FormValue("value") - setIntVal := func(f func(int)) { - ival, err := strconv.Atoi(value) - if err != nil { - msg = fmt.Sprintf("Failed setting value for %v: %v", varname, err) - return - } - f(ival) - msg = fmt.Sprintf("Setting %v to: %v", varname, value) + if varname == "" || value == "" { + http.Error(w, "Missing varname or value", http.StatusBadRequest) + return + } + + setIntVal := func(f func(int)) error { + ival, err := strconv.Atoi(value) + if err != nil { + return fmt.Errorf("invalid int value for %v: %v", varname, err) } - setIntValCtx := func(f func(context.Context, int) error) { - ival, err := strconv.Atoi(value) - if err == nil { - err = f(r.Context(), ival) - if err == nil { - msg = fmt.Sprintf("Setting %v to: %v", varname, value) - return - } - } - msg = fmt.Sprintf("Failed setting value for %v: %v", varname, err) + f(ival) + msg = fmt.Sprintf("Setting %v to: %v", varname, value) + return nil + } + + setIntValCtx := func(f func(context.Context, int) error) error { + ival, err := strconv.Atoi(value) + if err == nil { + err = f(r.Context(), ival) } - setInt64Val := func(f func(int64)) { - ival, err := strconv.ParseInt(value, 10, 64) - if err != nil { - msg = fmt.Sprintf("Failed setting value for %v: %v", varname, err) - return - } - f(ival) - msg = fmt.Sprintf("Setting %v to: %v", varname, value) + if err != nil { + return fmt.Errorf("failed setting value for %v: %v", varname, err) } - setDurationVal := func(f func(time.Duration)) { - durationVal, err := time.ParseDuration(value) - if err != nil { - msg = fmt.Sprintf("Failed setting value for %v: %v", varname, err) - return - } - f(durationVal) - msg = fmt.Sprintf("Setting %v to: %v", varname, value) + msg = fmt.Sprintf("Setting %v to: %v", varname, value) + return nil + } + + setInt64Val := func(f func(int64)) error { + ival, err := strconv.ParseInt(value, 10, 64) + if err != nil { + return fmt.Errorf("invalid int64 value for %v: %v", varname, err) } - setFloat64Val := func(f func(float64)) { - fval, err := strconv.ParseFloat(value, 64) - if err != nil { - msg = fmt.Sprintf("Failed setting value for %v: %v", varname, err) - return - } - f(fval) - msg = fmt.Sprintf("Setting %v to: %v", varname, value) + f(ival) + msg = fmt.Sprintf("Setting %v to: %v", varname, value) + return nil + } + + setDurationVal := func(f func(time.Duration)) error { + durationVal, err := time.ParseDuration(value) + if err != nil { + return fmt.Errorf("invalid duration value for %v: %v", varname, err) } - switch varname { - case "PoolSize": - setIntValCtx(tsv.SetPoolSize) - case "StreamPoolSize": - setIntValCtx(tsv.SetStreamPoolSize) - case "TxPoolSize": - setIntValCtx(tsv.SetTxPoolSize) - case "MaxResultSize": - setIntVal(tsv.SetMaxResultSize) - case "WarnResultSize": - setIntVal(tsv.SetWarnResultSize) - case "RowStreamerMaxInnoDBTrxHistLen": - setInt64Val(func(val int64) { tsv.Config().RowStreamer.MaxInnoDBTrxHistLen = val }) - case "RowStreamerMaxMySQLReplLagSecs": - setInt64Val(func(val int64) { tsv.Config().RowStreamer.MaxMySQLReplLagSecs = val }) - case "UnhealthyThreshold": - setDurationVal(func(d time.Duration) { tsv.Config().Healthcheck.UnhealthyThreshold = d }) - setDurationVal(tsv.hs.SetUnhealthyThreshold) - setDurationVal(tsv.sm.SetUnhealthyThreshold) - case "ThrottleMetricThreshold": - setFloat64Val(tsv.SetThrottleMetricThreshold) - case "Consolidator": - tsv.SetConsolidatorMode(value) - msg = fmt.Sprintf("Setting %v to: %v", varname, value) + f(durationVal) + msg = fmt.Sprintf("Setting %v to: %v", varname, value) + return nil + } + + setFloat64Val := func(f func(float64)) error { + fval, err := strconv.ParseFloat(value, 64) + if err != nil { + return fmt.Errorf("invalid float64 value for %v: %v", varname, err) } + f(fval) + msg = fmt.Sprintf("Setting %v to: %v", varname, value) + return nil + } + + var err error + switch varname { + case "ReadPoolSize": + err = setIntValCtx(tsv.SetPoolSize) + case "StreamPoolSize": + err = setIntValCtx(tsv.SetStreamPoolSize) + case "TransactionPoolSize": + err = setIntValCtx(tsv.SetTxPoolSize) + case "MaxResultSize": + err = setIntVal(tsv.SetMaxResultSize) + case "WarnResultSize": + err = setIntVal(tsv.SetWarnResultSize) + case "RowStreamerMaxInnoDBTrxHistLen": + err = setInt64Val(func(val int64) { tsv.Config().RowStreamer.MaxInnoDBTrxHistLen = val }) + case "RowStreamerMaxMySQLReplLagSecs": + err = setInt64Val(func(val int64) { tsv.Config().RowStreamer.MaxMySQLReplLagSecs = val }) + case "UnhealthyThreshold": + err = setDurationVal(func(d time.Duration) { tsv.Config().Healthcheck.UnhealthyThreshold = d }) + case "ThrottleMetricThreshold": + err = setFloat64Val(tsv.SetThrottleMetricThreshold) + case "Consolidator": + tsv.SetConsolidatorMode(value) + msg = fmt.Sprintf("Setting %v to: %v", varname, value) + } + + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return } + vars := getVars(tsv) + sendResponse(r, w, vars, msg) +} + +func handleGet(tsv *TabletServer, w http.ResponseWriter, r *http.Request) { + vars := getVars(tsv) + sendResponse(r, w, vars, "") +} + +func sendResponse(r *http.Request, w http.ResponseWriter, vars []envValue, msg string) { + format := r.FormValue("format") + if format == "json" { + respondWithJSON(w, vars, msg) + return + } + respondWithHTML(w, vars, msg) +} + +func getVars(tsv *TabletServer) []envValue { var vars []envValue - vars = addVar(vars, "PoolSize", tsv.PoolSize) + vars = addVar(vars, "ReadPoolSize", tsv.PoolSize) vars = addVar(vars, "StreamPoolSize", tsv.StreamPoolSize) - vars = addVar(vars, "TxPoolSize", tsv.TxPoolSize) - vars = addVar(vars, "QueryCacheCapacity", tsv.QueryPlanCacheCap) // QueryCacheCapacity is deprecated in v21, it is replaced by QueryEnginePlanCacheCapacity - vars = addVar(vars, "QueryEnginePlanCacheCapacity", tsv.QueryPlanCacheCap) + vars = addVar(vars, "TransactionPoolSize", tsv.TxPoolSize) vars = addVar(vars, "MaxResultSize", tsv.MaxResultSize) vars = addVar(vars, "WarnResultSize", tsv.WarnResultSize) vars = addVar(vars, "RowStreamerMaxInnoDBTrxHistLen", func() int64 { return tsv.Config().RowStreamer.MaxInnoDBTrxHistLen }) @@ -165,18 +207,22 @@ func debugEnvHandler(tsv *TabletServer, w http.ResponseWriter, r *http.Request) Value: tsv.ConsolidatorMode(), }) - format := r.FormValue("format") - if format == "json" { - mvars := make(map[string]string) - for _, v := range vars { - mvars[v.Name] = v.Value - } - w.Header().Set("Content-Type", "application/json") - _ = json.NewEncoder(w).Encode(mvars) - return + return vars +} + +func respondWithJSON(w http.ResponseWriter, vars []envValue, msg string) { + mvars := make(map[string]string) + for _, v := range vars { + mvars[v.Name] = v.Value } + if msg != "" { + mvars["ResponseMessage"] = msg + } + w.Header().Set("Content-Type", "application/json") + _ = json.NewEncoder(w).Encode(mvars) +} - // gridTable is reused from twopcz.go. +func respondWithHTML(w http.ResponseWriter, vars []envValue, msg string) { w.Write(gridTable) w.Write([]byte("

Internal Variables

\n")) if msg != "" { diff --git a/go/vt/vttablet/tabletserver/dt_executor_test.go b/go/vt/vttablet/tabletserver/dt_executor_test.go index d5322f352f9..b21667392d6 100644 --- a/go/vt/vttablet/tabletserver/dt_executor_test.go +++ b/go/vt/vttablet/tabletserver/dt_executor_test.go @@ -705,8 +705,10 @@ func TestNoTwopc(t *testing.T) { want := "2pc is not enabled" for _, tc := range testcases { - err := tc.fun() - require.EqualError(t, err, want) + t.Run(tc.desc, func(t *testing.T) { + err := tc.fun() + require.EqualError(t, err, want) + }) } } diff --git a/go/vt/vttablet/tabletserver/health_streamer.go b/go/vt/vttablet/tabletserver/health_streamer.go index f9f65d197b2..eaeba6315e3 100644 --- a/go/vt/vttablet/tabletserver/health_streamer.go +++ b/go/vt/vttablet/tabletserver/health_streamer.go @@ -293,8 +293,10 @@ func (hs *healthStreamer) SetUnhealthyThreshold(v time.Duration) { // so it can read and write to the MySQL instance for schema-tracking. func (hs *healthStreamer) MakePrimary(serving bool) { hs.fieldsMu.Lock() - defer hs.fieldsMu.Unlock() hs.isServingPrimary = serving + // We let go of the lock here because we don't want to hold the lock when calling RegisterNotifier. + // If we keep holding the lock, there is a potential deadlock that can happen. + hs.fieldsMu.Unlock() // We register for notifications from the schema Engine only when schema tracking is enabled, // and we are going to a serving primary state. if serving && hs.signalWhenSchemaChange { diff --git a/go/vt/vttablet/tabletserver/health_streamer_test.go b/go/vt/vttablet/tabletserver/health_streamer_test.go index 3421141ff80..9561518eed6 100644 --- a/go/vt/vttablet/tabletserver/health_streamer_test.go +++ b/go/vt/vttablet/tabletserver/health_streamer_test.go @@ -592,13 +592,14 @@ func TestDeadlockBwCloseAndReload(t *testing.T) { wg := sync.WaitGroup{} wg.Add(2) - // Try running Close and reload in parallel multiple times. + // Try running Close & MakePrimary and reload in parallel multiple times. // This reproduces the deadlock quite readily. go func() { defer wg.Done() for i := 0; i < 100; i++ { hs.Close() hs.Open() + hs.MakePrimary(true) } }() diff --git a/go/vt/vttablet/tabletserver/query_executor.go b/go/vt/vttablet/tabletserver/query_executor.go index abf296c0583..519b60b79d6 100644 --- a/go/vt/vttablet/tabletserver/query_executor.go +++ b/go/vt/vttablet/tabletserver/query_executor.go @@ -991,6 +991,8 @@ func (qre *QueryExecutor) execAlterMigration() (*sqltypes.Result, error) { return qre.tsv.onlineDDLExecutor.ForceCutOverMigration(qre.ctx, alterMigration.UUID) case sqlparser.ForceCutOverAllMigrationType: return qre.tsv.onlineDDLExecutor.ForceCutOverPendingMigrations(qre.ctx) + case sqlparser.SetCutOverThresholdMigrationType: + return qre.tsv.onlineDDLExecutor.SetMigrationCutOverThreshold(qre.ctx, alterMigration.UUID, alterMigration.Threshold) } return nil, vterrors.New(vtrpcpb.Code_UNIMPLEMENTED, "ALTER VITESS_MIGRATION not implemented") } diff --git a/go/vt/vttablet/tabletserver/query_executor_test.go b/go/vt/vttablet/tabletserver/query_executor_test.go index 78daad2e616..ade79ecaef5 100644 --- a/go/vt/vttablet/tabletserver/query_executor_test.go +++ b/go/vt/vttablet/tabletserver/query_executor_test.go @@ -1514,20 +1514,17 @@ func newTestTabletServer(ctx context.Context, flags executorFlags, db *fakesqldb } else { cfg.StrictTableACL = false } - if flags&noTwopc > 0 { - cfg.TwoPCEnable = false - } else { - cfg.TwoPCEnable = true - } if flags&disableOnlineDDL > 0 { cfg.EnableOnlineDDL = false } else { cfg.EnableOnlineDDL = true } - if flags&shortTwopcAge > 0 { - cfg.TwoPCAbandonAge = 0.5 + if flags&noTwopc > 0 { + cfg.TwoPCAbandonAge = 0 + } else if flags&shortTwopcAge > 0 { + cfg.TwoPCAbandonAge = 500 * time.Millisecond } else { - cfg.TwoPCAbandonAge = 10 + cfg.TwoPCAbandonAge = 10 * time.Second } if flags&smallResultSize > 0 { cfg.Oltp.MaxRows = 2 diff --git a/go/vt/vttablet/tabletserver/querylogz.go b/go/vt/vttablet/tabletserver/querylogz.go index 33341d1641b..09f375aa329 100644 --- a/go/vt/vttablet/tabletserver/querylogz.go +++ b/go/vt/vttablet/tabletserver/querylogz.go @@ -20,9 +20,10 @@ import ( "net/http" "strconv" "strings" - "text/template" "time" + "github.com/google/safehtml/template" + "vitess.io/vitess/go/acl" "vitess.io/vitess/go/vt/log" "vitess.io/vitess/go/vt/logz" diff --git a/go/vt/vttablet/tabletserver/querylogz_test.go b/go/vt/vttablet/tabletserver/querylogz_test.go index 25f03c762c7..ee26437f330 100644 --- a/go/vt/vttablet/tabletserver/querylogz_test.go +++ b/go/vt/vttablet/tabletserver/querylogz_test.go @@ -37,7 +37,7 @@ func TestQuerylogzHandler(t *testing.T) { req, _ := http.NewRequest("GET", "/querylogz?timeout=10&limit=1", nil) logStats := tabletenv.NewLogStats(context.Background(), "Execute") logStats.PlanType = planbuilder.PlanSelect.String() - logStats.OriginalSQL = "select name from test_table limit 1000" + logStats.OriginalSQL = "select name, 'inject ' from test_table limit 1000" logStats.RowsAffected = 1000 logStats.NumberOfQueries = 1 logStats.StartTime, _ = time.Parse("Jan 2 15:04:05", "Nov 29 13:33:09") @@ -64,7 +64,7 @@ func TestQuerylogzHandler(t *testing.T) { `0.001`, `1e-08`, `Select`, - `select name from test_table limit 1000`, + regexp.QuoteMeta(`select name,​ 'inject <script>alert()​;</script>' from test_table limit 1000`), `1`, `none`, `1000`, @@ -95,7 +95,7 @@ func TestQuerylogzHandler(t *testing.T) { `0.001`, `1e-08`, `Select`, - `select name from test_table limit 1000`, + regexp.QuoteMeta(`select name,​ 'inject <script>alert()​;</script>' from test_table limit 1000`), `1`, `none`, `1000`, @@ -126,7 +126,7 @@ func TestQuerylogzHandler(t *testing.T) { `0.001`, `1e-08`, `Select`, - `select name from test_table limit 1000`, + regexp.QuoteMeta(`select name,​ 'inject <script>alert()​;</script>' from test_table limit 1000`), `1`, `none`, `1000`, diff --git a/go/vt/vttablet/tabletserver/tabletenv/config.go b/go/vt/vttablet/tabletserver/tabletenv/config.go index 158f40d5202..994999f2368 100644 --- a/go/vt/vttablet/tabletserver/tabletenv/config.go +++ b/go/vt/vttablet/tabletserver/tabletenv/config.go @@ -156,8 +156,12 @@ func registerTabletEnvFlags(fs *pflag.FlagSet) { fs.BoolVar(¤tConfig.WatchReplication, "watch_replication_stream", false, "When enabled, vttablet will stream the MySQL replication stream from the local server, and use it to update schema when it sees a DDL.") fs.BoolVar(¤tConfig.TrackSchemaVersions, "track_schema_versions", false, "When enabled, vttablet will store versions of schemas at each position that a DDL is applied and allow retrieval of the schema corresponding to a position") fs.Int64Var(¤tConfig.SchemaVersionMaxAgeSeconds, "schema-version-max-age-seconds", 0, "max age of schema version records to kept in memory by the vreplication historian") - fs.BoolVar(¤tConfig.TwoPCEnable, "twopc_enable", defaultConfig.TwoPCEnable, "if the flag is on, 2pc is enabled. Other 2pc flags must be supplied.") - SecondsVar(fs, ¤tConfig.TwoPCAbandonAge, "twopc_abandon_age", defaultConfig.TwoPCAbandonAge, "time in seconds. Any unresolved transaction older than this time will be sent to the coordinator to be resolved.") + + _ = fs.Bool("twopc_enable", true, "TwoPC is enabled") + _ = fs.MarkDeprecated("twopc_enable", "TwoPC is always enabled, the transaction abandon age can be configured") + flagutil.FloatDuration(fs, ¤tConfig.TwoPCAbandonAge, "twopc_abandon_age", defaultConfig.TwoPCAbandonAge, + "Any unresolved transaction older than this time will be sent to the coordinator to be resolved. NOTE: Providing time as seconds (float64) is deprecated. Use time.Duration format (e.g., '1s', '2m', '1h').") + // Tx throttler config flagutil.DualFormatBoolVar(fs, ¤tConfig.EnableTxThrottler, "enable_tx_throttler", defaultConfig.EnableTxThrottler, "If true replication-lag-based throttling on transactions will be enabled.") flagutil.DualFormatVar(fs, currentConfig.TxThrottlerConfig, "tx_throttler_config", "The configuration of the transaction throttler as a text-formatted throttlerdata.Configuration protocol buffer message.") @@ -331,12 +335,11 @@ type TabletConfig struct { ExternalConnections map[string]*dbconfigs.DBConfigs `json:"externalConnections,omitempty"` - SanitizeLogMessages bool `json:"-"` - StrictTableACL bool `json:"-"` - EnableTableACLDryRun bool `json:"-"` - TableACLExemptACL string `json:"-"` - TwoPCEnable bool `json:"-"` - TwoPCAbandonAge Seconds `json:"-"` + SanitizeLogMessages bool `json:"-"` + StrictTableACL bool `json:"-"` + EnableTableACLDryRun bool `json:"-"` + TableACLExemptACL string `json:"-"` + TwoPCAbandonAge time.Duration `json:"-"` EnableTxThrottler bool `json:"-"` TxThrottlerConfig *TxThrottlerConfigFlag `json:"-"` @@ -1054,6 +1057,8 @@ var defaultConfig = TabletConfig{ }, EnablePerWorkloadTableMetrics: false, + + TwoPCAbandonAge: 15 * time.Minute, } // defaultTxThrottlerConfig returns the default TxThrottlerConfigFlag object based on diff --git a/go/vt/vttablet/tabletserver/tabletenv/seconds.go b/go/vt/vttablet/tabletserver/tabletenv/seconds.go deleted file mode 100644 index ae11121f2de..00000000000 --- a/go/vt/vttablet/tabletserver/tabletenv/seconds.go +++ /dev/null @@ -1,42 +0,0 @@ -/* -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 tabletenv - -import ( - "time" - - "github.com/spf13/pflag" -) - -// Seconds provides convenience functions for extracting -// duration from float64 seconds values. -type Seconds float64 - -// SecondsVar is like a flag.Float64Var, but it works for Seconds. -func SecondsVar(fs *pflag.FlagSet, p *Seconds, name string, value Seconds, usage string) { - fs.Float64Var((*float64)(p), name, float64(value), usage) -} - -// Get converts Seconds to time.Duration -func (s Seconds) Get() time.Duration { - return time.Duration(s * Seconds(1*time.Second)) -} - -// Set sets the value from time.Duration -func (s *Seconds) Set(d time.Duration) { - *s = Seconds(d) / Seconds(1*time.Second) -} diff --git a/go/vt/vttablet/tabletserver/tabletenv/seconds_test.go b/go/vt/vttablet/tabletserver/tabletenv/seconds_test.go deleted file mode 100644 index dc09a3f419f..00000000000 --- a/go/vt/vttablet/tabletserver/tabletenv/seconds_test.go +++ /dev/null @@ -1,53 +0,0 @@ -/* -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 tabletenv - -import ( - "testing" - "time" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "vitess.io/vitess/go/yaml2" -) - -func TestSecondsYaml(t *testing.T) { - type testSecond struct { - Value Seconds `json:"value"` - } - - ts := testSecond{ - Value: 1, - } - gotBytes, err := yaml2.Marshal(&ts) - require.NoError(t, err) - wantBytes := "value: 1\n" - assert.Equal(t, wantBytes, string(gotBytes)) - - var gotts testSecond - err = yaml2.Unmarshal([]byte(wantBytes), &gotts) - require.NoError(t, err) - assert.Equal(t, ts, gotts) -} - -func TestSecondsGetSet(t *testing.T) { - var val Seconds - val.Set(2 * time.Second) - assert.Equal(t, Seconds(2), val) - assert.Equal(t, 2*time.Second, val.Get()) -} diff --git a/go/vt/vttablet/tabletserver/throttle/base/metric_cache.go b/go/vt/vttablet/tabletserver/throttle/base/metric_cache.go index 8695cb83229..faad65ca79e 100644 --- a/go/vt/vttablet/tabletserver/throttle/base/metric_cache.go +++ b/go/vt/vttablet/tabletserver/throttle/base/metric_cache.go @@ -49,6 +49,7 @@ import ( "github.com/patrickmn/go-cache" "vitess.io/vitess/go/stats" + "vitess.io/vitess/go/vt/vttablet/tmclient" ) // MetricsQueryType indicates the type of metrics query on MySQL backend. See following. @@ -142,13 +143,13 @@ func (metric *ThrottleMetric) WithError(err error) *ThrottleMetric { // ReadThrottleMetrics returns a metric for the given probe. Either by explicit query // or via SHOW REPLICA STATUS -func ReadThrottleMetrics(ctx context.Context, probe *Probe, metricsFunc func(context.Context) ThrottleMetrics) ThrottleMetrics { +func ReadThrottleMetrics(ctx context.Context, probe *Probe, tmClient tmclient.TabletManagerClient, metricsFunc func(context.Context, tmclient.TabletManagerClient) ThrottleMetrics) ThrottleMetrics { if metrics := getCachedThrottleMetrics(probe); metrics != nil { return metrics } started := time.Now() - throttleMetrics := metricsFunc(ctx) + throttleMetrics := metricsFunc(ctx, tmClient) go func(metrics ThrottleMetrics, started time.Time) { stats.GetOrNewGauge("ThrottlerProbesLatency", "probes latency").Set(time.Since(started).Nanoseconds()) diff --git a/go/vt/vttablet/tabletserver/throttle/base/metric_name.go b/go/vt/vttablet/tabletserver/throttle/base/metric_name.go index 98e1288fb23..43bd2d17a8c 100644 --- a/go/vt/vttablet/tabletserver/throttle/base/metric_name.go +++ b/go/vt/vttablet/tabletserver/throttle/base/metric_name.go @@ -60,11 +60,14 @@ func (names MetricNames) Unique() MetricNames { } const ( - DefaultMetricName MetricName = "default" - LagMetricName MetricName = "lag" - ThreadsRunningMetricName MetricName = "threads_running" - CustomMetricName MetricName = "custom" - LoadAvgMetricName MetricName = "loadavg" + DefaultMetricName MetricName = "default" + LagMetricName MetricName = "lag" + ThreadsRunningMetricName MetricName = "threads_running" + CustomMetricName MetricName = "custom" + LoadAvgMetricName MetricName = "loadavg" + HistoryListLengthMetricName MetricName = "history_list_length" + MysqldLoadAvgMetricName MetricName = "mysqld-loadavg" + MysqldDatadirUsedRatioMetricName MetricName = "mysqld-datadir-used-ratio" ) func (metric MetricName) DefaultScope() Scope { diff --git a/go/vt/vttablet/tabletserver/throttle/base/metric_name_test.go b/go/vt/vttablet/tabletserver/throttle/base/metric_name_test.go index 9867ca18db3..c2e2b44b36f 100644 --- a/go/vt/vttablet/tabletserver/throttle/base/metric_name_test.go +++ b/go/vt/vttablet/tabletserver/throttle/base/metric_name_test.go @@ -21,6 +21,9 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "vitess.io/vitess/go/textutil" ) func TestAggregateName(t *testing.T) { @@ -238,4 +241,27 @@ func TestKnownMetricNames(t *testing.T) { assert.Contains(t, KnownMetricNames, LoadAvgMetricName) assert.Contains(t, KnownMetricNames, CustomMetricName) assert.Contains(t, KnownMetricNames, DefaultMetricName) + assert.Contains(t, KnownMetricNames, HistoryListLengthMetricName) + assert.Contains(t, KnownMetricNames, MysqldLoadAvgMetricName) + assert.Contains(t, KnownMetricNames, MysqldDatadirUsedRatioMetricName) +} + +func TestKnownMetricNamesPascalCase(t *testing.T) { + expectCases := map[MetricName]string{ + LagMetricName: "Lag", + ThreadsRunningMetricName: "ThreadsRunning", + LoadAvgMetricName: "Loadavg", + HistoryListLengthMetricName: "HistoryListLength", + CustomMetricName: "Custom", + DefaultMetricName: "Default", + MysqldLoadAvgMetricName: "MysqldLoadavg", + MysqldDatadirUsedRatioMetricName: "MysqldDatadirUsedRatio", + } + for _, metricName := range KnownMetricNames { + t.Run(metricName.String(), func(t *testing.T) { + expect, ok := expectCases[metricName] + require.True(t, ok) + assert.Equal(t, expect, textutil.PascalCase(metricName.String())) + }) + } } diff --git a/go/vt/vttablet/tabletserver/throttle/base/self_metric.go b/go/vt/vttablet/tabletserver/throttle/base/self_metric.go index 220dfa6bf60..88fbe2bdd13 100644 --- a/go/vt/vttablet/tabletserver/throttle/base/self_metric.go +++ b/go/vt/vttablet/tabletserver/throttle/base/self_metric.go @@ -21,15 +21,24 @@ import ( "fmt" "strconv" + "vitess.io/vitess/go/vt/topo" "vitess.io/vitess/go/vt/vttablet/tabletserver/connpool" + "vitess.io/vitess/go/vt/vttablet/tmclient" ) +type SelfMetricReadParams struct { + Throttler metricsPublisher + Conn *connpool.Conn + TmClient tmclient.TabletManagerClient + TabletInfo *topo.TabletInfo +} + type SelfMetric interface { Name() MetricName DefaultScope() Scope DefaultThreshold() float64 RequiresConn() bool - Read(ctx context.Context, throttler ThrottlerMetricsPublisher, conn *connpool.Conn) *ThrottleMetric + Read(ctx context.Context, params *SelfMetricReadParams) *ThrottleMetric } var ( diff --git a/go/vt/vttablet/tabletserver/throttle/base/self_metric_custom_query.go b/go/vt/vttablet/tabletserver/throttle/base/self_metric_custom_query.go index 585e63ea285..88f789e5dcd 100644 --- a/go/vt/vttablet/tabletserver/throttle/base/self_metric_custom_query.go +++ b/go/vt/vttablet/tabletserver/throttle/base/self_metric_custom_query.go @@ -18,8 +18,6 @@ package base import ( "context" - - "vitess.io/vitess/go/vt/vttablet/tabletserver/connpool" ) var _ SelfMetric = registerSelfMetric(&CustomQuerySelfMetric{}) @@ -43,6 +41,6 @@ func (m *CustomQuerySelfMetric) RequiresConn() bool { return true } -func (m *CustomQuerySelfMetric) Read(ctx context.Context, throttler ThrottlerMetricsPublisher, conn *connpool.Conn) *ThrottleMetric { - return ReadSelfMySQLThrottleMetric(ctx, conn, throttler.GetCustomMetricsQuery()) +func (m *CustomQuerySelfMetric) Read(ctx context.Context, params *SelfMetricReadParams) *ThrottleMetric { + return ReadSelfMySQLThrottleMetric(ctx, params.Conn, params.Throttler.GetCustomMetricsQuery()) } diff --git a/go/vt/vttablet/tabletserver/throttle/base/self_metric_default.go b/go/vt/vttablet/tabletserver/throttle/base/self_metric_default.go index 8bce295da7c..97309fa6ea9 100644 --- a/go/vt/vttablet/tabletserver/throttle/base/self_metric_default.go +++ b/go/vt/vttablet/tabletserver/throttle/base/self_metric_default.go @@ -19,8 +19,6 @@ package base import ( "context" "fmt" - - "vitess.io/vitess/go/vt/vttablet/tabletserver/connpool" ) var _ SelfMetric = registerSelfMetric(&DefaultSelfMetric{}) @@ -44,7 +42,7 @@ func (m *DefaultSelfMetric) RequiresConn() bool { return false } -func (m *DefaultSelfMetric) Read(ctx context.Context, throttler ThrottlerMetricsPublisher, conn *connpool.Conn) *ThrottleMetric { +func (m *DefaultSelfMetric) Read(ctx context.Context, params *SelfMetricReadParams) *ThrottleMetric { return &ThrottleMetric{ Err: fmt.Errorf("unexpected direct call to DefaultSelfMetric.Read"), } diff --git a/go/vt/vttablet/tabletserver/throttle/base/self_metric_innodb_history_list_length.go b/go/vt/vttablet/tabletserver/throttle/base/self_metric_innodb_history_list_length.go new file mode 100644 index 00000000000..2696b1750ea --- /dev/null +++ b/go/vt/vttablet/tabletserver/throttle/base/self_metric_innodb_history_list_length.go @@ -0,0 +1,68 @@ +/* +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 base + +import ( + "context" + "math" + "sync/atomic" + "time" +) + +var ( + historyListLengthQuery = "select count as history_len from information_schema.INNODB_METRICS where name = 'trx_rseg_history_len'" + + cachedHistoryListLengthMetric atomic.Pointer[ThrottleMetric] + historyListLengthCacheDuration = 5 * time.Second + historyListLengthDefaultThreshold = math.Pow10(9) +) + +var _ SelfMetric = registerSelfMetric(&HistoryListLengthSelfMetric{}) + +type HistoryListLengthSelfMetric struct { +} + +func (m *HistoryListLengthSelfMetric) Name() MetricName { + return HistoryListLengthMetricName +} + +func (m *HistoryListLengthSelfMetric) DefaultScope() Scope { + return SelfScope +} + +func (m *HistoryListLengthSelfMetric) DefaultThreshold() float64 { + return historyListLengthDefaultThreshold +} + +func (m *HistoryListLengthSelfMetric) RequiresConn() bool { + return true +} + +func (m *HistoryListLengthSelfMetric) Read(ctx context.Context, params *SelfMetricReadParams) *ThrottleMetric { + // This function will be called sequentially, and therefore does not need strong mutex protection. Still, we use atomics + // to ensure correctness in case an external goroutine tries to read the metric concurrently. + metric := cachedHistoryListLengthMetric.Load() + if metric != nil { + return metric + } + metric = ReadSelfMySQLThrottleMetric(ctx, params.Conn, historyListLengthQuery) + cachedHistoryListLengthMetric.Store(metric) + time.AfterFunc(historyListLengthCacheDuration, func() { + cachedHistoryListLengthMetric.Store(nil) + }) + return metric +} diff --git a/go/vt/vttablet/tabletserver/throttle/base/self_metric_lag.go b/go/vt/vttablet/tabletserver/throttle/base/self_metric_lag.go index dc25ee5622a..3d0e4beebe1 100644 --- a/go/vt/vttablet/tabletserver/throttle/base/self_metric_lag.go +++ b/go/vt/vttablet/tabletserver/throttle/base/self_metric_lag.go @@ -23,7 +23,6 @@ import ( "vitess.io/vitess/go/constants/sidecar" "vitess.io/vitess/go/vt/sqlparser" - "vitess.io/vitess/go/vt/vttablet/tabletserver/connpool" ) var ( @@ -65,6 +64,6 @@ func (m *LagSelfMetric) RequiresConn() bool { return true } -func (m *LagSelfMetric) Read(ctx context.Context, throttler ThrottlerMetricsPublisher, conn *connpool.Conn) *ThrottleMetric { - return ReadSelfMySQLThrottleMetric(ctx, conn, m.GetQuery()) +func (m *LagSelfMetric) Read(ctx context.Context, params *SelfMetricReadParams) *ThrottleMetric { + return ReadSelfMySQLThrottleMetric(ctx, params.Conn, m.GetQuery()) } diff --git a/go/vt/vttablet/tabletserver/throttle/base/self_metric_loadavg.go b/go/vt/vttablet/tabletserver/throttle/base/self_metric_loadavg.go index 40a2878421a..2d880169020 100644 --- a/go/vt/vttablet/tabletserver/throttle/base/self_metric_loadavg.go +++ b/go/vt/vttablet/tabletserver/throttle/base/self_metric_loadavg.go @@ -18,20 +18,16 @@ package base import ( "context" - "fmt" - "os" "runtime" - "strconv" - "strings" + "sync/atomic" + "time" - "vitess.io/vitess/go/vt/vttablet/tabletserver/connpool" + "vitess.io/vitess/go/osutil" ) var ( - loadavgOnlyAvailableOnLinuxMetric = &ThrottleMetric{ - Scope: SelfScope, - Err: fmt.Errorf("loadavg metric is only available on Linux"), - } + cachedLoadAvgMetric atomic.Pointer[ThrottleMetric] + loadAvgCacheDuration = 1 * time.Second ) var _ SelfMetric = registerSelfMetric(&LoadAvgSelfMetric{}) @@ -55,27 +51,26 @@ func (m *LoadAvgSelfMetric) RequiresConn() bool { return false } -func (m *LoadAvgSelfMetric) Read(ctx context.Context, throttler ThrottlerMetricsPublisher, conn *connpool.Conn) *ThrottleMetric { - if runtime.GOOS != "linux" { - return loadavgOnlyAvailableOnLinuxMetric +func (m *LoadAvgSelfMetric) Read(ctx context.Context, params *SelfMetricReadParams) *ThrottleMetric { + // This function will be called sequentially, and therefore does not need strong mutex protection. Still, we use atomics + // to ensure correctness in case an external goroutine tries to read the metric concurrently. + metric := cachedLoadAvgMetric.Load() + if metric != nil { + return metric } - metric := &ThrottleMetric{ + metric = &ThrottleMetric{ Scope: SelfScope, } - { - content, err := os.ReadFile("/proc/loadavg") - if err != nil { - return metric.WithError(err) - } - fields := strings.Fields(string(content)) - if len(fields) == 0 { - return metric.WithError(fmt.Errorf("unexpected /proc/loadavg content")) - } - loadAvg, err := strconv.ParseFloat(fields[0], 64) - if err != nil { - return metric.WithError(err) - } - metric.Value = loadAvg / float64(runtime.NumCPU()) + val, err := osutil.LoadAvg() + if err != nil { + return metric.WithError(err) } + metric.Value = val / float64(runtime.NumCPU()) + + cachedLoadAvgMetric.Store(metric) + time.AfterFunc(loadAvgCacheDuration, func() { + cachedLoadAvgMetric.Store(nil) + }) + return metric } diff --git a/go/vt/vttablet/tabletserver/throttle/base/self_metric_mysqld.go b/go/vt/vttablet/tabletserver/throttle/base/self_metric_mysqld.go new file mode 100644 index 00000000000..321837d86b4 --- /dev/null +++ b/go/vt/vttablet/tabletserver/throttle/base/self_metric_mysqld.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 base + +import ( + "context" + "errors" + "fmt" + "sync/atomic" + "time" + + "vitess.io/vitess/go/timer" + + tabletmanagerdatapb "vitess.io/vitess/go/vt/proto/tabletmanagerdata" +) + +var ( + mysqlHostMetricsRpcTimeout = 5 * time.Second + mysqlHostMetricsRateLimit = 10 * time.Second + mysqlHostMetricsRateLimiter atomic.Pointer[timer.RateLimiter] + lastMySQLHostMetricsResponse atomic.Pointer[tabletmanagerdatapb.MysqlHostMetricsResponse] +) + +// getMysqlMetricsRateLimiter returns a rate limiter that is active until the given context is cancelled. +// This function will be called sequentially, but nonetheless it offers _some_ concurrent safety. Namely, +// that a created rate limiter is guaranteed to be cleaned up +func getMysqlMetricsRateLimiter(ctx context.Context, rateLimit time.Duration) *timer.RateLimiter { + rateLimiter := mysqlHostMetricsRateLimiter.Load() + if rateLimiter == nil { + rateLimiter = timer.NewRateLimiter(rateLimit) + go func() { + defer mysqlHostMetricsRateLimiter.Store(nil) + defer rateLimiter.Stop() + <-ctx.Done() + }() + mysqlHostMetricsRateLimiter.Store(rateLimiter) + } + return rateLimiter +} + +// readMysqlHostMetrics reads MySQL host metrics sporadically from the tablet manager (which in turn reads +// them from mysql deamon). The metrics are then cached, whether successful or not. +// This idea is that is is very wasteful to read these metrics for every single query. E.g. right now the throttler +// can issue 4 reads per second, which is wasteful to go through two RPCs to get the disk space usage for example. Even the load +// average on the MySQL server is not that susceptible to change. +func readMysqlHostMetrics(ctx context.Context, params *SelfMetricReadParams) error { + if params.TmClient == nil { + return fmt.Errorf("tmClient is nil") + } + if params.TabletInfo == nil { + return fmt.Errorf("tabletInfo is nil") + } + rateLimiter := getMysqlMetricsRateLimiter(ctx, mysqlHostMetricsRateLimit) + err := rateLimiter.Do(func() error { + ctx, cancel := context.WithTimeout(ctx, mysqlHostMetricsRpcTimeout) + defer cancel() + + resp, err := params.TmClient.MysqlHostMetrics(ctx, params.TabletInfo.Tablet, &tabletmanagerdatapb.MysqlHostMetricsRequest{}) + if err != nil { + return err + } + lastMySQLHostMetricsResponse.Store(resp) + return nil + }) + return err +} + +// getMysqlHostMetric gets a metric from the last read MySQL host metrics. The metric will either be directly read from +// tablet manager (which then reads it from the mysql deamon), or from the cache. +func getMysqlHostMetric(ctx context.Context, params *SelfMetricReadParams, mysqlHostMetricName string) *ThrottleMetric { + metric := &ThrottleMetric{ + Scope: SelfScope, + } + if err := readMysqlHostMetrics(ctx, params); err != nil { + return metric.WithError(err) + } + resp := lastMySQLHostMetricsResponse.Load() + if resp == nil { + return metric.WithError(ErrNoResultYet) + } + mysqlMetric := resp.HostMetrics.Metrics[mysqlHostMetricName] + if mysqlMetric == nil { + return metric.WithError(ErrNoSuchMetric) + } + metric.Value = mysqlMetric.Value + if mysqlMetric.Error != nil { + metric.Err = errors.New(mysqlMetric.Error.Message) + } + return metric +} + +var _ SelfMetric = registerSelfMetric(&MysqldLoadAvgSelfMetric{}) +var _ SelfMetric = registerSelfMetric(&MysqldDatadirUsedRatioSelfMetric{}) + +// MysqldLoadAvgSelfMetric stands for the load average per cpu, on the MySQL host. +type MysqldLoadAvgSelfMetric struct { +} + +func (m *MysqldLoadAvgSelfMetric) Name() MetricName { + return MysqldLoadAvgMetricName +} + +func (m *MysqldLoadAvgSelfMetric) DefaultScope() Scope { + return SelfScope +} + +func (m *MysqldLoadAvgSelfMetric) DefaultThreshold() float64 { + return 1.0 +} + +func (m *MysqldLoadAvgSelfMetric) RequiresConn() bool { + return false +} + +func (m *MysqldLoadAvgSelfMetric) Read(ctx context.Context, params *SelfMetricReadParams) *ThrottleMetric { + return getMysqlHostMetric(ctx, params, "loadavg") +} + +// MysqldDatadirUsedRatioSelfMetric stands for the disk space usage of the mount where MySQL's datadir is located. +// Range: 0.0 (empty) - 1.0 (full) +type MysqldDatadirUsedRatioSelfMetric struct { +} + +func (m *MysqldDatadirUsedRatioSelfMetric) Name() MetricName { + return MysqldDatadirUsedRatioMetricName +} + +func (m *MysqldDatadirUsedRatioSelfMetric) DefaultScope() Scope { + return SelfScope +} + +func (m *MysqldDatadirUsedRatioSelfMetric) DefaultThreshold() float64 { + return 0.98 +} + +func (m *MysqldDatadirUsedRatioSelfMetric) RequiresConn() bool { + return false +} + +func (m *MysqldDatadirUsedRatioSelfMetric) Read(ctx context.Context, params *SelfMetricReadParams) *ThrottleMetric { + return getMysqlHostMetric(ctx, params, "datadir-used-ratio") +} diff --git a/go/vt/vttablet/tabletserver/throttle/base/self_metric_mysqld_test.go b/go/vt/vttablet/tabletserver/throttle/base/self_metric_mysqld_test.go new file mode 100644 index 00000000000..39d3f3f5ec2 --- /dev/null +++ b/go/vt/vttablet/tabletserver/throttle/base/self_metric_mysqld_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 base + +import ( + "context" + "fmt" + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +func TestGetMysqlMetricsRateLimiter(t *testing.T) { + rateLimit := 10 * time.Millisecond + for i := range 3 { + testName := fmt.Sprintf("iteration %d", i) + t.Run(testName, func(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + { + rateLimiter := mysqlHostMetricsRateLimiter.Load() + assert.Nil(t, rateLimiter) + } + rateLimiter := getMysqlMetricsRateLimiter(ctx, rateLimit) + assert.NotNil(t, rateLimiter) + for range 5 { + r := getMysqlMetricsRateLimiter(ctx, rateLimit) + // Returning the same rate limiter + assert.Equal(t, rateLimiter, r) + } + val := 0 + incr := func() error { + val++ + return nil + } + for range 10 { + rateLimiter.Do(incr) + time.Sleep(2 * rateLimit) + } + assert.EqualValues(t, 10, val) + cancel() + // There can be a race condition where the rate limiter still emits one final tick after the context is cancelled. + // So we wait enough time to ensure that tick is "wasted". + time.Sleep(2 * rateLimit) + // Now that the rate limited was stopped (we invoked `cancel()`), its `Do()` should not invoke the function anymore. + for range 7 { + rateLimiter.Do(incr) + time.Sleep(time.Millisecond) + } + assert.EqualValues(t, 10, val) // Same "10" value as before. + { + rateLimiter := mysqlHostMetricsRateLimiter.Load() + assert.Nil(t, rateLimiter) + } + }) + } +} diff --git a/go/vt/vttablet/tabletserver/throttle/base/self_metric_threads_running.go b/go/vt/vttablet/tabletserver/throttle/base/self_metric_threads_running.go index 08f7d408d1c..cb59547a768 100644 --- a/go/vt/vttablet/tabletserver/throttle/base/self_metric_threads_running.go +++ b/go/vt/vttablet/tabletserver/throttle/base/self_metric_threads_running.go @@ -18,8 +18,6 @@ package base import ( "context" - - "vitess.io/vitess/go/vt/vttablet/tabletserver/connpool" ) var ( @@ -47,6 +45,6 @@ func (m *ThreadsRunningSelfMetric) RequiresConn() bool { return true } -func (m *ThreadsRunningSelfMetric) Read(ctx context.Context, throttler ThrottlerMetricsPublisher, conn *connpool.Conn) *ThrottleMetric { - return ReadSelfMySQLThrottleMetric(ctx, conn, threadsRunningMetricQuery) +func (m *ThreadsRunningSelfMetric) Read(ctx context.Context, params *SelfMetricReadParams) *ThrottleMetric { + return ReadSelfMySQLThrottleMetric(ctx, params.Conn, threadsRunningMetricQuery) } diff --git a/go/vt/vttablet/tabletserver/throttle/base/throttler_metrics_publisher.go b/go/vt/vttablet/tabletserver/throttle/base/throttler_metrics_publisher.go index 1d2d4d0652c..10020af27e6 100644 --- a/go/vt/vttablet/tabletserver/throttle/base/throttler_metrics_publisher.go +++ b/go/vt/vttablet/tabletserver/throttle/base/throttler_metrics_publisher.go @@ -16,8 +16,8 @@ limitations under the License. package base -// ThrottlerMetricsPublisher is implemented by throttler.Throttler and is used by SelfMetric +// metricsPublisher is implemented by throttler.Throttler and is used by SelfMetric // implementations to query the throttler. -type ThrottlerMetricsPublisher interface { +type metricsPublisher interface { GetCustomMetricsQuery() string } diff --git a/go/vt/vttablet/tabletserver/throttle/check.go b/go/vt/vttablet/tabletserver/throttle/check.go index ccdfcb2ce23..d7f43d85e9d 100644 --- a/go/vt/vttablet/tabletserver/throttle/check.go +++ b/go/vt/vttablet/tabletserver/throttle/check.go @@ -188,9 +188,9 @@ func (check *ThrottlerCheck) Check(ctx context.Context, appName string, scope ba // Out of abundance of caution, we will protect against such a scenario. return } - stats.GetOrNewCounter(fmt.Sprintf("ThrottlerCheck%s%sTotal", textutil.SingleWordCamel(metricScope.String()), textutil.SingleWordCamel(metricName.String())), "").Add(1) + stats.GetOrNewCounter(fmt.Sprintf("ThrottlerCheck%s%sTotal", textutil.PascalCase(metricScope.String()), textutil.PascalCase(metricName.String())), "").Add(1) if !metricCheckResult.IsOK() { - stats.GetOrNewCounter(fmt.Sprintf("ThrottlerCheck%s%sError", textutil.SingleWordCamel(metricScope.String()), textutil.SingleWordCamel(metricName.String())), "").Add(1) + stats.GetOrNewCounter(fmt.Sprintf("ThrottlerCheck%s%sError", textutil.PascalCase(metricScope.String()), textutil.PascalCase(metricName.String())), "").Add(1) } }(metricCheckResult) } @@ -249,7 +249,7 @@ func (check *ThrottlerCheck) localCheck(ctx context.Context, aggregatedMetricNam check.throttler.markMetricHealthy(aggregatedMetricName) } if timeSinceHealthy, found := check.throttler.timeSinceMetricHealthy(aggregatedMetricName); found { - go stats.GetOrNewGauge(fmt.Sprintf("ThrottlerCheck%sSecondsSinceHealthy", textutil.SingleWordCamel(scope.String())), fmt.Sprintf("seconds since last healthy check for %v", scope)).Set(int64(timeSinceHealthy.Seconds())) + go stats.GetOrNewGauge(fmt.Sprintf("ThrottlerCheck%sSecondsSinceHealthy", textutil.PascalCase(scope.String())), fmt.Sprintf("seconds since last healthy check for %v", scope)).Set(int64(timeSinceHealthy.Seconds())) } return checkResult @@ -261,7 +261,7 @@ func (check *ThrottlerCheck) reportAggregated(aggregatedMetricName string, metri return } if value, err := metricResult.Get(); err == nil { - stats.GetOrNewGaugeFloat64(fmt.Sprintf("ThrottlerAggregated%s%s", textutil.SingleWordCamel(scope.String()), textutil.SingleWordCamel(metricName.String())), fmt.Sprintf("aggregated value for %v", scope)).Set(value) + stats.GetOrNewGaugeFloat64(fmt.Sprintf("ThrottlerAggregated%s%s", textutil.PascalCase(scope.String()), textutil.PascalCase(metricName.String())), fmt.Sprintf("aggregated value for %v", scope)).Set(value) } } diff --git a/go/vt/vttablet/tabletserver/throttle/throttler.go b/go/vt/vttablet/tabletserver/throttle/throttler.go index af7f59abb7e..839ba9d43b8 100644 --- a/go/vt/vttablet/tabletserver/throttle/throttler.go +++ b/go/vt/vttablet/tabletserver/throttle/throttler.go @@ -95,7 +95,6 @@ const ( DefaultThrottleRatio = 1.0 defaultReplicationLagQuery = "select unix_timestamp(now(6))-max(ts/1000000000) as replication_lag from %s.heartbeat" - threadsRunningQuery = "show global status like 'threads_running'" inventoryPrefix = "inventory/" throttlerConfigPrefix = "config/" @@ -137,6 +136,7 @@ type Throttler struct { keyspace string shard string tabletAlias *topodatapb.TabletAlias + tabletInfo atomic.Pointer[topo.TabletInfo] check *ThrottlerCheck isEnabled atomic.Bool @@ -190,7 +190,7 @@ type Throttler struct { cancelEnableContext context.CancelFunc throttledAppsMutex sync.Mutex - readSelfThrottleMetrics func(context.Context) base.ThrottleMetrics // overwritten by unit test + readSelfThrottleMetrics func(context.Context, tmclient.TabletManagerClient) base.ThrottleMetrics // overwritten by unit test } // ThrottlerStatus published some status values from the throttler @@ -262,8 +262,8 @@ func NewThrottler(env tabletenv.Env, srvTopoServer srvtopo.Server, ts *topo.Serv } throttler.StoreMetricsThreshold(base.RegisteredSelfMetrics[base.LagMetricName].DefaultThreshold()) - throttler.readSelfThrottleMetrics = func(ctx context.Context) base.ThrottleMetrics { - return throttler.readSelfThrottleMetricsInternal(ctx) + throttler.readSelfThrottleMetrics = func(ctx context.Context, tmClient tmclient.TabletManagerClient) base.ThrottleMetrics { + return throttler.readSelfThrottleMetricsInternal(ctx, tmClient) } return throttler } @@ -338,6 +338,15 @@ func (throttler *Throttler) initConfig() { // readThrottlerConfig proactively reads the throttler's config from SrvKeyspace in local topo func (throttler *Throttler) readThrottlerConfig(ctx context.Context) (*topodatapb.ThrottlerConfig, error) { + // since we're reading from topo, let's seize this opportunity to read table info as well + if throttler.tabletInfo.Load() == nil { + if ti, err := throttler.ts.GetTablet(ctx, throttler.tabletAlias); err == nil { + throttler.tabletInfo.Store(ti) + } else { + log.Errorf("Throttler: error reading tablet info: %v", err) + } + } + srvks, err := throttler.ts.GetSrvKeyspace(ctx, throttler.tabletAlias.Cell, throttler.keyspace) if err != nil { return nil, err @@ -804,7 +813,7 @@ func (throttler *Throttler) Operate(ctx context.Context, wg *sync.WaitGroup) { if throttler.IsOpen() { // frequent // Always collect self metrics: - throttler.collectSelfMetrics(ctx) + throttler.collectSelfMetrics(ctx, tmClient) if !throttler.isDormant() { throttler.collectShardMetrics(ctx, tmClient) } @@ -869,7 +878,7 @@ func (throttler *Throttler) Operate(ctx context.Context, wg *sync.WaitGroup) { }() } -func (throttler *Throttler) generateTabletProbeFunction(scope base.Scope, tmClient tmclient.TabletManagerClient, probe *base.Probe) (probeFunc func(context.Context) base.ThrottleMetrics) { +func (throttler *Throttler) generateTabletProbeFunction(scope base.Scope, probe *base.Probe) (probeFunc func(context.Context, tmclient.TabletManagerClient) base.ThrottleMetrics) { metricsWithError := func(err error) base.ThrottleMetrics { metrics := base.ThrottleMetrics{} for _, metricName := range base.KnownMetricNames { @@ -882,7 +891,7 @@ func (throttler *Throttler) generateTabletProbeFunction(scope base.Scope, tmClie } return metrics } - return func(ctx context.Context) base.ThrottleMetrics { + return func(ctx context.Context, tmClient tmclient.TabletManagerClient) base.ThrottleMetrics { // 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*activeCollectInterval) defer cancel() @@ -940,7 +949,7 @@ func (throttler *Throttler) generateTabletProbeFunction(scope base.Scope, tmClie // readSelfThrottleMetricsInternal rreads all registsred self metrics on this tablet (or backend MySQL server). // This is the actual place where metrics are read, to be later aggregated and/or propagated to other tablets. -func (throttler *Throttler) readSelfThrottleMetricsInternal(ctx context.Context) base.ThrottleMetrics { +func (throttler *Throttler) readSelfThrottleMetricsInternal(ctx context.Context, tmClient tmclient.TabletManagerClient) base.ThrottleMetrics { result := make(base.ThrottleMetrics, len(base.RegisteredSelfMetrics)) writeMetric := func(metric *base.ThrottleMetric) { select { @@ -950,15 +959,20 @@ func (throttler *Throttler) readSelfThrottleMetricsInternal(ctx context.Context) } } readMetric := func(selfMetric base.SelfMetric) *base.ThrottleMetric { - if !selfMetric.RequiresConn() { - return selfMetric.Read(ctx, throttler, nil) + params := &base.SelfMetricReadParams{ + Throttler: throttler, + TmClient: tmClient, + TabletInfo: throttler.tabletInfo.Load(), } - conn, err := throttler.pool.Get(ctx, nil) - if err != nil { - return &base.ThrottleMetric{Err: err} + if selfMetric.RequiresConn() { + conn, err := throttler.pool.Get(ctx, nil) + if err != nil { + return &base.ThrottleMetric{Err: err} + } + defer conn.Recycle() + params.Conn = conn.Conn } - defer conn.Recycle() - return selfMetric.Read(ctx, throttler, conn.Conn) + return selfMetric.Read(ctx, params) } for metricName, selfMetric := range base.RegisteredSelfMetrics { if metricName == base.DefaultMetricName { @@ -975,7 +989,7 @@ func (throttler *Throttler) readSelfThrottleMetricsInternal(ctx context.Context) return result } -func (throttler *Throttler) collectSelfMetrics(ctx context.Context) { +func (throttler *Throttler) collectSelfMetrics(ctx context.Context, tmClient tmclient.TabletManagerClient) { probe := throttler.inventory.ClustersProbes[throttler.tabletAliasString()] if probe == nil { // probe not created yet @@ -990,7 +1004,7 @@ func (throttler *Throttler) collectSelfMetrics(ctx context.Context) { defer atomic.StoreInt64(&probe.QueryInProgress, 0) // Throttler is probing its own tablet's metrics: - _ = base.ReadThrottleMetrics(ctx, probe, throttler.readSelfThrottleMetrics) + _ = base.ReadThrottleMetrics(ctx, probe, tmClient, throttler.readSelfThrottleMetrics) }() } @@ -1011,9 +1025,9 @@ func (throttler *Throttler) collectShardMetrics(ctx context.Context, tmClient tm defer atomic.StoreInt64(&probe.QueryInProgress, 0) // Throttler probing other tablets: - throttleMetricFunc := throttler.generateTabletProbeFunction(base.ShardScope, tmClient, probe) + throttleMetricFunc := throttler.generateTabletProbeFunction(base.ShardScope, probe) - throttleMetrics := base.ReadThrottleMetrics(ctx, probe, throttleMetricFunc) + throttleMetrics := base.ReadThrottleMetrics(ctx, probe, tmClient, throttleMetricFunc) for _, metric := range throttleMetrics { select { case <-ctx.Done(): diff --git a/go/vt/vttablet/tabletserver/throttle/throttler_test.go b/go/vt/vttablet/tabletserver/throttle/throttler_test.go index fd7921899da..352e641fa35 100644 --- a/go/vt/vttablet/tabletserver/throttle/throttler_test.go +++ b/go/vt/vttablet/tabletserver/throttle/throttler_test.go @@ -71,6 +71,24 @@ var ( Value: 2.718, Err: nil, }, + base.HistoryListLengthMetricName: &base.ThrottleMetric{ + Scope: base.SelfScope, + Alias: "", + Value: 5, + Err: nil, + }, + base.MysqldLoadAvgMetricName: &base.ThrottleMetric{ + Scope: base.SelfScope, + Alias: "", + Value: 0.3311, + Err: nil, + }, + base.MysqldDatadirUsedRatioMetricName: &base.ThrottleMetric{ + Scope: base.SelfScope, + Alias: "", + Value: 0.85, + Err: nil, + }, } replicaMetrics = map[string]*MetricResult{ base.LagMetricName.String(): { @@ -93,6 +111,21 @@ var ( ResponseCode: tabletmanagerdatapb.CheckThrottlerResponseCode_OK, Value: 5.1, }, + base.HistoryListLengthMetricName.String(): { + StatusCode: http.StatusOK, + ResponseCode: tabletmanagerdatapb.CheckThrottlerResponseCode_OK, + Value: 6, + }, + base.MysqldLoadAvgMetricName.String(): { + StatusCode: http.StatusOK, + ResponseCode: tabletmanagerdatapb.CheckThrottlerResponseCode_OK, + Value: 0.2211, + }, + base.MysqldDatadirUsedRatioMetricName.String(): { + StatusCode: http.StatusOK, + ResponseCode: tabletmanagerdatapb.CheckThrottlerResponseCode_OK, + Value: 0.87, + }, } nonPrimaryTabletType atomic.Int32 ) @@ -283,7 +316,7 @@ func newTestThrottler() *Throttler { throttler.recentCheckDormantDiff = int64(throttler.dormantPeriod / recentCheckRateLimiterInterval) throttler.recentCheckDiff = int64(3 * time.Second / recentCheckRateLimiterInterval) - throttler.readSelfThrottleMetrics = func(ctx context.Context) base.ThrottleMetrics { + throttler.readSelfThrottleMetrics = func(ctx context.Context, tmClient tmclient.TabletManagerClient) base.ThrottleMetrics { for _, metric := range selfMetrics { go func() { select { @@ -1827,10 +1860,13 @@ func TestChecks(t *testing.T) { assert.Equal(t, testAppName.String(), checkResult.AppName) assert.Equal(t, len(base.KnownMetricNames), len(checkResult.Metrics)) - assert.EqualValues(t, 0.3, checkResult.Metrics[base.LagMetricName.String()].Value) // self lag value, because flags.Scope is set - assert.EqualValues(t, 26, checkResult.Metrics[base.ThreadsRunningMetricName.String()].Value) // self value, because flags.Scope is set - assert.EqualValues(t, 17, checkResult.Metrics[base.CustomMetricName.String()].Value) // self value, because flags.Scope is set - assert.EqualValues(t, 2.718, checkResult.Metrics[base.LoadAvgMetricName.String()].Value) // self value, because flags.Scope is set + assert.EqualValues(t, 0.3, checkResult.Metrics[base.LagMetricName.String()].Value) // self lag value, because flags.Scope is set + assert.EqualValues(t, 26, checkResult.Metrics[base.ThreadsRunningMetricName.String()].Value) // self value, because flags.Scope is set + assert.EqualValues(t, 17, checkResult.Metrics[base.CustomMetricName.String()].Value) // self value, because flags.Scope is set + assert.EqualValues(t, 2.718, checkResult.Metrics[base.LoadAvgMetricName.String()].Value) // self value, because flags.Scope is set + assert.EqualValues(t, 5, checkResult.Metrics[base.HistoryListLengthMetricName.String()].Value) // self value, because flags.Scope is set + assert.EqualValues(t, 0.3311, checkResult.Metrics[base.MysqldLoadAvgMetricName.String()].Value) // self value, because flags.Scope is set + assert.EqualValues(t, 0.85, checkResult.Metrics[base.MysqldDatadirUsedRatioMetricName.String()].Value) // self value, because flags.Scope is set for _, metric := range checkResult.Metrics { assert.EqualValues(t, base.SelfScope.String(), metric.Scope) } @@ -1886,10 +1922,13 @@ func TestChecks(t *testing.T) { assert.Equal(t, testAppName.String(), checkResult.AppName) assert.Equal(t, len(base.KnownMetricNames), len(checkResult.Metrics)) - assert.EqualValues(t, 0.9, checkResult.Metrics[base.LagMetricName.String()].Value) // shard lag value, because flags.Scope is set - assert.EqualValues(t, 26, checkResult.Metrics[base.ThreadsRunningMetricName.String()].Value) // shard value, because flags.Scope is set - assert.EqualValues(t, 17, checkResult.Metrics[base.CustomMetricName.String()].Value) // shard value, because flags.Scope is set - assert.EqualValues(t, 5.1, checkResult.Metrics[base.LoadAvgMetricName.String()].Value) // shard value, because flags.Scope is set + assert.EqualValues(t, 0.9, checkResult.Metrics[base.LagMetricName.String()].Value) // shard lag value, because flags.Scope is set + assert.EqualValues(t, 26, checkResult.Metrics[base.ThreadsRunningMetricName.String()].Value) // shard value, because flags.Scope is set + assert.EqualValues(t, 17, checkResult.Metrics[base.CustomMetricName.String()].Value) // shard value, because flags.Scope is set + assert.EqualValues(t, 5.1, checkResult.Metrics[base.LoadAvgMetricName.String()].Value) // shard value, because flags.Scope is set + assert.EqualValues(t, 6, checkResult.Metrics[base.HistoryListLengthMetricName.String()].Value) // shard value, because flags.Scope is set + assert.EqualValues(t, 0.3311, checkResult.Metrics[base.MysqldLoadAvgMetricName.String()].Value) // shard value, because flags.Scope is set + assert.EqualValues(t, 0.87, checkResult.Metrics[base.MysqldDatadirUsedRatioMetricName.String()].Value) // shard value, because flags.Scope is set for _, metric := range checkResult.Metrics { assert.EqualValues(t, base.ShardScope.String(), metric.Scope) } @@ -1918,14 +1957,19 @@ func TestChecks(t *testing.T) { assert.ErrorIs(t, checkResult.Error, base.ErrThresholdExceeded) assert.Equal(t, len(base.KnownMetricNames), len(checkResult.Metrics)) - assert.EqualValues(t, 0.9, checkResult.Metrics[base.LagMetricName.String()].Value) // shard lag value, because "shard" is the default scope for lag - assert.EqualValues(t, 26, checkResult.Metrics[base.ThreadsRunningMetricName.String()].Value) // self value, because "self" is the default scope for threads_running - assert.EqualValues(t, 17, checkResult.Metrics[base.CustomMetricName.String()].Value) // self value, because "self" is the default scope for custom - assert.EqualValues(t, 2.718, checkResult.Metrics[base.LoadAvgMetricName.String()].Value) // self value, because "self" is the default scope for loadavg + assert.EqualValues(t, 0.9, checkResult.Metrics[base.LagMetricName.String()].Value) // shard lag value, because "shard" is the default scope for lag + assert.EqualValues(t, 26, checkResult.Metrics[base.ThreadsRunningMetricName.String()].Value) // self value, because "self" is the default scope for threads_running + assert.EqualValues(t, 17, checkResult.Metrics[base.CustomMetricName.String()].Value) // self value, because "self" is the default scope for custom + assert.EqualValues(t, 2.718, checkResult.Metrics[base.LoadAvgMetricName.String()].Value) // self value, because "self" is the default scope for loadavg + assert.EqualValues(t, 5, checkResult.Metrics[base.HistoryListLengthMetricName.String()].Value) // self value, because "self" is the default scope for loadavg + assert.EqualValues(t, 0.3311, checkResult.Metrics[base.MysqldLoadAvgMetricName.String()].Value) // self value, because "self" is the default scope for loadavg + assert.EqualValues(t, 0.85, checkResult.Metrics[base.MysqldDatadirUsedRatioMetricName.String()].Value) // self value, because "self" is the default scope for loadavg assert.EqualValues(t, base.ShardScope.String(), checkResult.Metrics[base.LagMetricName.String()].Scope) assert.EqualValues(t, base.SelfScope.String(), checkResult.Metrics[base.ThreadsRunningMetricName.String()].Scope) assert.EqualValues(t, base.SelfScope.String(), checkResult.Metrics[base.CustomMetricName.String()].Scope) assert.EqualValues(t, base.SelfScope.String(), checkResult.Metrics[base.LoadAvgMetricName.String()].Scope) + assert.EqualValues(t, base.SelfScope.String(), checkResult.Metrics[base.MysqldLoadAvgMetricName.String()].Scope) + assert.EqualValues(t, base.SelfScope.String(), checkResult.Metrics[base.MysqldDatadirUsedRatioMetricName.String()].Scope) }) }) t.Run("checks, defined scope masks explicit scope metrics", func(t *testing.T) { @@ -1939,6 +1983,9 @@ func TestChecks(t *testing.T) { base.MetricName("self/threads_running"), base.MetricName("custom"), base.MetricName("shard/loadavg"), + base.MetricName("shard/mysqld-loadavg"), + base.MetricName("self/history_list_length"), + base.MetricName("self/mysqld-datadir-used-ratio"), base.MetricName("default"), } checkResult := throttler.Check(ctx, testAppName.String(), metricNames, flags) @@ -1950,10 +1997,13 @@ func TestChecks(t *testing.T) { assert.ErrorIs(t, checkResult.Error, base.ErrThresholdExceeded) assert.Equal(t, len(metricNames), len(checkResult.Metrics)) - assert.EqualValues(t, 0.9, checkResult.Metrics[base.LagMetricName.String()].Value) // shard lag value, even though scope name is in metric name - assert.EqualValues(t, 26, checkResult.Metrics[base.ThreadsRunningMetricName.String()].Value) // shard value, even though scope name is in metric name - assert.EqualValues(t, 17, checkResult.Metrics[base.CustomMetricName.String()].Value) // shard value because flags.Scope is set - assert.EqualValues(t, 5.1, checkResult.Metrics[base.LoadAvgMetricName.String()].Value) // shard value, not because scope name is in metric name but because flags.Scope is set + assert.EqualValues(t, 0.9, checkResult.Metrics[base.LagMetricName.String()].Value) // shard lag value, even though scope name is in metric name + assert.EqualValues(t, 26, checkResult.Metrics[base.ThreadsRunningMetricName.String()].Value) // shard value, even though scope name is in metric name + assert.EqualValues(t, 17, checkResult.Metrics[base.CustomMetricName.String()].Value) // shard value because flags.Scope is set + assert.EqualValues(t, 5.1, checkResult.Metrics[base.LoadAvgMetricName.String()].Value) // shard value, not because scope name is in metric name but because flags.Scope is set + assert.EqualValues(t, 6, checkResult.Metrics[base.HistoryListLengthMetricName.String()].Value) // shard value, even though scope name is in metric name + assert.EqualValues(t, 0.3311, checkResult.Metrics[base.MysqldLoadAvgMetricName.String()].Value) // shard value, not because scope name is in metric name but because flags.Scope is set + assert.EqualValues(t, 0.87, checkResult.Metrics[base.MysqldDatadirUsedRatioMetricName.String()].Value) // shard value, even though scope name is in metric name for _, metric := range checkResult.Metrics { assert.EqualValues(t, base.ShardScope.String(), metric.Scope) } @@ -2222,8 +2272,13 @@ func TestReplica(t *testing.T) { base.DefaultMetricName: assert.Error(t, metricResult.Error, "metricName=%v, value=%v, threshold=%v", metricName, metricResult.Value, metricResult.Threshold) assert.ErrorIs(t, metricResult.Error, base.ErrThresholdExceeded) - case base.ThreadsRunningMetricName: + case base.ThreadsRunningMetricName, + base.HistoryListLengthMetricName, + base.MysqldLoadAvgMetricName, + base.MysqldDatadirUsedRatioMetricName: assert.NoError(t, metricResult.Error, "metricName=%v, value=%v, threshold=%v", metricName, metricResult.Value, metricResult.Threshold) + default: + assert.Fail(t, "unexpected metric", "name=%v", metricName) } } }) diff --git a/go/vt/vttablet/tabletserver/tx_engine.go b/go/vt/vttablet/tabletserver/tx_engine.go index b6e0e69b86d..d0ca4ec498c 100644 --- a/go/vt/vttablet/tabletserver/tx_engine.go +++ b/go/vt/vttablet/tabletserver/tx_engine.go @@ -116,20 +116,19 @@ func NewTxEngine(env tabletenv.Env, dxNotifier func()) *TxEngine { te.txPool = NewTxPool(env, limiter) // We initially allow twoPC (handles vttablet restarts). // We will disallow them for a few reasons - - // 1. when a new tablet is promoted if semi-sync is turned off. + // 1. When a new tablet is promoted if semi-sync is turned off. // 2. TabletControls have been set by a Resharding workflow. te.twopcAllowed = make([]bool, TwoPCAllowed_Len) for idx := range te.twopcAllowed { te.twopcAllowed[idx] = true } - te.twopcEnabled = config.TwoPCEnable - if te.twopcEnabled { - if config.TwoPCAbandonAge <= 0 { - log.Error("2PC abandon age not specified: Disabling 2PC") - te.twopcEnabled = false - } + te.twopcEnabled = true + if config.TwoPCAbandonAge <= 0 { + log.Error("2PC abandon age not specified: Disabling 2PC") + te.twopcEnabled = false } - te.abandonAge = config.TwoPCAbandonAge.Get() + + te.abandonAge = config.TwoPCAbandonAge te.ticks = timer.NewTimer(te.abandonAge / 2) // Set the prepared pool capacity to something lower than diff --git a/go/vt/vttablet/tabletserver/tx_engine_test.go b/go/vt/vttablet/tabletserver/tx_engine_test.go index f4dd0596691..cba7bf86e8f 100644 --- a/go/vt/vttablet/tabletserver/tx_engine_test.go +++ b/go/vt/vttablet/tabletserver/tx_engine_test.go @@ -613,8 +613,7 @@ func TestCheckReceivedError(t *testing.T) { cfg := tabletenv.NewDefaultConfig() cfg.DB = newDBConfigs(db) env := tabletenv.NewEnv(vtenv.NewTestEnv(), cfg, "TabletServerTest") - env.Config().TwoPCEnable = true - env.Config().TwoPCAbandonAge = 5 + env.Config().TwoPCAbandonAge = 5 * time.Second te := NewTxEngine(env, nil) te.AcceptReadWrite() @@ -791,8 +790,7 @@ func TestPrepareTx(t *testing.T) { db.AddRejectedQuery("retryable query", sqlerror.NewSQLError(sqlerror.CRConnectionError, "", "Retryable error")) cfg := tabletenv.NewDefaultConfig() cfg.DB = newDBConfigs(db) - cfg.TwoPCEnable = true - cfg.TwoPCAbandonAge = 200 + cfg.TwoPCAbandonAge = 200 * time.Second te := NewTxEngine(tabletenv.NewEnv(vtenv.NewTestEnv(), cfg, "TabletServerTest"), nil) te.AcceptReadWrite() db.ResetQueryLog() diff --git a/go/vt/vttablet/tabletserver/tx_pool.go b/go/vt/vttablet/tabletserver/tx_pool.go index ca8a0ea34b2..302a3d41050 100644 --- a/go/vt/vttablet/tabletserver/tx_pool.go +++ b/go/vt/vttablet/tabletserver/tx_pool.go @@ -40,9 +40,8 @@ import ( ) const ( - txLogInterval = 1 * time.Minute - beginWithCSRO = "start transaction with consistent snapshot, read only" - trackGtidQuery = "set session session_track_gtids = START_GTID" + txLogInterval = 1 * time.Minute + beginWithCSRO = "start transaction with consistent snapshot, read only" ) var txIsolations = map[querypb.ExecuteOptions_TransactionIsolation]string{ @@ -394,16 +393,6 @@ func createStartTxStmt(options *querypb.ExecuteOptions, readOnly bool) (string, } func handleConsistentSnapshotCase(ctx context.Context, conn *StatefulConnection) (beginSQL string, sessionStateChanges string, err error) { - _, err = conn.execWithRetry(ctx, trackGtidQuery, 1, false) - // We allow this to fail since this is a custom MySQL extension, but we return - // then if this query was executed or not. - // - // Callers also can know because the sessionStateChanges will be empty for a snapshot - // transaction and get GTID information in another (less efficient) way. - if err == nil { - beginSQL = trackGtidQuery + "; " - } - isolationLevel := txIsolations[querypb.ExecuteOptions_CONSISTENT_SNAPSHOT_READ_ONLY] execSQL, err := setIsolationLevel(ctx, conn, isolationLevel) diff --git a/go/vt/vttablet/tabletserver/tx_pool_test.go b/go/vt/vttablet/tabletserver/tx_pool_test.go index c03cac92878..22810d4c422 100644 --- a/go/vt/vttablet/tabletserver/tx_pool_test.go +++ b/go/vt/vttablet/tabletserver/tx_pool_test.go @@ -701,11 +701,11 @@ func TestTxPoolBeginStatements(t *testing.T) { expBeginSQL: "set transaction isolation level serializable; start transaction read only", }, { txIsolationLevel: querypb.ExecuteOptions_CONSISTENT_SNAPSHOT_READ_ONLY, - expBeginSQL: "set session session_track_gtids = START_GTID; set transaction isolation level repeatable read; start transaction with consistent snapshot, read only", + expBeginSQL: "set transaction isolation level repeatable read; start transaction with consistent snapshot, read only", }, { txIsolationLevel: querypb.ExecuteOptions_CONSISTENT_SNAPSHOT_READ_ONLY, readOnly: true, - expBeginSQL: "set session session_track_gtids = START_GTID; set transaction isolation level repeatable read; start transaction with consistent snapshot, read only", + expBeginSQL: "set transaction isolation level repeatable read; start transaction with consistent snapshot, read only", }, { txIsolationLevel: querypb.ExecuteOptions_AUTOCOMMIT, expBeginSQL: "", diff --git a/go/vt/vttablet/tabletserver/vstreamer/planbuilder.go b/go/vt/vttablet/tabletserver/vstreamer/planbuilder.go index 9bbc98ca2bd..e5115afe6d3 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/planbuilder.go +++ b/go/vt/vttablet/tabletserver/vstreamer/planbuilder.go @@ -89,6 +89,8 @@ const ( NotEqual // IsNotNull is used to filter a column if it is NULL IsNotNull + // In is used to filter a comparable column if equals any of the values from a specific tuple + In ) // Filter contains opcodes for filtering. @@ -97,6 +99,9 @@ type Filter struct { ColNum int Value sqltypes.Value + // Values will be used to store tuple/list values. + Values []sqltypes.Value + // Parameters for VindexMatch. // Vindex, VindexColumns and KeyRange, if set, will be used // to filter the row. @@ -166,6 +171,8 @@ func getOpcode(comparison *sqlparser.ComparisonExpr) (Opcode, error) { opcode = GreaterThanEqual case sqlparser.NotEqualOp: opcode = NotEqual + case sqlparser.InOp: + opcode = In default: return -1, fmt.Errorf("comparison operator %s not supported", comparison.Operator.ToString()) } @@ -238,6 +245,24 @@ func (plan *Plan) filter(values, result []sqltypes.Value, charsets []collations. if values[filter.ColNum].IsNull() { return false, nil } + case In: + if filter.Values == nil { + return false, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "unexpected empty filter values when performing IN operator") + } + found := false + for _, filterValue := range filter.Values { + match, err := compare(Equal, values[filter.ColNum], filterValue, plan.env.CollationEnv(), charsets[filter.ColNum]) + if err != nil { + return false, err + } + if match { + found = true + break + } + } + if !found { + return false, nil + } default: match, err := compare(filter.Opcode, values[filter.ColNum], filter.Value, plan.env.CollationEnv(), charsets[filter.ColNum]) if err != nil { @@ -514,6 +539,27 @@ func (plan *Plan) getColumnFuncExpr(columnName string) *sqlparser.FuncExpr { return nil } +func (plan *Plan) appendTupleFilter(values sqlparser.ValTuple, opcode Opcode, colnum int) error { + pv, err := evalengine.Translate(values, &evalengine.Config{ + Collation: plan.env.CollationEnv().DefaultConnectionCharset(), + Environment: plan.env, + }) + if err != nil { + return err + } + env := evalengine.EmptyExpressionEnv(plan.env) + resolved, err := env.Evaluate(pv) + if err != nil { + return err + } + plan.Filters = append(plan.Filters, Filter{ + Opcode: opcode, + ColNum: colnum, + Values: resolved.TupleValues(), + }) + return nil +} + func (plan *Plan) analyzeWhere(vschema *localVSchema, where *sqlparser.Where) error { if where == nil { return nil @@ -537,6 +583,20 @@ func (plan *Plan) analyzeWhere(vschema *localVSchema, where *sqlparser.Where) er if err != nil { return err } + // The Right Expr is typically expected to be a Literal value, + // except for the IN operator, where a Tuple value is expected. + // Handle the IN operator case first. + if opcode == In { + values, ok := expr.Right.(sqlparser.ValTuple) + if !ok { + return fmt.Errorf("unexpected: %v", sqlparser.String(expr)) + } + err := plan.appendTupleFilter(values, opcode, colnum) + if err != nil { + return err + } + continue + } val, ok := expr.Right.(*sqlparser.Literal) if !ok { return fmt.Errorf("unexpected: %v", sqlparser.String(expr)) diff --git a/go/vt/vttablet/tabletserver/vstreamer/planbuilder_test.go b/go/vt/vttablet/tabletserver/vstreamer/planbuilder_test.go index ba345b2a00b..aba74368802 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/planbuilder_test.go +++ b/go/vt/vttablet/tabletserver/vstreamer/planbuilder_test.go @@ -710,9 +710,15 @@ func TestPlanBuilderFilterComparison(t *testing.T) { outFilters: []Filter{{Opcode: LessThan, ColNum: 0, Value: sqltypes.NewInt64(2)}, {Opcode: LessThanEqual, ColNum: 1, Value: sqltypes.NewVarChar("xyz")}, }, + }, { + name: "in-operator", + inFilter: "select * from t1 where id in (1, 2)", + outFilters: []Filter{ + {Opcode: In, ColNum: 0, Values: []sqltypes.Value{sqltypes.NewInt64(1), sqltypes.NewInt64(2)}}, + }, }, { name: "vindex-and-operators", - inFilter: "select * from t1 where in_keyrange(id, 'hash', '-80') and id = 2 and val <> 'xyz'", + inFilter: "select * from t1 where in_keyrange(id, 'hash', '-80') and id = 2 and val <> 'xyz' and id in (100, 30)", outFilters: []Filter{ { Opcode: VindexMatch, @@ -727,6 +733,7 @@ func TestPlanBuilderFilterComparison(t *testing.T) { }, {Opcode: Equal, ColNum: 0, Value: sqltypes.NewInt64(2)}, {Opcode: NotEqual, ColNum: 1, Value: sqltypes.NewVarChar("xyz")}, + {Opcode: In, ColNum: 0, Values: []sqltypes.Value{sqltypes.NewInt64(100), sqltypes.NewInt64(30)}}, }, }} diff --git a/go/vt/vttablet/tabletserver/vstreamer/snapshot_conn.go b/go/vt/vttablet/tabletserver/vstreamer/snapshot_conn.go index ec326cc4159..ee141ce9859 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/snapshot_conn.go +++ b/go/vt/vttablet/tabletserver/vstreamer/snapshot_conn.go @@ -132,29 +132,6 @@ func (conn *snapshotConn) startSnapshot(ctx context.Context, table string) (gtid return replication.EncodePosition(mpos), nil } -// startSnapshotWithConsistentGTID performs the snapshotting without locking tables. This assumes -// session_track_gtids = START_GTID, which is a contribution to MySQL and is not in vanilla MySQL at the -// time of this writing. -func (conn *snapshotConn) startSnapshotWithConsistentGTID(ctx context.Context) (gtid string, err error) { - if _, err := conn.ExecuteFetch("set transaction isolation level repeatable read", 1, false); err != nil { - return "", err - } - result, err := conn.ExecuteFetch("start transaction with consistent snapshot, read only", 1, false) - if err != nil { - return "", err - } - // The "session_track_gtids = START_GTID" patch is only applicable to MySQL56 GTID, which is - // why we hardcode the position as mysql.Mysql56FlavorID - mpos, err := replication.ParsePosition(replication.Mysql56FlavorID, result.SessionStateChanges) - if err != nil { - return "", err - } - if _, err := conn.ExecuteFetch("set @@session.time_zone = '+00:00'", 1, false); err != nil { - return "", err - } - return replication.EncodePosition(mpos), nil -} - // Close rolls back any open transactions and closes the connection. func (conn *snapshotConn) Close() { _, _ = conn.ExecuteFetch("rollback", 1, false) diff --git a/go/vt/vttablet/tabletserver/vstreamer/vstreamer.go b/go/vt/vttablet/tabletserver/vstreamer/vstreamer.go index ea7f75cdc38..59db723ff2b 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/vstreamer.go +++ b/go/vt/vttablet/tabletserver/vstreamer/vstreamer.go @@ -375,7 +375,7 @@ func (vs *vstreamer) parseEvents(ctx context.Context, events <-chan mysql.Binlog } return fmt.Errorf("unexpected server EOF") } - vevents, err := vs.parseEvent(ev) + vevents, err := vs.parseEvent(ev, bufferAndTransmit) if err != nil { vs.vse.errorCounts.Add("ParseEvent", 1) return err @@ -416,7 +416,11 @@ func (vs *vstreamer) parseEvents(ctx context.Context, events <-chan mysql.Binlog } // parseEvent parses an event from the binlog and converts it to a list of VEvents. -func (vs *vstreamer) parseEvent(ev mysql.BinlogEvent) ([]*binlogdatapb.VEvent, error) { +// The bufferAndTransmit function must be passed if the event is a TransactionPayloadEvent +// as for larger payloads (> ZstdInMemoryDecompressorMaxSize) the internal events need +// to be streamed directly here in order to avoid holding the entire payload's contents, +// which can be 10s or even 100s of GiBs, all in memory. +func (vs *vstreamer) parseEvent(ev mysql.BinlogEvent, bufferAndTransmit func(vevent *binlogdatapb.VEvent) error) ([]*binlogdatapb.VEvent, error) { if !ev.IsValid() { return nil, fmt.Errorf("can't parse binlog event: invalid data: %#v", ev) } @@ -672,11 +676,31 @@ func (vs *vstreamer) parseEvent(ev mysql.BinlogEvent) ([]*binlogdatapb.VEvent, e } return nil, err } - tpvevents, err := vs.parseEvent(tpevent) + tpvevents, err := vs.parseEvent(tpevent, nil) // Parse the internal event if err != nil { return nil, vterrors.Wrap(err, "failed to parse transaction payload's internal event") } - vevents = append(vevents, tpvevents...) + if tp.StreamingContents { + // Transmit each internal event individually to avoid buffering + // the large transaction's entire payload of events in memory, as + // the uncompressed size can be 10s or even 100s of GiBs in size. + if bufferAndTransmit == nil { + return nil, vterrors.New(vtrpcpb.Code_INTERNAL, "[bug] cannot stream compressed transaction payload's internal events as no bufferAndTransmit function was provided") + } + for _, tpvevent := range tpvevents { + tpvevent.Timestamp = int64(ev.Timestamp()) + tpvevent.CurrentTime = time.Now().UnixNano() + if err := bufferAndTransmit(tpvevent); err != nil { + if err == io.EOF { + return nil, nil + } + vs.vse.errorCounts.Add("TransactionPayloadBufferAndTransmit", 1) + return nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "error sending compressed transaction payload's internal event: %v", err) + } + } + } else { // Process the payload's internal events all at once + vevents = append(vevents, tpvevents...) + } } vs.vse.vstreamerCompressedTransactionsDecoded.Add(1) } diff --git a/go/vt/vttablet/tabletserver/vstreamer/vstreamer_test.go b/go/vt/vttablet/tabletserver/vstreamer/vstreamer_test.go index 846d62202e7..5282b5f372d 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/vstreamer_test.go +++ b/go/vt/vttablet/tabletserver/vstreamer/vstreamer_test.go @@ -1966,7 +1966,7 @@ func TestFilteredMultipleWhere(t *testing.T) { filter: &binlogdatapb.Filter{ Rules: []*binlogdatapb.Rule{{ Match: "t1", - Filter: "select id1, val from t1 where in_keyrange('-80') and id2 = 200 and id3 = 1000 and val = 'newton'", + Filter: "select id1, val from t1 where in_keyrange('-80') and id2 = 200 and id3 = 1000 and val = 'newton' and id1 in (1, 2, 129)", }}, }, customFieldEvents: true, @@ -1988,9 +1988,7 @@ func TestFilteredMultipleWhere(t *testing.T) { {spec: &TestRowEventSpec{table: "t1", changes: []TestRowChange{{after: []string{"2", "newton"}}}}}, }}, {"insert into t1 values (3, 100, 2000, 'kepler')", noEvents}, - {"insert into t1 values (128, 200, 1000, 'newton')", []TestRowEvent{ - {spec: &TestRowEventSpec{table: "t1", changes: []TestRowChange{{after: []string{"128", "newton"}}}}}, - }}, + {"insert into t1 values (128, 200, 1000, 'newton')", noEvents}, {"insert into t1 values (5, 200, 2000, 'kepler')", noEvents}, {"insert into t1 values (129, 200, 1000, 'kepler')", noEvents}, {"commit", nil}, @@ -2080,3 +2078,33 @@ func TestGeneratedInvisiblePrimaryKey(t *testing.T) { }} ts.Run() } + +func TestFilteredInOperator(t *testing.T) { + 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 val in ('eee', 'bbb', 'ddd') and id1 in (4, 5)", + }}, + }, + }, + } + defer ts.Close() + 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')", noEvents}, + {"insert into t1 values (3, 100, 'ccc')", noEvents}, + {"insert into t1 values (4, 200, 'ddd')", nil}, + {"insert into t1 values (5, 200, 'eee')", nil}, + {"commit", nil}, + }} + ts.Run() +} diff --git a/go/vt/vttablet/tmrpctest/test_tm_rpc.go b/go/vt/vttablet/tmrpctest/test_tm_rpc.go index 88ba3506ba5..a106b43bf2c 100644 --- a/go/vt/vttablet/tmrpctest/test_tm_rpc.go +++ b/go/vt/vttablet/tmrpctest/test_tm_rpc.go @@ -1428,6 +1428,10 @@ func (fra *fakeRPCTM) Backup(ctx context.Context, logger logutil.Logger, request return nil } +func (fra *fakeRPCTM) IsBackupRunning() bool { + return false +} + func tmRPCTestBackup(ctx context.Context, t *testing.T, client tmclient.TabletManagerClient, tablet *topodatapb.Tablet) { req := &tabletmanagerdatapb.BackupRequest{Concurrency: testBackupConcurrency, AllowPrimary: testBackupAllowPrimary} stream, err := client.Backup(ctx, tablet, req) diff --git a/go/vt/vttest/local_cluster.go b/go/vt/vttest/local_cluster.go index 576a78bb761..fa7e40feb12 100644 --- a/go/vt/vttest/local_cluster.go +++ b/go/vt/vttest/local_cluster.go @@ -660,6 +660,7 @@ func (db *LocalCluster) JSONConfig() any { config := map[string]any{ "bind_address": db.vt.BindAddress, "port": db.vt.Port, + "grpc_bind_address": db.vt.BindAddressGprc, "socket": db.mysql.UnixSocket(), "vtcombo_mysql_port": db.Env.PortForProtocol("vtcombo_mysql_port", ""), "mysql": db.Env.PortForProtocol("mysql", ""), diff --git a/go/vt/vttest/vtprocess.go b/go/vt/vttest/vtprocess.go index 3f34994bb75..6371811a60e 100644 --- a/go/vt/vttest/vtprocess.go +++ b/go/vt/vttest/vtprocess.go @@ -44,16 +44,17 @@ type HealthChecker func(addr string) bool // It can be spawned manually or through one of the available // helper methods. type VtProcess struct { - Name string - Directory string - LogDirectory string - Binary string - ExtraArgs []string - Env []string - BindAddress string - Port int - PortGrpc int - HealthCheck HealthChecker + Name string + Directory string + LogDirectory string + Binary string + ExtraArgs []string + Env []string + BindAddress string + BindAddressGprc string + Port int + PortGrpc int + HealthCheck HealthChecker proc *exec.Cmd exit chan error @@ -139,6 +140,11 @@ func (vtp *VtProcess) WaitStart() (err error) { vtp.proc.Args = append(vtp.proc.Args, fmt.Sprintf("%d", vtp.PortGrpc)) } + if vtp.BindAddressGprc != "" { + vtp.proc.Args = append(vtp.proc.Args, "--grpc_bind_address") + vtp.proc.Args = append(vtp.proc.Args, vtp.BindAddressGprc) + } + 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...) @@ -199,16 +205,22 @@ func VtcomboProcess(environment Environment, args *Config, mysql MySQLManager) ( if args.VtComboBindAddress != "" { vtcomboBindAddress = args.VtComboBindAddress } + grpcBindAddress := "127.0.0.1" + if servenv.GRPCBindAddress() != "" { + grpcBindAddress = servenv.GRPCBindAddress() + } + 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"), - Env: environment.EnvVars(), + Name: "vtcombo", + Directory: environment.Directory(), + LogDirectory: environment.LogDirectory(), + Binary: environment.BinaryPath("vtcombo"), + BindAddress: vtcomboBindAddress, + BindAddressGprc: grpcBindAddress, + Port: environment.PortForProtocol("vtcombo", ""), + PortGrpc: environment.PortForProtocol("vtcombo", "grpc"), + HealthCheck: environment.ProcessHealthCheck("vtcombo"), + Env: environment.EnvVars(), } user, pass := mysql.Auth() diff --git a/go/vt/wrangler/fake_dbclient_test.go b/go/vt/wrangler/fake_dbclient_test.go index 14ef0913383..02ee79210d7 100644 --- a/go/vt/wrangler/fake_dbclient_test.go +++ b/go/vt/wrangler/fake_dbclient_test.go @@ -153,6 +153,10 @@ func (dc *fakeDBClient) Rollback() error { func (dc *fakeDBClient) Close() { } +func (dc *fakeDBClient) IsClosed() bool { + return false +} + // ExecuteFetch is part of the DBClient interface func (dc *fakeDBClient) ExecuteFetch(query string, maxrows int) (*sqltypes.Result, error) { dc.mu.Lock() diff --git a/go/vt/wrangler/materializer.go b/go/vt/wrangler/materializer.go index 7e24945cde7..bd7ae553130 100644 --- a/go/vt/wrangler/materializer.go +++ b/go/vt/wrangler/materializer.go @@ -551,11 +551,8 @@ func (wr *Wrangler) prepareCreateLookup(ctx context.Context, keyspace string, sp if len(vindexFromCols) != 1 { return nil, nil, nil, fmt.Errorf("unique vindex 'from' should have only one column: %v", vindex) } - } else { - if len(vindexFromCols) < 2 { - return nil, nil, nil, fmt.Errorf("non-unique vindex 'from' should have more than one column: %v", vindex) - } } + vindexToCol = vindex.Params["to"] // Make the vindex write_only. If one exists already in the vschema, // it will need to match this vindex exactly, including the write_only setting. diff --git a/go/vt/wrangler/materializer_test.go b/go/vt/wrangler/materializer_test.go index 1728ba6efc2..1871d778c6b 100644 --- a/go/vt/wrangler/materializer_test.go +++ b/go/vt/wrangler/materializer_test.go @@ -1599,7 +1599,7 @@ func TestCreateLookupVindexFailures(t *testing.T) { }, err: "unique vindex 'from' should have only one column", }, { - description: "non-unique lookup should have more than one column", + description: "non-unique lookup can have only one column", input: &vschemapb.Keyspace{ Vindexes: map[string]*vschemapb.Vindex{ "v": { @@ -1612,7 +1612,7 @@ func TestCreateLookupVindexFailures(t *testing.T) { }, }, }, - err: "non-unique vindex 'from' should have more than one column", + err: "", }, { description: "vindex not found", input: &vschemapb.Keyspace{ diff --git a/go/vt/wrangler/testlib/backup_test.go b/go/vt/wrangler/testlib/backup_test.go index cb61c4bab99..b540fc9f8f0 100644 --- a/go/vt/wrangler/testlib/backup_test.go +++ b/go/vt/wrangler/testlib/backup_test.go @@ -28,7 +28,6 @@ import ( "github.com/stretchr/testify/require" "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" @@ -36,6 +35,7 @@ import ( "vitess.io/vitess/go/vt/logutil" "vitess.io/vitess/go/vt/mysqlctl" "vitess.io/vitess/go/vt/mysqlctl/backupstorage" + "vitess.io/vitess/go/vt/mysqlctl/blackbox" "vitess.io/vitess/go/vt/mysqlctl/filebackupstorage" "vitess.io/vitess/go/vt/topo/memorytopo" "vitess.io/vitess/go/vt/topo/topoproto" @@ -132,7 +132,7 @@ func testBackupRestore(t *testing.T, cDetails *compressionDetails) error { require.NoError(t, os.MkdirAll(s, os.ModePerm)) } - needIt, err := needInnoDBRedoLogSubdir() + needIt, err := blackbox.NeedInnoDBRedoLogSubdir() require.NoError(t, err) if needIt { newPath := path.Join(sourceInnodbLogDir, mysql.DynamicRedoLogSubdir) @@ -371,7 +371,7 @@ func TestBackupRestoreLagged(t *testing.T) { } require.NoError(t, os.WriteFile(path.Join(sourceInnodbDataDir, "innodb_data_1"), []byte("innodb data 1 contents"), os.ModePerm)) - needIt, err := needInnoDBRedoLogSubdir() + needIt, err := blackbox.NeedInnoDBRedoLogSubdir() require.NoError(t, err) if needIt { newPath := path.Join(sourceInnodbLogDir, mysql.DynamicRedoLogSubdir) @@ -591,7 +591,7 @@ func TestRestoreUnreachablePrimary(t *testing.T) { } require.NoError(t, os.WriteFile(path.Join(sourceInnodbDataDir, "innodb_data_1"), []byte("innodb data 1 contents"), os.ModePerm)) - needIt, err := needInnoDBRedoLogSubdir() + needIt, err := blackbox.NeedInnoDBRedoLogSubdir() require.NoError(t, err) if needIt { newPath := path.Join(sourceInnodbLogDir, mysql.DynamicRedoLogSubdir) @@ -767,7 +767,7 @@ func TestDisableActiveReparents(t *testing.T) { } require.NoError(t, os.WriteFile(path.Join(sourceInnodbDataDir, "innodb_data_1"), []byte("innodb data 1 contents"), os.ModePerm)) - needIt, err := needInnoDBRedoLogSubdir() + needIt, err := blackbox.NeedInnoDBRedoLogSubdir() require.NoError(t, err) if needIt { newPath := path.Join(sourceInnodbLogDir, mysql.DynamicRedoLogSubdir) @@ -877,25 +877,3 @@ func TestDisableActiveReparents(t *testing.T) { assert.False(t, destTablet.FakeMysqlDaemon.Replicating) assert.True(t, destTablet.FakeMysqlDaemon.Running) } - -// needInnoDBRedoLogSubdir indicates whether we need to create a redo log subdirectory. -// Starting with MySQL 8.0.30, the InnoDB redo logs are stored in a subdirectory of the -// (/. by default) called "#innodb_redo". See: -// -// https://dev.mysql.com/doc/refman/8.0/en/innodb-redo-log.html#innodb-modifying-redo-log-capacity -func needInnoDBRedoLogSubdir() (needIt bool, err error) { - mysqldVersionStr, err := mysqlctl.GetVersionString() - if err != nil { - return needIt, err - } - _, sv, err := mysqlctl.ParseVersionString(mysqldVersionStr) - if err != nil { - return needIt, err - } - versionStr := fmt.Sprintf("%d.%d.%d", sv.Major, sv.Minor, sv.Patch) - capableOf := mysql.ServerVersionCapableOf(versionStr) - if capableOf == nil { - return needIt, fmt.Errorf("cannot determine database flavor details for version %s", versionStr) - } - return capableOf(capabilities.DynamicRedoLogCapacityFlavorCapability) -} diff --git a/proto/binlogdata.proto b/proto/binlogdata.proto index 595760dcd52..e1df792776b 100644 --- a/proto/binlogdata.proto +++ b/proto/binlogdata.proto @@ -353,6 +353,10 @@ message FieldEvent { repeated query.Field fields = 2; string keyspace = 3; string shard = 4; + + // Field numbers in the gap between shard (4) and enum_set_string_values + // (25) are NOT reserved and can be used. + // Are ENUM and SET field values already mapped to strings in the ROW // events? This allows us to transition VTGate VStream consumers from // the pre v20 behavior of having to do this mapping themselves to the @@ -362,6 +366,9 @@ message FieldEvent { // vstreams managed by the vstreamManager. bool enum_set_string_values = 25; bool is_internal_table = 26; // set for sidecardb tables + + // Add new members in the field number gap between shard (4) and + // enum_set_string_values (25). } // ShardGtid contains the GTID position for one shard. diff --git a/proto/replicationdata.proto b/proto/replicationdata.proto index 7107332233b..e788fc64bc3 100644 --- a/proto/replicationdata.proto +++ b/proto/replicationdata.proto @@ -50,6 +50,7 @@ message Status { bool has_replication_filters = 22; bool ssl_allowed = 23; bool replication_lag_unknown = 24; + bool backup_running = 25; } // Configuration holds replication configuration information gathered from performance_schema and global variables. diff --git a/proto/vtadmin.proto b/proto/vtadmin.proto index 78f086ec345..963d1fa5779 100644 --- a/proto/vtadmin.proto +++ b/proto/vtadmin.proto @@ -388,7 +388,11 @@ message WorkflowSwitchTrafficRequest { message ApplySchemaRequest { string cluster_id = 1; - vtctldata.ApplySchemaRequest request = 2; + // Request.Sql will be overriden by this Sql field. + string sql = 2; + // Request.CallerId will be overriden by this CallerId field. + string caller_id = 3; + vtctldata.ApplySchemaRequest request = 4; } message CancelSchemaMigrationRequest { diff --git a/proto/vtgate.proto b/proto/vtgate.proto index 5b080178218..aadf211f0a2 100644 --- a/proto/vtgate.proto +++ b/proto/vtgate.proto @@ -76,6 +76,8 @@ message Session { // reserved connection if a dedicated connection is needed int64 reserved_id = 4; bool vindex_only = 5; + // rows_affected tracks if any query has modified the rows. + bool rows_affected = 6; } // shard_sessions keep track of per-shard transaction info. repeated ShardSession shard_sessions = 2; diff --git a/test.go b/test.go index 14f51a06e9d..95d62af892f 100755 --- a/test.go +++ b/test.go @@ -77,7 +77,7 @@ For example: // Flags var ( flavor = flag.String("flavor", "mysql80", "comma-separated bootstrap flavor(s) to run against (when using Docker mode). Available flavors: all,"+flavors) - bootstrapVersion = flag.String("bootstrap-version", "38", "the version identifier to use for the docker images") + bootstrapVersion = flag.String("bootstrap-version", "39", "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/ci_workflow_gen.go b/test/ci_workflow_gen.go index 7956c491408..b24db1154fb 100644 --- a/test/ci_workflow_gen.go +++ b/test/ci_workflow_gen.go @@ -103,6 +103,7 @@ var ( "vtgate_vindex_heavy", "vtgate_vschema", "vtgate_queries", + "vtgate_plantests", "vtgate_schema_tracker", "vtgate_foreignkey_stress", "vtorc", @@ -157,6 +158,9 @@ var ( "vreplication_migrate", "vreplication_vtctldclient_vdiff2_movetables_tz", } + clusterRequiringMinio = []string{ + "21", + } ) type unitTest struct { @@ -174,6 +178,7 @@ type clusterTest struct { EnableBinlogTransactionCompression bool PartialKeyspace bool Cores16 bool + NeedsMinio bool } type vitessTesterTest struct { @@ -286,6 +291,13 @@ func generateClusterWorkflows(list []string, tpl string) { break } } + minioClusters := canonnizeList(clusterRequiringMinio) + for _, minioCluster := range minioClusters { + if minioCluster == cluster { + test.NeedsMinio = true + break + } + } if mysqlVersion == mysql57 { test.Platform = string(mysql57) } diff --git a/test/config.json b/test/config.json index c911232ce74..da0026f0125 100644 --- a/test/config.json +++ b/test/config.json @@ -136,6 +136,15 @@ "RetryMax": 1, "Tags": [] }, + "backup_s3": { + "File": "unused.go", + "Args": ["vitess.io/vitess/go/test/endtoend/backup/s3", "-timeout", "30m"], + "Command": [], + "Manual": false, + "Shard": "21", + "RetryMax": 1, + "Tags": [] + }, "backup_only": { "File": "unused.go", "Args": ["vitess.io/vitess/go/test/endtoend/backup/vtbackup", "-timeout", "20m"], @@ -340,17 +349,6 @@ "RetryMax": 1, "Tags": [] }, - "pitr": { - "File": "unused.go", - "Args": ["vitess.io/vitess/go/test/endtoend/recovery/pitr"], - "Command": [], - "Manual": false, - "Shard": "10", - "RetryMax": 1, - "Tags": [ - "site_test" - ] - }, "recovery": { "File": "unused.go", "Args": ["vitess.io/vitess/go/test/endtoend/recovery/unshardedrecovery"], @@ -887,6 +885,15 @@ "RetryMax": 1, "Tags": [] }, + "vtgate_plantests": { + "File": "unused.go", + "Args": ["vitess.io/vitess/go/test/endtoend/vtgate/plan_tests"], + "Command": [], + "Manual": false, + "Shard": "vtgate_plantests", + "RetryMax": 1, + "Tags": [] + }, "vtgate_unsharded": { "File": "unused.go", "Args": ["vitess.io/vitess/go/test/endtoend/vtgate/unsharded"], @@ -1238,6 +1245,17 @@ "RetryMax": 1, "Tags": [] }, + "vtop_example": { + "File": "", + "Args": [], + "Command": [ + "test/vtop_example.sh" + ], + "Manual": false, + "Shard": "", + "RetryMax": 1, + "Tags": [] + }, "vtorc_primary_failure": { "File": "unused.go", "Args": ["vitess.io/vitess/go/test/endtoend/vtorc/primaryfailure"], @@ -1373,6 +1391,15 @@ "RetryMax": 1, "Tags": [] }, + "loopkup_index": { + "File": "unused.go", + "Args": ["vitess.io/vitess/go/test/endtoend/vreplication", "-run", "TestLookupIndex"], + "Command": [], + "Manual": false, + "Shard": "vreplication_vtctldclient_vdiff2_movetables_tz", + "RetryMax": 1, + "Tags": [] + }, "vtadmin": { "File": "unused.go", "Args": ["vitess.io/vitess/go/test/endtoend/vtadmin"], diff --git a/test/templates/cluster_endtoend_test.tpl b/test/templates/cluster_endtoend_test.tpl index 01f4555e303..8d0a2f650b5 100644 --- a/test/templates/cluster_endtoend_test.tpl +++ b/test/templates/cluster_endtoend_test.tpl @@ -14,7 +14,7 @@ env: jobs: build: name: Run endtoend tests on {{.Name}} - runs-on: {{if .Cores16}}gh-hosted-runners-16cores-1{{else}}ubuntu-latest{{end}} + runs-on: {{if .Cores16}}gh-hosted-runners-16cores-1-24.04{{else}}ubuntu-24.04{{end}} steps: - name: Skip CI @@ -124,10 +124,19 @@ jobs: # Get key to latest MySQL repo 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.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 -qq update + + # We have to install this old version of libaio1 in case we end up testing with MySQL 5.7. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + # Install everything else we need, and configure sudo apt-get -qq install -y mysql-server mysql-shell mysql-client make unzip g++ etcd-client etcd-server curl git wget eatmydata xz-utils libncurses6 @@ -148,6 +157,15 @@ jobs: {{end}} + {{if .NeedsMinio }} + - name: Install Minio + if: steps.skip-workflow.outputs.skip-workflow == 'false' + run: | + wget https://dl.min.io/server/minio/release/linux-amd64/minio + chmod +x minio + mv minio /usr/local/bin + {{end}} + {{if .MakeTools}} - name: Installing zookeeper and consul diff --git a/test/templates/cluster_endtoend_test_docker.tpl b/test/templates/cluster_endtoend_test_docker.tpl index f68e4223c75..f7e8aa2c1d8 100644 --- a/test/templates/cluster_endtoend_test_docker.tpl +++ b/test/templates/cluster_endtoend_test_docker.tpl @@ -6,7 +6,7 @@ permissions: read-all jobs: build: name: Run endtoend tests on {{.Name}} - runs-on: {{if .Cores16}}gh-hosted-runners-16cores-1{{else}}ubuntu-latest{{end}} + runs-on: {{if .Cores16}}gh-hosted-runners-16cores-1-24.04{{else}}ubuntu-24.04{{end}} steps: - name: Skip CI diff --git a/test/templates/cluster_endtoend_test_mysql57.tpl b/test/templates/cluster_endtoend_test_mysql57.tpl index b51ffc9110e..f4152c939b0 100644 --- a/test/templates/cluster_endtoend_test_mysql57.tpl +++ b/test/templates/cluster_endtoend_test_mysql57.tpl @@ -19,7 +19,7 @@ env: jobs: build: name: Run endtoend tests on {{.Name}} - runs-on: {{if .Cores16}}gh-hosted-runners-16cores-1{{else}}ubuntu-latest{{end}} + runs-on: {{if .Cores16}}gh-hosted-runners-16cores-1-24.04{{else}}ubuntu-24.04{{end}} steps: - name: Skip CI @@ -126,13 +126,17 @@ jobs: # Get key to latest MySQL repo sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 echo mysql-apt-config mysql-apt-config/select-server select mysql-5.7 | sudo debconf-set-selections sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update + # We have to install this old version of libaio1. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb sudo DEBIAN_FRONTEND="noninteractive" apt-get install -y mysql-client=5.7* mysql-community-server=5.7* mysql-server=5.7* libncurses6 sudo apt-get install -y make unzip g++ etcd-client etcd-server curl git wget eatmydata diff --git a/test/templates/cluster_vitess_tester.tpl b/test/templates/cluster_vitess_tester.tpl index f0b5838d8e8..b8d77754ba6 100644 --- a/test/templates/cluster_vitess_tester.tpl +++ b/test/templates/cluster_vitess_tester.tpl @@ -14,7 +14,7 @@ env: jobs: build: name: Run endtoend tests on {{.Name}} - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -93,7 +93,7 @@ jobs: # Get key to latest MySQL repo 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.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-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 -qq update diff --git a/test/templates/dockerfile.tpl b/test/templates/dockerfile.tpl index 82388850947..af4376d3ca9 100644 --- a/test/templates/dockerfile.tpl +++ b/test/templates/dockerfile.tpl @@ -1,4 +1,4 @@ -ARG bootstrap_version=38 +ARG bootstrap_version=39 ARG image="vitess/bootstrap:${bootstrap_version}-{{.Platform}}" FROM "${image}" diff --git a/test/templates/unit_test.tpl b/test/templates/unit_test.tpl index c47b7a1d796..3704aebac4e 100644 --- a/test/templates/unit_test.tpl +++ b/test/templates/unit_test.tpl @@ -14,7 +14,7 @@ env: jobs: test: name: {{.Name}} - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Skip CI @@ -87,20 +87,20 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.unit_tests == 'true' run: | export DEBIAN_FRONTEND="noninteractive" - sudo apt-get -qq update + sudo apt-get update # Uninstall any previously installed MySQL first sudo systemctl stop apparmor - sudo DEBIAN_FRONTEND="noninteractive" apt-get -qq remove -y --purge mysql-server mysql-client mysql-common - sudo apt-get -qq -y autoremove - sudo apt-get -qq -y autoclean + 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 - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.32-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.33-1_all.deb {{if (eq .Platform "mysql57")}} # Bionic packages are still compatible for Jammy since there's no MySQL 5.7 @@ -108,25 +108,32 @@ jobs: echo mysql-apt-config mysql-apt-config/repo-codename select bionic | sudo debconf-set-selections echo mysql-apt-config mysql-apt-config/select-server select mysql-5.7 | sudo debconf-set-selections sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* - sudo apt-get -qq update - sudo DEBIAN_FRONTEND="noninteractive" apt-get -qq install -y mysql-client=5.7* mysql-community-server=5.7* mysql-server=5.7* libncurses6 + sudo apt-get update + # We have to install this old version of libaio1. See also: + # https://bugs.launchpad.net/ubuntu/+source/libaio/+bug/2067501 + curl -L -O http://mirrors.kernel.org/ubuntu/pool/main/liba/libaio/libaio1_0.3.112-13build1_amd64.deb + sudo dpkg -i libaio1_0.3.112-13build1_amd64.deb + # libtinfo5 is also needed for older MySQL 5.7 builds. + curl -L -O http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb + sudo DEBIAN_FRONTEND="noninteractive" apt-get install -y mysql-client=5.7* mysql-community-server=5.7* mysql-server=5.7* libncurses6 {{end}} {{if (eq .Platform "mysql80")}} 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 -qq update - sudo DEBIAN_FRONTEND="noninteractive" apt-get -qq install -y mysql-server mysql-client + sudo apt-get update + sudo DEBIAN_FRONTEND="noninteractive" apt-get install -y mysql-server mysql-client {{end}} {{if (eq .Platform "mysql84")}} echo mysql-apt-config mysql-apt-config/select-server select mysql-8.4-lts | sudo debconf-set-selections sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* - sudo apt-get -qq update - sudo DEBIAN_FRONTEND="noninteractive" apt-get -qq install -y mysql-server mysql-client + sudo apt-get update + sudo DEBIAN_FRONTEND="noninteractive" apt-get install -y mysql-server mysql-client {{end}} - sudo apt-get -qq install -y make unzip g++ curl git wget ant openjdk-11-jdk eatmydata + 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/ diff --git a/test/vtop_example.sh b/test/vtop_example.sh index 5ff90a2be7e..c537c0f844c 100755 --- a/test/vtop_example.sh +++ b/test/vtop_example.sh @@ -482,11 +482,12 @@ EOF waitForKeyspaceToBeServing customer 80- 1 } +kind delete cluster --name kind || true # Build the docker image for vitess/lite using the local code docker build -f docker/lite/Dockerfile -t vitess/lite:pr . # Build the docker image for vitess/vtadmin using the local code -docker build -f docker/binaries/vtadmin/Dockerfile --build-arg VT_BASE_VER=pr -t vitess/vtadmin:pr . +docker build -f docker/binaries/vtadmin/Dockerfile --build-arg VT_BASE_VER=pr -t vitess/vtadmin:pr ./docker/binaries/vtadmin # Print the docker images available docker image ls diff --git a/tools/get_kubectl_kind.sh b/tools/get_kubectl_kind.sh index 57df414fdd8..169b120aaa0 100755 --- a/tools/get_kubectl_kind.sh +++ b/tools/get_kubectl_kind.sh @@ -12,7 +12,7 @@ source build.env mkdir -p "$VTROOT/bin" cd "$VTROOT/bin" -KUBE_VERSION="${KUBE_VERSION:-v1.21.1}" +KUBE_VERSION="${KUBE_VERSION:-v1.31.0}" KUBERNETES_RELEASE_URL="${KUBERNETES_RELEASE_URL:-https://dl.k8s.io}" # Download kubectl if needed. @@ -28,7 +28,7 @@ ln -sf "kubectl-${KUBE_VERSION}" kubectl if ! command -v kind &> /dev/null then echo "Downloading kind..." - curl -L https://kind.sigs.k8s.io/dl/v0.12.0/kind-linux-amd64 > "kind" + curl -L https://kind.sigs.k8s.io/dl/v0.22.0/kind-linux-amd64 > "kind" chmod +x "kind" echo "Installed kind" else diff --git a/tools/map-shard-for-value/Makefile b/tools/map-shard-for-value/Makefile new file mode 100644 index 00000000000..61bc88ac0ed --- /dev/null +++ b/tools/map-shard-for-value/Makefile @@ -0,0 +1,22 @@ +# 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. + +build: + go build map-shard-for-value.go + +test: + echo "1\n-1\n99" | go run map-shard-for-value.go --total_shards=4 --vindex=xxhash + +clean: + rm -f map-shard-for-value diff --git a/tools/map-shard-for-value/map-shard-for-value.go b/tools/map-shard-for-value/map-shard-for-value.go new file mode 100755 index 00000000000..18a092d1371 --- /dev/null +++ b/tools/map-shard-for-value/map-shard-for-value.go @@ -0,0 +1,207 @@ +/* +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 ( + "bufio" + "context" + "encoding/hex" + "fmt" + "log" + "os" + "strconv" + "strings" + + flag "github.com/spf13/pflag" + + "vitess.io/vitess/go/vt/topo" + + "vitess.io/vitess/go/sqltypes" + "vitess.io/vitess/go/vt/key" + "vitess.io/vitess/go/vt/proto/topodata" + "vitess.io/vitess/go/vt/vtgate/vindexes" +) + +/* + * This tool reads a list of values from stdin and prints the + * corresponding keyspace ID and shard for each value. It uses the given vindex + * and shard ranges to determine the shard. The vindex is expected to be a + * single-column vindex. The shard ranges are specified as a comma-separated + * list of key ranges, example "-80,80-". + * If you have uniformly distributed shards, you can specify the total number + * of shards using the -total_shards flag, and the tool will generate the shard ranges + * using the same logic as the Vitess operator does (using the key.GenerateShardRanges() function). + * + * Example usage: + * echo "1\n2\n3" | go run shard-from-id.go -vindex=hash -shards=-80,80- + * + * Currently tested only for integer values and hash/xxhash vindexes. + */ + +func mapShard(allShards []*topodata.ShardReference, ksid key.DestinationKeyspaceID) (string, error) { + foundShard := "" + addShard := func(shard string) error { + foundShard = shard + return nil + } + if err := ksid.Resolve(allShards, addShard); err != nil { + return "", fmt.Errorf("failed to resolve keyspace ID: %v:: %s", ksid.String(), err) + } + + if foundShard == "" { + return "", fmt.Errorf("no shard found for keyspace ID: %v", ksid) + } + return foundShard, nil +} + +func selectShard(vindex vindexes.Vindex, value sqltypes.Value, allShards []*topodata.ShardReference) (string, key.DestinationKeyspaceID, error) { + ctx := context.Background() + + destinations, err := vindexes.Map(ctx, vindex, nil, [][]sqltypes.Value{{value}}) + if err != nil { + return "", nil, fmt.Errorf("failed to map value to keyspace ID: %w", err) + } + + if len(destinations) != 1 { + return "", nil, fmt.Errorf("unexpected number of destinations: %d", len(destinations)) + } + + ksid, ok := destinations[0].(key.DestinationKeyspaceID) + if !ok { + return "", nil, fmt.Errorf("unexpected destination type: %T", destinations[0]) + } + + foundShard, err := mapShard(allShards, ksid) + if err != nil { + return "", nil, fmt.Errorf("failed to map shard, original value %v, keyspace id %s: %w", value, ksid, err) + } + return foundShard, ksid, nil +} + +func getValue(valueStr, valueType string) (sqltypes.Value, error) { + var value sqltypes.Value + + switch valueType { + case "int": + valueInt, err := strconv.ParseInt(valueStr, 10, 64) + if err != nil { + return value, fmt.Errorf("failed to parse int value: %w", err) + } + value = sqltypes.NewInt64(valueInt) + case "uint": + valueUint, err := strconv.ParseUint(valueStr, 10, 64) + if err != nil { + return value, fmt.Errorf("failed to parse uint value: %w", err) + } + value = sqltypes.NewUint64(valueUint) + case "string": + value = sqltypes.NewVarChar(valueStr) + default: + return value, fmt.Errorf("unsupported value type: %s", valueType) + } + + return value, nil +} + +func getShardMap(shardsCSV *string) []*topodata.ShardReference { + var allShards []*topodata.ShardReference + + for _, shard := range strings.Split(*shardsCSV, ",") { + _, keyRange, err := topo.ValidateShardName(shard) + if err != nil { + log.Fatalf("invalid shard range: %s", shard) + } + allShards = append(allShards, &topodata.ShardReference{ + Name: shard, + KeyRange: keyRange, + }) + } + return allShards +} + +type output struct { + Value string + KeyspaceID string + Shard string +} + +func processValues(scanner *bufio.Scanner, shardsCSV *string, vindexName string, valueType string) ([]output, error) { + allShards := getShardMap(shardsCSV) + + vindex, err := vindexes.CreateVindex(vindexName, vindexName, nil) + if err != nil { + return nil, fmt.Errorf("failed to create vindex: %v", err) + } + var outputs []output + for scanner.Scan() { + valueStr := scanner.Text() + if valueStr == "" { + continue + } + value, err := getValue(valueStr, valueType) + if err != nil { + return nil, fmt.Errorf("failed to get value for: %v, value_type %s:: %v", valueStr, valueType, err) + } + shard, ksid, err := selectShard(vindex, value, allShards) + if err != nil { + // ignore errors so that we can go ahead with the computation for other values + continue + } + outputs = append(outputs, output{Value: valueStr, KeyspaceID: hex.EncodeToString(ksid), Shard: shard}) + } + return outputs, nil +} + +func printOutput(outputs []output) { + fmt.Println("value,keyspaceID,shard") + for _, output := range outputs { + fmt.Printf("%s,%s,%s\n", output.Value, output.KeyspaceID, output.Shard) + } +} + +func main() { + // Explicitly configuring the logger since it was flaky in displaying logs locally without this. + log.SetOutput(os.Stderr) + log.SetFlags(log.LstdFlags) + log.SetPrefix("LOG: ") + + vindexName := flag.String("vindex", "xxhash", "name of the vindex") + shardsCSV := flag.String("shards", "", "comma-separated list of shard ranges") + totalShards := flag.Int("total_shards", 0, "total number of uniformly distributed shards") + valueType := flag.String("value_type", "int", "type of the value (int, uint, or string)") + flag.Parse() + + if *totalShards > 0 { + if *shardsCSV != "" { + log.Fatalf("cannot specify both total_shards and shards") + } + shardArr, err := key.GenerateShardRanges(*totalShards) + if err != nil { + log.Fatalf("failed to generate shard ranges: %v", err) + } + *shardsCSV = strings.Join(shardArr, ",") + } + if *shardsCSV == "" { + log.Fatal("shards or total_shards must be specified") + } + scanner := bufio.NewScanner(os.Stdin) + outputs, err := processValues(scanner, shardsCSV, *vindexName, *valueType) + if err != nil { + log.Fatalf("failed to process values: %v", err) + } + printOutput(outputs) +} diff --git a/tools/map-shard-for-value/map-shard-for-value.md b/tools/map-shard-for-value/map-shard-for-value.md new file mode 100644 index 00000000000..17daf7f5fe5 --- /dev/null +++ b/tools/map-shard-for-value/map-shard-for-value.md @@ -0,0 +1,47 @@ +## Map Shard for Value Tool + +### Overview + +The `map-shard-for-value` tool maps a given value to a specific shard. This tool helps in determining +which shard a particular value belongs to, based on the vindex algorithm and shard ranges. + +### Features +- + +- Allows specifying the vindex type (e.g., `hash`, `xxhash`). +- Allows specifying the shard list of (for uniformly distributed shard ranges) the total number of shards to generate. +- Designed as a _filter_: Reads input values from `stdin` and outputs the corresponding shard information, so it can be + used to map values from a file or another program. + +### Usage + +```sh +make build +``` + +```sh +echo "1\n-1\n99" | ./map-shard-for-value --total_shards=4 --vindex=xxhash +value,keyspaceID,shard +1,d46405367612b4b7,c0- +-1,d8e2a6a7c8c7623d,c0- +99,200533312244abca,-40 + +echo "1\n-1\n99" | ./map-shard-for-value --vindex=hash --shards="-80,80-" +value,keyspaceID,shard +1,166b40b44aba4bd6,-80 +-1,355550b2150e2451,-80 +99,2c40ad56f4593c47,-80 +``` + +#### Flags + +- `--vindex`: Specifies the name of the vindex to use (e.g., `hash`, `xxhash`) (default `xxhash`) + +One (and only one) of these is required: + +- `--shards`: Comma-separated list of shard ranges +- `--total_shards`: Total number of shards, only if shards are uniformly distributed + +Optional: +- `--value_type`: Type of the value to map, one of int, uint, string (default `int`) + diff --git a/tools/map-shard-for-value/map-shard-for-value_test.go b/tools/map-shard-for-value/map-shard-for-value_test.go new file mode 100644 index 00000000000..ca014818bb9 --- /dev/null +++ b/tools/map-shard-for-value/map-shard-for-value_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 main + +import ( + "bufio" + "fmt" + "strings" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestProcess(t *testing.T) { + type testCase struct { + name string + shardsCSV string + vindexType string + values []int + valueType string + expected []output + } + testCases := []testCase{ + { + name: "hash,2 shards", + shardsCSV: "-80,80-", + vindexType: "hash", + values: []int{1, 99}, + valueType: "int", + expected: []output{ + { + Value: "1", + KeyspaceID: "166b40b44aba4bd6", + Shard: "-80", + }, + { + Value: "99", + KeyspaceID: "2c40ad56f4593c47", + Shard: "-80", + }, + }, + }, + { + name: "xxhash,4 shards", + shardsCSV: "-40,40-80,80-c0,c0-", + vindexType: "xxhash", + values: []int{1, 99}, + valueType: "int", + expected: []output{ + { + Value: "1", + KeyspaceID: "d46405367612b4b7", + Shard: "c0-", + }, + { + Value: "99", + KeyspaceID: "200533312244abca", + Shard: "-40", + }, + }, + }, + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + var input strings.Builder + for _, num := range tc.values { + fmt.Fprintf(&input, "%d\n", num) + } + reader := strings.NewReader(input.String()) + scanner := bufio.NewScanner(reader) + got, err := processValues(scanner, &tc.shardsCSV, tc.vindexType, tc.valueType) + require.NoError(t, err) + require.EqualValues(t, tc.expected, got) + }) + } +} diff --git a/web/vtadmin/package-lock.json b/web/vtadmin/package-lock.json index 6004278321a..8ad7c67a5b4 100644 --- a/web/vtadmin/package-lock.json +++ b/web/vtadmin/package-lock.json @@ -10863,15 +10863,16 @@ } }, "node_modules/nanoid": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", + "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", "funding": [ { "type": "github", "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "bin": { "nanoid": "bin/nanoid.cjs" }, diff --git a/web/vtadmin/src/api/http.ts b/web/vtadmin/src/api/http.ts index 3f75330d240..674df961ef0 100644 --- a/web/vtadmin/src/api/http.ts +++ b/web/vtadmin/src/api/http.ts @@ -1068,3 +1068,41 @@ export const showVDiff = async ({ clusterID, request }: ShowVDiffParams) => { return vtadmin.VDiffShowResponse.create(result); }; + +export const fetchSchemaMigrations = async (request: vtadmin.IGetSchemaMigrationsRequest) => { + const { result } = await vtfetch(`/api/migrations/`, { + body: JSON.stringify(request), + method: 'post', + }); + + const err = vtadmin.GetSchemaMigrationsResponse.verify(result); + if (err) throw Error(err); + + return vtadmin.GetSchemaMigrationsResponse.create(result); +}; + +export interface ApplySchemaParams { + clusterID: string; + keyspace: string; + callerID: string; + sql: string; + request: vtctldata.IApplySchemaRequest; +} + +export const applySchema = async ({ clusterID, keyspace, callerID, sql, request }: ApplySchemaParams) => { + const body = { + sql, + caller_id: callerID, + request, + }; + + const { result } = await vtfetch(`/api/migration/${clusterID}/${keyspace}`, { + body: JSON.stringify(body), + method: 'post', + }); + + const err = vtctldata.ApplySchemaResponse.verify(result); + if (err) throw Error(err); + + return vtctldata.ApplySchemaResponse.create(result); +}; diff --git a/web/vtadmin/src/components/App.tsx b/web/vtadmin/src/components/App.tsx index 79bb0d5c354..fd0f772ae19 100644 --- a/web/vtadmin/src/components/App.tsx +++ b/web/vtadmin/src/components/App.tsx @@ -46,6 +46,8 @@ import { Transaction } from './routes/transaction/Transaction'; import { CreateReshard } from './routes/createWorkflow/CreateReshard'; import { CreateMaterialize } from './routes/createWorkflow/CreateMaterialize'; import { TopologyTree } from './routes/topologyTree/TopologyTree'; +import { SchemaMigrations } from './routes/SchemaMigrations'; +import { CreateSchemaMigration } from './routes/createSchemaMigration/CreateSchemaMigration'; export const App = () => { return ( @@ -141,6 +143,16 @@ export const App = () => { + + + + + {!isReadOnlyMode() && ( + + + + )} + diff --git a/web/vtadmin/src/components/NavRail.tsx b/web/vtadmin/src/components/NavRail.tsx index 9f9e1bf1681..b30cd165684 100644 --- a/web/vtadmin/src/components/NavRail.tsx +++ b/web/vtadmin/src/components/NavRail.tsx @@ -65,6 +65,9 @@ export const NavRail = () => {
    +
  • + +
  • diff --git a/web/vtadmin/src/components/routes/createWorkflow/ErrorDialog.tsx b/web/vtadmin/src/components/dialog/ErrorDialog.tsx similarity index 94% rename from web/vtadmin/src/components/routes/createWorkflow/ErrorDialog.tsx rename to web/vtadmin/src/components/dialog/ErrorDialog.tsx index 25ac5dedb0b..087876e4cd2 100644 --- a/web/vtadmin/src/components/routes/createWorkflow/ErrorDialog.tsx +++ b/web/vtadmin/src/components/dialog/ErrorDialog.tsx @@ -14,8 +14,8 @@ * limitations under the License. */ import React from 'react'; -import Dialog from '../../dialog/Dialog'; -import { Icon, Icons } from '../../Icon'; +import Dialog from './Dialog'; +import { Icon, Icons } from '../Icon'; export interface ErrorDialogProps { errorTitle?: string; diff --git a/web/vtadmin/src/components/routes/SchemaMigrations.tsx b/web/vtadmin/src/components/routes/SchemaMigrations.tsx new file mode 100644 index 00000000000..1761d26de49 --- /dev/null +++ b/web/vtadmin/src/components/routes/SchemaMigrations.tsx @@ -0,0 +1,195 @@ +/** + * 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. + */ +import { useEffect, useState } from 'react'; +import { useKeyspaces, useSchemaMigrations } from '../../hooks/api'; +import { DataCell } from '../dataTable/DataCell'; +import { DataTable } from '../dataTable/DataTable'; +import { ContentContainer } from '../layout/ContentContainer'; +import { WorkspaceHeader } from '../layout/WorkspaceHeader'; +import { WorkspaceTitle } from '../layout/WorkspaceTitle'; +import { QueryLoadingPlaceholder } from '../placeholders/QueryLoadingPlaceholder'; +import { useDocumentTitle } from '../../hooks/useDocumentTitle'; +import { vtadmin } from '../../proto/vtadmin'; +import { Select } from '../inputs/Select'; +import { ShardLink } from '../links/ShardLink'; +import { formatDateTime } from '../../util/time'; +import { ReadOnlyGate } from '../ReadOnlyGate'; +import { formatSchemaMigrationStatus } from '../../util/schemaMigrations'; +import { Link } from 'react-router-dom'; +import { TabletLink } from '../links/TabletLink'; +import { formatAlias } from '../../util/tablets'; +import { useURLQuery } from '../../hooks/useURLQuery'; + +const COLUMNS = ['UUID', 'Status', 'DDL Action', 'Timestamps', 'Stage', 'Progress']; + +export const SchemaMigrations = () => { + useDocumentTitle('Schema Migrations'); + + const { query, replaceQuery } = useURLQuery(); + const urlKeyspace = query['keyspace']; + const urlCluster = query['cluster']; + + const keyspacesQuery = useKeyspaces(); + const { data: keyspaces = [], ...ksQuery } = keyspacesQuery; + + const [selectedKeyspace, setSelectedKeypsace] = useState(); + + const request: vtadmin.IGetSchemaMigrationsRequest = { + cluster_requests: [ + { + cluster_id: selectedKeyspace && selectedKeyspace.cluster?.id, + request: { + keyspace: selectedKeyspace && selectedKeyspace.keyspace?.name, + }, + }, + ], + }; + + const schemaMigrationsQuery = useSchemaMigrations(request, { + enabled: !!selectedKeyspace, + }); + + const schemaMigrations = schemaMigrationsQuery.data ? schemaMigrationsQuery.data.schema_migrations : []; + + const handleKeyspaceChange = (ks: vtadmin.Keyspace | null | undefined) => { + setSelectedKeypsace(ks); + + if (ks) { + replaceQuery({ keyspace: ks.keyspace?.name, cluster: ks.cluster?.id }); + } else { + replaceQuery({ keyspace: undefined, cluster: undefined }); + } + }; + + useEffect(() => { + if (urlKeyspace && urlCluster) { + const keyspace = keyspaces.find( + (ks) => ks.cluster?.id === String(urlCluster) && ks.keyspace?.name === String(urlKeyspace) + ); + + if (keyspace) { + setSelectedKeypsace(keyspace); + } else if (!ksQuery.isLoading) { + replaceQuery({ keyspace: undefined, cluster: undefined }); + } + } else { + setSelectedKeypsace(undefined); + } + }, [urlKeyspace, urlCluster, keyspaces, ksQuery.isLoading, replaceQuery]); + + const renderRows = (rows: vtadmin.ISchemaMigration[]) => { + return rows.map((row) => { + const migrationInfo = row.schema_migration; + + if (!migrationInfo) return <>; + + return ( + + +
    {migrationInfo.uuid}
    +
    + Tablet{' '} + + {formatAlias(migrationInfo.tablet)} + +
    +
    + Shard{' '} + + {`${migrationInfo.keyspace}/${migrationInfo.shard}`} + +
    +
    + +
    {formatSchemaMigrationStatus(migrationInfo)}
    +
    + {migrationInfo.ddl_action ? migrationInfo.ddl_action : '-'} + + {migrationInfo.added_at && ( +
    + Added + {formatDateTime(migrationInfo.added_at?.seconds)} +
    + )} + {migrationInfo.requested_at && ( +
    + Requested + {formatDateTime(migrationInfo.requested_at?.seconds)} +
    + )} + {migrationInfo.started_at && ( +
    + Started + {formatDateTime(migrationInfo.started_at?.seconds)} +
    + )} + {migrationInfo.completed_at && ( +
    + Completed + {formatDateTime(migrationInfo.completed_at?.seconds)} +
    + )} +
    + {migrationInfo.stage ? migrationInfo.stage : '-'} + {migrationInfo.progress ? `${migrationInfo.progress}%` : '-'} + + ); + }); + }; + + return ( +
    + +
    + Schema Migrations + +
    + + Create Schema Migration Request + +
    +
    +
    +
    + + +
    + ks?.keyspace?.name || ''} + items={clusterKeyspaces} + label="Keyspace" + onChange={(ks) => setFormData({ ...formData, keyspace: ks?.keyspace?.name || '' })} + placeholder={keyspacesQuery.isLoading ? 'Loading keyspaces...' : 'Select a keyspace'} + renderItem={(ks) => `${ks?.keyspace?.name}`} + selectedItem={selectedKeyspace} + /> + +
    + +