Skip to content

Commit 50bad68

Browse files
authored
planner: fix null-reject proof for WEEK and YEARWEEK (#68817)
close #68816
1 parent 351a32b commit 50bad68

3 files changed

Lines changed: 108 additions & 5 deletions

File tree

pkg/planner/util/null_misc.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,14 @@ func proveNullRejectedScalarFunc(
217217
return proveNullRejectedIn(ctx, innerSchema, expr, allowNullifiedFold)
218218
case ast.IsNull:
219219
return nullRejectProof{}
220+
case ast.Week, ast.YearWeek:
221+
// Only the date argument is NULL-preserving. A NULL mode argument is
222+
// treated as mode 0 by MySQL/TiDB, so these functions cannot be listed
223+
// in nullRejectNullPreservingFunctions.
224+
if proveNullRejected(ctx, innerSchema, expr.GetArgs()[0], allowNullifiedFold).mustNull {
225+
return nullRejectProof{nonTrue: true, mustNull: true}
226+
}
227+
return nullRejectProof{}
220228
}
221229

222230
if mode, ok := nullRejectRejectNullTests[expr.FuncName.L]; ok {

pkg/planner/util/null_misc_builtins.go

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ var nullRejectNullPreservingFunctions = map[string]struct{}{
3232
// Cast and unary operators.
3333
ast.Cast: {},
3434
ast.UnaryNot: {},
35-
ast.UnaryPlus: {},
3635
ast.UnaryMinus: {},
3736
ast.BitNeg: {},
3837

@@ -129,7 +128,6 @@ var nullRejectNullPreservingFunctions = map[string]struct{}{
129128
ast.Right: {},
130129
ast.Rpad: {},
131130
ast.RTrim: {},
132-
ast.Soundex: {},
133131
ast.Space: {},
134132
ast.Substr: {},
135133
ast.Substring: {},
@@ -185,11 +183,9 @@ var nullRejectNullPreservingFunctions = map[string]struct{}{
185183
ast.ToDays: {},
186184
ast.ToSeconds: {},
187185
ast.UnixTimestamp: {},
188-
ast.Week: {},
189186
ast.Weekday: {},
190187
ast.WeekOfYear: {},
191188
ast.Year: {},
192-
ast.YearWeek: {},
193189

194190
// Encryption, hashing, and compression.
195191
ast.Compress: {},

pkg/planner/util/null_misc_test.go

Lines changed: 100 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,15 @@ func TestNullRejectBuiltinRegistrySnapshot(t *testing.T) {
4040
require.NotEmpty(t, names)
4141
require.Equal(t, "729f5252bcd91efe1a4bbf0c383a36c5a2e52ed2d90d7aab0a3e0b450322294c", hex.EncodeToString(sum[:]))
4242

43+
internalScalarNames := map[string]struct{}{
44+
ast.Cast: {},
45+
}
46+
for name := range nullRejectNullPreservingFunctions {
47+
if _, ok := internalScalarNames[name]; ok {
48+
continue
49+
}
50+
require.Contains(t, names, name)
51+
}
4352
for name := range nullRejectRejectNullTests {
4453
require.Contains(t, names, name)
4554
}
@@ -55,7 +64,8 @@ func TestIsNullRejectedProofModes(t *testing.T) {
5564
outerC := newNullRejectIntColumn(3)
5665
innerS := newNullRejectStringColumn(4)
5766
innerUnsignedD := newNullRejectUintColumn(5)
58-
innerSchema := expression.NewSchema(innerA, innerB, innerS, innerUnsignedD)
67+
innerDate := newNullRejectDatetimeColumn(6)
68+
innerSchema := expression.NewSchema(innerA, innerB, innerS, innerUnsignedD, innerDate)
5969

6070
gtInnerAZero := newNullRejectFunc(t, exprCtx, ast.GT, types.NewFieldType(mysql.TypeTiny), innerA, expression.NewZero())
6171
eqInnerAZero := newNullRejectFunc(t, exprCtx, ast.EQ, types.NewFieldType(mysql.TypeTiny), innerA, expression.NewZero())
@@ -181,6 +191,38 @@ func TestIsNullRejectedProofModes(t *testing.T) {
181191
newNullRejectStringConst("abc"),
182192
innerS,
183193
)
194+
weekWithNullableMode := newNullRejectFunc(
195+
t,
196+
exprCtx,
197+
ast.Week,
198+
types.NewFieldType(mysql.TypeLonglong),
199+
newNullRejectStringConst("2024-01-08"),
200+
innerA,
201+
)
202+
yearWeekWithNullableMode := newNullRejectFunc(
203+
t,
204+
exprCtx,
205+
ast.YearWeek,
206+
types.NewFieldType(mysql.TypeLonglong),
207+
newNullRejectStringConst("2024-01-08"),
208+
innerA,
209+
)
210+
weekWithNullableDateAndOuterMode := newNullRejectFunc(
211+
t,
212+
exprCtx,
213+
ast.Week,
214+
types.NewFieldType(mysql.TypeLonglong),
215+
innerDate,
216+
outerC,
217+
)
218+
yearWeekWithNullableDateAndOuterMode := newNullRejectFunc(
219+
t,
220+
exprCtx,
221+
ast.YearWeek,
222+
types.NewFieldType(mysql.TypeLonglong),
223+
innerDate,
224+
outerC,
225+
)
184226
deferredInnerGTZero := newNullRejectDeferredConst(exprCtx, gtInnerAZero)
185227
deferredCoalesceInnerATwoGTTwo := newNullRejectDeferredConst(exprCtx,
186228
newNullRejectFunc(t, exprCtx, ast.GT, types.NewFieldType(mysql.TypeTiny), coalesceInnerATwo, newNullRejectIntConst(2)),
@@ -342,6 +384,54 @@ func TestIsNullRejectedProofModes(t *testing.T) {
342384
expr: newNullRejectNotNull(t, exprCtx, jsonSearchNullableEscape),
343385
expected: false,
344386
},
387+
{
388+
name: "week_nullable_mode_uses_default_mode_zero",
389+
expr: newNullRejectFunc(
390+
t,
391+
exprCtx,
392+
ast.GE,
393+
types.NewFieldType(mysql.TypeTiny),
394+
weekWithNullableMode,
395+
expression.NewZero(),
396+
),
397+
expected: false,
398+
},
399+
{
400+
name: "yearweek_nullable_mode_uses_default_mode_zero",
401+
expr: newNullRejectFunc(
402+
t,
403+
exprCtx,
404+
ast.GE,
405+
types.NewFieldType(mysql.TypeTiny),
406+
yearWeekWithNullableMode,
407+
expression.NewZero(),
408+
),
409+
expected: false,
410+
},
411+
{
412+
name: "week_nullable_date_rejects_null_even_with_outer_mode",
413+
expr: newNullRejectFunc(
414+
t,
415+
exprCtx,
416+
ast.GE,
417+
types.NewFieldType(mysql.TypeTiny),
418+
weekWithNullableDateAndOuterMode,
419+
expression.NewZero(),
420+
),
421+
expected: true,
422+
},
423+
{
424+
name: "yearweek_nullable_date_rejects_null_even_with_outer_mode",
425+
expr: newNullRejectFunc(
426+
t,
427+
exprCtx,
428+
ast.GE,
429+
types.NewFieldType(mysql.TypeTiny),
430+
yearWeekWithNullableDateAndOuterMode,
431+
expression.NewZero(),
432+
),
433+
expected: true,
434+
},
345435
{
346436
name: "deferred_expr_uses_symbolic_null_reject_proof",
347437
expr: deferredInnerGTZero,
@@ -393,6 +483,15 @@ func newNullRejectUintColumn(id int64) *expression.Column {
393483
}
394484
}
395485

486+
func newNullRejectDatetimeColumn(id int64) *expression.Column {
487+
return &expression.Column{
488+
UniqueID: id,
489+
ID: id,
490+
Index: int(id),
491+
RetType: types.NewFieldType(mysql.TypeDatetime),
492+
}
493+
}
494+
396495
func newNullRejectStringConst(value string) *expression.Constant {
397496
return &expression.Constant{
398497
Value: types.NewStringDatum(value),

0 commit comments

Comments
 (0)