import ply.yacc as yacc
import ply.lex as lex
from etcdb import PARSER_LOCK
from etcdb.sqlparser import etcdb_lexer
from etcdb.sqlparser.sql_tree import SQLTree
# noinspection PyUnresolvedReferences
from etcdb_lexer import tokens
precedence = (
('left', 'AND', 'OR'),
('right', 'UNOT'),
)
[docs]def p_statement(p):
"""statement : select_statement
| show_tables_statement
| create_table_statement
| create_database_statement
| show_databases_statement
| use_database_statement
| commit_statement
| set_statement
| insert_statement
| delete_statement
| drop_database_statement
| drop_table_statement
| desc_table_statement
| update_table_statement
| wait_statement"""
p[1].success = True
p[0] = p[1]
[docs]def p_wait_statement(p):
"""wait_statement : WAIT select_item_list FROM identifier opt_WHERE opt_AFTER"""
tree = SQLTree()
tree.query_type = "WAIT"
tree.table = p[4]
tree.expressions = p[2]
tree.where = p[5]
tree.options = p[6]
p[0] = tree
[docs]def p_opt_after_empty(p):
"""opt_AFTER : """
[docs]def p_opt_after(p):
"""opt_AFTER : AFTER NUMBER"""
after = {
'after': int(p[2])
}
p[0] = after
[docs]def p_update_table_statement(p):
"""update_table_statement : UPDATE identifier SET col_expr_list opt_WHERE opt_USE_LOCK"""
tree = SQLTree()
tree.query_type = "UPDATE"
tree.table = p[2]
tree.expressions = p[4]
tree.where = p[5]
tree.lock = p[6]
p[0] = tree
[docs]def p_col_expr_list_one(p):
"""col_expr_list : col_expr"""
p[0] = [p[1]]
[docs]def p_col_expr_list(p):
"""col_expr_list : col_expr_list ',' col_expr"""
p[1].append(p[3])
p[0] = p[1]
[docs]def p_col_expr(p):
"""col_expr : identifier '=' expr"""
p[0] = (p[1], p[3])
[docs]def p_desc_table_statement(p):
"""desc_table_statement : DESC identifier"""
tree = SQLTree()
tree.table = p[2]
tree.query_type = "DESC_TABLE"
p[0] = tree
[docs]def p_drop_database_statement(p):
"""drop_database_statement : DROP DATABASE identifier"""
tree = SQLTree()
tree.db = p[3]
tree.query_type = "DROP_DATABASE"
p[0] = tree
[docs]def p_drop_table_statement(p):
"""drop_table_statement : DROP TABLE identifier opt_IF_EXISTS"""
tree = SQLTree()
tree.table = p[3]
tree.query_type = "DROP_TABLE"
tree.options['if_exists'] = p[4]
p[0] = tree
[docs]def p_opt_if_exists_empty(p):
"""opt_IF_EXISTS : """
p[0] = False
[docs]def p_opt_if_exists(p):
"""opt_IF_EXISTS : IF EXISTS"""
p[0] = True
[docs]def p_insert_statement(p):
"""insert_statement : INSERT INTO identifier opt_fieldlist VALUES '(' values_list ')' opt_USE_LOCK"""
tree = SQLTree()
tree.query_type = "INSERT"
tree.table = p[3]
n_fields = len(p[4])
n_values = len(p[7])
if n_fields != n_values:
msg = 'There are {n_fields} fields, but {n_values} values'.format(
n_fields=n_fields,
n_values=n_values
)
raise SQLParserError(msg)
for i in xrange(n_fields):
tree.fields[p[4][i]] = p[7][i]
tree.lock = p[9]
p[0] = tree
[docs]def p_opt_fieldlist_empty(p):
"""opt_fieldlist : """
p[0] = {}
[docs]def p_opt_fieldlist(p):
"""opt_fieldlist : '(' fieldlist ')'"""
p[0] = p[2]
[docs]def p_fieldlist_one(p):
"""fieldlist : identifier"""
p[0] = [p[1]]
[docs]def p_fieldlist_many(p):
"""fieldlist : fieldlist ',' identifier """
if p[1] is None:
p[0] = [p[3]]
else:
p[1].append(p[3])
p[0] = p[1]
[docs]def p_values_list_one(p):
"""values_list : value"""
p[0] = [p[1]]
[docs]def p_values_list_many(p):
"""values_list : values_list ',' value"""
if p[1] is None:
p[0] = p[3]
else:
p[1].append(p[3])
p[0] = p[1]
[docs]def p_set_statement(p):
"""set_statement : set_autocommit_statement
| set_names_statement"""
p[0] = p[1]
[docs]def p_set_names_statement(p):
"""set_names_statement : SET NAMES STRING"""
tree = SQLTree()
tree.query_type = "SET_NAMES"
p[0] = tree
[docs]def p_set_statement_autocommit(p):
"""set_autocommit_statement : SET AUTOCOMMIT '=' NUMBER"""
tree = SQLTree()
tree.query_type = "SET_AUTOCOMMIT"
tree.options['autocommit'] = int(p[4])
p[0] = tree
[docs]def p_commit_statement(p):
"""commit_statement : COMMIT"""
tree = SQLTree()
tree.query_type = "COMMIT"
p[0] = tree
[docs]def p_create_table_statement(p):
"""create_table_statement : CREATE TABLE identifier '(' create_definition_list ')'"""
tree = SQLTree()
tree.query_type = "CREATE_TABLE"
tree.table = p[3]
tree.fields = p[5]
p[0] = tree
[docs]def p_create_database_statement(p):
"""create_database_statement : CREATE DATABASE identifier"""
tree = SQLTree()
tree.query_type = "CREATE_DATABASE"
tree.db = p[3]
p[0] = tree
[docs]def p_show_databases_statement(p):
"""show_databases_statement : SHOW DATABASES"""
tree = SQLTree()
tree.query_type = "SHOW_DATABASES"
p[0] = tree
[docs]def p_use_database_statement(p):
"""use_database_statement : USE identifier"""
tree = SQLTree()
tree.query_type = "USE_DATABASE"
tree.db = p[2]
p[0] = tree
[docs]def p_identifier(p):
"""identifier : STRING"""
p[0] = p[1]
[docs]def p_identifier_escaped(p):
"""identifier : '`' STRING '`'"""
p[0] = p[2]
[docs]def p_create_definition_list_one(p):
"""create_definition_list : create_definition"""
p[0] = {
p[1][0]: p[1][1]
}
[docs]def p_create_definition_list_many(p):
"""create_definition_list : create_definition_list ',' create_definition"""
create_definition_list = p[1]
create_definition_list[p[3][0]] = p[3][1]
p[0] = create_definition_list
[docs]def p_create_definition(p):
"""create_definition : identifier column_definition"""
p[0] = p[1], p[2]
[docs]def p_column_definition(p):
"""column_definition : data_type opt_column_def_options_list"""
p[0] = {
'type': p[1]
}
p[0]['options'] = p[2]
[docs]def p_data_type(p):
"""data_type : INTEGER opt_UNSIGNED
| VARCHAR '(' NUMBER ')'
| DATETIME
| DATETIME '(' NUMBER ')'
| INT opt_UNSIGNED
| LONGTEXT
| SMALLINT opt_UNSIGNED
| TINYINT
| BOOL"""
p[0] = p[1]
[docs]def p_opt_UNSIGNED(p):
"""opt_UNSIGNED :
| UNSIGNED"""
[docs]def p_opt_column_def_options_list_empty(p):
"""opt_column_def_options_list : """
p[0] = {
'nullable': True
}
[docs]def p_opt_column_def_options_list(p):
"""opt_column_def_options_list : opt_column_def_options opt_column_def_options_list"""
if p[2] is None:
p[0] = p[1]
else:
p[2].update(p[1])
p[0] = p[2]
[docs]def p_DEFAULT_CLAUSE(p):
"""opt_column_def_options : DEFAULT value"""
p[0] = {
'default': p[2]
}
[docs]def p_NULL(p):
"""opt_column_def_options : NULL"""
p[0] = {
'nullable': True
}
[docs]def p_NOT_NULL(p):
"""opt_column_def_options : NOT NULL"""
p[0] = {
'nullable': False
}
[docs]def p_AUTO_INCREMENT(p):
"""opt_column_def_options : AUTO_INCREMENT"""
p[0] = {
'auto_increment': True
}
[docs]def p_PRIMARY_KEY(p):
"""opt_column_def_options : PRIMARY KEY"""
p[0] = {
'primary': True
}
[docs]def p_UNIQUE(p):
"""opt_column_def_options : UNIQUE"""
p[0] = {
'unique': True
}
[docs]def p_value(p):
"""value : q_STRING
| NUMBER
| STRING_VALUE """
p[0] = p[1]
[docs]def p_q_STRING(p):
"""q_STRING : "'" STRING "'" """
p[0] = p[2]
[docs]def p_q_STRING_EMPTY(p):
"""q_STRING : """
p[0] = ""
[docs]def p_select_statement(p):
"""select_statement : SELECT select_item_list opt_FROM opt_WHERE opt_ORDER_BY opt_LIMIT"""
tree = SQLTree()
tree.query_type = "SELECT"
tree.db = p[3][0]
tree.table = p[3][1]
tree.expressions = p[2]
tree.where = p[4]
try:
tree.limit = int(p[6])
except TypeError:
tree.limit = None
tree.order = p[5]
p[0] = tree
[docs]def p_opt_ORDER_BY_empty(p):
"""opt_ORDER_BY : """
p[0] = None
[docs]def p_opt_ORDER_BY_simple(p):
"""opt_ORDER_BY : ORDER BY identifier opt_ORDER_DIRECTION"""
order = {
'by': p[3],
'direction': p[4]
}
p[0] = order
[docs]def p_opt_ORDER_BY_extended(p):
"""opt_ORDER_BY : ORDER BY identifier '.' identifier opt_ORDER_DIRECTION"""
order = {
'by': p[5],
'direction': p[6]
}
p[0] = order
[docs]def p_opt_ORDER_DIRECTION_empty(p):
"""opt_ORDER_DIRECTION : """
p[0] = 'ASC'
[docs]def p_opt_ORDER_DIRECTION(p):
"""opt_ORDER_DIRECTION : ASC
| DESC """
p[0] = p[1]
[docs]def p_opt_LIMIT_empty(p):
"""opt_LIMIT : """
p[0] = None
[docs]def p_opt_LIMIT(p):
"""opt_LIMIT : LIMIT NUMBER"""
p[0] = p[2]
[docs]def p_show_tables_statement(p):
"""show_tables_statement : SHOW opt_FULL TABLES"""
tree = SQLTree()
tree.query_type = "SHOW_TABLES"
tree.options['full'] = p[2]
p[0] = tree
[docs]def p_opt_from_empty(p):
"""opt_FROM : """
p[0] = None, None
[docs]def p_opt_from(p):
"""opt_FROM : FROM table_reference"""
p[0] = p[2]
[docs]def p_table_reference(p):
"""table_reference : identifier"""
p[0] = None, p[1]
[docs]def p_table_reference_w_database(p):
"""table_reference : identifier '.' identifier"""
p[0] = p[1], p[3]
[docs]def p_opt_FULL_empty(p):
"""opt_FULL : """
p[0] = False
[docs]def p_opt_FULL(p):
"""opt_FULL : FULL"""
p[0] = True
[docs]def p_select_item_list_select_item(p):
"""select_item_list : select_item """
p[0] = [p[1]]
[docs]def p_select_item_list(p):
"""select_item_list : select_item_list ',' select_item """
select_item_list = p[1]
select_item_list .append(p[3])
p[0] = select_item_list
[docs]def p_select_item_list_star(p):
"""select_item_list : '*'"""
p[0] = [
(
('*', None),
None
)
]
[docs]def p_select_item(p):
"""select_item : select_item2 select_alias"""
p[0] = (p[1], p[2])
[docs]def p_select_item2(p):
"""select_item2 : table_wild
| expr """
p[0] = p[1]
[docs]def p_select_alias_empty(p):
"""select_alias : """
p[0] = None
[docs]def p_select_alias(p):
"""select_alias : AS identifier"""
p[0] = p[2]
[docs]def p_table_wild(p):
"""table_wild : identifier '.' '*' """
p[0] = ("*", p[1])
[docs]def p_opt_USE_LOCK_empty(p):
"""opt_USE_LOCK : """
p[0] = None
[docs]def p_opt_USE_LOCK(p):
"""opt_USE_LOCK : USE LOCK STRING_VALUE """
p[0] = p[3]
[docs]def p_opt_WHERE_empty(p):
"""opt_WHERE : """
p[0] = None
[docs]def p_opt_WHERE(p):
"""opt_WHERE : WHERE expr"""
p[0] = p[2]
[docs]def p_expr_OR(p):
"""expr : expr OR expr"""
p[0] = ('OR', p[1], p[3])
[docs]def p_expr_AND(p):
"""expr : expr AND expr"""
p[0] = ('AND', p[1], p[3])
[docs]def p_expr_NOT(p):
"""expr : NOT expr %prec UNOT"""
p[0] = ('NOT', p[2])
[docs]def p_expr_bool_primary(p):
"""expr : boolean_primary"""
p[0] = ('bool_primary', p[1])
[docs]def p_boolean_primary_is_null(p):
"""boolean_primary : boolean_primary IS NULL"""
p[0] = ('IS NULL', p[1])
[docs]def p_boolean_primary_is_not_null(p):
"""boolean_primary : boolean_primary IS NOT NULL"""
p[0] = ('IS NOT NULL', p[1])
[docs]def p_boolean_primary_comparison(p):
"""boolean_primary : boolean_primary comparison_operator predicate"""
p[0] = (p[2], p[1], p[3])
[docs]def p_boolean_primary_predicate(p):
"""boolean_primary : predicate"""
p[0] = ('predicate', p[1])
[docs]def p_comparison_operator(p):
"""comparison_operator : '='
| GREATER_OR_EQ
| '>'
| LESS_OR_EQ
| '<'
| N_EQ"""
p[0] = p[1]
[docs]def p_predicate(p):
"""predicate : bit_expr """
p[0] = ('bit_expr', p[1])
[docs]def p_predicate_in(p):
"""predicate : bit_expr IN '(' list_expr ')'"""
p[0] = (
'IN',
p[1],
p[4]
)
[docs]def p_list_expr_one(p):
"""list_expr : expr"""
p[0] = [p[1]]
[docs]def p_list_expr(p):
"""list_expr : list_expr ',' expr"""
p[1].append(p[3])
p[0] = p[1]
[docs]def p_bit_expr(p):
"""bit_expr : simple_expr"""
p[0] = ('simple_expr', p[1])
[docs]def p_simple_expr_identifier(p):
"""simple_expr : identifier"""
p[0] = ('IDENTIFIER', p[1])
[docs]def p_simple_expr_identifier_full(p):
"""simple_expr : identifier '.' identifier"""
p[0] = ('IDENTIFIER', p[1] + '.' + p[3])
[docs]def p_simple_expr_parent(p):
"""simple_expr : '(' expr ')'"""
p[0] = ('expr', p[2])
[docs]def p_simple_expr_variable(p):
"""simple_expr : variable"""
p[0] = ('variable', p[1])
[docs]def p_variable(p):
"""variable : '@' '@' STRING"""
p[0] = p[3]
[docs]def p_simple_expr_literal(p):
"""simple_expr : literal"""
p[0] = ('literal', p[1])
[docs]def p_literal(p):
"""literal : q_STRING
| NUMBER
| STRING_VALUE"""
p[0] = p[1]
[docs]def p_simple_expr_function_call(p):
"""simple_expr : function_call"""
p[0] = ('function_call', p[1])
[docs]def p_function_call_version(p):
"""function_call : VERSION '(' ')'"""
p[0] = 'VERSION'
[docs]def p_function_call_count_star(p):
"""function_call : COUNT '(' '*' ')'"""
p[0] = 'COUNT'
[docs]def p_delete_statement(p):
"""delete_statement : DELETE FROM identifier opt_WHERE"""
tree = SQLTree()
tree.query_type = 'DELETE'
tree.table = p[3]
tree.where = p[4]
p[0] = tree
[docs]def p_error(t):
if t:
msg = "Syntax error at lexeme '{value}' (type: '{type}'). " \
"Line: {lineno}, position: {lexpos}".format(value=t.value,
type=t.type,
lineno=t.lineno,
lexpos=t.lexpos)
raise SQLParserError(msg)
else:
raise SQLParserError("Syntax error")
[docs]class SQLParser(object):
def __init__(self):
self._parser = yacc.yacc(debug=False)
[docs] def parse(self, *args, **kwargs):
try:
PARSER_LOCK.acquire()
# noinspection PyUnusedLocal
lexer = lex.lex(module=etcdb_lexer)
tree = self._parser.parse(*args, **kwargs)
tree.query = args[0]
return tree
except SQLParserError:
self._parser.restart()
raise
finally:
PARSER_LOCK.release()
[docs]class SQLParserError(Exception):
"""All SQL parsing errors"""