pg_spec_helper 1.5.0 → 1.7.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '0448eb4ca85825f030ce96b4138d8bbf1ecce2dd51e6767a936591582034bd99'
4
- data.tar.gz: bd24c7db5a5ad3c267dd4b26d7b753aaa547db79e662235d96bb027e5cc8e5e7
3
+ metadata.gz: f2030740ae096125c106b944c0984652488f3b48f811aa5c4a4caa9f972099d6
4
+ data.tar.gz: b3e24f7888d0302b98b69aa7b6c2b52db6d4d3708c9cb060c30d5b41d0ab9bb8
5
5
  SHA512:
6
- metadata.gz: 8ec24b586e7b1550c638b9c1387ef3ac60b244ab35c857f95ab75e661f725f2c2eefeb65326ca946c2f0c97593db15bb8e3e156831cbbe72631d3afc46f29215
7
- data.tar.gz: b4b3982f19db7d7fc2ee038167eb8fea189a368d1ac6cb98916da60008635ba2293998216a057e451523b45855b6c0d8d8992ecda80b86c030f929fa306d12f1
6
+ metadata.gz: 3d72899156ddd1196b5e1984c6ec3efb12eb1ea1372efe6975d44f9fb4181c948f65ab6b6b63e0b3cf2dc19743c4c7a14b1bb3057e94a2f424be13b2c3684472
7
+ data.tar.gz: c679e87bfd3fcdd08cec7516bf0388d02b159c4641ec023e08d1f1bd1e187726d74182d14f0a8ba15ca718e12ca7de967c0223a8c17fcc991413a9be6b9aa12f
data/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.7.0](https://github.com/craigulliott/pg_spec_helper/compare/v1.6.0...v1.7.0) (2023-08-06)
4
+
5
+
6
+ ### Features
7
+
8
+ * adding support for "instead of" in triggers ([488b4ab](https://github.com/craigulliott/pg_spec_helper/commit/488b4ab5fb458db2c7e2f60395b12dcfa9559459))
9
+
10
+ ## [1.6.0](https://github.com/craigulliott/pg_spec_helper/compare/v1.5.0...v1.6.0) (2023-08-06)
11
+
12
+
13
+ ### Features
14
+
15
+ * added support for triggers and functions ([f89bf5e](https://github.com/craigulliott/pg_spec_helper/commit/f89bf5e3afa6fc411e9d1f16cb62db74fc8dc987))
16
+
3
17
  ## [1.5.0](https://github.com/craigulliott/pg_spec_helper/compare/v1.4.0...v1.5.0) (2023-08-01)
4
18
 
5
19
 
@@ -8,7 +8,7 @@ class PGSpecHelper
8
8
  # create a column for the provided table
9
9
  def create_column schema_name, table_name, column_name, type, null = true
10
10
  # note the `type` is safe from sql_injection due to the validation above
11
- connection.exec(<<-SQL)
11
+ connection.exec(<<~SQL)
12
12
  ALTER TABLE #{connection.quote_ident schema_name.to_s}.#{connection.quote_ident table_name.to_s}
13
13
  ADD COLUMN #{connection.quote_ident column_name.to_s} #{sanitize_name type} #{null ? "" : "NOT NULL"}
14
14
  SQL
@@ -16,7 +16,7 @@ class PGSpecHelper
16
16
 
17
17
  # return an array of column names for the provided table
18
18
  def get_column_names schema_name, table_name
19
- rows = connection.exec_params(<<-SQL, [schema_name.to_s, table_name.to_s])
19
+ rows = connection.exec_params(<<~SQL, [schema_name.to_s, table_name.to_s])
20
20
  SELECT column_name
21
21
  FROM information_schema.columns
22
22
  WHERE table_schema = $1
@@ -28,7 +28,7 @@ class PGSpecHelper
28
28
 
29
29
  # return an array of column names for the provided table
30
30
  def is_column_nullable schema_name, table_name, column_name
31
- rows = connection.exec_params(<<-SQL, [schema_name.to_s, table_name.to_s, column_name.to_s])
31
+ rows = connection.exec_params(<<~SQL, [schema_name.to_s, table_name.to_s, column_name.to_s])
32
32
  SELECT is_nullable
33
33
  FROM information_schema.columns
34
34
  WHERE table_schema = $1
@@ -7,7 +7,7 @@ class PGSpecHelper
7
7
  column_names_sql = column_names.map { |n| sanitize_name n }.join(", ")
8
8
  foreign_column_names_sql = foreign_column_names.map { |n| sanitize_name n }.join(", ")
9
9
  # add the foreign key
10
- connection.exec(<<-SQL)
10
+ connection.exec(<<~SQL)
11
11
  ALTER TABLE #{sanitize_name schema_name}.#{sanitize_name table_name}
12
12
  ADD CONSTRAINT #{sanitize_name foreign_key_name}
13
13
  FOREIGN KEY (#{column_names_sql})
@@ -17,7 +17,7 @@ class PGSpecHelper
17
17
 
18
18
  # returns a list of foreign keys for the provided table
19
19
  def get_foreign_key_names schema_name, table_name
20
- rows = connection.exec_params(<<-SQL, [schema_name.to_s, table_name.to_s])
20
+ rows = connection.exec_params(<<~SQL, [schema_name.to_s, table_name.to_s])
21
21
  SELECT constraint_name
22
22
  FROM information_schema.table_constraints
23
23
  WHERE table_schema = $1
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ class PGSpecHelper
4
+ module Functions
5
+ # create a function
6
+ def create_function schema_name, function_name, function_definition
7
+ connection.exec <<~SQL
8
+ CREATE FUNCTION #{schema_name}.#{function_name}() returns trigger language plpgsql AS $$
9
+ BEGIN
10
+ #{function_definition};
11
+ RETURN NEW;
12
+ END $$;
13
+ SQL
14
+ end
15
+
16
+ # return a list of function names for the provided schema
17
+ def get_function_names schema_name
18
+ # get the function names
19
+ rows = connection.exec(<<~SQL, [schema_name.to_s])
20
+ SELECT
21
+ routine_name
22
+ FROM
23
+ information_schema.routines
24
+ WHERE
25
+ routine_schema = $1
26
+ SQL
27
+ rows.map { |r| r["routine_name"].to_sym }
28
+ end
29
+ end
30
+ end
@@ -5,7 +5,7 @@ class PGSpecHelper
5
5
  # Create an index
6
6
  def create_index schema_name, table_name, column_names, index_name
7
7
  column_names_sql = column_names.map { |n| sanitize_name n }.join(", ")
8
- connection.exec(<<-SQL)
8
+ connection.exec(<<~SQL)
9
9
  CREATE INDEX #{connection.quote_ident index_name.to_s}
10
10
  ON #{connection.quote_ident schema_name.to_s}.#{connection.quote_ident table_name.to_s} (#{column_names_sql})
11
11
  SQL
@@ -13,7 +13,7 @@ class PGSpecHelper
13
13
 
14
14
  # get a list of index names for the provided table
15
15
  def get_index_names schema_name, table_name
16
- rows = connection.exec_params(<<-SQL, [schema_name.to_s, table_name.to_s])
16
+ rows = connection.exec_params(<<~SQL, [schema_name.to_s, table_name.to_s])
17
17
  SELECT indexname
18
18
  FROM pg_indexes
19
19
  WHERE schemaname = $1
@@ -6,7 +6,7 @@ class PGSpecHelper
6
6
  def create_primary_key schema_name, table_name, column_names, primary_key_name
7
7
  column_names_sql = column_names.map { |n| connection.quote_ident n.to_s }.join(", ")
8
8
  # add the primary_key
9
- connection.exec(<<-SQL)
9
+ connection.exec(<<~SQL)
10
10
  ALTER TABLE #{sanitize_name schema_name.to_s}.#{sanitize_name table_name.to_s}
11
11
  ADD CONSTRAINT #{sanitize_name primary_key_name}
12
12
  PRIMARY KEY (#{column_names_sql})
@@ -16,7 +16,7 @@ class PGSpecHelper
16
16
  # get the primary_key name for the provided table
17
17
  def get_primary_key_name schema_name, table_name
18
18
  # get the primary_key name
19
- rows = connection.exec(<<-SQL, [schema_name.to_s, table_name.to_s])
19
+ rows = connection.exec(<<~SQL, [schema_name.to_s, table_name.to_s])
20
20
  SELECT
21
21
  constraint_name
22
22
  FROM
@@ -4,7 +4,7 @@ class PGSpecHelper
4
4
  module Schemas
5
5
  # create a new schema in the database
6
6
  def create_schema schema_name
7
- connection.exec(<<-SQL)
7
+ connection.exec(<<~SQL)
8
8
  CREATE SCHEMA #{connection.quote_ident schema_name.to_s};
9
9
  SQL
10
10
  end
@@ -13,7 +13,7 @@ class PGSpecHelper
13
13
  def get_schema_names
14
14
  ignored_schemas_sql = ignored_schemas.map { |n| sanitize_name n }.join("', '")
15
15
  # return a list of the schema names from the database
16
- results = connection.exec(<<-SQL)
16
+ results = connection.exec(<<~SQL)
17
17
  SELECT schema_name
18
18
  FROM information_schema.schemata
19
19
  WHERE
@@ -28,7 +28,7 @@ class PGSpecHelper
28
28
  def delete_all_schemas
29
29
  # delete all schemas except public
30
30
  get_schema_names.reject { |schema_name| schema_name == :public }.each do |schema_name|
31
- connection.exec(<<-SQL)
31
+ connection.exec(<<~SQL)
32
32
  -- temporarily set the client_min_messages to WARNING to
33
33
  -- suppress the NOTICE messages about cascading deletes
34
34
  SET client_min_messages TO WARNING;
@@ -4,7 +4,7 @@ class PGSpecHelper
4
4
  module Tables
5
5
  # create a new table in the provided schema
6
6
  def create_table schema_name, table_name
7
- connection.exec(<<-SQL)
7
+ connection.exec(<<~SQL)
8
8
  CREATE TABLE #{sanitize_name schema_name.to_s}.#{sanitize_name table_name.to_s}(
9
9
  -- tables are created empty, and have columns added to them later
10
10
  );
@@ -13,7 +13,7 @@ class PGSpecHelper
13
13
 
14
14
  # return an array of table names for the provided schema
15
15
  def get_table_names schema_name
16
- rows = connection.exec_params(<<-SQL, [schema_name.to_s])
16
+ rows = connection.exec_params(<<~SQL, [schema_name.to_s])
17
17
  SELECT table_name FROM information_schema.tables
18
18
  WHERE
19
19
  table_schema = $1
@@ -26,7 +26,7 @@ class PGSpecHelper
26
26
  # delete all tables in the provided schema
27
27
  def delete_tables schema_name
28
28
  get_table_names(schema_name).each do |table_name|
29
- connection.exec(<<-SQL)
29
+ connection.exec(<<~SQL)
30
30
  -- temporarily set the client_min_messages to WARNING to
31
31
  -- suppress the NOTICE messages about cascading deletes
32
32
  SET client_min_messages TO WARNING;
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ class PGSpecHelper
4
+ module Triggers
5
+ # create a trigger
6
+ class UnexpectedEventManipulationError < StandardError
7
+ end
8
+
9
+ class UnexpectedActionOrientationError < StandardError
10
+ end
11
+
12
+ class UnexpectedActionTimingError < StandardError
13
+ end
14
+
15
+ class UnexpectedConditionsError < StandardError
16
+ end
17
+
18
+ # create a postgres trigger
19
+ def create_trigger schema_name, table_name, name, action_timing:, event_manipulation:, action_orientation:, routine_schema:, routine_name:, action_condition: nil, action_reference_old_table: nil, action_reference_new_table: nil
20
+ unless [:insert, :delete, :update].include? event_manipulation
21
+ raise UnexpectedEventManipulationError, event_manipulation
22
+ end
23
+
24
+ unless action_condition.nil? || action_condition.is_a?(String)
25
+ raise UnexpectedConditionsError, "expected String but got `#{action_condition}`"
26
+ end
27
+
28
+ unless [:row, :statement].include? action_orientation
29
+ raise UnexpectedActionOrientationError, action_orientation
30
+ end
31
+
32
+ unless [:before, :after, :instead_of].include? action_timing
33
+ raise UnexpectedActionTimingError, action_timing
34
+ end
35
+
36
+ # "INSTEAD OF/BEFORE/AFTER" "INSERT/UPDATE/DELETE"
37
+ timing_sql = "#{action_timing.to_s.sub("_", " ")} #{event_manipulation}".upcase
38
+
39
+ condition_sql = action_condition.nil? ? "" : "WHEN (#{action_condition})"
40
+
41
+ temp_tables = []
42
+ unless action_reference_old_table.nil?
43
+ temp_tables << "OLD TABLE AS #{action_reference_old_table}"
44
+ end
45
+ unless action_reference_new_table.nil?
46
+ temp_tables << "NEW TABLE AS #{action_reference_new_table}"
47
+ end
48
+ temp_tables_sql = temp_tables.any? ? "REFERENCING #{temp_tables.join(" ")}" : ""
49
+
50
+ connection.exec <<~SQL
51
+ -- trigger names only need to be unique for this table
52
+ CREATE TRIGGER #{name}
53
+ #{timing_sql} ON #{schema_name}.#{table_name} #{temp_tables_sql}
54
+ FOR EACH #{action_orientation}
55
+ #{condition_sql}
56
+ EXECUTE PROCEDURE #{routine_schema}.#{routine_name}();
57
+ SQL
58
+ end
59
+
60
+ # return a list of trigger names for the provided table
61
+ def get_trigger_names schema_name, table_name
62
+ # get the trigger names
63
+ rows = connection.exec(<<~SQL, [schema_name.to_s, table_name.to_s])
64
+ SELECT
65
+ trigger_name
66
+ FROM
67
+ information_schema.triggers
68
+ WHERE
69
+ event_object_schema = $1
70
+ AND event_object_table = $2
71
+ SQL
72
+ rows.map { |r| r["trigger_name"].to_sym }
73
+ end
74
+ end
75
+ end
@@ -6,7 +6,7 @@ class PGSpecHelper
6
6
  def create_unique_constraint schema_name, table_name, column_names, constraint_key_name
7
7
  column_names_sql = column_names.map { |n| connection.quote_ident n.to_s }.join(", ")
8
8
  # add the constraint key
9
- connection.exec(<<-SQL)
9
+ connection.exec(<<~SQL)
10
10
  ALTER TABLE #{connection.quote_ident schema_name.to_s}.#{connection.quote_ident table_name.to_s}
11
11
  ADD CONSTRAINT #{connection.quote_ident constraint_key_name.to_s}
12
12
  UNIQUE (#{column_names_sql})
@@ -16,7 +16,7 @@ class PGSpecHelper
16
16
  # get a list of unique constraints for the provided table
17
17
  def get_unique_constraint_names schema_name, table_name
18
18
  # get the unique constraint names
19
- rows = connection.exec(<<-SQL, [schema_name.to_s, table_name.to_s])
19
+ rows = connection.exec(<<~SQL, [schema_name.to_s, table_name.to_s])
20
20
  SELECT
21
21
  constraint_name
22
22
  FROM
@@ -6,7 +6,7 @@ class PGSpecHelper
6
6
  def create_validation schema_name, table_name, validation_name, check_clause
7
7
  # todo the check_clause is vulnerable to sql injection (although this is very low risk because
8
8
  # it is only ever provided by the test suite, and is never provided by the user)
9
- connection.exec(<<-SQL)
9
+ connection.exec(<<~SQL)
10
10
  ALTER TABLE #{connection.quote_ident schema_name.to_s}.#{connection.quote_ident table_name.to_s}
11
11
  ADD CONSTRAINT #{connection.quote_ident validation_name.to_s} CHECK (#{check_clause})
12
12
  SQL
@@ -15,7 +15,7 @@ class PGSpecHelper
15
15
  # return a list of validation names for the provided table
16
16
  def get_validation_names schema_name, table_name
17
17
  # get the validation names
18
- rows = connection.exec(<<-SQL, [schema_name.to_s, table_name.to_s])
18
+ rows = connection.exec(<<~SQL, [schema_name.to_s, table_name.to_s])
19
19
  SELECT
20
20
  constraint_name
21
21
  FROM
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class PGSpecHelper
4
- VERSION = "1.5.0"
4
+ VERSION = "1.7.0"
5
5
  end
@@ -16,6 +16,8 @@ require "pg_spec_helper/foreign_keys"
16
16
  require "pg_spec_helper/unique_constraints"
17
17
  require "pg_spec_helper/primary_keys"
18
18
  require "pg_spec_helper/indexes"
19
+ require "pg_spec_helper/triggers"
20
+ require "pg_spec_helper/functions"
19
21
  require "pg_spec_helper/models"
20
22
  require "pg_spec_helper/materialized_views"
21
23
  require "pg_spec_helper/reset"
@@ -39,6 +41,8 @@ class PGSpecHelper
39
41
  include UniqueConstraints
40
42
  include PrimaryKeys
41
43
  include Indexes
44
+ include Triggers
45
+ include Functions
42
46
  include Models
43
47
  include MaterializedViews
44
48
  include Reset
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pg_spec_helper
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.5.0
4
+ version: 1.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Craig Ulliott
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-08-01 00:00:00.000000000 Z
11
+ date: 2023-08-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: pg
@@ -55,6 +55,7 @@ files:
55
55
  - lib/pg_spec_helper/connection.rb
56
56
  - lib/pg_spec_helper/empty_database.rb
57
57
  - lib/pg_spec_helper/foreign_keys.rb
58
+ - lib/pg_spec_helper/functions.rb
58
59
  - lib/pg_spec_helper/ignored_schemas.rb
59
60
  - lib/pg_spec_helper/indexes.rb
60
61
  - lib/pg_spec_helper/materialized_views.rb
@@ -66,6 +67,7 @@ files:
66
67
  - lib/pg_spec_helper/table_executer.rb
67
68
  - lib/pg_spec_helper/tables.rb
68
69
  - lib/pg_spec_helper/track_changes.rb
70
+ - lib/pg_spec_helper/triggers.rb
69
71
  - lib/pg_spec_helper/unique_constraints.rb
70
72
  - lib/pg_spec_helper/validations.rb
71
73
  - lib/pg_spec_helper/version.rb