From ed736783fc1bd3c29e937178c055b4a01bab5fe0 Mon Sep 17 00:00:00 2001 From: jilen Date: Tue, 15 Jul 2025 18:47:34 +0800 Subject: [PATCH] Convert to using --- .../scala/minisql/context/sql/SqlIdiom.scala | 211 +++++++++--------- .../scala/minisql/context/sql/SqlQuery.scala | 8 +- .../minisql/idiom/StatementInterpolator.scala | 23 +- 3 files changed, 120 insertions(+), 122 deletions(-) diff --git a/src/main/scala/minisql/context/sql/SqlIdiom.scala b/src/main/scala/minisql/context/sql/SqlIdiom.scala index bc10d3d..4b0f2c3 100644 --- a/src/main/scala/minisql/context/sql/SqlIdiom.scala +++ b/src/main/scala/minisql/context/sql/SqlIdiom.scala @@ -65,15 +65,12 @@ trait SqlIdiom extends Idiom { def defaultTokenizer(using naming: NamingStrategy): Tokenizer[Ast] = new Tokenizer[Ast] { - private val stableTokenizer = astTokenizer(using this, naming) - - extension (v: Ast) { - def token = stableTokenizer.token(v) - } + private def stableTokenizer = astTokenizer(using this, naming) + def token(v: Ast) = stableTokenizer.token(v) } - def astTokenizer(using + def astTokenizer(implicit astTokenizer: Tokenizer[Ast], strategy: NamingStrategy ): Tokenizer[Ast] = @@ -98,7 +95,7 @@ trait SqlIdiom extends Idiom { fail(s"Malformed or unsupported construct: $a.") } - implicit def ifTokenizer(implicit + given ifTokenizer(using astTokenizer: Tokenizer[Ast], strategy: NamingStrategy ): Tokenizer[If] = Tokenizer[If] { @@ -185,10 +182,28 @@ trait SqlIdiom extends Idiom { def apply = stmt"SELECT $withLimitOffset" } - implicit def sqlQueryTokenizer(implicit + given unaryOperatorTokenizer: Tokenizer[UnaryOperator] = + Tokenizer[UnaryOperator] { + case NumericOperator.`-` => stmt"-" + case BooleanOperator.`!` => stmt"NOT" + case StringOperator.`toUpperCase` => stmt"UPPER" + case StringOperator.`toLowerCase` => stmt"LOWER" + case StringOperator.`toLong` => stmt"" // cast is implicit + case StringOperator.`toInt` => stmt"" // cast is implicit + case SetOperator.`isEmpty` => stmt"NOT EXISTS" + case SetOperator.`nonEmpty` => stmt"EXISTS" + } + + given setOperationTokenizer: Tokenizer[SetOperation] = + Tokenizer[SetOperation] { + case UnionOperation => stmt"UNION" + case UnionAllOperation => stmt"UNION ALL" + } + + given sqlQueryTokenizer(using astTokenizer: Tokenizer[Ast], strategy: NamingStrategy - ): Tokenizer[SqlQuery] = Tokenizer[SqlQuery] { + ): Tokenizer[SqlQuery] = Tokenizer.withSelf[SqlQuery] { case q: FlattenSqlQuery => new FlattenSqlQueryTokenizerHelper(q).apply case SetOperationSqlQuery(a, op, b) => @@ -220,7 +235,16 @@ trait SqlIdiom extends Idiom { protected def tokenizeAlias(strategy: NamingStrategy, table: String) = strategy.default(table) - implicit def selectValueTokenizer(implicit + given aggregationOperatorTokenizer: Tokenizer[AggregationOperator] = + Tokenizer[AggregationOperator] { + case AggregationOperator.`min` => stmt"MIN" + case AggregationOperator.`max` => stmt"MAX" + case AggregationOperator.`avg` => stmt"AVG" + case AggregationOperator.`sum` => stmt"SUM" + case AggregationOperator.`size` => stmt"COUNT" + } + + given selectValueTokenizer(using astTokenizer: Tokenizer[Ast], strategy: NamingStrategy ): Tokenizer[SelectValue] = { @@ -260,7 +284,39 @@ trait SqlIdiom extends Idiom { tokenizer(using customAstTokenizer) } - implicit def operationTokenizer(implicit + given binaryOperatorTokenizer: Tokenizer[BinaryOperator] = + Tokenizer[BinaryOperator] { + case EqualityOperator.`==` => stmt"=" + case EqualityOperator.`!=` => stmt"<>" + case BooleanOperator.`&&` => stmt"AND" + case BooleanOperator.`||` => stmt"OR" + case StringOperator.`concat` => stmt"||" + case StringOperator.`startsWith` => + fail("bug: this code should be unreachable") + case StringOperator.`split` => stmt"SPLIT" + case NumericOperator.`-` => stmt"-" + case NumericOperator.`+` => stmt"+" + case NumericOperator.`*` => stmt"*" + case NumericOperator.`>` => stmt">" + case NumericOperator.`>=` => stmt">=" + case NumericOperator.`<` => stmt"<" + case NumericOperator.`<=` => stmt"<=" + case NumericOperator.`/` => stmt"/" + case NumericOperator.`%` => stmt"%" + case SetOperator.`contains` => stmt"IN" + } + + given optionOperationTokenizer(using + astTokenizer: Tokenizer[Ast], + strategy: NamingStrategy + ): Tokenizer[OptionOperation] = Tokenizer[OptionOperation] { + case OptionIsEmpty(ast) => stmt"${ast.token} IS NULL" + case OptionNonEmpty(ast) => stmt"${ast.token} IS NOT NULL" + case OptionIsDefined(ast) => stmt"${ast.token} IS NOT NULL" + case other => fail(s"Malformed or unsupported construct: $other.") + } + + given operationTokenizer(using astTokenizer: Tokenizer[Ast], strategy: NamingStrategy ): Tokenizer[Operation] = Tokenizer[Operation] { @@ -296,25 +352,9 @@ trait SqlIdiom extends Idiom { case e: FunctionApply => fail(s"Can't translate the ast to sql: '$e'") } - implicit def optionOperationTokenizer(implicit - astTokenizer: Tokenizer[Ast], - strategy: NamingStrategy - ): Tokenizer[OptionOperation] = Tokenizer[OptionOperation] { - case OptionIsEmpty(ast) => stmt"${ast.token} IS NULL" - case OptionNonEmpty(ast) => stmt"${ast.token} IS NOT NULL" - case OptionIsDefined(ast) => stmt"${ast.token} IS NOT NULL" - case other => fail(s"Malformed or unsupported construct: $other.") - } - - implicit val setOperationTokenizer: Tokenizer[SetOperation] = - Tokenizer[SetOperation] { - case UnionOperation => stmt"UNION" - case UnionAllOperation => stmt"UNION ALL" - } - protected def limitOffsetToken( query: Statement - )(implicit astTokenizer: Tokenizer[Ast], strategy: NamingStrategy) = + )(using astTokenizer: Tokenizer[Ast], strategy: NamingStrategy) = Tokenizer[(Option[Ast], Option[Ast])] { case (None, None) => query case (Some(limit), None) => stmt"$query LIMIT ${limit.token}" @@ -325,10 +365,25 @@ trait SqlIdiom extends Idiom { protected def tokenOrderBy( criterias: List[OrderByCriteria] - )(implicit astTokenizer: Tokenizer[Ast], strategy: NamingStrategy) = + )(using astTokenizer: Tokenizer[Ast], strategy: NamingStrategy) = stmt"ORDER BY ${criterias.token}" - implicit def sourceTokenizer(implicit + given entityTokenizer(using + astTokenizer: Tokenizer[Ast], + strategy: NamingStrategy + ): Tokenizer[Entity] = Tokenizer[Entity] { + case Entity.Opinionated(name, _, renameable) => + tokenizeTable(strategy, name, renameable).token + } + + given joinTypeTokenizer: Tokenizer[JoinType] = Tokenizer[JoinType] { + case JoinType.InnerJoin => stmt"INNER JOIN" + case JoinType.LeftJoin => stmt"LEFT JOIN" + case JoinType.RightJoin => stmt"RIGHT JOIN" + case JoinType.FullJoin => stmt"FULL JOIN" + } + + given sourceTokenizer(using astTokenizer: Tokenizer[Ast], strategy: NamingStrategy ): Tokenizer[FromContext] = Tokenizer[FromContext] { @@ -341,18 +396,12 @@ trait SqlIdiom extends Idiom { case InfixContext(infix, alias) => stmt"(${(infix: Ast).token}) AS ${strategy.default(alias).token}" case JoinContext(t, a, b, on) => - stmt"${a.token} ${t.token} ${b.token} ON ${on.token}" - case FlatJoinContext(t, a, on) => stmt"${t.token} ${a.token} ON ${on.token}" + stmt"${sourceTokenizer.token(a)} ${t.token} ${sourceTokenizer.token(b)} ON ${on.token}" + case FlatJoinContext(t, a, on) => + stmt"${t.token} ${sourceTokenizer.token(a)} ON ${on.token}" } - implicit val joinTypeTokenizer: Tokenizer[JoinType] = Tokenizer[JoinType] { - case JoinType.InnerJoin => stmt"INNER JOIN" - case JoinType.LeftJoin => stmt"LEFT JOIN" - case JoinType.RightJoin => stmt"RIGHT JOIN" - case JoinType.FullJoin => stmt"FULL JOIN" - } - - implicit def orderByCriteriaTokenizer(implicit + given orderByCriteriaTokenizer(using astTokenizer: Tokenizer[Ast], strategy: NamingStrategy ): Tokenizer[OrderByCriteria] = Tokenizer[OrderByCriteria] { @@ -370,50 +419,7 @@ trait SqlIdiom extends Idiom { stmt"${scopedTokenizer(ast)} DESC NULLS LAST" } - implicit val unaryOperatorTokenizer: Tokenizer[UnaryOperator] = - Tokenizer[UnaryOperator] { - case NumericOperator.`-` => stmt"-" - case BooleanOperator.`!` => stmt"NOT" - case StringOperator.`toUpperCase` => stmt"UPPER" - case StringOperator.`toLowerCase` => stmt"LOWER" - case StringOperator.`toLong` => stmt"" // cast is implicit - case StringOperator.`toInt` => stmt"" // cast is implicit - case SetOperator.`isEmpty` => stmt"NOT EXISTS" - case SetOperator.`nonEmpty` => stmt"EXISTS" - } - - implicit val aggregationOperatorTokenizer: Tokenizer[AggregationOperator] = - Tokenizer[AggregationOperator] { - case AggregationOperator.`min` => stmt"MIN" - case AggregationOperator.`max` => stmt"MAX" - case AggregationOperator.`avg` => stmt"AVG" - case AggregationOperator.`sum` => stmt"SUM" - case AggregationOperator.`size` => stmt"COUNT" - } - - implicit val binaryOperatorTokenizer: Tokenizer[BinaryOperator] = - Tokenizer[BinaryOperator] { - case EqualityOperator.`==` => stmt"=" - case EqualityOperator.`!=` => stmt"<>" - case BooleanOperator.`&&` => stmt"AND" - case BooleanOperator.`||` => stmt"OR" - case StringOperator.`concat` => stmt"||" - case StringOperator.`startsWith` => - fail("bug: this code should be unreachable") - case StringOperator.`split` => stmt"SPLIT" - case NumericOperator.`-` => stmt"-" - case NumericOperator.`+` => stmt"+" - case NumericOperator.`*` => stmt"*" - case NumericOperator.`>` => stmt">" - case NumericOperator.`>=` => stmt">=" - case NumericOperator.`<` => stmt"<" - case NumericOperator.`<=` => stmt"<=" - case NumericOperator.`/` => stmt"/" - case NumericOperator.`%` => stmt"%" - case SetOperator.`contains` => stmt"IN" - } - - implicit def propertyTokenizer(implicit + given propertyTokenizer(using astTokenizer: Tokenizer[Ast], strategy: NamingStrategy ): Tokenizer[Property] = { @@ -480,7 +486,7 @@ trait SqlIdiom extends Idiom { } } - implicit def valueTokenizer(implicit + given valueTokenizer(using astTokenizer: Tokenizer[Ast], strategy: NamingStrategy ): Tokenizer[Value] = Tokenizer[Value] { @@ -492,7 +498,7 @@ trait SqlIdiom extends Idiom { case CaseClass(values) => stmt"${values.map(_._2).token}" } - implicit def infixTokenizer(implicit + given infixTokenizer(using astTokenizer: Tokenizer[Ast], strategy: NamingStrategy ): Tokenizer[Infix] = Tokenizer[Infix] { @@ -502,19 +508,19 @@ trait SqlIdiom extends Idiom { Statement(Interleave(pt, pr)) } - implicit def identTokenizer(implicit + given identTokenizer(using astTokenizer: Tokenizer[Ast], strategy: NamingStrategy ): Tokenizer[Ident] = Tokenizer[Ident](e => strategy.default(e.name).token) - implicit def externalIdentTokenizer(implicit + given externalIdentTokenizer(using astTokenizer: Tokenizer[Ast], strategy: NamingStrategy ): Tokenizer[ExternalIdent] = Tokenizer[ExternalIdent](e => strategy.default(e.name).token) - implicit def assignmentTokenizer(implicit + given (using astTokenizer: Tokenizer[Ast], strategy: NamingStrategy ): Tokenizer[Assignment] = Tokenizer[Assignment] { @@ -522,7 +528,7 @@ trait SqlIdiom extends Idiom { stmt"${prop.token} = ${scopedTokenizer(value)}" } - implicit def defaultAstTokenizer(implicit + given defaultAstTokenizer(using astTokenizer: Tokenizer[Ast], strategy: NamingStrategy ): Tokenizer[Action] = { @@ -533,7 +539,7 @@ trait SqlIdiom extends Idiom { actionTokenizer(insertEntityTokenizer)(using actionAstTokenizer, strategy) } - protected def actionAstTokenizer(implicit + protected def actionAstTokenizer(using astTokenizer: Tokenizer[Ast], strategy: NamingStrategy ) = @@ -554,7 +560,7 @@ trait SqlIdiom extends Idiom { ) } - def returnListTokenizer(implicit + def returnListTokenizer(using tokenizer: Tokenizer[Ast], strategy: NamingStrategy ): Tokenizer[List[Ast]] = { @@ -574,7 +580,7 @@ trait SqlIdiom extends Idiom { protected def actionTokenizer( insertEntityTokenizer: Tokenizer[Entity] - )(implicit + )(using astTokenizer: Tokenizer[Ast], strategy: NamingStrategy ): Tokenizer[Action] = @@ -636,15 +642,7 @@ trait SqlIdiom extends Idiom { fail(s"Action ast can't be translated to sql: '$other'") } - implicit def entityTokenizer(implicit - astTokenizer: Tokenizer[Ast], - strategy: NamingStrategy - ): Tokenizer[Entity] = Tokenizer[Entity] { - case Entity.Opinionated(name, _, renameable) => - tokenizeTable(strategy, name, renameable).token - } - - protected def scopedTokenizer(ast: Ast)(implicit tokenizer: Tokenizer[Ast]) = + protected def scopedTokenizer(ast: Ast)(using tokenizer: Tokenizer[Ast]) = ast match { case _: Query => stmt"(${ast.token})" case _: BinaryOperation => stmt"(${ast.token})" @@ -680,13 +678,12 @@ object SqlIdiom { query: ReturningAction )(implicit strategy: NamingStrategy) = { val idiom = copyIdiom(parentIdiom, Some(query.alias)) - import idiom._ + import idiom.{*, given} - implicit val stableTokenizer: Tokenizer[Ast] = idiom.astTokenizer(using + given Tokenizer[Ast] = idiom.astTokenizer(using new Tokenizer[Ast] { self => - extension (v: Ast) { - def token = astTokenizer(using self, strategy).token(v) - } + def token(v: Ast) = astTokenizer(using self, strategy).token(v) + }, strategy ) diff --git a/src/main/scala/minisql/context/sql/SqlQuery.scala b/src/main/scala/minisql/context/sql/SqlQuery.scala index 2884662..ec5f3da 100644 --- a/src/main/scala/minisql/context/sql/SqlQuery.scala +++ b/src/main/scala/minisql/context/sql/SqlQuery.scala @@ -19,12 +19,10 @@ case class FlatJoinContext(t: JoinType, a: FromContext, on: Ast) sealed trait SqlQuery { override def toString = { - import MirrorSqlDialect.* + import MirrorSqlDialect.{*, given} import minisql.idiom.StatementInterpolator.* - given Tokenizer[SqlQuery] = sqlQueryTokenizer(using - defaultTokenizer(using Literal), - Literal - ) + given NamingStrategy = Literal + given Tokenizer[Ast] = defaultTokenizer(using Literal) summon[Tokenizer[SqlQuery]].token(this).toString() } } diff --git a/src/main/scala/minisql/idiom/StatementInterpolator.scala b/src/main/scala/minisql/idiom/StatementInterpolator.scala index 2893c8d..2f663b8 100644 --- a/src/main/scala/minisql/idiom/StatementInterpolator.scala +++ b/src/main/scala/minisql/idiom/StatementInterpolator.scala @@ -18,24 +18,27 @@ object StatementInterpolator { } } trait Tokenizer[T] { - extension (v: T) { - def token: Token - } + def token(v: T): Token } object Tokenizer { - def apply[T](f: T => Token): Tokenizer[T] = new Tokenizer[T] { - extension (v: T) { - def token: Token = f(v) + + def withSelf[T](f: Tokenizer[T] ?=> (T => Token)): Tokenizer[T] = + new Tokenizer[T] { self => + def token(v: T): Token = f(using self)(v) } + + def apply[T](f: T => Token): Tokenizer[T] = new Tokenizer[T] { + def token(v: T): Token = f(v) } def withFallback[T]( fallback: Tokenizer[T] => Tokenizer[T] )(pf: PartialFunction[T, Token]) = - new Tokenizer[T] { - extension (v: T) { - private def stable = fallback(this) - override def token = pf.applyOrElse(v, stable.token) + new Tokenizer[T] { self => + override def token(v: T): Token = { + def stable = fallback(self) + pf.applyOrElse(v, stable.token) } + } }