pg_spec_helper 1.5.0 → 1.7.0

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.
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