cartodb-postgresql/scripts-available/CDB_Quota.sql
2019-12-20 16:27:03 +01:00

173 lines
4.9 KiB
PL/PgSQL

CREATE OR REPLACE FUNCTION @extschema@._CDB_total_relation_size(_schema_name TEXT, _table_name TEXT)
RETURNS bigint AS
$$
DECLARE relation_size bigint := 0;
BEGIN
BEGIN
SELECT pg_total_relation_size(format('"%s"."%s"', _schema_name, _table_name)) INTO relation_size;
EXCEPTION
WHEN undefined_table OR OTHERS THEN
RAISE NOTICE '@extschema@._CDB_total_relation_size(''%'', ''%'') caught error: % (%)', _schema_name, _table_name, SQLERRM, SQLSTATE;
END;
RETURN relation_size;
END;
$$
LANGUAGE 'plpgsql' VOLATILE PARALLEL UNSAFE;
-- Return the estimated size of user data. Used for quota checking.
CREATE OR REPLACE FUNCTION @extschema@.CDB_UserDataSize(schema_name TEXT)
RETURNS bigint AS
$$
DECLARE
total_size INT8;
raster_available BOOLEAN;
raster_read_query TEXT;
BEGIN
-- Postgis 3+ might not install raster
raster_available := EXISTS (
SELECT 1
FROM pg_views
WHERE schemaname = '@postgisschema@'
AND viewname = 'raster_overviews'
);
IF raster_available THEN
raster_read_query := Format('SELECT o_table_name, r_table_name FROM @postgisschema@.raster_overviews
WHERE o_table_schema = %L AND o_table_catalog = current_database()', schema_name);
ELSE
raster_read_query := 'SELECT NULL::text AS o_table_name, NULL::text AS r_table_name';
END IF;
EXECUTE Format('
WITH raster_tables AS (
%s
),
user_tables AS (
SELECT table_name FROM @extschema@._CDB_NonAnalysisTablesInSchema(%L)
),
table_cat AS (
SELECT
table_name,
(
EXISTS(select * from raster_tables where o_table_name = table_name)
OR table_name SIMILAR TO @extschema@._CDB_OverviewTableDiscriminator() || ''[\w\d]*''
) AS is_overview,
EXISTS(SELECT * FROM raster_tables WHERE r_table_name = table_name) AS is_raster
FROM user_tables
),
sizes AS (
SELECT COALESCE(INT8(SUM(@extschema@._CDB_total_relation_size(%L, table_name)))) table_size,
CASE
WHEN is_overview THEN 0
WHEN is_raster THEN 1
ELSE 0.5 -- Division by 2 is for not counting the_geom_webmercator
END AS multiplier FROM table_cat GROUP BY is_overview, is_raster
)
SELECT sum(table_size*multiplier)::int8 FROM sizes
', raster_read_query, schema_name, schema_name) INTO total_size;
IF total_size IS NOT NULL THEN
RETURN total_size;
ELSE
RETURN 0;
END IF;
END;
$$
LANGUAGE 'plpgsql' VOLATILE PARALLEL UNSAFE;
-- Return the estimated size of user data. Used for quota checking.
-- Implicit schema version for backwards compatibility
CREATE OR REPLACE FUNCTION @extschema@.CDB_UserDataSize()
RETURNS bigint AS
$$
SELECT @extschema@.CDB_UserDataSize('public');
$$
LANGUAGE 'sql' VOLATILE PARALLEL UNSAFE;
-- Triggers cannot have declared arguments: pbfact float8, qmax int8, schema_name text
CREATE OR REPLACE FUNCTION @extschema@.CDB_CheckQuota()
RETURNS trigger AS
$$
DECLARE
pbfact float8;
qmax int8;
schema_name text;
dice float8;
quota float8;
BEGIN
IF TG_NARGS = 3 THEN
schema_name := TG_ARGV[2];
IF @extschema@.schema_exists(schema_name) = false THEN
RAISE EXCEPTION 'Invalid schema name "%"', schema_name;
END IF;
ELSE
schema_name := 'public';
END IF;
-- By default try to use quota function, and if not present then rely on the one specified by params
BEGIN
EXECUTE FORMAT('SELECT %I._CDB_UserQuotaInBytes();', schema_name) INTO qmax;
EXCEPTION WHEN undefined_function THEN
BEGIN
IF TG_NARGS >= 2 AND TG_ARGV[1] <> '-1' THEN
qmax := TG_ARGV[1];
ELSE
RAISE EXCEPTION 'Missing "%"._CDB_UserQuotaInBytes()', schema_name;
END IF;
END;
END;
pbfact := TG_ARGV[0];
dice := random();
IF dice < pbfact THEN
RAISE DEBUG 'Checking quota on table % (dice:%, needed:<%)', TG_RELID::text, dice, pbfact;
IF qmax = 0 THEN
RETURN NEW;
END IF;
SELECT @extschema@.CDB_UserDataSize(schema_name) INTO quota;
IF quota > qmax THEN
RAISE EXCEPTION 'Quota exceeded by %KB', (quota-qmax)/1024;
ELSE RAISE DEBUG 'User quota in bytes: % < % (max allowed)', quota, qmax;
END IF;
END IF;
RETURN NEW;
END;
$$
LANGUAGE 'plpgsql' VOLATILE PARALLEL UNSAFE;
CREATE OR REPLACE FUNCTION @extschema@.CDB_SetUserQuotaInBytes(schema_name text, bytes int8)
RETURNS int8 AS
$$
DECLARE
sql text;
BEGIN
IF @extschema@.schema_exists(schema_name::text) = false THEN
RAISE EXCEPTION 'Invalid schema name "%"', schema_name::text;
END IF;
sql := 'CREATE OR REPLACE FUNCTION "' || schema_name::text || '"._CDB_UserQuotaInBytes() '
|| 'RETURNS int8 AS $X$ SELECT ' || bytes
|| '::int8 $X$ LANGUAGE sql IMMUTABLE';
EXECUTE sql;
return bytes;
END
$$
LANGUAGE 'plpgsql' VOLATILE STRICT PARALLEL UNSAFE;
CREATE OR REPLACE FUNCTION @extschema@.CDB_SetUserQuotaInBytes(bytes int8)
RETURNS int8 AS
$$
BEGIN
return @extschema@.CDB_SetUserQuotaInBytes('public', bytes);
END;
$$
LANGUAGE 'plpgsql' VOLATILE STRICT PARALLEL UNSAFE;