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

参数化查询无法正确使用索引 #5939

Closed
Xscaperrr opened this issue Sep 5, 2024 · 2 comments
Closed

参数化查询无法正确使用索引 #5939

Xscaperrr opened this issue Sep 5, 2024 · 2 comments
Labels
affects/none PR/issue: this bug affects none version. process/fixed Process of bug severity/none Severity of bug type/bug Type: something is unexpected

Comments

@Xscaperrr
Copy link
Contributor

Xscaperrr commented Sep 5, 2024

Describe the bug (required)

正常情况下,以下两种查询应该有相同的行为

MATCH(v:player{name:"Tim Duncan"})  RETURN v
MATCH(v:player) WHERE v.player.name =="Tim Duncan"  RETURN v

在使用参数化查询代替字符串查询后,形如MATCH(v:player{name:$s}) RETURN v的查询无法正确使用索引
查看执行计划可以发现.并未构造出带PREFIX查询的indexCtx

Your Environments (required)

  • OS: x86_64 GNU/Linux
  • Compiler: g++ (GCC) 11.2.0
  • CPU: Intel(R) Xeon(R) Gold 6138T CPU
  • Commit id (cdeab593e39416959f254ab18085ca17ac96ad19)

How To Reproduce(required)

Steps to reproduce the behavior:

1.使用NebulaGraph Console载入数据

:play basketballplayer;
use  basketballplayer;

2.保存参数

:param s=>"Tim Duncan"
  1. 查看两类查询的执行计划
EXPLAIN MATCH(v:player{name:$s})  RETURN v
EXPLAIN MATCH(v:player) WHERE v.player.name ==$s  RETURN v

Expected behavior

两个执行计划基本相同,都能使用PREFIX查询进行索引搜索

Additional context

该问题在我的此PR #5938 触发的tck测试中发现(parameter.feature),根据本人调试,其具体表现是在src/graph/util/OptimizerUtils.cpp文件中以下部分

Status OptimizerUtils::createIndexQueryCtx(Expression* filter,
                                           QueryContext* qctx,
                                           const IndexScan* node,
                                           std::vector<IndexQueryContext>& iqctx) {
  if (!filter) {
    // Only filter is nullptr when lookup on tagname
    // Degenerate back to tag lookup
    return createIndexQueryCtx(iqctx, qctx, node);
  }

  // items used for optimal index fetch and index scan context optimize.
  // for example : where c1 > 1 and c1 < 2 , the FilterItems should be :
  //               {c1, kRelGT, 1} , {c1, kRelLT, 2}
  std::vector<FilterItem> items;
  ScanKind kind;
  // rewrite ParameterExpression to ConstantExpression
  // TODO: refactor index selector logic to avoid this rewriting
  auto* newFilter = ExpressionUtils::rewriteParameter(filter, qctx);

在此处调用ExpressionUtils::rewriteParameter(filter, qctx)时,会将表达式转换为一个错误的形式player.name==__EMPTY__,然后在尝试构造columnHints时产生错误
在形如MATCH(v:player) WHERE v.player.name ==$s RETURN v的查询中,该表达式在传入该模块前就会被正确转换,故不会产生此问题

@Xscaperrr Xscaperrr added the type/bug Type: something is unexpected label Sep 5, 2024
@github-actions github-actions bot added affects/none PR/issue: this bug affects none version. severity/none Severity of bug labels Sep 5, 2024
@Xscaperrr
Copy link
Contributor Author

究其原因,是因为对应VariableExpression的属性isInner_从false错误改写为了true
在解码已经封装到IndexQueryContext中的filter时,会调用Expression::decode函数,该函数重写Expression::Kind::kVar的对象时,会进行以下操作(src/common/expression/Expression.cpp)

    case Expression::Kind::kVar: {
      exp = VariableExpression::make(pool);
      exp->resetFrom(decoder);
      return exp;
    }

而对应的resetFrom在VariableExpression.cpp中可以看到:

void VariableExpression::resetFrom(Decoder& decoder) {
  var_ = decoder.readStr();
  isInner_ = true;
}

而此时的参数并非内部参数,因此在解析时会得到错误。

@Xscaperrr
Copy link
Contributor Author

比较麻烦的是判断一个参数是否是内部参数需要使用qctx->existParameter()函数,而Expression::decode是Storage也使用的公用函数,不接收qctx参数。而VariableExpression又不提供更改该参数的能力。
但实际上在实际解析的ExpressionUtils::rewriteParameter中已经确认改写的参数是外部参数

Expression *ExpressionUtils::rewriteParameter(const Expression *expr, QueryContext *qctx) {
  auto matcher = [qctx](const Expression *e) -> bool {
    return e->kind() == Expression::Kind::kVar &&
           qctx->existParameter(static_cast<const VariableExpression *>(e)->var());
  };
  auto rewriter = [qctx](const Expression *e) -> Expression * {
    DCHECK_EQ(e->kind(), Expression::Kind::kVar);
    static_cast<const VariableExpression*>(e).i
    auto &v = const_cast<Expression *>(e)->eval(graph::QueryExpressionContext(qctx->ectx())());
    return ConstantExpression::make(qctx->objPool(), v);
  };

  return graph::RewriteVisitor::transform(expr, matcher, rewriter);
}

可以看到,只有对应名称的外部参数存在时才会解析。
因此,可以在VariableExpression中增加一个修改内部变量isInner_的接口,ExpressionUtils::rewriteParameterrewriter中手动调用该接口,将其值设置为false。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
affects/none PR/issue: this bug affects none version. process/fixed Process of bug severity/none Severity of bug type/bug Type: something is unexpected
Projects
None yet
Development

No branches or pull requests

1 participant