def partial_eval(self): lhs = self.lhs.partial_eval() rhs = self.rhs.partial_eval() default = self.default.partial_eval() if is_literal(lhs) and is_literal(rhs): return Literal(builtin_ops[self.op](lhs.value, rhs.value)) return self.__class__([lhs, rhs], default=default)
def to_bq(self, schema, not_null=False, boolean=False): lhs = BQLang[self.lhs].partial_eval() rhs = BQLang[self.rhs].partial_eval() lhs_sql = lhs.to_bq(schema, not_null=True) rhs_sql = rhs.to_bq(schema, not_null=True) if is_literal(rhs) and lhs_sql[0].sql.b != None and rhs.value in ("T", "F"): rhs_sql = BooleanOp(rhs).to_bq(schema) if is_literal(lhs) and rhs_sql[0].sql.b != None and lhs.value in ("T", "F"): lhs_sql = BooleanOp(lhs).to_bq(schema) if len(lhs_sql) != len(rhs_sql): Log.error("lhs and rhs have different dimensionality!?") acc = [] for l, r in zip(lhs_sql, rhs_sql): for t in "bsnj": if r.sql[t] == None: if l.sql[t] == None: pass else: acc.append(ConcatSQL(l.sql[t], SQL_IS_NULL)) elif l.sql[t] == None: acc.append(ConcatSQL(r.sql[t], SQL_IS_NULL)) else: acc.append( ConcatSQL(sql_iso(l.sql[t]), SQL_EQ, sql_iso(r.sql[t]))) if not acc: return FALSE.to_bq(schema) else: return wrap([{"name": ".", "sql": {"b": JoinSQL(SQL_OR, acc)}}])
def partial_eval(self, lang): lhs = self.lhs.partial_eval(lang) rhs = self.rhs.partial_eval(lang) if is_literal(lhs) and is_literal(rhs): return Literal(builtin_ops[self.op](lhs, rhs)) return self.__class__([lhs, rhs])
def partial_eval(self, lang): default = self.default.partial_eval(lang) rhs = self.rhs.partial_eval(lang) if rhs is ZERO: return default lhs = self.lhs.partial_eval(lang) if is_literal(lhs) and is_literal(rhs): return Literal(builtin_ops[self.op](lhs.value, rhs.value)) return self.__class__([lhs, rhs], default=default)
def __data__(self): if is_literal(self.var) and len(self.offsets) == 1 and is_literal( self.offset): return {"get": {self.var.json, self.offsets[0].value}} else: return { "get": [self.var.__data__()] + [o.__data__() for o in self.offsets] }
def partial_eval(self): value = self.value.partial_eval() superset = self.superset.partial_eval() if superset is NULL: return FALSE elif is_literal(value) and is_literal(superset): return self.lang[Literal(self())] else: return self.lang[InOp([value, superset])]
def partial_eval(self, lang): lhs = self.lhs.partial_eval(lang) rhs = self.rhs.partial_eval(lang) default = self.default.partial_eval(lang) if is_literal(lhs) and is_literal(rhs): if lhs is NULL or rhs is NULL: return NULL return Literal(builtin_ops[self.op](lhs.value, rhs.value)) return self.__class__([lhs, rhs], default=default)
def __init__(self, value, prefix, suffix, default=NULL, start=NULL): Expression.__init__(self, []) self.value = value self.prefix = prefix self.suffix = suffix self.default = default self.start = start if is_literal(self.prefix) and is_literal(self.suffix): pass else: Log.error("Expecting literal prefix and suffix only")
def __init__(self, value, prefix, suffix, default=NULL, start=NULL): Expression.__init__(self, []) self.value = value self.prefix = coalesce(prefix, NULL) self.suffix = coalesce(suffix, NULL) self.default = coalesce(default, NULL) self.start = coalesce(start, NULL) if is_literal(self.prefix) and is_literal(self.suffix): pass else: Log.error("Expecting literal prefix and suffix only")
def partial_eval(self): lhs = self.lang[self.lhs].partial_eval() rhs = self.lang[self.rhs].partial_eval() if is_literal(lhs) and is_literal(rhs): return FALSE if value_compare(lhs.value, rhs.value) else TRUE else: return self.lang[self.lang[CaseOp([ WhenOp(lhs.missing(), **{"then": rhs.missing()}), WhenOp(rhs.missing(), **{"then": FALSE}), BasicEqOp([lhs, rhs]), ])]].partial_eval()
def partial_eval(self, lang): lhs = (self.lhs).partial_eval(lang) rhs = (self.rhs).partial_eval(lang) if is_literal(lhs) and is_literal(rhs): return FALSE if value_compare(lhs.value, rhs.value) else TRUE else: return CaseOp([ WhenOp(lhs.missing(lang), **{"then": rhs.missing(lang)}), WhenOp(rhs.missing(lang), **{"then": FALSE}), BasicEqOp([lhs, rhs]), ]).partial_eval(lang)
def partial_eval(self, lang): value = self.value.partial_eval(lang) superset = self.superset.partial_eval(lang) if superset is NULL: return FALSE elif value is NULL: return NULL elif is_literal(value) and is_literal(superset): return Literal(value() in superset()) elif is_op(value, NestedOp): return NestedOp(value.path, None, AndOp([BasicInOp([value.select, superset]), value.where])).exists().partial_eval(lang) else: return lang.BasicInOp([value, superset])
def __data__(self): if not self.expr: return {"prefix": {}} elif is_op(self.expr, Variable) and is_literal(self.prefix): return {"prefix": {self.expr.var: self.prefix.value}} else: return {"prefix": [self.expr.__data__(), self.prefix.__data__()]}
def partial_eval(self): term = self.lang[FirstOp(self.term)].partial_eval() if is_literal(term): if term is NULL: return NULL elif term is FALSE: return ZERO elif term is TRUE: return ONE v = term.value if isinstance(v, (text, Date)): return self.lang[Literal(float(v))] elif isinstance(v, (int, float)): return term else: Log.error("can not convert {{value|json}} to number", value=term.value) elif is_op(term, CaseOp): # REWRITING return self.lang[CaseOp([ WhenOp(t.when, **{"then": NumberOp(t.then)}) for t in term.whens[:-1] ] + [NumberOp(term.whens[-1])])].partial_eval() elif is_op(term, WhenOp): # REWRITING return self.lang[WhenOp( term.when, **{ "then": NumberOp(term.then), "else": NumberOp(term.els_) })].partial_eval() elif is_op(term, CoalesceOp): return self.lang[CoalesceOp([NumberOp(t) for t in term.terms])] return self.lang[NumberOp(term)]
def __data__(self): if self.expr is None: return {"suffix": {}} elif is_op(self.expr, Variable) and is_literal(self.suffix): return {"suffix": {self.expr.var: self.suffix.value}} else: return {"suffix": [self.expr.__data__(), self.suffix.__data__()]}
def partial_eval(self): term = self.lang[self.term].partial_eval() if is_op(term, FirstOp): return term elif is_op(term, CaseOp): # REWRITING return self.lang[ CaseOp( [ WhenOp(t.when, **{"then": FirstOp(t.then)}) for t in term.whens[:-1] ] + [FirstOp(term.whens[-1])] ) ].partial_eval() elif is_op(term, WhenOp): return self.lang[ WhenOp( term.when, **{"then": FirstOp(term.then), "else": FirstOp(term.els_)} ) ].partial_eval() elif term.type != OBJECT and not term.many: return term elif is_literal(term): Log.error("not handled yet") else: return self.lang[FirstOp(term)]
def __data__(self): if is_op(self.value, Variable) and is_literal(self.length): return {"not_right": {self.value.var: self.length.value}} else: return { "not_right": [self.value.__data__(), self.length.__data__()] }
def __data__(self): if is_op(self.lhs, Variable) and is_literal(self.rhs): return {self.op: {self.lhs.var, self.rhs.value}, "default": self.default} else: return { self.op: [self.lhs.__data__(), self.rhs.__data__()], "default": self.default, }
def __init__(self, terms, separator=Literal(""), default=NULL): if not is_many(terms): Log.error("Expecting many terms") if not is_literal(separator): Log.error("Expecting a literal separator") Expression.__init__(self, terms + [separator, default]) self.terms = terms self.separator = separator self.default = default
def __data__(self): f, s = self.terms[0], self.terms[1] if is_op(f, Variable) and is_literal(s): output = {"concat": {f.var: s.value}} else: output = {"concat": [t.__data__() for t in self.terms]} if self.separator.json != '""': output["separator"] = self.separator.__data__() return output
def partial_eval(self, lang): term = self.term.partial_eval(lang) if is_literal(term): if is_text(term.value): return (Literal(len(term.value))) else: return NULL else: return (LengthOp(term))
def partial_eval(self): term = self.lang[self.term].partial_eval() if is_literal(term): if is_text(term.value): return self.lang[Literal(len(term.value))] else: return NULL else: return self.lang[LengthOp(term)]
def __init__(self, term): Expression.__init__(self, term) if is_data(term): self.value, self.length = term.items()[0] else: self.value, self.length = term if is_literal(self.value): Log.note("")
def __init__(self, terms, **clauses): Expression.__init__(self, terms) if is_data(terms): self.terms = first(terms.items()) else: self.terms = terms self.separator = clauses.get(str("separator"), Literal("")) self.default = clauses.get(str("default"), NULL) if not is_literal(self.separator): Log.error("Expecting a literal separator")
def partial_eval(self): term = self.term.partial_eval() if is_op(self.term, LastOp): return term elif term.type != OBJECT and not term.many: return term elif term is NULL: return term elif is_literal(term): return last(term) else: return self.lang[LastOp(term)]
def partial_eval(self): acc = None terms = [] for t in self.terms: simple = t.partial_eval() if simple is NULL: pass elif is_literal(simple): if acc is None: acc = simple.value else: acc = builtin_ops[self.op](acc, simple.value) else: terms.append(simple) lang = self.lang if len(terms) == 0: if acc == None: return self.default.partial_eval() else: return lang[Literal(acc)] elif self.nulls: # DECISIVE if acc is not None: terms.append(Literal(acc)) output = lang[ WhenOp( AndOp([t.missing() for t in terms]), **{ "then": self.default, "else": operators["basic." + self.op]( [CoalesceOp([t, _jx_identity[self.op]]) for t in terms] ), } ) ].partial_eval() else: # CONSERVATIVE if acc is not None: terms.append(lang[Literal(acc)]) output = lang[ WhenOp( lang[OrOp([t.missing() for t in terms])], **{ "then": self.default, "else": operators["basic." + self.op](terms), } ) ].partial_eval() return output
def __data__(self): terms = self.terms if len(terms) == 0: return self.default.__data__() if len(terms) == 2 and is_op(terms[0], Variable) and is_literal( terms[1]): output = {"concat": {terms[0].var: terms[1].value}} else: output = {"concat": [t.__data__() for t in terms]} if self.separator.json != '""': output["separator"] = self.separator.__data__() return output
def __data__(self): if (is_op(self.value, Variable) and is_literal(self.prefix) and is_literal(self.suffix)): output = wrap({ "between": { self.value.var: [self.prefix.value, self.suffix.value] } }) else: output = wrap({ "between": [ self.value.__data__(), self.prefix.__data__(), self.suffix.__data__(), ] }) if self.start: output.start = self.start.__data__() if self.default: output.default = self.default.__data__() return output
def to_bq(self, schema, not_null=False, boolean=False): term = BQLang[self.term].partial_eval() if is_literal(term): val = term.value if isinstance(val, text): sql = quote_value(len(val)) elif isinstance(val, (float, int)): sql = quote_value(len(value2json(val))) else: return Null else: value = term.to_bq(schema, not_null=not_null)[0].sql.s sql = ConcatSQL(SQL("LENGTH"), sql_iso(value)) return wrap([{"name": ".", "sql": {"n": sql}}])
def inverse(term): if term is TRUE: return FALSE elif term is FALSE: return TRUE elif term is NULL: return TRUE elif is_literal(term): Log.error("`not` operator expects a Boolean term") elif is_op(term, WhenOp): output = self.lang[ WhenOp( term.when, **{"then": inverse(term.then), "else": inverse(term.els_)} ) ].partial_eval() elif is_op(term, CaseOp): # REWRITING output = self.lang[ CaseOp( [ WhenOp(w.when, **{"then": inverse(w.then)}) if is_op(w, WhenOp) else inverse(w) for w in term.whens ] ) ].partial_eval() elif is_op(term, AndOp): output = self.lang[ OrOp([inverse(t) for t in term.terms]) ].partial_eval() elif is_op(term, OrOp): output = self.lang[ AndOp([inverse(t) for t in term.terms]) ].partial_eval() elif is_op(term, MissingOp): output = self.lang[NotOp(term.expr.missing())] elif is_op(term, ExistsOp): output = term.field.missing().partial_eval() elif is_op(term, NotOp): output = self.lang[term.term].partial_eval() elif is_op(term, NeOp): output = self.lang[EqOp([term.lhs, term.rhs])].partial_eval() elif is_op(term, BasicIndexOfOp) or is_op(term, BasicSubstringOp): return FALSE else: output = self.lang[NotOp(term)] return output