     RETURN TRUE;
 END;
 $$ LANGUAGE plpgsql;
+-- END OF trisano.swap_schemas()

END;
$$ LANGUAGE plpgsql;

CREATE OR REPLACE FUNCTION trisano.build_form_tables() RETURNS void
    LANGUAGE plpgsql
    AS $$
DECLARE
    form_name             TEXT;
    question_name         TEXT;
    questions_per_table   INTEGER := 200;
    i                     INTEGER;
    table_count           INTEGER;
    cols_plus_vals_clause TEXT := '';
    colnames_clause       TEXT := '';
    insert_col_clause     TEXT := '';
    answer_rec            RECORD;
    last_event            INTEGER;
    values_clause         TEXT;
    tmp                   TEXT;
    table_name            TEXT;
BEGIN
    FOR form_name IN
                SELECT DISTINCT short_name
                FROM forms 
                WHERE short_name IS NOT NULL AND short_name != ''
                ORDER BY short_name LOOP
        -- For each form, find all the question short names that appear on it
        RAISE NOTICE 'Processing form name %', form_name;
        i := 0;
        colnames_clause := '';
        cols_plus_vals_clause := '';
        table_count := 1;
        FOR question_name IN SELECT DISTINCT lower(q.short_name)
                    FROM questions q JOIN form_elements fe ON fe.id = q.form_element_id
                    JOIN forms f ON f.id = fe.form_id JOIN answers a ON a.question_id = q.id
                    WHERE f.short_name = form_name
                    AND q.short_name IS NOT NULL
                    AND q.short_name != ''
                    AND a.text_answer IS NOT NULL
                    ORDER BY 1
                    LOOP
            RAISE NOTICE ' ** Processing question % in current form', question_name;
            -- Get some number of question short names
            IF colnames_clause != '' THEN
                colnames_clause := colnames_clause || ', ';
                cols_plus_vals_clause := cols_plus_vals_clause || ', ';
            END IF;
            colnames_clause := colnames_clause || quote_literal(question_name);
            cols_plus_vals_clause := cols_plus_vals_clause || quote_ident(question_name) || ' TEXT';
            i := i + 1;

            IF i > questions_per_table THEN
                PERFORM trisano.create_single_form_table(form_name, table_count, colnames_clause, cols_plus_vals_clause);
                table_count := table_count + 1;
                colnames_clause := '';
                cols_plus_vals_clause := '';
                i := 0;
            END IF;
        END LOOP;
        IF colnames_clause != '' THEN
            PERFORM trisano.create_single_form_table(form_name, table_count, colnames_clause, cols_plus_vals_clause);
        END IF;
    END LOOP;
END;
$$;

CREATE OR REPLACE FUNCTION trisano.create_single_form_table(form_name text, table_count integer, colnames_clause text, cols_plus_vals_clause text) RETURNS void
    LANGUAGE plpgsql
    AS $$
DECLARE
    insert_col_clause TEXT;
    values_clause     TEXT;
    last_event        INTEGER;
    last_type         TEXT;
    tmp               TEXT;
    answer_rec        RECORD;
    table_name        TEXT;
BEGIN
    table_name := quote_ident('formtable_' || lower(form_name) || '_' || table_count);
    insert_col_clause := '';
    values_clause := '';
    last_event := NULL;
    EXECUTE 'DROP TABLE IF EXISTS ' || table_name;
    RAISE NOTICE 'Creating table %', table_name;
    EXECUTE 'CREATE TABLE ' || table_name || ' (event_id INTEGER, type TEXT, ' || cols_plus_vals_clause || ')';
    -- Note that there's no ordering with this DISTINCT ON. There's nothing in
    -- the tables now to prevent multiple questions with the same short name
    -- being answered for the same event, so we've got to prevent it here
    tmp := 'SELECT DISTINCT ON (a.event_id, e.type, q.short_name) a.event_id, e.type, q.short_name, a.text_answer
            FROM answers a JOIN questions q ON (q.id = a.question_id)
            JOIN form_elements fe ON (fe.id = q.form_element_id)
            JOIN forms f ON (fe.form_id = f.id)
            JOIN events e ON (a.event_id = e.id)
            WHERE f.short_name = ' || quote_literal(form_name) || '
            AND lower(q.short_name) IN (' || colnames_clause || ')
            AND text_answer IS NOT NULL
            ORDER BY event_id';
    FOR answer_rec IN EXECUTE tmp LOOP
        IF last_event IS NOT NULL AND last_event != answer_rec.event_id THEN
            tmp := 'INSERT INTO ' || table_name || ' (event_id, type, ' || lower(insert_col_clause) || ') VALUES (' || last_event || ', ''' || last_type || ''', ' || values_clause || ')';
            RAISE NOTICE 'Running %', tmp;
            EXECUTE tmp;
            insert_col_clause := '';
            values_clause := '';
        END IF;
        last_event := answer_rec.event_id;
        last_type  := answer_rec.type;

        IF insert_col_clause != '' THEN
            insert_col_clause := insert_col_clause || ', ';
            values_clause := values_clause || ', ';
        END IF;
        insert_col_clause := insert_col_clause || quote_ident(answer_rec.short_name);
        values_clause := values_clause || quote_literal(answer_rec.text_answer);
    END LOOP;
    IF last_event IS NOT NULL THEN
        tmp := 'INSERT INTO ' || table_name || ' (event_id, type, ' || lower(insert_col_clause) || ') VALUES (' || last_event || ', ''' || last_type || ''', ' || values_clause || ')';
        RAISE NOTICE 'Running %', tmp;
        EXECUTE tmp;
    END IF;
END;
$$;

CREATE OR REPLACE FUNCTION trisano.swap_schemas() RETURNS BOOLEAN AS $$
DECLARE

);
INSERT INTO trisano.etl_success (success) VALUES (FALSE);

CREATE TABLE trisano.formbuilder_tables (
    id SERIAL PRIMARY KEY,
    short_name TEXT,
    table_name TEXT,
    modified BOOLEAN DEFAULT TRUE
);
CREATE INDEX formbuilder_form_name
    ON trisano.formbuilder_tables (short_name);
CREATE UNIQUE INDEX formbuilder_table_name
    ON trisano.formbuilder_tables (table_name);
CREATE INDEX formbuilder_tables_modified
    ON trisano.formbuilder_tables (modified);

CREATE TABLE trisano.formbuilder_columns (
    formbuilder_table_name TEXT REFERENCES trisano.formbuilder_tables(table_name)
        ON DELETE RESTRICT ON UPDATE RESTRICT,
    column_name TEXT,
    orig_column_name TEXT
);
CREATE UNIQUE INDEX formbuilder_columns_ix
    ON trisano.formbuilder_columns (formbuilder_table_name, column_name);
CREATE INDEX formbuilder_column_orig_name
    ON trisano.formbuilder_columns (orig_column_name);

CREATE OR REPLACE FUNCTION trisano.prepare_etl() RETURNS BOOLEAN AS $$
BEGIN
    RAISE NOTICE 'Preparing for ETL process by creating staging schema';
