Skip to content

Commit

Permalink
(#162) Add grammar and parsing for basic json where clause filtering.
Browse files Browse the repository at this point in the history
Summary:
Supporting the '->' and '->>' notations in our grammar for json columns. For a quick
overview of these operators with examples you can refer to:
http://clarkdave.net/2013/06/what-can-you-do-with-postgresql-and-json/. Note that, currently we do
not support any casts and hence the datatype after applying these operators are considered as
strings.

Test Plan: unit tests.

Reviewers: neil, mihnea, robert

Reviewed By: robert

Subscribers: yql

Differential Revision: https://phabricator.dev.yugabyte.com/D4617
  • Loading branch information
pritamdamania87 committed Apr 27, 2018
1 parent 0bc9d69 commit 193cd4c
Show file tree
Hide file tree
Showing 8 changed files with 274 additions and 4 deletions.
8 changes: 8 additions & 0 deletions src/yb/common/ql_type.h
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,10 @@ class QLType {
return IsInteger(id_);
}

bool IsJson() const {
return IsJson(id_);
}

bool IsNumeric() const {
return IsNumeric(id_);
}
Expand Down Expand Up @@ -377,6 +381,10 @@ class QLType {
return (t >= INT8 && t <= INT64) || t == VARINT;
}

static bool IsJson(DataType t) {
return t == JSONB;
}

static bool IsNumeric(DataType t) {
return IsInteger(t) || t == FLOAT || t == DOUBLE || t == DECIMAL;
}
Expand Down
28 changes: 25 additions & 3 deletions src/yb/yql/cql/ql/parser/parser_gram.y
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ typedef PTQualifiedNameListNode::SharedPtr PQualifiedNameListNode;

typedef PTBaseType::SharedPtr PType;
typedef PTCharBaseType::SharedPtr PCharBaseType;
typedef PTJsonOpListNode::SharedPtr PJsonOpListNode;

// Inactive parsing node types.
typedef UndefTreeNode::SharedPtr UndefType;
Expand Down Expand Up @@ -346,6 +347,8 @@ using namespace yb::ql;
%type <PTypeField> TypeField
%type <PTypeFieldListNode> TypeFieldList

%type <PJsonOpListNode> json_ref

// Name nodes.
%type <PName> indirection_el columnElem

Expand Down Expand Up @@ -669,9 +672,9 @@ using namespace yb::ql;
// Character constants.
%token <PChar> CCONST

// Multichar operators: "::", "..", ":=", "=>", "<=", ">=", "<>", "!=".
// Multichar operators: "::", "..", ":=", "=>", "<=", ">=", "<>", "!=", "->", "->>".
%token TYPECAST DOT_DOT COLON_EQUALS EQUALS_GREATER LESS_EQUALS
GREATER_EQUALS NOT_EQUALS
GREATER_EQUALS NOT_EQUALS SINGLE_ARROW DOUBLE_ARROW

// Special ops. The grammar treat them as keywords, but they are not in the kwlist.h list and so can
// never be entered directly. The filter in parser.c creates these tokens when required (based on
Expand Down Expand Up @@ -3106,7 +3109,9 @@ a_expr:
PTExprListNode::SharedPtr args = MAKE_NODE(@1, PTExprListNode, $3);
$$ = MAKE_NODE(@1, PTSubscriptedColumn, $1->name(), args);
}

| columnref json_ref {
$$ = MAKE_NODE(@1, PTJsonColumnWithOperators, $1->name(), $2);
}
// Logical expression.
| NOT a_expr {
$$ = MAKE_NODE(@1, PTLogic1, ExprOperator::kLogic1, QL_OP_NOT, $2);
Expand Down Expand Up @@ -4072,6 +4077,23 @@ columnref:
}
;

json_ref:
SINGLE_ARROW Sconst {
PTJsonOperator::SharedPtr node = MAKE_NODE(@1, PTJsonOperator, JsonOperator::JSON_OBJECT, $2);
$$ = MAKE_NODE(@1, PTJsonOpListNode, node);
}
| SINGLE_ARROW Sconst json_ref {
PTJsonOperator::SharedPtr json_op = MAKE_NODE(@1, PTJsonOperator, JsonOperator::JSON_OBJECT,
$2);
$3->Prepend(json_op);
$$ = $3;
}
| DOUBLE_ARROW Sconst {
PTJsonOperator::SharedPtr node = MAKE_NODE(@1, PTJsonOperator, JsonOperator::JSON_TEXT, $2);
$$ = MAKE_NODE(@1, PTJsonOpListNode, node);
}
;

indirection_el:
'.' attr_name {
$$ = MAKE_NODE(@1, PTName, $2);
Expand Down
12 changes: 12 additions & 0 deletions src/yb/yql/cql/ql/parser/scanner_lex.l
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,8 @@ less_equals "<="
greater_equals ">="
less_greater "<>"
not_equals "!="
single_arrow "->"
double_arrow "->>"

/*
* "self" is the set of chars that should be returned as single-character
Expand Down Expand Up @@ -814,6 +816,16 @@ other .
return GramProcessor::make_COLON_EQUALS(cursor_);
}

{single_arrow} {
SET_YYLLOC();
return GramProcessor::make_SINGLE_ARROW(cursor_);
}

{double_arrow} {
SET_YYLLOC();
return GramProcessor::make_DOUBLE_ARROW(cursor_);
}

{equals_greater} {
SET_YYLLOC();
return GramProcessor::make_EQUALS_GREATER(cursor_);
Expand Down
65 changes: 65 additions & 0 deletions src/yb/yql/cql/ql/ptree/pt_expr.cc
Original file line number Diff line number Diff line change
Expand Up @@ -594,6 +594,7 @@ CHECKED_STATUS PTRelationExpr::AnalyzeOperator(SemContext *sem_context,
// CheckLhsExpr already checks that this is either kRef or kBcall
DCHECK(op1->expr_op() == ExprOperator::kRef ||
op1->expr_op() == ExprOperator::kSubColRef ||
op1->expr_op() == ExprOperator::kJsonOperatorRef ||
op1->expr_op() == ExprOperator::kBcall);
if (op1->expr_op() == ExprOperator::kRef) {
const PTRef *ref = static_cast<const PTRef *>(op1.get());
Expand Down Expand Up @@ -743,6 +744,70 @@ void PTRef::PrintSemanticAnalysisResult(SemContext *sem_context) {
VLOG(3) << "SEMANTIC ANALYSIS RESULT (" << *loc_ << "):\n" << "Not yet avail";
}

PTJsonOperator::PTJsonOperator(MemoryContext *memctx,
YBLocation::SharedPtr loc,
const JsonOperator& json_operator,
const MCSharedPtr<MCString>& arg)
: TreeNode(memctx, loc),
json_operator_(json_operator),
arg_(arg) {
}

PTJsonOperator::~PTJsonOperator() {
}

Status PTJsonOperator::Analyze(SemContext *sem_context) {
return Status::OK();
}

//--------------------------------------------------------------------------------------------------

PTJsonColumnWithOperators::PTJsonColumnWithOperators(MemoryContext *memctx,
YBLocation::SharedPtr loc,
const PTQualifiedName::SharedPtr& name,
const PTJsonOpListNode::SharedPtr& args)
: PTOperator0(memctx, loc, ExprOperator::kJsonOperatorRef, yb::QLOperator::QL_OP_NOOP),
name_(name),
args_(args) {
}

PTJsonColumnWithOperators::~PTJsonColumnWithOperators() {
}

CHECKED_STATUS PTJsonColumnWithOperators::AnalyzeOperator(SemContext *sem_context) {

// Look for a column descriptor from symbol table.
RETURN_NOT_OK(name_->Analyze(sem_context));
desc_ = sem_context->GetColumnDesc(name_->last_name());
if (desc_ == nullptr) {
return sem_context->Error(this, "Column doesn't exist", ErrorCode::UNDEFINED_COLUMN);
}

SemState sem_state(sem_context);

if (!desc_->ql_type()->IsJson()) {
return sem_context->Error(this, "Column provided is not json data type",
ErrorCode::CQL_STATEMENT_INVALID);
}

// Analyze each operator.
RETURN_NOT_OK(args_->Analyze(sem_context));

// Without any CASTs, json columns are considered as strings.
ql_type_ = QLType::Create(DataType::JSONB);
if (args_->element(args_->size() - 1)->json_operator() == JsonOperator::JSON_OBJECT) {
internal_type_ = InternalType::kJsonbValue;
} else {
internal_type_ = InternalType::kStringValue;
}

return Status::OK();
}

CHECKED_STATUS PTJsonColumnWithOperators::CheckLhsExpr(SemContext *sem_context) {
return Status::OK();
}

//--------------------------------------------------------------------------------------------------

PTSubscriptedColumn::PTSubscriptedColumn(MemoryContext *memctx,
Expand Down
104 changes: 104 additions & 0 deletions src/yb/yql/cql/ql/ptree/pt_expr.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,53 @@ enum class ExprOperator : int {

// Relation operators that take unspecified number of operands.
kCollection = 14,

// Reference to a column with json operators.
kJsonOperatorRef = 15,
};

enum class JsonOperator {
JSON_OBJECT,
JSON_TEXT
};

// Class representing a json operator.
class PTJsonOperator : public TreeNode {
public:
//------------------------------------------------------------------------------------------------
// Public types.
typedef MCSharedPtr<PTJsonOperator> SharedPtr;
typedef MCSharedPtr<const PTJsonOperator> SharedPtrConst;

//------------------------------------------------------------------------------------------------
// Constructors and destructor.
PTJsonOperator(MemoryContext *memctx,
YBLocation::SharedPtr loc,
const JsonOperator& json_operator,
const MCSharedPtr<MCString>& arg);

virtual ~PTJsonOperator();

template<typename... TypeArgs>
inline static PTJsonOperator::SharedPtr MakeShared(MemoryContext *memctx,
TypeArgs&&... args) {
return MCMakeShared<PTJsonOperator>(memctx, std::forward<TypeArgs>(args)...);
}

// Node semantics analysis.
virtual CHECKED_STATUS Analyze(SemContext *sem_context);

const MCString& arg() const {
return *arg_;
}

JsonOperator json_operator() const {
return json_operator_;
}

protected:
JsonOperator json_operator_;
MCSharedPtr<MCString> arg_;
};

//--------------------------------------------------------------------------------------------------
Expand Down Expand Up @@ -269,6 +316,7 @@ class PTExpr : public TreeNode {
};

using PTExprListNode = TreeListNode<PTExpr>;
using PTJsonOpListNode = TreeListNode<PTJsonOperator>;

//--------------------------------------------------------------------------------------------------
// Tree Nodes for Collections -- treated as expressions with flexible arity
Expand Down Expand Up @@ -895,6 +943,62 @@ class PTRef : public PTOperator0 {
const ColumnDesc *desc_;
};

// A json column with json operators applied to the column.
class PTJsonColumnWithOperators : public PTOperator0 {
public:
//------------------------------------------------------------------------------------------------
// Public types.
typedef MCSharedPtr<PTJsonColumnWithOperators> SharedPtr;
typedef MCSharedPtr<const PTJsonColumnWithOperators> SharedPtrConst;

//------------------------------------------------------------------------------------------------
// Constructor and destructor.
PTJsonColumnWithOperators(MemoryContext *memctx,
YBLocation::SharedPtr loc,
const PTQualifiedName::SharedPtr& name,
const PTJsonOpListNode::SharedPtr& args);
virtual ~PTJsonColumnWithOperators();

// Support for shared_ptr.
template<typename... TypeArgs>
inline static PTJsonColumnWithOperators::SharedPtr MakeShared(MemoryContext *memctx,
TypeArgs&&... args) {
return MCMakeShared<PTJsonColumnWithOperators>(memctx, std::forward<TypeArgs>(args)...);
}

using PTOperatorExpr::AnalyzeOperator;
virtual CHECKED_STATUS AnalyzeOperator(SemContext *sem_context) override;

// Access function for name.
const PTQualifiedName::SharedPtr& name() const {
return name_;
}

const PTJsonOpListNode::SharedPtr& operators() const {
return args_;
}

// Access function for descriptor.
const ColumnDesc *desc() const {
return desc_;
}

// Node type.
virtual TreeNodeOpcode opcode() const override {
return TreeNodeOpcode::kPTJsonOp;
}

// Analyze LHS expression.
virtual CHECKED_STATUS CheckLhsExpr(SemContext *sem_context) override;

private:
PTQualifiedName::SharedPtr name_;
PTJsonOpListNode::SharedPtr args_;

// Fields that should be resolved by semantic analysis.
const ColumnDesc *desc_ = nullptr;
};

// SubColumn Reference. The datatype of this expression would need to be resolved by the analyzer.
class PTSubscriptedColumn : public PTOperator0 {
public:
Expand Down
3 changes: 2 additions & 1 deletion src/yb/yql/cql/ql/ptree/tree_node.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ YB_DEFINE_ENUM(TreeNodeOpcode,
(kPTSubscript)
(kPTAllColumns)
(kPTAssign)
(kPTBindVar));
(kPTBindVar)
(kPTJsonOp));

// TreeNode base class.
class TreeNode : public MCBase {
Expand Down
10 changes: 10 additions & 0 deletions src/yb/yql/cql/ql/test/ql-parser-test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,16 @@ TEST_F(QLTestParser, TestQLParser) {
// Create table with jsonb type.
PARSE_VALID_STMT("CREATE TABLE human_resource (h1 int, r1 int, data jsonb, "
"PRIMARY KEY ((h1), r1));");
// Valid json operators.
PARSE_VALID_STMT("SELECT * FROM t WHERE c2->'a' = '1';");
PARSE_VALID_STMT("SELECT * FROM t WHERE c2->'a'->'b' = '1';");
PARSE_VALID_STMT("SELECT * FROM t WHERE c2->'a'->'b'->>'c' = '1';");
PARSE_VALID_STMT("SELECT * FROM t WHERE c2->>'a' = '1';");

// Invalid json operators.
PARSE_INVALID_STMT("SELECT * FROM t WHERE c2->>'a'->'b' = '1';");
PARSE_INVALID_STMT("SELECT * FROM t WHERE c2->>a = '1';");
PARSE_INVALID_STMT("SELECT * FROM t WHERE c2->a = '1';");
}

TEST_F(QLTestParser, TestStaticColumn) {
Expand Down
Loading

0 comments on commit 193cd4c

Please sign in to comment.