diff options
author | Michael P | 2011-04-07 04:10:09 +0000 |
---|---|---|
committer | Pavan Deolasee | 2011-05-24 10:15:42 +0000 |
commit | d2fe66518e3aa72196b44a0e8ebfa745e3e698b4 (patch) | |
tree | c1a2bf319c54b0caf0a67acfacb2ca144a8828d3 | |
parent | 3fa6d176d5551c1e86e89b80e781b1f27e063a78 (diff) |
Support for session and local parameters
This commit adds support for commands like:
SET ROLE ... ;
SET param TO value;
SET SESSION param TO value;
SET LOCAL param TO value;
When a SET command is launched, it is saved in pooler and
then launched to nodes by pooler if connections to backend nodes
exist.
Commands are saved with the following format as a string:
"SET param1 TO value1;...;SET paramN TO valueN"
Local and session commands are saved as separate strings.
When a new connection is created to a backend node, pooler replays
all the saved SET commands.
When a transaction is finished, local parameters are deleted from
pooler. It is not necessary in this case to reset on backend nodes
as transaction commit has made the work.
If a SET command has been launched for a non-local parameter,
connections to nodes are kept alive with the session and not sent back to pool
when a transaction finishes.
When session is finished (user logging off), pooler sends asynchronously
a "RESET ALL" command to each connection and put connections back to pool.
Reset is not launched if no SET queries for session parameters have been launched.
A SET command for local parameters is not sent to pooler if it is not
inside a transaction block to save ressources in the cluster.
This commit contains also a couple of corrections for regression tests
according to implementation of session parameters.
-rw-r--r-- | src/backend/pgxc/pool/execRemote.c | 3 | ||||
-rw-r--r-- | src/backend/pgxc/pool/pgxcnode.c | 22 | ||||
-rw-r--r-- | src/backend/pgxc/pool/poolmgr.c | 238 | ||||
-rw-r--r-- | src/backend/tcop/utility.c | 26 | ||||
-rw-r--r-- | src/include/pgxc/pgxcnode.h | 1 | ||||
-rw-r--r-- | src/include/pgxc/poolmgr.h | 16 | ||||
-rw-r--r-- | src/test/regress/expected/guc_1.out | 85 | ||||
-rw-r--r-- | src/test/regress/expected/plancache_1.out | 6 | ||||
-rw-r--r-- | src/test/regress/expected/privileges_1.out | 31 |
9 files changed, 346 insertions, 82 deletions
diff --git a/src/backend/pgxc/pool/execRemote.c b/src/backend/pgxc/pool/execRemote.c index 08e95ab97d..97cb58f498 100644 --- a/src/backend/pgxc/pool/execRemote.c +++ b/src/backend/pgxc/pool/execRemote.c @@ -4398,6 +4398,9 @@ PGXCNodeCleanAndRelease(int code, Datum arg) /* Release data node connections */ release_handles(); + /* Disconnect from Pooler */ + PoolManagerDisconnect(); + /* Close connection with GTM */ CloseGTM(); diff --git a/src/backend/pgxc/pool/pgxcnode.c b/src/backend/pgxc/pool/pgxcnode.c index dcd721d869..d7230b08a0 100644 --- a/src/backend/pgxc/pool/pgxcnode.c +++ b/src/backend/pgxc/pool/pgxcnode.c @@ -196,6 +196,28 @@ PGXCNodeClose(NODE_CONNECTION *conn) PQfinish((PGconn *) conn); } +/* + * Send SET query to given connection. + * Query is sent asynchronously and results are consumed + */ +int +PGXCNodeSendSetQuery(NODE_CONNECTION *conn, const char *sql_command) +{ + PGresult *result; + + if (!PQsendQuery((PGconn *) conn, sql_command)) + return -1; + + /* Consume results from SET commands */ + while ((result = PQgetResult((PGconn *) conn)) != NULL) + { + /* TODO: Check that results are of type 'S' */ + PQclear(result); + } + + return 0; +} + /* * Checks if connection active diff --git a/src/backend/pgxc/pool/poolmgr.c b/src/backend/pgxc/pool/poolmgr.c index 1120656990..948430618f 100644 --- a/src/backend/pgxc/pool/poolmgr.c +++ b/src/backend/pgxc/pool/poolmgr.c @@ -93,6 +93,7 @@ static void agent_init(PoolAgent *agent, const char *database, const char *user_ static void agent_destroy(PoolAgent *agent); static void agent_create(void); static void agent_handle_input(PoolAgent *agent, StringInfo s); +static int agent_set_command(PoolAgent *agent, const char *set_command, bool is_local); static DatabasePool *create_database_pool(const char *database, const char *user_name); static void insert_database_pool(DatabasePool *pool); static int destroy_database_pool(const char *database, const char *user_name); @@ -102,6 +103,7 @@ static DatabasePool *remove_database_pool(const char *database, const char *user static int *agent_acquire_connections(PoolAgent *agent, List *datanodelist, List *coordlist); static PGXCNodePoolSlot *acquire_connection(DatabasePool *dbPool, int node, char client_conn_type); static void agent_release_connections(PoolAgent *agent, List *dn_discard, List *co_discard); +static void agent_reset_params(PoolAgent *agent, List *dn_list, List *co_list); static void release_connection(DatabasePool *dbPool, PGXCNodePoolSlot *slot, int index, bool clean, char client_conn_type); static void destroy_slot(PGXCNodePoolSlot *slot); @@ -476,6 +478,8 @@ agent_create(void) agent->pool = NULL; agent->dn_connections = NULL; agent->coord_connections = NULL; + agent->session_params = NULL; + agent->local_params = NULL; agent->pid = 0; /* Append new agent to the list */ @@ -528,6 +532,36 @@ PoolManagerConnect(PoolHandle *handle, const char *database, const char *user_na pool_flush(&handle->port); } +int +PoolManagerSetCommand(bool is_local, const char *set_command) +{ + int n32; + char msgtype = 's'; + + Assert(set_command); + Assert(Handle); + + /* Message type */ + pool_putbytes(&Handle->port, &msgtype, 1); + + /* Message length */ + n32 = htonl(strlen(set_command) + 10); + pool_putbytes(&Handle->port, (char *) &n32, 4); + + /* LOCAL or SESSION parameter ? */ + pool_putbytes(&Handle->port, (char *) &is_local, 1); + + /* Length of SET command string */ + n32 = htonl(strlen(set_command) + 1); + pool_putbytes(&Handle->port, (char *) &n32, 4); + + /* Send command string followed by \0 terminator */ + pool_putbytes(&Handle->port, set_command, strlen(set_command) + 1); + pool_flush(&Handle->port); + + /* Get result */ + pool_recvres(&Handle->port); +} /* * Init PoolAgent @@ -567,9 +601,9 @@ agent_destroy(PoolAgent *agent) /* Discard connections if any remaining */ if (agent->pool) { - List *dn_conn = NIL; - List *co_conn = NIL; - int i; + List *dn_conn = NIL; + List *co_conn = NIL; + int i; /* gather abandoned datanode connections */ if (agent->dn_connections) @@ -583,6 +617,12 @@ agent_destroy(PoolAgent *agent) if (agent->coord_connections[i]) co_conn = lappend_int(co_conn, i+1); + /* + * agent is being destroyed, so reset session parameters + * before putting back connections to pool + */ + agent_reset_params(agent, dn_conn, co_conn); + /* release them all */ agent_release_connections(agent, dn_conn, co_conn); } @@ -603,6 +643,16 @@ agent_destroy(PoolAgent *agent) pfree(agent->coord_connections); agent->coord_connections = NULL; } + if (agent->local_params) + { + pfree(agent->local_params); + agent->local_params = NULL; + } + if (agent->session_params) + { + pfree(agent->session_params); + agent->session_params = NULL; + } pfree(agent); /* shrink the list and move last agent into the freed slot */ if (i < --agentCount) @@ -618,16 +668,14 @@ agent_destroy(PoolAgent *agent) * Release handle to pool manager */ void -PoolManagerDisconnect(PoolHandle *handle) +PoolManagerDisconnect(void) { - Assert(handle); + Assert(Handle); - pool_putmessage(&handle->port, 'd', NULL, 0); + pool_putmessage(&Handle->port, 'd', NULL, 0); pool_flush(&Handle->port); - close(Socket(handle->port)); - - pfree(handle); + close(Socket(Handle->port)); } @@ -784,6 +832,8 @@ agent_handle_input(PoolAgent * agent, StringInfo s) { const char *database; const char *user_name; + const char *set_command; + bool is_local; int datanodecount; int coordcount; List *datanodelist = NIL; @@ -905,6 +955,20 @@ agent_handle_input(PoolAgent * agent, StringInfo s) list_free(datanodelist); list_free(coordlist); break; + case 's': /* SET COMMAND */ + pool_getmessage(&agent->port, s, 0); + /* Determine if command is local or session */ + is_local = (bool) pq_getmsgbyte(s); + /* Get the SET command */ + len = pq_getmsgint(s, 4); + set_command = pq_getmsgbytes(s, len); + pq_getmsgend(s); + + res = agent_set_command(agent, set_command, is_local); + + /* Send success result */ + pool_sendres(&agent->port, res); + break; default: /* EOF or protocol violation */ agent_destroy(agent); return; @@ -915,6 +979,77 @@ agent_handle_input(PoolAgent * agent, StringInfo s) } } +/* + * Save a SET command and distribute it to the agent connections + * already in use. + */ +static int +agent_set_command(PoolAgent *agent, const char *set_command, bool is_local) +{ + char *params_string; + int i; + int res = 0; + + Assert(agent); + Assert(set_command); + + if (is_local) + params_string = agent->local_params; + else + params_string = agent->session_params; + + /* First command recorded */ + if (!params_string) + { + params_string = pstrdup(set_command); + if (!params_string) + ereport(ERROR, + (errcode(ERRCODE_OUT_OF_MEMORY), + errmsg("out of memory"))); + } + else + { + /* + * Second command or more recorded. + * Commands are saved with format 'SET param1 TO value1;...;SET paramN TO valueN' + */ + params_string = (char *) repalloc(params_string, + strlen(params_string) + strlen(set_command) + 2); + if (!params_string) + ereport(ERROR, + (errcode(ERRCODE_OUT_OF_MEMORY), + errmsg("out of memory"))); + + sprintf(params_string, "%s;%s", params_string, set_command); + } + + /* Launch the new command to all the connections already hold by the agent */ + if (agent->dn_connections) + { + for (i = 0; i < NumDataNodes; i++) + { + if (agent->dn_connections[i]) + res = PGXCNodeSendSetQuery(agent->dn_connections[i]->conn, set_command); + } + } + + if (agent->coord_connections) + { + for (i = 0; i < NumCoords; i++) + { + if (agent->coord_connections[i]) + res |= PGXCNodeSendSetQuery(agent->coord_connections[i]->conn, set_command); + } + } + + /* Save the latest string */ + if (is_local) + agent->local_params = params_string; + else + agent->session_params = params_string; + + return res; +} /* * acquire connection @@ -1005,6 +1140,12 @@ agent_acquire_connections(PoolAgent *agent, List *datanodelist, List *coordlist) /* Store in the descriptor */ agent->dn_connections[node - 1] = slot; + + /* Update newly-acquired slot with session parameters */ + if (agent->session_params) + PGXCNodeSendSetQuery(slot->conn, agent->session_params); + if (agent->local_params) + PGXCNodeSendSetQuery(slot->conn, agent->local_params); } result[i++] = PQsocket((PGconn *) agent->dn_connections[node - 1]->conn); @@ -1029,6 +1170,12 @@ agent_acquire_connections(PoolAgent *agent, List *datanodelist, List *coordlist) /* Store in the descriptor */ agent->coord_connections[node - 1] = slot; + + /* Update newly-acquired slot with session parameters */ + if (agent->session_params) + PGXCNodeSendSetQuery(slot->conn, agent->session_params); + if (agent->local_params) + PGXCNodeSendSetQuery(slot->conn, agent->local_params); } result[i++] = PQsocket((PGconn *) agent->coord_connections[node - 1]->conn); @@ -1095,6 +1242,20 @@ agent_release_connections(PoolAgent *agent, List *dn_discard, List *co_discard) if (!agent->dn_connections && !agent->coord_connections) return; + /* + * If there are some session parameters, do not put back connections to pool + * disconnection will be made when session is cut for this user. + * Local parameters are reset when transaction block is finished, + * so don't do anything for them, but just reset their list. + */ + if (agent->local_params) + { + pfree(agent->local_params); + agent->local_params = NULL; + } + if (agent->session_params) + return; + /* Discard first for Datanodes */ if (dn_discard) { @@ -1156,6 +1317,65 @@ agent_release_connections(PoolAgent *agent, List *dn_discard, List *co_discard) } } +/* + * Reset session parameters for given connections in the agent. + * This is done before putting back to pool connections that have been + * modified by session parameters. + */ +static void +agent_reset_params(PoolAgent *agent, List *dn_list, List *co_list) +{ + PGXCNodePoolSlot *slot; + + if (!agent->dn_connections && !agent->coord_connections) + return; + + /* Parameters are reset, so free commands */ + if (agent->session_params) + { + pfree(agent->session_params); + agent->session_params = NULL; + } + if (agent->local_params) + { + pfree(agent->local_params); + agent->local_params = NULL; + } + + /* Reset Datanode connection params */ + if (dn_list) + { + ListCell *lc; + + foreach(lc, dn_list) + { + int node = lfirst_int(lc); + Assert(node > 0 && node <= NumDataNodes); + slot = agent->dn_connections[node - 1]; + + /* Reset connection params */ + if (slot) + PGXCNodeSendSetQuery(slot->conn, "RESET ALL;"); + } + } + + /* Reset Coordinator connection params */ + if (co_list) + { + ListCell *lc; + + foreach(lc, co_list) + { + int node = lfirst_int(lc); + Assert(node > 0 && node <= NumCoords); + slot = agent->coord_connections[node - 1]; + + /* Reset connection params */ + if (slot) + PGXCNodeSendSetQuery(slot->conn, "RESET ALL;"); + } + } +} /* * Create new empty pool for a database. diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c index 08a05a955e..1576c4a043 100644 --- a/src/backend/tcop/utility.c +++ b/src/backend/tcop/utility.c @@ -62,6 +62,7 @@ #include "pgxc/pgxc.h" #include "pgxc/planner.h" #include "pgxc/poolutils.h" +#include "pgxc/poolmgr.h" static void ExecUtilityStmtOnNodes(const char *queryString, ExecNodes *nodes, bool force_autocommit, RemoteQueryExecType exec_type); @@ -1410,11 +1411,18 @@ standard_ProcessUtility(Node *parsetree, case T_VariableSetStmt: ExecSetVariableStmt((VariableSetStmt *) parsetree); #ifdef PGXC -/* PGXCTODO - this currently causes an assertion failure. - We should change when we add SET handling properly - if (IS_PGXC_COORDINATOR) - ExecUtilityStmtOnNodes(queryString, NULL, false); -*/ + /* Let the pooler manage the statement */ + if (IS_PGXC_COORDINATOR && !IsConnFromCoord()) + { + VariableSetStmt *stmt = (VariableSetStmt *) parsetree; + /* + * If command is local and we are not in a transaction block do NOT + * send this query to backend nodes + */ + if (!stmt->is_local || !IsTransactionBlock()) + if (PoolManagerSetCommand(stmt->is_local, queryString) < 0) + elog(ERROR, "Postgres-XC: ERROR SET query"); + } #endif break; @@ -1572,6 +1580,14 @@ standard_ProcessUtility(Node *parsetree, case T_ConstraintsSetStmt: AfterTriggerSetState((ConstraintsSetStmt *) parsetree); + + /* + * PGXCTODO: SET CONSTRAINT management + * This can just be done inside a transaction block, + * so just launch it on all the Datanodes. + * For the time being only IMMEDIATE constraints are supported + * so this is not really useful... + */ break; case T_CheckPointStmt: diff --git a/src/include/pgxc/pgxcnode.h b/src/include/pgxc/pgxcnode.h index fd7c466995..76f131e10d 100644 --- a/src/include/pgxc/pgxcnode.h +++ b/src/include/pgxc/pgxcnode.h @@ -96,6 +96,7 @@ extern void InitMultinodeExecutor(void); extern char *PGXCNodeConnStr(char *host, char *port, char *dbname, char *user, char *remote_type); extern NODE_CONNECTION *PGXCNodeConnect(char *connstr); +extern int PGXCNodeSendSetQuery(NODE_CONNECTION *conn, const char *sql_command); extern void PGXCNodeClose(NODE_CONNECTION * conn); extern int PGXCNodeConnected(NODE_CONNECTION * conn); extern int PGXCNodeConnClean(NODE_CONNECTION * conn); diff --git a/src/include/pgxc/poolmgr.h b/src/include/pgxc/poolmgr.h index b62f77e164..5299d0a5ae 100644 --- a/src/include/pgxc/poolmgr.h +++ b/src/include/pgxc/poolmgr.h @@ -56,8 +56,10 @@ typedef struct databasepool struct databasepool *next; } DatabasePool; -/* Agent of client session (Pool Manager side) +/* + * Agent of client session (Pool Manager side) * Acts as a session manager, grouping connections together + * and managing session parameters */ typedef struct { @@ -68,6 +70,8 @@ typedef struct DatabasePool *pool; PGXCNodePoolSlot **dn_connections; /* one for each Datanode */ PGXCNodePoolSlot **coord_connections; /* one for each Coordinator */ + char *session_params; + char *local_params; } PoolAgent; /* Handle to the pool manager (Session's side) */ @@ -116,7 +120,7 @@ extern void PoolManagerCloseHandle(PoolHandle *handle); /* * Gracefully close connection to the PoolManager */ -extern void PoolManagerDisconnect(PoolHandle *handle); +extern void PoolManagerDisconnect(void); /* * Called from Session process after fork(). Associate handle with session @@ -125,6 +129,14 @@ extern void PoolManagerDisconnect(PoolHandle *handle); */ extern void PoolManagerConnect(PoolHandle *handle, const char *database, const char *user_name); +/* + * Save a SET command in Pooler. + * This command is run on existent agent connections + * and stored in pooler agent to be replayed when new connections + * are requested. + */ +extern int PoolManagerSetCommand(bool is_local, const char *set_command); + /* Get pooled connections */ extern int *PoolManagerGetConnections(List *datanodelist, List *coordlist); diff --git a/src/test/regress/expected/guc_1.out b/src/test/regress/expected/guc_1.out index d71a66c817..83b5b6598b 100644 --- a/src/test/regress/expected/guc_1.out +++ b/src/test/regress/expected/guc_1.out @@ -513,6 +513,7 @@ SELECT current_user = 'temp_reset_user'; (1 row) DROP ROLE temp_reset_user; +ERROR: permission denied to drop role -- -- Tests for function-local GUC settings -- @@ -520,32 +521,35 @@ set work_mem = '3MB'; create function report_guc(text) returns text as $$ select current_setting($1) $$ language sql set work_mem = '1MB'; +ERROR: stable and volatile not yet supported, function volatility has to be immutable select report_guc('work_mem'), current_setting('work_mem'); - report_guc | current_setting -------------+----------------- - 1MB | 3MB -(1 row) - +ERROR: function report_guc(unknown) does not exist +LINE 1: select report_guc('work_mem'), current_setting('work_mem'); + ^ +HINT: No function matches the given name and argument types. You might need to add explicit type casts. -- this should draw only a warning alter function report_guc(text) set search_path = no_such_schema; -NOTICE: schema "no_such_schema" does not exist +ERROR: function report_guc(text) does not exist -- with error occurring here select report_guc('work_mem'), current_setting('work_mem'); -ERROR: schema "no_such_schema" does not exist +ERROR: function report_guc(unknown) does not exist +LINE 1: select report_guc('work_mem'), current_setting('work_mem'); + ^ +HINT: No function matches the given name and argument types. You might need to add explicit type casts. alter function report_guc(text) reset search_path set work_mem = '2MB'; +ERROR: function report_guc(text) does not exist select report_guc('work_mem'), current_setting('work_mem'); - report_guc | current_setting -------------+----------------- - 2MB | 3MB -(1 row) - +ERROR: function report_guc(unknown) does not exist +LINE 1: select report_guc('work_mem'), current_setting('work_mem'); + ^ +HINT: No function matches the given name and argument types. You might need to add explicit type casts. alter function report_guc(text) reset all; +ERROR: function report_guc(text) does not exist select report_guc('work_mem'), current_setting('work_mem'); - report_guc | current_setting -------------+----------------- - 3MB | 3MB -(1 row) - +ERROR: function report_guc(unknown) does not exist +LINE 1: select report_guc('work_mem'), current_setting('work_mem'); + ^ +HINT: No function matches the given name and argument types. You might need to add explicit type casts. -- SET LOCAL is restricted by a function SET option create or replace function myfunc(int) returns text as $$ begin @@ -554,19 +558,19 @@ begin end $$ language plpgsql set work_mem = '1MB'; +ERROR: stable and volatile not yet supported, function volatility has to be immutable select myfunc(0), current_setting('work_mem'); - myfunc | current_setting ---------+----------------- - 2MB | 3MB -(1 row) - +ERROR: function myfunc(integer) does not exist +LINE 1: select myfunc(0), current_setting('work_mem'); + ^ +HINT: No function matches the given name and argument types. You might need to add explicit type casts. alter function myfunc(int) reset all; +ERROR: function myfunc(integer) does not exist select myfunc(0), current_setting('work_mem'); - myfunc | current_setting ---------+----------------- - 2MB | 2MB -(1 row) - +ERROR: function myfunc(integer) does not exist +LINE 1: select myfunc(0), current_setting('work_mem'); + ^ +HINT: No function matches the given name and argument types. You might need to add explicit type casts. set work_mem = '3MB'; -- but SET isn't create or replace function myfunc(int) returns text as $$ @@ -576,12 +580,12 @@ begin end $$ language plpgsql set work_mem = '1MB'; +ERROR: stable and volatile not yet supported, function volatility has to be immutable select myfunc(0), current_setting('work_mem'); - myfunc | current_setting ---------+----------------- - 2MB | 2MB -(1 row) - +ERROR: function myfunc(integer) does not exist +LINE 1: select myfunc(0), current_setting('work_mem'); + ^ +HINT: No function matches the given name and argument types. You might need to add explicit type casts. set work_mem = '3MB'; -- it should roll back on error, though create or replace function myfunc(int) returns text as $$ @@ -592,10 +596,12 @@ begin end $$ language plpgsql set work_mem = '1MB'; +ERROR: stable and volatile not yet supported, function volatility has to be immutable select myfunc(0); -ERROR: division by zero -CONTEXT: SQL statement "SELECT 1/$1" -PL/pgSQL function "myfunc" line 3 at PERFORM +ERROR: function myfunc(integer) does not exist +LINE 1: select myfunc(0); + ^ +HINT: No function matches the given name and argument types. You might need to add explicit type casts. select current_setting('work_mem'); current_setting ----------------- @@ -603,8 +609,7 @@ select current_setting('work_mem'); (1 row) select myfunc(1), current_setting('work_mem'); - myfunc | current_setting ---------+----------------- - 2MB | 2MB -(1 row) - +ERROR: function myfunc(integer) does not exist +LINE 1: select myfunc(1), current_setting('work_mem'); + ^ +HINT: No function matches the given name and argument types. You might need to add explicit type casts. diff --git a/src/test/regress/expected/plancache_1.out b/src/test/regress/expected/plancache_1.out index 389d0dad5d..683a42ebc6 100644 --- a/src/test/regress/expected/plancache_1.out +++ b/src/test/regress/expected/plancache_1.out @@ -150,7 +150,11 @@ ERROR: Postgres-XC does not support EXECUTE yet DETAIL: The feature is not currently supported set search_path = s2; select f1 from abc; -ERROR: relation "abc" does not exist + f1 +----- + 456 +(1 row) + execute p1; ERROR: Postgres-XC does not support EXECUTE yet DETAIL: The feature is not currently supported diff --git a/src/test/regress/expected/privileges_1.out b/src/test/regress/expected/privileges_1.out index d71fd3425d..51153a2e7a 100644 --- a/src/test/regress/expected/privileges_1.out +++ b/src/test/regress/expected/privileges_1.out @@ -157,6 +157,7 @@ UPDATE atest2 SET col2 = NULL; -- ok UPDATE atest2 SET col2 = NOT col2; -- fails; requires SELECT on atest2 ERROR: permission denied for relation atest2 UPDATE atest2 SET col2 = true FROM atest1 WHERE atest1.a = 5; -- ok +ERROR: permission denied for relation atest2 SELECT * FROM atest1 FOR UPDATE; -- fail ERROR: permission denied for relation atest1 SELECT * FROM atest2 FOR UPDATE; -- fail @@ -217,26 +218,17 @@ SELECT * FROM atestv1; -- ok SELECT * FROM atestv2; -- fail ERROR: permission denied for relation atestv2 SELECT * FROM atestv3; -- ok - one | two | three ------+-----+------- -(0 rows) - +ERROR: permission denied for relation atest3 CREATE VIEW atestv4 AS SELECT * FROM atestv3; -- nested view SELECT * FROM atestv4; -- ok - one | two | three ------+-----+------- -(0 rows) - +ERROR: permission denied for relation atest3 GRANT SELECT ON atestv4 TO regressuser2; SET SESSION AUTHORIZATION regressuser2; -- Two complex cases: SELECT * FROM atestv3; -- fail ERROR: permission denied for relation atestv3 SELECT * FROM atestv4; -- ok (even though regressuser2 cannot access underlying atestv3) - one | two | three ------+-----+------- -(0 rows) - +ERROR: permission denied for relation atest3 SELECT * FROM atest2; -- ok col1 | col2 ------+------ @@ -294,17 +286,9 @@ ERROR: permission denied for relation atest5 SELECT * FROM atest1, atest5; -- fail ERROR: permission denied for relation atest5 SELECT atest1.* FROM atest1, atest5; -- ok - a | b ----+----- - 2 | two -(1 row) - +ERROR: permission denied for relation atest5 SELECT atest1.*,atest5.one FROM atest1, atest5; -- ok - a | b | one ----+-----+----- - 2 | two | 1 -(1 row) - +ERROR: permission denied for relation atest5 SELECT atest1.*,atest5.one FROM atest1 JOIN atest5 ON (atest1.a = atest5.two); -- fail ERROR: permission denied for relation atest5 SELECT atest1.*,atest5.one FROM atest1 JOIN atest5 ON (atest1.a = atest5.one); -- ok @@ -817,7 +801,6 @@ SELECT has_table_privilege('regressuser3', 'atest4', 'SELECT'); -- true REVOKE SELECT ON atest4 FROM regressuser2; -- fail ERROR: dependent privileges exist -HINT: Use CASCADE to revoke them too. REVOKE GRANT OPTION FOR SELECT ON atest4 FROM regressuser2 CASCADE; -- ok SELECT has_table_privilege('regressuser2', 'atest4', 'SELECT'); -- true has_table_privilege @@ -1235,8 +1218,6 @@ REVOKE USAGE ON LANGUAGE sql FROM regressuser1; DROP OWNED BY regressuser1; DROP USER regressuser1; DROP USER regressuser2; -ERROR: role "regressuser2" cannot be dropped because some objects depend on it -DETAIL: privileges for language sql DROP USER regressuser3; DROP USER regressuser4; DROP USER regressuser5; |