My database is using Postgres schemas to provide a separated, multi-tenant environment for users. Every schema has a copy of the same tables.
I have one particular query where I need to join across the schemas, return a list of records (in this case, children
). I have this working via a dynamic SQL query, shown below. However, I want to add in a column to each result that specifies the name of the schema that row came from.
Current dynamic query
(Schema's look like: OPERATOR_SCHEMA_my-great-company
)
CREATE OR REPLACE FUNCTION all_children_dynamic() RETURNS SETOF children AS $$
DECLARE
schema RECORD;
BEGIN
FOR schema IN EXECUTE
format(
'SELECT schema_name FROM information_schema.schemata WHERE left(schema_name, 16) = %L',
'OPERATOR_SCHEMA_'
)
LOOP
RETURN QUERY EXECUTE
format('SELECT * FROM %I.children', schema.schema_name);
END LOOP;
END;
$$ LANGUAGE plpgsql;
--------
-- Usage: SELECT "id", "name" FROM all_children_dynamic();
This returns something like:
-------------
| id | name |
| 1 | Bob |
| 2 | Joe |
-------------
Whereas I 'd like it to return something like:
-------------------------------
| id | name | schema_name |
| 1 | Bob | darcy's-store |
| 2 | Joe | bob's-4th-store |
-------------------------------
It should be noted that the schema names are user defined, and can have quotes in them.
How can I add in the relevant schema name for each child?
I have tried a few variations of the following:
LOOP
RETURN QUERY EXECUTE
format('SELECT %s AS schema_name, * FROM %1$I.children', schema.schema_name);
END LOOP;
But I'm having some issues with formatting etc. There's probably some quote_X
functionality I should be using here.
I'm not very knowledgable on Postgres (and databases in general) so your patience is appreciated!
UPDATES
The following are the exact errors I am getting for a few variations.
Input:
format('SELECT %s AS schema_name, * FROM %1$I.children', schema.schema_name);
ERROR: column "operator_schema_don" does not exist
LINE 1: SELECT OPERATOR_SCHEMA_don-t-display-data AS schema_name, * ...
QUERY: SELECT OPERATOR_SCHEMA_don-t-display-data AS schema_name, * FROM "OPERATOR_SCHEMA_don-t-display-data".children
Input:
format('SELECT %s AS schema_name, * FROM %I.children', quote_literal(schema.schema_name), schema.schema_name);
ERROR: structure of query does not match function result type
DETAIL: Returned type unknown does not match expected type uuid in column 1.
UPDATE 2
I'm getting closer, but not quite there yet.
CREATE OR REPLACE FUNCTION all_children_dynamic() RETURNS TABLE (id uuid, schema_name varchar) AS $$
DECLARE
schema RECORD;
BEGIN
FOR schema IN EXECUTE
format(
'SELECT schema_name FROM information_schema.schemata WHERE left(schema_name, 16) = %L',
'OPERATOR_SCHEMA_'
)
LOOP
RETURN QUERY EXECUTE
format('SELECT id, %L AS schema_name FROM %I.children', quote_literal(schema.schema_name), schema.schema_name);
END LOOP;
END;
$$ LANGUAGE plpgsql;
ERROR: structure of query does not match function result type
DETAIL: Returned type unknown does not match expected type character varying in column 2.
CONTEXT: PL/pgSQL function all_children_dynamic() line 11 at RETURN QUERY
Why is the the return type coming back as unknown? I expected it would be inserting a string and just returning that type.
SELECT id, %L::varchar AS schema_name FROM %I.children