from common.ast import ColRef, TableInfo, ast_node, Context, include from common.groupby import groupby from common.join import join from common.expr import expr from common.orderby import assumption, orderby from common.scan import filter from common.utils import base62uuid, enlist, base62alp, has_other from common.ddl import create_table, outfile import copy class projection(ast_node): name='select' def __init__(self, parent:ast_node, node, context:Context = None, outname = None, disp = True): self.disp = disp self.outname = outname self.group_node = None self.assumptions = None self.where = None ast_node.__init__(self, parent, node, context) def init(self, _): if self.outname is None: self.outname = self.context.gen_tmptable() def produce(self, node): p = node['select'] self.projections = p if type(p) is list else [p] self.context.Info(node) def spawn(self, node): self.datasource = None if 'from' in node: from_clause = node['from']['table_source'] if type(from_clause) is list: # from joins join(self, from_clause) elif type(from_clause) is dict: if 'value' in from_clause: value = from_clause['value'] if type(value) is dict: if 'select' in value: # from subquery projection(self, from_clause, disp = False) else: # TODO: from func over table print(f'from func over table{node}') elif type(value) is str: self.datasource = self.context.tables_byname[value] if 'name' in value: self.datasource.add_alias(value['name']) if 'assuming' in node['from']: self.assumptions = enlist(node['from']['assuming']) elif type(from_clause) is str: self.datasource = self.context.tables_byname[from_clause] if self.datasource is None: raise ValueError('spawn error: from clause') if self.datasource is not None: self.datasource_changed = True self.prev_datasource = self.context.datasource self.context.datasource = self.datasource if 'where' in node: self.where = filter(self, node['where'], True) # self.datasource = filter(self, node['where'], True).output # self.context.datasource = self.datasource if 'groupby' in node: self.group_node = groupby(self, node['groupby']) self.datasource = copy.copy(self.datasource) # shallow copy self.datasource.groupinfo = self.group_node else: self.group_node = None def consume(self, node): self.inv = True disp_varname = 'd'+base62uuid(7) has_groupby = self.group_node is not None cexprs = [] flatten = False cols = [] self.out_table = TableInfo('out_'+base62uuid(4), [], self.context) if 'outfile' in node: flatten = True new_names = [] proj_raw_cols = [] for i, proj in enumerate(self.projections): cname = '' compound = False self.datasource.rec = set() if type(proj) is dict: if 'value' in proj: e = proj['value'] sname = expr(self, e) if type(sname.raw_col) is ColRef: proj_raw_cols.append(sname.raw_col) sname = sname._expr fname = expr.toCExpr(sname) # fastest access method at innermost context absname = expr(self, e, abs_col=True)._expr # absolute name at function scope # TODO: Make it single pass here. compound = True # compound column cexprs.append(fname) cname = e if type(e) is str else ''.join([a if a in base62alp else '' for a in expr.toCExpr(absname)()]) if 'name' in proj: # renaming column by AS keyword cname = proj['name'] new_names.append(cname) elif type(proj) is str: col = self.datasource.get_col_d(proj) if type(col) is ColRef: col.reference() compound = compound and has_groupby and has_other(self.datasource.rec, self.group_node.referenced) self.datasource.rec = None typename = f'decays' if not compound: typename = f'value_type<{typename}>' cols.append(ColRef(cname, expr.toCExpr(typename)(), self.out_table, 0, None, cname, i, compound=compound)) self.out_table.add_cols(cols, False) lineage = None if has_groupby: create_table(self, self.out_table) # creates empty out_table. if self.assumptions is not None: self.assumptions = assumption(self, self.assumptions, exclude=self.group_node.raw_groups) if not self.assumptions.empty(): self.group_node.deal_with_assumptions(self.assumptions, self.out_table) self.assumptions = None self.group_node.finalize(cexprs, self.out_table) else: # if all assumptions in projections, treat as orderby lineage = self.assumptions is not None and has_other(self.assumptions, proj_raw_cols) spawn = create_table(self, self.out_table, cexprs = cexprs, lineage = lineage) # create and populate out_table. if lineage and type(spawn.lineage) is str: lineage = spawn.lineage self.assumptions = orderby(self, self.assumptions) # do not exclude proj_raw_cols else: lineage = None if self.where is not None: self.where.finalize() if type(lineage) is str: order = 'order_' + base62uuid(6) self.emit(f'auto {order} = {self.datasource.cxt_name}->order_by<{self.assumptions.result()}>({lineage});') self.emit(f'{self.out_table.cxt_name}->materialize(*{order});') self.assumptions = None if self.assumptions is not None: orderby_node = orderby(self, self.assumptions) else: orderby_node = None if 'orderby' in node: self.datasource = self.out_table self.context.datasource = self.out_table # discard current ds orderbys = node['orderby'] orderby_node = orderby(self, orderbys) if orderby_node is None else orderby_node.merge(orderbys) if orderby_node is not None: self.emit(f'auto {disp_varname} = {self.out_table.reference()}->order_by_view<{orderby_node.result()}>();') else: disp_varname = f'*{self.out_table.cxt_name}' if self.disp: self.emit(f'print({disp_varname});') if flatten: outfile(self, node['outfile']) if self.datasource_changed: self.context.datasource = self.prev_datasource import sys include(sys.modules[__name__])