Repository: vitessio/vitess
Pull request #11195 (2022-09-09 14:07:53)

Add support for views in vtgate
## Description
This PR is a subtask for the RFC.

Adds support for views that are expanded in the vtgate. In the vschema, if the views definition is available, like so:

```json
  "keyspaces": {
    "user": {
      "sharded": true,
      "tables": {
        "user": {},
        "user_extra": {}
      },
      "views": {
        "user_details_view": "select user.id, user_extra.col from user join user_extra on user.id = user_extra.user_id"
      }
    }
```

they will be used to expand into a derived table. Example:

The query:

```sql
select * from user_details_view
```

would get expanded so it looks like:

```sql
select * from (
  select user.id, user_extra.col from user join user_extra on user.id = user_extra.user_id
) as user_details_view
```

## Related Issue(s)

<!-- List related issues and pull requests. If this PR fixes an issue, please add it using Fixes #????  -->

- https://github.com/vitessio/vitess/issues/11559

## Checklist

-   [X] Tests were added or are not required
-   [X] Documentation was added or is not required - It will be added as part of the RFC

vitess-bot[bot]: ### Review Checklist
            
Hello reviewers! :wave: Please follow this checklist when reviewing this Pull Request.

#### General
- [ ] Ensure that the Pull Request has a descriptive title.
- [ ] If this is a change that users need to know about, please apply the `release notes (needs details)` label so that merging is blocked unless the summary release notes document is included.
- [ ] If a new flag is being introduced, review whether it is really needed. The flag names should be clear and intuitive (as far as possible), and the flag's help should be descriptive. Additionally, flag names should use dashes (`-`) as word separators rather than underscores (`_`).
- [ ] If a workflow is added or modified, each items in `Jobs` should be named in order to mark it as `required`. If the workflow should be required, the GitHub Admin should be notified.

#### Bug fixes
- [ ] There should be at least one unit or end-to-end test.
- [ ] The Pull Request description should either include a link to an issue that describes the bug OR an actual description of the bug and how to reproduce, along with a description of the fix.

#### Non-trivial changes
- [ ] There should be some code comments as to why things are implemented the way they are.

#### New/Existing features
- [ ] Should be documented, either by modifying the existing documentation or creating new documentation.
- [ ] New features should have a link to a feature request issue or an RFC that documents the use cases, corner cases and test cases.

#### Backward compatibility            
- [ ] Protobuf changes should be wire-compatible.
- [ ] Changes to `_vt` tables and RPCs need to be backward compatible.
- [ ] `vtctl` command output order should be stable and `awk`-able.

deepthi: As a new feature, this is worth writing up in an issue/RFC.
The UX is important, it means a change to how people currently define their views.
Some other things that come to mind:
- Are we adding `ALTER VSCHEMA CREATE VIEW` type functionality?
- How does this interact with existing support in online DDL for views (which works for unsharded, but not sharded)
- Do we expect to hit topo server object size limits?

github-actions[bot]: This PR is being marked as stale because it has been open for 30 days with no activity. To rectify, you may do any of the following:

- Push additional commits to the associated branch.
- Remove the stale label.
- Add a comment indicating why it is not stale.

If no action is taken within 7 days, this PR will be closed.
harshit-gangal: > Is the description still accurate? We don't intend to store views in vschema, do we? There was some offline discussion about storing them in `_vt`.

The idea behind storing views in topo is that it is related to Vschema and today we store VSchema in topo. Therefore it becomes the obvious choice.
If we plan to move VSchema out of topo then, views will also move out naturally.
GuptaManan100: Are we storign the views in `_vt` now?
frouioui: > Are we storign the views in _vt now?

@GuptaManan100, yes this will be done in https://github.com/vitessio/vitess/pull/11896

Patch:
diff --git a/.github/workflows/check_make_vtadmin_web_proto.yml b/.github/workflows/check_make_vtadmin_web_proto.yml
index 96a9354ba4..5d09351355 100644
--- a/.github/workflows/check_make_vtadmin_web_proto.yml
+++ b/.github/workflows/check_make_vtadmin_web_proto.yml
@@ -50,7 +50,7 @@ jobs:
       uses: actions/setup-go@v3
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.proto_changes == 'true'
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Setup Node
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.proto_changes == 'true'
diff --git a/.github/workflows/cluster_endtoend_12.yml b/.github/workflows/cluster_endtoend_12.yml
index 964b35b57b..dd382ab73a 100644
--- a/.github/workflows/cluster_endtoend_12.yml
+++ b/.github/workflows/cluster_endtoend_12.yml
@@ -65,7 +65,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/cluster_endtoend_13.yml b/.github/workflows/cluster_endtoend_13.yml
index 4635fd002f..0d18111bac 100644
--- a/.github/workflows/cluster_endtoend_13.yml
+++ b/.github/workflows/cluster_endtoend_13.yml
@@ -65,7 +65,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/cluster_endtoend_15.yml b/.github/workflows/cluster_endtoend_15.yml
index 75bf45f4bf..a244ad9505 100644
--- a/.github/workflows/cluster_endtoend_15.yml
+++ b/.github/workflows/cluster_endtoend_15.yml
@@ -65,7 +65,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/cluster_endtoend_18.yml b/.github/workflows/cluster_endtoend_18.yml
index 9054e35a69..4759c2c1f0 100644
--- a/.github/workflows/cluster_endtoend_18.yml
+++ b/.github/workflows/cluster_endtoend_18.yml
@@ -65,7 +65,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/cluster_endtoend_21.yml b/.github/workflows/cluster_endtoend_21.yml
index 0b4cb166cd..aa68ba29e0 100644
--- a/.github/workflows/cluster_endtoend_21.yml
+++ b/.github/workflows/cluster_endtoend_21.yml
@@ -65,7 +65,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/cluster_endtoend_22.yml b/.github/workflows/cluster_endtoend_22.yml
index f9de0421fd..74e2e4eff0 100644
--- a/.github/workflows/cluster_endtoend_22.yml
+++ b/.github/workflows/cluster_endtoend_22.yml
@@ -65,7 +65,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/cluster_endtoend_backup_pitr.yml b/.github/workflows/cluster_endtoend_backup_pitr.yml
index 496d315f7b..73cfcbdb02 100644
--- a/.github/workflows/cluster_endtoend_backup_pitr.yml
+++ b/.github/workflows/cluster_endtoend_backup_pitr.yml
@@ -65,7 +65,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/cluster_endtoend_backup_pitr_mysql57.yml b/.github/workflows/cluster_endtoend_backup_pitr_mysql57.yml
index 03466d15d1..525cb052c0 100644
--- a/.github/workflows/cluster_endtoend_backup_pitr_mysql57.yml
+++ b/.github/workflows/cluster_endtoend_backup_pitr_mysql57.yml
@@ -65,7 +65,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/cluster_endtoend_ers_prs_newfeatures_heavy.yml b/.github/workflows/cluster_endtoend_ers_prs_newfeatures_heavy.yml
index e8aaf9ee19..63bd8d31f1 100644
--- a/.github/workflows/cluster_endtoend_ers_prs_newfeatures_heavy.yml
+++ b/.github/workflows/cluster_endtoend_ers_prs_newfeatures_heavy.yml
@@ -65,7 +65,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/cluster_endtoend_mysql80.yml b/.github/workflows/cluster_endtoend_mysql80.yml
index ba648a0bd1..4858abf59a 100644
--- a/.github/workflows/cluster_endtoend_mysql80.yml
+++ b/.github/workflows/cluster_endtoend_mysql80.yml
@@ -65,7 +65,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/cluster_endtoend_mysql_server_vault.yml b/.github/workflows/cluster_endtoend_mysql_server_vault.yml
index ee26ab2db5..e7030484ec 100644
--- a/.github/workflows/cluster_endtoend_mysql_server_vault.yml
+++ b/.github/workflows/cluster_endtoend_mysql_server_vault.yml
@@ -65,7 +65,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/cluster_endtoend_onlineddl_declarative.yml b/.github/workflows/cluster_endtoend_onlineddl_declarative.yml
index eedff92ac5..4194f41c53 100644
--- a/.github/workflows/cluster_endtoend_onlineddl_declarative.yml
+++ b/.github/workflows/cluster_endtoend_onlineddl_declarative.yml
@@ -66,7 +66,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/cluster_endtoend_onlineddl_declarative_mysql57.yml b/.github/workflows/cluster_endtoend_onlineddl_declarative_mysql57.yml
index 24b34b00cb..33bbe8f72d 100644
--- a/.github/workflows/cluster_endtoend_onlineddl_declarative_mysql57.yml
+++ b/.github/workflows/cluster_endtoend_onlineddl_declarative_mysql57.yml
@@ -66,7 +66,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/cluster_endtoend_onlineddl_ghost.yml b/.github/workflows/cluster_endtoend_onlineddl_ghost.yml
index c05ea26ea2..a309227ddb 100644
--- a/.github/workflows/cluster_endtoend_onlineddl_ghost.yml
+++ b/.github/workflows/cluster_endtoend_onlineddl_ghost.yml
@@ -66,7 +66,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/cluster_endtoend_onlineddl_ghost_mysql57.yml b/.github/workflows/cluster_endtoend_onlineddl_ghost_mysql57.yml
index 085d439381..549b4485f9 100644
--- a/.github/workflows/cluster_endtoend_onlineddl_ghost_mysql57.yml
+++ b/.github/workflows/cluster_endtoend_onlineddl_ghost_mysql57.yml
@@ -66,7 +66,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/cluster_endtoend_onlineddl_revert.yml b/.github/workflows/cluster_endtoend_onlineddl_revert.yml
index 5988c79863..5b18fb1f55 100644
--- a/.github/workflows/cluster_endtoend_onlineddl_revert.yml
+++ b/.github/workflows/cluster_endtoend_onlineddl_revert.yml
@@ -66,7 +66,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/cluster_endtoend_onlineddl_revert_mysql57.yml b/.github/workflows/cluster_endtoend_onlineddl_revert_mysql57.yml
index 852ffa190a..0a507dc814 100644
--- a/.github/workflows/cluster_endtoend_onlineddl_revert_mysql57.yml
+++ b/.github/workflows/cluster_endtoend_onlineddl_revert_mysql57.yml
@@ -66,7 +66,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/cluster_endtoend_onlineddl_revertible.yml b/.github/workflows/cluster_endtoend_onlineddl_revertible.yml
index 1a372f02ae..545f872a83 100644
--- a/.github/workflows/cluster_endtoend_onlineddl_revertible.yml
+++ b/.github/workflows/cluster_endtoend_onlineddl_revertible.yml
@@ -66,7 +66,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/cluster_endtoend_onlineddl_revertible_mysql57.yml b/.github/workflows/cluster_endtoend_onlineddl_revertible_mysql57.yml
index 22a5e58e3f..98a3eab46d 100644
--- a/.github/workflows/cluster_endtoend_onlineddl_revertible_mysql57.yml
+++ b/.github/workflows/cluster_endtoend_onlineddl_revertible_mysql57.yml
@@ -66,7 +66,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/cluster_endtoend_onlineddl_scheduler.yml b/.github/workflows/cluster_endtoend_onlineddl_scheduler.yml
index 2a3367a93f..bac52adac6 100644
--- a/.github/workflows/cluster_endtoend_onlineddl_scheduler.yml
+++ b/.github/workflows/cluster_endtoend_onlineddl_scheduler.yml
@@ -66,7 +66,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/cluster_endtoend_onlineddl_scheduler_mysql57.yml b/.github/workflows/cluster_endtoend_onlineddl_scheduler_mysql57.yml
index f09fc6b92a..bf360367e6 100644
--- a/.github/workflows/cluster_endtoend_onlineddl_scheduler_mysql57.yml
+++ b/.github/workflows/cluster_endtoend_onlineddl_scheduler_mysql57.yml
@@ -66,7 +66,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/cluster_endtoend_onlineddl_singleton.yml b/.github/workflows/cluster_endtoend_onlineddl_singleton.yml
index 61e31fad75..6e69def2b3 100644
--- a/.github/workflows/cluster_endtoend_onlineddl_singleton.yml
+++ b/.github/workflows/cluster_endtoend_onlineddl_singleton.yml
@@ -66,7 +66,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/cluster_endtoend_onlineddl_singleton_mysql57.yml b/.github/workflows/cluster_endtoend_onlineddl_singleton_mysql57.yml
index b748681c86..ee423966c5 100644
--- a/.github/workflows/cluster_endtoend_onlineddl_singleton_mysql57.yml
+++ b/.github/workflows/cluster_endtoend_onlineddl_singleton_mysql57.yml
@@ -66,7 +66,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/cluster_endtoend_onlineddl_vrepl.yml b/.github/workflows/cluster_endtoend_onlineddl_vrepl.yml
index c8ca620857..47d301b0b1 100644
--- a/.github/workflows/cluster_endtoend_onlineddl_vrepl.yml
+++ b/.github/workflows/cluster_endtoend_onlineddl_vrepl.yml
@@ -66,7 +66,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/cluster_endtoend_onlineddl_vrepl_mysql57.yml b/.github/workflows/cluster_endtoend_onlineddl_vrepl_mysql57.yml
index 07db4a64dc..9c916c2f72 100644
--- a/.github/workflows/cluster_endtoend_onlineddl_vrepl_mysql57.yml
+++ b/.github/workflows/cluster_endtoend_onlineddl_vrepl_mysql57.yml
@@ -66,7 +66,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/cluster_endtoend_onlineddl_vrepl_stress.yml b/.github/workflows/cluster_endtoend_onlineddl_vrepl_stress.yml
index faeac509a2..6a1f48c89b 100644
--- a/.github/workflows/cluster_endtoend_onlineddl_vrepl_stress.yml
+++ b/.github/workflows/cluster_endtoend_onlineddl_vrepl_stress.yml
@@ -66,7 +66,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/cluster_endtoend_onlineddl_vrepl_stress_mysql57.yml b/.github/workflows/cluster_endtoend_onlineddl_vrepl_stress_mysql57.yml
index 50de4e07e9..793505b628 100644
--- a/.github/workflows/cluster_endtoend_onlineddl_vrepl_stress_mysql57.yml
+++ b/.github/workflows/cluster_endtoend_onlineddl_vrepl_stress_mysql57.yml
@@ -66,7 +66,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/cluster_endtoend_onlineddl_vrepl_stress_suite.yml b/.github/workflows/cluster_endtoend_onlineddl_vrepl_stress_suite.yml
index ce8579a12d..ccfb0e1194 100644
--- a/.github/workflows/cluster_endtoend_onlineddl_vrepl_stress_suite.yml
+++ b/.github/workflows/cluster_endtoend_onlineddl_vrepl_stress_suite.yml
@@ -66,7 +66,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/cluster_endtoend_onlineddl_vrepl_stress_suite_mysql57.yml b/.github/workflows/cluster_endtoend_onlineddl_vrepl_stress_suite_mysql57.yml
index 53701a2cba..9a579afa93 100644
--- a/.github/workflows/cluster_endtoend_onlineddl_vrepl_stress_suite_mysql57.yml
+++ b/.github/workflows/cluster_endtoend_onlineddl_vrepl_stress_suite_mysql57.yml
@@ -66,7 +66,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/cluster_endtoend_onlineddl_vrepl_suite.yml b/.github/workflows/cluster_endtoend_onlineddl_vrepl_suite.yml
index 1acd8b3eca..f56b98c6aa 100644
--- a/.github/workflows/cluster_endtoend_onlineddl_vrepl_suite.yml
+++ b/.github/workflows/cluster_endtoend_onlineddl_vrepl_suite.yml
@@ -66,7 +66,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/cluster_endtoend_onlineddl_vrepl_suite_mysql57.yml b/.github/workflows/cluster_endtoend_onlineddl_vrepl_suite_mysql57.yml
index 59fbbf9b9d..2f4a68a87f 100644
--- a/.github/workflows/cluster_endtoend_onlineddl_vrepl_suite_mysql57.yml
+++ b/.github/workflows/cluster_endtoend_onlineddl_vrepl_suite_mysql57.yml
@@ -66,7 +66,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/cluster_endtoend_schemadiff_vrepl.yml b/.github/workflows/cluster_endtoend_schemadiff_vrepl.yml
index 21e50ab920..a3d530b92f 100644
--- a/.github/workflows/cluster_endtoend_schemadiff_vrepl.yml
+++ b/.github/workflows/cluster_endtoend_schemadiff_vrepl.yml
@@ -66,7 +66,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/cluster_endtoend_schemadiff_vrepl_mysql57.yml b/.github/workflows/cluster_endtoend_schemadiff_vrepl_mysql57.yml
index 2cc3463d38..f0796b9690 100644
--- a/.github/workflows/cluster_endtoend_schemadiff_vrepl_mysql57.yml
+++ b/.github/workflows/cluster_endtoend_schemadiff_vrepl_mysql57.yml
@@ -66,7 +66,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/cluster_endtoend_tabletmanager_consul.yml b/.github/workflows/cluster_endtoend_tabletmanager_consul.yml
index 3ffc46f011..450d87c1e6 100644
--- a/.github/workflows/cluster_endtoend_tabletmanager_consul.yml
+++ b/.github/workflows/cluster_endtoend_tabletmanager_consul.yml
@@ -65,7 +65,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/cluster_endtoend_tabletmanager_tablegc.yml b/.github/workflows/cluster_endtoend_tabletmanager_tablegc.yml
index d6bffc2d59..8b4dba45b5 100644
--- a/.github/workflows/cluster_endtoend_tabletmanager_tablegc.yml
+++ b/.github/workflows/cluster_endtoend_tabletmanager_tablegc.yml
@@ -65,7 +65,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/cluster_endtoend_tabletmanager_tablegc_mysql57.yml b/.github/workflows/cluster_endtoend_tabletmanager_tablegc_mysql57.yml
index eb421d744f..26066a95c5 100644
--- a/.github/workflows/cluster_endtoend_tabletmanager_tablegc_mysql57.yml
+++ b/.github/workflows/cluster_endtoend_tabletmanager_tablegc_mysql57.yml
@@ -65,7 +65,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/cluster_endtoend_tabletmanager_throttler.yml b/.github/workflows/cluster_endtoend_tabletmanager_throttler.yml
index ead73b0236..02f10f528d 100644
--- a/.github/workflows/cluster_endtoend_tabletmanager_throttler.yml
+++ b/.github/workflows/cluster_endtoend_tabletmanager_throttler.yml
@@ -65,7 +65,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/cluster_endtoend_tabletmanager_throttler_custom_config.yml b/.github/workflows/cluster_endtoend_tabletmanager_throttler_custom_config.yml
index c7bd79000e..6501d39170 100644
--- a/.github/workflows/cluster_endtoend_tabletmanager_throttler_custom_config.yml
+++ b/.github/workflows/cluster_endtoend_tabletmanager_throttler_custom_config.yml
@@ -65,7 +65,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/cluster_endtoend_tabletmanager_throttler_topo.yml b/.github/workflows/cluster_endtoend_tabletmanager_throttler_topo.yml
index 662fb027f0..dca1dc36e1 100644
--- a/.github/workflows/cluster_endtoend_tabletmanager_throttler_topo.yml
+++ b/.github/workflows/cluster_endtoend_tabletmanager_throttler_topo.yml
@@ -65,7 +65,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/cluster_endtoend_topo_connection_cache.yml b/.github/workflows/cluster_endtoend_topo_connection_cache.yml
index 4f08313e76..119f47dd3d 100644
--- a/.github/workflows/cluster_endtoend_topo_connection_cache.yml
+++ b/.github/workflows/cluster_endtoend_topo_connection_cache.yml
@@ -65,7 +65,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/cluster_endtoend_vreplication_across_db_versions.yml b/.github/workflows/cluster_endtoend_vreplication_across_db_versions.yml
index 1b1f4426c2..f259620adc 100644
--- a/.github/workflows/cluster_endtoend_vreplication_across_db_versions.yml
+++ b/.github/workflows/cluster_endtoend_vreplication_across_db_versions.yml
@@ -65,7 +65,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/cluster_endtoend_vreplication_basic.yml b/.github/workflows/cluster_endtoend_vreplication_basic.yml
index 7412d41ec7..4bb4bfd3b8 100644
--- a/.github/workflows/cluster_endtoend_vreplication_basic.yml
+++ b/.github/workflows/cluster_endtoend_vreplication_basic.yml
@@ -65,7 +65,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/cluster_endtoend_vreplication_cellalias.yml b/.github/workflows/cluster_endtoend_vreplication_cellalias.yml
index bc002facae..805ed44966 100644
--- a/.github/workflows/cluster_endtoend_vreplication_cellalias.yml
+++ b/.github/workflows/cluster_endtoend_vreplication_cellalias.yml
@@ -65,7 +65,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/cluster_endtoend_vreplication_migrate_vdiff2_convert_tz.yml b/.github/workflows/cluster_endtoend_vreplication_migrate_vdiff2_convert_tz.yml
index 8cec801ae0..5d17d83718 100644
--- a/.github/workflows/cluster_endtoend_vreplication_migrate_vdiff2_convert_tz.yml
+++ b/.github/workflows/cluster_endtoend_vreplication_migrate_vdiff2_convert_tz.yml
@@ -65,7 +65,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/cluster_endtoend_vreplication_multicell.yml b/.github/workflows/cluster_endtoend_vreplication_multicell.yml
index e068a31a42..8d341b6264 100644
--- a/.github/workflows/cluster_endtoend_vreplication_multicell.yml
+++ b/.github/workflows/cluster_endtoend_vreplication_multicell.yml
@@ -65,7 +65,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/cluster_endtoend_vreplication_v2.yml b/.github/workflows/cluster_endtoend_vreplication_v2.yml
index 251a448eee..ea3c0ddf42 100644
--- a/.github/workflows/cluster_endtoend_vreplication_v2.yml
+++ b/.github/workflows/cluster_endtoend_vreplication_v2.yml
@@ -65,7 +65,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/cluster_endtoend_vstream_failover.yml b/.github/workflows/cluster_endtoend_vstream_failover.yml
index d12f00a559..0a01f2558d 100644
--- a/.github/workflows/cluster_endtoend_vstream_failover.yml
+++ b/.github/workflows/cluster_endtoend_vstream_failover.yml
@@ -65,7 +65,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/cluster_endtoend_vstream_stoponreshard_false.yml b/.github/workflows/cluster_endtoend_vstream_stoponreshard_false.yml
index ed0bd68bce..38999a9433 100644
--- a/.github/workflows/cluster_endtoend_vstream_stoponreshard_false.yml
+++ b/.github/workflows/cluster_endtoend_vstream_stoponreshard_false.yml
@@ -65,7 +65,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/cluster_endtoend_vstream_stoponreshard_true.yml b/.github/workflows/cluster_endtoend_vstream_stoponreshard_true.yml
index 1309f8dbf6..f71c606f06 100644
--- a/.github/workflows/cluster_endtoend_vstream_stoponreshard_true.yml
+++ b/.github/workflows/cluster_endtoend_vstream_stoponreshard_true.yml
@@ -65,7 +65,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/cluster_endtoend_vstream_with_keyspaces_to_watch.yml b/.github/workflows/cluster_endtoend_vstream_with_keyspaces_to_watch.yml
index 5d0394a149..d543068616 100644
--- a/.github/workflows/cluster_endtoend_vstream_with_keyspaces_to_watch.yml
+++ b/.github/workflows/cluster_endtoend_vstream_with_keyspaces_to_watch.yml
@@ -65,7 +65,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/cluster_endtoend_vtbackup.yml b/.github/workflows/cluster_endtoend_vtbackup.yml
index 10eee95eda..c492e849c6 100644
--- a/.github/workflows/cluster_endtoend_vtbackup.yml
+++ b/.github/workflows/cluster_endtoend_vtbackup.yml
@@ -65,7 +65,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/cluster_endtoend_vtgate_concurrentdml.yml b/.github/workflows/cluster_endtoend_vtgate_concurrentdml.yml
index f4d7df5b79..71d568ecf4 100644
--- a/.github/workflows/cluster_endtoend_vtgate_concurrentdml.yml
+++ b/.github/workflows/cluster_endtoend_vtgate_concurrentdml.yml
@@ -65,7 +65,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/cluster_endtoend_vtgate_gen4.yml b/.github/workflows/cluster_endtoend_vtgate_gen4.yml
index 56895fb2f8..74a2f3768a 100644
--- a/.github/workflows/cluster_endtoend_vtgate_gen4.yml
+++ b/.github/workflows/cluster_endtoend_vtgate_gen4.yml
@@ -65,7 +65,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/cluster_endtoend_vtgate_general_heavy.yml b/.github/workflows/cluster_endtoend_vtgate_general_heavy.yml
index 285977dcbe..8c32aabd2b 100644
--- a/.github/workflows/cluster_endtoend_vtgate_general_heavy.yml
+++ b/.github/workflows/cluster_endtoend_vtgate_general_heavy.yml
@@ -65,7 +65,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/cluster_endtoend_vtgate_godriver.yml b/.github/workflows/cluster_endtoend_vtgate_godriver.yml
index fc31ffe883..d8112b7f98 100644
--- a/.github/workflows/cluster_endtoend_vtgate_godriver.yml
+++ b/.github/workflows/cluster_endtoend_vtgate_godriver.yml
@@ -65,7 +65,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/cluster_endtoend_vtgate_partial_keyspace.yml b/.github/workflows/cluster_endtoend_vtgate_partial_keyspace.yml
index c4e9801a00..f99bf4c077 100644
--- a/.github/workflows/cluster_endtoend_vtgate_partial_keyspace.yml
+++ b/.github/workflows/cluster_endtoend_vtgate_partial_keyspace.yml
@@ -65,7 +65,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/cluster_endtoend_vtgate_queries.yml b/.github/workflows/cluster_endtoend_vtgate_queries.yml
index cbd41722b4..4f8671d5ad 100644
--- a/.github/workflows/cluster_endtoend_vtgate_queries.yml
+++ b/.github/workflows/cluster_endtoend_vtgate_queries.yml
@@ -65,7 +65,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/cluster_endtoend_vtgate_readafterwrite.yml b/.github/workflows/cluster_endtoend_vtgate_readafterwrite.yml
index e0be8269a5..9167eba436 100644
--- a/.github/workflows/cluster_endtoend_vtgate_readafterwrite.yml
+++ b/.github/workflows/cluster_endtoend_vtgate_readafterwrite.yml
@@ -65,7 +65,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/cluster_endtoend_vtgate_reservedconn.yml b/.github/workflows/cluster_endtoend_vtgate_reservedconn.yml
index 2db0b53e78..c383c1a57a 100644
--- a/.github/workflows/cluster_endtoend_vtgate_reservedconn.yml
+++ b/.github/workflows/cluster_endtoend_vtgate_reservedconn.yml
@@ -65,7 +65,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/cluster_endtoend_vtgate_schema.yml b/.github/workflows/cluster_endtoend_vtgate_schema.yml
index df0bfefc9c..f8a8727948 100644
--- a/.github/workflows/cluster_endtoend_vtgate_schema.yml
+++ b/.github/workflows/cluster_endtoend_vtgate_schema.yml
@@ -65,7 +65,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/cluster_endtoend_vtgate_schema_tracker.yml b/.github/workflows/cluster_endtoend_vtgate_schema_tracker.yml
index 90a51e9ee8..f61eed9069 100644
--- a/.github/workflows/cluster_endtoend_vtgate_schema_tracker.yml
+++ b/.github/workflows/cluster_endtoend_vtgate_schema_tracker.yml
@@ -65,7 +65,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/cluster_endtoend_vtgate_tablet_healthcheck_cache.yml b/.github/workflows/cluster_endtoend_vtgate_tablet_healthcheck_cache.yml
index dbbe759a1b..7e698c7db6 100644
--- a/.github/workflows/cluster_endtoend_vtgate_tablet_healthcheck_cache.yml
+++ b/.github/workflows/cluster_endtoend_vtgate_tablet_healthcheck_cache.yml
@@ -65,7 +65,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/cluster_endtoend_vtgate_topo.yml b/.github/workflows/cluster_endtoend_vtgate_topo.yml
index a2c733199c..0a4f3f0064 100644
--- a/.github/workflows/cluster_endtoend_vtgate_topo.yml
+++ b/.github/workflows/cluster_endtoend_vtgate_topo.yml
@@ -65,7 +65,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/cluster_endtoend_vtgate_topo_consul.yml b/.github/workflows/cluster_endtoend_vtgate_topo_consul.yml
index 4cb875df52..0e7f79fd71 100644
--- a/.github/workflows/cluster_endtoend_vtgate_topo_consul.yml
+++ b/.github/workflows/cluster_endtoend_vtgate_topo_consul.yml
@@ -65,7 +65,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/cluster_endtoend_vtgate_topo_etcd.yml b/.github/workflows/cluster_endtoend_vtgate_topo_etcd.yml
index a3b8cff801..6c028c231e 100644
--- a/.github/workflows/cluster_endtoend_vtgate_topo_etcd.yml
+++ b/.github/workflows/cluster_endtoend_vtgate_topo_etcd.yml
@@ -65,7 +65,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/cluster_endtoend_vtgate_transaction.yml b/.github/workflows/cluster_endtoend_vtgate_transaction.yml
index 41e99ca515..0aba0402f2 100644
--- a/.github/workflows/cluster_endtoend_vtgate_transaction.yml
+++ b/.github/workflows/cluster_endtoend_vtgate_transaction.yml
@@ -65,7 +65,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/cluster_endtoend_vtgate_unsharded.yml b/.github/workflows/cluster_endtoend_vtgate_unsharded.yml
index 9a3cba15d4..2c79180d88 100644
--- a/.github/workflows/cluster_endtoend_vtgate_unsharded.yml
+++ b/.github/workflows/cluster_endtoend_vtgate_unsharded.yml
@@ -65,7 +65,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/cluster_endtoend_vtgate_vindex_heavy.yml b/.github/workflows/cluster_endtoend_vtgate_vindex_heavy.yml
index e3ad4f2883..6db0341aa0 100644
--- a/.github/workflows/cluster_endtoend_vtgate_vindex_heavy.yml
+++ b/.github/workflows/cluster_endtoend_vtgate_vindex_heavy.yml
@@ -65,7 +65,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/cluster_endtoend_vtgate_vschema.yml b/.github/workflows/cluster_endtoend_vtgate_vschema.yml
index 9e6c93fba9..9a35ed501b 100644
--- a/.github/workflows/cluster_endtoend_vtgate_vschema.yml
+++ b/.github/workflows/cluster_endtoend_vtgate_vschema.yml
@@ -65,7 +65,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/cluster_endtoend_vtorc.yml b/.github/workflows/cluster_endtoend_vtorc.yml
index ccc9decd71..ceabb5c74f 100644
--- a/.github/workflows/cluster_endtoend_vtorc.yml
+++ b/.github/workflows/cluster_endtoend_vtorc.yml
@@ -65,7 +65,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/cluster_endtoend_vtorc_mysql57.yml b/.github/workflows/cluster_endtoend_vtorc_mysql57.yml
index 18743d8829..dddd97477a 100644
--- a/.github/workflows/cluster_endtoend_vtorc_mysql57.yml
+++ b/.github/workflows/cluster_endtoend_vtorc_mysql57.yml
@@ -65,7 +65,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/cluster_endtoend_vttablet_prscomplex.yml b/.github/workflows/cluster_endtoend_vttablet_prscomplex.yml
index 25ff019245..30939a6af5 100644
--- a/.github/workflows/cluster_endtoend_vttablet_prscomplex.yml
+++ b/.github/workflows/cluster_endtoend_vttablet_prscomplex.yml
@@ -65,7 +65,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/cluster_endtoend_xb_backup.yml b/.github/workflows/cluster_endtoend_xb_backup.yml
index 00e89462d1..245b46ff75 100644
--- a/.github/workflows/cluster_endtoend_xb_backup.yml
+++ b/.github/workflows/cluster_endtoend_xb_backup.yml
@@ -65,7 +65,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/cluster_endtoend_xb_backup_mysql57.yml b/.github/workflows/cluster_endtoend_xb_backup_mysql57.yml
index 8b11e912ba..d1f7eda29b 100644
--- a/.github/workflows/cluster_endtoend_xb_backup_mysql57.yml
+++ b/.github/workflows/cluster_endtoend_xb_backup_mysql57.yml
@@ -69,7 +69,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/cluster_endtoend_xb_recovery.yml b/.github/workflows/cluster_endtoend_xb_recovery.yml
index 2260499e2d..b762fd9d94 100644
--- a/.github/workflows/cluster_endtoend_xb_recovery.yml
+++ b/.github/workflows/cluster_endtoend_xb_recovery.yml
@@ -65,7 +65,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/cluster_endtoend_xb_recovery_mysql57.yml b/.github/workflows/cluster_endtoend_xb_recovery_mysql57.yml
index c4da9bd6b0..67a17afe25 100644
--- a/.github/workflows/cluster_endtoend_xb_recovery_mysql57.yml
+++ b/.github/workflows/cluster_endtoend_xb_recovery_mysql57.yml
@@ -69,7 +69,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/codeql_analysis.yml b/.github/workflows/codeql_analysis.yml
index 1949620f82..ef2ce34800 100644
--- a/.github/workflows/codeql_analysis.yml
+++ b/.github/workflows/codeql_analysis.yml
@@ -41,7 +41,7 @@ jobs:
       - name: Set up Go
         uses: actions/setup-go@v2
         with:
-          go-version: 1.19.3
+          go-version: 1.19.4
 
       - name: Get base dependencies
         run: |
diff --git a/.github/workflows/create_release.yml b/.github/workflows/create_release.yml
index 443d87fa99..e26d6bf696 100644
--- a/.github/workflows/create_release.yml
+++ b/.github/workflows/create_release.yml
@@ -16,7 +16,7 @@ jobs:
     - name: Set up Go
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Tune the OS
       run: |
diff --git a/.github/workflows/endtoend.yml b/.github/workflows/endtoend.yml
index 4112ac9f7e..3f5f4535c0 100644
--- a/.github/workflows/endtoend.yml
+++ b/.github/workflows/endtoend.yml
@@ -51,7 +51,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Tune the OS
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/local_example.yml b/.github/workflows/local_example.yml
index cbbc987d0f..d795041a6f 100644
--- a/.github/workflows/local_example.yml
+++ b/.github/workflows/local_example.yml
@@ -57,7 +57,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.examples == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Tune the OS
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.examples == 'true'
diff --git a/.github/workflows/region_example.yml b/.github/workflows/region_example.yml
index 7552e716b4..3502ed91a1 100644
--- a/.github/workflows/region_example.yml
+++ b/.github/workflows/region_example.yml
@@ -57,7 +57,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.examples == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Tune the OS
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.examples == 'true'
diff --git a/.github/workflows/static_checks_etc.yml b/.github/workflows/static_checks_etc.yml
index d71e324309..1eed757db8 100644
--- a/.github/workflows/static_checks_etc.yml
+++ b/.github/workflows/static_checks_etc.yml
@@ -102,7 +102,7 @@ jobs:
         if: steps.skip-workflow.outputs.skip-workflow == 'false' && (steps.changes.outputs.go_files == 'true' || steps.changes.outputs.parser_changes == 'true' || steps.changes.outputs.proto_changes == 'true')
         uses: actions/setup-go@v3
         with:
-          go-version: 1.19.3
+          go-version: 1.19.4
 
       - name: Tune the OS
         if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.go_files == 'true'
diff --git a/.github/workflows/unit_race.yml b/.github/workflows/unit_race.yml
index 5121b90459..dc201802cd 100644
--- a/.github/workflows/unit_race.yml
+++ b/.github/workflows/unit_race.yml
@@ -55,7 +55,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.unit_tests == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Tune the OS
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.unit_tests == 'true'
diff --git a/Makefile b/Makefile
index ee9352b309..d9bb2f0b42 100644
--- a/Makefile
+++ b/Makefile
@@ -287,7 +287,7 @@ $(PROTO_GO_OUTS): minimaltools install_protoc-gen-go proto/*.proto
 # This rule builds the bootstrap images for all flavors.
 DOCKER_IMAGES_FOR_TEST = mariadb mariadb103 mysql57 mysql80 percona57 percona80
 DOCKER_IMAGES = common $(DOCKER_IMAGES_FOR_TEST)
-BOOTSTRAP_VERSION=13
+BOOTSTRAP_VERSION=14
 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 446cea2fb7..137d5dedb9 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.19.3 || fail "Go version reported: `go version`. Version 1.19.3+ required. See https://vitess.io/contributing/build-from-source for install instructions."
+goversion_min 1.19.4 || fail "Go version reported: `go version`. Version 1.19.4+ required. See https://vitess.io/contributing/build-from-source for install instructions."
 
 mkdir -p dist
 mkdir -p bin
diff --git a/doc/releasenotes/16_0_0_summary.md b/doc/releasenotes/16_0_0_summary.md
index 01b6acac82..af954e6d0f 100644
--- a/doc/releasenotes/16_0_0_summary.md
+++ b/doc/releasenotes/16_0_0_summary.md
@@ -43,8 +43,9 @@ Consider using `VTOrc` instead of `Orchestrator`.
 ### New command line flags and behavior
 
 #### VTGate: Support query timeout --query-timeout
+
 `--query-timeout` allows you to specify a timeout for queries. This timeout is applied to all queries.
-It can be overridden by setting the `query_timeout` session variable. 
+It can be overridden by setting the `query_timeout` session variable.
 Setting it as command line directive with `QUERY_TIMEOUT_MS` will override other values.
 
 #### VTTablet: VReplication parallel insert workers --vreplication-parallel-insert-workers
@@ -190,3 +191,22 @@ transaction_characteristic: {
 }
 ```
 This will allow users to start a transaction with these characteristics.
+
+#### Support for views
+
+Vitess now supports views in sharded keyspace. Views are not created on the underlying database but are logically stored
+in vschema.
+Any query using view will get re-rewritten as derived table during query planning.
+VSchema Example
+
+```json
+{
+  "sharded": true,
+  "vindexes": {},
+  "tables": {},
+  "views": {
+    "view1": "select * from t1",
+    "view2": "select * from t2",
+  }
+}
+```
\ No newline at end of file
diff --git a/docker/base/Dockerfile b/docker/base/Dockerfile
index 5f6e2b501b..0c32457d98 100644
--- a/docker/base/Dockerfile
+++ b/docker/base/Dockerfile
@@ -21,7 +21,7 @@
 # TODO(mberlin): Remove the symlink and this note once
 # https://github.com/docker/hub-feedback/issues/292 is fixed.
 
-ARG bootstrap_version=13
+ARG bootstrap_version=14
 ARG image="vitess/bootstrap:${bootstrap_version}-mysql57"
 
 FROM "${image}"
diff --git a/docker/base/Dockerfile.mariadb b/docker/base/Dockerfile.mariadb
index 3d6937c861..fe61dabff4 100644
--- a/docker/base/Dockerfile.mariadb
+++ b/docker/base/Dockerfile.mariadb
@@ -1,4 +1,4 @@
-ARG bootstrap_version=13
+ARG bootstrap_version=14
 ARG image="vitess/bootstrap:${bootstrap_version}-mariadb"
 
 FROM "${image}"
diff --git a/docker/base/Dockerfile.mariadb103 b/docker/base/Dockerfile.mariadb103
index 2dd7eeea12..ef6dc20bc5 100644
--- a/docker/base/Dockerfile.mariadb103
+++ b/docker/base/Dockerfile.mariadb103
@@ -1,4 +1,4 @@
-ARG bootstrap_version=13
+ARG bootstrap_version=14
 ARG image="vitess/bootstrap:${bootstrap_version}-mariadb103"
 
 FROM "${image}"
diff --git a/docker/base/Dockerfile.mysql80 b/docker/base/Dockerfile.mysql80
index f3d054a5cd..1ea94ce152 100644
--- a/docker/base/Dockerfile.mysql80
+++ b/docker/base/Dockerfile.mysql80
@@ -1,4 +1,4 @@
-ARG bootstrap_version=13
+ARG bootstrap_version=14
 ARG image="vitess/bootstrap:${bootstrap_version}-mysql80"
 
 FROM "${image}"
diff --git a/docker/base/Dockerfile.percona57 b/docker/base/Dockerfile.percona57
index 30dbc8285d..3370cdd4ec 100644
--- a/docker/base/Dockerfile.percona57
+++ b/docker/base/Dockerfile.percona57
@@ -1,4 +1,4 @@
-ARG bootstrap_version=13
+ARG bootstrap_version=14
 ARG image="vitess/bootstrap:${bootstrap_version}-percona57"
 
 FROM "${image}"
diff --git a/docker/base/Dockerfile.percona80 b/docker/base/Dockerfile.percona80
index 20787bb46c..9f47e63176 100644
--- a/docker/base/Dockerfile.percona80
+++ b/docker/base/Dockerfile.percona80
@@ -1,4 +1,4 @@
-ARG bootstrap_version=13
+ARG bootstrap_version=14
 ARG image="vitess/bootstrap:${bootstrap_version}-percona80"
 
 FROM "${image}"
diff --git a/docker/bootstrap/CHANGELOG.md b/docker/bootstrap/CHANGELOG.md
index 234598bf66..ec5b417211 100644
--- a/docker/bootstrap/CHANGELOG.md
+++ b/docker/bootstrap/CHANGELOG.md
@@ -48,4 +48,8 @@ List of changes between bootstrap image versions.
 
 ## [12] - 2022-10-14
 ### Changes
-- Update build to golang 1.18.7
\ No newline at end of file
+- Update build to golang 1.18.7
+
+## [13] - 2022-12-08
+### Changes
+- Update build to golang 1.19.4
\ No newline at end of file
diff --git a/docker/bootstrap/Dockerfile.common b/docker/bootstrap/Dockerfile.common
index 3c52944181..ff14e2b7a4 100644
--- a/docker/bootstrap/Dockerfile.common
+++ b/docker/bootstrap/Dockerfile.common
@@ -1,4 +1,4 @@
-FROM --platform=linux/amd64 golang:1.19.3-buster
+FROM --platform=linux/amd64 golang:1.19.4-buster
 
 # Install Vitess build dependencies
 RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
diff --git a/docker/lite/Dockerfile.alpine b/docker/lite/Dockerfile.alpine
index 87a52b4d9a..1d2e455f68 100644
--- a/docker/lite/Dockerfile.alpine
+++ b/docker/lite/Dockerfile.alpine
@@ -17,7 +17,7 @@
 #       ensure images contain the right binaries.
 
 # Use a temporary layer for the build stage.
-ARG bootstrap_version=13
+ARG bootstrap_version=14
 ARG image="vitess/bootstrap:${bootstrap_version}-mariadb103"
 
 FROM "${image}" AS builder
diff --git a/docker/lite/Dockerfile.mariadb b/docker/lite/Dockerfile.mariadb
index 0aeb7489eb..57cfe7a0cb 100644
--- a/docker/lite/Dockerfile.mariadb
+++ b/docker/lite/Dockerfile.mariadb
@@ -17,7 +17,7 @@
 #       ensure images contain the right binaries.
 
 # Use a temporary layer for the build stage.
-ARG bootstrap_version=13
+ARG bootstrap_version=14
 ARG image="vitess/bootstrap:${bootstrap_version}-mariadb"
 
 FROM "${image}" AS builder
diff --git a/docker/lite/Dockerfile.mariadb103 b/docker/lite/Dockerfile.mariadb103
index 6e004f4110..644256f554 100644
--- a/docker/lite/Dockerfile.mariadb103
+++ b/docker/lite/Dockerfile.mariadb103
@@ -17,7 +17,7 @@
 #       ensure images contain the right binaries.
 
 # Use a temporary layer for the build stage.
-ARG bootstrap_version=13
+ARG bootstrap_version=14
 ARG image="vitess/bootstrap:${bootstrap_version}-mariadb103"
 
 FROM "${image}" AS builder
diff --git a/docker/lite/Dockerfile.mysql57 b/docker/lite/Dockerfile.mysql57
index 0e06d59d41..58eb655c89 100644
--- a/docker/lite/Dockerfile.mysql57
+++ b/docker/lite/Dockerfile.mysql57
@@ -17,7 +17,7 @@
 #       ensure images contain the right binaries.
 
 # Use a temporary layer for the build stage.
-ARG bootstrap_version=13
+ARG bootstrap_version=14
 ARG image="vitess/bootstrap:${bootstrap_version}-mysql57"
 
 FROM "${image}" AS builder
diff --git a/docker/lite/Dockerfile.mysql80 b/docker/lite/Dockerfile.mysql80
index f4176ec1cb..68a0ea2917 100644
--- a/docker/lite/Dockerfile.mysql80
+++ b/docker/lite/Dockerfile.mysql80
@@ -17,7 +17,7 @@
 #       ensure images contain the right binaries.
 
 # Use a temporary layer for the build stage.
-ARG bootstrap_version=13
+ARG bootstrap_version=14
 ARG image="vitess/bootstrap:${bootstrap_version}-mysql80"
 
 FROM "${image}" AS builder
diff --git a/docker/lite/Dockerfile.percona57 b/docker/lite/Dockerfile.percona57
index c4cb6b0115..01e741d45c 100644
--- a/docker/lite/Dockerfile.percona57
+++ b/docker/lite/Dockerfile.percona57
@@ -17,7 +17,7 @@
 #       ensure images contain the right binaries.
 
 # Use a temporary layer for the build stage.
-ARG bootstrap_version=13
+ARG bootstrap_version=14
 ARG image="vitess/bootstrap:${bootstrap_version}-percona57"
 
 FROM "${image}" AS builder
diff --git a/docker/lite/Dockerfile.percona80 b/docker/lite/Dockerfile.percona80
index 68e320e2e8..4f0b75e2fc 100644
--- a/docker/lite/Dockerfile.percona80
+++ b/docker/lite/Dockerfile.percona80
@@ -17,7 +17,7 @@
 #       ensure images contain the right binaries.
 
 # Use a temporary layer for the build stage.
-ARG bootstrap_version=13
+ARG bootstrap_version=14
 ARG image="vitess/bootstrap:${bootstrap_version}-percona80"
 
 FROM "${image}" AS builder
diff --git a/docker/lite/Dockerfile.ubi7.mysql57 b/docker/lite/Dockerfile.ubi7.mysql57
index 53ef6004e3..dd02bde5a9 100644
--- a/docker/lite/Dockerfile.ubi7.mysql57
+++ b/docker/lite/Dockerfile.ubi7.mysql57
@@ -17,7 +17,7 @@
 #       ensure images contain the right binaries.
 
 # Use a temporary layer for the build stage.
-ARG bootstrap_version=13
+ARG bootstrap_version=14
 ARG image="vitess/bootstrap:${bootstrap_version}-mysql57"
 
 FROM "${image}" AS builder
diff --git a/docker/lite/Dockerfile.ubi7.mysql80 b/docker/lite/Dockerfile.ubi7.mysql80
index e93618abac..350c43f438 100644
--- a/docker/lite/Dockerfile.ubi7.mysql80
+++ b/docker/lite/Dockerfile.ubi7.mysql80
@@ -17,7 +17,7 @@
 #       ensure images contain the right binaries.
 
 # Use a temporary layer for the build stage.
-ARG bootstrap_version=13
+ARG bootstrap_version=14
 ARG image="vitess/bootstrap:${bootstrap_version}-mysql80"
 
 FROM "${image}" AS builder
diff --git a/docker/lite/Dockerfile.ubi7.percona57 b/docker/lite/Dockerfile.ubi7.percona57
index c12bfef96b..8163208f68 100644
--- a/docker/lite/Dockerfile.ubi7.percona57
+++ b/docker/lite/Dockerfile.ubi7.percona57
@@ -17,7 +17,7 @@
 #       ensure images contain the right binaries.
 
 # Use a temporary layer for the build stage.
-ARG bootstrap_version=13
+ARG bootstrap_version=14
 ARG image="vitess/bootstrap:${bootstrap_version}-percona57"
 
 FROM "${image}" AS builder
diff --git a/docker/lite/Dockerfile.ubi7.percona80 b/docker/lite/Dockerfile.ubi7.percona80
index f18288ff74..557eb65e51 100644
--- a/docker/lite/Dockerfile.ubi7.percona80
+++ b/docker/lite/Dockerfile.ubi7.percona80
@@ -17,7 +17,7 @@
 #       ensure images contain the right binaries.
 
 # Use a temporary layer for the build stage.
-ARG bootstrap_version=13
+ARG bootstrap_version=14
 ARG image="vitess/bootstrap:${bootstrap_version}-percona80"
 
 FROM "${image}" AS builder
diff --git a/docker/lite/Dockerfile.ubi8.arm64.mysql80 b/docker/lite/Dockerfile.ubi8.arm64.mysql80
index 3b1550c57a..f3a5427f2b 100644
--- a/docker/lite/Dockerfile.ubi8.arm64.mysql80
+++ b/docker/lite/Dockerfile.ubi8.arm64.mysql80
@@ -17,7 +17,7 @@
 #       ensure images contain the right binaries.
 
 # Use a temporary layer for the build stage.
-ARG bootstrap_version=13
+ARG bootstrap_version=14
 ARG image="vitess/bootstrap:${bootstrap_version}-mysql80"
 
 FROM "${image}" AS builder
diff --git a/docker/lite/Dockerfile.ubi8.mysql80 b/docker/lite/Dockerfile.ubi8.mysql80
index 1c8347c86f..487067645e 100644
--- a/docker/lite/Dockerfile.ubi8.mysql80
+++ b/docker/lite/Dockerfile.ubi8.mysql80
@@ -17,7 +17,7 @@
 #       ensure images contain the right binaries.
 
 # Use a temporary layer for the build stage.
-ARG bootstrap_version=13
+ARG bootstrap_version=14
 ARG image="vitess/bootstrap:${bootstrap_version}-mysql80"
 
 FROM "${image}" AS builder
diff --git a/docker/local/Dockerfile b/docker/local/Dockerfile
index 734425f0b2..62e2c68ae1 100644
--- a/docker/local/Dockerfile
+++ b/docker/local/Dockerfile
@@ -1,4 +1,4 @@
-ARG bootstrap_version=13
+ARG bootstrap_version=14
 ARG image="vitess/bootstrap:${bootstrap_version}-common"
 
 FROM "${image}"
diff --git a/go/vt/sqlparser/ast_rewriting.go b/go/vt/sqlparser/ast_rewriting.go
index 0b9c05618e..6f997a11f6 100644
--- a/go/vt/sqlparser/ast_rewriting.go
+++ b/go/vt/sqlparser/ast_rewriting.go
@@ -53,6 +53,10 @@ type ReservedVars struct {
 	sqNext       int64
 }
 
+type VSchemaViews interface {
+	FindView(name TableName) SelectStatement
+}
+
 // ReserveAll tries to reserve all the given variable names. If they're all available,
 // they are reserved and the function returns true. Otherwise, the function returns false.
 func (r *ReservedVars) ReserveAll(names ...string) bool {
@@ -203,6 +207,7 @@ func PrepareAST(
 	selectLimit int,
 	setVarComment string,
 	sysVars map[string]string,
+	views VSchemaViews,
 ) (*RewriteASTResult, error) {
 	if parameterize {
 		err := Normalize(in, reservedVars, bindVars)
@@ -210,13 +215,20 @@ func PrepareAST(
 			return nil, err
 		}
 	}
-	return RewriteAST(in, keyspace, selectLimit, setVarComment, sysVars)
+	return RewriteAST(in, keyspace, selectLimit, setVarComment, sysVars, views)
 }
 
 // RewriteAST rewrites the whole AST, replacing function calls and adding column aliases to queries.
 // SET_VAR comments are also added to the AST if required.
-func RewriteAST(in Statement, keyspace string, selectLimit int, setVarComment string, sysVars map[string]string) (*RewriteASTResult, error) {
-	er := newASTRewriter(keyspace, selectLimit, setVarComment, sysVars)
+func RewriteAST(
+	in Statement,
+	keyspace string,
+	selectLimit int,
+	setVarComment string,
+	sysVars map[string]string,
+	views VSchemaViews,
+) (*RewriteASTResult, error) {
+	er := newASTRewriter(keyspace, selectLimit, setVarComment, sysVars, views)
 	er.shouldRewriteDatabaseFunc = shouldRewriteDatabaseFunc(in)
 	result := Rewrite(in, er.rewrite, nil)
 
@@ -263,15 +275,17 @@ type astRewriter struct {
 	selectLimit   int
 	setVarComment string
 	sysVars       map[string]string
+	views         VSchemaViews
 }
 
-func newASTRewriter(keyspace string, selectLimit int, setVarComment string, sysVars map[string]string) *astRewriter {
+func newASTRewriter(keyspace string, selectLimit int, setVarComment string, sysVars map[string]string, views VSchemaViews) *astRewriter {
 	return &astRewriter{
 		bindVars:      &BindVarNeeds{},
 		keyspace:      keyspace,
 		selectLimit:   selectLimit,
 		setVarComment: setVarComment,
 		sysVars:       sysVars,
+		views:         views,
 	}
 }
 
@@ -293,7 +307,7 @@ const (
 )
 
 func (er *astRewriter) rewriteAliasedExpr(node *AliasedExpr) (*BindVarNeeds, error) {
-	inner := newASTRewriter(er.keyspace, er.selectLimit, er.setVarComment, er.sysVars)
+	inner := newASTRewriter(er.keyspace, er.selectLimit, er.setVarComment, er.sysVars, er.views)
 	inner.shouldRewriteDatabaseFunc = er.shouldRewriteDatabaseFunc
 	tmp := Rewrite(node.Expr, inner.rewrite, nil)
 	newExpr, ok := tmp.(Expr)
@@ -316,7 +330,6 @@ func (er *astRewriter) rewrite(cursor *Cursor) bool {
 	}
 
 	switch node := cursor.Node().(type) {
-	// select last_insert_id() -> select :__lastInsertId as `last_insert_id()`
 	case *Select:
 		for _, col := range node.SelectExprs {
 			_, hasStar := col.(*StarExpr)
@@ -328,6 +341,7 @@ func (er *astRewriter) rewrite(cursor *Cursor) bool {
 			if ok && aliasedExpr.As.IsEmpty() {
 				buf := NewTrackedBuffer(nil)
 				aliasedExpr.Expr.Format(buf)
+				// select last_insert_id() -> select :__lastInsertId as `last_insert_id()`
 				innerBindVarNeeds, err := er.rewriteAliasedExpr(aliasedExpr)
 				if err != nil {
 					er.err = err
@@ -386,21 +400,36 @@ func (er *astRewriter) rewrite(cursor *Cursor) bool {
 			cursor.Replace(inner)
 		}
 	case *AliasedTableExpr:
-		if !SystemSchema(er.keyspace) {
-			break
-		}
 		aliasTableName, ok := node.Expr.(TableName)
 		if !ok {
-			return true
+			break
 		}
 		// Qualifier should not be added to dual table
-		if aliasTableName.Name.String() == "dual" {
+		tblName := aliasTableName.Name.String()
+		if tblName == "dual" {
+			break
+		}
+		if SystemSchema(er.keyspace) {
+			if aliasTableName.Qualifier.IsEmpty() {
+				aliasTableName.Qualifier = NewIdentifierCS(er.keyspace)
+				node.Expr = aliasTableName
+				cursor.Replace(node)
+			}
+			break
+		}
+		if er.views == nil {
+			break
+		}
+		view := er.views.FindView(aliasTableName)
+		if view == nil {
 			break
 		}
-		if er.keyspace != "" && aliasTableName.Qualifier.IsEmpty() {
-			aliasTableName.Qualifier = NewIdentifierCS(er.keyspace)
-			node.Expr = aliasTableName
-			cursor.Replace(node)
+
+		node.Expr = &DerivedTable{
+			Select: CloneSelectStatement(view),
+		}
+		if node.As.IsEmpty() {
+			node.As = NewIdentifierCS(tblName)
 		}
 	case *ShowBasic:
 		if node.Command == VariableGlobal || node.Command == VariableSession {
diff --git a/go/vt/vtgate/executor.go b/go/vt/vtgate/executor.go
index 6b7428aa39..e19c1fca16 100644
--- a/go/vt/vtgate/executor.go
+++ b/go/vt/vtgate/executor.go
@@ -1007,6 +1007,7 @@ func (e *Executor) getPlan(ctx context.Context, vcursor *vcursorImpl, sql string
 			qo.getSelectLimit(),
 			setVarComment,
 			vcursor.safeSession.SystemVariables,
+			vcursor,
 		)
 		if err != nil {
 			return nil, nil, err
diff --git a/go/vt/vtgate/planbuilder/builder.go b/go/vt/vtgate/planbuilder/builder.go
index 098bc20706..1a1de8232a 100644
--- a/go/vt/vtgate/planbuilder/builder.go
+++ b/go/vt/vtgate/planbuilder/builder.go
@@ -101,7 +101,7 @@ func TestBuilder(query string, vschema plancontext.VSchema, keyspace string) (*e
 	if err != nil {
 		return nil, err
 	}
-	result, err := sqlparser.RewriteAST(stmt, keyspace, sqlparser.SQLSelectLimitUnset, "", nil)
+	result, err := sqlparser.RewriteAST(stmt, keyspace, sqlparser.SQLSelectLimitUnset, "", nil, vschema)
 	if err != nil {
 		return nil, err
 	}
diff --git a/go/vt/vtgate/planbuilder/plancontext/vschema.go b/go/vt/vtgate/planbuilder/plancontext/vschema.go
index b8e7b44cf7..a05729127e 100644
--- a/go/vt/vtgate/planbuilder/plancontext/vschema.go
+++ b/go/vt/vtgate/planbuilder/plancontext/vschema.go
@@ -22,6 +22,7 @@ type PlannerVersion = querypb.ExecuteOptions_PlannerVersion
 // info about tables.
 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)
 	TargetString() string
diff --git a/go/vt/vtgate/vcursor_impl.go b/go/vt/vtgate/vcursor_impl.go
index 7176ce1977..92e306a8de 100644
--- a/go/vt/vtgate/vcursor_impl.go
+++ b/go/vt/vtgate/vcursor_impl.go
@@ -229,6 +229,17 @@ 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())
+	if err != nil {
+		return nil
+	}
+	if ks == "" {
+		ks = vc.keyspace
+	}
+	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())
 	if err != nil {
diff --git a/go/vt/vtgate/vindexes/vschema.go b/go/vt/vtgate/vindexes/vschema.go
index 778f4e7b6b..4b69644870 100644
--- a/go/vt/vtgate/vindexes/vschema.go
+++ b/go/vt/vtgate/vindexes/vschema.go
@@ -59,7 +59,11 @@ const (
 // VSchema represents the denormalized version of SrvVSchema,
 // used for building routing plans.
 type VSchema struct {
-	RoutingRules      map[string]*RoutingRule `json:"routing_rules"`
+	RoutingRules map[string]*RoutingRule `json:"routing_rules"`
+
+	// uniqueTables contains the name of all tables in all keyspaces. if the table is uniquely named, the value will
+	// be the name of the keyspace where this table exists. if multiple keyspaces have a table with the same name, the
+	// value will be a `nil` value
 	globalTables      map[string]*Table
 	uniqueVindexes    map[string]Vindex
 	Keyspaces         map[string]*KeyspaceSchema `json:"keyspaces"`
@@ -167,6 +171,7 @@ type KeyspaceSchema struct {
 	Keyspace *Keyspace
 	Tables   map[string]*Table
 	Vindexes map[string]Vindex
+	Views    map[string]sqlparser.SelectStatement
 	Error    error
 }
 
@@ -268,6 +273,33 @@ func buildKeyspaces(source *vschemapb.SrvVSchema, vschema *VSchema) {
 	}
 }
 
+func (vschema *VSchema) AddView(ksname string, viewName, query string) error {
+	ks, ok := vschema.Keyspaces[ksname]
+	if !ok {
+		return fmt.Errorf("keyspace %s not found in vschema", ksname)
+	}
+	ast, err := sqlparser.Parse(query)
+	if err != nil {
+		return err
+	}
+	selectStmt, ok := ast.(sqlparser.SelectStatement)
+	if !ok {
+		return vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "expected SELECT or UNION query, got %T", ast)
+	}
+	if ks.Views == nil {
+		ks.Views = make(map[string]sqlparser.SelectStatement)
+	}
+	ks.Views[viewName] = selectStmt
+	t := &Table{
+		Type:                    "View",
+		Name:                    sqlparser.NewIdentifierCS(viewName),
+		Keyspace:                ks.Keyspace,
+		ColumnListAuthoritative: true,
+	}
+	vschema.addTableName(t)
+	return nil
+}
+
 func buildGlobalTables(source *vschemapb.SrvVSchema, vschema *VSchema) {
 	for ksname, ks := range source.Keyspaces {
 		ksvschema := vschema.Keyspaces[ksname]
@@ -276,11 +308,11 @@ func buildGlobalTables(source *vschemapb.SrvVSchema, vschema *VSchema) {
 		if ks.RequireExplicitRouting {
 			continue
 		}
-		buildKeyspaceGlobalTables(ks, vschema, ksvschema)
+		buildKeyspaceGlobalTables(vschema, ksvschema)
 	}
 }
 
-func buildKeyspaceGlobalTables(ks *vschemapb.Keyspace, vschema *VSchema, ksvschema *KeyspaceSchema) {
+func buildKeyspaceGlobalTables(vschema *VSchema, ksvschema *KeyspaceSchema) {
 	for tname, t := range ksvschema.Tables {
 		if gt, ok := vschema.globalTables[tname]; ok {
 			// There is already an entry table stored in global tables
@@ -306,19 +338,15 @@ func buildKeyspaceGlobalTables(ks *vschemapb.Keyspace, vschema *VSchema, ksvsche
 }
 
 func buildReferences(source *vschemapb.SrvVSchema, vschema *VSchema) {
-	for ksname, ks := range source.Keyspaces {
+	for ksname := range source.Keyspaces {
 		ksvschema := vschema.Keyspaces[ksname]
-		if err := buildKeyspaceReferences(ks, vschema, ksvschema); err != nil && ksvschema.Error == nil {
+		if err := buildKeyspaceReferences(vschema, ksvschema); err != nil && ksvschema.Error == nil {
 			ksvschema.Error = err
 		}
 	}
 }
 
-func buildKeyspaceReferences(
-	ks *vschemapb.Keyspace,
-	vschema *VSchema,
-	ksvschema *KeyspaceSchema,
-) error {
+func buildKeyspaceReferences(vschema *VSchema, ksvschema *KeyspaceSchema) error {
 	keyspace := ksvschema.Keyspace
 	for tname, t := range ksvschema.Tables {
 		source := t.Source
@@ -602,6 +630,15 @@ func buildTables(ks *vschemapb.Keyspace, vschema *VSchema, ksvschema *KeyspaceSc
 	return nil
 }
 
+func (vschema *VSchema) addTableName(t *Table) {
+	tname := t.Name.String()
+	if _, ok := vschema.globalTables[tname]; ok {
+		vschema.globalTables[tname] = nil
+	} else {
+		vschema.globalTables[tname] = t
+	}
+}
+
 func resolveAutoIncrement(source *vschemapb.SrvVSchema, vschema *VSchema) {
 	for ksname, ks := range source.Keyspaces {
 		ksvschema := vschema.Keyspaces[ksname]
@@ -806,8 +843,8 @@ func (vschema *VSchema) FindTable(keyspace, tablename string) (*Table, error) {
 // findTable is like FindTable, but does not return an error if a table is not found.
 func (vschema *VSchema) findTable(keyspace, tablename string) (*Table, error) {
 	if keyspace == "" {
-		table, ok := vschema.globalTables[tablename]
-		if table == nil {
+		t, ok := vschema.globalTables[tablename]
+		if t == nil {
 			if ok {
 				return nil, vterrors.Errorf(
 					vtrpcpb.Code_FAILED_PRECONDITION,
@@ -826,7 +863,7 @@ func (vschema *VSchema) findTable(keyspace, tablename string) (*Table, error) {
 				return &Table{Name: sqlparser.NewIdentifierCS(tablename), Keyspace: ks.Keyspace}, nil
 			}
 		}
-		return table, nil
+		keyspace = t.Keyspace.Name
 	}
 	ks, ok := vschema.Keyspaces[keyspace]
 	if !ok {
@@ -894,6 +931,48 @@ func (vschema *VSchema) FindTableOrVindex(keyspace, name string, tabletType topo
 	return nil, nil, NotFoundError{TableName: name}
 }
 
+func (vschema *VSchema) FindView(keyspace, name string) sqlparser.SelectStatement {
+	if keyspace == "" {
+		switch {
+		case len(vschema.Keyspaces) == 1:
+			for _, schema := range vschema.Keyspaces {
+				keyspace = schema.Keyspace.Name
+				break
+			}
+		default:
+			t, ok := vschema.globalTables[name]
+			if !ok {
+				return nil
+			}
+			if ok && t == nil {
+				return nil
+			}
+			keyspace = t.Keyspace.Name
+		}
+	}
+
+	ks := vschema.Keyspaces[keyspace]
+	if ks == nil {
+		return nil
+	}
+
+	statement, ok := ks.Views[name]
+	if !ok {
+		return nil
+	}
+
+	// We do this to make sure there is no shared state between uses of this AST
+	statement = sqlparser.CloneSelectStatement(statement)
+	sqlparser.Rewrite(statement, func(cursor *sqlparser.Cursor) bool {
+		col, ok := cursor.Node().(*sqlparser.ColName)
+		if ok {
+			cursor.Replace(sqlparser.NewColNameWithQualifier(col.Name.String(), col.Qualifier))
+		}
+		return true
+	}, nil)
+	return statement
+}
+
 func (vschema *VSchema) findKeyspaceAndTableBySource(source *Source) (*Keyspace, *Table, error) {
 	sourceKsname := source.Qualifier.String()
 	sourceTname := source.Name.String()


Test patch:
diff --git a/.github/workflows/check_make_vtadmin_authz_testgen.yml b/.github/workflows/check_make_vtadmin_authz_testgen.yml
index 2a8169fc31..c6b0b388c6 100644
--- a/.github/workflows/check_make_vtadmin_authz_testgen.yml
+++ b/.github/workflows/check_make_vtadmin_authz_testgen.yml
@@ -48,7 +48,7 @@ jobs:
       uses: actions/setup-go@v3
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.vtadmin_changes == 'true'
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Tune the OS
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.vtadmin_changes == 'true'
diff --git a/.github/workflows/cluster_endtoend_vtctlbackup_sharded_clustertest_heavy.yml b/.github/workflows/cluster_endtoend_vtctlbackup_sharded_clustertest_heavy.yml
index ea69581736..a104c356fa 100644
--- a/.github/workflows/cluster_endtoend_vtctlbackup_sharded_clustertest_heavy.yml
+++ b/.github/workflows/cluster_endtoend_vtctlbackup_sharded_clustertest_heavy.yml
@@ -65,7 +65,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/docker_test_cluster_10.yml b/.github/workflows/docker_test_cluster_10.yml
index 374709af69..8c3fa05e0b 100644
--- a/.github/workflows/docker_test_cluster_10.yml
+++ b/.github/workflows/docker_test_cluster_10.yml
@@ -53,7 +53,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Tune the OS
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/docker_test_cluster_25.yml b/.github/workflows/docker_test_cluster_25.yml
index 9474641c44..e676139aa1 100644
--- a/.github/workflows/docker_test_cluster_25.yml
+++ b/.github/workflows/docker_test_cluster_25.yml
@@ -53,7 +53,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Tune the OS
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/e2e_race.yml b/.github/workflows/e2e_race.yml
index fa1674514d..4241e1d962 100644
--- a/.github/workflows/e2e_race.yml
+++ b/.github/workflows/e2e_race.yml
@@ -51,7 +51,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Tune the OS
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/unit_test_mariadb103.yml b/.github/workflows/unit_test_mariadb103.yml
index 32aa77200c..ea13a6b46e 100644
--- a/.github/workflows/unit_test_mariadb103.yml
+++ b/.github/workflows/unit_test_mariadb103.yml
@@ -56,7 +56,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.unit_tests == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Tune the OS
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.unit_tests == 'true'
diff --git a/.github/workflows/unit_test_mysql57.yml b/.github/workflows/unit_test_mysql57.yml
index 5dd19a3d7d..5f1ca91605 100644
--- a/.github/workflows/unit_test_mysql57.yml
+++ b/.github/workflows/unit_test_mysql57.yml
@@ -56,7 +56,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.unit_tests == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Tune the OS
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.unit_tests == 'true'
diff --git a/.github/workflows/unit_test_mysql80.yml b/.github/workflows/unit_test_mysql80.yml
index d146c325f7..c7fa0c9758 100644
--- a/.github/workflows/unit_test_mysql80.yml
+++ b/.github/workflows/unit_test_mysql80.yml
@@ -56,7 +56,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.unit_tests == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Tune the OS
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.unit_tests == 'true'
diff --git a/.github/workflows/upgrade_downgrade_test_backups_e2e.yml b/.github/workflows/upgrade_downgrade_test_backups_e2e.yml
index b4e0922596..119824bf72 100644
--- a/.github/workflows/upgrade_downgrade_test_backups_e2e.yml
+++ b/.github/workflows/upgrade_downgrade_test_backups_e2e.yml
@@ -83,7 +83,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/upgrade_downgrade_test_backups_e2e_next_release.yml b/.github/workflows/upgrade_downgrade_test_backups_e2e_next_release.yml
index 824f7ff5fa..d92522c009 100644
--- a/.github/workflows/upgrade_downgrade_test_backups_e2e_next_release.yml
+++ b/.github/workflows/upgrade_downgrade_test_backups_e2e_next_release.yml
@@ -86,7 +86,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/upgrade_downgrade_test_backups_manual.yml b/.github/workflows/upgrade_downgrade_test_backups_manual.yml
index d7a19621b7..8d6044ae39 100644
--- a/.github/workflows/upgrade_downgrade_test_backups_manual.yml
+++ b/.github/workflows/upgrade_downgrade_test_backups_manual.yml
@@ -85,7 +85,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/upgrade_downgrade_test_backups_manual_next_release.yml b/.github/workflows/upgrade_downgrade_test_backups_manual_next_release.yml
index 63c427169b..2ee113a871 100644
--- a/.github/workflows/upgrade_downgrade_test_backups_manual_next_release.yml
+++ b/.github/workflows/upgrade_downgrade_test_backups_manual_next_release.yml
@@ -88,7 +88,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/upgrade_downgrade_test_query_serving_queries.yml b/.github/workflows/upgrade_downgrade_test_query_serving_queries.yml
index d8cc8f61be..78ed23fec0 100644
--- a/.github/workflows/upgrade_downgrade_test_query_serving_queries.yml
+++ b/.github/workflows/upgrade_downgrade_test_query_serving_queries.yml
@@ -85,7 +85,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/upgrade_downgrade_test_query_serving_queries_next_release.yml b/.github/workflows/upgrade_downgrade_test_query_serving_queries_next_release.yml
index bd5036ed08..3776a8891e 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
@@ -88,7 +88,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/upgrade_downgrade_test_query_serving_schema.yml b/.github/workflows/upgrade_downgrade_test_query_serving_schema.yml
index 4139ac1774..981a6635b9 100644
--- a/.github/workflows/upgrade_downgrade_test_query_serving_schema.yml
+++ b/.github/workflows/upgrade_downgrade_test_query_serving_schema.yml
@@ -85,7 +85,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/upgrade_downgrade_test_query_serving_schema_next_release.yml b/.github/workflows/upgrade_downgrade_test_query_serving_schema_next_release.yml
index 3f404799bc..2fb675c3bf 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
@@ -88,7 +88,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/upgrade_downgrade_test_reparent_new_vtctl.yml b/.github/workflows/upgrade_downgrade_test_reparent_new_vtctl.yml
index a3a1c18f87..74a7e1f71c 100644
--- a/.github/workflows/upgrade_downgrade_test_reparent_new_vtctl.yml
+++ b/.github/workflows/upgrade_downgrade_test_reparent_new_vtctl.yml
@@ -88,7 +88,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/upgrade_downgrade_test_reparent_new_vttablet.yml b/.github/workflows/upgrade_downgrade_test_reparent_new_vttablet.yml
index 0695277586..d5d024a377 100644
--- a/.github/workflows/upgrade_downgrade_test_reparent_new_vttablet.yml
+++ b/.github/workflows/upgrade_downgrade_test_reparent_new_vttablet.yml
@@ -88,7 +88,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/upgrade_downgrade_test_reparent_old_vtctl.yml b/.github/workflows/upgrade_downgrade_test_reparent_old_vtctl.yml
index 4f3c137236..d5f6be04cd 100644
--- a/.github/workflows/upgrade_downgrade_test_reparent_old_vtctl.yml
+++ b/.github/workflows/upgrade_downgrade_test_reparent_old_vtctl.yml
@@ -85,7 +85,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/.github/workflows/upgrade_downgrade_test_reparent_old_vttablet.yml b/.github/workflows/upgrade_downgrade_test_reparent_old_vttablet.yml
index 5be7b07621..937e8d6ee3 100644
--- a/.github/workflows/upgrade_downgrade_test_reparent_old_vttablet.yml
+++ b/.github/workflows/upgrade_downgrade_test_reparent_old_vttablet.yml
@@ -85,7 +85,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/docker/lite/Dockerfile.testing b/docker/lite/Dockerfile.testing
index 3eccd5c068..2ae71c2f23 100644
--- a/docker/lite/Dockerfile.testing
+++ b/docker/lite/Dockerfile.testing
@@ -17,7 +17,7 @@
 #       ensure images contain the right binaries.
 
 # Use a temporary layer for the build stage.
-ARG bootstrap_version=13
+ARG bootstrap_version=14
 ARG image="vitess/bootstrap:${bootstrap_version}-mysql57"
 
 FROM "${image}" AS builder
diff --git a/docker/vttestserver/Dockerfile.mysql57 b/docker/vttestserver/Dockerfile.mysql57
index 625a801371..da696ed737 100644
--- a/docker/vttestserver/Dockerfile.mysql57
+++ b/docker/vttestserver/Dockerfile.mysql57
@@ -17,7 +17,7 @@
 #       ensure images contain the right binaries.
 
 # Use a temporary layer for the build stage.
-ARG bootstrap_version=13
+ARG bootstrap_version=14
 ARG image="vitess/bootstrap:${bootstrap_version}-mysql57"
 
 FROM "${image}" AS builder
diff --git a/docker/vttestserver/Dockerfile.mysql80 b/docker/vttestserver/Dockerfile.mysql80
index 0df0c6a0bc..3c3e299f8f 100644
--- a/docker/vttestserver/Dockerfile.mysql80
+++ b/docker/vttestserver/Dockerfile.mysql80
@@ -17,7 +17,7 @@
 #       ensure images contain the right binaries.
 
 # Use a temporary layer for the build stage.
-ARG bootstrap_version=13
+ARG bootstrap_version=14
 ARG image="vitess/bootstrap:${bootstrap_version}-mysql80"
 
 FROM "${image}" AS builder
diff --git a/go/test/endtoend/vtgate/queries/misc/misc_test.go b/go/test/endtoend/vtgate/queries/misc/misc_test.go
index 16ef7428ab..bb1e52f3e9 100644
--- a/go/test/endtoend/vtgate/queries/misc/misc_test.go
+++ b/go/test/endtoend/vtgate/queries/misc/misc_test.go
@@ -146,6 +146,11 @@ func TestQueryTimeoutWithTables(t *testing.T) {
 		}
 		utils.Exec(t, mcmp.VtConn, fmt.Sprintf("insert /*vt+ QUERY_TIMEOUT_MS=1000 */ into t1(id1) values %s", str.String()))
 	}
+	// too much data added in the loop, do drop and recreate the table.
+	defer func() {
+		mcmp.Exec("drop table t1")
+		mcmp.Exec(schemaSQL)
+	}()
 
 	utils.Exec(t, mcmp.VtConn, "select count(*) from t1 where id1 > 31")
 	utils.Exec(t, mcmp.VtConn, "select /*vt+ PLANNER=gen4 QUERY_TIMEOUT_MS=100 */ count(*) from t1 where id1 > 31")
diff --git a/go/test/endtoend/vtgate/queries/misc/schema.sql b/go/test/endtoend/vtgate/queries/misc/schema.sql
index 3e78cab09d..ceac0c07e6 100644
--- a/go/test/endtoend/vtgate/queries/misc/schema.sql
+++ b/go/test/endtoend/vtgate/queries/misc/schema.sql
@@ -1,4 +1,4 @@
-create table t1(
+create table if not exists t1(
                    id1 bigint,
                    id2 bigint,
                    primary key(id1)
diff --git a/go/vt/sqlparser/ast_rewriting_test.go b/go/vt/sqlparser/ast_rewriting_test.go
index c690a53b5c..6fe59acbc8 100644
--- a/go/vt/sqlparser/ast_rewriting_test.go
+++ b/go/vt/sqlparser/ast_rewriting_test.go
@@ -286,6 +286,9 @@ func TestRewrites(in *testing.T) {
 	}, {
 		in:       "SELECT * FROM tbl WHERE exists(select col1, col2, count(*) from other_table where foo > bar group by col1, col2 having count(*) > 3)",
 		expected: "SELECT * FROM tbl WHERE exists(select col1, col2, count(*) from other_table where foo > bar group by col1, col2 having count(*) > 3 limit 1)",
+	}, {
+		in:       "SELECT id, name, salary FROM user_details",
+		expected: "SELECT id, name, salary FROM (select user.id, user.name, user_extra.salary from user join user_extra where user.id = user_extra.user_id) as user_details",
 	}, {
 		in:                          "SHOW VARIABLES",
 		expected:                    "SHOW VARIABLES",
@@ -332,7 +335,14 @@ func TestRewrites(in *testing.T) {
 			stmt, err := Parse(tc.in)
 			require.NoError(err)
 
-			result, err := RewriteAST(stmt, "ks", SQLSelectLimitUnset, "", nil) // passing `ks` just to test that no rewriting happens as it is not system schema
+			result, err := RewriteAST(
+				stmt,
+				"ks", // passing `ks` just to test that no rewriting happens as it is not system schema
+				SQLSelectLimitUnset,
+				"",
+				nil,
+				&fakeViews{},
+			)
 			require.NoError(err)
 
 			expected, err := Parse(tc.expected)
@@ -366,6 +376,19 @@ func TestRewrites(in *testing.T) {
 	}
 }
 
+type fakeViews struct{}
+
+func (*fakeViews) FindView(name TableName) SelectStatement {
+	if name.Name.String() != "user_details" {
+		return nil
+	}
+	statement, err := Parse("select user.id, user.name, user_extra.salary from user join user_extra where user.id = user_extra.user_id")
+	if err != nil {
+		return nil
+	}
+	return statement.(SelectStatement)
+}
+
 func TestRewritesWithSetVarComment(in *testing.T) {
 	tests := []testCaseSetVar{{
 		in:            "select 1",
@@ -407,7 +430,7 @@ func TestRewritesWithSetVarComment(in *testing.T) {
 			stmt, err := Parse(tc.in)
 			require.NoError(err)
 
-			result, err := RewriteAST(stmt, "ks", SQLSelectLimitUnset, tc.setVarComment, nil)
+			result, err := RewriteAST(stmt, "ks", SQLSelectLimitUnset, tc.setVarComment, nil, &fakeViews{})
 			require.NoError(err)
 
 			expected, err := Parse(tc.expected)
@@ -455,7 +478,7 @@ func TestRewritesSysVar(in *testing.T) {
 			stmt, err := Parse(tc.in)
 			require.NoError(err)
 
-			result, err := RewriteAST(stmt, "ks", SQLSelectLimitUnset, "", tc.sysVar)
+			result, err := RewriteAST(stmt, "ks", SQLSelectLimitUnset, "", tc.sysVar, &fakeViews{})
 			require.NoError(err)
 
 			expected, err := Parse(tc.expected)
@@ -505,7 +528,7 @@ func TestRewritesWithDefaultKeyspace(in *testing.T) {
 			stmt, err := Parse(tc.in)
 			require.NoError(err)
 
-			result, err := RewriteAST(stmt, "sys", SQLSelectLimitUnset, "", nil)
+			result, err := RewriteAST(stmt, "sys", SQLSelectLimitUnset, "", nil, &fakeViews{})
 			require.NoError(err)
 
 			expected, err := Parse(tc.expected)
diff --git a/go/vt/sqlparser/normalizer_test.go b/go/vt/sqlparser/normalizer_test.go
index 2ca153f4ea..fe3dc85b8c 100644
--- a/go/vt/sqlparser/normalizer_test.go
+++ b/go/vt/sqlparser/normalizer_test.go
@@ -493,7 +493,17 @@ func BenchmarkNormalizeVTGate(b *testing.B) {
 
 			// Normalize if possible and retry.
 			if CanNormalize(stmt) || MustRewriteAST(stmt, false) {
-				result, err := PrepareAST(stmt, NewReservedVars("vtg", reservedVars), bindVars, true, keyspace, SQLSelectLimitUnset, "", nil)
+				result, err := PrepareAST(
+					stmt,
+					NewReservedVars("vtg", reservedVars),
+					bindVars,
+					true,
+					keyspace,
+					SQLSelectLimitUnset,
+					"",
+					nil, /*sysvars*/
+					nil, /*views*/
+				)
 				if err != nil {
 					b.Fatal(err)
 				}
@@ -773,7 +783,17 @@ func benchmarkNormalization(b *testing.B, sqls []string) {
 			}
 
 			reservedVars := NewReservedVars("vtg", reserved)
-			_, err = PrepareAST(stmt, reservedVars, make(map[string]*querypb.BindVariable), true, "keyspace0", SQLSelectLimitUnset, "", nil)
+			_, err = PrepareAST(
+				stmt,
+				reservedVars,
+				make(map[string]*querypb.BindVariable),
+				true,
+				"keyspace0",
+				SQLSelectLimitUnset,
+				"",
+				nil,
+				nil,
+			)
 			if err != nil {
 				b.Fatal(err)
 			}
diff --git a/go/vt/vtgate/executor_select_test.go b/go/vt/vtgate/executor_select_test.go
index 4dd36d9a41..1c0885db16 100644
--- a/go/vt/vtgate/executor_select_test.go
+++ b/go/vt/vtgate/executor_select_test.go
@@ -3887,6 +3887,52 @@ func TestSelectCFC(t *testing.T) {
 	}
 }
 
+func TestSelectView(t *testing.T) {
+	executor, sbc, _, _ := createExecutorEnv()
+	// add the view to local vschema
+	err := executor.vschema.AddView(KsTestSharded, "user_details_view", "select user.id, user_extra.col from user join user_extra on user.id = user_extra.user_id")
+	require.NoError(t, err)
+
+	executor.normalize = true
+	session := NewAutocommitSession(&vtgatepb.Session{})
+
+	_, err = executor.Execute(context.Background(), "TestSelectView", session,
+		"select * from user_details_view", nil)
+	require.NoError(t, err)
+	wantQueries := []*querypb.BoundQuery{{
+		Sql:           "select * from (select `user`.id, user_extra.col from `user` join user_extra on `user`.id = user_extra.user_id) as user_details_view",
+		BindVariables: map[string]*querypb.BindVariable{},
+	}}
+	utils.MustMatch(t, wantQueries, sbc.Queries)
+
+	sbc.Queries = nil
+	_, err = executor.Execute(context.Background(), "TestSelectView", session,
+		"select * from user_details_view where id = 2", nil)
+	require.NoError(t, err)
+	wantQueries = []*querypb.BoundQuery{{
+		Sql: "select * from (select `user`.id, user_extra.col from `user` join user_extra on `user`.id = user_extra.user_id) as user_details_view where id = :id",
+		BindVariables: map[string]*querypb.BindVariable{
+			"id": sqltypes.Int64BindVariable(2),
+		},
+	}}
+	utils.MustMatch(t, wantQueries, sbc.Queries)
+
+	sbc.Queries = nil
+	_, err = executor.Execute(context.Background(), "TestSelectView", session,
+		"select * from user_details_view where id in (1,2,3,4,5)", nil)
+	require.NoError(t, err)
+	bvtg1, _ := sqltypes.BuildBindVariable([]int64{1, 2, 3, 4, 5})
+	bvals, _ := sqltypes.BuildBindVariable([]int64{1, 2})
+	wantQueries = []*querypb.BoundQuery{{
+		Sql: "select * from (select `user`.id, user_extra.col from `user` join user_extra on `user`.id = user_extra.user_id) as user_details_view where id in ::__vals",
+		BindVariables: map[string]*querypb.BindVariable{
+			"vtg1":   bvtg1,
+			"__vals": bvals,
+		},
+	}}
+	utils.MustMatch(t, wantQueries, sbc.Queries)
+}
+
 func TestMain(m *testing.M) {
 	_flag.ParseFlagsForTest()
 	os.Exit(m.Run())
diff --git a/go/vt/vtgate/planbuilder/plan_test.go b/go/vt/vtgate/planbuilder/plan_test.go
index 3a857ce1c7..c29a6b2a15 100644
--- a/go/vt/vtgate/planbuilder/plan_test.go
+++ b/go/vt/vtgate/planbuilder/plan_test.go
@@ -498,6 +498,15 @@ func loadSchema(t testing.TB, filename string, setCollation bool) *vindexes.VSch
 			t.Fatal(ks.Error)
 		}
 
+		// adding view in user keyspace
+		if ks.Keyspace.Name == "user" {
+			if err = vschema.AddView(ks.Keyspace.Name,
+				"user_details_view",
+				"select user.id, user_extra.col from user join user_extra on user.id = user_extra.user_id"); err != nil {
+				t.Fatal(err)
+			}
+		}
+
 		// setting a default value to all the text columns in the tables of this keyspace
 		// so that we can "simulate" a real case scenario where the vschema is aware of
 		// columns' collations.
@@ -639,6 +648,14 @@ func (vw *vschemaWrapper) FindTable(tab sqlparser.TableName) (*vindexes.Table, s
 	return table, destKeyspace, destTabletType, destTarget, nil
 }
 
+func (vw *vschemaWrapper) FindView(tab sqlparser.TableName) sqlparser.SelectStatement {
+	destKeyspace, _, _, err := topoproto.ParseDestination(tab.Qualifier.String(), topodatapb.TabletType_PRIMARY)
+	if err != nil {
+		return nil
+	}
+	return vw.v.FindView(destKeyspace, tab.Name.String())
+}
+
 func (vw *vschemaWrapper) FindTableOrVindex(tab sqlparser.TableName) (*vindexes.Table, vindexes.Vindex, string, topodatapb.TabletType, key.Destination, error) {
 	destKeyspace, destTabletType, destTarget, err := topoproto.ParseDestination(tab.Qualifier.String(), topodatapb.TabletType_PRIMARY)
 	if err != nil {
diff --git a/go/vt/vtgate/planbuilder/simplifier_test.go b/go/vt/vtgate/planbuilder/simplifier_test.go
index 774b81bcb1..057fb5ab13 100644
--- a/go/vt/vtgate/planbuilder/simplifier_test.go
+++ b/go/vt/vtgate/planbuilder/simplifier_test.go
@@ -43,7 +43,7 @@ func TestSimplifyBuggyQuery(t *testing.T) {
 	}
 	stmt, reserved, err := sqlparser.Parse2(query)
 	require.NoError(t, err)
-	rewritten, _ := sqlparser.RewriteAST(sqlparser.CloneStatement(stmt), vschema.currentDb(), sqlparser.SQLSelectLimitUnset, "", nil)
+	rewritten, _ := sqlparser.RewriteAST(sqlparser.CloneStatement(stmt), vschema.currentDb(), sqlparser.SQLSelectLimitUnset, "", nil, nil)
 	reservedVars := sqlparser.NewReservedVars("vtg", reserved)
 
 	simplified := simplifier.SimplifyStatement(
@@ -65,7 +65,7 @@ func TestSimplifyPanic(t *testing.T) {
 	}
 	stmt, reserved, err := sqlparser.Parse2(query)
 	require.NoError(t, err)
-	rewritten, _ := sqlparser.RewriteAST(sqlparser.CloneStatement(stmt), vschema.currentDb(), sqlparser.SQLSelectLimitUnset, "", nil)
+	rewritten, _ := sqlparser.RewriteAST(sqlparser.CloneStatement(stmt), vschema.currentDb(), sqlparser.SQLSelectLimitUnset, "", nil, nil)
 	reservedVars := sqlparser.NewReservedVars("vtg", reserved)
 
 	simplified := simplifier.SimplifyStatement(
@@ -95,7 +95,7 @@ func TestUnsupportedFile(t *testing.T) {
 				t.Skip()
 				return
 			}
-			rewritten, err := sqlparser.RewriteAST(stmt, vschema.currentDb(), sqlparser.SQLSelectLimitUnset, "", nil)
+			rewritten, err := sqlparser.RewriteAST(stmt, vschema.currentDb(), sqlparser.SQLSelectLimitUnset, "", nil, nil)
 			if err != nil {
 				t.Skip()
 			}
@@ -129,7 +129,7 @@ func keepSameError(query string, reservedVars *sqlparser.ReservedVars, vschema *
 	if err != nil {
 		panic(err)
 	}
-	rewritten, _ := sqlparser.RewriteAST(stmt, vschema.currentDb(), sqlparser.SQLSelectLimitUnset, "", nil)
+	rewritten, _ := sqlparser.RewriteAST(stmt, vschema.currentDb(), sqlparser.SQLSelectLimitUnset, "", nil, nil)
 	ast := rewritten.AST
 	_, expected := BuildFromStmt(query, ast, reservedVars, vschema, rewritten.BindVarNeeds, true, true)
 	if expected == nil {
diff --git a/go/vt/vtgate/planbuilder/testdata/from_cases.json b/go/vt/vtgate/planbuilder/testdata/from_cases.json
index f9c88a765f..e67e8c2850 100644
--- a/go/vt/vtgate/planbuilder/testdata/from_cases.json
+++ b/go/vt/vtgate/planbuilder/testdata/from_cases.json
@@ -6202,5 +6202,81 @@
         "user.user"
       ]
     }
+  },
+  {
+    "comment": "use a view",
+    "query": "select * from user.user_details_view",
+    "v3-plan": {
+      "QueryType": "SELECT",
+      "Original": "select * from user.user_details_view",
+      "Instructions": {
+        "OperatorType": "Route",
+        "Variant": "Scatter",
+        "Keyspace": {
+          "Name": "user",
+          "Sharded": true
+        },
+        "FieldQuery": "select * from (select `user`.id, user_extra.col from `user` join user_extra on `user`.id = user_extra.user_id where 1 != 1) as user_details_view where 1 != 1",
+        "Query": "select * from (select `user`.id, user_extra.col from `user` join user_extra on `user`.id = user_extra.user_id) as user_details_view",
+        "Table": "`user`, user_extra"
+      }
+    },
+    "gen4-plan":   {
+      "QueryType": "SELECT",
+      "Original": "select * from user.user_details_view",
+      "Instructions": {
+        "OperatorType": "Route",
+        "Variant": "Scatter",
+        "Keyspace": {
+          "Name": "user",
+          "Sharded": true
+        },
+        "FieldQuery": "select user_details_view.id, user_details_view.col from (select `user`.id, user_extra.col from `user`, user_extra where 1 != 1) as user_details_view where 1 != 1",
+        "Query": "select user_details_view.id, user_details_view.col from (select `user`.id, user_extra.col from `user`, user_extra where `user`.id = user_extra.user_id) as user_details_view",
+        "Table": "`user`, user_extra"
+      },
+      "TablesUsed": [
+        "user.user",
+        "user.user_extra"
+      ]
+    }
+  },
+  {
+    "comment" : "use a view without qualifying the keyspace",
+    "query": "select * from user_details_view",
+    "v3-plan":   {
+      "QueryType": "SELECT",
+      "Original": "select * from user_details_view",
+      "Instructions": {
+        "OperatorType": "Route",
+        "Variant": "Scatter",
+        "Keyspace": {
+          "Name": "user",
+          "Sharded": true
+        },
+        "FieldQuery": "select * from (select `user`.id, user_extra.col from `user` join user_extra on `user`.id = user_extra.user_id where 1 != 1) as user_details_view where 1 != 1",
+        "Query": "select * from (select `user`.id, user_extra.col from `user` join user_extra on `user`.id = user_extra.user_id) as user_details_view",
+        "Table": "`user`, user_extra"
+      }
+    },
+    "gen4-plan":   {
+      "QueryType": "SELECT",
+      "Original": "select * from user_details_view",
+      "Instructions": {
+        "OperatorType": "Route",
+        "Variant": "Scatter",
+        "Keyspace": {
+          "Name": "user",
+          "Sharded": true
+        },
+        "FieldQuery": "select user_details_view.id, user_details_view.col from (select `user`.id, user_extra.col from `user`, user_extra where 1 != 1) as user_details_view where 1 != 1",
+        "Query": "select user_details_view.id, user_details_view.col from (select `user`.id, user_extra.col from `user`, user_extra where `user`.id = user_extra.user_id) as user_details_view",
+        "Table": "`user`, user_extra"
+      },
+      "TablesUsed": [
+        "user.user",
+        "user.user_extra"
+      ]
+    }
   }
 ]
\ No newline at end of file
diff --git a/go/vt/vtgate/vindexes/vschema_test.go b/go/vt/vtgate/vindexes/vschema_test.go
index 1c0f58b1f4..0370fa9001 100644
--- a/go/vt/vtgate/vindexes/vschema_test.go
+++ b/go/vt/vtgate/vindexes/vschema_test.go
@@ -247,9 +247,7 @@ func TestUnshardedVSchemaValid(t *testing.T) {
 		Vindexes: make(map[string]*vschemapb.Vindex),
 		Tables:   make(map[string]*vschemapb.Table),
 	})
-	if err != nil {
-		t.Errorf("TestUnshardedVSchemaValid:\n%v", err)
-	}
+	require.NoError(t, err)
 }
 
 func TestUnshardedVSchema(t *testing.T) {
@@ -257,47 +255,22 @@ func TestUnshardedVSchema(t *testing.T) {
 		Keyspaces: map[string]*vschemapb.Keyspace{
 			"unsharded": {
 				Tables: map[string]*vschemapb.Table{
-					"t1": {},
-				},
-			},
-		},
-	}
+					"t1": {}}}}}
+
 	got := BuildVSchema(&good)
-	err := got.Keyspaces["unsharded"].Error
+	require.NoError(t, got.Keyspaces["unsharded"].Error)
+
+	table, err := got.FindTable("unsharded", "t1")
 	require.NoError(t, err)
-	ks := &Keyspace{
-		Name: "unsharded",
-	}
-	t1 := &Table{
-		Name:     sqlparser.NewIdentifierCS("t1"),
-		Keyspace: ks,
-	}
-	dual := &Table{
-		Name:     sqlparser.NewIdentifierCS("dual"),
-		Keyspace: ks,
-		Type:     TypeReference,
-	}
-	want := &VSchema{
-		RoutingRules: map[string]*RoutingRule{},
-		globalTables: map[string]*Table{
-			"t1":   t1,
-			"dual": dual,
-		},
-		uniqueVindexes: map[string]Vindex{},
-		Keyspaces: map[string]*KeyspaceSchema{
-			"unsharded": {
-				Keyspace: ks,
-				Tables: map[string]*Table{
-					"t1":   t1,
-					"dual": dual,
-				},
-				Vindexes: map[string]Vindex{},
-			},
-		},
-	}
-	if !reflect.DeepEqual(got, want) {
-		t.Errorf("BuildVSchema:\n%v, want\n%v", got, want)
-	}
+	assert.NotNil(t, table)
+
+	table, err = got.FindTable("", "t1")
+	require.NoError(t, err)
+	assert.NotNil(t, table)
+
+	table, err = got.FindTable("", "dual")
+	require.NoError(t, err)
+	assert.Equal(t, TypeReference, table.Type)
 }
 
 func TestVSchemaColumns(t *testing.T) {
@@ -307,64 +280,20 @@ func TestVSchemaColumns(t *testing.T) {
 				Tables: map[string]*vschemapb.Table{
 					"t1": {
 						Columns: []*vschemapb.Column{{
-							Name: "c1",
-						}, {
+							Name: "c1"}, {
 							Name: "c2",
-							Type: sqltypes.VarChar,
-						}},
-					},
-				},
-			},
-		},
-	}
+							Type: sqltypes.VarChar}}}}}}}
+
 	got := BuildVSchema(&good)
-	err := got.Keyspaces["unsharded"].Error
+	require.NoError(t, got.Keyspaces["unsharded"].Error)
+
+	t1, err := got.FindTable("unsharded", "t1")
 	require.NoError(t, err)
-	ks := &Keyspace{
-		Name: "unsharded",
-	}
-	t1 := &Table{
-		Name:     sqlparser.NewIdentifierCS("t1"),
-		Keyspace: ks,
-		Columns: []Column{{
-			Name: sqlparser.NewIdentifierCI("c1"),
-			Type: sqltypes.Null,
-		}, {
-			Name: sqlparser.NewIdentifierCI("c2"),
-			Type: sqltypes.VarChar,
-		}},
-	}
-	dual := &Table{
-		Name:     sqlparser.NewIdentifierCS("dual"),
-		Keyspace: ks,
-		Type:     TypeReference,
-	}
-	want := &VSchema{
-		RoutingRules: map[string]*RoutingRule{},
-		globalTables: map[string]*Table{
-			"t1":   t1,
-			"dual": dual,
-		},
-		uniqueVindexes: map[string]Vindex{},
-		Keyspaces: map[string]*KeyspaceSchema{
-			"unsharded": {
-				Keyspace: ks,
-				Tables: map[string]*Table{
-					"t1":   t1,
-					"dual": dual,
-				},
-				Vindexes: map[string]Vindex{},
-			},
-		},
-	}
-	if !reflect.DeepEqual(got, want) {
-		gotb, _ := json.Marshal(got)
-		wantb, _ := json.Marshal(want)
-		t.Errorf("BuildVSchema:\n%s, want\n%s", gotb, wantb)
-	}
+	assertColumn(t, t1.Columns[0], "c1", sqltypes.Null)
+	assertColumn(t, t1.Columns[1], "c2", sqltypes.VarChar)
 }
 
-func TestVSchemaColumnListAuthoritative(t *testing.T) {
+func TestVSchemaViews(t *testing.T) {
 	good := vschemapb.SrvVSchema{
 		Keyspaces: map[string]*vschemapb.Keyspace{
 			"unsharded": {
@@ -374,58 +303,47 @@ func TestVSchemaColumnListAuthoritative(t *testing.T) {
 							Name: "c1",
 						}, {
 							Name: "c2",
-							Type: sqltypes.VarChar,
-						}},
-						ColumnListAuthoritative: true,
-					},
-				},
-			},
-		},
-	}
-	got := BuildVSchema(&good)
-	ks := &Keyspace{
-		Name: "unsharded",
-	}
-	t1 := &Table{
-		Name:     sqlparser.NewIdentifierCS("t1"),
-		Keyspace: ks,
-		Columns: []Column{{
-			Name: sqlparser.NewIdentifierCI("c1"),
-			Type: sqltypes.Null,
-		}, {
-			Name: sqlparser.NewIdentifierCI("c2"),
-			Type: sqltypes.VarChar,
-		}},
-		ColumnListAuthoritative: true,
-	}
-	dual := &Table{
-		Name:     sqlparser.NewIdentifierCS("dual"),
-		Keyspace: ks,
-		Type:     TypeReference,
-	}
-	want := &VSchema{
-		RoutingRules: map[string]*RoutingRule{},
-		globalTables: map[string]*Table{
-			"t1":   t1,
-			"dual": dual,
-		},
-		uniqueVindexes: map[string]Vindex{},
-		Keyspaces: map[string]*KeyspaceSchema{
+							Type: sqltypes.VarChar}}}}},
+			"main": {
+				Tables: map[string]*vschemapb.Table{
+					"t1": {
+						Columns: []*vschemapb.Column{{
+							Name: "c1",
+						}, {
+							Name: "c2",
+							Type: sqltypes.VarChar}}}}}}}
+	vschema := BuildVSchema(&good)
+	require.NoError(t, vschema.Keyspaces["unsharded"].Error)
+
+	// add view to unsharded keyspace.
+	vschema.AddView("unsharded", "v1", "SELECT c1+c2 AS added FROM t1")
+
+	view := vschema.FindView("unsharded", "v1")
+	assert.Equal(t, "select c1 + c2 as added from t1", sqlparser.String(view))
+
+	view = vschema.FindView("", "v1")
+	assert.Equal(t, "select c1 + c2 as added from t1", sqlparser.String(view))
+}
+
+func TestVSchemaColumnListAuthoritative(t *testing.T) {
+	good := vschemapb.SrvVSchema{
+		Keyspaces: map[string]*vschemapb.Keyspace{
 			"unsharded": {
-				Keyspace: ks,
-				Tables: map[string]*Table{
-					"t1":   t1,
-					"dual": dual,
-				},
-				Vindexes: map[string]Vindex{},
-			},
-		},
-	}
-	if !reflect.DeepEqual(got, want) {
-		gotb, _ := json.Marshal(got)
-		wantb, _ := json.Marshal(want)
-		t.Errorf("BuildVSchema:\n%s, want\n%s", gotb, wantb)
-	}
+				Tables: map[string]*vschemapb.Table{
+					"t1": {
+						Columns: []*vschemapb.Column{{
+							Name: "c1"}, {
+							Name: "c2",
+							Type: sqltypes.VarChar}},
+						ColumnListAuthoritative: true}}}}}
+
+	got := BuildVSchema(&good)
+
+	t1, err := got.FindTable("unsharded", "t1")
+	require.NoError(t, err)
+	assert.True(t, t1.ColumnListAuthoritative)
+	assertColumn(t, t1.Columns[0], "c1", sqltypes.Null)
+	assertColumn(t, t1.Columns[1], "c2", sqltypes.VarChar)
 }
 
 func TestVSchemaColumnsFail(t *testing.T) {
@@ -435,21 +353,11 @@ func TestVSchemaColumnsFail(t *testing.T) {
 				Tables: map[string]*vschemapb.Table{
 					"t1": {
 						Columns: []*vschemapb.Column{{
-							Name: "c1",
-						}, {
-							Name: "c1",
-						}},
-					},
-				},
-			},
-		},
-	}
+							Name: "c1"}, {
+							Name: "c1"}}}}}}}
+
 	got := BuildVSchema(&good)
-	want := "duplicate column name 'c1' for table: t1"
-	err := got.Keyspaces["unsharded"].Error
-	if err == nil || err.Error() != want {
-		t.Errorf("BuildVSchema(dup col): %v, want %v", err, want)
-	}
+	require.EqualError(t, got.Keyspaces["unsharded"].Error, "duplicate column name 'c1' for table: t1")
 }
 
 func TestVSchemaPinned(t *testing.T) {
@@ -459,52 +367,16 @@ func TestVSchemaPinned(t *testing.T) {
 				Sharded: true,
 				Tables: map[string]*vschemapb.Table{
 					"t1": {
-						Pinned: "80",
-					},
-				},
-			},
-		},
-	}
+						Pinned: "80"}}}}}
+
 	got := BuildVSchema(&good)
+
 	err := got.Keyspaces["sharded"].Error
 	require.NoError(t, err)
-	ks := &Keyspace{
-		Name:    "sharded",
-		Sharded: true,
-	}
-	t1 := &Table{
-		Name:     sqlparser.NewIdentifierCS("t1"),
-		Keyspace: ks,
-		Pinned:   []byte{0x80},
-	}
-	dual := &Table{
-		Name:     sqlparser.NewIdentifierCS("dual"),
-		Keyspace: ks,
-		Type:     TypeReference,
-	}
-	want := &VSchema{
-		RoutingRules: map[string]*RoutingRule{},
-		globalTables: map[string]*Table{
-			"t1":   t1,
-			"dual": dual,
-		},
-		uniqueVindexes: map[string]Vindex{},
-		Keyspaces: map[string]*KeyspaceSchema{
-			"sharded": {
-				Keyspace: ks,
-				Tables: map[string]*Table{
-					"t1":   t1,
-					"dual": dual,
-				},
-				Vindexes: map[string]Vindex{},
-			},
-		},
-	}
-	if !reflect.DeepEqual(got, want) {
-		gotjson, _ := json.Marshal(got)
-		wantjson, _ := json.Marshal(want)
-		t.Errorf("BuildVSchema:\n%s, want\n%s", gotjson, wantjson)
-	}
+
+	t1, err := got.FindTable("sharded", "t1")
+	require.NoError(t, err)
+	assert.Equal(t, "\x80", string(t1.Pinned))
 }
 
 func TestShardedVSchemaOwned(t *testing.T) {
@@ -516,103 +388,37 @@ func TestShardedVSchemaOwned(t *testing.T) {
 					"stfu1": {
 						Type: "stfu",
 						Params: map[string]string{
-							"stfu1": "1",
-						},
-						Owner: "t1",
-					},
+							"stfu1": "1"},
+						Owner: "t1"},
 					"stln1": {
 						Type:  "stln",
-						Owner: "t1",
-					},
-				},
+						Owner: "t1"}},
 				Tables: map[string]*vschemapb.Table{
 					"t1": {
 						ColumnVindexes: []*vschemapb.ColumnVindex{
 							{
 								Column: "c1",
-								Name:   "stfu1",
-							}, {
+								Name:   "stfu1"}, {
 								Column: "c2",
-								Name:   "stln1",
-							},
-						},
-					},
-				},
-			},
-		},
-	}
+								Name:   "stln1"}}}}}}}
+
 	got := BuildVSchema(&good)
 	err := got.Keyspaces["sharded"].Error
 	require.NoError(t, err)
-	ks := &Keyspace{
-		Name:    "sharded",
-		Sharded: true,
-	}
+
+	t1, err := got.FindTable("sharded", "t1")
+	require.NoError(t, err)
+
 	vindex1 := &stFU{
 		name: "stfu1",
 		Params: map[string]string{
-			"stfu1": "1",
-		},
-	}
+			"stfu1": "1"}}
+	assertVindexMatches(t, t1.ColumnVindexes[0], vindex1, "stfu1", false)
+
 	vindex2 := &stLN{name: "stln1"}
-	t1 := &Table{
-		Name:     sqlparser.NewIdentifierCS("t1"),
-		Keyspace: ks,
-		ColumnVindexes: []*ColumnVindex{
-			{
-				Columns:  []sqlparser.IdentifierCI{sqlparser.NewIdentifierCI("c1")},
-				Type:     "stfu",
-				Name:     "stfu1",
-				Vindex:   vindex1,
-				isUnique: vindex1.IsUnique(),
-				cost:     vindex1.Cost(),
-			},
-			{
-				Columns:  []sqlparser.IdentifierCI{sqlparser.NewIdentifierCI("c2")},
-				Type:     "stln",
-				Name:     "stln1",
-				Owned:    true,
-				Vindex:   vindex2,
-				isUnique: vindex2.IsUnique(),
-				cost:     vindex2.Cost(),
-			},
-		},
-	}
-	t1.Ordered = []*ColumnVindex{
-		t1.ColumnVindexes[1],
-		t1.ColumnVindexes[0],
-	}
-	t1.Owned = t1.ColumnVindexes[1:]
-	dual := &Table{
-		Name:     sqlparser.NewIdentifierCS("dual"),
-		Keyspace: ks,
-		Type:     TypeReference,
-	}
-	want := &VSchema{
-		RoutingRules: map[string]*RoutingRule{},
-		globalTables: map[string]*Table{
-			"t1":   t1,
-			"dual": dual,
-		},
-		uniqueVindexes: map[string]Vindex{
-			"stfu1": vindex1,
-			"stln1": vindex2,
-		},
-		Keyspaces: map[string]*KeyspaceSchema{
-			"sharded": {
-				Keyspace: ks,
-				Tables: map[string]*Table{
-					"t1":   t1,
-					"dual": dual,
-				},
-				Vindexes: map[string]Vindex{
-					"stfu1": vindex1,
-					"stln1": vindex2,
-				},
-			},
-		},
-	}
-	utils.MustMatch(t, want, got)
+	assertVindexMatches(t, t1.ColumnVindexes[1], vindex2, "stln1", true)
+
+	assert.Equal(t, []string{"stln1", "stfu1"}, vindexNames(t1.Ordered))
 }
 
 func TestShardedVSchemaOwnerInfo(t *testing.T) {
@@ -986,64 +792,43 @@ func TestFindBestColVindex(t *testing.T) {
 				Sharded: true,
 				Vindexes: map[string]*vschemapb.Vindex{
 					"stfu": {
-						Type: "stfu",
-					},
+						Type: "stfu"},
 					"stfn": {
-						Type: "stfn",
-					},
+						Type: "stfn"},
 					"stlu": {
-						Type: "stlu",
-					},
+						Type: "stlu"},
 					"stln": {
-						Type: "stln",
-					},
+						Type: "stln"},
 					"cheap": {
-						Type: "cheap",
-					},
+						Type: "cheap"},
 				},
 				Tables: map[string]*vschemapb.Table{
 					"t1": {
 						ColumnVindexes: []*vschemapb.ColumnVindex{{
 							Name:    "stfu",
-							Columns: []string{"id"},
-						}},
-					},
+							Columns: []string{"id"}}}},
 					"nogoodvindex": {
 						ColumnVindexes: []*vschemapb.ColumnVindex{{
 							Name:    "stlu",
-							Columns: []string{"id"},
-						}},
-					},
+							Columns: []string{"id"}}}},
 					"thirdvindexgood": {
 						ColumnVindexes: []*vschemapb.ColumnVindex{{
 							Name:    "stlu",
-							Columns: []string{"id"},
-						}, {
+							Columns: []string{"id"}}, {
 							Name:    "stfn",
-							Columns: []string{"id"},
-						}, {
+							Columns: []string{"id"}}, {
 							Name:    "stfu",
-							Columns: []string{"id"},
-						}},
-					},
+							Columns: []string{"id"}}}},
 					"cheapest": {
 						ColumnVindexes: []*vschemapb.ColumnVindex{{
 							Name:    "stfu",
-							Columns: []string{"id"},
-						}, {
+							Columns: []string{"id"}}, {
 							Name:    "cheap",
-							Columns: []string{"id"},
-						}},
-					},
-				},
-			},
+							Columns: []string{"id"}}}}}},
 			"unsharded": {
 				Tables: map[string]*vschemapb.Table{
-					"t2": {},
-				},
-			},
-		},
-	}
+					"t2": {}}}}}
+
 	vs := BuildVSchema(testSrvVSchema)
 
 	testcases := []struct {
@@ -1212,24 +997,14 @@ func TestShardedVSchemaMultiColumnVindex(t *testing.T) {
 					"stfu1": {
 						Type: "stfu",
 						Params: map[string]string{
-							"stfu1": "1",
-						},
-						Owner: "t1",
-					},
-				},
+							"stfu1": "1"},
+						Owner: "t1"}},
 				Tables: map[string]*vschemapb.Table{
 					"t1": {
-						ColumnVindexes: []*vschemapb.ColumnVindex{
-							{
-								Columns: []string{"c1", "c2"},
-								Name:    "stfu1",
-							},
-						},
-					},
-				},
-			},
-		},
-	}
+						ColumnVindexes: []*vschemapb.ColumnVindex{{
+							Columns: []string{"c1", "c2"},
+							Name:    "stfu1"}}}}}}}
+
 	got := BuildVSchema(&good)
 	err := got.Keyspaces["sharded"].Error
 	require.NoError(t, err)
@@ -1279,14 +1054,10 @@ func TestShardedVSchemaMultiColumnVindex(t *testing.T) {
 				Keyspace: ks,
 				Tables: map[string]*Table{
 					"t1":   t1,
-					"dual": dual,
-				},
+					"dual": dual},
 				Vindexes: map[string]Vindex{
-					"stfu1": vindex1,
-				},
-			},
-		},
-	}
+					"stfu1": vindex1},
+			}}}
 	if !reflect.DeepEqual(got, want) {
 		gotjson, _ := json.Marshal(got)
 		wantjson, _ := json.Marshal(want)
@@ -1302,29 +1073,17 @@ func TestShardedVSchemaNotOwned(t *testing.T) {
 				Vindexes: map[string]*vschemapb.Vindex{
 					"stlu1": {
 						Type:  "stlu",
-						Owner: "",
-					},
+						Owner: ""},
 					"stfu1": {
 						Type:  "stfu",
-						Owner: "",
-					},
-				},
+						Owner: ""}},
 				Tables: map[string]*vschemapb.Table{
 					"t1": {
-						ColumnVindexes: []*vschemapb.ColumnVindex{
-							{
-								Column: "c1",
-								Name:   "stlu1",
-							}, {
-								Column: "c2",
-								Name:   "stfu1",
-							},
-						},
-					},
-				},
-			},
-		},
-	}
+						ColumnVindexes: []*vschemapb.ColumnVindex{{
+							Column: "c1",
+							Name:   "stlu1"}, {
+							Column: "c2",
+							Name:   "stfu1"}}}}}}}
 	got := BuildVSchema(&good)
 	err := got.Keyspaces["sharded"].Error
 	require.NoError(t, err)
@@ -1346,51 +1105,39 @@ func TestShardedVSchemaNotOwned(t *testing.T) {
 				Vindex:   vindex1,
 				isUnique: vindex1.IsUnique(),
 				cost:     vindex1.Cost(),
-			},
-			{
+			}, {
 				Columns:  []sqlparser.IdentifierCI{sqlparser.NewIdentifierCI("c2")},
 				Type:     "stfu",
 				Name:     "stfu1",
 				Owned:    false,
 				Vindex:   vindex2,
 				isUnique: vindex2.IsUnique(),
-				cost:     vindex2.Cost(),
-			},
-		},
-	}
+				cost:     vindex2.Cost()}}}
 	t1.Ordered = []*ColumnVindex{
 		t1.ColumnVindexes[1],
-		t1.ColumnVindexes[0],
-	}
+		t1.ColumnVindexes[0]}
 	dual := &Table{
 		Name:     sqlparser.NewIdentifierCS("dual"),
 		Keyspace: ks,
-		Type:     TypeReference,
-	}
+		Type:     TypeReference}
 	want := &VSchema{
 		RoutingRules: map[string]*RoutingRule{},
 		globalTables: map[string]*Table{
 			"t1":   t1,
-			"dual": dual,
-		},
+			"dual": dual},
 		uniqueVindexes: map[string]Vindex{
 			"stlu1": vindex1,
-			"stfu1": vindex2,
-		},
+			"stfu1": vindex2},
 		Keyspaces: map[string]*KeyspaceSchema{
 			"sharded": {
 				Keyspace: ks,
 				Tables: map[string]*Table{
 					"t1":   t1,
-					"dual": dual,
-				},
+					"dual": dual},
 				Vindexes: map[string]Vindex{
 					"stlu1": vindex1,
-					"stfu1": vindex2,
-				},
-			},
-		},
-	}
+					"stfu1": vindex2},
+			}}}
 	if !reflect.DeepEqual(got, want) {
 		gotjson, _ := json.Marshal(got)
 		wantjson, _ := json.Marshal(want)
@@ -1459,72 +1206,52 @@ func TestBuildVSchemaDupSeq(t *testing.T) {
 			"ksa": {
 				Tables: map[string]*vschemapb.Table{
 					"t1": {
-						Type: "sequence",
-					},
-				},
-			},
+						Type: "sequence"}}},
 			"ksb": {
 				Tables: map[string]*vschemapb.Table{
 					"t1": {
-						Type: "sequence",
-					},
-				},
-			},
-		},
-	}
+						Type: "sequence"}}}}}
 	ksa := &Keyspace{
-		Name: "ksa",
-	}
+		Name: "ksa"}
 	ksb := &Keyspace{
-		Name: "ksb",
-	}
+		Name: "ksb"}
 	got := BuildVSchema(&good)
 	t1a := &Table{
 		Name:     sqlparser.NewIdentifierCS("t1"),
 		Keyspace: ksa,
-		Type:     "sequence",
-	}
+		Type:     "sequence"}
 	t1b := &Table{
 		Name:     sqlparser.NewIdentifierCS("t1"),
 		Keyspace: ksb,
-		Type:     "sequence",
-	}
+		Type:     "sequence"}
 	duala := &Table{
 		Name:     sqlparser.NewIdentifierCS("dual"),
 		Keyspace: ksa,
-		Type:     TypeReference,
-	}
+		Type:     TypeReference}
 	dualb := &Table{
 		Name:     sqlparser.NewIdentifierCS("dual"),
 		Keyspace: ksb,
-		Type:     TypeReference,
-	}
+		Type:     TypeReference}
 	want := &VSchema{
 		RoutingRules: map[string]*RoutingRule{},
 		globalTables: map[string]*Table{
 			"t1":   nil,
-			"dual": duala,
-		},
+			"dual": duala},
 		uniqueVindexes: map[string]Vindex{},
 		Keyspaces: map[string]*KeyspaceSchema{
 			"ksa": {
 				Keyspace: ksa,
 				Tables: map[string]*Table{
 					"t1":   t1a,
-					"dual": duala,
-				},
+					"dual": duala},
 				Vindexes: map[string]Vindex{},
 			},
 			"ksb": {
 				Keyspace: ksb,
 				Tables: map[string]*Table{
 					"t1":   t1b,
-					"dual": dualb,
-				},
-				Vindexes: map[string]Vindex{},
-			},
-		},
-	}
+					"dual": dualb},
+				Vindexes: map[string]Vindex{}}}}
 	if !reflect.DeepEqual(got, want) {
 		gotjson, _ := json.Marshal(got)
 		wantjson, _ := json.Marshal(want)
@@ -3198,3 +2925,24 @@ func TestOtherTablesMakeReferenceTableAndSourceAmbiguous(t *testing.T) {
 	_, err := vs.FindTable("", "t1")
 	require.Error(t, err)
 }
+
+func vindexNames(vindexes []*ColumnVindex) (result []string) {
+	for _, vindex := range vindexes {
+		result = append(result, vindex.Name)
+	}
+	return
+}
+
+func assertVindexMatches(t *testing.T, cv *ColumnVindex, v Vindex, name string, owned bool) {
+	utils.MustMatch(t, v, cv.Vindex)
+	assert.Equal(t, name, cv.Name)
+	assert.Equal(t, v.Cost(), cv.Cost(), "cost not correct")
+	assert.Equal(t, v.IsUnique(), cv.IsUnique(), "isUnique not correct")
+	assert.Equal(t, owned, cv.Owned, "owned is not correct")
+}
+
+func assertColumn(t *testing.T, col Column, expectedName string, expectedType querypb.Type) {
+	assert.True(t, col.Name.EqualString(expectedName), "column name does not match")
+	assert.Equal(t, expectedType, col.Type, "column type does not match")
+
+}
diff --git a/test.go b/test.go
index 7f614ad83a..a31beb2f53 100755
--- a/test.go
+++ b/test.go
@@ -77,7 +77,7 @@ For example:
 // Flags
 var (
 	flavor           = flag.String("flavor", "mysql57", "comma-separated bootstrap flavor(s) to run against (when using Docker mode). Available flavors: all,"+flavors)
-	bootstrapVersion = flag.String("bootstrap-version", "13", "the version identifier to use for the docker images")
+	bootstrapVersion = flag.String("bootstrap-version", "14", "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/templates/cluster_endtoend_test.tpl b/test/templates/cluster_endtoend_test.tpl
index 0920731e7c..6095d0a1b7 100644
--- a/test/templates/cluster_endtoend_test.tpl
+++ b/test/templates/cluster_endtoend_test.tpl
@@ -66,7 +66,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/test/templates/cluster_endtoend_test_docker.tpl b/test/templates/cluster_endtoend_test_docker.tpl
index 68cac99469..850f00be3b 100644
--- a/test/templates/cluster_endtoend_test_docker.tpl
+++ b/test/templates/cluster_endtoend_test_docker.tpl
@@ -52,7 +52,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Tune the OS
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/test/templates/cluster_endtoend_test_mysql57.tpl b/test/templates/cluster_endtoend_test_mysql57.tpl
index 4cae56bb97..f0a3f4f4f1 100644
--- a/test/templates/cluster_endtoend_test_mysql57.tpl
+++ b/test/templates/cluster_endtoend_test_mysql57.tpl
@@ -71,7 +71,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Set up python
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true'
diff --git a/test/templates/dockerfile.tpl b/test/templates/dockerfile.tpl
index 0de256efe0..bdafa6a709 100644
--- a/test/templates/dockerfile.tpl
+++ b/test/templates/dockerfile.tpl
@@ -1,4 +1,4 @@
-ARG bootstrap_version=13
+ARG bootstrap_version=14
 ARG image="vitess/bootstrap:${bootstrap_version}-{{.Platform}}"
 
 FROM "${image}"
diff --git a/test/templates/unit_test.tpl b/test/templates/unit_test.tpl
index ff4906ca3c..e7fced8c09 100644
--- a/test/templates/unit_test.tpl
+++ b/test/templates/unit_test.tpl
@@ -54,7 +54,7 @@ jobs:
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.unit_tests == 'true'
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19.3
+        go-version: 1.19.4
 
     - name: Tune the OS
       if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.unit_tests == 'true'