@@ -11,6 +11,7 @@ Squeal expressions are the atoms used to build statements.
1111{-# OPTIONS_GHC -fno-warn-redundant-constraints #-}
1212{-# LANGUAGE
1313 AllowAmbiguousTypes
14+ , ConstraintKinds
1415 , DeriveGeneric
1516 , FlexibleContexts
1617 , FlexibleInstances
@@ -25,6 +26,7 @@ Squeal expressions are the atoms used to build statements.
2526 , TypeInType
2627 , TypeOperators
2728 , UndecidableInstances
29+ , RankNTypes
2830#-}
2931
3032module Squeal.PostgreSQL.Expression
@@ -48,6 +50,7 @@ module Squeal.PostgreSQL.Expression
4850 , unsafeBinaryOp
4951 , unsafeUnaryOp
5052 , unsafeFunction
53+ , unsafeVariadicFunction
5154 , atan2_
5255 , cast
5356 , quot_
@@ -83,6 +86,53 @@ module Squeal.PostgreSQL.Expression
8386 , upper
8487 , charLength
8588 , like
89+ -- ** json or jsonb operators
90+ , PGarray
91+ , PGarrayOf
92+ , PGjsonKey
93+ , PGjson_
94+ , (.->)
95+ , (.->>)
96+ , (.#>)
97+ , (.#>>)
98+ -- *** jsonb only operators
99+ , (.@>)
100+ , (.<@)
101+ , (.?)
102+ , (.?|)
103+ , (.?&)
104+ , (.-.)
105+ , (#-.)
106+ -- *** Functions
107+ , jsonLit
108+ , jsonbLit
109+ , toJson
110+ , toJsonb
111+ , arrayToJson
112+ , rowToJson
113+ , jsonBuildArray
114+ , jsonbBuildArray
115+ , jsonBuildObject
116+ , jsonbBuildObject
117+ , jsonObject
118+ , jsonbObject
119+ , jsonZipObject
120+ , jsonbZipObject
121+ , jsonArrayLength
122+ , jsonbArrayLength
123+ , jsonExtractPath
124+ , jsonbExtractPath
125+ , jsonExtractPathAsText
126+ , jsonbExtractPathAsText
127+ , jsonObjectKeys
128+ , jsonbObjectKeys
129+ , jsonTypeof
130+ , jsonbTypeof
131+ , jsonStripNulls
132+ , jsonbStripNulls
133+ , jsonbSet
134+ , jsonbInsert
135+ , jsonbPretty
86136 -- ** Aggregation
87137 , unsafeAggregate, unsafeAggregateDistinct
88138 , sum_, sumDistinct
@@ -135,8 +185,10 @@ module Squeal.PostgreSQL.Expression
135185import Control.Category
136186import Control.DeepSeq
137187import Data.ByteString (ByteString)
188+import Data.ByteString.Lazy (toStrict)
138189import Data.Function ((&))
139−import Data.Semigroup
190+import Data.Semigroup hiding (All)
191+import qualified Data.Aeson as JSON
140192import Data.Ratio
141193import Data.String
142194import Generics.SOP hiding (from)
@@ -482,6 +534,16 @@ unsafeFunction
482534unsafeFunction fun x = UnsafeExpression $
483535 fun <> parenthesized (renderExpression x)
484536
537+-- | Helper for defining variadic functions.
538+unsafeVariadicFunction
539+ :: SListI elems
540+ => ByteString
541+ -- ^ function
542+ -> NP (Expression schema relations grouping params) elems
543+ -> Expression schema relations grouping params ret
544+unsafeVariadicFunction fun x = UnsafeExpression $
545+ fun <> parenthesized (commaSeparated (hcollapse (hmap (K . renderExpression) x)))
546+
485547instance PGNum ty
486548 => Num (Expression schema relations grouping params (nullity ty)) where
487549 (+) = unsafeBinaryOp "+"
@@ -870,6 +932,400 @@ like
870932 -> Expression schema relations grouping params (nullity 'PGbool)
871933like = unsafeBinaryOp "LIKE"
872934
935+{-----------------------------------------
936+ -- json and jsonb support
937+
938+See https://www.postgresql.org/docs/10/static/functions-json.html -- most
939+comments lifted directly from this page.
940+
941+Table 9.44: json and jsonb operators
942+-----------------------------------------}
943+
944+-- | Get JSON value (object field or array element) at a key.
945+(.->)
946+ :: (PGjson_ json, PGjsonKey key)
947+ => Expression schema relations grouping params (nullity json)
948+ -> Expression schema relations grouping params (nullity key)
949+ -> Expression schema relations grouping params ('Null json)
950+(.->) = unsafeBinaryOp "->"
951+
952+-- | Get JSON value (object field or array element) at a key, as text.
953+(.->>)
954+ :: (PGjson_ json, PGjsonKey key)
955+ => Expression schema relations grouping params (nullity json)
956+ -> Expression schema relations grouping params (nullity key)
957+ -> Expression schema relations grouping params ('Null 'PGtext)
958+(.->>) = unsafeBinaryOp "->>"
959+
960+-- | Get JSON value at a specified path.
961+(.#>)
962+ :: (PGjson_ json, PGtextArray "(.#>)" path)
963+ => Expression schema relations grouping params (nullity json)
964+ -> Expression schema relations grouping params (nullity path)
965+ -> Expression schema relations grouping params ('Null json)
966+(.#>) = unsafeBinaryOp "#>"
967+
968+-- | Get JSON value at a specified path as text.
969+(.#>>)
970+ :: (PGjson_ json, PGtextArray "(.#>>)" path)
971+ => Expression schema relations grouping params (nullity json)
972+ -> Expression schema relations grouping params (nullity path)
973+ -> Expression schema relations grouping params ('Null 'PGtext)
974+(.#>>) = unsafeBinaryOp "#>>"
975+
976+-- Additional jsonb operators
977+
978+-- | Does the left JSON value contain the right JSON path/value entries at the
979+-- top level?
980+(.@>)
981+ :: Expression schema relations grouping params (nullity 'PGjsonb)
982+ -> Expression schema relations grouping params (nullity 'PGjsonb)
983+ -> Condition schema relations grouping params
984+(.@>) = unsafeBinaryOp "@>"
985+
986+-- | Are the left JSON path/value entries contained at the top level within the
987+-- right JSON value?
988+(.<@)
989+ :: Expression schema relations grouping params (nullity 'PGjsonb)
990+ -> Expression schema relations grouping params (nullity 'PGjsonb)
991+ -> Condition schema relations grouping params
992+(.<@) = unsafeBinaryOp "<@"
993+
994+-- | Does the string exist as a top-level key within the JSON value?
995+(.?)
996+ :: Expression schema relations grouping params (nullity 'PGjsonb)
997+ -> Expression schema relations grouping params (nullity 'PGtext)
998+ -> Condition schema relations grouping params
999+(.?) = unsafeBinaryOp "?"
1000+
1001+-- | Do any of these array strings exist as top-level keys?
1002+(.?|)
1003+ :: Expression schema relations grouping params (nullity 'PGjsonb)
1004+ -> Expression schema relations grouping params (nullity ('PGvararray ('NotNull 'PGtext)))
1005+ -> Condition schema relations grouping params
1006+(.?|) = unsafeBinaryOp "?|"
1007+
1008+-- | Do all of these array strings exist as top-level keys?
1009+(.?&)
1010+ :: Expression schema relations grouping params (nullity 'PGjsonb)
1011+ -> Expression schema relations grouping params (nullity ('PGvararray ('NotNull 'PGtext)))
1012+ -> Condition schema relations grouping params
1013+(.?&) = unsafeBinaryOp "?&"
1014+
1015+-- | Concatenate two jsonb values into a new jsonb value.
1016+instance
1017+ Semigroup (Expression schema relations grouping param (nullity 'PGjsonb)) where
1018+ (<>) = unsafeBinaryOp "||"
1019+
1020+-- | Delete a key or keys from a JSON object, or remove an array element.
1021+--
1022+-- If the right operand is..
1023+--
1024+-- @ text @: Delete key/value pair or string element from left operand. Key/value pairs
1025+-- are matched based on their key value.
1026+--
1027+-- @ text[] @: Delete multiple key/value pairs or string elements
1028+-- from left operand. Key/value pairs are matched based on their key value.
1029+--
1030+-- @ integer @: Delete the array element with specified index (Negative integers
1031+-- count from the end). Throws an error if top level container is not an array.
1032+(.-.)
1033+ :: (key `In` '[ 'PGtext, 'PGvararray ('NotNull 'PGtext), 'PGint4, 'PGint2 ]) -- hlint error without parens here
1034+ => Expression schema relations grouping params (nullity 'PGjsonb)
1035+ -> Expression schema relations grouping params (nullity key)
1036+ -> Expression schema relations grouping params (nullity 'PGjsonb)
1037+(.-.) = unsafeBinaryOp "-"
1038+
1039+-- | Delete the field or element with specified path (for JSON arrays, negative
1040+-- integers count from the end)
1041+(#-.)
1042+ :: PGtextArray "(#-.)" arrayty
1043+ => Expression schema relations grouping params (nullity 'PGjsonb)
1044+ -> Expression schema relations grouping params (nullity arrayty)
1045+ -> Expression schema relations grouping params (nullity 'PGjsonb)
1046+(#-.) = unsafeBinaryOp "#-"
1047+
1048+{-----------------------------------------
1049+Table 9.45: JSON creation functions
1050+-----------------------------------------}
1051+
1052+-- | Literal binary JSON
1053+jsonbLit :: JSON.Value -> Expression schema relations grouping params (nullity 'PGjsonb)
1054+jsonbLit = cast jsonb . UnsafeExpression . singleQuotedUtf8 . toStrict . JSON.encode
1055+
1056+-- | Literal JSON
1057+jsonLit :: JSON.Value -> Expression schema relations grouping params (nullity 'PGjson)
1058+jsonLit = cast json . UnsafeExpression . singleQuotedUtf8 . toStrict . JSON.encode
1059+
1060+-- | Returns the value as json. Arrays and composites are converted
1061+-- (recursively) to arrays and objects; otherwise, if there is a cast from the
1062+-- type to json, the cast function will be used to perform the conversion;
1063+-- otherwise, a scalar value is produced. For any scalar type other than a
1064+-- number, a Boolean, or a null value, the text representation will be used, in
1065+-- such a fashion that it is a valid json value.
1066+toJson
1067+ :: Expression schema relations grouping params (nullity ty)
1068+ -> Expression schema relations grouping params (nullity 'PGjson)
1069+toJson = unsafeFunction "to_json"
1070+
1071+-- | Returns the value as jsonb. Arrays and composites are converted
1072+-- (recursively) to arrays and objects; otherwise, if there is a cast from the
1073+-- type to json, the cast function will be used to perform the conversion;
1074+-- otherwise, a scalar value is produced. For any scalar type other than a
1075+-- number, a Boolean, or a null value, the text representation will be used, in
1076+-- such a fashion that it is a valid jsonb value.
1077+toJsonb
1078+ :: Expression schema relations grouping params (nullity ty)
1079+ -> Expression schema relations grouping params (nullity 'PGjsonb)
1080+toJsonb = unsafeFunction "to_jsonb"
1081+
1082+-- | Returns the array as a JSON array. A PostgreSQL multidimensional array
1083+-- becomes a JSON array of arrays.
1084+arrayToJson
1085+ :: PGarray "arrayToJson" arr
1086+ => Expression schema relations grouping params (nullity arr)
1087+ -> Expression schema relations grouping params (nullity 'PGjson)
1088+arrayToJson = unsafeFunction "array_to_json"
1089+
1090+-- | Returns the row as a JSON object.
1091+rowToJson
1092+ :: Expression schema relations grouping params (nullity ('PGcomposite ty))
1093+ -> Expression schema relations grouping params (nullity 'PGjson)
1094+rowToJson = unsafeFunction "row_to_json"
1095+
1096+-- | Builds a possibly-heterogeneously-typed JSON array out of a variadic
1097+-- argument list.
1098+jsonBuildArray
1099+ :: SListI elems
1100+ => NP (Expression schema relations grouping params) elems
1101+ -> Expression schema relations grouping params (nullity 'PGjson)
1102+jsonBuildArray = unsafeVariadicFunction "json_build_array"
1103+
1104+-- | Builds a possibly-heterogeneously-typed (binary) JSON array out of a
1105+-- variadic argument list.
1106+jsonbBuildArray
1107+ :: SListI elems
1108+ => NP (Expression schema relations grouping params) elems
1109+ -> Expression schema relations grouping params (nullity 'PGjsonb)
1110+jsonbBuildArray = unsafeVariadicFunction "jsonb_build_array"
1111+
1112+unsafeRowFunction
1113+ :: All Top elems
1114+ => NP (Aliased (Expression schema relations grouping params)) elems
1115+ -> [ByteString]
1116+unsafeRowFunction =
1117+ (`appEndo` []) . hcfoldMap (Proxy :: Proxy Top)
1118+ (\(col `As` name) -> Endo $ \xs ->
1119+ renderAliasString name : renderExpression col : xs)
1120+
1121+-- | Builds a possibly-heterogeneously-typed JSON object out of a variadic
1122+-- argument list. The elements of the argument list must alternate between text
1123+-- and values.
1124+jsonBuildObject
1125+ :: All Top elems
1126+ => NP (Aliased (Expression schema relations grouping params)) elems
1127+ -> Expression schema relations grouping params (nullity 'PGjson)
1128+jsonBuildObject
1129+ = unsafeFunction "json_build_object"
1130+ . UnsafeExpression
1131+ . commaSeparated
1132+ . unsafeRowFunction
1133+
1134+-- | Builds a possibly-heterogeneously-typed (binary) JSON object out of a
1135+-- variadic argument list. The elements of the argument list must alternate
1136+-- between text and values.
1137+jsonbBuildObject
1138+ :: All Top elems
1139+ => NP (Aliased (Expression schema relations grouping params)) elems
1140+ -> Expression schema relations grouping params (nullity 'PGjsonb)
1141+jsonbBuildObject
1142+ = unsafeFunction "jsonb_build_object"
1143+ . UnsafeExpression
1144+ . commaSeparated
1145+ . unsafeRowFunction
1146+
1147+-- | Builds a JSON object out of a text array. The array must have either
1148+-- exactly one dimension with an even number of members, in which case they are
1149+-- taken as alternating key/value pairs, or two dimensions such that each inner
1150+-- array has exactly two elements, which are taken as a key/value pair.
1151+jsonObject
1152+ :: PGarrayOf "jsonObject" arr ('NotNull 'PGtext)
1153+ => Expression schema relations grouping params (nullity arr)
1154+ -> Expression schema relations grouping params (nullity 'PGjson)
1155+jsonObject = unsafeFunction "json_object"
1156+
1157+-- | Builds a binary JSON object out of a text array. The array must have either
1158+-- exactly one dimension with an even number of members, in which case they are
1159+-- taken as alternating key/value pairs, or two dimensions such that each inner
1160+-- array has exactly two elements, which are taken as a key/value pair.
1161+jsonbObject
1162+ :: PGarrayOf "jsonbObject" arr ('NotNull 'PGtext)
1163+ => Expression schema relations grouping params (nullity arr)
1164+ -> Expression schema relations grouping params (nullity 'PGjsonb)
1165+jsonbObject = unsafeFunction "jsonb_object"
1166+
1167+-- | This is an alternate form of 'jsonObject' that takes two arrays; one for
1168+-- keys and one for values, that are zipped pairwise to create a JSON object.
1169+jsonZipObject
1170+ :: ( PGarrayOf "jsonZipObject" keysArray ('NotNull 'PGtext)
1171+ , PGarrayOf "jsonZipObject" valuesArray ('NotNull 'PGtext))
1172+ => Expression schema relations grouping params (nullity keysArray)
1173+ -> Expression schema relations grouping params (nullity valuesArray)
1174+ -> Expression schema relations grouping params (nullity 'PGjson)
1175+jsonZipObject ks vs =
1176+ unsafeVariadicFunction "json_object" (ks :* vs :* Nil)
1177+
1178+-- | This is an alternate form of 'jsonObject' that takes two arrays; one for
1179+-- keys and one for values, that are zipped pairwise to create a binary JSON
1180+-- object.
1181+jsonbZipObject
1182+ :: ( PGarrayOf "jsonbZipObject" keysArray ('NotNull 'PGtext)
1183+ , PGarrayOf "jsonbZipObject" valuesArray ('NotNull 'PGtext))
1184+ => Expression schema relations grouping params (nullity keysArray)
1185+ -> Expression schema relations grouping params (nullity valuesArray)
1186+ -> Expression schema relations grouping params (nullity 'PGjsonb)
1187+jsonbZipObject ks vs =
1188+ unsafeVariadicFunction "jsonb_object" (ks :* vs :* Nil)
1189+
1190+{-----------------------------------------
1191+Table 9.46: JSON processing functions
1192+-----------------------------------------}
1193+
1194+-- | Returns the number of elements in the outermost JSON array.
1195+jsonArrayLength
1196+ :: Expression schema relations grouping params (nullity 'PGjson)
1197+ -> Expression schema relations grouping params (nullity 'PGint4)
1198+jsonArrayLength = unsafeFunction "json_array_length"
1199+
1200+-- | Returns the number of elements in the outermost binary JSON array.
1201+jsonbArrayLength
1202+ :: Expression schema relations grouping params (nullity 'PGjsonb)
1203+ -> Expression schema relations grouping params (nullity 'PGint4)
1204+jsonbArrayLength = unsafeFunction "jsonb_array_length"
1205+
1206+-- | Returns JSON value pointed to by the given path (equivalent to #>
1207+-- operator).
1208+jsonExtractPath
1209+ :: SListI elems
1210+ => Expression schema relations grouping params (nullity 'PGjson)
1211+ -> NP (Expression schema relations grouping params) elems
1212+ -> Expression schema relations grouping params (nullity 'PGjsonb)
1213+jsonExtractPath x xs =
1214+ unsafeVariadicFunction "json_extract_path" (x :* xs)
1215+
1216+-- | Returns JSON value pointed to by the given path (equivalent to #>
1217+-- operator).
1218+jsonbExtractPath
1219+ :: SListI elems
1220+ => Expression schema relations grouping params (nullity 'PGjsonb)
1221+ -> NP (Expression schema relations grouping params) elems
1222+ -> Expression schema relations grouping params (nullity 'PGjsonb)
1223+jsonbExtractPath x xs =
1224+ unsafeVariadicFunction "jsonb_extract_path" (x :* xs)
1225+
1226+-- | Returns JSON value pointed to by the given path (equivalent to #>
1227+-- operator), as text.
1228+jsonExtractPathAsText
1229+ :: SListI elems
1230+ => Expression schema relations grouping params (nullity 'PGjson)
1231+ -> NP (Expression schema relations grouping params) elems
1232+ -> Expression schema relations grouping params (nullity 'PGjson)
1233+jsonExtractPathAsText x xs =
1234+ unsafeVariadicFunction "json_extract_path_text" (x :* xs)
1235+
1236+-- | Returns JSON value pointed to by the given path (equivalent to #>
1237+-- operator), as text.
1238+jsonbExtractPathAsText
1239+ :: SListI elems
1240+ => Expression schema relations grouping params (nullity 'PGjsonb)
1241+ -> NP (Expression schema relations grouping params) elems
1242+ -> Expression schema relations grouping params (nullity 'PGjsonb)
1243+jsonbExtractPathAsText x xs =
1244+ unsafeVariadicFunction "jsonb_extract_path_text" (x :* xs)
1245+
1246+-- | Returns set of keys in the outermost JSON object.
1247+jsonObjectKeys
1248+ :: Expression schema relations grouping params (nullity 'PGjson)
1249+ -> Expression schema relations grouping params (nullity 'PGtext)
1250+jsonObjectKeys = unsafeFunction "json_object_keys"
1251+
1252+-- | Returns set of keys in the outermost JSON object.
1253+jsonbObjectKeys
1254+ :: Expression schema relations grouping params (nullity 'PGjsonb)
1255+ -> Expression schema relations grouping params (nullity 'PGtext)
1256+jsonbObjectKeys = unsafeFunction "jsonb_object_keys"
1257+
1258+-- | Returns the type of the outermost JSON value as a text string. Possible
1259+-- types are object, array, string, number, boolean, and null.
1260+jsonTypeof
1261+ :: Expression schema relations grouping params (nullity 'PGjson)
1262+ -> Expression schema relations grouping params (nullity 'PGtext)
1263+jsonTypeof = unsafeFunction "json_typeof"
1264+
1265+-- | Returns the type of the outermost binary JSON value as a text string.
1266+-- Possible types are object, array, string, number, boolean, and null.
1267+jsonbTypeof
1268+ :: Expression schema relations grouping params (nullity 'PGjsonb)
1269+ -> Expression schema relations grouping params (nullity 'PGtext)
1270+jsonbTypeof = unsafeFunction "jsonb_typeof"
1271+
1272+-- | Returns its argument with all object fields that have null values omitted.
1273+-- Other null values are untouched.
1274+jsonStripNulls
1275+ :: Expression schema relations grouping params (nullity 'PGjson)
1276+ -> Expression schema relations grouping params (nullity 'PGjson)
1277+jsonStripNulls = unsafeFunction "json_strip_nulls"
1278+
1279+-- | Returns its argument with all object fields that have null values omitted.
1280+-- Other null values are untouched.
1281+jsonbStripNulls
1282+ :: Expression schema relations grouping params (nullity 'PGjsonb)
1283+ -> Expression schema relations grouping params (nullity 'PGjsonb)
1284+jsonbStripNulls = unsafeFunction "jsonb_strip_nulls"
1285+
1286+-- | @ jsonbSet target path new_value create_missing @
1287+--
1288+-- Returns target with the section designated by path replaced by new_value,
1289+-- or with new_value added if create_missing is true ( default is true) and the
1290+-- item designated by path does not exist. As with the path orientated
1291+-- operators, negative integers that appear in path count from the end of JSON
1292+-- arrays.
1293+jsonbSet
1294+ :: PGtextArray "jsonbSet" arr
1295+ => Expression schema relations grouping params (nullity 'PGjsonb)
1296+ -> Expression schema relations grouping params (nullity arr)
1297+ -> Expression schema relations grouping params (nullity 'PGjsonb)
1298+ -> Maybe (Expression schema relations grouping params (nullity 'PGbool))
1299+ -> Expression schema relations grouping params (nullity 'PGjsonb)
1300+jsonbSet tgt path val createMissing = case createMissing of
1301+ Just m -> unsafeVariadicFunction "jsonb_set" (tgt :* path :* val :* m :* Nil)
1302+ Nothing -> unsafeVariadicFunction "jsonb_set" (tgt :* path :* val :* Nil)
1303+
1304+-- | @ jsonbInsert target path new_value insert_after @
1305+--
1306+-- Returns target with new_value inserted. If target section designated by
1307+-- path is in a JSONB array, new_value will be inserted before target or after
1308+-- if insert_after is true (default is false). If target section designated by
1309+-- path is in JSONB object, new_value will be inserted only if target does not
1310+-- exist. As with the path orientated operators, negative integers that appear
1311+-- in path count from the end of JSON arrays.
1312+jsonbInsert
1313+ :: PGtextArray "jsonbInsert" arr
1314+ => Expression schema relations grouping params (nullity 'PGjsonb)
1315+ -> Expression schema relations grouping params (nullity arr)
1316+ -> Expression schema relations grouping params (nullity 'PGjsonb)
1317+ -> Maybe (Expression schema relations grouping params (nullity 'PGbool))
1318+ -> Expression schema relations grouping params (nullity 'PGjsonb)
1319+jsonbInsert tgt path val insertAfter = case insertAfter of
1320+ Just i -> unsafeVariadicFunction "jsonb_insert" (tgt :* path :* val :* i :* Nil)
1321+ Nothing -> unsafeVariadicFunction "jsonb_insert" (tgt :* path :* val :* Nil)
1322+
1323+-- | Returns its argument as indented JSON text.
1324+jsonbPretty
1325+ :: Expression schema relations grouping params (nullity 'PGjsonb)
1326+ -> Expression schema relations grouping params (nullity 'PGtext)
1327+jsonbPretty = unsafeFunction "jsonb_pretty"
1328+
8731329{-----------------------------------------
8741330aggregation
8751331-----------------------------------------}