fx 0.9.0 → 0.10.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 +4 -4
- data/.github/workflows/ci.yml +2 -2
- data/CHANGELOG.md +27 -1
- data/Gemfile +0 -1
- data/bin/rake +2 -3
- data/bin/rspec +13 -3
- data/bin/yard +13 -3
- data/fx.gemspec +3 -3
- data/lib/fx/adapters/postgres/connection.rb +12 -4
- data/lib/fx/adapters/postgres/functions.rb +11 -28
- data/lib/fx/adapters/postgres/query_executor.rb +34 -0
- data/lib/fx/adapters/postgres/triggers.rb +14 -29
- data/lib/fx/adapters/postgres.rb +10 -10
- data/lib/fx/configuration.rb +2 -2
- data/lib/fx/schema_dumper.rb +6 -14
- data/lib/fx/statements.rb +61 -58
- data/lib/fx/version.rb +1 -1
- data/lib/generators/fx/function/function_generator.rb +50 -53
- data/lib/generators/fx/function/templates/db/migrate/create_function.erb +1 -1
- data/lib/generators/fx/function/templates/db/migrate/update_function.erb +1 -1
- data/lib/generators/fx/migration_helper.rb +53 -0
- data/lib/generators/fx/name_helper.rb +33 -0
- data/lib/generators/fx/trigger/templates/db/migrate/create_trigger.erb +1 -1
- data/lib/generators/fx/trigger/templates/db/migrate/update_trigger.erb +1 -1
- data/lib/generators/fx/trigger/trigger_generator.rb +45 -64
- data/lib/generators/fx/version_helper.rb +55 -0
- data/spec/acceptance/user_manages_functions_spec.rb +6 -6
- data/spec/acceptance/user_manages_triggers_spec.rb +10 -10
- data/spec/acceptance_helper.rb +2 -2
- data/spec/features/functions/migrations_spec.rb +4 -4
- data/spec/features/functions/revert_spec.rb +4 -4
- data/spec/features/triggers/migrations_spec.rb +6 -6
- data/spec/features/triggers/revert_spec.rb +8 -8
- data/spec/fx/adapters/postgres/functions_spec.rb +12 -5
- data/spec/fx/adapters/postgres/query_executor_spec.rb +75 -0
- data/spec/fx/adapters/postgres/triggers_spec.rb +14 -7
- data/spec/fx/adapters/postgres_spec.rb +62 -20
- data/spec/fx/definition_spec.rb +6 -6
- data/spec/fx/schema_dumper_spec.rb +60 -14
- data/spec/fx_spec.rb +1 -1
- data/spec/generators/fx/function/function_generator_spec.rb +10 -10
- data/spec/generators/fx/migration_helper_spec.rb +133 -0
- data/spec/generators/fx/name_helper_spec.rb +114 -0
- data/spec/generators/fx/trigger/trigger_generator_spec.rb +42 -19
- data/spec/generators/fx/version_helper_spec.rb +157 -0
- data/spec/spec_helper.rb +2 -0
- data/spec/support/generator_setup.rb +46 -5
- metadata +28 -11
|
@@ -1,39 +1,43 @@
|
|
|
1
1
|
require "rails/generators"
|
|
2
2
|
require "rails/generators/active_record"
|
|
3
|
+
require "generators/fx/version_helper"
|
|
4
|
+
require "generators/fx/migration_helper"
|
|
5
|
+
require "generators/fx/name_helper"
|
|
3
6
|
|
|
4
7
|
module Fx
|
|
5
8
|
module Generators
|
|
6
9
|
# @api private
|
|
7
10
|
class TriggerGenerator < Rails::Generators::NamedBase
|
|
8
11
|
include Rails::Generators::Migration
|
|
12
|
+
|
|
9
13
|
source_root File.expand_path("../templates", __FILE__)
|
|
10
14
|
argument :table_name, type: :hash, required: true
|
|
11
15
|
|
|
16
|
+
DEFINITION_PATH = %w[db triggers].freeze
|
|
17
|
+
|
|
12
18
|
class_option :migration, type: :boolean
|
|
13
19
|
|
|
14
20
|
def create_triggers_directory
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
21
|
+
return if trigger_definition_path.exist?
|
|
22
|
+
|
|
23
|
+
empty_directory(trigger_definition_path)
|
|
18
24
|
end
|
|
19
25
|
|
|
20
26
|
def create_trigger_definition
|
|
21
|
-
create_file
|
|
27
|
+
create_file(definition.path)
|
|
22
28
|
end
|
|
23
29
|
|
|
24
30
|
def create_migration_file
|
|
25
|
-
return if
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
)
|
|
36
|
-
end
|
|
31
|
+
return if migration_helper.skip_creation?
|
|
32
|
+
|
|
33
|
+
template_info = migration_helper.migration_template_info(
|
|
34
|
+
object_type: :trigger,
|
|
35
|
+
file_name: file_name,
|
|
36
|
+
updating_existing: version_helper.updating_existing?,
|
|
37
|
+
version: version_helper.current_version
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
migration_template(template_info[:template], template_info[:filename])
|
|
37
41
|
end
|
|
38
42
|
|
|
39
43
|
def self.next_migration_number(dir)
|
|
@@ -42,84 +46,61 @@ module Fx
|
|
|
42
46
|
|
|
43
47
|
no_tasks do
|
|
44
48
|
def previous_version
|
|
45
|
-
|
|
46
|
-
.map { |name| version_regex.match(name).try(:[], "version").to_i }
|
|
47
|
-
.max
|
|
49
|
+
version_helper.previous_version
|
|
48
50
|
end
|
|
49
51
|
|
|
50
52
|
def version
|
|
51
|
-
|
|
53
|
+
version_helper.current_version
|
|
52
54
|
end
|
|
53
55
|
|
|
54
56
|
def migration_class_name
|
|
55
|
-
if
|
|
56
|
-
|
|
57
|
+
if version_helper.updating_existing?
|
|
58
|
+
migration_helper.update_migration_class_name(
|
|
59
|
+
object_type: :trigger,
|
|
60
|
+
class_name: class_name,
|
|
61
|
+
version: version
|
|
62
|
+
)
|
|
57
63
|
else
|
|
58
64
|
super
|
|
59
65
|
end
|
|
60
66
|
end
|
|
61
67
|
|
|
62
|
-
def
|
|
63
|
-
|
|
64
|
-
"ActiveRecord::Migration[#{ActiveRecord::Migration.current_version}]"
|
|
65
|
-
else
|
|
66
|
-
"ActiveRecord::Migration"
|
|
67
|
-
end
|
|
68
|
+
def active_record_migration_class
|
|
69
|
+
migration_helper.active_record_migration_class
|
|
68
70
|
end
|
|
69
71
|
|
|
70
72
|
def formatted_name
|
|
71
|
-
|
|
72
|
-
"\"#{singular_name}\""
|
|
73
|
-
else
|
|
74
|
-
":#{singular_name}"
|
|
75
|
-
end
|
|
73
|
+
NameHelper.format_for_migration(singular_name)
|
|
76
74
|
end
|
|
77
75
|
|
|
78
76
|
def formatted_table_name
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
if name.nil?
|
|
82
|
-
raise(
|
|
83
|
-
ArgumentError,
|
|
84
|
-
"Either `table_name:NAME` or `on:NAME` must be specified"
|
|
85
|
-
)
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
if name.include?(".")
|
|
89
|
-
"\"#{name}\""
|
|
90
|
-
else
|
|
91
|
-
":#{name}"
|
|
92
|
-
end
|
|
77
|
+
NameHelper.format_table_name_from_hash(table_name)
|
|
93
78
|
end
|
|
94
79
|
end
|
|
95
80
|
|
|
96
81
|
private
|
|
97
82
|
|
|
98
|
-
def
|
|
99
|
-
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
def updating_existing_trigger?
|
|
103
|
-
previous_version > 0
|
|
83
|
+
def trigger_definition_path
|
|
84
|
+
@_trigger_definition_path ||= Rails.root.join(*DEFINITION_PATH)
|
|
104
85
|
end
|
|
105
86
|
|
|
106
|
-
def
|
|
107
|
-
Fx::
|
|
87
|
+
def version_helper
|
|
88
|
+
@_version_helper ||= Fx::Generators::VersionHelper.new(
|
|
89
|
+
file_name: file_name,
|
|
90
|
+
definition_path: trigger_definition_path
|
|
91
|
+
)
|
|
108
92
|
end
|
|
109
93
|
|
|
110
|
-
def
|
|
111
|
-
@
|
|
94
|
+
def migration_helper
|
|
95
|
+
@_migration_helper ||= Fx::Generators::MigrationHelper.new(options)
|
|
112
96
|
end
|
|
113
97
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
def skip_migration_creation?
|
|
117
|
-
!migration
|
|
98
|
+
def definition
|
|
99
|
+
version_helper.definition_for_version(version: version, type: :trigger)
|
|
118
100
|
end
|
|
119
101
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
options[:migration] != false
|
|
102
|
+
def updating_existing_trigger?
|
|
103
|
+
version_helper.updating_existing?
|
|
123
104
|
end
|
|
124
105
|
end
|
|
125
106
|
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
module Fx
|
|
2
|
+
module Generators
|
|
3
|
+
# @api private
|
|
4
|
+
class VersionHelper
|
|
5
|
+
def initialize(file_name:, definition_path:)
|
|
6
|
+
@file_name = file_name
|
|
7
|
+
@definition_path = definition_path
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def previous_version
|
|
11
|
+
@previous_version ||= existing_versions.max || 0
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def current_version
|
|
15
|
+
previous_version.next
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def updating_existing?
|
|
19
|
+
previous_version > 0
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def creating_new?
|
|
23
|
+
previous_version == 0
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def definition_for_version(version:, type:)
|
|
27
|
+
case type
|
|
28
|
+
when :function
|
|
29
|
+
Fx::Definition.function(name: file_name, version: version)
|
|
30
|
+
when :trigger
|
|
31
|
+
Fx::Definition.trigger(name: file_name, version: version)
|
|
32
|
+
else
|
|
33
|
+
raise(
|
|
34
|
+
ArgumentError,
|
|
35
|
+
"Unknown type: #{type}. Must be :function or :trigger"
|
|
36
|
+
)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
private
|
|
41
|
+
|
|
42
|
+
VERSION_PATTERN = /v(\d+)/
|
|
43
|
+
private_constant :VERSION_PATTERN
|
|
44
|
+
|
|
45
|
+
attr_reader :file_name, :definition_path
|
|
46
|
+
|
|
47
|
+
def existing_versions
|
|
48
|
+
Dir
|
|
49
|
+
.glob("#{file_name}_v*.sql", base: definition_path)
|
|
50
|
+
.map { |file| file[VERSION_PATTERN, 1].to_i }
|
|
51
|
+
.compact
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -3,14 +3,14 @@ require "acceptance_helper"
|
|
|
3
3
|
RSpec.describe "User manages functions" do
|
|
4
4
|
it "handles simple functions" do
|
|
5
5
|
successfully "rails generate fx:function test"
|
|
6
|
-
write_function_definition "test_v01", <<~
|
|
6
|
+
write_function_definition "test_v01", <<~SQL
|
|
7
7
|
CREATE OR REPLACE FUNCTION test()
|
|
8
8
|
RETURNS text AS $$
|
|
9
9
|
BEGIN
|
|
10
10
|
RETURN 'test';
|
|
11
11
|
END;
|
|
12
12
|
$$ LANGUAGE plpgsql;
|
|
13
|
-
|
|
13
|
+
SQL
|
|
14
14
|
successfully "rake db:migrate"
|
|
15
15
|
|
|
16
16
|
result = execute("SELECT * FROM test() AS result")
|
|
@@ -21,14 +21,14 @@ RSpec.describe "User manages functions" do
|
|
|
21
21
|
"db/functions/test_v01.sql",
|
|
22
22
|
"db/functions/test_v02.sql"
|
|
23
23
|
)
|
|
24
|
-
write_function_definition "test_v02", <<~
|
|
24
|
+
write_function_definition "test_v02", <<~SQL
|
|
25
25
|
CREATE OR REPLACE FUNCTION test()
|
|
26
26
|
RETURNS text AS $$
|
|
27
27
|
BEGIN
|
|
28
28
|
RETURN 'testest';
|
|
29
29
|
END;
|
|
30
30
|
$$ LANGUAGE plpgsql;
|
|
31
|
-
|
|
31
|
+
SQL
|
|
32
32
|
successfully "rake db:migrate"
|
|
33
33
|
|
|
34
34
|
result = execute("SELECT * FROM test() AS result")
|
|
@@ -37,14 +37,14 @@ RSpec.describe "User manages functions" do
|
|
|
37
37
|
|
|
38
38
|
it "handles functions with arguments" do
|
|
39
39
|
successfully "rails generate fx:function adder"
|
|
40
|
-
write_function_definition "adder_v01", <<~
|
|
40
|
+
write_function_definition "adder_v01", <<~SQL
|
|
41
41
|
CREATE FUNCTION adder(x int, y int)
|
|
42
42
|
RETURNS int AS $$
|
|
43
43
|
BEGIN
|
|
44
44
|
RETURN $1 + $2;
|
|
45
45
|
END;
|
|
46
46
|
$$ LANGUAGE plpgsql;
|
|
47
|
-
|
|
47
|
+
SQL
|
|
48
48
|
successfully "rake db:migrate"
|
|
49
49
|
|
|
50
50
|
result = execute("SELECT * FROM adder(1, 2) AS result")
|
|
@@ -4,7 +4,7 @@ RSpec.describe "User manages triggers" do
|
|
|
4
4
|
it "handles simple triggers" do
|
|
5
5
|
successfully "rails generate model user name:string upper_name:string"
|
|
6
6
|
successfully "rails generate fx:function uppercase_users_name"
|
|
7
|
-
write_function_definition "uppercase_users_name_v01", <<~
|
|
7
|
+
write_function_definition "uppercase_users_name_v01", <<~SQL
|
|
8
8
|
CREATE OR REPLACE FUNCTION uppercase_users_name()
|
|
9
9
|
RETURNS trigger AS $$
|
|
10
10
|
BEGIN
|
|
@@ -12,38 +12,38 @@ RSpec.describe "User manages triggers" do
|
|
|
12
12
|
RETURN NEW;
|
|
13
13
|
END;
|
|
14
14
|
$$ LANGUAGE plpgsql;
|
|
15
|
-
|
|
15
|
+
SQL
|
|
16
16
|
successfully "rails generate fx:trigger uppercase_users_name table_name:users"
|
|
17
|
-
write_trigger_definition "uppercase_users_name_v01", <<~
|
|
17
|
+
write_trigger_definition "uppercase_users_name_v01", <<~SQL
|
|
18
18
|
CREATE TRIGGER uppercase_users_name
|
|
19
19
|
BEFORE INSERT ON users
|
|
20
20
|
FOR EACH ROW
|
|
21
21
|
EXECUTE FUNCTION uppercase_users_name();
|
|
22
|
-
|
|
22
|
+
SQL
|
|
23
23
|
successfully "rake db:migrate"
|
|
24
24
|
|
|
25
|
-
execute <<~
|
|
25
|
+
execute <<~SQL
|
|
26
26
|
INSERT INTO users
|
|
27
27
|
(name, created_at, updated_at)
|
|
28
28
|
VALUES
|
|
29
29
|
('Bob', NOW(), NOW());
|
|
30
|
-
|
|
30
|
+
SQL
|
|
31
31
|
result = execute("SELECT upper_name FROM users WHERE name = 'Bob';")
|
|
32
32
|
expect(result).to eq("upper_name" => "BOB")
|
|
33
33
|
|
|
34
34
|
successfully "rails generate fx:trigger uppercase_users_name table_name:users"
|
|
35
|
-
write_trigger_definition "uppercase_users_name_v02", <<~
|
|
35
|
+
write_trigger_definition "uppercase_users_name_v02", <<~SQL
|
|
36
36
|
CREATE TRIGGER uppercase_users_name
|
|
37
37
|
BEFORE UPDATE ON users
|
|
38
38
|
FOR EACH ROW
|
|
39
39
|
EXECUTE FUNCTION uppercase_users_name();
|
|
40
|
-
|
|
40
|
+
SQL
|
|
41
41
|
successfully "rake db:migrate"
|
|
42
|
-
execute <<~
|
|
42
|
+
execute <<~SQL
|
|
43
43
|
UPDATE users
|
|
44
44
|
SET name = 'Alice'
|
|
45
45
|
WHERE id = 1;
|
|
46
|
-
|
|
46
|
+
SQL
|
|
47
47
|
|
|
48
48
|
result = execute("SELECT upper_name FROM users WHERE name = 'Alice';")
|
|
49
49
|
expect(result).to eq("upper_name" => "ALICE")
|
data/spec/acceptance_helper.rb
CHANGED
|
@@ -13,8 +13,8 @@ RSpec.configure do |config|
|
|
|
13
13
|
Dir.chdir("spec/dummy") do
|
|
14
14
|
system <<~CMD
|
|
15
15
|
git init -b master 1>/dev/null &&
|
|
16
|
-
git config user.email "fx@example.com"
|
|
17
|
-
git config user.name "Fx"
|
|
16
|
+
git config user.email "fx@example.com" &&
|
|
17
|
+
git config user.name "Fx" &&
|
|
18
18
|
git add -A &&
|
|
19
19
|
git commit --no-gpg-sign --message 'initial' 1>/dev/null
|
|
20
20
|
CMD
|
|
@@ -2,14 +2,14 @@ require "spec_helper"
|
|
|
2
2
|
|
|
3
3
|
RSpec.describe "Function migrations", :db do
|
|
4
4
|
around do |example|
|
|
5
|
-
sql_definition = <<~
|
|
5
|
+
sql_definition = <<~SQL
|
|
6
6
|
CREATE OR REPLACE FUNCTION test()
|
|
7
7
|
RETURNS text AS $$
|
|
8
8
|
BEGIN
|
|
9
9
|
RETURN 'test';
|
|
10
10
|
END;
|
|
11
11
|
$$ LANGUAGE plpgsql;
|
|
12
|
-
|
|
12
|
+
SQL
|
|
13
13
|
with_function_definition(name: :test, sql_definition: sql_definition) do
|
|
14
14
|
example.run
|
|
15
15
|
end
|
|
@@ -40,14 +40,14 @@ RSpec.describe "Function migrations", :db do
|
|
|
40
40
|
it "can run migrations that updates functions" do
|
|
41
41
|
connection.create_function(:test)
|
|
42
42
|
|
|
43
|
-
sql_definition = <<~
|
|
43
|
+
sql_definition = <<~SQL
|
|
44
44
|
CREATE OR REPLACE FUNCTION test()
|
|
45
45
|
RETURNS text AS $$
|
|
46
46
|
BEGIN
|
|
47
47
|
RETURN 'testest';
|
|
48
48
|
END;
|
|
49
49
|
$$ LANGUAGE plpgsql;
|
|
50
|
-
|
|
50
|
+
SQL
|
|
51
51
|
with_function_definition(
|
|
52
52
|
name: :test,
|
|
53
53
|
version: 2,
|
|
@@ -2,14 +2,14 @@ require "spec_helper"
|
|
|
2
2
|
|
|
3
3
|
RSpec.describe "Reverting migrations", :db do
|
|
4
4
|
around do |example|
|
|
5
|
-
sql_definition = <<~
|
|
5
|
+
sql_definition = <<~SQL
|
|
6
6
|
CREATE OR REPLACE FUNCTION test()
|
|
7
7
|
RETURNS text AS $$
|
|
8
8
|
BEGIN
|
|
9
9
|
RETURN 'test';
|
|
10
10
|
END;
|
|
11
11
|
$$ LANGUAGE plpgsql;
|
|
12
|
-
|
|
12
|
+
SQL
|
|
13
13
|
with_function_definition(name: :test, sql_definition: sql_definition) do
|
|
14
14
|
example.run
|
|
15
15
|
end
|
|
@@ -50,14 +50,14 @@ RSpec.describe "Reverting migrations", :db do
|
|
|
50
50
|
it "can run reversible migrations for updating functions" do
|
|
51
51
|
connection.create_function(:test)
|
|
52
52
|
|
|
53
|
-
sql_definition = <<~
|
|
53
|
+
sql_definition = <<~SQL
|
|
54
54
|
CREATE OR REPLACE FUNCTION test()
|
|
55
55
|
RETURNS text AS $$
|
|
56
56
|
BEGIN
|
|
57
57
|
RETURN 'bar';
|
|
58
58
|
END;
|
|
59
59
|
$$ LANGUAGE plpgsql;
|
|
60
|
-
|
|
60
|
+
SQL
|
|
61
61
|
with_function_definition(
|
|
62
62
|
name: :test,
|
|
63
63
|
version: 2,
|
|
@@ -2,14 +2,14 @@ require "spec_helper"
|
|
|
2
2
|
|
|
3
3
|
RSpec.describe "Trigger migrations", :db do
|
|
4
4
|
around do |example|
|
|
5
|
-
connection.execute <<~
|
|
5
|
+
connection.execute <<~SQL
|
|
6
6
|
CREATE TABLE users (
|
|
7
7
|
id int PRIMARY KEY,
|
|
8
8
|
name varchar(256),
|
|
9
9
|
upper_name varchar(256)
|
|
10
10
|
);
|
|
11
|
-
|
|
12
|
-
Fx.database.create_function <<~
|
|
11
|
+
SQL
|
|
12
|
+
Fx.database.create_function <<~SQL
|
|
13
13
|
CREATE OR REPLACE FUNCTION uppercase_users_name()
|
|
14
14
|
RETURNS trigger AS $$
|
|
15
15
|
BEGIN
|
|
@@ -17,13 +17,13 @@ RSpec.describe "Trigger migrations", :db do
|
|
|
17
17
|
RETURN NEW;
|
|
18
18
|
END;
|
|
19
19
|
$$ LANGUAGE plpgsql;
|
|
20
|
-
|
|
21
|
-
sql_definition = <<~
|
|
20
|
+
SQL
|
|
21
|
+
sql_definition = <<~SQL
|
|
22
22
|
CREATE TRIGGER uppercase_users_name
|
|
23
23
|
BEFORE INSERT ON users
|
|
24
24
|
FOR EACH ROW
|
|
25
25
|
EXECUTE FUNCTION uppercase_users_name();
|
|
26
|
-
|
|
26
|
+
SQL
|
|
27
27
|
with_trigger_definition(
|
|
28
28
|
name: :uppercase_users_name,
|
|
29
29
|
sql_definition: sql_definition
|
|
@@ -2,14 +2,14 @@ require "spec_helper"
|
|
|
2
2
|
|
|
3
3
|
RSpec.describe "Reverting migrations", :db do
|
|
4
4
|
around do |example|
|
|
5
|
-
connection.execute <<~
|
|
5
|
+
connection.execute <<~SQL
|
|
6
6
|
CREATE TABLE users (
|
|
7
7
|
id int PRIMARY KEY,
|
|
8
8
|
name varchar(256),
|
|
9
9
|
upper_name varchar(256)
|
|
10
10
|
);
|
|
11
|
-
|
|
12
|
-
Fx.database.create_function <<~
|
|
11
|
+
SQL
|
|
12
|
+
Fx.database.create_function <<~SQL
|
|
13
13
|
CREATE OR REPLACE FUNCTION uppercase_users_name()
|
|
14
14
|
RETURNS trigger AS $$
|
|
15
15
|
BEGIN
|
|
@@ -17,13 +17,13 @@ RSpec.describe "Reverting migrations", :db do
|
|
|
17
17
|
RETURN NEW;
|
|
18
18
|
END;
|
|
19
19
|
$$ LANGUAGE plpgsql;
|
|
20
|
-
|
|
21
|
-
sql_definition = <<~
|
|
20
|
+
SQL
|
|
21
|
+
sql_definition = <<~SQL
|
|
22
22
|
CREATE TRIGGER uppercase_users_name
|
|
23
23
|
BEFORE INSERT ON users
|
|
24
24
|
FOR EACH ROW
|
|
25
25
|
EXECUTE FUNCTION uppercase_users_name();
|
|
26
|
-
|
|
26
|
+
SQL
|
|
27
27
|
with_trigger_definition(
|
|
28
28
|
name: :uppercase_users_name,
|
|
29
29
|
sql_definition: sql_definition
|
|
@@ -67,12 +67,12 @@ RSpec.describe "Reverting migrations", :db do
|
|
|
67
67
|
it "can run reversible migrations for updating triggers" do
|
|
68
68
|
connection.create_trigger(:uppercase_users_name)
|
|
69
69
|
|
|
70
|
-
sql_definition = <<~
|
|
70
|
+
sql_definition = <<~SQL
|
|
71
71
|
CREATE TRIGGER uppercase_users_name
|
|
72
72
|
BEFORE UPDATE ON users
|
|
73
73
|
FOR EACH ROW
|
|
74
74
|
EXECUTE FUNCTION uppercase_users_name();
|
|
75
|
-
|
|
75
|
+
SQL
|
|
76
76
|
with_trigger_definition(
|
|
77
77
|
name: :uppercase_users_name,
|
|
78
78
|
sql_definition: sql_definition,
|
|
@@ -4,21 +4,21 @@ RSpec.describe Fx::Adapters::Postgres::Functions, :db do
|
|
|
4
4
|
describe ".all" do
|
|
5
5
|
it "returns `Function` objects" do
|
|
6
6
|
connection = ActiveRecord::Base.connection
|
|
7
|
-
connection.execute <<~
|
|
7
|
+
connection.execute <<~SQL
|
|
8
8
|
CREATE OR REPLACE FUNCTION test()
|
|
9
9
|
RETURNS text AS $$
|
|
10
10
|
BEGIN
|
|
11
11
|
RETURN 'test';
|
|
12
12
|
END;
|
|
13
13
|
$$ LANGUAGE plpgsql;
|
|
14
|
-
|
|
14
|
+
SQL
|
|
15
15
|
|
|
16
|
-
functions = Fx::Adapters::Postgres::Functions.
|
|
16
|
+
functions = Fx::Adapters::Postgres::Functions.all(connection)
|
|
17
17
|
|
|
18
18
|
first = functions.first
|
|
19
19
|
expect(functions.size).to eq(1)
|
|
20
20
|
expect(first.name).to eq("test")
|
|
21
|
-
expect(first.definition).to eq(<<~
|
|
21
|
+
expect(first.definition).to eq(<<~SQL)
|
|
22
22
|
CREATE OR REPLACE FUNCTION public.test()
|
|
23
23
|
RETURNS text
|
|
24
24
|
LANGUAGE plpgsql
|
|
@@ -27,7 +27,14 @@ RSpec.describe Fx::Adapters::Postgres::Functions, :db do
|
|
|
27
27
|
RETURN 'test';
|
|
28
28
|
END;
|
|
29
29
|
$function$
|
|
30
|
-
|
|
30
|
+
SQL
|
|
31
|
+
|
|
32
|
+
connection.execute "CREATE SCHEMA IF NOT EXISTS other;"
|
|
33
|
+
connection.execute "SET search_path = 'other';"
|
|
34
|
+
|
|
35
|
+
functions = Fx::Adapters::Postgres::Functions.all(connection)
|
|
36
|
+
|
|
37
|
+
expect(functions).to be_empty
|
|
31
38
|
end
|
|
32
39
|
end
|
|
33
40
|
end
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
require "spec_helper"
|
|
2
|
+
|
|
3
|
+
RSpec.describe Fx::Adapters::Postgres::QueryExecutor, :db do
|
|
4
|
+
it "executes the query and maps results to objects" do
|
|
5
|
+
connection = ActiveRecord::Base.connection
|
|
6
|
+
query = "SELECT 'Hello World' as message, 'english' as language"
|
|
7
|
+
greeter = Class.new do
|
|
8
|
+
attr_reader :message, :language
|
|
9
|
+
|
|
10
|
+
def initialize(row)
|
|
11
|
+
@message = row.fetch("message")
|
|
12
|
+
@language = row.fetch("language")
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
results = described_class.call(
|
|
17
|
+
connection: connection,
|
|
18
|
+
query: query,
|
|
19
|
+
model_class: greeter
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
expect(results.size).to eq(1)
|
|
23
|
+
expect(results.first).to be_a(greeter)
|
|
24
|
+
expect(results.first.message).to eq("Hello World")
|
|
25
|
+
expect(results.first.language).to eq("english")
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
it "executes query with multiple results" do
|
|
29
|
+
connection = ActiveRecord::Base.connection
|
|
30
|
+
query = <<~SQL
|
|
31
|
+
SELECT 'first' as name
|
|
32
|
+
UNION ALL
|
|
33
|
+
SELECT 'second' as name
|
|
34
|
+
ORDER BY name
|
|
35
|
+
SQL
|
|
36
|
+
simple_name = Class.new do
|
|
37
|
+
attr_reader :name
|
|
38
|
+
|
|
39
|
+
def initialize(row)
|
|
40
|
+
@name = row.fetch("name")
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
results = described_class.call(
|
|
45
|
+
connection: connection,
|
|
46
|
+
query: query,
|
|
47
|
+
model_class: simple_name
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
expect(results.size).to eq(2)
|
|
51
|
+
expect(results).to all(be_a(simple_name))
|
|
52
|
+
expect(results.first.name).to eq("first")
|
|
53
|
+
expect(results.last.name).to eq("second")
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
it "returns an empty array when query returns no results" do
|
|
57
|
+
connection = ActiveRecord::Base.connection
|
|
58
|
+
query = "SELECT 'test' as name WHERE 1 = 0"
|
|
59
|
+
simple_name = Class.new do
|
|
60
|
+
attr_reader :name
|
|
61
|
+
|
|
62
|
+
def initialize(row)
|
|
63
|
+
@name = row.fetch("name")
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
results = described_class.call(
|
|
68
|
+
connection: connection,
|
|
69
|
+
query: query,
|
|
70
|
+
model_class: simple_name
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
expect(results).to eq([])
|
|
74
|
+
end
|
|
75
|
+
end
|
|
@@ -4,14 +4,14 @@ RSpec.describe Fx::Adapters::Postgres::Triggers, :db do
|
|
|
4
4
|
describe ".all" do
|
|
5
5
|
it "returns `Trigger` objects" do
|
|
6
6
|
connection = ActiveRecord::Base.connection
|
|
7
|
-
connection.execute <<~
|
|
7
|
+
connection.execute <<~SQL
|
|
8
8
|
CREATE TABLE users (
|
|
9
9
|
id int PRIMARY KEY,
|
|
10
10
|
name varchar(256),
|
|
11
11
|
upper_name varchar(256)
|
|
12
12
|
);
|
|
13
|
-
|
|
14
|
-
connection.execute <<~
|
|
13
|
+
SQL
|
|
14
|
+
connection.execute <<~SQL
|
|
15
15
|
CREATE OR REPLACE FUNCTION uppercase_users_name()
|
|
16
16
|
RETURNS trigger AS $$
|
|
17
17
|
BEGIN
|
|
@@ -19,15 +19,15 @@ RSpec.describe Fx::Adapters::Postgres::Triggers, :db do
|
|
|
19
19
|
RETURN NEW;
|
|
20
20
|
END;
|
|
21
21
|
$$ LANGUAGE plpgsql;
|
|
22
|
-
|
|
23
|
-
connection.execute <<~
|
|
22
|
+
SQL
|
|
23
|
+
connection.execute <<~SQL
|
|
24
24
|
CREATE TRIGGER uppercase_users_name
|
|
25
25
|
BEFORE INSERT ON users
|
|
26
26
|
FOR EACH ROW
|
|
27
27
|
EXECUTE FUNCTION uppercase_users_name();
|
|
28
|
-
|
|
28
|
+
SQL
|
|
29
29
|
|
|
30
|
-
triggers = Fx::Adapters::Postgres::Triggers.
|
|
30
|
+
triggers = Fx::Adapters::Postgres::Triggers.all(connection)
|
|
31
31
|
|
|
32
32
|
first = triggers.first
|
|
33
33
|
expect(triggers.size).to eq(1)
|
|
@@ -36,6 +36,13 @@ RSpec.describe Fx::Adapters::Postgres::Triggers, :db do
|
|
|
36
36
|
expect(first.definition).to match(/ON [public.ser|]/)
|
|
37
37
|
expect(first.definition).to include("FOR EACH ROW")
|
|
38
38
|
expect(first.definition).to include("EXECUTE FUNCTION uppercase_users_name()")
|
|
39
|
+
|
|
40
|
+
connection.execute "CREATE SCHEMA IF NOT EXISTS other;"
|
|
41
|
+
connection.execute "SET search_path = 'other';"
|
|
42
|
+
|
|
43
|
+
triggers = Fx::Adapters::Postgres::Triggers.all(connection)
|
|
44
|
+
|
|
45
|
+
expect(triggers).to be_empty
|
|
39
46
|
end
|
|
40
47
|
end
|
|
41
48
|
end
|