{-# OPTIONS_GHC -fno-warn-orphans #-}

-- | Convert the simple BigQuery AST to an SQL query, ready to be passed
-- to the odbc package's query/exec functions.
module Hasura.Backends.BigQuery.ToQuery
  ( fromSelect,
    fromReselect,
    fromExpression,
    toBuilderFlat,
    toBuilderPretty,
    toTextFlat,
    toTextPretty,
    Printer (..),
    renderBuilderFlat,
    renderBuilderPretty,
    paramName,
  )
where

import Data.Aeson (ToJSON (..))
import Data.Bifunctor
import Data.Containers.ListUtils
import Data.HashMap.Strict.InsOrd qualified as InsOrdHashMap
import Data.List (intersperse)
import Data.List.NonEmpty qualified as NE
import Data.String
import Data.Text qualified as T
import Data.Text.Lazy qualified as LT
import Data.Text.Lazy.Builder (Builder)
import Data.Text.Lazy.Builder qualified as LT
import Data.Tuple
import Data.Vector qualified as V
import Hasura.Backends.BigQuery.Types
import Hasura.NativeQuery.Metadata (InterpolatedItem (..), InterpolatedQuery (..))
import Hasura.Prelude hiding (second)

--------------------------------------------------------------------------------
-- Types

data Printer
  = SeqPrinter [Printer]
  | SepByPrinter Printer [Printer]
  | NewlinePrinter
  | UnsafeTextPrinter Text
  | IndentPrinter Int Printer
  | ValuePrinter TypedValue
  deriving (Int -> Printer -> ShowS
[Printer] -> ShowS
Printer -> String
(Int -> Printer -> ShowS)
-> (Printer -> String) -> ([Printer] -> ShowS) -> Show Printer
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> Printer -> ShowS
showsPrec :: Int -> Printer -> ShowS
$cshow :: Printer -> String
show :: Printer -> String
$cshowList :: [Printer] -> ShowS
showList :: [Printer] -> ShowS
Show, Printer -> Printer -> Bool
(Printer -> Printer -> Bool)
-> (Printer -> Printer -> Bool) -> Eq Printer
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: Printer -> Printer -> Bool
== :: Printer -> Printer -> Bool
$c/= :: Printer -> Printer -> Bool
/= :: Printer -> Printer -> Bool
Eq)

instance IsString Printer where
  fromString :: String -> Printer
fromString = Text -> Printer
UnsafeTextPrinter (Text -> Printer) -> (String -> Text) -> String -> Printer
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Text
forall a. IsString a => String -> a
fromString

(<+>) :: Printer -> Printer -> Printer
<+> :: Printer -> Printer -> Printer
(<+>) Printer
x Printer
y = [Printer] -> Printer
SeqPrinter [Printer
x, Printer
y]

--------------------------------------------------------------------------------
-- Instances

-- This is a debug instance, only here because it avoids a circular
-- dependency between this module and Types.hs.
instance ToJSON Expression where
  toJSON :: Expression -> Value
toJSON = Text -> Value
forall a. ToJSON a => a -> Value
toJSON (Text -> Value) -> (Expression -> Text) -> Expression -> Value
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Printer -> Text
toTextPretty (Printer -> Text) -> (Expression -> Printer) -> Expression -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Expression -> Printer
fromExpression

--------------------------------------------------------------------------------
-- Printer generators

fromExpression :: Expression -> Printer
fromExpression :: Expression -> Printer
fromExpression =
  \case
    CastExpression Expression
e ScalarType
scalarType ->
      Printer
"CAST(" Printer -> Printer -> Printer
<+> Expression -> Printer
fromExpression Expression
e Printer -> Printer -> Printer
<+> Printer
" AS " Printer -> Printer -> Printer
<+> ScalarType -> Printer
fromScalarType ScalarType
scalarType Printer -> Printer -> Printer
<+> Printer
")"
    InExpression Expression
e (TypedValue ScalarType
ty Value
val) ->
      Printer
"(" Printer -> Printer -> Printer
<+> Expression -> Printer
fromExpression Expression
e Printer -> Printer -> Printer
<+> Printer
") IN UNNEST(" Printer -> Printer -> Printer
<+> ScalarType -> Value -> Printer
fromValue ScalarType
ty Value
val Printer -> Printer -> Printer
<+> Printer
")"
    JsonQueryExpression Expression
e -> Printer
"JSON_QUERY(" Printer -> Printer -> Printer
<+> Expression -> Printer
fromExpression Expression
e Printer -> Printer -> Printer
<+> Printer
")"
    JsonValueExpression Expression
e JsonPath
path ->
      Printer
"JSON_VALUE(" Printer -> Printer -> Printer
<+> Expression -> Printer
fromExpression Expression
e Printer -> Printer -> Printer
<+> JsonPath -> Printer
fromPath JsonPath
path Printer -> Printer -> Printer
<+> Printer
")"
    ValueExpression (TypedValue ScalarType
ty Value
val) -> ScalarType -> Value -> Printer
fromValue ScalarType
ty Value
val
    AndExpression [Expression]
xs ->
      Printer -> [Printer] -> Printer
SepByPrinter
        (Printer
NewlinePrinter Printer -> Printer -> Printer
<+> Printer
"AND ")
        ( NonEmpty Printer -> [Printer]
forall a. NonEmpty a -> [a]
forall (t :: * -> *) a. Foldable t => t a -> [a]
toList
            ( (Expression -> Printer) -> NonEmpty Expression -> NonEmpty Printer
forall a b. (a -> b) -> NonEmpty a -> NonEmpty b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap
                (\Expression
x -> Printer
"(" Printer -> Printer -> Printer
<+> Expression -> Printer
fromExpression Expression
x Printer -> Printer -> Printer
<+> Printer
")")
                (NonEmpty Expression
-> Maybe (NonEmpty Expression) -> NonEmpty Expression
forall a. a -> Maybe a -> a
fromMaybe (Expression -> NonEmpty Expression
forall a. a -> NonEmpty a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Expression
trueExpression) ([Expression] -> Maybe (NonEmpty Expression)
forall a. [a] -> Maybe (NonEmpty a)
NE.nonEmpty [Expression]
xs))
            )
        )
    OrExpression [Expression]
xs ->
      Printer -> [Printer] -> Printer
SepByPrinter
        (Printer
NewlinePrinter Printer -> Printer -> Printer
<+> Printer
" OR ")
        ( NonEmpty Printer -> [Printer]
forall a. NonEmpty a -> [a]
forall (t :: * -> *) a. Foldable t => t a -> [a]
toList
            ( (Expression -> Printer) -> NonEmpty Expression -> NonEmpty Printer
forall a b. (a -> b) -> NonEmpty a -> NonEmpty b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap
                (\Expression
x -> Printer
"(" Printer -> Printer -> Printer
<+> Expression -> Printer
fromExpression Expression
x Printer -> Printer -> Printer
<+> Printer
")")
                (NonEmpty Expression
-> Maybe (NonEmpty Expression) -> NonEmpty Expression
forall a. a -> Maybe a -> a
fromMaybe (Expression -> NonEmpty Expression
forall a. a -> NonEmpty a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Expression
falseExpression) ([Expression] -> Maybe (NonEmpty Expression)
forall a. [a] -> Maybe (NonEmpty a)
NE.nonEmpty [Expression]
xs))
            )
        )
    NotExpression Expression
expression -> Printer
"NOT " Printer -> Printer -> Printer
<+> (Expression -> Printer
fromExpression Expression
expression)
    ExistsExpression Select
select ->
      Printer
"EXISTS (" Printer -> Printer -> Printer
<+> Int -> Printer -> Printer
IndentPrinter Int
9 (Select -> Printer
fromSelect Select
select) Printer -> Printer -> Printer
<+> Printer
")"
    IsNullExpression Expression
expression ->
      Printer
"(" Printer -> Printer -> Printer
<+> Expression -> Printer
fromExpression Expression
expression Printer -> Printer -> Printer
<+> Printer
") IS NULL"
    IsNotNullExpression Expression
expression ->
      Printer
"(" Printer -> Printer -> Printer
<+> Expression -> Printer
fromExpression Expression
expression Printer -> Printer -> Printer
<+> Printer
") IS NOT NULL"
    ColumnExpression FieldName
fieldName -> FieldName -> Printer
fromFieldName FieldName
fieldName
    EqualExpression Expression
x Expression
y ->
      Printer
"(" Printer -> Printer -> Printer
<+> Expression -> Printer
fromExpression Expression
x Printer -> Printer -> Printer
<+> Printer
") = (" Printer -> Printer -> Printer
<+> Expression -> Printer
fromExpression Expression
y Printer -> Printer -> Printer
<+> Printer
")"
    NotEqualExpression Expression
x Expression
y ->
      Printer
"(" Printer -> Printer -> Printer
<+> Expression -> Printer
fromExpression Expression
x Printer -> Printer -> Printer
<+> Printer
") != (" Printer -> Printer -> Printer
<+> Expression -> Printer
fromExpression Expression
y Printer -> Printer -> Printer
<+> Printer
")"
    ToStringExpression Expression
e -> Printer
"CONCAT(" Printer -> Printer -> Printer
<+> Expression -> Printer
fromExpression Expression
e Printer -> Printer -> Printer
<+> Printer
", '')"
    SelectExpression Select
s -> Printer
"(" Printer -> Printer -> Printer
<+> Int -> Printer -> Printer
IndentPrinter Int
1 (Select -> Printer
fromSelect Select
s) Printer -> Printer -> Printer
<+> Printer
")"
    ListExpression [Expression]
xs -> Printer
" UNNEST ([" Printer -> Printer -> Printer
<+> Printer -> [Printer] -> Printer
SepByPrinter Printer
", " (Expression -> Printer
fromExpression (Expression -> Printer) -> [Expression] -> [Printer]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [Expression]
xs) Printer -> Printer -> Printer
<+> Printer
"])"
    OpExpression Op
op Expression
x Expression
y ->
      Printer
"("
        Printer -> Printer -> Printer
<+> Expression -> Printer
fromExpression Expression
x
        Printer -> Printer -> Printer
<+> Printer
") "
        Printer -> Printer -> Printer
<+> Op -> Printer
fromOp Op
op
        Printer -> Printer -> Printer
<+> Expression -> Printer
fromExpression Expression
y
    FunctionExpression FunctionName
function [Expression]
args ->
      FunctionName -> Printer
fromFunctionName FunctionName
function Printer -> Printer -> Printer
<+> Printer
"(" Printer -> Printer -> Printer
<+> Printer -> [Printer] -> Printer
SepByPrinter Printer
", " (Expression -> Printer
fromExpression (Expression -> Printer) -> [Expression] -> [Printer]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [Expression]
args) Printer -> Printer -> Printer
<+> Printer
")"
    ConditionalProjection Expression
expression FieldName
fieldName ->
      Printer
"(CASE WHEN("
        Printer -> Printer -> Printer
<+> Expression -> Printer
fromExpression Expression
expression
        Printer -> Printer -> Printer
<+> Printer
") THEN "
        Printer -> Printer -> Printer
<+> FieldName -> Printer
fromFieldName FieldName
fieldName
        Printer -> Printer -> Printer
<+> Printer
" ELSE NULL END)"
    FunctionNamedArgument Text
argName Expression
argValue ->
      Text -> Printer
fromNameText Text
argName Printer -> Printer -> Printer
<+> Printer
" => " Printer -> Printer -> Printer
<+> Expression -> Printer
fromExpression Expression
argValue

fromScalarType :: ScalarType -> Printer
fromScalarType :: ScalarType -> Printer
fromScalarType =
  \case
    ScalarType
StringScalarType -> Printer
"STRING"
    ScalarType
BytesScalarType -> Printer
"BYTES"
    ScalarType
IntegerScalarType -> Printer
"INT64"
    ScalarType
FloatScalarType -> Printer
"FLOAT64"
    ScalarType
BoolScalarType -> Printer
"BOOL"
    ScalarType
TimestampScalarType -> Printer
"TIMESTAMP"
    ScalarType
DateScalarType -> Printer
"DATE"
    ScalarType
TimeScalarType -> Printer
"TIME"
    ScalarType
DatetimeScalarType -> Printer
"DATETIME"
    ScalarType
GeographyScalarType -> Printer
"GEOGRAPHY"
    ScalarType
StructScalarType -> Printer
"STRUCT"
    ScalarType
DecimalScalarType -> Printer
"DECIMAL"
    ScalarType
BigDecimalScalarType -> Printer
"BIGDECIMAL"
    ScalarType
JsonScalarType -> Printer
"JSON"

fromOp :: Op -> Printer
fromOp :: Op -> Printer
fromOp =
  \case
    Op
LessOp -> Printer
"<"
    Op
MoreOp -> Printer
">"
    Op
MoreOrEqualOp -> Printer
">="
    Op
LessOrEqualOp -> Printer
"<="
    Op
InOp -> Printer
"IN"
    Op
NotInOp -> Printer
"NOT IN"
    Op
LikeOp -> Printer
"LIKE"
    Op
NotLikeOp -> Printer
"NOT LIKE"

fromPath :: JsonPath -> Printer
fromPath :: JsonPath -> Printer
fromPath JsonPath
path =
  Printer
", " Printer -> Printer -> Printer
<+> JsonPath -> Printer
string JsonPath
path
  where
    string :: JsonPath -> Printer
string =
      Expression -> Printer
fromExpression
        (Expression -> Printer)
-> (JsonPath -> Expression) -> JsonPath -> Printer
forall b c a. (b -> c) -> (a -> b) -> a -> c
. TypedValue -> Expression
ValueExpression
        (TypedValue -> Expression)
-> (JsonPath -> TypedValue) -> JsonPath -> Expression
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ScalarType -> Value -> TypedValue
TypedValue ScalarType
StringScalarType
        (Value -> TypedValue)
-> (JsonPath -> Value) -> JsonPath -> TypedValue
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Value
StringValue
        (Text -> Value) -> (JsonPath -> Text) -> JsonPath -> Value
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Text
LT.toStrict
        (Text -> Text) -> (JsonPath -> Text) -> JsonPath -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Builder -> Text
LT.toLazyText
        (Builder -> Text) -> (JsonPath -> Builder) -> JsonPath -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. JsonPath -> Builder
go
    go :: JsonPath -> Builder
go =
      \case
        JsonPath
RootPath -> Builder
"$"
        IndexPath JsonPath
r Integer
i -> JsonPath -> Builder
go JsonPath
r Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
"[" Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> String -> Builder
LT.fromString (Integer -> String
forall a. Show a => a -> String
show Integer
i) Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
"]"
        FieldPath JsonPath
r Text
f -> JsonPath -> Builder
go JsonPath
r Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
"." Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Text -> Builder
LT.fromText Text
f

fromFieldName :: FieldName -> Printer
fromFieldName :: FieldName -> Printer
fromFieldName (FieldName {Text
fieldName :: Text
fieldNameEntity :: Text
$sel:fieldName:FieldName :: FieldName -> Text
$sel:fieldNameEntity:FieldName :: FieldName -> Text
..}) =
  Text -> Printer
fromNameText Text
fieldNameEntity Printer -> Printer -> Printer
<+> Printer
"." Printer -> Printer -> Printer
<+> Text -> Printer
fromNameText Text
fieldName

fromSelect :: Select -> Printer
fromSelect :: Select -> Printer
fromSelect Select {[FieldName]
[Join]
Maybe [Text]
Maybe (NonEmpty OrderBy)
Maybe Expression
Maybe With
NonEmpty Projection
From
Top
AsStruct
Cardinality
Where
selectWith :: Maybe With
selectTop :: Top
selectAsStruct :: AsStruct
selectProjections :: NonEmpty Projection
selectFrom :: From
selectJoins :: [Join]
selectWhere :: Where
selectOrderBy :: Maybe (NonEmpty OrderBy)
selectOffset :: Maybe Expression
selectGroupBy :: [FieldName]
selectFinalWantedFields :: Maybe [Text]
selectCardinality :: Cardinality
$sel:selectWith:Select :: Select -> Maybe With
$sel:selectTop:Select :: Select -> Top
$sel:selectAsStruct:Select :: Select -> AsStruct
$sel:selectProjections:Select :: Select -> NonEmpty Projection
$sel:selectFrom:Select :: Select -> From
$sel:selectJoins:Select :: Select -> [Join]
$sel:selectWhere:Select :: Select -> Where
$sel:selectOrderBy:Select :: Select -> Maybe (NonEmpty OrderBy)
$sel:selectOffset:Select :: Select -> Maybe Expression
$sel:selectGroupBy:Select :: Select -> [FieldName]
$sel:selectFinalWantedFields:Select :: Select -> Maybe [Text]
$sel:selectCardinality:Select :: Select -> Cardinality
..} = Printer
finalExpression
  where
    finalExpression :: Printer
finalExpression = Printer
inner
    projections :: Printer
projections =
      Printer -> [Printer] -> Printer
SepByPrinter
        (Printer
"," Printer -> Printer -> Printer
<+> Printer
NewlinePrinter)
        ((Projection -> Printer) -> [Projection] -> [Printer]
forall a b. (a -> b) -> [a] -> [b]
map Projection -> Printer
fromProjection (NonEmpty Projection -> [Projection]
forall a. NonEmpty a -> [a]
forall (t :: * -> *) a. Foldable t => t a -> [a]
toList (NonEmpty Projection -> NonEmpty Projection
cleanProjections NonEmpty Projection
selectProjections)))
    fromAsStruct :: AsStruct -> Printer
fromAsStruct = \case
      AsStruct
AsStruct -> Printer
"AS STRUCT"
      AsStruct
NoAsStruct -> Printer
""
    interpolatedQuery :: InterpolatedItem Expression -> Printer
interpolatedQuery = \case
      IIText Text
t -> Text -> Printer
UnsafeTextPrinter Text
t
      IIVariable Expression
v -> Expression -> Printer
fromExpression Expression
v
    fromWith :: Maybe With -> Printer
fromWith = \case
      Just (With NonEmpty (Aliased (InterpolatedQuery Expression))
expressions) -> do
        let go :: InterpolatedQuery Expression -> Printer
            go :: InterpolatedQuery Expression -> Printer
go = (InterpolatedItem Expression -> Printer -> Printer)
-> Printer -> [InterpolatedItem Expression] -> Printer
forall a b. (a -> b -> b) -> b -> [a] -> b
forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr (Printer -> Printer -> Printer
(<+>) (Printer -> Printer -> Printer)
-> (InterpolatedItem Expression -> Printer)
-> InterpolatedItem Expression
-> Printer
-> Printer
forall b c a. (b -> c) -> (a -> b) -> a -> c
. InterpolatedItem Expression -> Printer
interpolatedQuery) Printer
"" ([InterpolatedItem Expression] -> Printer)
-> (InterpolatedQuery Expression -> [InterpolatedItem Expression])
-> InterpolatedQuery Expression
-> Printer
forall b c a. (b -> c) -> (a -> b) -> a -> c
. InterpolatedQuery Expression -> [InterpolatedItem Expression]
forall variable.
InterpolatedQuery variable -> [InterpolatedItem variable]
getInterpolatedQuery

        Printer
"WITH "
          Printer -> Printer -> Printer
<+> Printer -> [Printer] -> Printer
SepByPrinter
            (Printer
"," Printer -> Printer -> Printer
<+> Printer
NewlinePrinter)
            [ Text -> Printer
fromNameText Text
alias Printer -> Printer -> Printer
<+> Printer
" AS " Printer -> Printer -> Printer
<+> Printer -> Printer
parens (InterpolatedQuery Expression -> Printer
go InterpolatedQuery Expression
thing)
              | Aliased InterpolatedQuery Expression
thing Text
alias <- NonEmpty (Aliased (InterpolatedQuery Expression))
-> [Aliased (InterpolatedQuery Expression)]
forall a. NonEmpty a -> [a]
forall (t :: * -> *) a. Foldable t => t a -> [a]
toList NonEmpty (Aliased (InterpolatedQuery Expression))
expressions
            ]
      Maybe With
Nothing -> Printer
""
    fromJoinType :: JoinType -> a
fromJoinType JoinType
LeftOuter = a
"LEFT OUTER JOIN "
    fromJoinType JoinType
Inner = a
"INNER JOIN "
    inner :: Printer
inner =
      Printer -> [Printer] -> Printer
SepByPrinter
        Printer
NewlinePrinter
        [ Maybe With -> Printer
fromWith Maybe With
selectWith,
          Printer
"SELECT ",
          AsStruct -> Printer
fromAsStruct AsStruct
selectAsStruct,
          Int -> Printer -> Printer
IndentPrinter Int
7 Printer
projections,
          Printer
"FROM " Printer -> Printer -> Printer
<+> Int -> Printer -> Printer
IndentPrinter Int
5 (From -> Printer
fromFrom From
selectFrom),
          Printer -> [Printer] -> Printer
SepByPrinter
            Printer
NewlinePrinter
            ( (Join -> Printer) -> [Join] -> [Printer]
forall a b. (a -> b) -> [a] -> [b]
map
                ( \Join {[(FieldName, FieldName)]
Maybe Text
Text
EntityAlias
JoinSource
JoinProvenance
JoinType
joinSource :: JoinSource
joinAlias :: EntityAlias
joinOn :: [(FieldName, FieldName)]
joinProvenance :: JoinProvenance
joinFieldName :: Text
joinExtractPath :: Maybe Text
joinRightTable :: EntityAlias
joinType :: JoinType
$sel:joinSource:Join :: Join -> JoinSource
$sel:joinAlias:Join :: Join -> EntityAlias
$sel:joinOn:Join :: Join -> [(FieldName, FieldName)]
$sel:joinProvenance:Join :: Join -> JoinProvenance
$sel:joinFieldName:Join :: Join -> Text
$sel:joinExtractPath:Join :: Join -> Maybe Text
$sel:joinRightTable:Join :: Join -> EntityAlias
$sel:joinType:Join :: Join -> JoinType
..} ->
                    [Printer] -> Printer
SeqPrinter
                      [ JoinType -> Printer
forall {a}. IsString a => JoinType -> a
fromJoinType JoinType
joinType
                          Printer -> Printer -> Printer
<+> Int -> Printer -> Printer
IndentPrinter Int
16 (JoinSource -> Printer
fromJoinSource JoinSource
joinSource),
                        Printer
NewlinePrinter,
                        Printer
"AS " Printer -> Printer -> Printer
<+> EntityAlias -> Printer
fromJoinAlias EntityAlias
joinAlias,
                        Printer
NewlinePrinter,
                        Printer
"ON ("
                          Printer -> Printer -> Printer
<+> Int -> Printer -> Printer
IndentPrinter
                            Int
4
                            ( Printer -> [Printer] -> Printer
SepByPrinter
                                (Printer
" AND " Printer -> Printer -> Printer
<+> Printer
NewlinePrinter)
                                (((FieldName, FieldName) -> Printer)
-> [(FieldName, FieldName)] -> [Printer]
forall a b. (a -> b) -> [a] -> [b]
map (FieldName, FieldName) -> Printer
fromOn [(FieldName, FieldName)]
joinOn)
                            )
                          Printer -> Printer -> Printer
<+> Printer
")"
                      ]
                )
                [Join]
selectJoins
            ),
          Where -> Printer
fromWhere Where
selectWhere,
          Top -> Maybe Expression -> Maybe (NonEmpty OrderBy) -> Printer
fromOrderBys Top
selectTop Maybe Expression
selectOffset Maybe (NonEmpty OrderBy)
selectOrderBy,
          case [FieldName]
selectGroupBy of
            [] -> Printer
""
            [FieldName]
fieldNames ->
              Printer
"GROUP BY " Printer -> Printer -> Printer
<+> Printer -> [Printer] -> Printer
SepByPrinter Printer
", " ((FieldName -> Printer) -> [FieldName] -> [Printer]
forall a b. (a -> b) -> [a] -> [b]
map FieldName -> Printer
fromFieldName [FieldName]
fieldNames)
        ]

fromOn :: (FieldName, FieldName) -> Printer
fromOn :: (FieldName, FieldName) -> Printer
fromOn (FieldName
x, FieldName
y) = FieldName -> Printer
fromFieldName FieldName
x Printer -> Printer -> Printer
<+> Printer
" = " Printer -> Printer -> Printer
<+> FieldName -> Printer
fromFieldName FieldName
y

fromJoinSource :: JoinSource -> Printer
fromJoinSource :: JoinSource -> Printer
fromJoinSource =
  \case
    JoinSelect Select
select -> Printer
"(" Printer -> Printer -> Printer
<+> Int -> Printer -> Printer
IndentPrinter Int
1 (Select -> Printer
fromSelect Select
select) Printer -> Printer -> Printer
<+> Printer
")"

-- We're not using existingJoins at the moment, which was used to
-- avoid re-joining on the same table twice.
-- JoinReselect reselect -> "(" <+> fromReselect reselect <+> ")"

fromReselect :: Reselect -> Printer
fromReselect :: Reselect -> Printer
fromReselect Reselect {NonEmpty Projection
Where
reselectProjections :: NonEmpty Projection
reselectWhere :: Where
$sel:reselectProjections:Reselect :: Reselect -> NonEmpty Projection
$sel:reselectWhere:Reselect :: Reselect -> Where
..} =
  Printer -> [Printer] -> Printer
SepByPrinter
    Printer
NewlinePrinter
    [ Printer
"SELECT "
        Printer -> Printer -> Printer
<+> Int -> Printer -> Printer
IndentPrinter Int
7 Printer
projections,
      Where -> Printer
fromWhere Where
reselectWhere
    ]
  where
    projections :: Printer
projections =
      Printer -> [Printer] -> Printer
SepByPrinter
        (Printer
"," Printer -> Printer -> Printer
<+> Printer
NewlinePrinter)
        ((Projection -> Printer) -> [Projection] -> [Printer]
forall a b. (a -> b) -> [a] -> [b]
map Projection -> Printer
fromProjection (NonEmpty Projection -> [Projection]
forall a. NonEmpty a -> [a]
forall (t :: * -> *) a. Foldable t => t a -> [a]
toList (NonEmpty Projection -> NonEmpty Projection
cleanProjections NonEmpty Projection
reselectProjections)))

fromOrderBys ::
  Top -> Maybe Expression -> Maybe (NonEmpty OrderBy) -> Printer
fromOrderBys :: Top -> Maybe Expression -> Maybe (NonEmpty OrderBy) -> Printer
fromOrderBys Top
NoTop Maybe Expression
Nothing Maybe (NonEmpty OrderBy)
Nothing = Printer
"" -- An ORDER BY is wasteful if not needed.
fromOrderBys Top
top Maybe Expression
moffset Maybe (NonEmpty OrderBy)
morderBys =
  Printer -> [Printer] -> Printer
SepByPrinter
    Printer
NewlinePrinter
    [ case Maybe (NonEmpty OrderBy)
morderBys of
        Maybe (NonEmpty OrderBy)
Nothing -> Printer
""
        Just NonEmpty OrderBy
orderBys ->
          [Printer] -> Printer
SeqPrinter
            [ Printer
"ORDER BY ",
              Printer -> [Printer] -> Printer
SepByPrinter
                (Printer
"," Printer -> Printer -> Printer
<+> Printer
NewlinePrinter)
                ((OrderBy -> Printer) -> [OrderBy] -> [Printer]
forall a b. (a -> b) -> [a] -> [b]
map OrderBy -> Printer
fromOrderBy (NonEmpty OrderBy -> [OrderBy]
forall a. NonEmpty a -> [a]
forall (t :: * -> *) a. Foldable t => t a -> [a]
toList NonEmpty OrderBy
orderBys))
            ],
      case (Top
top, Maybe Expression
moffset) of
        (Top
NoTop, Maybe Expression
Nothing) -> Printer
""
        (Top
NoTop, Just Expression
offset) ->
          Printer
"LIMIT 9223372036854775807 /* Maximum */"
            -- Above: OFFSET is not supported without a LIMIT, therefore
            -- we set LIMIT to the maximum integer value. Such a large
            -- number of rows (9 quintillion) would not be possible to
            -- service: 9223 petabytes. No machine has such capacity at
            -- present.
            Printer -> Printer -> Printer
<+> Printer
" OFFSET "
            Printer -> Printer -> Printer
<+> Expression -> Printer
fromExpression Expression
offset
        (Top Int64
n, Maybe Expression
Nothing) -> Printer
"LIMIT " Printer -> Printer -> Printer
<+> ScalarType -> Value -> Printer
fromValue ScalarType
IntegerScalarType (Int64 -> Value
IntegerValue (Int64 -> Int64
intToInt64 Int64
n))
        (Top Int64
n, Just Expression
offset) ->
          Printer
"LIMIT "
            Printer -> Printer -> Printer
<+> ScalarType -> Value -> Printer
fromValue ScalarType
IntegerScalarType (Int64 -> Value
IntegerValue (Int64 -> Int64
intToInt64 Int64
n))
            Printer -> Printer -> Printer
<+> Printer
" OFFSET "
            Printer -> Printer -> Printer
<+> Expression -> Printer
fromExpression Expression
offset
    ]

fromOrderBy :: OrderBy -> Printer
fromOrderBy :: OrderBy -> Printer
fromOrderBy OrderBy {FieldName
NullsOrder
Order
orderByFieldName :: FieldName
orderByOrder :: Order
orderByNullsOrder :: NullsOrder
$sel:orderByFieldName:OrderBy :: OrderBy -> FieldName
$sel:orderByOrder:OrderBy :: OrderBy -> Order
$sel:orderByNullsOrder:OrderBy :: OrderBy -> NullsOrder
..} =
  Printer
"("
    Printer -> Printer -> Printer
<+> FieldName -> Printer
fromFieldName FieldName
orderByFieldName
    Printer -> Printer -> Printer
<+> Printer
") "
    Printer -> Printer -> Printer
<+> Order -> Printer
fromOrder Order
orderByOrder
    Printer -> Printer -> Printer
<+> NullsOrder -> Printer
fromNullsOrder NullsOrder
orderByNullsOrder

fromOrder :: Order -> Printer
fromOrder :: Order -> Printer
fromOrder =
  \case
    Order
AscOrder -> Printer
"ASC"
    Order
DescOrder -> Printer
"DESC"

fromNullsOrder :: NullsOrder -> Printer
fromNullsOrder :: NullsOrder -> Printer
fromNullsOrder =
  \case
    NullsOrder
NullsAnyOrder -> Printer
""
    NullsOrder
NullsFirst -> Printer
" NULLS FIRST"
    NullsOrder
NullsLast -> Printer
" NULLS LAST"

fromJoinAlias :: EntityAlias -> Printer
fromJoinAlias :: EntityAlias -> Printer
fromJoinAlias EntityAlias {Text
entityAliasText :: Text
$sel:entityAliasText:EntityAlias :: EntityAlias -> Text
entityAliasText} =
  Text -> Printer
fromNameText Text
entityAliasText

fromProjection :: Projection -> Printer
fromProjection :: Projection -> Printer
fromProjection =
  \case
    WindowProjection Aliased WindowFunction
aliasedWindowFunction ->
      Aliased Printer -> Printer
fromAliased ((WindowFunction -> Printer)
-> Aliased WindowFunction -> Aliased Printer
forall a b. (a -> b) -> Aliased a -> Aliased b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap WindowFunction -> Printer
fromWindowFunction Aliased WindowFunction
aliasedWindowFunction)
    ExpressionProjection Aliased Expression
aliasedExpression ->
      Aliased Printer -> Printer
fromAliased ((Expression -> Printer) -> Aliased Expression -> Aliased Printer
forall a b. (a -> b) -> Aliased a -> Aliased b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Expression -> Printer
fromExpression Aliased Expression
aliasedExpression)
    FieldNameProjection Aliased FieldName
aliasedFieldName ->
      Aliased Printer -> Printer
fromAliased ((FieldName -> Printer) -> Aliased FieldName -> Aliased Printer
forall a b. (a -> b) -> Aliased a -> Aliased b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap FieldName -> Printer
fromFieldName Aliased FieldName
aliasedFieldName)
    AggregateProjection Aliased Aggregate
aliasedAggregate ->
      Aliased Printer -> Printer
fromAliased ((Aggregate -> Printer) -> Aliased Aggregate -> Aliased Printer
forall a b. (a -> b) -> Aliased a -> Aliased b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Aggregate -> Printer
fromAggregate Aliased Aggregate
aliasedAggregate)
    AggregateProjections Aliased (NonEmpty (Aliased Aggregate))
aliasedAggregates ->
      Aliased Printer -> Printer
fromAliased
        ( (NonEmpty (Aliased Aggregate) -> Printer)
-> Aliased (NonEmpty (Aliased Aggregate)) -> Aliased Printer
forall a b. (a -> b) -> Aliased a -> Aliased b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap
            ( \NonEmpty (Aliased Aggregate)
aggs ->
                Printer
"STRUCT("
                  Printer -> Printer -> Printer
<+> Int -> Printer -> Printer
IndentPrinter
                    Int
7
                    ( Printer -> [Printer] -> Printer
SepByPrinter
                        Printer
", "
                        ((Aliased Aggregate -> Printer) -> [Aliased Aggregate] -> [Printer]
forall a b. (a -> b) -> [a] -> [b]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (Aliased Printer -> Printer
fromAliased (Aliased Printer -> Printer)
-> (Aliased Aggregate -> Aliased Printer)
-> Aliased Aggregate
-> Printer
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Aggregate -> Printer) -> Aliased Aggregate -> Aliased Printer
forall a b. (a -> b) -> Aliased a -> Aliased b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Aggregate -> Printer
fromAggregate) (NonEmpty (Aliased Aggregate) -> [Aliased Aggregate]
forall a. NonEmpty a -> [a]
forall (t :: * -> *) a. Foldable t => t a -> [a]
toList NonEmpty (Aliased Aggregate)
aggs))
                    )
                  Printer -> Printer -> Printer
<+> Printer
")"
            )
            Aliased (NonEmpty (Aliased Aggregate))
aliasedAggregates
        )
    Projection
StarProjection -> Printer
"*"
    ArrayAggProjection Aliased ArrayAgg
aliasedAgg -> Aliased Printer -> Printer
fromAliased ((ArrayAgg -> Printer) -> Aliased ArrayAgg -> Aliased Printer
forall a b. (a -> b) -> Aliased a -> Aliased b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ArrayAgg -> Printer
fromArrayAgg Aliased ArrayAgg
aliasedAgg)
    EntityProjection Aliased [(FieldName, FieldOrigin)]
aliasedEntity ->
      Aliased Printer -> Printer
fromAliased
        ( ([(FieldName, FieldOrigin)] -> Printer)
-> Aliased [(FieldName, FieldOrigin)] -> Aliased Printer
forall a b. (a -> b) -> Aliased a -> Aliased b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap
            ( \([(FieldName, FieldOrigin)]
fields :: [(FieldName, FieldOrigin)]) ->
                -- Example:
                --   STRUCT(
                --     IFNULL(
                --       `aa_articles1`.`aggregate`,
                --       STRUCT(0 as count, struct(null as id) as sum)
                --     ) as aggregate
                --   ) AS `articles_aggregate`
                --
                -- The (AS `articles_aggregate`) part at the end is rendered by 'fromAliased' evaluating
                -- at the root of this branch, and not by anything below
                Printer
"STRUCT("
                  Printer -> Printer -> Printer
<+> ( Printer -> [Printer] -> Printer
SepByPrinter
                          Printer
", "
                          ( [(FieldName, FieldOrigin)]
fields
                              [(FieldName, FieldOrigin)]
-> ((FieldName, FieldOrigin) -> Printer) -> [Printer]
forall (f :: * -> *) a b. Functor f => f a -> (a -> b) -> f b
<&> \(fName :: FieldName
fName@FieldName {Text
$sel:fieldName:FieldName :: FieldName -> Text
$sel:fieldNameEntity:FieldName :: FieldName -> Text
fieldName :: Text
fieldNameEntity :: Text
..}, FieldOrigin
fieldOrigin :: FieldOrigin) ->
                                Printer
"IFNULL("
                                  Printer -> Printer -> Printer
<+> FieldName -> Printer
fromFieldName FieldName
fName
                                  Printer -> Printer -> Printer
<+> Printer
", "
                                  Printer -> Printer -> Printer
<+> FieldOrigin -> Printer
fromFieldOrigin FieldOrigin
fieldOrigin
                                  Printer -> Printer -> Printer
<+> Printer
") AS "
                                  Printer -> Printer -> Printer
<+> Text -> Printer
fromNameText Text
fieldName
                          )
                      )
                  Printer -> Printer -> Printer
<+> Printer
")"
            )
            Aliased [(FieldName, FieldOrigin)]
aliasedEntity
        )
    ArrayEntityProjection EntityAlias
entityAlias Aliased [FieldName]
aliasedEntity ->
      Aliased Printer -> Printer
fromAliased
        ( ([FieldName] -> Printer) -> Aliased [FieldName] -> Aliased Printer
forall a b. (a -> b) -> Aliased a -> Aliased b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap
            ( \[FieldName]
aggs ->
                Printer
"ARRAY(SELECT AS STRUCT "
                  Printer -> Printer -> Printer
<+> Int -> Printer -> Printer
IndentPrinter
                    Int
7
                    (Printer -> [Printer] -> Printer
SepByPrinter Printer
", " ((FieldName -> Printer) -> [FieldName] -> [Printer]
forall a b. (a -> b) -> [a] -> [b]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap FieldName -> Printer
fromFieldNameNaked ([FieldName] -> [FieldName]
forall a. [a] -> [a]
forall (t :: * -> *) a. Foldable t => t a -> [a]
toList [FieldName]
aggs)))
                  Printer -> Printer -> Printer
<+> Printer
" FROM "
                  Printer -> Printer -> Printer
<+> EntityAlias -> Printer
fromJoinAlias EntityAlias
entityAlias
                  Printer -> Printer -> Printer
<+> Printer
".agg)"
            )
            Aliased [FieldName]
aliasedEntity
        )
      where
        fromFieldNameNaked :: FieldName -> Printer
        fromFieldNameNaked :: FieldName -> Printer
fromFieldNameNaked (FieldName {Text
$sel:fieldName:FieldName :: FieldName -> Text
$sel:fieldNameEntity:FieldName :: FieldName -> Text
fieldName :: Text
fieldNameEntity :: Text
..}) =
          Text -> Printer
fromNameText Text
fieldName

fromFieldOrigin :: FieldOrigin -> Printer
fromFieldOrigin :: FieldOrigin -> Printer
fromFieldOrigin = \case
  FieldOrigin
NoOrigin -> Printer
"NULL"
  AggregateOrigin [Aliased Aggregate]
aliasedAggregates ->
    Printer
"STRUCT("
      Printer -> Printer -> Printer
<+>
      -- Example: "0 AS count, STRUCT(NULL AS id) AS sum"
      Printer -> [Printer] -> Printer
SepByPrinter Printer
", " (Aliased Printer -> Printer
fromAliased (Aliased Printer -> Printer)
-> (Aliased Aggregate -> Aliased Printer)
-> Aliased Aggregate
-> Printer
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Aggregate -> Printer) -> Aliased Aggregate -> Aliased Printer
forall a b. (a -> b) -> Aliased a -> Aliased b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Aggregate -> Printer
fromNullAggregate (Aliased Aggregate -> Printer) -> [Aliased Aggregate] -> [Printer]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [Aliased Aggregate]
aliasedAggregates)
      Printer -> Printer -> Printer
<+> Printer
")"

fromWindowFunction :: WindowFunction -> Printer
fromWindowFunction :: WindowFunction -> Printer
fromWindowFunction (RowNumberOverPartitionBy NonEmpty FieldName
fieldNames Maybe (NonEmpty OrderBy)
morderBys) =
  Printer
"ROW_NUMBER() OVER(PARTITION BY "
    Printer -> Printer -> Printer
<+> Printer -> [Printer] -> Printer
SepByPrinter Printer
", " ((FieldName -> Printer) -> [FieldName] -> [Printer]
forall a b. (a -> b) -> [a] -> [b]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap FieldName -> Printer
fromFieldName (NonEmpty FieldName -> [FieldName]
forall a. NonEmpty a -> [a]
forall (t :: * -> *) a. Foldable t => t a -> [a]
toList NonEmpty FieldName
fieldNames))
    Printer -> Printer -> Printer
<+> ( case Maybe (NonEmpty OrderBy)
morderBys of
            Just {} -> Printer
" " Printer -> Printer -> Printer
<+> Top -> Maybe Expression -> Maybe (NonEmpty OrderBy) -> Printer
fromOrderBys Top
NoTop Maybe Expression
forall a. Maybe a
Nothing Maybe (NonEmpty OrderBy)
morderBys
            Maybe (NonEmpty OrderBy)
Nothing -> Printer
""
        )
    Printer -> Printer -> Printer
<+> Printer
")"

fromArrayAgg :: ArrayAgg -> Printer
fromArrayAgg :: ArrayAgg -> Printer
fromArrayAgg ArrayAgg {Maybe (NonEmpty OrderBy)
NonEmpty Projection
Top
arrayAggProjections :: NonEmpty Projection
arrayAggOrderBy :: Maybe (NonEmpty OrderBy)
arrayAggTop :: Top
$sel:arrayAggProjections:ArrayAgg :: ArrayAgg -> NonEmpty Projection
$sel:arrayAggOrderBy:ArrayAgg :: ArrayAgg -> Maybe (NonEmpty OrderBy)
$sel:arrayAggTop:ArrayAgg :: ArrayAgg -> Top
..} =
  [Printer] -> Printer
SeqPrinter
    [ Printer
"ARRAY_AGG(",
      Int -> Printer -> Printer
IndentPrinter Int
10
        (Printer -> Printer) -> Printer -> Printer
forall a b. (a -> b) -> a -> b
$ Printer -> [Printer] -> Printer
SepByPrinter
          Printer
" "
          [ Printer
"STRUCT(" Printer -> Printer -> Printer
<+> Int -> Printer -> Printer
IndentPrinter Int
7 Printer
projections Printer -> Printer -> Printer
<+> Printer
")",
            Top -> Maybe Expression -> Maybe (NonEmpty OrderBy) -> Printer
fromOrderBys
              Top
arrayAggTop
              Maybe Expression
forall a. Maybe a
Nothing
              ( (NonEmpty OrderBy -> NonEmpty OrderBy)
-> Maybe (NonEmpty OrderBy) -> Maybe (NonEmpty OrderBy)
forall a b. (a -> b) -> Maybe a -> Maybe b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap
                  ( (OrderBy -> OrderBy) -> NonEmpty OrderBy -> NonEmpty OrderBy
forall a b. (a -> b) -> NonEmpty a -> NonEmpty b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap
                      ( \OrderBy
orderBy ->
                          OrderBy
orderBy
                            { $sel:orderByNullsOrder:OrderBy :: NullsOrder
orderByNullsOrder = NullsOrder
NullsAnyOrder
                            -- Because BigQuery reports:
                            -- > NULLS FIRST not supported with descending sort order in aggregate functions
                            -- And the same error with 'ascending'.
                            }
                      )
                  )
                  Maybe (NonEmpty OrderBy)
arrayAggOrderBy
              )
          ],
      Printer
")"
    ]
  where
    projections :: Printer
projections =
      Printer -> [Printer] -> Printer
SepByPrinter
        (Printer
"," Printer -> Printer -> Printer
<+> Printer
NewlinePrinter)
        ((Projection -> Printer) -> [Projection] -> [Printer]
forall a b. (a -> b) -> [a] -> [b]
map Projection -> Printer
fromProjection (NonEmpty Projection -> [Projection]
forall a. NonEmpty a -> [a]
forall (t :: * -> *) a. Foldable t => t a -> [a]
toList (NonEmpty Projection -> NonEmpty Projection
cleanProjections NonEmpty Projection
arrayAggProjections)))

fromNullAggregate :: Aggregate -> Printer
fromNullAggregate :: Aggregate -> Printer
fromNullAggregate = \case
  CountAggregate Countable FieldName
_ -> Printer
"0"
  OpAggregate Text
_text Expression
_exp -> Printer
"NULL"
  OpAggregates Text
_text NonEmpty (Text, Expression)
exps ->
    Printer
"STRUCT(" Printer -> Printer -> Printer
<+> Printer -> [Printer] -> Printer
SepByPrinter Printer
", " (NonEmpty (Text, Expression) -> [(Text, Expression)]
forall a. NonEmpty a -> [a]
forall (t :: * -> *) a. Foldable t => t a -> [a]
toList NonEmpty (Text, Expression)
exps [(Text, Expression)]
-> ((Text, Expression) -> Printer) -> [Printer]
forall (f :: * -> *) a b. Functor f => f a -> (a -> b) -> f b
<&> \(Text
alias, Expression
_exp) -> Printer
"NULL AS " Printer -> Printer -> Printer
<+> Text -> Printer
fromNameText Text
alias) Printer -> Printer -> Printer
<+> Printer
")"
  TextAggregate Text
_text -> Printer
"NULL"

fromAggregate :: Aggregate -> Printer
fromAggregate :: Aggregate -> Printer
fromAggregate =
  \case
    -- There's no reason why we'd be aggregating a ValueExpression /unless/
    -- that ValueExpression is a reserved GraphQL name like __typename. In
    -- which case, we don't want to run it through the aggregation function;
    -- we just want to return the value.
    OpAggregate Text
_ (ValueExpression (TypedValue ScalarType
ty Value
val)) -> ScalarType -> Value -> Printer
fromValue ScalarType
ty Value
val
    CountAggregate Countable FieldName
countable -> Printer
"COUNT(" Printer -> Printer -> Printer
<+> Countable FieldName -> Printer
fromCountable Countable FieldName
countable Printer -> Printer -> Printer
<+> Printer
")"
    OpAggregate Text
text Expression
arg ->
      Text -> Printer
UnsafeTextPrinter Text
text Printer -> Printer -> Printer
<+> Printer
"(" Printer -> Printer -> Printer
<+> Expression -> Printer
fromExpression Expression
arg Printer -> Printer -> Printer
<+> Printer
")"
    OpAggregates Text
text NonEmpty (Text, Expression)
args ->
      Printer
"STRUCT("
        Printer -> Printer -> Printer
<+> Int -> Printer -> Printer
IndentPrinter
          Int
7
          ( Printer -> [Printer] -> Printer
SepByPrinter
              Printer
", "
              ( ((Text, Expression) -> Printer)
-> [(Text, Expression)] -> [Printer]
forall a b. (a -> b) -> [a] -> [b]
map
                  ( \(Text
alias, Expression
arg) ->
                      Aggregate -> Printer
fromAggregate (Text -> Expression -> Aggregate
OpAggregate Text
text Expression
arg)
                        Printer -> Printer -> Printer
<+> Printer
" AS "
                        Printer -> Printer -> Printer
<+> Text -> Printer
fromNameText Text
alias
                  )
                  (NonEmpty (Text, Expression) -> [(Text, Expression)]
forall a. NonEmpty a -> [a]
forall (t :: * -> *) a. Foldable t => t a -> [a]
toList NonEmpty (Text, Expression)
args)
              )
          )
        Printer -> Printer -> Printer
<+> Printer
")"
    TextAggregate Text
text -> Expression -> Printer
fromExpression (TypedValue -> Expression
ValueExpression (ScalarType -> Value -> TypedValue
TypedValue ScalarType
StringScalarType (Text -> Value
StringValue Text
text)))

fromCountable :: Countable FieldName -> Printer
fromCountable :: Countable FieldName -> Printer
fromCountable =
  \case
    Countable FieldName
StarCountable -> Printer
"*"
    NonNullFieldCountable NonEmpty FieldName
fields ->
      Printer -> [Printer] -> Printer
SepByPrinter Printer
", " ((FieldName -> Printer) -> [FieldName] -> [Printer]
forall a b. (a -> b) -> [a] -> [b]
map FieldName -> Printer
fromFieldName (NonEmpty FieldName -> [FieldName]
forall a. NonEmpty a -> [a]
forall (t :: * -> *) a. Foldable t => t a -> [a]
toList NonEmpty FieldName
fields))
    DistinctCountable NonEmpty FieldName
fields ->
      Printer
"DISTINCT "
        Printer -> Printer -> Printer
<+> Printer -> [Printer] -> Printer
SepByPrinter Printer
", " ((FieldName -> Printer) -> [FieldName] -> [Printer]
forall a b. (a -> b) -> [a] -> [b]
map FieldName -> Printer
fromFieldName (NonEmpty FieldName -> [FieldName]
forall a. NonEmpty a -> [a]
forall (t :: * -> *) a. Foldable t => t a -> [a]
toList NonEmpty FieldName
fields))

fromWhere :: Where -> Printer
fromWhere :: Where -> Printer
fromWhere =
  \case
    Where [Expression]
expressions ->
      case ((Expression -> Bool) -> [Expression] -> [Expression]
forall a. (a -> Bool) -> [a] -> [a]
filter ((Expression -> Expression -> Bool
forall a. Eq a => a -> a -> Bool
/= Expression
trueExpression) (Expression -> Bool)
-> (Expression -> Expression) -> Expression -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Expression -> Expression
collapse)) [Expression]
expressions of
        [] -> Printer
""
        [Expression]
collapsedExpressions ->
          Printer
"WHERE "
            Printer -> Printer -> Printer
<+> Int -> Printer -> Printer
IndentPrinter Int
6 (Expression -> Printer
fromExpression ([Expression] -> Expression
AndExpression [Expression]
collapsedExpressions))
      where
        collapse :: Expression -> Expression
collapse (AndExpression [Expression
x]) = Expression -> Expression
collapse Expression
x
        collapse (AndExpression []) = Expression
trueExpression
        collapse (OrExpression [Expression
x]) = Expression -> Expression
collapse Expression
x
        collapse Expression
x = Expression
x

fromSelectJson :: SelectJson -> Printer
fromSelectJson :: SelectJson -> Printer
fromSelectJson SelectJson {[(ColumnName, ScalarType)]
Expression
selectJsonBody :: Expression
selectJsonFields :: [(ColumnName, ScalarType)]
$sel:selectJsonBody:SelectJson :: SelectJson -> Expression
$sel:selectJsonFields:SelectJson :: SelectJson -> [(ColumnName, ScalarType)]
..} = Printer
finalExpression
  where
    finalExpression :: Printer
finalExpression =
      Printer -> [Printer] -> Printer
SepByPrinter
        Printer
NewlinePrinter
        [ Printer
"SELECT " Printer -> Printer -> Printer
<+> Int -> Printer -> Printer
IndentPrinter Int
7 Printer
fields,
          Printer
"FROM UNNEST(JSON_QUERY_ARRAY("
            Printer -> Printer -> Printer
<+> Int -> Printer -> Printer
IndentPrinter Int
29 (Expression -> Printer
fromExpression Expression
selectJsonBody)
            Printer -> Printer -> Printer
<+> Printer
"))",
          Printer
" AS " Printer -> Printer -> Printer
<+> Printer
jsonStringField
        ]
    jsonStringField :: Printer
jsonStringField = Text -> Printer
fromNameText Text
"json"
    fields :: Printer
fields =
      Printer -> [Printer] -> Printer
SepByPrinter
        (Printer
"," Printer -> Printer -> Printer
<+> Printer
NewlinePrinter)
        (((ColumnName, ScalarType) -> Printer)
-> [(ColumnName, ScalarType)] -> [Printer]
forall a b. (a -> b) -> [a] -> [b]
map (ColumnName, ScalarType) -> Printer
extractJsonField [(ColumnName, ScalarType)]
selectJsonFields)
    extractJsonField :: (ColumnName, ScalarType) -> Printer
extractJsonField (ColumnName Text
name, ScalarType
scalarType) =
      Printer
"CAST(JSON_VALUE("
        Printer -> Printer -> Printer
<+> Printer
jsonStringField
        Printer -> Printer -> Printer
<+> Printer
", '$."
        Printer -> Printer -> Printer
<+> String -> Printer
forall a. IsString a => String -> a
fromString (Text -> String
T.unpack Text
name)
        Printer -> Printer -> Printer
<+> Printer
"') AS "
        Printer -> Printer -> Printer
<+> ScalarType -> Printer
fromScalarType ScalarType
scalarType
        Printer -> Printer -> Printer
<+> Printer
") AS "
        Printer -> Printer -> Printer
<+> Text -> Printer
fromNameText Text
name

fromFrom :: From -> Printer
fromFrom :: From -> Printer
fromFrom =
  \case
    FromQualifiedTable Aliased TableName
aliasedQualifiedTableName ->
      Aliased Printer -> Printer
fromAliased ((TableName -> Printer) -> Aliased TableName -> Aliased Printer
forall a b. (a -> b) -> Aliased a -> Aliased b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap TableName -> Printer
fromTableName Aliased TableName
aliasedQualifiedTableName)
    FromSelect Aliased Select
select -> Aliased Printer -> Printer
fromAliased ((Select -> Printer) -> Aliased Select -> Aliased Printer
forall a b. (a -> b) -> Aliased a -> Aliased b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (Printer -> Printer
parens (Printer -> Printer) -> (Select -> Printer) -> Select -> Printer
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Select -> Printer
fromSelect) Aliased Select
select)
    FromSelectJson Aliased SelectJson
selectJson ->
      Aliased Printer -> Printer
fromAliased ((SelectJson -> Printer) -> Aliased SelectJson -> Aliased Printer
forall a b. (a -> b) -> Aliased a -> Aliased b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (Printer -> Printer
parens (Printer -> Printer)
-> (SelectJson -> Printer) -> SelectJson -> Printer
forall b c a. (b -> c) -> (a -> b) -> a -> c
. SelectJson -> Printer
fromSelectJson) Aliased SelectJson
selectJson)
    FromFunction Aliased SelectFromFunction
selectFromFunction ->
      Aliased Printer -> Printer
fromAliased
        ( (SelectFromFunction -> Printer)
-> Aliased SelectFromFunction -> Aliased Printer
forall a b. (a -> b) -> Aliased a -> Aliased b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap
            ( \SelectFromFunction {[Expression]
FunctionName
sffFunctionName :: FunctionName
sffArguments :: [Expression]
$sel:sffFunctionName:SelectFromFunction :: SelectFromFunction -> FunctionName
$sel:sffArguments:SelectFromFunction :: SelectFromFunction -> [Expression]
..} ->
                Expression -> Printer
fromExpression (Expression -> Printer) -> Expression -> Printer
forall a b. (a -> b) -> a -> b
$ FunctionName -> [Expression] -> Expression
FunctionExpression FunctionName
sffFunctionName [Expression]
sffArguments
            )
            Aliased SelectFromFunction
selectFromFunction
        )
    -- Native Queries are bound as CTEs, so usage sites don't do "nativeQueryName AS alias".
    FromNativeQuery (Aliased {Text
aliasedAlias :: Text
$sel:aliasedAlias:Aliased :: forall a. Aliased a -> Text
aliasedAlias}) -> Text -> Printer
fromNameText Text
aliasedAlias

fromTableName :: TableName -> Printer
fromTableName :: TableName -> Printer
fromTableName TableName {Text
tableName :: Text
$sel:tableName:TableName :: TableName -> Text
tableName, Text
tableNameSchema :: Text
$sel:tableNameSchema:TableName :: TableName -> Text
tableNameSchema} =
  Text -> Printer
fromNameText Text
tableNameSchema Printer -> Printer -> Printer
<+> Printer
"." Printer -> Printer -> Printer
<+> Text -> Printer
fromNameText Text
tableName

fromFunctionName :: FunctionName -> Printer
fromFunctionName :: FunctionName -> Printer
fromFunctionName = \case
  FunctionName Text
name Maybe Text
Nothing ->
    -- Function without dataset are system functions like 'ARRAY', 'UNNEST' etc.
    Text -> Printer
UnsafeTextPrinter Text
name
  FunctionName Text
name (Just Text
dataset) -> Text -> Printer
fromNameText Text
dataset Printer -> Printer -> Printer
<+> Printer
"." Printer -> Printer -> Printer
<+> Text -> Printer
fromNameText Text
name

fromAliased :: Aliased Printer -> Printer
fromAliased :: Aliased Printer -> Printer
fromAliased Aliased {Text
Printer
$sel:aliasedAlias:Aliased :: forall a. Aliased a -> Text
aliasedThing :: Printer
aliasedAlias :: Text
$sel:aliasedThing:Aliased :: forall a. Aliased a -> a
..} =
  Printer
aliasedThing
    Printer -> Printer -> Printer
<+> ((Printer
" AS " Printer -> Printer -> Printer
<+>) (Printer -> Printer) -> (Text -> Printer) -> Text -> Printer
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Printer
fromNameText) Text
aliasedAlias

fromNameText :: Text -> Printer
fromNameText :: Text -> Printer
fromNameText Text
t = Text -> Printer
UnsafeTextPrinter (Text
"`" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
t Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"`")

trueExpression :: Expression
trueExpression :: Expression
trueExpression = TypedValue -> Expression
ValueExpression (ScalarType -> Value -> TypedValue
TypedValue ScalarType
BoolScalarType (Bool -> Value
BoolValue Bool
True))

falseExpression :: Expression
falseExpression :: Expression
falseExpression = TypedValue -> Expression
ValueExpression (ScalarType -> Value -> TypedValue
TypedValue ScalarType
BoolScalarType (Bool -> Value
BoolValue Bool
False))

fromValue :: ScalarType -> Value -> Printer
fromValue :: ScalarType -> Value -> Printer
fromValue ScalarType
ty Value
val = TypedValue -> Printer
ValuePrinter (ScalarType -> Value -> TypedValue
TypedValue ScalarType
ty Value
val)

parens :: Printer -> Printer
parens :: Printer -> Printer
parens Printer
x = Printer
"(" Printer -> Printer -> Printer
<+> Int -> Printer -> Printer
IndentPrinter Int
1 Printer
x Printer -> Printer -> Printer
<+> Printer
")"

--------------------------------------------------------------------------------
-- Quick and easy query printer

toBuilderFlat :: Printer -> Builder
toBuilderFlat :: Printer -> Builder
toBuilderFlat = (State (InsOrdHashMap TypedValue Int) Builder
 -> InsOrdHashMap TypedValue Int -> Builder)
-> InsOrdHashMap TypedValue Int
-> State (InsOrdHashMap TypedValue Int) Builder
-> Builder
forall a b c. (a -> b -> c) -> b -> a -> c
flip State (InsOrdHashMap TypedValue Int) Builder
-> InsOrdHashMap TypedValue Int -> Builder
forall s a. State s a -> s -> a
evalState InsOrdHashMap TypedValue Int
forall a. Monoid a => a
mempty (State (InsOrdHashMap TypedValue Int) Builder -> Builder)
-> (Printer -> State (InsOrdHashMap TypedValue Int) Builder)
-> Printer
-> Builder
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Printer -> State (InsOrdHashMap TypedValue Int) Builder
runBuilderFlat

toBuilderPretty :: Printer -> Builder
toBuilderPretty :: Printer -> Builder
toBuilderPretty = (State (InsOrdHashMap TypedValue Int) Builder
 -> InsOrdHashMap TypedValue Int -> Builder)
-> InsOrdHashMap TypedValue Int
-> State (InsOrdHashMap TypedValue Int) Builder
-> Builder
forall a b c. (a -> b -> c) -> b -> a -> c
flip State (InsOrdHashMap TypedValue Int) Builder
-> InsOrdHashMap TypedValue Int -> Builder
forall s a. State s a -> s -> a
evalState InsOrdHashMap TypedValue Int
forall a. Monoid a => a
mempty (State (InsOrdHashMap TypedValue Int) Builder -> Builder)
-> (Printer -> State (InsOrdHashMap TypedValue Int) Builder)
-> Printer
-> Builder
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Printer -> State (InsOrdHashMap TypedValue Int) Builder
runBuilderPretty

toTextPretty :: Printer -> Text
toTextPretty :: Printer -> Text
toTextPretty = Text -> Text
LT.toStrict (Text -> Text) -> (Printer -> Text) -> Printer -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Builder -> Text
LT.toLazyText (Builder -> Text) -> (Printer -> Builder) -> Printer -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Printer -> Builder
toBuilderPretty

toTextFlat :: Printer -> Text
toTextFlat :: Printer -> Text
toTextFlat = Text -> Text
LT.toStrict (Text -> Text) -> (Printer -> Text) -> Printer -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Builder -> Text
LT.toLazyText (Builder -> Text) -> (Printer -> Builder) -> Printer -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Printer -> Builder
toBuilderFlat

--------------------------------------------------------------------------------
-- Printer ready for consumption

-- | Produces a query with holes, and a mapping for each
renderBuilderFlat :: Printer -> (Builder, InsOrdHashMap Int TypedValue)
renderBuilderFlat :: Printer -> (Builder, InsOrdHashMap Int TypedValue)
renderBuilderFlat =
  (InsOrdHashMap TypedValue Int -> InsOrdHashMap Int TypedValue)
-> (Builder, InsOrdHashMap TypedValue Int)
-> (Builder, InsOrdHashMap Int TypedValue)
forall b c a. (b -> c) -> (a, b) -> (a, c)
forall (p :: * -> * -> *) b c a.
Bifunctor p =>
(b -> c) -> p a b -> p a c
second ([(Int, TypedValue)] -> InsOrdHashMap Int TypedValue
forall k v. (Eq k, Hashable k) => [(k, v)] -> InsOrdHashMap k v
InsOrdHashMap.fromList ([(Int, TypedValue)] -> InsOrdHashMap Int TypedValue)
-> (InsOrdHashMap TypedValue Int -> [(Int, TypedValue)])
-> InsOrdHashMap TypedValue Int
-> InsOrdHashMap Int TypedValue
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((TypedValue, Int) -> (Int, TypedValue))
-> [(TypedValue, Int)] -> [(Int, TypedValue)]
forall a b. (a -> b) -> [a] -> [b]
map (TypedValue, Int) -> (Int, TypedValue)
forall a b. (a, b) -> (b, a)
swap ([(TypedValue, Int)] -> [(Int, TypedValue)])
-> (InsOrdHashMap TypedValue Int -> [(TypedValue, Int)])
-> InsOrdHashMap TypedValue Int
-> [(Int, TypedValue)]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. InsOrdHashMap TypedValue Int -> [(TypedValue, Int)]
forall k v. InsOrdHashMap k v -> [(k, v)]
InsOrdHashMap.toList)
    ((Builder, InsOrdHashMap TypedValue Int)
 -> (Builder, InsOrdHashMap Int TypedValue))
-> (Printer -> (Builder, InsOrdHashMap TypedValue Int))
-> Printer
-> (Builder, InsOrdHashMap Int TypedValue)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (State (InsOrdHashMap TypedValue Int) Builder
 -> InsOrdHashMap TypedValue Int
 -> (Builder, InsOrdHashMap TypedValue Int))
-> InsOrdHashMap TypedValue Int
-> State (InsOrdHashMap TypedValue Int) Builder
-> (Builder, InsOrdHashMap TypedValue Int)
forall a b c. (a -> b -> c) -> b -> a -> c
flip State (InsOrdHashMap TypedValue Int) Builder
-> InsOrdHashMap TypedValue Int
-> (Builder, InsOrdHashMap TypedValue Int)
forall s a. State s a -> s -> (a, s)
runState InsOrdHashMap TypedValue Int
forall a. Monoid a => a
mempty
    (State (InsOrdHashMap TypedValue Int) Builder
 -> (Builder, InsOrdHashMap TypedValue Int))
-> (Printer -> State (InsOrdHashMap TypedValue Int) Builder)
-> Printer
-> (Builder, InsOrdHashMap TypedValue Int)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Printer -> State (InsOrdHashMap TypedValue Int) Builder
runBuilderFlat

-- | Produces a query with holes, and a mapping for each
renderBuilderPretty :: Printer -> (Builder, InsOrdHashMap Int TypedValue)
renderBuilderPretty :: Printer -> (Builder, InsOrdHashMap Int TypedValue)
renderBuilderPretty =
  (InsOrdHashMap TypedValue Int -> InsOrdHashMap Int TypedValue)
-> (Builder, InsOrdHashMap TypedValue Int)
-> (Builder, InsOrdHashMap Int TypedValue)
forall b c a. (b -> c) -> (a, b) -> (a, c)
forall (p :: * -> * -> *) b c a.
Bifunctor p =>
(b -> c) -> p a b -> p a c
second ([(Int, TypedValue)] -> InsOrdHashMap Int TypedValue
forall k v. (Eq k, Hashable k) => [(k, v)] -> InsOrdHashMap k v
InsOrdHashMap.fromList ([(Int, TypedValue)] -> InsOrdHashMap Int TypedValue)
-> (InsOrdHashMap TypedValue Int -> [(Int, TypedValue)])
-> InsOrdHashMap TypedValue Int
-> InsOrdHashMap Int TypedValue
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((TypedValue, Int) -> (Int, TypedValue))
-> [(TypedValue, Int)] -> [(Int, TypedValue)]
forall a b. (a -> b) -> [a] -> [b]
map (TypedValue, Int) -> (Int, TypedValue)
forall a b. (a, b) -> (b, a)
swap ([(TypedValue, Int)] -> [(Int, TypedValue)])
-> (InsOrdHashMap TypedValue Int -> [(TypedValue, Int)])
-> InsOrdHashMap TypedValue Int
-> [(Int, TypedValue)]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. InsOrdHashMap TypedValue Int -> [(TypedValue, Int)]
forall k v. InsOrdHashMap k v -> [(k, v)]
InsOrdHashMap.toList)
    ((Builder, InsOrdHashMap TypedValue Int)
 -> (Builder, InsOrdHashMap Int TypedValue))
-> (Printer -> (Builder, InsOrdHashMap TypedValue Int))
-> Printer
-> (Builder, InsOrdHashMap Int TypedValue)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (State (InsOrdHashMap TypedValue Int) Builder
 -> InsOrdHashMap TypedValue Int
 -> (Builder, InsOrdHashMap TypedValue Int))
-> InsOrdHashMap TypedValue Int
-> State (InsOrdHashMap TypedValue Int) Builder
-> (Builder, InsOrdHashMap TypedValue Int)
forall a b c. (a -> b -> c) -> b -> a -> c
flip State (InsOrdHashMap TypedValue Int) Builder
-> InsOrdHashMap TypedValue Int
-> (Builder, InsOrdHashMap TypedValue Int)
forall s a. State s a -> s -> (a, s)
runState InsOrdHashMap TypedValue Int
forall a. Monoid a => a
mempty
    (State (InsOrdHashMap TypedValue Int) Builder
 -> (Builder, InsOrdHashMap TypedValue Int))
-> (Printer -> State (InsOrdHashMap TypedValue Int) Builder)
-> Printer
-> (Builder, InsOrdHashMap TypedValue Int)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Printer -> State (InsOrdHashMap TypedValue Int) Builder
runBuilderPretty

--------------------------------------------------------------------------------
-- Real printer engines

paramName :: Int -> Builder
paramName :: Int -> Builder
paramName Int
next = Builder
"param" Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> String -> Builder
forall a. IsString a => String -> a
fromString (Int -> String
forall a. Show a => a -> String
show Int
next)

runBuilderFlat :: Printer -> State (InsOrdHashMap TypedValue Int) Builder
runBuilderFlat :: Printer -> State (InsOrdHashMap TypedValue Int) Builder
runBuilderFlat = Int -> Printer -> State (InsOrdHashMap TypedValue Int) Builder
go Int
0
  where
    go :: Int -> Printer -> State (InsOrdHashMap TypedValue Int) Builder
go Int
level =
      \case
        UnsafeTextPrinter Text
q -> Builder -> State (InsOrdHashMap TypedValue Int) Builder
forall a. a -> StateT (InsOrdHashMap TypedValue Int) Identity a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Text -> Builder
LT.fromText Text
q)
        SeqPrinter [Printer]
xs -> ([Builder] -> Builder)
-> StateT (InsOrdHashMap TypedValue Int) Identity [Builder]
-> State (InsOrdHashMap TypedValue Int) Builder
forall a b.
(a -> b)
-> StateT (InsOrdHashMap TypedValue Int) Identity a
-> StateT (InsOrdHashMap TypedValue Int) Identity b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ([Builder] -> Builder
forall a. Monoid a => [a] -> a
mconcat ([Builder] -> Builder)
-> ([Builder] -> [Builder]) -> [Builder] -> Builder
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Builder -> Bool) -> [Builder] -> [Builder]
forall a. (a -> Bool) -> [a] -> [a]
filter Builder -> Bool
notEmpty) ((Printer -> State (InsOrdHashMap TypedValue Int) Builder)
-> [Printer]
-> StateT (InsOrdHashMap TypedValue Int) Identity [Builder]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
forall (m :: * -> *) a b. Monad m => (a -> m b) -> [a] -> m [b]
mapM (Int -> Printer -> State (InsOrdHashMap TypedValue Int) Builder
go Int
level) [Printer]
xs)
        SepByPrinter Printer
x [Printer]
xs -> do
          Builder
i <- Int -> Printer -> State (InsOrdHashMap TypedValue Int) Builder
go Int
level Printer
x
          ([Builder] -> Builder)
-> StateT (InsOrdHashMap TypedValue Int) Identity [Builder]
-> State (InsOrdHashMap TypedValue Int) Builder
forall a b.
(a -> b)
-> StateT (InsOrdHashMap TypedValue Int) Identity a
-> StateT (InsOrdHashMap TypedValue Int) Identity b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ([Builder] -> Builder
forall a. Monoid a => [a] -> a
mconcat ([Builder] -> Builder)
-> ([Builder] -> [Builder]) -> [Builder] -> Builder
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Builder -> [Builder] -> [Builder]
forall a. a -> [a] -> [a]
intersperse Builder
i ([Builder] -> [Builder])
-> ([Builder] -> [Builder]) -> [Builder] -> [Builder]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Builder -> Bool) -> [Builder] -> [Builder]
forall a. (a -> Bool) -> [a] -> [a]
filter Builder -> Bool
notEmpty) ((Printer -> State (InsOrdHashMap TypedValue Int) Builder)
-> [Printer]
-> StateT (InsOrdHashMap TypedValue Int) Identity [Builder]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
forall (m :: * -> *) a b. Monad m => (a -> m b) -> [a] -> m [b]
mapM (Int -> Printer -> State (InsOrdHashMap TypedValue Int) Builder
go Int
level) [Printer]
xs)
        Printer
NewlinePrinter -> Builder -> State (InsOrdHashMap TypedValue Int) Builder
forall a. a -> StateT (InsOrdHashMap TypedValue Int) Identity a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Builder
" "
        IndentPrinter Int
n Printer
p -> Int -> Printer -> State (InsOrdHashMap TypedValue Int) Builder
go (Int
level Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
n) Printer
p
        ValuePrinter (TypedValue ScalarType
_ (ArrayValue Vector Value
x)) | Vector Value -> Bool
forall a. Vector a -> Bool
V.null Vector Value
x -> Builder -> State (InsOrdHashMap TypedValue Int) Builder
forall a. a -> StateT (InsOrdHashMap TypedValue Int) Identity a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Builder
"[]"
        ValuePrinter TypedValue
tv -> do
          InsOrdHashMap TypedValue Int
themap <- StateT
  (InsOrdHashMap TypedValue Int)
  Identity
  (InsOrdHashMap TypedValue Int)
forall s (m :: * -> *). MonadState s m => m s
get
          Int
next <-
            TypedValue -> InsOrdHashMap TypedValue Int -> Maybe Int
forall k v. (Eq k, Hashable k) => k -> InsOrdHashMap k v -> Maybe v
InsOrdHashMap.lookup TypedValue
tv InsOrdHashMap TypedValue Int
themap Maybe Int
-> StateT (InsOrdHashMap TypedValue Int) Identity Int
-> StateT (InsOrdHashMap TypedValue Int) Identity Int
forall (m :: * -> *) a. Applicative m => Maybe a -> m a -> m a
`onNothing` do
              Int
next <- (InsOrdHashMap TypedValue Int -> Int)
-> StateT (InsOrdHashMap TypedValue Int) Identity Int
forall s (m :: * -> *) a. MonadState s m => (s -> a) -> m a
gets InsOrdHashMap TypedValue Int -> Int
forall k v. InsOrdHashMap k v -> Int
InsOrdHashMap.size
              (InsOrdHashMap TypedValue Int -> InsOrdHashMap TypedValue Int)
-> StateT (InsOrdHashMap TypedValue Int) Identity ()
forall s (m :: * -> *). MonadState s m => (s -> s) -> m ()
modify (TypedValue
-> Int
-> InsOrdHashMap TypedValue Int
-> InsOrdHashMap TypedValue Int
forall k v.
(Eq k, Hashable k) =>
k -> v -> InsOrdHashMap k v -> InsOrdHashMap k v
InsOrdHashMap.insert TypedValue
tv Int
next)
              Int -> StateT (InsOrdHashMap TypedValue Int) Identity Int
forall a. a -> StateT (InsOrdHashMap TypedValue Int) Identity a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Int
next
          Builder -> State (InsOrdHashMap TypedValue Int) Builder
forall a. a -> StateT (InsOrdHashMap TypedValue Int) Identity a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Builder
"@" Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Int -> Builder
paramName Int
next)
    notEmpty :: Builder -> Bool
notEmpty = (Builder -> Builder -> Bool
forall a. Eq a => a -> a -> Bool
/= Builder
forall a. Monoid a => a
mempty)

runBuilderPretty :: Printer -> State (InsOrdHashMap TypedValue Int) Builder
runBuilderPretty :: Printer -> State (InsOrdHashMap TypedValue Int) Builder
runBuilderPretty = Int -> Printer -> State (InsOrdHashMap TypedValue Int) Builder
go Int
0
  where
    go :: Int -> Printer -> State (InsOrdHashMap TypedValue Int) Builder
go Int
level =
      \case
        UnsafeTextPrinter Text
q -> Builder -> State (InsOrdHashMap TypedValue Int) Builder
forall a. a -> StateT (InsOrdHashMap TypedValue Int) Identity a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Text -> Builder
LT.fromText Text
q)
        SeqPrinter [Printer]
xs -> ([Builder] -> Builder)
-> StateT (InsOrdHashMap TypedValue Int) Identity [Builder]
-> State (InsOrdHashMap TypedValue Int) Builder
forall a b.
(a -> b)
-> StateT (InsOrdHashMap TypedValue Int) Identity a
-> StateT (InsOrdHashMap TypedValue Int) Identity b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ([Builder] -> Builder
forall a. Monoid a => [a] -> a
mconcat ([Builder] -> Builder)
-> ([Builder] -> [Builder]) -> [Builder] -> Builder
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Builder -> Bool) -> [Builder] -> [Builder]
forall a. (a -> Bool) -> [a] -> [a]
filter Builder -> Bool
notEmpty) ((Printer -> State (InsOrdHashMap TypedValue Int) Builder)
-> [Printer]
-> StateT (InsOrdHashMap TypedValue Int) Identity [Builder]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
forall (m :: * -> *) a b. Monad m => (a -> m b) -> [a] -> m [b]
mapM (Int -> Printer -> State (InsOrdHashMap TypedValue Int) Builder
go Int
level) [Printer]
xs)
        SepByPrinter Printer
x [Printer]
xs -> do
          Builder
i <- Int -> Printer -> State (InsOrdHashMap TypedValue Int) Builder
go Int
level Printer
x
          ([Builder] -> Builder)
-> StateT (InsOrdHashMap TypedValue Int) Identity [Builder]
-> State (InsOrdHashMap TypedValue Int) Builder
forall a b.
(a -> b)
-> StateT (InsOrdHashMap TypedValue Int) Identity a
-> StateT (InsOrdHashMap TypedValue Int) Identity b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ([Builder] -> Builder
forall a. Monoid a => [a] -> a
mconcat ([Builder] -> Builder)
-> ([Builder] -> [Builder]) -> [Builder] -> Builder
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Builder -> [Builder] -> [Builder]
forall a. a -> [a] -> [a]
intersperse Builder
i ([Builder] -> [Builder])
-> ([Builder] -> [Builder]) -> [Builder] -> [Builder]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Builder -> Bool) -> [Builder] -> [Builder]
forall a. (a -> Bool) -> [a] -> [a]
filter Builder -> Bool
notEmpty) ((Printer -> State (InsOrdHashMap TypedValue Int) Builder)
-> [Printer]
-> StateT (InsOrdHashMap TypedValue Int) Identity [Builder]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
forall (m :: * -> *) a b. Monad m => (a -> m b) -> [a] -> m [b]
mapM (Int -> Printer -> State (InsOrdHashMap TypedValue Int) Builder
go Int
level) [Printer]
xs)
        Printer
NewlinePrinter -> Builder -> State (InsOrdHashMap TypedValue Int) Builder
forall a. a -> StateT (InsOrdHashMap TypedValue Int) Identity a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Builder
"\n" Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Int -> Builder
indentation Int
level)
        IndentPrinter Int
n Printer
p -> Int -> Printer -> State (InsOrdHashMap TypedValue Int) Builder
go (Int
level Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
n) Printer
p
        ValuePrinter (TypedValue ScalarType
_ (ArrayValue Vector Value
x))
          | Vector Value -> Bool
forall a. Vector a -> Bool
V.null Vector Value
x -> Builder -> State (InsOrdHashMap TypedValue Int) Builder
forall a. a -> StateT (InsOrdHashMap TypedValue Int) Identity a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Builder
"[]"
        ValuePrinter TypedValue
tv -> do
          InsOrdHashMap TypedValue Int
themap <- StateT
  (InsOrdHashMap TypedValue Int)
  Identity
  (InsOrdHashMap TypedValue Int)
forall s (m :: * -> *). MonadState s m => m s
get
          Int
next <-
            TypedValue -> InsOrdHashMap TypedValue Int -> Maybe Int
forall k v. (Eq k, Hashable k) => k -> InsOrdHashMap k v -> Maybe v
InsOrdHashMap.lookup TypedValue
tv InsOrdHashMap TypedValue Int
themap Maybe Int
-> StateT (InsOrdHashMap TypedValue Int) Identity Int
-> StateT (InsOrdHashMap TypedValue Int) Identity Int
forall (m :: * -> *) a. Applicative m => Maybe a -> m a -> m a
`onNothing` do
              Int
next <- (InsOrdHashMap TypedValue Int -> Int)
-> StateT (InsOrdHashMap TypedValue Int) Identity Int
forall s (m :: * -> *) a. MonadState s m => (s -> a) -> m a
gets InsOrdHashMap TypedValue Int -> Int
forall k v. InsOrdHashMap k v -> Int
InsOrdHashMap.size
              (InsOrdHashMap TypedValue Int -> InsOrdHashMap TypedValue Int)
-> StateT (InsOrdHashMap TypedValue Int) Identity ()
forall s (m :: * -> *). MonadState s m => (s -> s) -> m ()
modify (TypedValue
-> Int
-> InsOrdHashMap TypedValue Int
-> InsOrdHashMap TypedValue Int
forall k v.
(Eq k, Hashable k) =>
k -> v -> InsOrdHashMap k v -> InsOrdHashMap k v
InsOrdHashMap.insert TypedValue
tv Int
next)
              Int -> StateT (InsOrdHashMap TypedValue Int) Identity Int
forall a. a -> StateT (InsOrdHashMap TypedValue Int) Identity a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Int
next
          Builder -> State (InsOrdHashMap TypedValue Int) Builder
forall a. a -> StateT (InsOrdHashMap TypedValue Int) Identity a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Builder
"@" Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Int -> Builder
paramName Int
next)
    indentation :: Int -> Builder
indentation Int
n = Text -> Builder
LT.fromText (Int -> Text -> Text
T.replicate Int
n Text
" ")
    notEmpty :: Builder -> Bool
notEmpty = (Builder -> Builder -> Bool
forall a. Eq a => a -> a -> Bool
/= Builder
forall a. Monoid a => a
mempty)

--------------------------------------------------------------------------------
-- Projection cleanup

-- | TODO: For now, we're littering this around where projections are
-- built. I'd prefer to use ordered set, or else a newtype wrapper to
-- prove it's been sorted. But that would interrupt code
-- elsewhere. For now, this is an acceptable solution.
-- Plus, a warning issued about duplicates might be useful.
cleanProjections :: NonEmpty Projection -> NonEmpty Projection
cleanProjections :: NonEmpty Projection -> NonEmpty Projection
cleanProjections = NonEmpty Projection -> NonEmpty Projection
neOrdNub
  where
    neOrdNub :: NonEmpty Projection -> NonEmpty Projection
    neOrdNub :: NonEmpty Projection -> NonEmpty Projection
neOrdNub = [Projection] -> NonEmpty Projection
forall a. HasCallStack => [a] -> NonEmpty a
NE.fromList ([Projection] -> NonEmpty Projection)
-> (NonEmpty Projection -> [Projection])
-> NonEmpty Projection
-> NonEmpty Projection
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Projection -> Maybe Text) -> [Projection] -> [Projection]
forall b a. Ord b => (a -> b) -> [a] -> [a]
nubOrdOn Projection -> Maybe Text
projectionAlias ([Projection] -> [Projection])
-> (NonEmpty Projection -> [Projection])
-> NonEmpty Projection
-> [Projection]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. NonEmpty Projection -> [Projection]
forall a. NonEmpty a -> [a]
NE.toList