activerecord 5.2.4.5 → 6.0.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +299 -739
- data/MIT-LICENSE +3 -1
- data/README.rdoc +1 -1
- data/examples/performance.rb +1 -1
- data/lib/active_record.rb +2 -1
- data/lib/active_record/aggregations.rb +4 -2
- data/lib/active_record/associations.rb +16 -12
- data/lib/active_record/associations/association.rb +35 -19
- data/lib/active_record/associations/association_scope.rb +4 -6
- data/lib/active_record/associations/belongs_to_association.rb +36 -42
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +0 -4
- data/lib/active_record/associations/builder/belongs_to.rb +14 -50
- data/lib/active_record/associations/builder/collection_association.rb +3 -3
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -38
- data/lib/active_record/associations/collection_association.rb +11 -25
- data/lib/active_record/associations/collection_proxy.rb +32 -6
- data/lib/active_record/associations/foreign_association.rb +7 -0
- data/lib/active_record/associations/has_many_association.rb +1 -1
- data/lib/active_record/associations/has_many_through_association.rb +25 -18
- data/lib/active_record/associations/has_one_association.rb +28 -30
- data/lib/active_record/associations/has_one_through_association.rb +5 -5
- data/lib/active_record/associations/join_dependency.rb +15 -20
- data/lib/active_record/associations/join_dependency/join_association.rb +11 -26
- data/lib/active_record/associations/join_dependency/join_part.rb +2 -2
- data/lib/active_record/associations/preloader.rb +32 -29
- data/lib/active_record/associations/preloader/association.rb +1 -2
- data/lib/active_record/associations/singular_association.rb +2 -16
- data/lib/active_record/attribute_assignment.rb +7 -10
- data/lib/active_record/attribute_methods.rb +34 -56
- data/lib/active_record/attribute_methods/dirty.rb +64 -26
- data/lib/active_record/attribute_methods/primary_key.rb +8 -7
- data/lib/active_record/attribute_methods/read.rb +16 -48
- data/lib/active_record/attribute_methods/serialization.rb +1 -1
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +1 -1
- data/lib/active_record/attribute_methods/write.rb +15 -16
- data/lib/active_record/autosave_association.rb +7 -21
- data/lib/active_record/base.rb +2 -2
- data/lib/active_record/callbacks.rb +3 -17
- data/lib/active_record/collection_cache_key.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +13 -36
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +25 -84
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +17 -14
- data/lib/active_record/connection_adapters/abstract/quoting.rb +5 -11
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +15 -11
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +30 -13
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +0 -2
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +41 -27
- data/lib/active_record/connection_adapters/abstract/transaction.rb +81 -52
- data/lib/active_record/connection_adapters/abstract_adapter.rb +95 -31
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +65 -90
- data/lib/active_record/connection_adapters/connection_specification.rb +52 -42
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +5 -9
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +29 -7
- data/lib/active_record/connection_adapters/mysql/quoting.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +3 -4
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +65 -10
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +8 -4
- data/lib/active_record/connection_adapters/postgresql/column.rb +1 -2
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +16 -1
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +9 -7
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +4 -4
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +11 -36
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +9 -2
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +38 -20
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +2 -1
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +75 -56
- data/lib/active_record/connection_adapters/schema_cache.rb +5 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +5 -5
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +14 -9
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +95 -62
- data/lib/active_record/connection_handling.rb +132 -26
- data/lib/active_record/core.rb +76 -43
- data/lib/active_record/counter_cache.rb +4 -29
- data/lib/active_record/database_configurations.rb +184 -0
- data/lib/active_record/database_configurations/database_config.rb +37 -0
- data/lib/active_record/database_configurations/hash_config.rb +50 -0
- data/lib/active_record/database_configurations/url_config.rb +74 -0
- data/lib/active_record/enum.rb +22 -7
- data/lib/active_record/errors.rb +24 -21
- data/lib/active_record/explain.rb +1 -1
- data/lib/active_record/fixture_set/model_metadata.rb +33 -0
- data/lib/active_record/fixture_set/render_context.rb +17 -0
- data/lib/active_record/fixture_set/table_row.rb +153 -0
- data/lib/active_record/fixture_set/table_rows.rb +47 -0
- data/lib/active_record/fixtures.rb +140 -472
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +12 -2
- data/lib/active_record/integration.rb +56 -16
- data/lib/active_record/internal_metadata.rb +5 -1
- data/lib/active_record/locking/optimistic.rb +2 -2
- data/lib/active_record/locking/pessimistic.rb +3 -3
- data/lib/active_record/log_subscriber.rb +7 -26
- data/lib/active_record/migration.rb +38 -37
- data/lib/active_record/migration/command_recorder.rb +35 -5
- data/lib/active_record/migration/compatibility.rb +34 -16
- data/lib/active_record/model_schema.rb +30 -9
- data/lib/active_record/nested_attributes.rb +2 -2
- data/lib/active_record/no_touching.rb +7 -0
- data/lib/active_record/persistence.rb +18 -7
- data/lib/active_record/query_cache.rb +11 -4
- data/lib/active_record/querying.rb +19 -11
- data/lib/active_record/railtie.rb +71 -42
- data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
- data/lib/active_record/railties/controller_runtime.rb +30 -35
- data/lib/active_record/railties/databases.rake +94 -43
- data/lib/active_record/reflection.rb +60 -44
- data/lib/active_record/relation.rb +150 -69
- data/lib/active_record/relation/batches.rb +13 -10
- data/lib/active_record/relation/calculations.rb +38 -28
- data/lib/active_record/relation/delegation.rb +4 -13
- data/lib/active_record/relation/finder_methods.rb +12 -25
- data/lib/active_record/relation/merger.rb +2 -6
- data/lib/active_record/relation/predicate_builder.rb +4 -6
- data/lib/active_record/relation/predicate_builder/array_handler.rb +5 -4
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +1 -4
- data/lib/active_record/relation/predicate_builder/base_handler.rb +1 -2
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -4
- data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
- data/lib/active_record/relation/query_attribute.rb +15 -12
- data/lib/active_record/relation/query_methods.rb +29 -52
- data/lib/active_record/relation/where_clause.rb +4 -0
- data/lib/active_record/relation/where_clause_factory.rb +1 -2
- data/lib/active_record/result.rb +30 -11
- data/lib/active_record/sanitization.rb +2 -39
- data/lib/active_record/schema.rb +1 -10
- data/lib/active_record/schema_dumper.rb +12 -6
- data/lib/active_record/schema_migration.rb +4 -0
- data/lib/active_record/scoping.rb +9 -8
- data/lib/active_record/scoping/default.rb +10 -3
- data/lib/active_record/scoping/named.rb +10 -14
- data/lib/active_record/statement_cache.rb +32 -5
- data/lib/active_record/store.rb +39 -8
- data/lib/active_record/table_metadata.rb +1 -4
- data/lib/active_record/tasks/database_tasks.rb +89 -23
- data/lib/active_record/tasks/mysql_database_tasks.rb +2 -4
- data/lib/active_record/tasks/postgresql_database_tasks.rb +5 -7
- data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -8
- data/lib/active_record/test_databases.rb +38 -0
- data/lib/active_record/test_fixtures.rb +224 -0
- data/lib/active_record/timestamp.rb +4 -6
- data/lib/active_record/transactions.rb +3 -22
- data/lib/active_record/translation.rb +1 -1
- data/lib/active_record/type.rb +3 -4
- data/lib/active_record/type/adapter_specific_registry.rb +1 -8
- data/lib/active_record/type_caster/connection.rb +1 -6
- data/lib/active_record/type_caster/map.rb +1 -4
- data/lib/active_record/validations/uniqueness.rb +13 -25
- data/lib/arel.rb +44 -0
- data/lib/arel/alias_predication.rb +9 -0
- data/lib/arel/attributes.rb +22 -0
- data/lib/arel/attributes/attribute.rb +37 -0
- data/lib/arel/collectors/bind.rb +24 -0
- data/lib/arel/collectors/composite.rb +31 -0
- data/lib/arel/collectors/plain_string.rb +20 -0
- data/lib/arel/collectors/sql_string.rb +20 -0
- data/lib/arel/collectors/substitute_binds.rb +28 -0
- data/lib/arel/crud.rb +42 -0
- data/lib/arel/delete_manager.rb +18 -0
- data/lib/arel/errors.rb +9 -0
- data/lib/arel/expressions.rb +29 -0
- data/lib/arel/factory_methods.rb +49 -0
- data/lib/arel/insert_manager.rb +49 -0
- data/lib/arel/math.rb +45 -0
- data/lib/arel/nodes.rb +67 -0
- data/lib/arel/nodes/and.rb +32 -0
- data/lib/arel/nodes/ascending.rb +23 -0
- data/lib/arel/nodes/binary.rb +52 -0
- data/lib/arel/nodes/bind_param.rb +36 -0
- data/lib/arel/nodes/case.rb +55 -0
- data/lib/arel/nodes/casted.rb +50 -0
- data/lib/arel/nodes/count.rb +12 -0
- data/lib/arel/nodes/delete_statement.rb +45 -0
- data/lib/arel/nodes/descending.rb +23 -0
- data/lib/arel/nodes/equality.rb +18 -0
- data/lib/arel/nodes/extract.rb +24 -0
- data/lib/arel/nodes/false.rb +16 -0
- data/lib/arel/nodes/full_outer_join.rb +8 -0
- data/lib/arel/nodes/function.rb +44 -0
- data/lib/arel/nodes/grouping.rb +8 -0
- data/lib/arel/nodes/in.rb +8 -0
- data/lib/arel/nodes/infix_operation.rb +80 -0
- data/lib/arel/nodes/inner_join.rb +8 -0
- data/lib/arel/nodes/insert_statement.rb +37 -0
- data/lib/arel/nodes/join_source.rb +20 -0
- data/lib/arel/nodes/matches.rb +18 -0
- data/lib/arel/nodes/named_function.rb +23 -0
- data/lib/arel/nodes/node.rb +50 -0
- data/lib/arel/nodes/node_expression.rb +13 -0
- data/lib/arel/nodes/outer_join.rb +8 -0
- data/lib/arel/nodes/over.rb +15 -0
- data/lib/arel/nodes/regexp.rb +16 -0
- data/lib/arel/nodes/right_outer_join.rb +8 -0
- data/lib/arel/nodes/select_core.rb +63 -0
- data/lib/arel/nodes/select_statement.rb +41 -0
- data/lib/arel/nodes/sql_literal.rb +16 -0
- data/lib/arel/nodes/string_join.rb +11 -0
- data/lib/arel/nodes/table_alias.rb +27 -0
- data/lib/arel/nodes/terminal.rb +16 -0
- data/lib/arel/nodes/true.rb +16 -0
- data/lib/arel/nodes/unary.rb +44 -0
- data/lib/arel/nodes/unary_operation.rb +20 -0
- data/lib/arel/nodes/unqualified_column.rb +22 -0
- data/lib/arel/nodes/update_statement.rb +41 -0
- data/lib/arel/nodes/values.rb +16 -0
- data/lib/arel/nodes/values_list.rb +24 -0
- data/lib/arel/nodes/window.rb +126 -0
- data/lib/arel/nodes/with.rb +11 -0
- data/lib/arel/order_predications.rb +13 -0
- data/lib/arel/predications.rb +257 -0
- data/lib/arel/select_manager.rb +271 -0
- data/lib/arel/table.rb +110 -0
- data/lib/arel/tree_manager.rb +72 -0
- data/lib/arel/update_manager.rb +34 -0
- data/lib/arel/visitors.rb +20 -0
- data/lib/arel/visitors/depth_first.rb +199 -0
- data/lib/arel/visitors/dot.rb +292 -0
- data/lib/arel/visitors/ibm_db.rb +21 -0
- data/lib/arel/visitors/informix.rb +56 -0
- data/lib/arel/visitors/mssql.rb +143 -0
- data/lib/arel/visitors/mysql.rb +83 -0
- data/lib/arel/visitors/oracle.rb +159 -0
- data/lib/arel/visitors/oracle12.rb +67 -0
- data/lib/arel/visitors/postgresql.rb +116 -0
- data/lib/arel/visitors/sqlite.rb +39 -0
- data/lib/arel/visitors/to_sql.rb +913 -0
- data/lib/arel/visitors/visitor.rb +42 -0
- data/lib/arel/visitors/where_sql.rb +23 -0
- data/lib/arel/window_predications.rb +9 -0
- data/lib/rails/generators/active_record/migration.rb +14 -1
- data/lib/rails/generators/active_record/migration/migration_generator.rb +2 -5
- data/lib/rails/generators/active_record/model/model_generator.rb +1 -0
- metadata +104 -26
@@ -0,0 +1,67 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Arel # :nodoc: all
|
4
|
+
module Visitors
|
5
|
+
class Oracle12 < Arel::Visitors::ToSql
|
6
|
+
private
|
7
|
+
|
8
|
+
def visit_Arel_Nodes_SelectStatement(o, collector)
|
9
|
+
# Oracle does not allow LIMIT clause with select for update
|
10
|
+
if o.limit && o.lock
|
11
|
+
raise ArgumentError, <<-MSG
|
12
|
+
'Combination of limit and lock is not supported.
|
13
|
+
because generated SQL statements
|
14
|
+
`SELECT FOR UPDATE and FETCH FIRST n ROWS` generates ORA-02014.`
|
15
|
+
MSG
|
16
|
+
end
|
17
|
+
super
|
18
|
+
end
|
19
|
+
|
20
|
+
def visit_Arel_Nodes_SelectOptions(o, collector)
|
21
|
+
collector = maybe_visit o.offset, collector
|
22
|
+
collector = maybe_visit o.limit, collector
|
23
|
+
maybe_visit o.lock, collector
|
24
|
+
end
|
25
|
+
|
26
|
+
def visit_Arel_Nodes_Limit(o, collector)
|
27
|
+
collector << "FETCH FIRST "
|
28
|
+
collector = visit o.expr, collector
|
29
|
+
collector << " ROWS ONLY"
|
30
|
+
end
|
31
|
+
|
32
|
+
def visit_Arel_Nodes_Offset(o, collector)
|
33
|
+
collector << "OFFSET "
|
34
|
+
visit o.expr, collector
|
35
|
+
collector << " ROWS"
|
36
|
+
end
|
37
|
+
|
38
|
+
def visit_Arel_Nodes_Except(o, collector)
|
39
|
+
collector << "( "
|
40
|
+
collector = infix_value o, collector, " MINUS "
|
41
|
+
collector << " )"
|
42
|
+
end
|
43
|
+
|
44
|
+
def visit_Arel_Nodes_UpdateStatement(o, collector)
|
45
|
+
# Oracle does not allow ORDER BY/LIMIT in UPDATEs.
|
46
|
+
if o.orders.any? && o.limit.nil?
|
47
|
+
# However, there is no harm in silently eating the ORDER BY clause if no LIMIT has been provided,
|
48
|
+
# otherwise let the user deal with the error
|
49
|
+
o = o.dup
|
50
|
+
o.orders = []
|
51
|
+
end
|
52
|
+
|
53
|
+
super
|
54
|
+
end
|
55
|
+
|
56
|
+
def visit_Arel_Nodes_BindParam(o, collector)
|
57
|
+
collector.add_bind(o.value) { |i| ":a#{i}" }
|
58
|
+
end
|
59
|
+
|
60
|
+
def is_distinct_from(o, collector)
|
61
|
+
collector << "DECODE("
|
62
|
+
collector = visit [o.left, o.right, 0, 1], collector
|
63
|
+
collector << ")"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Arel # :nodoc: all
|
4
|
+
module Visitors
|
5
|
+
class PostgreSQL < Arel::Visitors::ToSql
|
6
|
+
CUBE = "CUBE"
|
7
|
+
ROLLUP = "ROLLUP"
|
8
|
+
GROUPING_SETS = "GROUPING SETS"
|
9
|
+
LATERAL = "LATERAL"
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def visit_Arel_Nodes_Matches(o, collector)
|
14
|
+
op = o.case_sensitive ? " LIKE " : " ILIKE "
|
15
|
+
collector = infix_value o, collector, op
|
16
|
+
if o.escape
|
17
|
+
collector << " ESCAPE "
|
18
|
+
visit o.escape, collector
|
19
|
+
else
|
20
|
+
collector
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def visit_Arel_Nodes_DoesNotMatch(o, collector)
|
25
|
+
op = o.case_sensitive ? " NOT LIKE " : " NOT ILIKE "
|
26
|
+
collector = infix_value o, collector, op
|
27
|
+
if o.escape
|
28
|
+
collector << " ESCAPE "
|
29
|
+
visit o.escape, collector
|
30
|
+
else
|
31
|
+
collector
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def visit_Arel_Nodes_Regexp(o, collector)
|
36
|
+
op = o.case_sensitive ? " ~ " : " ~* "
|
37
|
+
infix_value o, collector, op
|
38
|
+
end
|
39
|
+
|
40
|
+
def visit_Arel_Nodes_NotRegexp(o, collector)
|
41
|
+
op = o.case_sensitive ? " !~ " : " !~* "
|
42
|
+
infix_value o, collector, op
|
43
|
+
end
|
44
|
+
|
45
|
+
def visit_Arel_Nodes_DistinctOn(o, collector)
|
46
|
+
collector << "DISTINCT ON ( "
|
47
|
+
visit(o.expr, collector) << " )"
|
48
|
+
end
|
49
|
+
|
50
|
+
def visit_Arel_Nodes_BindParam(o, collector)
|
51
|
+
collector.add_bind(o.value) { |i| "$#{i}" }
|
52
|
+
end
|
53
|
+
|
54
|
+
def visit_Arel_Nodes_GroupingElement(o, collector)
|
55
|
+
collector << "( "
|
56
|
+
visit(o.expr, collector) << " )"
|
57
|
+
end
|
58
|
+
|
59
|
+
def visit_Arel_Nodes_Cube(o, collector)
|
60
|
+
collector << CUBE
|
61
|
+
grouping_array_or_grouping_element o, collector
|
62
|
+
end
|
63
|
+
|
64
|
+
def visit_Arel_Nodes_RollUp(o, collector)
|
65
|
+
collector << ROLLUP
|
66
|
+
grouping_array_or_grouping_element o, collector
|
67
|
+
end
|
68
|
+
|
69
|
+
def visit_Arel_Nodes_GroupingSet(o, collector)
|
70
|
+
collector << GROUPING_SETS
|
71
|
+
grouping_array_or_grouping_element o, collector
|
72
|
+
end
|
73
|
+
|
74
|
+
def visit_Arel_Nodes_Lateral(o, collector)
|
75
|
+
collector << LATERAL
|
76
|
+
collector << SPACE
|
77
|
+
grouping_parentheses o, collector
|
78
|
+
end
|
79
|
+
|
80
|
+
def visit_Arel_Nodes_IsNotDistinctFrom(o, collector)
|
81
|
+
collector = visit o.left, collector
|
82
|
+
collector << " IS NOT DISTINCT FROM "
|
83
|
+
visit o.right, collector
|
84
|
+
end
|
85
|
+
|
86
|
+
def visit_Arel_Nodes_IsDistinctFrom(o, collector)
|
87
|
+
collector = visit o.left, collector
|
88
|
+
collector << " IS DISTINCT FROM "
|
89
|
+
visit o.right, collector
|
90
|
+
end
|
91
|
+
|
92
|
+
# Used by Lateral visitor to enclose select queries in parentheses
|
93
|
+
def grouping_parentheses(o, collector)
|
94
|
+
if o.expr.is_a? Nodes::SelectStatement
|
95
|
+
collector << "("
|
96
|
+
visit o.expr, collector
|
97
|
+
collector << ")"
|
98
|
+
else
|
99
|
+
visit o.expr, collector
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
# Utilized by GroupingSet, Cube & RollUp visitors to
|
104
|
+
# handle grouping aggregation semantics
|
105
|
+
def grouping_array_or_grouping_element(o, collector)
|
106
|
+
if o.expr.is_a? Array
|
107
|
+
collector << "( "
|
108
|
+
visit o.expr, collector
|
109
|
+
collector << " )"
|
110
|
+
else
|
111
|
+
visit o.expr, collector
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Arel # :nodoc: all
|
4
|
+
module Visitors
|
5
|
+
class SQLite < Arel::Visitors::ToSql
|
6
|
+
private
|
7
|
+
|
8
|
+
# Locks are not supported in SQLite
|
9
|
+
def visit_Arel_Nodes_Lock(o, collector)
|
10
|
+
collector
|
11
|
+
end
|
12
|
+
|
13
|
+
def visit_Arel_Nodes_SelectStatement(o, collector)
|
14
|
+
o.limit = Arel::Nodes::Limit.new(-1) if o.offset && !o.limit
|
15
|
+
super
|
16
|
+
end
|
17
|
+
|
18
|
+
def visit_Arel_Nodes_True(o, collector)
|
19
|
+
collector << "1"
|
20
|
+
end
|
21
|
+
|
22
|
+
def visit_Arel_Nodes_False(o, collector)
|
23
|
+
collector << "0"
|
24
|
+
end
|
25
|
+
|
26
|
+
def visit_Arel_Nodes_IsNotDistinctFrom(o, collector)
|
27
|
+
collector = visit o.left, collector
|
28
|
+
collector << " IS "
|
29
|
+
visit o.right, collector
|
30
|
+
end
|
31
|
+
|
32
|
+
def visit_Arel_Nodes_IsDistinctFrom(o, collector)
|
33
|
+
collector = visit o.left, collector
|
34
|
+
collector << " IS NOT "
|
35
|
+
visit o.right, collector
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,913 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Arel # :nodoc: all
|
4
|
+
module Visitors
|
5
|
+
class UnsupportedVisitError < StandardError
|
6
|
+
def initialize(object)
|
7
|
+
super "Unsupported argument type: #{object.class.name}. Construct an Arel node instead."
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class ToSql < Arel::Visitors::Visitor
|
12
|
+
##
|
13
|
+
# This is some roflscale crazy stuff. I'm roflscaling this because
|
14
|
+
# building SQL queries is a hotspot. I will explain the roflscale so that
|
15
|
+
# others will not rm this code.
|
16
|
+
#
|
17
|
+
# In YARV, string literals in a method body will get duped when the byte
|
18
|
+
# code is executed. Let's take a look:
|
19
|
+
#
|
20
|
+
# > puts RubyVM::InstructionSequence.new('def foo; "bar"; end').disasm
|
21
|
+
#
|
22
|
+
# == disasm: <RubyVM::InstructionSequence:foo@<compiled>>=====
|
23
|
+
# 0000 trace 8
|
24
|
+
# 0002 trace 1
|
25
|
+
# 0004 putstring "bar"
|
26
|
+
# 0006 trace 16
|
27
|
+
# 0008 leave
|
28
|
+
#
|
29
|
+
# The `putstring` bytecode will dup the string and push it on the stack.
|
30
|
+
# In many cases in our SQL visitor, that string is never mutated, so there
|
31
|
+
# is no need to dup the literal.
|
32
|
+
#
|
33
|
+
# If we change to a constant lookup, the string will not be duped, and we
|
34
|
+
# can reduce the objects in our system:
|
35
|
+
#
|
36
|
+
# > puts RubyVM::InstructionSequence.new('BAR = "bar"; def foo; BAR; end').disasm
|
37
|
+
#
|
38
|
+
# == disasm: <RubyVM::InstructionSequence:foo@<compiled>>========
|
39
|
+
# 0000 trace 8
|
40
|
+
# 0002 trace 1
|
41
|
+
# 0004 getinlinecache 11, <ic:0>
|
42
|
+
# 0007 getconstant :BAR
|
43
|
+
# 0009 setinlinecache <ic:0>
|
44
|
+
# 0011 trace 16
|
45
|
+
# 0013 leave
|
46
|
+
#
|
47
|
+
# `getconstant` should be a hash lookup, and no object is duped when the
|
48
|
+
# value of the constant is pushed on the stack. Hence the crazy
|
49
|
+
# constants below.
|
50
|
+
#
|
51
|
+
# `matches` and `doesNotMatch` operate case-insensitively via Visitor subclasses
|
52
|
+
# specialized for specific databases when necessary.
|
53
|
+
#
|
54
|
+
|
55
|
+
WHERE = " WHERE " # :nodoc:
|
56
|
+
SPACE = " " # :nodoc:
|
57
|
+
COMMA = ", " # :nodoc:
|
58
|
+
GROUP_BY = " GROUP BY " # :nodoc:
|
59
|
+
ORDER_BY = " ORDER BY " # :nodoc:
|
60
|
+
WINDOW = " WINDOW " # :nodoc:
|
61
|
+
AND = " AND " # :nodoc:
|
62
|
+
|
63
|
+
DISTINCT = "DISTINCT" # :nodoc:
|
64
|
+
|
65
|
+
def initialize(connection)
|
66
|
+
super()
|
67
|
+
@connection = connection
|
68
|
+
end
|
69
|
+
|
70
|
+
def compile(node, collector = Arel::Collectors::SQLString.new)
|
71
|
+
accept(node, collector).value
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
def visit_Arel_Nodes_DeleteStatement(o, collector)
|
77
|
+
o = prepare_delete_statement(o)
|
78
|
+
|
79
|
+
if has_join_sources?(o)
|
80
|
+
collector << "DELETE "
|
81
|
+
visit o.relation.left, collector
|
82
|
+
collector << " FROM "
|
83
|
+
else
|
84
|
+
collector << "DELETE FROM "
|
85
|
+
end
|
86
|
+
collector = visit o.relation, collector
|
87
|
+
|
88
|
+
collect_nodes_for o.wheres, collector, " WHERE ", " AND "
|
89
|
+
collect_nodes_for o.orders, collector, " ORDER BY "
|
90
|
+
maybe_visit o.limit, collector
|
91
|
+
end
|
92
|
+
|
93
|
+
def visit_Arel_Nodes_UpdateStatement(o, collector)
|
94
|
+
o = prepare_update_statement(o)
|
95
|
+
|
96
|
+
collector << "UPDATE "
|
97
|
+
collector = visit o.relation, collector
|
98
|
+
collect_nodes_for o.values, collector, " SET "
|
99
|
+
|
100
|
+
collect_nodes_for o.wheres, collector, " WHERE ", " AND "
|
101
|
+
collect_nodes_for o.orders, collector, " ORDER BY "
|
102
|
+
maybe_visit o.limit, collector
|
103
|
+
end
|
104
|
+
|
105
|
+
def visit_Arel_Nodes_InsertStatement(o, collector)
|
106
|
+
collector << "INSERT INTO "
|
107
|
+
collector = visit o.relation, collector
|
108
|
+
if o.columns.any?
|
109
|
+
collector << " (#{o.columns.map { |x|
|
110
|
+
quote_column_name x.name
|
111
|
+
}.join ', '})"
|
112
|
+
end
|
113
|
+
|
114
|
+
if o.values
|
115
|
+
maybe_visit o.values, collector
|
116
|
+
elsif o.select
|
117
|
+
maybe_visit o.select, collector
|
118
|
+
else
|
119
|
+
collector
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def visit_Arel_Nodes_Exists(o, collector)
|
124
|
+
collector << "EXISTS ("
|
125
|
+
collector = visit(o.expressions, collector) << ")"
|
126
|
+
if o.alias
|
127
|
+
collector << " AS "
|
128
|
+
visit o.alias, collector
|
129
|
+
else
|
130
|
+
collector
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def visit_Arel_Nodes_Casted(o, collector)
|
135
|
+
collector << quoted(o.val, o.attribute).to_s
|
136
|
+
end
|
137
|
+
|
138
|
+
def visit_Arel_Nodes_Quoted(o, collector)
|
139
|
+
collector << quoted(o.expr, nil).to_s
|
140
|
+
end
|
141
|
+
|
142
|
+
def visit_Arel_Nodes_True(o, collector)
|
143
|
+
collector << "TRUE"
|
144
|
+
end
|
145
|
+
|
146
|
+
def visit_Arel_Nodes_False(o, collector)
|
147
|
+
collector << "FALSE"
|
148
|
+
end
|
149
|
+
|
150
|
+
def visit_Arel_Nodes_ValuesList(o, collector)
|
151
|
+
collector << "VALUES "
|
152
|
+
|
153
|
+
len = o.rows.length - 1
|
154
|
+
o.rows.each_with_index { |row, i|
|
155
|
+
collector << "("
|
156
|
+
row_len = row.length - 1
|
157
|
+
row.each_with_index do |value, k|
|
158
|
+
case value
|
159
|
+
when Nodes::SqlLiteral, Nodes::BindParam
|
160
|
+
collector = visit(value, collector)
|
161
|
+
else
|
162
|
+
collector << quote(value)
|
163
|
+
end
|
164
|
+
collector << COMMA unless k == row_len
|
165
|
+
end
|
166
|
+
collector << ")"
|
167
|
+
collector << COMMA unless i == len
|
168
|
+
}
|
169
|
+
collector
|
170
|
+
end
|
171
|
+
|
172
|
+
def visit_Arel_Nodes_Values(o, collector)
|
173
|
+
collector << "VALUES ("
|
174
|
+
|
175
|
+
len = o.expressions.length - 1
|
176
|
+
o.expressions.each_with_index { |value, i|
|
177
|
+
case value
|
178
|
+
when Nodes::SqlLiteral, Nodes::BindParam
|
179
|
+
collector = visit value, collector
|
180
|
+
else
|
181
|
+
collector << quote(value).to_s
|
182
|
+
end
|
183
|
+
unless i == len
|
184
|
+
collector << COMMA
|
185
|
+
end
|
186
|
+
}
|
187
|
+
|
188
|
+
collector << ")"
|
189
|
+
end
|
190
|
+
|
191
|
+
def visit_Arel_Nodes_SelectStatement(o, collector)
|
192
|
+
if o.with
|
193
|
+
collector = visit o.with, collector
|
194
|
+
collector << SPACE
|
195
|
+
end
|
196
|
+
|
197
|
+
collector = o.cores.inject(collector) { |c, x|
|
198
|
+
visit_Arel_Nodes_SelectCore(x, c)
|
199
|
+
}
|
200
|
+
|
201
|
+
unless o.orders.empty?
|
202
|
+
collector << ORDER_BY
|
203
|
+
len = o.orders.length - 1
|
204
|
+
o.orders.each_with_index { |x, i|
|
205
|
+
collector = visit(x, collector)
|
206
|
+
collector << COMMA unless len == i
|
207
|
+
}
|
208
|
+
end
|
209
|
+
|
210
|
+
visit_Arel_Nodes_SelectOptions(o, collector)
|
211
|
+
end
|
212
|
+
|
213
|
+
def visit_Arel_Nodes_SelectOptions(o, collector)
|
214
|
+
collector = maybe_visit o.limit, collector
|
215
|
+
collector = maybe_visit o.offset, collector
|
216
|
+
maybe_visit o.lock, collector
|
217
|
+
end
|
218
|
+
|
219
|
+
def visit_Arel_Nodes_SelectCore(o, collector)
|
220
|
+
collector << "SELECT"
|
221
|
+
|
222
|
+
collector = maybe_visit o.set_quantifier, collector
|
223
|
+
|
224
|
+
collect_nodes_for o.projections, collector, SPACE
|
225
|
+
|
226
|
+
if o.source && !o.source.empty?
|
227
|
+
collector << " FROM "
|
228
|
+
collector = visit o.source, collector
|
229
|
+
end
|
230
|
+
|
231
|
+
collect_nodes_for o.wheres, collector, WHERE, AND
|
232
|
+
collect_nodes_for o.groups, collector, GROUP_BY
|
233
|
+
collect_nodes_for o.havings, collector, " HAVING ", AND
|
234
|
+
collect_nodes_for o.windows, collector, WINDOW
|
235
|
+
|
236
|
+
collector
|
237
|
+
end
|
238
|
+
|
239
|
+
def collect_nodes_for(nodes, collector, spacer, connector = COMMA)
|
240
|
+
unless nodes.empty?
|
241
|
+
collector << spacer
|
242
|
+
inject_join nodes, collector, connector
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
def visit_Arel_Nodes_Bin(o, collector)
|
247
|
+
visit o.expr, collector
|
248
|
+
end
|
249
|
+
|
250
|
+
def visit_Arel_Nodes_Distinct(o, collector)
|
251
|
+
collector << DISTINCT
|
252
|
+
end
|
253
|
+
|
254
|
+
def visit_Arel_Nodes_DistinctOn(o, collector)
|
255
|
+
raise NotImplementedError, "DISTINCT ON not implemented for this db"
|
256
|
+
end
|
257
|
+
|
258
|
+
def visit_Arel_Nodes_With(o, collector)
|
259
|
+
collector << "WITH "
|
260
|
+
inject_join o.children, collector, COMMA
|
261
|
+
end
|
262
|
+
|
263
|
+
def visit_Arel_Nodes_WithRecursive(o, collector)
|
264
|
+
collector << "WITH RECURSIVE "
|
265
|
+
inject_join o.children, collector, COMMA
|
266
|
+
end
|
267
|
+
|
268
|
+
def visit_Arel_Nodes_Union(o, collector)
|
269
|
+
infix_value_with_paren(o, collector, " UNION ")
|
270
|
+
end
|
271
|
+
|
272
|
+
def visit_Arel_Nodes_UnionAll(o, collector)
|
273
|
+
infix_value_with_paren(o, collector, " UNION ALL ")
|
274
|
+
end
|
275
|
+
|
276
|
+
def visit_Arel_Nodes_Intersect(o, collector)
|
277
|
+
collector << "( "
|
278
|
+
infix_value(o, collector, " INTERSECT ") << " )"
|
279
|
+
end
|
280
|
+
|
281
|
+
def visit_Arel_Nodes_Except(o, collector)
|
282
|
+
collector << "( "
|
283
|
+
infix_value(o, collector, " EXCEPT ") << " )"
|
284
|
+
end
|
285
|
+
|
286
|
+
def visit_Arel_Nodes_NamedWindow(o, collector)
|
287
|
+
collector << quote_column_name(o.name)
|
288
|
+
collector << " AS "
|
289
|
+
visit_Arel_Nodes_Window o, collector
|
290
|
+
end
|
291
|
+
|
292
|
+
def visit_Arel_Nodes_Window(o, collector)
|
293
|
+
collector << "("
|
294
|
+
|
295
|
+
collect_nodes_for o.partitions, collector, "PARTITION BY "
|
296
|
+
|
297
|
+
if o.orders.any?
|
298
|
+
collector << SPACE if o.partitions.any?
|
299
|
+
collector << "ORDER BY "
|
300
|
+
collector = inject_join o.orders, collector, ", "
|
301
|
+
end
|
302
|
+
|
303
|
+
if o.framing
|
304
|
+
collector << SPACE if o.partitions.any? || o.orders.any?
|
305
|
+
collector = visit o.framing, collector
|
306
|
+
end
|
307
|
+
|
308
|
+
collector << ")"
|
309
|
+
end
|
310
|
+
|
311
|
+
def visit_Arel_Nodes_Rows(o, collector)
|
312
|
+
if o.expr
|
313
|
+
collector << "ROWS "
|
314
|
+
visit o.expr, collector
|
315
|
+
else
|
316
|
+
collector << "ROWS"
|
317
|
+
end
|
318
|
+
end
|
319
|
+
|
320
|
+
def visit_Arel_Nodes_Range(o, collector)
|
321
|
+
if o.expr
|
322
|
+
collector << "RANGE "
|
323
|
+
visit o.expr, collector
|
324
|
+
else
|
325
|
+
collector << "RANGE"
|
326
|
+
end
|
327
|
+
end
|
328
|
+
|
329
|
+
def visit_Arel_Nodes_Preceding(o, collector)
|
330
|
+
collector = if o.expr
|
331
|
+
visit o.expr, collector
|
332
|
+
else
|
333
|
+
collector << "UNBOUNDED"
|
334
|
+
end
|
335
|
+
|
336
|
+
collector << " PRECEDING"
|
337
|
+
end
|
338
|
+
|
339
|
+
def visit_Arel_Nodes_Following(o, collector)
|
340
|
+
collector = if o.expr
|
341
|
+
visit o.expr, collector
|
342
|
+
else
|
343
|
+
collector << "UNBOUNDED"
|
344
|
+
end
|
345
|
+
|
346
|
+
collector << " FOLLOWING"
|
347
|
+
end
|
348
|
+
|
349
|
+
def visit_Arel_Nodes_CurrentRow(o, collector)
|
350
|
+
collector << "CURRENT ROW"
|
351
|
+
end
|
352
|
+
|
353
|
+
def visit_Arel_Nodes_Over(o, collector)
|
354
|
+
case o.right
|
355
|
+
when nil
|
356
|
+
visit(o.left, collector) << " OVER ()"
|
357
|
+
when Arel::Nodes::SqlLiteral
|
358
|
+
infix_value o, collector, " OVER "
|
359
|
+
when String, Symbol
|
360
|
+
visit(o.left, collector) << " OVER #{quote_column_name o.right.to_s}"
|
361
|
+
else
|
362
|
+
infix_value o, collector, " OVER "
|
363
|
+
end
|
364
|
+
end
|
365
|
+
|
366
|
+
def visit_Arel_Nodes_Offset(o, collector)
|
367
|
+
collector << "OFFSET "
|
368
|
+
visit o.expr, collector
|
369
|
+
end
|
370
|
+
|
371
|
+
def visit_Arel_Nodes_Limit(o, collector)
|
372
|
+
collector << "LIMIT "
|
373
|
+
visit o.expr, collector
|
374
|
+
end
|
375
|
+
|
376
|
+
def visit_Arel_Nodes_Lock(o, collector)
|
377
|
+
visit o.expr, collector
|
378
|
+
end
|
379
|
+
|
380
|
+
def visit_Arel_Nodes_Grouping(o, collector)
|
381
|
+
if o.expr.is_a? Nodes::Grouping
|
382
|
+
visit(o.expr, collector)
|
383
|
+
else
|
384
|
+
collector << "("
|
385
|
+
visit(o.expr, collector) << ")"
|
386
|
+
end
|
387
|
+
end
|
388
|
+
|
389
|
+
def visit_Arel_SelectManager(o, collector)
|
390
|
+
collector << "("
|
391
|
+
visit(o.ast, collector) << ")"
|
392
|
+
end
|
393
|
+
|
394
|
+
def visit_Arel_Nodes_Ascending(o, collector)
|
395
|
+
visit(o.expr, collector) << " ASC"
|
396
|
+
end
|
397
|
+
|
398
|
+
def visit_Arel_Nodes_Descending(o, collector)
|
399
|
+
visit(o.expr, collector) << " DESC"
|
400
|
+
end
|
401
|
+
|
402
|
+
def visit_Arel_Nodes_Group(o, collector)
|
403
|
+
visit o.expr, collector
|
404
|
+
end
|
405
|
+
|
406
|
+
def visit_Arel_Nodes_NamedFunction(o, collector)
|
407
|
+
collector << o.name
|
408
|
+
collector << "("
|
409
|
+
collector << "DISTINCT " if o.distinct
|
410
|
+
collector = inject_join(o.expressions, collector, ", ") << ")"
|
411
|
+
if o.alias
|
412
|
+
collector << " AS "
|
413
|
+
visit o.alias, collector
|
414
|
+
else
|
415
|
+
collector
|
416
|
+
end
|
417
|
+
end
|
418
|
+
|
419
|
+
def visit_Arel_Nodes_Extract(o, collector)
|
420
|
+
collector << "EXTRACT(#{o.field.to_s.upcase} FROM "
|
421
|
+
visit(o.expr, collector) << ")"
|
422
|
+
end
|
423
|
+
|
424
|
+
def visit_Arel_Nodes_Count(o, collector)
|
425
|
+
aggregate "COUNT", o, collector
|
426
|
+
end
|
427
|
+
|
428
|
+
def visit_Arel_Nodes_Sum(o, collector)
|
429
|
+
aggregate "SUM", o, collector
|
430
|
+
end
|
431
|
+
|
432
|
+
def visit_Arel_Nodes_Max(o, collector)
|
433
|
+
aggregate "MAX", o, collector
|
434
|
+
end
|
435
|
+
|
436
|
+
def visit_Arel_Nodes_Min(o, collector)
|
437
|
+
aggregate "MIN", o, collector
|
438
|
+
end
|
439
|
+
|
440
|
+
def visit_Arel_Nodes_Avg(o, collector)
|
441
|
+
aggregate "AVG", o, collector
|
442
|
+
end
|
443
|
+
|
444
|
+
def visit_Arel_Nodes_TableAlias(o, collector)
|
445
|
+
collector = visit o.relation, collector
|
446
|
+
collector << " "
|
447
|
+
collector << quote_table_name(o.name)
|
448
|
+
end
|
449
|
+
|
450
|
+
def visit_Arel_Nodes_Between(o, collector)
|
451
|
+
collector = visit o.left, collector
|
452
|
+
collector << " BETWEEN "
|
453
|
+
visit o.right, collector
|
454
|
+
end
|
455
|
+
|
456
|
+
def visit_Arel_Nodes_GreaterThanOrEqual(o, collector)
|
457
|
+
collector = visit o.left, collector
|
458
|
+
collector << " >= "
|
459
|
+
visit o.right, collector
|
460
|
+
end
|
461
|
+
|
462
|
+
def visit_Arel_Nodes_GreaterThan(o, collector)
|
463
|
+
collector = visit o.left, collector
|
464
|
+
collector << " > "
|
465
|
+
visit o.right, collector
|
466
|
+
end
|
467
|
+
|
468
|
+
def visit_Arel_Nodes_LessThanOrEqual(o, collector)
|
469
|
+
collector = visit o.left, collector
|
470
|
+
collector << " <= "
|
471
|
+
visit o.right, collector
|
472
|
+
end
|
473
|
+
|
474
|
+
def visit_Arel_Nodes_LessThan(o, collector)
|
475
|
+
collector = visit o.left, collector
|
476
|
+
collector << " < "
|
477
|
+
visit o.right, collector
|
478
|
+
end
|
479
|
+
|
480
|
+
def visit_Arel_Nodes_Matches(o, collector)
|
481
|
+
collector = visit o.left, collector
|
482
|
+
collector << " LIKE "
|
483
|
+
collector = visit o.right, collector
|
484
|
+
if o.escape
|
485
|
+
collector << " ESCAPE "
|
486
|
+
visit o.escape, collector
|
487
|
+
else
|
488
|
+
collector
|
489
|
+
end
|
490
|
+
end
|
491
|
+
|
492
|
+
def visit_Arel_Nodes_DoesNotMatch(o, collector)
|
493
|
+
collector = visit o.left, collector
|
494
|
+
collector << " NOT LIKE "
|
495
|
+
collector = visit o.right, collector
|
496
|
+
if o.escape
|
497
|
+
collector << " ESCAPE "
|
498
|
+
visit o.escape, collector
|
499
|
+
else
|
500
|
+
collector
|
501
|
+
end
|
502
|
+
end
|
503
|
+
|
504
|
+
def visit_Arel_Nodes_JoinSource(o, collector)
|
505
|
+
if o.left
|
506
|
+
collector = visit o.left, collector
|
507
|
+
end
|
508
|
+
if o.right.any?
|
509
|
+
collector << SPACE if o.left
|
510
|
+
collector = inject_join o.right, collector, SPACE
|
511
|
+
end
|
512
|
+
collector
|
513
|
+
end
|
514
|
+
|
515
|
+
def visit_Arel_Nodes_Regexp(o, collector)
|
516
|
+
raise NotImplementedError, "~ not implemented for this db"
|
517
|
+
end
|
518
|
+
|
519
|
+
def visit_Arel_Nodes_NotRegexp(o, collector)
|
520
|
+
raise NotImplementedError, "!~ not implemented for this db"
|
521
|
+
end
|
522
|
+
|
523
|
+
def visit_Arel_Nodes_StringJoin(o, collector)
|
524
|
+
visit o.left, collector
|
525
|
+
end
|
526
|
+
|
527
|
+
def visit_Arel_Nodes_FullOuterJoin(o, collector)
|
528
|
+
collector << "FULL OUTER JOIN "
|
529
|
+
collector = visit o.left, collector
|
530
|
+
collector << SPACE
|
531
|
+
visit o.right, collector
|
532
|
+
end
|
533
|
+
|
534
|
+
def visit_Arel_Nodes_OuterJoin(o, collector)
|
535
|
+
collector << "LEFT OUTER JOIN "
|
536
|
+
collector = visit o.left, collector
|
537
|
+
collector << " "
|
538
|
+
visit o.right, collector
|
539
|
+
end
|
540
|
+
|
541
|
+
def visit_Arel_Nodes_RightOuterJoin(o, collector)
|
542
|
+
collector << "RIGHT OUTER JOIN "
|
543
|
+
collector = visit o.left, collector
|
544
|
+
collector << SPACE
|
545
|
+
visit o.right, collector
|
546
|
+
end
|
547
|
+
|
548
|
+
def visit_Arel_Nodes_InnerJoin(o, collector)
|
549
|
+
collector << "INNER JOIN "
|
550
|
+
collector = visit o.left, collector
|
551
|
+
if o.right
|
552
|
+
collector << SPACE
|
553
|
+
visit(o.right, collector)
|
554
|
+
else
|
555
|
+
collector
|
556
|
+
end
|
557
|
+
end
|
558
|
+
|
559
|
+
def visit_Arel_Nodes_On(o, collector)
|
560
|
+
collector << "ON "
|
561
|
+
visit o.expr, collector
|
562
|
+
end
|
563
|
+
|
564
|
+
def visit_Arel_Nodes_Not(o, collector)
|
565
|
+
collector << "NOT ("
|
566
|
+
visit(o.expr, collector) << ")"
|
567
|
+
end
|
568
|
+
|
569
|
+
def visit_Arel_Table(o, collector)
|
570
|
+
if o.table_alias
|
571
|
+
collector << "#{quote_table_name o.name} #{quote_table_name o.table_alias}"
|
572
|
+
else
|
573
|
+
collector << quote_table_name(o.name)
|
574
|
+
end
|
575
|
+
end
|
576
|
+
|
577
|
+
def visit_Arel_Nodes_In(o, collector)
|
578
|
+
if Array === o.right && !o.right.empty?
|
579
|
+
o.right.delete_if { |value| unboundable?(value) }
|
580
|
+
end
|
581
|
+
|
582
|
+
if Array === o.right && o.right.empty?
|
583
|
+
collector << "1=0"
|
584
|
+
else
|
585
|
+
collector = visit o.left, collector
|
586
|
+
collector << " IN ("
|
587
|
+
visit(o.right, collector) << ")"
|
588
|
+
end
|
589
|
+
end
|
590
|
+
|
591
|
+
def visit_Arel_Nodes_NotIn(o, collector)
|
592
|
+
if Array === o.right && !o.right.empty?
|
593
|
+
o.right.delete_if { |value| unboundable?(value) }
|
594
|
+
end
|
595
|
+
|
596
|
+
if Array === o.right && o.right.empty?
|
597
|
+
collector << "1=1"
|
598
|
+
else
|
599
|
+
collector = visit o.left, collector
|
600
|
+
collector << " NOT IN ("
|
601
|
+
collector = visit o.right, collector
|
602
|
+
collector << ")"
|
603
|
+
end
|
604
|
+
end
|
605
|
+
|
606
|
+
def visit_Arel_Nodes_And(o, collector)
|
607
|
+
inject_join o.children, collector, " AND "
|
608
|
+
end
|
609
|
+
|
610
|
+
def visit_Arel_Nodes_Or(o, collector)
|
611
|
+
collector = visit o.left, collector
|
612
|
+
collector << " OR "
|
613
|
+
visit o.right, collector
|
614
|
+
end
|
615
|
+
|
616
|
+
def visit_Arel_Nodes_Assignment(o, collector)
|
617
|
+
case o.right
|
618
|
+
when Arel::Nodes::Node, Arel::Attributes::Attribute
|
619
|
+
collector = visit o.left, collector
|
620
|
+
collector << " = "
|
621
|
+
visit o.right, collector
|
622
|
+
else
|
623
|
+
collector = visit o.left, collector
|
624
|
+
collector << " = "
|
625
|
+
collector << quote(o.right).to_s
|
626
|
+
end
|
627
|
+
end
|
628
|
+
|
629
|
+
def visit_Arel_Nodes_Equality(o, collector)
|
630
|
+
right = o.right
|
631
|
+
|
632
|
+
return collector << "1=0" if unboundable?(right)
|
633
|
+
|
634
|
+
collector = visit o.left, collector
|
635
|
+
|
636
|
+
if right.nil?
|
637
|
+
collector << " IS NULL"
|
638
|
+
else
|
639
|
+
collector << " = "
|
640
|
+
visit right, collector
|
641
|
+
end
|
642
|
+
end
|
643
|
+
|
644
|
+
def visit_Arel_Nodes_IsNotDistinctFrom(o, collector)
|
645
|
+
if o.right.nil?
|
646
|
+
collector = visit o.left, collector
|
647
|
+
collector << " IS NULL"
|
648
|
+
else
|
649
|
+
collector = is_distinct_from(o, collector)
|
650
|
+
collector << " = 0"
|
651
|
+
end
|
652
|
+
end
|
653
|
+
|
654
|
+
def visit_Arel_Nodes_IsDistinctFrom(o, collector)
|
655
|
+
if o.right.nil?
|
656
|
+
collector = visit o.left, collector
|
657
|
+
collector << " IS NOT NULL"
|
658
|
+
else
|
659
|
+
collector = is_distinct_from(o, collector)
|
660
|
+
collector << " = 1"
|
661
|
+
end
|
662
|
+
end
|
663
|
+
|
664
|
+
def visit_Arel_Nodes_NotEqual(o, collector)
|
665
|
+
right = o.right
|
666
|
+
|
667
|
+
return collector << "1=1" if unboundable?(right)
|
668
|
+
|
669
|
+
collector = visit o.left, collector
|
670
|
+
|
671
|
+
if right.nil?
|
672
|
+
collector << " IS NOT NULL"
|
673
|
+
else
|
674
|
+
collector << " != "
|
675
|
+
visit right, collector
|
676
|
+
end
|
677
|
+
end
|
678
|
+
|
679
|
+
def visit_Arel_Nodes_As(o, collector)
|
680
|
+
collector = visit o.left, collector
|
681
|
+
collector << " AS "
|
682
|
+
visit o.right, collector
|
683
|
+
end
|
684
|
+
|
685
|
+
def visit_Arel_Nodes_Case(o, collector)
|
686
|
+
collector << "CASE "
|
687
|
+
if o.case
|
688
|
+
visit o.case, collector
|
689
|
+
collector << " "
|
690
|
+
end
|
691
|
+
o.conditions.each do |condition|
|
692
|
+
visit condition, collector
|
693
|
+
collector << " "
|
694
|
+
end
|
695
|
+
if o.default
|
696
|
+
visit o.default, collector
|
697
|
+
collector << " "
|
698
|
+
end
|
699
|
+
collector << "END"
|
700
|
+
end
|
701
|
+
|
702
|
+
def visit_Arel_Nodes_When(o, collector)
|
703
|
+
collector << "WHEN "
|
704
|
+
visit o.left, collector
|
705
|
+
collector << " THEN "
|
706
|
+
visit o.right, collector
|
707
|
+
end
|
708
|
+
|
709
|
+
def visit_Arel_Nodes_Else(o, collector)
|
710
|
+
collector << "ELSE "
|
711
|
+
visit o.expr, collector
|
712
|
+
end
|
713
|
+
|
714
|
+
def visit_Arel_Nodes_UnqualifiedColumn(o, collector)
|
715
|
+
collector << "#{quote_column_name o.name}"
|
716
|
+
collector
|
717
|
+
end
|
718
|
+
|
719
|
+
def visit_Arel_Attributes_Attribute(o, collector)
|
720
|
+
join_name = o.relation.table_alias || o.relation.name
|
721
|
+
collector << "#{quote_table_name join_name}.#{quote_column_name o.name}"
|
722
|
+
end
|
723
|
+
alias :visit_Arel_Attributes_Integer :visit_Arel_Attributes_Attribute
|
724
|
+
alias :visit_Arel_Attributes_Float :visit_Arel_Attributes_Attribute
|
725
|
+
alias :visit_Arel_Attributes_Decimal :visit_Arel_Attributes_Attribute
|
726
|
+
alias :visit_Arel_Attributes_String :visit_Arel_Attributes_Attribute
|
727
|
+
alias :visit_Arel_Attributes_Time :visit_Arel_Attributes_Attribute
|
728
|
+
alias :visit_Arel_Attributes_Boolean :visit_Arel_Attributes_Attribute
|
729
|
+
|
730
|
+
def literal(o, collector); collector << o.to_s; end
|
731
|
+
|
732
|
+
def visit_Arel_Nodes_BindParam(o, collector)
|
733
|
+
collector.add_bind(o.value) { "?" }
|
734
|
+
end
|
735
|
+
|
736
|
+
alias :visit_Arel_Nodes_SqlLiteral :literal
|
737
|
+
alias :visit_Integer :literal
|
738
|
+
|
739
|
+
def quoted(o, a)
|
740
|
+
if a && a.able_to_type_cast?
|
741
|
+
quote(a.type_cast_for_database(o))
|
742
|
+
else
|
743
|
+
quote(o)
|
744
|
+
end
|
745
|
+
end
|
746
|
+
|
747
|
+
def unsupported(o, collector)
|
748
|
+
raise UnsupportedVisitError.new(o)
|
749
|
+
end
|
750
|
+
|
751
|
+
alias :visit_ActiveSupport_Multibyte_Chars :unsupported
|
752
|
+
alias :visit_ActiveSupport_StringInquirer :unsupported
|
753
|
+
alias :visit_BigDecimal :unsupported
|
754
|
+
alias :visit_Class :unsupported
|
755
|
+
alias :visit_Date :unsupported
|
756
|
+
alias :visit_DateTime :unsupported
|
757
|
+
alias :visit_FalseClass :unsupported
|
758
|
+
alias :visit_Float :unsupported
|
759
|
+
alias :visit_Hash :unsupported
|
760
|
+
alias :visit_NilClass :unsupported
|
761
|
+
alias :visit_String :unsupported
|
762
|
+
alias :visit_Symbol :unsupported
|
763
|
+
alias :visit_Time :unsupported
|
764
|
+
alias :visit_TrueClass :unsupported
|
765
|
+
|
766
|
+
def visit_Arel_Nodes_InfixOperation(o, collector)
|
767
|
+
collector = visit o.left, collector
|
768
|
+
collector << " #{o.operator} "
|
769
|
+
visit o.right, collector
|
770
|
+
end
|
771
|
+
|
772
|
+
alias :visit_Arel_Nodes_Addition :visit_Arel_Nodes_InfixOperation
|
773
|
+
alias :visit_Arel_Nodes_Subtraction :visit_Arel_Nodes_InfixOperation
|
774
|
+
alias :visit_Arel_Nodes_Multiplication :visit_Arel_Nodes_InfixOperation
|
775
|
+
alias :visit_Arel_Nodes_Division :visit_Arel_Nodes_InfixOperation
|
776
|
+
|
777
|
+
def visit_Arel_Nodes_UnaryOperation(o, collector)
|
778
|
+
collector << " #{o.operator} "
|
779
|
+
visit o.expr, collector
|
780
|
+
end
|
781
|
+
|
782
|
+
def visit_Array(o, collector)
|
783
|
+
inject_join o, collector, ", "
|
784
|
+
end
|
785
|
+
alias :visit_Set :visit_Array
|
786
|
+
|
787
|
+
def quote(value)
|
788
|
+
return value if Arel::Nodes::SqlLiteral === value
|
789
|
+
@connection.quote value
|
790
|
+
end
|
791
|
+
|
792
|
+
def quote_table_name(name)
|
793
|
+
return name if Arel::Nodes::SqlLiteral === name
|
794
|
+
@connection.quote_table_name(name)
|
795
|
+
end
|
796
|
+
|
797
|
+
def quote_column_name(name)
|
798
|
+
return name if Arel::Nodes::SqlLiteral === name
|
799
|
+
@connection.quote_column_name(name)
|
800
|
+
end
|
801
|
+
|
802
|
+
def maybe_visit(thing, collector)
|
803
|
+
return collector unless thing
|
804
|
+
collector << " "
|
805
|
+
visit thing, collector
|
806
|
+
end
|
807
|
+
|
808
|
+
def inject_join(list, collector, join_str)
|
809
|
+
len = list.length - 1
|
810
|
+
list.each_with_index.inject(collector) { |c, (x, i)|
|
811
|
+
if i == len
|
812
|
+
visit x, c
|
813
|
+
else
|
814
|
+
visit(x, c) << join_str
|
815
|
+
end
|
816
|
+
}
|
817
|
+
end
|
818
|
+
|
819
|
+
def unboundable?(value)
|
820
|
+
value.respond_to?(:unboundable?) && value.unboundable?
|
821
|
+
end
|
822
|
+
|
823
|
+
def has_join_sources?(o)
|
824
|
+
o.relation.is_a?(Nodes::JoinSource) && !o.relation.right.empty?
|
825
|
+
end
|
826
|
+
|
827
|
+
def has_limit_or_offset_or_orders?(o)
|
828
|
+
o.limit || o.offset || !o.orders.empty?
|
829
|
+
end
|
830
|
+
|
831
|
+
# The default strategy for an UPDATE with joins is to use a subquery. This doesn't work
|
832
|
+
# on MySQL (even when aliasing the tables), but MySQL allows using JOIN directly in
|
833
|
+
# an UPDATE statement, so in the MySQL visitor we redefine this to do that.
|
834
|
+
def prepare_update_statement(o)
|
835
|
+
if o.key && (has_limit_or_offset_or_orders?(o) || has_join_sources?(o))
|
836
|
+
stmt = o.clone
|
837
|
+
stmt.limit = nil
|
838
|
+
stmt.offset = nil
|
839
|
+
stmt.orders = []
|
840
|
+
stmt.wheres = [Nodes::In.new(o.key, [build_subselect(o.key, o)])]
|
841
|
+
stmt.relation = o.relation.left if has_join_sources?(o)
|
842
|
+
stmt
|
843
|
+
else
|
844
|
+
o
|
845
|
+
end
|
846
|
+
end
|
847
|
+
alias :prepare_delete_statement :prepare_update_statement
|
848
|
+
|
849
|
+
# FIXME: we should probably have a 2-pass visitor for this
|
850
|
+
def build_subselect(key, o)
|
851
|
+
stmt = Nodes::SelectStatement.new
|
852
|
+
core = stmt.cores.first
|
853
|
+
core.froms = o.relation
|
854
|
+
core.wheres = o.wheres
|
855
|
+
core.projections = [key]
|
856
|
+
stmt.limit = o.limit
|
857
|
+
stmt.offset = o.offset
|
858
|
+
stmt.orders = o.orders
|
859
|
+
stmt
|
860
|
+
end
|
861
|
+
|
862
|
+
def infix_value(o, collector, value)
|
863
|
+
collector = visit o.left, collector
|
864
|
+
collector << value
|
865
|
+
visit o.right, collector
|
866
|
+
end
|
867
|
+
|
868
|
+
def infix_value_with_paren(o, collector, value, suppress_parens = false)
|
869
|
+
collector << "( " unless suppress_parens
|
870
|
+
collector = if o.left.class == o.class
|
871
|
+
infix_value_with_paren(o.left, collector, value, true)
|
872
|
+
else
|
873
|
+
visit o.left, collector
|
874
|
+
end
|
875
|
+
collector << value
|
876
|
+
collector = if o.right.class == o.class
|
877
|
+
infix_value_with_paren(o.right, collector, value, true)
|
878
|
+
else
|
879
|
+
visit o.right, collector
|
880
|
+
end
|
881
|
+
collector << " )" unless suppress_parens
|
882
|
+
collector
|
883
|
+
end
|
884
|
+
|
885
|
+
def aggregate(name, o, collector)
|
886
|
+
collector << "#{name}("
|
887
|
+
if o.distinct
|
888
|
+
collector << "DISTINCT "
|
889
|
+
end
|
890
|
+
collector = inject_join(o.expressions, collector, ", ") << ")"
|
891
|
+
if o.alias
|
892
|
+
collector << " AS "
|
893
|
+
visit o.alias, collector
|
894
|
+
else
|
895
|
+
collector
|
896
|
+
end
|
897
|
+
end
|
898
|
+
|
899
|
+
def is_distinct_from(o, collector)
|
900
|
+
collector << "CASE WHEN "
|
901
|
+
collector = visit o.left, collector
|
902
|
+
collector << " = "
|
903
|
+
collector = visit o.right, collector
|
904
|
+
collector << " OR ("
|
905
|
+
collector = visit o.left, collector
|
906
|
+
collector << " IS NULL AND "
|
907
|
+
collector = visit o.right, collector
|
908
|
+
collector << " IS NULL)"
|
909
|
+
collector << " THEN 0 ELSE 1 END"
|
910
|
+
end
|
911
|
+
end
|
912
|
+
end
|
913
|
+
end
|