activerecord-jdbc-adapter 1.2.9.1 → 1.3.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +3 -0
- data/Appraisals +12 -4
- data/Gemfile +3 -3
- data/Gemfile.lock +19 -19
- data/History.txt +90 -16
- data/LICENSE.txt +2 -1
- data/README.md +14 -1
- data/activerecord-jdbc-adapter.gemspec +2 -2
- data/gemfiles/rails23.gemfile +5 -5
- data/gemfiles/rails23.gemfile.lock +27 -27
- data/gemfiles/rails30.gemfile +3 -3
- data/gemfiles/rails30.gemfile.lock +8 -8
- data/gemfiles/rails31.gemfile +4 -4
- data/gemfiles/rails31.gemfile.lock +18 -18
- data/gemfiles/rails32.gemfile +4 -4
- data/gemfiles/rails32.gemfile.lock +17 -17
- data/gemfiles/rails40.gemfile +17 -0
- data/gemfiles/rails40.gemfile.lock +126 -0
- data/lib/activerecord-jdbc-adapter.rb +0 -7
- data/lib/arjdbc.rb +6 -5
- data/lib/arjdbc/db2.rb +1 -1
- data/lib/arjdbc/db2/adapter.rb +52 -29
- data/lib/arjdbc/db2/connection_methods.rb +13 -14
- data/lib/arjdbc/derby.rb +1 -1
- data/lib/arjdbc/derby/adapter.rb +29 -9
- data/lib/arjdbc/derby/connection_methods.rb +17 -20
- data/lib/arjdbc/firebird.rb +1 -1
- data/lib/arjdbc/h2.rb +2 -2
- data/lib/arjdbc/h2/adapter.rb +1 -1
- data/lib/arjdbc/h2/connection_methods.rb +12 -16
- data/lib/arjdbc/hsqldb.rb +1 -1
- data/lib/arjdbc/hsqldb/connection_methods.rb +13 -16
- data/lib/arjdbc/informix.rb +1 -1
- data/lib/arjdbc/informix/connection_methods.rb +8 -10
- data/lib/arjdbc/jdbc.rb +1 -1
- data/lib/arjdbc/jdbc/adapter.rb +125 -53
- data/lib/arjdbc/jdbc/adapter_java.jar +0 -0
- data/lib/arjdbc/jdbc/base_ext.rb +34 -9
- data/lib/arjdbc/jdbc/column.rb +15 -2
- data/lib/arjdbc/jdbc/connection.rb +0 -2
- data/lib/arjdbc/jdbc/connection_methods.rb +10 -3
- data/lib/arjdbc/jdbc/driver.rb +2 -2
- data/lib/arjdbc/jdbc/extension.rb +35 -21
- data/lib/arjdbc/jdbc/java.rb +0 -2
- data/lib/arjdbc/jdbc/missing_functionality_helper.rb +35 -25
- data/lib/arjdbc/jdbc/railtie.rb +2 -9
- data/lib/arjdbc/mimer.rb +1 -1
- data/lib/arjdbc/mssql.rb +2 -2
- data/lib/arjdbc/mssql/adapter.rb +271 -92
- data/lib/arjdbc/mssql/connection_methods.rb +30 -32
- data/lib/arjdbc/mssql/explain_support.rb +107 -0
- data/lib/arjdbc/mssql/limit_helpers.rb +48 -18
- data/lib/arjdbc/mysql.rb +1 -1
- data/lib/arjdbc/mysql/adapter.rb +63 -14
- data/lib/arjdbc/mysql/connection_methods.rb +22 -24
- data/lib/arjdbc/mysql/explain_support.rb +2 -5
- data/lib/arjdbc/oracle.rb +1 -1
- data/lib/arjdbc/oracle/adapter.rb +78 -38
- data/lib/arjdbc/oracle/connection_methods.rb +9 -10
- data/lib/arjdbc/postgresql.rb +1 -1
- data/lib/arjdbc/postgresql/adapter.rb +964 -380
- data/lib/arjdbc/postgresql/column_cast.rb +136 -0
- data/lib/arjdbc/postgresql/connection_methods.rb +19 -21
- data/lib/arjdbc/postgresql/explain_support.rb +3 -6
- data/lib/arjdbc/railtie.rb +9 -0
- data/lib/arjdbc/sqlite3.rb +1 -1
- data/lib/arjdbc/sqlite3/adapter.rb +73 -26
- data/lib/arjdbc/sqlite3/connection_methods.rb +27 -28
- data/lib/arjdbc/sqlite3/explain_support.rb +2 -5
- data/lib/arjdbc/sybase.rb +1 -1
- data/lib/arjdbc/version.rb +5 -4
- data/pom.xml +8 -0
- data/rakelib/02-test.rake +57 -51
- data/rakelib/compile.rake +17 -5
- data/rakelib/rails.rake +42 -31
- data/src/java/arjdbc/db2/DB2RubyJdbcConnection.java +4 -3
- data/src/java/arjdbc/derby/DerbyModule.java +98 -85
- data/src/java/arjdbc/derby/DerbyRubyJdbcConnection.java +70 -0
- data/src/java/arjdbc/h2/H2RubyJdbcConnection.java +0 -4
- data/src/java/arjdbc/jdbc/AdapterJavaService.java +26 -15
- data/src/java/arjdbc/jdbc/Callable.java +44 -0
- data/src/java/arjdbc/jdbc/JdbcConnectionFactory.java +10 -2
- data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +1675 -834
- data/src/java/arjdbc/jdbc/SQLBlock.java +9 -3
- data/src/java/arjdbc/mssql/MSSQLRubyJdbcConnection.java +73 -36
- data/src/java/arjdbc/mysql/MySQLModule.java +11 -10
- data/src/java/arjdbc/mysql/MySQLRubyJdbcConnection.java +86 -80
- data/src/java/arjdbc/oracle/OracleRubyJdbcConnection.java +27 -7
- data/src/java/arjdbc/postgresql/PostgreSQLRubyJdbcConnection.java +214 -0
- data/src/java/arjdbc/postgresql/PostgresqlRubyJdbcConnection.java +25 -67
- data/src/java/arjdbc/sqlite3/SQLite3RubyJdbcConnection.java +52 -49
- data/src/java/arjdbc/util/QuotingUtils.java +6 -6
- data/test/abstract_db_create.rb +11 -11
- data/test/activerecord/connection_adapters/type_conversion_test.rb +18 -12
- data/test/db/db2.rb +1 -1
- data/test/{db2_binary_test.rb → db/db2/binary_test.rb} +0 -0
- data/test/db/db2/has_many_through_test.rb +6 -0
- data/test/{db2_reset_column_information_test.rb → db/db2/reset_column_information_test.rb} +1 -2
- data/test/{db2_serialize_test.rb → db/db2/serialize_test.rb} +0 -0
- data/test/{db2_simple_test.rb → db/db2/simple_test.rb} +1 -8
- data/test/db/db2/test_helper.rb +6 -0
- data/test/{db2_test.rb → db/db2/unit_test.rb} +1 -1
- data/test/db/derby.rb +1 -1
- data/test/{derby_binary_test.rb → db/derby/binary_test.rb} +0 -0
- data/test/{derby_migration_test.rb → db/derby/migration_test.rb} +0 -0
- data/test/{derby_reset_column_information_test.rb → db/derby/reset_column_information_test.rb} +0 -0
- data/test/{derby_row_locking_test.rb → db/derby/row_locking_test.rb} +1 -4
- data/test/db/derby/schema_dump_test.rb +5 -0
- data/test/{derby_serialize_test.rb → db/derby/serialize_test.rb} +0 -0
- data/test/{derby_simple_test.rb → db/derby/simple_test.rb} +23 -38
- data/test/db/derby/test_helper.rb +6 -0
- data/test/db/derby/unit_test.rb +32 -0
- data/test/db/derby/xml_column_test.rb +17 -0
- data/test/db/h2.rb +1 -1
- data/test/{h2_binary_test.rb → db/h2/binary_test.rb} +0 -0
- data/test/{h2_change_column_test.rb → db/h2/change_column_test.rb} +1 -0
- data/test/{h2_schema_dump_test.rb → db/h2/schema_dump_test.rb} +0 -0
- data/test/{h2_serialize_test.rb → db/h2/serialize_test.rb} +0 -0
- data/test/{h2_simple_test.rb → db/h2/simple_test.rb} +3 -1
- data/test/db/hsqldb.rb +1 -1
- data/test/{hsqldb_binary_test.rb → db/hsqldb/binary_test.rb} +0 -0
- data/test/{hsqldb_schema_dump_test.rb → db/hsqldb/schema_dump_test.rb} +0 -0
- data/test/{hsqldb_serialize_test.rb → db/hsqldb/serialize_test.rb} +0 -0
- data/test/{hsqldb_simple_test.rb → db/hsqldb/simple_test.rb} +3 -1
- data/test/db/informix.rb +1 -1
- data/test/db/jdbc.rb +3 -2
- data/test/db/jdbc_derby.rb +1 -1
- data/test/db/jdbc_h2.rb +1 -1
- data/test/db/jdbc_mysql.rb +1 -1
- data/test/db/jdbc_postgres.rb +1 -1
- data/test/db/jndi_config.rb +1 -2
- data/test/db/jndi_pooled_config.rb +2 -3
- data/test/db/mssql.rb +2 -2
- data/test/{mssql_binary_test.rb → db/mssql/binary_test.rb} +0 -0
- data/test/{mssql_db_create_test.rb → db/mssql/db_create_test.rb} +1 -1
- data/test/db/mssql/exec_proc_test.rb +46 -0
- data/test/{mssql_identity_insert_test.rb → db/mssql/identity_insert_test.rb} +0 -0
- data/test/db/mssql/ignore_system_views_test.rb +40 -0
- data/test/{mssql_limit_offset_test.rb → db/mssql/limit_offset_test.rb} +10 -1
- data/test/{mssql_multibyte_test.rb → db/mssql/multibyte_test.rb} +0 -0
- data/test/db/mssql/multiple_connections_test.rb +71 -0
- data/test/{mssql_reset_column_information_test.rb → db/mssql/reset_column_information_test.rb} +0 -0
- data/test/{mssql_row_locking_test.rb → db/mssql/row_locking_test.rb} +0 -0
- data/test/{mssql_serialize_test.rb → db/mssql/serialize_test.rb} +1 -1
- data/test/db/mssql/simple_test.rb +140 -0
- data/test/db/mssql/transaction_test.rb +6 -0
- data/test/db/mssql/types_test.rb +205 -0
- data/test/{mssql_test.rb → db/mssql/unit_test.rb} +2 -2
- data/test/db/mysql.rb +1 -2
- data/test/db/mysql/_rails_test_mysql.32.out +6768 -0
- data/test/{mysql_binary_test.rb → db/mysql/binary_test.rb} +0 -0
- data/test/db/mysql/connection_test.rb +51 -0
- data/test/{mysql_db_create_test.rb → db/mysql/db_create_test.rb} +0 -0
- data/test/{mysql_index_length_test.rb → db/mysql/index_length_test.rb} +0 -0
- data/test/{mysql_multibyte_test.rb → db/mysql/multibyte_test.rb} +0 -0
- data/test/{mysql_nonstandard_primary_key_test.rb → db/mysql/nonstandard_primary_key_test.rb} +0 -0
- data/test/{mysql_reset_column_information_test.rb → db/mysql/reset_column_information_test.rb} +0 -0
- data/test/{mysql_schema_dump_test.rb → db/mysql/schema_dump_test.rb} +9 -1
- data/test/{mysql_serialize_test.rb → db/mysql/serialize_test.rb} +0 -0
- data/test/{mysql_simple_test.rb → db/mysql/simple_test.rb} +16 -8
- data/test/db/mysql/transaction_test.rb +6 -0
- data/test/db/mysql/types_test.rb +30 -0
- data/test/{mysql_test.rb → db/mysql/unit_test.rb} +1 -1
- data/test/db/mysql_config.rb +1 -1
- data/test/db/oracle.rb +1 -1
- data/test/{oracle_binary_test.rb → db/oracle/binary_test.rb} +0 -0
- data/test/{oracle_limit_test.rb → db/oracle/limit_test.rb} +0 -0
- data/test/db/oracle/multibyte_test.rb +22 -0
- data/test/{oracle_reset_column_information_test.rb → db/oracle/reset_column_information_test.rb} +0 -0
- data/test/{oracle_serialize_test.rb → db/oracle/serialize_test.rb} +0 -0
- data/test/{oracle_simple_test.rb → db/oracle/simple_test.rb} +14 -19
- data/test/{oracle_specific_test.rb → db/oracle/specific_test.rb} +62 -16
- data/test/db/oracle/transaction_test.rb +31 -0
- data/test/db/oracle/unit_test.rb +31 -0
- data/test/db/postgres.rb +1 -1
- data/test/db/postgres/_rails_test_postgres.32.out +6777 -0
- data/test/db/postgres/a_custom_primary_key_test.rb +50 -0
- data/test/db/postgres/array_type_test.rb +101 -0
- data/test/{postgres_binary_test.rb → db/postgres/binary_test.rb} +0 -0
- data/test/db/postgres/connection_test.rb +55 -0
- data/test/db/postgres/data_types_test.rb +703 -0
- data/test/{postgres_db_create_test.rb → db/postgres/db_create_test.rb} +1 -1
- data/test/{postgres_drop_db_test.rb → db/postgres/db_drop_test.rb} +2 -0
- data/test/db/postgres/hstore_test.rb +200 -0
- data/test/db/postgres/information_schema_leak_test.rb +30 -0
- data/test/db/postgres/json_test.rb +86 -0
- data/test/db/postgres/ltree_test.rb +50 -0
- data/test/{postgres_mixed_case_test.rb → db/postgres/mixed_case_test.rb} +0 -0
- data/test/db/postgres/native_types_test.rb +128 -0
- data/test/{postgres_reserved_test.rb → db/postgres/reserved_test.rb} +0 -0
- data/test/{postgres_reset_column_information_test.rb → db/postgres/reset_column_information_test.rb} +0 -0
- data/test/{postgres_row_locking_test.rb → db/postgres/row_locking_test.rb} +0 -0
- data/test/{postgres_schema_dump_test.rb → db/postgres/schema_dump_test.rb} +4 -4
- data/test/db/postgres/schema_test.rb +113 -0
- data/test/{postgres_simple_test.rb → db/postgres/simple_test.rb} +48 -8
- data/test/{postgres_table_alias_length_test.rb → db/postgres/table_alias_length_test.rb} +2 -1
- data/test/db/postgres/transaction_test.rb +6 -0
- data/test/{postgres_test.rb → db/postgres/unit_test.rb} +3 -3
- data/test/db/sqlite3.rb +1 -1
- data/test/db/sqlite3/_rails_test_sqlite3.32.out +6502 -0
- data/test/db/sqlite3/has_many_though_test.rb +6 -0
- data/test/{sqlite3_reset_column_information_test.rb → db/sqlite3/reset_column_information_test.rb} +0 -0
- data/test/{sqlite3_schema_dump_test.rb → db/sqlite3/schema_dump_test.rb} +0 -0
- data/test/{sqlite3_serialize_test.rb → db/sqlite3/serialize_test.rb} +0 -0
- data/test/{sqlite3_simple_test.rb → db/sqlite3/simple_test.rb} +63 -63
- data/test/db/sqlite3/transaction_test.rb +32 -0
- data/test/{sqlite3_type_conversion_test.rb → db/sqlite3/type_conversion_test.rb} +0 -0
- data/test/has_many_through.rb +29 -64
- data/test/jdbc/oracle.rb +11 -0
- data/test/jndi_test.rb +16 -4
- data/test/models/auto_id.rb +1 -1
- data/test/models/rights_and_roles.rb +57 -0
- data/test/row_locking.rb +3 -0
- data/test/schema_dump.rb +24 -10
- data/test/simple.rb +359 -104
- data/test/test_helper.rb +4 -2
- data/test/transaction.rb +109 -0
- metadata +119 -86
- data/lib/arjdbc/jdbc/compatibility.rb +0 -51
- data/lib/arjdbc/jdbc/core_ext.rb +0 -24
- data/lib/arjdbc/jdbc/discover.rb +0 -18
- data/test/derby_schema_dump_test.rb +0 -9
- data/test/mssql_ignore_system_views_test.rb +0 -30
- data/test/mssql_legacy_types_test.rb +0 -58
- data/test/mssql_null_test.rb +0 -14
- data/test/mssql_simple_test.rb +0 -51
- data/test/postgres_information_schema_leak_test.rb +0 -28
- data/test/postgres_native_type_mapping_test.rb +0 -93
- data/test/postgres_nonseq_pkey_test.rb +0 -38
- data/test/postgres_schema_search_path_test.rb +0 -48
- data/test/postgres_type_conversion_test.rb +0 -33
Binary file
|
data/lib/arjdbc/jdbc/base_ext.rb
CHANGED
@@ -1,15 +1,40 @@
|
|
1
1
|
module ActiveRecord
|
2
|
-
|
2
|
+
# ActiveRecord::Base extensions.
|
3
|
+
Base.class_eval do
|
3
4
|
class << self
|
4
|
-
# Allow adapters to provide their own reset_column_information
|
5
|
-
#
|
6
|
-
|
7
|
-
|
8
|
-
# Invoke the adapter-specific reset_column_information method
|
5
|
+
# Allow adapters to provide their own {#reset_column_information} method.
|
6
|
+
# @note This only affects the current thread's connection.
|
7
|
+
def reset_column_information_with_arjdbc
|
8
|
+
# invoke the adapter-specific reset_column_information method
|
9
9
|
connection.reset_column_information if connection.respond_to?(:reset_column_information)
|
10
|
-
|
10
|
+
reset_column_information_without_arjdbc
|
11
|
+
end
|
12
|
+
unless method_defined?("reset_column_information_without_arjdbc")
|
13
|
+
alias_method_chain :reset_column_information, :arjdbc
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# Represents exceptions that have propagated up through the JDBC API.
|
19
|
+
class JDBCError < ActiveRecordError
|
20
|
+
# The vendor code or error number that came from the database
|
21
|
+
attr_accessor :errno
|
22
|
+
# The full Java SQLException object that was raised.
|
23
|
+
attr_accessor :sql_exception
|
24
|
+
end
|
25
|
+
|
26
|
+
module ConnectionAdapters # :nodoc:
|
27
|
+
# Allows properly re-defining methods that may already be alias_method_chain'd.
|
28
|
+
module ShadowCoreMethods
|
29
|
+
def alias_chained_method(name, feature, target)
|
30
|
+
# NOTE: aliasing for things such as columns (with feature query_cache)
|
31
|
+
# seems to only be used for 2.3 since 3.0 methods are not aliased ...
|
32
|
+
if method_defined?(method = "#{name}_without_#{feature}")
|
33
|
+
alias_method method, target
|
34
|
+
else
|
35
|
+
alias_method name, target if name.to_s != target.to_s
|
36
|
+
end
|
11
37
|
end
|
12
|
-
alias_method_chain :reset_column_information, :arjdbc_base_ext unless instance_methods.include?("reset_column_information_without_arjdbc_base_ext")
|
13
38
|
end
|
14
39
|
end
|
15
|
-
end
|
40
|
+
end
|
data/lib/arjdbc/jdbc/column.rb
CHANGED
@@ -3,14 +3,27 @@ module ActiveRecord
|
|
3
3
|
class JdbcColumn < Column
|
4
4
|
attr_writer :limit, :precision
|
5
5
|
|
6
|
-
def initialize(config, name,
|
7
|
-
|
6
|
+
def initialize(config, name, *args)
|
7
|
+
if self.class == JdbcColumn
|
8
|
+
# NOTE: extending classes do not want this if they do they shall call
|
9
|
+
call_discovered_column_callbacks(config) if config
|
10
|
+
default = args.shift
|
11
|
+
else # for extending classes allow ignoring first argument :
|
12
|
+
if ! config.nil? && ! config.is_a?(Hash)
|
13
|
+
# initialize(name, default, *args)
|
14
|
+
default = name; name = config
|
15
|
+
else
|
16
|
+
default = args.shift
|
17
|
+
end
|
18
|
+
end
|
19
|
+
# super : (name, default, sql_type = nil, null = true)
|
8
20
|
super(name, default_value(default), *args)
|
9
21
|
init_column(name, default, *args)
|
10
22
|
end
|
11
23
|
|
12
24
|
def init_column(*args); end
|
13
25
|
|
26
|
+
# NOTE: our custom #extract_value_from_default(default)
|
14
27
|
def default_value(value); value; end
|
15
28
|
|
16
29
|
protected
|
@@ -20,8 +20,6 @@ module ActiveRecord
|
|
20
20
|
connection # force the connection to load (@see RubyJDbcConnection.connection)
|
21
21
|
set_native_database_types
|
22
22
|
@stmts = {} # AR compatibility - statement cache not used
|
23
|
-
rescue ::ActiveRecord::ActiveRecordError
|
24
|
-
raise
|
25
23
|
rescue Java::JavaSql::SQLException => e
|
26
24
|
e = e.cause if defined?(NativeException) && e.is_a?(NativeException) # JRuby-1.6.8
|
27
25
|
error = e.getMessage || e.getSQLState
|
@@ -1,5 +1,11 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
module ArJdbc
|
2
|
+
if defined? ActiveRecord::ConnectionHandling # 4.0
|
3
|
+
ConnectionMethods = ActiveRecord::ConnectionHandling
|
4
|
+
else # 3.x
|
5
|
+
ConnectionMethods = (class << ActiveRecord::Base; self; end)
|
6
|
+
end
|
7
|
+
ConnectionMethods.module_eval do
|
8
|
+
|
3
9
|
def jdbc_connection(config)
|
4
10
|
adapter_class = config[:adapter_class]
|
5
11
|
adapter_class ||= ::ActiveRecord::ConnectionAdapters::JdbcAdapter
|
@@ -12,5 +18,6 @@ class ActiveRecord::Base
|
|
12
18
|
config[:password] ||= ""
|
13
19
|
jdbc_connection(config)
|
14
20
|
end
|
21
|
+
|
15
22
|
end
|
16
|
-
end
|
23
|
+
end
|
data/lib/arjdbc/jdbc/driver.rb
CHANGED
@@ -13,11 +13,11 @@ module ActiveRecord
|
|
13
13
|
properties.each { |key, val| @properties[key] = val.to_s } if properties
|
14
14
|
end
|
15
15
|
end
|
16
|
-
|
16
|
+
|
17
17
|
def driver_class
|
18
18
|
@driver_class ||= begin
|
19
19
|
driver_class_const = (@name[0...1].capitalize + @name[1..@name.length]).gsub(/\./, '_')
|
20
|
-
Jdbc::
|
20
|
+
Jdbc::DriverManager.java_class.synchronized do # avoid 2 threads here
|
21
21
|
unless Jdbc.const_defined?(driver_class_const)
|
22
22
|
driver_class_name = @name
|
23
23
|
Jdbc.module_eval do
|
@@ -1,31 +1,28 @@
|
|
1
1
|
module ArJdbc
|
2
|
-
|
3
|
-
#
|
4
|
-
#
|
5
|
-
# ActiveRecord::ConnectionAdapters::AbstractAdapter.
|
6
|
-
# declare your extension, you provide a block that detects when a
|
7
|
-
# database configured to use the extension is present and loads the
|
8
|
-
#
|
9
|
-
#
|
10
|
-
# instance of it with your extension module.
|
2
|
+
|
3
|
+
# Defines an AR-JDBC extension. An extension consists of a declaration using
|
4
|
+
# this method and an ArJdbc::XYZ module that contains implementation and
|
5
|
+
# overrides for methods in ActiveRecord::ConnectionAdapters::AbstractAdapter.
|
6
|
+
# When you declare your extension, you provide a block that detects when a
|
7
|
+
# database configured to use the extension is present and loads the necessary
|
8
|
+
# code for it. AR-JDBC will patch the code into the base JdbcAdapter by
|
9
|
+
# extending an instance of it with your extension module.
|
11
10
|
#
|
12
|
-
# +name+
|
13
|
-
# defined under the +ArJdbc+ module.
|
11
|
+
# +name+ the name of a module to be defined under the +ArJdbc+ module.
|
14
12
|
#
|
15
|
-
# +block+ should be a one- or two-arity block that receives the
|
16
|
-
#
|
17
|
-
#
|
18
|
-
# argument.
|
13
|
+
# +block+ should be a one- or two-arity block that receives the dialect name
|
14
|
+
# or driver class name as the first argument, and optionally the whole
|
15
|
+
# database configuration hash as a second argument
|
19
16
|
#
|
20
17
|
# Example:
|
21
18
|
#
|
22
|
-
#
|
23
|
-
#
|
24
|
-
#
|
25
|
-
#
|
26
|
-
# true
|
27
|
-
# end
|
19
|
+
# ArJdbc.extension :FRoB do |name|
|
20
|
+
# if name =~ /frob/i
|
21
|
+
# require 'arjdbc/frob' # contains ArJdbc::FRoB
|
22
|
+
# true
|
28
23
|
# end
|
24
|
+
# end
|
25
|
+
#
|
29
26
|
def self.extension(name, &block)
|
30
27
|
if const_defined?(name)
|
31
28
|
mod = const_get(name)
|
@@ -44,4 +41,21 @@ module ArJdbc
|
|
44
41
|
end
|
45
42
|
end
|
46
43
|
end
|
44
|
+
|
45
|
+
private
|
46
|
+
def self.discover_extensions
|
47
|
+
if defined?(::Gem) && ::Gem.respond_to?(:find_files)
|
48
|
+
files = ::Gem.find_files('arjdbc/discover')
|
49
|
+
else
|
50
|
+
files = $LOAD_PATH.map do |p|
|
51
|
+
discover = File.join(p, 'arjdbc', 'discover.rb')
|
52
|
+
File.exist?(discover) ? discover : nil
|
53
|
+
end.compact
|
54
|
+
end
|
55
|
+
files.each do |file|
|
56
|
+
puts "Loading AR-JDBC extension #{file}" if $DEBUG
|
57
|
+
require file
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
47
61
|
end
|
data/lib/arjdbc/jdbc/java.rb
CHANGED
@@ -4,11 +4,9 @@ require 'arjdbc/jdbc/adapter_java'
|
|
4
4
|
module ActiveRecord
|
5
5
|
module ConnectionAdapters
|
6
6
|
module Jdbc
|
7
|
-
Mutex = java.lang.Object.new
|
8
7
|
DriverManager = java.sql.DriverManager
|
9
8
|
Types = java.sql.Types
|
10
9
|
end
|
11
|
-
|
12
10
|
java_import "arjdbc.jdbc.JdbcConnectionFactory"
|
13
11
|
end
|
14
12
|
end
|
@@ -1,42 +1,44 @@
|
|
1
1
|
module ArJdbc
|
2
2
|
module MissingFunctionalityHelper
|
3
|
-
|
3
|
+
|
4
|
+
# taken from SQLite adapter, code loosely based on http://git.io/P7tFQA
|
4
5
|
|
5
6
|
def alter_table(table_name, options = {}) #:nodoc:
|
6
7
|
table_name = table_name.to_s.downcase
|
7
|
-
altered_table_name = "
|
8
|
-
caller = lambda {|definition| yield definition if block_given?}
|
8
|
+
altered_table_name = "a#{table_name}"
|
9
|
+
caller = lambda { |definition| yield definition if block_given? }
|
9
10
|
|
10
11
|
transaction do
|
11
12
|
# A temporary table might improve performance here, but
|
12
13
|
# it doesn't seem to maintain indices across the whole move.
|
13
|
-
move_table(table_name, altered_table_name,
|
14
|
-
options)
|
14
|
+
move_table(table_name, altered_table_name, options)
|
15
15
|
move_table(altered_table_name, table_name, &caller)
|
16
16
|
end
|
17
17
|
end
|
18
|
-
|
18
|
+
|
19
19
|
def move_table(from, to, options = {}, &block) #:nodoc:
|
20
20
|
copy_table(from, to, options, &block)
|
21
21
|
drop_table(from)
|
22
22
|
end
|
23
23
|
|
24
|
-
def copy_table(from, to, options = {})
|
25
|
-
|
26
|
-
create_table(to, options) do |definition|
|
24
|
+
def copy_table(from, to, options = {}) # :nodoc:
|
25
|
+
from_primary_key = primary_key(from)
|
26
|
+
create_table(to, options.merge(:id => false)) do |definition|
|
27
27
|
@definition = definition
|
28
|
+
@definition.primary_key(from_primary_key) if from_primary_key.present?
|
28
29
|
columns(from).each do |column|
|
29
30
|
column_name = options[:rename] ?
|
30
31
|
(options[:rename][column.name] ||
|
31
32
|
options[:rename][column.name.to_sym] ||
|
32
33
|
column.name) : column.name
|
33
|
-
|
34
|
+
|
35
|
+
next if column_name == from_primary_key
|
36
|
+
|
34
37
|
@definition.column(column_name, column.type,
|
35
38
|
:limit => column.limit, :default => column.default,
|
36
39
|
:precision => column.precision, :scale => column.scale,
|
37
40
|
:null => column.null)
|
38
41
|
end
|
39
|
-
@definition.primary_key(primary_key(from)) if primary_key(from)
|
40
42
|
yield @definition if block_given?
|
41
43
|
end
|
42
44
|
|
@@ -49,20 +51,19 @@ module ArJdbc
|
|
49
51
|
def copy_table_indexes(from, to, rename = {}) #:nodoc:
|
50
52
|
indexes(from).each do |index|
|
51
53
|
name = index.name.downcase
|
52
|
-
if to == "
|
53
|
-
name = "
|
54
|
-
elsif from == "
|
55
|
-
name = name[
|
54
|
+
if to == "a#{from}"
|
55
|
+
name = "t#{name}"
|
56
|
+
elsif from == "a#{to}"
|
57
|
+
name = name[1..-1]
|
56
58
|
end
|
57
59
|
|
58
60
|
to_column_names = columns(to).map(&:name)
|
59
|
-
columns = index.columns.map {|
|
60
|
-
|
61
|
-
end
|
61
|
+
columns = index.columns.map { |column| rename[column] || column }
|
62
|
+
columns = columns.select { |column| to_column_names.include?(column) }
|
62
63
|
|
63
64
|
unless columns.empty?
|
64
65
|
# index name can't be the same
|
65
|
-
opts = { :name => name.gsub(/(_
|
66
|
+
opts = { :name => name.gsub(/(^|_)(#{from})_/, "\\1#{to}_"), :internal => true }
|
66
67
|
opts[:unique] = true if index.unique
|
67
68
|
add_index(to, columns, opts)
|
68
69
|
end
|
@@ -70,19 +71,28 @@ module ArJdbc
|
|
70
71
|
end
|
71
72
|
|
72
73
|
def copy_table_contents(from, to, columns, rename = {}) #:nodoc:
|
73
|
-
column_mappings = Hash[
|
74
|
-
rename.
|
74
|
+
column_mappings = Hash[ columns.map { |name| [name, name] } ]
|
75
|
+
rename.each { |a| column_mappings[a.last] = a.first }
|
75
76
|
from_columns = columns(from).collect {|col| col.name}
|
76
|
-
columns = columns.find_all{|col| from_columns.include?(column_mappings[col])}
|
77
|
+
columns = columns.find_all{ |col| from_columns.include?(column_mappings[col]) }
|
77
78
|
quoted_columns = columns.map { |col| quote_column_name(col) } * ','
|
78
79
|
|
79
80
|
quoted_to = quote_table_name(to)
|
80
|
-
|
81
|
+
|
82
|
+
raw_column_mappings = Hash[ columns(from).map { |c| [c.name, c] } ]
|
83
|
+
|
84
|
+
execute("SELECT * FROM #{quote_table_name(from)}", 'Copy Table').each do |row|
|
81
85
|
sql = "INSERT INTO #{quoted_to} (#{quoted_columns}) VALUES ("
|
82
|
-
|
86
|
+
|
87
|
+
column_values = columns.map do |col|
|
88
|
+
quote(row[column_mappings[col]], raw_column_mappings[col])
|
89
|
+
end
|
90
|
+
|
91
|
+
sql << column_values * ', '
|
83
92
|
sql << ')'
|
84
|
-
|
93
|
+
exec_insert sql, 'Copy Table', []
|
85
94
|
end
|
86
95
|
end
|
96
|
+
|
87
97
|
end
|
88
98
|
end
|
data/lib/arjdbc/jdbc/railtie.rb
CHANGED
@@ -1,9 +1,2 @@
|
|
1
|
-
require '
|
2
|
-
|
3
|
-
module ::ArJdbc
|
4
|
-
class Railtie < ::Rails::Railtie
|
5
|
-
rake_tasks do
|
6
|
-
load File.expand_path('../rake_tasks.rb', __FILE__)
|
7
|
-
end
|
8
|
-
end
|
9
|
-
end
|
1
|
+
warn "DEPRECATED: require 'arjdbc/railtie' instead of 'arjdbc/jdbc/railtie' "
|
2
|
+
require 'arjdbc/railtie'
|
data/lib/arjdbc/mimer.rb
CHANGED
@@ -1,2 +1,2 @@
|
|
1
|
-
require 'arjdbc
|
1
|
+
require 'arjdbc'
|
2
2
|
require 'arjdbc/mimer/adapter'
|
data/lib/arjdbc/mssql.rb
CHANGED
data/lib/arjdbc/mssql/adapter.rb
CHANGED
@@ -3,12 +3,15 @@ require 'arjdbc/mssql/utils'
|
|
3
3
|
require 'arjdbc/mssql/tsql_methods'
|
4
4
|
require 'arjdbc/mssql/limit_helpers'
|
5
5
|
require 'arjdbc/mssql/lock_helpers'
|
6
|
+
require 'arjdbc/mssql/explain_support'
|
6
7
|
require 'arjdbc/jdbc/serialized_attributes_helper'
|
7
8
|
|
8
9
|
module ArJdbc
|
9
10
|
module MSSQL
|
10
11
|
include Utils
|
11
12
|
include TSqlMethods
|
13
|
+
|
14
|
+
include ExplainSupport
|
12
15
|
|
13
16
|
@@_lob_callback_added = nil
|
14
17
|
|
@@ -39,8 +42,13 @@ module ArJdbc
|
|
39
42
|
extend LimitHelpers::SqlServerAddLimitOffset
|
40
43
|
end
|
41
44
|
base.config[:sqlserver_version] ||= version
|
45
|
+
base.configure_connection
|
42
46
|
end
|
43
47
|
|
48
|
+
def configure_connection
|
49
|
+
use_database # config[:database]
|
50
|
+
end
|
51
|
+
|
44
52
|
def self.column_selector
|
45
53
|
[ /sqlserver|tds|Microsoft SQL/i, lambda { |cfg, column| column.extend(::ArJdbc::MSSQL::Column) } ]
|
46
54
|
end
|
@@ -49,6 +57,10 @@ module ArJdbc
|
|
49
57
|
::ActiveRecord::ConnectionAdapters::MSSQLJdbcConnection
|
50
58
|
end
|
51
59
|
|
60
|
+
def jdbc_column_class
|
61
|
+
::ActiveRecord::ConnectionAdapters::MSSQLColumn
|
62
|
+
end
|
63
|
+
|
52
64
|
def self.arel2_visitors(config)
|
53
65
|
require 'arel/visitors/sql_server'
|
54
66
|
visitors = config[:sqlserver_version] == '2000' ?
|
@@ -101,7 +113,8 @@ module ArJdbc
|
|
101
113
|
module Column
|
102
114
|
include LockHelpers::SqlServerAddLock
|
103
115
|
|
104
|
-
attr_accessor :identity, :
|
116
|
+
attr_accessor :identity, :special
|
117
|
+
alias_method :is_special, :special # #deprecated
|
105
118
|
|
106
119
|
def simplified_type(field_type)
|
107
120
|
case field_type
|
@@ -145,6 +158,14 @@ module ArJdbc
|
|
145
158
|
|
146
159
|
def extract_limit(sql_type)
|
147
160
|
case sql_type
|
161
|
+
when /^smallint/i
|
162
|
+
2
|
163
|
+
when /^int/i
|
164
|
+
4
|
165
|
+
when /^bigint/i
|
166
|
+
8
|
167
|
+
when /\(max\)/, /decimal/, /numeric/
|
168
|
+
nil
|
148
169
|
when /text|ntext|xml|binary|image|varbinary|bit/
|
149
170
|
nil
|
150
171
|
else
|
@@ -153,7 +174,7 @@ module ArJdbc
|
|
153
174
|
end
|
154
175
|
|
155
176
|
def is_utf8?
|
156
|
-
sql_type =~ /nvarchar|ntext|nchar/i
|
177
|
+
!!( sql_type =~ /nvarchar|ntext|nchar/i )
|
157
178
|
end
|
158
179
|
|
159
180
|
def unquote(value)
|
@@ -220,16 +241,75 @@ module ArJdbc
|
|
220
241
|
else
|
221
242
|
super
|
222
243
|
end
|
244
|
+
when Date, Time
|
245
|
+
if column && column.type == :time
|
246
|
+
"'#{quoted_time(value)}'"
|
247
|
+
elsif column && column.sql_type.index('datetimeoffset')
|
248
|
+
"'#{quoted_full_iso8601(value)}'"
|
249
|
+
elsif column && column.sql_type.index('datetime')
|
250
|
+
"'#{quoted_datetime(value)}'"
|
251
|
+
else
|
252
|
+
super
|
253
|
+
end
|
223
254
|
when TrueClass then '1'
|
224
255
|
when FalseClass then '0'
|
225
256
|
else super
|
226
257
|
end
|
227
258
|
end
|
259
|
+
|
260
|
+
def quoted_date(value)
|
261
|
+
if value.respond_to?(:usec)
|
262
|
+
"#{super}.#{sprintf("%03d", value.usec / 1000)}"
|
263
|
+
else
|
264
|
+
super
|
265
|
+
end
|
266
|
+
end
|
228
267
|
|
268
|
+
def quoted_datetime(value)
|
269
|
+
if value.acts_like?(:time)
|
270
|
+
time_zone_qualified_value = quoted_value_acts_like_time_filter(value)
|
271
|
+
if value.is_a?(Date)
|
272
|
+
time_zone_qualified_value.to_time.xmlschema.to(18)
|
273
|
+
else
|
274
|
+
if value.is_a?(ActiveSupport::TimeWithZone) && RUBY_VERSION < '1.9'
|
275
|
+
time_zone_qualified_value = time_zone_qualified_value.to_time
|
276
|
+
end
|
277
|
+
time_zone_qualified_value.iso8601(3).to(22)
|
278
|
+
end
|
279
|
+
else
|
280
|
+
quoted_date(value)
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
284
|
+
def quoted_time(value)
|
285
|
+
if value.acts_like?(:time)
|
286
|
+
tz_value = quoted_value_acts_like_time_filter(value)
|
287
|
+
sprintf("%02d:%02d:%02d.%03d", tz_value.hour, tz_value.min, tz_value.sec, value.usec / 1000)
|
288
|
+
else
|
289
|
+
quoted_date(value)
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
def quoted_full_iso8601(value)
|
294
|
+
if value.acts_like?(:time)
|
295
|
+
value.is_a?(Date) ?
|
296
|
+
quoted_value_acts_like_time_filter(value).to_time.xmlschema.to(18) :
|
297
|
+
quoted_value_acts_like_time_filter(value).iso8601(7).to(22)
|
298
|
+
else
|
299
|
+
quoted_date(value)
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
def quoted_value_acts_like_time_filter(value)
|
304
|
+
method = ActiveRecord::Base.default_timezone == :utc ? :getutc : :getlocal
|
305
|
+
value.respond_to?(method) ? value.send(method) : value
|
306
|
+
end
|
307
|
+
protected :quoted_value_acts_like_time_filter
|
308
|
+
|
229
309
|
def quote_table_name(name)
|
230
310
|
quote_column_name(name)
|
231
311
|
end
|
232
|
-
|
312
|
+
|
233
313
|
def quote_column_name(name)
|
234
314
|
name.to_s.split('.').map do |n| # "[#{name}]"
|
235
315
|
n =~ /^\[.*\]$/ ? n : "[#{n.gsub(']', ']]')}]"
|
@@ -257,40 +337,99 @@ module ArJdbc
|
|
257
337
|
true
|
258
338
|
end
|
259
339
|
|
340
|
+
# NOTE: Dynamic Name Resolution - SQL Server 2000 vs. 2005
|
341
|
+
#
|
342
|
+
# A query such as "select * from table1" in SQL Server 2000 goes through
|
343
|
+
# a set of steps to resolve and validate the object references before
|
344
|
+
# execution.
|
345
|
+
# The search first looks at the identity of the connection executing
|
346
|
+
# the query.
|
347
|
+
#
|
348
|
+
# However SQL Server 2005 provides a mechanism to allow finer control over
|
349
|
+
# name resolution to the administrators. By manipulating the value of the
|
350
|
+
# default_schema_name columns in the sys.database_principals.
|
351
|
+
#
|
352
|
+
# http://blogs.msdn.com/b/mssqlisv/archive/2007/03/23/upgrading-to-sql-server-2005-and-default-schema-setting.aspx
|
353
|
+
|
354
|
+
# Returns the default schema (to be used for table resolution) used for
|
355
|
+
# the {#current_user}.
|
356
|
+
def default_schema
|
357
|
+
return current_user if sqlserver_2000?
|
358
|
+
@default_schema ||=
|
359
|
+
select_value("SELECT default_schema_name FROM sys.database_principals WHERE name = CURRENT_USER")
|
360
|
+
end
|
361
|
+
alias_method :current_schema, :default_schema
|
362
|
+
|
363
|
+
# Allows for changing of the default schema (to be used during unqualified
|
364
|
+
# table name resolution).
|
365
|
+
# @note This is not supported on SQL Server 2000 !
|
366
|
+
def default_schema=(default_schema) # :nodoc:
|
367
|
+
raise "changing DEFAULT_SCHEMA only supported on SQLServer 2005+" if sqlserver_2000?
|
368
|
+
execute("ALTER #{current_user} WITH DEFAULT_SCHEMA=#{default_schema}")
|
369
|
+
@default_schema = nil if defined?(@default_schema)
|
370
|
+
end
|
371
|
+
alias_method :current_schema=, :default_schema=
|
372
|
+
|
373
|
+
# `SELECT CURRENT_USER`
|
374
|
+
def current_user
|
375
|
+
@current_user ||= select_value("SELECT CURRENT_USER")
|
376
|
+
end
|
377
|
+
|
378
|
+
def charset
|
379
|
+
select_value "SELECT SERVERPROPERTY('SqlCharSetName')"
|
380
|
+
end
|
381
|
+
|
382
|
+
def current_database
|
383
|
+
select_value 'SELECT DB_NAME()'
|
384
|
+
end
|
385
|
+
|
386
|
+
def use_database(database = nil)
|
387
|
+
database ||= config[:database]
|
388
|
+
execute "USE #{quote_table_name(database)}" unless database.blank?
|
389
|
+
end
|
390
|
+
|
260
391
|
def recreate_database(name, options = {})
|
261
392
|
drop_database(name)
|
262
393
|
create_database(name, options)
|
263
394
|
end
|
264
395
|
|
396
|
+
def recreate_database!(database = nil)
|
397
|
+
current_db = current_database
|
398
|
+
database ||= current_db
|
399
|
+
use_database('master') if this_db = ( database.to_s == current_db )
|
400
|
+
drop_database(database)
|
401
|
+
create_database(database)
|
402
|
+
ensure
|
403
|
+
use_database(current_db) if this_db
|
404
|
+
end
|
405
|
+
|
265
406
|
def drop_database(name)
|
266
|
-
execute "
|
267
|
-
execute "DROP DATABASE #{name}"
|
407
|
+
execute "DROP DATABASE #{quote_table_name(name)}"
|
268
408
|
end
|
269
409
|
|
270
410
|
def create_database(name, options = {})
|
271
|
-
execute "CREATE DATABASE #{name}"
|
272
|
-
execute "USE #{name}"
|
411
|
+
execute "CREATE DATABASE #{quote_table_name(name)}"
|
273
412
|
end
|
274
|
-
|
275
|
-
def rename_table(
|
276
|
-
clear_cached_table(
|
277
|
-
execute "EXEC sp_rename '#{
|
413
|
+
|
414
|
+
def rename_table(table_name, new_table_name)
|
415
|
+
clear_cached_table(table_name)
|
416
|
+
execute "EXEC sp_rename '#{table_name}', '#{new_table_name}'"
|
278
417
|
end
|
279
418
|
|
280
419
|
# Adds a new column to the named table.
|
281
420
|
# See TableDefinition#column for details of the options you can use.
|
282
421
|
def add_column(table_name, column_name, type, options = {})
|
283
422
|
clear_cached_table(table_name)
|
284
|
-
add_column_sql = "ALTER TABLE #{table_name} ADD #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
|
423
|
+
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])}"
|
285
424
|
add_column_options!(add_column_sql, options)
|
286
425
|
# TODO: Add support to mimic date columns, using constraints to mark them as such in the database
|
287
426
|
# add_column_sql << " CONSTRAINT ck__#{table_name}__#{column_name}__date_only CHECK ( CONVERT(CHAR(12), #{quote_column_name(column_name)}, 14)='00:00:00:000' )" if type == :date
|
288
427
|
execute(add_column_sql)
|
289
428
|
end
|
290
429
|
|
291
|
-
def rename_column(
|
292
|
-
clear_cached_table(
|
293
|
-
execute "EXEC sp_rename '#{
|
430
|
+
def rename_column(table_name, column_name, new_column_name)
|
431
|
+
clear_cached_table(table_name)
|
432
|
+
execute "EXEC sp_rename '#{table_name}.#{column_name}', '#{new_column_name}', 'COLUMN'"
|
294
433
|
end
|
295
434
|
|
296
435
|
def change_column(table_name, column_name, type, options = {}) #:nodoc:
|
@@ -301,10 +440,8 @@ module ArJdbc
|
|
301
440
|
|
302
441
|
def change_column_type(table_name, column_name, type, options = {}) #:nodoc:
|
303
442
|
clear_cached_table(table_name)
|
304
|
-
sql = "ALTER TABLE #{table_name} ALTER COLUMN #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
|
305
|
-
if options.has_key?(:null)
|
306
|
-
sql += (options[:null] ? " NULL" : " NOT NULL")
|
307
|
-
end
|
443
|
+
sql = "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
|
444
|
+
sql += (options[:null] ? " NULL" : " NOT NULL") if options.has_key?(:null)
|
308
445
|
execute(sql)
|
309
446
|
end
|
310
447
|
|
@@ -312,16 +449,19 @@ module ArJdbc
|
|
312
449
|
clear_cached_table(table_name)
|
313
450
|
remove_default_constraint(table_name, column_name)
|
314
451
|
unless default.nil?
|
315
|
-
execute "ALTER TABLE #{table_name} ADD CONSTRAINT DF_#{table_name}_#{column_name} DEFAULT #{quote(default)} FOR #{quote_column_name(column_name)}"
|
452
|
+
execute "ALTER TABLE #{quote_table_name(table_name)} ADD CONSTRAINT DF_#{table_name}_#{column_name} DEFAULT #{quote(default)} FOR #{quote_column_name(column_name)}"
|
316
453
|
end
|
317
454
|
end
|
318
455
|
|
319
|
-
def remove_column(table_name, *column_names)
|
456
|
+
def remove_column(table_name, *column_names)
|
457
|
+
raise ArgumentError.new("You must specify at least one column name. Example: remove_column(:people, :first_name)") if column_names.empty?
|
458
|
+
# remove_columns(:posts, :foo, :bar) old syntax : remove_columns(:posts, [:foo, :bar])
|
320
459
|
clear_cached_table(table_name)
|
321
|
-
|
460
|
+
column_names.flatten.each do |column_name|
|
322
461
|
remove_check_constraints(table_name, column_name)
|
323
462
|
remove_default_constraint(table_name, column_name)
|
324
|
-
|
463
|
+
remove_indexes(table_name, column_name) unless sqlserver_2000?
|
464
|
+
execute "ALTER TABLE #{quote_table_name(table_name)} DROP COLUMN #{quote_column_name(column_name)}"
|
325
465
|
end
|
326
466
|
end
|
327
467
|
|
@@ -355,36 +495,54 @@ module ArJdbc
|
|
355
495
|
end
|
356
496
|
end
|
357
497
|
|
358
|
-
def
|
359
|
-
|
498
|
+
def remove_indexes(table_name, column_name)
|
499
|
+
indexes = self.indexes(table_name)
|
500
|
+
indexes.select{ |index| index.columns.include?(column_name.to_s) }.each do |index|
|
501
|
+
remove_index(table_name, { :name => index.name })
|
502
|
+
end
|
360
503
|
end
|
361
|
-
|
362
|
-
def
|
363
|
-
|
504
|
+
|
505
|
+
def remove_index(table_name, options = {})
|
506
|
+
execute "DROP INDEX #{quote_table_name(table_name)}.#{index_name(table_name, options)}"
|
364
507
|
end
|
365
508
|
|
366
509
|
SKIP_COLUMNS_TABLE_NAMES_RE = /^information_schema\./i # :nodoc:
|
510
|
+
IDENTITY_COLUMN_TYPE_RE = /identity/i # :nodoc:
|
511
|
+
# NOTE: these do not handle = equality as expected {#repair_special_columns}
|
512
|
+
# (TEXT, NTEXT, and IMAGE data types are deprecated)
|
513
|
+
SPECIAL_COLUMN_TYPE_RE = /text|ntext|image|xml/i # :nodoc:
|
367
514
|
|
368
|
-
|
515
|
+
EMPTY_ARRAY = [].freeze # :nodoc:
|
516
|
+
|
517
|
+
def columns(table_name, name = nil, default = EMPTY_ARRAY)
|
369
518
|
# It's possible for table_name to be an empty string, or nil, if something
|
370
519
|
# attempts to issue SQL which doesn't involve a table.
|
371
520
|
# IE. "SELECT 1" or "SELECT * FROM someFunction()".
|
372
|
-
return
|
521
|
+
return default if table_name.blank?
|
373
522
|
|
374
523
|
table_name = unquote_table_name(table_name)
|
375
524
|
|
376
|
-
return
|
525
|
+
return default if table_name =~ SKIP_COLUMNS_TABLE_NAMES_RE
|
377
526
|
|
378
|
-
unless (@table_columns ||= {})[table_name]
|
379
|
-
|
380
|
-
|
381
|
-
column.identity = true if column.sql_type =~
|
382
|
-
column.
|
527
|
+
unless columns = ( @table_columns ||= {} )[table_name]
|
528
|
+
columns = super(table_name, name)
|
529
|
+
for column in columns
|
530
|
+
column.identity = true if column.sql_type =~ IDENTITY_COLUMN_TYPE_RE
|
531
|
+
column.special = true if column.sql_type =~ SPECIAL_COLUMN_TYPE_RE
|
383
532
|
end
|
533
|
+
@table_columns[table_name] = columns
|
384
534
|
end
|
385
|
-
|
535
|
+
columns
|
386
536
|
end
|
387
537
|
|
538
|
+
def clear_cached_table(table_name)
|
539
|
+
( @table_columns ||= {} ).delete(table_name.to_s)
|
540
|
+
end
|
541
|
+
|
542
|
+
def reset_column_information
|
543
|
+
@table_columns = nil if defined? @table_columns
|
544
|
+
end
|
545
|
+
|
388
546
|
# Turns IDENTITY_INSERT ON for table during execution of the block
|
389
547
|
# N.B. This sets the state of IDENTITY_INSERT to OFF after the
|
390
548
|
# block has been executed without regard to its previous state
|
@@ -401,91 +559,107 @@ module ArJdbc
|
|
401
559
|
raise ActiveRecord::ActiveRecordError, "IDENTITY_INSERT could not be turned" +
|
402
560
|
" #{enable ? 'ON' : 'OFF'} for table #{table_name} due : #{e.inspect}"
|
403
561
|
end
|
404
|
-
|
405
|
-
def identity_column(table_name)
|
406
|
-
for column in columns(table_name)
|
407
|
-
return column.name if column.identity
|
408
|
-
end
|
409
|
-
nil
|
410
|
-
end
|
411
562
|
|
412
|
-
|
563
|
+
# @see ArJdbc::MSSQL::LimitHelpers
|
564
|
+
def determine_order_clause(sql)
|
565
|
+
return $1 if sql =~ /ORDER BY (.*)$/i
|
413
566
|
table_name = get_table_name(sql)
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
567
|
+
# determine primary key for table :
|
568
|
+
columns = self.columns(table_name)
|
569
|
+
primary_column = columns.find { |column| column.primary || column.identity }
|
570
|
+
unless primary_column # look for an id column and return it,
|
571
|
+
# without changing case, to cover DBs with a case-sensitive collation :
|
572
|
+
primary_column = columns.find { |column| column.name =~ /^id$/i }
|
573
|
+
raise "no columns for table: #{table_name}" if columns.empty?
|
418
574
|
end
|
575
|
+
# NOTE: if still no PK column simply get something for ORDER BY ...
|
576
|
+
"#{table_name}.#{(primary_column || columns.first).name}"
|
419
577
|
end
|
420
578
|
|
421
|
-
def
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
(@table_columns ||= {}).delete(name.to_s)
|
579
|
+
def exec_proc(proc_name, *variables)
|
580
|
+
vars =
|
581
|
+
if variables.any? && variables.first.is_a?(Hash)
|
582
|
+
variables.first.map { |k, v| "@#{k} = #{quote(v)}" }
|
583
|
+
else
|
584
|
+
variables.map { |v| quote(v) }
|
585
|
+
end.join(', ')
|
586
|
+
sql = "EXEC #{proc_name} #{vars}".strip
|
587
|
+
log(sql, 'Execute Procedure') do
|
588
|
+
result = @connection.execute_query_raw(sql)
|
589
|
+
result.map! do |row|
|
590
|
+
row = row.is_a?(Hash) ? row.with_indifferent_access : row
|
591
|
+
yield(row) if block_given?
|
592
|
+
row
|
593
|
+
end
|
594
|
+
result
|
595
|
+
end
|
439
596
|
end
|
440
|
-
|
441
|
-
|
442
|
-
|
597
|
+
alias_method :execute_procedure, :exec_proc # AR-SQLServer-Adapter naming
|
598
|
+
|
599
|
+
# @override
|
600
|
+
def exec_query(sql, name = 'SQL', binds = []) # :nodoc:
|
601
|
+
# NOTE: we allow to execute SQL as requested returning a results.
|
602
|
+
# e.g. this allows to use SQLServer's EXEC with a result set ...
|
603
|
+
sql = repair_special_columns to_sql(sql, binds)
|
604
|
+
log(sql, name || 'SQL') { @connection.execute_query(sql) }
|
443
605
|
end
|
444
606
|
|
445
607
|
private
|
446
608
|
|
447
|
-
def sqlserver_2000?
|
448
|
-
sqlserver_version <= '2000'
|
449
|
-
end
|
450
|
-
|
451
609
|
def _execute(sql, name = nil)
|
452
610
|
# Match the start of the SQL to determine appropriate behavior.
|
453
611
|
# Be aware of multi-line SQL which might begin with 'create stored_proc'
|
454
612
|
# and contain 'insert into ...' lines.
|
455
|
-
# TODO test and refactor using `self.class.insert?(sql)` etc
|
456
613
|
# NOTE: ignoring comment blocks prior to the first statement ?!
|
457
|
-
if
|
458
|
-
if
|
459
|
-
|
460
|
-
with_identity_insert_enabled(table_name) do
|
614
|
+
if self.class.insert?(sql)
|
615
|
+
if id_insert_table_name = identity_insert_table_name(sql)
|
616
|
+
with_identity_insert_enabled(id_insert_table_name) do
|
461
617
|
@connection.execute_insert(sql)
|
462
618
|
end
|
463
619
|
else
|
464
620
|
@connection.execute_insert(sql)
|
465
621
|
end
|
466
|
-
elsif
|
467
|
-
|
468
|
-
|
469
|
-
else # sql.lstrip =~ /\A(create|exec)/i
|
622
|
+
elsif self.class.select?(sql)
|
623
|
+
@connection.execute_query_raw repair_special_columns(sql)
|
624
|
+
else # create | exec
|
470
625
|
@connection.execute_update(sql)
|
471
626
|
end
|
472
627
|
end
|
473
628
|
|
629
|
+
def identity_insert_table_name(sql)
|
630
|
+
table_name = get_table_name(sql)
|
631
|
+
id_column = identity_column_name(table_name)
|
632
|
+
if id_column && sql.strip =~ /INSERT INTO [^ ]+ ?\((.+?)\)/i
|
633
|
+
insert_columns = $1.split(/, */).map(&method(:unquote_column_name))
|
634
|
+
return table_name if insert_columns.include?(id_column)
|
635
|
+
end
|
636
|
+
end
|
637
|
+
|
638
|
+
def identity_column_name(table_name)
|
639
|
+
for column in columns(table_name)
|
640
|
+
return column.name if column.identity
|
641
|
+
end
|
642
|
+
nil
|
643
|
+
end
|
644
|
+
|
474
645
|
def repair_special_columns(sql)
|
475
646
|
qualified_table_name = get_table_name(sql, true)
|
476
|
-
special_columns =
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
647
|
+
if special_columns = special_column_names(qualified_table_name)
|
648
|
+
return sql if special_columns.empty?
|
649
|
+
special_columns = special_columns.sort { |n1, n2| n2.size <=> n1.size }
|
650
|
+
for column in special_columns
|
651
|
+
sql.gsub!(/\s?\[?#{column}\]?\s?=\s?/, " [#{column}] LIKE ")
|
652
|
+
sql.gsub!(/ORDER BY \[?#{column}\]?/i, '') # NOTE: a bit stupid
|
653
|
+
end
|
654
|
+
end
|
481
655
|
sql
|
482
656
|
end
|
483
657
|
|
484
|
-
def
|
658
|
+
def special_column_names(qualified_table_name)
|
659
|
+
columns = self.columns(qualified_table_name, nil, nil)
|
660
|
+
return columns if ! columns || columns.empty?
|
485
661
|
special = []
|
486
|
-
columns
|
487
|
-
special << column.name if column.is_special
|
488
|
-
end
|
662
|
+
columns.each { |column| special << column.name if column.special }
|
489
663
|
special
|
490
664
|
end
|
491
665
|
|
@@ -496,3 +670,8 @@ module ArJdbc
|
|
496
670
|
end
|
497
671
|
end
|
498
672
|
|
673
|
+
module ActiveRecord::ConnectionAdapters
|
674
|
+
class MSSQLColumn < JdbcColumn
|
675
|
+
include ArJdbc::MSSQL::Column
|
676
|
+
end
|
677
|
+
end
|