
org.tresql.macro.scala Maven / Gradle / Ivy
The newest version!
package org.tresql
import ast.{Arr, Const, Exp}
import parsing.QueryParsers
import scala.util.Try
package object macro_ {
implicit class TresqlMacroInterpolator(val sc: StringContext) extends AnyVal {
def macro_(args: Exp*)(implicit p: QueryParsers): Exp = {
p.parseExp(sc.standardInterpolator(StringContext.processEscapes, args.map(_.tresql)))
}
}
}
class Macros {
def sql(b: QueryBuilder, const: QueryBuilder#ConstExpr) = b.SQLExpr(String valueOf const.value, Nil)
private def containsVar(b: QueryBuilder, v: QueryBuilder#VarExpr) =
if (v.members != null && v.members.nonEmpty) b.env.contains(v.name, v.members)
else b.env contains v.name
def if_defined(b: QueryBuilder, v: Expr, e: Expr): Expr = v match {
case ve: QueryBuilder#VarExpr => if (containsVar(b, ve)) e else null
case null => null
case _ => e
}
def if_defined_or_else(b: QueryBuilder, v: Expr, e1: Expr, e2: Expr): Expr =
Option(if_defined(b, v, e1)).getOrElse(e2)
def if_missing(b: QueryBuilder, v: Expr, e: Expr): Expr = v match {
case ve: QueryBuilder#VarExpr => if (containsVar(b, ve)) null else e
case null => e
case _ => null
}
def if_all_defined(b: QueryBuilder, e: Expr*): Expr = {
if (e.size < 2) sys.error("if_all_defined macro must have at least two arguments")
val vars = e dropRight 1
val expr = e.last
if (vars forall {
case v: QueryBuilder#VarExpr => containsVar(b, v)
case null => false
case _ => true
}) expr
else null
}
def if_any_defined(b: QueryBuilder, e: Expr*): Expr = {
if (e.size < 2) sys.error("if_any_defined macro must have at least two arguments")
val vars = e dropRight 1
val expr = e.last
if (vars exists {
case v: QueryBuilder#VarExpr => containsVar(b, v)
case null => false
case _ => true
}) expr
else null
}
def if_all_missing(b: QueryBuilder, e: Expr*): Expr = {
if (e.size < 2) sys.error("if_all_missing macro must have at least two arguments")
val vars = e dropRight 1
val expr = e.last
if (vars forall {
case v: QueryBuilder#VarExpr => !containsVar(b, v)
case null => true
case _ => false
}) expr
else null
}
def if_any_missing(b: QueryBuilder, e: Expr*): Expr = {
if (e.size < 2) sys.error("if_any_missing macro must have at least two arguments")
val vars = e dropRight 1
val expr = e.last
if (vars exists {
case v: QueryBuilder#VarExpr => !containsVar(b, v)
case null => true
case _ => false
}) expr
else null
}
def sql_concat(b: QueryBuilder, exprs: Expr*): Expr =
b.SQLConcatExpr(exprs: _*)
def bin_op_function(b: QueryBuilder, op: QueryBuilder#ConstExpr, lop: Expr, rop: Expr): Expr = {
if (lop == null || rop == null) null else {
def ex(o: String) = b.BinExpr(o, b.FunExpr("lower", List(lop)), b.FunExpr("lower", List(rop)))
String.valueOf(op.value) match {
case "~~" => ex("~")
case "!~~" => ex("!~")
case x => b.BinExpr(x, lop, rop)
}
}
}
/** Allows to specify table name as bind variable value.
* Like {{{ []dynamic_table(:table)[deptno = 10]{dname} }}}
* */
def dynamic_table(b: QueryBuilder, table_name: QueryBuilder#VarExpr): b.Table = {
b.Table(b.IdentExpr(List(String.valueOf(table_name()))), null, null, null, false, null)
}
/** Transforms expression passed in an argument as follows:
* 1. Finds array expressions in an argument and checks whether all arrays are equal (otherwise throws assertion error)
* 2. maps all elements in an array found to transformed expression which replaces array found with concrete element
* 3. Returns resulting array expression
*
* Examples:
* {{{map_exprs(parser, parser.parseExp("x || [a, b, c]")).tresql}}} returns
* {{{[x || a, x || b, x || c]}}}
*
* {{{map_exprs(parser, parser.parseExp("fun(x || [a, b, c] || y, [a, b, c])")).tresql}}} returns
* {{{[fun(x || a || y, a), fun(x || b || y, b), fun(x || c || y, c)]}}}
*
* NOTE: function may throw an error array element is transformed into the place where array is required,
* like in Values expresion for example.
* */
def map_exps(p: QueryParsers, exp: Exp): Exp = {
val arrs = p.traverser[List[Arr]] (al => { case a: Arr => a :: al })(Nil)(exp)
def mapArr(exps: List[Exp]) =
exps.map { e => p.transformer { case _: Arr => e }(exp) }
arrs match {
case Nil => exp
case List(Arr(els)) => Arr(mapArr(els))
case Arr(els) :: tail =>
require(tail.forall(els == _.elements),
s"map_exps macro error. All arrays to be mapped in expression ($exp) must be equal!")
Arr(mapArr(els))
}
}
/** Similar to scala {{{list.mkString(start, sep, end)}}} method
* prefix, sep, postfix parameters must be string constants
* Resulting string must be parseable tresql expression
* */
def concat_exps(p: QueryParsers, prefix: Exp, sep: Exp, postfix: Exp, exprs: Exp*): Exp = {
val (prefixStr: String, sepStr: String, postfixStr: String) = (prefix, sep, postfix) match {
case (pf: Const, sp: Const, psf: Const) => (pf.value.toString, sp.value.toString, psf.value.toString)
case _ => new IllegalArgumentException(
s"Prefix, separator, postfix parameters must be string constants, instead found: " +
s"prefix - $prefix, sep - $sep, postfix - $postfix")
}
val exprs_to_concat = exprs match {
case Seq(x: Arr) => x.elements
case x => x
}
val expStr = exprs_to_concat.map(_.tresql).mkString(prefixStr, sepStr, postfixStr)
Try(p.parseExp(expStr)).recover {
case e: Exception => throw new IllegalArgumentException("concat_exps macro produced invalid tresql", e)
}.get
}
/**
*
* Below ORT functionality macros
*
* */
def _lookup_upsert(b: ORT,
objProp: QueryBuilder#ConstExpr,
idProp: QueryBuilder#ConstExpr,
lookupUpsertExpr: Expr,
idSelExpr: Expr) =
b.LookupUpsertExpr(
String valueOf objProp.value,
String valueOf idProp.value,
lookupUpsertExpr,
idSelExpr
)
def _upsert(b: ORT, updateExpr: Expr, insertExpr: Expr) = b.UpsertExpr(updateExpr, insertExpr)
def _delete_missing_children(b: ORT,
objName: QueryBuilder#ConstExpr,
key: QueryBuilder#ArrExpr,
keyValExprs: QueryBuilder#ArrExpr,
deleteExpr: Expr) =
b.DeleteMissingChildrenExpr(
String valueOf objName.value,
key.elements.collect {
case b.IdentExpr(n) => n.mkString(".")
case x => sys.error(s"Unrecognized key type - $x")
},
keyValExprs.elements,
deleteExpr)
def _not_delete_keys(b: ORT, key: QueryBuilder#ArrExpr, keyValExprs: QueryBuilder#ArrExpr) =
b.NotDeleteKeysExpr(key.elements, keyValExprs.elements)
def _id_ref_id(b: ORT,
idRef: QueryBuilder#IdentExpr,
id: QueryBuilder#IdentExpr) =
b.IdRefIdExpr(idRef.name.mkString("."), id.name.mkString("."))
def _id_by_key(b: ORT, idExpr: Expr) = b.IdByKeyExpr(idExpr)
def _update_by_key(b: ORT, table: QueryBuilder#IdentExpr, setIdExpr: Expr, updateExpr: Expr) = {
b.UpdateByKeyExpr(table.name.mkString("."), setIdExpr, updateExpr)
}
def _deferred_build(b: ORT, exp: Exp) = {
b.DeferredBuildExpr(exp)
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy