arel_toolkit 0.2.0 → 0.3.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/.rubocop.yml +3 -0
- data/.travis.yml +8 -0
- data/CHANGELOG.md +56 -7
- data/Gemfile.lock +54 -1
- data/Guardfile +19 -12
- data/README.md +56 -2
- data/Rakefile +8 -0
- data/arel_toolkit.gemspec +6 -0
- data/bin/console +1 -0
- data/lib/arel/extensions/assignment.rb +22 -0
- data/lib/arel/extensions/at_time_zone.rb +30 -0
- data/lib/arel/extensions/contained_within_equals.rb +10 -0
- data/lib/arel/extensions/contains.rb +2 -2
- data/lib/arel/extensions/contains_equals.rb +10 -0
- data/lib/arel/extensions/delete_manager.rb +9 -0
- data/lib/arel/extensions/delete_statement.rb +7 -0
- data/lib/arel/extensions/distinct_from.rb +3 -16
- data/lib/arel/extensions/equality.rb +2 -4
- data/lib/arel/extensions/extract_from.rb +32 -0
- data/lib/arel/extensions/insert_manager.rb +5 -0
- data/lib/arel/extensions/insert_statement.rb +10 -3
- data/lib/arel/extensions/json_get_field.rb +10 -0
- data/lib/arel/extensions/json_get_object.rb +10 -0
- data/lib/arel/extensions/json_path_get_field.rb +10 -0
- data/lib/arel/extensions/json_path_get_object.rb +10 -0
- data/lib/arel/extensions/jsonb_all_key_exists.rb +10 -0
- data/lib/arel/extensions/jsonb_any_key_exists.rb +10 -0
- data/lib/arel/extensions/jsonb_key_exists.rb +10 -0
- data/lib/arel/extensions/named_argument.rb +29 -0
- data/lib/arel/extensions/not_distinct_from.rb +3 -16
- data/lib/arel/extensions/not_equal.rb +2 -4
- data/lib/arel/extensions/overlap.rb +1 -1
- data/lib/arel/extensions/overlaps.rb +40 -0
- data/lib/arel/extensions/overlay.rb +44 -0
- data/lib/arel/extensions/position.rb +32 -0
- data/lib/arel/extensions/select_manager.rb +9 -0
- data/lib/arel/extensions/substring.rb +38 -0
- data/lib/arel/extensions/transaction.rb +50 -0
- data/lib/arel/extensions/trim.rb +36 -0
- data/lib/arel/extensions/type_cast.rb +4 -0
- data/lib/arel/{sql_to_arel → extensions}/unbound_column_reference.rb +1 -1
- data/lib/arel/extensions/update_manager.rb +9 -0
- data/lib/arel/extensions/update_statement.rb +8 -0
- data/lib/arel/extensions/variable_set.rb +46 -0
- data/lib/arel/extensions/variable_show.rb +31 -0
- data/lib/arel/extensions.rb +26 -0
- data/lib/arel/middleware/chain.rb +97 -0
- data/lib/arel/middleware/postgresql_adapter.rb +26 -0
- data/lib/arel/middleware/railtie.rb +11 -0
- data/lib/arel/middleware.rb +23 -0
- data/lib/arel/sql_formatter.rb +59 -0
- data/lib/arel/sql_to_arel/error.rb +6 -0
- data/lib/arel/sql_to_arel/pg_query_visitor/frame_options.rb +112 -0
- data/lib/arel/sql_to_arel/pg_query_visitor.rb +271 -52
- data/lib/arel/sql_to_arel/result.rb +17 -0
- data/lib/arel/sql_to_arel.rb +4 -3
- data/lib/arel_toolkit/version.rb +1 -1
- data/lib/arel_toolkit.rb +2 -0
- metadata +120 -4
- data/lib/arel/sql_to_arel/frame_options.rb +0 -110
@@ -0,0 +1,26 @@
|
|
1
|
+
module Arel
|
2
|
+
module Middleware
|
3
|
+
module PostgreSQLAdapter
|
4
|
+
def initialize(*args)
|
5
|
+
Arel.middleware.none do
|
6
|
+
super(*args)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def execute(sql, name = nil)
|
11
|
+
sql = Arel::Middleware.current_chain.execute(sql)
|
12
|
+
super(sql, name)
|
13
|
+
end
|
14
|
+
|
15
|
+
def exec_no_cache(sql, name, binds)
|
16
|
+
sql = Arel::Middleware.current_chain.execute(sql, binds)
|
17
|
+
super(sql, name, binds)
|
18
|
+
end
|
19
|
+
|
20
|
+
def exec_cache(sql, name, binds)
|
21
|
+
sql = Arel::Middleware.current_chain.execute(sql, binds)
|
22
|
+
super(sql, name, binds)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
require_relative './middleware/railtie'
|
3
|
+
require_relative './middleware/chain'
|
4
|
+
require_relative './middleware/postgresql_adapter'
|
5
|
+
|
6
|
+
module Arel
|
7
|
+
module Middleware
|
8
|
+
class << self
|
9
|
+
def current_chain
|
10
|
+
Thread.current[:arel_toolkit_middleware_current_chain] ||=
|
11
|
+
Arel::Middleware::Chain.new
|
12
|
+
end
|
13
|
+
|
14
|
+
def current_chain=(new_chain)
|
15
|
+
Thread.current[:arel_toolkit_middleware_current_chain] = new_chain
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.middleware
|
21
|
+
Arel::Middleware.current_chain
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Arel
|
2
|
+
module Nodes
|
3
|
+
class Node
|
4
|
+
def to_formatted_sql(engine = Table.engine)
|
5
|
+
collector = Arel::Collectors::SQLString.new
|
6
|
+
Arel::SqlFormatter.new(engine.connection).accept self, collector
|
7
|
+
collector.value
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class TreeManager
|
13
|
+
def to_formatted_sql(engine = Table.engine)
|
14
|
+
collector = Arel::Collectors::SQLString.new
|
15
|
+
Arel::SqlFormatter.new(engine.connection).accept @ast, collector
|
16
|
+
collector.value
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class SqlFormatter < Arel::Visitors::PostgreSQL
|
21
|
+
def accept(object, collector)
|
22
|
+
super object, collector
|
23
|
+
collector << "\n"
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
# rubocop:disable Naming/MethodName
|
29
|
+
# rubocop:disable Naming/UncommunicativeMethodParamName
|
30
|
+
# rubocop:disable Metrics/AbcSize
|
31
|
+
def visit_Arel_Nodes_SelectCore(o, collector)
|
32
|
+
collector << "SELECT\n"
|
33
|
+
|
34
|
+
collector = maybe_visit o.top, collector
|
35
|
+
|
36
|
+
collector = maybe_visit o.set_quantifier, collector
|
37
|
+
|
38
|
+
collect_nodes_for(o.projections, collector, SPACE, ",\n")
|
39
|
+
|
40
|
+
if o.source && !o.source.empty?
|
41
|
+
collector << ' FROM '
|
42
|
+
collector = visit o.source, collector
|
43
|
+
end
|
44
|
+
|
45
|
+
collect_nodes_for o.wheres, collector, WHERE, AND
|
46
|
+
collect_nodes_for o.groups, collector, GROUP_BY
|
47
|
+
unless o.havings.empty?
|
48
|
+
collector << ' HAVING '
|
49
|
+
inject_join o.havings, collector, AND
|
50
|
+
end
|
51
|
+
collect_nodes_for o.windows, collector, WINDOW
|
52
|
+
|
53
|
+
collector
|
54
|
+
end
|
55
|
+
# rubocop:enable Metrics/AbcSize
|
56
|
+
# rubocop:enable Naming/MethodName
|
57
|
+
# rubocop:enable Naming/UncommunicativeMethodParamName
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
module Arel
|
2
|
+
module SqlToArel
|
3
|
+
class PgQueryVisitor
|
4
|
+
class FrameOptions
|
5
|
+
class << self
|
6
|
+
def arel(frame_options, start_offset, end_offset)
|
7
|
+
frame_option_names = calculate_frame_option_names(frame_options)
|
8
|
+
return unless frame_option_names.include?('FRAMEOPTION_NONDEFAULT')
|
9
|
+
|
10
|
+
range_klass = if frame_option_names.include?('FRAMEOPTION_RANGE')
|
11
|
+
Arel::Nodes::Range
|
12
|
+
else
|
13
|
+
Arel::Nodes::Rows
|
14
|
+
end
|
15
|
+
|
16
|
+
start_node = calculate_frame_node(
|
17
|
+
'FRAMEOPTION_START_',
|
18
|
+
frame_option_names,
|
19
|
+
start_offset,
|
20
|
+
)
|
21
|
+
end_node = calculate_frame_node(
|
22
|
+
'FRAMEOPTION_END_',
|
23
|
+
frame_option_names,
|
24
|
+
end_offset,
|
25
|
+
)
|
26
|
+
|
27
|
+
if frame_option_names.include?('FRAMEOPTION_BETWEEN')
|
28
|
+
Arel::Nodes::Between.new(
|
29
|
+
range_klass.new,
|
30
|
+
Arel::Nodes::And.new([start_node, end_node]),
|
31
|
+
)
|
32
|
+
else
|
33
|
+
range_klass.new start_node
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
# always NONDEFAULT
|
40
|
+
# RANGE or ROWS
|
41
|
+
# mandatory BETWEEN
|
42
|
+
# RANGE only unbounded
|
43
|
+
# ROWS all
|
44
|
+
# https://github.com/postgres/postgres/blob/REL_10_1/src/include/nodes/parsenodes.h
|
45
|
+
FRAMEOPTIONS = {
|
46
|
+
'FRAMEOPTION_NONDEFAULT' => 0x00001,
|
47
|
+
'FRAMEOPTION_RANGE' => 0x00002,
|
48
|
+
'FRAMEOPTION_ROWS' => 0x00004,
|
49
|
+
'FRAMEOPTION_BETWEEN' => 0x00008,
|
50
|
+
'FRAMEOPTION_START_UNBOUNDED_PRECEDING' => 0x00010,
|
51
|
+
'FRAMEOPTION_END_UNBOUNDED_PRECEDING' => 0x00020,
|
52
|
+
'FRAMEOPTION_START_UNBOUNDED_FOLLOWING' => 0x00040,
|
53
|
+
'FRAMEOPTION_END_UNBOUNDED_FOLLOWING' => 0x00080,
|
54
|
+
'FRAMEOPTION_START_CURRENT_ROW' => 0x00100,
|
55
|
+
'FRAMEOPTION_END_CURRENT_ROW' => 0x00200,
|
56
|
+
'FRAMEOPTION_START_VALUE_PRECEDING' => 0x00400,
|
57
|
+
'FRAMEOPTION_END_VALUE_PRECEDING' => 0x00800,
|
58
|
+
'FRAMEOPTION_START_VALUE_FOLLOWING' => 0x01000,
|
59
|
+
'FRAMEOPTION_END_VALUE_FOLLOWING' => 0x02000
|
60
|
+
}.freeze
|
61
|
+
|
62
|
+
def biggest_detractable_number(number, candidates)
|
63
|
+
high_to_low_candidates = candidates.sort { |a, b| b <=> a }
|
64
|
+
high_to_low_candidates.find do |candidate|
|
65
|
+
number - candidate >= 0
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def calculate_frame_option_names(frame_options, names = [])
|
70
|
+
return names if frame_options.zero?
|
71
|
+
|
72
|
+
number = biggest_detractable_number(frame_options, FRAMEOPTIONS.values)
|
73
|
+
name = FRAMEOPTIONS.key(number)
|
74
|
+
calculate_frame_option_names(
|
75
|
+
frame_options - number, names + [name]
|
76
|
+
)
|
77
|
+
end
|
78
|
+
|
79
|
+
def calculate_frame_node(pattern, frame_option_names, offset)
|
80
|
+
node_name = frame_option_names.select { |n| n.start_with?(pattern) }
|
81
|
+
raise "Don't know how to handle multiple nodes" if node_name.length > 1
|
82
|
+
|
83
|
+
node_name = node_name.first.gsub(/FRAMEOPTION_(START|END)_/, '')
|
84
|
+
name_to_node(node_name, offset)
|
85
|
+
end
|
86
|
+
|
87
|
+
def name_to_node(node_name, offset)
|
88
|
+
case node_name
|
89
|
+
when 'UNBOUNDED_PRECEDING'
|
90
|
+
Arel::Nodes::Preceding.new
|
91
|
+
|
92
|
+
when 'UNBOUNDED_FOLLOWING'
|
93
|
+
Arel::Nodes::Following.new
|
94
|
+
|
95
|
+
when 'CURRENT_ROW'
|
96
|
+
Arel::Nodes::CurrentRow.new
|
97
|
+
|
98
|
+
when 'VALUE_PRECEDING'
|
99
|
+
Arel::Nodes::Preceding.new offset
|
100
|
+
|
101
|
+
when 'VALUE_FOLLOWING'
|
102
|
+
Arel::Nodes::Following.new offset
|
103
|
+
|
104
|
+
else
|
105
|
+
raise "Unknown start / end frame node `#{node_name}`"
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|