Skip to content

Commit 320bd4f

Browse files
committed
sql: fix serialization of datums and prefix casts
This change addresses a few related problems (described in more detail in #12532): - ambiguous datum types after serialization - invalid prefix casts after expression processing (like `INTERVAL INTERVAL `1.5s`'). Changes: - a new FmtFlag adds type annotations to datums and placeholders. This ensures that expressions that contain `DDInterval`, `DDate` can be formatted and re-parsed correctly. This replaces ad-hoc type prepending like `INTERVAL '..'` or `DATE '..'`. - `CastExpr.TypeCheck` now returns the underlying type-checked expression directly when it is a Datum that we know for sure is of identical type. - `castPrefix` (renamed to `castPrepend`) now only functions if the underlying expression is a string literal (preventing incorrect expressions like `DECIMAL '1.0':::STRING`). `castPrefixParens` is removed (it was incorrect). - `AnnotateTypeExpr.TypeCheck` now returns the underlying type-checked expression rather than changing the `AnnotateTypeExpr` in-place and returning it (which could result in double annotations with the new `FmtFlag`). `AnnotateTypeExpr` is no longer a `TypedExpr` so it is never part of a type-checked expression (which makes sense). - type checking tests are extended to verify the serialized type-checked expression, which verifies both serialization and type resolution. Note that changing the prepend syntax to a `AnnotateTypeExpr` was considered but that doesn't work for things like `DECIMAL '2.0'`. Fixes #12532. Helps with #12611.
1 parent 62f24f8 commit 320bd4f

18 files changed

+331
-203
lines changed

pkg/sql/alter_table.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -487,7 +487,7 @@ func applyColumnMutation(
487487
); err != nil {
488488
return err
489489
}
490-
s := t.Default.String()
490+
s := parser.Serialize(t.Default)
491491
col.DefaultExpr = &s
492492
}
493493

pkg/sql/create.go

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -383,17 +383,21 @@ func (p *planner) CreateView(n *parser.CreateView) (planNode, error) {
383383

384384
var queryBuf bytes.Buffer
385385
var fmtErr error
386-
n.AsSource.Format(&queryBuf, parser.FmtNormalizeTableNames(
387-
parser.FmtSimple,
388-
func(t *parser.NormalizableTableName) *parser.TableName {
389-
tn, err := p.QualifyWithDatabase(t)
390-
if err != nil {
391-
log.Warningf(p.ctx(), "failed to qualify table name %q with database name: %v", t, err)
392-
fmtErr = err
393-
return nil
394-
}
395-
return tn
396-
}))
386+
n.AsSource.Format(
387+
&queryBuf,
388+
parser.FmtNormalizeTableNames(
389+
parser.FmtParsable,
390+
func(t *parser.NormalizableTableName) *parser.TableName {
391+
tn, err := p.QualifyWithDatabase(t)
392+
if err != nil {
393+
log.Warningf(p.ctx(), "failed to qualify table name %q with database name: %v", t, err)
394+
fmtErr = err
395+
return nil
396+
}
397+
return tn
398+
},
399+
),
400+
)
397401
if fmtErr != nil {
398402
return nil, fmtErr
399403
}
@@ -1471,7 +1475,7 @@ func makeCheckConstraint(
14711475
inuseNames[name] = struct{}{}
14721476
}
14731477
}
1474-
return &sqlbase.TableDescriptor_CheckConstraint{Expr: d.Expr.String(), Name: name}, nil
1478+
return &sqlbase.TableDescriptor_CheckConstraint{Expr: parser.Serialize(d.Expr), Name: name}, nil
14751479
}
14761480

14771481
// CreateTestTableDescriptor converts a SQL string to a table for test purposes.

pkg/sql/distsql_physical_planner.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -364,7 +364,7 @@ func mergePlans(
364364
// exprFmtFlagsBase are FmtFlags used for serializing expressions; a proper
365365
// IndexedVar formatting function needs to be added on.
366366
var exprFmtFlagsBase = parser.FmtStarDatumFormat(
367-
parser.FmtSimple,
367+
parser.FmtParsable,
368368
func(buf *bytes.Buffer, _ parser.FmtFlags) {
369369
fmt.Fprintf(buf, "0")
370370
},

pkg/sql/parser/datum.go

Lines changed: 61 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,17 +56,25 @@ var (
5656
// Datum represents a SQL value.
5757
type Datum interface {
5858
TypedExpr
59+
60+
// AmbiguousFormat indicates whether the result of formatting this Datum can
61+
// be interpreted into more than one type. Used with
62+
// fmtFlags.disambiguateDatumTypes.
63+
AmbiguousFormat() bool
64+
5965
// Compare returns -1 if the receiver is less than other, 0 if receiver is
6066
// equal to other and +1 if receiver is greater than other.
6167
// TODO(nvanbenschoten) Should we look into merging this with cmpOps?
6268
Compare(other Datum) int
69+
6370
// Prev returns the previous datum and true, if one exists, or nil
6471
// and false. The previous datum satisfied the following
6572
// definition: if the receiver is "b" and the returned datum is "a",
6673
// then "a < b" and no other datum will compare such that "a < c <
6774
// b".
6875
// The return value is undefined if `IsMin()` returns true.
6976
Prev() (Datum, bool)
77+
7078
// IsMin returns true if the datum is equal to the minimum value the datum
7179
// type can hold.
7280
IsMin() bool
@@ -78,6 +86,7 @@ type Datum interface {
7886
// b".
7987
// The return value is undefined if `IsMax()` returns true.
8088
Next() (Datum, bool)
89+
8190
// IsMax returns true if the datum is equal to the maximum value the datum
8291
// type can hold.
8392
IsMax() bool
@@ -202,6 +211,9 @@ func (d *DBool) max() (Datum, bool) {
202211
return DBoolTrue, true
203212
}
204213

214+
// AmbiguousFormat implements the Datum interface.
215+
func (*DBool) AmbiguousFormat() bool { return false }
216+
205217
// Format implements the NodeFormatter interface.
206218
func (d *DBool) Format(buf *bytes.Buffer, f FmtFlags) {
207219
buf.WriteString(strconv.FormatBool(bool(*d)))
@@ -291,6 +303,9 @@ func (d *DInt) min() (Datum, bool) {
291303
return dMinInt, true
292304
}
293305

306+
// AmbiguousFormat implements the Datum interface.
307+
func (*DInt) AmbiguousFormat() bool { return true }
308+
294309
// Format implements the NodeFormatter interface.
295310
func (d *DInt) Format(buf *bytes.Buffer, f FmtFlags) {
296311
buf.WriteString(strconv.FormatInt(int64(*d), 10))
@@ -391,6 +406,9 @@ func (d *DFloat) min() (Datum, bool) {
391406
return dMinFloat, true
392407
}
393408

409+
// AmbiguousFormat implements the Datum interface.
410+
func (*DFloat) AmbiguousFormat() bool { return true }
411+
394412
// Format implements the NodeFormatter interface.
395413
func (d *DFloat) Format(buf *bytes.Buffer, f FmtFlags) {
396414
fl := float64(*d)
@@ -474,6 +492,9 @@ func (d *DDecimal) min() (Datum, bool) {
474492
return nil, false
475493
}
476494

495+
// AmbiguousFormat implements the Datum interface.
496+
func (*DDecimal) AmbiguousFormat() bool { return true }
497+
477498
// Format implements the NodeFormatter interface.
478499
func (d *DDecimal) Format(buf *bytes.Buffer, f FmtFlags) {
479500
buf.WriteString(d.Dec.String())
@@ -551,6 +572,9 @@ func (d *DString) max() (Datum, bool) {
551572
return nil, false
552573
}
553574

575+
// AmbiguousFormat implements the Datum interface.
576+
func (*DString) AmbiguousFormat() bool { return true }
577+
554578
// Format implements the NodeFormatter interface.
555579
func (d *DString) Format(buf *bytes.Buffer, f FmtFlags) {
556580
encodeSQLStringWithFlags(buf, string(*d), f)
@@ -607,6 +631,9 @@ func NewDCollatedString(contents string, locale string, env *CollationEnvironmen
607631
return &d
608632
}
609633

634+
// AmbiguousFormat implements the Datum interface.
635+
func (*DCollatedString) AmbiguousFormat() bool { return false }
636+
610637
// Format implements the NodeFormatter interface.
611638
func (d *DCollatedString) Format(buf *bytes.Buffer, f FmtFlags) {
612639
encodeSQLString(buf, d.Contents)
@@ -731,6 +758,9 @@ func (d *DBytes) max() (Datum, bool) {
731758
return nil, false
732759
}
733760

761+
// AmbiguousFormat implements the Datum interface.
762+
func (*DBytes) AmbiguousFormat() bool { return false }
763+
734764
// Format implements the NodeFormatter interface.
735765
func (d *DBytes) Format(buf *bytes.Buffer, f FmtFlags) {
736766
encodeSQLBytes(buf, string(*d))
@@ -848,6 +878,9 @@ func (d *DDate) min() (Datum, bool) {
848878
return nil, false
849879
}
850880

881+
// AmbiguousFormat implements the Datum interface.
882+
func (*DDate) AmbiguousFormat() bool { return true }
883+
851884
// Format implements the NodeFormatter interface.
852885
func (d *DDate) Format(buf *bytes.Buffer, f FmtFlags) {
853886
if !f.bareStrings {
@@ -1003,6 +1036,9 @@ func (d *DTimestamp) max() (Datum, bool) {
10031036
return nil, false
10041037
}
10051038

1039+
// AmbiguousFormat implements the Datum interface.
1040+
func (*DTimestamp) AmbiguousFormat() bool { return true }
1041+
10061042
// Format implements the NodeFormatter interface.
10071043
func (d *DTimestamp) Format(buf *bytes.Buffer, f FmtFlags) {
10081044
if !f.bareStrings {
@@ -1107,6 +1143,9 @@ func (d *DTimestampTZ) max() (Datum, bool) {
11071143
return nil, false
11081144
}
11091145

1146+
// AmbiguousFormat implements the Datum interface.
1147+
func (*DTimestampTZ) AmbiguousFormat() bool { return true }
1148+
11101149
// Format implements the NodeFormatter interface.
11111150
func (d *DTimestampTZ) Format(buf *bytes.Buffer, f FmtFlags) {
11121151
if !f.bareStrings {
@@ -1321,11 +1360,18 @@ func (d *DInterval) ValueAsString() string {
13211360
return (time.Duration(d.Duration.Nanos) * time.Nanosecond).String()
13221361
}
13231362

1324-
// Format implements the NodeFormatter interface. Example: "INTERVAL `1h2m`".
1363+
// AmbiguousFormat implements the Datum interface.
1364+
func (*DInterval) AmbiguousFormat() bool { return true }
1365+
1366+
// Format implements the NodeFormatter interface.
13251367
func (d *DInterval) Format(buf *bytes.Buffer, f FmtFlags) {
1326-
buf.WriteString("INTERVAL '")
1368+
if !f.bareStrings {
1369+
buf.WriteByte('\'')
1370+
}
13271371
buf.WriteString(d.ValueAsString())
1328-
buf.WriteByte('\'')
1372+
if !f.bareStrings {
1373+
buf.WriteByte('\'')
1374+
}
13291375
}
13301376

13311377
// Size implements the Datum interface.
@@ -1482,6 +1528,9 @@ func (d *DTuple) IsMin() bool {
14821528
return true
14831529
}
14841530

1531+
// AmbiguousFormat implements the Datum interface.
1532+
func (*DTuple) AmbiguousFormat() bool { return false }
1533+
14851534
// Format implements the NodeFormatter interface.
14861535
func (d *DTuple) Format(buf *bytes.Buffer, f FmtFlags) {
14871536
buf.WriteByte('(')
@@ -1602,6 +1651,9 @@ func (dNull) min() (Datum, bool) {
16021651
return DNull, true
16031652
}
16041653

1654+
// AmbiguousFormat implements the Datum interface.
1655+
func (dNull) AmbiguousFormat() bool { return false }
1656+
16051657
// Format implements the NodeFormatter interface.
16061658
func (dNull) Format(buf *bytes.Buffer, f FmtFlags) {
16071659
buf.WriteString("NULL")
@@ -1691,6 +1743,9 @@ func (d *DArray) IsMin() bool {
16911743
return d.Len() == 0
16921744
}
16931745

1746+
// AmbiguousFormat implements the Datum interface.
1747+
func (*DArray) AmbiguousFormat() bool { return false }
1748+
16941749
// Format implements the NodeFormatter interface.
16951750
func (d *DArray) Format(buf *bytes.Buffer, f FmtFlags) {
16961751
buf.WriteByte('{')
@@ -1760,6 +1815,9 @@ type DTable struct {
17601815
ValueGenerator
17611816
}
17621817

1818+
// AmbiguousFormat implements the Datum interface.
1819+
func (*DTable) AmbiguousFormat() bool { return false }
1820+
17631821
// Format implements the NodeFormatter interface.
17641822
func (t *DTable) Format(buf *bytes.Buffer, _ FmtFlags) {
17651823
buf.WriteString("<generated>")

pkg/sql/parser/datum_test.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -93,15 +93,15 @@ func TestDatumOrdering(t *testing.T) {
9393

9494
// Intervals
9595
{`'1 day':::interval`, noPrev, noNext,
96-
`INTERVAL '-9223372036854775808m-9223372036854775808d-2562047h47m16.854775808s'`,
97-
`INTERVAL '9223372036854775807m9223372036854775807d2562047h47m16.854775807s'`},
96+
`'-9223372036854775808m-9223372036854775808d-2562047h47m16.854775808s'`,
97+
`'9223372036854775807m9223372036854775807d2562047h47m16.854775807s'`},
9898
// Max interval: we use Postgres syntax, because Go doesn't accept
9999
// months/days and ISO8601 doesn't accept nanoseconds.
100100
{`'9223372036854775807 months 9223372036854775807 days ` +
101101
`2562047 hours 47 minutes 16 seconds 854775807 nanoseconds':::interval`,
102102
noPrev, valIsMax,
103-
`INTERVAL '-9223372036854775808m-9223372036854775808d-2562047h47m16.854775808s'`,
104-
`INTERVAL '9223372036854775807m9223372036854775807d2562047h47m16.854775807s'`},
103+
`'-9223372036854775808m-9223372036854775808d-2562047h47m16.854775808s'`,
104+
`'9223372036854775807m9223372036854775807d2562047h47m16.854775807s'`},
105105
// It's hard to generate a minimum interval! We can't use a
106106
// negative value inside the string constant, because that's not
107107
// allowed in a Postgres duration specification. We can't use the
@@ -113,8 +113,8 @@ func TestDatumOrdering(t *testing.T) {
113113
`2562047 hours 47 minutes 16 seconds':::interval` +
114114
`-'1 month 1 day 854775808 nanoseconds':::interval`,
115115
valIsMin, noNext,
116-
`INTERVAL '-9223372036854775808m-9223372036854775808d-2562047h47m16.854775808s'`,
117-
`INTERVAL '9223372036854775807m9223372036854775807d2562047h47m16.854775807s'`},
116+
`'-9223372036854775808m-9223372036854775808d-2562047h47m16.854775808s'`,
117+
`'9223372036854775807m9223372036854775807d2562047h47m16.854775807s'`},
118118

119119
// NULL
120120
{`NULL`, valIsMin, valIsMax, `NULL`, `NULL`},

pkg/sql/parser/eval.go

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2248,11 +2248,6 @@ func (expr *IndirectionExpr) Eval(ctx *EvalContext) (Datum, error) {
22482248
return arr.Array[subscriptIdx-1], nil
22492249
}
22502250

2251-
// Eval implements the TypedExpr interface.
2252-
func (expr *AnnotateTypeExpr) Eval(ctx *EvalContext) (Datum, error) {
2253-
return expr.Expr.(TypedExpr).Eval(ctx)
2254-
}
2255-
22562251
// Eval implements the TypedExpr interface.
22572252
func (expr *CollateExpr) Eval(ctx *EvalContext) (Datum, error) {
22582253
d, err := expr.Expr.(TypedExpr).Eval(ctx)

0 commit comments

Comments
 (0)