pg_objects 1.4.7 → 1.5.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/.evilution.yml +74 -0
- data/.github/workflows/ci.yml +1 -1
- data/.gitignore +3 -0
- data/.rubocop.yml +13 -2
- data/CHANGELOG.md +78 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +96 -78
- data/README.md +174 -3
- data/Rakefile +2 -0
- data/bin/benchmark +42 -7
- data/bin/console +1 -0
- data/lib/generators/pg_objects/install/install_generator.rb +2 -0
- data/lib/pg_objects/config.rb +53 -2
- data/lib/pg_objects/db_object.rb +6 -1
- data/lib/pg_objects/db_object_factory.rb +11 -3
- data/lib/pg_objects/logger.rb +34 -3
- data/lib/pg_objects/manager.rb +66 -15
- data/lib/pg_objects/parsed_object/aggregate.rb +9 -1
- data/lib/pg_objects/parsed_object/base.rb +38 -0
- data/lib/pg_objects/parsed_object/base_type.rb +17 -0
- data/lib/pg_objects/parsed_object/conversion.rb +9 -1
- data/lib/pg_objects/parsed_object/domain.rb +16 -0
- data/lib/pg_objects/parsed_object/enum_type.rb +16 -0
- data/lib/pg_objects/parsed_object/event_trigger.rb +3 -1
- data/lib/pg_objects/parsed_object/extension.rb +10 -0
- data/lib/pg_objects/parsed_object/function.rb +9 -1
- data/lib/pg_objects/parsed_object/index.rb +17 -0
- data/lib/pg_objects/parsed_object/materialized_view.rb +9 -1
- data/lib/pg_objects/parsed_object/operator.rb +9 -1
- data/lib/pg_objects/parsed_object/operator_class.rb +9 -1
- data/lib/pg_objects/parsed_object/policy.rb +10 -0
- data/lib/pg_objects/parsed_object/range_type.rb +16 -0
- data/lib/pg_objects/parsed_object/rule.rb +10 -0
- data/lib/pg_objects/parsed_object/sequence.rb +16 -0
- data/lib/pg_objects/parsed_object/table.rb +9 -1
- data/lib/pg_objects/parsed_object/text_search_parser.rb +9 -1
- data/lib/pg_objects/parsed_object/text_search_template.rb +9 -1
- data/lib/pg_objects/parsed_object/trigger.rb +3 -1
- data/lib/pg_objects/parsed_object/type.rb +9 -1
- data/lib/pg_objects/parsed_object/view.rb +9 -1
- data/lib/pg_objects/parsed_object.rb +11 -0
- data/lib/pg_objects/parsed_object_factory.rb +52 -90
- data/lib/pg_objects/parser.rb +28 -12
- data/lib/pg_objects/railtie.rb +2 -0
- data/lib/pg_objects/version.rb +3 -1
- data/lib/pg_objects/yaml_configurable.rb +25 -7
- data/lib/pg_objects.rb +54 -4
- data/lib/tasks/pg_objects_tasks.rake +33 -8
- data/pg_objects.gemspec +5 -3
- metadata +17 -20
- data/.github/copilot-instructions.md +0 -131
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
#
|
|
2
4
|
# Base class for parsed objects
|
|
3
5
|
#
|
|
@@ -10,7 +12,43 @@ class PgObjects::ParsedObject::Base
|
|
|
10
12
|
raise NotImplementedError
|
|
11
13
|
end
|
|
12
14
|
|
|
15
|
+
# Schema-qualified name (+schema.name+) when a schema is present, otherwise
|
|
16
|
+
# the same as +name+. Used to disambiguate same-named objects across schemas.
|
|
17
|
+
def qualified_name
|
|
18
|
+
schema_name = schema
|
|
19
|
+
schema_name && !schema_name.empty? ? "#{schema_name}.#{name}" : name
|
|
20
|
+
end
|
|
21
|
+
|
|
13
22
|
private
|
|
14
23
|
|
|
15
24
|
attr_reader :stmt
|
|
25
|
+
|
|
26
|
+
# Schema of the object, or nil when unqualified. Subclasses that carry a
|
|
27
|
+
# schema override this; the rest inherit the unqualified default.
|
|
28
|
+
def schema
|
|
29
|
+
nil
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Returns the schema element of a pg_query name list (e.g. funcname,
|
|
33
|
+
# defnames) when the name is qualified (+schema.name+), otherwise nil.
|
|
34
|
+
def qualifier(list)
|
|
35
|
+
list[-2].string.sval if list.size > 1
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Wraps name extraction so a malformed statement (nil field, empty array)
|
|
39
|
+
# surfaces an explicit error with context. Only NoMethodErrors from
|
|
40
|
+
# dereferencing nil are wrapped; genuine programming errors propagate.
|
|
41
|
+
def extract_name
|
|
42
|
+
yield
|
|
43
|
+
rescue NoMethodError => e
|
|
44
|
+
raise unless nil_dereference?(e)
|
|
45
|
+
|
|
46
|
+
raise PgObjects::MalformedStatementError, "malformed statement for #{self.class}: #{e.message}"
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def nil_dereference?(error)
|
|
50
|
+
error.receiver.nil?
|
|
51
|
+
rescue ArgumentError
|
|
52
|
+
false
|
|
53
|
+
end
|
|
16
54
|
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
#
|
|
4
|
+
# Base (scalar) TYPE object representation (CREATE TYPE with I/O functions or
|
|
5
|
+
# a shell CREATE TYPE without attributes) — parsed as a define_stmt.
|
|
6
|
+
#
|
|
7
|
+
class PgObjects::ParsedObject::BaseType < PgObjects::ParsedObject::Base
|
|
8
|
+
def name
|
|
9
|
+
extract_name { stmt.define_stmt.defnames.last.string.sval }
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
private
|
|
13
|
+
|
|
14
|
+
def schema
|
|
15
|
+
extract_name { qualifier(stmt.define_stmt.defnames) }
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -1,8 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
#
|
|
2
4
|
# CONVERSION object representation
|
|
3
5
|
#
|
|
4
6
|
class PgObjects::ParsedObject::Conversion < PgObjects::ParsedObject::Base
|
|
5
7
|
def name
|
|
6
|
-
stmt.create_conversion_stmt.conversion_name
|
|
8
|
+
extract_name { stmt.create_conversion_stmt.conversion_name.last.string.sval }
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
private
|
|
12
|
+
|
|
13
|
+
def schema
|
|
14
|
+
extract_name { qualifier(stmt.create_conversion_stmt.conversion_name) }
|
|
7
15
|
end
|
|
8
16
|
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
#
|
|
4
|
+
# DOMAIN object representation (CREATE DOMAIN)
|
|
5
|
+
#
|
|
6
|
+
class PgObjects::ParsedObject::Domain < PgObjects::ParsedObject::Base
|
|
7
|
+
def name
|
|
8
|
+
extract_name { stmt.create_domain_stmt.domainname.last.string.sval }
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
private
|
|
12
|
+
|
|
13
|
+
def schema
|
|
14
|
+
extract_name { qualifier(stmt.create_domain_stmt.domainname) }
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
#
|
|
4
|
+
# ENUM TYPE object representation (CREATE TYPE ... AS ENUM)
|
|
5
|
+
#
|
|
6
|
+
class PgObjects::ParsedObject::EnumType < PgObjects::ParsedObject::Base
|
|
7
|
+
def name
|
|
8
|
+
extract_name { stmt.create_enum_stmt.type_name.last.string.sval }
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
private
|
|
12
|
+
|
|
13
|
+
def schema
|
|
14
|
+
extract_name { qualifier(stmt.create_enum_stmt.type_name) }
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
#
|
|
2
4
|
# EVENT TRIGGER object representation
|
|
3
5
|
#
|
|
4
6
|
class PgObjects::ParsedObject::EventTrigger < PgObjects::ParsedObject::Base
|
|
5
7
|
def name
|
|
6
|
-
stmt.create_event_trig_stmt.trigname
|
|
8
|
+
extract_name { stmt.create_event_trig_stmt.trigname }
|
|
7
9
|
end
|
|
8
10
|
end
|
|
@@ -1,8 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
#
|
|
2
4
|
# FUNCTION object representation
|
|
3
5
|
#
|
|
4
6
|
class PgObjects::ParsedObject::Function < PgObjects::ParsedObject::Base
|
|
5
7
|
def name
|
|
6
|
-
stmt.create_function_stmt.funcname
|
|
8
|
+
extract_name { stmt.create_function_stmt.funcname.last.string.sval }
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
private
|
|
12
|
+
|
|
13
|
+
def schema
|
|
14
|
+
extract_name { qualifier(stmt.create_function_stmt.funcname) }
|
|
7
15
|
end
|
|
8
16
|
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
#
|
|
4
|
+
# INDEX object representation
|
|
5
|
+
#
|
|
6
|
+
class PgObjects::ParsedObject::Index < PgObjects::ParsedObject::Base
|
|
7
|
+
def name
|
|
8
|
+
extract_name { stmt.index_stmt.idxname }
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
private
|
|
12
|
+
|
|
13
|
+
# An index lives in the schema of the table it indexes.
|
|
14
|
+
def schema
|
|
15
|
+
extract_name { stmt.index_stmt.relation.schemaname }
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -1,8 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
#
|
|
2
4
|
# MATERIALIZED VIEW object representation
|
|
3
5
|
#
|
|
4
6
|
class PgObjects::ParsedObject::MaterializedView < PgObjects::ParsedObject::Base
|
|
5
7
|
def name
|
|
6
|
-
stmt.create_table_as_stmt.into.rel.relname
|
|
8
|
+
extract_name { stmt.create_table_as_stmt.into.rel.relname }
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
private
|
|
12
|
+
|
|
13
|
+
def schema
|
|
14
|
+
extract_name { stmt.create_table_as_stmt.into.rel.schemaname }
|
|
7
15
|
end
|
|
8
16
|
end
|
|
@@ -1,8 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
#
|
|
2
4
|
# OPERATOR object representation
|
|
3
5
|
#
|
|
4
6
|
class PgObjects::ParsedObject::Operator < PgObjects::ParsedObject::Base
|
|
5
7
|
def name
|
|
6
|
-
stmt.define_stmt.defnames
|
|
8
|
+
extract_name { stmt.define_stmt.defnames.last.string.sval }
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
private
|
|
12
|
+
|
|
13
|
+
def schema
|
|
14
|
+
extract_name { qualifier(stmt.define_stmt.defnames) }
|
|
7
15
|
end
|
|
8
16
|
end
|
|
@@ -1,8 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
#
|
|
2
4
|
# OPERATOR CLASS object representation
|
|
3
5
|
#
|
|
4
6
|
class PgObjects::ParsedObject::OperatorClass < PgObjects::ParsedObject::Base
|
|
5
7
|
def name
|
|
6
|
-
stmt.create_op_class_stmt.opclassname
|
|
8
|
+
extract_name { stmt.create_op_class_stmt.opclassname.last.string.sval }
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
private
|
|
12
|
+
|
|
13
|
+
def schema
|
|
14
|
+
extract_name { qualifier(stmt.create_op_class_stmt.opclassname) }
|
|
7
15
|
end
|
|
8
16
|
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
#
|
|
4
|
+
# RANGE TYPE object representation (CREATE TYPE ... AS RANGE)
|
|
5
|
+
#
|
|
6
|
+
class PgObjects::ParsedObject::RangeType < PgObjects::ParsedObject::Base
|
|
7
|
+
def name
|
|
8
|
+
extract_name { stmt.create_range_stmt.type_name.last.string.sval }
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
private
|
|
12
|
+
|
|
13
|
+
def schema
|
|
14
|
+
extract_name { qualifier(stmt.create_range_stmt.type_name) }
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
#
|
|
4
|
+
# SEQUENCE object representation
|
|
5
|
+
#
|
|
6
|
+
class PgObjects::ParsedObject::Sequence < PgObjects::ParsedObject::Base
|
|
7
|
+
def name
|
|
8
|
+
extract_name { stmt.create_seq_stmt.sequence.relname }
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
private
|
|
12
|
+
|
|
13
|
+
def schema
|
|
14
|
+
extract_name { stmt.create_seq_stmt.sequence.schemaname }
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -1,8 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
#
|
|
2
4
|
# TABLE object representation
|
|
3
5
|
#
|
|
4
6
|
class PgObjects::ParsedObject::Table < PgObjects::ParsedObject::Base
|
|
5
7
|
def name
|
|
6
|
-
stmt.create_stmt.relation.relname
|
|
8
|
+
extract_name { stmt.create_stmt.relation.relname }
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
private
|
|
12
|
+
|
|
13
|
+
def schema
|
|
14
|
+
extract_name { stmt.create_stmt.relation.schemaname }
|
|
7
15
|
end
|
|
8
16
|
end
|
|
@@ -1,8 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
#
|
|
2
4
|
# TEXT SEARCH PARSER object representation
|
|
3
5
|
#
|
|
4
6
|
class PgObjects::ParsedObject::TextSearchParser < PgObjects::ParsedObject::Base
|
|
5
7
|
def name
|
|
6
|
-
stmt.define_stmt.defnames
|
|
8
|
+
extract_name { stmt.define_stmt.defnames.last.string.sval }
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
private
|
|
12
|
+
|
|
13
|
+
def schema
|
|
14
|
+
extract_name { qualifier(stmt.define_stmt.defnames) }
|
|
7
15
|
end
|
|
8
16
|
end
|
|
@@ -1,8 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
#
|
|
2
4
|
# TEXT SEARCH TEMPLATE object representation
|
|
3
5
|
#
|
|
4
6
|
class PgObjects::ParsedObject::TextSearchTemplate < PgObjects::ParsedObject::Base
|
|
5
7
|
def name
|
|
6
|
-
stmt.define_stmt.defnames
|
|
8
|
+
extract_name { stmt.define_stmt.defnames.last.string.sval }
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
private
|
|
12
|
+
|
|
13
|
+
def schema
|
|
14
|
+
extract_name { qualifier(stmt.define_stmt.defnames) }
|
|
7
15
|
end
|
|
8
16
|
end
|
|
@@ -1,8 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
#
|
|
2
4
|
# TYPE object representation
|
|
3
5
|
#
|
|
4
6
|
class PgObjects::ParsedObject::Type < PgObjects::ParsedObject::Base
|
|
5
7
|
def name
|
|
6
|
-
stmt.composite_type_stmt.typevar.relname
|
|
8
|
+
extract_name { stmt.composite_type_stmt.typevar.relname }
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
private
|
|
12
|
+
|
|
13
|
+
def schema
|
|
14
|
+
extract_name { stmt.composite_type_stmt.typevar.schemaname }
|
|
7
15
|
end
|
|
8
16
|
end
|
|
@@ -1,8 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
#
|
|
2
4
|
# VIEW object representation
|
|
3
5
|
#
|
|
4
6
|
class PgObjects::ParsedObject::View < PgObjects::ParsedObject::Base
|
|
5
7
|
def name
|
|
6
|
-
stmt.view_stmt.view.relname
|
|
8
|
+
extract_name { stmt.view_stmt.view.relname }
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
private
|
|
12
|
+
|
|
13
|
+
def schema
|
|
14
|
+
extract_name { stmt.view_stmt.view.schemaname }
|
|
7
15
|
end
|
|
8
16
|
end
|
|
@@ -1,14 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module PgObjects::ParsedObject # rubocop: disable Style/Documentation
|
|
2
4
|
end
|
|
3
5
|
|
|
4
6
|
require 'pg_objects/parsed_object/base'
|
|
5
7
|
require 'pg_objects/parsed_object/aggregate'
|
|
8
|
+
require 'pg_objects/parsed_object/base_type'
|
|
6
9
|
require 'pg_objects/parsed_object/conversion'
|
|
10
|
+
require 'pg_objects/parsed_object/domain'
|
|
11
|
+
require 'pg_objects/parsed_object/enum_type'
|
|
7
12
|
require 'pg_objects/parsed_object/event_trigger'
|
|
13
|
+
require 'pg_objects/parsed_object/extension'
|
|
8
14
|
require 'pg_objects/parsed_object/function'
|
|
15
|
+
require 'pg_objects/parsed_object/index'
|
|
9
16
|
require 'pg_objects/parsed_object/materialized_view'
|
|
10
17
|
require 'pg_objects/parsed_object/operator'
|
|
11
18
|
require 'pg_objects/parsed_object/operator_class'
|
|
19
|
+
require 'pg_objects/parsed_object/policy'
|
|
20
|
+
require 'pg_objects/parsed_object/range_type'
|
|
21
|
+
require 'pg_objects/parsed_object/rule'
|
|
22
|
+
require 'pg_objects/parsed_object/sequence'
|
|
12
23
|
require 'pg_objects/parsed_object/table'
|
|
13
24
|
require 'pg_objects/parsed_object/text_search_parser'
|
|
14
25
|
require 'pg_objects/parsed_object/text_search_template'
|
|
@@ -1,103 +1,65 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
#
|
|
2
4
|
# Returns an object of the respective class based on the provided parsed query
|
|
3
5
|
#
|
|
4
6
|
class PgObjects::ParsedObjectFactory
|
|
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
|
-
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
|
|
7
|
+
DISPATCH = {
|
|
8
|
+
composite_type_stmt: PgObjects::ParsedObject::Type,
|
|
9
|
+
create_conversion_stmt: PgObjects::ParsedObject::Conversion,
|
|
10
|
+
create_domain_stmt: PgObjects::ParsedObject::Domain,
|
|
11
|
+
create_enum_stmt: PgObjects::ParsedObject::EnumType,
|
|
12
|
+
create_event_trig_stmt: PgObjects::ParsedObject::EventTrigger,
|
|
13
|
+
create_extension_stmt: PgObjects::ParsedObject::Extension,
|
|
14
|
+
create_function_stmt: PgObjects::ParsedObject::Function,
|
|
15
|
+
create_op_class_stmt: PgObjects::ParsedObject::OperatorClass,
|
|
16
|
+
create_policy_stmt: PgObjects::ParsedObject::Policy,
|
|
17
|
+
create_range_stmt: PgObjects::ParsedObject::RangeType,
|
|
18
|
+
create_seq_stmt: PgObjects::ParsedObject::Sequence,
|
|
19
|
+
create_stmt: PgObjects::ParsedObject::Table,
|
|
20
|
+
create_trig_stmt: PgObjects::ParsedObject::Trigger,
|
|
21
|
+
index_stmt: PgObjects::ParsedObject::Index,
|
|
22
|
+
rule_stmt: PgObjects::ParsedObject::Rule,
|
|
23
|
+
view_stmt: PgObjects::ParsedObject::View
|
|
24
|
+
}.freeze
|
|
25
|
+
|
|
26
|
+
DEFINE_STMT_DISPATCH = {
|
|
27
|
+
OBJECT_AGGREGATE: PgObjects::ParsedObject::Aggregate,
|
|
28
|
+
OBJECT_OPERATOR: PgObjects::ParsedObject::Operator,
|
|
29
|
+
OBJECT_TSPARSER: PgObjects::ParsedObject::TextSearchParser,
|
|
30
|
+
OBJECT_TSTEMPLATE: PgObjects::ParsedObject::TextSearchTemplate,
|
|
31
|
+
OBJECT_TYPE: PgObjects::ParsedObject::BaseType
|
|
32
|
+
}.freeze
|
|
33
|
+
|
|
34
|
+
def self.create_object(input_data)
|
|
35
|
+
new(input_data).create_object
|
|
36
|
+
end
|
|
80
37
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
38
|
+
def initialize(input_data)
|
|
39
|
+
@input_data = input_data
|
|
40
|
+
@stmt = input_data.tree.stmts[0].stmt
|
|
41
|
+
end
|
|
84
42
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
43
|
+
def create_object
|
|
44
|
+
determine_class.new(stmt)
|
|
45
|
+
end
|
|
88
46
|
|
|
89
|
-
|
|
90
|
-
try_check_get_result { stmt.composite_type_stmt.typevar.present? }
|
|
91
|
-
end
|
|
47
|
+
private
|
|
92
48
|
|
|
93
|
-
|
|
94
|
-
try_check_get_result { stmt.view_stmt.view.present? }
|
|
95
|
-
end
|
|
49
|
+
attr_reader :stmt, :input_data
|
|
96
50
|
|
|
97
|
-
|
|
98
|
-
|
|
51
|
+
def determine_class
|
|
52
|
+
dispatch_class || raise(PgObjects::UnknownObjectTypeError, "unknown object type: #{stmt.node}")
|
|
53
|
+
end
|
|
99
54
|
|
|
100
|
-
|
|
55
|
+
def dispatch_class
|
|
56
|
+
case stmt.node
|
|
57
|
+
when :define_stmt
|
|
58
|
+
DEFINE_STMT_DISPATCH[stmt.define_stmt.kind]
|
|
59
|
+
when :create_table_as_stmt
|
|
60
|
+
PgObjects::ParsedObject::MaterializedView if stmt.create_table_as_stmt.objtype == :OBJECT_MATVIEW
|
|
61
|
+
else
|
|
62
|
+
DISPATCH[stmt.node]
|
|
101
63
|
end
|
|
102
64
|
end
|
|
103
65
|
end
|
data/lib/pg_objects/parser.rb
CHANGED
|
@@ -1,20 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'pg_query'
|
|
2
4
|
|
|
3
5
|
##
|
|
4
6
|
# Reads directives from SQL-comments
|
|
5
7
|
#
|
|
6
|
-
# --!depends_on
|
|
8
|
+
# --!depends_on name_a
|
|
9
|
+
# --!depends_on name_a, name_b, name_c
|
|
10
|
+
# #!depends_on name_a
|
|
11
|
+
#
|
|
12
|
+
# The directive must start the line (no leading whitespace). Dependencies may
|
|
13
|
+
# be listed on one line separated by commas and/or whitespace, or across
|
|
14
|
+
# several directive lines.
|
|
7
15
|
#
|
|
8
16
|
# name_of_dependency: short or full name of object as well as object_name
|
|
9
17
|
#
|
|
10
18
|
class PgObjects::Parser
|
|
11
19
|
include Import['parsed_object_factory']
|
|
12
|
-
include Memery
|
|
13
20
|
|
|
14
|
-
|
|
21
|
+
DEPENDS_ON_DIRECTIVE = /\A(?:--|#)!depends_on\s+(.+)/
|
|
15
22
|
|
|
16
23
|
def load(source)
|
|
17
24
|
@source = source
|
|
25
|
+
@parsed = nil
|
|
26
|
+
@parsed_object = nil
|
|
18
27
|
self
|
|
19
28
|
end
|
|
20
29
|
|
|
@@ -25,26 +34,33 @@ class PgObjects::Parser
|
|
|
25
34
|
end
|
|
26
35
|
|
|
27
36
|
def fetch_object_name
|
|
28
|
-
parse_query
|
|
29
37
|
parsed_object.name
|
|
30
|
-
rescue PgQuery::ParseError,
|
|
38
|
+
rescue PgQuery::ParseError, PgObjects::UnknownObjectTypeError
|
|
31
39
|
nil
|
|
32
40
|
end
|
|
33
41
|
|
|
34
|
-
|
|
42
|
+
def fetch_qualified_object_name
|
|
43
|
+
parsed_object.qualified_name
|
|
44
|
+
rescue PgQuery::ParseError, PgObjects::UnknownObjectTypeError
|
|
45
|
+
nil
|
|
46
|
+
end
|
|
35
47
|
|
|
36
|
-
|
|
48
|
+
private
|
|
37
49
|
|
|
38
|
-
|
|
39
|
-
|
|
50
|
+
# Parse once per loaded source; both fetch_* methods reuse the result and
|
|
51
|
+
# +load+ clears it, so a new source is always reparsed (no stale cache).
|
|
52
|
+
def parsed
|
|
53
|
+
@parsed ||= PgQuery.parse(@source)
|
|
40
54
|
end
|
|
41
55
|
|
|
42
|
-
memoize
|
|
43
56
|
def parsed_object
|
|
44
|
-
parsed_object_factory.create_object(parsed)
|
|
57
|
+
@parsed_object ||= parsed_object_factory.create_object(parsed)
|
|
45
58
|
end
|
|
46
59
|
|
|
47
60
|
def fetch_dependencies
|
|
48
|
-
@source.
|
|
61
|
+
@source.each_line.flat_map do |line|
|
|
62
|
+
match = DEPENDS_ON_DIRECTIVE.match(line)
|
|
63
|
+
match ? match[1].split(/[\s,]+/).reject(&:empty?) : []
|
|
64
|
+
end
|
|
49
65
|
end
|
|
50
66
|
end
|
data/lib/pg_objects/railtie.rb
CHANGED
data/lib/pg_objects/version.rb
CHANGED