diff --git a/src/bison/bison_parser.y b/src/bison/bison_parser.y index 0453279..af91407 100644 --- a/src/bison/bison_parser.y +++ b/src/bison/bison_parser.y @@ -12,7 +12,6 @@ *********************************/ #include "Statement.h" -#include "List.h" #include "bison_parser.h" #include "flex_lexer.h" @@ -82,7 +81,7 @@ typedef void* yyscan_t; %token SELECT FROM WHERE GROUP BY HAVING %token JOIN ON INNER OUTER LEFT RIGHT CROSS USING NATURAL %token CREATE TABLE DATABASE INDEX -%token AS NOT AND OR NULL +%token AS NOT AND OR NULL LIKE %token NAME STRING COMPARISON %token FLOAT %token EQUALS NOTEQUALS LESS GREATER LESSEQ GREATEREQ @@ -94,12 +93,34 @@ typedef void* yyscan_t; %type select_statement %type table_name %type from_clause table_ref table_ref_atomic -%type expr column_name scalar_exp literal -%type comparison_predicate predicate search_condition where_clause +%type expr scalar_expr unary_expr binary_expr function_expr star_expr +%type column_name literal +%type comp_expr where_clause %type expr_list group_clause select_list %type table_ref_commalist +/****************************** + ** Token Precedence and Associativity + ** Precedence: lowest to highest + ******************************/ +%left OR +%left AND +%right NOT +%right '=' EQUALS NOTEQUALS +%nonassoc '<' '>' LESS GREATER LESSEQ GREATEREQ + +%nonassoc NOTNULL +%nonassoc ISNULL +%nonassoc IS /* sets precedence for IS NULL, etc */ +%left '+' '-' +%left '*' '/' '%' +%left '^' + +/* Unary Operators */ +%left '[' ']' +%left '(' ')' +%left '.' %% /********************************* @@ -142,8 +163,8 @@ select_statement: select_list: - '*' { $$ = new List(new Expr(eExprStar)); } - | expr_list; + expr_list + ; from_clause: @@ -152,7 +173,7 @@ from_clause: where_clause: - WHERE search_condition { $$ = $2; } + WHERE expr { $$ = $2; } | /* empty */ { $$ = NULL; } ; @@ -163,59 +184,70 @@ group_clause: ; -// TODO: multiple predicates -search_condition: - predicate +/****************************** + ** Expressions + ******************************/ +expr_list: + expr { $$ = new List($1); } + | expr_list ',' expr { $1->push_back($3); $$ = $1; } ; -predicate: - comparison_predicate - ; - - -comparison_predicate: - scalar_exp EQUALS scalar_exp { $$ = makePredicate($1, SQL_EQUALS, $3); } - | scalar_exp NOTEQUALS scalar_exp { $$ = makePredicate($1, SQL_NOTEQUALS, $3); } - | scalar_exp LESS scalar_exp { $$ = makePredicate($1, SQL_LESS, $3); } - | scalar_exp GREATER scalar_exp { $$ = makePredicate($1, SQL_GREATER, $3); } - | scalar_exp LESSEQ scalar_exp { $$ = makePredicate($1, SQL_LESSEQ, $3); } - | scalar_exp GREATEREQ scalar_exp { $$ = makePredicate($1, SQL_GREATEREQ, $3); } - ; - -// TODO: Expression can also be scalar_exp expr: + '(' expr ')' { $$ = $2; } + | scalar_expr + | unary_expr + | binary_expr + | function_expr + ; + +scalar_expr: column_name - | NAME '(' expr ')' { $$ = makeFunctionRef($1, $3); } + | star_expr + | literal + ; + +unary_expr: + '-' expr { $$ = NULL; } // TODO + | '+' expr { $$ = NULL; } + | NOT expr { $$ = NULL; } + ; + +binary_expr: + comp_expr + | expr '-' expr { $$ = Expr::makeOpBinary($1, '-', $3); } + | expr '+' expr { $$ = Expr::makeOpBinary($1, '+', $3); } + | expr '/' expr { $$ = Expr::makeOpBinary($1, '/', $3); } + | expr '*' expr { $$ = Expr::makeOpBinary($1, '*', $3); } + | expr AND expr { $$ = Expr::makeOpBinary($1, AND, $3); } + | expr OR expr { $$ = Expr::makeOpBinary($1, OR, $3); } ; -// TODO: Scalar expressions can also be arithmetic -scalar_exp: - column_name - | literal +comp_expr: + expr EQUALS expr { $$ = Expr::makeOpBinary($1, '=', $3); } + | expr NOTEQUALS expr { $$ = Expr::makeOpBinary($1, NOT_EQUALS, $3); } + | expr LESS expr { $$ = Expr::makeOpBinary($1, '<', $3); } + | expr GREATER expr { $$ = Expr::makeOpBinary($1, '>', $3); } + | expr LESSEQ expr { $$ = Expr::makeOpBinary($1, LESS_EQ, $3); } + | expr GREATEREQ expr { $$ = Expr::makeOpBinary($1, GREATER_EQ, $3); } + ; + +function_expr: + NAME '(' expr ')' { $$ = makeFunctionRef($1, $3); } ; column_name: NAME { $$ = makeColumnRef($1); } ; - literal: STRING { $$ = makeStringLiteral($1); } | FLOAT { $$ = makeFloatLiteral($1); } ; - -opt_semicolon: - ';' - | /* empty */ - ; - - -expr_list: - expr { $$ = new List($1); } - | expr_list ',' expr { $1->push_back($3); $$ = $1; } +star_expr: + '*' { $$ = new Expr(eExprStar); } ; @@ -256,6 +288,13 @@ table_name: ; + +opt_semicolon: + ';' + | /* empty */ + ; + + %% /********************************* ** Section 4: Additional C code diff --git a/src/bison/flex_lexer.l b/src/bison/flex_lexer.l index 981434a..ecf086e 100644 --- a/src/bison/flex_lexer.l +++ b/src/bison/flex_lexer.l @@ -11,7 +11,6 @@ %{ #include "Statement.h" -#include "List.h" #include "bison_parser.h" #include diff --git a/src/build_and_run_tests.sh b/src/build_and_run_tests.sh index 936ab3c..ddd80da 100644 --- a/src/build_and_run_tests.sh +++ b/src/build_and_run_tests.sh @@ -1,8 +1,9 @@ make clean -make tests -./bin/tests +# make tests +# ./bin/tests make execution -./bin/sql_execution "SELECT col1, col2 FROM table, (SELECT * FROM a, table, (SELECT * FROM (SELECT * FROM foo))) GROUP BY col1;" \ No newline at end of file +./bin/sql_execution "SELECT a FROM foo WHERE a > 12 OR b > 3 AND c = 3" +./bin/sql_execution "SELECT col1, col2, 'test' FROM table, foo WHERE age > 12 AND zipcode = 12345 GROUP BY col1;" \ No newline at end of file diff --git a/src/lib/Expr.cpp b/src/lib/Expr.cpp index dcca445..b40beb7 100644 --- a/src/lib/Expr.cpp +++ b/src/lib/Expr.cpp @@ -1,6 +1,15 @@ #include "Expr.h" #include +#include + +char* substr(const char* source, int from, int to) { + int len = to-from; + char* copy = new char[len+1]; + strncpy(copy, source+from, len); + copy[len] = '\0'; + return copy; +} Expr* makeColumnRef(char* name) { ALLOC_EXPR(e, eExprColumnRef); @@ -15,15 +24,6 @@ Expr* makeFunctionRef(char* func_name, Expr* expr) { return e; } -Expr* makePredicate(Expr* expr1, uint op, Expr* expr2) { - ALLOC_EXPR(e, eExprPredicate); - // printf("Pred: %u\n", op); - e->pred_type = op; - e->expr = expr1; - e->expr2 = expr2; - return e; -} - Expr* makeFloatLiteral(float value) { ALLOC_EXPR(e, eExprLiteralFloat); e->float_literal = value; @@ -32,6 +32,33 @@ Expr* makeFloatLiteral(float value) { Expr* makeStringLiteral(char* string) { ALLOC_EXPR(e, eExprLiteralString); - e->name = string; + e->name = substr(string, 1, strlen(string)-1); + delete string; + return e; +} + + +Expr* Expr::makeOpUnary(OperatorType op, Expr* expr) { + ALLOC_EXPR(e, eExprOperator); + e->op_type = op; + e->expr = expr; + return e; +} + +Expr* Expr::makeOpBinary(Expr* expr1, OperatorType op, Expr* expr2) { + ALLOC_EXPR(e, eExprOperator); + e->op_type = op; + e->op_char = 0; + e->expr = expr1; + e->expr2 = expr2; + return e; +} + +Expr* Expr::makeOpBinary(Expr* expr1, char op, Expr* expr2) { + ALLOC_EXPR(e, eExprOperator); + e->op_type = TRIVIAL_OP; + e->op_char = op; + e->expr = expr1; + e->expr2 = expr2; return e; } diff --git a/src/lib/Expr.h b/src/lib/Expr.h index 8e36e1e..91eacc8 100644 --- a/src/lib/Expr.h +++ b/src/lib/Expr.h @@ -4,27 +4,54 @@ #include + typedef enum { eExprLiteralFloat, eExprLiteralString, eExprStar, eExprColumnRef, eExprFunctionRef, - eExprPredicate -} EExprType; + eExprOperator +} ExprType; + + +/** + * Trivial types are those that can be descriped by a sigle character e.g: + * + - * / < > = % + * Non-trivial are: + * <> <= >= LIKE ISNULL NOT + */ +typedef enum { + TRIVIAL_OP, + NOT_EQUALS, + LESS_EQ, + GREATER_EQ, + LIKE, + ISNULL, + NOT, + AND, + OR +} OperatorType; + + typedef struct Expr Expr; - struct Expr { - Expr(EExprType type) : type(type) {}; + Expr(ExprType type) : type(type) {}; - EExprType type; + ExprType type; Expr* expr; Expr* expr2; char* name; - uint pred_type; float float_literal; + + OperatorType op_type; + char op_char; + + static Expr* makeOpUnary(OperatorType op, Expr* expr); + static Expr* makeOpBinary(Expr* expr1, char op, Expr* expr2); + static Expr* makeOpBinary(Expr* expr1, OperatorType op, Expr* expr2); }; // Zero initializes an Expr object and assigns it to a space in the heap @@ -40,7 +67,6 @@ struct Expr { Expr* makeColumnRef(char* name); Expr* makeFunctionRef(char* func_name, Expr* expr); -Expr* makePredicate(Expr* expr1, uint op, Expr* expr2); Expr* makeFloatLiteral(float value); Expr* makeStringLiteral(char* string); diff --git a/src/lib/sqlhelper.cpp b/src/lib/sqlhelper.cpp index 90ef43d..a6e8af6 100644 --- a/src/lib/sqlhelper.cpp +++ b/src/lib/sqlhelper.cpp @@ -3,10 +3,14 @@ #include #include -const char* indent(uint num_indent) { return std::string(num_indent, ' ').c_str(); } +void printExpression(Expr* expr, uint num_indent); +void printOperatorExpression(Expr* expr, uint num_indent); + +const char* indent(uint num_indent) { return std::string(num_indent, '\t').c_str(); } void inprint(int val, uint num_indent) { printf("%s%d\n", indent(num_indent), val); } void inprint(float val, uint num_indent) { printf("%s%f\n", indent(num_indent), val); } void inprint(const char* val, uint num_indent) { printf("%s%s\n", indent(num_indent), val); } +void inprint(char val, uint num_indent) { printf("%s%c\n", indent(num_indent), val); } void printTableRefInfo(TableRef* table, uint num_indent) { switch (table->type) { @@ -22,19 +26,39 @@ void printTableRefInfo(TableRef* table, uint num_indent) { } } +void printOperatorExpression(Expr* expr, uint num_indent) { + switch (expr->op_type) { + case TRIVIAL_OP: inprint(expr->op_char, num_indent); break; + case AND: inprint("AND", num_indent); break; + case OR: inprint("OR", num_indent); break; + default: inprint(expr->op_type, num_indent); break; + } + printExpression(expr->expr, num_indent+1); + if (expr->expr2 != NULL) printExpression(expr->expr2, num_indent+1); +} + +void printExpression(Expr* expr, uint num_indent) { + switch (expr->type) { + case eExprStar: inprint("*", num_indent); break; + case eExprColumnRef: inprint(expr->name, num_indent); break; + case eExprLiteralFloat: inprint(expr->float_literal, num_indent); break; + case eExprLiteralString: inprint(expr->name, num_indent); break; + case eExprFunctionRef: /* todo */ break; + case eExprOperator: printOperatorExpression(expr, num_indent); break; + default: + fprintf(stderr, "Unrecognized expression type %d\n", expr->type); + break; + } +} + void printSelectStatementInfo(SelectStatement* stmt, uint num_indent) { inprint("SelectStatement", num_indent); inprint("Fields:", num_indent+1); - for (Expr* expr : stmt->select_list->_vector) { - switch (expr->type) { - case eExprStar: inprint("*", num_indent+2); break; - case eExprColumnRef: inprint(expr->name, num_indent+2); break; - case eExprLiteralFloat: inprint(expr->float_literal, num_indent+2); break; - case eExprLiteralString: inprint(expr->name, num_indent+2); break; - case eExprFunctionRef: /* todo */ break; - case eExprPredicate: /* todo */ break; - } - } + for (Expr* expr : stmt->select_list->_vector) printExpression(expr, num_indent+2); + inprint("Sources:", num_indent+1); printTableRefInfo(stmt->from_table, num_indent+2); + + inprint("Search Conditions:", num_indent+1); + printExpression(stmt->where_clause, num_indent+2); } diff --git a/src/sql_tests.cpp b/src/sql_tests.cpp index f720c4f..f6473bc 100644 --- a/src/sql_tests.cpp +++ b/src/sql_tests.cpp @@ -18,7 +18,7 @@ void SelectTest1() { printf("Test: SelectTest1... "); fflush(stdout); - const char* sql = "SELECT age, name, address from table WHERE age < 12.5;"; + const char* sql = "SELECT age, (name), address from table WHERE age < 12.5;"; Statement* sqlStatement = SQLParser::parseSQLString(sql); ASSERT(sqlStatement != NULL); ASSERT(sqlStatement->type == eSelect); @@ -48,7 +48,7 @@ void SelectTest1() { void SelectTest2() { printf("Test: SelectTest2... "); fflush(stdout); - const char* sql = "SELECT * FROM (SELECT age FROM table, table2);"; + const char* sql = "SELECT * FROM (SELECT age+zipcode FROM table, table2);"; Statement* stmt = SQLParser::parseSQLString(sql); ASSERT(stmt != NULL); ASSERT(stmt->type == eSelect);