In PostgreSQL, we can get the current user and database by
select current_user;
select current_database();
Is select in both statements the same select in a SQL query?
Are current_user and current_database() both column names?
What are the tables they are selected from?
Thanks.
select is the very same select as used in any select query
current_database() is a "System Information Function"
current_user is also a "System Information Function" (see note below)
A function typically takes parameters and therefore are used with parentheses to hold those parameters, but some system functions, such as current_database() don't require any parameters, but the empty parentheses remain.
System Information Functions "extract session and system information" see: https://www.postgresql.org/docs/current/static/functions-info.html
Note
current_catalog, current_role, current_schema, current_user,
session_user, and user have special syntactic status in SQL: they must
be called without trailing parentheses.
Related
This is a symptom of database and user names being different between my dev/staging/live environments, but is there a way to GRANT permissions to a user, determined by some kind of sub-query?
Something like this (not valid syntax):
GRANT UPDATE (my_column) ON my_table TO (SELECT CASE current_database()
WHEN 'account-dev' THEN 'c-app'
WHEN 'account-staging' THEN 'x-app'
WHEN 'account-live' THEN 'a-app'
END);
Use psql and its wonderful \gexec:
SELECT format(
'GRANT UPDATE (my_column) ON my_table TO %I;',
CASE current_database()
WHEN 'account-dev' THEN 'c-app'
WHEN 'account-staging' THEN 'x-app'
WHEN 'account-live' THEN 'a-app'
END
) \gexec
Alternatively, you can write a DO statement that uses EXECUTE to execute a dynamic statement constructed as above.
How can I write a dynamic SELECT INTO query inside a PL/pgSQL function in Postgres?
Say I have a variable called tb_name which is filled in a FOR loop from information_schema.tables. Now I have a variable called tc which will be taking the row count for each table. I want something like the following:
FOR tb_name in select table_name from information_schema.tables where table_schema='some_schema' and table_name like '%1%'
LOOP
EXECUTE FORMAT('select count(*) into' || tc 'from' || tb_name);
END LOOP
What should be the data type of tb_name and tc in this case?
CREATE OR REPLACE FUNCTION myfunc(_tbl_pattern text, _schema text = 'public')
RETURNS void AS -- or whatever you want to return
$func$
DECLARE
_tb_name information_schema.tables.table_name%TYPE; -- currently varchar
_tc bigint; -- count() returns bigint
BEGIN
FOR _tb_name IN
SELECT table_name
FROM information_schema.tables
WHERE table_schema = _schema
AND table_name ~ _tbl_pattern -- see below!
LOOP
EXECUTE format('SELECT count(*) FROM %I.%I', _schema, _tb_name)
INTO _tc;
-- do something with _tc
END LOOP;
END
$func$ LANGUAGE plpgsql;
Notes
I prepended all parameters and variables with an underscore (_) to avoid naming collisions with table columns. Just a useful convention.
_tc should be bigint, since that's what the aggregate function count() returns.
The data type of _tb_name is derived from its parent column dynamically: information_schema.tables.table_name%TYPE. See the chapter Copying Types in the manual.
Are you sure you only want tables listed in information_schema.tables? Makes sense, but be aware of implications. See:
How to check if a table exists in a given schema
a_horse already pointed to the manual and Andy provided a code example. This is how you assign a single row or value returned from a dynamic query with EXECUTE to a (row) variable. A single column (like count in the example) is decomposed from the row type automatically, so we can assign to the scalar variable tc directly - in the same way we would assign a whole row to a record or row variable. Related:
How to get the value of a dynamically generated field name in PL/pgSQL
Schema-qualify the table name in the dynamic query. There may be other tables of the same name in the current search_path, which would result in completely wrong (and very confusing!) results without schema-qualification. Sneaky bug! Or this schema is not in the search_path at all, which would make the function raise an exception immediately.
How does the search_path influence identifier resolution and the "current schema"
Always quote identifiers properly to defend against SQL injection and random errors. Schema and table have to be quoted separately! See:
Table name as a PostgreSQL function parameter
Truncating all tables in a Postgres database
I use the regular expression operator ~ in table_name ~ _tbl_pattern instead of table_name LIKE ('%' || _tbl_pattern || '%'), that's simpler. Be wary of special characters in the pattern parameter either way! See:
PostgreSQL Reverse LIKE
Escape function for regular expression or LIKE patterns
Pattern matching with LIKE, SIMILAR TO or regular expressions in PostgreSQL
I set a default for the schema name in the function call: _schema text = 'public'. Just for convenience, you may or may not want that. See:
Assigning default value for type
Addressing your comment: to pass values, use the USING clause like:
EXECUTE format('SELECT count(*) FROM %I.%I
WHERE some_column = $1', _schema, _tb_name,column_name)
USING user_def_variable;
Related:
INSERT with dynamic table name in trigger function
It looks like you want the %I placeholder for FORMAT so that it treats your variable as an identifier. Also, the INTO clause should go outside the prepared statement.
FOR tb_name in select table_name from information_schema.tables where table_schema='some_schema' and table_name like '%1%'
LOOP
EXECUTE FORMAT('select count(*) from %I', tb_name) INTO tc;
END LOOP
I accidently wrote a query like select from my_table; and surprisingly it is valid statement. Even more interesting to me is that even SELECT; is a valid query in PostgreSQL. You can try to write a lot funny queries with this:
select union all select;
with t as (select) select;
select from (select) a, (select) b;
select where exists (select);
create table a (b int); with t as (select) insert into a (select from t);
Is this a consequence of some definition SQL standard, or there is some use case for it, or it is just funny behavior that no one cared to programatically restrict?
Right from the manual:
The list of output expressions after SELECT can be empty, producing a zero-column result table. This is not valid syntax according to the SQL standard. PostgreSQL allows it to be consistent with allowing zero-column tables. However, an empty list is not allowed when DISTINCT is used.
The possibility of "zero-column" tables is a side effect of the table inheritance if I'm not mistaken. There were discussions over this on the Postgres mailing lists (but I can't find them right now)
I'm working with a PostgresQL database, in which a trigger function logs changes to a history table. I'm trying to add a column which keeps a logical "commit ID" to group master and detail records together. I've created a (non-temporary) sequence, and before I start the batch of updates, I bump this. All my SQL is logged to a log file, so you can clearly see this happening:
2015-04-16 10:43:37 SQLSelect: SELECT nextval('commit_id_seq')
2015-04-16 10:43:37 commit_id_seq: 8
...but then I attempt the UPDATE, my trigger function attempts to use currval, and it fails:
2015-04-16 10:43:37 ERROR: ERROR: currval of sequence "commit_id_seq" is not yet defined in this session
CONTEXT: SQL statement "INSERT INTO history (table_name, record_id, sec_user_id, created, action, notes, status, before, after, commit_id)
SELECT TG_TABLE_NAME, rec.id, (SELECT oid FROM pg_roles WHERE rolname = CURRENT_USER), now(), SUBSTR(TG_OP,1,1), note, stat, hstore(old), hstore(new), currval('commit_id_seq')"
PL/pgSQL function log_to_history() line 18 at SQL statement
[3]
So my question is basically: WTF?
One of two reasons:
Search_path differences, so you're actually talking about two different sequences.
Different sessions. The "current value" is only defined for the session you call nextval() in.
You can add process-id to the logfile format to check if they are different sessions.
For example, here is what I am trying to accomplish:
EXECUTE 'SELECT name, address INTO variable1, variable2
FROM employee WHERE id=1';
Better alternative
Actually, there is nothing indicating a need for dynamic SQL. All identifiers are stable. Assuming you only want to parameterize the id value, simplify to:
SELECT name,address from employee where id = $1
INTO variable1, variable2
Or:
SELECT INTO variable1, variable2
e.name, e.address
FROM employee e
WHERE e.id = _my_variable;
The appropriate chapter in the manual: "Executing a Query with a Single-row Result"
Dynamic SQL would be indicated when working with parameterized table or column names.
Here is a list of related question for dynamic SQL
Cause for immediate problem
The reason why your original code did not work, per documentation:
SELECT INTO is not currently supported within EXECUTE; instead,
execute a plain SELECT command and specify INTO as part of the
EXECUTE itself.
#a_horse demonstrates valid syntax in his answer.
Either alternative relies on the query to return a single row. If your id is defined unique, that cannot break.
EXECUTE 'SELECT name,address from employee where id=1'
INTO variable1, variable2;
More details in the manual: http://www.postgresql.org/docs/current/static/plpgsql-statements.html#PLPGSQL-STATEMENTS-EXECUTING-DYN
Better to also use a parameter instead of concatenated values (to prevent SQL injection):
l_emp_id := 1;
EXECUTE 'SELECT name,address from employee where id=$1'
INTO variable1, variable2
USING l_emp_id;
Assign value, general note
(see this other question for assign value to variable at declaration section)
The language PLpgSQL syntax have many ways to say:
Y := f(X);
The EXECUTE clause is only for "dynamic execution" (less performance),
EXECUTE 'f(X)' INTO Y;
Use Y := f(X); or SELECT for execute static declarations,
SELECT f(X) INTO Y;
Use PERFORM statment when discard the results or to work with void returns:
PERFORM f(X);