Bug fixes for alias&join. Add test in presentation.
This commit is contained in:
+36
-18
@@ -73,7 +73,7 @@ class projection(ast_node):
|
||||
self.datasource = join(self, [], self.context) # datasource is Join instead of TableInfo
|
||||
self.assumptions = []
|
||||
if 'from' in node:
|
||||
from_clause = node['from']
|
||||
from_clause = node['from']['table_source']
|
||||
self.datasource = join(self, from_clause)
|
||||
if 'assumptions' in from_clause:
|
||||
self.assumptions = enlist(from_clause['assumptions'])
|
||||
@@ -129,12 +129,17 @@ class projection(ast_node):
|
||||
if not proj_expr.is_special:
|
||||
y = lambda x:x
|
||||
name = eval('f\'' + name + '\'')
|
||||
offset = len(col_exprs)
|
||||
if name not in self.var_table:
|
||||
self.var_table[name] = len(col_exprs)
|
||||
proj_map[i] = [this_type, len(col_exprs), proj_expr]
|
||||
self.var_table[name] = offset
|
||||
if proj_expr.is_ColExpr and type(proj_expr.raw_col) is ColRef:
|
||||
for n in (proj_expr.raw_col.table.alias):
|
||||
self.var_table[f'{n}.'+name] = offset
|
||||
proj_map[i] = [this_type, offset, proj_expr]
|
||||
col_expr = name + ' AS ' + alias if alias else name
|
||||
if alias:
|
||||
self.var_table[alias] = len(col_exprs)
|
||||
self.var_table[alias] = offset
|
||||
|
||||
col_exprs.append((col_expr, proj_expr.type))
|
||||
else:
|
||||
self.context.headers.add('"./server/aggregations.h"')
|
||||
@@ -164,10 +169,12 @@ class projection(ast_node):
|
||||
self.add(', '.join([c[0] for c in col_exprs] + col_ext_names))
|
||||
|
||||
_base_offset = len(col_exprs)
|
||||
for i, col in enumerate(col_ext_names):
|
||||
if col not in self.var_table:
|
||||
self.var_table[col] = i + _base_offset
|
||||
|
||||
for i, col in enumerate(self.col_ext):
|
||||
if col.name not in self.var_table:
|
||||
offset = i + _base_offset
|
||||
self.var_table[col.name] = offset
|
||||
for n in (col.table.alias):
|
||||
self.var_table[f'{n}.'+col.name] = offset
|
||||
|
||||
def finialize(astnode:ast_node):
|
||||
if(astnode is not None):
|
||||
@@ -548,24 +555,25 @@ class join(ast_node):
|
||||
self.tables_dir = dict()
|
||||
self.rec = None
|
||||
self.top_level = self.parent and type(self.parent) is projection
|
||||
self.have_sep = False
|
||||
# self.tmp_name = 'join_' + base62uuid(4)
|
||||
# self.datasource = TableInfo(self.tmp_name, [], self.context)
|
||||
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:
|
||||
self.joins.append(alias(tbls.__str__()))
|
||||
self.joins.append((alias(tbls.__str__()), tbls.have_sep))
|
||||
self.tables += tbls.tables
|
||||
self.tables_dir = {**self.tables_dir, **tbls.tables_dir}
|
||||
|
||||
elif type(tbls) is TableInfo:
|
||||
self.joins.append(alias(tbls.table_name))
|
||||
self.joins.append((alias(tbls.table_name), False))
|
||||
self.tables.append(tbls)
|
||||
self.tables_dir[tbls.table_name] = tbls
|
||||
for a in tbls.alias:
|
||||
self.tables_dir[a] = tbls
|
||||
|
||||
elif type(tbls) is projection:
|
||||
self.joins.append(alias(tbls.finalize()))
|
||||
self.joins.append((alias(tbls.finalize()), False))
|
||||
|
||||
def produce(self, node):
|
||||
if type(node) is list:
|
||||
@@ -589,13 +597,14 @@ class join(ast_node):
|
||||
tbl.add_alias(node['name'])
|
||||
self.append(tbl, alias)
|
||||
else:
|
||||
keys = node.keys()
|
||||
keys = list(node.keys())
|
||||
if keys[0].lower().endswith('join'):
|
||||
self.have_sep = True
|
||||
j = join(self, node[keys[0]])
|
||||
tablename = f' {keys[0]} {j}'
|
||||
if keys[1].lower() == 'on':
|
||||
if len(keys) > 1 and keys[1].lower() == 'on':
|
||||
tablename += f' on {expr(self, node[keys[1]])}'
|
||||
self.joins.append(tablename)
|
||||
self.joins.append((tablename, self.have_sep))
|
||||
self.tables += j.tables
|
||||
self.tables_dir = {**self.tables_dir, **j.tables_dir}
|
||||
|
||||
@@ -622,18 +631,27 @@ class join(ast_node):
|
||||
if datasource is None:
|
||||
raise ValueError(f'Table name/alias not defined{parsedColExpr[0]}')
|
||||
else:
|
||||
return datasource.parse_col_names(parsedColExpr[1])
|
||||
datasource.rec = self.rec
|
||||
ret = datasource.parse_col_names(parsedColExpr[1])
|
||||
datasource.rec = None
|
||||
return ret
|
||||
|
||||
@property
|
||||
def all_cols(self):
|
||||
return set([c for t in self.tables for c in t.columns])
|
||||
def consume(self, node):
|
||||
self.sql = ', '.join(self.joins)
|
||||
self.sql = ''
|
||||
for j in self.joins:
|
||||
if not self.sql or j[1]:
|
||||
self.sql += j[0]
|
||||
else:
|
||||
self.sql += ', ' + j[0]
|
||||
if node and self.sql and self.top_level:
|
||||
self.sql = ' FROM ' + self.sql
|
||||
return super().consume(node)
|
||||
|
||||
def __str__(self):
|
||||
return ', '.join(self.joins)
|
||||
return self.sql
|
||||
def __repr__(self):
|
||||
return self.__str__()
|
||||
|
||||
|
||||
+128
-110
@@ -87,116 +87,119 @@ class expr(ast_node):
|
||||
from reconstruct.ast import udf
|
||||
|
||||
if type(node) is dict:
|
||||
if len(node) > 1:
|
||||
print(f'Parser Error: {node} has more than 1 dict entry.')
|
||||
if 'literal' in node:
|
||||
node = node['literal']
|
||||
else:
|
||||
if len(node) > 1:
|
||||
print(f'Parser Error: {node} has more than 1 dict entry.')
|
||||
|
||||
for key, val in node.items():
|
||||
if key in self.operators:
|
||||
if key in builtin_func:
|
||||
if self.is_agg_func:
|
||||
self.root.is_special = True # Nested Aggregation
|
||||
else:
|
||||
self.is_agg_func = True
|
||||
|
||||
op = self.operators[key]
|
||||
count_distinct = False
|
||||
if key == 'count' and type(val) is dict and 'distinct' in val:
|
||||
count_distinct = True
|
||||
val = val['distinct']
|
||||
val = enlist(val)
|
||||
exp_vals = [expr(self, v, c_code = self.c_code) for v in val]
|
||||
self.children = exp_vals
|
||||
self.opname = key
|
||||
|
||||
str_vals = [e.sql for e in exp_vals]
|
||||
type_vals = [e.type for e in exp_vals]
|
||||
is_compound = any([e.is_compound for e in exp_vals])
|
||||
if key in self.ext_aggfuncs:
|
||||
self.is_compound = False
|
||||
else:
|
||||
self.is_compound = is_compound
|
||||
try:
|
||||
self.type = op.return_type(*type_vals)
|
||||
except AttributeError as e:
|
||||
if type(self.root) is not udf:
|
||||
# TODO: do something when this is not an error
|
||||
# print(f'alert: {e}')
|
||||
pass
|
||||
self.type = AnyT
|
||||
for key, val in node.items():
|
||||
if key in self.operators:
|
||||
if key in builtin_func:
|
||||
if self.is_agg_func:
|
||||
self.root.is_special = True # Nested Aggregation
|
||||
else:
|
||||
self.is_agg_func = True
|
||||
|
||||
if count_distinct: # inject distinct col later
|
||||
self.sql = f'{{{op(self.c_code, *str_vals, True)}}}'
|
||||
else:
|
||||
self.sql = op(self.c_code, *str_vals)
|
||||
|
||||
special_func = [*self.context.udf_map.keys(), *self.context.module_map.keys(),
|
||||
"maxs", "mins", "avgs", "sums", "deltas"]
|
||||
if self.context.special_gb:
|
||||
special_func = [*special_func, *self.ext_aggfuncs]
|
||||
|
||||
if key in special_func and not self.is_special:
|
||||
self.is_special = True
|
||||
if key in self.context.udf_map:
|
||||
self.root.udf_called = self.context.udf_map[key]
|
||||
if self.is_udfexpr and key == self.root.udf.name:
|
||||
self.root.is_recursive_call_inudf = True
|
||||
elif key in user_module_func.keys():
|
||||
udf.try_init_udf(self.context)
|
||||
# TODO: make udf_called a set!
|
||||
p = self.parent
|
||||
while type(p) is expr and not p.udf_called:
|
||||
p.udf_called = self.udf_called
|
||||
p = p.parent
|
||||
p = self.parent
|
||||
while type(p) is expr and not p.is_special:
|
||||
p.is_special = True
|
||||
p = p.parent
|
||||
|
||||
need_decltypestr = any([e.need_decltypestr for e in exp_vals])
|
||||
if need_decltypestr or (self.udf_called and type(op) is udf):
|
||||
decltypestr_vals = [e.udf_decltypecall for e in exp_vals]
|
||||
self.udf_decltypecall = op(self.c_code, *decltypestr_vals)
|
||||
|
||||
if self.udf_called and type(op) is udf:
|
||||
self.udf_decltypecall = op.decltypecall(self.c_code, *decltypestr_vals)
|
||||
|
||||
elif self.is_udfexpr:
|
||||
var_table = self.root.udf.var_table
|
||||
vec = key.split('.')
|
||||
_vars = [*var_table, *self.builtin_vars]
|
||||
def get_vname (node):
|
||||
if node in self.builtin_vars:
|
||||
self.root.udf.builtin[node].enabled = True
|
||||
self.builtin_var = node
|
||||
return node
|
||||
else:
|
||||
return var_table[node]
|
||||
if vec[0] not in _vars:
|
||||
# print(f'Use of undefined variable {vec[0]}')
|
||||
# TODO: do something when this is not an error
|
||||
pass
|
||||
else:
|
||||
vname = get_vname(vec[0])
|
||||
op = self.operators[key]
|
||||
count_distinct = False
|
||||
if key == 'count' and type(val) is dict and 'distinct' in val:
|
||||
count_distinct = True
|
||||
val = val['distinct']
|
||||
val = enlist(val)
|
||||
if(len(val) > 2):
|
||||
print('Warning: more than 2 indexes found for subvec operator.')
|
||||
ex = [expr(self, v, c_code = self.c_code) for v in val]
|
||||
idxs = ', '.join([e.sql for e in ex])
|
||||
self.sql = f'{vname}.subvec({idxs})'
|
||||
if any([e.need_decltypestr for e in ex]):
|
||||
self.udf_decltypecall = f'{vname}.subvec({[", ".join([e.udf_decltypecall for e in ex])]})'
|
||||
if key == 'get' and len(val) > 1:
|
||||
ex_vname = expr(self, val[0], c_code=self.c_code)
|
||||
self.sql = f'{ex_vname.sql}[{expr(self, val[1], c_code=self.c_code).sql}]'
|
||||
if hasattr(ex_vname, 'builtin_var'):
|
||||
if not hasattr(self, 'builtin_var'):
|
||||
self.builtin_var = []
|
||||
self.builtin_var = [*self.builtin_var, *ex_vname.builtin_var]
|
||||
self.udf_decltypecall = ex_vname.sql
|
||||
else:
|
||||
print(f'Undefined expr: {key}{val}')
|
||||
exp_vals = [expr(self, v, c_code = self.c_code) for v in val]
|
||||
self.children = exp_vals
|
||||
self.opname = key
|
||||
|
||||
str_vals = [e.sql for e in exp_vals]
|
||||
type_vals = [e.type for e in exp_vals]
|
||||
is_compound = any([e.is_compound for e in exp_vals])
|
||||
if key in self.ext_aggfuncs:
|
||||
self.is_compound = False
|
||||
else:
|
||||
self.is_compound = is_compound
|
||||
try:
|
||||
self.type = op.return_type(*type_vals)
|
||||
except AttributeError as e:
|
||||
if type(self.root) is not udf:
|
||||
# TODO: do something when this is not an error
|
||||
# print(f'alert: {e}')
|
||||
pass
|
||||
self.type = AnyT
|
||||
|
||||
if count_distinct: # inject distinct col later
|
||||
self.sql = f'{{{op(self.c_code, *str_vals, True)}}}'
|
||||
else:
|
||||
self.sql = op(self.c_code, *str_vals)
|
||||
|
||||
special_func = [*self.context.udf_map.keys(), *self.context.module_map.keys(),
|
||||
"maxs", "mins", "avgs", "sums", "deltas", "last"]
|
||||
if self.context.special_gb:
|
||||
special_func = [*special_func, *self.ext_aggfuncs]
|
||||
|
||||
if key in special_func and not self.is_special:
|
||||
self.is_special = True
|
||||
if key in self.context.udf_map:
|
||||
self.root.udf_called = self.context.udf_map[key]
|
||||
if self.is_udfexpr and key == self.root.udf.name:
|
||||
self.root.is_recursive_call_inudf = True
|
||||
elif key in user_module_func.keys():
|
||||
udf.try_init_udf(self.context)
|
||||
# TODO: make udf_called a set!
|
||||
p = self.parent
|
||||
while type(p) is expr and not p.udf_called:
|
||||
p.udf_called = self.udf_called
|
||||
p = p.parent
|
||||
p = self.parent
|
||||
while type(p) is expr and not p.is_special:
|
||||
p.is_special = True
|
||||
p = p.parent
|
||||
|
||||
elif type(node) is str:
|
||||
need_decltypestr = any([e.need_decltypestr for e in exp_vals])
|
||||
if need_decltypestr or (self.udf_called and type(op) is udf):
|
||||
decltypestr_vals = [e.udf_decltypecall for e in exp_vals]
|
||||
self.udf_decltypecall = op(self.c_code, *decltypestr_vals)
|
||||
|
||||
if self.udf_called and type(op) is udf:
|
||||
self.udf_decltypecall = op.decltypecall(self.c_code, *decltypestr_vals)
|
||||
|
||||
elif self.is_udfexpr:
|
||||
var_table = self.root.udf.var_table
|
||||
vec = key.split('.')
|
||||
_vars = [*var_table, *self.builtin_vars]
|
||||
def get_vname (node):
|
||||
if node in self.builtin_vars:
|
||||
self.root.udf.builtin[node].enabled = True
|
||||
self.builtin_var = node
|
||||
return node
|
||||
else:
|
||||
return var_table[node]
|
||||
if vec[0] not in _vars:
|
||||
# print(f'Use of undefined variable {vec[0]}')
|
||||
# TODO: do something when this is not an error
|
||||
pass
|
||||
else:
|
||||
vname = get_vname(vec[0])
|
||||
val = enlist(val)
|
||||
if(len(val) > 2):
|
||||
print('Warning: more than 2 indexes found for subvec operator.')
|
||||
ex = [expr(self, v, c_code = self.c_code) for v in val]
|
||||
idxs = ', '.join([e.sql for e in ex])
|
||||
self.sql = f'{vname}.subvec({idxs})'
|
||||
if any([e.need_decltypestr for e in ex]):
|
||||
self.udf_decltypecall = f'{vname}.subvec({[", ".join([e.udf_decltypecall for e in ex])]})'
|
||||
if key == 'get' and len(val) > 1:
|
||||
ex_vname = expr(self, val[0], c_code=self.c_code)
|
||||
self.sql = f'{ex_vname.sql}[{expr(self, val[1], c_code=self.c_code).sql}]'
|
||||
if hasattr(ex_vname, 'builtin_var'):
|
||||
if not hasattr(self, 'builtin_var'):
|
||||
self.builtin_var = []
|
||||
self.builtin_var = [*self.builtin_var, *ex_vname.builtin_var]
|
||||
self.udf_decltypecall = ex_vname.sql
|
||||
else:
|
||||
print(f'Undefined expr: {key}{val}')
|
||||
|
||||
if type(node) is str:
|
||||
if self.is_udfexpr:
|
||||
curr_udf : udf = self.root.udf
|
||||
var_table = curr_udf.var_table
|
||||
@@ -231,14 +234,29 @@ class expr(ast_node):
|
||||
self.raw_col = self.raw_col if type(self.raw_col) is ColRef else None
|
||||
if self.raw_col is not None:
|
||||
self.is_ColExpr = True
|
||||
self.sql = self.raw_col.name
|
||||
table_name = ''
|
||||
if '.' in node:
|
||||
table_name = self.raw_col.table.table_name
|
||||
if self.raw_col.table.alias:
|
||||
alias = iter(self.raw_col.table.alias)
|
||||
try:
|
||||
a = next(alias)
|
||||
while(not a or a == table_name):
|
||||
a = next(alias)
|
||||
if (a and a != table_name):
|
||||
table_name = a
|
||||
except StopIteration:
|
||||
pass
|
||||
if table_name:
|
||||
table_name = table_name + '.'
|
||||
self.sql = table_name + self.raw_col.name
|
||||
self.type = self.raw_col.type
|
||||
self.is_compound = True
|
||||
self.opname = self.raw_col
|
||||
else:
|
||||
self.sql = node
|
||||
self.sql = '\'' + node + '\''
|
||||
self.type = StrT
|
||||
self.opname = node
|
||||
self.opname = self.sql
|
||||
if self.c_code and self.datasource is not None:
|
||||
self.sql = f'{{y(\"{self.sql}\")}}'
|
||||
elif type(node) is bool:
|
||||
@@ -248,7 +266,7 @@ class expr(ast_node):
|
||||
self.sql = '1' if node else '0'
|
||||
else:
|
||||
self.sql = 'TRUE' if node else 'FALSE'
|
||||
else:
|
||||
elif type(node) is not dict:
|
||||
self.sql = f'{node}'
|
||||
self.opname = node
|
||||
if type(node) is int:
|
||||
|
||||
Reference in New Issue
Block a user