-- This program is open source, licensed under the PostgreSQL License.
-- For license terms, see the LICENSE file.
--
-- Copyright (C) 2015-2018: Julien Rouhaud

-- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "CREATE EXTENSION hypopg" to load this file. \quit

SET LOCAL client_encoding = 'UTF8';

-- General functions
--

CREATE FUNCTION hypopg_reset()
    RETURNS void
    LANGUAGE C VOLATILE COST 100
AS '$libdir/hypopg', 'hypopg_reset';

-- Hypothetical indexes related functions
--

CREATE FUNCTION hypopg_reset_index()
    RETURNS void
    LANGUAGE C VOLATILE COST 100
AS '$libdir/hypopg', 'hypopg_reset_index';

CREATE FUNCTION
hypopg_create_index(IN sql_order text, OUT indexrelid oid, OUT indexname text)
    RETURNS SETOF record
    LANGUAGE C STRICT VOLATILE COST 100
AS '$libdir/hypopg', 'hypopg_create_index';

CREATE FUNCTION
hypopg_drop_index(IN indexid oid)
    RETURNS bool
    LANGUAGE C STRICT VOLATILE COST 100
AS '$libdir/hypopg', 'hypopg_drop_index';

CREATE FUNCTION hypopg(OUT indexname text, OUT indexrelid oid,
                       OUT indrelid oid, OUT innatts integer,
                       OUT indisunique boolean, OUT indkey int2vector,
                       OUT indcollation oidvector, OUT indclass oidvector,
                       OUT indoption oidvector, OUT indexprs pg_node_tree,
                       OUT indpred pg_node_tree, OUT amid oid)
    RETURNS SETOF record
    LANGUAGE c COST 100
AS '$libdir/hypopg', 'hypopg';

CREATE FUNCTION hypopg_list_indexes(OUT indexrelid oid, OUT indexname text, OUT nspname name, OUT relname name, OUT amname name)
    RETURNS SETOF record
AS
$_$
    SELECT h.indexrelid, h.indexname, n.nspname, c.relname, am.amname
    FROM hypopg() h
    JOIN pg_class c ON c.oid = h.indrelid
    JOIN pg_namespace n ON n.oid = c.relnamespace
    JOIN pg_am am ON am.oid = h.amid
$_$
LANGUAGE sql;

CREATE FUNCTION
hypopg_relation_size(IN indexid oid)
    RETURNS bigint
LANGUAGE C STRICT VOLATILE COST 100
AS '$libdir/hypopg', 'hypopg_relation_size';

CREATE FUNCTION
hypopg_get_indexdef(IN indexid oid)
    RETURNS text
LANGUAGE C STRICT VOLATILE COST 100
AS '$libdir/hypopg', 'hypopg_get_indexdef';

-- Hypothetical partitioning related functions
--

CREATE FUNCTION
hypopg_add_partition(IN partition_name name, IN partition_of_clause text,
    IN partition_by_clause text DEFAULT NULL,
    OUT relid oid, OUT tablename text)
    RETURNS SETOF record
    LANGUAGE C VOLATILE COST 100
AS '$libdir/hypopg', 'hypopg_add_partition';

CREATE FUNCTION
hypopg_partition_table(IN tablename regclass, IN partition_by_clause text)
    RETURNS bool
    LANGUAGE C STRICT VOLATILE COSt 100
AS '$libdir/hypopg', 'hypopg_partition_table';

CREATE FUNCTION hypopg_reset_table()
    RETURNS void
    LANGUAGE C VOLATILE COST 100
AS '$libdir/hypopg', 'hypopg_reset_table';

CREATE FUNCTION hypopg_drop_table(IN relid oid)
    RETURNS void
    LANGUAGE C VOLATILE COST 100
AS '$libdir/hypopg', 'hypopg_drop_table';

CREATE FUNCTION hypopg_table(OUT relid oid, OUT tablename text,
    OUT parentid oid, OUT rootid oid,
    OUT partition_by_clause text, OUT partition_bounds text)
    RETURNS SETOF record
    LANGUAGE c COST 100
AS '$libdir/hypopg', 'hypopg_table';

CREATE FUNCTION hypopg_analyze(IN tablename regclass, IN fraction real = 1)
    RETURNS void
    LANGUAGE c COST 100
AS '$libdir/hypopg', 'hypopg_analyze';

CREATE FUNCTION hypopg_statistic()
    RETURNS SETOF pg_catalog.pg_statistic
    LANGUAGE c COST 100
AS '$libdir/hypopg', 'hypopg_statistic';

-- The original anyarray columns must be casted to text, because it's not
-- allowed to create a column of such a type.
DO
$_$
DECLARE
  v_has_rls bool;
BEGIN
    SELECT COUNT(*) = 1 INTO v_has_rls
    FROM pg_class c
    JOIN pg_attribute a ON a.attrelid = c.oid
    WHERE c.relname = 'pg_class'
    AND a.attname = 'relrowsecurity';

    IF v_has_rls THEN
        CREATE VIEW hypopg_stats
        WITH (security_barrier = true)
        AS
        SELECT n.nspname AS schemaname,
            t.tablename AS tablename,
            a.attname,
            s.stainherit AS inherited,
            s.stanullfrac AS null_frac,
            s.stawidth AS avg_width,
            s.stadistinct AS n_distinct,
                CASE
                    WHEN s.stakind1 = 1 THEN s.stavalues1::text
                    WHEN s.stakind2 = 1 THEN s.stavalues2::text
                    WHEN s.stakind3 = 1 THEN s.stavalues3::text
                    WHEN s.stakind4 = 1 THEN s.stavalues4::text
                    WHEN s.stakind5 = 1 THEN s.stavalues5::text
                    ELSE NULL::text
                END AS most_common_vals,
                CASE
                    WHEN s.stakind1 = 1 THEN s.stanumbers1
                    WHEN s.stakind2 = 1 THEN s.stanumbers2
                    WHEN s.stakind3 = 1 THEN s.stanumbers3
                    WHEN s.stakind4 = 1 THEN s.stanumbers4
                    WHEN s.stakind5 = 1 THEN s.stanumbers5
                    ELSE NULL::real[]
                END AS most_common_freqs,
                CASE
                    WHEN s.stakind1 = 2 THEN s.stavalues1::text
                    WHEN s.stakind2 = 2 THEN s.stavalues2::text
                    WHEN s.stakind3 = 2 THEN s.stavalues3::text
                    WHEN s.stakind4 = 2 THEN s.stavalues4::text
                    WHEN s.stakind5 = 2 THEN s.stavalues5::text
                    ELSE NULL::text
                END AS histogram_bounds,
                CASE
                    WHEN s.stakind1 = 3 THEN s.stanumbers1[1]
                    WHEN s.stakind2 = 3 THEN s.stanumbers2[1]
                    WHEN s.stakind3 = 3 THEN s.stanumbers3[1]
                    WHEN s.stakind4 = 3 THEN s.stanumbers4[1]
                    WHEN s.stakind5 = 3 THEN s.stanumbers5[1]
                    ELSE NULL::real
                END AS correlation,
                CASE
                    WHEN s.stakind1 = 4 THEN s.stavalues1::text
                    WHEN s.stakind2 = 4 THEN s.stavalues2::text
                    WHEN s.stakind3 = 4 THEN s.stavalues3::text
                    WHEN s.stakind4 = 4 THEN s.stavalues4::text
                    WHEN s.stakind5 = 4 THEN s.stavalues5::text
                    ELSE NULL::text
                END AS most_common_elems,
                CASE
                    WHEN s.stakind1 = 4 THEN s.stanumbers1
                    WHEN s.stakind2 = 4 THEN s.stanumbers2
                    WHEN s.stakind3 = 4 THEN s.stanumbers3
                    WHEN s.stakind4 = 4 THEN s.stanumbers4
                    WHEN s.stakind5 = 4 THEN s.stanumbers5
                    ELSE NULL::real[]
                END AS most_common_elem_freqs,
                CASE
                    WHEN s.stakind1 = 5 THEN s.stanumbers1
                    WHEN s.stakind2 = 5 THEN s.stanumbers2
                    WHEN s.stakind3 = 5 THEN s.stanumbers3
                    WHEN s.stakind4 = 5 THEN s.stanumbers4
                    WHEN s.stakind5 = 5 THEN s.stanumbers5
                    ELSE NULL::real[]
                END AS elem_count_histogram
            FROM hypopg_statistic() s
                JOIN hypopg_table() t ON t.relid = s.starelid
                JOIN pg_class c ON c.oid = t.rootid
                JOIN pg_attribute a ON c.oid = a.attrelid AND a.attnum = s.staattnum
                LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
            WHERE NOT a.attisdropped
                AND has_column_privilege(c.oid, a.attnum, 'select'::text)
                AND (c.relrowsecurity = false OR NOT row_security_active(c.oid));
    ELSE
        CREATE VIEW hypopg_stats
        WITH (security_barrier = true)
        AS
        SELECT n.nspname AS schemaname,
            t.tablename AS tablename,
            a.attname,
            s.stainherit AS inherited,
            s.stanullfrac AS null_frac,
            s.stawidth AS avg_width,
            s.stadistinct AS n_distinct,
                CASE
                    WHEN s.stakind1 = 1 THEN s.stavalues1::text
                    WHEN s.stakind2 = 1 THEN s.stavalues2::text
                    WHEN s.stakind3 = 1 THEN s.stavalues3::text
                    WHEN s.stakind4 = 1 THEN s.stavalues4::text
                    WHEN s.stakind5 = 1 THEN s.stavalues5::text
                    ELSE NULL::text
                END AS most_common_vals,
                CASE
                    WHEN s.stakind1 = 1 THEN s.stanumbers1
                    WHEN s.stakind2 = 1 THEN s.stanumbers2
                    WHEN s.stakind3 = 1 THEN s.stanumbers3
                    WHEN s.stakind4 = 1 THEN s.stanumbers4
                    WHEN s.stakind5 = 1 THEN s.stanumbers5
                    ELSE NULL::real[]
                END AS most_common_freqs,
                CASE
                    WHEN s.stakind1 = 2 THEN s.stavalues1::text
                    WHEN s.stakind2 = 2 THEN s.stavalues2::text
                    WHEN s.stakind3 = 2 THEN s.stavalues3::text
                    WHEN s.stakind4 = 2 THEN s.stavalues4::text
                    WHEN s.stakind5 = 2 THEN s.stavalues5::text
                    ELSE NULL::text
                END AS histogram_bounds,
                CASE
                    WHEN s.stakind1 = 3 THEN s.stanumbers1[1]
                    WHEN s.stakind2 = 3 THEN s.stanumbers2[1]
                    WHEN s.stakind3 = 3 THEN s.stanumbers3[1]
                    WHEN s.stakind4 = 3 THEN s.stanumbers4[1]
                    WHEN s.stakind5 = 3 THEN s.stanumbers5[1]
                    ELSE NULL::real
                END AS correlation,
                CASE
                    WHEN s.stakind1 = 4 THEN s.stavalues1::text
                    WHEN s.stakind2 = 4 THEN s.stavalues2::text
                    WHEN s.stakind3 = 4 THEN s.stavalues3::text
                    WHEN s.stakind4 = 4 THEN s.stavalues4::text
                    WHEN s.stakind5 = 4 THEN s.stavalues5::text
                    ELSE NULL::text
                END AS most_common_elems,
                CASE
                    WHEN s.stakind1 = 4 THEN s.stanumbers1
                    WHEN s.stakind2 = 4 THEN s.stanumbers2
                    WHEN s.stakind3 = 4 THEN s.stanumbers3
                    WHEN s.stakind4 = 4 THEN s.stanumbers4
                    WHEN s.stakind5 = 4 THEN s.stanumbers5
                    ELSE NULL::real[]
                END AS most_common_elem_freqs,
                CASE
                    WHEN s.stakind1 = 5 THEN s.stanumbers1
                    WHEN s.stakind2 = 5 THEN s.stanumbers2
                    WHEN s.stakind3 = 5 THEN s.stanumbers3
                    WHEN s.stakind4 = 5 THEN s.stanumbers4
                    WHEN s.stakind5 = 5 THEN s.stanumbers5
                    ELSE NULL::real[]
                END AS elem_count_histogram
            FROM hypopg_statistic() s
                JOIN hypopg_table() t ON t.relid = s.starelid
                JOIN pg_class c ON c.oid = t.rootid
                JOIN pg_attribute a ON c.oid = a.attrelid AND a.attnum = s.staattnum
                LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
            WHERE NOT a.attisdropped
                AND has_column_privilege(c.oid, a.attnum, 'select'::text);
    END IF;
END;
$_$ language plpgsql;
