Skip to content

Commit

Permalink
Arrays (not tested)
Browse files Browse the repository at this point in the history
  • Loading branch information
Katrix committed Jul 20, 2024
1 parent d0c1e16 commit 062501f
Show file tree
Hide file tree
Showing 27 changed files with 576 additions and 178 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ trait DefaultCompleteSql extends SqlQueryPlatform {
override type AnyDbValue = DbValue[Any]

type Impl <: DefaultCompleteImpl
trait DefaultCompleteImpl extends SqlBaseImpl, SqlDbValueImpl:
override def asc[A](v: DbValue[A]): Ord = Ord.Asc(v.unsafeAsAnyDbVal)
override def desc[A](v: DbValue[A]): Ord = Ord.Desc(v.unsafeAsAnyDbVal)
trait DefaultCompleteImpl extends SqlBaseImpl, SqlDbValueImpl, SqlQueriesImpl:
override def asc[A](v: DbValue[A]): Ord = Ord.Asc(v.asAnyDbVal)
override def desc[A](v: DbValue[A]): Ord = Ord.Desc(v.asAnyDbVal)
override def unsafeAsAnyDbVal[A](v: DbValue[A]): DbValue[Any] = v.asInstanceOf[DbValue[Any]]

sealed trait OrdSeq extends SqlOrdSeqBase
Expand All @@ -36,17 +36,11 @@ trait DefaultCompleteSql extends SqlQueryPlatform {
end MultiOrdSeq

type ValueSource[A[_[_]]] = SqlValueSource[A]
type ValueSourceCompanion = SqlValueSource.type
val ValueSource: ValueSourceCompanion = SqlValueSource
type ValueSourceCompanion = SqlValueSourceCompanionImpl
object ValueSource extends SqlValueSourceCompanionImpl

extension [A[_[_]]](sqlValueSource: SqlValueSource[A]) def liftSqlValueSource: ValueSource[A] = sqlValueSource

extension (c: ValueSourceCompanion)
@targetName("valueSourceGetFromQuery") def getFromQuery[A[_[_]]](query: Query[A]): ValueSource[A] =
query match
case baseQuery: SqlQuery.SqlQueryFromStage[A] => baseQuery.valueSource
case _ => SqlValueSource.FromQuery(query)

type Query[A[_[_]]] = SqlQuery[A]
type QueryGrouped[A[_[_]]] = SqlQueryGrouped[A]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,9 @@ trait SqlQueryPlatformBase extends QueryPlatform { platform =>
extension [A](tpe: Type[A])
@targetName("typeTypedChoiceNotNull") protected def typedChoice(
using NotGiven[A <:< Option[?]]
): NullabilityTypeChoice[Codec, A] = tpe.choice.asInstanceOf[NullabilityTypeChoice[Codec, A]]
): NullabilityTypeChoice[Codec, A, tpe.Dimension] = tpe.choice.asInstanceOf[NullabilityTypeChoice[Codec, A, tpe.Dimension]]

extension [A](tpe: Type[Option[A]])
@targetName("typeTypedChoiceNullable") protected def typedChoice: NullabilityTypeChoice[Codec, A] =
tpe.choice.asInstanceOf[NullabilityTypeChoice[Codec, A]]
@targetName("typeTypedChoiceNullable") protected def typedChoice: NullabilityTypeChoice[Codec, A, tpe.Dimension] =
tpe.choice.asInstanceOf[NullabilityTypeChoice[Codec, A, tpe.Dimension]]
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package dataprism.platform.sql.implementations

import dataprism.platform.sql.value.{SqlBitwiseOps, SqlHyperbolicTrigFunctions, SqlTrigFunctions}
import dataprism.platform.sql.value.{SqlArrays, SqlBitwiseOps, SqlHyperbolicTrigFunctions, SqlTrigFunctions}
import dataprism.platform.sql.{DefaultCompleteSql, DefaultSqlOperations, SqlMergeOperations}
import dataprism.sharedast.H2AstRenderer

Expand All @@ -10,7 +10,8 @@ trait H2Platform
SqlMergeOperations,
SqlBitwiseOps,
SqlTrigFunctions,
SqlHyperbolicTrigFunctions {
SqlHyperbolicTrigFunctions,
SqlArrays {
platform =>

override type CastType[A] = Type[A]
Expand Down Expand Up @@ -76,7 +77,8 @@ trait H2Platform
SqlStringApi,
SqlOperationApi,
SqlMergeApi,
SqlQueryApi {
SqlQueryApi,
SqlArraysApi {
export platform.given
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package dataprism.platform.sql.implementations

import cats.syntax.all.*
import dataprism.platform.sql.value.{SqlBitwiseOps, SqlHyperbolicTrigFunctions, SqlTrigFunctions}
import dataprism.platform.sql.value.{SqlArrays, SqlBitwiseOps, SqlHyperbolicTrigFunctions, SqlTrigFunctions}
import dataprism.platform.sql.{DefaultCompleteSql, DefaultSqlOperations, SqlMergeOperations}
import dataprism.sharedast.{PostgresAstRenderer, SqlExpr}
import dataprism.sharedast.PostgresAstRenderer
import dataprism.sql.*

//noinspection SqlNoDataSourceInspection, ScalaUnusedSymbol
Expand All @@ -13,7 +13,8 @@ trait PostgresPlatform
SqlMergeOperations,
SqlBitwiseOps,
SqlTrigFunctions,
SqlHyperbolicTrigFunctions { platform =>
SqlHyperbolicTrigFunctions,
SqlArrays { platform =>

override type InFilterCapability = Unit
override type InMapCapability = Unit
Expand Down Expand Up @@ -91,14 +92,13 @@ trait PostgresPlatform
SqlStringApi,
SqlOperationApi,
SqlMergeApi,
SqlQueryApi {
SqlQueryApi,
SqlArraysApi {
export platform.given
}

lazy val sqlRenderer: PostgresAstRenderer[Codec] =
new PostgresAstRenderer[Codec](AnsiTypes, [A] => (codec: Codec[A]) => codec.name)
type ArrayTypeArgs[A]
protected def arrayType[A](elemType: Type[A])(using extraArrayTypeArgs: ArrayTypeArgs[A]): Type[Seq[A]]

override type CastType[A] = Type[A]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -879,6 +879,18 @@ trait SqlQueries extends SqlQueriesBase { platform: SqlQueryPlatform =>

end SqlQueryCompanionImpl

type Impl <: SqlQueriesImpl & SqlValuesBaseImpl & SqlBaseImpl
trait SqlQueriesImpl {
def queryFunction[A[_[_]]](
function: SqlExpr.FunctionName,
arguments: Seq[AnyDbValue],
types: A[Type]
)(using FA: ApplyKC[A], FT: TraverseKC[A]): Query[A] =
SqlQuery
.SqlQueryFromStage(SqlValueSource.FromTableFunction(function, arguments, types, FA, FT).liftSqlValueSource)
.liftSqlQuery
}

extension [A](query: Query[[F[_]] =>> F[A]])
// TODO: Make use of an implicit conversion here?
@targetName("queryAsMany") override def asMany: Many[A] = query.asDbValue.unsafeDbValAsMany
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ trait SqlQueriesBase extends SqlQueryPlatformBase, SqlDbValuesBase { platform =>

def distinctOnSeq(on: A[DbValue] => Seq[AnyDbValue])(using DistinctOnCapability): Query[A]
def distinctOnK[B[_[_]]: FoldableKC](on: A[DbValue] => B[DbValue])(using DistinctOnCapability): Query[A] =
this.distinctOnSeq(a => on(a).foldMapK([Z] => (v: DbValue[Z]) => Seq(v.unsafeAsAnyDbVal)))
this.distinctOnSeq(a => on(a).foldMapK([Z] => (v: DbValue[Z]) => Seq(v.asAnyDbVal)))

inline def distinctOn[B](on: A[DbValue] => B)(using mr: MapRes[DbValue, B])(
using cap: DistinctOnCapability
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,13 @@ trait SqlValueSources extends SqlValueSourcesBase { platform: SqlQueryPlatform =
rhs: ValueSource[B],
on: (A[DbValue], B[DbValue]) => DbValue[Boolean]
) extends SqlValueSource[[F[_]] =>> (A[Compose2[F, Nullable]], B[Compose2[F, Nullable]])]
case FromTableFunction(
function: SqlExpr.FunctionName,
arguments: Seq[AnyDbValue],
types: A[Type],
apply: ApplyKC[A],
traverseKC: TraverseKC[A]
)

private def mapOptCompose[F[_[_]], X[_], Y[_]](fa: F[Compose2[X, Nullable]])(f: X :~>: Y)(
using F: FunctorKC[F]
Expand Down Expand Up @@ -140,6 +147,7 @@ trait SqlValueSources extends SqlValueSourcesBase { platform: SqlQueryPlatform =
}

make[lt, rt]
case SqlValueSource.FromTableFunction(_, _, _, apply, _) => apply
end applyKC

private def fromPartJoin[A[_[_]], B[_[_]], R[_[_]]](
Expand Down Expand Up @@ -228,8 +236,36 @@ trait SqlValueSources extends SqlValueSourcesBase { platform: SqlQueryPlatform =
SelectAst.From.FullOuterJoin.apply,
(a, b) => (mapJoinNullable[l](a), mapJoinNullable[r](b))
)
case SqlValueSource.FromTableFunction(function, arguments, types, apply, traverse) =>
given TraverseKC[A] = traverse
for {
args <- arguments.traverse(_.ast)
fromName <- State[TaggedState, String](st => (st.withNewQueryNum(st.queryNum + 1), s"y${st.queryNum}"))
dbValsAndNames <- types.traverseK[TagState, Tuple2K[Const[String], DbValue]] {
[X] =>
(tpe: Type[X]) =>
State[TaggedState, (String, DbValue[X])] { st =>
val columnName = s"x${st.columnNum}"
(
st.withNewColumnNum(st.columnNum + 1),
(columnName, SqlDbValue.QueryColumn(columnName, fromName, tpe).lift)
)
}
}

} yield
val names = dbValsAndNames.foldMapK([X] => (t: (String, DbValue[X])) => List(t._1))
val dbVals = dbValsAndNames.mapK([X] => (t: (String, DbValue[X])) => t._2)
ValueSourceAstMetaData(SelectAst.From.FromTableFunction(function, args, fromName, names), dbVals)

end fromPartAndValues
}

trait SqlValueSourceCompanionImpl extends SqlValueSourceCompanion:
def getFromQuery[A[_[_]]](query: Query[A]): ValueSource[A] =
query match
case baseQuery: SqlQuery.SqlQueryFromStage[A] => baseQuery.valueSource
case _ => SqlValueSource.FromQuery(query).liftSqlValueSource

extension [A[_[_]]](sqlValueSource: SqlValueSource[A]) def liftSqlValueSource: ValueSource[A]
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,7 @@
package dataprism.platform.sql.query

import scala.annotation.targetName

import cats.data.State
import cats.syntax.all.*
import dataprism.platform.sql.SqlQueryPlatformBase
import dataprism.sharedast.{SelectAst, SqlExpr}
import dataprism.sql.*
import dataprism.sharedast.SelectAst
import perspective.*

trait SqlValueSourcesBase extends SqlQueryPlatformBase { platform =>
Expand All @@ -21,10 +16,11 @@ trait SqlValueSourcesBase extends SqlQueryPlatformBase { platform =>
}

type ValueSource[A[_[_]]] <: SqlValueSourceBase[A]
type ValueSourceCompanion
val ValueSource: ValueSourceCompanion
trait SqlValueSourceCompanion {
def getFromQuery[A[_[_]]](query: Query[A]): ValueSource[A]
}

extension (c: ValueSourceCompanion)
@targetName("valueSourceGetFromQuery") def getFromQuery[A[_[_]]](query: Query[A]): ValueSource[A]
type ValueSourceCompanion <: SqlValueSourceCompanion
val ValueSource: ValueSourceCompanion

}
146 changes: 146 additions & 0 deletions common/src/main/scala/dataprism/platform/sql/value/SqlArrays.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
package dataprism.platform.sql.value

import scala.annotation.targetName

import cats.Applicative
import dataprism.platform.MapRes
import dataprism.sharedast.SqlExpr
import perspective.{:~>#:, :~>:, ApplyKC, Compose2, TraverseKC}

trait SqlArrays extends SqlDbValuesBase { platform =>

def arrayOfType[A](tpe: Type[A]): Type[Seq[A]]

case class ArrayConcatBinOp[A, LHS <: Seq[A] | A, RHS <: Seq[A] | A](arrType: Type[Seq[A]])
extends BinOp[LHS, RHS, Seq[A]]:
override def name: String = "array_concat"

override def ast: SqlExpr.BinaryOperation = SqlExpr.BinaryOperation.ArrayConcat

override def tpe(lhs: DbValue[LHS], rhs: DbValue[RHS]): Type[Seq[A]] = arrType

trait SqlDbArrayCompanion {
def of[A](value: DbValue[A], values: DbValue[A]*): DbValue[Seq[A]] =
Impl.function(SqlExpr.FunctionName.ArrayConstruction, (value +: values).map(_.asAnyDbVal), arrayOfType(value.tpe))

private type NullableK[A[_[_]]] = [F[_]] =>> A[Compose2[F, Nullable]]

private given nullableUnnestInstance[F[_[_]]](
using FA: ApplyKC[F],
FT: TraverseKC[F]
): (ApplyKC[NullableK[F]] & TraverseKC[NullableK[F]]) = new ApplyKC[NullableK[F]] with TraverseKC[NullableK[F]] {

extension [A[_], C](fa: F[Compose2[A, Nullable]])
override def mapK[B[_]](f: A :~>: B): F[Compose2[B, Nullable]] =
FA.mapK(fa)([X] => (a: A[Nullable[X]]) => f(a))

override def map2K[B[_], Z[_]](fb: F[Compose2[B, Nullable]])(
f: [X] => (A[X], B[X]) => Z[X]
): F[Compose2[Z, Nullable]] =
FA.map2K(fa)(fb)([X] => (a: A[Nullable[X]], b: B[Nullable[X]]) => f(a, b))

override def foldLeftK[B](b: B)(f: B => A :~>#: B): B =
FT.foldLeftK(fa)(b)(b => [X] => (a: A[Nullable[X]]) => f(b)(a))
override def foldRightK[B](b: B)(f: A :~>#: (B => B)): B =
FT.foldRightK(fa)(b)([X] => (a: A[Nullable[X]]) => b => f(a)(b))

override def traverseK[G[_]: Applicative, B[_]](f: A :~>: Compose2[G, B]): G[F[Compose2[B, Nullable]]] =
FT.traverseK(fa)([X] => (a: A[Nullable[X]]) => f(a))
}

def unnestK[A[_[_]]](
arrs: A[Compose2[DbValue, Seq]]
)(using FA: ApplyKC[A], FT: TraverseKC[A]): Query[[F[_]] =>> A[Compose2[F, Nullable]]] =
Impl.queryFunction(
SqlExpr.FunctionName.Unnest,
arrs.foldMapK([X] => (arr: DbValue[Seq[X]]) => List(arr.asAnyDbVal)),
arrs.mapK[Compose2[Type, Nullable]](
[X] =>
(arr: DbValue[Seq[X]]) =>
val tpe = arr.tpe
import scala.compiletime.ops.int.*
tpe
.elementType(using <:<.refl.asInstanceOf[(tpe.Dimension > 0) =:= true])
.choice
.nullable
.asInstanceOf[Type[Nullable[X]]]
)
)

def unnest[T](arrs: T)(using mr: MapRes[Compose2[DbValue, Seq], T]): Query[[F[_]] =>> mr.K[Compose2[F, Nullable]]] =
unnestK(mr.toK(arrs))(using mr.applyKC, mr.traverseKC)
}
type DbArrayCompanion <: SqlDbArrayCompanion
val DbArray: DbArrayCompanion

trait DbArrayLike[Col, A] {
extension (arr: DbValue[Col])
def apply(idx: DbValue[Int])(using n: Nullability[A]): DbValue[n.N[A]]

def cardinality: DbValue[Int]

def concat(other: DbValue[Col]): DbValue[Col]

@targetName("append")
def :+(other: DbValue[A]): DbValue[Col]

@targetName("prepend")
def +:(other: DbValue[A]): DbValue[Col]

def contains(a: DbValue[A]): DbValue[Boolean]

def drop(n: DbValue[Int]): DbValue[Col]
}
object DbArrayLike {
given seqIsDbArray[A]: DbArrayLike[Seq[A], A] with {
extension (arr: DbValue[Seq[A]])

override def apply(idx: DbValue[Int])(using n: Nullability[A]): DbValue[n.N[A]] =
import scala.compiletime.ops.int.*
val tpe = arr.tpe
Impl.function(
SqlExpr.FunctionName.ArrayGet,
Seq(arr.asAnyDbVal, idx.asAnyDbVal),
n.wrapType(
tpe.elementType(using <:<.refl.asInstanceOf[(tpe.Dimension > 0) =:= true]).asInstanceOf[Type[A]]
)
)

override def cardinality: DbValue[Int] =
Impl.function(SqlExpr.FunctionName.Cardinality, Seq(arr.asAnyDbVal), AnsiTypes.integer)

override def concat(other: DbValue[Seq[A]]): DbValue[Seq[A]] =
Impl.binaryOp(arr, other, ArrayConcatBinOp(arr.tpe))

@targetName("append")
override def :+(other: DbValue[A]): DbValue[Seq[A]] = Impl.binaryOp(arr, other, ArrayConcatBinOp(arr.tpe))

@targetName("prepend")
override def +:(other: DbValue[A]): DbValue[Seq[A]] = Impl.binaryOp(other, arr, ArrayConcatBinOp(arr.tpe))

override def contains(a: DbValue[A]): DbValue[Boolean] =
Impl.function(SqlExpr.FunctionName.ArrayContains, Seq(arr.asAnyDbVal, a.asAnyDbVal), AnsiTypes.boolean)

override def drop(n: DbValue[Int]): DbValue[Seq[A]] =
Impl.function(SqlExpr.FunctionName.TrimArray, Seq(arr.asAnyDbVal, n.asAnyDbVal), arr.tpe)
}
}

type Impl <: SqlArraysImpl & SqlValuesBaseImpl & SqlBaseImpl
trait SqlArraysImpl {
def queryFunction[A[_[_]]: ApplyKC: TraverseKC](
function: SqlExpr.FunctionName,
arguments: Seq[AnyDbValue],
types: A[Type]
): Query[A]
}

type Api <: SqlArraysApi & SqlDbValueApi & QueryApi
trait SqlArraysApi {
export platform.DbArrayLike
export platform.DbArrayLike.given

type DbArrayCompanion = platform.DbArrayCompanion
inline def DbArray: DbArrayCompanion = platform.DbArray
}
}
Loading

0 comments on commit 062501f

Please sign in to comment.