Skip to content

Commit c8be926

Browse files
committed
endpoints: rewrite SQL builders with hasql-interpolate ^{} splices
Replace string-concat + rawSql glue with single multiline [HI.sql|...|] quasiquotes using ^{sql} splices for fragments and #{val} for values. Drop NeatInterpolation usage. Collapse countEndpointInbox default to correct enp alias.
1 parent d35f83f commit c8be926

7 files changed

Lines changed: 171 additions & 202 deletions

File tree

cabal.project

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -119,12 +119,12 @@ source-repository-package
119119
tag: bb90850a0eeed02a0b03244283a1236b5ce03471
120120
subdir: .
121121

122-
-- Fork: swap haskell-src-meta -> ghc-hs-meta so #{x.y} record-dot
123-
-- splices parse correctly. Drop once upstream merges (PR open).
122+
-- Upstream merge of haskell-src-meta -> ghc-hs-meta (PR #32) not yet on
123+
-- Hackage; pin to the merge commit until next release.
124124
source-repository-package
125125
type: git
126-
location: https://github.com/tonyalaribe/hasql-interpolate
127-
tag: 22909b5316bb314cbb495208061b8f0f268bf0aa
126+
location: https://github.com/awkward-squad/hasql-interpolate
127+
tag: 8a13a8b06babc569ffd216fd9561aae58818b66f
128128

129129
constraints:
130130
streamly ^>=0.10.0,

src/Models/Apis/Endpoints.hs

Lines changed: 138 additions & 169 deletions
Large diffs are not rendered by default.

src/Models/Apis/LogQueries.hs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -532,8 +532,8 @@ fetchLogPatterns enableTfReads pid queryAST dateRange sourceM targetM skip = do
532532
-- Fetch member hashes for merged patterns so their hourly stats are included
533533
memberHashMap :: HM.HashMap Text [Text] <- HM.fromListWith (++) . map (\(canonical, mHash) -> (canonical, [mHash])) <$> Hasql.interp [HI.sql|SELECT c.pattern_hash, m.pattern_hash FROM apis.log_patterns m JOIN apis.log_patterns c ON m.canonical_id = c.id WHERE c.project_id = #{pid} AND c.source_field = #{target} AND c.pattern_hash = ANY(#{hashes})|]
534534
let allHashes = V.fromList $ concatMap (\h -> h : fromMaybe [] (HM.lookup h memberHashMap)) $ V.toList hashes
535-
hourlyRows :: [(Text, UTCTime, Int)] <- Hasql.interp [HI.sql|SELECT pattern_hash, hour_bucket, event_count::BIGINT FROM apis.log_pattern_hourly_stats WHERE project_id = #{pid} AND source_field = #{target} AND pattern_hash = ANY(#{allHashes}) AND hour_bucket >= #{hourlyFrom} AND hour_bucket <= #{hourlyTo} ORDER BY pattern_hash, hour_bucket|]
536-
let volumeMap = HM.fromListWith (++) [(h, [(t, c)]) | (h, t, c) <- hourlyRows]
535+
hourlyRows :: [(Text, UTCTime, Int64)] <- Hasql.interp [HI.sql|SELECT pattern_hash, hour_bucket, event_count::BIGINT FROM apis.log_pattern_hourly_stats WHERE project_id = #{pid} AND source_field = #{target} AND pattern_hash = ANY(#{allHashes}) AND hour_bucket >= #{hourlyFrom} AND hour_bucket <= #{hourlyTo} ORDER BY pattern_hash, hour_bucket|]
536+
let volumeMap = HM.fromListWith (++) [(h, [(t, fromIntegral c :: Int)]) | (h, t, c) <- hourlyRows]
537537
lookupVolume h = buildHourlyBuckets now $ concatMap (\mh -> fromMaybe [] $ HM.lookup mh volumeMap) (h : fromMaybe [] (HM.lookup h memberHashMap))
538538
pure (totalPatterns, [PatternRow{logPattern = pat, count = cnt, level = lvl, service = svc, volume = lookupVolume h, mergedCount = mc, isError = isErr} | (pat, cnt, lvl, svc, h, mc, _, isErr) <- precomputed])
539539
else do

src/Models/Projects/Projects.hs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -210,12 +210,18 @@ insertUser :: DB es => User -> Eff es ()
210210
insertUser u = EHasql.interpExecute_ [HI.sql| INSERT INTO users.users (id, created_at, updated_at, deleted_at, active, first_name, last_name, display_image_url, email, phone_number, is_sudo) VALUES (#{u.id}, #{u.createdAt}, #{u.updatedAt}, #{u.deletedAt}, #{u.active}, #{u.firstName}, #{u.lastName}, #{u.displayImageUrl}, #{u.email}, #{u.phoneNumber}, #{u.isSudo}) |]
211211

212212

213+
-- | hasql-interpolate's strict OID check rejects the @email@ domain (OID ≠ text);
214+
-- list columns explicitly and cast @email::text@ so the generic 'DecodeRow' just works.
215+
userColsSql :: HI.Sql
216+
userColsSql = [HI.sql| id, created_at, updated_at, deleted_at, active, first_name, last_name, display_image_url, email::text, is_sudo, phone_number |]
217+
218+
213219
userById :: DB es => UserId -> Eff es (Maybe User)
214-
userById uid = EHasql.interpOne [HI.sql| SELECT * FROM users.users WHERE id = #{uid} |]
220+
userById uid = EHasql.interpOne $ [HI.sql| SELECT |] <> userColsSql <> [HI.sql| FROM users.users WHERE id = #{uid} |]
215221

216222

217223
userByEmail :: DB es => Text -> Eff es (Maybe User)
218-
userByEmail email = EHasql.interpOne [HI.sql| SELECT * FROM users.users WHERE email = #{email} |]
224+
userByEmail email = EHasql.interpOne $ [HI.sql| SELECT |] <> userColsSql <> [HI.sql| FROM users.users WHERE email = #{email} |]
219225

220226

221227
userIdByEmail :: DB es => Text -> Eff es (Maybe UserId)
@@ -592,7 +598,7 @@ queryLibHistoryForUser pid uid =
592598
)
593599
UNION ALL
594600
(
595-
SELECT id, project_id, created_at, updated_at, user_id, query_type, query_text, query_ast, title, user_id=#{uid}::uuid as byMe
601+
SELECT id, project_id, created_at, updated_at, user_id, query_type::text, query_text, query_ast, title, user_id=#{uid}::uuid as byMe
596602
FROM projects.query_library
597603
WHERE user_id = #{uid}::uuid AND project_id = #{pid}::uuid AND query_type = 'saved'
598604
ORDER BY created_at DESC

src/Models/Telemetry/Telemetry.hs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1239,8 +1239,14 @@ data Context = Context
12391239
deriving (AE.FromJSON, AE.ToJSON) via DAE.Snake Context
12401240

12411241

1242-
-- Field order matches 'otelSpanColsSql' (and the INSERT path in 'otelColumns')
1242+
-- | Field order matches 'otelSpanColsSql' (and the INSERT path in 'otelColumns')
12431243
-- so FromRow / DecodeRow can be derived generically. Reorder both in lockstep.
1244+
--
1245+
-- Field count is pinned via doctest so any add/remove fails CI unless
1246+
-- 'otelSpanColsSql' + 'otelColumns' are touched in the same change:
1247+
--
1248+
-- >>> length otelColumns
1249+
-- 25
12441250
data OtelLogsAndSpans = OtelLogsAndSpans
12451251
{ project_id :: Text
12461252
, id :: Text -- UUID

src/Pages/Charts/Charts.hs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -275,10 +275,10 @@ fetchMetricsData respDataType sqlQuery now fromD toD authCtx dbSource = do
275275

276276
try @SomePostgreSqlException $ checkpoint (toAnnotation (respDataType, sqlQuery)) $ case respDataType of
277277
DTFloat -> do
278-
chartData <- withResource pool \conn -> query_ conn (Query $ encodeUtf8 sqlQuery) :: IO [Only Double]
278+
chartData <- withResource pool \conn -> query_ conn (Query $ encodeUtf8 sqlQuery) :: IO [Only (Maybe Double)]
279279
pure
280280
baseMetricsData
281-
{ dataFloat = fromOnly <$> listToMaybe chartData
281+
{ dataFloat = listToMaybe chartData >>= fromOnly
282282
, rowsCount = 1
283283
}
284284
DTMetric -> do

src/Pkg/DeriveUtils.hs

Lines changed: 9 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -190,14 +190,14 @@ instance (KnownSymbol prefix, Show a) => HI.EncodeValue (WrappedEnum prefix a) w
190190
encodeValue = (\(WrappedEnum a) -> T.toUpper $ toText $ drop (length $ symbolVal (Proxy @prefix)) $ show a) `contramap` E.text
191191

192192

193+
-- | Shared helper for hasql 'D.DecodeValue' instances that parse text and refine into a Haskell value.
194+
refineText :: Text -> (Text -> Maybe a) -> D.Value a
195+
refineText ctx f = D.refine (\t -> maybe (Left (ctx <> ": cannot parse " <> t)) Right (f t)) D.text
196+
197+
193198
instance (KnownSymbol prefix, Read a) => HI.DecodeValue (WrappedEnum prefix a) where
194-
decodeValue =
195-
D.refine
196-
( \t -> case readMaybe (symbolVal (Proxy @prefix) <> toString (T.toTitle t)) of
197-
Just a -> Right (WrappedEnum a)
198-
Nothing -> Left ("WrappedEnum: cannot parse " <> t)
199-
)
200-
D.text
199+
decodeValue = refineText "WrappedEnum" \t ->
200+
WrappedEnum <$> readMaybe (symbolVal (Proxy @prefix) <> toString (T.toTitle t))
201201

202202

203203
instance (KnownSymbol prefix, Read a) => HI.DecodeRow (WrappedEnum prefix a) where
@@ -233,13 +233,7 @@ instance (KnownSymbol prefix, Show a) => HI.EncodeValue (WrappedEnumSC prefix a)
233233

234234

235235
instance (KnownSymbol prefix, Read a) => HI.DecodeValue (WrappedEnumSC prefix a) where
236-
decodeValue =
237-
D.refine
238-
( \t -> case decodeEnumSC @prefix (toString t) of
239-
Just a -> Right (WrappedEnumSC a)
240-
Nothing -> Left ("WrappedEnumSC: cannot parse " <> t)
241-
)
242-
D.text
236+
decodeValue = refineText "WrappedEnumSC" (fmap WrappedEnumSC . decodeEnumSC @prefix . toString)
243237

244238

245239
instance (KnownSymbol prefix, Read a) => HI.DecodeRow (WrappedEnumSC prefix a) where
@@ -334,13 +328,7 @@ instance Show a => HI.EncodeValue (WrappedEnumShow a) where
334328

335329

336330
instance Read a => HI.DecodeValue (WrappedEnumShow a) where
337-
decodeValue =
338-
D.refine
339-
( \t -> case readMaybe (toString t) of
340-
Just a -> Right (WrappedEnumShow a)
341-
Nothing -> Left ("WrappedEnumShow: cannot parse " <> t)
342-
)
343-
D.text
331+
decodeValue = refineText "WrappedEnumShow" (fmap WrappedEnumShow . readMaybe . toString)
344332

345333

346334
data BaselineState = BSLearning | BSEstablished

0 commit comments

Comments
 (0)