diff --git a/pkg/sql/catalog/tabledesc/table.go b/pkg/sql/catalog/tabledesc/table.go index 00d8f956dc01..13e0011669d2 100644 --- a/pkg/sql/catalog/tabledesc/table.go +++ b/pkg/sql/catalog/tabledesc/table.go @@ -24,6 +24,7 @@ import ( "github.com/cockroachdb/cockroach/pkg/sql/catalog/schemaexpr" "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgcode" "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgerror" + "github.com/cockroachdb/cockroach/pkg/sql/sem/cast" "github.com/cockroachdb/cockroach/pkg/sql/sem/eval" "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" "github.com/cockroachdb/cockroach/pkg/sql/sem/volatility" @@ -145,13 +146,22 @@ func MakeColumnDefDescs( col.Type = resType if d.HasDefaultExpr() { + var innerErr error // Verify the default expression type is compatible with the column type // and does not contain invalid functions. ret.DefaultExpr, err = schemaexpr.SanitizeVarFreeExpr( ctx, d.DefaultExpr.Expr, resType, "DEFAULT", semaCtx, volatility.Volatile, ) if err != nil { - 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) + if innerErr != nil { + return nil, err + } + if ok := cast.ValidCast(resType, ret.DefaultExpr.ResolvedType(), cast.ContextAssignment); !ok { + return nil, err + } } // Keep the type checked expression so that the type annotation gets @@ -167,10 +177,20 @@ func MakeColumnDefDescs( if d.HasOnUpdateExpr() { // Verify the on update expression type is compatible with the column type // and does not contain invalid functions. + var innerErr error 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 default and column type to differ. + ret.OnUpdateExpr, innerErr = d.OnUpdateExpr.Expr.TypeCheck(ctx, semaCtx, types.Any) + if innerErr != nil { + return nil, err + } + if ok := cast.ValidCast(resType, ret.OnUpdateExpr.ResolvedType(), cast.ContextAssignment); !ok { + return nil, err + } return nil, err } diff --git a/pkg/sql/logictest/testdata/logic_test/cast b/pkg/sql/logictest/testdata/logic_test/cast index 82f2b6521dd4..d188aab9928d 100644 --- a/pkg/sql/logictest/testdata/logic_test/cast +++ b/pkg/sql/logictest/testdata/logic_test/cast @@ -1401,3 +1401,25 @@ 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 +statement ok +CREATE TABLE def_assn_cast ( +a INT4 DEFAULT 1.0::FLOAT4, +b VARCHAR DEFAULT 'true'::BOOL, +c NAME DEFAULT 'foo'::BPCHAR, +d JSONB DEFAULT 'null'::CHAR, +) + +# Due to cast context we support cases that Postgres doesn't +statement ok +CREATE TABLE nonpg_assn_cast ( +a BOOL DEFAULT 'foo', +b JSONB DEFAULT 'null'::CHAR, +) + +statement error pq: expected DEFAULT expression to have type ., but . has type . +CREATE TABLE def_assn_cast ( +a INT4 DEFAULT 1.0::BOOL +)