Advertisement
Guest User

Untitled

a guest
Aug 4th, 2015
162
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 7.32 KB | None | 0 0
  1. <?php
  2.  
  3. class m150704_234851_audit extends \yii\db\Migration
  4. {
  5. public function safeUp()
  6. {
  7. $query = <<<SQL
  8. CREATE SCHEMA audits
  9. SQL;
  10. $this->execute($query);
  11. $query = <<<SQL
  12. CREATE TYPE audits.action_type AS ENUM ('INSERT', 'SELECT', 'UPDATE', 'DELETE', 'TRUNCATE')
  13. SQL;
  14. $this->execute($query);
  15. $query = <<<SQL
  16. CREATE TABLE "audits"."changesets" (
  17. "id" serial NOT NULL PRIMARY KEY,
  18. "transaction_id" bigint,
  19. "user_id" integer,
  20. "session_id" text,
  21. "request_date" timestamp(0) with time zone NOT NULL,
  22. "request_url" text,
  23. "request_addr" inet
  24. )
  25. SQL;
  26. $this->execute($query);
  27. $query = <<<SQL
  28. CREATE INDEX ON audits.changesets (transaction_id)
  29. SQL;
  30. $this->execute($query);
  31. $query = <<<SQL
  32. CREATE INDEX ON audits.changesets (User_id)
  33. SQL;
  34. $this->execute($query);
  35. $query = <<<SQL
  36. CREATE INDEX ON audits.changesets (session_id)
  37. SQL;
  38. $this->execute($query);
  39. $query = <<<SQL
  40. CREATE INDEX ON audits.changesets (request_url)
  41. SQL;
  42. $this->execute($query);
  43. $query = <<<SQL
  44. CREATE INDEX ON audits.changesets (request_addr)
  45. SQL;
  46. $this->execute($query);
  47. $query = <<<SQL
  48. CREATE TABLE "audits"."logged_actions" (
  49. "action_id" bigserial NOT NULL PRIMARY KEY,
  50. "schema_name" text NOT NULL,
  51. "table_name" text NOT NULL,
  52. "relation_id" oid NOT NULL,
  53. "transaction_date" timestamp(0) with time zone NOT NULL,
  54. "statement_date" timestamp(0) with time zone NOT NULL,
  55. "action_date" timestamp(0) with time zone NOT NULL,
  56. "transaction_id" bigint,
  57. "session_user_name" text,
  58. "application_name" text,
  59. "client_addr" inet,
  60. "client_port" integer,
  61. "query" text,
  62. "action_type" audits.action_type NOT NULL,
  63. "row_data" jsonb,
  64. "changed_fields" jsonb,
  65. "statement_only" boolean NOT NULL DEFAULT FALSE,
  66. "key_type" char(1) NOT NULL CHECK (key_type IN ('c', 't', 'a')),
  67. "changeset_id" integer REFERENCES audits.changesets (id) ON UPDATE CASCADE ON DELETE CASCADE
  68. )
  69. SQL;
  70. $this->execute($query);
  71. $query = <<<SQL
  72. CREATE INDEX ON audits.logged_actions (schema_name, table_name)
  73. SQL;
  74. $this->execute($query);
  75. $query = <<<SQL
  76. CREATE INDEX ON audits.logged_actions (relation_id)
  77. SQL;
  78. $this->execute($query);
  79. $query = <<<SQL
  80. CREATE INDEX ON audits.logged_actions (statement_date)
  81. SQL;
  82. $this->execute($query);
  83. $query = <<<SQL
  84. CREATE INDEX ON audits.logged_actions (action_type)
  85. SQL;
  86. $this->execute($query);
  87. $query = <<<SQL
  88. CREATE INDEX ON audits.logged_actions USING GIN (row_data jsonb_path_ops)
  89. SQL;
  90. $this->execute($query);
  91. $query = <<<SQL
  92. CREATE OR REPLACE FUNCTION audits.json_object_delete_keys(_json json, VARIADIC _keys TEXT[]) RETURNS json AS \$BODY$
  93. SELECT json_object_agg(key, value) AS json
  94. FROM json_each(_json)
  95. WHERE key != ALL (_keys)
  96. \$BODY$
  97. LANGUAGE sql
  98. IMMUTABLE STRICT
  99. SQL;
  100. $this->execute($query);
  101. $query = <<<SQL
  102. CREATE OR REPLACE FUNCTION audits.json_object_delete_values(_json json, _values json) RETURNS json AS \$BODY$
  103. SELECT json_object_agg(a.key, a.value) AS json
  104. FROM json_each_text(_json) a
  105. JOIN json_each_text(_values) b ON a.key = b.key AND a.value IS DISTINCT FROM b.value
  106. \$BODY$
  107. LANGUAGE sql
  108. IMMUTABLE STRICT
  109. SQL;
  110. $this->execute($query);
  111. $query = <<<SQL
  112. CREATE OR REPLACE FUNCTION audits.log_action() RETURNS trigger AS \$BODY$
  113. DECLARE
  114. audit_row audits.logged_actions;
  115. include_values boolean;
  116. log_diffs boolean;
  117. h_old jsonb;
  118. h_new jsonb;
  119. excluded_cols text[] = ARRAY[]::text[];
  120. BEGIN
  121. IF TG_WHEN <> 'AFTER' THEN
  122. RAISE EXCEPTION 'audits.log_action() may only run as an AFTER trigger';
  123. END IF;
  124.  
  125. audit_row = ROW(
  126. nextval('audits.logged_actions_action_id_seq') -- action_id
  127. ,TG_TABLE_SCHEMA::text -- schema_name
  128. ,TG_TABLE_NAME::text -- table_name
  129. ,TG_RELID -- relation OID for much quicker searches
  130. ,current_timestamp -- transaction_date
  131. ,statement_timestamp() -- statement_date
  132. ,clock_timestamp() -- action_date
  133. ,txid_current() -- transaction_id
  134. ,NULL::text -- session_user_name
  135. ,NULL::text -- application_name
  136. ,NULL::inet -- client_addr
  137. ,NULL::integer -- client_port
  138. ,NULL::text -- top-level query or queries (if multistatement) from client
  139. ,TG_OP -- action_type
  140. ,NULL::jsonb -- row_data
  141. ,NULL::jsonb -- changed_fields
  142. ,FALSE -- statement_only
  143. ,NULL -- key_type
  144. ,NULL
  145. );
  146.  
  147. IF TG_ARGV[0]::boolean IS NOT DISTINCT FROM TRUE THEN
  148. audit_row.query = current_query();
  149. END IF;
  150.  
  151. IF TG_ARGV[1] IS NOT NULL THEN
  152. excluded_cols = TG_ARGV[1]::text[];
  153. END IF;
  154.  
  155. IF TG_ARGV[2]::boolean IS NOT DISTINCT FROM TRUE THEN
  156. audit_row.session_user_name = session_user::text;
  157. audit_row.application_name = current_setting('application_name');
  158. audit_row.client_addr = inet_client_addr();
  159. audit_row.client_port = inet_client_port();
  160. END IF;
  161.  
  162. BEGIN
  163. SELECT NULLIF(current_setting('audit.changeset_id'), '') INTO audit_row.changeset_id;
  164. EXCEPTION
  165. WHEN undefined_object THEN audit_row.changeset_id = NULL;
  166. WHEN data_exception THEN audit_row.changeset_id = NULL;
  167. END;
  168.  
  169. audit_row.key_type = CASE
  170. WHEN audit_row.changeset_id IS NOT NULL THEN 'c'
  171. WHEN audit_row.key_type IS NOT NULL THEN 't'
  172. ELSE 'a'
  173. END;
  174.  
  175. IF (TG_OP = 'UPDATE' AND TG_LEVEL = 'ROW') THEN
  176. audit_row.row_data = audits.json_object_delete_keys(row_to_json(OLD), VARIADIC excluded_cols)::jsonb;
  177. audit_row.changed_fields = audits.json_object_delete_keys(audits.json_object_delete_values(row_to_json(NEW), row_to_json(OLD)), VARIADIC excluded_cols)::jsonb;
  178. IF audit_row.changed_fields IS NULL THEN
  179. -- All changed fields are ignored. Skip this update.
  180. RETURN NULL;
  181. END IF;
  182. ELSIF (TG_OP = 'DELETE' AND TG_LEVEL = 'ROW') THEN
  183. audit_row.row_data = audits.json_object_delete_keys(row_to_json(OLD), VARIADIC excluded_cols)::jsonb;
  184. ELSIF (TG_OP = 'INSERT' AND TG_LEVEL = 'ROW') THEN
  185. audit_row.row_data = audits.json_object_delete_keys(row_to_json(NEW), VARIADIC excluded_cols)::jsonb;
  186. ELSIF (TG_LEVEL = 'STATEMENT' AND TG_OP IN ('INSERT','UPDATE','DELETE','TRUNCATE')) THEN
  187. audit_row.statement_only = TRUE;
  188. ELSE
  189. RAISE EXCEPTION '[audits.log_action] - Trigger func added as trigger for unhandled case: %, %', TG_OP, TG_LEVEL;
  190. RETURN NULL;
  191. END IF;
  192. INSERT INTO audits.logged_actions VALUES (audit_row.*);
  193. RETURN NULL;
  194. END;
  195. \$BODY$
  196. LANGUAGE plpgsql VOLATILE SECURITY DEFINER
  197. SQL;
  198. $this->execute($query);
  199. }
  200.  
  201. public function safeDown()
  202. {
  203. $query = <<<SQL
  204. DROP FUNCTION audits.json_object_delete_keys(json, text[])
  205. SQL;
  206. $this->execute($query);
  207. $query = <<<SQL
  208. DROP FUNCTION audits.json_object_delete_values(json, json)
  209. SQL;
  210. $this->execute($query);
  211. $query = <<<SQL
  212. DROP FUNCTION audits.log_action()
  213. SQL;
  214. $this->execute($query);
  215. $query = <<<SQL
  216. DROP TABLE "audits"."logged_actions"
  217. SQL;
  218. $this->execute($query);
  219. $query = <<<SQL
  220. DROP TABLE "audits"."changesets"
  221. SQL;
  222. $this->execute($query);
  223. }
  224. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement