2013-10-11 23:07:35 +00:00
|
|
|
/*
|
|
|
|
* 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 <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* THIS FILE HAS BEEN GENERATED USING THE SOURCE IN parser/scan.rl */
|
|
|
|
|
|
|
|
#include <string.h>
|
|
|
|
#include "sql-parser.h"
|
|
|
|
#include "parser/gram.c"
|
|
|
|
|
|
|
|
#define PARSE(token, value) \
|
|
|
|
state->current = ts; \
|
|
|
|
Parse(parser, SQL_PARSER_##token, value, state)
|
|
|
|
|
|
|
|
/**
|
|
|
|
* SECTION: sql-parser
|
|
|
|
* @Short_description: a simple DML query parser
|
|
|
|
* @Title: SqlParser
|
|
|
|
*
|
|
|
|
* This is the parser used by Hedera to transform a string containing one or
|
|
|
|
* more SQL data management queries into an #SqlObject and thus have an idea
|
|
|
|
* of what must happen when a change is made in the affected data.
|
|
|
|
**/
|
|
|
|
static gchar * get_token (const gchar * ts, const gchar * te)
|
|
|
|
{
|
|
|
|
if (!(ts && te)) return NULL;
|
|
|
|
gsize len = (gsize) (te-ts);
|
|
|
|
gchar * str = g_malloc (len+1);
|
|
|
|
g_memmove (str, ts, len);
|
|
|
|
str[len] = '\0';
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
|
|
|
%%{
|
|
|
|
machine sql_scanner;
|
|
|
|
|
|
|
|
#/*-----------------------# SYMBOL Definition #-----------------------*/#
|
|
|
|
# Keywords
|
|
|
|
AS = "AS"i;
|
|
|
|
UNION = "UNION"i;
|
|
|
|
ALL = "ALL"i;
|
|
|
|
ANY = "ANY"i;
|
|
|
|
EXCEPT = "EXCEPT"i;
|
|
|
|
INTERSECT = "INTERSECT"i;
|
|
|
|
SELECT = "SELECT"i;
|
|
|
|
FROM = "FROM"i;
|
|
|
|
WHERE = "WHERE"i;
|
|
|
|
DISTINCT = "DISTINCT"i;
|
|
|
|
JOIN = "JOIN"i;
|
|
|
|
INNER = "INNER"i;
|
|
|
|
LEFT = ("OUTER"i space+|"") "LEFT"i;
|
|
|
|
RIGHT = ("OUTER"i space+|"") "RIGHT"i;
|
|
|
|
ON = "ON"i;
|
|
|
|
USING = "USING"i;
|
|
|
|
NATURAL = "NATURAL"i;
|
|
|
|
GROUP = "GROUP" space+ "BY"i;
|
|
|
|
HAVING = "HAVING"i;
|
|
|
|
ORDER = "ORDER"i space+ "BY"i;
|
|
|
|
ASC = "ASC"i;
|
|
|
|
DESC = "DESC"i;
|
|
|
|
LIMIT = "LIMIT"i;
|
|
|
|
OFFSET = "OFFSET"i;
|
|
|
|
UPDATE = "UPDATE"i;
|
|
|
|
SET = "SET"i;
|
|
|
|
INSERT = "INSERT"i space+ "INTO"i;
|
|
|
|
DEFAULT = "DEFAULT"i;
|
|
|
|
VALUES = "VALUES"i;
|
|
|
|
DELETE = "DELETE"i space+ "FROM"i;
|
|
|
|
RETURNING = "RETURNING"i;
|
|
|
|
# Parenthesis
|
|
|
|
LP = "(";
|
|
|
|
RP = ")";
|
|
|
|
# Colon
|
|
|
|
DOT = ".";
|
|
|
|
# Semicolon
|
|
|
|
SC = ";";
|
|
|
|
# Comma
|
|
|
|
CM = ",";
|
|
|
|
# Identifier quote
|
|
|
|
IQ = ("\"" | "`");
|
|
|
|
# Operators
|
|
|
|
PLUS = "+";
|
|
|
|
MINUS = "-";
|
|
|
|
STAR = "*";
|
|
|
|
DIV = "/";
|
|
|
|
MOD = "%";
|
|
|
|
EQ = "=";
|
|
|
|
NE = ("<>" | "!=");
|
|
|
|
GT = ">";
|
|
|
|
GE = ">=";
|
|
|
|
LT = "<";
|
|
|
|
LE = "<=";
|
|
|
|
# Operator Keywords
|
|
|
|
AND = "AND"i;
|
|
|
|
OR = "OR"i;
|
|
|
|
NOT = ("NOT"i | "!");
|
|
|
|
LIKE = "LIKE"i;
|
|
|
|
IS = "IS"i;
|
|
|
|
XOR = "XOR"i;
|
|
|
|
# Boolean Keywords
|
|
|
|
BOOLEAN = ("TRUE"i | "FALSE"i);
|
|
|
|
|
|
|
|
Op = (AND | OR | NOT | LIKE | IS | XOR);
|
|
|
|
keyword = ( Op | BOOLEAN | AS | UNION | ALL | ANY | EXCEPT | INTERSECT |
|
|
|
|
SELECT | FROM | WHERE | DISTINCT | JOIN | INNER | LEFT| RIGHT | ON |
|
|
|
|
USING | NATURAL | GROUP | HAVING | ORDER | ASC | DESC | LIMIT | OFFSET |
|
|
|
|
UPDATE | SET | INSERT | DEFAULT | VALUES | RETURNING | DELETE);
|
|
|
|
|
|
|
|
# Identifiers
|
|
|
|
Iescape = ("\"\"" | "" );
|
|
|
|
IDENTIFIER = ((alpha | "_" | "$") (alnum | "_" | "$")*) - keyword;
|
|
|
|
QIDENTIFIER = IQ ((any - IQ)* Iescape (any - IQ)*) IQ;
|
|
|
|
PLACEHOLDER = "#" IDENTIFIER;
|
|
|
|
|
|
|
|
# Escaped string characters
|
|
|
|
escape = ("\\'"| "''" | "");
|
|
|
|
|
|
|
|
# Data values
|
|
|
|
STRING = "'" (any - "'")* escape (any - "'")* "'";
|
|
|
|
INTEGER = digit+;
|
|
|
|
FLOAT = digit+ ("." digit+)?;
|
|
|
|
|
|
|
|
# Comments
|
|
|
|
comment = ("-- " (any - "\n")* "\n" |
|
|
|
|
"/*" (any* -- "*/") "*/");
|
|
|
|
|
|
|
|
#/*-----------------------# SCANNER Definition #-----------------------*/#
|
|
|
|
main :=
|
|
|
|
|*
|
|
|
|
#// Keywords
|
|
|
|
AS => { PARSE (AS, 0); };
|
|
|
|
UNION => { PARSE (UNION, 0); };
|
|
|
|
ALL => { PARSE (ALL, 0); };
|
|
|
|
ANY => { PARSE (ANY, 0); };
|
|
|
|
EXCEPT => { PARSE (EXCEPT, 0); };
|
|
|
|
INTERSECT => { PARSE (INTERSECT, 0); };
|
|
|
|
SELECT => { PARSE (SELECT, 0); };
|
|
|
|
DISTINCT => { PARSE (DISTINCT, 0); };
|
|
|
|
FROM => { PARSE (FROM, 0); };
|
|
|
|
WHERE => { PARSE (WHERE, 0); };
|
|
|
|
JOIN => { PARSE (JOIN, 0); };
|
|
|
|
INNER => { PARSE (INNER, 0); };
|
|
|
|
LEFT => { PARSE (LEFT, 0); };
|
|
|
|
RIGHT => { PARSE (RIGHT, 0); };
|
|
|
|
ON => { PARSE (ON, 0); };
|
|
|
|
USING => { PARSE (USING, 0); };
|
|
|
|
#// NATURAL => { PARSE (NATURAL, 0); };
|
|
|
|
GROUP => { PARSE (GROUP, 0); };
|
|
|
|
HAVING => { PARSE (HAVING, 0); };
|
|
|
|
ORDER => { PARSE (ORDER, 0); };
|
|
|
|
ASC => { PARSE (ASC, 0); };
|
|
|
|
DESC => { PARSE (DESC, 0); };
|
|
|
|
LIMIT => { PARSE (LIMIT, 0); };
|
|
|
|
OFFSET => { PARSE (OFFSET, 0); };
|
|
|
|
UPDATE => { PARSE (UPDATE, 0); };
|
|
|
|
SET => { PARSE (SET, 0); };
|
|
|
|
INSERT => { PARSE (INSERT, 0); };
|
|
|
|
DEFAULT => { PARSE (DEFAULT, 0); };
|
|
|
|
VALUES => { PARSE (VALUES, 0); };
|
|
|
|
DELETE => { PARSE (DELETE, 0); };
|
|
|
|
|
|
|
|
#// Punctuation symbols
|
|
|
|
LP => { PARSE (LP, 0); };
|
|
|
|
RP => { PARSE (RP, 0); };
|
|
|
|
DOT => { PARSE (DOT, 0); };
|
|
|
|
SC => { PARSE (SC, 0); };
|
|
|
|
CM => { PARSE (CM, 0); };
|
|
|
|
|
|
|
|
#// Identifiers
|
|
|
|
IDENTIFIER => { PARSE (IDENTIFIER, get_token (ts, te)); };
|
|
|
|
QIDENTIFIER => { PARSE (IDENTIFIER, get_token (ts+1, te-1)); };
|
|
|
|
PLACEHOLDER => { PARSE (PLACEHOLDER, get_token (ts+1, te)); };
|
|
|
|
|
|
|
|
#// Data values
|
|
|
|
STRING => { PARSE (STRING, get_token (ts+1, te-1)); };
|
|
|
|
INTEGER => { PARSE (INTEGER, get_token (ts, te)); };
|
|
|
|
FLOAT => { PARSE (FLOAT, get_token (ts, te)); };
|
|
|
|
BOOLEAN => { PARSE (BOOLEAN, get_token(ts, te)); };
|
|
|
|
|
|
|
|
#// Operators
|
|
|
|
PLUS => { PARSE (PLUS, 0); };
|
|
|
|
MINUS => { PARSE (MINUS, 0); };
|
|
|
|
STAR => { PARSE (STAR, 0); };
|
|
|
|
DIV => { PARSE (DIV, 0); };
|
|
|
|
MOD => { PARSE (MOD, 0); };
|
|
|
|
EQ => { PARSE (EQ, 0); };
|
|
|
|
NE => { PARSE (NE, 0); };
|
|
|
|
GT => { PARSE (GT, 0); };
|
|
|
|
GE => { PARSE (GE, 0); };
|
|
|
|
LT => { PARSE (LT, 0); };
|
|
|
|
LE => { PARSE (LE, 0); };
|
|
|
|
|
|
|
|
#// Operator Keywords
|
|
|
|
AND => { PARSE (AND, 0); };
|
|
|
|
OR => { PARSE (OR, 0); };
|
|
|
|
NOT => { PARSE (NOT, 0); };
|
|
|
|
LIKE => { PARSE (LIKE, 0); };
|
|
|
|
IS => { PARSE (IS, 0); };
|
|
|
|
XOR => { PARSE (XOR, 0); };
|
|
|
|
|
|
|
|
#// Ignored
|
|
|
|
space;
|
|
|
|
comment;
|
|
|
|
*|;
|
|
|
|
}%%
|
|
|
|
%% write data;
|
|
|
|
|
2013-12-13 09:07:52 +00:00
|
|
|
SqlObject * sql_parser_parse (const gchar * sql, GError ** err)
|
2013-10-11 23:07:35 +00:00
|
|
|
{
|
|
|
|
gint cs, act;
|
|
|
|
gchar * p, * pe, * ts, * te;
|
|
|
|
gpointer eof, parser;
|
|
|
|
ParseState * state;
|
|
|
|
SqlObject * object;
|
2013-12-13 09:07:52 +00:00
|
|
|
gchar * query;
|
2013-10-11 23:07:35 +00:00
|
|
|
|
|
|
|
if (!sql)
|
|
|
|
{
|
2013-12-13 09:07:52 +00:00
|
|
|
g_set_error (err, SQL_PARSER_LOG_DOMAIN,
|
|
|
|
SQL_PARSER_ERROR_EMPTY_QUERY, "Empty query!\n");
|
2013-10-11 23:07:35 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2013-12-13 09:07:52 +00:00
|
|
|
query = g_strdup (sql);
|
|
|
|
|
2013-10-11 23:07:35 +00:00
|
|
|
state = g_new (ParseState, 1);
|
|
|
|
state->object = NULL;
|
2013-12-13 09:07:52 +00:00
|
|
|
state->error = err;
|
|
|
|
state->string = state->current = p = query;
|
2013-10-11 23:07:35 +00:00
|
|
|
pe = p + strlen (p) + 1;
|
|
|
|
eof = pe;
|
|
|
|
|
|
|
|
parser = ParseAlloc (g_malloc);
|
|
|
|
|
|
|
|
%% write init;
|
|
|
|
%% write exec;
|
|
|
|
|
|
|
|
Parse (parser, 0, 0, state);
|
|
|
|
ParseFree (parser, g_free);
|
|
|
|
|
2013-12-13 09:07:52 +00:00
|
|
|
if (state->failed)
|
2013-10-11 23:07:35 +00:00
|
|
|
{
|
|
|
|
if (state->object && G_IS_OBJECT (state->object))
|
2013-10-14 12:08:06 +00:00
|
|
|
g_object_unref (state->object);
|
2013-10-11 23:07:35 +00:00
|
|
|
object = NULL;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
object = g_object_ref_sink (state->object);
|
|
|
|
|
|
|
|
g_free (state);
|
2013-12-13 09:07:52 +00:00
|
|
|
g_free (query);
|
2013-10-11 23:07:35 +00:00
|
|
|
|
|
|
|
return object;
|
|
|
|
}
|
2014-02-11 12:16:39 +00:00
|
|
|
|
|
|
|
SqlField * sql_parser_parse_field (const gchar * field)
|
|
|
|
{
|
|
|
|
gchar ** split;
|
|
|
|
SqlExpr * object = NULL;
|
|
|
|
|
|
|
|
if (!field || !g_strcmp0 (field, ""))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
split = g_strsplit (field, ".", 0);
|
|
|
|
|
|
|
|
switch (g_strv_length (split))
|
|
|
|
{
|
|
|
|
case 3:
|
|
|
|
{
|
|
|
|
object = sql_field_new
|
|
|
|
(g_strstrip (g_strdelimit (split[2], "`\"", ' '))
|
|
|
|
,g_strstrip (g_strdelimit (split[1], "`\"", ' '))
|
|
|
|
,g_strstrip (g_strdelimit (split[0], "`\"", ' ')));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 2:
|
|
|
|
{
|
|
|
|
object = sql_field_new
|
|
|
|
(g_strstrip (g_strdelimit (split[1], "`\"", ' '))
|
|
|
|
,g_strstrip (g_strdelimit (split[0], "`\"", ' '))
|
|
|
|
,NULL);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 1:
|
|
|
|
{
|
|
|
|
object = sql_field_new
|
|
|
|
(g_strstrip (g_strdelimit (split[0], "`\"", ' '))
|
|
|
|
,NULL
|
|
|
|
,NULL);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
g_strfreev (split);
|
|
|
|
return (SqlField *) object;
|
|
|
|
}
|