pg_spec_helper 1.4.0 → 1.6.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: 68e7d2fca3250ae565d38d0709b1eb73d212b5e6af6c642702e6345123a55e3a
4
- data.tar.gz: 9d2340c8d9f285e97ffce51942c359843067c663f0383e0e4a9f752382b91eff
3
+ metadata.gz: 5c885ddde22232c3907a08da5411a55e09b1105a87afbac5aac0b2b443c63ee1
4
+ data.tar.gz: a8220ca48f1dea784d20e45ecc74dfd0f464c8524a55240715bb7154dc6f8879
5
5
  SHA512:
6
- metadata.gz: d124e944257073e9a37a1c39ef1a50ba64a86adc4f436312af1b69554a6b8ebdf36d0fedb375e89a9a521fc7e23f2c08f23bf80b15cdbe7c90c27e04542fc656
7
- data.tar.gz: 3cb9e58e2bd59553f94cee19af7f991f14174e8ee51fc8160eb923ebe467e54543a03b9d0895a6f09dd58489763af2b0299dbee3c6e04e8063af28502c17f2f1
6
+ metadata.gz: '08770ab6431e7b5d2f21b0443d88282400ea597d78b946fe5c68b4c4ff562ae7bd14e0ddb5db4c5353ec8954ee0472ce5e5a2858259186504469f0a6783d66df'
7
+ data.tar.gz: f811831ee0dc0381a3252556d28c1ac85f476707ab3ac6fd3c951c0c2ecb985ea7d18a1daf663d27ce533c06cab94e91d0963a200a88590bae5004a14d64b0d7
data/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.6.0](https://github.com/craigulliott/pg_spec_helper/compare/v1.5.0...v1.6.0) (2023-08-06)
4
+
5
+
6
+ ### Features
7
+
8
+ * added support for triggers and functions ([f89bf5e](https://github.com/craigulliott/pg_spec_helper/commit/f89bf5e3afa6fc411e9d1f16cb62db74fc8dc987))
9
+
10
+ ## [1.5.0](https://github.com/craigulliott/pg_spec_helper/compare/v1.4.0...v1.5.0) (2023-08-01)
11
+
12
+
13
+ ### Features
14
+
15
+ * adding a new create_model method which yields a block to expose a DSL for easily working with the table structure ([dc28d5e](https://github.com/craigulliott/pg_spec_helper/commit/dc28d5ef599d8306564aa7c29d2220fb22ee6dd6))
16
+
3
17
  ## [1.4.0](https://github.com/craigulliott/pg_spec_helper/compare/v1.3.0...v1.4.0) (2023-07-10)
4
18
 
5
19
 
@@ -2,12 +2,15 @@
2
2
 
3
3
  class PGSpecHelper
4
4
  module Columns
5
+ class ColumnDoesNotExistError < StandardError
6
+ end
7
+
5
8
  # create a column for the provided table
6
- def create_column schema_name, table_name, column_name, type
9
+ def create_column schema_name, table_name, column_name, type, null = true
7
10
  # note the `type` is safe from sql_injection due to the validation above
8
11
  connection.exec(<<-SQL)
9
12
  ALTER TABLE #{connection.quote_ident schema_name.to_s}.#{connection.quote_ident table_name.to_s}
10
- ADD COLUMN #{connection.quote_ident column_name.to_s} #{sanitize_name type}
13
+ ADD COLUMN #{connection.quote_ident column_name.to_s} #{sanitize_name type} #{null ? "" : "NOT NULL"}
11
14
  SQL
12
15
  end
13
16
 
@@ -22,5 +25,18 @@ class PGSpecHelper
22
25
  SQL
23
26
  rows.map { |row| row["column_name"].to_sym }
24
27
  end
28
+
29
+ # return an array of column names for the provided table
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])
32
+ SELECT is_nullable
33
+ FROM information_schema.columns
34
+ WHERE table_schema = $1
35
+ AND table_name = $2
36
+ AND column_name = $3;
37
+ SQL
38
+ raise ColumnDoesNotExistError if rows.first.nil?
39
+ rows.first["is_nullable"] == "YES"
40
+ end
25
41
  end
26
42
  end
@@ -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
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ class PGSpecHelper
4
+ module Models
5
+ # create a new table in the provided schema with an auto incrementing id
6
+ # created_at and updated_at, optionally provide a block which will be
7
+ # yeilded This allows for syntax such as:
8
+ #
9
+ # create_model :users, :optional_schema_name do |t|
10
+ # t.add_colmn string :name
11
+ # end
12
+ def create_model schema_name, table_name, &block
13
+ unless schema_exists? schema_name
14
+ create_schema schema_name
15
+ end
16
+ # create the table
17
+ create_table schema_name, table_name
18
+ # create the standard columns
19
+ create_column schema_name, table_name, :id, :serial
20
+ create_column schema_name, table_name, :created_at, :timestamp
21
+ create_column schema_name, table_name, :updated_at, :timestamp
22
+ # add the primary key
23
+ create_primary_key schema_name, table_name, [:id], :pk
24
+ # execute the optional block
25
+ if block
26
+ this = self
27
+ if this.is_a? PGSpecHelper
28
+ TableExecuter.new(this, schema_name, table_name, &block)
29
+ else
30
+ raise "Module should be added to PGSpecHelper"
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ class PGSpecHelper
4
+ class TableExecuter
5
+ def initialize pg_spec_helper, schema_name, table_name, &block
6
+ @pg_spec_helper = pg_spec_helper
7
+ @schema_name = schema_name
8
+ @table_name = table_name
9
+ instance_exec(&block)
10
+ end
11
+
12
+ def add_column column_name, type, null = true
13
+ @pg_spec_helper.create_column @schema_name, @table_name, column_name, type, null
14
+ end
15
+
16
+ def add_foreign_key column_names, foreign_schema_name, foreign_table_name, foreign_column_names, foreign_key_name
17
+ @pg_spec_helper.create_foreign_key @schema_name, @table_name, column_names, foreign_schema_name, foreign_table_name, foreign_column_names, foreign_key_name
18
+ end
19
+
20
+ def add_index column_names, index_name, unique = false
21
+ @pg_spec_helper.create_index @schema_name, @table_name, column_names, index_name
22
+ end
23
+
24
+ def add_primary_key column_names, primary_key_name
25
+ @pg_spec_helper.create_primary_key @schema_name, @table_name, column_names, primary_key_name
26
+ end
27
+
28
+ def add_unique_constraint column_names, constraint_key_name
29
+ @pg_spec_helper.create_unique_constraint @schema_name, @table_name, column_names, constraint_key_name
30
+ end
31
+
32
+ def add_validation validation_name, check_clause
33
+ @pg_spec_helper.create_validation @schema_name, @table_name, validation_name, check_clause
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,78 @@
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 UnexpectedActionOrderError < StandardError
10
+ end
11
+
12
+ class UnexpectedActionStatementError < StandardError
13
+ end
14
+
15
+ class UnexpectedActionOrientationError < StandardError
16
+ end
17
+
18
+ class UnexpectedActionTimingError < StandardError
19
+ end
20
+
21
+ # create a postgres trigger
22
+ 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
23
+ unless [:insert, :delete, :update].include? event_manipulation
24
+ raise UnexpectedEventManipulationError, event_manipulation
25
+ end
26
+
27
+ unless action_condition.nil? || action_condition.is_a?(String)
28
+ raise ExpectedStringError, action_condition
29
+ end
30
+
31
+ unless [:row, :statement].include? action_orientation
32
+ raise UnexpectedActionOrientationError, action_orientation
33
+ end
34
+
35
+ unless [:before, :after].include? action_timing
36
+ raise UnexpectedActionTimingError, action_timing
37
+ end
38
+
39
+ # BEFORE INSERT / AFTER INSERT / BEFORE UPDATE / AFTER UPDATE / BEFORE DELETE / AFTER DELETE
40
+ timing_sql = "#{action_timing} #{event_manipulation}".upcase
41
+
42
+ condition_sql = action_condition.nil? ? "" : "WHEN (#{action_condition})"
43
+
44
+ temp_tables = []
45
+ unless action_reference_old_table.nil?
46
+ temp_tables << "OLD TABLE AS #{action_reference_old_table}"
47
+ end
48
+ unless action_reference_new_table.nil?
49
+ temp_tables << "NEW TABLE AS #{action_reference_new_table}"
50
+ end
51
+ temp_tables_sql = temp_tables.any? ? "REFERENCING #{temp_tables.join(" ")}" : ""
52
+
53
+ connection.exec <<-SQL
54
+ -- trigger names only need to be unique for this table
55
+ CREATE TRIGGER #{name}
56
+ #{timing_sql} ON #{schema_name}.#{table_name} #{temp_tables_sql}
57
+ FOR EACH #{action_orientation}
58
+ #{condition_sql}
59
+ EXECUTE PROCEDURE #{routine_schema}.#{routine_name}();
60
+ SQL
61
+ end
62
+
63
+ # return a list of trigger names for the provided table
64
+ def get_trigger_names schema_name, table_name
65
+ # get the trigger names
66
+ rows = connection.exec(<<-SQL, [schema_name.to_s, table_name.to_s])
67
+ SELECT
68
+ trigger_name
69
+ FROM
70
+ information_schema.triggers
71
+ WHERE
72
+ event_object_schema = $1
73
+ AND event_object_table = $2
74
+ SQL
75
+ rows.map { |r| r["trigger_name"].to_sym }
76
+ end
77
+ end
78
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class PGSpecHelper
4
- VERSION = "1.4.0"
4
+ VERSION = "1.6.0"
5
5
  end
@@ -16,11 +16,16 @@ 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"
21
+ require "pg_spec_helper/models"
19
22
  require "pg_spec_helper/materialized_views"
20
23
  require "pg_spec_helper/reset"
21
24
  require "pg_spec_helper/empty_database"
22
25
  require "pg_spec_helper/track_changes"
23
26
 
27
+ require "pg_spec_helper/table_executer"
28
+
24
29
  class PGSpecHelper
25
30
  class MissingRequiredOptionError < StandardError
26
31
  end
@@ -36,6 +41,9 @@ class PGSpecHelper
36
41
  include UniqueConstraints
37
42
  include PrimaryKeys
38
43
  include Indexes
44
+ include Triggers
45
+ include Functions
46
+ include Models
39
47
  include MaterializedViews
40
48
  include Reset
41
49
  include EmptyDatabase
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.4.0
4
+ version: 1.6.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-07-10 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,15 +55,19 @@ 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
62
+ - lib/pg_spec_helper/models.rb
61
63
  - lib/pg_spec_helper/primary_keys.rb
62
64
  - lib/pg_spec_helper/reset.rb
63
65
  - lib/pg_spec_helper/sanitize.rb
64
66
  - lib/pg_spec_helper/schemas.rb
67
+ - lib/pg_spec_helper/table_executer.rb
65
68
  - lib/pg_spec_helper/tables.rb
66
69
  - lib/pg_spec_helper/track_changes.rb
70
+ - lib/pg_spec_helper/triggers.rb
67
71
  - lib/pg_spec_helper/unique_constraints.rb
68
72
  - lib/pg_spec_helper/validations.rb
69
73
  - lib/pg_spec_helper/version.rb