activerecord-jdbc-adapter-onsite 1.2.2
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.
- data/.gitignore +22 -0
- data/.travis.yml +14 -0
- data/Appraisals +16 -0
- data/Gemfile +11 -0
- data/Gemfile.lock +45 -0
- data/History.txt +488 -0
- data/LICENSE.txt +21 -0
- data/README.rdoc +214 -0
- data/Rakefile +62 -0
- data/activerecord-jdbc-adapter.gemspec +23 -0
- data/bench/bench_attributes.rb +13 -0
- data/bench/bench_attributes_new.rb +14 -0
- data/bench/bench_create.rb +12 -0
- data/bench/bench_find_all.rb +12 -0
- data/bench/bench_find_all_mt.rb +25 -0
- data/bench/bench_model.rb +85 -0
- data/bench/bench_new.rb +12 -0
- data/bench/bench_new_valid.rb +12 -0
- data/bench/bench_valid.rb +13 -0
- data/gemfiles/rails23.gemfile +10 -0
- data/gemfiles/rails23.gemfile.lock +38 -0
- data/gemfiles/rails30.gemfile +9 -0
- data/gemfiles/rails30.gemfile.lock +33 -0
- data/gemfiles/rails31.gemfile +9 -0
- data/gemfiles/rails31.gemfile.lock +35 -0
- data/gemfiles/rails32.gemfile +9 -0
- data/gemfiles/rails32.gemfile.lock +35 -0
- data/lib/active_record/connection_adapters/derby_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/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/activerecord-jdbc-adapter.rb +8 -0
- data/lib/arel/engines/sql/compilers/db2_compiler.rb +9 -0
- data/lib/arel/engines/sql/compilers/derby_compiler.rb +6 -0
- data/lib/arel/engines/sql/compilers/h2_compiler.rb +6 -0
- data/lib/arel/engines/sql/compilers/hsqldb_compiler.rb +15 -0
- data/lib/arel/engines/sql/compilers/jdbc_compiler.rb +6 -0
- data/lib/arel/engines/sql/compilers/mssql_compiler.rb +46 -0
- data/lib/arel/visitors/compat.rb +13 -0
- data/lib/arel/visitors/db2.rb +17 -0
- data/lib/arel/visitors/derby.rb +32 -0
- data/lib/arel/visitors/firebird.rb +24 -0
- data/lib/arel/visitors/hsqldb.rb +26 -0
- data/lib/arel/visitors/sql_server.rb +46 -0
- data/lib/arjdbc.rb +24 -0
- data/lib/arjdbc/db2.rb +2 -0
- data/lib/arjdbc/db2/adapter.rb +541 -0
- data/lib/arjdbc/derby.rb +7 -0
- data/lib/arjdbc/derby/adapter.rb +358 -0
- data/lib/arjdbc/derby/connection_methods.rb +19 -0
- data/lib/arjdbc/discover.rb +92 -0
- data/lib/arjdbc/firebird.rb +2 -0
- data/lib/arjdbc/firebird/adapter.rb +140 -0
- data/lib/arjdbc/h2.rb +4 -0
- data/lib/arjdbc/h2/adapter.rb +54 -0
- data/lib/arjdbc/h2/connection_methods.rb +13 -0
- data/lib/arjdbc/hsqldb.rb +4 -0
- data/lib/arjdbc/hsqldb/adapter.rb +184 -0
- data/lib/arjdbc/hsqldb/connection_methods.rb +15 -0
- data/lib/arjdbc/informix.rb +3 -0
- data/lib/arjdbc/informix/adapter.rb +142 -0
- data/lib/arjdbc/informix/connection_methods.rb +11 -0
- data/lib/arjdbc/jdbc.rb +2 -0
- data/lib/arjdbc/jdbc/adapter.rb +356 -0
- data/lib/arjdbc/jdbc/adapter_java.jar +0 -0
- data/lib/arjdbc/jdbc/base_ext.rb +15 -0
- data/lib/arjdbc/jdbc/callbacks.rb +44 -0
- data/lib/arjdbc/jdbc/column.rb +47 -0
- data/lib/arjdbc/jdbc/compatibility.rb +51 -0
- data/lib/arjdbc/jdbc/connection.rb +134 -0
- data/lib/arjdbc/jdbc/connection_methods.rb +16 -0
- data/lib/arjdbc/jdbc/core_ext.rb +24 -0
- data/lib/arjdbc/jdbc/discover.rb +18 -0
- data/lib/arjdbc/jdbc/driver.rb +35 -0
- data/lib/arjdbc/jdbc/extension.rb +47 -0
- data/lib/arjdbc/jdbc/java.rb +14 -0
- data/lib/arjdbc/jdbc/jdbc.rake +131 -0
- data/lib/arjdbc/jdbc/missing_functionality_helper.rb +88 -0
- data/lib/arjdbc/jdbc/quoted_primary_key.rb +28 -0
- data/lib/arjdbc/jdbc/railtie.rb +9 -0
- data/lib/arjdbc/jdbc/rake_tasks.rb +10 -0
- data/lib/arjdbc/jdbc/require_driver.rb +16 -0
- data/lib/arjdbc/jdbc/type_converter.rb +126 -0
- data/lib/arjdbc/mimer.rb +2 -0
- data/lib/arjdbc/mimer/adapter.rb +142 -0
- data/lib/arjdbc/mssql.rb +4 -0
- data/lib/arjdbc/mssql/adapter.rb +477 -0
- data/lib/arjdbc/mssql/connection_methods.rb +31 -0
- data/lib/arjdbc/mssql/limit_helpers.rb +101 -0
- data/lib/arjdbc/mssql/lock_helpers.rb +72 -0
- data/lib/arjdbc/mssql/tsql_helper.rb +61 -0
- data/lib/arjdbc/mysql.rb +4 -0
- data/lib/arjdbc/mysql/adapter.rb +505 -0
- data/lib/arjdbc/mysql/connection_methods.rb +28 -0
- data/lib/arjdbc/oracle.rb +3 -0
- data/lib/arjdbc/oracle/adapter.rb +432 -0
- data/lib/arjdbc/oracle/connection_methods.rb +12 -0
- data/lib/arjdbc/postgresql.rb +4 -0
- data/lib/arjdbc/postgresql/adapter.rb +861 -0
- data/lib/arjdbc/postgresql/connection_methods.rb +23 -0
- data/lib/arjdbc/sqlite3.rb +4 -0
- data/lib/arjdbc/sqlite3/adapter.rb +389 -0
- data/lib/arjdbc/sqlite3/connection_methods.rb +35 -0
- data/lib/arjdbc/sybase.rb +2 -0
- data/lib/arjdbc/sybase/adapter.rb +46 -0
- data/lib/arjdbc/version.rb +8 -0
- data/lib/generators/jdbc/USAGE +10 -0
- data/lib/generators/jdbc/jdbc_generator.rb +9 -0
- data/lib/jdbc_adapter.rb +2 -0
- data/lib/jdbc_adapter/rake_tasks.rb +3 -0
- data/lib/jdbc_adapter/version.rb +3 -0
- data/lib/pg.rb +26 -0
- data/pom.xml +57 -0
- data/rails_generators/jdbc_generator.rb +15 -0
- data/rails_generators/templates/config/initializers/jdbc.rb +7 -0
- data/rails_generators/templates/lib/tasks/jdbc.rake +8 -0
- data/rakelib/bundler_ext.rb +11 -0
- data/rakelib/compile.rake +23 -0
- data/rakelib/db.rake +39 -0
- data/rakelib/rails.rake +41 -0
- data/src/java/arjdbc/db2/DB2RubyJdbcConnection.java +69 -0
- data/src/java/arjdbc/derby/DerbyModule.java +324 -0
- data/src/java/arjdbc/h2/H2RubyJdbcConnection.java +70 -0
- data/src/java/arjdbc/informix/InformixRubyJdbcConnection.java +74 -0
- data/src/java/arjdbc/jdbc/AdapterJavaService.java +68 -0
- data/src/java/arjdbc/jdbc/JdbcConnectionFactory.java +36 -0
- data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +1346 -0
- data/src/java/arjdbc/jdbc/SQLBlock.java +48 -0
- data/src/java/arjdbc/mssql/MssqlRubyJdbcConnection.java +127 -0
- data/src/java/arjdbc/mysql/MySQLModule.java +134 -0
- data/src/java/arjdbc/mysql/MySQLRubyJdbcConnection.java +161 -0
- data/src/java/arjdbc/oracle/OracleRubyJdbcConnection.java +85 -0
- data/src/java/arjdbc/postgresql/PostgresqlRubyJdbcConnection.java +82 -0
- data/src/java/arjdbc/sqlite3/Sqlite3RubyJdbcConnection.java +126 -0
- data/test/abstract_db_create.rb +135 -0
- data/test/activerecord/connection_adapters/type_conversion_test.rb +31 -0
- data/test/activerecord/connections/native_jdbc_mysql/connection.rb +25 -0
- data/test/activerecord/jall.sh +7 -0
- data/test/activerecord/jtest.sh +3 -0
- data/test/db/db2.rb +11 -0
- data/test/db/derby.rb +12 -0
- data/test/db/h2.rb +11 -0
- data/test/db/hsqldb.rb +13 -0
- data/test/db/informix.rb +11 -0
- data/test/db/jdbc.rb +12 -0
- data/test/db/jndi_config.rb +40 -0
- data/test/db/logger.rb +3 -0
- data/test/db/mssql.rb +9 -0
- data/test/db/mysql.rb +10 -0
- data/test/db/oracle.rb +34 -0
- data/test/db/postgres.rb +18 -0
- data/test/db/sqlite3.rb +11 -0
- data/test/db2_reset_column_information_test.rb +8 -0
- data/test/db2_simple_test.rb +66 -0
- data/test/derby_migration_test.rb +68 -0
- data/test/derby_multibyte_test.rb +12 -0
- data/test/derby_reset_column_information_test.rb +8 -0
- data/test/derby_row_locking_test.rb +9 -0
- data/test/derby_simple_test.rb +139 -0
- data/test/generic_jdbc_connection_test.rb +29 -0
- data/test/h2_change_column_test.rb +68 -0
- data/test/h2_simple_test.rb +41 -0
- data/test/has_many_through.rb +79 -0
- data/test/helper.rb +108 -0
- data/test/hsqldb_simple_test.rb +6 -0
- data/test/informix_simple_test.rb +48 -0
- data/test/jdbc_common.rb +28 -0
- data/test/jndi_callbacks_test.rb +36 -0
- data/test/jndi_test.rb +25 -0
- data/test/manualTestDatabase.rb +191 -0
- data/test/models/add_not_null_column_to_table.rb +9 -0
- data/test/models/auto_id.rb +15 -0
- data/test/models/custom_pk_name.rb +14 -0
- data/test/models/data_types.rb +30 -0
- data/test/models/entry.rb +40 -0
- data/test/models/mixed_case.rb +22 -0
- data/test/models/reserved_word.rb +15 -0
- data/test/models/string_id.rb +17 -0
- data/test/models/thing.rb +16 -0
- data/test/models/validates_uniqueness_of_string.rb +19 -0
- data/test/mssql_db_create_test.rb +26 -0
- data/test/mssql_identity_insert_test.rb +19 -0
- data/test/mssql_ignore_system_views_test.rb +27 -0
- data/test/mssql_legacy_types_test.rb +58 -0
- data/test/mssql_limit_offset_test.rb +136 -0
- data/test/mssql_multibyte_test.rb +18 -0
- data/test/mssql_null_test.rb +14 -0
- data/test/mssql_reset_column_information_test.rb +8 -0
- data/test/mssql_row_locking_sql_test.rb +159 -0
- data/test/mssql_row_locking_test.rb +9 -0
- data/test/mssql_simple_test.rb +55 -0
- data/test/mysql_db_create_test.rb +27 -0
- data/test/mysql_index_length_test.rb +58 -0
- data/test/mysql_info_test.rb +123 -0
- data/test/mysql_multibyte_test.rb +10 -0
- data/test/mysql_nonstandard_primary_key_test.rb +42 -0
- data/test/mysql_reset_column_information_test.rb +8 -0
- data/test/mysql_simple_test.rb +125 -0
- data/test/oracle_reset_column_information_test.rb +8 -0
- data/test/oracle_simple_test.rb +18 -0
- data/test/oracle_specific_test.rb +83 -0
- data/test/postgres_db_create_test.rb +32 -0
- data/test/postgres_drop_db_test.rb +16 -0
- data/test/postgres_information_schema_leak_test.rb +29 -0
- data/test/postgres_mixed_case_test.rb +29 -0
- data/test/postgres_native_type_mapping_test.rb +93 -0
- data/test/postgres_nonseq_pkey_test.rb +38 -0
- data/test/postgres_reserved_test.rb +22 -0
- data/test/postgres_reset_column_information_test.rb +8 -0
- data/test/postgres_schema_search_path_test.rb +48 -0
- data/test/postgres_simple_test.rb +168 -0
- data/test/postgres_table_alias_length_test.rb +15 -0
- data/test/postgres_type_conversion_test.rb +34 -0
- data/test/row_locking.rb +90 -0
- data/test/simple.rb +731 -0
- data/test/sqlite3_reset_column_information_test.rb +8 -0
- data/test/sqlite3_simple_test.rb +316 -0
- data/test/sybase_jtds_simple_test.rb +28 -0
- data/test/sybase_reset_column_information_test.rb +8 -0
- metadata +288 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
class ActiveRecord::Base
|
|
2
|
+
class << self
|
|
3
|
+
def mssql_connection(config)
|
|
4
|
+
require "arjdbc/mssql"
|
|
5
|
+
config[:host] ||= "localhost"
|
|
6
|
+
config[:port] ||= 1433
|
|
7
|
+
config[:driver] ||= "net.sourceforge.jtds.jdbc.Driver"
|
|
8
|
+
config[:adapter_spec] = ::ArJdbc::MsSQL
|
|
9
|
+
|
|
10
|
+
url = "jdbc:jtds:sqlserver://#{config[:host]}:#{config[:port]}/#{config[:database]}"
|
|
11
|
+
|
|
12
|
+
# Instance is often a preferrable alternative to port when dynamic ports are used.
|
|
13
|
+
# If instance is specified then port is essentially ignored.
|
|
14
|
+
url << ";instance=#{config[:instance]}" if config[:instance]
|
|
15
|
+
|
|
16
|
+
# This will enable windows domain-based authentication and will require the JTDS native libraries be available.
|
|
17
|
+
url << ";domain=#{config[:domain]}" if config[:domain]
|
|
18
|
+
|
|
19
|
+
# AppName is shown in sql server as additional information against the connection.
|
|
20
|
+
url << ";appname=#{config[:appname]}" if config[:appname]
|
|
21
|
+
config[:url] ||= url
|
|
22
|
+
|
|
23
|
+
if !config[:domain]
|
|
24
|
+
config[:username] ||= "sa"
|
|
25
|
+
config[:password] ||= ""
|
|
26
|
+
end
|
|
27
|
+
jdbc_connection(config)
|
|
28
|
+
end
|
|
29
|
+
alias_method :jdbcmssql_connection, :mssql_connection
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
module ::ArJdbc
|
|
2
|
+
module MsSQL
|
|
3
|
+
module LimitHelpers
|
|
4
|
+
module_function
|
|
5
|
+
def get_table_name(sql)
|
|
6
|
+
if sql =~ /^\s*insert\s+into\s+([^\(\s,]+)\s*|^\s*update\s+([^\(\s,]+)\s*/i
|
|
7
|
+
$1
|
|
8
|
+
elsif sql =~ /\bfrom\s+([^\(\s,]+)\s*/i
|
|
9
|
+
$1
|
|
10
|
+
else
|
|
11
|
+
nil
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
module SqlServer2000ReplaceLimitOffset
|
|
16
|
+
module_function
|
|
17
|
+
def replace_limit_offset!(sql, limit, offset, order)
|
|
18
|
+
if limit
|
|
19
|
+
offset ||= 0
|
|
20
|
+
start_row = offset + 1
|
|
21
|
+
end_row = offset + limit.to_i
|
|
22
|
+
find_select = /\b(SELECT(?:\s+DISTINCT)?)\b(.*)/im
|
|
23
|
+
whole, select, rest_of_query = find_select.match(sql).to_a
|
|
24
|
+
if (start_row == 1) && (end_row ==1)
|
|
25
|
+
new_sql = "#{select} TOP 1 #{rest_of_query}"
|
|
26
|
+
sql.replace(new_sql)
|
|
27
|
+
else
|
|
28
|
+
#UGLY
|
|
29
|
+
#KLUDGY?
|
|
30
|
+
#removing out stuff before the FROM...
|
|
31
|
+
rest = rest_of_query[/FROM/i=~ rest_of_query.. -1]
|
|
32
|
+
#need the table name for avoiding amiguity
|
|
33
|
+
table_name = LimitHelpers.get_table_name(sql)
|
|
34
|
+
primary_key = order[/(\w*id\w*)/i] || "id"
|
|
35
|
+
#I am not sure this will cover all bases. but all the tests pass
|
|
36
|
+
if order[/ORDER/].nil?
|
|
37
|
+
new_order = "ORDER BY #{order}, #{table_name}.#{primary_key}" if order.index("#{table_name}.#{primary_key}").nil?
|
|
38
|
+
else
|
|
39
|
+
new_order ||= order
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
if (rest_of_query.match(/WHERE/).nil?)
|
|
43
|
+
new_sql = "#{select} TOP #{limit} #{rest_of_query} WHERE #{table_name}.#{primary_key} NOT IN (#{select} TOP #{offset} #{table_name}.#{primary_key} #{rest} #{new_order}) #{order} "
|
|
44
|
+
else
|
|
45
|
+
new_sql = "#{select} TOP #{limit} #{rest_of_query} AND #{table_name}.#{primary_key} NOT IN (#{select} TOP #{offset} #{table_name}.#{primary_key} #{rest} #{new_order}) #{order} "
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
sql.replace(new_sql)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
sql
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
module SqlServer2000AddLimitOffset
|
|
56
|
+
def add_limit_offset!(sql, options)
|
|
57
|
+
if options[:limit]
|
|
58
|
+
order = "ORDER BY #{options[:order] || determine_order_clause(sql)}"
|
|
59
|
+
sql.sub!(/ ORDER BY.*$/i, '')
|
|
60
|
+
SqlServerReplaceLimitOffset.replace_limit_offset!(sql, options[:limit], options[:offset], order)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
module SqlServerReplaceLimitOffset
|
|
66
|
+
module_function
|
|
67
|
+
def replace_limit_offset!(sql, limit, offset, order)
|
|
68
|
+
if limit
|
|
69
|
+
offset ||= 0
|
|
70
|
+
start_row = offset + 1
|
|
71
|
+
end_row = offset + limit.to_i
|
|
72
|
+
find_select = /\b(SELECT(?:\s+DISTINCT)?)\b(.*)/im
|
|
73
|
+
whole, select, rest_of_query = find_select.match(sql).to_a
|
|
74
|
+
rest_of_query.strip!
|
|
75
|
+
if rest_of_query[0...1] == "1" && rest_of_query !~ /1 AS/i
|
|
76
|
+
rest_of_query[0] = "*"
|
|
77
|
+
end
|
|
78
|
+
if rest_of_query[0] == "*"
|
|
79
|
+
from_table = LimitHelpers.get_table_name(rest_of_query)
|
|
80
|
+
rest_of_query = from_table + '.' + rest_of_query
|
|
81
|
+
end
|
|
82
|
+
new_sql = "#{select} t.* FROM (SELECT ROW_NUMBER() OVER(#{order}) AS _row_num, #{rest_of_query}"
|
|
83
|
+
new_sql << ") AS t WHERE t._row_num BETWEEN #{start_row.to_s} AND #{end_row.to_s}"
|
|
84
|
+
sql.replace(new_sql)
|
|
85
|
+
end
|
|
86
|
+
sql
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
module SqlServerAddLimitOffset
|
|
91
|
+
def add_limit_offset!(sql, options)
|
|
92
|
+
if options[:limit]
|
|
93
|
+
order = "ORDER BY #{options[:order] || determine_order_clause(sql)}"
|
|
94
|
+
sql.sub!(/ ORDER BY.*$/i, '')
|
|
95
|
+
SqlServerReplaceLimitOffset.replace_limit_offset!(sql, options[:limit], options[:offset], order)
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
module ::ArJdbc
|
|
2
|
+
module MsSQL
|
|
3
|
+
module LockHelpers
|
|
4
|
+
module SqlServerAddLock
|
|
5
|
+
# Microsoft SQL Server uses its own syntax for SELECT .. FOR UPDATE:
|
|
6
|
+
# SELECT .. FROM table1 WITH(ROWLOCK,UPDLOCK), table2 WITH(ROWLOCK,UPDLOCK) WHERE ..
|
|
7
|
+
#
|
|
8
|
+
# This does in-place modification of the passed-in string.
|
|
9
|
+
def add_lock!(sql, options)
|
|
10
|
+
if options[:lock] and sql =~ /\A\s*SELECT/mi
|
|
11
|
+
# Check for and extract the :limit/:offset sub-query
|
|
12
|
+
if sql =~ /\A(\s*SELECT t\.\* FROM \()(.*)(\) AS t WHERE t._row_num BETWEEN \d+ AND \d+\s*)\Z/m
|
|
13
|
+
prefix, subselect, suffix = [$1, $2, $3]
|
|
14
|
+
add_lock!(subselect, options)
|
|
15
|
+
return sql.replace(prefix + subselect + suffix)
|
|
16
|
+
end
|
|
17
|
+
unless sql =~ /\A(\s*SELECT\s.*?)(\sFROM\s)(.*?)(\sWHERE\s.*|)\Z/mi
|
|
18
|
+
# If you get this error, this driver probably needs to be fixed.
|
|
19
|
+
raise NotImplementedError, "Don't know how to add_lock! to SQL statement: #{sql.inspect}"
|
|
20
|
+
end
|
|
21
|
+
select_clause, from_word, from_tables, where_clause = [$1, $2, $3, $4]
|
|
22
|
+
with_clause = options[:lock].is_a?(String) ? " #{options[:lock]} " : " WITH(ROWLOCK,UPDLOCK) "
|
|
23
|
+
|
|
24
|
+
# Split the FROM clause into its constituent tables, and add the with clause after each one.
|
|
25
|
+
new_from_tables = []
|
|
26
|
+
s = StringScanner.new(from_tables)
|
|
27
|
+
until s.eos?
|
|
28
|
+
prev_pos = s.pos
|
|
29
|
+
if s.scan_until(/,|(INNER\s+JOIN|CROSS\s+JOIN|(LEFT|RIGHT|FULL)(\s+OUTER)?\s+JOIN)\s+/mi)
|
|
30
|
+
join_operand = s.pre_match[prev_pos..-1]
|
|
31
|
+
join_operator = s.matched
|
|
32
|
+
else
|
|
33
|
+
join_operand = s.rest
|
|
34
|
+
join_operator = ""
|
|
35
|
+
s.terminate
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# At this point, we have something like:
|
|
39
|
+
# join_operand == "appointments "
|
|
40
|
+
# join_operator == "INNER JOIN "
|
|
41
|
+
# or:
|
|
42
|
+
# join_operand == "appointment_details AS d1 ON appointments.[id] = d1.[appointment_id]"
|
|
43
|
+
# join_operator == ""
|
|
44
|
+
if join_operand =~ /\A(.*)(\s+ON\s+.*)\Z/mi
|
|
45
|
+
table_spec, on_clause = [$1, $2]
|
|
46
|
+
else
|
|
47
|
+
table_spec = join_operand
|
|
48
|
+
on_clause = ""
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Add the "WITH(ROWLOCK,UPDLOCK)" option to the table specification
|
|
52
|
+
table_spec << with_clause unless table_spec =~ /\A\(\s*SELECT\s+/mi # HACK - this parser isn't so great
|
|
53
|
+
join_operand = table_spec + on_clause
|
|
54
|
+
|
|
55
|
+
# So now we have something like:
|
|
56
|
+
# join_operand == "appointments WITH(ROWLOCK,UPDLOCK) "
|
|
57
|
+
# join_operator == "INNER JOIN "
|
|
58
|
+
# or:
|
|
59
|
+
# join_operand == "appointment_details AS d1 WITH(ROWLOCK,UPDLOCK) ON appointments.[id] = d1.[appointment_id]"
|
|
60
|
+
# join_operator == ""
|
|
61
|
+
|
|
62
|
+
new_from_tables << join_operand
|
|
63
|
+
new_from_tables << join_operator
|
|
64
|
+
end
|
|
65
|
+
sql.replace([select_clause, from_word, new_from_tables, where_clause].flatten.join)
|
|
66
|
+
end
|
|
67
|
+
sql
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Common methods for handling TSQL databases.
|
|
2
|
+
module TSqlMethods
|
|
3
|
+
|
|
4
|
+
def modify_types(tp) #:nodoc:
|
|
5
|
+
tp[:primary_key] = "int NOT NULL IDENTITY(1, 1) PRIMARY KEY"
|
|
6
|
+
tp[:integer][:limit] = nil
|
|
7
|
+
tp[:boolean] = {:name => "bit"}
|
|
8
|
+
tp[:binary] = { :name => "image"}
|
|
9
|
+
tp
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def type_to_sql(type, limit = nil, precision = nil, scale = nil) #:nodoc:
|
|
13
|
+
limit = nil if %w(text binary).include? type.to_s
|
|
14
|
+
return 'uniqueidentifier' if (type.to_s == 'uniqueidentifier')
|
|
15
|
+
return super unless type.to_s == 'integer'
|
|
16
|
+
|
|
17
|
+
if limit.nil? || limit == 4
|
|
18
|
+
'int'
|
|
19
|
+
elsif limit == 2
|
|
20
|
+
'smallint'
|
|
21
|
+
elsif limit == 1
|
|
22
|
+
'tinyint'
|
|
23
|
+
else
|
|
24
|
+
'bigint'
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def add_limit_offset!(sql, options)
|
|
29
|
+
if options[:limit] and options[:offset]
|
|
30
|
+
total_rows = select_all("SELECT count(*) as TotalRows from (#{sql.gsub(/\bSELECT(\s+DISTINCT)?\b/i, "SELECT\\1 TOP 1000000000")}) tally")[0]["TotalRows"].to_i
|
|
31
|
+
if (options[:limit] + options[:offset]) >= total_rows
|
|
32
|
+
options[:limit] = (total_rows - options[:offset] >= 0) ? (total_rows - options[:offset]) : 0
|
|
33
|
+
end
|
|
34
|
+
sql.sub!(/^\s*SELECT(\s+DISTINCT)?/i, "SELECT * FROM (SELECT TOP #{options[:limit]} * FROM (SELECT\\1 TOP #{options[:limit] + options[:offset]} ")
|
|
35
|
+
sql << ") AS tmp1"
|
|
36
|
+
if options[:order]
|
|
37
|
+
options[:order] = options[:order].split(',').map do |field|
|
|
38
|
+
parts = field.split(" ")
|
|
39
|
+
tc = parts[0]
|
|
40
|
+
if sql =~ /\.\[/ and tc =~ /\./ # if column quoting used in query
|
|
41
|
+
tc.gsub!(/\./, '\\.\\[')
|
|
42
|
+
tc << '\\]'
|
|
43
|
+
end
|
|
44
|
+
if sql =~ /#{tc} AS (t\d_r\d\d?)/
|
|
45
|
+
parts[0] = $1
|
|
46
|
+
elsif parts[0] =~ /\w+\.(\w+)/
|
|
47
|
+
parts[0] = $1
|
|
48
|
+
end
|
|
49
|
+
parts.join(' ')
|
|
50
|
+
end.join(', ')
|
|
51
|
+
sql << " ORDER BY #{change_order_direction(options[:order])}) AS tmp2 ORDER BY #{options[:order]}"
|
|
52
|
+
else
|
|
53
|
+
sql << " ) AS tmp2"
|
|
54
|
+
end
|
|
55
|
+
elsif sql !~ /^\s*SELECT (@@|COUNT\()/i
|
|
56
|
+
sql.sub!(/^\s*SELECT(\s+DISTINCT)?/i) do
|
|
57
|
+
"SELECT#{$1} TOP #{options[:limit]}"
|
|
58
|
+
end unless options[:limit].nil?
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
data/lib/arjdbc/mysql.rb
ADDED
|
@@ -0,0 +1,505 @@
|
|
|
1
|
+
require 'active_record/connection_adapters/abstract/schema_definitions'
|
|
2
|
+
|
|
3
|
+
module ::ArJdbc
|
|
4
|
+
module MySQL
|
|
5
|
+
def self.column_selector
|
|
6
|
+
[/mysql/i, lambda {|cfg,col| col.extend(::ArJdbc::MySQL::ColumnExtensions)}]
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def self.extended(adapter)
|
|
10
|
+
adapter.configure_connection
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def configure_connection
|
|
14
|
+
execute("SET SQL_AUTO_IS_NULL=0")
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def self.jdbc_connection_class
|
|
18
|
+
::ActiveRecord::ConnectionAdapters::MySQLJdbcConnection
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
module ColumnExtensions
|
|
22
|
+
def extract_default(default)
|
|
23
|
+
if sql_type =~ /blob/i || type == :text
|
|
24
|
+
if default.blank?
|
|
25
|
+
return null ? nil : ''
|
|
26
|
+
else
|
|
27
|
+
raise ArgumentError, "#{type} columns cannot have a default value: #{default.inspect}"
|
|
28
|
+
end
|
|
29
|
+
elsif missing_default_forged_as_empty_string?(default)
|
|
30
|
+
nil
|
|
31
|
+
else
|
|
32
|
+
super
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def has_default?
|
|
37
|
+
return false if sql_type =~ /blob/i || type == :text #mysql forbids defaults on blob and text columns
|
|
38
|
+
super
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def simplified_type(field_type)
|
|
42
|
+
case field_type
|
|
43
|
+
when /tinyint\(1\)|bit/i then :boolean
|
|
44
|
+
when /enum/i then :string
|
|
45
|
+
when /year/i then :integer
|
|
46
|
+
else
|
|
47
|
+
super
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def extract_limit(sql_type)
|
|
52
|
+
case sql_type
|
|
53
|
+
when /blob|text/i
|
|
54
|
+
case sql_type
|
|
55
|
+
when /tiny/i
|
|
56
|
+
255
|
|
57
|
+
when /medium/i
|
|
58
|
+
16777215
|
|
59
|
+
when /long/i
|
|
60
|
+
2147483647 # mysql only allows 2^31-1, not 2^32-1, somewhat inconsistently with the tiny/medium/normal cases
|
|
61
|
+
else
|
|
62
|
+
nil # we could return 65535 here, but we leave it undecorated by default
|
|
63
|
+
end
|
|
64
|
+
when /^enum/i; 255
|
|
65
|
+
when /^bigint/i; 8
|
|
66
|
+
when /^int/i; 4
|
|
67
|
+
when /^mediumint/i; 3
|
|
68
|
+
when /^smallint/i; 2
|
|
69
|
+
when /^tinyint/i; 1
|
|
70
|
+
when /^(bool|date|float|int|time)/i
|
|
71
|
+
nil
|
|
72
|
+
else
|
|
73
|
+
super
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# MySQL misreports NOT NULL column default when none is given.
|
|
78
|
+
# We can't detect this for columns which may have a legitimate ''
|
|
79
|
+
# default (string) but we can for others (integer, datetime, boolean,
|
|
80
|
+
# and the rest).
|
|
81
|
+
#
|
|
82
|
+
# Test whether the column has default '', is not null, and is not
|
|
83
|
+
# a type allowing default ''.
|
|
84
|
+
def missing_default_forged_as_empty_string?(default)
|
|
85
|
+
type != :string && !null && default == ''
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def modify_types(tp)
|
|
90
|
+
tp[:primary_key] = "int(11) DEFAULT NULL auto_increment PRIMARY KEY"
|
|
91
|
+
tp[:integer] = { :name => 'int', :limit => 4 }
|
|
92
|
+
tp[:decimal] = { :name => "decimal" }
|
|
93
|
+
tp[:timestamp] = { :name => "datetime" }
|
|
94
|
+
tp[:datetime][:limit] = nil
|
|
95
|
+
tp
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def adapter_name #:nodoc:
|
|
99
|
+
'MySQL'
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def self.arel2_visitors(config)
|
|
103
|
+
{}.tap {|v| %w(mysql mysql2 jdbcmysql).each {|a| v[a] = ::Arel::Visitors::MySQL } }
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def case_sensitive_equality_operator
|
|
107
|
+
"= BINARY"
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def case_sensitive_modifier(node)
|
|
111
|
+
Arel::Nodes::Bin.new(node)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def limited_update_conditions(where_sql, quoted_table_name, quoted_primary_key)
|
|
115
|
+
where_sql
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# QUOTING ==================================================
|
|
119
|
+
|
|
120
|
+
def quote(value, column = nil)
|
|
121
|
+
return value.quoted_id if value.respond_to?(:quoted_id)
|
|
122
|
+
|
|
123
|
+
if column && column.type == :primary_key
|
|
124
|
+
value.to_s
|
|
125
|
+
elsif column && String === value && column.type == :binary && column.class.respond_to?(:string_to_binary)
|
|
126
|
+
s = column.class.string_to_binary(value).unpack("H*")[0]
|
|
127
|
+
"x'#{s}'"
|
|
128
|
+
elsif BigDecimal === value
|
|
129
|
+
"'#{value.to_s("F")}'"
|
|
130
|
+
else
|
|
131
|
+
super
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def quote_column_name(name)
|
|
136
|
+
"`#{name.to_s.gsub('`', '``')}`"
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def quoted_true
|
|
140
|
+
"1"
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def quoted_false
|
|
144
|
+
"0"
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def supports_savepoints? #:nodoc:
|
|
148
|
+
true
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def create_savepoint
|
|
152
|
+
execute("SAVEPOINT #{current_savepoint_name}")
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def rollback_to_savepoint
|
|
156
|
+
execute("ROLLBACK TO SAVEPOINT #{current_savepoint_name}")
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def release_savepoint
|
|
160
|
+
execute("RELEASE SAVEPOINT #{current_savepoint_name}")
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def disable_referential_integrity(&block) #:nodoc:
|
|
164
|
+
old = select_value("SELECT @@FOREIGN_KEY_CHECKS")
|
|
165
|
+
begin
|
|
166
|
+
update("SET FOREIGN_KEY_CHECKS = 0")
|
|
167
|
+
yield
|
|
168
|
+
ensure
|
|
169
|
+
update("SET FOREIGN_KEY_CHECKS = #{old}")
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
# SCHEMA STATEMENTS ========================================
|
|
174
|
+
|
|
175
|
+
def structure_dump #:nodoc:
|
|
176
|
+
if supports_views?
|
|
177
|
+
sql = "SHOW FULL TABLES WHERE Table_type = 'BASE TABLE'"
|
|
178
|
+
else
|
|
179
|
+
sql = "SHOW TABLES"
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
select_all(sql).inject("") do |structure, table|
|
|
183
|
+
table.delete('Table_type')
|
|
184
|
+
|
|
185
|
+
hash = show_create_table(table.to_a.first.last)
|
|
186
|
+
|
|
187
|
+
if(table = hash["Create Table"])
|
|
188
|
+
structure += table + ";\n\n"
|
|
189
|
+
elsif(view = hash["Create View"])
|
|
190
|
+
structure += view + ";\n\n"
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
# based on:
|
|
196
|
+
# https://github.com/rails/rails/blob/3-1-stable/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb#L756
|
|
197
|
+
# Required for passing rails column caching tests
|
|
198
|
+
# Returns a table's primary key and belonging sequence.
|
|
199
|
+
def pk_and_sequence_for(table) #:nodoc:
|
|
200
|
+
keys = []
|
|
201
|
+
result = execute("SHOW INDEX FROM #{quote_table_name(table)} WHERE Key_name = 'PRIMARY'", 'SCHEMA')
|
|
202
|
+
result.each do |h|
|
|
203
|
+
keys << h["Column_name"]
|
|
204
|
+
end
|
|
205
|
+
keys.length == 1 ? [keys.first, nil] : nil
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
# based on:
|
|
209
|
+
# https://github.com/rails/rails/blob/3-1-stable/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb#L647
|
|
210
|
+
# Returns an array of indexes for the given table.
|
|
211
|
+
def indexes(table_name, name = nil)#:nodoc:
|
|
212
|
+
indexes = []
|
|
213
|
+
current_index = nil
|
|
214
|
+
result = execute("SHOW KEYS FROM #{quote_table_name(table_name)}", name)
|
|
215
|
+
result.each do |row|
|
|
216
|
+
key_name = row["Key_name"]
|
|
217
|
+
if current_index != key_name
|
|
218
|
+
next if key_name == "PRIMARY" # skip the primary key
|
|
219
|
+
current_index = key_name
|
|
220
|
+
indexes << ::ActiveRecord::ConnectionAdapters::IndexDefinition.new(
|
|
221
|
+
row["Table"], key_name, row["Non_unique"] == 0, [], [])
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
indexes.last.columns << row["Column_name"]
|
|
225
|
+
indexes.last.lengths << row["Sub_part"]
|
|
226
|
+
end
|
|
227
|
+
indexes
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
def jdbc_columns(table_name, name = nil)#:nodoc:
|
|
231
|
+
sql = "SHOW FIELDS FROM #{quote_table_name(table_name)}"
|
|
232
|
+
execute(sql, 'SCHEMA').map do |field|
|
|
233
|
+
::ActiveRecord::ConnectionAdapters::MysqlColumn.new(field["Field"], field["Default"], field["Type"], field["Null"] == "YES")
|
|
234
|
+
end
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
# Returns just a table's primary key
|
|
238
|
+
def primary_key(table)
|
|
239
|
+
pk_and_sequence = pk_and_sequence_for(table)
|
|
240
|
+
pk_and_sequence && pk_and_sequence.first
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
def recreate_database(name, options = {}) #:nodoc:
|
|
244
|
+
drop_database(name)
|
|
245
|
+
create_database(name, options)
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
def create_database(name, options = {}) #:nodoc:
|
|
249
|
+
if options[:collation]
|
|
250
|
+
execute "CREATE DATABASE `#{name}` DEFAULT CHARACTER SET `#{options[:charset] || 'utf8'}` COLLATE `#{options[:collation]}`"
|
|
251
|
+
else
|
|
252
|
+
execute "CREATE DATABASE `#{name}` DEFAULT CHARACTER SET `#{options[:charset] || 'utf8'}`"
|
|
253
|
+
end
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
def drop_database(name) #:nodoc:
|
|
257
|
+
execute "DROP DATABASE IF EXISTS `#{name}`"
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
def current_database
|
|
261
|
+
select_one("SELECT DATABASE() as db")["db"]
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
def create_table(name, options = {}) #:nodoc:
|
|
265
|
+
super(name, {:options => "ENGINE=InnoDB DEFAULT CHARSET=utf8"}.merge(options))
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
def rename_table(name, new_name)
|
|
269
|
+
execute "RENAME TABLE #{quote_table_name(name)} TO #{quote_table_name(new_name)}"
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
def add_column(table_name, column_name, type, options = {})
|
|
273
|
+
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])}"
|
|
274
|
+
add_column_options!(add_column_sql, options)
|
|
275
|
+
add_column_position!(add_column_sql, options)
|
|
276
|
+
execute(add_column_sql)
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
def change_column_default(table_name, column_name, default) #:nodoc:
|
|
280
|
+
column = column_for(table_name, column_name)
|
|
281
|
+
change_column table_name, column_name, column.sql_type, :default => default
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
def change_column_null(table_name, column_name, null, default = nil)
|
|
285
|
+
column = column_for(table_name, column_name)
|
|
286
|
+
|
|
287
|
+
unless null || default.nil?
|
|
288
|
+
execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
change_column table_name, column_name, column.sql_type, :null => null
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
def change_column(table_name, column_name, type, options = {}) #:nodoc:
|
|
295
|
+
column = column_for(table_name, column_name)
|
|
296
|
+
|
|
297
|
+
unless options_include_default?(options)
|
|
298
|
+
options[:default] = column.default
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
unless options.has_key?(:null)
|
|
302
|
+
options[:null] = column.null
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
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])}"
|
|
306
|
+
add_column_options!(change_column_sql, options)
|
|
307
|
+
add_column_position!(change_column_sql, options)
|
|
308
|
+
execute(change_column_sql)
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
def rename_column(table_name, column_name, new_column_name) #:nodoc:
|
|
312
|
+
options = {}
|
|
313
|
+
if column = columns(table_name).find { |c| c.name == column_name.to_s }
|
|
314
|
+
options[:default] = column.default
|
|
315
|
+
options[:null] = column.null
|
|
316
|
+
else
|
|
317
|
+
raise ActiveRecord::ActiveRecordError, "No such column: #{table_name}.#{column_name}"
|
|
318
|
+
end
|
|
319
|
+
current_type = select_one("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE '#{column_name}'")["Type"]
|
|
320
|
+
rename_column_sql = "ALTER TABLE #{quote_table_name(table_name)} CHANGE #{quote_column_name(column_name)} #{quote_column_name(new_column_name)} #{current_type}"
|
|
321
|
+
add_column_options!(rename_column_sql, options)
|
|
322
|
+
execute(rename_column_sql)
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
def add_limit_offset!(sql, options) #:nodoc:
|
|
326
|
+
limit, offset = options[:limit], options[:offset]
|
|
327
|
+
if limit && offset
|
|
328
|
+
sql << " LIMIT #{offset.to_i}, #{sanitize_limit(limit)}"
|
|
329
|
+
elsif limit
|
|
330
|
+
sql << " LIMIT #{sanitize_limit(limit)}"
|
|
331
|
+
elsif offset
|
|
332
|
+
sql << " OFFSET #{offset.to_i}"
|
|
333
|
+
end
|
|
334
|
+
sql
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
# Taken from: https://github.com/gfmurphy/rails/blob/3-1-stable/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb#L540
|
|
338
|
+
#
|
|
339
|
+
# In the simple case, MySQL allows us to place JOINs directly into the UPDATE
|
|
340
|
+
# query. However, this does not allow for LIMIT, OFFSET and ORDER. To support
|
|
341
|
+
# these, we must use a subquery. However, MySQL is too stupid to create a
|
|
342
|
+
# temporary table for this automatically, so we have to give it some prompting
|
|
343
|
+
# in the form of a subsubquery. Ugh!
|
|
344
|
+
def join_to_update(update, select) #:nodoc:
|
|
345
|
+
if select.limit || select.offset || select.orders.any?
|
|
346
|
+
subsubselect = select.clone
|
|
347
|
+
subsubselect.projections = [update.key]
|
|
348
|
+
|
|
349
|
+
subselect = Arel::SelectManager.new(select.engine)
|
|
350
|
+
subselect.project Arel.sql(update.key.name)
|
|
351
|
+
subselect.from subsubselect.as('__active_record_temp')
|
|
352
|
+
|
|
353
|
+
update.where update.key.in(subselect)
|
|
354
|
+
else
|
|
355
|
+
update.table select.source
|
|
356
|
+
update.wheres = select.constraints
|
|
357
|
+
end
|
|
358
|
+
end
|
|
359
|
+
|
|
360
|
+
def show_variable(var)
|
|
361
|
+
res = execute("show variables like '#{var}'")
|
|
362
|
+
result_row = res.detect {|row| row["Variable_name"] == var }
|
|
363
|
+
result_row && result_row["Value"]
|
|
364
|
+
end
|
|
365
|
+
|
|
366
|
+
def charset
|
|
367
|
+
show_variable("character_set_database")
|
|
368
|
+
end
|
|
369
|
+
|
|
370
|
+
def collation
|
|
371
|
+
show_variable("collation_database")
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
def type_to_sql(type, limit = nil, precision = nil, scale = nil)
|
|
375
|
+
return super unless type.to_s == 'integer'
|
|
376
|
+
|
|
377
|
+
case limit
|
|
378
|
+
when 1; 'tinyint'
|
|
379
|
+
when 2; 'smallint'
|
|
380
|
+
when 3; 'mediumint'
|
|
381
|
+
when nil, 4, 11; 'int(11)' # compatibility with MySQL default
|
|
382
|
+
when 5..8; 'bigint'
|
|
383
|
+
else raise(ActiveRecordError, "No integer type has byte size #{limit}")
|
|
384
|
+
end
|
|
385
|
+
end
|
|
386
|
+
|
|
387
|
+
def add_column_position!(sql, options)
|
|
388
|
+
if options[:first]
|
|
389
|
+
sql << " FIRST"
|
|
390
|
+
elsif options[:after]
|
|
391
|
+
sql << " AFTER #{quote_column_name(options[:after])}"
|
|
392
|
+
end
|
|
393
|
+
end
|
|
394
|
+
|
|
395
|
+
protected
|
|
396
|
+
def quoted_columns_for_index(column_names, options = {})
|
|
397
|
+
length = options[:length] if options.is_a?(Hash)
|
|
398
|
+
|
|
399
|
+
case length
|
|
400
|
+
when Hash
|
|
401
|
+
column_names.map { |name| length[name] ? "#{quote_column_name(name)}(#{length[name]})" : quote_column_name(name) }
|
|
402
|
+
when Fixnum
|
|
403
|
+
column_names.map { |name| "#{quote_column_name(name)}(#{length})" }
|
|
404
|
+
else
|
|
405
|
+
column_names.map { |name| quote_column_name(name) }
|
|
406
|
+
end
|
|
407
|
+
end
|
|
408
|
+
|
|
409
|
+
def translate_exception(exception, message)
|
|
410
|
+
return super unless exception.respond_to?(:errno)
|
|
411
|
+
|
|
412
|
+
case exception.errno
|
|
413
|
+
when 1062
|
|
414
|
+
::ActiveRecord::RecordNotUnique.new(message, exception)
|
|
415
|
+
when 1452
|
|
416
|
+
::ActiveRecord::InvalidForeignKey.new(message, exception)
|
|
417
|
+
else
|
|
418
|
+
super
|
|
419
|
+
end
|
|
420
|
+
end
|
|
421
|
+
|
|
422
|
+
private
|
|
423
|
+
def column_for(table_name, column_name)
|
|
424
|
+
unless column = columns(table_name).find { |c| c.name == column_name.to_s }
|
|
425
|
+
raise "No such column: #{table_name}.#{column_name}"
|
|
426
|
+
end
|
|
427
|
+
column
|
|
428
|
+
end
|
|
429
|
+
|
|
430
|
+
def show_create_table(table)
|
|
431
|
+
select_one("SHOW CREATE TABLE #{quote_table_name(table)}")
|
|
432
|
+
end
|
|
433
|
+
|
|
434
|
+
def supports_views?
|
|
435
|
+
false
|
|
436
|
+
end
|
|
437
|
+
end
|
|
438
|
+
end
|
|
439
|
+
|
|
440
|
+
module ActiveRecord
|
|
441
|
+
module ConnectionAdapters
|
|
442
|
+
# Remove any vestiges of core/Ruby MySQL adapter
|
|
443
|
+
remove_const(:MysqlColumn) if const_defined?(:MysqlColumn)
|
|
444
|
+
remove_const(:MysqlAdapter) if const_defined?(:MysqlAdapter)
|
|
445
|
+
|
|
446
|
+
class MysqlColumn < JdbcColumn
|
|
447
|
+
include ArJdbc::MySQL::ColumnExtensions
|
|
448
|
+
|
|
449
|
+
def initialize(name, *args)
|
|
450
|
+
if Hash === name
|
|
451
|
+
super
|
|
452
|
+
else
|
|
453
|
+
super(nil, name, *args)
|
|
454
|
+
end
|
|
455
|
+
end
|
|
456
|
+
|
|
457
|
+
def call_discovered_column_callbacks(*)
|
|
458
|
+
end
|
|
459
|
+
end
|
|
460
|
+
|
|
461
|
+
class MysqlAdapter < JdbcAdapter
|
|
462
|
+
include ArJdbc::MySQL
|
|
463
|
+
|
|
464
|
+
def initialize(*args)
|
|
465
|
+
super
|
|
466
|
+
configure_connection
|
|
467
|
+
end
|
|
468
|
+
|
|
469
|
+
def jdbc_connection_class(spec)
|
|
470
|
+
::ArJdbc::MySQL.jdbc_connection_class
|
|
471
|
+
end
|
|
472
|
+
|
|
473
|
+
def jdbc_column_class
|
|
474
|
+
ActiveRecord::ConnectionAdapters::MysqlColumn
|
|
475
|
+
end
|
|
476
|
+
|
|
477
|
+
alias_chained_method :columns, :query_cache, :jdbc_columns
|
|
478
|
+
|
|
479
|
+
protected
|
|
480
|
+
def exec_insert(sql, name, binds)
|
|
481
|
+
binds = binds.dup
|
|
482
|
+
|
|
483
|
+
# Pretend to support bind parameters
|
|
484
|
+
unless binds.empty?
|
|
485
|
+
sql = sql.gsub('?') { quote(*binds.shift.reverse) }
|
|
486
|
+
end
|
|
487
|
+
execute sql, name
|
|
488
|
+
end
|
|
489
|
+
alias :exec_update :exec_insert
|
|
490
|
+
alias :exec_delete :exec_insert
|
|
491
|
+
|
|
492
|
+
end
|
|
493
|
+
end
|
|
494
|
+
end
|
|
495
|
+
|
|
496
|
+
module Mysql # :nodoc:
|
|
497
|
+
remove_const(:Error) if const_defined?(:Error)
|
|
498
|
+
|
|
499
|
+
class Error < ::ActiveRecord::JDBCError
|
|
500
|
+
end
|
|
501
|
+
|
|
502
|
+
def self.client_version
|
|
503
|
+
50400 # faked out for AR tests
|
|
504
|
+
end
|
|
505
|
+
end
|