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