Function 2 – Computing the size of a table without locks

Here is a function that does what pg_relation_size does, more or less, without taking any locks. Because of this, it is always fast, but it may give an incorrect result if the table is being heavily altered at the same time:

CREATE OR REPLACE FUNCTION pg_relation_size_nolock(tablename regclass) 
RETURNS BIGINT
LANGUAGE plpgsql
AS $$
DECLARE
classoutput RECORD;
tsid INTEGER;
rid INTEGER;
dbid INTEGER;
filepath TEXT;
filename TEXT;
datadir TEXT;
i INTEGER := 0;
tablesize BIGINT;
BEGIN
--
-- Get data directory
--
EXECUTE 'SHOW data_directory' INTO datadir;
--
-- Get relfilenode and reltablespace
--
SELECT
reltablespace as tsid
,relfilenode as rid
INTO classoutput
FROM pg_class
WHERE oid = tablename
AND relkind = 'r';
--
-- Throw an error if we can't find the tablename specified
--
IF NOT FOUND THEN
RAISE EXCEPTION 'tablename % not found', tablename;
END IF;
tsid := classoutput.tsid;
rid := classoutput.rid;
--
-- Get the database object identifier (oid)
--
SELECT oid INTO dbid
FROM pg_database
WHERE datname = current_database();
--
-- Use some internals knowledge to set the filepath
--
IF tsid = 0 THEN
filepath := datadir || '/base/' || dbid || '/' || rid;
ELSE
filepath := datadir || '/pg_tblspc/' || tsid || '/'
|| dbid || '/' || rid;
END IF;
--
-- Look for the first file. Report if missing
--
SELECT (pg_stat_file(filepath)).size
INTO tablesize;
--
-- Sum the sizes of additional files, if any
--
WHILE FOUND LOOP
i := i + 1;
filename := filepath || '.' || i;
--
-- pg_stat_file returns ERROR if it cannot see file
-- so we must trap the error and exit loop
--
BEGIN
SELECT tablesize + (pg_stat_file(filename)).size
INTO tablesize;
EXCEPTION
WHEN OTHERS THEN
EXIT;
END;
END LOOP;
RETURN tablesize;
END;
$$;

This function can also work on Windows with a few minor changes, which are left as an exercise for you.