mirror of
https://github.com/postgres/postgres.git
synced 2025-07-22 00:01:40 -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
|
# $ 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
|
bc6041b61f6678d32a5cfb70744653cd8f8d01c0 # 2023-08-30 15:56:22 +0900
|
||||||
# Fix code indentation vioaltion introduced in commit 3c662643c4.
|
# Fix code indentation vioaltion introduced in commit 3c662643c4.
|
||||||
|
|
||||||
|
@ -184,7 +184,7 @@
|
|||||||
descr => 'database\'s default template',
|
descr => 'database\'s default template',
|
||||||
datname => 'template1', encoding => 'ENCODING',
|
datname => 'template1', encoding => 'ENCODING',
|
||||||
datlocprovider => 'LOCALE_PROVIDER', datistemplate => 't',
|
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',
|
datminmxid => '1', dattablespace => 'pg_default', datcollate => 'LC_COLLATE',
|
||||||
datctype => 'LC_CTYPE', daticulocale => 'ICU_LOCALE', datacl => '_null_' },
|
datctype => 'LC_CTYPE', daticulocale => 'ICU_LOCALE', datacl => '_null_' },
|
||||||
|
|
||||||
|
@ -3035,19 +3035,6 @@ SCRAM-SHA-256$<replaceable><iteration count></replaceable>:<replaceable>&l
|
|||||||
</para></entry>
|
</para></entry>
|
||||||
</row>
|
</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>
|
<row>
|
||||||
<entry role="catalog_table_entry"><para role="column_definition">
|
<entry role="catalog_table_entry"><para role="column_definition">
|
||||||
<structfield>datconnlimit</structfield> <type>int4</type>
|
<structfield>datconnlimit</structfield> <type>int4</type>
|
||||||
|
@ -4769,7 +4769,6 @@ datdba = 10 (type: 1)
|
|||||||
encoding = 0 (type: 5)
|
encoding = 0 (type: 5)
|
||||||
datistemplate = t (type: 1)
|
datistemplate = t (type: 1)
|
||||||
datallowconn = t (type: 1)
|
datallowconn = t (type: 1)
|
||||||
dathasloginevt = f (type: 1)
|
|
||||||
datconnlimit = -1 (type: 5)
|
datconnlimit = -1 (type: 5)
|
||||||
datfrozenxid = 379 (type: 1)
|
datfrozenxid = 379 (type: 1)
|
||||||
dattablespace = 1663 (type: 1)
|
dattablespace = 1663 (type: 1)
|
||||||
@ -4794,7 +4793,6 @@ datdba = 10 (type: 1)
|
|||||||
encoding = 0 (type: 5)
|
encoding = 0 (type: 5)
|
||||||
datistemplate = f (type: 1)
|
datistemplate = f (type: 1)
|
||||||
datallowconn = t (type: 1)
|
datallowconn = t (type: 1)
|
||||||
dathasloginevt = f (type: 1)
|
|
||||||
datconnlimit = -1 (type: 5)
|
datconnlimit = -1 (type: 5)
|
||||||
datfrozenxid = 379 (type: 1)
|
datfrozenxid = 379 (type: 1)
|
||||||
dattablespace = 1663 (type: 1)
|
dattablespace = 1663 (type: 1)
|
||||||
|
@ -28,7 +28,6 @@
|
|||||||
An event trigger fires whenever the event with which it is associated
|
An event trigger fires whenever the event with which it is associated
|
||||||
occurs in the database in which it is defined. Currently, the only
|
occurs in the database in which it is defined. Currently, the only
|
||||||
supported events are
|
supported events are
|
||||||
<literal>login</literal>,
|
|
||||||
<literal>ddl_command_start</literal>,
|
<literal>ddl_command_start</literal>,
|
||||||
<literal>ddl_command_end</literal>,
|
<literal>ddl_command_end</literal>,
|
||||||
<literal>table_rewrite</literal>
|
<literal>table_rewrite</literal>
|
||||||
@ -36,24 +35,6 @@
|
|||||||
Support for additional events may be added in future releases.
|
Support for additional events may be added in future releases.
|
||||||
</para>
|
</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>
|
<para>
|
||||||
The <literal>ddl_command_start</literal> event occurs just before the
|
The <literal>ddl_command_start</literal> event occurs just before the
|
||||||
execution of a <literal>CREATE</literal>, <literal>ALTER</literal>, <literal>DROP</literal>,
|
execution of a <literal>CREATE</literal>, <literal>ALTER</literal>, <literal>DROP</literal>,
|
||||||
@ -1316,81 +1297,6 @@ $$;
|
|||||||
CREATE EVENT TRIGGER no_rewrite_allowed
|
CREATE EVENT TRIGGER no_rewrite_allowed
|
||||||
ON table_rewrite
|
ON table_rewrite
|
||||||
EXECUTE FUNCTION no_rewrite();
|
EXECUTE FUNCTION no_rewrite();
|
||||||
</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>
|
</programlisting>
|
||||||
</para>
|
</para>
|
||||||
</sect1>
|
</sect1>
|
||||||
|
@ -1794,10 +1794,10 @@ ReleaseBulkInsertStatePin(BulkInsertState bistate)
|
|||||||
bistate->current_buf = InvalidBuffer;
|
bistate->current_buf = InvalidBuffer;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Despite the name, we also reset bulk relation extension state.
|
* Despite the name, we also reset bulk relation extension
|
||||||
* Otherwise we can end up erroring out due to looking for free space in
|
* state. Otherwise we can end up erroring out due to looking for free
|
||||||
* ->next_free of one partition, even though ->next_free was set when
|
* space in ->next_free of one partition, even though ->next_free was set
|
||||||
* extending another partition. It could obviously also be bad for
|
* when extending another partition. It's obviously also could be bad for
|
||||||
* efficiency to look at existing blocks at offsets from another
|
* efficiency to look at existing blocks at offsets from another
|
||||||
* partition, even if we don't error out.
|
* 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 void movedb_failure_callback(int code, Datum arg);
|
||||||
static bool get_db_info(const char *name, LOCKMODE lockmode,
|
static bool get_db_info(const char *name, LOCKMODE lockmode,
|
||||||
Oid *dbIdP, Oid *ownerIdP,
|
Oid *dbIdP, Oid *ownerIdP,
|
||||||
int *encodingP, bool *dbIsTemplateP, bool *dbAllowConnP, bool *dbHasLoginEvtP,
|
int *encodingP, bool *dbIsTemplateP, bool *dbAllowConnP,
|
||||||
TransactionId *dbFrozenXidP, MultiXactId *dbMinMultiP,
|
TransactionId *dbFrozenXidP, MultiXactId *dbMinMultiP,
|
||||||
Oid *dbTablespace, char **dbCollate, char **dbCtype, char **dbIculocale,
|
Oid *dbTablespace, char **dbCollate, char **dbCtype, char **dbIculocale,
|
||||||
char **dbIcurules,
|
char **dbIcurules,
|
||||||
@ -680,7 +680,6 @@ createdb(ParseState *pstate, const CreatedbStmt *stmt)
|
|||||||
char src_locprovider = '\0';
|
char src_locprovider = '\0';
|
||||||
char *src_collversion = NULL;
|
char *src_collversion = NULL;
|
||||||
bool src_istemplate;
|
bool src_istemplate;
|
||||||
bool src_hasloginevt;
|
|
||||||
bool src_allowconn;
|
bool src_allowconn;
|
||||||
TransactionId src_frozenxid = InvalidTransactionId;
|
TransactionId src_frozenxid = InvalidTransactionId;
|
||||||
MultiXactId src_minmxid = InvalidMultiXactId;
|
MultiXactId src_minmxid = InvalidMultiXactId;
|
||||||
@ -969,7 +968,7 @@ createdb(ParseState *pstate, const CreatedbStmt *stmt)
|
|||||||
|
|
||||||
if (!get_db_info(dbtemplate, ShareLock,
|
if (!get_db_info(dbtemplate, ShareLock,
|
||||||
&src_dboid, &src_owner, &src_encoding,
|
&src_dboid, &src_owner, &src_encoding,
|
||||||
&src_istemplate, &src_allowconn, &src_hasloginevt,
|
&src_istemplate, &src_allowconn,
|
||||||
&src_frozenxid, &src_minmxid, &src_deftablespace,
|
&src_frozenxid, &src_minmxid, &src_deftablespace,
|
||||||
&src_collate, &src_ctype, &src_iculocale, &src_icurules, &src_locprovider,
|
&src_collate, &src_ctype, &src_iculocale, &src_icurules, &src_locprovider,
|
||||||
&src_collversion))
|
&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_datlocprovider - 1] = CharGetDatum(dblocprovider);
|
||||||
new_record[Anum_pg_database_datistemplate - 1] = BoolGetDatum(dbistemplate);
|
new_record[Anum_pg_database_datistemplate - 1] = BoolGetDatum(dbistemplate);
|
||||||
new_record[Anum_pg_database_datallowconn - 1] = BoolGetDatum(dballowconnections);
|
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_datconnlimit - 1] = Int32GetDatum(dbconnlimit);
|
||||||
new_record[Anum_pg_database_datfrozenxid - 1] = TransactionIdGetDatum(src_frozenxid);
|
new_record[Anum_pg_database_datfrozenxid - 1] = TransactionIdGetDatum(src_frozenxid);
|
||||||
new_record[Anum_pg_database_datminmxid - 1] = TransactionIdGetDatum(src_minmxid);
|
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);
|
pgdbrel = table_open(DatabaseRelationId, RowExclusiveLock);
|
||||||
|
|
||||||
if (!get_db_info(dbname, AccessExclusiveLock, &db_id, NULL, NULL,
|
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)
|
if (!missing_ok)
|
||||||
{
|
{
|
||||||
@ -1819,7 +1817,7 @@ RenameDatabase(const char *oldname, const char *newname)
|
|||||||
*/
|
*/
|
||||||
rel = table_open(DatabaseRelationId, RowExclusiveLock);
|
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))
|
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL))
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_UNDEFINED_DATABASE),
|
(errcode(ERRCODE_UNDEFINED_DATABASE),
|
||||||
@ -1929,7 +1927,7 @@ movedb(const char *dbname, const char *tblspcname)
|
|||||||
*/
|
*/
|
||||||
pgdbrel = table_open(DatabaseRelationId, RowExclusiveLock);
|
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))
|
NULL, NULL, NULL, NULL, &src_tblspcoid, NULL, NULL, NULL, NULL, NULL, NULL))
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_UNDEFINED_DATABASE),
|
(errcode(ERRCODE_UNDEFINED_DATABASE),
|
||||||
@ -2695,7 +2693,7 @@ pg_database_collation_actual_version(PG_FUNCTION_ARGS)
|
|||||||
static bool
|
static bool
|
||||||
get_db_info(const char *name, LOCKMODE lockmode,
|
get_db_info(const char *name, LOCKMODE lockmode,
|
||||||
Oid *dbIdP, Oid *ownerIdP,
|
Oid *dbIdP, Oid *ownerIdP,
|
||||||
int *encodingP, bool *dbIsTemplateP, bool *dbAllowConnP, bool *dbHasLoginEvtP,
|
int *encodingP, bool *dbIsTemplateP, bool *dbAllowConnP,
|
||||||
TransactionId *dbFrozenXidP, MultiXactId *dbMinMultiP,
|
TransactionId *dbFrozenXidP, MultiXactId *dbMinMultiP,
|
||||||
Oid *dbTablespace, char **dbCollate, char **dbCtype, char **dbIculocale,
|
Oid *dbTablespace, char **dbCollate, char **dbCtype, char **dbIculocale,
|
||||||
char **dbIcurules,
|
char **dbIcurules,
|
||||||
@ -2780,9 +2778,6 @@ get_db_info(const char *name, LOCKMODE lockmode,
|
|||||||
/* allowed as template? */
|
/* allowed as template? */
|
||||||
if (dbIsTemplateP)
|
if (dbIsTemplateP)
|
||||||
*dbIsTemplateP = dbform->datistemplate;
|
*dbIsTemplateP = dbform->datistemplate;
|
||||||
/* Has on login event trigger? */
|
|
||||||
if (dbHasLoginEvtP)
|
|
||||||
*dbHasLoginEvtP = dbform->dathasloginevt;
|
|
||||||
/* allowing connections? */
|
/* allowing connections? */
|
||||||
if (dbAllowConnP)
|
if (dbAllowConnP)
|
||||||
*dbAllowConnP = dbform->datallowconn;
|
*dbAllowConnP = dbform->datallowconn;
|
||||||
|
@ -20,7 +20,6 @@
|
|||||||
#include "catalog/dependency.h"
|
#include "catalog/dependency.h"
|
||||||
#include "catalog/indexing.h"
|
#include "catalog/indexing.h"
|
||||||
#include "catalog/objectaccess.h"
|
#include "catalog/objectaccess.h"
|
||||||
#include "catalog/pg_database.h"
|
|
||||||
#include "catalog/pg_event_trigger.h"
|
#include "catalog/pg_event_trigger.h"
|
||||||
#include "catalog/pg_namespace.h"
|
#include "catalog/pg_namespace.h"
|
||||||
#include "catalog/pg_opclass.h"
|
#include "catalog/pg_opclass.h"
|
||||||
@ -38,18 +37,15 @@
|
|||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
#include "parser/parse_func.h"
|
#include "parser/parse_func.h"
|
||||||
#include "pgstat.h"
|
#include "pgstat.h"
|
||||||
#include "storage/lmgr.h"
|
|
||||||
#include "tcop/deparse_utility.h"
|
#include "tcop/deparse_utility.h"
|
||||||
#include "tcop/utility.h"
|
#include "tcop/utility.h"
|
||||||
#include "utils/acl.h"
|
#include "utils/acl.h"
|
||||||
#include "utils/builtins.h"
|
#include "utils/builtins.h"
|
||||||
#include "utils/evtcache.h"
|
#include "utils/evtcache.h"
|
||||||
#include "utils/fmgroids.h"
|
#include "utils/fmgroids.h"
|
||||||
#include "utils/inval.h"
|
|
||||||
#include "utils/lsyscache.h"
|
#include "utils/lsyscache.h"
|
||||||
#include "utils/memutils.h"
|
#include "utils/memutils.h"
|
||||||
#include "utils/rel.h"
|
#include "utils/rel.h"
|
||||||
#include "utils/snapmgr.h"
|
|
||||||
#include "utils/syscache.h"
|
#include "utils/syscache.h"
|
||||||
|
|
||||||
typedef struct EventTriggerQueryState
|
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 void EventTriggerInvoke(List *fn_oid_list, EventTriggerData *trigdata);
|
||||||
static const char *stringify_grant_objtype(ObjectType objtype);
|
static const char *stringify_grant_objtype(ObjectType objtype);
|
||||||
static const char *stringify_adefprivs_objtype(ObjectType objtype);
|
static const char *stringify_adefprivs_objtype(ObjectType objtype);
|
||||||
static void SetDatatabaseHasLoginEventTriggers(void);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Create an event trigger.
|
* Create an event trigger.
|
||||||
@ -138,7 +133,6 @@ CreateEventTrigger(CreateEventTrigStmt *stmt)
|
|||||||
if (strcmp(stmt->eventname, "ddl_command_start") != 0 &&
|
if (strcmp(stmt->eventname, "ddl_command_start") != 0 &&
|
||||||
strcmp(stmt->eventname, "ddl_command_end") != 0 &&
|
strcmp(stmt->eventname, "ddl_command_end") != 0 &&
|
||||||
strcmp(stmt->eventname, "sql_drop") != 0 &&
|
strcmp(stmt->eventname, "sql_drop") != 0 &&
|
||||||
strcmp(stmt->eventname, "login") != 0 &&
|
|
||||||
strcmp(stmt->eventname, "table_rewrite") != 0)
|
strcmp(stmt->eventname, "table_rewrite") != 0)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||||
@ -171,10 +165,6 @@ CreateEventTrigger(CreateEventTrigStmt *stmt)
|
|||||||
else if (strcmp(stmt->eventname, "table_rewrite") == 0
|
else if (strcmp(stmt->eventname, "table_rewrite") == 0
|
||||||
&& tags != NULL)
|
&& tags != NULL)
|
||||||
validate_table_rewrite_tags("tag", tags);
|
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
|
* 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);
|
CatalogTupleInsert(tgrel, tuple);
|
||||||
heap_freetuple(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. */
|
/* Depend on owner. */
|
||||||
recordDependencyOnOwner(EventTriggerRelationId, trigoid, evtOwner);
|
recordDependencyOnOwner(EventTriggerRelationId, trigoid, evtOwner);
|
||||||
|
|
||||||
@ -374,41 +357,6 @@ filter_list_to_array(List *filterlist)
|
|||||||
return PointerGetDatum(construct_array_builtin(data, l, TEXTOID));
|
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
|
* ALTER EVENT TRIGGER foo ENABLE|DISABLE|ENABLE ALWAYS|REPLICA
|
||||||
*/
|
*/
|
||||||
@ -443,14 +391,6 @@ AlterEventTrigger(AlterEventTrigStmt *stmt)
|
|||||||
|
|
||||||
CatalogTupleUpdate(tgrel, &tup->t_self, tup);
|
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,
|
InvokeObjectPostAlterHook(EventTriggerRelationId,
|
||||||
trigoid, 0);
|
trigoid, 0);
|
||||||
|
|
||||||
@ -609,15 +549,6 @@ filter_event_trigger(CommandTag tag, EventTriggerCacheItem *item)
|
|||||||
return true;
|
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
|
* 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
|
* of functions to run; if there are any, trigdata is filled with an
|
||||||
@ -626,7 +557,7 @@ EventTriggerGetTag(Node *parsetree, EventTriggerEvent event)
|
|||||||
static List *
|
static List *
|
||||||
EventTriggerCommonSetup(Node *parsetree,
|
EventTriggerCommonSetup(Node *parsetree,
|
||||||
EventTriggerEvent event, const char *eventstr,
|
EventTriggerEvent event, const char *eventstr,
|
||||||
EventTriggerData *trigdata, bool unfiltered)
|
EventTriggerData *trigdata)
|
||||||
{
|
{
|
||||||
CommandTag tag;
|
CommandTag tag;
|
||||||
List *cachelist;
|
List *cachelist;
|
||||||
@ -651,12 +582,10 @@ EventTriggerCommonSetup(Node *parsetree,
|
|||||||
{
|
{
|
||||||
CommandTag dbgtag;
|
CommandTag dbgtag;
|
||||||
|
|
||||||
dbgtag = EventTriggerGetTag(parsetree, event);
|
dbgtag = CreateCommandTag(parsetree);
|
||||||
|
|
||||||
if (event == EVT_DDLCommandStart ||
|
if (event == EVT_DDLCommandStart ||
|
||||||
event == EVT_DDLCommandEnd ||
|
event == EVT_DDLCommandEnd ||
|
||||||
event == EVT_SQLDrop ||
|
event == EVT_SQLDrop)
|
||||||
event == EVT_Login)
|
|
||||||
{
|
{
|
||||||
if (!command_tag_event_trigger_ok(dbgtag))
|
if (!command_tag_event_trigger_ok(dbgtag))
|
||||||
elog(ERROR, "unexpected command tag \"%s\"", GetCommandTagName(dbgtag));
|
elog(ERROR, "unexpected command tag \"%s\"", GetCommandTagName(dbgtag));
|
||||||
@ -675,7 +604,7 @@ EventTriggerCommonSetup(Node *parsetree,
|
|||||||
return NIL;
|
return NIL;
|
||||||
|
|
||||||
/* Get the command tag. */
|
/* Get the command tag. */
|
||||||
tag = EventTriggerGetTag(parsetree, event);
|
tag = CreateCommandTag(parsetree);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Filter list of event triggers by command tag, and copy them into our
|
* Filter list of event triggers by command tag, and copy them into our
|
||||||
@ -688,7 +617,7 @@ EventTriggerCommonSetup(Node *parsetree,
|
|||||||
{
|
{
|
||||||
EventTriggerCacheItem *item = lfirst(lc);
|
EventTriggerCacheItem *item = lfirst(lc);
|
||||||
|
|
||||||
if (unfiltered || filter_event_trigger(tag, item))
|
if (filter_event_trigger(tag, item))
|
||||||
{
|
{
|
||||||
/* We must plan to fire this trigger. */
|
/* We must plan to fire this trigger. */
|
||||||
runlist = lappend_oid(runlist, item->fnoid);
|
runlist = lappend_oid(runlist, item->fnoid);
|
||||||
@ -741,7 +670,7 @@ EventTriggerDDLCommandStart(Node *parsetree)
|
|||||||
runlist = EventTriggerCommonSetup(parsetree,
|
runlist = EventTriggerCommonSetup(parsetree,
|
||||||
EVT_DDLCommandStart,
|
EVT_DDLCommandStart,
|
||||||
"ddl_command_start",
|
"ddl_command_start",
|
||||||
&trigdata, false);
|
&trigdata);
|
||||||
if (runlist == NIL)
|
if (runlist == NIL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -789,7 +718,7 @@ EventTriggerDDLCommandEnd(Node *parsetree)
|
|||||||
|
|
||||||
runlist = EventTriggerCommonSetup(parsetree,
|
runlist = EventTriggerCommonSetup(parsetree,
|
||||||
EVT_DDLCommandEnd, "ddl_command_end",
|
EVT_DDLCommandEnd, "ddl_command_end",
|
||||||
&trigdata, false);
|
&trigdata);
|
||||||
if (runlist == NIL)
|
if (runlist == NIL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -835,7 +764,7 @@ EventTriggerSQLDrop(Node *parsetree)
|
|||||||
|
|
||||||
runlist = EventTriggerCommonSetup(parsetree,
|
runlist = EventTriggerCommonSetup(parsetree,
|
||||||
EVT_SQLDrop, "sql_drop",
|
EVT_SQLDrop, "sql_drop",
|
||||||
&trigdata, false);
|
&trigdata);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Nothing to do if run list is empty. Note this typically can't happen,
|
* Nothing to do if run list is empty. Note this typically can't happen,
|
||||||
@ -876,97 +805,6 @@ EventTriggerSQLDrop(Node *parsetree)
|
|||||||
list_free(runlist);
|
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.
|
* Fire table_rewrite triggers.
|
||||||
@ -997,7 +835,7 @@ EventTriggerTableRewrite(Node *parsetree, Oid tableOid, int reason)
|
|||||||
runlist = EventTriggerCommonSetup(parsetree,
|
runlist = EventTriggerCommonSetup(parsetree,
|
||||||
EVT_TableRewrite,
|
EVT_TableRewrite,
|
||||||
"table_rewrite",
|
"table_rewrite",
|
||||||
&trigdata, false);
|
&trigdata);
|
||||||
if (runlist == NIL)
|
if (runlist == NIL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -1060,44 +1060,6 @@ LockSharedObject(Oid classid, Oid objid, uint16 objsubid,
|
|||||||
AcceptInvalidationMessages();
|
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
|
* UnlockSharedObject
|
||||||
*/
|
*/
|
||||||
|
@ -36,7 +36,6 @@
|
|||||||
#include "access/xact.h"
|
#include "access/xact.h"
|
||||||
#include "catalog/pg_type.h"
|
#include "catalog/pg_type.h"
|
||||||
#include "commands/async.h"
|
#include "commands/async.h"
|
||||||
#include "commands/event_trigger.h"
|
|
||||||
#include "commands/prepare.h"
|
#include "commands/prepare.h"
|
||||||
#include "common/pg_prng.h"
|
#include "common/pg_prng.h"
|
||||||
#include "jit/jit.h"
|
#include "jit/jit.h"
|
||||||
@ -4290,9 +4289,6 @@ PostgresMain(const char *dbname, const char *username)
|
|||||||
initStringInfo(&row_description_buf);
|
initStringInfo(&row_description_buf);
|
||||||
MemoryContextSwitchTo(TopMemoryContext);
|
MemoryContextSwitchTo(TopMemoryContext);
|
||||||
|
|
||||||
/* Fire any defined login event triggers, if appropriate */
|
|
||||||
EventTriggerOnLogin();
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* POSTGRES main processing loop begins here
|
* 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;
|
event = EVT_SQLDrop;
|
||||||
else if (strcmp(evtevent, "table_rewrite") == 0)
|
else if (strcmp(evtevent, "table_rewrite") == 0)
|
||||||
event = EVT_TableRewrite;
|
event = EVT_TableRewrite;
|
||||||
else if (strcmp(evtevent, "login") == 0)
|
|
||||||
event = EVT_Login;
|
|
||||||
else
|
else
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
@ -90,8 +90,6 @@ Oid MyDatabaseId = InvalidOid;
|
|||||||
|
|
||||||
Oid MyDatabaseTableSpace = InvalidOid;
|
Oid MyDatabaseTableSpace = InvalidOid;
|
||||||
|
|
||||||
bool MyDatabaseHasLoginEventTriggers = false;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* DatabasePath is the path (relative to DataDir) of my database's
|
* DatabasePath is the path (relative to DataDir) of my database's
|
||||||
* primary directory, ie, its directory in the default tablespace.
|
* primary directory, ie, its directory in the default tablespace.
|
||||||
|
@ -1103,7 +1103,6 @@ InitPostgres(const char *in_dbname, Oid dboid,
|
|||||||
}
|
}
|
||||||
|
|
||||||
MyDatabaseTableSpace = datform->dattablespace;
|
MyDatabaseTableSpace = datform->dattablespace;
|
||||||
MyDatabaseHasLoginEventTriggers = datform->dathasloginevt;
|
|
||||||
/* pass the database name back to the caller */
|
/* pass the database name back to the caller */
|
||||||
if (out_dbname)
|
if (out_dbname)
|
||||||
strcpy(out_dbname, dbname);
|
strcpy(out_dbname, dbname);
|
||||||
|
@ -24,7 +24,6 @@
|
|||||||
#include "common/controldata_utils.h"
|
#include "common/controldata_utils.h"
|
||||||
#include "funcapi.h"
|
#include "funcapi.h"
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
#include "storage/lwlock.h"
|
|
||||||
#include "utils/builtins.h"
|
#include "utils/builtins.h"
|
||||||
#include "utils/pg_lsn.h"
|
#include "utils/pg_lsn.h"
|
||||||
#include "utils/timestamp.h"
|
#include "utils/timestamp.h"
|
||||||
@ -43,9 +42,7 @@ pg_control_system(PG_FUNCTION_ARGS)
|
|||||||
elog(ERROR, "return type must be a row type");
|
elog(ERROR, "return type must be a row type");
|
||||||
|
|
||||||
/* read the control file */
|
/* read the control file */
|
||||||
LWLockAcquire(ControlFileLock, LW_SHARED);
|
|
||||||
ControlFile = get_controlfile(DataDir, &crc_ok);
|
ControlFile = get_controlfile(DataDir, &crc_ok);
|
||||||
LWLockRelease(ControlFileLock);
|
|
||||||
if (!crc_ok)
|
if (!crc_ok)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errmsg("calculated CRC checksum does not match value stored in file")));
|
(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");
|
elog(ERROR, "return type must be a row type");
|
||||||
|
|
||||||
/* Read the control file. */
|
/* Read the control file. */
|
||||||
LWLockAcquire(ControlFileLock, LW_SHARED);
|
|
||||||
ControlFile = get_controlfile(DataDir, &crc_ok);
|
ControlFile = get_controlfile(DataDir, &crc_ok);
|
||||||
LWLockRelease(ControlFileLock);
|
|
||||||
if (!crc_ok)
|
if (!crc_ok)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errmsg("calculated CRC checksum does not match value stored in file")));
|
(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");
|
elog(ERROR, "return type must be a row type");
|
||||||
|
|
||||||
/* read the control file */
|
/* read the control file */
|
||||||
LWLockAcquire(ControlFileLock, LW_SHARED);
|
|
||||||
ControlFile = get_controlfile(DataDir, &crc_ok);
|
ControlFile = get_controlfile(DataDir, &crc_ok);
|
||||||
LWLockRelease(ControlFileLock);
|
|
||||||
if (!crc_ok)
|
if (!crc_ok)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errmsg("calculated CRC checksum does not match value stored in file")));
|
(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");
|
elog(ERROR, "return type must be a row type");
|
||||||
|
|
||||||
/* read the control file */
|
/* read the control file */
|
||||||
LWLockAcquire(ControlFileLock, LW_SHARED);
|
|
||||||
ControlFile = get_controlfile(DataDir, &crc_ok);
|
ControlFile = get_controlfile(DataDir, &crc_ok);
|
||||||
LWLockRelease(ControlFileLock);
|
|
||||||
if (!crc_ok)
|
if (!crc_ok)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errmsg("calculated CRC checksum does not match value stored in file")));
|
(errmsg("calculated CRC checksum does not match value stored in file")));
|
||||||
|
@ -3263,11 +3263,6 @@ dumpDatabase(Archive *fout)
|
|||||||
appendPQExpBufferStr(delQry, ";\n");
|
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 */
|
/* Add database-specific SET options */
|
||||||
dumpDatabaseConfig(fout, creaQry, datname, dbCatId.oid);
|
dumpDatabaseConfig(fout, creaQry, datname, dbCatId.oid);
|
||||||
|
|
||||||
|
@ -3552,8 +3552,8 @@ psql_completion(const char *text, int start, int end)
|
|||||||
COMPLETE_WITH("ON");
|
COMPLETE_WITH("ON");
|
||||||
/* Complete CREATE EVENT TRIGGER <name> ON with event_type */
|
/* Complete CREATE EVENT TRIGGER <name> ON with event_type */
|
||||||
else if (Matches("CREATE", "EVENT", "TRIGGER", MatchAny, "ON"))
|
else if (Matches("CREATE", "EVENT", "TRIGGER", MatchAny, "ON"))
|
||||||
COMPLETE_WITH("ddl_command_start", "ddl_command_end", "login",
|
COMPLETE_WITH("ddl_command_start", "ddl_command_end", "sql_drop",
|
||||||
"sql_drop", "table_rewrite");
|
"table_rewrite");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Complete CREATE EVENT TRIGGER <name> ON <event_type>. EXECUTE FUNCTION
|
* Complete CREATE EVENT TRIGGER <name> ON <event_type>. EXECUTE FUNCTION
|
||||||
|
@ -57,6 +57,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/* yyyymmddN */
|
/* yyyymmddN */
|
||||||
#define CATALOG_VERSION_NO 202310161
|
#define CATALOG_VERSION_NO 202310141
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
descr => 'default template for new databases',
|
descr => 'default template for new databases',
|
||||||
datname => 'template1', encoding => 'ENCODING',
|
datname => 'template1', encoding => 'ENCODING',
|
||||||
datlocprovider => 'LOCALE_PROVIDER', datistemplate => 't',
|
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',
|
datminmxid => '1', dattablespace => 'pg_default', datcollate => 'LC_COLLATE',
|
||||||
datctype => 'LC_CTYPE', daticulocale => 'ICU_LOCALE',
|
datctype => 'LC_CTYPE', daticulocale => 'ICU_LOCALE',
|
||||||
daticurules => 'ICU_RULES', datacl => '_null_' },
|
daticurules => 'ICU_RULES', datacl => '_null_' },
|
||||||
|
@ -49,9 +49,6 @@ CATALOG(pg_database,1262,DatabaseRelationId) BKI_SHARED_RELATION BKI_ROWTYPE_OID
|
|||||||
/* new connections allowed? */
|
/* new connections allowed? */
|
||||||
bool datallowconn;
|
bool datallowconn;
|
||||||
|
|
||||||
/* database has login event triggers? */
|
|
||||||
bool dathasloginevt;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Max connections allowed. Negative values have special meaning, see
|
* Max connections allowed. Negative values have special meaning, see
|
||||||
* DATCONNLIMIT_* defines below.
|
* DATCONNLIMIT_* defines below.
|
||||||
|
@ -56,7 +56,6 @@ extern void EventTriggerDDLCommandStart(Node *parsetree);
|
|||||||
extern void EventTriggerDDLCommandEnd(Node *parsetree);
|
extern void EventTriggerDDLCommandEnd(Node *parsetree);
|
||||||
extern void EventTriggerSQLDrop(Node *parsetree);
|
extern void EventTriggerSQLDrop(Node *parsetree);
|
||||||
extern void EventTriggerTableRewrite(Node *parsetree, Oid tableOid, int reason);
|
extern void EventTriggerTableRewrite(Node *parsetree, Oid tableOid, int reason);
|
||||||
extern void EventTriggerOnLogin(void);
|
|
||||||
|
|
||||||
extern bool EventTriggerBeginCompleteQuery(void);
|
extern bool EventTriggerBeginCompleteQuery(void);
|
||||||
extern void EventTriggerEndCompleteQuery(void);
|
extern void EventTriggerEndCompleteQuery(void);
|
||||||
|
@ -203,8 +203,6 @@ extern PGDLLIMPORT Oid MyDatabaseId;
|
|||||||
|
|
||||||
extern PGDLLIMPORT Oid MyDatabaseTableSpace;
|
extern PGDLLIMPORT Oid MyDatabaseTableSpace;
|
||||||
|
|
||||||
extern PGDLLIMPORT bool MyDatabaseHasLoginEventTriggers;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Date/Time Configuration
|
* 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) */
|
/* Lock a shared-across-databases object (other than a relation) */
|
||||||
extern void LockSharedObject(Oid classid, Oid objid, uint16 objsubid,
|
extern void LockSharedObject(Oid classid, Oid objid, uint16 objsubid,
|
||||||
LOCKMODE lockmode);
|
LOCKMODE lockmode);
|
||||||
extern bool ConditionalLockSharedObject(Oid classid, Oid objid, uint16 objsubid,
|
|
||||||
LOCKMODE lockmode);
|
|
||||||
extern void UnlockSharedObject(Oid classid, Oid objid, uint16 objsubid,
|
extern void UnlockSharedObject(Oid classid, Oid objid, uint16 objsubid,
|
||||||
LOCKMODE lockmode);
|
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_LISTEN, "LISTEN", false, false, false)
|
||||||
PG_CMDTAG(CMDTAG_LOAD, "LOAD", false, false, false)
|
PG_CMDTAG(CMDTAG_LOAD, "LOAD", false, false, false)
|
||||||
PG_CMDTAG(CMDTAG_LOCK_TABLE, "LOCK TABLE", 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_MERGE, "MERGE", false, false, true)
|
||||||
PG_CMDTAG(CMDTAG_MOVE, "MOVE", false, false, true)
|
PG_CMDTAG(CMDTAG_MOVE, "MOVE", false, false, true)
|
||||||
PG_CMDTAG(CMDTAG_NOTIFY, "NOTIFY", false, false, false)
|
PG_CMDTAG(CMDTAG_NOTIFY, "NOTIFY", false, false, false)
|
||||||
|
@ -22,8 +22,7 @@ typedef enum
|
|||||||
EVT_DDLCommandStart,
|
EVT_DDLCommandStart,
|
||||||
EVT_DDLCommandEnd,
|
EVT_DDLCommandEnd,
|
||||||
EVT_SQLDrop,
|
EVT_SQLDrop,
|
||||||
EVT_TableRewrite,
|
EVT_TableRewrite
|
||||||
EVT_Login,
|
|
||||||
} EventTriggerEvent;
|
} EventTriggerEvent;
|
||||||
|
|
||||||
typedef struct
|
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',
|
$node_primary->safe_psql('postgres',
|
||||||
"CREATE TABLE tab_int AS SELECT generate_series(1,1002) AS a");
|
"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
|
# Wait for standbys to catch up
|
||||||
$node_primary->wait_for_replay_catchup($node_standby_1);
|
$node_primary->wait_for_replay_catchup($node_standby_1);
|
||||||
$node_standby_1->wait_for_replay_catchup($node_standby_2, $node_primary);
|
$node_standby_1->wait_for_replay_catchup($node_standby_2, $node_primary);
|
||||||
@ -403,13 +384,6 @@ sub replay_check
|
|||||||
|
|
||||||
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";
|
note "enabling hot_standby_feedback";
|
||||||
|
|
||||||
# Enable hs_feedback. The slot should gain an xmin. We set the status interval
|
# 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);
|
CREATE POLICY pguc ON event_trigger_test USING (FALSE);
|
||||||
SET event_triggers = 'off';
|
SET event_triggers = 'off';
|
||||||
DROP POLICY pguc ON event_trigger_test;
|
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);
|
CREATE POLICY pguc ON event_trigger_test USING (FALSE);
|
||||||
SET event_triggers = 'off';
|
SET event_triggers = 'off';
|
||||||
DROP POLICY pguc ON event_trigger_test;
|
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