Skip to content

Fix parsing of CAST() statements#10512

Merged
vmg merged 3 commits intovitessio:mainfrom
planetscale:dbussink/fix-cast-parsing
Jun 15, 2022
Merged

Fix parsing of CAST() statements#10512
vmg merged 3 commits intovitessio:mainfrom
planetscale:dbussink/fix-cast-parsing

Conversation

@dbussink
Copy link
Copy Markdown
Member

CAST() was treated as an alias for CONVERT() but with slightly different syntax.

This is also described in the documentation at https://dev.mysql.com/doc/refman/8.0/en/cast-functions.html, specifically:


With CAST(expr AS type) syntax, the CAST() function takes an expression of any type and produces a result value of the specified type. This operation may also be expressed as CONVERT(expr, type), which is equivalent. If expr is NULL, CAST() returns NULL.


This is wrong sadly. CAST() is not equivalent to CONVERT(), specifically in the context of a CREATE TABLE. For JSON keys, the ARRAY attribute is possible on a CAST(), but that is not accepted for a CONVERT().

The difference in parsing also shows in MySQL:

mysql> select convert(json_keys(c), char(64) array) from t;
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'array) from t' at line 1
mysql> select cast(json_keys(c) as char(64) array) from t;
ERROR 1235 (42000): This version of MySQL doesn't yet support 'Use of CAST( .. AS .. ARRAY) outside of functional index in CREATE(non-SELECT)/ALTER TABLE or in general expressions'

Here the first statement can't be parsed at all. The second is properly parsed, but ARRAY is not allowed in the context of CAST() in this situation (and only in a CREATE TABLE).

This means we should really treat these as two separate expressions and don't store them both in the same structure. The change here creates a separate CAST structure, removes the ARRAY option from CONVERT and updates the grammar and all tests accordingly.

Related Issue(s)

Found as part of work related to #10203

Checklist

  • "Backport me!" label has been added if this change should be backported
  • Tests were added or are not required
  • Documentation was added or is not required

CAST() was treated as an alias for CONVERT() but with slightly different
syntax.

This is also described in the documentation at
https://dev.mysql.com/doc/refman/8.0/en/cast-functions.html,
specifically:

With CAST(expr AS type syntax, the CAST() function takes an expression of
any type and produces a result value of the specified type. This operation
may also be expressed as CONVERT(expr, type), which is equivalent. If expr
is NULL, CAST() returns NULL.

This is wrong sadly. CAST() is not equivalent to CONVERT(), specifically
in the context of a CREATE TABLE. For JSON keys, the ARRAY attribute is
possible on a CAST(), but that is not accepted for a CONVERT().

The difference in parsing also shows in MySQL:

```
mysql> select convert(json_keys(c), char(64) array) from t;
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'array) from t' at line 1
mysql> select cast(json_keys(c) as char(64) array) from t;
ERROR 1235 (42000): This version of MySQL doesn't yet support 'Use of CAST( .. AS .. ARRAY) outside of functional index in CREATE(non-SELECT)/ALTER TABLE or in general expressions'
```

Here the first statement can't be parsed at all. The second is properly
parsed, but ARRAY is not allowed in the context of CAST() in this
situation (and only in a CREATE TABLE).

This means we should really treat these as two separate expressions and
don't store them both in the same structure. The change here creates a
separate CAST structure, removes the ARRAY option from CONVERT and
updates the grammar and all tests accordingly.

Signed-off-by: Dirkjan Bussink <d.bussink@gmail.com>
@github-actions
Copy link
Copy Markdown
Contributor

Review Checklist

Hello reviewers! 👋 Please follow this checklist when reviewing this Pull Request.

General

  • Ensure that the Pull Request has the correct release notes label. release notes none should only be used for PRs that are so trivial that they need not be 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.
  • 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.

Signed-off-by: Dirkjan Bussink <d.bussink@gmail.com>
@dbussink dbussink requested a review from frouioui as a code owner June 15, 2022 09:01
if aggr, ok := a.Func.(sqlparser.AggrFunc); ok {
collID = ctx.SemTable.CollationForExpr(aggr.GetArg())
}
collID := ctx.SemTable.CollationForExpr(a.Func.GetArg())
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Linter mentioned that this was a useless type check since it's already an AggrFunc so I cleaned that up.

Copy link
Copy Markdown
Contributor

@GuptaManan100 GuptaManan100 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, but let's ask one more @vitessio/query-serving member to view this

unary, ok := expr.(*sqlparser.ConvertExpr)
if ok && sqlparser.IsColName(unary.Expr) {
switch unary := expr.(type) {
case *sqlparser.CastExpr:
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in the old code we were doing IsColName(unary.Expr) before accepting the type, but that was lost here. was that intentional?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@systay Ugh, missed this comment, that wasn't intentional. Let me fix that up.

Copy link
Copy Markdown
Collaborator

@systay systay left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

Signed-off-by: Vicent Marti <vmg@strn.cat>
Copy link
Copy Markdown
Collaborator

@vmg vmg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The evalengine code was needlessly duplicated. Fixed that myself.

@vmg vmg merged commit 425feff into vitessio:main Jun 15, 2022
@vmg vmg deleted the dbussink/fix-cast-parsing branch June 15, 2022 13:48
dbussink added a commit to planetscale/vitess that referenced this pull request Jun 15, 2022
This was accidentally removed in
vitessio#10512 but it shouldn't have
been.

Signed-off-by: Dirkjan Bussink <d.bussink@gmail.com>
deepthi pushed a commit that referenced this pull request Jun 15, 2022
This was accidentally removed in
#10512 but it shouldn't have
been.

Signed-off-by: Dirkjan Bussink <d.bussink@gmail.com>
GuptaManan100 pushed a commit to planetscale/vitess that referenced this pull request Jun 16, 2022
* Fix parsing of CAST() statements

CAST() was treated as an alias for CONVERT() but with slightly different
syntax.

This is also described in the documentation at
https://dev.mysql.com/doc/refman/8.0/en/cast-functions.html,
specifically:

With CAST(expr AS type syntax, the CAST() function takes an expression of
any type and produces a result value of the specified type. This operation
may also be expressed as CONVERT(expr, type), which is equivalent. If expr
is NULL, CAST() returns NULL.

This is wrong sadly. CAST() is not equivalent to CONVERT(), specifically
in the context of a CREATE TABLE. For JSON keys, the ARRAY attribute is
possible on a CAST(), but that is not accepted for a CONVERT().

The difference in parsing also shows in MySQL:

```
mysql> select convert(json_keys(c), char(64) array) from t;
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'array) from t' at line 1
mysql> select cast(json_keys(c) as char(64) array) from t;
ERROR 1235 (42000): This version of MySQL doesn't yet support 'Use of CAST( .. AS .. ARRAY) outside of functional index in CREATE(non-SELECT)/ALTER TABLE or in general expressions'
```

Here the first statement can't be parsed at all. The second is properly
parsed, but ARRAY is not allowed in the context of CAST() in this
situation (and only in a CREATE TABLE).

This means we should really treat these as two separate expressions and
don't store them both in the same structure. The change here creates a
separate CAST structure, removes the ARRAY option from CONVERT and
updates the grammar and all tests accordingly.

Signed-off-by: Dirkjan Bussink <d.bussink@gmail.com>

* Handle new cast expression in evalengine and planbuilder

Signed-off-by: Dirkjan Bussink <d.bussink@gmail.com>

* evalengine: do not duplicate CAST/CONVERT translation

Signed-off-by: Vicent Marti <vmg@strn.cat>

Co-authored-by: Vicent Marti <vmg@strn.cat>
Signed-off-by: Manan Gupta <manan@planetscale.com>
GuptaManan100 pushed a commit to planetscale/vitess that referenced this pull request Jun 16, 2022
This was accidentally removed in
vitessio#10512 but it shouldn't have
been.

Signed-off-by: Dirkjan Bussink <d.bussink@gmail.com>
dbussink added a commit that referenced this pull request Jun 16, 2022
* Fix parsing of CAST() statements

CAST() was treated as an alias for CONVERT() but with slightly different
syntax.

This is also described in the documentation at
https://dev.mysql.com/doc/refman/8.0/en/cast-functions.html,
specifically:

With CAST(expr AS type syntax, the CAST() function takes an expression of
any type and produces a result value of the specified type. This operation
may also be expressed as CONVERT(expr, type), which is equivalent. If expr
is NULL, CAST() returns NULL.

This is wrong sadly. CAST() is not equivalent to CONVERT(), specifically
in the context of a CREATE TABLE. For JSON keys, the ARRAY attribute is
possible on a CAST(), but that is not accepted for a CONVERT().

The difference in parsing also shows in MySQL:

```
mysql> select convert(json_keys(c), char(64) array) from t;
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'array) from t' at line 1
mysql> select cast(json_keys(c) as char(64) array) from t;
ERROR 1235 (42000): This version of MySQL doesn't yet support 'Use of CAST( .. AS .. ARRAY) outside of functional index in CREATE(non-SELECT)/ALTER TABLE or in general expressions'
```

Here the first statement can't be parsed at all. The second is properly
parsed, but ARRAY is not allowed in the context of CAST() in this
situation (and only in a CREATE TABLE).

This means we should really treat these as two separate expressions and
don't store them both in the same structure. The change here creates a
separate CAST structure, removes the ARRAY option from CONVERT and
updates the grammar and all tests accordingly.

Signed-off-by: Dirkjan Bussink <d.bussink@gmail.com>

* Handle new cast expression in evalengine and planbuilder

Signed-off-by: Dirkjan Bussink <d.bussink@gmail.com>

* evalengine: do not duplicate CAST/CONVERT translation

Signed-off-by: Vicent Marti <vmg@strn.cat>

Co-authored-by: Vicent Marti <vmg@strn.cat>
Signed-off-by: Dirkjan Bussink <d.bussink@gmail.com>

Co-authored-by: Vicent Marti <vmg@strn.cat>
GuptaManan100 added a commit that referenced this pull request Jun 16, 2022
…10514 (#10517)

* Fix parsing of CAST() statements (#10512)

* Fix parsing of CAST() statements

CAST() was treated as an alias for CONVERT() but with slightly different
syntax.

This is also described in the documentation at
https://dev.mysql.com/doc/refman/8.0/en/cast-functions.html,
specifically:

With CAST(expr AS type syntax, the CAST() function takes an expression of
any type and produces a result value of the specified type. This operation
may also be expressed as CONVERT(expr, type), which is equivalent. If expr
is NULL, CAST() returns NULL.

This is wrong sadly. CAST() is not equivalent to CONVERT(), specifically
in the context of a CREATE TABLE. For JSON keys, the ARRAY attribute is
possible on a CAST(), but that is not accepted for a CONVERT().

The difference in parsing also shows in MySQL:

```
mysql> select convert(json_keys(c), char(64) array) from t;
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'array) from t' at line 1
mysql> select cast(json_keys(c) as char(64) array) from t;
ERROR 1235 (42000): This version of MySQL doesn't yet support 'Use of CAST( .. AS .. ARRAY) outside of functional index in CREATE(non-SELECT)/ALTER TABLE or in general expressions'
```

Here the first statement can't be parsed at all. The second is properly
parsed, but ARRAY is not allowed in the context of CAST() in this
situation (and only in a CREATE TABLE).

This means we should really treat these as two separate expressions and
don't store them both in the same structure. The change here creates a
separate CAST structure, removes the ARRAY option from CONVERT and
updates the grammar and all tests accordingly.

Signed-off-by: Dirkjan Bussink <d.bussink@gmail.com>

* Handle new cast expression in evalengine and planbuilder

Signed-off-by: Dirkjan Bussink <d.bussink@gmail.com>

* evalengine: do not duplicate CAST/CONVERT translation

Signed-off-by: Vicent Marti <vmg@strn.cat>

Co-authored-by: Vicent Marti <vmg@strn.cat>
Signed-off-by: Manan Gupta <manan@planetscale.com>

* Add back unary single column expression check (#10514)

This was accidentally removed in
#10512 but it shouldn't have
been.

Signed-off-by: Dirkjan Bussink <d.bussink@gmail.com>

Co-authored-by: Dirkjan Bussink <d.bussink@gmail.com>
Co-authored-by: Vicent Marti <vmg@strn.cat>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants