activerecord-jdbc-adapter-ficoh 1.3.21-java
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +35 -0
- data/.travis.yml +462 -0
- data/.yardopts +4 -0
- data/Appraisals +36 -0
- data/CONTRIBUTING.md +49 -0
- data/Gemfile +68 -0
- data/History.md +1191 -0
- data/LICENSE.txt +25 -0
- data/README.md +277 -0
- data/RUNNING_TESTS.md +88 -0
- data/Rakefile +298 -0
- data/Rakefile.jdbc +20 -0
- data/activerecord-jdbc-adapter.gemspec +63 -0
- data/lib/active_record/connection_adapters/as400_adapter.rb +2 -0
- data/lib/active_record/connection_adapters/db2_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/derby_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/firebird_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/h2_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/hsqldb_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/informix_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/jdbc_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/jndi_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/mariadb_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/mssql_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/mysql_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/oracle_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/sqlserver_adapter.rb +1 -0
- data/lib/activerecord-jdbc-adapter.rb +1 -0
- data/lib/arel/visitors/compat.rb +64 -0
- data/lib/arel/visitors/db2.rb +137 -0
- data/lib/arel/visitors/derby.rb +112 -0
- data/lib/arel/visitors/firebird.rb +79 -0
- data/lib/arel/visitors/h2.rb +25 -0
- data/lib/arel/visitors/hsqldb.rb +32 -0
- data/lib/arel/visitors/postgresql_jdbc.rb +6 -0
- data/lib/arel/visitors/sql_server.rb +225 -0
- data/lib/arel/visitors/sql_server/ng42.rb +293 -0
- data/lib/arjdbc.rb +22 -0
- data/lib/arjdbc/db2.rb +4 -0
- data/lib/arjdbc/db2/adapter.rb +802 -0
- data/lib/arjdbc/db2/as400.rb +137 -0
- data/lib/arjdbc/db2/column.rb +177 -0
- data/lib/arjdbc/db2/connection_methods.rb +45 -0
- data/lib/arjdbc/derby.rb +3 -0
- data/lib/arjdbc/derby/active_record_patch.rb +13 -0
- data/lib/arjdbc/derby/adapter.rb +567 -0
- data/lib/arjdbc/derby/connection_methods.rb +16 -0
- data/lib/arjdbc/derby/schema_creation.rb +15 -0
- data/lib/arjdbc/discover.rb +104 -0
- data/lib/arjdbc/firebird.rb +4 -0
- data/lib/arjdbc/firebird/adapter.rb +468 -0
- data/lib/arjdbc/firebird/connection_methods.rb +20 -0
- data/lib/arjdbc/h2.rb +3 -0
- data/lib/arjdbc/h2/adapter.rb +335 -0
- data/lib/arjdbc/h2/connection_methods.rb +22 -0
- data/lib/arjdbc/hsqldb.rb +3 -0
- data/lib/arjdbc/hsqldb/adapter.rb +304 -0
- data/lib/arjdbc/hsqldb/connection_methods.rb +23 -0
- data/lib/arjdbc/hsqldb/explain_support.rb +35 -0
- data/lib/arjdbc/hsqldb/schema_creation.rb +11 -0
- data/lib/arjdbc/informix.rb +5 -0
- data/lib/arjdbc/informix/adapter.rb +160 -0
- data/lib/arjdbc/informix/connection_methods.rb +9 -0
- data/lib/arjdbc/jdbc.rb +62 -0
- data/lib/arjdbc/jdbc/adapter.rb +997 -0
- data/lib/arjdbc/jdbc/adapter_require.rb +46 -0
- data/lib/arjdbc/jdbc/arel_support.rb +149 -0
- data/lib/arjdbc/jdbc/base_ext.rb +34 -0
- data/lib/arjdbc/jdbc/callbacks.rb +52 -0
- data/lib/arjdbc/jdbc/column.rb +83 -0
- data/lib/arjdbc/jdbc/connection.rb +26 -0
- data/lib/arjdbc/jdbc/connection_methods.rb +59 -0
- data/lib/arjdbc/jdbc/driver.rb +44 -0
- data/lib/arjdbc/jdbc/error.rb +75 -0
- data/lib/arjdbc/jdbc/extension.rb +69 -0
- data/lib/arjdbc/jdbc/java.rb +13 -0
- data/lib/arjdbc/jdbc/type_cast.rb +154 -0
- data/lib/arjdbc/jdbc/type_converter.rb +142 -0
- data/lib/arjdbc/mssql.rb +7 -0
- data/lib/arjdbc/mssql/adapter.rb +822 -0
- data/lib/arjdbc/mssql/column.rb +207 -0
- data/lib/arjdbc/mssql/connection_methods.rb +72 -0
- data/lib/arjdbc/mssql/explain_support.rb +99 -0
- data/lib/arjdbc/mssql/limit_helpers.rb +231 -0
- data/lib/arjdbc/mssql/lock_methods.rb +77 -0
- data/lib/arjdbc/mssql/types.rb +343 -0
- data/lib/arjdbc/mssql/utils.rb +82 -0
- data/lib/arjdbc/mysql.rb +3 -0
- data/lib/arjdbc/mysql/adapter.rb +998 -0
- data/lib/arjdbc/mysql/bulk_change_table.rb +150 -0
- data/lib/arjdbc/mysql/column.rb +167 -0
- data/lib/arjdbc/mysql/connection_methods.rb +137 -0
- data/lib/arjdbc/mysql/explain_support.rb +82 -0
- data/lib/arjdbc/mysql/schema_creation.rb +58 -0
- data/lib/arjdbc/oracle.rb +4 -0
- data/lib/arjdbc/oracle/adapter.rb +968 -0
- data/lib/arjdbc/oracle/column.rb +136 -0
- data/lib/arjdbc/oracle/connection_methods.rb +21 -0
- data/lib/arjdbc/postgresql.rb +3 -0
- data/lib/arjdbc/postgresql/_bc_time_cast_patch.rb +21 -0
- data/lib/arjdbc/postgresql/adapter.rb +1498 -0
- data/lib/arjdbc/postgresql/base/array_parser.rb +95 -0
- data/lib/arjdbc/postgresql/base/oid.rb +412 -0
- data/lib/arjdbc/postgresql/base/pgconn.rb +8 -0
- data/lib/arjdbc/postgresql/base/schema_definitions.rb +132 -0
- data/lib/arjdbc/postgresql/column.rb +640 -0
- data/lib/arjdbc/postgresql/connection_methods.rb +44 -0
- data/lib/arjdbc/postgresql/explain_support.rb +53 -0
- data/lib/arjdbc/postgresql/oid/bytea.rb +3 -0
- data/lib/arjdbc/postgresql/oid_types.rb +265 -0
- data/lib/arjdbc/postgresql/schema_creation.rb +60 -0
- data/lib/arjdbc/railtie.rb +11 -0
- data/lib/arjdbc/sqlite3.rb +3 -0
- data/lib/arjdbc/sqlite3/adapter.rb +654 -0
- data/lib/arjdbc/sqlite3/connection_methods.rb +36 -0
- data/lib/arjdbc/sqlite3/explain_support.rb +29 -0
- data/lib/arjdbc/sybase.rb +2 -0
- data/lib/arjdbc/sybase/adapter.rb +47 -0
- data/lib/arjdbc/tasks.rb +13 -0
- data/lib/arjdbc/tasks/database_tasks.rb +66 -0
- data/lib/arjdbc/tasks/databases.rake +91 -0
- data/lib/arjdbc/tasks/databases3.rake +239 -0
- data/lib/arjdbc/tasks/databases4.rake +39 -0
- data/lib/arjdbc/tasks/db2_database_tasks.rb +104 -0
- data/lib/arjdbc/tasks/derby_database_tasks.rb +95 -0
- data/lib/arjdbc/tasks/h2_database_tasks.rb +31 -0
- data/lib/arjdbc/tasks/hsqldb_database_tasks.rb +70 -0
- data/lib/arjdbc/tasks/jdbc_database_tasks.rb +169 -0
- data/lib/arjdbc/tasks/mssql_database_tasks.rb +46 -0
- data/lib/arjdbc/tasks/oracle/enhanced_structure_dump.rb +297 -0
- data/lib/arjdbc/tasks/oracle_database_tasks.rb +65 -0
- data/lib/arjdbc/util/quoted_cache.rb +60 -0
- data/lib/arjdbc/util/serialized_attributes.rb +98 -0
- data/lib/arjdbc/util/table_copier.rb +108 -0
- data/lib/arjdbc/version.rb +8 -0
- data/lib/generators/jdbc/USAGE +9 -0
- data/lib/generators/jdbc/jdbc_generator.rb +17 -0
- data/pom.xml +285 -0
- data/rails_generators/jdbc_generator.rb +15 -0
- data/rails_generators/templates/config/initializers/jdbc.rb +10 -0
- data/rails_generators/templates/lib/tasks/jdbc.rake +11 -0
- data/rakelib/01-tomcat.rake +51 -0
- data/rakelib/02-test.rake +151 -0
- data/rakelib/bundler_ext.rb +11 -0
- data/rakelib/db.rake +58 -0
- data/rakelib/rails.rake +77 -0
- data/src/java/arjdbc/ArJdbcModule.java +288 -0
- data/src/java/arjdbc/db2/DB2Module.java +77 -0
- data/src/java/arjdbc/db2/DB2RubyJdbcConnection.java +128 -0
- data/src/java/arjdbc/derby/DerbyModule.java +180 -0
- data/src/java/arjdbc/derby/DerbyRubyJdbcConnection.java +153 -0
- data/src/java/arjdbc/firebird/FirebirdRubyJdbcConnection.java +190 -0
- data/src/java/arjdbc/h2/H2Module.java +50 -0
- data/src/java/arjdbc/h2/H2RubyJdbcConnection.java +86 -0
- data/src/java/arjdbc/hsqldb/HSQLDBModule.java +74 -0
- data/src/java/arjdbc/informix/InformixRubyJdbcConnection.java +76 -0
- data/src/java/arjdbc/jdbc/AdapterJavaService.java +43 -0
- data/src/java/arjdbc/jdbc/Callable.java +44 -0
- data/src/java/arjdbc/jdbc/ConnectionFactory.java +77 -0
- data/src/java/arjdbc/jdbc/DataSourceConnectionFactory.java +156 -0
- data/src/java/arjdbc/jdbc/DriverConnectionFactory.java +63 -0
- data/src/java/arjdbc/jdbc/DriverWrapper.java +128 -0
- data/src/java/arjdbc/jdbc/JdbcConnectionFactory.java +32 -0
- data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +4541 -0
- data/src/java/arjdbc/jdbc/SQLBlock.java +54 -0
- data/src/java/arjdbc/jdbc/WithResultSet.java +37 -0
- data/src/java/arjdbc/mssql/MSSQLModule.java +91 -0
- data/src/java/arjdbc/mssql/MSSQLRubyJdbcConnection.java +193 -0
- data/src/java/arjdbc/mysql/MySQLModule.java +140 -0
- data/src/java/arjdbc/mysql/MySQLRubyJdbcConnection.java +456 -0
- data/src/java/arjdbc/oracle/OracleModule.java +81 -0
- data/src/java/arjdbc/oracle/OracleRubyJdbcConnection.java +477 -0
- data/src/java/arjdbc/postgresql/ByteaUtils.java +171 -0
- data/src/java/arjdbc/postgresql/DriverImplementation.java +78 -0
- data/src/java/arjdbc/postgresql/PGDriverImplementation.java +535 -0
- data/src/java/arjdbc/postgresql/PostgreSQLModule.java +189 -0
- data/src/java/arjdbc/postgresql/PostgreSQLRubyJdbcConnection.java +489 -0
- data/src/java/arjdbc/sqlite3/SQLite3Module.java +93 -0
- data/src/java/arjdbc/sqlite3/SQLite3RubyJdbcConnection.java +405 -0
- data/src/java/arjdbc/util/CallResultSet.java +826 -0
- data/src/java/arjdbc/util/DateTimeUtils.java +517 -0
- data/src/java/arjdbc/util/NumberUtils.java +50 -0
- data/src/java/arjdbc/util/ObjectSupport.java +65 -0
- data/src/java/arjdbc/util/QuotingUtils.java +139 -0
- data/src/java/arjdbc/util/StringCache.java +60 -0
- data/src/java/arjdbc/util/StringHelper.java +155 -0
- metadata +288 -0
data/lib/arjdbc/mysql.rb
ADDED
@@ -0,0 +1,998 @@
|
|
1
|
+
ArJdbc.load_java_part :MySQL
|
2
|
+
|
3
|
+
require 'bigdecimal'
|
4
|
+
require 'active_record/connection_adapters/abstract/schema_definitions'
|
5
|
+
|
6
|
+
module ArJdbc
|
7
|
+
module MySQL
|
8
|
+
|
9
|
+
require 'arjdbc/mysql/column'
|
10
|
+
require 'arjdbc/mysql/bulk_change_table'
|
11
|
+
require 'arjdbc/mysql/explain_support'
|
12
|
+
require 'arjdbc/mysql/schema_creation' # AR 4.x
|
13
|
+
|
14
|
+
include BulkChangeTable if const_defined? :BulkChangeTable
|
15
|
+
|
16
|
+
# @private
|
17
|
+
ActiveRecordError = ::ActiveRecord::ActiveRecordError
|
18
|
+
|
19
|
+
JdbcConnection = ::ActiveRecord::ConnectionAdapters::MySQLJdbcConnection
|
20
|
+
|
21
|
+
# @deprecated
|
22
|
+
# @see ActiveRecord::ConnectionAdapters::JdbcAdapter#jdbc_connection_class
|
23
|
+
def self.jdbc_connection_class; JdbcConnection end
|
24
|
+
|
25
|
+
def configure_connection
|
26
|
+
unless ( variables = config[:variables] ) == false # AR-JDBC allows disabling
|
27
|
+
# as this can be configured with a JDBC pool, defaults executed are :
|
28
|
+
# SET NAMES utf8,
|
29
|
+
# @@SESSION.sql_auto_is_null = 0,
|
30
|
+
# @@SESSION.wait_timeout = 2147483,
|
31
|
+
# @@SESSION.sql_mode = 'STRICT_ALL_TABLES'
|
32
|
+
|
33
|
+
variables ||= {}
|
34
|
+
# By default, MySQL 'where id is null' selects the last inserted id. Turn this off.
|
35
|
+
variables[:sql_auto_is_null] = 0 # execute "SET SQL_AUTO_IS_NULL=0"
|
36
|
+
|
37
|
+
# Increase timeout so the server doesn't disconnect us.
|
38
|
+
wait_timeout = config[:wait_timeout]
|
39
|
+
wait_timeout = self.class.type_cast_config_to_integer(wait_timeout)
|
40
|
+
variables[:wait_timeout] = wait_timeout.is_a?(Fixnum) ? wait_timeout : 2147483
|
41
|
+
|
42
|
+
# Make MySQL reject illegal values rather than truncating or blanking them, see
|
43
|
+
# http://dev.mysql.com/doc/refman/5.0/en/server-sql-mode.html#sqlmode_strict_all_tables
|
44
|
+
# If the user has provided another value for sql_mode, don't replace it.
|
45
|
+
if strict_mode? && ! variables.has_key?(:sql_mode)
|
46
|
+
variables[:sql_mode] = 'STRICT_ALL_TABLES' # SET SQL_MODE='STRICT_ALL_TABLES'
|
47
|
+
end
|
48
|
+
|
49
|
+
# Gather up all of the SET variables...
|
50
|
+
variable_assignments = variables.map do |k, v|
|
51
|
+
if v == ':default' || v == :default
|
52
|
+
"@@SESSION.#{k} = DEFAULT" # Sets the value to the global or compile default
|
53
|
+
elsif ! v.nil?
|
54
|
+
"@@SESSION.#{k} = #{quote(v)}"
|
55
|
+
end
|
56
|
+
# or else nil; compact to clear nils out
|
57
|
+
end
|
58
|
+
variable_assignments.compact!
|
59
|
+
end
|
60
|
+
|
61
|
+
# NAMES does not have an equals sign, see
|
62
|
+
# http://dev.mysql.com/doc/refman/5.0/en/set-statement.html#id944430
|
63
|
+
# (trailing comma because variable_assignments will always have content)
|
64
|
+
if encoding = config[:encoding]
|
65
|
+
( variable_assignments ||= [] ).unshift("NAMES #{encoding}")
|
66
|
+
end
|
67
|
+
|
68
|
+
# ...and send them all in one query
|
69
|
+
execute("SET #{variable_assignments.join(', ')}", :skip_logging) if variable_assignments
|
70
|
+
end
|
71
|
+
|
72
|
+
def strict_mode? # strict_mode is default since AR 4.0
|
73
|
+
return @strict_mode unless ( @strict_mode ||= nil ).nil?
|
74
|
+
|
75
|
+
@strict_mode = config.key?(:strict) ?
|
76
|
+
self.class.type_cast_config_to_boolean(config[:strict]) :
|
77
|
+
AR40 # strict_mode is default since AR 4.0
|
78
|
+
end
|
79
|
+
|
80
|
+
# @private
|
81
|
+
@@emulate_booleans = true
|
82
|
+
|
83
|
+
# Boolean emulation can be disabled using (or using the adapter method) :
|
84
|
+
#
|
85
|
+
# ArJdbc::MySQL.emulate_booleans = false
|
86
|
+
#
|
87
|
+
# @see ActiveRecord::ConnectionAdapters::MysqlAdapter#emulate_booleans
|
88
|
+
def self.emulate_booleans?; @@emulate_booleans; end
|
89
|
+
# @deprecated Use {#emulate_booleans?} instead.
|
90
|
+
def self.emulate_booleans; @@emulate_booleans; end
|
91
|
+
# @see #emulate_booleans?
|
92
|
+
def self.emulate_booleans=(emulate); @@emulate_booleans = emulate; end
|
93
|
+
|
94
|
+
NATIVE_DATABASE_TYPES = {
|
95
|
+
:primary_key => "int(11) auto_increment PRIMARY KEY",
|
96
|
+
:string => { :name => "varchar", :limit => 255 },
|
97
|
+
:text => { :name => "text" },
|
98
|
+
:integer => { :name => "int", :limit => 4 },
|
99
|
+
:float => { :name => "float" },
|
100
|
+
# :double => { :name=>"double", :limit=>17 }
|
101
|
+
# :real => { :name=>"real", :limit=>17 }
|
102
|
+
:numeric => { :name => "numeric" }, # :limit => 65
|
103
|
+
:decimal => { :name => "decimal" }, # :limit => 65
|
104
|
+
:datetime => { :name => "datetime" },
|
105
|
+
# TIMESTAMP has varying properties depending on MySQL version (SQL mode)
|
106
|
+
:timestamp => { :name => "datetime" },
|
107
|
+
:time => { :name => "time" },
|
108
|
+
:date => { :name => "date" },
|
109
|
+
:binary => { :name => "blob" },
|
110
|
+
:boolean => { :name => "tinyint", :limit => 1 },
|
111
|
+
# AR-JDBC added :
|
112
|
+
:bit => { :name => "bit" }, # :limit => 1
|
113
|
+
:enum => { :name => "enum" },
|
114
|
+
:set => { :name => "set" }, # :limit => 64
|
115
|
+
:char => { :name => "char" }, # :limit => 255
|
116
|
+
}
|
117
|
+
|
118
|
+
# @override
|
119
|
+
def native_database_types
|
120
|
+
NATIVE_DATABASE_TYPES
|
121
|
+
end
|
122
|
+
|
123
|
+
ADAPTER_NAME = 'MySQL'.freeze
|
124
|
+
|
125
|
+
# @override
|
126
|
+
def adapter_name
|
127
|
+
ADAPTER_NAME
|
128
|
+
end
|
129
|
+
|
130
|
+
def self.arel_visitor_type(config = nil)
|
131
|
+
::Arel::Visitors::MySQL
|
132
|
+
end
|
133
|
+
|
134
|
+
# @see ActiveRecord::ConnectionAdapters::JdbcAdapter#bind_substitution
|
135
|
+
# @private
|
136
|
+
class BindSubstitution < Arel::Visitors::MySQL
|
137
|
+
include Arel::Visitors::BindVisitor
|
138
|
+
end if defined? Arel::Visitors::BindVisitor
|
139
|
+
|
140
|
+
def case_sensitive_equality_operator
|
141
|
+
"= BINARY"
|
142
|
+
end
|
143
|
+
|
144
|
+
def case_sensitive_modifier(node)
|
145
|
+
Arel::Nodes::Bin.new(node)
|
146
|
+
end unless AR42
|
147
|
+
|
148
|
+
def case_sensitive_modifier(node, table_attribute)
|
149
|
+
node = Arel::Nodes.build_quoted node, table_attribute
|
150
|
+
Arel::Nodes::Bin.new(node)
|
151
|
+
end if AR42
|
152
|
+
|
153
|
+
def case_sensitive_comparison(table, attribute, column, value)
|
154
|
+
if column.case_sensitive?
|
155
|
+
table[attribute].eq(value)
|
156
|
+
else
|
157
|
+
super
|
158
|
+
end
|
159
|
+
end if AR42
|
160
|
+
|
161
|
+
def case_insensitive_comparison(table, attribute, column, value)
|
162
|
+
if column.case_sensitive?
|
163
|
+
super
|
164
|
+
else
|
165
|
+
table[attribute].eq(value)
|
166
|
+
end
|
167
|
+
end if AR42
|
168
|
+
|
169
|
+
def limited_update_conditions(where_sql, quoted_table_name, quoted_primary_key)
|
170
|
+
where_sql
|
171
|
+
end
|
172
|
+
|
173
|
+
def initialize_schema_migrations_table
|
174
|
+
if @config[:encoding] == 'utf8mb4'
|
175
|
+
ActiveRecord::SchemaMigration.create_table(191)
|
176
|
+
else
|
177
|
+
ActiveRecord::SchemaMigration.create_table
|
178
|
+
end
|
179
|
+
end if AR40
|
180
|
+
|
181
|
+
# HELPER METHODS ===========================================
|
182
|
+
|
183
|
+
# @private Only for Rails core compatibility.
|
184
|
+
def new_column(field, default, type, null, collation, extra = "")
|
185
|
+
Column.new(field, default, type, null, collation, strict_mode?, extra)
|
186
|
+
end unless AR42
|
187
|
+
|
188
|
+
# @private Only for Rails core compatibility.
|
189
|
+
def new_column(field, default, cast_type, sql_type = nil, null = true, collation = "", extra = "")
|
190
|
+
Column.new(field, default, cast_type, sql_type, null, collation, strict_mode?, extra)
|
191
|
+
end if AR42
|
192
|
+
|
193
|
+
# @private Only for Rails core compatibility.
|
194
|
+
def error_number(exception)
|
195
|
+
exception.error_code if exception.respond_to?(:error_code)
|
196
|
+
end
|
197
|
+
|
198
|
+
# QUOTING ==================================================
|
199
|
+
|
200
|
+
# @override
|
201
|
+
def quote(value, column = nil)
|
202
|
+
return value.quoted_id if value.respond_to?(:quoted_id)
|
203
|
+
return value if sql_literal?(value)
|
204
|
+
return value.to_s if column && column.type == :primary_key
|
205
|
+
|
206
|
+
if value.kind_of?(String) && column && column.type == :binary
|
207
|
+
"x'#{value.unpack("H*")[0]}'"
|
208
|
+
elsif value.kind_of?(BigDecimal)
|
209
|
+
value.to_s("F")
|
210
|
+
else
|
211
|
+
super
|
212
|
+
end
|
213
|
+
end unless AR42
|
214
|
+
|
215
|
+
# @private since AR 4.2
|
216
|
+
def _quote(value)
|
217
|
+
if value.is_a?(Type::Binary::Data)
|
218
|
+
"x'#{value.hex}'"
|
219
|
+
else
|
220
|
+
super
|
221
|
+
end
|
222
|
+
end if AR42
|
223
|
+
|
224
|
+
# @override
|
225
|
+
def quote_column_name(name)
|
226
|
+
"`#{name.to_s.gsub('`', '``')}`"
|
227
|
+
end
|
228
|
+
|
229
|
+
# @override
|
230
|
+
def quote_table_name(name)
|
231
|
+
quote_column_name(name).gsub('.', '`.`')
|
232
|
+
end
|
233
|
+
|
234
|
+
# @override
|
235
|
+
def supports_migrations?
|
236
|
+
true
|
237
|
+
end
|
238
|
+
|
239
|
+
# @override
|
240
|
+
def supports_primary_key?
|
241
|
+
true
|
242
|
+
end
|
243
|
+
|
244
|
+
# @override
|
245
|
+
def supports_index_sort_order?
|
246
|
+
# Technically MySQL allows to create indexes with the sort order syntax
|
247
|
+
# but at the moment (5.5) it doesn't yet implement them.
|
248
|
+
true
|
249
|
+
end
|
250
|
+
|
251
|
+
# @override
|
252
|
+
def supports_indexes_in_create?
|
253
|
+
true
|
254
|
+
end
|
255
|
+
|
256
|
+
# @override
|
257
|
+
def supports_transaction_isolation?
|
258
|
+
# MySQL 4 technically support transaction isolation, but it is affected by
|
259
|
+
# a bug where the transaction level gets persisted for the whole session:
|
260
|
+
# http://bugs.mysql.com/bug.php?id=39170
|
261
|
+
version[0] && version[0] >= 5
|
262
|
+
end
|
263
|
+
|
264
|
+
# @override
|
265
|
+
def supports_views?
|
266
|
+
version[0] && version[0] >= 5
|
267
|
+
end
|
268
|
+
|
269
|
+
def supports_rename_index?
|
270
|
+
return false if mariadb? || ! version[0]
|
271
|
+
(version[0] == 5 && version[1] >= 7) || version[0] >= 6
|
272
|
+
end
|
273
|
+
|
274
|
+
def index_algorithms
|
275
|
+
{ :default => 'ALGORITHM = DEFAULT', :copy => 'ALGORITHM = COPY', :inplace => 'ALGORITHM = INPLACE' }
|
276
|
+
end if AR42
|
277
|
+
|
278
|
+
# @override
|
279
|
+
def supports_transaction_isolation?(level = nil)
|
280
|
+
version[0] && version[0] >= 5 # MySQL 5+
|
281
|
+
end
|
282
|
+
|
283
|
+
# NOTE: handled by JdbcAdapter only to have statements in logs :
|
284
|
+
|
285
|
+
# @private
|
286
|
+
BEGIN_LOG = '/* BEGIN */ SET autocommit=0'
|
287
|
+
private_constant :BEGIN_LOG if respond_to?(:private_constant)
|
288
|
+
|
289
|
+
# @override
|
290
|
+
def begin_db_transaction
|
291
|
+
log(BEGIN_LOG) { @connection.begin }
|
292
|
+
end
|
293
|
+
|
294
|
+
# @override
|
295
|
+
def commit_db_transaction
|
296
|
+
log('COMMIT; SET autocommit=1') { @connection.commit }
|
297
|
+
end
|
298
|
+
|
299
|
+
# @override
|
300
|
+
def rollback_db_transaction
|
301
|
+
log('ROLLBACK; SET autocommit=1') { @connection.rollback }
|
302
|
+
end
|
303
|
+
|
304
|
+
# Starts a database transaction.
|
305
|
+
# @param isolation the transaction isolation to use
|
306
|
+
# @since 1.3.0
|
307
|
+
# @override on **AR-4.0**
|
308
|
+
def begin_isolated_db_transaction(isolation)
|
309
|
+
name = isolation.to_s.upcase; name.sub!('_', ' ')
|
310
|
+
log("SET TRANSACTION ISOLATION LEVEL #{name}; #{BEGIN_LOG}") do
|
311
|
+
@connection.begin(isolation)
|
312
|
+
end
|
313
|
+
end
|
314
|
+
|
315
|
+
# @override
|
316
|
+
def supports_savepoints?
|
317
|
+
true
|
318
|
+
end
|
319
|
+
|
320
|
+
# @override
|
321
|
+
def create_savepoint(name = current_savepoint_name(true))
|
322
|
+
log("SAVEPOINT #{name}") { @connection.create_savepoint(name) }
|
323
|
+
end
|
324
|
+
|
325
|
+
# @override
|
326
|
+
def rollback_to_savepoint(name = current_savepoint_name(true))
|
327
|
+
log("ROLLBACK TO SAVEPOINT #{name}") { @connection.rollback_savepoint(name) }
|
328
|
+
end
|
329
|
+
|
330
|
+
# @override
|
331
|
+
def release_savepoint(name = current_savepoint_name(false))
|
332
|
+
log("RELEASE SAVEPOINT #{name}") { @connection.release_savepoint(name) }
|
333
|
+
end
|
334
|
+
|
335
|
+
def disable_referential_integrity
|
336
|
+
fk_checks = select_value("SELECT @@FOREIGN_KEY_CHECKS")
|
337
|
+
begin
|
338
|
+
update("SET FOREIGN_KEY_CHECKS = 0")
|
339
|
+
yield
|
340
|
+
ensure
|
341
|
+
update("SET FOREIGN_KEY_CHECKS = #{fk_checks}")
|
342
|
+
end
|
343
|
+
end
|
344
|
+
|
345
|
+
# @override make it public just like native MySQL adapter does
|
346
|
+
def update_sql(sql, name = nil); super end
|
347
|
+
|
348
|
+
# Returns just a table's primary key.
|
349
|
+
# @override
|
350
|
+
def primary_key(table)
|
351
|
+
#pk_and_sequence = pk_and_sequence_for(table)
|
352
|
+
#pk_and_sequence && pk_and_sequence.first
|
353
|
+
@connection.primary_keys(table).first
|
354
|
+
end
|
355
|
+
|
356
|
+
# Returns a table's primary key and belonging sequence.
|
357
|
+
# @note Not used, only here for potential compatibility with native adapter.
|
358
|
+
# @override
|
359
|
+
def pk_and_sequence_for(table)
|
360
|
+
result = execute("SHOW CREATE TABLE #{quote_table_name(table)}", 'SCHEMA').first
|
361
|
+
if result['Create Table'].to_s =~ /PRIMARY KEY\s+(?:USING\s+\w+\s+)?\((.+)\)/
|
362
|
+
keys = $1.split(","); keys.map! { |key| key.gsub(/[`"]/, "") }
|
363
|
+
return keys.length == 1 ? [ keys.first, nil ] : nil
|
364
|
+
else
|
365
|
+
return nil
|
366
|
+
end
|
367
|
+
end
|
368
|
+
|
369
|
+
# @private
|
370
|
+
IndexDefinition = ::ActiveRecord::ConnectionAdapters::IndexDefinition
|
371
|
+
|
372
|
+
INDEX_TYPES = [ :fulltext, :spatial ] if AR40
|
373
|
+
INDEX_USINGS = [ :btree, :hash ] if AR40
|
374
|
+
|
375
|
+
# Returns an array of indexes for the given table.
|
376
|
+
# @override
|
377
|
+
def indexes(table_name, name = nil)
|
378
|
+
indexes = []
|
379
|
+
current_index = nil
|
380
|
+
result = execute("SHOW KEYS FROM #{quote_table_name(table_name)}", name || 'SCHEMA')
|
381
|
+
result.each do |row|
|
382
|
+
key_name = row['Key_name']
|
383
|
+
if current_index != key_name
|
384
|
+
next if key_name == 'PRIMARY' # skip the primary key
|
385
|
+
current_index = key_name
|
386
|
+
indexes <<
|
387
|
+
if self.class.const_defined?(:INDEX_TYPES) # AR 4.0
|
388
|
+
mysql_index_type = row['Index_type'].downcase.to_sym
|
389
|
+
index_type = INDEX_TYPES.include?(mysql_index_type) ? mysql_index_type : nil
|
390
|
+
index_using = INDEX_USINGS.include?(mysql_index_type) ? mysql_index_type : nil
|
391
|
+
IndexDefinition.new(row['Table'], key_name, row['Non_unique'].to_i == 0, [], [], nil, nil, index_type, index_using)
|
392
|
+
else
|
393
|
+
IndexDefinition.new(row['Table'], key_name, row['Non_unique'].to_i == 0, [], [])
|
394
|
+
end
|
395
|
+
end
|
396
|
+
|
397
|
+
indexes.last.columns << row["Column_name"]
|
398
|
+
indexes.last.lengths << row["Sub_part"]
|
399
|
+
end
|
400
|
+
indexes
|
401
|
+
end
|
402
|
+
|
403
|
+
# Returns an array of `Column` objects for the table specified.
|
404
|
+
# @override
|
405
|
+
def columns(table_name, name = nil)
|
406
|
+
sql = "SHOW FULL #{AR40 ? 'FIELDS' : 'COLUMNS'} FROM #{quote_table_name(table_name)}"
|
407
|
+
columns = execute(sql, name || 'SCHEMA')
|
408
|
+
strict = strict_mode?
|
409
|
+
pass_cast_type = respond_to?(:lookup_cast_type)
|
410
|
+
columns.map! do |field|
|
411
|
+
sql_type = field['Type']
|
412
|
+
null = field['Null'] == "YES"
|
413
|
+
if pass_cast_type
|
414
|
+
cast_type = lookup_cast_type(sql_type)
|
415
|
+
Column.new(field['Field'], field['Default'], cast_type, sql_type, null, field['Collation'], strict, field['Extra'])
|
416
|
+
else
|
417
|
+
Column.new(field['Field'], field['Default'], sql_type, null, field['Collation'], strict, field['Extra'])
|
418
|
+
end
|
419
|
+
end
|
420
|
+
columns
|
421
|
+
end
|
422
|
+
|
423
|
+
if defined? ::ActiveRecord::ConnectionAdapters::AbstractAdapter::SchemaCreation
|
424
|
+
|
425
|
+
class SchemaCreation < ::ActiveRecord::ConnectionAdapters::AbstractAdapter::SchemaCreation
|
426
|
+
|
427
|
+
# @private
|
428
|
+
def visit_AddColumn(o)
|
429
|
+
add_column_position!(super, column_options(o))
|
430
|
+
end
|
431
|
+
|
432
|
+
# @private re-defined since AR 4.1
|
433
|
+
def visit_ChangeColumnDefinition(o)
|
434
|
+
column = o.column
|
435
|
+
options = o.options
|
436
|
+
sql_type = type_to_sql(o.type, options[:limit], options[:precision], options[:scale])
|
437
|
+
change_column_sql = "CHANGE #{quote_column_name(column.name)} #{quote_column_name(options[:name])} #{sql_type}"
|
438
|
+
add_column_options!(change_column_sql, options.merge(:column => column))
|
439
|
+
add_column_position!(change_column_sql, options)
|
440
|
+
end
|
441
|
+
|
442
|
+
# @private since AR 4.2
|
443
|
+
def visit_DropForeignKey(name)
|
444
|
+
"DROP FOREIGN KEY #{name}"
|
445
|
+
end
|
446
|
+
|
447
|
+
# @private since AR 4.2
|
448
|
+
def visit_TableDefinition(o)
|
449
|
+
name = o.name
|
450
|
+
create_sql = "CREATE#{' TEMPORARY' if o.temporary} TABLE #{quote_table_name(name)} "
|
451
|
+
|
452
|
+
statements = o.columns.map { |c| accept c }
|
453
|
+
statements.concat(o.indexes.map { |column_name, options| index_in_create(name, column_name, options) })
|
454
|
+
|
455
|
+
create_sql << "(#{statements.join(', ')}) " if statements.present?
|
456
|
+
create_sql << "#{o.options}"
|
457
|
+
create_sql << " AS #{@conn.to_sql(o.as)}" if o.as
|
458
|
+
create_sql
|
459
|
+
end if AR42
|
460
|
+
|
461
|
+
private
|
462
|
+
|
463
|
+
def add_column_position!(sql, options)
|
464
|
+
if options[:first]
|
465
|
+
sql << " FIRST"
|
466
|
+
elsif options[:after]
|
467
|
+
sql << " AFTER #{quote_column_name(options[:after])}"
|
468
|
+
end
|
469
|
+
sql
|
470
|
+
end
|
471
|
+
|
472
|
+
def column_options(o)
|
473
|
+
column_options = {}
|
474
|
+
column_options[:null] = o.null unless o.null.nil?
|
475
|
+
column_options[:default] = o.default unless o.default.nil?
|
476
|
+
column_options[:column] = o
|
477
|
+
column_options[:first] = o.first
|
478
|
+
column_options[:after] = o.after
|
479
|
+
column_options
|
480
|
+
end
|
481
|
+
|
482
|
+
def index_in_create(table_name, column_name, options)
|
483
|
+
index_name, index_type, index_columns, index_options, index_algorithm, index_using = @conn.add_index_options(table_name, column_name, options)
|
484
|
+
"#{index_type} INDEX #{quote_column_name(index_name)} #{index_using} (#{index_columns})#{index_options} #{index_algorithm}"
|
485
|
+
end
|
486
|
+
|
487
|
+
end
|
488
|
+
|
489
|
+
def schema_creation; SchemaCreation.new self end
|
490
|
+
|
491
|
+
end
|
492
|
+
|
493
|
+
# @private
|
494
|
+
def recreate_database(name, options = {})
|
495
|
+
drop_database(name)
|
496
|
+
create_database(name, options)
|
497
|
+
reconnect!
|
498
|
+
end
|
499
|
+
|
500
|
+
# @override
|
501
|
+
def create_database(name, options = {})
|
502
|
+
if options[:collation]
|
503
|
+
execute "CREATE DATABASE `#{name}` DEFAULT CHARACTER SET `#{options[:charset] || 'utf8'}` COLLATE `#{options[:collation]}`"
|
504
|
+
else
|
505
|
+
execute "CREATE DATABASE `#{name}` DEFAULT CHARACTER SET `#{options[:charset] || 'utf8'}`"
|
506
|
+
end
|
507
|
+
end
|
508
|
+
|
509
|
+
# @override
|
510
|
+
def drop_database(name)
|
511
|
+
execute "DROP DATABASE IF EXISTS `#{name}`"
|
512
|
+
end
|
513
|
+
|
514
|
+
def current_database
|
515
|
+
select_one("SELECT DATABASE() as db")['db']
|
516
|
+
end
|
517
|
+
|
518
|
+
def truncate(table_name, name = nil)
|
519
|
+
execute "TRUNCATE TABLE #{quote_table_name(table_name)}", name
|
520
|
+
end
|
521
|
+
|
522
|
+
# @override
|
523
|
+
def create_table(name, options = {})
|
524
|
+
super(name, { :options => "ENGINE=InnoDB" }.merge(options))
|
525
|
+
end
|
526
|
+
|
527
|
+
def drop_table(table_name, options = {})
|
528
|
+
execute "DROP#{' TEMPORARY' if options[:temporary]} TABLE #{quote_table_name(table_name)}"
|
529
|
+
end
|
530
|
+
|
531
|
+
# @override
|
532
|
+
def rename_table(table_name, new_name)
|
533
|
+
execute "RENAME TABLE #{quote_table_name(table_name)} TO #{quote_table_name(new_name)}"
|
534
|
+
rename_table_indexes(table_name, new_name) if respond_to?(:rename_table_indexes) # AR-4.0 SchemaStatements
|
535
|
+
end
|
536
|
+
|
537
|
+
# @override
|
538
|
+
def remove_index!(table_name, index_name)
|
539
|
+
# missing table_name quoting in AR-2.3
|
540
|
+
execute "DROP INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)}"
|
541
|
+
end
|
542
|
+
|
543
|
+
# @override
|
544
|
+
def rename_index(table_name, old_name, new_name)
|
545
|
+
if supports_rename_index?
|
546
|
+
validate_index_length!(table_name, new_name) if respond_to?(:validate_index_length!)
|
547
|
+
execute "ALTER TABLE #{quote_table_name(table_name)} RENAME INDEX #{quote_table_name(old_name)} TO #{quote_table_name(new_name)}"
|
548
|
+
else
|
549
|
+
super
|
550
|
+
end
|
551
|
+
end
|
552
|
+
|
553
|
+
# @private
|
554
|
+
ForeignKeyDefinition = ::ActiveRecord::ConnectionAdapters::ForeignKeyDefinition if ::ActiveRecord::ConnectionAdapters.const_defined? :ForeignKeyDefinition
|
555
|
+
|
556
|
+
# @override
|
557
|
+
def supports_foreign_keys?; true end
|
558
|
+
|
559
|
+
def foreign_keys(table_name)
|
560
|
+
fk_info = select_all "" <<
|
561
|
+
"SELECT fk.referenced_table_name as 'to_table' " <<
|
562
|
+
",fk.referenced_column_name as 'primary_key' " <<
|
563
|
+
",fk.column_name as 'column' " <<
|
564
|
+
",fk.constraint_name as 'name' " <<
|
565
|
+
"FROM information_schema.key_column_usage fk " <<
|
566
|
+
"WHERE fk.referenced_column_name is not null " <<
|
567
|
+
"AND fk.table_schema = '#{current_database}' " <<
|
568
|
+
"AND fk.table_name = '#{table_name}'"
|
569
|
+
|
570
|
+
create_table_info = select_one("SHOW CREATE TABLE #{quote_table_name(table_name)}")["Create Table"]
|
571
|
+
|
572
|
+
fk_info.map! do |row|
|
573
|
+
options = {
|
574
|
+
:column => row['column'], :name => row['name'], :primary_key => row['primary_key']
|
575
|
+
}
|
576
|
+
options[:on_update] = extract_foreign_key_action(create_table_info, row['name'], "UPDATE")
|
577
|
+
options[:on_delete] = extract_foreign_key_action(create_table_info, row['name'], "DELETE")
|
578
|
+
|
579
|
+
ForeignKeyDefinition.new(table_name, row['to_table'], options)
|
580
|
+
end
|
581
|
+
end if defined? ForeignKeyDefinition
|
582
|
+
|
583
|
+
def extract_foreign_key_action(structure, name, action)
|
584
|
+
if structure =~ /CONSTRAINT #{quote_column_name(name)} FOREIGN KEY .* REFERENCES .* ON #{action} (CASCADE|SET NULL|RESTRICT)/
|
585
|
+
case $1
|
586
|
+
when 'CASCADE'; :cascade
|
587
|
+
when 'SET NULL'; :nullify
|
588
|
+
end
|
589
|
+
end
|
590
|
+
end
|
591
|
+
private :extract_foreign_key_action
|
592
|
+
|
593
|
+
# @override
|
594
|
+
def add_column(table_name, column_name, type, options = {})
|
595
|
+
add_column_sql = "ALTER TABLE #{quote_table_name(table_name)} ADD #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
|
596
|
+
add_column_options!(add_column_sql, options)
|
597
|
+
add_column_position!(add_column_sql, options)
|
598
|
+
execute(add_column_sql)
|
599
|
+
end unless const_defined? :SchemaCreation
|
600
|
+
|
601
|
+
def change_column_default(table_name, column_name, default)
|
602
|
+
column = column_for(table_name, column_name)
|
603
|
+
change_column table_name, column_name, column.sql_type, :default => default
|
604
|
+
end # unless const_defined? :SchemaCreation
|
605
|
+
|
606
|
+
def change_column_null(table_name, column_name, null, default = nil)
|
607
|
+
column = column_for(table_name, column_name)
|
608
|
+
|
609
|
+
unless null || default.nil?
|
610
|
+
execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
|
611
|
+
end
|
612
|
+
|
613
|
+
change_column table_name, column_name, column.sql_type, :null => null
|
614
|
+
end # unless const_defined? :SchemaCreation
|
615
|
+
|
616
|
+
# @override
|
617
|
+
def change_column(table_name, column_name, type, options = {})
|
618
|
+
column = column_for(table_name, column_name)
|
619
|
+
|
620
|
+
unless options_include_default?(options)
|
621
|
+
# NOTE: no defaults for BLOB/TEXT columns with MySQL
|
622
|
+
options[:default] = column.default if type != :text && type != :binary
|
623
|
+
end
|
624
|
+
|
625
|
+
unless options.has_key?(:null)
|
626
|
+
options[:null] = column.null
|
627
|
+
end
|
628
|
+
|
629
|
+
change_column_sql = "ALTER TABLE #{quote_table_name(table_name)} CHANGE #{quote_column_name(column_name)} #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
|
630
|
+
add_column_options!(change_column_sql, options)
|
631
|
+
add_column_position!(change_column_sql, options)
|
632
|
+
execute(change_column_sql)
|
633
|
+
end
|
634
|
+
|
635
|
+
# @private
|
636
|
+
def change_column(table_name, column_name, type, options = {})
|
637
|
+
execute("ALTER TABLE #{quote_table_name(table_name)} #{change_column_sql(table_name, column_name, type, options)}")
|
638
|
+
end if AR42
|
639
|
+
|
640
|
+
# @override
|
641
|
+
def rename_column(table_name, column_name, new_column_name)
|
642
|
+
options = {}
|
643
|
+
|
644
|
+
if column = columns(table_name).find { |c| c.name == column_name.to_s }
|
645
|
+
type = column.type
|
646
|
+
options[:default] = column.default if type != :text && type != :binary
|
647
|
+
options[:null] = column.null
|
648
|
+
else
|
649
|
+
raise ActiveRecordError, "No such column: #{table_name}.#{column_name}"
|
650
|
+
end
|
651
|
+
|
652
|
+
current_type = select_one("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE '#{column_name}'")["Type"]
|
653
|
+
|
654
|
+
rename_column_sql = "ALTER TABLE #{quote_table_name(table_name)} CHANGE #{quote_column_name(column_name)} #{quote_column_name(new_column_name)} #{current_type}"
|
655
|
+
add_column_options!(rename_column_sql, options)
|
656
|
+
execute(rename_column_sql)
|
657
|
+
rename_column_indexes(table_name, column_name, new_column_name) if respond_to?(:rename_column_indexes) # AR-4.0 SchemaStatements
|
658
|
+
end
|
659
|
+
|
660
|
+
def add_column_position!(sql, options)
|
661
|
+
if options[:first]
|
662
|
+
sql << " FIRST"
|
663
|
+
elsif options[:after]
|
664
|
+
sql << " AFTER #{quote_column_name(options[:after])}"
|
665
|
+
end
|
666
|
+
end unless const_defined? :SchemaCreation
|
667
|
+
|
668
|
+
# @note Only used with (non-AREL) ActiveRecord **2.3**.
|
669
|
+
# @see Arel::Visitors::MySQL
|
670
|
+
def add_limit_offset!(sql, options)
|
671
|
+
limit, offset = options[:limit], options[:offset]
|
672
|
+
if limit && offset
|
673
|
+
sql << " LIMIT #{offset.to_i}, #{sanitize_limit(limit)}"
|
674
|
+
elsif limit
|
675
|
+
sql << " LIMIT #{sanitize_limit(limit)}"
|
676
|
+
elsif offset
|
677
|
+
sql << " OFFSET #{offset.to_i}"
|
678
|
+
end
|
679
|
+
sql
|
680
|
+
end if ::ActiveRecord::VERSION::MAJOR < 3
|
681
|
+
|
682
|
+
# In the simple case, MySQL allows us to place JOINs directly into the UPDATE
|
683
|
+
# query. However, this does not allow for LIMIT, OFFSET and ORDER. To support
|
684
|
+
# these, we must use a subquery. However, MySQL is too stupid to create a
|
685
|
+
# temporary table for this automatically, so we have to give it some prompting
|
686
|
+
# in the form of a subsubquery. Ugh!
|
687
|
+
# @private based on mysql_adapter.rb from 3.1-stable
|
688
|
+
def join_to_update(update, select)
|
689
|
+
if select.limit || select.offset || select.orders.any?
|
690
|
+
subsubselect = select.clone
|
691
|
+
subsubselect.projections = [update.key]
|
692
|
+
|
693
|
+
subselect = Arel::SelectManager.new(select.engine)
|
694
|
+
subselect.project Arel.sql(update.key.name)
|
695
|
+
subselect.from subsubselect.as('__active_record_temp')
|
696
|
+
|
697
|
+
update.where update.key.in(subselect)
|
698
|
+
else
|
699
|
+
update.table select.source
|
700
|
+
update.wheres = select.constraints
|
701
|
+
end
|
702
|
+
end
|
703
|
+
|
704
|
+
def show_variable(var)
|
705
|
+
res = execute("show variables like '#{var}'")
|
706
|
+
result_row = res.detect {|row| row["Variable_name"] == var }
|
707
|
+
result_row && result_row["Value"]
|
708
|
+
end
|
709
|
+
|
710
|
+
def charset
|
711
|
+
show_variable("character_set_database")
|
712
|
+
end
|
713
|
+
|
714
|
+
def collation
|
715
|
+
show_variable("collation_database")
|
716
|
+
end
|
717
|
+
|
718
|
+
# Maps logical Rails types to MySQL-specific data types.
|
719
|
+
def type_to_sql(type, limit = nil, precision = nil, scale = nil)
|
720
|
+
case type.to_s
|
721
|
+
when 'binary'
|
722
|
+
case limit
|
723
|
+
when 0..0xfff; "varbinary(#{limit})"
|
724
|
+
when nil; "blob"
|
725
|
+
when 0x1000..0xffffffff; "blob(#{limit})"
|
726
|
+
else raise(ActiveRecordError, "No binary type has character length #{limit}")
|
727
|
+
end
|
728
|
+
when 'integer'
|
729
|
+
case limit
|
730
|
+
when 1; 'tinyint'
|
731
|
+
when 2; 'smallint'
|
732
|
+
when 3; 'mediumint'
|
733
|
+
when nil, 4, 11; 'int(11)' # compatibility with MySQL default
|
734
|
+
when 5..8; 'bigint'
|
735
|
+
else raise(ActiveRecordError, "No integer type has byte size #{limit}")
|
736
|
+
end
|
737
|
+
when 'text'
|
738
|
+
case limit
|
739
|
+
when 0..0xff; 'tinytext'
|
740
|
+
when nil, 0x100..0xffff; 'text'
|
741
|
+
when 0x10000..0xffffff; 'mediumtext'
|
742
|
+
when 0x1000000..0xffffffff; 'longtext'
|
743
|
+
else raise(ActiveRecordError, "No text type has character length #{limit}")
|
744
|
+
end
|
745
|
+
when 'datetime'
|
746
|
+
return super unless precision
|
747
|
+
|
748
|
+
case precision
|
749
|
+
when 0..6; "datetime(#{precision})"
|
750
|
+
else raise(ActiveRecordError, "No datetime type has precision of #{precision}. The allowed range of precision is from 0 to 6.")
|
751
|
+
end
|
752
|
+
else
|
753
|
+
super
|
754
|
+
end
|
755
|
+
end
|
756
|
+
|
757
|
+
# @override
|
758
|
+
def empty_insert_statement_value
|
759
|
+
"VALUES ()"
|
760
|
+
end
|
761
|
+
|
762
|
+
# @note since AR 4.2
|
763
|
+
def valid_type?(type)
|
764
|
+
! native_database_types[type].nil?
|
765
|
+
end
|
766
|
+
|
767
|
+
def clear_cache!
|
768
|
+
super
|
769
|
+
reload_type_map
|
770
|
+
end if AR42
|
771
|
+
|
772
|
+
# @private since AR 4.2
|
773
|
+
def prepare_column_options(column, types)
|
774
|
+
spec = super
|
775
|
+
spec.delete(:limit) if column.type == :boolean
|
776
|
+
spec
|
777
|
+
end if AR42
|
778
|
+
|
779
|
+
# @private
|
780
|
+
Type = ActiveRecord::Type if AR42
|
781
|
+
|
782
|
+
protected
|
783
|
+
|
784
|
+
# @private
|
785
|
+
def initialize_type_map(m)
|
786
|
+
super
|
787
|
+
|
788
|
+
register_class_with_limit m, %r(char)i, MysqlString
|
789
|
+
|
790
|
+
m.register_type %r(tinytext)i, Type::Text.new(:limit => 2**8 - 1)
|
791
|
+
m.register_type %r(tinyblob)i, Type::Binary.new(:limit => 2**8 - 1)
|
792
|
+
m.register_type %r(text)i, Type::Text.new(:limit => 2**16 - 1)
|
793
|
+
m.register_type %r(blob)i, Type::Binary.new(:limit => 2**16 - 1)
|
794
|
+
m.register_type %r(mediumtext)i, Type::Text.new(:limit => 2**24 - 1)
|
795
|
+
m.register_type %r(mediumblob)i, Type::Binary.new(:limit => 2**24 - 1)
|
796
|
+
m.register_type %r(longtext)i, Type::Text.new(:limit => 2**32 - 1)
|
797
|
+
m.register_type %r(longblob)i, Type::Binary.new(:limit => 2**32 - 1)
|
798
|
+
m.register_type %r(^float)i, Type::Float.new(:limit => 24)
|
799
|
+
m.register_type %r(^double)i, Type::Float.new(:limit => 53)
|
800
|
+
|
801
|
+
register_integer_type m, %r(^bigint)i, :limit => 8
|
802
|
+
register_integer_type m, %r(^int)i, :limit => 4
|
803
|
+
register_integer_type m, %r(^mediumint)i, :limit => 3
|
804
|
+
register_integer_type m, %r(^smallint)i, :limit => 2
|
805
|
+
register_integer_type m, %r(^tinyint)i, :limit => 1
|
806
|
+
|
807
|
+
m.alias_type %r(tinyint\(1\))i, 'boolean' if emulate_booleans
|
808
|
+
m.alias_type %r(set)i, 'varchar'
|
809
|
+
m.alias_type %r(year)i, 'integer'
|
810
|
+
m.alias_type %r(bit)i, 'binary'
|
811
|
+
|
812
|
+
m.register_type(%r(datetime)i) do |sql_type|
|
813
|
+
precision = extract_precision(sql_type)
|
814
|
+
MysqlDateTime.new(:precision => precision)
|
815
|
+
end
|
816
|
+
|
817
|
+
m.register_type(%r(enum)i) do |sql_type|
|
818
|
+
limit = sql_type[/^enum\((.+)\)/i, 1].split(',').
|
819
|
+
map{|enum| enum.strip.length - 2}.max
|
820
|
+
MysqlString.new(:limit => limit)
|
821
|
+
end
|
822
|
+
end if AR42
|
823
|
+
|
824
|
+
# @private
|
825
|
+
def register_integer_type(mapping, key, options)
|
826
|
+
mapping.register_type(key) do |sql_type|
|
827
|
+
if /unsigned/i =~ sql_type
|
828
|
+
Type::UnsignedInteger.new(options)
|
829
|
+
else
|
830
|
+
Type::Integer.new(options)
|
831
|
+
end
|
832
|
+
end
|
833
|
+
end if AR42
|
834
|
+
|
835
|
+
# MySQL is too stupid to create a temporary table for use subquery, so we have
|
836
|
+
# to give it some prompting in the form of a subsubquery. Ugh!
|
837
|
+
# @note since AR 4.2
|
838
|
+
def subquery_for(key, select)
|
839
|
+
subsubselect = select.clone
|
840
|
+
subsubselect.projections = [key]
|
841
|
+
|
842
|
+
subselect = Arel::SelectManager.new(select.engine)
|
843
|
+
subselect.project Arel.sql(key.name)
|
844
|
+
subselect.from subsubselect.as('__active_record_temp')
|
845
|
+
end if AR42
|
846
|
+
|
847
|
+
def quoted_columns_for_index(column_names, options = {})
|
848
|
+
length = options[:length] if options.is_a?(Hash)
|
849
|
+
|
850
|
+
case length
|
851
|
+
when Hash
|
852
|
+
column_names.map { |name| length[name] ? "#{quote_column_name(name)}(#{length[name]})" : quote_column_name(name) }
|
853
|
+
when Fixnum
|
854
|
+
column_names.map { |name| "#{quote_column_name(name)}(#{length})" }
|
855
|
+
else
|
856
|
+
column_names.map { |name| quote_column_name(name) }
|
857
|
+
end
|
858
|
+
end
|
859
|
+
|
860
|
+
# @override
|
861
|
+
def translate_exception(exception, message)
|
862
|
+
return super unless exception.respond_to?(:errno)
|
863
|
+
|
864
|
+
case exception.errno
|
865
|
+
when 1062
|
866
|
+
::ActiveRecord::RecordNotUnique.new(message, exception)
|
867
|
+
when 1452
|
868
|
+
::ActiveRecord::InvalidForeignKey.new(message, exception)
|
869
|
+
else
|
870
|
+
super
|
871
|
+
end
|
872
|
+
end
|
873
|
+
|
874
|
+
private
|
875
|
+
|
876
|
+
def column_for(table_name, column_name)
|
877
|
+
unless column = columns(table_name).find { |c| c.name == column_name.to_s }
|
878
|
+
raise "No such column: #{table_name}.#{column_name}"
|
879
|
+
end
|
880
|
+
column
|
881
|
+
end
|
882
|
+
|
883
|
+
def mariadb?; !! ( full_version =~ /mariadb/i ) end
|
884
|
+
|
885
|
+
def version
|
886
|
+
return @version ||= begin
|
887
|
+
version = []
|
888
|
+
java_connection = jdbc_connection(true)
|
889
|
+
if java_connection.java_class.name == 'com.mysql.jdbc.ConnectionImpl'
|
890
|
+
version << jdbc_connection.serverMajorVersion
|
891
|
+
version << jdbc_connection.serverMinorVersion
|
892
|
+
version << jdbc_connection.serverSubMinorVersion
|
893
|
+
else
|
894
|
+
if match = full_version.match(/^(\d+)\.(\d+)\.(\d+)/)
|
895
|
+
version << match[1].to_i
|
896
|
+
version << match[2].to_i
|
897
|
+
version << match[3].to_i
|
898
|
+
end
|
899
|
+
end
|
900
|
+
version.freeze
|
901
|
+
end
|
902
|
+
end
|
903
|
+
|
904
|
+
def full_version
|
905
|
+
@full_version ||= begin
|
906
|
+
result = execute 'SELECT VERSION()', 'SCHEMA'
|
907
|
+
result.first.values.first # [{"VERSION()"=>"5.5.37-0ubuntu..."}]
|
908
|
+
end
|
909
|
+
end
|
910
|
+
|
911
|
+
# @private
|
912
|
+
def emulate_booleans; ::ArJdbc::MySQL.emulate_booleans?; end # due AR 4.2
|
913
|
+
public :emulate_booleans
|
914
|
+
|
915
|
+
# @private
|
916
|
+
class MysqlDateTime < Type::DateTime
|
917
|
+
private
|
918
|
+
|
919
|
+
def has_precision?
|
920
|
+
precision || 0
|
921
|
+
end
|
922
|
+
end if AR42
|
923
|
+
|
924
|
+
# @private
|
925
|
+
class MysqlString < Type::String
|
926
|
+
def type_cast_for_database(value)
|
927
|
+
case value
|
928
|
+
when true then "1"
|
929
|
+
when false then "0"
|
930
|
+
else super
|
931
|
+
end
|
932
|
+
end
|
933
|
+
|
934
|
+
private
|
935
|
+
|
936
|
+
def cast_value(value)
|
937
|
+
case value
|
938
|
+
when true then "1"
|
939
|
+
when false then "0"
|
940
|
+
else super
|
941
|
+
end
|
942
|
+
end
|
943
|
+
end if AR42
|
944
|
+
|
945
|
+
end
|
946
|
+
end
|
947
|
+
|
948
|
+
module ActiveRecord
|
949
|
+
module ConnectionAdapters
|
950
|
+
# Remove any vestiges of core/Ruby MySQL adapter
|
951
|
+
remove_const(:MysqlAdapter) if const_defined?(:MysqlAdapter)
|
952
|
+
|
953
|
+
class MysqlAdapter < JdbcAdapter
|
954
|
+
include ::ArJdbc::MySQL
|
955
|
+
include ::ArJdbc::MySQL::ExplainSupport
|
956
|
+
|
957
|
+
# By default, the MysqlAdapter will consider all columns of type
|
958
|
+
# __tinyint(1)__ as boolean. If you wish to disable this :
|
959
|
+
# ```
|
960
|
+
# ActiveRecord::ConnectionAdapters::Mysql[2]Adapter.emulate_booleans = false
|
961
|
+
# ```
|
962
|
+
def self.emulate_booleans?; ::ArJdbc::MySQL.emulate_booleans?; end
|
963
|
+
def self.emulate_booleans; ::ArJdbc::MySQL.emulate_booleans?; end # native adapter
|
964
|
+
def self.emulate_booleans=(emulate); ::ArJdbc::MySQL.emulate_booleans = emulate; end
|
965
|
+
|
966
|
+
class Column < JdbcColumn
|
967
|
+
include ::ArJdbc::MySQL::ColumnMethods
|
968
|
+
|
969
|
+
# @note {#ArJdbc::MySQL::ColumnMethods} uses this to check for boolean emulation
|
970
|
+
def adapter; MysqlAdapter end
|
971
|
+
|
972
|
+
end
|
973
|
+
end
|
974
|
+
|
975
|
+
if ActiveRecord::VERSION::MAJOR < 3 ||
|
976
|
+
( ActiveRecord::VERSION::MAJOR == 3 && ActiveRecord::VERSION::MINOR <= 1 )
|
977
|
+
remove_const(:MysqlColumn) if const_defined?(:MysqlColumn)
|
978
|
+
MysqlColumn = MysqlAdapter::Column
|
979
|
+
end
|
980
|
+
|
981
|
+
if ActiveRecord::VERSION::MAJOR > 3 ||
|
982
|
+
( ActiveRecord::VERSION::MAJOR == 3 && ActiveRecord::VERSION::MINOR >= 1 )
|
983
|
+
remove_const(:Mysql2Adapter) if const_defined?(:Mysql2Adapter)
|
984
|
+
Mysql2Adapter = MysqlAdapter
|
985
|
+
if ActiveRecord::VERSION::MAJOR == 3 && ActiveRecord::VERSION::MINOR == 1
|
986
|
+
remove_const(:Mysql2Column) if const_defined?(:Mysql2Column)
|
987
|
+
Mysql2Column = MysqlAdapter::Column
|
988
|
+
end
|
989
|
+
end
|
990
|
+
|
991
|
+
end
|
992
|
+
end
|
993
|
+
|
994
|
+
module ArJdbc
|
995
|
+
module MySQL
|
996
|
+
Column = ::ActiveRecord::ConnectionAdapters::MysqlAdapter::Column
|
997
|
+
end
|
998
|
+
end
|