Use default values for function parameters and dynamic SQL ...
Demo function
CREATE OR REPLACE FUNCTION create_patient(
_name text = NULL -- always updated, NULL if not provided
,_email text = NULL
,_phone text = NULL
,_password text = NULL
,_field1 text = NULL -- variable parameters
,_field2 text = NULL
,_field3 timestamp = NULL
,_language text = NULL
,_cols text[] = '{field1,field2,field3,language}'
,OUT _pid text
,OUT _id int)
RETURNS record AS
$func$
BEGIN
EXECUTE format(
'INSERT INTO patients (field1, field2, field3, name, email, phone)
VALUES (%s, %s, %s, $4, $5, $6 )
RETURNING id'
,CASE WHEN 'field1' = ANY(_cols) THEN '$1' ELSE 'DEFAULT' END
,CASE WHEN 'field2' = ANY(_cols) THEN '$2' ELSE 'DEFAULT' END
,CASE WHEN 'field3' = ANY(_cols) THEN '$3' ELSE 'DEFAULT' END)
INTO _pid -- return value, also used in 2nd insert
USING _field1, _field2, _field3, _name, _email, _phone;
EXECUTE format(
'INSERT INTO users (username, password, type, pid, phone, language)
VALUES ($1, $2, $$patient$$, $3, $4, %s )
RETURNING id'
,CASE WHEN 'language' = ANY(_cols) THEN '$4' ELSE 'DEFAULT' END)
INTO _id -- return value
USING _email, _password, _pid, _phone, _language;
END
$func$ LANGUAGE plpgsql;
Call
SELECT * FROM create_patient('myname','myemail','myphone','mypassword'
,'myfield1','myfield2',NULL,'English','{field2,language,field1}'::text[]);
As the function uses named parameters and each has a default value, you can even call it like this:
SELECT * FROM create_patient(_name := 'myname');
May not work for your tables if some non-null values are required, but goes to demonstrate that you can omit any parameters with defaults on them once you provide named parameters. Omitted parameters take the default value as declared (not to be confused with column defaults). More in this related answer:
Functions with variable number of input parameters
Major points
Make use of the DEFAULT keyword of the INSERT command. It makes the system insert the column default of the table.
Alternative would be to only list columns in the INSERT line that get a corresponding item in the VALUES line.
You have to use dynamic SQL and EXECUTE to manipulate the statement itself, not just the values.
"Swing columns" are field1 to field3 and language, the rest is hardwired as per definition. Vary as needed.
My function works for all cases, you can even provide a NULL value instead of the column default. That requires a parameter _cols providing the information which columns are to be be inserted.
If all involved columns were declared NOT NULL - which has not been clarified - you can simplify: pass NULL for any column that should get the column default and adapt the CASE statements.
If you omit _cols, all fields will be inserted. As _cols is the last IN parameter and has a default value, you can always omit it.
I employ the USING clause for EXECUTE to pass parameters as values and prevent SQL injection with dynamically built query strings.
I employ format() to simplify statement assembly and avoid multiple assignments. Cheaper in PL/pgSQL.
Don't DECLARE _id and _pid in the function body, since they are declared by OUT parameters in the header and returned automatically.
You can insert a constant value for type in the INSERT statement directly. This way you don't need any variables and save additional assignments.
Tested with PostgreSQL 9.1, but should work with all versions since 8.4 - except for format() which was introduced with 9.1.