Skip to content

From parser to executor, modifing nGQL statement

约 670 个字 107 行代码 预计阅读时间 5 分钟

这里以为Create TAG语句增加 VECTOR 属性为例进行叙述,既是给读者一份更改源码的参考,也是对自己更改代码逻辑的梳理。

我们最后期望的语句形式如下:

  • 有 Default value
    C++
    CREATE TAG person(name STRING, embedding VECTOR(2) DEFAULT [0.01, 0.01],
        embedding2 VECTOR(3) DEFAULT [0.02, 0.02, 0.02])
    
  • 有 TTL 选项
    C++
    CREATE TAG person(name STRING, embedding VECTOR(128) NOT NULL,
        embedding2 VECTOR(256) NOT NULL)
        ttl_duration = 100, ttl_col = "create_time"
    

0.0 Context & Plan

  • Query、Session、Storage 等打包成 Request Context,随后将 Request Context 打包成 Query Context
    C++
    RequestContextPtr rctx_;                    // Session 传入
    std::unique_ptr<ValidateContext> vctx_;     // validate 修改
    std::unique_ptr<ExecutionContext> ectx_;    // Execution 修改
    std::unique_ptr<ExecutionPlan> ep_;         // planner 修改
    
  • CreateSchemaContext CreateTagValidator 自己拥有的上下文,用于将 Sentence 转变为 Plan,里面是需要传递给 CreateTagNode 的参数。
    C++
    struct CreateSchemaContext final : public AstContext {
    bool ifNotExist{false};
    std::string name;
    meta::cpp2::Schema schema;
    };
    
  • 执行计划中的每个 Node 实际上需要的是 QueryContext,以及其他 Validator 自己生成的 Schema 或 Name 等变量

    CreateTag 需要QueryContext* qctx, std::string name, meta::cpp2::Schema schema, 只有 qctx 是整个 nGQL 都需要维护的

  • 在 Scheduler 中将 Logical Plan 变成 Physical Plan,这里是 CreateTag 变成 CreateTagExecutor,这里调用了 Meta 服务中的 createTagSchema 方法

0.1 Processing

  1. Parser: From string to sentences
    - 最外层是 CreateTagSentence,里面每个属性是 ColumnSpecification
  2. Validater:
    - 验证 column,将验证后的 column 加入 schema,并将这个新构建的 schema 传入 CreateTagContext
    - CreateTagPlanner 通过 transform 生成 Logical Plan
  3. Optimizer: 这里暂时没有优化,先跳过
  4. Executor: 在 Scheduler 中将生成的 Logical Plan 变成 Physical Plan,在 graphd 的 executor 执行过程中会向 metad 请求服务

1. Parser modifing

我们需要对 scanner.lex 和 parser.yy 进行修改,这里我们可能需要对 bison 中的语法进行检查,出现复杂问题我们可能还要进行 debug,我的另一篇文章中有详细叙述该过程,这里不再赘述。

scanner.lex 修改

主要就是增加 VECTOR 关键字的解析

C++
"VECTOR"  { return TokenType::KW_VECTOR; }

parser.yy 修改

这里我们需要对 VECTOR 的语法进行解析,首先一个问题就是,对[0.01, 0.02, 0.03]的解析会有两种解析方式成立:

  • 一种是解析成 List
  • 一种是我们希望的解析成 Vector
C++
  Example: L_BRACKET DOUBLE  R_BRACKET
  First reduce derivation
    container_expression
     list_expression
       L_BRACKET opt_expression_list       R_BRACKET
                   expression_list
                     expression_internal
                       constant_expression
                         DOUBLE 
  Second reduce derivation
    container_expression
     vector_expression
       L_BRACKET vector_item_list            R_BRACKET
                   vector_element_expression
                     DOUBLE 

暂时我们的处理是解析成 Vector 的规则放在首位,因为 bison 是默认从头到尾匹配规则。

我们同样需要处理 Column Spec 支持 VECTOR 类型,而 VECTOR 类型需要有 dimension 参数,我们在ColumnSpecification中增加一个成员变量

C++
column_spec
    : name_label type_spec {
        $$ = new ColumnSpecification($1, $2->type, new ColumnProperties(), $2->type_length_ref().value_or(0), $2->geo_shape_ref().value_or(meta::cpp2::GeoShape::ANY), $2->dimension_ref().value_or(0));
        delete $2;
    }
    | name_label type_spec column_properties {
        $$ = new ColumnSpecification($1, $2->type, $3, $2->type_length_ref().value_or(0), $2->geo_shape_ref().value_or(meta::cpp2::GeoShape::ANY), $2->dimension_ref().value_or(0));
        delete $2;
    }

Transfer Statement

  • 需要修改 ColumnSpecification 支持 VECTOR 类型,由于 VECTOR 类型中有 dimension 参数,我们需要增加 dimension 参数
    C++
    ColumnSpecification(std::string *name,
                        nebula::cpp2::PropertyType type,
                        ColumnProperties *properties = nullptr,
                        int16_t typeLen = 0,
                        meta::cpp2::GeoShape geoShape = meta::cpp2::GeoShape::ANY,
                        int16_t dimension = 0)
        : name_(name),
          type_(type),
          properties_(DCHECK_NOTNULL(properties)),
          typeLen_(typeLen),
          geoShape_(geoShape),
          dimension_(dimension) {}
    
  • 需要修改ColumnTypeDef支持 VECTOR 类型,由于 VECTOR 类型中有 dimension 参数,我们需要在 meta.thrift 中添加该参数
C++
struct ColumnTypeDef {
    1: required common.PropertyType    type,
    // type_length is valid for fixed_string type
    2: optional i16                    type_length = 0,
    // geo_shape is valid for geography type
    3: optional GeoShape               geo_shape,
    // dim is valid for vector type
    4: optional i16                    dimension,
}

2. Validater Modifing

修改 validate columns 来实现对 VECTOR 类型 column 的验证

C++
static Status validateColumns(const std::vector<ColumnSpecification *> &columnSpecs,
                              meta::cpp2::Schema &schema,
                              bool isAlter = false) {
  for (auto &spec : columnSpecs) {
    meta::cpp2::ColumnDef column;
    auto type = spec->type();
    column.name_ref() = *spec->name();
    column.type.type_ref() = type;
    if (nebula::cpp2::PropertyType::FIXED_STRING == type) {
      column.type.type_length_ref() = spec->typeLen();
    } else if (nebula::cpp2::PropertyType::GEOGRAPHY == type) {
      column.type.geo_shape_ref() = spec->geoShape();
    } else if (nebula::cpp2::PropertyType::VECTOR == type) {
      column.type.dimension_ref() = spec->dimension();
    }
    // ...
    schema.columns_ref().value().emplace_back(std::move(column));
  }
  return Status::OK();
}

修改 Schema 来实现对 VECTOR 类型的处理

  • 首先是增加 SchemaField 对 VECTOR 的处理
    C++
    SchemaField(const std::string& name,
                nebula::cpp2::PropertyType type,
                bool nullable,
                bool hasDefault,
                std::string defaultValue,
                size_t size,
                size_t offset,
                size_t nullFlagPos,
                cpp2::GeoShape geoShape,
                int32_t dimension)
        : name_(name),
          type_(type),
          nullable_(nullable),
          hasDefault_(hasDefault),
          defaultValue_(defaultValue),
          size_(size),
          offset_(offset),
          nullFlagPos_(nullFlagPos),
          geoShape_(geoShape),
          dimension_(dimension) {}
    
  • 增加 NebulaSchemaProvider 添加 VECTOR 属性 column 的方法
    C++
    void addField(const std::string& name,
                  nebula::cpp2::PropertyType type,
                  size_t fixedStrLen = 0,
                  bool nullable = false,
                  std::string defaultValue = "",
                  cpp2::GeoShape geoShape = cpp2::GeoShape::ANY,
                  int32_t dimension = 0);