pg_objects 1.0.3 → 1.2.1
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/dependabot.yml +11 -0
- data/.github/workflows/ci.yml +1 -1
- data/.rubocop.yml +4 -1
- data/Gemfile +3 -1
- data/Gemfile.lock +189 -74
- data/lib/generators/pg_objects/install/install_generator.rb +7 -9
- data/lib/pg_objects/config.rb +8 -10
- data/lib/pg_objects/container.rb +30 -0
- data/lib/pg_objects/db_object.rb +40 -22
- data/lib/pg_objects/db_object_factory.rb +13 -0
- data/lib/pg_objects/logger.rb +7 -14
- data/lib/pg_objects/manager.rb +51 -51
- data/lib/pg_objects/parsed_object/aggregate.rb +8 -0
- data/lib/pg_objects/parsed_object/base.rb +16 -0
- data/lib/pg_objects/parsed_object/conversion.rb +8 -0
- data/lib/pg_objects/parsed_object/event_trigger.rb +8 -0
- data/lib/pg_objects/parsed_object/function.rb +8 -0
- data/lib/pg_objects/parsed_object/materialized_view.rb +8 -0
- data/lib/pg_objects/parsed_object/operator.rb +8 -0
- data/lib/pg_objects/parsed_object/operator_class.rb +8 -0
- data/lib/pg_objects/parsed_object/table.rb +8 -0
- data/lib/pg_objects/parsed_object/text_search_parser.rb +8 -0
- data/lib/pg_objects/parsed_object/text_search_template.rb +8 -0
- data/lib/pg_objects/parsed_object/trigger.rb +8 -0
- data/lib/pg_objects/parsed_object/type.rb +8 -0
- data/lib/pg_objects/parsed_object/view.rb +8 -0
- data/lib/pg_objects/parsed_object.rb +18 -0
- data/lib/pg_objects/parsed_object_factory.rb +103 -0
- data/lib/pg_objects/parser.rb +46 -43
- data/lib/pg_objects/railtie.rb +5 -10
- data/lib/pg_objects/version.rb +1 -1
- data/lib/pg_objects.rb +9 -0
- data/pg_objects.gemspec +9 -4
- metadata +100 -11
data/lib/pg_objects/manager.rb
CHANGED
@@ -1,69 +1,69 @@
|
|
1
|
-
|
1
|
+
##
|
2
|
+
# Manages process to create objects
|
3
|
+
#
|
4
|
+
# Usage:
|
5
|
+
#
|
6
|
+
# Manager.new(config, logger).load_files(:before).create_objects
|
7
|
+
#
|
8
|
+
# or
|
9
|
+
#
|
10
|
+
# Manager.new(config, logger).load_files(:after).create_objects
|
11
|
+
class PgObjects::Manager
|
12
|
+
include Import['db_object_factory', 'config', 'logger']
|
13
|
+
|
2
14
|
##
|
3
|
-
#
|
4
|
-
#
|
5
|
-
# Usage:
|
6
|
-
#
|
7
|
-
# Manager.new.load_files(:before).create_objects
|
15
|
+
# event: +:before+ or +:after+
|
8
16
|
#
|
9
|
-
#
|
10
|
-
|
11
|
-
|
12
|
-
class Manager
|
13
|
-
attr_reader :objects, :config, :log
|
14
|
-
|
15
|
-
def initialize
|
16
|
-
raise UnsupportedAdapterError if ActiveRecord::Base.connection.adapter_name != 'PostgreSQL'
|
17
|
+
# used to reference configuration settings +before_path+ and +after_path+
|
18
|
+
def load_files(event)
|
19
|
+
validate_workability
|
17
20
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
+
dir = config.send "#{event}_path"
|
22
|
+
Dir[File.join(dir, '**', "*.{#{config.extensions.join(',')}}")].each do |path|
|
23
|
+
objects << db_object_factory.create_instance(path)
|
21
24
|
end
|
22
25
|
|
23
|
-
|
24
|
-
|
25
|
-
#
|
26
|
-
# used to reference configuration settings +before_path+ and +after_path+
|
27
|
-
def load_files(event)
|
28
|
-
dir = config.send "#{event}_path"
|
29
|
-
Dir[File.join(dir, '**', "*.{#{config.extensions.join(',')}}")].each do |path|
|
30
|
-
@objects << PgObjects::DbObject.new(path)
|
31
|
-
end
|
32
|
-
|
33
|
-
self
|
34
|
-
end
|
26
|
+
self
|
27
|
+
end
|
35
28
|
|
36
|
-
|
37
|
-
|
38
|
-
|
29
|
+
def create_objects
|
30
|
+
objects.each { create_object(_1) }
|
31
|
+
end
|
39
32
|
|
40
|
-
|
33
|
+
def objects
|
34
|
+
@objects ||= []
|
35
|
+
end
|
41
36
|
|
42
|
-
|
43
|
-
return if obj.status == :done
|
44
|
-
raise CyclicDependencyError, obj.name if obj.status == :processing
|
37
|
+
private
|
45
38
|
|
46
|
-
|
39
|
+
def validate_workability
|
40
|
+
raise PgObjects::UnsupportedAdapterError if ActiveRecord::Base.connection.adapter_name != 'PostgreSQL'
|
41
|
+
end
|
47
42
|
|
48
|
-
|
43
|
+
def create_object(obj)
|
44
|
+
return if obj.status == :done
|
45
|
+
raise PgObjects::CyclicDependencyError, obj.name if obj.status == :processing
|
49
46
|
|
50
|
-
|
51
|
-
ActiveRecord::Base.connection.execute obj.sql_query
|
47
|
+
obj.status = :processing
|
52
48
|
|
53
|
-
|
54
|
-
end
|
49
|
+
create_dependencies(obj.dependencies)
|
55
50
|
|
56
|
-
|
57
|
-
|
58
|
-
end
|
51
|
+
logger.write("creating #{obj.name}")
|
52
|
+
ActiveRecord::Base.connection.execute(obj.sql_query)
|
59
53
|
|
60
|
-
|
61
|
-
|
54
|
+
obj.status = :done
|
55
|
+
end
|
62
56
|
|
63
|
-
|
64
|
-
|
57
|
+
def create_dependencies(dependencies)
|
58
|
+
dependencies.each { |dep_name| create_object(find_object(dep_name)) }
|
59
|
+
end
|
65
60
|
|
66
|
-
|
67
|
-
|
61
|
+
def find_object(dep_name)
|
62
|
+
result = objects.select { |obj| [obj.name, obj.full_name, obj.object_name].compact.include? dep_name }
|
63
|
+
|
64
|
+
raise PgObjects::AmbiguousDependencyError, dep_name if result.size > 1
|
65
|
+
raise PgObjects::DependencyNotExistError, dep_name if result.empty?
|
66
|
+
|
67
|
+
result[0]
|
68
68
|
end
|
69
69
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module PgObjects::ParsedObject # rubocop: disable Style/Documentation
|
2
|
+
end
|
3
|
+
|
4
|
+
require 'pg_objects/parsed_object/base'
|
5
|
+
require 'pg_objects/parsed_object/aggregate'
|
6
|
+
require 'pg_objects/parsed_object/conversion'
|
7
|
+
require 'pg_objects/parsed_object/event_trigger'
|
8
|
+
require 'pg_objects/parsed_object/function'
|
9
|
+
require 'pg_objects/parsed_object/materialized_view'
|
10
|
+
require 'pg_objects/parsed_object/operator'
|
11
|
+
require 'pg_objects/parsed_object/operator_class'
|
12
|
+
require 'pg_objects/parsed_object/table'
|
13
|
+
require 'pg_objects/parsed_object/text_search_parser'
|
14
|
+
require 'pg_objects/parsed_object/text_search_template'
|
15
|
+
require 'pg_objects/parsed_object/trigger'
|
16
|
+
require 'pg_objects/parsed_object/type'
|
17
|
+
require 'pg_objects/parsed_object/view'
|
18
|
+
require 'pg_objects/parsed_object_factory'
|
@@ -0,0 +1,103 @@
|
|
1
|
+
#
|
2
|
+
# Returns an object of the respective class based on the provided parsed query
|
3
|
+
#
|
4
|
+
class PgObjects::ParsedObjectFactory
|
5
|
+
class << self
|
6
|
+
include Dry::Monads[:try, :result]
|
7
|
+
|
8
|
+
SUPPORTED_TYPES = %i[
|
9
|
+
aggregate
|
10
|
+
conversion
|
11
|
+
event_trigger
|
12
|
+
function
|
13
|
+
materialized_view
|
14
|
+
operator
|
15
|
+
operator_class
|
16
|
+
table
|
17
|
+
text_search_parser
|
18
|
+
text_search_template
|
19
|
+
trigger
|
20
|
+
type
|
21
|
+
view
|
22
|
+
].freeze
|
23
|
+
|
24
|
+
def create_object(input_data)
|
25
|
+
@input_data = input_data
|
26
|
+
@stmt = input_data.tree.stmts[0].stmt
|
27
|
+
|
28
|
+
determine_class.new(stmt)
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
attr_reader :stmt, :input_data
|
34
|
+
|
35
|
+
def determine_class
|
36
|
+
SUPPORTED_TYPES.each do |type|
|
37
|
+
return class_for(type) if send("#{type}?")
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def class_for(type)
|
42
|
+
"PgObjects::ParsedObject::#{type.to_s.classify}".constantize
|
43
|
+
end
|
44
|
+
|
45
|
+
def aggregate?
|
46
|
+
try_check_get_result { stmt.define_stmt.kind == :OBJECT_AGGREGATE }
|
47
|
+
end
|
48
|
+
|
49
|
+
def conversion?
|
50
|
+
try_check_get_result { stmt.create_conversion_stmt.conversion_name.present? }
|
51
|
+
end
|
52
|
+
|
53
|
+
def event_trigger?
|
54
|
+
try_check_get_result { stmt.create_event_trig_stmt.trigname.present? }
|
55
|
+
end
|
56
|
+
|
57
|
+
def function?
|
58
|
+
try_check_get_result { stmt.create_function_stmt.funcname.present? }
|
59
|
+
end
|
60
|
+
|
61
|
+
def materialized_view?
|
62
|
+
try_check_get_result { stmt.create_table_as_stmt.objtype == :OBJECT_MATVIEW }
|
63
|
+
end
|
64
|
+
|
65
|
+
def operator_class?
|
66
|
+
try_check_get_result { stmt.create_op_class_stmt.opclassname.present? }
|
67
|
+
end
|
68
|
+
|
69
|
+
def operator?
|
70
|
+
try_check_get_result { stmt.define_stmt.kind == :OBJECT_OPERATOR }
|
71
|
+
end
|
72
|
+
|
73
|
+
def table?
|
74
|
+
try_check_get_result { stmt.create_stmt.table_elts.present? }
|
75
|
+
end
|
76
|
+
|
77
|
+
def text_search_parser?
|
78
|
+
try_check_get_result { stmt.define_stmt.kind == :OBJECT_TSPARSER }
|
79
|
+
end
|
80
|
+
|
81
|
+
def text_search_template?
|
82
|
+
try_check_get_result { stmt.define_stmt.kind == :OBJECT_TSTEMPLATE }
|
83
|
+
end
|
84
|
+
|
85
|
+
def trigger?
|
86
|
+
try_check_get_result { stmt.create_trig_stmt.trigname.present? }
|
87
|
+
end
|
88
|
+
|
89
|
+
def type?
|
90
|
+
try_check_get_result { stmt.composite_type_stmt.typevar.present? }
|
91
|
+
end
|
92
|
+
|
93
|
+
def view?
|
94
|
+
try_check_get_result { stmt.view_stmt.view.present? }
|
95
|
+
end
|
96
|
+
|
97
|
+
def try_check_get_result(&)
|
98
|
+
result = Try(&).to_result
|
99
|
+
|
100
|
+
result.success? && result.value!
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
data/lib/pg_objects/parser.rb
CHANGED
@@ -1,47 +1,50 @@
|
|
1
1
|
require 'pg_query'
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
3
|
+
##
|
4
|
+
# Reads directives from SQL-comments
|
5
|
+
#
|
6
|
+
# --!depends_on [name_of_dependency]
|
7
|
+
#
|
8
|
+
# name_of_dependency: short or full name of object as well as object_name
|
9
|
+
#
|
10
|
+
class PgObjects::Parser
|
11
|
+
include Import['parsed_object_factory']
|
12
|
+
include Memery
|
13
|
+
|
14
|
+
PG_ENTITIES = %i[operator_class trigger define_statement conversion event_trigger type function table].freeze
|
15
|
+
|
16
|
+
def load(source)
|
17
|
+
@source = source
|
18
|
+
self
|
19
|
+
end
|
20
|
+
|
21
|
+
def fetch_directives
|
22
|
+
{
|
23
|
+
depends_on: fetch_dependencies
|
24
|
+
}
|
25
|
+
end
|
26
|
+
|
27
|
+
def fetch_object_name
|
28
|
+
parse_query
|
29
|
+
parsed_object.name
|
30
|
+
rescue PgQuery::ParseError, NoMethodError
|
31
|
+
nil
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
attr_reader :parsed
|
37
|
+
|
38
|
+
def parse_query
|
39
|
+
@parsed = PgQuery.parse(@source)
|
40
|
+
end
|
41
|
+
|
42
|
+
memoize
|
43
|
+
def parsed_object
|
44
|
+
parsed_object_factory.create_object(parsed)
|
45
|
+
end
|
46
|
+
|
47
|
+
def fetch_dependencies
|
48
|
+
@source.split("\n").grep(/^(--|#)!/).map { |ln| ln.split[1] if ln =~ /!depends_on/ }.compact
|
46
49
|
end
|
47
50
|
end
|
data/lib/pg_objects/railtie.rb
CHANGED
@@ -1,12 +1,7 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
# end
|
7
|
-
|
8
|
-
rake_tasks do
|
9
|
-
load 'tasks/pg_objects_tasks.rake'
|
10
|
-
end
|
1
|
+
##
|
2
|
+
# Brings rake tasks to rails app
|
3
|
+
class PgObjects::Railtie < Rails::Railtie
|
4
|
+
rake_tasks do
|
5
|
+
load 'tasks/pg_objects_tasks.rake'
|
11
6
|
end
|
12
7
|
end
|
data/lib/pg_objects/version.rb
CHANGED
data/lib/pg_objects.rb
CHANGED
@@ -1,10 +1,19 @@
|
|
1
1
|
require 'pg_objects/version'
|
2
2
|
require 'pg_objects/railtie' if defined?(Rails)
|
3
3
|
|
4
|
+
require 'dry-configurable'
|
5
|
+
require 'dry-container'
|
6
|
+
require 'dry-auto_inject'
|
7
|
+
require 'dry/monads'
|
8
|
+
require 'memery'
|
9
|
+
|
10
|
+
require 'pg_objects/container'
|
4
11
|
require 'pg_objects/config'
|
5
12
|
require 'pg_objects/db_object'
|
13
|
+
require 'pg_objects/db_object_factory'
|
6
14
|
require 'pg_objects/logger'
|
7
15
|
require 'pg_objects/manager'
|
16
|
+
require 'pg_objects/parsed_object'
|
8
17
|
require 'pg_objects/parser'
|
9
18
|
|
10
19
|
module PgObjects
|
data/pg_objects.gemspec
CHANGED
@@ -11,7 +11,7 @@ Gem::Specification.new do |spec|
|
|
11
11
|
spec.summary = %q(Simple manager for PostgreSQL objects like triggers and functions)
|
12
12
|
spec.homepage = 'https://github.com/marinazzio/pg_objects'
|
13
13
|
|
14
|
-
spec.required_ruby_version = '>=
|
14
|
+
spec.required_ruby_version = '>= 3.1.0'
|
15
15
|
|
16
16
|
spec.metadata = {
|
17
17
|
'bug_tracker_uri' => 'https://github.com/marinazzio/pg_objects/issues',
|
@@ -43,8 +43,13 @@ Gem::Specification.new do |spec|
|
|
43
43
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
44
44
|
spec.require_paths = ['lib']
|
45
45
|
|
46
|
-
spec.add_dependency 'activerecord', '>= 6.
|
47
|
-
spec.add_dependency '
|
48
|
-
spec.add_dependency '
|
46
|
+
spec.add_dependency 'activerecord', '>= 6.1.7.0', '< 8'
|
47
|
+
spec.add_dependency 'dry-auto_inject', '~> 1'
|
48
|
+
spec.add_dependency 'dry-configurable', '~> 1'
|
49
|
+
spec.add_dependency 'dry-container', '0.11.0'
|
50
|
+
spec.add_dependency 'dry-monads', '~> 1.6'
|
51
|
+
spec.add_dependency 'memery', '~> 1.5.0'
|
52
|
+
spec.add_dependency 'pg_query', '~> 5'
|
53
|
+
spec.add_dependency 'railties', '>= 4', '< 8'
|
49
54
|
spec.add_dependency 'rake-hooks', '~> 1'
|
50
55
|
end
|