/* 

Creates a normalised table and insert function

Needed variables:

  :entity_plural
  :entity

use function get_XXX_id(YYY) to get the ID of a XXX for value YYY.
The generated function is fast and reliable (race conditions!).


Example for entity host, hosts:

Creates a function calles get_host_id(VARCHAR) and a table with an ID and 
a "host" column.
When calling get_host_id, it returns the ID of an already existing 
row with the given host or inserts a new one and returns this ID.

For reliability get_host_id(host) has three steps:

  1. Try to SELECT the ID, finish when found some
  2. When none found, try to INSERT a new row and return ID, finish when no conflice
  3. When conflict (concurrent insert of the same host), then again try to SELECT.

*/

\set insert_function _insert_ :entity
\set get_function    get_ :entity _id
\set entity_in       :entity _in


CREATE TABLE :entity_plural 
    (
    id      SERIAL  NOT NULL PRIMARY KEY,
    :entity VARCHAR NOT NULL
    );

\set table :entity_plural
\ir 21-grant-reader.sql

CREATE UNIQUE INDEX ON :entity_plural(:entity varchar_pattern_ops);



-- only internal use:
-- insert or do nothing when already exists

-- ugly, to trick out psql and psql variables in quoted strings ...
\set code '$CODE$ INSERT INTO ' :entity_plural ' (' :entity ') VALUES (' :entity_in ') ON CONFLICT (' :entity ') DO NOTHING RETURNING id; $CODE$'

CREATE OR REPLACE FUNCTION :insert_function(:entity_in VARCHAR)
RETURNS INTEGER AS
    :code
    LANGUAGE sql
    VOLATILE
    RETURNS NULL ON NULL INPUT;


-- Select ID; or Insert; or select (because possible race condition: other process may inserted this ID in the meantime concurrently!).

\set _select '(SELECT id FROM ' :entity_plural ' WHERE ' :entity ' = ' :entity_in ')'
\set _insert :insert_function '(' :entity_in ')'
\set code   '$CODE$ SELECT COALESCE( ' :_select ', ' :_insert ', ' :_select '); $CODE$'

CREATE OR REPLACE FUNCTION :get_function(:entity_in VARCHAR)
    RETURNS INTEGER AS
        :code
        LANGUAGE sql
        VOLATILE
        RETURNS NULL ON NULL INPUT
        SECURITY DEFINER
        SET search_path TO :schema, pg_temp;


\set function get_ :entity _id(VARCHAR)
\ir 52-function-inserter.sql


