File: table_log.md

package info (click to toggle)
tablelog 0.6.4-4
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 244 kB
  • sloc: ansic: 1,284; sql: 708; makefile: 12; sh: 2
file content (526 lines) | stat: -rw-r--r-- 19,077 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
table_log for PostgreSQL
========================

Table of contents:

1. Info
2. License
3. Installation
4. Documentation
   4.1. Manual table log and trigger creation
   4.2. Restore table data
5. Hints
   5.1. Security tips
6. Bugs
7. Todo
8. Changes
9. Contact



# 1. Info

table_log is a set of functions to log changes on a table in PostgreSQL
and to restore the state of the table or a specific row on any time
in the past.

For now it contains 2 functions:

* `table_log()` -- log changes to another table
* `table_log_restore_table()` -- restore a table or a specific column

NOTE: you can only restore a table where the original table and the
logging table has a primary key!

This means: you can log everything, but for the restore function you must
have a primary key on the original table (and of course a different pkey
on the log table).

In the beginning (for table_log()) i have used some code from noup.c
(you will find this in the contrib directory), but there should be
no code left from noup, since i rewrote everything during the development.
In fact, it makes no difference since both software is licensed with
the BSD style licence which is used by PostgreSQL.



# 2. License

Copyright (c) 2002-2007  Andreas Scherbaum

Basically it's the same as the PostgreSQL license (BSD license).

Permission to use, copy, modify, and distribute this software and
its documentation for any purpose and without fee is hereby
granted, provided that the above copyright notice appear in all
copies and that both that copyright notice and this permission
notice appear in supporting documentation, and that the name of the
author not be used in advertising or publicity pertaining to
distribution of the software without specific, written prior
permission. The author makes no representations about the
suitability of this software for any purpose. It is provided "as
is" without express or implied warranty.



# 3. Installation

Build table_log:

```
make USE_PGXS=1
(and as root)
make USE_PGXS=1 install
```

If you are using a version >= 9.1 of PostgreSQL, you can use the new
regression tests to verify the working state of the module:

```
USE_PGXS=1 make installcheck
```

Since the regression checks require the extension infrastructure, this won't work
on version below 9.1.

## 3.1 Pre-9.1 installation procedure

The pg_config tool must be in your $PATH for installation and
you must have the PostgreSQL development packages installed.
Of course, the usual development tools like make, gcc ect. should
also be there ;-)

After this you have to create some new functions:
- in every database you want to use this functions
- if you add this functions to template1, they will be copied to every
  new database
- for older pg versions <= 7.2 change "RETURNS trigger" to "RETURNS opaque",
  but versions < 7.4 are no longer supported

```
CREATE FUNCTION "table_log_basic" ()
    RETURNS trigger
    AS '$libdir/table_log', 'table_log' LANGUAGE 'C';
CREATE FUNCTION "table_log" ()
    RETURNS trigger
    AS '$libdir/table_log', 'table_log' LANGUAGE 'C';
CREATE FUNCTION "table_log_restore_table" (VARCHAR, VARCHAR, CHAR, CHAR, CHAR, TIMESTAMPTZ, CHAR, INT, INT)
    RETURNS VARCHAR
    AS '$libdir/table_log', 'table_log_restore_table' LANGUAGE 'C';
CREATE FUNCTION "table_log_restore_table" (VARCHAR, VARCHAR, CHAR, CHAR, CHAR, TIMESTAMPTZ, CHAR, INT)
    RETURNS VARCHAR
    AS '$libdir/table_log', 'table_log_restore_table' LANGUAGE 'C';
CREATE FUNCTION "table_log_restore_table" (VARCHAR, VARCHAR, CHAR, CHAR, CHAR, TIMESTAMPTZ, CHAR)
    RETURNS VARCHAR
    AS '$libdir/table_log', 'table_log_restore_table' LANGUAGE 'C';
CREATE FUNCTION "table_log_restore_table" (VARCHAR, VARCHAR, CHAR, CHAR, CHAR, TIMESTAMPTZ)
    RETURNS VARCHAR
    AS '$libdir/table_log', 'table_log_restore_table' LANGUAGE 'C';
```

There's also a script available in PATH_TO_YOUR_PGSQL/share/contrib, which contains all
required function definitions plus some regression tests, for example:

```
psql -f PATH_TO_YOUR_PGSQL/share/contrib/table_log.sql
```

Install table_log_init() by running `psql < table_log_init.sql`.
This function does all the work which is necessary for creating a
logging table.

NOTE:

Currently the Makefile doesn't distinguish between extension and contrib
installation procedures and will install table_log--x.x.sql and control files
in pre-9.1 versions nevertheless.
This might get solved in the future, but don't use those scripts in pre-9.1
installations.

## 3.2 Installation procedure with versions >= 9.1

Starting with PostgreSQL 9.1 you should use the new extension
infrastructure. table_log supports this starting with version 0.5. After
compiling and installing the module, all you need to do is to connect
to the target database and issue the following command:

```
CREATE EXTENSION table_log;
```

This will install all functions including table_log_init() into the
public schema.

NOTE: The old contrib scripts are going to be installed, still. We currently don't have
distinguished the installation procedure version-wise within the Makefile, so don't
get confused if you still find the old table_log.sql scripts in share/extension
directories.

## 3.3 Upgrading from earlier installations to the new extension infrastructure

If you are upgrading from an earlier pre-9.1 installation, and you want to use
the new extension infrastructure, you could use the unpackaged creation
procedure to migrate an existing table_log installation within a database into
an extension. To accomplish this, you need to execute the following command
in the new 9.1+ database (of course, after installing the new version of table_log):

```
CREATE EXTENSION table_log FROM unpackaged;
```

# 4. Documentation

The entire log table and trigger creation can be done using the
table_log_init(ncols, ...) function. The parameter ncols decides how many
extra columns will be added to the created log table, it can be 3, 4 or 5.
The extra columns are described in chapter 4.1.

The function can be used with the following parameters:

  table_log_init(ncols, tablename):
    create the log table as tablename_log
    
  table_log_init(ncols, tablename, logschema):
    create the log table with the same name as the original table, but in
    the schema logschema.

  table_log_init(ncols, tableschema, tablename, logschema, logname):
    log the changes in table tableschema.tablename into the log table
    logschema.logname.

  table_log_init(ncols, tableschema, tablename, logschema, logname, partition_mode, basic_mode, log_actions):
    log the changes in table tableschema.tablename into the log table
    logschema.logname. The parameter partition_mode can be SINGLE or PARTITION, which
    creates two log tables *_0 and *_1 which can be switched by setting
    table_log.active_partition to the corresponding partition id 1 or 2.
    basic_mode defines wether we use full logging mode or basic mode. When set to TRUE,
    table_log_basic() will be used internally which suppresses logging of NEW values
    by UPDATE actions.
    log_actions is a TEXT[] array which specifies a list of either INSERT, DELETE or UPDATE
    or any combinations from them to tell when a table_log trigger should be fired.

    NOTE:

    When using basic_mode and/or log_actions without all actions, you won't be
    able to use table_log_restore_table() anymore.

    When calling table_log_init(), you can omit logname in any case. The function
    will then generate a log tablename from the given source tablename and a
    `_log` string appended.

## 4.1. Manual table log and trigger creation

Create an trigger on a table with log table name as argument.
If no table name is given, the actual table name
plus `_log` will be used by table_log.

Example:

```
CREATE TRIGGER test_log_chg AFTER UPDATE OR INSERT OR DELETE ON test_table FOR EACH ROW
               EXECUTE PROCEDURE table_log();
^^^^^ 'test_table_log' will be used to log changes

CREATE TRIGGER test_log_chg AFTER UPDATE OR INSERT OR DELETE ON test_table FOR EACH ROW
               EXECUTE PROCEDURE table_log('log_table');
^^^^^ 'log_table' will be used to log changes
```


The log table needs exact the same columns as the original table
(but without any constraints)
plus three, four or five extra columns:

```
trigger_mode VARCHAR(10)
trigger_tuple VARCHAR(5)
trigger_changed TIMESTAMPTZ
trigger_user VARCHAR(32)     -- optional

trigger_mode contains 'INSERT', 'UPDATE' or 'DELETE'
trigger_tuple contains 'old' or 'new'
trigger_changed is the actual timestamp inside the trancaction
   (or maybe i should use here the actual system timestamp?)
trigger_user contains the session user name (the one who connected to the
   database, this must not be the actual one)
```

On INSERT, a log entry with the 'new' tuple will be written.
On DELETE, a log entry with the 'old' tuple will be written.
On UPDATE, a log entry with the old tuple and a log entry with
the new tuple will be written.

A fourth column is possible on the log table:
trigger_id BIGINT
contains an unique id for sorting table log entries

NOTE: for the restore function you must have this 4. column!

  Q: Why do i need this column?
  A: Because we have to sort the logs to get them back in correct order.
     Hint: if you are sure, you don't have an OID wrapover yet, you can
           use the OID column as unique id (but if you have an OID wrapover
           later, the new OIDs doesn't follow a linear scheme,
           see VACUUM documentation)

A fifth column is possible on the log table:
trigger_user VARCHAR(32)
contains the username from the user who originally opened the database connection

  Q: Why not the actual user?
  A: If someone changed the actual user with a setuid function,
     you always know the original username

  Q: Why 32 bytes?
  A: The function doesn't uses 32 bytes but instead NAMEDATALEN defined
     at compile time (which defaults to 32 bytes)

  Q: May i skip the log table name?
  A: No, because Pg then thinks, the '1' is the first parameter and will fail
     to use '1' as logging table
     This is an backwards compatibility issue, sorry for this.


For backward compatibility table_log() works with 3, 4 or 5 extra
columns, but you should use the 4 or 5 column version everytimes.

A good method to create the log table is to use the existing table:

```
-- create the table without data
SELECT * INTO test_log FROM test LIMIT 0;
ALTER TABLE test_log ADD COLUMN trigger_mode VARCHAR(10);
ALTER TABLE test_log ADD COLUMN trigger_tuple VARCHAR(5);
ALTER TABLE test_log ADD COLUMN trigger_changed TIMESTAMPTZ;
-- now activate the history function
CREATE TRIGGER test_log_chg AFTER UPDATE OR INSERT OR DELETE ON test FOR EACH ROW
               EXECUTE PROCEDURE table_log();

-- or the 4 column method:
SELECT * INTO test_log FROM test LIMIT 0;
ALTER TABLE test_log ADD COLUMN trigger_mode VARCHAR(10);
ALTER TABLE test_log ADD COLUMN trigger_tuple VARCHAR(5);
ALTER TABLE test_log ADD COLUMN trigger_changed TIMESTAMPTZ;
ALTER TABLE test_log ADD COLUMN trigger_id BIGINT;
CREATE SEQUENCE test_log_id;
SELECT SETVAL('test_log_id', 1, FALSE);
ALTER TABLE test_log ALTER COLUMN trigger_id SET DEFAULT NEXTVAL('test_log_id');
-- now activate the history function
CREATE TRIGGER test_log_chg AFTER UPDATE OR INSERT OR DELETE ON test FOR EACH ROW
               EXECUTE PROCEDURE table_log();

-- or the 5 column method (with user name in 'trigger_user'):
SELECT * INTO test_log FROM test LIMIT 0;
ALTER TABLE test_log ADD COLUMN trigger_mode VARCHAR(10);
ALTER TABLE test_log ADD COLUMN trigger_tuple VARCHAR(5);
ALTER TABLE test_log ADD COLUMN trigger_changed TIMESTAMPTZ;
ALTER TABLE test_log ADD COLUMN trigger_user VARCHAR(32);
ALTER TABLE test_log ADD COLUMN trigger_id BIGINT;
CREATE SEQUENCE test_log_id;
SELECT SETVAL('test_log_id', 1, FALSE);
ALTER TABLE test_log ALTER COLUMN trigger_id SET DEFAULT NEXTVAL('test_log_id');
-- now activate the history function
-- you have to give the log table name!
CREATE TRIGGER test_log_chg AFTER UPDATE OR INSERT OR DELETE ON test FOR EACH ROW
               EXECUTE PROCEDURE table_log('test_log', 1);
```

See table_log.sql for a demo



## 4.2. Restore table data

Now insert, update and delete some data in table 'test'.
After this, you may want to restore your table data:

```
SELECT table_log_restore_table(<original table name>,
                               <original table primary key>,
                               <log table name>,
                               <log table primary key>,
                               <restore table name>,
                               <timestamp>,
                               <primary key to restore>,
                               <restore method: 0/1>,
                               <dont create temporary table: 0/1>);
```

The parameter list means:

- original table name: string
  The name of the original table (test in your example above)
- original table primary key: string
  The primary key name of the original table
- log table name: string
  The name of the logging table
- log table primary key: string
  The primary key of the logging table (trigger_id in your example above)
  Note: this cannot be the same as the original table pkey!
- restore table name: string
  The name for the restore table
  Note: this table must not exist!
  Also see <dont create temporary table>
- timestamp: timestamp
  The timestamp in past
  Note: if you give a timestamp where no logging data exists,
        absolutly nothing will happen. But see <restore method>
- primary key to restore: string (or NULL)
  If you want to restore only a single primary key, name it here.
  Then only data for this pkey will be searched and restored
  Note: this parameter is optional and defaults to NULL (restore all pkeys)
        you can say NULL here, if you want to skip this parameter
- restore method: 0/1 (or NULL)
  0 means: first create the restore table and then restore forward from the
           beginning of the log table
  1 means: first create the log table and copy the actual content of the
           original table into the log table, then restore backwards
  Note: this can speed up things, if you know, that your timestamp point
        is near the end or the beginning
  Note: this parameter is optional and defaults to NULL (= 0)
- dont create temporary table: 0/1 (or NULL)
  Normal the restore table will be created temporarly, this means, the table
  is only available inside your session and will be deleted, if your
  session (session means connection, not transaction) is closed
  This parameter allows you to create a normal table instead
  Note: if you want to use the restore function sometimes inside a session
        and you want to use the same restore table name again, you have to
        drop the restore table or the restore function will blame you
  Note: this parameter is optional and defaults to NULL (= 0)



# 5. Hints

- an index on the log table primary key (trigger_id) and the trigger_changed
  column will speed up things
- You can find another nice explanation in my blog:
  http://ads.wars-nicht.de/blog/archives/100-Log-Table-Changes-in-PostgreSQL-with-tablelog.html



## 5.1. Security Tips

- You can create the table_log functions with 'SECURITY DEFINER' privileges
  and revoke the permissions for the logging table for any normal user.
  This allows you to have an audit which cannot be modified by users who
  are accessing the data table.
- Existing functions can be modified with:
  ALTER FUNCTION table_log() SECURITY DEFINER;



# 6. Bugs

- nothing known, but tell me, if you find one



# 7. Todo

- table_log_show_column()
  allows select of previous state (possible with PostgreSQL 7.3 and higher)
    see Table Function API
- is it binary safe? (\000)
- do not only check the number columns in both tables,
  really check the names of the columns



# 8. Changes:

- 2002-04-06: Andreas 'ads' Scherbaum (ads@ufp.de)
  first release

- 2002-04-25: Steve Head (smhf@onthe.net.au)
  there was a bug with NULL values, thanks to
  Steve Head for reporting this.

- 2002-04-25: Andreas 'ads' Scherbaum (ads@ufp.de)
  now using version numbers (0.0.5 for this release)

- 2002-09-09: Andreas 'ads' Scherbaum (ads@ufp.de)
  fix bug in calculating log table name
  release 0.0.6

- 2003-03-22: Andreas 'ads' Scherbaum (ads@ufp.de)
  fix some error messages (old name from 'noup' renamed to 'table_log')
  one additional check that the trigger is fired after
  release 0.0.7

- 2003-03-23: Andreas 'ads' Scherbaum (ads@ufp.de)
  create a second Makefile for installing from source
  release 0.1.0

- 2003-04-20: Andreas 'ads' Scherbaum (ads@ufp.de)
  change Makefile.nocontrib to Linux only and make a comment about
  installation informations for other platforms
  (its too difficult to have all install options here,
   i dont have the ability to test all platforms)

- 2003-06-12: Andreas 'ads' Scherbaum (ads@ufp.de)
  update documentation (thanks to Erik Thiele <erik@thiele-hydraulik.de>
  for pointing this out)

- 2003-06-13: Andreas 'ads' Scherbaum (ads@ufp.de)
  - release 0.2.0
  now allow 3 or 4 columns
  update documentation about trigger_id column

- 2003-06-13: Andreas 'ads' Scherbaum (ads@ufp.de)
  - release 0.2.1
  add debugging (activate TABLE_LOG_DEBUG in head of table_log.c and recompile)

- 2003-11-27: Andreas 'ads' Scherbaum (ads@ufp.de)
  - release 0.3.0
  add function for restoring table from log
  cleanup source
  add more debugging

- 2003-12-11: Andreas 'ads' Scherbaum (ads@ufp.de)
  - release 0.3.1
  add session_user to log table on request
  thanks to iago@patela.org.uk for the feature request
  fix a minor bug in returning the table name

- 2005-01-14: Andreas 'ads' Scherbaum (ads@wars-nicht.de)
  - release 0.4.0
  ignore dropped columns on tables (this may cause errors, if you
  restore or use older backups)
  change email address, the old one does no longer work
  
- 2005-01-24: Andreas 'ads' Scherbaum (ads@wars-nicht.de)
  - release 0.4.1
  there seems to be an problem with session_user
  
- 2005-04-22: Kim Hansen <kimhanse@gmail.com>
  - release 0.4.2
  added table_log_init()
  added schema support

- 2006-08-30: Andreas 'ads' Scherbaum (ads@wars-nicht.de)
  - release 0.4.3
  drop support for < 7.4 (return type is TRIGGER now)
  fix bug with dropped columns

- 2007-05-18: Andreas 'ads' Scherbaum (ads@pgug.de)
  - release 0.4.4
  compatibility issues with 8.2.x
  some small fixes in table_log.sql.in
  remove some warnings
  docu cleanups
  Thanks to Michael Graziano <mgraziano@invision.net> for pointing
    out the 8.2 fix
  Thanks to Alexander Wirt <formorer@formorer.de> for Debian packaging
  Thanks to Devrim GÜNDÜZ <devrim@CommandPrompt.com> for RPM packaging


# 9. Contact

The project is hosted at http://pgfoundry.org/projects/tablelog/

If you have any hints, changes or improvements, please contact me.

my gpg key:
pub  1024D/4813B5FE 2000-09-29 Andreas Scherbaum <ads@wars-nicht.de>
     Key fingerprint = 9F67 73D3 43AA B30E CA8F  56E5 3002 8D24 4813 B5FE