-
PostgreSQL의 Parser Stage데이터베이스 개발 2019. 8. 6. 19:13
PostgreSQL의 Parser Stage를 간단하게 정리한다.
Parser Stage는 lexer 단계와 parser 단계로 구성된다.
lexer는 src/backend/parser/scan.l 파일에 정의되어 있다. 식별자(SQL 키워드)를 인식하며 일반적으로 token이라고 한다.
parser는 src/backend/parser/gram.y 파일에 정의되어 있다. grammar rule과 action으로 구성되어 있다.
scan.l 파일은 flex 프로그램을 통해 scan.c로 변환된다.
gram.y 파일은 bison 프로그램을 통해 gram.c로 변환된다.
변환 및 컴파일은 makefile을 사용해 자동화된다.
gram.y 파일 내용을 이해하기 위해서는 bison을 알아야 한다.
PostgreSQL에 그래프 처리가 가능하도록 확장한 AgensGraph의 Parser에 대해 알아보자.
다음은 AgensGraph의 gram.y 파일의 일부이다.
/* * Agens Graph */ CreateGraphStmt: CREATE GRAPH ColId AUTHORIZATION RoleSpec { CreateGraphStmt *n = makeNode(CreateGraphStmt); n->graphname = $3; n->authrole = $5; n->if_not_exists = false; $$ = (Node *) n; } | CREATE GRAPH ColId { CreateGraphStmt *n = makeNode(CreateGraphStmt); n->graphname = $3; n->authrole = NULL; n->if_not_exists = false; $$ = (Node *) n; } | CREATE GRAPH IF_P NOT EXISTS ColId AUTHORIZATION RoleSpec { CreateGraphStmt *n = makeNode(CreateGraphStmt); n->graphname = $6; n->authrole = $8; n->if_not_exists = true; $$ = (Node *) n; } | CREATE GRAPH IF_P NOT EXISTS ColId { CreateGraphStmt *n = makeNode(CreateGraphStmt); n->graphname = $6; n->authrole = NULL; n->if_not_exists = true; $$ = (Node *) n; } ; ... cypher_expr: cypher_expr OR cypher_expr { $$ = makeOrExpr($1, $3, @2); } | cypher_expr AND cypher_expr { $$ = makeAndExpr($1, $3, @2); } | NOT cypher_expr { $$ = makeNotExpr($2, @1); } | cypher_expr '=' cypher_expr { $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "=", $1, $3, @2); } | cypher_expr NOT_EQUALS cypher_expr { $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "<>", $1, $3, @2); } | cypher_expr '<' cypher_expr { $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "<", $1, $3, @2); } | cypher_expr '>' cypher_expr { $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, ">", $1, $3, @2); } | cypher_expr LESS_EQUALS cypher_expr { $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "<=", $1, $3, @2); } | cypher_expr GREATER_EQUALS cypher_expr { $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, ">=", $1, $3, @2); } | cypher_expr '+' cypher_expr { $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "+", $1, $3, @2); } | cypher_expr '-' cypher_expr { $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "-", $1, $3, @2); } | cypher_expr '*' cypher_expr { $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "*", $1, $3, @2); } | cypher_expr '/' cypher_expr { $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "/", $1, $3, @2); } | cypher_expr '%' cypher_expr { $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "%", $1, $3, @2); } | cypher_expr '^' cypher_expr { $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "^", $1, $3, @2); } | '+' cypher_expr %prec UMINUS { $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "+", NULL, $2, @1); } | '-' cypher_expr %prec UMINUS { if (IsA($2, A_Const)) { A_Const *con = (A_Const *) $2; if (con->val.type == T_Integer) { con->val.val.ival = -con->val.val.ival; con->location = @1; $$ = $2; } else if (con->val.type == T_Float) { doNegateFloat(&con->val); con->location = @1; $$ = $2; } else { $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "-", NULL, $2, @1); } } else { $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "-", NULL, $2, @1); } } | cypher_expr qual_Op cypher_expr %prec Op { $$ = (Node *) makeA_Expr(AEXPR_OP, $2, $1, $3, @2); } | qual_Op cypher_expr %prec Op { $$ = (Node *) makeA_Expr(AEXPR_OP, $1, NULL, $2, @1); } | cypher_expr qual_Op %prec POSTFIXOP { $$ = (Node *) makeA_Expr(AEXPR_OP, $2, $1, NULL, @2); } | cypher_expr '[' cypher_expr ']' { A_Indices *ind; A_Indirection *n; ind = makeNode(A_Indices); ind->is_slice = false; ind->lidx = NULL; ind->uidx = $3; if (IsA($1, A_Indirection)) { n = (A_Indirection *) $1; n->indirection = lappend(n->indirection, ind); $$ = $1; } else { n = makeNode(A_Indirection); n->arg = $1; n->indirection = list_make1(ind); $$ = (Node *) n; } } | cypher_expr '[' cypher_expr_opt DOT_DOT cypher_expr_opt ']' { A_Indices *ind; A_Indirection *n; ind = makeNode(A_Indices); ind->is_slice = true; ind->lidx = $3; ind->uidx = $5; if (IsA($1, A_Indirection)) { n = (A_Indirection *) $1; n->indirection = lappend(n->indirection, ind); $$ = $1; } else { n = makeNode(A_Indirection); n->arg = $1; n->indirection = list_make1(ind); $$ = (Node *) n; } } | cypher_expr IN_P cypher_expr { if (IsA($3, SubLink)) { SubLink *n; n = (SubLink *) $3; n->subLinkType = ANY_SUBLINK; n->testexpr = $1; n->location = @2; $$ = (Node *) n; } else { $$ = (Node *) makeSimpleA_Expr(AEXPR_IN, "=", $1, $3, @2); } } | cypher_expr STARTS WITH cypher_expr %prec STARTS { $$ = (Node *) makeFuncCall( SystemFuncName("string_starts_with"), list_make2($1, $4), @2); } | cypher_expr ENDS WITH cypher_expr %prec ENDS { $$ = (Node *) makeFuncCall( SystemFuncName("string_ends_with"), list_make2($1, $4), @2); } | cypher_expr CONTAINS cypher_expr { $$ = (Node *) makeFuncCall( SystemFuncName("string_contains"), list_make2($1, $3), @2); } | cypher_expr EQUALS_TILDE cypher_expr { $$ = (Node *) makeFuncCall( SystemFuncName("string_regex"), list_make2($1, $3), @2); } | cypher_expr IS NULL_P %prec IS { NullTest *n; n = makeNode(NullTest); n->arg = (Expr *) $1; n->nulltesttype = IS_NULL; n->location = @2; $$ = (Node *) n; } | cypher_expr IS NOT NULL_P %prec IS { NullTest *n; n = makeNode(NullTest); n->arg = (Expr *) $1; n->nulltesttype = IS_NOT_NULL; n->location = @2; $$ = (Node *) n; } | cypher_expr TYPECAST type_function_name { TypeName *n; n = (TypeName *) makeTypeName($3); n->location = @3; $$ = makeTypeCast($1, n, @2); } | cypher_expr '.' cypher_expr_escaped_name { A_Indirection *n; if (IsA($1, A_Indirection)) { n = (A_Indirection *) $1; n->indirection = lappend(n->indirection, $3); $$ = $1; } else { n = makeNode(A_Indirection); n->arg = $1; n->indirection = list_make1($3); $$ = (Node *) n; } } | cypher_expr '.' cypher_expr_name { A_Indirection *n; if (IsA($1, ColumnRef)) { ColumnRef *cref = (ColumnRef *) $1; cref->fields = lappend(cref->fields, $3); $$ = $1; } else if (IsA($1, A_Indirection)) { n = (A_Indirection *) $1; n->indirection = lappend(n->indirection, $3); $$ = $1; } else { n = makeNode(A_Indirection); n->arg = $1; n->indirection = list_make1($3); $$ = (Node *) n; } } | cypher_c_expr ;
위 예제에 대해 줄 단위로 살펴보자.
...