arel_toolkit 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.codeclimate.yml +29 -0
- data/.gitignore +5 -0
- data/.rubocop.yml +34 -0
- data/.ruby-version +1 -1
- data/.travis.yml +16 -2
- data/CHANGELOG.md +10 -0
- data/Gemfile +1 -1
- data/Gemfile.lock +94 -1
- data/Guardfile +42 -0
- data/README.md +23 -4
- data/Rakefile +3 -3
- data/arel_toolkit.gemspec +32 -15
- data/bin/console +4 -7
- data/lib/arel/extensions.rb +71 -0
- data/lib/arel/extensions/absolute.rb +10 -0
- data/lib/arel/extensions/all.rb +23 -0
- data/lib/arel/extensions/any.rb +23 -0
- data/lib/arel/extensions/array.rb +29 -0
- data/lib/arel/extensions/array_subselect.rb +23 -0
- data/lib/arel/extensions/between_symmetric.rb +23 -0
- data/lib/arel/extensions/bit_string.rb +27 -0
- data/lib/arel/extensions/bitwise_xor.rb +10 -0
- data/lib/arel/extensions/coalesce.rb +9 -0
- data/lib/arel/extensions/conflict.rb +47 -0
- data/lib/arel/extensions/contained_by.rb +10 -0
- data/lib/arel/extensions/contains.rb +10 -0
- data/lib/arel/extensions/cross_join.rb +21 -0
- data/lib/arel/extensions/cube_root.rb +10 -0
- data/lib/arel/extensions/current_catalog.rb +20 -0
- data/lib/arel/extensions/current_date.rb +20 -0
- data/lib/arel/extensions/current_of_expression.rb +29 -0
- data/lib/arel/extensions/current_role.rb +20 -0
- data/lib/arel/extensions/current_schema.rb +20 -0
- data/lib/arel/extensions/current_time.rb +22 -0
- data/lib/arel/extensions/current_timestamp.rb +22 -0
- data/lib/arel/extensions/current_user.rb +20 -0
- data/lib/arel/extensions/default_values.rb +21 -0
- data/lib/arel/extensions/delete_statement.rb +49 -0
- data/lib/arel/extensions/distinct_from.rb +22 -0
- data/lib/arel/extensions/equality.rb +30 -0
- data/lib/arel/extensions/except_all.rb +21 -0
- data/lib/arel/extensions/exponentiation.rb +10 -0
- data/lib/arel/extensions/factorial.rb +33 -0
- data/lib/arel/extensions/function.rb +68 -0
- data/lib/arel/extensions/generate_series.rb +9 -0
- data/lib/arel/extensions/greatest.rb +9 -0
- data/lib/arel/extensions/indirection.rb +32 -0
- data/lib/arel/extensions/infer.rb +35 -0
- data/lib/arel/extensions/insert_statement.rb +69 -0
- data/lib/arel/extensions/intersect_all.rb +21 -0
- data/lib/arel/extensions/lateral.rb +34 -0
- data/lib/arel/extensions/least.rb +9 -0
- data/lib/arel/extensions/local_time.rb +22 -0
- data/lib/arel/extensions/local_timestamp.rb +22 -0
- data/lib/arel/extensions/modulo.rb +10 -0
- data/lib/arel/extensions/named_function.rb +15 -0
- data/lib/arel/extensions/natural_join.rb +21 -0
- data/lib/arel/extensions/not_between.rb +22 -0
- data/lib/arel/extensions/not_between_symmetric.rb +23 -0
- data/lib/arel/extensions/not_distinct_from.rb +22 -0
- data/lib/arel/extensions/not_equal.rb +30 -0
- data/lib/arel/extensions/not_similar.rb +29 -0
- data/lib/arel/extensions/null_if.rb +24 -0
- data/lib/arel/extensions/ordering.rb +47 -0
- data/lib/arel/extensions/overlap.rb +10 -0
- data/lib/arel/extensions/range_function.rb +23 -0
- data/lib/arel/extensions/rank.rb +9 -0
- data/lib/arel/extensions/row.rb +30 -0
- data/lib/arel/extensions/select_statement.rb +26 -0
- data/lib/arel/extensions/session_user.rb +20 -0
- data/lib/arel/extensions/set_to_default.rb +21 -0
- data/lib/arel/extensions/similar.rb +32 -0
- data/lib/arel/extensions/square_root.rb +10 -0
- data/lib/arel/extensions/table.rb +49 -0
- data/lib/arel/extensions/time_with_precision.rb +13 -0
- data/lib/arel/extensions/type_cast.rb +30 -0
- data/lib/arel/extensions/unknown.rb +20 -0
- data/lib/arel/extensions/update_statement.rb +63 -0
- data/lib/arel/extensions/user.rb +20 -0
- data/lib/arel/extensions/with_ordinality.rb +22 -0
- data/lib/arel/sql_to_arel.rb +8 -0
- data/lib/arel/sql_to_arel/frame_options.rb +110 -0
- data/lib/arel/sql_to_arel/pg_query_visitor.rb +1005 -0
- data/lib/arel/sql_to_arel/unbound_column_reference.rb +5 -0
- data/lib/arel_toolkit.rb +4 -3
- data/lib/arel_toolkit/version.rb +1 -1
- metadata +250 -4
@@ -0,0 +1,26 @@
|
|
1
|
+
# rubocop:disable Naming/MethodName
|
2
|
+
# rubocop:disable Naming/UncommunicativeMethodParamName
|
3
|
+
|
4
|
+
module Arel
|
5
|
+
module Nodes
|
6
|
+
Arel::Nodes::SelectStatement.class_eval do
|
7
|
+
# For INSERT statements
|
8
|
+
attr_accessor :values_lists
|
9
|
+
attr_accessor :union
|
10
|
+
attr_writer :cores
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
module Visitors
|
15
|
+
class ToSql
|
16
|
+
alias old_visit_Nodes_SelectStatement visit_Arel_Nodes_SelectStatement
|
17
|
+
def visit_Arel_Nodes_SelectStatement(o, collector)
|
18
|
+
visit(o.union, collector) if o.union
|
19
|
+
old_visit_Nodes_SelectStatement(o, collector)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# rubocop:enable Naming/MethodName
|
26
|
+
# rubocop:enable Naming/UncommunicativeMethodParamName
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# rubocop:disable Naming/MethodName
|
2
|
+
# rubocop:disable Naming/UncommunicativeMethodParamName
|
3
|
+
|
4
|
+
module Arel
|
5
|
+
module Nodes
|
6
|
+
class SessionUser < Arel::Nodes::Node
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
module Visitors
|
11
|
+
class ToSql
|
12
|
+
def visit_Arel_Nodes_SessionUser(_o, collector)
|
13
|
+
collector << 'session_user'
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# rubocop:enable Naming/MethodName
|
20
|
+
# rubocop:enable Naming/UncommunicativeMethodParamName
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# rubocop:disable Naming/MethodName
|
2
|
+
# rubocop:disable Naming/UncommunicativeMethodParamName
|
3
|
+
|
4
|
+
module Arel
|
5
|
+
module Nodes
|
6
|
+
# https://www.postgresql.org/docs/9.5/sql-insert.html
|
7
|
+
class SetToDefault < Arel::Nodes::Node
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
module Visitors
|
12
|
+
class ToSql
|
13
|
+
def visit_Arel_Nodes_SetToDefault(_o, collector)
|
14
|
+
collector << 'DEFAULT'
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# rubocop:enable Naming/MethodName
|
21
|
+
# rubocop:enable Naming/UncommunicativeMethodParamName
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# rubocop:disable Naming/MethodName
|
2
|
+
# rubocop:disable Naming/UncommunicativeMethodParamName
|
3
|
+
|
4
|
+
module Arel
|
5
|
+
module Nodes
|
6
|
+
# Postgres: https://www.postgresql.org/docs/9/functions-matching.html
|
7
|
+
class Similar < Arel::Nodes::Matches
|
8
|
+
def initialize(left, right, escape = nil)
|
9
|
+
super(left, right, escape, false)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
module Visitors
|
15
|
+
class ToSql
|
16
|
+
def visit_Arel_Nodes_Similar(o, collector)
|
17
|
+
visit o.left, collector
|
18
|
+
collector << ' SIMILAR TO '
|
19
|
+
visit o.right, collector
|
20
|
+
if o.escape
|
21
|
+
collector << ' ESCAPE '
|
22
|
+
visit o.escape, collector
|
23
|
+
else
|
24
|
+
collector
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# rubocop:enable Naming/MethodName
|
32
|
+
# rubocop:enable Naming/UncommunicativeMethodParamName
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# rubocop:disable Naming/MethodName
|
2
|
+
# rubocop:disable Naming/UncommunicativeMethodParamName
|
3
|
+
# rubocop:disable Metrics/ParameterLists
|
4
|
+
|
5
|
+
module Arel
|
6
|
+
module Nodes
|
7
|
+
Arel::Table.class_eval do
|
8
|
+
# postgres only: https://www.postgresql.org/docs/9.5/sql-select.html
|
9
|
+
attr_accessor :only
|
10
|
+
# postgres only: https://www.postgresql.org/docs/9.5/ddl-schemas.html
|
11
|
+
attr_accessor :schema_name
|
12
|
+
# postgres only: https://www.postgresql.org/docs/9.1/catalog-pg-class.html
|
13
|
+
attr_accessor :relpersistence
|
14
|
+
|
15
|
+
alias_method :old_initialize, :initialize
|
16
|
+
def initialize(
|
17
|
+
name,
|
18
|
+
as: nil,
|
19
|
+
type_caster: nil,
|
20
|
+
only: false,
|
21
|
+
schema_name: nil,
|
22
|
+
relpersistence: 'p'
|
23
|
+
)
|
24
|
+
@only = only
|
25
|
+
@schema_name = schema_name
|
26
|
+
@relpersistence = relpersistence
|
27
|
+
|
28
|
+
old_initialize(name, as: as, type_caster: type_caster)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
module Visitors
|
34
|
+
class ToSql
|
35
|
+
alias old_visit_Arel_Table visit_Arel_Table
|
36
|
+
def visit_Arel_Table(o, collector)
|
37
|
+
collector << 'ONLY ' if o.only
|
38
|
+
|
39
|
+
collector << "\"#{o.schema_name}\"." if o.schema_name
|
40
|
+
|
41
|
+
old_visit_Arel_Table(o, collector)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# rubocop:enable Naming/MethodName
|
48
|
+
# rubocop:enable Naming/UncommunicativeMethodParamName
|
49
|
+
# rubocop:enable Metrics/ParameterLists
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# rubocop:disable Naming/MethodName
|
2
|
+
# rubocop:disable Naming/UncommunicativeMethodParamName
|
3
|
+
|
4
|
+
module Arel
|
5
|
+
module Nodes
|
6
|
+
# Postgres: https://www.postgresql.org/docs/9.1/sql-expressions.html
|
7
|
+
class TypeCast < Arel::Nodes::Node
|
8
|
+
attr_reader :arg
|
9
|
+
attr_reader :type_name
|
10
|
+
|
11
|
+
def initialize(arg, type_name)
|
12
|
+
@arg = arg
|
13
|
+
@type_name = type_name
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
module Visitors
|
19
|
+
class ToSql
|
20
|
+
def visit_Arel_Nodes_TypeCast(o, collector)
|
21
|
+
visit o.arg, collector
|
22
|
+
collector << '::'
|
23
|
+
collector << o.type_name
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# rubocop:enable Naming/MethodName
|
30
|
+
# rubocop:enable Naming/UncommunicativeMethodParamName
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# rubocop:disable Naming/MethodName
|
2
|
+
# rubocop:disable Naming/UncommunicativeMethodParamName
|
3
|
+
|
4
|
+
module Arel
|
5
|
+
module Nodes
|
6
|
+
class Unknown < Arel::Nodes::Node
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
module Visitors
|
11
|
+
class ToSql
|
12
|
+
def visit_Arel_Nodes_Unknown(_o, collector)
|
13
|
+
collector << 'UNKNOWN'
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# rubocop:enable Naming/MethodName
|
20
|
+
# rubocop:enable Naming/UncommunicativeMethodParamName
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# rubocop:disable Naming/MethodName
|
2
|
+
# rubocop:disable Naming/UncommunicativeMethodParamName
|
3
|
+
|
4
|
+
module Arel
|
5
|
+
module Nodes
|
6
|
+
# https://www.postgresql.org/docs/10/sql-update.html
|
7
|
+
Arel::Nodes::UpdateStatement.class_eval do
|
8
|
+
attr_accessor :with
|
9
|
+
attr_accessor :froms
|
10
|
+
attr_accessor :returning
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
module Visitors
|
15
|
+
class ToSql
|
16
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
17
|
+
# rubocop:disable Metrics/AbcSize
|
18
|
+
# rubocop:disable Metrics/PerceivedComplexity
|
19
|
+
def visit_Arel_Nodes_UpdateStatement(o, collector)
|
20
|
+
if o.with
|
21
|
+
collector = visit o.with, collector
|
22
|
+
collector << SPACE
|
23
|
+
end
|
24
|
+
|
25
|
+
wheres = if o.orders.empty? && o.limit.nil?
|
26
|
+
o.wheres
|
27
|
+
else
|
28
|
+
[Nodes::In.new(o.key, [build_subselect(o.key, o)])]
|
29
|
+
end
|
30
|
+
|
31
|
+
collector << 'UPDATE '
|
32
|
+
collector = visit o.relation, collector
|
33
|
+
unless o.values.empty?
|
34
|
+
collector << ' SET '
|
35
|
+
collector = inject_join o.values, collector, ', '
|
36
|
+
end
|
37
|
+
|
38
|
+
unless o.froms.empty?
|
39
|
+
collector << ' FROM '
|
40
|
+
collector = inject_join o.froms, collector, ', '
|
41
|
+
end
|
42
|
+
|
43
|
+
unless wheres.empty?
|
44
|
+
collector << ' WHERE '
|
45
|
+
collector = inject_join wheres, collector, ' AND '
|
46
|
+
end
|
47
|
+
|
48
|
+
unless o.returning.empty?
|
49
|
+
collector << ' RETURNING '
|
50
|
+
collector = inject_join o.returning, collector, ', '
|
51
|
+
end
|
52
|
+
|
53
|
+
collector
|
54
|
+
end
|
55
|
+
# rubocop:enable Metrics/AbcSize
|
56
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
57
|
+
# rubocop:enable Metrics/PerceivedComplexity
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# rubocop:enable Naming/MethodName
|
63
|
+
# rubocop:enable Naming/UncommunicativeMethodParamName
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# rubocop:disable Naming/MethodName
|
2
|
+
# rubocop:disable Naming/UncommunicativeMethodParamName
|
3
|
+
|
4
|
+
module Arel
|
5
|
+
module Nodes
|
6
|
+
class User < Arel::Nodes::Node
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
module Visitors
|
11
|
+
class ToSql
|
12
|
+
def visit_Arel_Nodes_User(_o, collector)
|
13
|
+
collector << 'user'
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# rubocop:enable Naming/MethodName
|
20
|
+
# rubocop:enable Naming/UncommunicativeMethodParamName
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# rubocop:disable Naming/MethodName
|
2
|
+
# rubocop:disable Naming/UncommunicativeMethodParamName
|
3
|
+
|
4
|
+
module Arel
|
5
|
+
module Nodes
|
6
|
+
# Postgres: https://paquier.xyz/postgresql-2/postgres-9-4-feature-highlight-with-ordinality/
|
7
|
+
class WithOrdinality < Arel::Nodes::Unary
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
module Visitors
|
12
|
+
class ToSql
|
13
|
+
def visit_Arel_Nodes_WithOrdinality(o, collector)
|
14
|
+
visit o.expr, collector
|
15
|
+
collector << ' WITH ORDINALITY'
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# rubocop:enable Naming/MethodName
|
22
|
+
# rubocop:enable Naming/UncommunicativeMethodParamName
|
@@ -0,0 +1,110 @@
|
|
1
|
+
module Arel
|
2
|
+
module SqlToArel
|
3
|
+
class FrameOptions
|
4
|
+
class << self
|
5
|
+
def arel(frame_options, start_offset, end_offset)
|
6
|
+
frame_option_names = calculate_frame_option_names(frame_options)
|
7
|
+
return unless frame_option_names.include?('FRAMEOPTION_NONDEFAULT')
|
8
|
+
|
9
|
+
range_klass = if frame_option_names.include?('FRAMEOPTION_RANGE')
|
10
|
+
Arel::Nodes::Range
|
11
|
+
else
|
12
|
+
Arel::Nodes::Rows
|
13
|
+
end
|
14
|
+
|
15
|
+
start_node = calculate_frame_node(
|
16
|
+
'FRAMEOPTION_START_',
|
17
|
+
frame_option_names,
|
18
|
+
start_offset,
|
19
|
+
)
|
20
|
+
end_node = calculate_frame_node(
|
21
|
+
'FRAMEOPTION_END_',
|
22
|
+
frame_option_names,
|
23
|
+
end_offset,
|
24
|
+
)
|
25
|
+
|
26
|
+
if frame_option_names.include?('FRAMEOPTION_BETWEEN')
|
27
|
+
Arel::Nodes::Between.new(
|
28
|
+
range_klass.new,
|
29
|
+
Arel::Nodes::And.new([start_node, end_node]),
|
30
|
+
)
|
31
|
+
else
|
32
|
+
range_klass.new start_node
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
# always NONDEFAULT
|
39
|
+
# RANGE or ROWS
|
40
|
+
# mandatory BETWEEN
|
41
|
+
# RANGE only unbounded
|
42
|
+
# ROWS all
|
43
|
+
# https://github.com/postgres/postgres/blob/REL_10_1/src/include/nodes/parsenodes.h
|
44
|
+
FRAMEOPTIONS = {
|
45
|
+
'FRAMEOPTION_NONDEFAULT' => 0x00001,
|
46
|
+
'FRAMEOPTION_RANGE' => 0x00002,
|
47
|
+
'FRAMEOPTION_ROWS' => 0x00004,
|
48
|
+
'FRAMEOPTION_BETWEEN' => 0x00008,
|
49
|
+
'FRAMEOPTION_START_UNBOUNDED_PRECEDING' => 0x00010,
|
50
|
+
'FRAMEOPTION_END_UNBOUNDED_PRECEDING' => 0x00020,
|
51
|
+
'FRAMEOPTION_START_UNBOUNDED_FOLLOWING' => 0x00040,
|
52
|
+
'FRAMEOPTION_END_UNBOUNDED_FOLLOWING' => 0x00080,
|
53
|
+
'FRAMEOPTION_START_CURRENT_ROW' => 0x00100,
|
54
|
+
'FRAMEOPTION_END_CURRENT_ROW' => 0x00200,
|
55
|
+
'FRAMEOPTION_START_VALUE_PRECEDING' => 0x00400,
|
56
|
+
'FRAMEOPTION_END_VALUE_PRECEDING' => 0x00800,
|
57
|
+
'FRAMEOPTION_START_VALUE_FOLLOWING' => 0x01000,
|
58
|
+
'FRAMEOPTION_END_VALUE_FOLLOWING' => 0x02000
|
59
|
+
}.freeze
|
60
|
+
|
61
|
+
def biggest_detractable_number(number, candidates)
|
62
|
+
high_to_low_candidates = candidates.sort { |a, b| b <=> a }
|
63
|
+
high_to_low_candidates.find do |candidate|
|
64
|
+
number - candidate >= 0
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def calculate_frame_option_names(frame_options, names = [])
|
69
|
+
return names if frame_options.zero?
|
70
|
+
|
71
|
+
number = biggest_detractable_number(frame_options, FRAMEOPTIONS.values)
|
72
|
+
name = FRAMEOPTIONS.key(number)
|
73
|
+
calculate_frame_option_names(
|
74
|
+
frame_options - number, names + [name]
|
75
|
+
)
|
76
|
+
end
|
77
|
+
|
78
|
+
def calculate_frame_node(pattern, frame_option_names, offset)
|
79
|
+
node_name = frame_option_names.select { |n| n.start_with?(pattern) }
|
80
|
+
raise "Don't know how to handle multiple nodes" if node_name.length > 1
|
81
|
+
|
82
|
+
node_name = node_name.first.gsub(/FRAMEOPTION_(START|END)_/, '')
|
83
|
+
name_to_node(node_name, offset)
|
84
|
+
end
|
85
|
+
|
86
|
+
def name_to_node(node_name, offset)
|
87
|
+
case node_name
|
88
|
+
when 'UNBOUNDED_PRECEDING'
|
89
|
+
Arel::Nodes::Preceding.new
|
90
|
+
|
91
|
+
when 'UNBOUNDED_FOLLOWING'
|
92
|
+
Arel::Nodes::Following.new
|
93
|
+
|
94
|
+
when 'CURRENT_ROW'
|
95
|
+
Arel::Nodes::CurrentRow.new
|
96
|
+
|
97
|
+
when 'VALUE_PRECEDING'
|
98
|
+
Arel::Nodes::Preceding.new offset
|
99
|
+
|
100
|
+
when 'VALUE_FOLLOWING'
|
101
|
+
Arel::Nodes::Following.new offset
|
102
|
+
|
103
|
+
else
|
104
|
+
raise "Unknown start / end frame node `#{node_name}`"
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|