from engine.types import * class ColRef: def __init__(self, _ty, cobj, table:'TableInfo', name, id, compound = False): self.type : Types = AnyT if type(_ty) is str: self.type = builtin_types[_ty.lower()] elif type(_ty) is Types: self.type = _ty self.cobj = cobj self.table = table self.name = name self.alias = set() self.id = id # position in table self.compound = compound # compound field (list as a field) # e.g. order by, group by, filter by expressions self.__arr__ = (_ty, cobj, table, name, id) def __getitem__(self, key): if type(key) is str: return getattr(self, key) else: return self.__arr__[key] def __setitem__(self, key, value): self.__arr__[key] = value class TableInfo: def __init__(self, table_name, cols, cxt:'Context'): # statics self.table_name = table_name self.alias = set([table_name]) self.columns_byname = dict() # column_name, type self.columns = [] self.cxt = cxt # keep track of temp vars self.rec = None self.add_cols(cols) # runtime self.order = [] # assumptions cxt.tables_byname[self.table_name] = self # construct reverse map def add_cols(self, cols, new = True): for i, c in enumerate(cols): self.add_col(c, new, i) def add_col(self, c, new = True, i = 0): _ty = c['type'] if new: _ty = _ty if type(c) is ColRef else list(_ty.keys())[0] col_object = ColRef(_ty, c, self, c['name'], len(self.columns)) else: col_object = c c.table = self self.columns_byname[c['name']] = col_object self.columns.append(col_object) def add_alias(self, alias): if alias in self.cxt.tables_byname.keys(): print("Error: table alias already exists") return self.cxt.tables_byname[alias] = self self.alias.add(alias) def parse_col_names(self, colExpr) -> ColRef: parsedColExpr = colExpr.split('.') if len(parsedColExpr) <= 1: col = self.columns_byname[colExpr] if type(self.rec) is set: self.rec.add(col) return col else: datasource = self.cxt.tables_byname[parsedColExpr[0]] if datasource is None: raise ValueError(f'Table name/alias not defined{parsedColExpr[0]}') else: return datasource.parse_col_names(parsedColExpr[1]) class Context: def new(self): self.headers = set(['\"./server/libaquery.h\"', '\"./server/monetdb_conn.h\"']) self.ccode = '' self.sql = '' self.finalized = False self.udf = None def __init__(self): self.tables_byname = dict() self.col_byname = dict() self.tables = [] self.cols = [] self.datasource = None self.udf_map = dict() self.use_columnstore = False self.print = print self.has_dll = False self.dialect = 'MonetDB' self.new() def emit(self, sql:str): self.sql += sql + ' ' def emitc(self, c:str): self.ccode += c + '\n' def add_table(self, table_name, cols): tbl = TableInfo(table_name, cols, self) self.tables.append(tbl) return tbl function_head = ''' extern "C" int __DLLEXPORT__ dllmain(Context* cxt) { using namespace std; using namespace types; auto server = static_cast(cxt->alt_server); ''' udf_head = ('#pragma once\n' '#include \"./server/libaquery.h\"\n' '#include \"./server/aggregations.h\"\n\n' ) def finalize(self): if not self.finalized: headers = '' for h in self.headers: if h[0] != '"': headers += '#include <' + h + '>\n' else: headers += '#include ' + h + '\n' self.ccode = headers + self.function_head + self.ccode + 'return 0;\n}' self.headers = set() return self.ccode