pg_audit_log 0.4.0 → 0.4.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +1 -0
- data/.rvmrc +1 -1
- data/README.rdoc +3 -1
- data/lib/pg_audit_log/entry.rb +5 -1
- data/lib/pg_audit_log/extensions/shared/postgresql_adapter.rb +5 -1
- data/lib/pg_audit_log/function.rb +26 -27
- data/lib/pg_audit_log/triggers.rb +9 -1
- data/lib/pg_audit_log/version.rb +1 -1
- data/pg_audit_log.gemspec +1 -1
- data/spec/pg_audit_log_spec.rb +13 -0
- data/spec/triggers_spec.rb +17 -0
- metadata +17 -17
data/.gitignore
CHANGED
data/.rvmrc
CHANGED
@@ -1 +1 @@
|
|
1
|
-
rvm 1.9.
|
1
|
+
rvm 1.9.3-p0@pg_audit_log --create
|
data/README.rdoc
CHANGED
@@ -9,7 +9,9 @@ Comes with specs for your project and a rake task to generate the reverse SQL to
|
|
9
9
|
|
10
10
|
All SQL INSERTs, UPDATEs, and DELETEs will be captured. Record columns that do not change do not generate an audit log entry.
|
11
11
|
|
12
|
-
Compatible with Rails 3.0.x and 3.
|
12
|
+
Compatible with Rails 3.0.x, 3.1.x and 3.2.x
|
13
|
+
|
14
|
+
On a 2.93GHz i7 with postgresql 9.1 the audit log has an overhead of about 0.0035 seconds to each INSERT, UPDATE or DELETE
|
13
15
|
|
14
16
|
== INSTALL
|
15
17
|
|
data/lib/pg_audit_log/entry.rb
CHANGED
@@ -1,6 +1,10 @@
|
|
1
1
|
class PgAuditLog::Entry < ActiveRecord::Base
|
2
2
|
TABLE_NAME = "audit_log"
|
3
|
-
|
3
|
+
if ::ActiveRecord::VERSION::MAJOR == 3 && ::ActiveRecord::VERSION::MINOR >= 2
|
4
|
+
self.table_name = TABLE_NAME
|
5
|
+
else
|
6
|
+
set_table_name TABLE_NAME
|
7
|
+
end
|
4
8
|
|
5
9
|
class CannotDeleteError < StandardError
|
6
10
|
def message
|
@@ -30,7 +30,11 @@ class ActiveRecord::ConnectionAdapters::PostgreSQLAdapter
|
|
30
30
|
if PgAuditLog::Triggers.tables_with_triggers.include?(table_name)
|
31
31
|
PgAuditLog::Triggers.drop_for_table(table_name)
|
32
32
|
end
|
33
|
-
|
33
|
+
if ::ActiveRecord::VERSION::MAJOR == 3 && ::ActiveRecord::VERSION::MINOR >= 2
|
34
|
+
drop_table_without_auditing(table_name)
|
35
|
+
else
|
36
|
+
drop_table_without_auditing(table_name, options)
|
37
|
+
end
|
34
38
|
end
|
35
39
|
alias_method_chain :drop_table, :auditing
|
36
40
|
|
@@ -61,40 +61,39 @@ module PgAuditLog
|
|
61
61
|
unique_name varchar;
|
62
62
|
column_name varchar;
|
63
63
|
BEGIN
|
64
|
+
primary_key_column := NULL;
|
65
|
+
EXECUTE 'SELECT pg_attribute.attname
|
66
|
+
FROM pg_index, pg_class, pg_attribute
|
67
|
+
WHERE pg_class.oid = $1::regclass
|
68
|
+
AND indrelid = pg_class.oid
|
69
|
+
AND pg_attribute.attrelid = pg_class.oid
|
70
|
+
AND pg_attribute.attnum = any(pg_index.indkey)
|
71
|
+
AND indisprimary'
|
72
|
+
INTO primary_key_column USING TG_RELNAME;
|
73
|
+
user_identifier := pg_temp.pg_audit_log_user_identifier();
|
74
|
+
unique_name := pg_temp.pg_audit_log_user_unique_name();
|
75
|
+
primary_key_value := NULL;
|
76
|
+
|
64
77
|
FOR col IN SELECT * FROM information_schema.columns WHERE table_name = TG_RELNAME LOOP
|
65
78
|
new_value := NULL;
|
66
79
|
old_value := NULL;
|
67
|
-
primary_key_column := NULL;
|
68
|
-
primary_key_value := NULL;
|
69
|
-
user_identifier := pg_temp.pg_audit_log_user_identifier();
|
70
|
-
unique_name := pg_temp.pg_audit_log_user_unique_name();
|
71
80
|
column_name := col.column_name;
|
72
|
-
|
73
|
-
EXECUTE 'SELECT pg_attribute.attname
|
74
|
-
FROM pg_index, pg_class, pg_attribute
|
75
|
-
WHERE pg_class.oid = $1::regclass
|
76
|
-
AND indrelid = pg_class.oid
|
77
|
-
AND pg_attribute.attrelid = pg_class.oid
|
78
|
-
AND pg_attribute.attnum = any(pg_index.indkey)
|
79
|
-
AND indisprimary'
|
80
|
-
INTO primary_key_column USING TG_RELNAME;
|
81
|
-
|
82
|
-
IF TG_OP = 'INSERT' OR TG_OP = 'UPDATE' THEN
|
83
|
-
EXECUTE 'SELECT CAST($1 . '|| column_name ||' AS TEXT)' INTO new_value USING NEW;
|
84
|
-
IF primary_key_column IS NOT NULL THEN
|
85
|
-
EXECUTE 'SELECT CAST($1 . '|| primary_key_column ||' AS VARCHAR)' INTO primary_key_value USING NEW;
|
86
|
-
END IF;
|
87
|
-
END IF;
|
88
|
-
IF TG_OP = 'DELETE' OR TG_OP = 'UPDATE' THEN
|
89
|
-
EXECUTE 'SELECT CAST($1 . '|| column_name ||' AS TEXT)' INTO old_value USING OLD;
|
90
|
-
IF primary_key_column IS NOT NULL THEN
|
91
|
-
EXECUTE 'SELECT CAST($1 . '|| primary_key_column ||' AS VARCHAR)' INTO primary_key_value USING OLD;
|
92
|
-
END IF;
|
93
|
-
END IF;
|
94
|
-
|
95
81
|
IF TG_RELNAME = '#{users_table_name}' AND column_name = '#{users_access_column}' THEN
|
96
82
|
NULL;
|
97
83
|
ELSE
|
84
|
+
IF TG_OP = 'INSERT' OR TG_OP = 'UPDATE' THEN
|
85
|
+
EXECUTE 'SELECT CAST($1 . '|| column_name ||' AS TEXT)' INTO new_value USING NEW;
|
86
|
+
IF primary_key_value IS NULL AND primary_key_column IS NOT NULL THEN
|
87
|
+
EXECUTE 'SELECT CAST($1 . '|| primary_key_column ||' AS VARCHAR)' INTO primary_key_value USING NEW;
|
88
|
+
END IF;
|
89
|
+
END IF;
|
90
|
+
IF TG_OP = 'DELETE' OR TG_OP = 'UPDATE' THEN
|
91
|
+
EXECUTE 'SELECT CAST($1 . '|| column_name ||' AS TEXT)' INTO old_value USING OLD;
|
92
|
+
IF primary_key_value IS NULL AND primary_key_column IS NOT NULL THEN
|
93
|
+
EXECUTE 'SELECT CAST($1 . '|| primary_key_column ||' AS VARCHAR)' INTO primary_key_value USING OLD;
|
94
|
+
END IF;
|
95
|
+
END IF;
|
96
|
+
|
98
97
|
IF TG_OP != 'UPDATE' OR new_value != old_value OR (TG_OP = 'UPDATE' AND ( (new_value IS NULL AND old_value IS NOT NULL) OR (new_value IS NOT NULL AND old_value IS NULL))) THEN
|
99
98
|
INSERT INTO audit_log("operation",
|
100
99
|
"table_name",
|
@@ -11,7 +11,11 @@ module PgAuditLog
|
|
11
11
|
|
12
12
|
class << self
|
13
13
|
def tables
|
14
|
-
|
14
|
+
skip_tables = (PgAuditLog::IGNORED_TABLES + [PgAuditLog::Entry.table_name])
|
15
|
+
connection.tables.reject do |table|
|
16
|
+
skip_tables.include?(table) ||
|
17
|
+
skip_tables.any? { |skip_table| skip_table =~ table if skip_table.is_a? Regexp }
|
18
|
+
end
|
15
19
|
end
|
16
20
|
|
17
21
|
def tables_with_triggers
|
@@ -28,6 +32,10 @@ module PgAuditLog
|
|
28
32
|
tables - tables_with_triggers
|
29
33
|
end
|
30
34
|
|
35
|
+
def all_tables_without_triggers
|
36
|
+
connection.tables - tables_with_triggers
|
37
|
+
end
|
38
|
+
|
31
39
|
def install
|
32
40
|
tables.each do |table|
|
33
41
|
create_for_table(table) unless tables_with_triggers.include?(table)
|
data/lib/pg_audit_log/version.rb
CHANGED
data/pg_audit_log.gemspec
CHANGED
@@ -19,7 +19,7 @@ Gem::Specification.new do |s|
|
|
19
19
|
|
20
20
|
s.add_dependency("rails", ">= 3.0.0")
|
21
21
|
s.add_dependency("pg", ">= 0.9.0")
|
22
|
-
s.add_development_dependency('rspec-rails')
|
22
|
+
s.add_development_dependency('rspec-rails', "= 2.7")
|
23
23
|
s.add_development_dependency('with_model', '>= 0.1.3')
|
24
24
|
s.add_development_dependency('autotest')
|
25
25
|
end
|
data/spec/pg_audit_log_spec.rb
CHANGED
@@ -222,6 +222,19 @@ describe PgAuditLog do
|
|
222
222
|
its(:primary_key) { should be_nil }
|
223
223
|
end
|
224
224
|
end
|
225
|
+
|
226
|
+
describe "performance" do
|
227
|
+
xit "should perform well" do
|
228
|
+
require "benchmark"
|
229
|
+
results = Benchmark.measure do
|
230
|
+
1000.times do
|
231
|
+
AuditedModel.create!(attributes)
|
232
|
+
end
|
233
|
+
end
|
234
|
+
puts results.real
|
235
|
+
puts results.real / 1000.0
|
236
|
+
end
|
237
|
+
end
|
225
238
|
end
|
226
239
|
|
227
240
|
describe "during migrations" do
|
data/spec/triggers_spec.rb
CHANGED
@@ -17,6 +17,23 @@ describe PgAuditLog::Triggers do
|
|
17
17
|
PgAuditLog::Triggers.drop_for_table(TableWithoutTriggers.table_name) rescue nil
|
18
18
|
end
|
19
19
|
|
20
|
+
describe ".tables" do
|
21
|
+
subject { PgAuditLog::Triggers.tables }
|
22
|
+
|
23
|
+
with_model :table_to_ignore do
|
24
|
+
table {}
|
25
|
+
end
|
26
|
+
|
27
|
+
before do
|
28
|
+
PgAuditLog::IGNORED_TABLES << /ignore/
|
29
|
+
end
|
30
|
+
|
31
|
+
it { should include(TableWithTriggers.table_name) }
|
32
|
+
it { should include(TableWithoutTriggers.table_name) }
|
33
|
+
it { should_not include(PgAuditLog::Entry.table_name) }
|
34
|
+
it { should_not include(TableToIgnore.table_name) }
|
35
|
+
end
|
36
|
+
|
20
37
|
describe ".tables_with_triggers" do
|
21
38
|
it "should return an array of all tables that do have an audit trigger installed" do
|
22
39
|
PgAuditLog::Triggers.tables_with_triggers.should include(TableWithTriggers.table_name)
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pg_audit_log
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.4
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2012-02-09 00:00:00.000000000Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rails
|
16
|
-
requirement: &
|
16
|
+
requirement: &70094371058580 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: 3.0.0
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *70094371058580
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: pg
|
27
|
-
requirement: &
|
27
|
+
requirement: &70094371056820 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ! '>='
|
@@ -32,21 +32,21 @@ dependencies:
|
|
32
32
|
version: 0.9.0
|
33
33
|
type: :runtime
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *70094371056820
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: rspec-rails
|
38
|
-
requirement: &
|
38
|
+
requirement: &70094371056040 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
|
-
- -
|
41
|
+
- - =
|
42
42
|
- !ruby/object:Gem::Version
|
43
|
-
version: '
|
43
|
+
version: '2.7'
|
44
44
|
type: :development
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *70094371056040
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: with_model
|
49
|
-
requirement: &
|
49
|
+
requirement: &70094371055100 !ruby/object:Gem::Requirement
|
50
50
|
none: false
|
51
51
|
requirements:
|
52
52
|
- - ! '>='
|
@@ -54,10 +54,10 @@ dependencies:
|
|
54
54
|
version: 0.1.3
|
55
55
|
type: :development
|
56
56
|
prerelease: false
|
57
|
-
version_requirements: *
|
57
|
+
version_requirements: *70094371055100
|
58
58
|
- !ruby/object:Gem::Dependency
|
59
59
|
name: autotest
|
60
|
-
requirement: &
|
60
|
+
requirement: &70094371054000 !ruby/object:Gem::Requirement
|
61
61
|
none: false
|
62
62
|
requirements:
|
63
63
|
- - ! '>='
|
@@ -65,7 +65,7 @@ dependencies:
|
|
65
65
|
version: '0'
|
66
66
|
type: :development
|
67
67
|
prerelease: false
|
68
|
-
version_requirements: *
|
68
|
+
version_requirements: *70094371054000
|
69
69
|
description: A completely transparent audit logging component for your application
|
70
70
|
using a stored procedure and triggers. Comes with specs for your project and a rake
|
71
71
|
task to generate the reverse SQL to undo changes logged
|
@@ -120,7 +120,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
120
120
|
version: '0'
|
121
121
|
segments:
|
122
122
|
- 0
|
123
|
-
hash: -
|
123
|
+
hash: -4309702284919187778
|
124
124
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
125
125
|
none: false
|
126
126
|
requirements:
|
@@ -129,10 +129,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
129
129
|
version: '0'
|
130
130
|
segments:
|
131
131
|
- 0
|
132
|
-
hash: -
|
132
|
+
hash: -4309702284919187778
|
133
133
|
requirements: []
|
134
134
|
rubyforge_project:
|
135
|
-
rubygems_version: 1.8.
|
135
|
+
rubygems_version: 1.8.6
|
136
136
|
signing_key:
|
137
137
|
specification_version: 3
|
138
138
|
summary: postgresql only database-level audit logging of all databases changes
|