Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sql: Allow DEFAULT/ON UPDATE types that can be assignment-cast #81071

Merged
merged 2 commits into from
Jun 9, 2022

Conversation

e-mbrown
Copy link
Contributor

@e-mbrown e-mbrown commented May 5, 2022

Resolves #74854

postgres:

postgres=# create table casts (b BOOL DEFAULT 'foo'::VARCHAR);
 ERROR:  column "b" is of type boolean but default expression is of type character varying>
HINT:  You will need to rewrite or cast the expression.

crdb:

demo@127.0.0.1:26257/movr> CREATE TABLE t (b BOOL DEFAULT 'foo'::VARCHAR);
CREATE TABLE

Time: 8ms total (execution 8ms / network 0ms)

Release note (sql change): A column's DEFAULT/ON UPDATE clause
can now have a type that differs from the column type, as long as
that type can be assignment-cast into the column's type. This
increases our PostgreSQL compatibility.

@e-mbrown e-mbrown requested review from mgartner and a team May 5, 2022 20:57
@cockroach-teamcity
Copy link
Member

This change is Reviewable

@e-mbrown e-mbrown marked this pull request as ready for review May 5, 2022 22:19
@e-mbrown e-mbrown requested a review from a team May 5, 2022 22:19
@e-mbrown
Copy link
Contributor Author

e-mbrown commented May 5, 2022

I noticed this particular solution causes us to support use cases that postgres does not. Not sure if we want to disallow these cases.

Copy link
Collaborator

@mgartner mgartner left a comment

Choose a reason for hiding this comment

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

Great start!

return nil, err
// Check if the default expression type can be assignment-cast into the
// column type. If it can allow the default and column type to differ.
ret.DefaultExpr, innerErr = d.DefaultExpr.Expr.TypeCheck(ctx, semaCtx, types.Any)
Copy link
Collaborator

Choose a reason for hiding this comment

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

schemaexpr.SanitizeVarFreeExpr above could return an error for reasons other than the expression not matching the column type. For example, it could fail if the expression contains references to other columns. We only want to check if the default expression type can be assignment-cast into the column type if schemaexpr.SanitizeVarFreeExpr fails during type-checking. Unfortunately, this isn't easy to do from outside schemaexpr.SanitizeVarFreeExpr. One option is to modify schemaexpr.SanitizeVarFreeExpr to add a new argument, allowAssignmentCast bool and move his assignment-cast logic into the function directly where type-checking fails.

I think you could add a test case for this too, like:

CREATE TABLE fail_assn_cast (
  a BOOL,
  b STRING DEFAULT a
)

@@ -1401,3 +1401,28 @@ select CASE WHEN false THEN 1::REGTYPE ELSE 2::REGNAMESPACE END;
select CASE WHEN false THEN 1::REGTYPE ELSE 2::REGPROC END;
select CASE WHEN false THEN 1::REGTYPE ELSE 2::REGPROCEDURE END;
select CASE WHEN false THEN 1::REGTYPE ELSE 2::REGROLE END;


# Test that default/on update expression differing from column type
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit: sentence seems incomplete

# Test that default/on update expression differing from column type
statement ok
CREATE TABLE def_assn_cast (
a INT4 DEFAULT 1.0::FLOAT4,
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit: indent the lines with column definitions here and below.

ret.OnUpdateExpr, err = schemaexpr.SanitizeVarFreeExpr(
ctx, d.OnUpdateExpr.Expr, resType, "ON UPDATE", semaCtx, volatility.Volatile,
)
if err != nil {
// Check if the on update expression type can be assignment-cast into the
// column type. If it can allow the on update expr and column type to differ.
ret.OnUpdateExpr, innerErr = d.OnUpdateExpr.Expr.TypeCheck(ctx, semaCtx, types.Any)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can you add tests for ON UPDATE too?

@e-mbrown e-mbrown requested review from a team as code owners May 17, 2022 19:39
@e-mbrown e-mbrown requested review from ajwerner and removed request for a team May 17, 2022 19:39
@e-mbrown e-mbrown force-pushed the eb/defassigncast branch 3 times, most recently from 016c1b0 to c14a307 Compare May 18, 2022 16:49
@e-mbrown e-mbrown requested a review from mgartner May 18, 2022 18:21
expr = s.walkExprTree(expr)
texpr, err := tree.TypeCheckAndRequire(s.builder.ctx, expr, s.builder.semaCtx, desired, s.context.String())
texpr, err := tree.TypeCheckAndRequire(s.builder.ctx, expr, s.builder.semaCtx, desired, s.context.String(), true, mutSuffix)
Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't think we can allow assignment casts here in all cases. But, taking a step back, I'm curious why the optbuilder changes were necessary. What was failing before this change?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

After making the table with differing column and default types, trying to insert into the table would result in an error. This was caused by the condition in TypeCheckRequire
if typ := typedExpr.ResolvedType(); !(typ.Equivalent(required) || typ.Family() == types.UnknownFamily) {
I changed the function in opt builder because I noticed the mutation suffix could be passed here to be used in TypeCheckRequire.
Would specifying that the mutation suffix has to be default or update narrow it down to only the cases we want?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Interesting. I'd expect the types to be fine given that assignment casts should be added in these cases by optbuilder automatically:

mb.addAssignmentCasts(mb.insertColIDs)

I'll check out your branch and play around with it a bit to better understand what to do here. Thanks for being patient with me!

Copy link
Collaborator

Choose a reason for hiding this comment

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

Ok, I think the change required in optbuilder was simpler, though tricky to figure out. I put up a PR: #81863. If you rebase your changes on that, you shouldn't need to make any changes in optbuilder.

Copy link
Collaborator

Choose a reason for hiding this comment

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

That PR has been merged, so optbuilder should behave as expected if you rebase on the latest version of master.

@@ -168,7 +168,7 @@ func MakeColumnDefDescs(
// Verify the on update expression type is compatible with the column type
// and does not contain invalid functions.
ret.OnUpdateExpr, err = schemaexpr.SanitizeVarFreeExpr(
ctx, d.OnUpdateExpr.Expr, resType, "ON UPDATE", semaCtx, volatility.Volatile,
ctx, d.OnUpdateExpr.Expr, resType, "ON UPDATE", semaCtx, volatility.Volatile, true,
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit: It's helpful to label arguments with simple constants to make it more clear, like true /* allowAssignmentCast */.

mgartner added a commit to mgartner/cockroach that referenced this pull request May 25, 2022
This commit allows the optimizer to build assignment casts for `DEFAULT`
and `ON UPDATE` expressions that have types that are not equivalent with
their column's type. If the cast from the `DEFAULT` or `ON UPDATE`
expression type to the column type is not a valid assignment cast, an
error is returned. In practice, this error should never occur because 1)
it is currently impossible to create `DEFAULT` and `ON UPDATE`
expressions that do not match their column's type (the tests here get
around this limitation because they use the optimizer test catalog which
has much looser restrictions) and 2) a future PR will allow creating
`DEFAULT` and `ON UPDATE` expressions that do not match their column's
type only if the cast from the expression type to the column type is a
valid assignment cast.

Unblocks cockroachdb#81071

Release note: None
craig bot pushed a commit that referenced this pull request May 26, 2022
81863: opt: assignment casts of non-equivalent types in DEFAULT and ON UPDATE r=mgartner a=mgartner

This commit allows the optimizer to build assignment casts for `DEFAULT`
and `ON UPDATE` expressions that have types that are not equivalent with
their column's type. If the cast from the `DEFAULT` or `ON UPDATE`
expression type to the column type is not a valid assignment cast, an
error is returned. In practice, this error should never occur because 1)
it is currently impossible to create `DEFAULT` and `ON UPDATE`
expressions that do not match their column's type (the tests here get
around this limitation because they use the optimizer test catalog which
has much looser restrictions) and 2) a future PR will allow creating
`DEFAULT` and `ON UPDATE` expressions that do not match their column's
type only if the cast from the expression type to the column type is a
valid assignment cast.

Unblocks #81071

Release note: None

Co-authored-by: Marcus Gartner <marcus@cockroachlabs.com>
Copy link
Collaborator

@rafiss rafiss left a comment

Choose a reason for hiding this comment

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

Reviewable status: :shipit: complete! 0 of 0 LGTMs obtained (waiting on @ajwerner, @e-mbrown, and @mgartner)


pkg/sql/catalog/tabledesc/table.go line 168 at r5 (raw file):

			ctx, d.DefaultExpr.Expr, resType, "DEFAULT", semaCtx, volatility.Volatile,
		)
		if err != nil {

i think it would be more readable if var innerErr error was here (near where the code where it's assigned), instead of having it earlier in the function


pkg/sql/catalog/tabledesc/table.go line 173 at r5 (raw file):

			ret.DefaultExpr, innerErr = d.DefaultExpr.Expr.TypeCheck(ctx, semaCtx, types.Any)
			if innerErr != nil {
				return nil, err

is it intentional to not return innerErr here? (i think maybe it is, but in that case, add a comment explaining why)


pkg/sql/catalog/tabledesc/table.go line 197 at r5 (raw file):

			ctx, d.OnUpdateExpr.Expr, resType, "ON UPDATE", semaCtx, volatility.Volatile,
		)
		if err != nil {

i think it would be more readable if var innerErr error was here (near where the code where it's assigned), instead of having it earlier in the function


pkg/sql/catalog/tabledesc/table.go line 202 at r5 (raw file):

			ret.OnUpdateExpr, innerErr = d.OnUpdateExpr.Expr.TypeCheck(ctx, semaCtx, types.Any)
			if innerErr != nil {
				return nil, err

nit: add a comment for why we don't return innerErr


pkg/sql/catalog/tabledesc/table.go line 184 at r6 (raw file):

		// and does not contain invalid functions.
		ret.OnUpdateExpr, err = schemaexpr.SanitizeVarFreeExpr(
			ctx, d.OnUpdateExpr.Expr, resType, "ON UPDATE", semaCtx, volatility.Volatile, true,

nit: add an inline comment here, and all the other places where it's called

SanitizeVarFreeExpr(..., true /* allowAssignmentCasts */)

pkg/sql/opt/optbuilder/scope.go line 480 at r6 (raw file):

// desired type.
func (s *scope) resolveAndRequireType(
	expr tree.Expr, desired *types.T, mutSuffix string,

is mutSuffix an accident? it doesn't seem to be used

@mgartner mgartner requested a review from a team June 2, 2022 16:40
@e-mbrown e-mbrown force-pushed the eb/defassigncast branch 2 times, most recently from 67e73b1 to f042fae Compare June 6, 2022 20:25
Release note (sql change): A column's DEFAULT/ON UPDATE clause
can now have a type that differs from the column type,as long as
that type can be assignment-cast into the column's type. This
increases our PostgreSQL compatibility.
Copy link
Collaborator

@mgartner mgartner left a comment

Choose a reason for hiding this comment

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

LGTM. Very nice!

default/on update expr with differing types

Release note: None
Copy link
Collaborator

@rafiss rafiss left a comment

Choose a reason for hiding this comment

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

lgtm!

Reviewed 14 of 17 files at r10, 1 of 1 files at r11, all commit messages.
Reviewable status: :shipit: complete! 0 of 0 LGTMs obtained (waiting on @ajwerner, @e-mbrown, and @mgartner)

@e-mbrown
Copy link
Contributor Author

e-mbrown commented Jun 9, 2022

bors r+

@craig craig bot merged commit e09ce91 into cockroachdb:master Jun 9, 2022
@craig
Copy link
Contributor

craig bot commented Jun 9, 2022

Build succeeded:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

sql: allow DEFAULT/ON UPDATE types that can be assignment-cast to column type
4 participants