parent
634ed382f5
commit
824db2a43b
@ -1,45 +1,45 @@
|
|||||||
import mariadb
|
import mariadb
|
||||||
|
|
||||||
class dbconn:
|
class dbconn:
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
self.db = None
|
self.db = None
|
||||||
self.cur = None
|
self.cur = None
|
||||||
def clear(self):
|
def clear(self):
|
||||||
drop_all = f'''
|
drop_all = f'''
|
||||||
SET FOREIGN_KEY_CHECKS = 0;
|
SET FOREIGN_KEY_CHECKS = 0;
|
||||||
|
|
||||||
SET @tables = NULL;
|
SET @tables = NULL;
|
||||||
|
|
||||||
SELECT GROUP_CONCAT('`', table_schema, '`.`', table_name, '`') INTO @tables
|
SELECT GROUP_CONCAT('`', table_schema, '`.`', table_name, '`') INTO @tables
|
||||||
FROM information_schema.tables
|
FROM information_schema.tables
|
||||||
WHERE table_schema = '{self.db.database}';
|
WHERE table_schema = '{self.db.database}';
|
||||||
|
|
||||||
SET @tables = CONCAT('DROP TABLE ', @tables);
|
SET @tables = CONCAT('DROP TABLE ', @tables);
|
||||||
PREPARE stmt FROM @tables;
|
PREPARE stmt FROM @tables;
|
||||||
EXECUTE stmt;
|
EXECUTE stmt;
|
||||||
DEALLOCATE PREPARE stmt;
|
DEALLOCATE PREPARE stmt;
|
||||||
SET FOREIGN_KEY_CHECKS = 1;
|
SET FOREIGN_KEY_CHECKS = 1;
|
||||||
'''
|
'''
|
||||||
if self.db:
|
if self.db:
|
||||||
if not self.cur:
|
if not self.cur:
|
||||||
self.cur = self.db.cursor()
|
self.cur = self.db.cursor()
|
||||||
self.cur.execute(drop_all)
|
self.cur.execute(drop_all)
|
||||||
|
|
||||||
def connect(self, ip, password = '0508', user = 'root', db = 'db', port = 3306):
|
def connect(self, ip, password = '0508', user = 'root', db = 'db', port = 3306):
|
||||||
try:
|
try:
|
||||||
self.db = mariadb.connect(
|
self.db = mariadb.connect(
|
||||||
user = user,
|
user = user,
|
||||||
password = password,
|
password = password,
|
||||||
host = ip,
|
host = ip,
|
||||||
port = port,
|
port = port,
|
||||||
database = db
|
database = db
|
||||||
)
|
)
|
||||||
self.cur = self.db.cursor()
|
self.cur = self.db.cursor()
|
||||||
|
|
||||||
except mariadb.Error as e:
|
except mariadb.Error as e:
|
||||||
print(e)
|
print(e)
|
||||||
self.db = None
|
self.db = None
|
||||||
self.cur = None
|
self.cur = None
|
||||||
|
|
||||||
def exec(self, sql, params = None):
|
def exec(self, sql, params = None):
|
||||||
self.cur.execute(sql)
|
self.cur.execute(sql)
|
@ -1,11 +1,11 @@
|
|||||||
from reconstruct.ast import Context, ast_node
|
from reconstruct.ast import Context, ast_node
|
||||||
|
|
||||||
def initialize():
|
def initialize():
|
||||||
return Context()
|
return Context()
|
||||||
|
|
||||||
def generate(ast, cxt):
|
def generate(ast, cxt):
|
||||||
for k in ast.keys():
|
for k in ast.keys():
|
||||||
if k in ast_node.types.keys():
|
if k in ast_node.types.keys():
|
||||||
ast_node.types[k](None, ast, cxt)
|
ast_node.types[k](None, ast, cxt)
|
||||||
|
|
||||||
__all__ = ["initialize", "generate"]
|
__all__ = ["initialize", "generate"]
|
||||||
|
@ -1,303 +1,303 @@
|
|||||||
from engine.utils import enlist, base62uuid, base62alp
|
from engine.utils import enlist, base62uuid, base62alp
|
||||||
from reconstruct.storage import Context, TableInfo, ColRef
|
from reconstruct.storage import Context, TableInfo, ColRef
|
||||||
|
|
||||||
class ast_node:
|
class ast_node:
|
||||||
header = []
|
header = []
|
||||||
types = dict()
|
types = dict()
|
||||||
first_order = False
|
first_order = False
|
||||||
|
|
||||||
def __init__(self, parent:"ast_node", node, context:Context = None):
|
def __init__(self, parent:"ast_node", node, context:Context = None):
|
||||||
self.context = parent.context if context is None else context
|
self.context = parent.context if context is None else context
|
||||||
self.parent = parent
|
self.parent = parent
|
||||||
self.sql = ''
|
self.sql = ''
|
||||||
self.datasource = None
|
self.datasource = None
|
||||||
self.init(node)
|
self.init(node)
|
||||||
self.produce(node)
|
self.produce(node)
|
||||||
self.spawn(node)
|
self.spawn(node)
|
||||||
self.consume(node)
|
self.consume(node)
|
||||||
|
|
||||||
def emit(self, code):
|
def emit(self, code):
|
||||||
self.context.emit(code)
|
self.context.emit(code)
|
||||||
def add(self, code):
|
def add(self, code):
|
||||||
self.sql += code + ' '
|
self.sql += code + ' '
|
||||||
|
|
||||||
name = 'null'
|
name = 'null'
|
||||||
|
|
||||||
def init(self, _):
|
def init(self, _):
|
||||||
self.add(self.__class__.name.upper())
|
self.add(self.__class__.name.upper())
|
||||||
def produce(self, _):
|
def produce(self, _):
|
||||||
pass
|
pass
|
||||||
def spawn(self, _):
|
def spawn(self, _):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def consume(self, _):
|
def consume(self, _):
|
||||||
if self.parent is None:
|
if self.parent is None:
|
||||||
self.emit(self.sql+';\n')
|
self.emit(self.sql+';\n')
|
||||||
|
|
||||||
|
|
||||||
from reconstruct.expr import expr
|
from reconstruct.expr import expr
|
||||||
|
|
||||||
|
|
||||||
class projection(ast_node):
|
class projection(ast_node):
|
||||||
name = 'projection'
|
name = 'projection'
|
||||||
first_order = 'select'
|
first_order = 'select'
|
||||||
|
|
||||||
def init(self, _):
|
def init(self, _):
|
||||||
pass
|
pass
|
||||||
def produce(self, node):
|
def produce(self, node):
|
||||||
p = node['select']
|
p = node['select']
|
||||||
self.projections = p if type(p) is list else [p]
|
self.projections = p if type(p) is list else [p]
|
||||||
self.add('SELECT')
|
self.add('SELECT')
|
||||||
|
|
||||||
def spawn(self, node):
|
def spawn(self, node):
|
||||||
self.datasource = None # datasource is Join instead of TableInfo
|
self.datasource = None # datasource is Join instead of TableInfo
|
||||||
if 'from' in node:
|
if 'from' in node:
|
||||||
from_clause = node['from']
|
from_clause = node['from']
|
||||||
self.datasource = join(self, from_clause)
|
self.datasource = join(self, from_clause)
|
||||||
if 'assumptions' in from_clause:
|
if 'assumptions' in from_clause:
|
||||||
self.assumptions = enlist(from_clause['assumptions'])
|
self.assumptions = enlist(from_clause['assumptions'])
|
||||||
|
|
||||||
if self.datasource is not None:
|
if self.datasource is not None:
|
||||||
self.datasource_changed = True
|
self.datasource_changed = True
|
||||||
self.prev_datasource = self.context.datasource
|
self.prev_datasource = self.context.datasource
|
||||||
self.context.datasource = self.datasource
|
self.context.datasource = self.datasource
|
||||||
|
|
||||||
if 'where' in node:
|
if 'where' in node:
|
||||||
self.where = filter(self, node['where'])
|
self.where = filter(self, node['where'])
|
||||||
else:
|
else:
|
||||||
self.where = None
|
self.where = None
|
||||||
|
|
||||||
if 'groupby' in node:
|
if 'groupby' in node:
|
||||||
self.group_node = groupby(self, node['groupby'])
|
self.group_node = groupby(self, node['groupby'])
|
||||||
else:
|
else:
|
||||||
self.group_node = None
|
self.group_node = None
|
||||||
|
|
||||||
def consume(self, node):
|
def consume(self, node):
|
||||||
# deal with projections
|
# deal with projections
|
||||||
self.out_table = TableInfo('out_'+base62uuid(4), [], self.context)
|
self.out_table = TableInfo('out_'+base62uuid(4), [], self.context)
|
||||||
cols = []
|
cols = []
|
||||||
col_exprs = []
|
col_exprs = []
|
||||||
for i, proj in enumerate(self.projections):
|
for i, proj in enumerate(self.projections):
|
||||||
compound = False
|
compound = False
|
||||||
self.datasource.rec = set()
|
self.datasource.rec = set()
|
||||||
name = ''
|
name = ''
|
||||||
if type(proj) is dict:
|
if type(proj) is dict:
|
||||||
|
|
||||||
if 'value' in proj:
|
if 'value' in proj:
|
||||||
e = proj['value']
|
e = proj['value']
|
||||||
name = expr(self, e).sql
|
name = expr(self, e).sql
|
||||||
disp_name = ''.join([a if a in base62alp else '' for a in name])
|
disp_name = ''.join([a if a in base62alp else '' for a in name])
|
||||||
compound = True # compound column
|
compound = True # compound column
|
||||||
if 'name' in proj: # renaming column by AS keyword
|
if 'name' in proj: # renaming column by AS keyword
|
||||||
name += ' ' + proj['name']
|
name += ' ' + proj['name']
|
||||||
col_exprs.append(name)
|
col_exprs.append(name)
|
||||||
|
|
||||||
elif type(proj) is str:
|
elif type(proj) is str:
|
||||||
col = self.datasource.get_col(proj)
|
col = self.datasource.get_col(proj)
|
||||||
name = col.name
|
name = col.name
|
||||||
self.datasource.rec = None
|
self.datasource.rec = None
|
||||||
# TODO: Type deduction in Python
|
# TODO: Type deduction in Python
|
||||||
cols.append(ColRef('unknown', self.out_table, None, disp_name, i, compound=compound))
|
cols.append(ColRef('unknown', self.out_table, None, disp_name, i, compound=compound))
|
||||||
self.add(', '.join(col_exprs))
|
self.add(', '.join(col_exprs))
|
||||||
|
|
||||||
def finialize(astnode:ast_node):
|
def finialize(astnode:ast_node):
|
||||||
if(astnode is not None):
|
if(astnode is not None):
|
||||||
self.add(astnode.sql)
|
self.add(astnode.sql)
|
||||||
self.add('FROM')
|
self.add('FROM')
|
||||||
finialize(self.datasource)
|
finialize(self.datasource)
|
||||||
finialize(self.where)
|
finialize(self.where)
|
||||||
finialize(self.group_node)
|
finialize(self.group_node)
|
||||||
if 'orderby' in node:
|
if 'orderby' in node:
|
||||||
self.add(orderby(self, node['orderby']).sql)
|
self.add(orderby(self, node['orderby']).sql)
|
||||||
if 'outfile' in node:
|
if 'outfile' in node:
|
||||||
self.add(outfile(self, node['outfile']).sql)
|
self.add(outfile(self, node['outfile']).sql)
|
||||||
if self.parent is None:
|
if self.parent is None:
|
||||||
self.emit(self.sql+';\n')
|
self.emit(self.sql+';\n')
|
||||||
else:
|
else:
|
||||||
# TODO: subquery, name create tmp-table from subquery w/ alias as name
|
# TODO: subquery, name create tmp-table from subquery w/ alias as name
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class orderby(ast_node):
|
class orderby(ast_node):
|
||||||
name = 'order by'
|
name = 'order by'
|
||||||
def produce(self, node):
|
def produce(self, node):
|
||||||
if node is None:
|
if node is None:
|
||||||
self.sql = ''
|
self.sql = ''
|
||||||
return
|
return
|
||||||
elif type(node) is not list:
|
elif type(node) is not list:
|
||||||
node = [node]
|
node = [node]
|
||||||
|
|
||||||
o_list = []
|
o_list = []
|
||||||
|
|
||||||
for o in node:
|
for o in node:
|
||||||
o_str = expr(self, o['value']).sql
|
o_str = expr(self, o['value']).sql
|
||||||
if 'sort' in o and f'{o["sort"]}'.lower() == 'desc':
|
if 'sort' in o and f'{o["sort"]}'.lower() == 'desc':
|
||||||
o_str += ' ' + 'DESC'
|
o_str += ' ' + 'DESC'
|
||||||
o_list.append(o_str)
|
o_list.append(o_str)
|
||||||
self.add(', '.join(o_list))
|
self.add(', '.join(o_list))
|
||||||
|
|
||||||
|
|
||||||
class groupby(orderby):
|
class groupby(orderby):
|
||||||
name = 'group by'
|
name = 'group by'
|
||||||
|
|
||||||
|
|
||||||
class join(ast_node):
|
class join(ast_node):
|
||||||
name = 'join'
|
name = 'join'
|
||||||
def init(self, _):
|
def init(self, _):
|
||||||
self.joins:list = []
|
self.joins:list = []
|
||||||
self.tables = []
|
self.tables = []
|
||||||
self.tables_dir = dict()
|
self.tables_dir = dict()
|
||||||
# self.tmp_name = 'join_' + base62uuid(4)
|
# self.tmp_name = 'join_' + base62uuid(4)
|
||||||
# self.datasource = TableInfo(self.tmp_name, [], self.context)
|
# self.datasource = TableInfo(self.tmp_name, [], self.context)
|
||||||
def append(self, tbls, __alias = ''):
|
def append(self, tbls, __alias = ''):
|
||||||
alias = lambda t : '(' + t + ') ' + __alias if len(__alias) else t
|
alias = lambda t : '(' + t + ') ' + __alias if len(__alias) else t
|
||||||
if type(tbls) is join:
|
if type(tbls) is join:
|
||||||
self.joins.append(alias(tbls.__str__()))
|
self.joins.append(alias(tbls.__str__()))
|
||||||
self.tables += tbls.tables
|
self.tables += tbls.tables
|
||||||
self.tables_dir = {**self.tables_dir, **tbls.tables_dir}
|
self.tables_dir = {**self.tables_dir, **tbls.tables_dir}
|
||||||
|
|
||||||
elif type(tbls) is TableInfo:
|
elif type(tbls) is TableInfo:
|
||||||
self.joins.append(alias(tbls.table_name))
|
self.joins.append(alias(tbls.table_name))
|
||||||
self.tables.append(tbls)
|
self.tables.append(tbls)
|
||||||
self.tables_dir[tbls.table_name] = tbls
|
self.tables_dir[tbls.table_name] = tbls
|
||||||
for a in tbls.alias:
|
for a in tbls.alias:
|
||||||
self.tables_dir[a] = tbls
|
self.tables_dir[a] = tbls
|
||||||
|
|
||||||
elif type(tbls) is projection:
|
elif type(tbls) is projection:
|
||||||
self.joins.append(alias(tbls.finalize()))
|
self.joins.append(alias(tbls.finalize()))
|
||||||
|
|
||||||
def produce(self, node):
|
def produce(self, node):
|
||||||
if type(node) is list:
|
if type(node) is list:
|
||||||
for d in node:
|
for d in node:
|
||||||
self.append(join(self, d).__str__())
|
self.append(join(self, d).__str__())
|
||||||
|
|
||||||
elif type(node) is dict:
|
elif type(node) is dict:
|
||||||
alias = ''
|
alias = ''
|
||||||
if 'value' in node:
|
if 'value' in node:
|
||||||
table_name = node['value']
|
table_name = node['value']
|
||||||
tbl = None
|
tbl = None
|
||||||
if 'name' in node:
|
if 'name' in node:
|
||||||
alias = node['name']
|
alias = node['name']
|
||||||
if type(table_name) is dict:
|
if type(table_name) is dict:
|
||||||
if 'select' in table_name:
|
if 'select' in table_name:
|
||||||
# TODO: subquery, create and register TableInfo in projection
|
# TODO: subquery, create and register TableInfo in projection
|
||||||
tbl = projection(self, table_name).finalize()
|
tbl = projection(self, table_name).finalize()
|
||||||
else:
|
else:
|
||||||
tbl = self.context.tables_byname[table_name]
|
tbl = self.context.tables_byname[table_name]
|
||||||
if 'name' in node:
|
if 'name' in node:
|
||||||
tbl.add_alias(node['name'])
|
tbl.add_alias(node['name'])
|
||||||
|
|
||||||
self.append(tbl, alias)
|
self.append(tbl, alias)
|
||||||
else:
|
else:
|
||||||
keys = node.keys()
|
keys = node.keys()
|
||||||
if keys[0].lower().endswith('join'):
|
if keys[0].lower().endswith('join'):
|
||||||
j = join(self, node[keys[0]])
|
j = join(self, node[keys[0]])
|
||||||
tablename = f' {keys[0]} {j}'
|
tablename = f' {keys[0]} {j}'
|
||||||
if keys[1].lower() == 'on':
|
if keys[1].lower() == 'on':
|
||||||
tablename += f' on {expr(self, node[keys[1]])}'
|
tablename += f' on {expr(self, node[keys[1]])}'
|
||||||
self.joins.append(tablename)
|
self.joins.append(tablename)
|
||||||
self.tables += j.tables
|
self.tables += j.tables
|
||||||
self.tables_dir = {**self.tables_dir, **j.tables_dir}
|
self.tables_dir = {**self.tables_dir, **j.tables_dir}
|
||||||
|
|
||||||
elif type(node) is str:
|
elif type(node) is str:
|
||||||
self.append(self.context.tables_byname[node])
|
self.append(self.context.tables_byname[node])
|
||||||
|
|
||||||
def get_cols(self, colExpr: str) -> ColRef:
|
def get_cols(self, colExpr: str) -> ColRef:
|
||||||
for t in self.tables:
|
for t in self.tables:
|
||||||
if colExpr in t.columns_byname:
|
if colExpr in t.columns_byname:
|
||||||
return t.columns_byname[colExpr]
|
return t.columns_byname[colExpr]
|
||||||
|
|
||||||
def parse_col_names(self, colExpr:str) -> ColRef:
|
def parse_col_names(self, colExpr:str) -> ColRef:
|
||||||
parsedColExpr = colExpr.split('.')
|
parsedColExpr = colExpr.split('.')
|
||||||
if len(parsedColExpr) <= 1:
|
if len(parsedColExpr) <= 1:
|
||||||
return self.get_cols(colExpr)
|
return self.get_cols(colExpr)
|
||||||
else:
|
else:
|
||||||
datasource = self.tables_dir[parsedColExpr[0]]
|
datasource = self.tables_dir[parsedColExpr[0]]
|
||||||
if datasource is None:
|
if datasource is None:
|
||||||
raise ValueError(f'Table name/alias not defined{parsedColExpr[0]}')
|
raise ValueError(f'Table name/alias not defined{parsedColExpr[0]}')
|
||||||
else:
|
else:
|
||||||
return datasource.parse_col_names(parsedColExpr[1])
|
return datasource.parse_col_names(parsedColExpr[1])
|
||||||
|
|
||||||
def consume(self, _):
|
def consume(self, _):
|
||||||
self.sql = ', '.join(self.joins)
|
self.sql = ', '.join(self.joins)
|
||||||
return super().consume(_)
|
return super().consume(_)
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return ', '.join(self.joins)
|
return ', '.join(self.joins)
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return self.__str__()
|
return self.__str__()
|
||||||
|
|
||||||
|
|
||||||
class filter(ast_node):
|
class filter(ast_node):
|
||||||
name = 'where'
|
name = 'where'
|
||||||
def produce(self, node):
|
def produce(self, node):
|
||||||
self.add(expr(self, node).sql)
|
self.add(expr(self, node).sql)
|
||||||
|
|
||||||
|
|
||||||
class create_table(ast_node):
|
class create_table(ast_node):
|
||||||
name = 'create_table'
|
name = 'create_table'
|
||||||
first_order = name
|
first_order = name
|
||||||
def init(self, node):
|
def init(self, node):
|
||||||
self.sql = 'CREATE TABLE '
|
self.sql = 'CREATE TABLE '
|
||||||
|
|
||||||
def produce(self, node):
|
def produce(self, node):
|
||||||
ct = node[self.name]
|
ct = node[self.name]
|
||||||
tbl = self.context.add_table(ct['name'], ct['columns'])
|
tbl = self.context.add_table(ct['name'], ct['columns'])
|
||||||
self.sql = f'CREATE TABLE {tbl.table_name}('
|
self.sql = f'CREATE TABLE {tbl.table_name}('
|
||||||
columns = []
|
columns = []
|
||||||
for c in tbl.columns:
|
for c in tbl.columns:
|
||||||
columns.append(f'{c.name} {c.type.upper()}')
|
columns.append(f'{c.name} {c.type.upper()}')
|
||||||
self.sql += ', '.join(columns)
|
self.sql += ', '.join(columns)
|
||||||
self.sql += ')'
|
self.sql += ')'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class insert(ast_node):
|
class insert(ast_node):
|
||||||
name = 'insert'
|
name = 'insert'
|
||||||
first_order = name
|
first_order = name
|
||||||
def produce(self, node):
|
def produce(self, node):
|
||||||
values = node['query']['select']
|
values = node['query']['select']
|
||||||
tbl = node['insert']
|
tbl = node['insert']
|
||||||
self.sql = f'INSERT INTO {tbl} VALUES('
|
self.sql = f'INSERT INTO {tbl} VALUES('
|
||||||
# if len(values) != table.n_cols:
|
# if len(values) != table.n_cols:
|
||||||
# raise ValueError("Column Mismatch")
|
# raise ValueError("Column Mismatch")
|
||||||
list_values = []
|
list_values = []
|
||||||
for i, s in enumerate(values):
|
for i, s in enumerate(values):
|
||||||
if 'value' in s:
|
if 'value' in s:
|
||||||
list_values.append(f"{s['value']}")
|
list_values.append(f"{s['value']}")
|
||||||
else:
|
else:
|
||||||
# subquery, dispatch to select astnode
|
# subquery, dispatch to select astnode
|
||||||
pass
|
pass
|
||||||
self.sql += ', '.join(list_values) + ')'
|
self.sql += ', '.join(list_values) + ')'
|
||||||
|
|
||||||
|
|
||||||
class load(ast_node):
|
class load(ast_node):
|
||||||
name="load"
|
name="load"
|
||||||
first_order = name
|
first_order = name
|
||||||
def produce(self, node):
|
def produce(self, node):
|
||||||
node = node['load']
|
node = node['load']
|
||||||
s1 = 'LOAD DATA INFILE '
|
s1 = 'LOAD DATA INFILE '
|
||||||
s2 = 'INTO TABLE '
|
s2 = 'INTO TABLE '
|
||||||
s3 = 'FIELDS TERMINATED BY '
|
s3 = 'FIELDS TERMINATED BY '
|
||||||
self.sql = f'{s1} \"{node["file"]["literal"]}\" {s2} {node["table"]}'
|
self.sql = f'{s1} \"{node["file"]["literal"]}\" {s2} {node["table"]}'
|
||||||
if 'term' in node:
|
if 'term' in node:
|
||||||
self.sql += f' {s3} \"{node["term"]["literal"]}\"'
|
self.sql += f' {s3} \"{node["term"]["literal"]}\"'
|
||||||
|
|
||||||
|
|
||||||
class outfile(ast_node):
|
class outfile(ast_node):
|
||||||
name="_outfile"
|
name="_outfile"
|
||||||
def produce(self, node):
|
def produce(self, node):
|
||||||
filename = node['loc']['literal'] if 'loc' in node else node['literal']
|
filename = node['loc']['literal'] if 'loc' in node else node['literal']
|
||||||
self.sql = f'INTO OUTFILE "{filename}"'
|
self.sql = f'INTO OUTFILE "{filename}"'
|
||||||
if 'term' in node:
|
if 'term' in node:
|
||||||
self.sql += f' FIELDS TERMINATED BY \"{node["term"]["literal"]}\"'
|
self.sql += f' FIELDS TERMINATED BY \"{node["term"]["literal"]}\"'
|
||||||
|
|
||||||
|
|
||||||
def include(objs):
|
def include(objs):
|
||||||
import inspect
|
import inspect
|
||||||
for _, cls in inspect.getmembers(objs):
|
for _, cls in inspect.getmembers(objs):
|
||||||
if inspect.isclass(cls) and issubclass(cls, ast_node) and type(cls.first_order) is str:
|
if inspect.isclass(cls) and issubclass(cls, ast_node) and type(cls.first_order) is str:
|
||||||
ast_node.types[cls.first_order] = cls
|
ast_node.types[cls.first_order] = cls
|
||||||
|
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
include(sys.modules[__name__])
|
include(sys.modules[__name__])
|
@ -1,128 +1,128 @@
|
|||||||
from reconstruct.ast import ast_node
|
from reconstruct.ast import ast_node
|
||||||
from reconstruct.storage import ColRef, TableInfo
|
from reconstruct.storage import ColRef, TableInfo
|
||||||
|
|
||||||
|
|
||||||
class expr(ast_node):
|
class expr(ast_node):
|
||||||
name='expr'
|
name='expr'
|
||||||
builtin_func_maps = {
|
builtin_func_maps = {
|
||||||
'max': 'MAX',
|
'max': 'MAX',
|
||||||
'min': 'MIN',
|
'min': 'MIN',
|
||||||
'avg': 'AVG',
|
'avg': 'AVG',
|
||||||
'sum': 'SUM',
|
'sum': 'SUM',
|
||||||
'count' : 'COUNT',
|
'count' : 'COUNT',
|
||||||
'mins': ['mins', 'minw'],
|
'mins': ['mins', 'minw'],
|
||||||
'maxs': ['maxs', 'maxw'],
|
'maxs': ['maxs', 'maxw'],
|
||||||
'avgs': ['avgs', 'avgw'],
|
'avgs': ['avgs', 'avgw'],
|
||||||
'sums': ['sums', 'sumw'],
|
'sums': ['sums', 'sumw'],
|
||||||
}
|
}
|
||||||
|
|
||||||
binary_ops = {
|
binary_ops = {
|
||||||
'sub':'-',
|
'sub':'-',
|
||||||
'add':'+',
|
'add':'+',
|
||||||
'mul':'*',
|
'mul':'*',
|
||||||
'div':'/',
|
'div':'/',
|
||||||
'mod':'%',
|
'mod':'%',
|
||||||
'and':' AND ',
|
'and':' AND ',
|
||||||
'or':' OR ',
|
'or':' OR ',
|
||||||
'xor' : ' XOR ',
|
'xor' : ' XOR ',
|
||||||
'gt':'>',
|
'gt':'>',
|
||||||
'lt':'<',
|
'lt':'<',
|
||||||
'le':'<=',
|
'le':'<=',
|
||||||
'gt':'>='
|
'gt':'>='
|
||||||
}
|
}
|
||||||
|
|
||||||
compound_ops = {
|
compound_ops = {
|
||||||
}
|
}
|
||||||
|
|
||||||
unary_ops = {
|
unary_ops = {
|
||||||
'neg' : '-',
|
'neg' : '-',
|
||||||
'not' : ' NOT '
|
'not' : ' NOT '
|
||||||
}
|
}
|
||||||
|
|
||||||
coumpound_generating_ops = ['avgs', 'mins', 'maxs', 'sums'] + \
|
coumpound_generating_ops = ['avgs', 'mins', 'maxs', 'sums'] + \
|
||||||
list(binary_ops.keys()) + list(compound_ops.keys()) + list(unary_ops.keys() )
|
list(binary_ops.keys()) + list(compound_ops.keys()) + list(unary_ops.keys() )
|
||||||
|
|
||||||
def __init__(self, parent, node):
|
def __init__(self, parent, node):
|
||||||
self.raw_col = None
|
self.raw_col = None
|
||||||
self.inside_agg = False
|
self.inside_agg = False
|
||||||
if(type(parent) is expr):
|
if(type(parent) is expr):
|
||||||
self.inside_agg = parent.inside_agg
|
self.inside_agg = parent.inside_agg
|
||||||
ast_node.__init__(self, parent, node, None)
|
ast_node.__init__(self, parent, node, None)
|
||||||
|
|
||||||
def init(self, _):
|
def init(self, _):
|
||||||
from engine.projection import projection
|
from engine.projection import projection
|
||||||
parent = self.parent
|
parent = self.parent
|
||||||
self.isvector = parent.isvector if type(parent) is expr else False
|
self.isvector = parent.isvector if type(parent) is expr else False
|
||||||
self.is_compound = parent.is_compound if type(parent) is expr else False
|
self.is_compound = parent.is_compound if type(parent) is expr else False
|
||||||
if type(parent) in [projection, expr]:
|
if type(parent) in [projection, expr]:
|
||||||
self.datasource = parent.datasource
|
self.datasource = parent.datasource
|
||||||
else:
|
else:
|
||||||
self.datasource = self.context.datasource
|
self.datasource = self.context.datasource
|
||||||
self.udf_map = parent.context.udf_map
|
self.udf_map = parent.context.udf_map
|
||||||
self.func_maps = {**self.udf_map, **self.builtin_func_maps}
|
self.func_maps = {**self.udf_map, **self.builtin_func_maps}
|
||||||
|
|
||||||
def produce(self, node):
|
def produce(self, node):
|
||||||
if type(node) is dict:
|
if type(node) is dict:
|
||||||
for key, val in node.items():
|
for key, val in node.items():
|
||||||
if key in self.func_maps:
|
if key in self.func_maps:
|
||||||
# TODO: distinguish between UDF agg functions and other UDF functions.
|
# TODO: distinguish between UDF agg functions and other UDF functions.
|
||||||
self.inside_agg = True
|
self.inside_agg = True
|
||||||
if type(val) is list and len(val) > 1:
|
if type(val) is list and len(val) > 1:
|
||||||
cfunc = self.func_maps[key]
|
cfunc = self.func_maps[key]
|
||||||
cfunc = cfunc[len(val) - 1] if type(cfunc) is list else cfunc
|
cfunc = cfunc[len(val) - 1] if type(cfunc) is list else cfunc
|
||||||
self.sql += f"{cfunc}("
|
self.sql += f"{cfunc}("
|
||||||
for i, p in enumerate(val):
|
for i, p in enumerate(val):
|
||||||
self.sql += expr(self, p).sql + (',' if i < len(val) - 1 else '')
|
self.sql += expr(self, p).sql + (',' if i < len(val) - 1 else '')
|
||||||
else:
|
else:
|
||||||
funcname = self.func_maps[key]
|
funcname = self.func_maps[key]
|
||||||
funcname = funcname[0] if type(funcname) is list else funcname
|
funcname = funcname[0] if type(funcname) is list else funcname
|
||||||
self.sql += f"{funcname}("
|
self.sql += f"{funcname}("
|
||||||
self.sql += expr(self, val).sql
|
self.sql += expr(self, val).sql
|
||||||
self.sql += ')'
|
self.sql += ')'
|
||||||
self.inside_agg = False
|
self.inside_agg = False
|
||||||
elif key in self.binary_ops:
|
elif key in self.binary_ops:
|
||||||
l = expr(self, val[0]).sql
|
l = expr(self, val[0]).sql
|
||||||
r = expr(self, val[1]).sql
|
r = expr(self, val[1]).sql
|
||||||
self.sql += f'({l}{self.binary_ops[key]}{r})'
|
self.sql += f'({l}{self.binary_ops[key]}{r})'
|
||||||
elif key in self.compound_ops:
|
elif key in self.compound_ops:
|
||||||
x = []
|
x = []
|
||||||
if type(val) is list:
|
if type(val) is list:
|
||||||
for v in val:
|
for v in val:
|
||||||
x.append(expr(self, v).sql)
|
x.append(expr(self, v).sql)
|
||||||
self.sql = self.compound_ops[key][1](x)
|
self.sql = self.compound_ops[key][1](x)
|
||||||
elif key in self.unary_ops:
|
elif key in self.unary_ops:
|
||||||
self.sql += f'{self.unary_ops[key]}({expr(self, val).sql})'
|
self.sql += f'{self.unary_ops[key]}({expr(self, val).sql})'
|
||||||
else:
|
else:
|
||||||
print(f'Undefined expr: {key}{val}')
|
print(f'Undefined expr: {key}{val}')
|
||||||
|
|
||||||
if key in self.coumpound_generating_ops and not self.is_compound:
|
if key in self.coumpound_generating_ops and not self.is_compound:
|
||||||
self.is_compound = True
|
self.is_compound = True
|
||||||
p = self.parent
|
p = self.parent
|
||||||
while type(p) is expr and not p.is_compound:
|
while type(p) is expr and not p.is_compound:
|
||||||
p.is_compound = True
|
p.is_compound = True
|
||||||
p = p.parent
|
p = p.parent
|
||||||
|
|
||||||
elif type(node) is str:
|
elif type(node) is str:
|
||||||
p = self.parent
|
p = self.parent
|
||||||
while type(p) is expr and not p.isvector:
|
while type(p) is expr and not p.isvector:
|
||||||
p.isvector = True
|
p.isvector = True
|
||||||
p = p.parent
|
p = p.parent
|
||||||
|
|
||||||
self.raw_col = self.datasource.parse_col_names(node)
|
self.raw_col = self.datasource.parse_col_names(node)
|
||||||
self.raw_col = self.raw_col if type(self.raw_col) is ColRef else None
|
self.raw_col = self.raw_col if type(self.raw_col) is ColRef else None
|
||||||
if self.raw_col is not None:
|
if self.raw_col is not None:
|
||||||
self.sql = self.raw_col.name
|
self.sql = self.raw_col.name
|
||||||
else:
|
else:
|
||||||
self.sql = node
|
self.sql = node
|
||||||
|
|
||||||
elif type(node) is bool:
|
elif type(node) is bool:
|
||||||
self.sql = '1' if node else '0'
|
self.sql = '1' if node else '0'
|
||||||
else:
|
else:
|
||||||
self.sql = f'{node}'
|
self.sql = f'{node}'
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.sql
|
return self.sql
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return self.__str__()
|
return self.__str__()
|
||||||
|
|
||||||
|
|
@ -1,89 +1,89 @@
|
|||||||
class ColRef:
|
class ColRef:
|
||||||
def __init__(self, _ty, cobj, table:'TableInfo', name, id, compound = False):
|
def __init__(self, _ty, cobj, table:'TableInfo', name, id, compound = False):
|
||||||
self.type = _ty
|
self.type = _ty
|
||||||
self.cobj = cobj
|
self.cobj = cobj
|
||||||
self.table = table
|
self.table = table
|
||||||
self.name = name
|
self.name = name
|
||||||
self.alias = set()
|
self.alias = set()
|
||||||
self.id = id # position in table
|
self.id = id # position in table
|
||||||
self.compound = compound # compound field (list as a field)
|
self.compound = compound # compound field (list as a field)
|
||||||
# e.g. order by, group by, filter by expressions
|
# e.g. order by, group by, filter by expressions
|
||||||
|
|
||||||
self.__arr__ = (_ty, cobj, table, name, id)
|
self.__arr__ = (_ty, cobj, table, name, id)
|
||||||
def __getitem__(self, key):
|
def __getitem__(self, key):
|
||||||
if type(key) is str:
|
if type(key) is str:
|
||||||
return getattr(self, key)
|
return getattr(self, key)
|
||||||
else:
|
else:
|
||||||
return self.__arr__[key]
|
return self.__arr__[key]
|
||||||
|
|
||||||
def __setitem__(self, key, value):
|
def __setitem__(self, key, value):
|
||||||
self.__arr__[key] = value
|
self.__arr__[key] = value
|
||||||
|
|
||||||
|
|
||||||
class TableInfo:
|
class TableInfo:
|
||||||
def __init__(self, table_name, cols, cxt:'Context'):
|
def __init__(self, table_name, cols, cxt:'Context'):
|
||||||
# statics
|
# statics
|
||||||
self.table_name = table_name
|
self.table_name = table_name
|
||||||
self.alias = set([table_name])
|
self.alias = set([table_name])
|
||||||
self.columns_byname = dict() # column_name, type
|
self.columns_byname = dict() # column_name, type
|
||||||
self.columns = []
|
self.columns = []
|
||||||
self.cxt = cxt
|
self.cxt = cxt
|
||||||
# keep track of temp vars
|
# keep track of temp vars
|
||||||
self.rec = None
|
self.rec = None
|
||||||
self.add_cols(cols)
|
self.add_cols(cols)
|
||||||
# runtime
|
# runtime
|
||||||
self.order = [] # assumptions
|
self.order = [] # assumptions
|
||||||
|
|
||||||
cxt.tables_byname[self.table_name] = self # construct reverse map
|
cxt.tables_byname[self.table_name] = self # construct reverse map
|
||||||
|
|
||||||
def add_cols(self, cols, new = True):
|
def add_cols(self, cols, new = True):
|
||||||
for i, c in enumerate(cols):
|
for i, c in enumerate(cols):
|
||||||
self.add_col(c, new, i)
|
self.add_col(c, new, i)
|
||||||
def add_col(self, c, new = True, i = 0):
|
def add_col(self, c, new = True, i = 0):
|
||||||
_ty = c['type']
|
_ty = c['type']
|
||||||
if new:
|
if new:
|
||||||
_ty = _ty if type(c) is ColRef else list(_ty.keys())[0]
|
_ty = _ty if type(c) is ColRef else list(_ty.keys())[0]
|
||||||
col_object = ColRef(_ty, c, self, c['name'], len(self.columns))
|
col_object = ColRef(_ty, c, self, c['name'], len(self.columns))
|
||||||
else:
|
else:
|
||||||
col_object = c
|
col_object = c
|
||||||
c.table = self
|
c.table = self
|
||||||
self.columns_byname[c['name']] = col_object
|
self.columns_byname[c['name']] = col_object
|
||||||
self.columns.append(col_object)
|
self.columns.append(col_object)
|
||||||
|
|
||||||
def add_alias(self, alias):
|
def add_alias(self, alias):
|
||||||
if alias in self.cxt.tables_byname.keys():
|
if alias in self.cxt.tables_byname.keys():
|
||||||
print("Error: table alias already exists")
|
print("Error: table alias already exists")
|
||||||
return
|
return
|
||||||
self.cxt.tables_byname[alias] = self
|
self.cxt.tables_byname[alias] = self
|
||||||
self.alias.add(alias)
|
self.alias.add(alias)
|
||||||
def parse_col_names(self, colExpr) -> ColRef:
|
def parse_col_names(self, colExpr) -> ColRef:
|
||||||
parsedColExpr = colExpr.split('.')
|
parsedColExpr = colExpr.split('.')
|
||||||
if len(parsedColExpr) <= 1:
|
if len(parsedColExpr) <= 1:
|
||||||
return self.columns_byname[colExpr]
|
return self.columns_byname[colExpr]
|
||||||
else:
|
else:
|
||||||
datasource = self.cxt.tables_byname[parsedColExpr[0]]
|
datasource = self.cxt.tables_byname[parsedColExpr[0]]
|
||||||
if datasource is None:
|
if datasource is None:
|
||||||
raise ValueError(f'Table name/alias not defined{parsedColExpr[0]}')
|
raise ValueError(f'Table name/alias not defined{parsedColExpr[0]}')
|
||||||
else:
|
else:
|
||||||
return datasource.parse_col_names(parsedColExpr[1])
|
return datasource.parse_col_names(parsedColExpr[1])
|
||||||
|
|
||||||
|
|
||||||
class Context:
|
class Context:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.sql = ''
|
self.sql = ''
|
||||||
self.tables_byname = dict()
|
self.tables_byname = dict()
|
||||||
self.col_byname = dict()
|
self.col_byname = dict()
|
||||||
self.tables = []
|
self.tables = []
|
||||||
self.cols = []
|
self.cols = []
|
||||||
self.datasource = None
|
self.datasource = None
|
||||||
self.udf_map = dict()
|
self.udf_map = dict()
|
||||||
|
|
||||||
def emit(self, sql:str):
|
def emit(self, sql:str):
|
||||||
self.sql += sql + ' '
|
self.sql += sql + ' '
|
||||||
|
|
||||||
def add_table(self, table_name, cols):
|
def add_table(self, table_name, cols):
|
||||||
tbl = TableInfo(table_name, cols, self)
|
tbl = TableInfo(table_name, cols, self)
|
||||||
self.tables.append(tbl)
|
self.tables.append(tbl)
|
||||||
return tbl
|
return tbl
|
||||||
|
|
||||||
|
|
Binary file not shown.
@ -1,27 +0,0 @@
|
|||||||
{
|
|
||||||
"BackgroundImageAbsolutePath": "c:\\users\\bill\\appdata\\local\\microsoft\\visualstudio\\17.0_03c65567\\extensions\\atkxhose.05t\\Images\\background.png",
|
|
||||||
"BackgroundImagesDirectoryAbsolutePath": "c:\\users\\bill\\appdata\\local\\microsoft\\visualstudio\\17.0_03c65567\\extensions\\atkxhose.05t\\Images",
|
|
||||||
"ExpandToIDE": false,
|
|
||||||
"Extensions": ".png, .jpg, .gif, .bmp",
|
|
||||||
"ImageBackgroundType": 0,
|
|
||||||
"ImageFadeAnimationInterval": "PT5S",
|
|
||||||
"ImageStretch": 0,
|
|
||||||
"IsLimitToMainlyEditorWindow": false,
|
|
||||||
"LoopSlideshow": true,
|
|
||||||
"MaxHeight": 0,
|
|
||||||
"MaxWidth": 0,
|
|
||||||
"Opacity": 0.35,
|
|
||||||
"PositionHorizon": 1,
|
|
||||||
"PositionVertical": 1,
|
|
||||||
"ShuffleSlideshow": false,
|
|
||||||
"SoftEdgeX": 0,
|
|
||||||
"SoftEdgeY": 0,
|
|
||||||
"TileMode": 0,
|
|
||||||
"UpdateImageInterval": "PT1M",
|
|
||||||
"ViewBoxPointX": 0,
|
|
||||||
"ViewBoxPointY": 0,
|
|
||||||
"ViewPortHeight": 1,
|
|
||||||
"ViewPortPointX": 0,
|
|
||||||
"ViewPortPointY": 0,
|
|
||||||
"ViewPortWidth": 1
|
|
||||||
}
|
|
Loading…
Reference in new issue