Thread: audit information

audit information

From
"Keith Worthington"
Date:
Hi All,

I need to add an audit trail to my application.  I thought that I would add
the user id and timestamp to the tables where I need to track 'last modified
by...'.  Currently there is a table tbl_employee that I would like to be able
to cross reference to in order to obtain the user's real name when necessary.

MYDB=# \d peachtree.tbl_employee
           Table "peachtree.tbl_employee"
     Column     |         Type          | Modifiers
----------------+-----------------------+-----------
 id             | character varying(20) | not null
 first_name     | character varying(15) | not null
 middle_initial | character(1)          |
 last_name      | character varying(20) | not null
 inactive       | boolean               | not null
 email          | character varying(64) |
Indexes:
    "tbl_employee_pkey" PRIMARY KEY, btree (id)

Is there a way to obtain the postgres user id or must I store the output of
session_user in the tables?

Are there some existing functions and or tools for implementing audit trails?

What technique do most people use for this type of functionality?

Your input is appreciated.

Kind Regards,
Keith

Re: audit information

From
Michael Fuhr
Date:
On Mon, Apr 25, 2005 at 10:41:09AM -0400, Keith Worthington wrote:
>
> Is there a way to obtain the postgres user id or must I store the output of
> session_user in the tables?

You could grab usesysid from pg_user:

SELECT usesysid FROM pg_user WHERE usename = session_user;

You can convert the user ID back to the name with pg_get_userbyid():

SELECT pg_get_userbyid(1);

--
Michael Fuhr
http://www.fuhr.org/~mfuhr/

Re: audit information

From
"Keith Worthington"
Date:
On Mon, 25 Apr 2005 11:01:51 -0400, Sean Davis wrote
> On Apr 25, 2005, at 10:41 AM, Keith Worthington wrote:
>
> > Hi All,
> >
> > I need to add an audit trail to my application.  I thought that
> > I would add the user id and timestamp to the tables where I need
> > to track 'last modified by...'.  Currently there is a table
> > tbl_employee that I would like to be able to cross reference to
> > in order to obtain the user's real name when  necessary.
> >
> > MYDB=# \d tbl_employee
> >            Table "tbl_employee"
> >      Column     |         Type          | Modifiers
> > ----------------+-----------------------+-----------
> >  id             | character varying(20) | not null
> >  first_name     | character varying(15) | not null
> >  middle_initial | character(1)          |
> >  last_name      | character varying(20) | not null
> >  inactive       | boolean               | not null
> >  email          | character varying(64) |
> > Indexes:
> >     "tbl_employee_pkey" PRIMARY KEY, btree (id)
> >
> > Is there a way to obtain the postgres user id or must I store
> > the output of session_user in the tables?
>
> Not sure on this one.  What is the "id" in your table?  Is it the
> postgres username?
>
> > Are there some existing functions and or tools for
> > implementing audit trails?
>
> Would a trigger on the table that you want to audit do the trick?
>
> Sean

The tbl_employee.id column contains data from the financal software.

Yes, I believe a BEFORE trigger would do the trick nicely.  I can
simply insert the approriate data on the way by.

My question is do I have to use session_user which returns type name
or is there a function that returns the usesysid which is type int4?

Regardless of whether the data I use is int4 or name I need to add a
column to tbl_employee for cross referencing to the employee's real
name.

Kind Regards,
Keith

Re: audit information

From
"Keith Worthington"
Date:
On Mon, 25 Apr 2005 09:35:34 -0600, Michael Fuhr wrote
> On Mon, Apr 25, 2005 at 10:41:09AM -0400, Keith Worthington wrote:
> >
> > Is there a way to obtain the postgres user id or must I store
> > the output of session_user in the tables?
>
> You could grab usesysid from pg_user:
>
> SELECT usesysid FROM pg_user WHERE usename = session_user;
>
> You can convert the user ID back to the name with pg_get_userbyid():
>
> SELECT pg_get_userbyid(1);
>
> --
> Michael Fuhr
> http://www.fuhr.org/~mfuhr/

Hmmm, that is interesting.  I really expected someone to point out a
session_id function of some sort.  For symmetry if no other reason.

I suppose I could write my own function to do that but the code you provided
is simple enough that I am not convinced that there is a real benefit.

In most cases I will want to convert the user ID to the employee name.

Thanks for the post.

Kind Regards,
Keith

Re: audit information

From
Tom Lane
Date:
Michael Fuhr <mike@fuhr.org> writes:
> On Mon, Apr 25, 2005 at 10:41:09AM -0400, Keith Worthington wrote:
>> Is there a way to obtain the postgres user id or must I store the output of
>> session_user in the tables?

> You could grab usesysid from pg_user:
> SELECT usesysid FROM pg_user WHERE usename = session_user;
> You can convert the user ID back to the name with pg_get_userbyid():
> SELECT pg_get_userbyid(1);

My suggestion would be to store the user name as text.  Depending on
numeric user IDs to remain stable across the long haul (dump/reload,
version updates, etc) doesn't seem like a good idea --- it's pretty
much exactly the same mistake as relying on OIDs as primary keys in
user tables.

It's fairly likely that in 8.1 or so we will actually abandon the
notion of "usesysid" per se --- the pg_roles project will probably
use OIDs to identify roles, which will mean you can't forcibly assign
a particular numeric ID to a particular user.  If that happens then
storing numeric IDs *will* break when you reload the data into 8.1.

If you're really desperate for space you could keep your own auxiliary
table to associate user names with integer keys, but I doubt the space
savings would amount to all that much ...

            regards, tom lane

Re: audit information

From
"Keith Worthington"
Date:
On Mon, 25 Apr 2005 11:57:08 -0400, Tom Lane wrote
> Michael Fuhr <mike@fuhr.org> writes:
> > On Mon, Apr 25, 2005 at 10:41:09AM -0400, Keith Worthington wrote:
> >> Is there a way to obtain the postgres user id or must I store
> >> the output of session_user in the tables?
>
> > You could grab usesysid from pg_user:
> > SELECT usesysid FROM pg_user WHERE usename = session_user;
> > You can convert the user ID back to the name with pg_get_userbyid():
> > SELECT pg_get_userbyid(1);
>
> My suggestion would be to store the user name as text.  Depending on
> numeric user IDs to remain stable across the long haul (dump/reload,
> version updates, etc) doesn't seem like a good idea --- it's pretty
> much exactly the same mistake as relying on OIDs as primary keys in
> user tables.
>
> It's fairly likely that in 8.1 or so we will actually abandon the
> notion of "usesysid" per se --- the pg_roles project will probably
> use OIDs to identify roles, which will mean you can't forcibly assign
> a particular numeric ID to a particular user.  If that happens then
> storing numeric IDs *will* break when you reload the data into 8.1.
>
> If you're really desperate for space you could keep your own
> auxiliary table to associate user names with integer keys, but I
> doubt the space savings would amount to all that much ...
>
>             regards, tom lane

Okay.  That works for me.  As you point out the space difference isn't that
much.  On a million record table it would be < 60MB.

Is there a reason not to store the user name as type name?

Do you really mean text or is char(64) more appropriate?

I am assuming there is no reason to use varchar(64).

TIA

Kind Regards,
Keith

Re: audit information

From
Tom Lane
Date:
"Keith Worthington" <keithw@narrowpathinc.com> writes:
> Is there a reason not to store the user name as type name?

I'd use text; name is fixed-width and will waste space on trailing
blanks.  (Same objection to char(64) of course.)

> I am assuming there is no reason to use varchar(64).

Not particularly.

            regards, tom lane

Re: audit information

From
"Keith Worthington"
Date:
On Mon, 25 Apr 2005 11:57:08 -0400, Tom Lane wrote
> Michael Fuhr <mike@fuhr.org> writes:
> > On Mon, Apr 25, 2005 at 10:41:09AM -0400, Keith Worthington wrote:
> >> Is there a way to obtain the postgres user id or must I
> >> store the output of session_user in the tables?
>
> > You could grab usesysid from pg_user:
> > SELECT usesysid FROM pg_user WHERE usename = session_user;
> > You can convert the user ID back to the name with
> > pg_get_userbyid(): SELECT pg_get_userbyid(1);
>
> My suggestion would be to store the user name as text.
> Depending on numeric user IDs to remain stable across the
> long haul (dump/reload, version updates, etc) doesn't seem
> like a good idea --- it's pretty much exactly the same
> mistake as relying on OIDs as primary keys in user tables.
>
> It's fairly likely that in 8.1 or so we will actually
> abandon the notion of "usesysid" per se --- the pg_roles
> project will probably use OIDs to identify roles, which
> will mean you can't forcibly assign a particular numeric
> ID to a particular user.  If that happens then storing
> numeric IDs *will* break when you reload the data into 8.1.
>
> If you're really desperate for space you could keep your
> own auxiliary table to associate user names with integer
> keys, but I doubt the space savings would amount to all
> that much ...
>
>             regards, tom lane

I have created the following function to create the necessary data for my
audit trail.  Unfortunately I do not know how I can test it without putting
the whole modification in place. (This is a small part of a much bigger project.)

Can anyone suggest how to test this functionality without implementing it as a
trigger on a table?

Can this function be implemented as STABLE STRICT?

CREATE OR REPLACE FUNCTION interface.tf_audit_data()
  RETURNS "trigger" AS
$BODY$
   BEGIN
--    Set the user name.
      IF NEW.user_name IS NULL THEN
         NEW.user_name := SELECT * FROM session_user;
      END IF;
--    Set the timestamp.
      IF NEW.review_timestamp IS NULL THEN
         NEW.review_timestamp := ('now'::text)::timestamp(6) with time zone;
      END IF;
      RETURN NEW;
   END;
$BODY$
  LANGUAGE 'plpgsql' VOLATILE;

Kind Regards,
Keith

Re: audit information

From
Tom Lane
Date:
"Keith Worthington" <keithw@narrowpathinc.com> writes:
> I have created the following function to create the necessary data for my
> audit trail.  Unfortunately I do not know how I can test it without putting
> the whole modification in place. (This is a small part of a much bigger project.)

> Can anyone suggest how to test this functionality without implementing it as a
> trigger on a table?

You can't.  But you could make a test table and install the function as
a trigger on that.

> Can this function be implemented as STABLE STRICT?

Nope.  STRICTness is meaningless anyway for a trigger, since it has no
explicit arguments to be strict about.  And triggers are always volatile
since the entire point is to have side-effects.  In the current code I
am not sure whether it matters if you try to mark a trigger function
stable ... but if the system were actually to enforce the STABLE
restrictions against your function, you'd not like the result.  Some day
it probably will enforce them...

            regards, tom lane