diff --git a/build.sbt b/build.sbt index 67a69a6..86502a5 100644 --- a/build.sbt +++ b/build.sbt @@ -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" +) diff --git a/src/main/scala/minisql/context/MirrorContext.scala b/src/main/scala/minisql/context/MirrorContext.scala index ba00db1..7501274 100644 --- a/src/main/scala/minisql/context/MirrorContext.scala +++ b/src/main/scala/minisql/context/MirrorContext.scala @@ -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 + } } diff --git a/src/main/scala/minisql/context/mirror.scala b/src/main/scala/minisql/context/mirror.scala index a67fdd2..81b8f98 100644 --- a/src/main/scala/minisql/context/mirror.scala +++ b/src/main/scala/minisql/context/mirror.scala @@ -1,71 +1,139 @@ 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] +trait MirrorCodecs { + ctx: MirrorContext[?, ?] => -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'" - ) + 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) } } -} -type Encoder[E] = ParamEncoder[E] { - type Stmt = Statement -} + final protected def mirrorColumnDecoder[X]( + conv: Any => Option[X] + ): Decoder[X] = + new ColumnDecoder[X] { + type DBRow = ctx.DBRow + def decode(row: DBRow, idx: Int): Try[X] = { + row.data + .lift(idx) + .flatMap { x => + conv(x) + } + .toRight(new Exception(s"Cannot convert value at ${idx}")) + .toTry + } + } -private def encoder[V]: Encoder[V] = new ParamEncoder[V] { - - type Stmt = Map[Int, Any] - - 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] = - new ColumnDecoder[X] { - type DBRow = Row - def decode(row: Row, idx: Int): Try[X] = { - row._1 - .lift(idx) - .flatMap { x => - conv(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) } - .toRight(new Exception(s"Cannot convert value at ${idx}")) - .toTry } } -given Decoder[Long] = apply(x => - x match { - case l: Long => Some(l) - case _ => 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 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] +} diff --git a/src/main/scala/minisql/context/sql/MirrorSqlContext.scala b/src/main/scala/minisql/context/sql/MirrorSqlContext.scala new file mode 100644 index 0000000..0a035f9 --- /dev/null +++ b/src/main/scala/minisql/context/sql/MirrorSqlContext.scala @@ -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 {} diff --git a/src/main/scala/minisql/MirrorSqlDialect.scala b/src/main/scala/minisql/context/sql/MirrorSqlDialect.scala similarity index 97% rename from src/main/scala/minisql/MirrorSqlDialect.scala rename to src/main/scala/minisql/context/sql/MirrorSqlDialect.scala index 563f770..7ee69ba 100644 --- a/src/main/scala/minisql/MirrorSqlDialect.scala +++ b/src/main/scala/minisql/context/sql/MirrorSqlDialect.scala @@ -1,4 +1,4 @@ -package minisql +package minisql.context.sql import minisql.context.{ CanReturnClause, diff --git a/src/main/scala/minisql/context/sql/OnConflictSupport.scala b/src/main/scala/minisql/context/sql/OnConflictSupport.scala index 940d5bf..3d0d6ce 100644 --- a/src/main/scala/minisql/context/sql/OnConflictSupport.scala +++ b/src/main/scala/minisql/context/sql/OnConflictSupport.scala @@ -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) } } diff --git a/src/main/scala/minisql/context/sql/SqlContext.scala b/src/main/scala/minisql/context/sql/SqlContext.scala new file mode 100644 index 0000000..7c185d5 --- /dev/null +++ b/src/main/scala/minisql/context/sql/SqlContext.scala @@ -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] +} diff --git a/src/main/scala/minisql/context/sql/SqlIdiom.scala b/src/main/scala/minisql/context/sql/SqlIdiom.scala index c593153..ba099b0 100644 --- a/src/main/scala/minisql/context/sql/SqlIdiom.scala +++ b/src/main/scala/minisql/context/sql/SqlIdiom.scala @@ -26,9 +26,7 @@ trait SqlIdiom extends Idiom { protected def concatBehavior: ConcatBehavior = AnsiConcat protected def equalityBehavior: EqualityBehavior = AnsiEquality - - protected def actionAlias: Option[Ident] = None - + 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") } } } diff --git a/src/main/scala/minisql/context/sql/SqlQuery.scala b/src/main/scala/minisql/context/sql/SqlQuery.scala index 06ec412..2884662 100644 --- a/src/main/scala/minisql/context/sql/SqlQuery.scala +++ b/src/main/scala/minisql/context/sql/SqlQuery.scala @@ -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), diff --git a/src/main/scala/minisql/context/sql/norm/ExpandNestedQueries.scala b/src/main/scala/minisql/context/sql/norm/ExpandNestedQueries.scala index 56095e9..8063c7f 100644 --- a/src/main/scala/minisql/context/sql/norm/ExpandNestedQueries.scala +++ b/src/main/scala/minisql/context/sql/norm/ExpandNestedQueries.scala @@ -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(_)) diff --git a/src/main/scala/minisql/context/sql/norm/FlattenGroupByAggregation.scala b/src/main/scala/minisql/context/sql/norm/FlattenGroupByAggregation.scala index 30abb53..9b9b354 100644 --- a/src/main/scala/minisql/context/sql/norm/FlattenGroupByAggregation.scala +++ b/src/main/scala/minisql/context/sql/norm/FlattenGroupByAggregation.scala @@ -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) diff --git a/src/main/scala/minisql/context/sql/norm/SqlNormalize.scala b/src/main/scala/minisql/context/sql/norm/SqlNormalize.scala index c239b63..bf80bf5 100644 --- a/src/main/scala/minisql/context/sql/norm/SqlNormalize.scala +++ b/src/main/scala/minisql/context/sql/norm/SqlNormalize.scala @@ -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) diff --git a/src/main/scala/minisql/context/sql/norm/nested/ExpandSelect.scala b/src/main/scala/minisql/context/sql/norm/nested/ExpandSelect.scala index a8fd5d6..1b8818e 100644 --- a/src/main/scala/minisql/context/sql/norm/nested/ExpandSelect.scala +++ b/src/main/scala/minisql/context/sql/norm/nested/ExpandSelect.scala @@ -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]] = diff --git a/src/main/scala/minisql/context/sql/norm/nested/FindUnexpressedInfixes.scala b/src/main/scala/minisql/context/sql/norm/nested/FindUnexpressedInfixes.scala index 2ea1320..7149ea2 100644 --- a/src/main/scala/minisql/context/sql/norm/nested/FindUnexpressedInfixes.scala +++ b/src/main/scala/minisql/context/sql/norm/nested/FindUnexpressedInfixes.scala @@ -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 { diff --git a/src/main/scala/minisql/idiom/MirrorIdiom.scala b/src/main/scala/minisql/idiom/MirrorIdiom.scala index b325b7d..fd18549 100644 --- a/src/main/scala/minisql/idiom/MirrorIdiom.scala +++ b/src/main/scala/minisql/idiom/MirrorIdiom.scala @@ -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.* diff --git a/src/test/scala/minisql/parsing/QuotedSuite.scala b/src/test/scala/minisql/mirror/QuotedSuite.scala similarity index 65% rename from src/test/scala/minisql/parsing/QuotedSuite.scala rename to src/test/scala/minisql/mirror/QuotedSuite.scala index d2f8981..65b9cf3 100644 --- a/src/test/scala/minisql/parsing/QuotedSuite.scala +++ b/src/test/scala/minisql/mirror/QuotedSuite.scala @@ -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 } diff --git a/src/test/scala/minisql/mirror/context.scala b/src/test/scala/minisql/mirror/context.scala new file mode 100644 index 0000000..240a475 --- /dev/null +++ b/src/test/scala/minisql/mirror/context.scala @@ -0,0 +1,6 @@ +package minisql.context.mirror + +import minisql.* +import minisql.idiom.MirrorIdiom + +val mirrorContext = new MirrorContext(MirrorIdiom, Literal)