{-| Functions for migrating the database to create the necessary functions for the package. Users can use these functions or copy and paste the tables to create these tables through a standalone migration system. -} {-# OPTIONS_HADDOCK prune #-} {-# LANGUAGE QuasiQuotes, OverloadedStrings #-} module Hasql.Queue.Migrate where import qualified Hasql.Queue.Internal as I import Data.String import Data.String.Here.Interpolated import Hasql.Connection import Hasql.Session {-| The DDL statements to create the schema given a value type. -} migrationQueryString :: String -- ^ @value@ column type, e.g. @int4@ or -- @jsonb@. -> String migrationQueryString valueType = [i| CREATE OR REPLACE FUNCTION notify_on(channel text) RETURNs VOID AS $$ BEGIN EXECUTE (format(E'NOTIFY %I', channel)); END; $$ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION listen_on(channel text) RETURNS VOID AS $$ BEGIN EXECUTE (format(E'LISTEN %I', channel)); END; $$ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION unlisten_on(channel text) RETURNS VOID AS $$ BEGIN EXECUTE (format(E'UNLISTEN %I', channel)); END; $$ LANGUAGE plpgsql; DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'state_t') THEN CREATE TYPE state_t AS ENUM ('enqueued', 'failed'); END IF; END$$; CREATE SEQUENCE IF NOT EXISTS modified_index START 1; CREATE TABLE IF NOT EXISTS payloads ( id BIGSERIAL PRIMARY KEY , attempts int NOT NULL DEFAULT 0 , state state_t NOT NULL DEFAULT 'enqueued' , modified_at int8 NOT NULL DEFAULT nextval('modified_index') , value ${valueType} NOT NULL ); CREATE INDEX IF NOT EXISTS active_modified_at_idx ON payloads USING btree (modified_at) WHERE (state = 'enqueued'); |] {-| This function creates a table and enumeration type that is appriopiate for the queue. The following sql is used. @ DO $$ CREATE OR REPLACE FUNCTION notify_on(channel text) RETURNs VOID AS $$ BEGIN EXECUTE (format(E'NOTIFY %I', channel)); END; $$ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION listen_on(channel text) RETURNS VOID AS $$ BEGIN EXECUTE (format(E'LISTEN %I', channel)); END; $$ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION unlisten_on(channel text) RETURNS VOID AS $$ BEGIN EXECUTE (format(E'UNLISTEN %I', channel)); END; $$ LANGUAGE plpgsql; DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'state_t') THEN CREATE TYPE state_t AS ENUM ('enqueued', 'failed'); END IF; END$$; CREATE SEQUENCE IF NOT EXISTS modified_index START 1; CREATE TABLE IF NOT EXISTS payloads ( id BIGSERIAL PRIMARY KEY , attempts int NOT NULL DEFAULT 0 , state state_t NOT NULL DEFAULT 'enqueued' , modified_at int8 NOT NULL DEFAULT nextval('modified_index') , value ${VALUE_TYPE} NOT NULL ); CREATE INDEX IF NOT EXISTS active_modified_at_idx ON payloads USING btree (modified_at, state) WHERE (state = 'enqueued'); @ The @VALUE_TYPE@ needs to passed in through the second argument. -} migrate :: Connection -> String -- ^ The type of the @value@ column -> IO () migrate conn valueType = I.runThrow (sql $ fromString $ migrationQueryString valueType) conn {-| Drop everything created by 'migrate' -} teardown :: Connection -> IO () teardown conn = do let theQuery = [i| DROP TABLE IF EXISTS payloads; DROP TYPE IF EXISTS state_t; DROP SEQUENCE IF EXISTS modified_index; DROP FUNCTION IF EXISTS notify_on; DROP FUNCTION IF EXISTS listen_on; DROP FUNCTION IF EXISTS unlisten_on; |] I.runThrow (sql theQuery) conn