squeal-postgresql-0.7.0.0: src/Squeal/PostgreSQL/Expression/Array.hs
{-|
Module: Squeal.PostgreSQL.Expression.Array
Description: array functions
Copyright: (c) Eitan Chatav, 2019
Maintainer: eitan@morphism.tech
Stability: experimental
array functions
-}
{-# LANGUAGE
AllowAmbiguousTypes
, DataKinds
, FlexibleContexts
, FlexibleInstances
, MultiParamTypeClasses
, OverloadedLabels
, OverloadedStrings
, RankNTypes
, ScopedTypeVariables
, TypeApplications
, TypeFamilies
, TypeOperators
, UndecidableInstances
#-}
module Squeal.PostgreSQL.Expression.Array
( -- * Array Functions
array
, array0
, array1
, array2
, cardinality
, index
, index1
, index2
, unnest
, arrAny
, arrAll
) where
import Data.String
import Data.Word (Word64)
import GHC.TypeNats
import qualified Generics.SOP as SOP
import Squeal.PostgreSQL.Expression
import Squeal.PostgreSQL.Expression.Logic
import Squeal.PostgreSQL.Expression.Type
import Squeal.PostgreSQL.Query.From.Set
import Squeal.PostgreSQL.Render
import Squeal.PostgreSQL.Type.Alias
import Squeal.PostgreSQL.Type.List
import Squeal.PostgreSQL.Type.Schema
-- $setup
-- >>> import Squeal.PostgreSQL
-- | Construct an array.
--
-- >>> printSQL $ array [null_, false, true]
-- ARRAY[NULL, FALSE, TRUE]
array
:: [Expression grp lat with db params from ty]
-- ^ array elements
-> Expression grp lat with db params from (null ('PGvararray ty))
array xs = UnsafeExpression $ "ARRAY" <>
bracketed (commaSeparated (renderSQL <$> xs))
-- | Safely construct an empty array.
--
-- >>> printSQL $ array0 text
-- (ARRAY[] :: text[])
array0
:: TypeExpression db ty
-> Expression grp lat with db params from (null ('PGvararray ty))
array0 ty = array [] & astype (vararray ty)
{- | Construct a fixed length array.
>>> printSQL $ array1 (null_ :* false *: true)
ARRAY[NULL, FALSE, TRUE]
>>> :type array1 (null_ :* false *: true)
array1 (null_ :* false *: true)
:: Expression
grp
lat
with
db
params
from
(null ('PGfixarray '[3] ('Null 'PGbool)))
-}
array1
:: (n ~ Length tys, SOP.All ((~) ty) tys)
=> NP (Expression grp lat with db params from) tys
-- ^ array elements
-> Expression grp lat with db params from (null ('PGfixarray '[n] ty))
array1 xs = UnsafeExpression $ "ARRAY" <>
bracketed (renderCommaSeparated renderSQL xs)
{- | Construct a fixed size matrix.
>>> printSQL $ array2 ((null_ :* false *: true) *: (false :* null_ *: true))
ARRAY[[NULL, FALSE, TRUE], [FALSE, NULL, TRUE]]
>>> :type array2 ((null_ :* false *: true) *: (false :* null_ *: true))
array2 ((null_ :* false *: true) *: (false :* null_ *: true))
:: Expression
grp
lat
with
db
params
from
(null ('PGfixarray '[2, 3] ('Null 'PGbool)))
-}
array2
:: ( SOP.All ((~) tys) tyss
, SOP.All SOP.SListI tyss
, Length tyss ~ n1
, SOP.All ((~) ty) tys
, Length tys ~ n2 )
=> NP (NP (Expression grp lat with db params from)) tyss
-- ^ matrix elements
-> Expression grp lat with db params from (null ('PGfixarray '[n1,n2] ty))
array2 xss = UnsafeExpression $ "ARRAY" <>
bracketed (renderCommaSeparatedConstraint @SOP.SListI (bracketed . renderCommaSeparated renderSQL) xss)
-- | >>> printSQL $ cardinality (array [null_, false, true])
-- cardinality(ARRAY[NULL, FALSE, TRUE])
cardinality :: null ('PGvararray ty) --> null 'PGint8
cardinality = unsafeFunction "cardinality"
-- | >>> printSQL $ array [null_, false, true] & index 2
-- (ARRAY[NULL, FALSE, TRUE])[2]
index
:: Word64 -- ^ index
-> null ('PGvararray ty) --> NullifyType ty
index i arr = UnsafeExpression $
parenthesized (renderSQL arr) <> "[" <> fromString (show i) <> "]"
-- | Typesafe indexing of fixed length arrays.
--
-- >>> printSQL $ array1 (true *: false) & index1 @1
-- (ARRAY[TRUE, FALSE])[1]
index1
:: forall i n ty
. (1 <= i, i <= n, KnownNat i)
=> 'NotNull ('PGfixarray '[n] ty) --> ty
-- ^ vector index
index1 arr = UnsafeExpression $
parenthesized (renderSQL arr)
<> "[" <> fromString (show (natVal (SOP.Proxy @i))) <> "]"
-- | Typesafe indexing of fixed size matrices.
--
-- >>> printSQL $ array2 ((true *: false) *: (false *: true)) & index2 @1 @2
-- (ARRAY[[TRUE, FALSE], [FALSE, TRUE]])[1][2]
index2
:: forall i j m n ty
. ( 1 <= i, i <= m, KnownNat i
, 1 <= j, j <= n, KnownNat j
)
=> 'NotNull ('PGfixarray '[m,n] ty) --> ty
-- ^ matrix index
index2 arr = UnsafeExpression $
parenthesized (renderSQL arr)
<> "[" <> fromString (show (natVal (SOP.Proxy @i))) <> "]"
<> "[" <> fromString (show (natVal (SOP.Proxy @j))) <> "]"
-- | Expand an array to a set of rows
--
-- >>> printSQL $ unnest (array [null_, false, true])
-- unnest(ARRAY[NULL, FALSE, TRUE])
unnest :: null ('PGvararray ty) -|-> ("unnest" ::: '["unnest" ::: ty])
unnest = unsafeSetFunction "unnest"
{- |
The right-hand side is a parenthesized expression,
which must yield an array value. The left-hand expression
is evaluated and compared to each element of the array using
the given `Operator`, which must yield a Boolean result.
The result of `arrAll` is `true` if all comparisons yield true
(including the case where the array has zero elements).
The result is `false` if any false result is found.
If the array expression yields a null array,
the result of `arrAll` will be null. If the left-hand expression yields null,
the result of `arrAll` is ordinarily null
(though a non-strict comparison `Operator`
could possibly yield a different result).
Also, if the right-hand array contains any null
elements and no false comparison result is obtained,
the result of `arrAll` will be null, not true
(again, assuming a strict comparison `Operator`).
This is in accordance with SQL's normal rules for Boolean
combinations of null values.
>>> printSQL $ arrAll true (.==) (array [true, false, null_])
(TRUE = ALL (ARRAY[TRUE, FALSE, NULL]))
>>> printSQL $ arrAll "hi" like (array ["bi","hi"])
((E'hi' :: text) LIKE ALL (ARRAY[(E'bi' :: text), (E'hi' :: text)]))
-}
arrAll
:: Expression grp lat with db params from ty1 -- ^ expression
-> Operator ty1 ty2 ('Null 'PGbool) -- ^ operator
-> Expression grp lat with db params from ('Null ('PGvararray ty2)) -- ^ array
-> Condition grp lat with db params from
arrAll x (?) xs = x ? (UnsafeExpression $ "ALL" <+> parenthesized (renderSQL xs))
{- |
The right-hand side is a parenthesized expression, which must yield an array
value. The left-hand expression is evaluated and compared to each element of
the array using the given `Operator`, which must yield a Boolean result. The
result of `arrAny` is `true` if any true result is obtained. The result is
`false` if no true result is found (including the case where the array
has zero elements).
If the array expression yields a null array, the result of `arrAny` will
be null. If the left-hand expression yields null, the result of `arrAny` is
ordinarily null (though a non-strict comparison `Operator` could possibly
yield a different result). Also, if the right-hand array contains any
null elements and no true comparison result is obtained, the result of
`arrAny` will be null, not false
(again, assuming a strict comparison `Operator`).
This is in accordance with SQL's normal rules for
Boolean combinations of null values.
>>> printSQL $ arrAny true (.==) (array [true, false, null_])
(TRUE = ANY (ARRAY[TRUE, FALSE, NULL]))
>>> printSQL $ arrAny "hi" like (array ["bi","hi"])
((E'hi' :: text) LIKE ANY (ARRAY[(E'bi' :: text), (E'hi' :: text)]))
-}
arrAny
:: Expression grp lat with db params from ty1 -- ^ expression
-> Operator ty1 ty2 ('Null 'PGbool) -- ^ operator
-> Expression grp lat with db params from ('Null ('PGvararray ty2)) -- ^ array
-> Condition grp lat with db params from
arrAny x (?) xs = x ? (UnsafeExpression $ "ANY" <+> parenthesized (renderSQL xs))