{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
module Arbiter.Core.Job.Schema.Groups
(
createJobQueueGroupKeyIndexSQL
, createJobQueueGroupRetriedIndexSQL
, createJobQueueGroupedDueIndexSQL
, createJobQueueGroupInFlightIndexSQL
, createGroupsTableSQL
, migrateGroupsReadyRankingSQL
, createGroupsEmptiedIndexSQL
, groupAggregates
, inFlightPredicate
, createGroupsTriggerFunctionsSQL
, createGroupsTriggersSQL
) where
import Data.Text (Text)
import Data.Text qualified as T
import NeatInterpolation (text)
import Arbiter.Core.Job.Schema
( createMaintenanceTriggersSQL
, jobQueueGroupsTable
, jobQueueTable
, maintenanceFunctionNames
)
import Arbiter.Core.SqlLiterals (quoteIdentifier)
createJobQueueGroupKeyIndexSQL :: Text -> Text -> Text
createJobQueueGroupKeyIndexSQL :: Text -> Text -> Text
createJobQueueGroupKeyIndexSQL Text
schemaName Text
tableName =
[Text] -> Text
T.unlines
[ Text
"CREATE INDEX IF NOT EXISTS " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text -> Text
quoteIdentifier (Text
"idx_" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
tableName Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"_group_key")
, Text
"ON " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text -> Text -> Text
jobQueueTable Text
schemaName Text
tableName Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
" (group_key, priority ASC, id ASC)"
, Text
"WHERE group_key IS NOT NULL;"
]
createJobQueueGroupRetriedIndexSQL :: Text -> Text -> Text
createJobQueueGroupRetriedIndexSQL :: Text -> Text -> Text
createJobQueueGroupRetriedIndexSQL Text
schemaName Text
tableName =
[Text] -> Text
T.unlines
[ Text
"CREATE INDEX IF NOT EXISTS " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text -> Text
quoteIdentifier (Text
"idx_" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
tableName Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"_group_retried")
, Text
"ON " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text -> Text -> Text
jobQueueTable Text
schemaName Text
tableName Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
" (group_key, attempts DESC, priority ASC, id ASC)"
, Text
"WHERE group_key IS NOT NULL AND attempts > 0;"
]
createJobQueueGroupedDueIndexSQL :: Text -> Text -> Text
createJobQueueGroupedDueIndexSQL :: Text -> Text -> Text
createJobQueueGroupedDueIndexSQL Text
schemaName Text
tableName =
[Text] -> Text
T.unlines
[ Text
"CREATE INDEX IF NOT EXISTS " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text -> Text
quoteIdentifier (Text
"idx_" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
tableName Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"_grouped_due")
, Text
"ON " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text -> Text -> Text
jobQueueTable Text
schemaName Text
tableName Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
" (group_key, not_visible_until ASC)"
, Text
"WHERE group_key IS NOT NULL AND not_visible_until IS NOT NULL AND NOT suspended;"
]
createJobQueueGroupInFlightIndexSQL :: Text -> Text -> Text
createJobQueueGroupInFlightIndexSQL :: Text -> Text -> Text
createJobQueueGroupInFlightIndexSQL Text
schemaName Text
tableName =
[Text] -> Text
T.unlines
[ Text
"CREATE INDEX IF NOT EXISTS " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text -> Text
quoteIdentifier (Text
"idx_" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
tableName Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"_group_in_flight")
, Text
"ON " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text -> Text -> Text
jobQueueTable Text
schemaName Text
tableName Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
" (group_key, not_visible_until DESC NULLS LAST)"
, Text
"WHERE group_key IS NOT NULL AND not_visible_until IS NOT NULL AND NOT suspended AND (attempts > 0 OR throttled_until IS NOT NULL);"
]
createGroupsTableSQL :: Text -> Text -> Text
createGroupsTableSQL :: Text -> Text -> Text
createGroupsTableSQL Text
schemaName Text
tableName =
let groupsTbl :: Text
groupsTbl = Text -> Text -> Text
jobQueueGroupsTable Text
schemaName Text
tableName
in [Text] -> Text
T.unlines
[ Text
"CREATE TABLE IF NOT EXISTS " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
groupsTbl Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
" ("
, Text
" group_key TEXT PRIMARY KEY,"
, Text
" min_priority INT NOT NULL DEFAULT 0,"
, Text
" min_id BIGINT NOT NULL DEFAULT 0,"
, Text
" job_count INT NOT NULL DEFAULT 0,"
, Text
" in_flight_until TIMESTAMPTZ DEFAULT NULL"
, Text
");"
]
migrateGroupsReadyRankingSQL :: Text -> Text -> Text
migrateGroupsReadyRankingSQL :: Text -> Text -> Text
migrateGroupsReadyRankingSQL Text
schemaName Text
tableName =
let groupsTbl :: Text
groupsTbl = Text -> Text -> Text
jobQueueGroupsTable Text
schemaName Text
tableName
tbl :: Text
tbl = Text -> Text -> Text
jobQueueTable Text
schemaName Text
tableName
qidx :: Text -> Text
qidx Text
suffix = Text -> Text
quoteIdentifier (Text
"idx_" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
tableName Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
suffix)
in [Text] -> Text
T.unlines
[ Text
"ALTER TABLE " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
groupsTbl Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
" ADD COLUMN IF NOT EXISTS ready_count INT NOT NULL DEFAULT 0;"
, Text
"ALTER TABLE " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
groupsTbl Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
" ADD COLUMN IF NOT EXISTS next_due TIMESTAMPTZ;"
, Text
"UPDATE " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
groupsTbl Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
" g SET"
, Text
" min_priority = sub.mp, min_id = sub.mi, ready_count = COALESCE(sub.rc, 0), next_due = sub.nd"
, Text
"FROM ("
, Text
" SELECT group_key,"
, Text
" MIN(priority) AS mp,"
, Text
" MIN(id) AS mi,"
, Text
" COUNT(*) FILTER (WHERE not_visible_until IS NULL AND NOT suspended) AS rc,"
, Text
" MIN(not_visible_until) FILTER (WHERE not_visible_until IS NOT NULL AND NOT suspended) AS nd"
, Text
" FROM " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
tbl Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
" WHERE group_key IS NOT NULL GROUP BY group_key"
, Text
") sub WHERE g.group_key = sub.group_key;"
, Text
"DROP INDEX IF EXISTS " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text -> Text
quoteIdentifier Text
schemaName Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"." Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text -> Text
qidx Text
"_groups_ranking" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
";"
, Text
"CREATE INDEX IF NOT EXISTS "
Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text -> Text
qidx Text
"_groups_ranking"
Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
" ON "
Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
groupsTbl
Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
" (min_priority ASC, min_id ASC) WHERE ready_count > 0 AND in_flight_until IS NULL;"
, Text
"CREATE INDEX IF NOT EXISTS "
Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text -> Text
qidx Text
"_groups_next_due"
Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
" ON "
Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
groupsTbl
Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
" (next_due ASC) WHERE next_due IS NOT NULL;"
]
createGroupsEmptiedIndexSQL :: Text -> Text -> Text
createGroupsEmptiedIndexSQL :: Text -> Text -> Text
createGroupsEmptiedIndexSQL Text
schemaName Text
tableName =
[Text] -> Text
T.unlines
[ Text
"CREATE INDEX IF NOT EXISTS " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text -> Text
quoteIdentifier (Text
"idx_" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
tableName Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"_groups_emptied")
, Text
"ON " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text -> Text -> Text
jobQueueGroupsTable Text
schemaName Text
tableName Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
" (group_key)"
, Text
"WHERE job_count = 0;"
]
groupsMergeSet :: Text -> Text
groupsMergeSet :: Text -> Text
groupsMergeSet Text
groupsTbl =
let takesHead :: Text
takesHead =
[text|${groupsTbl}.job_count = 0 OR (EXCLUDED.min_priority, EXCLUDED.min_id) < (${groupsTbl}.min_priority, ${groupsTbl}.min_id)|]
in [text|
min_priority = CASE WHEN ${takesHead} THEN EXCLUDED.min_priority
ELSE ${groupsTbl}.min_priority END,
min_id = CASE WHEN ${takesHead} THEN EXCLUDED.min_id
ELSE ${groupsTbl}.min_id END,
job_count = ${groupsTbl}.job_count + EXCLUDED.job_count,
ready_count = ${groupsTbl}.ready_count + EXCLUDED.ready_count,
next_due = LEAST(${groupsTbl}.next_due, EXCLUDED.next_due)
|]
groupsLock :: Text -> Text -> Text
groupsLock :: Text -> Text -> Text
groupsLock Text
groupsTbl Text
keys =
[text|
PERFORM 1 FROM ${groupsTbl} g
WHERE g.group_key IN (${keys})
ORDER BY g.group_key FOR UPDATE;
|]
groupsInsertFunction :: Text -> Text -> Text -> Text
groupsInsertFunction :: Text -> Text -> Text -> Text
groupsInsertFunction Text
funcName Text
groupsTbl Text
dollarQuote =
let mergeSet :: Text
mergeSet = Text -> Text
groupsMergeSet Text
groupsTbl
aggs :: Text
aggs = Text -> Text
groupAggregates Text
""
lockRows :: Text
lockRows = Text -> Text -> Text
groupsLock Text
groupsTbl Text
"SELECT group_key FROM new_table WHERE group_key IS NOT NULL"
in [text|
CREATE OR REPLACE FUNCTION ${funcName}()
RETURNS TRIGGER AS ${dollarQuote}
BEGIN
IF NOT EXISTS (SELECT 1 FROM new_table WHERE group_key IS NOT NULL LIMIT 1) THEN
RETURN NULL;
END IF;
${lockRows}
INSERT INTO ${groupsTbl} (group_key, min_priority, min_id, job_count, ready_count, next_due)
SELECT group_key,
${aggs}
FROM new_table
WHERE group_key IS NOT NULL
GROUP BY group_key
ORDER BY group_key
ON CONFLICT (group_key) DO UPDATE SET
${mergeSet},
in_flight_until = CASE WHEN ${groupsTbl}.in_flight_until <= NOW()
THEN NULL ELSE ${groupsTbl}.in_flight_until END;
RETURN NULL;
END;
${dollarQuote} LANGUAGE plpgsql;
|]
groupAggregates :: Text -> Text
groupAggregates :: Text -> Text
groupAggregates Text
col =
let ready :: Text
ready = Text -> Text
readyPredicate Text
col
scheduled :: Text
scheduled = Text -> Text
scheduledPredicate Text
col
in [text|
MIN(${col}priority) AS min_priority,
(MIN(ARRAY[${col}priority::bigint, ${col}id]))[2] AS min_id,
COUNT(*) AS job_count,
COUNT(*) FILTER (WHERE ${ready}) AS ready_count,
MIN(${col}not_visible_until) FILTER (WHERE ${scheduled}) AS next_due
|]
readyPredicate :: Text -> Text
readyPredicate :: Text -> Text
readyPredicate Text
col = Text
col Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"not_visible_until IS NULL AND NOT " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
col Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"suspended"
scheduledPredicate :: Text -> Text
scheduledPredicate :: Text -> Text
scheduledPredicate Text
col = Text
col Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"not_visible_until IS NOT NULL AND NOT " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
col Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"suspended"
inFlightPredicate :: Text -> Text
inFlightPredicate :: Text -> Text
inFlightPredicate Text
col =
Text
col
Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"not_visible_until > NOW() AND NOT "
Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
col
Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"suspended AND ("
Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
col
Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"attempts > 0 OR "
Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
col
Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"throttled_until > NOW())"
summaryValueChanged :: Text
summaryValueChanged :: Text
summaryValueChanged =
Text
"n.priority IS DISTINCT FROM o.priority"
Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
" OR n.not_visible_until IS DISTINCT FROM o.not_visible_until"
Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
" OR n.suspended IS DISTINCT FROM o.suspended"
Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
" OR n.attempts IS DISTINCT FROM o.attempts"
Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
" OR n.throttled_until IS DISTINCT FROM o.throttled_until"
groupsSnapshotUpdate :: Text -> Text -> Text -> Text
groupsSnapshotUpdate :: Text -> Text -> Text -> Text
groupsSnapshotUpdate Text
groupsTbl Text
tbl Text
deltaRows =
let scheduled :: Text
scheduled = Text -> Text
scheduledPredicate Text
"q."
inFlight :: Text
inFlight = Text -> Text
inFlightPredicate Text
"q."
emptied :: Text
emptied = Text
"hp.priority IS NULL"
in [text|
WITH t AS (
SELECT g0.group_key,
CASE WHEN ${emptied} THEN 0 ELSE GREATEST(0, g0.job_count + d.count_delta) END AS job_count,
CASE WHEN ${emptied} THEN 0 ELSE GREATEST(0, g0.ready_count + d.ready_delta) END AS ready_count,
COALESCE(hp.priority, 0) AS min_priority,
COALESCE(hp.id, 0) AS min_id,
nd.not_visible_until AS next_due,
fi.not_visible_until AS in_flight_until
FROM (
${deltaRows}
) d
JOIN ${groupsTbl} g0 ON g0.group_key = d.group_key
LEFT JOIN LATERAL (
SELECT q.priority, q.id FROM ${tbl} q
WHERE q.group_key = d.group_key
ORDER BY q.priority ASC, q.id ASC LIMIT 1
) hp ON TRUE
LEFT JOIN LATERAL (
SELECT q.not_visible_until FROM ${tbl} q
WHERE q.group_key = d.group_key AND ${scheduled}
ORDER BY q.not_visible_until ASC LIMIT 1
) nd ON TRUE
LEFT JOIN LATERAL (
SELECT q.not_visible_until FROM ${tbl} q
WHERE q.group_key = d.group_key AND ${inFlight}
ORDER BY q.not_visible_until DESC NULLS LAST LIMIT 1
) fi ON TRUE
)
UPDATE ${groupsTbl} g
SET job_count = t.job_count,
ready_count = t.ready_count,
min_priority = t.min_priority,
min_id = t.min_id,
next_due = t.next_due,
in_flight_until = t.in_flight_until
FROM t
WHERE g.group_key = t.group_key
AND (g.job_count, g.ready_count, g.min_priority, g.min_id, g.next_due, g.in_flight_until)
IS DISTINCT FROM (t.job_count, t.ready_count, t.min_priority, t.min_id, t.next_due, t.in_flight_until);
|]
groupsDeleteFunction :: Text -> Text -> Text -> Text -> Text
groupsDeleteFunction :: Text -> Text -> Text -> Text -> Text
groupsDeleteFunction Text
funcName Text
groupsTbl Text
tbl Text
dollarQuote =
let ready :: Text
ready = Text -> Text
readyPredicate Text
""
lockRows :: Text
lockRows = Text -> Text -> Text
groupsLock Text
groupsTbl Text
"SELECT group_key FROM old_table WHERE group_key IS NOT NULL"
removeUpdate :: Text
removeUpdate =
Text -> Text -> Text -> Text
groupsSnapshotUpdate
Text
groupsTbl
Text
tbl
[text|
SELECT group_key, (-COUNT(*))::int AS count_delta,
(-COUNT(*) FILTER (WHERE ${ready}))::int AS ready_delta
FROM old_table WHERE group_key IS NOT NULL GROUP BY group_key
|]
in [text|
CREATE OR REPLACE FUNCTION ${funcName}()
RETURNS TRIGGER AS ${dollarQuote}
BEGIN
IF NOT EXISTS (SELECT 1 FROM old_table WHERE group_key IS NOT NULL LIMIT 1) THEN
RETURN NULL;
END IF;
${lockRows}
${removeUpdate}
RETURN NULL;
END;
${dollarQuote} LANGUAGE plpgsql;
|]
groupsUpdateFunction :: Text -> Text -> Text -> Text -> Text
groupsUpdateFunction :: Text -> Text -> Text -> Text -> Text
groupsUpdateFunction Text
funcName Text
groupsTbl Text
tbl Text
dollarQuote =
let valueChanged :: Text
valueChanged = Text
summaryValueChanged
changed :: Text
changed = Text
"n.group_key IS DISTINCT FROM o.group_key OR " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
valueChanged
readyOld :: Text
readyOld = Text -> Text
readyPredicate Text
"o."
readyNew :: Text
readyNew = Text -> Text
readyPredicate Text
"n."
aggsN :: Text
aggsN = Text -> Text
groupAggregates Text
"n."
mergeSet :: Text
mergeSet = Text -> Text
groupsMergeSet Text
groupsTbl
lockRows :: Text
lockRows =
Text -> Text -> Text
groupsLock
Text
groupsTbl
[text|
SELECT group_key
FROM new_table
WHERE group_key IS NOT NULL
UNION
SELECT group_key
FROM old_table
WHERE group_key IS NOT NULL
|]
departureUpdate :: Text
departureUpdate =
Text -> Text -> Text -> Text
groupsSnapshotUpdate
Text
groupsTbl
Text
tbl
[text|
SELECT o.group_key, (-COUNT(*))::int AS count_delta,
(-COUNT(*) FILTER (WHERE ${readyOld}))::int AS ready_delta
FROM old_table o JOIN new_table n ON n.id = o.id
WHERE o.group_key IS NOT NULL AND o.group_key IS DISTINCT FROM n.group_key
GROUP BY o.group_key
|]
arrivalUpdate :: Text
arrivalUpdate =
Text -> Text -> Text -> Text
groupsSnapshotUpdate
Text
groupsTbl
Text
tbl
[text|
SELECT n.group_key, 0 AS count_delta, 0 AS ready_delta
FROM new_table n JOIN old_table o ON o.id = n.id
WHERE n.group_key IS NOT NULL AND o.group_key IS DISTINCT FROM n.group_key
GROUP BY n.group_key
|]
sameGroupUpdate :: Text
sameGroupUpdate =
Text -> Text -> Text -> Text
groupsSnapshotUpdate
Text
groupsTbl
Text
tbl
[text|
SELECT n.group_key, 0 AS count_delta,
SUM((${readyNew})::int - (${readyOld})::int)::int AS ready_delta
FROM new_table n JOIN old_table o ON o.id = n.id
WHERE n.group_key IS NOT NULL
AND n.group_key IS NOT DISTINCT FROM o.group_key
AND (${valueChanged})
GROUP BY n.group_key
|]
in [text|
CREATE OR REPLACE FUNCTION ${funcName}()
RETURNS TRIGGER AS ${dollarQuote}
BEGIN
IF NOT EXISTS (SELECT 1 FROM new_table WHERE group_key IS NOT NULL LIMIT 1)
AND NOT EXISTS (SELECT 1 FROM old_table WHERE group_key IS NOT NULL LIMIT 1) THEN
RETURN NULL;
END IF;
IF NOT EXISTS (
SELECT 1 FROM new_table n JOIN old_table o ON o.id = n.id
WHERE (n.group_key IS NOT NULL OR o.group_key IS NOT NULL)
AND (${changed})
LIMIT 1
) THEN
RETURN NULL;
END IF;
${lockRows}
IF EXISTS (
SELECT 1 FROM new_table n JOIN old_table o ON o.id = n.id
WHERE o.group_key IS DISTINCT FROM n.group_key LIMIT 1
) THEN
${departureUpdate}
INSERT INTO ${groupsTbl} (group_key, min_priority, min_id, job_count, ready_count, next_due)
SELECT n.group_key,
${aggsN}
FROM new_table n JOIN old_table o ON o.id = n.id
WHERE n.group_key IS NOT NULL AND o.group_key IS DISTINCT FROM n.group_key
GROUP BY n.group_key ORDER BY n.group_key
ON CONFLICT (group_key) DO UPDATE SET ${mergeSet};
${arrivalUpdate}
END IF;
${sameGroupUpdate}
RETURN NULL;
END;
${dollarQuote} LANGUAGE plpgsql;
|]
createGroupsTriggerFunctionsSQL :: Text -> Text -> Text
createGroupsTriggerFunctionsSQL :: Text -> Text -> Text
createGroupsTriggerFunctionsSQL Text
schemaName Text
tableName =
let groupsTbl :: Text
groupsTbl = Text -> Text -> Text
jobQueueGroupsTable Text
schemaName Text
tableName
tbl :: Text
tbl = Text -> Text -> Text
jobQueueTable Text
schemaName Text
tableName
baseName :: Text
baseName = Text
"maintain_" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
tableName Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"_groups"
(Text
funcInsert, Text
funcDelete, Text
funcUpdate) = Text -> Text -> (Text, Text, Text)
maintenanceFunctionNames Text
schemaName Text
baseName
dollarQuote :: Text
dollarQuote = Text
"$$"
in [Text] -> Text
T.unlines
[ Text -> Text -> Text -> Text
groupsInsertFunction Text
funcInsert Text
groupsTbl Text
dollarQuote
, Text -> Text -> Text -> Text -> Text
groupsDeleteFunction Text
funcDelete Text
groupsTbl Text
tbl Text
dollarQuote
, Text -> Text -> Text -> Text -> Text
groupsUpdateFunction Text
funcUpdate Text
groupsTbl Text
tbl Text
dollarQuote
]
createGroupsTriggersSQL :: Text -> Text -> Text
Text
schemaName Text
tableName =
Text -> Text -> Text -> Text
createMaintenanceTriggersSQL Text
schemaName (Text -> Text -> Text
jobQueueTable Text
schemaName Text
tableName) (Text
"maintain_" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
tableName Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"_groups")