Postgres/Postgres Internal
Flex와 Bison을 이용한 쿼리문 파싱 예제 만들기
moxie2ks
2025. 3. 6. 18:01
728x90
반응형
개요
Postgres에는 쿼리 파싱을 위해 사용되는 두 가지 주요 툴체인이 존재한다: flex
와 bison
이다. 이들은 각각 문자열 파싱을 위한 문법을 제공하며, 그에 맞게 특정 문자열을 파싱 하는 데 사용된다. 본 문서에서는 flex
와 bison
을 활용하여 SQL의 SELECT 문을 파싱하는 예제를 제시함으로써 PG 내부 쿼리 파서를 보다 쉽게 이해할 수 있도록 한다.
gram.y 파일 생성
쿼리 파싱을 위한 토큰을 정의하고, 각 토큰에 올 수 있는 다른 토큰들을 규정한다.
// C code block between '%{' and '%}'
%{
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// prototype of creating token function
int yylex();
// prototype of printing error message function
void yyerror(const char* s);
%}
// Defines the different data types that can be used in parsing.
%union {
int ival;
char* sval;
}
// %token defines the tokens that can be used in gram.
%token INTEGER IDENTIFIER // token for integer and identifier
%token SELECT FROM WHERE EQ SEMICOLON // token for query keyword
%token STAR // token for '*'
// %% is start of gram rule
%%
// query defines grammar rule. This rule parses SELECT statement.
query:
SELECT select_list FROM table_name where_clause SEMICOLON
{
// print success message if this rule is parsed successfully.
printf("Parsed query successfully.\n");
}
;
// select_list defines a list of columns to select.
select_list:
IDENTIFIER
| select_list ',' IDENTIFIER // handles selecting multiple columns.
| STAR // Processes when all columns(*) are selected.
;
// table_name defines the table name, consisting of a single IDENTIFIER.
table_name:
IDENTIFIER
;
// where_clause defines a conditional clause.
where_clause:
WHERE condition // if there is a condition.
| /* empty */ //handles the absence of a condition.
;
// condition defines the content of the conditional clause.
condition:
IDENTIFIER EQ INTEGER // A condition that indicates that a particular column is equal to a particular integer.
;
// %% is end of gram rule
%%
// The yyerror function is called when an error occurs during parsing.
void yyerror(const char* s) {
fprintf(stderr, "Error: %s\n", s);
}
scan.l 파일 생성
정의된 토큰들이 발견되면 어떤 동작을 수행할 것인지에 대한 규칙을 설정한다.
%{
#include <string.h>
#include "gram.tab.h"
%}
%% /* start of flex rule. */
SELECT { return SELECT; }
FROM { return FROM; }
WHERE { return WHERE; }
[0-9]+ { yylval.ival = atoi(yytext); return INTEGER; }
[a-zA-Z][a-zA-Z0-9_]* { yylval.sval = strdup(yytext); return IDENTIFIER; }
\* { return STAR; }
= { return EQ; }
; { return SEMICOLON; }
[ \t\n]+ /* ignore whitespace */
. { return yytext[0]; }
%% /* end of flex rule. */
int yywrap() {
return 1;
}
main.c 생성
위 두 코드를 실행할 main
함수를 간략히 작성한다.
#include <stdio.h>
#include "gram.tab.h"
int main() {
printf("Kisoon SQL Parser.\n");
printf("Enter SQL query : ");
yyparse();
return 0;
}
Makefile 생성
위 코드를 컴파일하고 실행 바이너리를 만드는 파일
CC = gcc
YACC = bison
LEX = flex
all: parser
parser: gram scan
$(CC) -o parser gram.tab.c lex.yy.c main.c
gram: gram.y
$(YACC) -d gram.y
scan: scan.l
$(LEX) scan.l
clean:
rm -rf parser gram.tab.c gram.tab.h lex.yy.c parser.dSYM
빌드 및 실행
$ make all
bison -d gram.y
flex scan.l
gcc -o parser gram.tab.c lex.yy.c main.c
$ ./parser
Kisoon SQL Parser.
Enter SQL query : SELECT a, b FROM table WHERE a = 10;
Parsed query successfully.
결론
위 예제를 통해 flex
와 bison
을 활용한 SQL SELECT 문 파싱의 기초를 이해할 수 있다. 이 예제는 PG의 쿼리 파서 구조를 이해하는 데 큰 도움이 될 것이다.
참고문헌
728x90
반응형