You could likely accomplish what you're looking to do in a couple of different ways, though both would require you to GRANT a combination of pg_write_server_files and/or pg_execute_server_program to the executing role/user.
Method 1: Using Dynamic SQL (with pg_write_server_files)
Here, you'd extrapolate upon your initial plan of using the COPY command, changing
COPY v_originaltext to '/var/lib/postgresql/sftp/smrptesting1.json';
to instead DECLARE a SQL string variable (v_sql) within the function block that you'd then EXECUTE, i.e. something like
CREATE OR REPLACE FUNCTION json_putter(_parameters TEXT)
RETURNS VOID
LANGUAGE plpgsql
AS $function$
DECLARE
v_sql TEXT DEFAULT NULL;
v_originaltext JSONB;
v_filename TEXT;
BEGIN
-- < ... code to populate v_originaltext here ... >
v_filename := '/var/lib/postgresql/sftp/' || _parameters || '.json';
v_sql := 'COPY $1 TO $2';
EXECUTE v_sql USING v_originaltext, v_filename;
-- or, alternatively, with literal single quotes:
v_sql := 'COPY (SELECT v_originaltext) TO ' || CHR(39) || v_filename || CHR(39);
EXECUTE v_sql;
END
$function$
which will build up the COPY statement as if you were manually running from the client.
Method 2: Using server-side psql command (with pg_execute_server_program)
You can output the json directly from the psql command, and redirect the stdout to a file descriptor that you generate from the parameters.
To implement this, you'd probably want to create two atomic functions for simplicity's sake:
- One that acts as a wrapper to generate the value of v_originaltext [e.g. fx_originaltext(_parameters => text)]
- One that will run a generic command via psql using the COPY ... FROM PROGRAM utility and consumes the stdout with a temp table or similar:
--
CREATE OR REPLACE FUNCTION psql_runner(_function TEXT, _parameters TEXT)
RETURNS VOID
LANGUAGE plpgsql
AS $function$
DECLARE
v_filename TEXT;
BEGIN
v_filename := '/var/lib/postgresql/sftp/' || _parameters || '.json';
CREATE TEMP TABLE _out ( stdout text );
COPY _out FROM PROGRAM ('psql -tq -c "SELECT ' || _function || '(_parameters :=' || quote_literal(_parameters) || ') " > ' || v_filename);
END
$function$
Notably, the second method requires a larger scope of privileges, and depending on which version of postgres you're using, may need to execute the COPY as dynamic SQL as in the first method.