-
Notifications
You must be signed in to change notification settings - Fork 6.2k
planner: rewrite FTS predicates to LIKE for evaluation of non-TiCI query plan (#65626) #68743
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
base: release-8.5
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -60,6 +60,11 @@ go_library( | |
| "expr_to_pb.go", | ||
| "expression.go", | ||
| "extension.go", | ||
| <<<<<<< HEAD | ||
| ======= | ||
| "fts_helper.go", | ||
| "fts_to_like.go", | ||
| >>>>>>> f96cd1c2fd5 (planner: rewrite FTS predicates to LIKE for evaluation of non-TiCI query plan (#65626)) | ||
|
Comment on lines
+63
to
+67
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix this Bazel srcs block before merge. This hunk still has cherry-pick markers, and 🤖 Prompt for AI Agents |
||
| "function_traits.go", | ||
| "grouping_sets.go", | ||
| "helper.go", | ||
|
|
@@ -193,6 +198,7 @@ go_test( | |
| "evaluator_test.go", | ||
| "expr_to_pb_test.go", | ||
| "expression_test.go", | ||
| "fts_to_like_test.go", | ||
| "function_traits_test.go", | ||
| "grouping_sets_test.go", | ||
| "helper_test.go", | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -969,6 +969,13 @@ var funcs = map[string]functionClass{ | |
| ast.VecFromText: &vecFromTextFunctionClass{baseFunctionClass{ast.VecFromText, 1, 1}}, | ||
| ast.VecAsText: &vecAsTextFunctionClass{baseFunctionClass{ast.VecAsText, 1, 1}}, | ||
|
|
||
| <<<<<<< HEAD | ||
| ======= | ||
| // fts functions | ||
| ast.FTSMatchWord: &ftsMatchWordFunctionClass{baseFunctionClass{ast.FTSMatchWord, 2, 2}}, | ||
| ast.FTSMysqlMatchAgainst: &ftsMysqlMatchAgainstFunctionClass{baseFunctionClass{ast.FTSMysqlMatchAgainst, 2, -1}}, | ||
|
|
||
| >>>>>>> f96cd1c2fd5 (planner: rewrite FTS predicates to LIKE for evaluation of non-TiCI query plan (#65626)) | ||
|
Comment on lines
+972
to
+978
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove the unresolved conflict markers in the builtin registry. The 🤖 Prompt for AI Agents |
||
| // TiDB internal function. | ||
| ast.TiDBDecodeKey: &tidbDecodeKeyFunctionClass{baseFunctionClass{ast.TiDBDecodeKey, 1, 1}}, | ||
| ast.TiDBMVCCInfo: &tidbMVCCInfoFunctionClass{baseFunctionClass: baseFunctionClass{ast.TiDBMVCCInfo, 1, 1}}, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,167 @@ | ||
| // Copyright 2025 PingCAP, Inc. | ||
| // | ||
| // Licensed under the Apache License, Version 2.0 (the "License"); | ||
| // you may not use this file except in compliance with the License. | ||
| // You may obtain a copy of the License at | ||
| // | ||
| // http://www.apache.org/licenses/LICENSE-2.0 | ||
| // | ||
| // Unless required by applicable law or agreed to in writing, software | ||
| // distributed under the License is distributed on an "AS IS" BASIS, | ||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| // See the License for the specific language governing permissions and | ||
| // limitations under the License. | ||
|
|
||
| package expression | ||
|
|
||
| import ( | ||
| "github.com/pingcap/errors" | ||
| "github.com/pingcap/tidb/pkg/parser/ast" | ||
| "github.com/pingcap/tidb/pkg/types" | ||
| "github.com/pingcap/tidb/pkg/util/chunk" | ||
| "github.com/pingcap/tipb/go-tipb" | ||
| ) | ||
|
|
||
| var ( | ||
| _ functionClass = &ftsMatchWordFunctionClass{} | ||
| _ functionClass = &ftsMysqlMatchAgainstFunctionClass{} | ||
| ) | ||
|
|
||
| var ( | ||
| _ builtinFunc = &builtinFtsMatchWordSig{} | ||
| _ builtinFunc = &builtinFtsMysqlMatchAgainstSig{} | ||
| ) | ||
|
|
||
| type ftsMatchWordFunctionClass struct { | ||
| baseFunctionClass | ||
| } | ||
|
|
||
| type builtinFtsMatchWordSig struct { | ||
| baseBuiltinFunc | ||
| } | ||
|
|
||
| type ftsMysqlMatchAgainstFunctionClass struct { | ||
| baseFunctionClass | ||
| } | ||
|
|
||
| type builtinFtsMysqlMatchAgainstSig struct { | ||
| baseBuiltinFunc | ||
| modifier ast.FulltextSearchModifier | ||
| } | ||
|
|
||
| func (b *builtinFtsMatchWordSig) Clone() builtinFunc { | ||
| newSig := &builtinFtsMatchWordSig{} | ||
| newSig.cloneFrom(&b.baseBuiltinFunc) | ||
| return newSig | ||
| } | ||
|
|
||
| func (b *builtinFtsMysqlMatchAgainstSig) Clone() builtinFunc { | ||
| newSig := &builtinFtsMysqlMatchAgainstSig{} | ||
| newSig.cloneFrom(&b.baseBuiltinFunc) | ||
| newSig.modifier = b.modifier | ||
| return newSig | ||
| } | ||
|
|
||
| func (b *builtinFtsMysqlMatchAgainstSig) SetModifier(modifier ast.FulltextSearchModifier) { | ||
| b.modifier = modifier | ||
| } | ||
|
|
||
| // SetFTSMysqlMatchAgainstModifier sets the modifier for the internal `MATCH ... AGAINST` builtin signature. | ||
| // It is expected to be called by planner right after building the scalar function. | ||
| func SetFTSMysqlMatchAgainstModifier(sf *ScalarFunction, modifier ast.FulltextSearchModifier) error { | ||
| sig, ok := sf.Function.(*builtinFtsMysqlMatchAgainstSig) | ||
| if !ok { | ||
| return errors.Errorf("unexpected builtin signature for %s: %T", ast.FTSMysqlMatchAgainst, sf.Function) | ||
| } | ||
| sig.SetModifier(modifier) | ||
| return nil | ||
| } | ||
|
|
||
| func (c *ftsMatchWordFunctionClass) getFunction(ctx BuildContext, args []Expression) (builtinFunc, error) { | ||
| if err := c.verifyArgs(args); err != nil { | ||
| return nil, err | ||
| } | ||
|
|
||
| argAgainst := args[0] | ||
| argAgainstConstant, ok := argAgainst.(*Constant) | ||
| if !ok { | ||
| return nil, ErrNotSupportedYet.GenWithStackByArgs("match against a non-constant string") | ||
| } | ||
| if argAgainstConstant.Value.Kind() != types.KindString { | ||
| return nil, ErrNotSupportedYet.GenWithStackByArgs("match against a non-constant string") | ||
| } | ||
| argsMatch := args[1:] | ||
| for _, arg := range argsMatch { | ||
| _, ok := arg.(*Column) | ||
| if !ok { | ||
| return nil, ErrNotSupportedYet.GenWithStackByArgs("not matching a column") | ||
| } | ||
| } | ||
|
|
||
| argTps := make([]types.EvalType, 0, len(args)) | ||
| argTps = append(argTps, types.ETString, types.ETString) | ||
|
|
||
| bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETReal, argTps...) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
|
|
||
| sig := &builtinFtsMatchWordSig{bf} | ||
| sig.setPbCode(tipb.ScalarFuncSig_FTSMatchWord) | ||
| return sig, nil | ||
| } | ||
|
|
||
| func (b *builtinFtsMatchWordSig) evalReal(ctx EvalContext, row chunk.Row) (float64, bool, error) { | ||
| // Reject executing match against in TiDB side. | ||
| return 0, false, errors.Errorf("cannot use 'FTS_MATCH_WORD()' outside of fulltext index") | ||
| } | ||
|
|
||
| func (c *ftsMysqlMatchAgainstFunctionClass) getFunction(ctx BuildContext, args []Expression) (builtinFunc, error) { | ||
| if err := c.verifyArgs(args); err != nil { | ||
| return nil, err | ||
| } | ||
|
|
||
| argAgainst := args[0] | ||
| argAgainstConstant, ok := argAgainst.(*Constant) | ||
| if !ok { | ||
| return nil, ErrNotSupportedYet.GenWithStackByArgs("match against a non-constant string") | ||
| } | ||
| if argAgainstConstant.Value.Kind() != types.KindString && !argAgainstConstant.Value.IsNull() { | ||
| return nil, ErrNotSupportedYet.GenWithStackByArgs("match against a non-string constant") | ||
| } | ||
|
|
||
| argsMatch := args[1:] | ||
| for _, arg := range argsMatch { | ||
| _, ok := arg.(*Column) | ||
| if !ok { | ||
| return nil, ErrNotSupportedYet.GenWithStackByArgs("not matching a column") | ||
| } | ||
| } | ||
|
|
||
| argTps := make([]types.EvalType, 0, len(args)) | ||
| argTps = append(argTps, types.ETString) | ||
| for _, arg := range argsMatch { | ||
| if arg.GetType(ctx.GetEvalCtx()).EvalType() != types.ETString { | ||
| return nil, ErrNotSupportedYet.GenWithStackByArgs("Doesn't support match search on a non-string column without fulltext index") | ||
| } | ||
| argTps = append(argTps, types.ETString) | ||
| } | ||
|
|
||
| bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETReal, argTps...) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
|
|
||
| sig := &builtinFtsMysqlMatchAgainstSig{baseBuiltinFunc: bf} | ||
| sig.setPbCode(tipb.ScalarFuncSig_FTSMatchExpression) | ||
| return sig, nil | ||
| } | ||
|
|
||
| func (b *builtinFtsMysqlMatchAgainstSig) evalReal(ctx EvalContext, row chunk.Row) (float64, bool, error) { | ||
| // args[0] is validated to be a *Constant by getFunction; guard defensively | ||
| // since the sig may be reconstructed via the distsql path without that check. | ||
| if constArg, ok := b.args[0].(*Constant); ok && constArg.Value.IsNull() { | ||
| return 0, true, nil | ||
| } | ||
| return 0, false, errors.Errorf("cannot use 'MATCH ... AGAINST' outside of fulltext index") | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Resolve the leftover cherry-pick conflict in
.gitignore.This hunk still contains Git conflict markers and both sides of the merge. Please collapse it to the intended final ignore list before merging; otherwise the file stays corrupted and the added ignore rules are not reviewable.
🤖 Prompt for AI Agents