mirror of
https://github.com/postgres/postgres.git
synced 2025-07-21 00:01:45 -04:00
Compare commits
No commits in common. "1f89b73c4e053c463d75608c27ca564770fd3158" and "5f27b5f848a433ba54c521ccb889788b8f4d6ba7" have entirely different histories.
1f89b73c4e
...
5f27b5f848
@ -14,15 +14,6 @@
|
||||
#
|
||||
# $ git log --pretty=format:"%H # %cd%n# %s" $PGINDENTGITHASH -1 --date=iso
|
||||
|
||||
e9718b4bd3e4234ffd5a4907a903367fc483c843 # 2023-10-16 09:36:31 +0900
|
||||
# Fix code indentation violations in e83d1b0c40cc
|
||||
|
||||
01529c7040088db2718628d0814058598152bd39 # 2023-10-16 13:32:41 +1300
|
||||
# Fix comment from commit 22655aa231.
|
||||
|
||||
b6a77c6a6ccf698787201b001cbbbf9c89fe5715 # 2023-10-11 17:14:31 -0400
|
||||
# Reindent comment in GenericXLogFinish().
|
||||
|
||||
bc6041b61f6678d32a5cfb70744653cd8f8d01c0 # 2023-08-30 15:56:22 +0900
|
||||
# Fix code indentation vioaltion introduced in commit 3c662643c4.
|
||||
|
||||
|
@ -184,7 +184,7 @@
|
||||
descr => 'database\'s default template',
|
||||
datname => 'template1', encoding => 'ENCODING',
|
||||
datlocprovider => 'LOCALE_PROVIDER', datistemplate => 't',
|
||||
datallowconn => 't', dathasloginevt => 'f', datconnlimit => '-1', datfrozenxid => '0',
|
||||
datallowconn => 't', datconnlimit => '-1', datfrozenxid => '0',
|
||||
datminmxid => '1', dattablespace => 'pg_default', datcollate => 'LC_COLLATE',
|
||||
datctype => 'LC_CTYPE', daticulocale => 'ICU_LOCALE', datacl => '_null_' },
|
||||
|
||||
|
@ -3035,19 +3035,6 @@ SCRAM-SHA-256$<replaceable><iteration count></replaceable>:<replaceable>&l
|
||||
</para></entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry role="catalog_table_entry"><para role="column_definition">
|
||||
<structfield>dathasloginevt</structfield> <type>bool</type>
|
||||
</para>
|
||||
<para>
|
||||
Indicates that there are login event triggers defined for this database.
|
||||
This flag is used to avoid extra lookups on the
|
||||
<structname>pg_event_trigger</structname> table during each backend
|
||||
startup. This flag is used internally by <productname>PostgreSQL</productname>
|
||||
and should not be manually altered or read for monitoring purposes.
|
||||
</para></entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry role="catalog_table_entry"><para role="column_definition">
|
||||
<structfield>datconnlimit</structfield> <type>int4</type>
|
||||
|
@ -4769,7 +4769,6 @@ datdba = 10 (type: 1)
|
||||
encoding = 0 (type: 5)
|
||||
datistemplate = t (type: 1)
|
||||
datallowconn = t (type: 1)
|
||||
dathasloginevt = f (type: 1)
|
||||
datconnlimit = -1 (type: 5)
|
||||
datfrozenxid = 379 (type: 1)
|
||||
dattablespace = 1663 (type: 1)
|
||||
@ -4794,7 +4793,6 @@ datdba = 10 (type: 1)
|
||||
encoding = 0 (type: 5)
|
||||
datistemplate = f (type: 1)
|
||||
datallowconn = t (type: 1)
|
||||
dathasloginevt = f (type: 1)
|
||||
datconnlimit = -1 (type: 5)
|
||||
datfrozenxid = 379 (type: 1)
|
||||
dattablespace = 1663 (type: 1)
|
||||
|
@ -28,7 +28,6 @@
|
||||
An event trigger fires whenever the event with which it is associated
|
||||
occurs in the database in which it is defined. Currently, the only
|
||||
supported events are
|
||||
<literal>login</literal>,
|
||||
<literal>ddl_command_start</literal>,
|
||||
<literal>ddl_command_end</literal>,
|
||||
<literal>table_rewrite</literal>
|
||||
@ -36,24 +35,6 @@
|
||||
Support for additional events may be added in future releases.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The <literal>login</literal> event occurs when an authenticated user logs
|
||||
into the system. Any bug in a trigger procedure for this event may
|
||||
prevent successful login to the system. Such bugs may be fixed by
|
||||
setting <xref linkend="guc-event-triggers"/> is set to <literal>false</literal>
|
||||
either in a connection string or configuration file. Alternative is
|
||||
restarting the system in single-user mode (as event triggers are
|
||||
disabled in this mode). See the <xref linkend="app-postgres"/> reference
|
||||
page for details about using single-user mode.
|
||||
The <literal>login</literal> event will also fire on standby servers.
|
||||
To prevent servers from becoming inaccessible, such triggers must avoid
|
||||
writing anything to the database when running on a standby.
|
||||
Also, it's recommended to avoid long-running queries in
|
||||
<literal>login</literal> event triggers. Notes that, for instance,
|
||||
cancelling connection in <application>psql</application> wouldn't cancel
|
||||
the in-progress <literal>login</literal> trigger.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The <literal>ddl_command_start</literal> event occurs just before the
|
||||
execution of a <literal>CREATE</literal>, <literal>ALTER</literal>, <literal>DROP</literal>,
|
||||
@ -1319,79 +1300,4 @@ CREATE EVENT TRIGGER no_rewrite_allowed
|
||||
</programlisting>
|
||||
</para>
|
||||
</sect1>
|
||||
|
||||
<sect1 id="event-trigger-database-login-example">
|
||||
<title>A Database Login Event Trigger Example</title>
|
||||
|
||||
<para>
|
||||
The event trigger on the <literal>login</literal> event can be
|
||||
useful for logging user logins, for verifying the connection and
|
||||
assigning roles according to current circumstances, or for session
|
||||
data initialization. It is very important that any event trigger using
|
||||
the <literal>login</literal> event checks whether or not the database is
|
||||
in recovery before performing any writes. Writing to a standby server
|
||||
will make it inaccessible.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The following example demonstrates these options.
|
||||
<programlisting>
|
||||
-- create test tables and roles
|
||||
CREATE TABLE user_login_log (
|
||||
"user" text,
|
||||
"session_start" timestamp with time zone
|
||||
);
|
||||
CREATE ROLE day_worker;
|
||||
CREATE ROLE night_worker;
|
||||
|
||||
-- the example trigger function
|
||||
CREATE OR REPLACE FUNCTION init_session()
|
||||
RETURNS event_trigger SECURITY DEFINER
|
||||
LANGUAGE plpgsql AS
|
||||
$$
|
||||
DECLARE
|
||||
hour integer = EXTRACT('hour' FROM current_time at time zone 'utc');
|
||||
rec boolean;
|
||||
BEGIN
|
||||
-- 1. Forbid logging in between 2AM and 4AM.
|
||||
IF hour BETWEEN 2 AND 4 THEN
|
||||
RAISE EXCEPTION 'Login forbidden';
|
||||
END IF;
|
||||
|
||||
-- The checks below cannot be performed on standby servers so
|
||||
-- ensure the database is not in recovery before we perform any
|
||||
-- operations.
|
||||
SELECT pg_is_in_recovery() INTO rec;
|
||||
IF rec THEN
|
||||
RETURN;
|
||||
END IF;
|
||||
|
||||
-- 2. Assign some roles. At daytime, grant the day_worker role, else the
|
||||
-- night_worker role.
|
||||
IF hour BETWEEN 8 AND 20 THEN
|
||||
EXECUTE 'REVOKE night_worker FROM ' || quote_ident(session_user);
|
||||
EXECUTE 'GRANT day_worker TO ' || quote_ident(session_user);
|
||||
ELSE
|
||||
EXECUTE 'REVOKE day_worker FROM ' || quote_ident(session_user);
|
||||
EXECUTE 'GRANT night_worker TO ' || quote_ident(session_user);
|
||||
END IF;
|
||||
|
||||
-- 3. Initialize user session data
|
||||
CREATE TEMP TABLE session_storage (x float, y integer);
|
||||
ALTER TABLE session_storage OWNER TO session_user;
|
||||
|
||||
-- 4. Log the connection time
|
||||
INSERT INTO public.user_login_log VALUES (session_user, current_timestamp);
|
||||
|
||||
END;
|
||||
$$;
|
||||
|
||||
-- trigger definition
|
||||
CREATE EVENT TRIGGER init_session
|
||||
ON login
|
||||
EXECUTE FUNCTION init_session();
|
||||
ALTER EVENT TRIGGER init_session ENABLE ALWAYS;
|
||||
</programlisting>
|
||||
</para>
|
||||
</sect1>
|
||||
</chapter>
|
||||
|
@ -1794,10 +1794,10 @@ ReleaseBulkInsertStatePin(BulkInsertState bistate)
|
||||
bistate->current_buf = InvalidBuffer;
|
||||
|
||||
/*
|
||||
* Despite the name, we also reset bulk relation extension state.
|
||||
* Otherwise we can end up erroring out due to looking for free space in
|
||||
* ->next_free of one partition, even though ->next_free was set when
|
||||
* extending another partition. It could obviously also be bad for
|
||||
* Despite the name, we also reset bulk relation extension
|
||||
* state. Otherwise we can end up erroring out due to looking for free
|
||||
* space in ->next_free of one partition, even though ->next_free was set
|
||||
* when extending another partition. It's obviously also could be bad for
|
||||
* efficiency to look at existing blocks at offsets from another
|
||||
* partition, even if we don't error out.
|
||||
*/
|
||||
|
@ -116,7 +116,7 @@ static void movedb(const char *dbname, const char *tblspcname);
|
||||
static void movedb_failure_callback(int code, Datum arg);
|
||||
static bool get_db_info(const char *name, LOCKMODE lockmode,
|
||||
Oid *dbIdP, Oid *ownerIdP,
|
||||
int *encodingP, bool *dbIsTemplateP, bool *dbAllowConnP, bool *dbHasLoginEvtP,
|
||||
int *encodingP, bool *dbIsTemplateP, bool *dbAllowConnP,
|
||||
TransactionId *dbFrozenXidP, MultiXactId *dbMinMultiP,
|
||||
Oid *dbTablespace, char **dbCollate, char **dbCtype, char **dbIculocale,
|
||||
char **dbIcurules,
|
||||
@ -680,7 +680,6 @@ createdb(ParseState *pstate, const CreatedbStmt *stmt)
|
||||
char src_locprovider = '\0';
|
||||
char *src_collversion = NULL;
|
||||
bool src_istemplate;
|
||||
bool src_hasloginevt;
|
||||
bool src_allowconn;
|
||||
TransactionId src_frozenxid = InvalidTransactionId;
|
||||
MultiXactId src_minmxid = InvalidMultiXactId;
|
||||
@ -969,7 +968,7 @@ createdb(ParseState *pstate, const CreatedbStmt *stmt)
|
||||
|
||||
if (!get_db_info(dbtemplate, ShareLock,
|
||||
&src_dboid, &src_owner, &src_encoding,
|
||||
&src_istemplate, &src_allowconn, &src_hasloginevt,
|
||||
&src_istemplate, &src_allowconn,
|
||||
&src_frozenxid, &src_minmxid, &src_deftablespace,
|
||||
&src_collate, &src_ctype, &src_iculocale, &src_icurules, &src_locprovider,
|
||||
&src_collversion))
|
||||
@ -1376,7 +1375,6 @@ createdb(ParseState *pstate, const CreatedbStmt *stmt)
|
||||
new_record[Anum_pg_database_datlocprovider - 1] = CharGetDatum(dblocprovider);
|
||||
new_record[Anum_pg_database_datistemplate - 1] = BoolGetDatum(dbistemplate);
|
||||
new_record[Anum_pg_database_datallowconn - 1] = BoolGetDatum(dballowconnections);
|
||||
new_record[Anum_pg_database_dathasloginevt - 1] = BoolGetDatum(src_hasloginevt);
|
||||
new_record[Anum_pg_database_datconnlimit - 1] = Int32GetDatum(dbconnlimit);
|
||||
new_record[Anum_pg_database_datfrozenxid - 1] = TransactionIdGetDatum(src_frozenxid);
|
||||
new_record[Anum_pg_database_datminmxid - 1] = TransactionIdGetDatum(src_minmxid);
|
||||
@ -1605,7 +1603,7 @@ dropdb(const char *dbname, bool missing_ok, bool force)
|
||||
pgdbrel = table_open(DatabaseRelationId, RowExclusiveLock);
|
||||
|
||||
if (!get_db_info(dbname, AccessExclusiveLock, &db_id, NULL, NULL,
|
||||
&db_istemplate, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL))
|
||||
&db_istemplate, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL))
|
||||
{
|
||||
if (!missing_ok)
|
||||
{
|
||||
@ -1819,7 +1817,7 @@ RenameDatabase(const char *oldname, const char *newname)
|
||||
*/
|
||||
rel = table_open(DatabaseRelationId, RowExclusiveLock);
|
||||
|
||||
if (!get_db_info(oldname, AccessExclusiveLock, &db_id, NULL, NULL, NULL,
|
||||
if (!get_db_info(oldname, AccessExclusiveLock, &db_id, NULL, NULL,
|
||||
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_UNDEFINED_DATABASE),
|
||||
@ -1929,7 +1927,7 @@ movedb(const char *dbname, const char *tblspcname)
|
||||
*/
|
||||
pgdbrel = table_open(DatabaseRelationId, RowExclusiveLock);
|
||||
|
||||
if (!get_db_info(dbname, AccessExclusiveLock, &db_id, NULL, NULL, NULL,
|
||||
if (!get_db_info(dbname, AccessExclusiveLock, &db_id, NULL, NULL,
|
||||
NULL, NULL, NULL, NULL, &src_tblspcoid, NULL, NULL, NULL, NULL, NULL, NULL))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_UNDEFINED_DATABASE),
|
||||
@ -2695,7 +2693,7 @@ pg_database_collation_actual_version(PG_FUNCTION_ARGS)
|
||||
static bool
|
||||
get_db_info(const char *name, LOCKMODE lockmode,
|
||||
Oid *dbIdP, Oid *ownerIdP,
|
||||
int *encodingP, bool *dbIsTemplateP, bool *dbAllowConnP, bool *dbHasLoginEvtP,
|
||||
int *encodingP, bool *dbIsTemplateP, bool *dbAllowConnP,
|
||||
TransactionId *dbFrozenXidP, MultiXactId *dbMinMultiP,
|
||||
Oid *dbTablespace, char **dbCollate, char **dbCtype, char **dbIculocale,
|
||||
char **dbIcurules,
|
||||
@ -2780,9 +2778,6 @@ get_db_info(const char *name, LOCKMODE lockmode,
|
||||
/* allowed as template? */
|
||||
if (dbIsTemplateP)
|
||||
*dbIsTemplateP = dbform->datistemplate;
|
||||
/* Has on login event trigger? */
|
||||
if (dbHasLoginEvtP)
|
||||
*dbHasLoginEvtP = dbform->dathasloginevt;
|
||||
/* allowing connections? */
|
||||
if (dbAllowConnP)
|
||||
*dbAllowConnP = dbform->datallowconn;
|
||||
|
@ -20,7 +20,6 @@
|
||||
#include "catalog/dependency.h"
|
||||
#include "catalog/indexing.h"
|
||||
#include "catalog/objectaccess.h"
|
||||
#include "catalog/pg_database.h"
|
||||
#include "catalog/pg_event_trigger.h"
|
||||
#include "catalog/pg_namespace.h"
|
||||
#include "catalog/pg_opclass.h"
|
||||
@ -38,18 +37,15 @@
|
||||
#include "miscadmin.h"
|
||||
#include "parser/parse_func.h"
|
||||
#include "pgstat.h"
|
||||
#include "storage/lmgr.h"
|
||||
#include "tcop/deparse_utility.h"
|
||||
#include "tcop/utility.h"
|
||||
#include "utils/acl.h"
|
||||
#include "utils/builtins.h"
|
||||
#include "utils/evtcache.h"
|
||||
#include "utils/fmgroids.h"
|
||||
#include "utils/inval.h"
|
||||
#include "utils/lsyscache.h"
|
||||
#include "utils/memutils.h"
|
||||
#include "utils/rel.h"
|
||||
#include "utils/snapmgr.h"
|
||||
#include "utils/syscache.h"
|
||||
|
||||
typedef struct EventTriggerQueryState
|
||||
@ -107,7 +103,6 @@ static void validate_table_rewrite_tags(const char *filtervar, List *taglist);
|
||||
static void EventTriggerInvoke(List *fn_oid_list, EventTriggerData *trigdata);
|
||||
static const char *stringify_grant_objtype(ObjectType objtype);
|
||||
static const char *stringify_adefprivs_objtype(ObjectType objtype);
|
||||
static void SetDatatabaseHasLoginEventTriggers(void);
|
||||
|
||||
/*
|
||||
* Create an event trigger.
|
||||
@ -138,7 +133,6 @@ CreateEventTrigger(CreateEventTrigStmt *stmt)
|
||||
if (strcmp(stmt->eventname, "ddl_command_start") != 0 &&
|
||||
strcmp(stmt->eventname, "ddl_command_end") != 0 &&
|
||||
strcmp(stmt->eventname, "sql_drop") != 0 &&
|
||||
strcmp(stmt->eventname, "login") != 0 &&
|
||||
strcmp(stmt->eventname, "table_rewrite") != 0)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||
@ -171,10 +165,6 @@ CreateEventTrigger(CreateEventTrigStmt *stmt)
|
||||
else if (strcmp(stmt->eventname, "table_rewrite") == 0
|
||||
&& tags != NULL)
|
||||
validate_table_rewrite_tags("tag", tags);
|
||||
else if (strcmp(stmt->eventname, "login") == 0 && tags != NULL)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("Tag filtering is not supported for login event trigger")));
|
||||
|
||||
/*
|
||||
* Give user a nice error message if an event trigger of the same name
|
||||
@ -306,13 +296,6 @@ insert_event_trigger_tuple(const char *trigname, const char *eventname, Oid evtO
|
||||
CatalogTupleInsert(tgrel, tuple);
|
||||
heap_freetuple(tuple);
|
||||
|
||||
/*
|
||||
* Login event triggers have an additional flag in pg_database to avoid
|
||||
* faster lookups in hot codepaths. Set the flag unless already True.
|
||||
*/
|
||||
if (strcmp(eventname, "login") == 0)
|
||||
SetDatatabaseHasLoginEventTriggers();
|
||||
|
||||
/* Depend on owner. */
|
||||
recordDependencyOnOwner(EventTriggerRelationId, trigoid, evtOwner);
|
||||
|
||||
@ -374,41 +357,6 @@ filter_list_to_array(List *filterlist)
|
||||
return PointerGetDatum(construct_array_builtin(data, l, TEXTOID));
|
||||
}
|
||||
|
||||
/*
|
||||
* Set pg_database.dathasloginevt flag for current database indicating that
|
||||
* current database has on login triggers.
|
||||
*/
|
||||
void
|
||||
SetDatatabaseHasLoginEventTriggers(void)
|
||||
{
|
||||
/* Set dathasloginevt flag in pg_database */
|
||||
Form_pg_database db;
|
||||
Relation pg_db = table_open(DatabaseRelationId, RowExclusiveLock);
|
||||
HeapTuple tuple;
|
||||
|
||||
/*
|
||||
* Use shared lock to prevent a conflit with EventTriggerOnLogin() trying
|
||||
* to reset pg_database.dathasloginevt flag. Note, this lock doesn't
|
||||
* effectively blocks database or other objection. It's just custom lock
|
||||
* tag used to prevent multiple backends changing
|
||||
* pg_database.dathasloginevt flag.
|
||||
*/
|
||||
LockSharedObject(DatabaseRelationId, MyDatabaseId, 0, AccessExclusiveLock);
|
||||
|
||||
tuple = SearchSysCacheCopy1(DATABASEOID, ObjectIdGetDatum(MyDatabaseId));
|
||||
if (!HeapTupleIsValid(tuple))
|
||||
elog(ERROR, "cache lookup failed for database %u", MyDatabaseId);
|
||||
db = (Form_pg_database) GETSTRUCT(tuple);
|
||||
if (!db->dathasloginevt)
|
||||
{
|
||||
db->dathasloginevt = true;
|
||||
CatalogTupleUpdate(pg_db, &tuple->t_self, tuple);
|
||||
CommandCounterIncrement();
|
||||
}
|
||||
table_close(pg_db, RowExclusiveLock);
|
||||
heap_freetuple(tuple);
|
||||
}
|
||||
|
||||
/*
|
||||
* ALTER EVENT TRIGGER foo ENABLE|DISABLE|ENABLE ALWAYS|REPLICA
|
||||
*/
|
||||
@ -443,14 +391,6 @@ AlterEventTrigger(AlterEventTrigStmt *stmt)
|
||||
|
||||
CatalogTupleUpdate(tgrel, &tup->t_self, tup);
|
||||
|
||||
/*
|
||||
* Login event triggers have an additional flag in pg_database to avoid
|
||||
* faster lookups in hot codepaths. Set the flag unless already True.
|
||||
*/
|
||||
if (namestrcmp(&evtForm->evtevent, "login") == 0 &&
|
||||
tgenabled != TRIGGER_DISABLED)
|
||||
SetDatatabaseHasLoginEventTriggers();
|
||||
|
||||
InvokeObjectPostAlterHook(EventTriggerRelationId,
|
||||
trigoid, 0);
|
||||
|
||||
@ -609,15 +549,6 @@ filter_event_trigger(CommandTag tag, EventTriggerCacheItem *item)
|
||||
return true;
|
||||
}
|
||||
|
||||
static CommandTag
|
||||
EventTriggerGetTag(Node *parsetree, EventTriggerEvent event)
|
||||
{
|
||||
if (event == EVT_Login)
|
||||
return CMDTAG_LOGIN;
|
||||
else
|
||||
return CreateCommandTag(parsetree);
|
||||
}
|
||||
|
||||
/*
|
||||
* Setup for running triggers for the given event. Return value is an OID list
|
||||
* of functions to run; if there are any, trigdata is filled with an
|
||||
@ -626,7 +557,7 @@ EventTriggerGetTag(Node *parsetree, EventTriggerEvent event)
|
||||
static List *
|
||||
EventTriggerCommonSetup(Node *parsetree,
|
||||
EventTriggerEvent event, const char *eventstr,
|
||||
EventTriggerData *trigdata, bool unfiltered)
|
||||
EventTriggerData *trigdata)
|
||||
{
|
||||
CommandTag tag;
|
||||
List *cachelist;
|
||||
@ -651,12 +582,10 @@ EventTriggerCommonSetup(Node *parsetree,
|
||||
{
|
||||
CommandTag dbgtag;
|
||||
|
||||
dbgtag = EventTriggerGetTag(parsetree, event);
|
||||
|
||||
dbgtag = CreateCommandTag(parsetree);
|
||||
if (event == EVT_DDLCommandStart ||
|
||||
event == EVT_DDLCommandEnd ||
|
||||
event == EVT_SQLDrop ||
|
||||
event == EVT_Login)
|
||||
event == EVT_SQLDrop)
|
||||
{
|
||||
if (!command_tag_event_trigger_ok(dbgtag))
|
||||
elog(ERROR, "unexpected command tag \"%s\"", GetCommandTagName(dbgtag));
|
||||
@ -675,7 +604,7 @@ EventTriggerCommonSetup(Node *parsetree,
|
||||
return NIL;
|
||||
|
||||
/* Get the command tag. */
|
||||
tag = EventTriggerGetTag(parsetree, event);
|
||||
tag = CreateCommandTag(parsetree);
|
||||
|
||||
/*
|
||||
* Filter list of event triggers by command tag, and copy them into our
|
||||
@ -688,7 +617,7 @@ EventTriggerCommonSetup(Node *parsetree,
|
||||
{
|
||||
EventTriggerCacheItem *item = lfirst(lc);
|
||||
|
||||
if (unfiltered || filter_event_trigger(tag, item))
|
||||
if (filter_event_trigger(tag, item))
|
||||
{
|
||||
/* We must plan to fire this trigger. */
|
||||
runlist = lappend_oid(runlist, item->fnoid);
|
||||
@ -741,7 +670,7 @@ EventTriggerDDLCommandStart(Node *parsetree)
|
||||
runlist = EventTriggerCommonSetup(parsetree,
|
||||
EVT_DDLCommandStart,
|
||||
"ddl_command_start",
|
||||
&trigdata, false);
|
||||
&trigdata);
|
||||
if (runlist == NIL)
|
||||
return;
|
||||
|
||||
@ -789,7 +718,7 @@ EventTriggerDDLCommandEnd(Node *parsetree)
|
||||
|
||||
runlist = EventTriggerCommonSetup(parsetree,
|
||||
EVT_DDLCommandEnd, "ddl_command_end",
|
||||
&trigdata, false);
|
||||
&trigdata);
|
||||
if (runlist == NIL)
|
||||
return;
|
||||
|
||||
@ -835,7 +764,7 @@ EventTriggerSQLDrop(Node *parsetree)
|
||||
|
||||
runlist = EventTriggerCommonSetup(parsetree,
|
||||
EVT_SQLDrop, "sql_drop",
|
||||
&trigdata, false);
|
||||
&trigdata);
|
||||
|
||||
/*
|
||||
* Nothing to do if run list is empty. Note this typically can't happen,
|
||||
@ -876,97 +805,6 @@ EventTriggerSQLDrop(Node *parsetree)
|
||||
list_free(runlist);
|
||||
}
|
||||
|
||||
/*
|
||||
* Fire login event triggers if any are present. The dathasloginevt
|
||||
* pg_database flag is left when an event trigger is dropped, to avoid
|
||||
* complicating the codepath in the case of multiple event triggers. This
|
||||
* function will instead unset the flag if no trigger is defined.
|
||||
*/
|
||||
void
|
||||
EventTriggerOnLogin(void)
|
||||
{
|
||||
List *runlist;
|
||||
EventTriggerData trigdata;
|
||||
|
||||
/*
|
||||
* See EventTriggerDDLCommandStart for a discussion about why event
|
||||
* triggers are disabled in single user mode or via a GUC. We also need a
|
||||
* database connection (some background workers doesn't have it).
|
||||
*/
|
||||
if (!IsUnderPostmaster || !event_triggers ||
|
||||
!OidIsValid(MyDatabaseId) || !MyDatabaseHasLoginEventTriggers)
|
||||
return;
|
||||
|
||||
StartTransactionCommand();
|
||||
runlist = EventTriggerCommonSetup(NULL,
|
||||
EVT_Login, "login",
|
||||
&trigdata, false);
|
||||
|
||||
if (runlist != NIL)
|
||||
{
|
||||
/*
|
||||
* Event trigger execution may require an active snapshot.
|
||||
*/
|
||||
PushActiveSnapshot(GetTransactionSnapshot());
|
||||
|
||||
/* Run the triggers. */
|
||||
EventTriggerInvoke(runlist, &trigdata);
|
||||
|
||||
/* Cleanup. */
|
||||
list_free(runlist);
|
||||
|
||||
PopActiveSnapshot();
|
||||
}
|
||||
|
||||
/*
|
||||
* There is no active login event trigger, but our
|
||||
* pg_database.dathasloginevt was set. Try to unset this flag. We use the
|
||||
* lock to prevent concurrent SetDatatabaseHasLoginEventTriggers(), but we
|
||||
* don't want to hang the connection waiting on the lock. Thus, we are
|
||||
* just trying to acquire the lock conditionally.
|
||||
*/
|
||||
else if (ConditionalLockSharedObject(DatabaseRelationId, MyDatabaseId,
|
||||
0, AccessExclusiveLock))
|
||||
{
|
||||
/*
|
||||
* The lock is held. Now we need to recheck that login event triggers
|
||||
* list is still empty. Once the list is empty, we know that even if
|
||||
* there is a backend, which concurrently inserts/enables login
|
||||
* trigger, it will update pg_database.dathasloginevt *afterwards*.
|
||||
*/
|
||||
runlist = EventTriggerCommonSetup(NULL,
|
||||
EVT_Login, "login",
|
||||
&trigdata, true);
|
||||
|
||||
if (runlist == NIL)
|
||||
{
|
||||
Relation pg_db = table_open(DatabaseRelationId, RowExclusiveLock);
|
||||
HeapTuple tuple;
|
||||
Form_pg_database db;
|
||||
|
||||
tuple = SearchSysCacheCopy1(DATABASEOID,
|
||||
ObjectIdGetDatum(MyDatabaseId));
|
||||
|
||||
if (!HeapTupleIsValid(tuple))
|
||||
elog(ERROR, "cache lookup failed for database %u", MyDatabaseId);
|
||||
|
||||
db = (Form_pg_database) GETSTRUCT(tuple);
|
||||
if (db->dathasloginevt)
|
||||
{
|
||||
db->dathasloginevt = false;
|
||||
CatalogTupleUpdate(pg_db, &tuple->t_self, tuple);
|
||||
}
|
||||
table_close(pg_db, RowExclusiveLock);
|
||||
heap_freetuple(tuple);
|
||||
}
|
||||
else
|
||||
{
|
||||
list_free(runlist);
|
||||
}
|
||||
}
|
||||
CommitTransactionCommand();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Fire table_rewrite triggers.
|
||||
@ -997,7 +835,7 @@ EventTriggerTableRewrite(Node *parsetree, Oid tableOid, int reason)
|
||||
runlist = EventTriggerCommonSetup(parsetree,
|
||||
EVT_TableRewrite,
|
||||
"table_rewrite",
|
||||
&trigdata, false);
|
||||
&trigdata);
|
||||
if (runlist == NIL)
|
||||
return;
|
||||
|
||||
|
@ -1060,44 +1060,6 @@ LockSharedObject(Oid classid, Oid objid, uint16 objsubid,
|
||||
AcceptInvalidationMessages();
|
||||
}
|
||||
|
||||
/*
|
||||
* ConditionalLockSharedObject
|
||||
*
|
||||
* As above, but only lock if we can get the lock without blocking.
|
||||
* Returns true iff the lock was acquired.
|
||||
*/
|
||||
bool
|
||||
ConditionalLockSharedObject(Oid classid, Oid objid, uint16 objsubid,
|
||||
LOCKMODE lockmode)
|
||||
{
|
||||
LOCKTAG tag;
|
||||
LOCALLOCK *locallock;
|
||||
LockAcquireResult res;
|
||||
|
||||
SET_LOCKTAG_OBJECT(tag,
|
||||
InvalidOid,
|
||||
classid,
|
||||
objid,
|
||||
objsubid);
|
||||
|
||||
res = LockAcquireExtended(&tag, lockmode, false, true, true, &locallock);
|
||||
|
||||
if (res == LOCKACQUIRE_NOT_AVAIL)
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Now that we have the lock, check for invalidation messages; see notes
|
||||
* in LockRelationOid.
|
||||
*/
|
||||
if (res != LOCKACQUIRE_ALREADY_CLEAR)
|
||||
{
|
||||
AcceptInvalidationMessages();
|
||||
MarkLockClear(locallock);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* UnlockSharedObject
|
||||
*/
|
||||
|
@ -36,7 +36,6 @@
|
||||
#include "access/xact.h"
|
||||
#include "catalog/pg_type.h"
|
||||
#include "commands/async.h"
|
||||
#include "commands/event_trigger.h"
|
||||
#include "commands/prepare.h"
|
||||
#include "common/pg_prng.h"
|
||||
#include "jit/jit.h"
|
||||
@ -4290,9 +4289,6 @@ PostgresMain(const char *dbname, const char *username)
|
||||
initStringInfo(&row_description_buf);
|
||||
MemoryContextSwitchTo(TopMemoryContext);
|
||||
|
||||
/* Fire any defined login event triggers, if appropriate */
|
||||
EventTriggerOnLogin();
|
||||
|
||||
/*
|
||||
* POSTGRES main processing loop begins here
|
||||
*
|
||||
|
2
src/backend/utils/cache/evtcache.c
vendored
2
src/backend/utils/cache/evtcache.c
vendored
@ -167,8 +167,6 @@ BuildEventTriggerCache(void)
|
||||
event = EVT_SQLDrop;
|
||||
else if (strcmp(evtevent, "table_rewrite") == 0)
|
||||
event = EVT_TableRewrite;
|
||||
else if (strcmp(evtevent, "login") == 0)
|
||||
event = EVT_Login;
|
||||
else
|
||||
continue;
|
||||
|
||||
|
@ -90,8 +90,6 @@ Oid MyDatabaseId = InvalidOid;
|
||||
|
||||
Oid MyDatabaseTableSpace = InvalidOid;
|
||||
|
||||
bool MyDatabaseHasLoginEventTriggers = false;
|
||||
|
||||
/*
|
||||
* DatabasePath is the path (relative to DataDir) of my database's
|
||||
* primary directory, ie, its directory in the default tablespace.
|
||||
|
@ -1103,7 +1103,6 @@ InitPostgres(const char *in_dbname, Oid dboid,
|
||||
}
|
||||
|
||||
MyDatabaseTableSpace = datform->dattablespace;
|
||||
MyDatabaseHasLoginEventTriggers = datform->dathasloginevt;
|
||||
/* pass the database name back to the caller */
|
||||
if (out_dbname)
|
||||
strcpy(out_dbname, dbname);
|
||||
|
@ -24,7 +24,6 @@
|
||||
#include "common/controldata_utils.h"
|
||||
#include "funcapi.h"
|
||||
#include "miscadmin.h"
|
||||
#include "storage/lwlock.h"
|
||||
#include "utils/builtins.h"
|
||||
#include "utils/pg_lsn.h"
|
||||
#include "utils/timestamp.h"
|
||||
@ -43,9 +42,7 @@ pg_control_system(PG_FUNCTION_ARGS)
|
||||
elog(ERROR, "return type must be a row type");
|
||||
|
||||
/* read the control file */
|
||||
LWLockAcquire(ControlFileLock, LW_SHARED);
|
||||
ControlFile = get_controlfile(DataDir, &crc_ok);
|
||||
LWLockRelease(ControlFileLock);
|
||||
if (!crc_ok)
|
||||
ereport(ERROR,
|
||||
(errmsg("calculated CRC checksum does not match value stored in file")));
|
||||
@ -83,9 +80,7 @@ pg_control_checkpoint(PG_FUNCTION_ARGS)
|
||||
elog(ERROR, "return type must be a row type");
|
||||
|
||||
/* Read the control file. */
|
||||
LWLockAcquire(ControlFileLock, LW_SHARED);
|
||||
ControlFile = get_controlfile(DataDir, &crc_ok);
|
||||
LWLockRelease(ControlFileLock);
|
||||
if (!crc_ok)
|
||||
ereport(ERROR,
|
||||
(errmsg("calculated CRC checksum does not match value stored in file")));
|
||||
@ -174,9 +169,7 @@ pg_control_recovery(PG_FUNCTION_ARGS)
|
||||
elog(ERROR, "return type must be a row type");
|
||||
|
||||
/* read the control file */
|
||||
LWLockAcquire(ControlFileLock, LW_SHARED);
|
||||
ControlFile = get_controlfile(DataDir, &crc_ok);
|
||||
LWLockRelease(ControlFileLock);
|
||||
if (!crc_ok)
|
||||
ereport(ERROR,
|
||||
(errmsg("calculated CRC checksum does not match value stored in file")));
|
||||
@ -215,9 +208,7 @@ pg_control_init(PG_FUNCTION_ARGS)
|
||||
elog(ERROR, "return type must be a row type");
|
||||
|
||||
/* read the control file */
|
||||
LWLockAcquire(ControlFileLock, LW_SHARED);
|
||||
ControlFile = get_controlfile(DataDir, &crc_ok);
|
||||
LWLockRelease(ControlFileLock);
|
||||
if (!crc_ok)
|
||||
ereport(ERROR,
|
||||
(errmsg("calculated CRC checksum does not match value stored in file")));
|
||||
|
@ -3263,11 +3263,6 @@ dumpDatabase(Archive *fout)
|
||||
appendPQExpBufferStr(delQry, ";\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* We do not restore pg_database.dathasloginevt because it is set
|
||||
* automatically on login event trigger creation.
|
||||
*/
|
||||
|
||||
/* Add database-specific SET options */
|
||||
dumpDatabaseConfig(fout, creaQry, datname, dbCatId.oid);
|
||||
|
||||
|
@ -3552,8 +3552,8 @@ psql_completion(const char *text, int start, int end)
|
||||
COMPLETE_WITH("ON");
|
||||
/* Complete CREATE EVENT TRIGGER <name> ON with event_type */
|
||||
else if (Matches("CREATE", "EVENT", "TRIGGER", MatchAny, "ON"))
|
||||
COMPLETE_WITH("ddl_command_start", "ddl_command_end", "login",
|
||||
"sql_drop", "table_rewrite");
|
||||
COMPLETE_WITH("ddl_command_start", "ddl_command_end", "sql_drop",
|
||||
"table_rewrite");
|
||||
|
||||
/*
|
||||
* Complete CREATE EVENT TRIGGER <name> ON <event_type>. EXECUTE FUNCTION
|
||||
|
@ -57,6 +57,6 @@
|
||||
*/
|
||||
|
||||
/* yyyymmddN */
|
||||
#define CATALOG_VERSION_NO 202310161
|
||||
#define CATALOG_VERSION_NO 202310141
|
||||
|
||||
#endif
|
||||
|
@ -16,7 +16,7 @@
|
||||
descr => 'default template for new databases',
|
||||
datname => 'template1', encoding => 'ENCODING',
|
||||
datlocprovider => 'LOCALE_PROVIDER', datistemplate => 't',
|
||||
datallowconn => 't', dathasloginevt => 'f', datconnlimit => '-1', datfrozenxid => '0',
|
||||
datallowconn => 't', datconnlimit => '-1', datfrozenxid => '0',
|
||||
datminmxid => '1', dattablespace => 'pg_default', datcollate => 'LC_COLLATE',
|
||||
datctype => 'LC_CTYPE', daticulocale => 'ICU_LOCALE',
|
||||
daticurules => 'ICU_RULES', datacl => '_null_' },
|
||||
|
@ -49,9 +49,6 @@ CATALOG(pg_database,1262,DatabaseRelationId) BKI_SHARED_RELATION BKI_ROWTYPE_OID
|
||||
/* new connections allowed? */
|
||||
bool datallowconn;
|
||||
|
||||
/* database has login event triggers? */
|
||||
bool dathasloginevt;
|
||||
|
||||
/*
|
||||
* Max connections allowed. Negative values have special meaning, see
|
||||
* DATCONNLIMIT_* defines below.
|
||||
|
@ -56,7 +56,6 @@ extern void EventTriggerDDLCommandStart(Node *parsetree);
|
||||
extern void EventTriggerDDLCommandEnd(Node *parsetree);
|
||||
extern void EventTriggerSQLDrop(Node *parsetree);
|
||||
extern void EventTriggerTableRewrite(Node *parsetree, Oid tableOid, int reason);
|
||||
extern void EventTriggerOnLogin(void);
|
||||
|
||||
extern bool EventTriggerBeginCompleteQuery(void);
|
||||
extern void EventTriggerEndCompleteQuery(void);
|
||||
|
@ -203,8 +203,6 @@ extern PGDLLIMPORT Oid MyDatabaseId;
|
||||
|
||||
extern PGDLLIMPORT Oid MyDatabaseTableSpace;
|
||||
|
||||
extern PGDLLIMPORT bool MyDatabaseHasLoginEventTriggers;
|
||||
|
||||
/*
|
||||
* Date/Time Configuration
|
||||
*
|
||||
|
@ -99,8 +99,6 @@ extern void UnlockDatabaseObject(Oid classid, Oid objid, uint16 objsubid,
|
||||
/* Lock a shared-across-databases object (other than a relation) */
|
||||
extern void LockSharedObject(Oid classid, Oid objid, uint16 objsubid,
|
||||
LOCKMODE lockmode);
|
||||
extern bool ConditionalLockSharedObject(Oid classid, Oid objid, uint16 objsubid,
|
||||
LOCKMODE lockmode);
|
||||
extern void UnlockSharedObject(Oid classid, Oid objid, uint16 objsubid,
|
||||
LOCKMODE lockmode);
|
||||
|
||||
|
@ -186,7 +186,6 @@ PG_CMDTAG(CMDTAG_INSERT, "INSERT", false, false, true)
|
||||
PG_CMDTAG(CMDTAG_LISTEN, "LISTEN", false, false, false)
|
||||
PG_CMDTAG(CMDTAG_LOAD, "LOAD", false, false, false)
|
||||
PG_CMDTAG(CMDTAG_LOCK_TABLE, "LOCK TABLE", false, false, false)
|
||||
PG_CMDTAG(CMDTAG_LOGIN, "LOGIN", true, false, false)
|
||||
PG_CMDTAG(CMDTAG_MERGE, "MERGE", false, false, true)
|
||||
PG_CMDTAG(CMDTAG_MOVE, "MOVE", false, false, true)
|
||||
PG_CMDTAG(CMDTAG_NOTIFY, "NOTIFY", false, false, false)
|
||||
|
@ -22,8 +22,7 @@ typedef enum
|
||||
EVT_DDLCommandStart,
|
||||
EVT_DDLCommandEnd,
|
||||
EVT_SQLDrop,
|
||||
EVT_TableRewrite,
|
||||
EVT_Login,
|
||||
EVT_TableRewrite
|
||||
} EventTriggerEvent;
|
||||
|
||||
typedef struct
|
||||
|
@ -1,189 +0,0 @@
|
||||
# Copyright (c) 2021-2023, PostgreSQL Global Development Group
|
||||
|
||||
# Tests of authentication via login trigger. Mostly for rejection via
|
||||
# exception, because this scenario cannot be covered with *.sql/*.out regress
|
||||
# tests.
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use PostgreSQL::Test::Cluster;
|
||||
use PostgreSQL::Test::Utils;
|
||||
use Test::More;
|
||||
|
||||
# Execute a psql command and compare its output towards given regexps
|
||||
sub psql_command
|
||||
{
|
||||
local $Test::Builder::Level = $Test::Builder::Level + 1;
|
||||
my ($node, $sql, $expected_ret, $test_name, %params) = @_;
|
||||
|
||||
my $connstr;
|
||||
if (defined($params{connstr}))
|
||||
{
|
||||
$connstr = $params{connstr};
|
||||
}
|
||||
else
|
||||
{
|
||||
$connstr = '';
|
||||
}
|
||||
|
||||
# Execute command
|
||||
my ($ret, $stdout, $stderr) =
|
||||
$node->psql('postgres', $sql, connstr => "$connstr");
|
||||
|
||||
# Check return code
|
||||
is($ret, $expected_ret, "$test_name: exit code $expected_ret");
|
||||
|
||||
# Check stdout
|
||||
if (defined($params{log_like}))
|
||||
{
|
||||
my @log_like = @{ $params{log_like} };
|
||||
while (my $regex = shift @log_like)
|
||||
{
|
||||
like($stdout, $regex, "$test_name: log matches");
|
||||
}
|
||||
}
|
||||
if (defined($params{log_unlike}))
|
||||
{
|
||||
my @log_unlike = @{ $params{log_unlike} };
|
||||
while (my $regex = shift @log_unlike)
|
||||
{
|
||||
unlike($stdout, $regex, "$test_name: log unmatches");
|
||||
}
|
||||
}
|
||||
if (defined($params{log_exact}))
|
||||
{
|
||||
is($stdout, $params{log_exact}, "$test_name: log equals");
|
||||
}
|
||||
|
||||
# Check stderr
|
||||
if (defined($params{err_like}))
|
||||
{
|
||||
my @err_like = @{ $params{err_like} };
|
||||
while (my $regex = shift @err_like)
|
||||
{
|
||||
like($stderr, $regex, "$test_name: err matches");
|
||||
}
|
||||
}
|
||||
if (defined($params{err_unlike}))
|
||||
{
|
||||
my @err_unlike = @{ $params{err_unlike} };
|
||||
while (my $regex = shift @err_unlike)
|
||||
{
|
||||
unlike($stderr, $regex, "$test_name: err unmatches");
|
||||
}
|
||||
}
|
||||
if (defined($params{err_exact}))
|
||||
{
|
||||
is($stderr, $params{err_exact}, "$test_name: err equals");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
# New node
|
||||
my $node = PostgreSQL::Test::Cluster->new('main');
|
||||
$node->init(extra => [ '--locale=C', '--encoding=UTF8' ]);
|
||||
$node->append_conf(
|
||||
'postgresql.conf', q{
|
||||
wal_level = 'logical'
|
||||
max_replication_slots = 4
|
||||
max_wal_senders = 4
|
||||
});
|
||||
$node->start;
|
||||
|
||||
# Create temporary roles and log table
|
||||
psql_command(
|
||||
$node, 'CREATE ROLE regress_alice WITH LOGIN;
|
||||
CREATE ROLE regress_mallory WITH LOGIN;
|
||||
CREATE TABLE user_logins(id serial, who text);
|
||||
GRANT SELECT ON user_logins TO public;
|
||||
', 0, 'create tmp objects',
|
||||
log_exact => '',
|
||||
err_exact => ''),
|
||||
;
|
||||
|
||||
# Create login event function and trigger
|
||||
psql_command(
|
||||
$node,
|
||||
'CREATE FUNCTION on_login_proc() RETURNS event_trigger AS $$
|
||||
BEGIN
|
||||
INSERT INTO user_logins (who) VALUES (SESSION_USER);
|
||||
IF SESSION_USER = \'regress_mallory\' THEN
|
||||
RAISE EXCEPTION \'Hello %! You are NOT welcome here!\', SESSION_USER;
|
||||
END IF;
|
||||
RAISE NOTICE \'Hello %! You are welcome!\', SESSION_USER;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql SECURITY DEFINER;
|
||||
', 0, 'create trigger function',
|
||||
log_exact => '',
|
||||
err_exact => '');
|
||||
|
||||
psql_command(
|
||||
$node,
|
||||
'CREATE EVENT TRIGGER on_login_trigger '
|
||||
. 'ON login EXECUTE PROCEDURE on_login_proc();', 0,
|
||||
'create event trigger',
|
||||
log_exact => '',
|
||||
err_exact => '');
|
||||
psql_command(
|
||||
$node, 'ALTER EVENT TRIGGER on_login_trigger ENABLE ALWAYS;', 0,
|
||||
'alter event trigger',
|
||||
log_exact => '',
|
||||
err_like => [qr/You are welcome/]);
|
||||
|
||||
# Check the two requests were logged via login trigger
|
||||
psql_command(
|
||||
$node, 'SELECT COUNT(*) FROM user_logins;', 0, 'select count',
|
||||
log_exact => '2',
|
||||
err_like => [qr/You are welcome/]);
|
||||
|
||||
# Try to log as allowed Alice and disallowed Mallory (two times)
|
||||
psql_command(
|
||||
$node, 'SELECT 1;', 0, 'try regress_alice',
|
||||
connstr => 'user=regress_alice',
|
||||
log_exact => '1',
|
||||
err_like => [qr/You are welcome/],
|
||||
err_unlike => [qr/You are NOT welcome/]);
|
||||
psql_command(
|
||||
$node, 'SELECT 1;', 2, 'try regress_mallory',
|
||||
connstr => 'user=regress_mallory',
|
||||
log_exact => '',
|
||||
err_like => [qr/You are NOT welcome/],
|
||||
err_unlike => [qr/You are welcome/]);
|
||||
psql_command(
|
||||
$node, 'SELECT 1;', 2, 'try regress_mallory',
|
||||
connstr => 'user=regress_mallory',
|
||||
log_exact => '',
|
||||
err_like => [qr/You are NOT welcome/],
|
||||
err_unlike => [qr/You are welcome/]);
|
||||
|
||||
# Check that Alice's login record is here, while the Mallory's one is not
|
||||
psql_command(
|
||||
$node, 'SELECT * FROM user_logins;', 0, 'select *',
|
||||
log_like => [qr/3\|regress_alice/],
|
||||
log_unlike => [qr/regress_mallory/],
|
||||
err_like => [qr/You are welcome/]);
|
||||
|
||||
# Check total number of successful logins so far
|
||||
psql_command(
|
||||
$node, 'SELECT COUNT(*) FROM user_logins;', 0, 'select count',
|
||||
log_exact => '5',
|
||||
err_like => [qr/You are welcome/]);
|
||||
|
||||
# Cleanup the temporary stuff
|
||||
psql_command(
|
||||
$node, 'DROP EVENT TRIGGER on_login_trigger;', 0,
|
||||
'drop event trigger',
|
||||
log_exact => '',
|
||||
err_like => [qr/You are welcome/]);
|
||||
psql_command(
|
||||
$node, 'DROP TABLE user_logins;
|
||||
DROP FUNCTION on_login_proc;
|
||||
DROP ROLE regress_mallory;
|
||||
DROP ROLE regress_alice;
|
||||
', 0, 'cleanup',
|
||||
log_exact => '',
|
||||
err_exact => '');
|
||||
|
||||
done_testing();
|
@ -46,25 +46,6 @@ $node_standby_2->start;
|
||||
$node_primary->safe_psql('postgres',
|
||||
"CREATE TABLE tab_int AS SELECT generate_series(1,1002) AS a");
|
||||
|
||||
$node_primary->safe_psql(
|
||||
'postgres', q{
|
||||
CREATE TABLE user_logins(id serial, who text);
|
||||
|
||||
CREATE FUNCTION on_login_proc() RETURNS EVENT_TRIGGER AS $$
|
||||
BEGIN
|
||||
IF NOT pg_is_in_recovery() THEN
|
||||
INSERT INTO user_logins (who) VALUES (session_user);
|
||||
END IF;
|
||||
IF session_user = 'regress_hacker' THEN
|
||||
RAISE EXCEPTION 'You are not welcome!';
|
||||
END IF;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql SECURITY DEFINER;
|
||||
|
||||
CREATE EVENT TRIGGER on_login_trigger ON login EXECUTE FUNCTION on_login_proc();
|
||||
ALTER EVENT TRIGGER on_login_trigger ENABLE ALWAYS;
|
||||
});
|
||||
|
||||
# Wait for standbys to catch up
|
||||
$node_primary->wait_for_replay_catchup($node_standby_1);
|
||||
$node_standby_1->wait_for_replay_catchup($node_standby_2, $node_primary);
|
||||
@ -403,13 +384,6 @@ sub replay_check
|
||||
|
||||
replay_check();
|
||||
|
||||
my $evttrig = $node_standby_1->safe_psql('postgres',
|
||||
"SELECT evtname FROM pg_event_trigger WHERE evtevent = 'login'");
|
||||
is($evttrig, 'on_login_trigger', 'Name of login trigger');
|
||||
$evttrig = $node_standby_2->safe_psql('postgres',
|
||||
"SELECT evtname FROM pg_event_trigger WHERE evtevent = 'login'");
|
||||
is($evttrig, 'on_login_trigger', 'Name of login trigger');
|
||||
|
||||
note "enabling hot_standby_feedback";
|
||||
|
||||
# Enable hs_feedback. The slot should gain an xmin. We set the status interval
|
||||
|
@ -638,48 +638,3 @@ NOTICE: DROP POLICY dropped policy
|
||||
CREATE POLICY pguc ON event_trigger_test USING (FALSE);
|
||||
SET event_triggers = 'off';
|
||||
DROP POLICY pguc ON event_trigger_test;
|
||||
-- Login event triggers
|
||||
CREATE TABLE user_logins(id serial, who text);
|
||||
GRANT SELECT ON user_logins TO public;
|
||||
CREATE FUNCTION on_login_proc() RETURNS event_trigger AS $$
|
||||
BEGIN
|
||||
INSERT INTO user_logins (who) VALUES (SESSION_USER);
|
||||
RAISE NOTICE 'You are welcome!';
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
CREATE EVENT TRIGGER on_login_trigger ON login EXECUTE PROCEDURE on_login_proc();
|
||||
ALTER EVENT TRIGGER on_login_trigger ENABLE ALWAYS;
|
||||
\c
|
||||
NOTICE: You are welcome!
|
||||
SELECT COUNT(*) FROM user_logins;
|
||||
count
|
||||
-------
|
||||
1
|
||||
(1 row)
|
||||
|
||||
\c
|
||||
NOTICE: You are welcome!
|
||||
SELECT COUNT(*) FROM user_logins;
|
||||
count
|
||||
-------
|
||||
2
|
||||
(1 row)
|
||||
|
||||
-- Check dathasloginevt in system catalog
|
||||
SELECT dathasloginevt FROM pg_database WHERE datname= :'DBNAME';
|
||||
dathasloginevt
|
||||
----------------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
-- Cleanup
|
||||
DROP TABLE user_logins;
|
||||
DROP EVENT TRIGGER on_login_trigger;
|
||||
DROP FUNCTION on_login_proc();
|
||||
\c
|
||||
SELECT dathasloginevt FROM pg_database WHERE datname= :'DBNAME';
|
||||
dathasloginevt
|
||||
----------------
|
||||
f
|
||||
(1 row)
|
||||
|
||||
|
@ -495,29 +495,3 @@ DROP POLICY pguc ON event_trigger_test;
|
||||
CREATE POLICY pguc ON event_trigger_test USING (FALSE);
|
||||
SET event_triggers = 'off';
|
||||
DROP POLICY pguc ON event_trigger_test;
|
||||
|
||||
-- Login event triggers
|
||||
CREATE TABLE user_logins(id serial, who text);
|
||||
GRANT SELECT ON user_logins TO public;
|
||||
CREATE FUNCTION on_login_proc() RETURNS event_trigger AS $$
|
||||
BEGIN
|
||||
INSERT INTO user_logins (who) VALUES (SESSION_USER);
|
||||
RAISE NOTICE 'You are welcome!';
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
CREATE EVENT TRIGGER on_login_trigger ON login EXECUTE PROCEDURE on_login_proc();
|
||||
ALTER EVENT TRIGGER on_login_trigger ENABLE ALWAYS;
|
||||
\c
|
||||
SELECT COUNT(*) FROM user_logins;
|
||||
\c
|
||||
SELECT COUNT(*) FROM user_logins;
|
||||
|
||||
-- Check dathasloginevt in system catalog
|
||||
SELECT dathasloginevt FROM pg_database WHERE datname= :'DBNAME';
|
||||
|
||||
-- Cleanup
|
||||
DROP TABLE user_logins;
|
||||
DROP EVENT TRIGGER on_login_trigger;
|
||||
DROP FUNCTION on_login_proc();
|
||||
\c
|
||||
SELECT dathasloginevt FROM pg_database WHERE datname= :'DBNAME';
|
||||
|
Loading…
x
Reference in New Issue
Block a user