%include { /* * Copyright (C) 2012 - Juan Ferrer Toribio * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include "gram.h" typedef struct { SqlObject * object; gchar * string; gchar * current; gboolean failed; GError ** error; } ParseState; typedef enum { SQL_PARSER_ERROR_EMPTY_QUERY = 1 << 0, SQL_PARSER_ERROR_PARSING = 1 << 1, SQL_PARSER_ERROR_STACK_OVERFLOW = 1 << 2, } SqlParserError; static inline SqlList * list_add (gpointer item, SqlList * list) { sql_list_add (list, item); return list; } static inline SqlList * list_new (gpointer item, GType gtype) { SqlList * list = sql_list_new (gtype); return list_add (item, list); } } %default_type {SqlObject *} %token_type {gchar *} %token_destructor {g_free ($$);} %token_prefix SQL_PARSER_ // Return pointer %extra_argument { ParseState * state } // Error treatment %syntax_error { gchar * pref = ".", * err_str = NULL; if (state->current) { err_str = state->current - 15 >= state->string ? state->current - 10 : state->string; pref = err_str == state->string ? " near: " : " near: [...]"; } g_set_error (state->error, SQL_PARSER_LOG_DOMAIN, SQL_PARSER_ERROR_PARSING, "Parsing error%s%s", pref, err_str); state->failed = TRUE; } %stack_overflow { g_set_error (state->error, SQL_PARSER_LOG_DOMAIN, SQL_PARSER_ERROR_STACK_OVERFLOW, "Parser stack overflow. Parsing terminated.\n"); state->failed = TRUE; } // Operator precedence %left UNION EXCEPT. %left INSERSECT. %left OR. %left XOR. %left AND. %right NOT. %right EQ NE. %left GT GE LT LE. %left LIKE. %left IS. %left PLUS MINUS. %left STAR DIV MOD. %right SIGN. // Start symbol: %start_symbol start start ::= multi_stmt (A). { state->object = SQL_OBJECT (A); } //+++++++++++++++++++++++++++++++++++++++++++++++++++ Multi statement multi_stmt(A) ::= stmt_list(stmt_list). { guint len = sql_list_length (stmt_list); if (len == 1) { A = sql_list_get (stmt_list, 0); g_object_ref_sink (G_OBJECT (A)); g_object_unref (g_object_ref_sink (stmt_list)); } else // FIXME Reverse list! A = g_object_new (SQL_TYPE_MULTI_STMT, "stmts", stmt_list, NULL); } %type stmt_list {SqlList *} stmt_list(A) ::= stmt(X). { A = list_new (X, SQL_TYPE_STMT); } stmt_list(A) ::= stmt(X) SC. { A = list_new (X, SQL_TYPE_STMT); } stmt_list(A) ::= stmt(X) SC stmt_list(B). { A = list_add (X, B); } //+++++++++++++++++++++++++++++++++++++++++++++++++++ Statemet stmt(A) ::= select_stmt(X). { A = X; } stmt(A) ::= delete_stmt(X). { A = X; } stmt(A) ::= update_stmt(X). { A = X; } stmt(A) ::= insert_stmt(X). { A = X; } //+++++++++++++++++++++++++++++++++++++++++++++++++++ Select select_stmt(A) ::= select_stmt(B) set_op(type) simple_select(X). { g_object_set (B, "exprs", type, "next", X, NULL); A = B; } select_stmt(A) ::= simple_select(X). { A = X; } %type set_op {SqlSelectType} set_op(A) ::= UNION ANY. { A = SQL_SELECT_UNION_ANY; } set_op(A) ::= UNION ALL. { A = SQL_SELECT_UNION_ALL; } set_op(A) ::= UNION. { A = SQL_SELECT_UNION_ALL; } set_op(A) ::= INTERSECT. { A = SQL_SELECT_INTERSECT; } set_op(A) ::= EXCEPT. { A = SQL_SELECT_EXCEPT; } simple_select(A) ::= LP simple_select(X) RP. { A = X; } simple_select(A) ::= SELECT distinct(distinct_c) select_field_list(field_list) from(target) where(where_c) group(group_c) having(having_c) order(order_c) limit(limit_c). { A = g_object_new (SQL_TYPE_SELECT ,"distinct" ,distinct_c ,"fields" ,field_list ,"targets" ,target ,"where" ,where_c ,"group" ,group_c ,"order" ,order_c ,NULL ); if (having_c) g_object_set (A, "having", having_c, NULL); if (limit_c) g_object_set (A ,"limit-offset" ,limit_c->offset ,"limit-count" ,limit_c->count ,NULL ); } %type distinct {gboolean} distinct(A) ::= DISTINCT. { A = TRUE; } distinct(A) ::= ALL. { A = FALSE; } distinct(A) ::= . { A = FALSE; } select_field(A) ::= expr(X). { A = g_object_new (SQL_TYPE_SELECT_FIELD, "expr", X, NULL); } select_field(A) ::= expr(X) alias(Y). { A = g_object_new (SQL_TYPE_SELECT_FIELD, "expr", X, "alias", Y, NULL); } %type select_field_list {SqlList *} select_field_list(A) ::= select_field(X). { A = list_new (X, SQL_TYPE_SELECT_FIELD); } select_field_list(A) ::= select_field_list(B) CM select_field(X). { A = list_add (X, B); } %type from {SqlList *} from(A) ::= FROM target_list(X). { A = X; } from(A) ::= . { A = NULL; } %type group {SqlList *} group(A) ::= GROUP expr_list(X). { A = X; } group(A) ::= . { A = NULL; } having(A) ::= HAVING expr(X). { A = X; } having(A) ::= . { A = NULL; } %type order {SqlList *} order(A) ::= ORDER order_list (X). { A = X; } order(A) ::= . { A = NULL; } %type order_list {SqlList *} order_list(A) ::= order_expr(X). { A = list_new (X, SQL_TYPE_SELECT_ORDER); } order_list(A) ::= order_list(B) CM order_expr(X). { A = list_add (X, B); } order_expr(A) ::= expr(X) order_way(Y). { A = g_object_new (SQL_TYPE_SELECT_ORDER, "expr", X, "way", Y, NULL); } %type order_way {SqlSelectOrderWay} order_way(A) ::= ASC. { A = SQL_SELECT_ORDER_ASC; } order_way(A) ::= DESC. { A = SQL_SELECT_ORDER_DESC; } order_way(A) ::= . { A = SQL_SELECT_ORDER_ASC; } %type limit {SelectLimit *} %destructor limit {g_free ($$);} %include { typedef struct { guint offset; guint count; } SelectLimit; static inline SelectLimit * create_limit (gchar * offset, gchar * count) { SelectLimit * limit = g_new (SelectLimit, 1); limit->offset = offset ? atoi (offset) : 0; limit->count = count ? atoi (count) : 0; return limit; } } limit(A) ::= LIMIT INTEGER(X). { A = create_limit (NULL, X); } limit(A) ::= OFFSET INTEGER(Y). { A = create_limit (Y, NULL); } limit(A) ::= LIMIT INTEGER(X) OFFSET INTEGER(Y). { A = create_limit (Y, X); } limit(A) ::= LIMIT INTEGER(X) CM INTEGER(Y). { A = create_limit (Y, X); } limit(A) ::= . { A = NULL; } //+++++++++++++++++++++++++++++++++++++++++++++++++++ Delete delete_stmt(A) ::= DELETE table_list(table_list) del_using(del_using) where(where_c). { A = g_object_new (SQL_TYPE_DELETE ,"targets" ,table_list ,"tables" ,del_using ,"where" ,where_c ,NULL ); } %type table_list {SqlList *} table_list(A) ::= table(X). { A = list_new (X, SQL_TYPE_TABLE); } table_list(A) ::= table_list(B) CM table(X). { A = list_add (X, B); } %type del_using {SqlList *} del_using(A) ::= USING target_list(X). { A = X; } del_using(A) ::= . { A = NULL; } //+++++++++++++++++++++++++++++++++++++++++++++++++++ Update update_stmt(A) ::= UPDATE table_list(table) SET update_list(update_list) where(where_c). { A = g_object_new (SQL_TYPE_UPDATE ,"targets" ,table ,"sets" ,update_list ,"where" ,where_c ,NULL ); } %type update_list {SqlList *} update_list(A) ::= update_set(X). { A = list_new (X, SQL_TYPE_UPDATE_SET); } update_list(A) ::= update_list(B) CM update_set(X). { A = list_add (X, B); } update_set(A) ::= field(X) EQ def_expr(Y). { A = g_object_new (SQL_TYPE_UPDATE_SET ,"field" ,X ,"expr" ,Y ,NULL ); } //+++++++++++++++++++++++++++++++++++++++++++++++++++ Insert insert_stmt(A) ::= INSERT table(tab) insert_fields(insert_fields) insert_values(insert_values). { A = g_object_new (SQL_TYPE_INSERT ,"table" ,tab ,"fields" ,insert_fields ,NULL ); if (SQL_IS_SELECT (insert_values)) g_object_set (A, "select", insert_values, NULL); else g_object_set (A, "values", insert_values, NULL); } %type insert_fields {SqlList *} insert_fields(A) ::= LP field_list(X) RP. { A = X; } insert_fields(A) ::= . { A = NULL; } %type insert_values {gpointer} insert_values(A) ::= DEFAULT VALUES. { A = NULL; } insert_values(A) ::= VALUES set_list(X). { A = X; } //insert_values(A) ::= select_stmt(X). { A = X; } %type set_list {SqlList *} set_list(A) ::= set(X). { A = list_new (X, SQL_TYPE_SET); } set_list(A) ::= set_list(B) CM set(X). { A = list_add (X, B); } set(A) ::= LP def_expr_list(X) RP. { A = g_object_new (SQL_TYPE_SET, "exprs", X, NULL); } %type def_expr_list {SqlList *} def_expr_list(A) ::= def_expr(X). { A = list_new (X, SQL_TYPE_EXPR); } def_expr_list(A) ::= def_expr_list(B) CM def_expr(X). { A = list_add (X, B); } //+++++++++++++++++++++++++++++++++++++++++++++++++++ Common to statements where(A) ::= WHERE expr(X). { A = X; } where(A) ::= . { A = NULL; } def_expr(A) ::= expr(X). { A = X; } def_expr(A) ::= DEFAULT. { A = NULL; } //+++++++++++++++++++++++++++++++++++++++++++++++++++ Target target(A) ::= unaliased_target(X). { A = X; } target(A) ::= unaliased_target(X) alias(Y). { g_object_set (X, "alias", Y, NULL); A = X; } //target(A) ::= LP target(X) RP. { A = X; } %type target_list {SqlList *} target_list(A) ::= target(X). { A = list_new (X, SQL_TYPE_TARGET); } target_list(A) ::= target_list(B) CM target(X). { A = list_add (X, B); } unaliased_target(A) ::= join(X). { A = X; } unaliased_target(A) ::= subquery(X). { A = X; } unaliased_target(A) ::= table(X). { A = X; } //+++++++++++++++++++++++++++++++++++++++++++++++++++ Join join(A) ::= target(target_left) join_type(join_type) target(target_right) join_cond(join_cond). { A = g_object_new (SQL_TYPE_JOIN ,"target-left" ,target_left ,"target-right" ,target_right ,"join-type" ,join_type ,NULL ); if (SQL_IS_LIST (join_cond)) // USING { GList * n; SqlList * exprs = sql_list_new (SQL_TYPE_EXPR); SqlOperation * op_and = g_object_new (SQL_TYPE_OPERATION ,"operator" ,SQL_OPERATION_TYPE_AND ,"operands" ,exprs ,NULL ); for (n = sql_list_get_items (join_cond); n; n = n->next) { SqlList * equal = sql_list_new (SQL_TYPE_EXPR); SqlOperation * op = g_object_new (SQL_TYPE_OPERATION ,"operator" ,SQL_OPERATION_TYPE_EQUAL ,"operands" ,equal ,NULL ); gchar * target = SQL_TARGET (target_left)->alias ? SQL_TARGET (target_left)->alias : SQL_TABLE (target_left)->name; gchar * schema = SQL_IS_TABLE(target_left) && SQL_TABLE (target_left)->schema ? SQL_TABLE (target_left)->schema : NULL; g_object_set (n->data, "target", target, "schema", schema, NULL); sql_list_add (equal, n->data); target = SQL_TARGET (target_right)->alias ? SQL_TARGET (target_right)->alias : SQL_TABLE (target_right)->name; schema = SQL_IS_TABLE (target_right) && SQL_TABLE (target_right)->schema ? SQL_TABLE (target_right)->schema : NULL; sql_list_add (equal, sql_field_new_with_target (SQL_FIELD (n->data)->name, target, schema)); sql_list_add (exprs, op); } g_object_set (A, "condition", op_and, NULL); g_object_unref (g_object_ref_sink (join_cond)); } else g_object_set (A, "condition", join_cond, NULL); } %type join_type {SqlJoinType} join_type(A) ::= INNER JOIN. { A = SQL_JOIN_TYPE_INNER; } join_type(A) ::= JOIN. { A = SQL_JOIN_TYPE_INNER; } join_type(A) ::= LEFT JOIN. { A = SQL_JOIN_TYPE_LEFT; } join_type(A) ::= RIGHT JOIN. { A = SQL_JOIN_TYPE_RIGHT; } %type join_cond {gpointer} join_cond(A) ::= ON expr(X). { A = X; } join_cond(A) ::= USING LP field_list(X) RP. { A = X; } //+++++++++++++++++++++++++++++++++++++++++++++++++++ Subquery subquery(A) ::= LP select_stmt(stmts) RP. { A = g_object_new (SQL_TYPE_SUBQUERY, "stms", stmts, NULL); } //+++++++++++++++++++++++++++++++++++++++++++++++++++ Table table(A) ::= identifier(X). { A = g_object_new (SQL_TYPE_TABLE, "name", X, NULL); } table(A) ::= identifier(Y) DOT identifier(X). { A = g_object_new (SQL_TYPE_TABLE, "name", X, "schema", Y, NULL); } //+++++++++++++++++++++++++++++++++++++++++++++++++++ Expr expr(A) ::= LP expr(X) RP. { A = X; } %type expr_list {SqlList *} expr_list(A) ::= expr(X). { A = list_new (X, SQL_TYPE_EXPR); } expr_list(A) ::= expr_list(B) CM expr(X). { A = list_add (X, B); } expr(A) ::= field(X). { A = X; } expr(A) ::= function(X). { A = X; } expr(A) ::= operation(X). { A = X; } expr(A) ::= value(X). { A = X; } //+++++++++++++++++++++++++++++++++++++++++++++++++++ Field field(A) ::= unqualified_field(X). { A = X; } field(A) ::= identifier(Y) DOT unqualified_field(X). { g_object_set (X, "target", Y, NULL); A = X; } field(A) ::= identifier(Z) DOT identifier(Y) DOT unqualified_field(X). { g_object_set (X, "target", Y, "schema", Z, NULL); A = X; } unqualified_field(A) ::= identifier(X). { A = g_object_new (SQL_TYPE_FIELD, "name", X, NULL); } unqualified_field(A) ::= STAR. { A = g_object_new (SQL_TYPE_FIELD, "name", "*", NULL); } %type field_list {SqlList *} field_list(A) ::= field(X). { A = list_new (X, SQL_TYPE_FIELD); } field_list(A) ::= field_list(B) CM field(X). { A = list_add (X, B); } //+++++++++++++++++++++++++++++++++++++++++++++++++++ Function function(A) ::= unqualified_function(X). { A = X; } function(A) ::= identifier(Z) DOT unqualified_function(X). { g_object_set (X, "schema", Z, NULL); A = X; } unqualified_function(A) ::= identifier(X) LP function_expr_list(Y) RP. { A = g_object_new (SQL_TYPE_FUNCTION, "name", X, "params", Y, NULL); } %type function_expr_list {SqlList *} function_expr_list(A) ::= . { A = NULL; } function_expr_list(A) ::= expr_list(X). { A = X; } //+++++++++++++++++++++++++++++++++++++++++++++++++++ Operation %include { static inline SqlObject * create_operation (const gpointer X, const gpointer Y, const SqlOperationType type) { SqlList * exprs = sql_list_new (SQL_TYPE_EXPR); sql_list_add (exprs, X); if (Y) // Binary operation sql_list_add (exprs, Y); return g_object_new (SQL_TYPE_OPERATION, "operator", type, "operands", exprs, NULL); } } operation(A) ::= MINUS expr(X). [SIGN] { GValue value = {0}; SqlObject * minus = sql_value_new (); g_value_init (&value, G_TYPE_INT); g_value_set_int (&value, -1); gvn_param_set_value (GVN_PARAM (minus), &value); A = create_operation (X, minus, SQL_OPERATION_TYPE_MULTIPLICATION); } operation(A) ::= NOT expr(X). { A = create_operation (X, NULL, SQL_OPERATION_TYPE_NOT); } operation(A) ::= PLUS expr(X). [SIGN] { A = create_operation (X, NULL, SQL_OPERATION_TYPE_SUM); } operation(A) ::= expr(X) PLUS expr(Y). { A = create_operation (X, Y, SQL_OPERATION_TYPE_SUM); } operation(A) ::= expr(X) MINUS expr(Y). { A = create_operation (X, Y, SQL_OPERATION_TYPE_SUBTRACTION); } operation(A) ::= expr(X) STAR expr(Y). { A = create_operation (X, Y, SQL_OPERATION_TYPE_MULTIPLICATION); } operation(A) ::= expr(X) DIV expr(Y). { A = create_operation (X, Y, SQL_OPERATION_TYPE_DIVISION); } operation(A) ::= expr(X) OR expr(Y). { A = create_operation (X, Y, SQL_OPERATION_TYPE_OR); } operation(A) ::= expr(X) XOR expr(Y). { A = create_operation (X, Y, SQL_OPERATION_TYPE_XOR); } operation(A) ::= expr(X) AND expr(Y). { A = create_operation (X, Y, SQL_OPERATION_TYPE_AND); } operation(A) ::= expr(X) EQ expr(Y). { A = create_operation (X, Y, SQL_OPERATION_TYPE_EQUAL); } operation(A) ::= expr(X) NE expr(Y). { A = create_operation (X, Y, SQL_OPERATION_TYPE_NOT_EQUAL); } //XXX the following should be %nonassoc operators but are %left to avoid // conflicts. The DB will warn about a bad use if used as %left. operation(A) ::= expr(X) GT expr(Y). { A = create_operation (X, Y, SQL_OPERATION_TYPE_GREATER); } operation(A) ::= expr(X) GE expr(Y). { A = create_operation (X, Y, SQL_OPERATION_TYPE_GREATER_EQUAL); } operation(A) ::= expr(X) LT expr(Y). { A = create_operation (X, Y, SQL_OPERATION_TYPE_LOWER); } operation(A) ::= expr(X) LE expr(Y). { A = create_operation (X, Y, SQL_OPERATION_TYPE_LOWER_EQUAL); } operation(A) ::= expr(X) LIKE expr(Y). { A = create_operation (X, Y, SQL_OPERATION_TYPE_LIKE); } operation(A) ::= expr(X) IS expr(Y). { A = create_operation (X, Y, SQL_OPERATION_TYPE_IS); } operation(A) ::= expr(X) MOD expr(Y). { A = create_operation (X, Y, SQL_OPERATION_TYPE_MOD); } //+++++++++++++++++++++++++++++++++++++++++++++++++++ Value value(A) ::= INTEGER(X). { GValue value = {0}; g_value_set_int (g_value_init (&value, G_TYPE_INT), atoi (X)); A = g_object_new (SQL_TYPE_VALUE, "value", &value, NULL); g_value_unset (&value); } value(A) ::= FLOAT(X). { GValue value = {0}; g_value_set_double (g_value_init (&value, G_TYPE_DOUBLE), g_ascii_strtod (X, NULL)); A = g_object_new (SQL_TYPE_VALUE, "value", &value, NULL); g_value_unset (&value); } value(A) ::= STRING(X). { GValue value = {0}; g_value_set_string (g_value_init (&value, G_TYPE_STRING), X); A = g_object_new (SQL_TYPE_VALUE, "value", &value, NULL); g_value_unset (&value); } value(A) ::= BOOLEAN(X). { GValue value = {0}; g_value_set_boolean (g_value_init (&value, G_TYPE_BOOLEAN) ,(!g_strcmp0 (X, "TRUE") || !g_strcmp0 (X, "true")) ? TRUE : FALSE); A = g_object_new (SQL_TYPE_VALUE, "value", &value, NULL); g_value_unset (&value); } //+++++++++++++++++++++++++++++++++++++++++++++++++++ Alias %type alias {gchar *} alias(A) ::= AS identifier(X). { A = X; } alias(A) ::= identifier(X). { A = X; } %type identifier {gchar *} identifier(A) ::= IDENTIFIER(X). { A = X; } //+++++++++++++++++++++++++++++++++++++++++++++++++++ Placeholder placeholder(A) ::= PLACEHOLDER(X). { A = g_object_new (SQL_TYPE_HOLDER, "id", X, NULL); } stmt ::= placeholder. target ::= placeholder. expr ::= placeholder.