def __init__(self, case_when, from_clause, cte, cte_name, rename): #print(json.dumps(case_when, indent=2)) self.case_when = case_when self.expr = None self.result = None self.from_clause = from_clause self.cte = cte self.cte_name = cte_name self.rename = rename if "A_Expr" in self.case_when["expr"]: temp_case = self.case_when["expr"]["A_Expr"] left = temp_case["lexpr"] right = temp_case["rexpr"] operator = temp_case["name"][0]["String"]["str"] self.expr = AExpression(left, operator, right, self.from_clause, self.cte, self.cte_name, self.rename) if "A_Const" in self.case_when["result"]: temp_case = self.case_when["result"]["A_Const"]["val"] if "Integer" in temp_case: self.result = temp_case["Integer"]["ival"] elif "String" in temp_case: self.result = temp_case["String"]["str"] elif "Float" in temp_case: self.result = temp_case["Float"]["str"] elif "Null" in temp_case: self.result = "NULL" elif "ColumnRef" in self.case_when["result"]: col = Column(self.case_when["result"]["ColumnRef"], self.from_clause, self.cte, self.cte_name, self.rename) self.result = col.transform_into_cypher()
class JoinCondition: def __init__(self, raw_join_cond, left, right): self.raw_join_cond = raw_join_cond self.location = None self.left_alias = left self.right_alias = right self.operator = None if "A_Expr" in self.raw_join_cond.keys(): self.operator = self.raw_join_cond["A_Expr"]["name"][0]["String"][ "str"] if "A_Expr" in self.raw_join_cond.keys(): self.location = self.raw_join_cond["A_Expr"]["location"] if "ColumnRef" in self.raw_join_cond["A_Expr"]["lexpr"]: self.left = Column( self.raw_join_cond["A_Expr"]["lexpr"]["ColumnRef"]) if "ColumnRef" in self.raw_join_cond["A_Expr"]["rexpr"]: self.right = Column( self.raw_join_cond["A_Expr"]["rexpr"]["ColumnRef"]) def transform_into_cypher(self): res = "" res += self.left.transform_into_cypher() + " " res += self.operator + " " res += self.right.transform_into_cypher() return res
def __init__(self, raw_join_cond, left, right): self.raw_join_cond = raw_join_cond self.location = None self.left_alias = left self.right_alias = right self.operator = None if "A_Expr" in self.raw_join_cond.keys(): self.operator = self.raw_join_cond["A_Expr"]["name"][0]["String"][ "str"] if "A_Expr" in self.raw_join_cond.keys(): self.location = self.raw_join_cond["A_Expr"]["location"] if "ColumnRef" in self.raw_join_cond["A_Expr"]["lexpr"]: self.left = Column( self.raw_join_cond["A_Expr"]["lexpr"]["ColumnRef"]) if "ColumnRef" in self.raw_join_cond["A_Expr"]["rexpr"]: self.right = Column( self.raw_join_cond["A_Expr"]["rexpr"]["ColumnRef"])
def __init__(self, target_list, from_clause, cte=False, cte_name=""): self.res_target = target_list self.from_clause = from_clause self.cte = cte self.cte_name = cte_name self.columns = [] self.functions = [] self.aexpressions = [] for i, elem in enumerate(target_list): if "ResTarget" in elem: rename = None if "name" in elem["ResTarget"]: rename = elem["ResTarget"]["name"] if "val" in elem["ResTarget"]: temp_val = elem["ResTarget"]["val"] if "ColumnRef" in temp_val: col = Column( temp_val["ColumnRef"], self.from_clause, self.cte, self.cte_name, rename) self.columns.append(col) elif "FuncCall" in temp_val: func = FuncCall( temp_val["FuncCall"], self.from_clause, self.cte, self.cte_name, rename, i) col_refer = func.get_col_refer() self.columns.append(col_refer) self.functions.append(func) elif "A_Expr" in temp_val: left = temp_val["A_Expr"]["lexpr"] right = temp_val["A_Expr"]["rexpr"] operator = temp_val["A_Expr"]["name"][0]["String"]["str"] a_expr = AExpression( left, operator, right, self.from_clause, self.cte, self.cte_name, rename) col_refer = a_expr.get_col_refer() self.columns.append(col_refer) self.aexpressions.append(a_expr) elif "CaseExpr" in temp_val: case_expr = CaseExpression( temp_val["CaseExpr"], self.from_clause, self.cte, self.cte_name, rename) self.columns.append(case_expr)
def __init__(self, left, operator, right, from_clause=None, cte=None, cte_name=None, rename=None): self.left_initial = left self.left = None self.operator = operator self.right_initial = right self.right = None self.col_refer = None self.expr = None self.from_clause = from_clause self.cte = cte self.cte_name = cte_name self.rename = rename if "A_Const" in self.left_initial.keys(): temp_left = self.left_initial["A_Const"]["val"] if "String" in temp_left: self.left = temp_left["String"]["str"] elif "Integer" in temp_left: self.left = temp_left["Integer"]["ival"] elif "Float" in temp_left: self.left = temp_left["Float"]["str"] elif "FuncCall" in self.left_initial.keys(): self.left = FuncCall( self.left_initial["FuncCall"], self.from_clause, self.cte, self.cte_name, self.rename) self.col_refer = self.left.get_col_refer() elif "ColumnRef" in self.left_initial.keys(): self.left = Column( self.left_initial["ColumnRef"], self.from_clause, True, self.cte_name, self.rename) if "A_Const" in self.right_initial.keys(): temp_right = self.right_initial["A_Const"]["val"] if "String" in temp_right: self.right = temp_right["String"]["str"] elif "Integer" in temp_right: self.right = temp_right["Integer"]["ival"] elif "Float" in temp_right: self.right = temp_right["Float"]["str"] elif "FuncCall" in self.right_initial.keys(): self.right = FuncCall( self.right_initial["FuncCall"], self.from_clause, self.cte, self.cte_name, self.rename) self.col_refer = self.right.get_col_refer() elif "ColumnRef" in self.right_initial.keys(): self.right = Column( self.right_initial["ColumnRef"], self.from_clause, True, self.cte_name, self.rename)
def __init__(self, raw_expr, from_clause, cte=False, cte_name=""): self.raw_expr = raw_expr self.sources = [] self.from_clause = from_clause self.cte = cte self.cte_name = cte_name self.boolop = self.raw_expr["boolop"] self.mapped_boolop = None self.edge_types = graph_db.get_edge_types() self.joins = [] self.possible_joins = [] self.filters = [] if self.boolop == 0: self.mapped_boolop = "AND" elif self.boolop == 1: self.mapped_boolop = "OR" elif self.boolop == 2: self.mapped_boolop = "NOT" # First possible join conditions are extracted from the boolean expression # Those joins that cannot be transformed into pattern match are appended to filter conditions for elem in self.raw_expr["args"]: if "A_Expr" in elem.keys(): left_side = elem["A_Expr"]["lexpr"] right_side = elem["A_Expr"]["rexpr"] operator = elem["A_Expr"]["name"][0]["String"]["str"] if type(left_side) == dict and type(right_side) == dict: if "ColumnRef" in left_side.keys( ) and "ColumnRef" in right_side.keys(): left = Column(left_side["ColumnRef"], self.from_clause, self.cte, self.cte_name) right = Column(right_side["ColumnRef"], self.from_clause, self.cte, self.cte_name) left_table = left.get_collection() right_table = right.get_collection() if left_table and right_table: if rel_db.contains_table( left_table) and rel_db.contains_table( right_table) and operator == "=": fk = left.get_field() pk = right.get_field() """ It is not a trivial task to check if a collection of equalities given in the where clause defines a graph pattern. This part relies on certain assumptions in the underlaying data transformation. But nothing guarantees that the data transformation follows those assumptions and it does not need to follow them. """ if fk + "_" + pk in self.edge_types: self.from_clause.add_join(left, right) elif pk + "_" + fk in self.edge_types: self.from_clause.add_join(right, left) else: self.filters.append(elem) else: self.filters.append(elem) else: self.filters.append(elem) else: self.filters.append(elem) else: self.filters.append(elem) else: self.filters.append(elem) # Analyze those conditions that do not define graph patterns for elem in self.filters: if "BoolExpr" in elem.keys(): self.sources.append( BooleanExpression(elem["BoolExpr"], self.from_clause, self.cte, self.cte_name)) elif "A_Expr" in elem.keys(): self.sources.append( Where(elem, self.from_clause, self.cte, self.cte_name)) elif "NullTest" in elem.keys(): self.sources.append( Where(elem, self.from_clause, self.cte, self.cte_name))
class AExpression: def __init__(self, left, operator, right, from_clause=None, cte=None, cte_name=None, rename=None): self.left_initial = left self.left = None self.operator = operator self.right_initial = right self.right = None self.col_refer = None self.expr = None self.from_clause = from_clause self.cte = cte self.cte_name = cte_name self.rename = rename if "A_Const" in self.left_initial.keys(): temp_left = self.left_initial["A_Const"]["val"] if "String" in temp_left: self.left = temp_left["String"]["str"] elif "Integer" in temp_left: self.left = temp_left["Integer"]["ival"] elif "Float" in temp_left: self.left = temp_left["Float"]["str"] elif "FuncCall" in self.left_initial.keys(): self.left = FuncCall( self.left_initial["FuncCall"], self.from_clause, self.cte, self.cte_name, self.rename) self.col_refer = self.left.get_col_refer() elif "ColumnRef" in self.left_initial.keys(): self.left = Column( self.left_initial["ColumnRef"], self.from_clause, True, self.cte_name, self.rename) if "A_Const" in self.right_initial.keys(): temp_right = self.right_initial["A_Const"]["val"] if "String" in temp_right: self.right = temp_right["String"]["str"] elif "Integer" in temp_right: self.right = temp_right["Integer"]["ival"] elif "Float" in temp_right: self.right = temp_right["Float"]["str"] elif "FuncCall" in self.right_initial.keys(): self.right = FuncCall( self.right_initial["FuncCall"], self.from_clause, self.cte, self.cte_name, self.rename) self.col_refer = self.right.get_col_refer() elif "ColumnRef" in self.right_initial.keys(): self.right = Column( self.right_initial["ColumnRef"], self.from_clause, True, self.cte_name, self.rename) def transform_into_cypher(self, with_with=True): res = "" if with_with: res += "WITH *, " if self.left and self.right: if type(self.left) == str or type(self.left) == int: res += str(self.left) + " " else: res += self.left.transform_into_cypher(with_with=False) + " " res += self.operator + " " if type(self.right) == str or type(self.right) == int: res += str(self.right) else: res += self.right.transform_into_cypher(with_with=False) if with_with and self.col_refer: res += " AS " + self.col_refer.get_field() return res def get_col_refer(self): return self.col_refer
def __init__(self, func_call, from_clause, cte, cte_name, rename, index=0): self.func_call = func_call self.from_clause = from_clause self.cte = cte self.cte_name = cte_name self.rename = rename self.col_refer = None self.func = None self.index = index self.distinct = False if "agg_distinct" in self.func_call: self.distinct = self.func_call["agg_distinct"] func_name = self.func_call["funcname"][-1]["String"]["str"] if func_name == "count": star = False if "agg_star" in self.func_call: star = self.func_call["agg_star"] if star: self.func = "count(*)" self.col_refer = Column( {"fields": [{ "String": { "str": "c" + str(self.index) } }]}, self.from_clause, self.cte, self.cte_name, rename=self.rename, accept_collection_alias=False) else: for elem in self.func_call["args"]: if "CaseExpr" in elem: from model_transformations.query_transformations.parse_tree_trasformations.case_expression import CaseExpression case_expr = CaseExpression(elem["CaseExpr"], self.from_clause, self.cte, self.cte_name, self.rename) if self.distinct: self.func = "count(distinct " + \ case_expr.transform_into_cypher() + ")" else: self.func = "count( " + \ case_expr.transform_into_cypher() + ")" self.col_refer = Column( { "fields": [{ "String": { "str": "c" + str(self.index) } }] }, self.from_clause, self.cte, self.cte_name, rename=self.rename, accept_collection_alias=False) elif "ColumnRef": col = Column(elem["ColumnRef"], self.from_clause, True, self.cte_name, self.rename) if self.distinct: self.func = "count(distinct " else: self.func = "count(" self.func += col.transform_into_cypher( with_with=False) + ")" self.col_refer = Column( { "fields": [{ "String": { "str": "c" + str(self.index) } }] }, self.from_clause, self.cte, self.cte_name, rename=self.rename, accept_collection_alias=False) else: call = pg_functions_to_neo4j_functions(self.func_call, self.from_clause, self.cte, self.cte_name, self.rename) for elem in call["fields"]: self.func = call["func"]["pre"] + elem.transform_into_cypher( False) + call["func"]["post"] self.col_refer = Column( {"fields": [{ "String": { "str": "func" + str(self.index) } }]}, self.from_clause, self.cte, self.cte_name, self.rename, accept_collection_alias=False)
class Where: """ This class implements only filtering conditional where clauses. Where clauses that create joins between tables are handled in Join class. In practice this means that this class handles all other cases expect those where parse tree has ColumnRef on both left and right side and these ColumnRefs induce a valid edge in the graph schema. /* * A_Expr - infix, prefix, and postfix expressions */ typedef enum A_Expr_Kind { AEXPR_OP, /* 0 normal operator */ AEXPR_OP_ANY, /* 1 scalar op ANY (array) */ AEXPR_OP_ALL, /* 2 scalar op ALL (array) */ AEXPR_DISTINCT, /* 3 IS DISTINCT FROM - name must be "=" */ AEXPR_NOT_DISTINCT, /* 4 IS NOT DISTINCT FROM - name must be "=" */ AEXPR_NULLIF, /* 5 NULLIF - name must be "=" */ AEXPR_IN, /* 6 [NOT] IN - name must be "=" or "<>" */ AEXPR_LIKE, /* 7 [NOT] LIKE - name must be "~~" or "!~~" */ AEXPR_ILIKE, /* 8 [NOT] ILIKE - name must be "~~*" or "!~~*" */ AEXPR_SIMILAR, /* 9 [NOT] SIMILAR - name must be "~" or "!~" */ AEXPR_BETWEEN, /* 10 name must be "BETWEEN" */ AEXPR_NOT_BETWEEN, /* 11 name must be "NOT BETWEEN" */ AEXPR_BETWEEN_SYM, /* 12 name must be "BETWEEN SYMMETRIC" */ AEXPR_NOT_BETWEEN_SYM /* 13 name must be "NOT BETWEEN SYMMETRIC" */ } A_Expr_Kind; """ def __init__(self, where_clause, from_clause, cte=False, cte_name=""): #print(json.dumps(where_clause, indent = 2)) self.where_clause = where_clause self.left = None self.operator = None self.right = None self.joins = [] self.from_clause = from_clause self.cte = cte self.cte_name = cte_name self.type = None self.left_side_typecasted = False self.right_side_typecasted = False self.kind = None if "A_Expr" in self.where_clause.keys(): self.left_side = self.where_clause["A_Expr"]["lexpr"] self.right_side = self.where_clause["A_Expr"]["rexpr"] self.operator = self.where_clause["A_Expr"]["name"][0]["String"]["str"] self.kind = self.where_clause["A_Expr"]["kind"] if self.kind == 0: if type(self.left_side) == dict and type(self.right_side) == dict: if "ColumnRef" in self.left_side.keys() and "ColumnRef" in self.right_side.keys(): self.left = Column( self.left_side["ColumnRef"], self.from_clause, self.cte, self.cte_name) self.right = Column( self.right_side["ColumnRef"], self.from_clause, self.cte, self.cte_name) else: if "ColumnRef" in self.left_side.keys(): self.left = Column( self.left_side["ColumnRef"], self.from_clause, self.cte, self.cte_name) elif "A_Const" in self.left_side.keys(): self.left = self.handle_a_const(self.left_side) elif "TypeCast" in self.left_side.keys(): self.left, self.type = self.handle_typecast( self.left_side) self.left_side_typecasted = True if "ColumnRef" in self.right_side.keys(): self.right = Column( self.right_side["ColumnRef"], self.from_clause, self.cte, self.cte_name) elif "A_Const" in self.right_side.keys(): self.right = self.handle_a_const(self.right_side) elif "TypeCast" in self.right_side.keys(): self.right, self.type = self.handle_typecast( self.right_side) self.right_side_typecasted = True elif self.kind == 7: if type(self.left_side) == dict and type(self.right_side) == list: if "ColumnRef" in self.left_side.keys(): self.left = Column( self.left_side["ColumnRef"], self.from_clause, self.cte, self.cte_name) self.right = [] for elem in self.right_side: if "A_Const" in elem.keys(): self.right.append(self.handle_a_const(elem)) elif "TypeCast" in elem.keys(): res, self.type = self.handle_typecast( self.right_side) self.right.append(res) self.right_side_typecasted = True elif self.kind == 11: if type(self.left_side) == dict and type(self.right_side) == list: if "ColumnRef" in self.left_side.keys(): self.left = Column( self.left_side["ColumnRef"], self.from_clause, self.cte, self.cte_name) self.right = [] for elem in self.right_side: if "A_Const" in elem.keys(): self.right.append(self.handle_a_const(elem)) elif "TypeCast" in elem.keys(): res, self.type = self.handle_typecast(elem) self.right.append(res) self.right_side_typecasted = True elif "NullTest" in self.where_clause.keys(): self.kind = "nulltest" self.left = Column( self.where_clause["NullTest"]["arg"]["ColumnRef"], self.from_clause, self.cte, self.cte_name) self.operator = "IS" self.right = "NULL" def transform_into_cypher(self, add_where=True): res = "" if self.left and self.right: if add_where: res = "WHERE " """ Left side of the single where clause """ if self.kind == 11: if self.operator == "BETWEEN": if self.type == "timestamp": if len(self.right) == 2: """ Strange but Neo4j lacks datetime comparisions. Transforming datetimes into integers and comparing them is the workaround here. """ res += self.right[0] + ".epochMillis " + " <= " + pg_types_to_neo4j_types(self.type, self.left.transform_into_cypher(), "column") + ".epochMillis <= " + self.right[1] +".epochMillis" + "\n" else: if type(self.left) == str: res += '"' + str(self.left) + '"' elif type(self.left) == int: res += str(self.left) else: if self.right_side_typecasted: res += pg_types_to_neo4j_types(self.type, self.left.transform_into_cypher(), "column") + " " else: res += self.left.transform_into_cypher() + " " """ Operator i.e. equality, inequality or other comparision, inclusion etc. Most of the time Postgres and Neo4j have same naming here. """ if self.kind == 7: res += "IN " else: res += self.operator + " " """ Right side of the single where clause """ if type(self.right) == str: res += '"' + str(self.right) + '"' elif type(self.right) == int: res += str(self.right) elif type(self.right) == list: if self.kind == 7: res += "[" for elem in self.right: res += "'" + elem + "'" + ", " res = res[0:-2] + "]" else: if self.left_side_typecasted: res += pg_types_to_neo4j_types(self.type, self.right.transform_into_cypher(), "column") + " " else: res += self.right.transform_into_cypher() return res + "\n" return res + "\n" def get_left(self): return self.left def get_right(self): return self.right def handle_a_const(self, elem): temp = elem["A_Const"]["val"] if "Float" in temp.keys(): return temp["Float"]["str"] elif "String" in temp.keys(): return temp["String"]["str"] elif "Integer" in temp.keys(): return temp["Integer"]["ival"] def handle_typecast(self, elem): value = elem["TypeCast"]["arg"]["A_Const"]["val"]["String"]["str"] type = elem["TypeCast"]["typeName"]["TypeName"]["names"][-1]["String"]["str"] result = pg_types_to_neo4j_types(type, value) return result, type
def __init__(self, where_clause, from_clause, cte=False, cte_name=""): #print(json.dumps(where_clause, indent = 2)) self.where_clause = where_clause self.left = None self.operator = None self.right = None self.joins = [] self.from_clause = from_clause self.cte = cte self.cte_name = cte_name self.type = None self.left_side_typecasted = False self.right_side_typecasted = False self.kind = None if "A_Expr" in self.where_clause.keys(): self.left_side = self.where_clause["A_Expr"]["lexpr"] self.right_side = self.where_clause["A_Expr"]["rexpr"] self.operator = self.where_clause["A_Expr"]["name"][0]["String"]["str"] self.kind = self.where_clause["A_Expr"]["kind"] if self.kind == 0: if type(self.left_side) == dict and type(self.right_side) == dict: if "ColumnRef" in self.left_side.keys() and "ColumnRef" in self.right_side.keys(): self.left = Column( self.left_side["ColumnRef"], self.from_clause, self.cte, self.cte_name) self.right = Column( self.right_side["ColumnRef"], self.from_clause, self.cte, self.cte_name) else: if "ColumnRef" in self.left_side.keys(): self.left = Column( self.left_side["ColumnRef"], self.from_clause, self.cte, self.cte_name) elif "A_Const" in self.left_side.keys(): self.left = self.handle_a_const(self.left_side) elif "TypeCast" in self.left_side.keys(): self.left, self.type = self.handle_typecast( self.left_side) self.left_side_typecasted = True if "ColumnRef" in self.right_side.keys(): self.right = Column( self.right_side["ColumnRef"], self.from_clause, self.cte, self.cte_name) elif "A_Const" in self.right_side.keys(): self.right = self.handle_a_const(self.right_side) elif "TypeCast" in self.right_side.keys(): self.right, self.type = self.handle_typecast( self.right_side) self.right_side_typecasted = True elif self.kind == 7: if type(self.left_side) == dict and type(self.right_side) == list: if "ColumnRef" in self.left_side.keys(): self.left = Column( self.left_side["ColumnRef"], self.from_clause, self.cte, self.cte_name) self.right = [] for elem in self.right_side: if "A_Const" in elem.keys(): self.right.append(self.handle_a_const(elem)) elif "TypeCast" in elem.keys(): res, self.type = self.handle_typecast( self.right_side) self.right.append(res) self.right_side_typecasted = True elif self.kind == 11: if type(self.left_side) == dict and type(self.right_side) == list: if "ColumnRef" in self.left_side.keys(): self.left = Column( self.left_side["ColumnRef"], self.from_clause, self.cte, self.cte_name) self.right = [] for elem in self.right_side: if "A_Const" in elem.keys(): self.right.append(self.handle_a_const(elem)) elif "TypeCast" in elem.keys(): res, self.type = self.handle_typecast(elem) self.right.append(res) self.right_side_typecasted = True elif "NullTest" in self.where_clause.keys(): self.kind = "nulltest" self.left = Column( self.where_clause["NullTest"]["arg"]["ColumnRef"], self.from_clause, self.cte, self.cte_name) self.operator = "IS" self.right = "NULL"