Simplify Mirror Codec
This commit is contained in:
parent
1bc6baad68
commit
17e97495b7
17 changed files with 275 additions and 112 deletions
|
@ -1,7 +1,14 @@
|
|||
name := "minisql"
|
||||
|
||||
scalaVersion := "3.7.0"
|
||||
scalaVersion := "3.7.1"
|
||||
|
||||
libraryDependencies ++= Seq(
|
||||
"org.scalameta" %% "munit" % "1.0.3" % Test
|
||||
)
|
||||
|
||||
scalacOptions ++= Seq(
|
||||
"-deprecation",
|
||||
"-feature",
|
||||
"-source:3.7-migration",
|
||||
"-rewrite"
|
||||
)
|
||||
|
|
|
@ -1,14 +1,23 @@
|
|||
package minisql
|
||||
|
||||
import minisql.context.mirror.*
|
||||
import minisql.util.Messages.fail
|
||||
import scala.reflect.ClassTag
|
||||
|
||||
class MirrorContext[Idiom <: idiom.Idiom, Naming <: NamingStrategy](
|
||||
val idiom: Idiom,
|
||||
val naming: Naming
|
||||
) extends context.Context[Idiom, Naming] {
|
||||
) extends context.Context[Idiom, Naming]
|
||||
with MirrorCodecs {
|
||||
|
||||
type DBRow = Row
|
||||
type DBRow = IArray[Any] *: EmptyTuple
|
||||
type DBResultSet = Iterable[DBRow]
|
||||
type DBStatement = Map[Int, Any]
|
||||
|
||||
type DBResultSet = ResultSet
|
||||
extension (r: DBRow) {
|
||||
|
||||
def data: IArray[Any] = r._1
|
||||
def add(value: Any): DBRow = (r.data :+ value) *: EmptyTuple
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,59 +1,31 @@
|
|||
package minisql.context.mirror
|
||||
|
||||
import minisql.{MirrorContext, NamingStrategy, ParamEncoder, ColumnDecoder}
|
||||
import minisql.idiom.Idiom
|
||||
import minisql.MirrorContext
|
||||
import java.time.LocalDate
|
||||
import java.util.{Date, UUID}
|
||||
import minisql.{ParamEncoder, ColumnDecoder}
|
||||
import minisql.util.Messages.fail
|
||||
import scala.util.{Failure, Success, Try}
|
||||
import scala.util.Try
|
||||
import scala.reflect.ClassTag
|
||||
|
||||
/**
|
||||
* No extra class defined
|
||||
*/
|
||||
opaque type Row = IArray[Any] *: EmptyTuple
|
||||
opaque type ResultSet = Iterable[Row]
|
||||
opaque type Statement = Map[Int, Any]
|
||||
|
||||
extension (r: Row) {
|
||||
|
||||
def data: IArray[Any] = r._1
|
||||
|
||||
def add(value: Any): Row = (r.data :+ value) *: EmptyTuple
|
||||
|
||||
def apply[T](idx: Int)(using t: ClassTag[T]): T = {
|
||||
r.data(idx) match {
|
||||
case v: T => v
|
||||
case other =>
|
||||
fail(
|
||||
s"Invalid column type. Expected '${t.runtimeClass}', but got '$other'"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type Encoder[E] = ParamEncoder[E] {
|
||||
type Stmt = Statement
|
||||
}
|
||||
|
||||
private def encoder[V]: Encoder[V] = new ParamEncoder[V] {
|
||||
|
||||
type Stmt = Map[Int, Any]
|
||||
trait MirrorCodecs {
|
||||
ctx: MirrorContext[?, ?] =>
|
||||
|
||||
final protected def mirrorEncoder[V]: Encoder[V] = new ParamEncoder[V] {
|
||||
type Stmt = ctx.DBStatement
|
||||
def setParam(s: Stmt, idx: Int, v: V): Stmt = {
|
||||
s + (idx -> v)
|
||||
}
|
||||
}
|
||||
|
||||
given Encoder[Long] = encoder[Long]
|
||||
|
||||
type Decoder[A] = ColumnDecoder[A] {
|
||||
type DBRow = Row
|
||||
}
|
||||
|
||||
private def apply[X](conv: Any => Option[X]): Decoder[X] =
|
||||
final protected def mirrorColumnDecoder[X](
|
||||
conv: Any => Option[X]
|
||||
): Decoder[X] =
|
||||
new ColumnDecoder[X] {
|
||||
type DBRow = Row
|
||||
def decode(row: Row, idx: Int): Try[X] = {
|
||||
row._1
|
||||
type DBRow = ctx.DBRow
|
||||
def decode(row: DBRow, idx: Int): Try[X] = {
|
||||
row.data
|
||||
.lift(idx)
|
||||
.flatMap { x =>
|
||||
conv(x)
|
||||
|
@ -63,9 +35,105 @@ private def apply[X](conv: Any => Option[X]): Decoder[X] =
|
|||
}
|
||||
}
|
||||
|
||||
given Decoder[Long] = apply(x =>
|
||||
given optionDecoder[T](using d: Decoder[T]): Decoder[Option[T]] = {
|
||||
new ColumnDecoder[Option[T]] {
|
||||
type DBRow = ctx.DBRow
|
||||
override def decode(row: DBRow, idx: Int): Try[Option[T]] =
|
||||
row.data.lift(idx) match {
|
||||
case Some(null) => Success(None)
|
||||
case Some(value) => d.decode(row, idx).map(Some(_))
|
||||
case None => Success(None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
given optionEncoder[T](using e: Encoder[T]): Encoder[Option[T]] =
|
||||
new ParamEncoder[Option[T]] {
|
||||
type Stmt = ctx.DBStatement
|
||||
override def setParam(
|
||||
s: Stmt,
|
||||
idx: Int,
|
||||
v: Option[T]
|
||||
): Stmt =
|
||||
v match {
|
||||
case Some(value) => e.setParam(s, idx, value)
|
||||
case None =>
|
||||
s + (idx -> null)
|
||||
}
|
||||
}
|
||||
|
||||
// Implement all required decoders using mirrorColumnDecoder from MirrorCodecs
|
||||
given stringDecoder: Decoder[String] = mirrorColumnDecoder[String](x =>
|
||||
x match { case s: String => Some(s); case _ => None }
|
||||
)
|
||||
given bigDecimalDecoder: Decoder[BigDecimal] =
|
||||
mirrorColumnDecoder[BigDecimal](x =>
|
||||
x match {
|
||||
case l: Long => Some(l)
|
||||
case bd: BigDecimal => Some(bd); case i: Int => Some(BigDecimal(i));
|
||||
case l: Long => Some(BigDecimal(l));
|
||||
case d: Double => Some(BigDecimal(d)); case _ => None
|
||||
}
|
||||
)
|
||||
given booleanDecoder: Decoder[Boolean] = mirrorColumnDecoder[Boolean](x =>
|
||||
x match { case b: Boolean => Some(b); case _ => None }
|
||||
)
|
||||
given byteDecoder: Decoder[Byte] = mirrorColumnDecoder[Byte](x =>
|
||||
x match {
|
||||
case b: Byte => Some(b); case i: Int => Some(i.toByte); case _ => None
|
||||
}
|
||||
)
|
||||
given shortDecoder: Decoder[Short] = mirrorColumnDecoder[Short](x =>
|
||||
x match {
|
||||
case s: Short => Some(s); case i: Int => Some(i.toShort); case _ => None
|
||||
}
|
||||
)
|
||||
given intDecoder: Decoder[Int] = mirrorColumnDecoder[Int](x =>
|
||||
x match { case i: Int => Some(i); case _ => None }
|
||||
)
|
||||
given longDecoder: Decoder[Long] = mirrorColumnDecoder[Long](x =>
|
||||
x match {
|
||||
case l: Long => Some(l); case i: Int => Some(i.toLong); case _ => None
|
||||
}
|
||||
)
|
||||
given floatDecoder: Decoder[Float] = mirrorColumnDecoder[Float](x =>
|
||||
x match {
|
||||
case f: Float => Some(f); case d: Double => Some(d.toFloat);
|
||||
case _ => None
|
||||
}
|
||||
)
|
||||
given doubleDecoder: Decoder[Double] = mirrorColumnDecoder[Double](x =>
|
||||
x match {
|
||||
case d: Double => Some(d); case f: Float => Some(f.toDouble);
|
||||
case _ => None
|
||||
}
|
||||
)
|
||||
given byteArrayDecoder: Decoder[Array[Byte]] =
|
||||
mirrorColumnDecoder[Array[Byte]](x =>
|
||||
x match { case ba: Array[Byte] => Some(ba); case _ => None }
|
||||
)
|
||||
given dateDecoder: Decoder[Date] = mirrorColumnDecoder[Date](x =>
|
||||
x match { case d: Date => Some(d); case _ => None }
|
||||
)
|
||||
given localDateDecoder: Decoder[LocalDate] =
|
||||
mirrorColumnDecoder[LocalDate](x =>
|
||||
x match { case ld: LocalDate => Some(ld); case _ => None }
|
||||
)
|
||||
given uuidDecoder: Decoder[UUID] = mirrorColumnDecoder[UUID](x =>
|
||||
x match { case uuid: UUID => Some(uuid); case _ => None }
|
||||
)
|
||||
|
||||
// Implement all required encoders using mirrorEncoder from MirrorCodecs
|
||||
given stringEncoder: Encoder[String] = mirrorEncoder[String]
|
||||
given bigDecimalEncoder: Encoder[BigDecimal] = mirrorEncoder[BigDecimal]
|
||||
given booleanEncoder: Encoder[Boolean] = mirrorEncoder[Boolean]
|
||||
given byteEncoder: Encoder[Byte] = mirrorEncoder[Byte]
|
||||
given shortEncoder: Encoder[Short] = mirrorEncoder[Short]
|
||||
given intEncoder: Encoder[Int] = mirrorEncoder[Int]
|
||||
given longEncoder: Encoder[Long] = mirrorEncoder[Long]
|
||||
given floatEncoder: Encoder[Float] = mirrorEncoder[Float]
|
||||
given doubleEncoder: Encoder[Double] = mirrorEncoder[Double]
|
||||
given byteArrayEncoder: Encoder[Array[Byte]] = mirrorEncoder[Array[Byte]]
|
||||
given dateEncoder: Encoder[Date] = mirrorEncoder[Date]
|
||||
given localDateEncoder: Encoder[LocalDate] = mirrorEncoder[LocalDate]
|
||||
given uuidEncoder: Encoder[UUID] = mirrorEncoder[UUID]
|
||||
}
|
||||
|
|
24
src/main/scala/minisql/context/sql/MirrorSqlContext.scala
Normal file
24
src/main/scala/minisql/context/sql/MirrorSqlContext.scala
Normal file
|
@ -0,0 +1,24 @@
|
|||
package minisql.context.sql
|
||||
|
||||
import minisql.{NamingStrategy, MirrorContext}
|
||||
import minisql.context.Context
|
||||
import minisql.idiom.Idiom // Changed from minisql.idiom.* to avoid ambiguity with Statement
|
||||
import minisql.context.mirror.MirrorCodecs
|
||||
import minisql.context.ReturningClauseSupported
|
||||
import minisql.context.ReturningCapability
|
||||
|
||||
class MirrorSqlIdiom extends idiom.SqlIdiom {
|
||||
override def concatFunction: String = "CONCAT"
|
||||
override def idiomReturningCapability: ReturningCapability =
|
||||
ReturningClauseSupported
|
||||
|
||||
// Implementations previously provided by MirrorIdiomBase
|
||||
override def prepareForProbing(string: String): String = string
|
||||
override def liftingPlaceholder(index: Int): String = "?"
|
||||
}
|
||||
object MirrorSqlIdiom extends MirrorSqlIdiom
|
||||
|
||||
class MirrorSqlContext[N <: NamingStrategy](naming: N)
|
||||
extends MirrorContext[MirrorSqlIdiom, N](MirrorSqlIdiom, naming)
|
||||
with SqlContext[MirrorSqlIdiom, N]
|
||||
with MirrorCodecs {}
|
|
@ -1,4 +1,4 @@
|
|||
package minisql
|
||||
package minisql.context.sql
|
||||
|
||||
import minisql.context.{
|
||||
CanReturnClause,
|
|
@ -20,13 +20,13 @@ trait OnConflictSupport {
|
|||
}
|
||||
|
||||
val customAstTokenizer =
|
||||
Tokenizer.withFallback[Ast](self.astTokenizer(_, strategy)) {
|
||||
Tokenizer.withFallback[Ast](self.astTokenizer(using _, strategy)) {
|
||||
case _: OnConflict.Excluded => stmt"EXCLUDED"
|
||||
case OnConflict.Existing(a) => stmt"${a.token}"
|
||||
case a: Action =>
|
||||
self
|
||||
.actionTokenizer(customEntityTokenizer)(
|
||||
actionAstTokenizer,
|
||||
using actionAstTokenizer,
|
||||
strategy
|
||||
)
|
||||
.token(a)
|
||||
|
@ -37,7 +37,7 @@ trait OnConflictSupport {
|
|||
def doUpdateStmt(i: Token, t: Token, u: Update) = {
|
||||
val assignments = u.assignments
|
||||
.map(a =>
|
||||
stmt"${actionAstTokenizer.token(a.property)} = ${scopedTokenizer(a.value)(customAstTokenizer)}"
|
||||
stmt"${actionAstTokenizer.token(a.property)} = ${scopedTokenizer(a.value)(using customAstTokenizer)}"
|
||||
)
|
||||
.mkStmt()
|
||||
|
||||
|
@ -65,6 +65,6 @@ trait OnConflictSupport {
|
|||
case OnConflict(i, p: Properties, Ignore) => doNothingStmt(i, p.token)
|
||||
}
|
||||
|
||||
tokenizer(customAstTokenizer)
|
||||
tokenizer(using customAstTokenizer)
|
||||
}
|
||||
}
|
||||
|
|
44
src/main/scala/minisql/context/sql/SqlContext.scala
Normal file
44
src/main/scala/minisql/context/sql/SqlContext.scala
Normal file
|
@ -0,0 +1,44 @@
|
|||
package minisql.context.sql
|
||||
|
||||
import java.time.LocalDate
|
||||
|
||||
import minisql.idiom.{Idiom => BaseIdiom}
|
||||
import java.util.{Date, UUID}
|
||||
|
||||
import minisql.context.Context
|
||||
import minisql.NamingStrategy
|
||||
|
||||
trait SqlContext[Idiom <: BaseIdiom, Naming <: NamingStrategy]
|
||||
extends Context[Idiom, Naming] {
|
||||
|
||||
given optionDecoder[T](using d: Decoder[T]): Decoder[Option[T]]
|
||||
given optionEncoder[T](using d: Encoder[T]): Encoder[Option[T]]
|
||||
|
||||
given stringDecoder: Decoder[String]
|
||||
given bigDecimalDecoder: Decoder[BigDecimal]
|
||||
given booleanDecoder: Decoder[Boolean]
|
||||
given byteDecoder: Decoder[Byte]
|
||||
given shortDecoder: Decoder[Short]
|
||||
given intDecoder: Decoder[Int]
|
||||
given longDecoder: Decoder[Long]
|
||||
given floatDecoder: Decoder[Float]
|
||||
given doubleDecoder: Decoder[Double]
|
||||
given byteArrayDecoder: Decoder[Array[Byte]]
|
||||
given dateDecoder: Decoder[Date]
|
||||
given localDateDecoder: Decoder[LocalDate]
|
||||
given uuidDecoder: Decoder[UUID]
|
||||
|
||||
given stringEncoder: Encoder[String]
|
||||
given bigDecimalEncoder: Encoder[BigDecimal]
|
||||
given booleanEncoder: Encoder[Boolean]
|
||||
given byteEncoder: Encoder[Byte]
|
||||
given shortEncoder: Encoder[Short]
|
||||
given intEncoder: Encoder[Int]
|
||||
given longEncoder: Encoder[Long]
|
||||
given floatEncoder: Encoder[Float]
|
||||
given doubleEncoder: Encoder[Double]
|
||||
given byteArrayEncoder: Encoder[Array[Byte]]
|
||||
given dateEncoder: Encoder[Date]
|
||||
given localDateEncoder: Encoder[LocalDate]
|
||||
given uuidEncoder: Encoder[UUID]
|
||||
}
|
|
@ -26,9 +26,7 @@ trait SqlIdiom extends Idiom {
|
|||
|
||||
protected def concatBehavior: ConcatBehavior = AnsiConcat
|
||||
protected def equalityBehavior: EqualityBehavior = AnsiEquality
|
||||
|
||||
protected def actionAlias: Option[Ident] = None
|
||||
|
||||
override def format(queryString: String): String = queryString
|
||||
|
||||
def querifyAst(ast: Ast) = SqlQuery(ast)
|
||||
|
@ -67,7 +65,7 @@ trait SqlIdiom extends Idiom {
|
|||
|
||||
def defaultTokenizer(implicit naming: NamingStrategy): Tokenizer[Ast] =
|
||||
new Tokenizer[Ast] {
|
||||
private val stableTokenizer = astTokenizer(this, naming)
|
||||
private val stableTokenizer = astTokenizer(using this, naming)
|
||||
|
||||
extension (v: Ast) {
|
||||
def token = stableTokenizer.token(v)
|
||||
|
@ -249,7 +247,9 @@ trait SqlIdiom extends Idiom {
|
|||
}
|
||||
|
||||
val customAstTokenizer =
|
||||
Tokenizer.withFallback[Ast](SqlIdiom.this.astTokenizer(_, strategy)) {
|
||||
Tokenizer.withFallback[Ast](
|
||||
SqlIdiom.this.astTokenizer(using _, strategy)
|
||||
) {
|
||||
case Aggregation(op, Ident(_) | Tuple(_)) => stmt"${op.token}(*)"
|
||||
case Aggregation(op, Distinct(ast)) =>
|
||||
stmt"${op.token}(DISTINCT ${ast.token})"
|
||||
|
@ -257,7 +257,7 @@ trait SqlIdiom extends Idiom {
|
|||
case Aggregation(op, ast) => stmt"${op.token}(${ast.token})"
|
||||
}
|
||||
|
||||
tokenizer(customAstTokenizer)
|
||||
tokenizer(using customAstTokenizer)
|
||||
}
|
||||
|
||||
implicit def operationTokenizer(implicit
|
||||
|
@ -528,14 +528,14 @@ trait SqlIdiom extends Idiom {
|
|||
case Entity.Opinionated(name, _, renameable) =>
|
||||
stmt"INTO ${tokenizeTable(strategy, name, renameable).token}"
|
||||
}
|
||||
actionTokenizer(insertEntityTokenizer)(actionAstTokenizer, strategy)
|
||||
actionTokenizer(insertEntityTokenizer)(using actionAstTokenizer, strategy)
|
||||
}
|
||||
|
||||
protected def actionAstTokenizer(implicit
|
||||
astTokenizer: Tokenizer[Ast],
|
||||
strategy: NamingStrategy
|
||||
) =
|
||||
Tokenizer.withFallback[Ast](SqlIdiom.this.astTokenizer(_, strategy)) {
|
||||
Tokenizer.withFallback[Ast](SqlIdiom.this.astTokenizer(using _, strategy)) {
|
||||
case q: Query => astTokenizer.token(q)
|
||||
case Property(Property.Opinionated(_, name, renameable, _), "isEmpty") =>
|
||||
stmt"${renameable.fixedOr(name)(tokenizeColumn(strategy, name, renameable)).token} IS NULL"
|
||||
|
@ -557,14 +557,16 @@ trait SqlIdiom extends Idiom {
|
|||
strategy: NamingStrategy
|
||||
): Tokenizer[List[Ast]] = {
|
||||
val customAstTokenizer =
|
||||
Tokenizer.withFallback[Ast](SqlIdiom.this.astTokenizer(_, strategy)) {
|
||||
Tokenizer.withFallback[Ast](
|
||||
SqlIdiom.this.astTokenizer(using _, strategy)
|
||||
) {
|
||||
case sq: Query =>
|
||||
stmt"(${tokenizer.token(sq)})"
|
||||
}
|
||||
|
||||
Tokenizer[List[Ast]] {
|
||||
case list =>
|
||||
list.mkStmt(", ")(customAstTokenizer)
|
||||
list.mkStmt(", ")(using customAstTokenizer)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -653,7 +655,7 @@ object SqlIdiom {
|
|||
private[minisql] def copyIdiom(
|
||||
parent: SqlIdiom,
|
||||
newActionAlias: Option[Ident]
|
||||
) =
|
||||
): SqlIdiom =
|
||||
new SqlIdiom {
|
||||
override protected def actionAlias: Option[Ident] = newActionAlias
|
||||
override def prepareForProbing(string: String): String =
|
||||
|
@ -678,10 +680,10 @@ object SqlIdiom {
|
|||
val idiom = copyIdiom(parentIdiom, Some(query.alias))
|
||||
import idiom._
|
||||
|
||||
implicit val stableTokenizer: Tokenizer[Ast] = idiom.astTokenizer(
|
||||
implicit val stableTokenizer: Tokenizer[Ast] = idiom.astTokenizer(using
|
||||
new Tokenizer[Ast] { self =>
|
||||
extension (v: Ast) {
|
||||
def token = astTokenizer(self, strategy).token(v)
|
||||
def token = astTokenizer(using self, strategy).token(v)
|
||||
}
|
||||
},
|
||||
strategy
|
||||
|
@ -695,6 +697,8 @@ object SqlIdiom {
|
|||
stmt"${action.token} RETURNING ${returnListTokenizer.token(
|
||||
ExpandReturning(r)(idiom, strategy).map(_._1)
|
||||
)}"
|
||||
case r =>
|
||||
fail(s"Unsupported Returning construct: $r")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ case class FlatJoinContext(t: JoinType, a: FromContext, on: Ast)
|
|||
|
||||
sealed trait SqlQuery {
|
||||
override def toString = {
|
||||
import minisql.MirrorSqlDialect._
|
||||
import MirrorSqlDialect.*
|
||||
import minisql.idiom.StatementInterpolator.*
|
||||
given Tokenizer[SqlQuery] = sqlQueryTokenizer(using
|
||||
defaultTokenizer(using Literal),
|
||||
|
|
|
@ -66,7 +66,7 @@ class ExpandNestedQueries(strategy: NamingStrategy) {
|
|||
|
||||
// Need to unhide properties that were used during the query
|
||||
def replaceProps(ast: Ast) =
|
||||
BetaReduction(ast, replacedRefs: _*)
|
||||
BetaReduction(ast, replacedRefs*)
|
||||
def replacePropsOption(ast: Option[Ast]) =
|
||||
ast.map(replaceProps(_))
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@ case class FlattenGroupByAggregation(agg: Ident) extends StatelessTransformer {
|
|||
super.apply(other)
|
||||
}
|
||||
|
||||
private[this] def isGroupByAggregation(ast: Ast): Boolean =
|
||||
private def isGroupByAggregation(ast: Ast): Boolean =
|
||||
ast match {
|
||||
case Aggregation(a, b) => isGroupByAggregation(b)
|
||||
case Map(a, b, c) => isGroupByAggregation(a)
|
||||
|
|
|
@ -22,31 +22,31 @@ class SqlNormalize(
|
|||
) {
|
||||
|
||||
private val normalize =
|
||||
(identity[Ast] _)
|
||||
(identity[Ast])
|
||||
.andThen(trace("original"))
|
||||
.andThen(DemarcateExternalAliases.apply _)
|
||||
.andThen(DemarcateExternalAliases.apply)
|
||||
.andThen(trace("DemarcateReturningAliases"))
|
||||
.andThen(new FlattenOptionOperation(concatBehavior).apply _)
|
||||
.andThen(new FlattenOptionOperation(concatBehavior).apply)
|
||||
.andThen(trace("FlattenOptionOperation"))
|
||||
.andThen(new SimplifyNullChecks(equalityBehavior).apply _)
|
||||
.andThen(new SimplifyNullChecks(equalityBehavior).apply)
|
||||
.andThen(trace("SimplifyNullChecks"))
|
||||
.andThen(Normalize.apply _)
|
||||
.andThen(Normalize.apply)
|
||||
.andThen(trace("Normalize"))
|
||||
// Need to do RenameProperties before ExpandJoin which normalizes-out all the tuple indexes
|
||||
// on which RenameProperties relies
|
||||
.andThen(RenameProperties.apply _)
|
||||
.andThen(RenameProperties.apply)
|
||||
.andThen(trace("RenameProperties"))
|
||||
.andThen(ExpandDistinct.apply _)
|
||||
.andThen(ExpandDistinct.apply)
|
||||
.andThen(trace("ExpandDistinct"))
|
||||
.andThen(NestImpureMappedInfix.apply _)
|
||||
.andThen(NestImpureMappedInfix.apply)
|
||||
.andThen(trace("NestMappedInfix"))
|
||||
.andThen(Normalize.apply _)
|
||||
.andThen(Normalize.apply)
|
||||
.andThen(trace("Normalize"))
|
||||
.andThen(ExpandJoin.apply _)
|
||||
.andThen(ExpandJoin.apply)
|
||||
.andThen(trace("ExpandJoin"))
|
||||
.andThen(ExpandMappedInfix.apply _)
|
||||
.andThen(ExpandMappedInfix.apply)
|
||||
.andThen(trace("ExpandMappedInfix"))
|
||||
.andThen(Normalize.apply _)
|
||||
.andThen(Normalize.apply)
|
||||
.andThen(trace("Normalize"))
|
||||
|
||||
def apply(ast: Ast) = normalize(ast)
|
||||
|
|
|
@ -85,16 +85,16 @@ private class ExpandSelect(
|
|||
|
||||
val orderedSelect = ref match {
|
||||
case pp @ Property(ast: Property, TupleIndex(idx)) =>
|
||||
trace"Reference is a sub-property of a tuple index: $idx. Walking inside." andReturn
|
||||
trace"Reference is a sub-property of a tuple index: $idx. Walking inside." `andReturn`
|
||||
expandReference(ast) match {
|
||||
case OrderedSelect(o, SelectValue(Tuple(elems), alias, c)) =>
|
||||
trace"Expressing Element $idx of $elems " andReturn
|
||||
trace"Expressing Element $idx of $elems " `andReturn`
|
||||
OrderedSelect(
|
||||
o :+ idx,
|
||||
SelectValue(elems(idx), concat(alias, idx), c)
|
||||
)
|
||||
case OrderedSelect(o, SelectValue(ast, alias, c)) =>
|
||||
trace"Appending $idx to $alias " andReturn
|
||||
trace"Appending $idx to $alias " `andReturn`
|
||||
OrderedSelect(o, SelectValue(ast, concat(alias, idx), c))
|
||||
}
|
||||
case pp @ Property.Opinionated(
|
||||
|
@ -103,7 +103,7 @@ private class ExpandSelect(
|
|||
renameable,
|
||||
visible
|
||||
) =>
|
||||
trace"Reference is a sub-property. Walking inside." andReturn
|
||||
trace"Reference is a sub-property. Walking inside." `andReturn`
|
||||
expandReference(ast) match {
|
||||
case OrderedSelect(o, SelectValue(ast, nested, c)) =>
|
||||
// Alias is the name of the column after the naming strategy
|
||||
|
@ -121,7 +121,7 @@ private class ExpandSelect(
|
|||
// this may need to change based on how distinct appends table names instead of just tuple indexes
|
||||
// into the property path.
|
||||
|
||||
trace"...inside walk completed, continuing to return: " andReturn
|
||||
trace"...inside walk completed, continuing to return: " `andReturn`
|
||||
OrderedSelect(
|
||||
o,
|
||||
SelectValue(
|
||||
|
@ -135,7 +135,7 @@ private class ExpandSelect(
|
|||
)
|
||||
}
|
||||
case pp @ Property(_, TupleIndex(idx)) =>
|
||||
trace"Reference is a tuple index: $idx from $select." andReturn
|
||||
trace"Reference is a tuple index: $idx from $select." `andReturn`
|
||||
select(idx) match {
|
||||
case OrderedSelect(o, SelectValue(ast, alias, c)) =>
|
||||
OrderedSelect(o, SelectValue(ast, concat(alias, idx), c))
|
||||
|
@ -155,13 +155,13 @@ private class ExpandSelect(
|
|||
s"Cannot find element $name in $cc"
|
||||
)
|
||||
}
|
||||
trace"Reference is a case class member: " andReturn
|
||||
trace"Reference is a case class member: " `andReturn`
|
||||
OrderedSelect(
|
||||
o :+ index,
|
||||
SelectValue(ast, Some(expandColumn(name, renameable)), c)
|
||||
)
|
||||
case List(OrderedSelect(o, SelectValue(i: Ident, _, c))) =>
|
||||
trace"Reference is an identifier: " andReturn
|
||||
trace"Reference is an identifier: " `andReturn`
|
||||
OrderedSelect(
|
||||
o,
|
||||
SelectValue(
|
||||
|
@ -171,7 +171,7 @@ private class ExpandSelect(
|
|||
)
|
||||
)
|
||||
case other =>
|
||||
trace"Reference is unidentified: $other returning:" andReturn
|
||||
trace"Reference is unidentified: $other returning:" `andReturn`
|
||||
OrderedSelect(
|
||||
Integer.MAX_VALUE,
|
||||
SelectValue(
|
||||
|
@ -191,7 +191,7 @@ private class ExpandSelect(
|
|||
)
|
||||
)
|
||||
|
||||
trace"Expanded $ref into $orderedSelect then Normalized to $normalizedOrderedSelect" andReturn
|
||||
trace"Expanded $ref into $orderedSelect then Normalized to $normalizedOrderedSelect" `andReturn`
|
||||
normalizedOrderedSelect
|
||||
}
|
||||
|
||||
|
@ -201,7 +201,7 @@ private class ExpandSelect(
|
|||
_,
|
||||
sv @ SelectValue(Property(Ident(_), propName), Some(alias), _)
|
||||
) if (propName == alias) =>
|
||||
trace"Detected select value with un-needed alias: $os removing it:" andReturn
|
||||
trace"Detected select value with un-needed alias: $os removing it:" `andReturn`
|
||||
os.copy(selectValue = sv.copy(alias = None))
|
||||
case _ => os
|
||||
}
|
||||
|
@ -224,7 +224,7 @@ private class ExpandSelect(
|
|||
// are there any selects that have infix values which we have not already selected? We need to include
|
||||
// them because they could be doing essential things e.g. RANK ... ORDER BY
|
||||
val remainingSelectsWithInfixes =
|
||||
trace"Searching Selects with Infix:" andReturn
|
||||
trace"Searching Selects with Infix:" `andReturn`
|
||||
new FindUnexpressedInfixes(select)(mappedRefs)
|
||||
|
||||
implicit val ordering: scala.math.Ordering[List[Int]] =
|
||||
|
|
|
@ -47,7 +47,7 @@ class FindUnexpressedInfixes(select: List[OrderedSelect]) {
|
|||
): List[(Ast, List[Int])] = {
|
||||
trace"Searching for infix: $ast in the sub-path $parentOrder".andLog()
|
||||
if (pathExists(parentOrder))
|
||||
trace"No infixes found" andContinue
|
||||
trace"No infixes found" `andContinue`
|
||||
List()
|
||||
else
|
||||
ast match {
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
package minisql
|
||||
package minisql.idiom
|
||||
|
||||
import minisql.NamingStrategy
|
||||
import minisql.ast.Renameable.{ByStrategy, Fixed}
|
||||
import minisql.ast.Visibility.Hidden
|
||||
import minisql.ast._
|
||||
import minisql.ast.*
|
||||
import minisql.context.CanReturnClause
|
||||
import minisql.idiom.{Idiom, SetContainsToken, Statement}
|
||||
import minisql.idiom.StatementInterpolator.*
|
||||
|
|
|
@ -1,20 +1,20 @@
|
|||
package minisql.parsing
|
||||
package minisql.context.mirror
|
||||
|
||||
import minisql.*
|
||||
import minisql.ast.*
|
||||
import minisql.idiom.*
|
||||
import minisql.NamingStrategy
|
||||
import minisql.MirrorContext
|
||||
import minisql.MirrorIdiom
|
||||
import minisql.context.mirror.{*, given}
|
||||
|
||||
class QuotedSuite extends munit.FunSuite {
|
||||
val ctx = new MirrorContext(MirrorIdiom, SnakeCase)
|
||||
|
||||
case class Foo(id: Long)
|
||||
|
||||
import mirrorContext.given
|
||||
|
||||
test("SimpleQuery") {
|
||||
val o = ctx.io(query[Foo]("foo").filter(_.id > 0))
|
||||
val o = mirrorContext.io(query[Foo]("foo").filter(_.id > 0))
|
||||
println("============" + o)
|
||||
o
|
||||
}
|
6
src/test/scala/minisql/mirror/context.scala
Normal file
6
src/test/scala/minisql/mirror/context.scala
Normal file
|
@ -0,0 +1,6 @@
|
|||
package minisql.context.mirror
|
||||
|
||||
import minisql.*
|
||||
import minisql.idiom.MirrorIdiom
|
||||
|
||||
val mirrorContext = new MirrorContext(MirrorIdiom, Literal)
|
Loading…
Add table
Add a link
Reference in a new issue