%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; } %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; } %parse_accept {} // 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 %type start {SqlObject *} start ::= multi_stmt (A). { state->object = SQL_OBJECT (A); } // SqlMultiStmt %type multi_stmt {SqlStmt *} multi_stmt(multi) ::= stmt_list(stmt). { gint len = g_slist_length (stmt); if (len > 1) { multi = SQL_STMT (sql_multi_stmt_new ()); GSList * n; for (n = stmt; n; n = n->next) sql_multi_stmt_add_stmt (SQL_MULTI_STMT (multi), n->data); } else if (len != 0) multi = SQL_STMT (stmt->data); else multi = NULL; g_slist_free (stmt); } %type stmt_list {GSList *} %destructor stmt_list {g_slist_free ($$);} stmt_list(A) ::= stmt(X) SC stmt_list(B). { A = g_slist_prepend (B, X); } stmt_list(A) ::= stmt(X). { A = g_slist_prepend (A, X); } stmt_list(A) ::= stmt(X) SC. { A = g_slist_prepend (A, X); } // SqlStmt %type stmt {SqlStmt *} // SqlSelect stmt(stmt) ::= multi_select(select). { stmt = SQL_STMT (select); } %type multi_select {SqlSelect *} multi_select(A) ::= multi_select(B) set_op(type) select_stmt(X). { sql_select_set_next (B, X, type); A = B; } multi_select(A) ::= select_stmt(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; } %type select_stmt {SqlSelect *} select_stmt(A) ::= LP select_stmt(X) RP. { A = X; } select_stmt(select) ::= SELECT distinct(distinct) select_expr(expr_l) from(target_l) where(where) group(group_l) having(having) order(order_l) orderway(way) limit(limit). { GSList * n; select = sql_select_new (); sql_select_set_distinct (select, distinct); for (n = expr_l; n; n = n->next) { SelectExpr * se = n->data; sql_select_add_expr (select, se->expr); if (se->alias) { sql_select_set_alias (select, se->expr, se->alias); g_free (se->alias); } g_free (se); } g_slist_free (expr_l); for (n = target_l; n; n = n->next) sql_dml_add_target (SQL_DML (select), n->data); g_slist_free (target_l); if (where) sql_dml_set_where (SQL_DML (select), where); if (group_l) { for (n = group_l; n; n = n->next) sql_select_add_group (select, n->data); g_slist_free (group_l); if (having) sql_select_set_having (select, having); } if (order_l) { for (n = order_l; n; n = n->next) sql_select_add_order (select, n->data, way); g_slist_free (order_l); } if (limit) { sql_select_set_limit (select ,limit[0] ? (guint) atoi (limit[0]): 0 ,limit[1] ? (guint) atoi (limit[1]): 0 ); g_strfreev (limit); } } %type distinct {gboolean} distinct(A) ::= DISTINCT. { A = TRUE; } distinct(A) ::= ALL. { A = FALSE; } distinct(A) ::= . { A = FALSE; } // Expressions with optional alias, only found in the SELECT clause %type select_expr {GSList *} %include { typedef struct { SqlExpr * expr; gchar * alias; } SelectExpr; static inline GSList * sql_parser_alias_expr_add (GSList * list, SqlExpr * expr, gchar * alias) { SelectExpr * sexpr = g_new (SelectExpr, 1); sexpr->expr = expr; sexpr->alias = alias; return g_slist_append (list, sexpr); } } %destructor select_expr {g_slist_free_full ($$, (GDestroyNotify) g_free);} select_expr(A) ::= select_expr(B) CM expr(X). { A = sql_parser_alias_expr_add (B, X, NULL); } select_expr(A) ::= select_expr(B) CM expr(X) alias(Y). { A = sql_parser_alias_expr_add (B, X, Y); } select_expr(A) ::= expr(X). { A = sql_parser_alias_expr_add (A, X, NULL); } select_expr(A) ::= expr(X) alias(Y). { A = sql_parser_alias_expr_add (A, X, Y); } %type from {GSList *} %destructor from {g_slist_free ($$);} from(A) ::= FROM target_list(X). { A = X; } from(A) ::= . { A = NULL; } %type group {GSList *} %destructor group {g_slist_free ($$);} group(A) ::= GROUP expr_list(X). { A = X; } group(A) ::= . { A = NULL; } %type having {SqlExpr *} having(A) ::= HAVING expr(X). { A = X; } having(A) ::= . { A = NULL; } %type order {GSList *} %destructor order {g_slist_free ($$);} order(A) ::= ORDER expr_list (X). { A = X; } order(A) ::= . { A = NULL; } %type orderway {SqlOrderWay} orderway(A) ::= ASC. { A = SQL_ORDER_ASC; } orderway(A) ::= DESC. { A = SQL_ORDER_DESC; } orderway(A) ::= . { A = SQL_ORDER_ASC; } %type limit {gchar **} %include { gchar ** sql_parser_limit (gchar * limit, gchar * offset) { gchar ** r = g_new (gchar *, 3); r[0] = limit; r[1] = offset; r[2] = NULL; return r; } } limit(A) ::= LIMIT INTEGER(X). { A = sql_parser_limit (X, NULL); } limit(A) ::= OFFSET INTEGER(Y). { A = sql_parser_limit (NULL, Y); } limit(A) ::= LIMIT INTEGER(X) OFFSET INTEGER(Y). { A = sql_parser_limit (X, Y); } limit(A) ::= LIMIT INTEGER(X) CM INTEGER(Y). { A = sql_parser_limit (Y, X); } limit(A) ::= . { A = NULL; } // SqlDelete stmt(stmt) ::= delete_stmt(delete). { stmt = SQL_STMT (delete); } %type delete_stmt {SqlDelete *} delete_stmt(delete) ::= DELETE table_list(target) del_using(using) where(where). { GSList * n; delete = sql_delete_new (); for (n = target; n; n = n->next) sql_dml_add_target (SQL_DML (delete), n->data); g_slist_free (target); for (n = using; n; n = n->next) sql_delete_add_table (delete, n->data); g_slist_free (using); if (where) sql_dml_set_where (SQL_DML (delete), where); } %type table_list {GSList *} %destructor table_list {g_slist_free ($$);} table_list(A) ::= table_list(B) CM table(X). { A = g_slist_append (B, X); } table_list(A) ::= table(X). { A = g_slist_append (A, X); } %type del_using {GSList *} del_using(A) ::= USING target_list(X). { A = X; } del_using(A) ::= . { A = NULL; } // SqlUpdate stmt(stmt) ::= update_stmt(update). { stmt = SQL_STMT (update); } %type update_stmt {SqlUpdate *} update_stmt(update) ::= UPDATE table(table) SET update_list(u_list) where(where). { GSList * n; update = sql_update_new (); sql_dml_add_target (SQL_DML (update), table); for (n = u_list; n; n = n->next) { sql_update_add_set (update ,((SqlUpdateSet *) n->data)->field ,((SqlUpdateSet *) n->data)->expr ); g_free (n->data); } g_slist_free (u_list); if (where) sql_dml_set_where (SQL_DML (update), where); } %type update_list {GSList *} %destructor update_list {g_slist_free ($$);} update_list(A) ::= update_list(B) CM update_set(X). { A = g_slist_append (B, X); } update_list(A) ::= update_set(X). { A = g_slist_append (A, X); } %type update_set {SqlUpdateSet *} update_set(A) ::= field(X) EQ expr(Y). { A = g_new (SqlUpdateSet, 1); A->field = SQL_FIELD (X); A->expr = Y; } update_set(A) ::= field(X) EQ DEFAULT. { A = g_new (SqlUpdateSet, 1); A->field = SQL_FIELD (X); A->expr = NULL; } // Common to SELECT, DELETE and UPDATE %type where {SqlExpr *} where(A) ::= WHERE expr(X). { A = SQL_EXPR (X); } where(A) ::= . { A = NULL; } // SqlInsert stmt(stmt) ::= insert_stmt(insert). { stmt = SQL_STMT (insert); } %type insert_stmt {SqlInsert *} insert_stmt(insert) ::= INSERT table(table) ins_fields(field) ins_values(value). { GSList * ll, * n; insert = sql_insert_new (); sql_insert_set_table (insert, SQL_TABLE (table)); for (n = field; n; n = n->next) sql_insert_add_field (insert, n->data); g_slist_free (field); for (ll = value; ll; ll = ll->next) { sql_insert_add_row (insert); for (n = ll->data; n; n = n->next) sql_insert_add_expr (insert, n->data); g_slist_free (ll->data); } g_slist_free (ll); } %type ins_fields {GSList *} %destructor ins_fields {g_slist_free ($$);} ins_fields(A) ::= LP field_list(X) RP. { A = X; } ins_fields(A) ::= . { A = NULL; } %type ins_values {GSList *} ins_values ::= DEFAULT VALUES. ins_values(A) ::= VALUES list_list(X). { A = X; } //ins_values ::= multi_select.// not supported by the parsetree; %type list_list {GSList *} %destructor list_list {g_slist_free ($$);} list_list(A) ::= list_list(B) CM LP def_expr_list(X) RP. { A = g_slist_append (B, X); } list_list(A) ::= LP def_expr_list(X) RP. { A = g_slist_append (A, X); } %type def_expr_list {GSList *} %destructor def_expr_list {g_slist_free ($$);} def_expr_list(A) ::= def_expr_list(B) CM def_expr(X). { A = g_slist_append (B, X); } def_expr_list(A) ::= def_expr(X). { A = g_slist_append (A, X); } %type def_expr {SqlExpr *} def_expr(A) ::= DEFAULT. { A = NULL; } def_expr(A) ::= expr(X). { A = X; } // Common to all the statements // SqlTarget %type target {SqlTarget *} //target(A) ::= LP target(B) RP. { A = B; } target(A) ::= target(B) alias(X). { sql_target_set_alias (B, X); g_free (X); A = B; } %type target_list {GSList *} %destructor target_list {g_slist_free ($$);} target_list(A) ::= target_list(B) CM target(X). { A = g_slist_append (B, X); } target_list(A) ::= target(X). { A = g_slist_append (A, X); } target(A) ::= join(X). { A = X; } target(A) ::= subquery(X). { A = X; } target(A) ::= table(X). { A = X; } // SqlJoin %type join {SqlTarget *} join(join) ::= target(left) join_type(jtype) target(right) join_cond(condition). { join = sql_join_new (left, right, jtype); if (condition) { if (SQL_IS_EXPR (condition)) sql_join_set_condition (SQL_JOIN (join), condition); else // USING list (GSList of SqlField) { SqlOperation * op = NULL; GSList * n, * cond_list = condition; gboolean use_cond = cond_list && cond_list->next ? TRUE : FALSE; SqlOperation * cond = use_cond ? sql_operation_new (SQL_OPERATION_TYPE_AND) : NULL; for (n = condition; n; n = n->next) { gchar * target = left->alias ? left->alias : SQL_TABLE (left)->name; gchar * schema = SQL_IS_TABLE(left) && SQL_TABLE (left)->schema ? SQL_TABLE (left)->schema : NULL; op = sql_operation_new (SQL_OPERATION_TYPE_EQUAL); g_object_set (n->data, "target", target, "schema", schema, NULL); sql_operation_add_expr (op, n->data); target = right->alias ? right->alias : SQL_TABLE (right)->name; schema = SQL_IS_TABLE (right) && SQL_TABLE (right)->schema ? SQL_TABLE (right)->schema : NULL; sql_operation_add_expr (op, sql_field_new (SQL_FIELD (n->data)->name, target, schema)); if (use_cond) sql_operation_add_expr (cond, SQL_EXPR (op)); } g_slist_free (condition); sql_join_set_condition (SQL_JOIN (join), use_cond ? SQL_EXPR (cond) : SQL_EXPR (op)); } } } %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; }// A : SqlExpr join_cond(A) ::= USING LP field_list(X) RP. { A = X; }// A : GSList // SqlSubquery %type subquery {SqlTarget *} subquery(sub) ::= LP multi_select(stmt) RP. { sub = SQL_TARGET (sql_subquery_new (stmt)); } //SqlTable %type table {SqlTarget *} table(A) ::= identifier(X). { A = sql_table_new (X); g_free (X); } table(A) ::= identifier(Y) DOT identifier(X). { A = sql_table_new (X); g_object_set (A, "schema", Y, NULL); g_free (Y); g_free (X); } // SqlExpr %type expr {SqlExpr *} expr(A) ::= LP expr(X) RP. { A = X; } %type expr_list {GSList *} %destructor expr_list {g_slist_free ($$);} expr_list(A) ::= expr_list(B) CM expr(X). { A = g_slist_append (B, X); } expr_list(A) ::= expr(X). { A = g_slist_append (A, X); } expr(A) ::= field(X). { A = X; } expr(A) ::= function(X). { A = X; } expr(A) ::= operation(X). { A = SQL_EXPR (X); } expr(A) ::= value(X). { A = X; } // SqlField %type field {SqlExpr *} field(A) ::= identifier(X). { A = sql_field_new (X, NULL, NULL); g_free (X); } field(A) ::= STAR. { A = sql_field_new ("*", NULL, NULL); } field(A) ::= identifier(Y) DOT identifier(X). { A = sql_field_new (X, Y, NULL); g_free (X); g_free (Y); } field(A) ::= identifier(Y) DOT STAR. { A = sql_field_new ("*", Y, NULL); g_free (Y); } field(A) ::= identifier(Z) DOT identifier(Y) DOT identifier(X). { A = sql_field_new (X, Y, Z); g_free (X); g_free (Y); g_free (Z); } field(A) ::= identifier(Z) DOT identifier(Y) DOT STAR. { A = sql_field_new ("*", Y, Z); g_free (Y); g_free (Z); } %type field_list {GSList *} %destructor field_list {g_slist_free ($$);} field_list(A) ::= field_list(B) CM field(X). { A = g_slist_append (B, X); } field_list(A) ::= field(X). { A = g_slist_append (A, X); } // SqlFunction %type function {SqlExpr *} function(A) ::= identifier(Y) DOT identifier(X) LP RP. { A = SQL_EXPR (sql_function_new (X, Y)); g_free (X); g_free (Y); } function(A) ::= identifier(X) LP RP. { A = SQL_EXPR (sql_function_new (X, NULL)); g_free (X); } function(A) ::= identifier(Z) DOT identifier(X) LP expr_list(Y) RP. { GSList * n = NULL; A = SQL_EXPR (sql_function_new (X, Z)); for ( n = Y; n; n = n->next) sql_function_add_param (SQL_FUNCTION (A), n->data); g_slist_free (Y); g_free (X); } function(A) ::= identifier(X) LP expr_list(Y) RP. { GSList * n = NULL; A = SQL_EXPR (sql_function_new (X, NULL)); for ( n = Y; n; n = n->next) sql_function_add_param (SQL_FUNCTION (A), n->data); g_slist_free (Y); g_free (X); } function(A) ::= expr(X) MOD expr(Y). { A = SQL_EXPR (sql_function_new ("MOD", NULL)); sql_function_add_param (SQL_FUNCTION (A), SQL_EXPR (X)); sql_function_add_param (SQL_FUNCTION (A), SQL_EXPR (Y)); } // SqlOperation %type operation {SqlOperation *} %include { static inline SqlOperation * sql_parser_create_operation (const gpointer X, const gpointer Y, const SqlOperationType type) { SqlOperation * op = sql_operation_new (type); sql_operation_add_expr (op, SQL_EXPR (X)); if (Y) // Y == NULL for unary operations sql_operation_add_expr (op, SQL_EXPR (Y)); return op; } } operation(A) ::= MINUS expr(X). [SIGN] { GValue value = G_VALUE_INIT; SqlExpr * minus = sql_value_new (); g_value_init (&value, G_TYPE_INT); g_value_set_int (&value, -1); sql_value_set_value (SQL_VALUE (minus), &value); A = sql_parser_create_operation (X, minus, SQL_OPERATION_TYPE_MULTIPLICATION); } operation(A) ::= PLUS expr(X). [SIGN] { A = sql_parser_create_operation (X, NULL, SQL_OPERATION_TYPE_SUM); } operation(A) ::= expr(X) PLUS expr(Y). { A = sql_parser_create_operation (X, Y, SQL_OPERATION_TYPE_SUM); } operation(A) ::= expr(X) MINUS expr(Y). { A = sql_parser_create_operation (X, Y, SQL_OPERATION_TYPE_REST); } operation(A) ::= expr(X) STAR expr(Y). { A = sql_parser_create_operation (X, Y, SQL_OPERATION_TYPE_MULTIPLICATION); } operation(A) ::= expr(X) DIV expr(Y). { A = sql_parser_create_operation (X, Y, SQL_OPERATION_TYPE_DIVISION); } operation(A) ::= expr(X) OR expr(Y). { A = sql_parser_create_operation (X, Y, SQL_OPERATION_TYPE_OR); } operation(A) ::= expr(X) XOR expr(Y). { A = sql_parser_create_operation (X, Y, SQL_OPERATION_TYPE_XOR); } operation(A) ::= expr(X) AND expr(Y). { A = sql_parser_create_operation (X, Y, SQL_OPERATION_TYPE_AND); } operation(A) ::= NOT expr(X). { A = sql_parser_create_operation (X, NULL, SQL_OPERATION_TYPE_NOT); } operation(A) ::= expr(X) EQ expr(Y). { A = sql_parser_create_operation (X, Y, SQL_OPERATION_TYPE_EQUAL); } operation(A) ::= expr(X) NE expr(Y). { A = sql_parser_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 = sql_parser_create_operation (X, Y, SQL_OPERATION_TYPE_GREATER); } operation(A) ::= expr(X) GE expr(Y). { A = sql_parser_create_operation (X, Y, SQL_OPERATION_TYPE_GREATER_EQUAL); } operation(A) ::= expr(X) LT expr(Y). { A = sql_parser_create_operation (X, Y, SQL_OPERATION_TYPE_LOWER); } operation(A) ::= expr(X) LE expr(Y). { A = sql_parser_create_operation (X, Y, SQL_OPERATION_TYPE_LOWER_EQUAL); } operation(A) ::= expr(X) LIKE expr(Y). { A = sql_parser_create_operation (X, Y, SQL_OPERATION_TYPE_LIKE); } operation(A) ::= expr(X) IS expr(Y). { A = sql_parser_create_operation (X, Y, SQL_OPERATION_TYPE_IS); } // SqlValue %type value {SqlExpr *} value(A) ::= INTEGER(X). { GValue value = G_VALUE_INIT; SqlExpr * v = sql_value_new (); g_value_set_int (g_value_init (&value, G_TYPE_INT), atoi (X)); sql_value_set_value (SQL_VALUE (v), &value); g_free (X); A = v; } value(A) ::= FLOAT(X). { GValue value = G_VALUE_INIT; SqlExpr * v = sql_value_new (); g_value_set_double (g_value_init (&value, G_TYPE_DOUBLE), g_ascii_strtod (X, NULL)); sql_value_set_value (SQL_VALUE (v), &value); g_free (X); A = v; } value(A) ::= STRING(X). { GValue value = G_VALUE_INIT; SqlExpr * v = sql_value_new (); g_value_set_string (g_value_init (&value, G_TYPE_STRING), X); sql_value_set_value (SQL_VALUE (v), &value); g_free (X); A = v; } value(A) ::= BOOLEAN(X). { GValue value = G_VALUE_INIT; SqlExpr * v = sql_value_new (); g_value_set_boolean (g_value_init (&value, G_TYPE_BOOLEAN) ,(!g_strcmp0 (X, "TRUE") || !g_strcmp0 (X, "true")) ? TRUE : FALSE); sql_value_set_value (SQL_VALUE (v), &value); g_free (X); A = v; } // Alias %type alias {gchar *} %destructor alias {g_free ($$);} alias(A) ::= AS identifier(X). { A = X; } alias(A) ::= identifier(X). { A = X; } identifier(A) ::= IDENTIFIER(X). { A = X; } // Placeholders %type placeholder {gchar *} %destructor expr {g_free ($$);} placeholder(A) ::= PLACEHOLDER(X). { A = X; } stmt ::= placeholder. target ::= placeholder. expr ::= placeholder.