Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .mill-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.12.0
0.12.10
2 changes: 1 addition & 1 deletion .scalafmt.conf
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
version = "3.8.1"
version = "3.9.5"

align.preset = none
align.openParenCallSite = false
Expand Down
86 changes: 62 additions & 24 deletions build.mill
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@ import de.tobiasroeser.mill.vcs.version.VcsVersion
import com.goyeau.mill.scalafix.ScalafixModule
import mill._, scalalib._, publish._

val scalaVersions = Seq("2.13.12", "3.6.2")
val scala3 = "3.6.2"
val scalaVersions = Seq("2.13.12", scala3)
val scala3NamedTuples = "3.7.0"

trait Common extends CrossScalaModule with PublishModule with ScalafixModule{
def scalaVersion = crossScalaVersion
trait CommonBase extends ScalaModule with PublishModule with ScalafixModule { common =>

def publishVersion = VcsVersion.vcsState().format()

Expand All @@ -33,18 +34,13 @@ trait Common extends CrossScalaModule with PublishModule with ScalafixModule{
Seq("-Wunused:privates,locals,explicits,implicits,params") ++
Option.when(scalaVersion().startsWith("2."))("-Xsource:3")
}
}


object scalasql extends Cross[ScalaSql](scalaVersions)
trait ScalaSql extends Common{ common =>
def moduleDeps = Seq(query, operations)
def ivyDeps = Agg.empty[Dep] ++ Option.when(scalaVersion().startsWith("2."))(
ivy"org.scala-lang:scala-reflect:${scalaVersion()}"
)

def semanticDbVersion: T[String] =
// last version that works with Scala 2.13.12
"4.12.3"

object test extends ScalaTests with ScalafixModule{
trait CommonTest extends ScalaTests with ScalafixModule {
def semanticDbVersion: T[String] = common.semanticDbVersion
def scalacOptions = common.scalacOptions
def ivyDeps = Agg(
ivy"com.github.vertical-blank:sql-formatter:2.0.4",
Expand All @@ -61,10 +57,51 @@ trait ScalaSql extends Common{ common =>
ivy"com.zaxxer:HikariCP:5.1.0"
)

def recordedTestsFile: String
def recordedSuiteDescriptionsFile: String

def testFramework = "scalasql.UtestFramework"

def forkArgs = Seq("-Duser.timezone=Asia/Singapore")
def forkEnv = Map("MILL_WORKSPACE_ROOT" -> T.workspace.toString())

def forkEnv = Map(
"MILL_WORKSPACE_ROOT" -> T.workspace.toString(),
"SCALASQL_RECORDED_TESTS_NAME" -> recordedTestsFile,
"SCALASQL_RECORDED_SUITE_DESCRIPTIONS_NAME" -> recordedSuiteDescriptionsFile
)
}
}
trait Common extends CommonBase with CrossScalaModule

object `scalasql-namedtuples` extends CommonBase {
def scalaVersion: T[String] = scala3NamedTuples
def millSourcePath: os.Path = scalasql(scala3).millSourcePath / "namedtuples"
def moduleDeps: Seq[PublishModule] = Seq(scalasql(scala3))

// override def scalacOptions: Target[Seq[String]] = T {
// super.scalacOptions() :+ "-Xprint:inlining"
// }

object test extends CommonTest {
def resources = scalasql(scala3).test.resources
def moduleDeps = super.moduleDeps ++ Seq(scalasql(scala3), scalasql(scala3).test)
def recordedTestsFile: String = "recordedTestsNT.json"
def recordedSuiteDescriptionsFile: String = "recordedSuiteDescriptionsNT.json"
}
}

object scalasql extends Cross[ScalaSql](scalaVersions)
trait ScalaSql extends Common { common =>
def moduleDeps = Seq(query, operations)
def ivyDeps = Agg.empty[Dep] ++ Option.when(scalaVersion().startsWith("2."))(
ivy"org.scala-lang:scala-reflect:${scalaVersion()}"
)

override def consoleScalacOptions: T[Seq[String]] = Seq("-Xprint:typer")

object test extends CommonTest {
def recordedTestsFile: String = "recordedTests.json"
def recordedSuiteDescriptionsFile: String = "recordedSuiteDescriptions.json"
}

private def indent(code: Iterable[String]): String =
Expand All @@ -74,15 +111,14 @@ trait ScalaSql extends Common{ common =>
def ivyDeps = Agg(
ivy"com.lihaoyi::geny:1.0.0",
ivy"com.lihaoyi::sourcecode:0.3.1",
ivy"com.lihaoyi::pprint:0.8.1",
ivy"com.lihaoyi::pprint:0.8.1"
) ++ Option.when(scalaVersion().startsWith("2."))(
ivy"org.scala-lang:scala-reflect:${scalaVersion()}"
)

def generatedSources = T {
def commaSep0(i: Int, f: Int => String) = Range.inclusive(1, i).map(f).mkString(", ")


val queryableRowDefs = for (i <- Range.inclusive(2, 22)) yield {
def commaSep(f: Int => String) = commaSep0(i, f)
s"""implicit def Tuple${i}Queryable[${commaSep(j => s"Q$j")}, ${commaSep(j => s"R$j")}](
Expand All @@ -98,7 +134,6 @@ trait ScalaSql extends Common{ common =>
|}""".stripMargin
}


os.write(
T.dest / "Generated.scala",
s"""package scalasql.core.generated
Expand All @@ -113,15 +148,13 @@ trait ScalaSql extends Common{ common =>

}


object operations extends Common with CrossValue{
object operations extends Common with CrossValue {
def moduleDeps = Seq(core)
}

object query extends Common with CrossValue{
object query extends Common with CrossValue {
def moduleDeps = Seq(core)


def generatedSources = T {
def commaSep0(i: Int, f: Int => String) = Range.inclusive(1, i).map(f).mkString(", ")

Expand All @@ -139,7 +172,9 @@ trait ScalaSql extends Common{ common =>
| )
|
|""".stripMargin
s"""def batched[${commaSep(j => s"T$j")}](${commaSep(j => s"f$j: V[Column] => Column[T$j]")})(
s"""def batched[${commaSep(j => s"T$j")}](${commaSep(j =>
s"f$j: V[Column] => Column[T$j]"
)})(
| items: (${commaSep(j => s"Expr[T$j]")})*
|)(implicit qr: Queryable[V[Column], R]): scalasql.query.InsertColumns[V, R] $impl""".stripMargin
}
Expand All @@ -165,12 +200,15 @@ trait ScalaSql extends Common{ common =>

val commaSepQ = commaSep(j => s"Q$j")
val commaSepR = commaSep(j => s"R$j")
val joinAppendType = s"scalasql.query.JoinAppend[($commaSepQ), QA, ($commaSepQ, QA), ($commaSepR, RA)]"
val joinAppendType =
s"scalasql.query.JoinAppend[($commaSepQ), QA, ($commaSepQ, QA), ($commaSepR, RA)]"
s"""
|implicit def append$i[$commaSepQ, QA, $commaSepR, RA](
| implicit qr0: Queryable.Row[($commaSepQ, QA), ($commaSepR, RA)],
| @annotation.nowarn("msg=never used") qr20: Queryable.Row[QA, RA]): $joinAppendType = new $joinAppendType {
| override def appendTuple(t: ($commaSepQ), v: QA): ($commaSepQ, QA) = (${commaSep(j => s"t._$j")}, v)
| override def appendTuple(t: ($commaSepQ), v: QA): ($commaSepQ, QA) = (${commaSep(j =>
s"t._$j"
)}, v)
|
| def qr: Queryable.Row[($commaSepQ, QA), ($commaSepR, RA)] = qr0
|}""".stripMargin
Expand Down
4 changes: 3 additions & 1 deletion docs/reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -416,7 +416,9 @@ try {

throw new FooException
}
} catch { case e: FooException => /*donothing*/ }
} catch {
case e: FooException => /*donothing*/
}

dbClient.transaction(_.run(Purchase.select.size)) ==> 7
```
Expand Down
8 changes: 6 additions & 2 deletions docs/tutorial.md
Original file line number Diff line number Diff line change
Expand Up @@ -1213,7 +1213,9 @@ try {

throw new Exception()
}
} catch { case e: Exception => /*do nothing*/ }
} catch {
case e: Exception => /*do nothing*/
}

dbClient.transaction { implicit db =>
db.run(City.select.filter(_.countryCode === "SGP").single) ==>
Expand Down Expand Up @@ -1255,7 +1257,9 @@ dbClient.transaction { implicit db =>
db.run(City.select.filter(_.countryCode === "SGP")) ==> Seq()
throw new Exception()
}
} catch { case e: Exception => /*do nothing*/ }
} catch {
case e: Exception => /*do nothing*/
}

db.run(City.select.filter(_.countryCode === "SGP").single) ==>
City[Sc](3208, "Singapore", "SGP", district = "", population = 4017733)
Expand Down
148 changes: 148 additions & 0 deletions scalasql/namedtuples/src/SimpleTable.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
package scalasql.namedtuples

import scalasql.core.DbApi.Impl
import scalasql.core.DialectTypeMappers
import scalasql.core.Expr
import scalasql.core.Queryable
import scalasql.core.Queryable.ResultSetIterator
import scalasql.core.Sc
import scalasql.dialects.Dialect
import scalasql.dialects.TableOps
import scalasql.namedtuples.SimpleTable.Internal.Tombstone
import scalasql.query.Column
import scalasql.query.Query
import scalasql.query.Table
import scalasql.query.Table.ImplicitMetadata
import sourcecode.Name

import scala.NamedTuple.AnyNamedTuple
import scala.language.implicitConversions

class SimpleTable[C]()(
using val name0: sourcecode.Name,
metadata0: => SimpleTable.Metadata[C]
) extends Table.Base
with SimpleTable.LowPri[C] {

lazy val metadata: SimpleTable.Metadata[C] = metadata0

override protected[scalasql] def tableName: String = name0.value

override protected[scalasql] def schemaName: String = ""

override protected[scalasql] def tableLabels: Seq[String] = {
metadata.metadata0.walkLabels0()
}

override protected[scalasql] def escape: Boolean = false

given simpleTableImplicitMetadata: SimpleTable.WrappedMetadata[C] =
SimpleTable.WrappedMetadata(metadata)

implicit def containerQr(
implicit dialect: DialectTypeMappers,
f: SimpleTableMacros.Mask[C]
): Queryable.Row[f.Result[Expr], C] =
val tableMetadata = metadata.metadata0
tableMetadata
.queryable(
tableMetadata.walkLabels0,
dialect,
new Table.Metadata.QueryableProxy(tableMetadata.queryables(dialect, _))
)
.asInstanceOf[Queryable.Row[f.Result[Expr], C]]
}

object SimpleTable {

trait LowPri[C] { this: SimpleTable[C] =>
implicit def containerQr2(
implicit dialect: DialectTypeMappers,
f: SimpleTableMacros.Mask[C]
): Queryable.Row[f.Result[Column], C] =
containerQr.asInstanceOf[Queryable.Row[f.Result[Column], C]]
}

implicit def TableOpsConv[C: {SimpleTableMacros.Mask as f}](
t: SimpleTable[C]
)(using dialect: Dialect): TableOps[f.Result] =
// assume types in f.Result matches
val tableMetadata = t.metadata.metadata0.asInstanceOf[Table.Metadata[f.Result]]
dialect.TableOpsConv(new Table[f.Result](using t.name0, tableMetadata) {
override protected[scalasql] def tableName: String = t.tableName

override protected[scalasql] def schemaName: String = t.schemaName

override protected[scalasql] def tableLabels: Seq[String] = t.tableLabels

override protected[scalasql] def escape: Boolean = t.escape
})

final class Record[C, Mask <: AnyNamedTuple](data: IArray[AnyRef]) extends Selectable:
type Fields = Mask
def recordIterator: Iterator[Any] = data.iterator.asInstanceOf[Iterator[Any]]
def apply(i: Int): AnyRef = data(i)
def updates(fs: ((u: RecordUpdater[C, Mask]) => u.Patch)*): Record[C, Mask] =
val u = recordUpdater[C, Mask]
val arr = IArray.genericWrapArray(data).toArray
fs.foreach: f =>
val patch = f(u)
val idx = patch.idx
arr(idx) = patch.f(arr(idx))
Record(IArray.unsafeFromArray(arr))

inline def selectDynamic(name: String): AnyRef =
apply(compiletime.constValue[Record.IndexOf[name.type, Record.Names[C], 0]])

private object RecordUpdaterImpl extends RecordUpdater[Any, AnyNamedTuple]
def recordUpdater[C, Mask <: AnyNamedTuple]: RecordUpdater[C, Mask] =
RecordUpdaterImpl.asInstanceOf[RecordUpdater[C, Mask]]
sealed trait RecordUpdater[C, Mask <: AnyNamedTuple] extends Selectable:
final case class Patch(idx: Int, f: AnyRef => AnyRef)
type Fields = NamedTuple.Map[
Mask,
[X] =>> (X => X) => Patch
]
def apply(i: Int): (AnyRef => AnyRef) => Patch =
f => Patch(i, f)
inline def selectDynamic(name: String): (AnyRef => AnyRef) => Patch =
apply(compiletime.constValue[Record.IndexOf[name.type, Record.Names[C], 0]])

object Record:
import scala.compiletime.ops.int.*
type Names[C] = NamedTuple.Names[NamedTuple.From[C]]
type IndexOf[N, T <: Tuple, Acc <: Int] <: Int = T match {
case EmptyTuple => -1
case N *: _ => Acc
case _ *: t => IndexOf[N, t, S[Acc]]
}
def fromIArray(data: IArray[AnyRef]): Record[Any, AnyNamedTuple] =
Record(data)

object Internal {
case object Tombstone
}

opaque type WrappedMetadata[C] = Metadata[C]
object WrappedMetadata {
def apply[C](metadata: Metadata[C]): WrappedMetadata[C] = metadata
extension [C](m: WrappedMetadata[C]) {
def metadata: Metadata[C] = m
}
}
class Metadata[C](val f: SimpleTableMacros.Mask[C])(
val metadata0: Table.Metadata[f.Result]
):
def rowExpr(
mappers: DialectTypeMappers
): Queryable.Row[f.Result[Expr], C] =
metadata0
.queryable(
metadata0.walkLabels0,
mappers,
new Table.Metadata.QueryableProxy(metadata0.queryables(mappers, _))
)
.asInstanceOf[Queryable.Row[f.Result[Expr], C]]

object Metadata extends SimpleTableMacros
}
Loading