/*
 * Greenplum system views and functions.
 *
 * Copyright (c) 2009-2010, Greenplum inc.
 */

BEGIN;

GRANT USAGE ON SCHEMA gp_toolkit TO public;

--------------------------------------------------------------------------------
-- Auxiliary functions & views
--------------------------------------------------------------------------------

--------------------------------------------------------------------------------
-- @view:
--        gp_toolkit.__gp_is_append_only
--
-- @doc:
--        Determines if a table is an AOT; returns true if OID refers to an AOT,
--        false if OID refers to a non-AOT relation; empty rowset if OID is invalid
--
--------------------------------------------------------------------------------
CREATE VIEW gp_toolkit.__gp_is_append_only
AS
    SELECT
        pgc.oid AS iaooid,
        CASE
            WHEN pgao.relid IS NULL THEN false ELSE true
        END
        AS iaotype
    FROM
        pg_catalog.pg_class pgc

        LEFT JOIN pg_catalog.pg_appendonly pgao ON (pgc.oid = pgao.relid);

GRANT SELECT ON TABLE gp_toolkit.__gp_is_append_only TO public;


--------------------------------------------------------------------------------
-- @view:
--        gp_toolkit.__gp_fullname
--
-- @doc:
--        Constructs fully qualified names
--
--------------------------------------------------------------------------------
CREATE VIEW gp_toolkit.__gp_fullname
AS
    SELECT
        pgc.oid AS fnoid,
        nspname AS fnnspname,
        relname AS fnrelname
    FROM
        pg_catalog.pg_class pgc,
        pg_catalog.pg_namespace pgn
    WHERE pgc.relnamespace = pgn.oid;

GRANT SELECT ON TABLE gp_toolkit.__gp_fullname TO public;


--------------------------------------------------------------------------------
-- @view:
--        gp_toolkit.__gp_user_namespaces
--
-- @doc:
--        Shorthand for namespaces that contain user data
--
--------------------------------------------------------------------------------
CREATE VIEW gp_toolkit.__gp_user_namespaces
AS
    SELECT
        oid as aunoid,
        nspname as aunnspname
    FROM
        pg_catalog.pg_namespace
    WHERE
        nspname NOT LIKE 'pg_%'
    AND nspname <> 'gp_toolkit'
    AND nspname <> 'information_schema';

GRANT SELECT ON TABLE gp_toolkit.__gp_user_namespaces TO public;


--------------------------------------------------------------------------------
-- @view:
--        gp_toolkit.__gp_user_tables
--
-- @doc:
--        Shorthand for tables in user namespaces
--
--------------------------------------------------------------------------------
CREATE VIEW gp_toolkit.__gp_user_tables
AS
    SELECT
        fn.fnnspname as autnspname,
        fn.fnrelname as autrelname,
        relkind as autrelkind,
        reltuples as autreltuples,
        relpages as autrelpages,
        relacl as autrelacl,
        pgc.oid as autoid,
        pgc.reltoastrelid as auttoastoid,
        pgc.relstorage as autrelstorage
    FROM
        pg_catalog.pg_class pgc,
        gp_toolkit.__gp_fullname fn
    WHERE pgc.relnamespace IN
    (
        SELECT aunoid
        FROM gp_toolkit.__gp_user_namespaces
    )
    AND pgc.relkind = 'r'
    AND pgc.oid = fn.fnoid;

GRANT SELECT ON TABLE gp_toolkit.__gp_user_tables TO public;


--------------------------------------------------------------------------------
-- @view:
--        gp_toolkit.__gp_user_data_tables
--
-- @doc:
--        Shorthand for tables in user namespaces that may hold data
--
--------------------------------------------------------------------------------
CREATE VIEW gp_toolkit.__gp_user_data_tables
AS
    SELECT aut.*
    FROM
        gp_toolkit.__gp_user_tables aut
    LEFT OUTER JOIN
        pg_catalog.pg_partition pgp
    ON aut.autoid = pgp.parrelid
    WHERE pgp.parrelid IS NULL;

GRANT SELECT ON TABLE gp_toolkit.__gp_user_data_tables TO public;


--------------------------------------------------------------------------------
-- @view:
--        gp_toolkit.__gp_user_data_tables_readable
--
-- @doc:
--        Shorthand for tables in user namespaces that may hold data and are
--        readable by current user
--
--------------------------------------------------------------------------------
CREATE VIEW gp_toolkit.__gp_user_data_tables_readable
AS
    SELECT *
    FROM gp_toolkit.__gp_user_tables aut
    WHERE has_table_privilege(aut.autoid, 'select');

GRANT SELECT ON TABLE gp_toolkit.__gp_user_data_tables_readable TO public;


--------------------------------------------------------------------------------
-- @table:
--        gp_toolkit.__gp_localid
--
-- @doc:
--        External table that determines the local segment id
--
--------------------------------------------------------------------------------
CREATE EXTERNAL WEB TABLE gp_toolkit.__gp_localid
(
    localid    int
)
EXECUTE E'echo $GP_SEGMENT_ID' FORMAT 'TEXT';

GRANT SELECT ON TABLE gp_toolkit.__gp_localid TO public;


--------------------------------------------------------------------------------
-- @table:
--        gp_toolkit.__gp_masterid
--
-- @doc:
--        External table that determines the master's segment id
--
--------------------------------------------------------------------------------
CREATE EXTERNAL WEB TABLE gp_toolkit.__gp_masterid
(
    masterid    int
)
EXECUTE E'echo $GP_SEGMENT_ID' ON MASTER FORMAT 'TEXT';

GRANT SELECT ON TABLE gp_toolkit.__gp_masterid TO public;


--------------------------------------------------------------------------------
-- @view:
--        gp_toolkit.__gp_number_of_segments
--
-- @doc:
--        Determines number of segments in a system
--
--------------------------------------------------------------------------------
CREATE VIEW gp_toolkit.__gp_number_of_segments
AS
    SELECT
        count(*)::smallint as numsegments
    FROM
        pg_catalog.gp_segment_configuration
    WHERE
        preferred_role = 'p'
        AND content >= 0;

GRANT SELECT ON TABLE gp_toolkit.__gp_number_of_segments TO public;


--------------------------------------------------------------------------------
-- log-reading external tables and views
--------------------------------------------------------------------------------

--------------------------------------------------------------------------------
-- @table:
--        gp_toolkit.gp_log_segment_ext
--
-- @doc:
--        External table to read segment log; requires superuser privilege
--
--------------------------------------------------------------------------------
CREATE EXTERNAL WEB TABLE gp_toolkit.__gp_log_segment_ext
(
    logtime timestamp with time zone,
    loguser text,
    logdatabase text,
    logpid text,
    logthread text,
    loghost text,
    logport text,
    logsessiontime timestamp with time zone,
    logtransaction int,
    logsession text,
    logcmdcount text,
    logsegment text,
    logslice text,
    logdistxact text,
    loglocalxact text,
    logsubxact text,
    logseverity text,
    logstate text,
    logmessage text,
    logdetail text,
    loghint text,
    logquery text,
    logquerypos int,
    logcontext text,
    logdebug text,
    logcursorpos int,
    logfunction text,
    logfile text,
    logline int,
    logstack text
)
EXECUTE E'cat $GP_SEG_DATADIR/pg_log/*.csv'
FORMAT 'CSV' (DELIMITER AS ',' NULL AS '' QUOTE AS '"');

REVOKE ALL ON TABLE gp_toolkit.__gp_log_segment_ext FROM public;


--------------------------------------------------------------------------------
-- @table:
--        gp_toolkit.gp_log_master
--
-- @doc:
--        External table to read the master log; requires superuser privilege
--
--------------------------------------------------------------------------------
CREATE EXTERNAL WEB TABLE gp_toolkit.__gp_log_master_ext
(
    logtime timestamp with time zone,
    loguser text,
    logdatabase text,
    logpid text,
    logthread text,
    loghost text,
    logport text,
    logsessiontime timestamp with time zone,
    logtransaction int,
    logsession text,
    logcmdcount text,
    logsegment text,
    logslice text,
    logdistxact text,
    loglocalxact text,
    logsubxact text,
    logseverity text,
    logstate text,
    logmessage text,
    logdetail text,
    loghint text,
    logquery text,
    logquerypos int,
    logcontext text,
    logdebug text,
    logcursorpos int,
    logfunction text,
    logfile text,
    logline int,
    logstack text
)
EXECUTE E'cat $GP_SEG_DATADIR/pg_log/*.csv' ON MASTER
FORMAT 'CSV' (DELIMITER AS ',' NULL AS '' QUOTE AS '"');

REVOKE ALL ON TABLE gp_toolkit.__gp_log_master_ext FROM public;


--------------------------------------------------------------------------------
-- @view:
--        gp_toolkit.gp_log_system
--
-- @doc:
--        View of segment and master logs
--
--------------------------------------------------------------------------------
CREATE VIEW gp_toolkit.gp_log_system
AS
    SELECT * FROM gp_toolkit.__gp_log_segment_ext
    UNION ALL
    SELECT * FROM gp_toolkit.__gp_log_master_ext
    ORDER BY logtime;

REVOKE ALL ON TABLE gp_toolkit.gp_log_system FROM public;


--------------------------------------------------------------------------------
-- @view:
--        gp_toolkit.gp_log_database
--
-- @doc:
--        Shorthand to view error logs of current database only;
--        requires superuser privilege
--
--------------------------------------------------------------------------------
CREATE VIEW gp_toolkit.gp_log_database
AS
    SELECT * FROM gp_toolkit.gp_log_system
    WHERE logdatabase = current_database();

REVOKE ALL ON TABLE gp_toolkit.gp_log_database FROM public;


--------------------------------------------------------------------------------
-- @view:
--        gp_toolkit.gp_log_master_concise
--
-- @doc:
--        Shorthand to view most important columns of master log only;
--        requires superuser privilege
--
--------------------------------------------------------------------------------
CREATE VIEW gp_toolkit.gp_log_master_concise
AS
    SELECT
        logtime
--        ,loguser
        ,logdatabase
--        ,logpid
--        ,logthread
--        ,loghost
--        ,logport
--        ,logsessiontime
--        ,logtransaction
        ,logsession
        ,logcmdcount
--        ,logsegment
--        ,logslice
--        ,logdistxact
--        ,loglocalxact
--        ,logsubxact
        ,logseverity
--        ,logstate
        ,logmessage
--        ,logdetail
--        ,loghint
--        ,logquery
--        ,logquerypos
--        ,logcontext
--        ,logdebug
--        ,logcursorpos
--        ,logfunction
--        ,logfile
--        ,logline
--        ,logstack
    FROM gp_toolkit.__gp_log_master_ext;

REVOKE ALL ON TABLE gp_toolkit.gp_log_master_concise FROM public;


--------------------------------------------------------------------------------
-- @view:
--        gp_toolkit.gp_log_command_timings
--
-- @doc:
--        list all commands together with first and last timestamp of logged
--        activity; requires superuser privilege
--
--------------------------------------------------------------------------------
CREATE VIEW gp_toolkit.gp_log_command_timings
AS
    SELECT
        logsession,
        logcmdcount,
        logdatabase,
        loguser,
        logpid,
        MIN(logtime) AS logtimemin,
        MAX(logtime) AS logtimemax,
        MAX(logtime) - MIN(logtime) AS logduration
    FROM
        gp_toolkit.__gp_log_master_ext
    WHERE
        logsession IS NOT NULL
        AND logcmdcount IS NOT NULL
        AND logdatabase IS NOT NULL
    GROUP BY 1,2,3,4,5;

REVOKE ALL ON TABLE gp_toolkit.gp_log_command_timings FROM public;


--------------------------------------------------------------------------------
-- PARAM specific views
--------------------------------------------------------------------------------

--------------------------------------------------------------------------------
-- @type:
--        gp_toolkit.gp_param_setting_t
--
-- @doc:
--        Record type to combine segment id, param's name and param's value
--
--------------------------------------------------------------------------------
CREATE TYPE gp_toolkit.gp_param_setting_t
AS
(
    paramsegment int,
    paramname text,
    paramvalue text
);


--------------------------------------------------------------------------------
-- @function:
--        gp_toolkit.__gp_param_local_setting
-- @in:
--        varchar - name of PARAM
-- @out:
--        int - segment id
--        text - name of PARAM
--        text - value of PARAM
--
-- @doc:
--        Evaluate current_setting for a PARAM; function is immutable and may be
--        executed on segments;
--
--------------------------------------------------------------------------------
CREATE FUNCTION gp_toolkit.__gp_param_local_setting(varchar)
RETURNS SETOF gp_toolkit.gp_param_setting_t
AS
$$
    SELECT gp_execution_segment(), $1, current_setting($1)::text;
$$
LANGUAGE SQL
IMMUTABLE CONTAINS SQL;

GRANT EXECUTE ON FUNCTION gp_toolkit.__gp_param_local_setting(varchar) TO public;


--------------------------------------------------------------------------------
-- @function:
--        gp_toolkit.gp_param_setting
-- @in:
--        varchar - name of PARAM
-- @out:
--        int - segment id
--        text - name of PARAM
--        text - value of PARAM
--
-- @doc:
--        Collect value of a PARAM from all segments
--
--------------------------------------------------------------------------------
CREATE FUNCTION gp_toolkit.gp_param_setting(varchar)
RETURNS SETOF gp_toolkit.gp_param_setting_t
AS
$$
DECLARE
    paramsettings gp_toolkit.gp_param_setting_t;
    paramdummy record;

BEGIN
    -- attempt local execution first to validate the name of the PARAM
    SELECT gp_toolkit.__gp_param_local_setting($1) INTO paramdummy;

    FOR paramsettings IN
        SELECT gs.*
        FROM gp_toolkit.__gp_localid, gp_toolkit.__gp_param_local_setting($1) AS gs

        UNION ALL

        SELECT gs.*
        FROM gp_toolkit.__gp_masterid, gp_toolkit.__gp_param_local_setting($1) AS gs

    LOOP
        RETURN NEXT paramsettings;
    END LOOP;
END
$$
LANGUAGE plpgSQL READS SQL DATA;

GRANT EXECUTE ON FUNCTION gp_toolkit.gp_param_setting(varchar) TO public;


--------------------------------------------------------------------------------
-- @function:
--        gp_toolkit.gp_param_settings
--
-- @doc:
--        Collect values of a all parameters from all segments
--
--------------------------------------------------------------------------------
CREATE FUNCTION gp_toolkit.gp_param_settings()
RETURNS SETOF gp_toolkit.gp_param_setting_t
AS
$$
DECLARE
    paramsettings gp_toolkit.gp_param_setting_t;
    param text;

BEGIN
    FOR param in (SELECT name FROM pg_settings order by name) LOOP
        FOR paramsettings IN
            SELECT pls.*
            FROM gp_toolkit.__gp_localid, gp_toolkit.__gp_param_local_setting(param) AS pls
        LOOP
            RETURN NEXT paramsettings;
        END LOOP;
    END LOOP;
END
$$
LANGUAGE plpgSQL READS SQL DATA;

GRANT EXECUTE ON FUNCTION gp_toolkit.gp_param_settings() TO public;


--------------------------------------------------------------------------------
-- @view:
--        gp_toolkit.gp_param_settings_seg_value_diffs
--
-- @doc:
--        Show parameters that do not have same values on all segments
--        (parameters that are supposed to have different values are excluded)
--
--------------------------------------------------------------------------------
CREATE VIEW gp_toolkit.gp_param_settings_seg_value_diffs
AS
    SELECT
        paramname  AS psdname,
        paramvalue AS psdvalue,
        count(*)   AS psdcount
    FROM
        gp_toolkit.gp_param_settings()
    WHERE
        paramname NOT IN ('config_file', 'data_directory', 'gp_contentid', 'gp_dbid', 'hba_file', 'ident_file', 'port')
    GROUP BY
        1,2
    HAVING
        count(*) < (select numsegments from gp_toolkit.__gp_number_of_segments)
    ORDER BY
        1,2,3;


GRANT SELECT ON TABLE gp_toolkit.gp_param_settings_seg_value_diffs TO public;


--------------------------------------------------------------------------------
-- @view:
--        gp_toolkit.gp_pgdatabase_invalid
--
-- @doc:
--        Information about the invalid segments only
--
--------------------------------------------------------------------------------
CREATE VIEW gp_toolkit.gp_pgdatabase_invalid
AS
    SELECT
        dbid           AS pgdbidbid,
        isprimary      AS pgdbiisprimary,
        content        AS pgdbicontent,
        valid          AS pgdbivalid,
        definedprimary AS pgdbidefinedprimary
    FROM
        pg_catalog.gp_pgdatabase
    WHERE
        not valid
    ORDER BY
        dbid;

GRANT SELECT ON TABLE gp_toolkit.gp_pgdatabase_invalid TO public;


--------------------------------------------------------------------------------
-- skew analysis
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
-- @type:
--        gp_toolkit.gp_skew_details_t
--
-- @doc:
--        Type to accomodate skew details
--
--------------------------------------------------------------------------------
CREATE TYPE gp_toolkit.gp_skew_details_t
AS
(
    segoid oid,
    segid int,
    segtupcount bigint
);

--------------------------------------------------------------------------------
-- @function:
--        gp_toolkit.gp_skew_details
-- @in:
--        oid - oid of table for which to determine distribution
-- @out:
--        oid - input oid
--        int - segment id
--        bigint - number of rows on segment
--
-- @doc:
--        Get details for skew
--
--------------------------------------------------------------------------------
CREATE FUNCTION gp_toolkit.gp_skew_details(oid)
RETURNS setof gp_toolkit.gp_skew_details_t
AS
$$
DECLARE
    skewcrs refcursor;
    skewrec record;
    skewarray bigint[];
    skewaot bool;
    skewsegid int;
    skewtablename record;

BEGIN

    SELECT INTO skewrec *
    FROM pg_catalog.pg_appendonly pga, pg_catalog.pg_roles pgr
    WHERE pga.relid = $1::regclass and pgr.rolname = current_user and pgr.rolsuper = 't';

    IF FOUND THEN
        -- append only table

        FOR skewrec IN
            SELECT $1, segid, COALESCE(tupcount, 0)::bigint AS cnt
            FROM (SELECT generate_series(0, numsegments - 1) FROM gp_toolkit.__gp_number_of_segments) segs(segid)
            LEFT OUTER JOIN pg_catalog.get_ao_distribution($1)
            ON segid = segmentid
        LOOP
            RETURN NEXT skewrec;
        END LOOP;

    ELSE
        -- heap table

        SELECT * INTO skewtablename FROM gp_toolkit.__gp_fullname
        WHERE fnoid = $1;

        OPEN skewcrs
            FOR
            EXECUTE
                'SELECT ' || $1 || '::oid, segid, CASE WHEN gp_segment_id IS NULL THEN 0 ELSE cnt END ' ||
                'FROM (SELECT generate_series(0, numsegments - 1) FROM gp_toolkit.__gp_number_of_segments) segs(segid) ' ||
                'LEFT OUTER JOIN ' ||
                    '(SELECT gp_segment_id, COUNT(*) AS cnt FROM ' ||
                        quote_ident(skewtablename.fnnspname) ||
                        '.' ||
                        quote_ident(skewtablename.fnrelname) ||
                    ' GROUP BY 1) details ' ||
                'ON segid = gp_segment_id';

        FOR skewsegid IN
            SELECT generate_series(1, numsegments)
            FROM gp_toolkit.__gp_number_of_segments
        LOOP
            FETCH skewcrs INTO skewrec;
            IF FOUND THEN
                RETURN NEXT skewrec;
            ELSE
                RETURN;
            END IF;
        END LOOP;
        CLOSE skewcrs;
    END IF;

    RETURN;
END
$$
LANGUAGE plpgSQL READS SQL DATA;

GRANT EXECUTE ON FUNCTION gp_toolkit.gp_skew_details(oid) TO public;


--------------------------------------------------------------------------------
-- @type:
--        gp_toolkit.gp_skew_analysis_t
--
-- @doc:
--        Type to accomodate skew analysis
--
--------------------------------------------------------------------------------
CREATE TYPE gp_toolkit.gp_skew_analysis_t
AS
(
    skewoid oid,
    skewval numeric
);


--------------------------------------------------------------------------------
-- @function:
--        gp_toolkit.gp_skew_coefficient
-- @in:
--        oid - oid of table for which to compute skew coefficient
-- @out:
--        oid - input oid
--        numeric - skew coefficient
--
-- @doc:
--        Compute coefficient of variance given an array of rowcounts;
--        Multiply by 100 to be in sync with gpperfmon's measure
--
--------------------------------------------------------------------------------
CREATE FUNCTION gp_toolkit.gp_skew_coefficient(targetoid oid, OUT skcoid oid, OUT skccoeff numeric)
RETURNS record
AS
$$
    SELECT
        $1 as skcoid,
        CASE
            WHEN skewmean > 0 THEN ((skewdev/skewmean) * 100.0)
            ELSE 0
        END
        AS skccoeff
    FROM
    (
        SELECT STDDEV(segtupcount) AS skewdev, AVG(segtupcount) AS skewmean, COUNT(*) AS skewcnt
        FROM gp_toolkit.gp_skew_details($1)
    ) AS skew

$$
LANGUAGE sql READS SQL DATA;

GRANT EXECUTE ON FUNCTION gp_toolkit.gp_skew_coefficient(oid, OUT oid, OUT numeric) TO public;


--------------------------------------------------------------------------------
-- @function:
--        gp_toolkit.__gp_skew_coefficients
-- @in:
-- @out:
--        oid - oid of analyzed table
--        numeric - skew coefficient of table
--
-- @doc:
--        Wrapper to call coefficient function on all user tables
--
--------------------------------------------------------------------------------
CREATE FUNCTION gp_toolkit.__gp_skew_coefficients()
RETURNS SETOF gp_toolkit.gp_skew_analysis_t
AS
$$
DECLARE
    skcoid oid;
    skcrec record;

BEGIN
    FOR skcoid IN SELECT autoid from gp_toolkit.__gp_user_data_tables_readable WHERE autrelstorage != 'x'
    LOOP
        SELECT * INTO skcrec
        FROM
            gp_toolkit.gp_skew_coefficient(skcoid);
        RETURN NEXT skcrec;
    END LOOP;
END
$$
LANGUAGE plpgsql READS SQL DATA;

GRANT EXECUTE ON FUNCTION gp_toolkit.__gp_skew_coefficients() TO public;


--------------------------------------------------------------------------------
-- @function:
--        gp_toolkit.gp_skew_coefficients
--
-- @doc:
--        Wrapper view around previous function
--
--------------------------------------------------------------------------------
CREATE VIEW gp_toolkit.gp_skew_coefficients
AS
    SELECT
        skew.skewoid AS skcoid,
        pgn.nspname  AS skcnamespace,
        pgc.relname  AS skcrelname,
        skew.skewval AS skccoeff
    FROM gp_toolkit.__gp_skew_coefficients() skew

    JOIN
    pg_catalog.pg_class pgc
    ON (skew.skewoid = pgc.oid)

    JOIN
    pg_catalog.pg_namespace pgn
    ON (pgc.relnamespace = pgn.oid);

GRANT SELECT ON TABLE gp_toolkit.gp_skew_coefficients TO public;


--------------------------------------------------------------------------------
-- @view:
--        gp_toolkit.gp_skew_idle_fraction
-- @in:
--        oid - oid of table for which to compute idle fraction due to skew
-- @out:
--        oid - input oid
--        numeric - idle fraction
--
-- @doc:
--        Compute skew area quotient for a given table
--
--------------------------------------------------------------------------------
CREATE FUNCTION gp_toolkit.gp_skew_idle_fraction(targetoid oid, OUT sifoid oid, OUT siffraction numeric)
RETURNS record
AS
$$
    SELECT
        $1 as sifoid,
        CASE
            WHEN MIN(skewmax) = 0 THEN 0
            ELSE (SUM(skewmax - segtupcount) / (MIN(skewmax) * MIN(numsegments)))
        END
        AS siffraction
    FROM
    (
        SELECT segid, segtupcount, COUNT(segid) OVER () AS numsegments, MAX(segtupcount) OVER () AS skewmax
        FROM gp_toolkit.gp_skew_details($1)
    ) AS skewbaseline

$$
LANGUAGE sql READS SQL DATA;

GRANT EXECUTE ON FUNCTION gp_toolkit.gp_skew_idle_fraction(oid, OUT oid, OUT numeric) TO public;


--------------------------------------------------------------------------------
-- @function:
--        gp_toolkit.__gp_skew_idle_fractions
-- @in:
-- @out:
--        oid - oid of analyzed table
--        numeric - idle fraction
--
-- @doc:
--        Wrapper to call fraction calculation on all user tables
--
--------------------------------------------------------------------------------
CREATE FUNCTION gp_toolkit.__gp_skew_idle_fractions()
RETURNS SETOF gp_toolkit.gp_skew_analysis_t
AS
$$
DECLARE
    skcoid oid;
    skcrec record;

BEGIN
    FOR skcoid IN SELECT autoid from gp_toolkit.__gp_user_data_tables_readable WHERE autrelstorage != 'x'
    LOOP
        SELECT * INTO skcrec
        FROM
            gp_toolkit.gp_skew_idle_fraction(skcoid);
        RETURN NEXT skcrec;
    END LOOP;
END
$$
LANGUAGE plpgsql READS SQL DATA;

GRANT EXECUTE ON FUNCTION gp_toolkit.__gp_skew_idle_fractions() TO public;


--------------------------------------------------------------------------------
-- @view:
--        gp_toolkit.gp_skew_idle_fractions
--
-- @doc:
--        Wrapper view around previous function
--
--------------------------------------------------------------------------------
CREATE VIEW gp_toolkit.gp_skew_idle_fractions
AS
    SELECT
        skew.skewoid AS sifoid,
        pgn.nspname  AS sifnamespace,
        pgc.relname  AS sifrelname,
        skew.skewval AS siffraction
    FROM gp_toolkit.__gp_skew_idle_fractions() skew

    JOIN
    pg_catalog.pg_class pgc
    ON (skew.skewoid = pgc.oid)

    JOIN
    pg_catalog.pg_namespace pgn
    ON (pgc.relnamespace = pgn.oid);

GRANT SELECT ON TABLE gp_toolkit.gp_skew_idle_fractions TO public;


--------------------------------------------------------------------------------
-- detection of missing stats
--------------------------------------------------------------------------------

--------------------------------------------------------------------------------
-- @view:
--        gp_toolkit.gp_stats_missing
--
-- @doc:
--        List all tables with no or insufficient stats; includes empty tables
--
--------------------------------------------------------------------------------
CREATE VIEW gp_toolkit.gp_stats_missing
AS
    SELECT
        aut.autnspname as smischema,
        aut.autrelname as smitable,
        CASE WHEN aut.autrelpages = 0 OR aut.autreltuples = 0 THEN false ELSE true END AS smisize,
        attcnt AS smicols,
        COALESCE(stacnt, 0) AS smirecs
    FROM
        gp_toolkit.__gp_user_tables aut

        JOIN
        (
            SELECT attrelid, count(*) AS attcnt
            FROM pg_catalog.pg_attribute
            WHERE attnum > 0 and attisdropped = false
            GROUP BY attrelid
        ) attrs
        ON aut.autoid = attrelid

        LEFT OUTER JOIN
        (
            SELECT starelid, count(*) AS stacnt
            FROM pg_catalog.pg_statistic
            GROUP BY starelid
        ) bar
        ON aut.autoid = starelid
    WHERE aut.autrelkind = 'r'
    AND (aut.autrelpages = 0 OR aut.autreltuples = 0) OR (stacnt IS NOT NULL AND attcnt > stacnt);

GRANT SELECT ON TABLE gp_toolkit.gp_stats_missing TO public;


--------------------------------------------------------------------------------
-- Misc
--------------------------------------------------------------------------------

--------------------------------------------------------------------------------
-- @table:
--        gp_toolkit.gp_disk_free
--
-- @doc:
--        External table to determine free space on disk on a per-segment basis
--
--------------------------------------------------------------------------------
CREATE EXTERNAL WEB TABLE gp_toolkit.gp_disk_free
(
    dfsegment int,
    dfhostname text,
    dfdevice text,
    dfspace bigint
)
EXECUTE E'python -c "from gppylib.commands import unix; df=unix.DiskFree.get_disk_free_info_local(''token'',''$GP_SEG_DATADIR''); print ''%s, %s, %s, %s'' % (''$GP_SEGMENT_ID'', unix.getLocalHostname(), df[0], df[3])"' FORMAT 'CSV';


--------------------------------------------------------------------------------
-- Determine table bloat
--------------------------------------------------------------------------------

--------------------------------------------------------------------------------
-- @view:
--        gp_toolkit.gp_bloat_expected_pages
--
-- @doc:
--        compute number of expected pages for given table;
--        do not attempt to model sophisticated aspects of row width -- the
--        statistics this is based on are not stable enough to allow a finer
--        granularity of modelling
--
--------------------------------------------------------------------------------
CREATE VIEW gp_toolkit.gp_bloat_expected_pages
AS
    SELECT
        btdrelid,
        btdrelpages,
        CASE WHEN btdexppages < numsegments
            THEN numsegments
            ELSE btdexppages
        END as btdexppages
    FROM
    (
        SELECT
            oid as btdrelid,
            pgc.relpages as btdrelpages,
            CEIL((pgc.reltuples * (25 + width))::numeric / current_setting('block_size')::numeric) AS btdexppages,
            (select numsegments from gp_toolkit.__gp_number_of_segments) as numsegments
        FROM
            (
                SELECT pgc.oid, pgc.reltuples, pgc.relpages
                FROM pg_class pgc
                WHERE NOT EXISTS
                (
                    SELECT iaooid
                    FROM gp_toolkit.__gp_is_append_only
                    WHERE iaooid = pgc.oid AND iaotype = 't'
                )
            )
            AS pgc
        LEFT OUTER JOIN
            (
                SELECT  starelid, SUM(stawidth * (1.0 - stanullfrac)) AS width
                FROM pg_statistic pgs
                GROUP BY 1
            )
            AS btwcols
        ON pgc.oid = btwcols.starelid
        WHERE starelid IS NOT NULL
    ) AS subq;

GRANT SELECT ON TABLE gp_toolkit.gp_bloat_expected_pages TO public;


--------------------------------------------------------------------------------
-- @function:
--        gp_toolkit.gp_bloat_diag
-- @in:
--        int - actual number of pages according to statistics
--        int - expecte number of pages
--        bool - is AO table?
-- @out:
--        int - bloat indicator
--        text - textual version of bloat indicator
--
-- @doc:
--        diagnose table bloat based on expected and actual number of pages
--
--------------------------------------------------------------------------------
CREATE FUNCTION gp_toolkit.gp_bloat_diag(btdrelpages int, btdexppages int, aotable bool,
    OUT bltidx int, OUT bltdiag text)
AS
$$
    SELECT
        bloatidx,
        CASE
            WHEN bloatidx = 0
                THEN 'no bloat detected'::text
            WHEN bloatidx = 1
                THEN 'moderate amount of bloat suspected'::text
            WHEN bloatidx = 2
                THEN 'significant amount of bloat suspected'::text
            WHEN bloatidx = -1
                THEN 'diagnosis inconclusive or no bloat suspected'::text
        END AS bloatdiag
    FROM
    (
        SELECT
            CASE
                WHEN $3 = 't' THEN 0
                WHEN $1 < 10 AND $2 = 0 THEN -1
                WHEN $2 = 0 THEN 2
                WHEN $1 < $2 THEN 0
                WHEN ($1/$2)::numeric > 10 THEN 2
                WHEN ($1/$2)::numeric > 3 THEN 1
                ELSE -1
            END AS bloatidx
    ) AS bloatmapping

$$
LANGUAGE SQL READS SQL DATA;

GRANT EXECUTE ON FUNCTION gp_toolkit.gp_bloat_diag(int, int, bool, OUT int, OUT text) TO public;


--------------------------------------------------------------------------------
-- @view:
--        gp_toolkit.gp_bloat_diag
--
-- @doc:
--        Shorthand for running bloat diag over all tables, incl. catalog
--
--------------------------------------------------------------------------------
CREATE VIEW gp_toolkit.gp_bloat_diag
AS
    SELECT
        btdrelid AS bdirelid,
        fnnspname AS bdinspname,
        fnrelname AS bdirelname,
        btdrelpages AS bdirelpages,
        btdexppages AS bdiexppages,
        bltdiag(bd) AS bdidiag
    FROM
    (
        SELECT
            fn.*, beg.*,
            gp_toolkit.gp_bloat_diag(btdrelpages::int, btdexppages::int, iao.iaotype::bool) AS bd
        FROM
            gp_toolkit.gp_bloat_expected_pages beg,
            pg_catalog.pg_class pgc,
            gp_toolkit.__gp_fullname fn,
            gp_toolkit.__gp_is_append_only iao

        WHERE beg.btdrelid = pgc.oid
            AND pgc.oid = fn.fnoid
            AND iao.iaooid = pgc.oid
    ) as bloatsummary
    WHERE bltidx(bd) > 0;

GRANT SELECT ON TABLE gp_toolkit.gp_bloat_diag TO public;


--------------------------------------------------------------------------------
-- resource queue diagnostics
--------------------------------------------------------------------------------

--------------------------------------------------------------------------------
-- @view:
--        gp_toolkit.gp_resq_activity
--
-- @doc:
--        View that summarizes all activity in the system wrt resource queues
--
--------------------------------------------------------------------------------
CREATE VIEW gp_toolkit.gp_resq_activity
AS
    SELECT
        psa.procpid as resqprocpid,
        psa.usename as resqrole,
        resq.resqoid,
        resq.rsqname as resqname,
        psa.query_start as resqstart,
        CASE
            WHEN resqgranted = 'f' THEN 'waiting' ELSE 'running'
        END as resqstatus
    FROM
        pg_catalog.pg_stat_activity psa
    JOIN
    (
        SELECT
            pgrq.oid as resqoid,
            pgrq.rsqname,
            pgl.pid as resqprocid,
            pgl.granted as resqgranted
        FROM
            pg_catalog.pg_resqueue pgrq,
            pg_catalog.pg_locks pgl
        WHERE
            pgl.objid = pgrq.oid
    ) as resq
    ON resqprocid = procpid
    WHERE current_query != '<IDLE>'
    ORDER BY resqstart;

GRANT SELECT ON TABLE gp_toolkit.gp_resq_activity TO public;


--------------------------------------------------------------------------------
-- @view:
--        gp_toolkit.gp_resq_activity_by_queue
--
-- @doc:
--        Rollup of activity view
--
--------------------------------------------------------------------------------
CREATE VIEW gp_toolkit.gp_resq_activity_by_queue
AS
    SELECT
        resqoid,
        resqname,
        MAX(resqstart) as resqlast,
        resqstatus,
        COUNT(*) as resqtotal
    FROM
        gp_toolkit.gp_resq_activity
    GROUP BY
        resqoid, resqname, resqstatus
    ORDER BY resqoid, resqlast;

GRANT SELECT ON TABLE gp_toolkit.gp_resq_activity_by_queue TO public;

--------------------------------------------------------------------------------
-- @view:
--        gp_toolkit.gp_resq_priority_backend
--
-- @doc:
--        Priorities associated with each backend executing currently
--
--------------------------------------------------------------------------------
CREATE VIEW gp_toolkit.gp_resq_priority_backend
AS
    SELECT
        session_id as rqpsession,
        command_count as rqpcommand,
        priority as rqppriority,
        weight as rqpweight
    FROM
        gp_list_backend_priorities()
            AS L(session_id int, command_count int, priority text, weight int);

GRANT SELECT ON TABLE gp_toolkit.gp_resq_priority_backend TO public;


--------------------------------------------------------------------------------
-- @view:
--        gp_toolkit.gp_resq_priority_statement
--
-- @doc:
--        Priorities associated with each statement
--
--------------------------------------------------------------------------------
CREATE VIEW gp_toolkit.gp_resq_priority_statement
AS
    SELECT
        psa.datname AS rqpdatname,
        psa.usename AS rqpusename,
        rpb.rqpsession,
        rpb.rqpcommand,
        rpb.rqppriority,
        rpb.rqpweight,
        psa.current_query AS rqpquery
    FROM
        gp_toolkit.gp_resq_priority_backend rpb
        JOIN pg_stat_activity psa ON (rpb.rqpsession = psa.sess_id)
    WHERE psa.current_query != '<IDLE>';

GRANT SELECT ON TABLE gp_toolkit.gp_resq_priority_statement TO public;


--------------------------------------------------------------------------------
-- @view:
--        gp_toolkit.gp_resq_role
--
-- @doc:
--        Assigned resource queue to roles
--
--------------------------------------------------------------------------------
CREATE VIEW gp_toolkit.gp_resq_role
AS
    SELECT
        pgr.rolname  AS rrrolname,
        pgrq.rsqname AS rrrsqname
    FROM pg_catalog.pg_roles pgr

    LEFT JOIN pg_catalog.pg_resqueue pgrq
    ON (pgr.rolresqueue = pgrq.oid)
;

GRANT SELECT ON TABLE gp_toolkit.gp_resq_role TO public;


--------------------------------------------------------------------------------
-- @view:
--        gp_toolkit.gp_locks_on_resqueue
--
-- @doc:
--        Locks of type 'resource queue' (sessions waiting because of
--        resource queue restrictions)
--
--------------------------------------------------------------------------------
CREATE VIEW gp_toolkit.gp_locks_on_resqueue
AS
    SELECT
        pgsa.usename      AS lorusename,
        pgrq.rsqname      AS lorrsqname,
        pgl.locktype      AS lorlocktype,
        pgl.objid         AS lorobjid,
        pgl.transactionid AS lortransaction,
        pgl.pid           AS lorpid,
        pgl.mode          AS lormode,
        pgl.granted       AS lorgranted,
        pgsa.waiting      AS lorwaiting
    FROM pg_catalog.pg_stat_activity pgsa

    JOIN pg_catalog.pg_locks pgl
    ON (pgsa.procpid = pgl.pid)

    JOIN pg_catalog.pg_resqueue pgrq
    ON (pgl.objid = pgrq.oid);

GRANT SELECT ON TABLE gp_toolkit.gp_locks_on_resqueue TO public;


--------------------------------------------------------------------------------
-- @view:
--        gp_toolkit.gp_locks_on_relation
--
-- @doc:
--        Locks of type 'relation' (sessions waiting because of relation locks)
--
--------------------------------------------------------------------------------
CREATE VIEW gp_toolkit.gp_locks_on_relation
AS
    SELECT
        pgl.locktype       AS lorlocktype,
        pgl.database       AS lordatabase,
        pgc.relname        AS lorrelname,
        pgl.relation       AS lorrelation,
        pgl.transactionid  AS lortransaction,
        pgl.pid            AS lorpid,
        pgl.mode           AS lormode,
        pgl.granted        AS lorgranted,
        pgsa.current_query AS lorcurrentquery
    FROM pg_catalog.pg_locks pgl

    JOIN pg_catalog.pg_class pgc
    ON (pgl.relation = pgc.oid)

    JOIN pg_catalog.pg_stat_activity pgsa
    ON (pgl.pid = pgsa.procpid)

    ORDER BY
        pgc.relname
;

GRANT SELECT ON TABLE gp_toolkit.gp_locks_on_relation TO public;


--------------------------------------------------------------------------------
-- @view:
--        gp_toolkit.gp_roles_assigned
--
-- @doc:
--        Shows directly assigned roles
--
--------------------------------------------------------------------------------
CREATE VIEW gp_toolkit.gp_roles_assigned
AS
    SELECT
        pgr.oid      AS raroleid,
        pgr.rolname  AS rarolename,
        pgam.member  AS ramemberid,
        pgr2.rolname AS ramembername
    FROM
    pg_catalog.pg_roles pgr

    LEFT JOIN pg_catalog.pg_auth_members pgam
    ON (pgr.oid = pgam.roleid)

    LEFT JOIN pg_catalog.pg_roles pgr2
    ON (pgam.member = pgr2.oid)
;

GRANT SELECT ON TABLE gp_toolkit.gp_roles_assigned TO public;



--------------------------------------------------------------------------------
-- @view:
--              gp_toolkit.gp_size_of_index
--
-- @doc:
--              Calculates index sizes
--
--------------------------------------------------------------------------------
CREATE VIEW gp_toolkit.gp_size_of_index
AS
    SELECT
        soi.soioid            AS soioid,
        soi.soitableoid        AS soitableoid,
        soi.soisize            AS soisize,
        fnidx.fnnspname     AS soiindexschemaname,
        fnidx.fnrelname     AS soiindexname,
        fntbl.fnnspname     AS soitableschemaname,
        fntbl.fnrelname     AS soitablename
    FROM
        (SELECT
            pgi.indexrelid                              AS soioid,
            pgi.indrelid                                AS soitableoid,
            pg_catalog.pg_relation_size(pgi.indexrelid) AS soisize
        FROM pg_catalog.pg_index pgi

        JOIN gp_toolkit.__gp_user_data_tables_readable ut
        ON (pgi.indrelid = ut.autoid)
        ) AS soi

    JOIN gp_toolkit.__gp_fullname fnidx
    ON (soi.soioid = fnidx.fnoid)

    JOIN gp_toolkit.__gp_fullname fntbl
    ON (soi.soitableoid = fntbl.fnoid)
    ;

GRANT SELECT ON TABLE gp_toolkit.gp_size_of_index TO public;


--------------------------------------------------------------------------------
-- @view:
--              gp_toolkit.gp_size_of_table_disk
--
-- @doc:
--              Calculates on-disk table size
--
--------------------------------------------------------------------------------
CREATE VIEW gp_toolkit.gp_size_of_table_disk
AS
    SELECT
        sotd.sotdoid                AS sotdoid,
        sotd.sotdsize                AS sotdsize,
        sotd.sotdtoastsize            AS sotdtoastsize,
        sotd.sotdadditionalsize        AS sotdadditionalsize,
        fn.fnnspname                 AS sotdschemaname,
        fn.fnrelname                 AS sotdtablename
    FROM
        (SELECT
            autoid                              AS sotdoid,
            pg_catalog.pg_relation_size(autoid) AS sotdsize,
            CASE
                WHEN auttoastoid > 0
                THEN pg_catalog.pg_total_relation_size(auttoastoid)
                ELSE 0
            END
            AS sotdtoastsize,
            CASE
                WHEN ao.segrelid IS NOT NULL AND ao.segrelid > 0
                THEN pg_total_relation_size(ao.segrelid)
                ELSE 0
            END
            +
            CASE
                WHEN ao.blkdirrelid IS NOT NULL AND ao.blkdirrelid > 0
                THEN pg_total_relation_size(ao.blkdirrelid)
                ELSE 0
            END
            +
            CASE
                WHEN ao.visimaprelid IS NOT NULL AND ao.visimaprelid > 0
                THEN pg_total_relation_size(ao.visimaprelid)
                ELSE 0
            END
            AS sotdadditionalsize
        FROM
            (SELECT *
             FROM gp_toolkit.__gp_user_data_tables_readable
             WHERE autrelstorage != 'x'
            ) AS udtr

        LEFT JOIN pg_catalog.pg_appendonly ao
        ON (udtr.autoid = ao.relid)
        ) AS sotd

    JOIN gp_toolkit.__gp_fullname fn
    ON (sotd.sotdoid = fn.fnoid)
    ;

GRANT SELECT ON TABLE gp_toolkit.gp_size_of_table_disk TO public;


--------------------------------------------------------------------------------
-- @view:
--              gp_toolkit.gp_size_of_table_uncompressed
--
-- @doc:
--              Calculates uncompressed table size for AO tables, for heap
--              tables shows disk size
--
--------------------------------------------------------------------------------
CREATE VIEW gp_toolkit.gp_size_of_table_uncompressed
AS
    SELECT
        sotu.sotuoid    AS sotuoid,
        sotu.sotusize    AS sotusize,
        fn.fnnspname     AS sotuschemaname,
        fn.fnrelname     AS sotutablename
    FROM
        (SELECT
            sotd.sotdoid AS sotuoid,
            CASE
                WHEN iao.iaotype
                THEN
                    CASE
                        WHEN pg_catalog.pg_relation_size(sotd.sotdoid) = 0
                        THEN 0
                        ELSE pg_catalog.pg_relation_size(sotd.sotdoid) *
                                CASE
                                    WHEN pg_catalog.get_ao_compression_ratio(sotd.sotdoid) = -1
                                    THEN NULL
                                    ELSE pg_catalog.get_ao_compression_ratio(sotd.sotdoid)
                                END
                    END
                ELSE sotd.sotdsize
            END +
            sotd.sotdtoastsize +
            sotd.sotdadditionalsize
            AS sotusize
        FROM gp_toolkit.gp_size_of_table_disk sotd

        JOIN gp_toolkit.__gp_is_append_only iao
        ON (sotd.sotdoid = iao.iaooid)
        ) AS sotu

    JOIN gp_toolkit.__gp_fullname fn
    ON (sotu.sotuoid = fn.fnoid)
    ;

REVOKE ALL ON TABLE gp_toolkit.gp_size_of_table_uncompressed FROM public;


--------------------------------------------------------------------------------
-- @view:
--              gp_toolkit.gp_table_indexes
--
-- @doc:
--              Shows indexes that belong to a table
--
--------------------------------------------------------------------------------
CREATE VIEW gp_toolkit.gp_table_indexes
AS
    SELECT
        ti.tireloid         AS tireloid,
        ti.tiidxoid         AS tiidxoid,
        fntbl.fnnspname        AS titableschemaname,
        fntbl.fnrelname     AS titablename,
        fnidx.fnnspname     AS tiindexschemaname,
        fnidx.fnrelname     AS tiindexname
    FROM
        (SELECT
            pgc.oid  AS tireloid,
            pgc2.oid AS tiidxoid
        FROM pg_catalog.pg_class pgc

        JOIN pg_catalog.pg_index pgi
        ON (pgc.oid = pgi.indrelid)

        JOIN pg_catalog.pg_class pgc2
        ON (pgi.indexrelid = pgc2.oid)

        JOIN gp_toolkit.__gp_user_data_tables_readable udt
        ON (udt.autoid = pgc.oid)
        ) as ti

    JOIN gp_toolkit.__gp_fullname fntbl
    ON (ti.tireloid = fntbl.fnoid)

    JOIN gp_toolkit.__gp_fullname fnidx
    ON (ti.tiidxoid = fnidx.fnoid)
    ;


GRANT SELECT ON TABLE gp_toolkit.gp_table_indexes TO public;


--------------------------------------------------------------------------------
-- @view:
--              gp_toolkit.gp_size_of_all_table_indexes
--
-- @doc:
--              Calculates total size of all indexes on a table
--
--------------------------------------------------------------------------------
CREATE VIEW gp_toolkit.gp_size_of_all_table_indexes
AS
    SELECT
        soati.soatioid        AS soatioid,
        soati.soatisize        AS soatisize,
        fn.fnnspname         AS soatischemaname,
        fn.fnrelname         AS soatitablename
    FROM
        (SELECT
            tireloid                                   AS soatioid,
            sum(pg_catalog.pg_relation_size(tiidxoid)) AS soatisize
        FROM
            gp_toolkit.gp_table_indexes ti
        GROUP BY
            soatioid
        ) AS soati

    JOIN gp_toolkit.__gp_fullname fn
    ON (soati.soatioid = fn.fnoid)
    ;

GRANT SELECT ON gp_toolkit.gp_size_of_all_table_indexes TO public;


--------------------------------------------------------------------------------
-- @view:
--              gp_toolkit.gp_size_of_table_and_indexes_disk
--
-- @doc:
--              Calculates table disk size and index disk size
--
--------------------------------------------------------------------------------
CREATE VIEW gp_toolkit.gp_size_of_table_and_indexes_disk
AS
    SELECT
        sotaid.sotaidoid        AS sotaidoid,
        sotaid.sotaidtablesize    AS sotaidtablesize,
        sotaid.sotaididxsize    AS sotaididxsize,
        fn.fnnspname             AS sotaidschemaname,
        fn.fnrelname             AS sotaidtablename
    FROM
        (SELECT
            sotd.sotdoid            AS sotaidoid,
            sotd.sotdsize +
            sotd.sotdtoastsize +
            sotd.sotdadditionalsize AS sotaidtablesize,
            CASE
                WHEN soati.soatisize IS NULL THEN 0
                ELSE soati.soatisize
            END
            AS sotaididxsize
        FROM gp_toolkit.gp_size_of_table_disk sotd

        LEFT JOIN gp_toolkit.gp_size_of_all_table_indexes soati
        ON (sotd.sotdoid = soati.soatioid)
        ) AS sotaid

    JOIN gp_toolkit.__gp_fullname fn
    ON (sotaid.sotaidoid = fn.fnoid)
    ;

GRANT SELECT ON TABLE gp_toolkit.gp_size_of_table_and_indexes_disk TO public;


--------------------------------------------------------------------------------
-- @view:
--              gp_toolkit.gp_size_of_table_and_indexes_licensing
--
-- @doc:
--              Calculates table and indexes size for licensing purposes
--
--------------------------------------------------------------------------------
CREATE VIEW gp_toolkit.gp_size_of_table_and_indexes_licensing
AS
    SELECT
        sotail.sotailoid                    AS sotailoid,
        sotail.sotailtablesizedisk            AS sotailtablesizedisk,
        sotail.sotailtablesizeuncompressed    AS sotailtablesizeuncompressed,
        sotail.sotailindexessize            AS sotailindexessize,
        fn.fnnspname                         AS sotailschemaname,
        fn.fnrelname                         AS sotailtablename
    FROM
        (SELECT
            sotu.sotuoid           AS sotailoid,
            sotaid.sotaidtablesize AS sotailtablesizedisk,
            sotu.sotusize          AS sotailtablesizeuncompressed,
            sotaid.sotaididxsize   AS sotailindexessize
        FROM gp_toolkit.gp_size_of_table_uncompressed sotu

        JOIN gp_toolkit.gp_size_of_table_and_indexes_disk sotaid
        ON (sotu.sotuoid = sotaid.sotaidoid)
        ) AS sotail

    JOIN gp_toolkit.__gp_fullname fn
    ON (sotail.sotailoid = fn.fnoid)
    ;

REVOKE ALL ON TABLE gp_toolkit.gp_size_of_table_and_indexes_licensing FROM public;


--------------------------------------------------------------------------------
-- @view:
--              gp_toolkit.gp_size_of_partition_and_indexes_disk
--
-- @doc:
--              Calculates partition table disk size and partition indexes disk size
--
--------------------------------------------------------------------------------
CREATE VIEW gp_toolkit.gp_size_of_partition_and_indexes_disk
AS
    SELECT
        sopaid.sopaidparentoid                AS sopaidparentoid,
        sopaid.sopaidpartitionoid            AS sopaidpartitionoid,
        sopaid.sopaidpartitiontablesize        AS sopaidpartitiontablesize,
        sopaid.sopaidpartitionindexessize    AS sopaidpartitionindexessize,
        fnparent.fnnspname                     AS sopaidparentschemaname,
        fnparent.fnrelname                     AS sopaidparenttablename,
        fnpart.fnnspname                     AS sopaidpartitionschemaname,
        fnpart.fnrelname                     AS sopaidpartitiontablename
    FROM
        (SELECT
            pgp.parrelid                 AS sopaidparentoid,
            pgpr.parchildrelid           AS sopaidpartitionoid,
            sotd.sotdsize +
            sotd.sotdtoastsize +
            sotd.sotdadditionalsize      AS sopaidpartitiontablesize,
            COALESCE(soati.soatisize, 0) AS sopaidpartitionindexessize
        FROM pg_catalog.pg_partition pgp

        JOIN pg_partition_rule pgpr ON (pgp.oid = pgpr.paroid)

        JOIN gp_toolkit.gp_size_of_table_disk sotd
        ON (sotd.sotdoid = pgpr.parchildrelid)

        LEFT JOIN gp_toolkit.gp_size_of_all_table_indexes soati
        ON (soati.soatioid = pgpr.parchildrelid)
        ) AS sopaid

    JOIN gp_toolkit.__gp_fullname fnparent
    ON (sopaid.sopaidparentoid = fnparent.fnoid)

    JOIN gp_toolkit.__gp_fullname fnpart
    ON (sopaid.sopaidpartitionoid = fnpart.fnoid)
    ;

GRANT SELECT ON TABLE gp_toolkit.gp_size_of_partition_and_indexes_disk TO public;


--------------------------------------------------------------------------------
-- @view:
--              gp_toolkit.gp_size_of_schema_disk
--
-- @doc:
--              Calculates user schema sizes in current database
--
--------------------------------------------------------------------------------
CREATE VIEW gp_toolkit.gp_size_of_schema_disk
AS
    SELECT
        un.aunnspname                             AS sosdnsp,
        COALESCE(sum(sotaid.sotaidtablesize), 0)  AS sosdschematablesize,
        COALESCE(sum(sotaid.sotaididxsize)  , 0)  AS sosdschemaidxsize
    FROM gp_toolkit.gp_size_of_table_and_indexes_disk sotaid

    JOIN gp_toolkit.__gp_fullname fn
    ON (sotaid.sotaidoid = fn.fnoid)

    RIGHT JOIN gp_toolkit.__gp_user_namespaces un
    ON (un.aunnspname = fn.fnnspname)

    GROUP BY
        un.aunnspname;

GRANT SELECT ON gp_toolkit.gp_size_of_schema_disk TO public;


--------------------------------------------------------------------------------
-- @view:
--              gp_toolkit.gp_size_of_database
--
-- @doc:
--              Calculates user database sizes
--
--------------------------------------------------------------------------------
CREATE VIEW gp_toolkit.gp_size_of_database
AS
    SELECT
        datname                          AS sodddatname,
        pg_catalog.pg_database_size(oid) AS sodddatsize
    FROM
        pg_catalog.pg_database
    WHERE
        datname <> 'template0'
    AND datname <> 'template1'
    AND datname <> 'postgres';

GRANT SELECT ON TABLE gp_toolkit.gp_size_of_database TO public;

--------------------------------------------------------------------------------
-- @view:
--              gp_toolkit.gp_pg_resqueue_status
--
-- @doc:
--              New version of pg_catalog.pg_resqueue_status that shows memory limits
--
--------------------------------------------------------------------------------

CREATE VIEW gp_toolkit.gp_resqueue_status
AS
    SELECT
        q.oid as queueid,
        q.rsqname as rsqname,
        t1.value::int as rsqcountlimit,
        t2.value::int as rsqcountvalue,
        t3.value::real as rsqcostlimit,
        t4.value::real as rsqcostvalue,
        t5.value::real as rsqmemorylimit,
        t6.value::real as rsqmemoryvalue,
        t7.value::int as rsqwaiters,
        t8.value::int as rsqholders
    FROM
        pg_resqueue q,
        pg_resqueue_status_kv() t1 (queueid oid, key text, value text),
        pg_resqueue_status_kv() t2 (queueid oid, key text, value text),
        pg_resqueue_status_kv() t3 (queueid oid, key text, value text),
        pg_resqueue_status_kv() t4 (queueid oid, key text, value text),
        pg_resqueue_status_kv() t5 (queueid oid, key text, value text),
        pg_resqueue_status_kv() t6 (queueid oid, key text, value text),
        pg_resqueue_status_kv() t7 (queueid oid, key text, value text),
        pg_resqueue_status_kv() t8 (queueid oid, key text, value text)
    WHERE
        q.oid = t1.queueid
        AND t1.queueid = t2.queueid
        AND t2.queueid = t3.queueid
        AND t3.queueid = t4.queueid
        AND t4.queueid = t5.queueid
        AND t5.queueid = t6.queueid
        AND t6.queueid = t7.queueid
        AND t7.queueid = t8.queueid
        AND t1.key = 'rsqcountlimit'
        AND t2.key = 'rsqcountvalue'
        AND t3.key = 'rsqcostlimit'
        AND t4.key = 'rsqcostvalue'
        AND t5.key = 'rsqmemorylimit'
        AND t6.key = 'rsqmemoryvalue'
        AND t7.key = 'rsqwaiters'
        AND t8.key = 'rsqholders'
    ;

GRANT SELECT ON gp_toolkit.gp_resqueue_status TO public;

--------------------------------------------------------------------------------
-- AO/CO diagnostics functions
--------------------------------------------------------------------------------

CREATE FUNCTION gp_toolkit.__gp_aoseg_history(oid)
RETURNS TABLE(gp_tid tid,
    gp_xmin integer,
    gp_xmin_status text,
    gp_xmin_commit_distrib_id text,
    gp_xmax integer,
    gp_xmax_status text,
    gp_xmax_commit_distrib_id text,
    gp_command_id integer,
    gp_infomask text,
    gp_update_tid tid,
    gp_visibility text,
    segno integer,
    tupcount bigint,
    eof bigint,
    eof_uncompressed bigint,
    modcount bigint,
    state smallint)
AS '$libdir/gp_ao_co_diagnostics', 'gp_aoseg_history_wrapper'
LANGUAGE C STRICT;
GRANT EXECUTE ON FUNCTION gp_toolkit.__gp_aoseg_history(oid) TO public;

CREATE FUNCTION gp_toolkit.__gp_aocsseg(oid)
RETURNS TABLE(gp_tid tid,
    segno integer,
    column_num smallint,
    physical_segno integer,
    tupcount bigint,
    eof bigint,
    eof_uncompressed bigint,
    modcount bigint,
    state smallint)
AS '$libdir/gp_ao_co_diagnostics', 'gp_aocsseg_wrapper'
LANGUAGE C STRICT;
GRANT EXECUTE ON FUNCTION gp_toolkit.__gp_aocsseg(oid) TO public;

CREATE FUNCTION gp_toolkit.__gp_aocsseg_name(text)
RETURNS TABLE (gp_tid tid,
    segno integer,
    column_num smallint,
    physical_segno integer,
    tupcount bigint,
    eof bigint,
    eof_uncompressed bigint,
    modcount bigint,
    state smallint)
AS '$libdir/gp_ao_co_diagnostics', 'gp_aocsseg_name_wrapper'
LANGUAGE C STRICT;
GRANT EXECUTE ON FUNCTION gp_toolkit.__gp_aocsseg_name(text) TO public;

CREATE FUNCTION gp_toolkit.__gp_aocsseg_history(oid)
RETURNS TABLE(gp_tid tid,
    gp_xmin integer,
    gp_xmin_status text,
    gp_xmin_distrib_id text,
    gp_xmax integer,
    gp_xmax_status text,
    gp_xmax_distrib_id text,
    gp_command_id integer,
    gp_infomask text,
    gp_update_tid tid,
    gp_visibility text,
    segno integer,
    column_num smallint,
    physical_segno integer,
    tupcount bigint,
    eof bigint,
    eof_uncompressed bigint,
    modcount bigint,
    state smallint)
AS '$libdir/gp_ao_co_diagnostics' , 'gp_aocsseg_history_wrapper'
LANGUAGE C STRICT;
GRANT EXECUTE ON FUNCTION gp_toolkit.__gp_aocsseg_history(oid) TO public;

CREATE FUNCTION gp_toolkit.__gp_aovisimap(oid)
RETURNS TABLE (tid tid,
    segno int,
    row_num bigint)
AS '$libdir/gp_ao_co_diagnostics', 'gp_aovisimap_wrapper'
LANGUAGE C IMMUTABLE;
GRANT EXECUTE ON FUNCTION gp_toolkit.__gp_aovisimap(oid) TO public;

CREATE FUNCTION gp_toolkit.__gp_aovisimap_name(text)
RETURNS TABLE (tid tid,
    segno integer,
    row_num bigint)
AS '$libdir/gp_ao_co_diagnostics', 'gp_aovisimap_name_wrapper'
LANGUAGE C STRICT;
GRANT EXECUTE ON FUNCTION gp_toolkit.__gp_aovisimap_name(text) TO public;

CREATE FUNCTION gp_toolkit.__gp_aovisimap_hidden_info(oid)
RETURNS TABLE (segno integer,
    hidden_tupcount bigint,
    total_tupcount bigint)
AS '$libdir/gp_ao_co_diagnostics', 'gp_aovisimap_hidden_info_wrapper'
LANGUAGE C STRICT;
GRANT EXECUTE ON FUNCTION gp_toolkit.__gp_aovisimap_hidden_info(oid) TO public;

CREATE FUNCTION gp_toolkit.__gp_aovisimap_hidden_info_name(text)
RETURNS TABLE (segno integer,
    hidden_tupcount bigint,
    total_tupcount bigint)
AS '$libdir/gp_ao_co_diagnostics', 'gp_aovisimap_hidden_info_name_wrapper'
LANGUAGE C STRICT;
GRANT EXECUTE ON FUNCTION gp_toolkit.__gp_aovisimap_hidden_info_name(text) TO public;

CREATE FUNCTION gp_toolkit.__gp_aovisimap_entry(oid)
RETURNS TABLE(segno integer,
    first_row_num bigint,
    hidden_tupcount int,
    bitmap text)
AS '$libdir/gp_ao_co_diagnostics','gp_aovisimap_entry_wrapper'
LANGUAGE C STRICT;
GRANT EXECUTE ON FUNCTION gp_toolkit.__gp_aovisimap_entry(oid) TO public;

CREATE FUNCTION gp_toolkit.__gp_aovisimap_entry_name(text)
RETURNS TABLE(segno integer,
    first_row_num bigint,
    hidden_tupcount int,
    bitmap text)
AS '$libdir/gp_ao_co_diagnostics', 'gp_aovisimap_entry_name_wrapper' LANGUAGE C STRICT;
GRANT EXECUTE ON FUNCTION gp_toolkit.__gp_aovisimap_entry_name(text) TO public;

CREATE FUNCTION gp_toolkit.__gp_aoseg_name(text)
RETURNS TABLE (segno integer, eof bigint,
    tupcount bigint,
    varblockcount bigint,
    eof_uncompressed bigint,
    modcount bigint, state smallint)
AS '$libdir/gp_ao_co_diagnostics', 'gp_aoseg_name_wrapper'
LANGUAGE C STRICT;
GRANT EXECUTE ON FUNCTION gp_toolkit.__gp_aoseg_name(text) TO public;

CREATE TYPE gp_toolkit.__gp_aovisimap_hidden_t AS (seg int, hidden bigint, total bigint);
CREATE FUNCTION gp_toolkit.__gp_aovisimap_hidden_typed(oid)
    RETURNS SETOF gp_toolkit.__gp_aovisimap_hidden_t AS $$
    SELECT * FROM gp_toolkit.__gp_aovisimap_hidden_info($1);
$$ LANGUAGE SQL;

CREATE FUNCTION gp_toolkit.__gp_aovisimap_compaction_info(ao_oid oid,
    OUT content int, OUT datafile int, OUT compaction_possible boolean,
    OUT hidden_tupcount bigint, OUT total_tupcount bigint, OUT percent_hidden numeric)
    RETURNS SETOF RECORD AS $$
DECLARE
    hinfo_row RECORD;
    threshold float;
BEGIN
    EXECUTE 'show gp_appendonly_compaction_threshold' INTO threshold;
    FOR hinfo_row IN SELECT gp_segment_id,
    gp_toolkit.__gp_aovisimap_hidden_typed(ao_oid)::gp_toolkit.__gp_aovisimap_hidden_t
    FROM gp_dist_random('gp_id') LOOP
        content := hinfo_row.gp_segment_id;
        datafile := (hinfo_row.__gp_aovisimap_hidden_typed).seg;
        hidden_tupcount := (hinfo_row.__gp_aovisimap_hidden_typed).hidden;
        total_tupcount := (hinfo_row.__gp_aovisimap_hidden_typed).total;
        compaction_possible := false;
        IF total_tupcount > 0 THEN
            percent_hidden := (100 * hidden_tupcount / total_tupcount::numeric)::numeric(5,2);
        ELSE
            percent_hidden := 0::numeric(5,2);
        END IF;
        IF percent_hidden > threshold THEN
            compaction_possible := true;
        END IF;
        RETURN NEXT;
    END LOOP;
    RAISE NOTICE 'gp_appendonly_compaction_threshold = %', threshold;
    RETURN;
END;
$$ LANGUAGE plpgsql;

-- Workfile views
--------------------------------------------------------------------------------

--------------------------------------------------------------------------------
-- @function:
--        gp_toolkit.__gp_workfile_entries_f
--
-- @in:
--
-- @out:
--        int - segment id
--        text - path to workfile set,
--        int - hash value of the spilling operator,
--        bigint - size in bytes,
--        int - state,
--        int - workmem in kilobytes,
--        int - type of the spilling operator,
--        int - containing slice,
--        int - sessionid,
--        int - command_cnt,
--        timestamptz - time of query start,
--        int - number of files
--
-- @doc:
--        UDF to retrieve workfile sets currently present on disk on one segment
--
--------------------------------------------------------------------------------

CREATE FUNCTION gp_toolkit.__gp_workfile_entries_f()
RETURNS SETOF record
AS '$libdir/gp_workfile_mgr', 'gp_workfile_mgr_cache_entries'
LANGUAGE C IMMUTABLE;

GRANT EXECUTE ON FUNCTION gp_toolkit.__gp_workfile_entries_f() TO public;

--------------------------------------------------------------------------------
-- @view:
--        gp_toolkit.gp_workfile_entries
--
-- @doc:
--        List of all the workfile sets currently present on disk
--
--------------------------------------------------------------------------------

CREATE VIEW gp_toolkit.gp_workfile_entries AS
WITH all_entries AS (
   SELECT C.*
          FROM gp_toolkit.__gp_localid, gp_toolkit.__gp_workfile_entries_f() AS C (
            segid int,
            path text,
            hash int,
            size bigint,
            state int,
            workmem int,
            optype text,
            slice int,
            sessionid int,
            commandid int,
            query_start timestamptz,
            numfiles int
          )
    UNION ALL
    SELECT C.*
          FROM gp_toolkit.__gp_masterid, gp_toolkit.__gp_workfile_entries_f() AS C (
            segid int,
            path text,
            hash int,
            size bigint,
            state int,
            workmem int,
            optype text,
            slice int,
            sessionid int,
            commandid int,
            query_start timestamptz,
            numfiles int
          ))
SELECT S.datname,
       (CASE WHEN (C.state = 1) THEN S.procpid ELSE NULL END) AS procpid,
       C.sessionid as sess_id,
       C.commandid as command_cnt,
       S.usename,
       (CASE WHEN (C.state = 1) THEN S.current_query ELSE NULL END) as current_query,
       C.segid,
       C.slice,
       C.optype,
       C.workmem,
       C.size,
       C.numfiles,
       C.path as directory,
       (CASE WHEN (C.state = 1) THEN 'RUNNING' WHEN (C.state = 2) THEN 'CACHED' WHEN (C.state = 3) THEN 'DELETING' ELSE 'UNKNOWN' END) as state
FROM all_entries C LEFT OUTER JOIN
pg_stat_activity as S
ON C.sessionid = S.sess_id;

GRANT SELECT ON gp_toolkit.gp_workfile_entries TO public;

--------------------------------------------------------------------------------
-- @view:
--        gp_toolkit.gp_workfile_usage_per_segment
--
-- @doc:
--        Amount of disk space used for workfiles at each segment
--
--------------------------------------------------------------------------------

CREATE VIEW gp_toolkit.gp_workfile_usage_per_segment AS
SELECT gpseg.content AS segid, COALESCE(SUM(wfe.size),0) AS size,
    SUM(wfe.numfiles) AS numfiles
FROM (
    SELECT content
    FROM gp_segment_configuration
    WHERE role = 'p') gpseg
LEFT JOIN gp_toolkit.gp_workfile_entries wfe
ON (gpseg.content = wfe.segid)
GROUP BY gpseg.content;

GRANT SELECT ON gp_toolkit.gp_workfile_usage_per_segment TO public;

--------------------------------------------------------------------------------
-- @view:
--        gp_toolkit.gp_workfile_usage_per_query
--
-- @doc:
--        Amount of disk space used for workfiles by each query
--
--------------------------------------------------------------------------------

CREATE VIEW gp_toolkit.gp_workfile_usage_per_query AS
SELECT datname, procpid, sess_id, command_cnt, usename, current_query, segid, state,
    SUM(size) AS size, SUM(numfiles) AS numfiles
FROM gp_toolkit.gp_workfile_entries
GROUP BY (datname, procpid, sess_id, command_cnt, usename, current_query, segid, state);

GRANT SELECT ON gp_toolkit.gp_workfile_usage_per_query TO public;

--------------------------------------------------------------------------------
-- @function:
--        gp_toolkit.gp_dump_query_oids(text)
--
-- @in:
--        text - SQL text
-- @out:
--        text - serialized json string of oids
--
-- @doc:
--        Dump query oids for a given SQL text
--
--------------------------------------------------------------------------------

CREATE FUNCTION gp_toolkit.gp_dump_query_oids(text)
RETURNS text
AS '$libdir/gpoptutils', 'gp_dump_query_oids' LANGUAGE C STRICT;

GRANT EXECUTE ON FUNCTION gp_toolkit.gp_dump_query_oids(text) TO public;

--------------------------------------------------------------------------------

-- Finalize install
COMMIT;


-- EOF
