ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 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
    		;
    

    위 예제에 대해 줄 단위로 살펴보자.

    ...

Designed by Tistory.