activerecord-jdbc-adapter 1.0.3-java → 50.1-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.
Files changed (268) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +33 -0
  3. data/.travis.yml +79 -0
  4. data/.yardopts +4 -0
  5. data/CONTRIBUTING.md +50 -0
  6. data/Gemfile +91 -0
  7. data/History.md +1191 -0
  8. data/LICENSE.txt +22 -17
  9. data/README.md +169 -0
  10. data/RUNNING_TESTS.md +127 -0
  11. data/Rakefile +294 -5
  12. data/Rakefile.jdbc +20 -0
  13. data/activerecord-jdbc-adapter.gemspec +55 -0
  14. data/lib/active_record/connection_adapters/as400_adapter.rb +2 -0
  15. data/lib/active_record/connection_adapters/db2_adapter.rb +1 -0
  16. data/lib/active_record/connection_adapters/firebird_adapter.rb +1 -0
  17. data/lib/active_record/connection_adapters/mariadb_adapter.rb +1 -0
  18. data/lib/active_record/connection_adapters/sqlserver_adapter.rb +1 -0
  19. data/lib/activerecord-jdbc-adapter.rb +0 -5
  20. data/lib/arel/visitors/compat.rb +60 -0
  21. data/lib/arel/visitors/db2.rb +128 -6
  22. data/lib/arel/visitors/derby.rb +103 -10
  23. data/lib/arel/visitors/firebird.rb +79 -0
  24. data/lib/arel/visitors/h2.rb +25 -0
  25. data/lib/arel/visitors/hsqldb.rb +18 -10
  26. data/lib/arel/visitors/postgresql_jdbc.rb +6 -0
  27. data/lib/arel/visitors/sql_server.rb +225 -0
  28. data/lib/arel/visitors/sql_server/ng42.rb +293 -0
  29. data/lib/arjdbc.rb +11 -21
  30. data/lib/arjdbc/abstract/connection_management.rb +35 -0
  31. data/lib/arjdbc/abstract/core.rb +64 -0
  32. data/lib/arjdbc/abstract/database_statements.rb +64 -0
  33. data/lib/arjdbc/abstract/statement_cache.rb +58 -0
  34. data/lib/arjdbc/abstract/transaction_support.rb +86 -0
  35. data/lib/arjdbc/db2.rb +3 -1
  36. data/lib/arjdbc/db2/adapter.rb +630 -250
  37. data/lib/arjdbc/db2/as400.rb +130 -0
  38. data/lib/arjdbc/db2/column.rb +167 -0
  39. data/lib/arjdbc/db2/connection_methods.rb +44 -0
  40. data/lib/arjdbc/derby.rb +1 -5
  41. data/lib/arjdbc/derby/active_record_patch.rb +13 -0
  42. data/lib/arjdbc/derby/adapter.rb +409 -217
  43. data/lib/arjdbc/derby/connection_methods.rb +16 -14
  44. data/lib/arjdbc/derby/schema_creation.rb +15 -0
  45. data/lib/arjdbc/discover.rb +62 -50
  46. data/lib/arjdbc/firebird.rb +3 -1
  47. data/lib/arjdbc/firebird/adapter.rb +365 -62
  48. data/lib/arjdbc/firebird/connection_methods.rb +23 -0
  49. data/lib/arjdbc/h2.rb +2 -3
  50. data/lib/arjdbc/h2/adapter.rb +273 -6
  51. data/lib/arjdbc/h2/connection_methods.rb +23 -8
  52. data/lib/arjdbc/hsqldb.rb +2 -3
  53. data/lib/arjdbc/hsqldb/adapter.rb +204 -77
  54. data/lib/arjdbc/hsqldb/connection_methods.rb +24 -10
  55. data/lib/arjdbc/hsqldb/explain_support.rb +35 -0
  56. data/lib/arjdbc/hsqldb/schema_creation.rb +11 -0
  57. data/lib/arjdbc/informix.rb +4 -2
  58. data/lib/arjdbc/informix/adapter.rb +78 -54
  59. data/lib/arjdbc/informix/connection_methods.rb +8 -9
  60. data/lib/arjdbc/jdbc.rb +59 -2
  61. data/lib/arjdbc/jdbc/adapter.rb +356 -166
  62. data/lib/arjdbc/jdbc/adapter_java.jar +0 -0
  63. data/lib/arjdbc/jdbc/adapter_require.rb +46 -0
  64. data/lib/arjdbc/jdbc/base_ext.rb +15 -0
  65. data/lib/arjdbc/jdbc/callbacks.rb +27 -18
  66. data/lib/arjdbc/jdbc/column.rb +79 -20
  67. data/lib/arjdbc/jdbc/connection.rb +5 -119
  68. data/lib/arjdbc/jdbc/connection_methods.rb +32 -4
  69. data/lib/arjdbc/jdbc/error.rb +65 -0
  70. data/lib/arjdbc/jdbc/extension.rb +41 -29
  71. data/lib/arjdbc/jdbc/java.rb +5 -6
  72. data/lib/arjdbc/jdbc/jdbc.rake +3 -126
  73. data/lib/arjdbc/jdbc/railtie.rb +2 -9
  74. data/lib/arjdbc/jdbc/rake_tasks.rb +3 -10
  75. data/lib/arjdbc/jdbc/serialized_attributes_helper.rb +3 -0
  76. data/lib/arjdbc/jdbc/type_cast.rb +166 -0
  77. data/lib/arjdbc/jdbc/type_converter.rb +35 -19
  78. data/lib/arjdbc/mssql.rb +6 -3
  79. data/lib/arjdbc/mssql/adapter.rb +630 -298
  80. data/lib/arjdbc/mssql/column.rb +200 -0
  81. data/lib/arjdbc/mssql/connection_methods.rb +66 -17
  82. data/lib/arjdbc/mssql/explain_support.rb +99 -0
  83. data/lib/arjdbc/mssql/limit_helpers.rb +189 -50
  84. data/lib/arjdbc/mssql/lock_methods.rb +77 -0
  85. data/lib/arjdbc/mssql/types.rb +343 -0
  86. data/lib/arjdbc/mssql/utils.rb +82 -0
  87. data/lib/arjdbc/mysql.rb +2 -3
  88. data/lib/arjdbc/mysql/adapter.rb +86 -356
  89. data/lib/arjdbc/mysql/connection_methods.rb +159 -23
  90. data/lib/arjdbc/oracle/adapter.rb +714 -263
  91. data/lib/arjdbc/postgresql.rb +2 -3
  92. data/lib/arjdbc/postgresql/_bc_time_cast_patch.rb +24 -0
  93. data/lib/arjdbc/postgresql/adapter.rb +570 -400
  94. data/lib/arjdbc/postgresql/base/array_decoder.rb +26 -0
  95. data/lib/arjdbc/postgresql/base/array_encoder.rb +25 -0
  96. data/lib/arjdbc/postgresql/base/array_parser.rb +95 -0
  97. data/lib/arjdbc/postgresql/base/pgconn.rb +11 -0
  98. data/lib/arjdbc/postgresql/column.rb +51 -0
  99. data/lib/arjdbc/postgresql/connection_methods.rb +57 -18
  100. data/lib/arjdbc/postgresql/name.rb +24 -0
  101. data/lib/arjdbc/postgresql/oid_types.rb +192 -0
  102. data/lib/arjdbc/railtie.rb +11 -0
  103. data/lib/arjdbc/sqlite3.rb +2 -3
  104. data/lib/arjdbc/sqlite3/adapter.rb +518 -198
  105. data/lib/arjdbc/sqlite3/connection_methods.rb +49 -24
  106. data/lib/arjdbc/sybase.rb +2 -2
  107. data/lib/arjdbc/sybase/adapter.rb +7 -6
  108. data/lib/arjdbc/tasks.rb +13 -0
  109. data/lib/arjdbc/tasks/database_tasks.rb +52 -0
  110. data/lib/arjdbc/tasks/databases.rake +91 -0
  111. data/lib/arjdbc/tasks/databases3.rake +215 -0
  112. data/lib/arjdbc/tasks/databases4.rake +39 -0
  113. data/lib/arjdbc/tasks/db2_database_tasks.rb +104 -0
  114. data/lib/arjdbc/tasks/derby_database_tasks.rb +95 -0
  115. data/lib/arjdbc/tasks/h2_database_tasks.rb +31 -0
  116. data/lib/arjdbc/tasks/hsqldb_database_tasks.rb +70 -0
  117. data/lib/arjdbc/tasks/jdbc_database_tasks.rb +169 -0
  118. data/lib/arjdbc/tasks/mssql_database_tasks.rb +46 -0
  119. data/lib/arjdbc/util/quoted_cache.rb +60 -0
  120. data/lib/arjdbc/util/serialized_attributes.rb +98 -0
  121. data/lib/arjdbc/util/table_copier.rb +110 -0
  122. data/lib/arjdbc/version.rb +1 -6
  123. data/lib/generators/jdbc/USAGE +9 -0
  124. data/lib/generators/jdbc/jdbc_generator.rb +8 -0
  125. data/lib/jdbc_adapter.rb +1 -1
  126. data/lib/jdbc_adapter/rake_tasks.rb +3 -2
  127. data/lib/jdbc_adapter/version.rb +2 -1
  128. data/pom.xml +114 -0
  129. data/rails_generators/jdbc_generator.rb +1 -1
  130. data/rails_generators/templates/config/initializers/jdbc.rb +8 -5
  131. data/rails_generators/templates/lib/tasks/jdbc.rake +7 -4
  132. data/rakelib/01-tomcat.rake +51 -0
  133. data/rakelib/02-test.rake +132 -0
  134. data/rakelib/bundler_ext.rb +11 -0
  135. data/rakelib/compile.rake +67 -22
  136. data/rakelib/db.rake +61 -0
  137. data/rakelib/rails.rake +204 -29
  138. data/src/java/arjdbc/ArJdbcModule.java +286 -0
  139. data/src/java/arjdbc/db2/DB2Module.java +76 -0
  140. data/src/java/arjdbc/db2/DB2RubyJdbcConnection.java +126 -0
  141. data/src/java/arjdbc/derby/DerbyModule.java +99 -243
  142. data/src/java/arjdbc/derby/DerbyRubyJdbcConnection.java +152 -0
  143. data/src/java/arjdbc/firebird/FirebirdRubyJdbcConnection.java +174 -0
  144. data/src/java/arjdbc/{jdbc/JdbcConnectionFactory.java → h2/H2Module.java} +20 -6
  145. data/src/java/arjdbc/h2/H2RubyJdbcConnection.java +27 -12
  146. data/src/java/arjdbc/hsqldb/HSQLDBModule.java +73 -0
  147. data/src/java/arjdbc/informix/InformixRubyJdbcConnection.java +7 -6
  148. data/src/java/arjdbc/jdbc/AdapterJavaService.java +7 -29
  149. data/src/java/arjdbc/jdbc/Callable.java +44 -0
  150. data/src/java/arjdbc/jdbc/ConnectionFactory.java +132 -0
  151. data/src/java/arjdbc/jdbc/DataSourceConnectionFactory.java +157 -0
  152. data/src/java/arjdbc/jdbc/DriverConnectionFactory.java +63 -0
  153. data/src/java/arjdbc/jdbc/DriverWrapper.java +119 -0
  154. data/src/java/arjdbc/jdbc/JdbcResult.java +130 -0
  155. data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +3622 -948
  156. data/src/java/arjdbc/mssql/MSSQLModule.java +90 -0
  157. data/src/java/arjdbc/mssql/MSSQLRubyJdbcConnection.java +181 -0
  158. data/src/java/arjdbc/mysql/MySQLModule.java +99 -81
  159. data/src/java/arjdbc/mysql/MySQLRubyJdbcConnection.java +294 -0
  160. data/src/java/arjdbc/oracle/OracleModule.java +80 -0
  161. data/src/java/arjdbc/oracle/OracleRubyJdbcConnection.java +387 -17
  162. data/src/java/arjdbc/postgresql/ByteaUtils.java +157 -0
  163. data/src/java/arjdbc/postgresql/PgResultSetMetaDataWrapper.java +23 -0
  164. data/src/java/arjdbc/postgresql/PostgreSQLModule.java +77 -0
  165. data/src/java/arjdbc/postgresql/PostgreSQLResult.java +184 -0
  166. data/src/java/arjdbc/postgresql/PostgreSQLRubyJdbcConnection.java +952 -0
  167. data/src/java/arjdbc/sqlite3/SQLite3Module.java +73 -0
  168. data/src/java/arjdbc/sqlite3/SQLite3RubyJdbcConnection.java +525 -0
  169. data/src/java/arjdbc/util/CallResultSet.java +826 -0
  170. data/src/java/arjdbc/util/DateTimeUtils.java +580 -0
  171. data/src/java/arjdbc/util/ObjectSupport.java +65 -0
  172. data/src/java/arjdbc/util/QuotingUtils.java +138 -0
  173. data/src/java/arjdbc/util/StringCache.java +63 -0
  174. data/src/java/arjdbc/util/StringHelper.java +159 -0
  175. metadata +245 -268
  176. data/History.txt +0 -369
  177. data/Manifest.txt +0 -180
  178. data/README.txt +0 -181
  179. data/lib/active_record/connection_adapters/oracle_adapter.rb +0 -1
  180. data/lib/arel/engines/sql/compilers/db2_compiler.rb +0 -9
  181. data/lib/arel/engines/sql/compilers/derby_compiler.rb +0 -6
  182. data/lib/arel/engines/sql/compilers/h2_compiler.rb +0 -6
  183. data/lib/arel/engines/sql/compilers/hsqldb_compiler.rb +0 -15
  184. data/lib/arel/engines/sql/compilers/jdbc_compiler.rb +0 -6
  185. data/lib/arel/engines/sql/compilers/mssql_compiler.rb +0 -46
  186. data/lib/arel/visitors/mssql.rb +0 -44
  187. data/lib/arjdbc/jdbc/compatibility.rb +0 -51
  188. data/lib/arjdbc/jdbc/core_ext.rb +0 -24
  189. data/lib/arjdbc/jdbc/discover.rb +0 -18
  190. data/lib/arjdbc/jdbc/driver.rb +0 -44
  191. data/lib/arjdbc/jdbc/missing_functionality_helper.rb +0 -87
  192. data/lib/arjdbc/jdbc/quoted_primary_key.rb +0 -28
  193. data/lib/arjdbc/jdbc/require_driver.rb +0 -16
  194. data/lib/arjdbc/mimer.rb +0 -2
  195. data/lib/arjdbc/mimer/adapter.rb +0 -142
  196. data/lib/arjdbc/mssql/tsql_helper.rb +0 -61
  197. data/lib/arjdbc/oracle.rb +0 -3
  198. data/lib/arjdbc/oracle/connection_methods.rb +0 -11
  199. data/lib/pg.rb +0 -4
  200. data/rakelib/package.rake +0 -92
  201. data/rakelib/test.rake +0 -81
  202. data/src/java/arjdbc/jdbc/SQLBlock.java +0 -48
  203. data/src/java/arjdbc/mssql/MssqlRubyJdbcConnection.java +0 -127
  204. data/src/java/arjdbc/postgresql/PostgresqlRubyJdbcConnection.java +0 -57
  205. data/src/java/arjdbc/sqlite3/Sqlite3RubyJdbcConnection.java +0 -64
  206. data/test/abstract_db_create.rb +0 -117
  207. data/test/activerecord/connection_adapters/type_conversion_test.rb +0 -31
  208. data/test/activerecord/connections/native_jdbc_mysql/connection.rb +0 -25
  209. data/test/db/db2.rb +0 -11
  210. data/test/db/derby.rb +0 -12
  211. data/test/db/h2.rb +0 -11
  212. data/test/db/hsqldb.rb +0 -13
  213. data/test/db/informix.rb +0 -11
  214. data/test/db/jdbc.rb +0 -11
  215. data/test/db/jndi_config.rb +0 -40
  216. data/test/db/logger.rb +0 -3
  217. data/test/db/mssql.rb +0 -9
  218. data/test/db/mysql.rb +0 -10
  219. data/test/db/oracle.rb +0 -34
  220. data/test/db/postgres.rb +0 -9
  221. data/test/db/sqlite3.rb +0 -11
  222. data/test/db2_simple_test.rb +0 -66
  223. data/test/derby_migration_test.rb +0 -68
  224. data/test/derby_multibyte_test.rb +0 -12
  225. data/test/derby_simple_test.rb +0 -99
  226. data/test/generic_jdbc_connection_test.rb +0 -29
  227. data/test/h2_simple_test.rb +0 -41
  228. data/test/has_many_through.rb +0 -79
  229. data/test/helper.rb +0 -5
  230. data/test/hsqldb_simple_test.rb +0 -6
  231. data/test/informix_simple_test.rb +0 -48
  232. data/test/jdbc_common.rb +0 -25
  233. data/test/jndi_callbacks_test.rb +0 -40
  234. data/test/jndi_test.rb +0 -25
  235. data/test/manualTestDatabase.rb +0 -191
  236. data/test/models/add_not_null_column_to_table.rb +0 -12
  237. data/test/models/auto_id.rb +0 -18
  238. data/test/models/data_types.rb +0 -28
  239. data/test/models/entry.rb +0 -43
  240. data/test/models/mixed_case.rb +0 -25
  241. data/test/models/reserved_word.rb +0 -18
  242. data/test/models/string_id.rb +0 -18
  243. data/test/models/validates_uniqueness_of_string.rb +0 -19
  244. data/test/mssql_db_create_test.rb +0 -26
  245. data/test/mssql_identity_insert_test.rb +0 -19
  246. data/test/mssql_legacy_types_test.rb +0 -58
  247. data/test/mssql_limit_offset_test.rb +0 -136
  248. data/test/mssql_multibyte_test.rb +0 -18
  249. data/test/mssql_simple_test.rb +0 -55
  250. data/test/mysql_db_create_test.rb +0 -27
  251. data/test/mysql_info_test.rb +0 -113
  252. data/test/mysql_multibyte_test.rb +0 -10
  253. data/test/mysql_nonstandard_primary_key_test.rb +0 -42
  254. data/test/mysql_simple_test.rb +0 -49
  255. data/test/oracle_simple_test.rb +0 -18
  256. data/test/oracle_specific_test.rb +0 -83
  257. data/test/pick_rails_version.rb +0 -3
  258. data/test/postgres_db_create_test.rb +0 -32
  259. data/test/postgres_drop_db_test.rb +0 -16
  260. data/test/postgres_mixed_case_test.rb +0 -29
  261. data/test/postgres_nonseq_pkey_test.rb +0 -38
  262. data/test/postgres_reserved_test.rb +0 -22
  263. data/test/postgres_schema_search_path_test.rb +0 -44
  264. data/test/postgres_simple_test.rb +0 -51
  265. data/test/postgres_table_alias_length_test.rb +0 -15
  266. data/test/simple.rb +0 -546
  267. data/test/sqlite3_simple_test.rb +0 -233
  268. data/test/sybase_jtds_simple_test.rb +0 -28
@@ -1,4 +1,7 @@
1
- require 'arjdbc/jdbc'
2
- jdbc_require_driver 'jdbc/jtds', 'jdbc-mssql'
3
- require 'arjdbc/mssql/connection_methods'
1
+ require 'arjdbc'
4
2
  require 'arjdbc/mssql/adapter'
3
+ require 'arjdbc/mssql/connection_methods'
4
+ module ArJdbc
5
+ MsSQL = MSSQL # compatibility with 1.2
6
+ end
7
+ ArJdbc.warn_unsupported_adapter 'mssql', [4, 2] # warns on AR >= 4.2
@@ -1,69 +1,175 @@
1
- require 'arjdbc/mssql/tsql_helper'
2
- require 'arjdbc/mssql/limit_helpers'
1
+ # NOTE: file contains code adapted from **sqlserver** adapter, license follows
2
+ =begin
3
+ Copyright (c) 2008-2015
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
+ =end
24
+
25
+ ArJdbc.load_java_part :MSSQL
26
+
27
+ require 'strscan'
28
+
29
+ module ArJdbc
30
+ module MSSQL
31
+
32
+ require 'arjdbc/mssql/utils'
33
+ require 'arjdbc/mssql/limit_helpers'
34
+ require 'arjdbc/mssql/lock_methods'
35
+ require 'arjdbc/mssql/column'
36
+ require 'arjdbc/mssql/explain_support'
37
+ require 'arjdbc/mssql/types' if AR42
38
+ require 'arel/visitors/sql_server'
3
39
 
4
- module ::ArJdbc
5
- module MsSQL
6
- include TSqlMethods
7
40
  include LimitHelpers
41
+ include Utils
42
+ include ExplainSupport
8
43
 
9
- def self.extended(mod)
10
- unless @lob_callback_added
11
- ActiveRecord::Base.class_eval do
12
- def after_save_with_mssql_lob
13
- self.class.columns.select { |c| c.sql_type =~ /image/i }.each do |c|
14
- value = self[c.name]
15
- value = value.to_yaml if unserializable_attribute?(c.name, c)
16
- next if value.nil? || (value == '')
17
-
18
- connection.write_large_object(c.type == :binary, c.name, self.class.table_name, self.class.primary_key, quote_value(id), value)
19
- end
20
- end
21
- end
44
+ # @private
45
+ def self.extended(adapter)
46
+ initialize!
22
47
 
23
- ActiveRecord::Base.after_save :after_save_with_mssql_lob
24
- @lob_callback_added = true
25
- end
26
- mod.add_version_specific_add_limit_offset
48
+ version = adapter.config[:sqlserver_version] ||= adapter.sqlserver_version
49
+ adapter.send(:setup_limit_offset!, version)
27
50
  end
28
51
 
29
- def self.column_selector
30
- [/sqlserver|tds|Microsoft SQL/i, lambda {|cfg,col| col.extend(::ArJdbc::MsSQL::Column)}]
52
+ # @private
53
+ @@_initialized = nil
54
+
55
+ # @private
56
+ def self.initialize!
57
+ return if @@_initialized; @@_initialized = true
58
+
59
+ require 'arjdbc/util/serialized_attributes'
60
+ Util::SerializedAttributes.setup /image/i, 'after_save_with_mssql_lob'
31
61
  end
32
62
 
33
- def self.jdbc_connection_class
34
- ::ActiveRecord::ConnectionAdapters::MssqlJdbcConnection
63
+ # @private
64
+ @@update_lob_values = true
65
+
66
+ # Updating records with LOB values (binary/text columns) in a separate
67
+ # statement can be disabled using :
68
+ #
69
+ # ArJdbc::MSSQL.update_lob_values = false
70
+ #
71
+ # @note This only applies when prepared statements are not used.
72
+ def self.update_lob_values?; @@update_lob_values; end
73
+ # @see #update_lob_values?
74
+ def self.update_lob_values=(update); @@update_lob_values = update; end
75
+
76
+ # @private
77
+ @@cs_equality_operator = 'COLLATE Latin1_General_CS_AS_WS'
78
+
79
+ # Operator for sorting strings in SQLServer, setup as :
80
+ #
81
+ # ArJdbc::MSSQL.cs_equality_operator = 'COLLATE Latin1_General_CS_AS_WS'
82
+ #
83
+ def self.cs_equality_operator; @@cs_equality_operator; end
84
+ # @see #cs_equality_operator
85
+ def self.cs_equality_operator=(operator); @@cs_equality_operator = operator; end
86
+
87
+ # @see #quote
88
+ # @private
89
+ BLOB_VALUE_MARKER = "''"
90
+
91
+ # @see #update_lob_values?
92
+ # @see ArJdbc::Util::SerializedAttributes#update_lob_columns
93
+ def update_lob_value?(value, column = nil)
94
+ MSSQL.update_lob_values? && ! prepared_statements? # && value
35
95
  end
36
96
 
37
- def arel2_visitors
38
- require 'arel/visitors/mssql'
39
- visitor_class = sqlserver_version == "2000" ? ::Arel::Visitors::SQLServer2000 : ::Arel::Visitors::SQLServer
40
- { 'mssql' => visitor_class, 'jdbcmssql' => visitor_class}
97
+ # @see ActiveRecord::ConnectionAdapters::JdbcAdapter#jdbc_connection_class
98
+ def self.jdbc_connection_class
99
+ ::ActiveRecord::ConnectionAdapters::MSSQLJdbcConnection
41
100
  end
42
101
 
43
- def sqlserver_version
44
- @sqlserver_version ||= select_value("select @@version")[/Microsoft SQL Server\s+(\d{4})/, 1]
102
+ # @see ActiveRecord::ConnectionAdapters::JdbcAdapter#jdbc_column_class
103
+ def jdbc_column_class; ::ActiveRecord::ConnectionAdapters::MSSQLColumn end
104
+
105
+ def configure_connection
106
+ use_database # config[:database]
45
107
  end
46
108
 
47
- def add_version_specific_add_limit_offset
48
- if sqlserver_version == "2000"
49
- extend LimitHelpers::SqlServer2000AddLimitOffset
50
- else
51
- extend LimitHelpers::SqlServerAddLimitOffset
109
+ def sqlserver_version
110
+ @sqlserver_version ||= begin
111
+ config_version = config[:sqlserver_version]
112
+ config_version ? config_version.to_s :
113
+ select_value("SELECT @@version")[/(Microsoft SQL Server\s+|Microsoft SQL Azure.+\n.+)(\d{4})/, 2]
52
114
  end
53
115
  end
54
116
 
55
- def modify_types(tp) #:nodoc:
56
- super(tp)
57
- tp[:string] = {:name => "NVARCHAR", :limit => 255}
58
- if sqlserver_version == "2000"
59
- tp[:text] = {:name => "NTEXT"}
117
+ NATIVE_DATABASE_TYPES = {
118
+ :primary_key => 'int NOT NULL IDENTITY(1,1) PRIMARY KEY',
119
+ :integer => { :name => 'int', }, # :limit => 4
120
+ :boolean => { :name => 'bit' },
121
+ :decimal => { :name => 'decimal' },
122
+ :float => { :name => 'float' },
123
+ :bigint => { :name => 'bigint' },
124
+ :real => { :name => 'real' },
125
+ :date => { :name => 'date' },
126
+ :time => { :name => 'time' },
127
+ :datetime => { :name => 'datetime' },
128
+ :timestamp => { :name => 'datetime' },
129
+
130
+ :string => { :name => 'nvarchar', :limit => 4000 },
131
+ #:varchar => { :name => 'varchar' }, # limit: 8000
132
+ :text => { :name => 'nvarchar(max)' },
133
+ :text_basic => { :name => 'text' },
134
+ #:ntext => { :name => 'ntext' },
135
+ :char => { :name => 'char' },
136
+ #:nchar => { :name => 'nchar' },
137
+ :binary => { :name => 'image' }, # NOTE: :name => 'varbinary(max)'
138
+ :binary_basic => { :name => 'binary' },
139
+ :uuid => { :name => 'uniqueidentifier' },
140
+ :money => { :name => 'money' },
141
+ #:smallmoney => { :name => 'smallmoney' },
142
+ }
143
+
144
+ def native_database_types
145
+ # NOTE: due compatibility we're using the generic type resolution
146
+ # ... NATIVE_DATABASE_TYPES won't be used at all on SQLServer 2K
147
+ sqlserver_2000? ? super : super.merge(NATIVE_DATABASE_TYPES)
148
+ end
149
+
150
+ def modify_types(types)
151
+ if sqlserver_2000?
152
+ types[:primary_key] = NATIVE_DATABASE_TYPES[:primary_key]
153
+ types[:string] = NATIVE_DATABASE_TYPES[:string]
154
+ types[:boolean] = NATIVE_DATABASE_TYPES[:boolean]
155
+ types[:text] = { :name => "ntext" }
156
+ types[:integer][:limit] = nil
157
+ types[:binary] = { :name => "image" }
60
158
  else
61
- tp[:text] = {:name => "NVARCHAR(MAX)"}
159
+ # ~ private types for better "native" adapter compatibility
160
+ types[:varchar_max] = { :name => 'varchar(max)' }
161
+ types[:nvarchar_max] = { :name => 'nvarchar(max)' }
162
+ types[:varbinary_max] = { :name => 'varbinary(max)' }
62
163
  end
63
- tp
164
+ types[:string][:limit] = 255 unless AR40 # backwards compatibility
165
+ types
64
166
  end
65
167
 
66
- def type_to_sql(type, limit = nil, precision = nil, scale = nil) #:nodoc:
168
+ # @private these cannot specify a limit
169
+ NO_LIMIT_TYPES = %w( text binary boolean date datetime )
170
+
171
+ def type_to_sql(type, limit = nil, precision = nil, scale = nil)
172
+ type_s = type.to_s
67
173
  # MSSQL's NVARCHAR(n | max) column supports either a number between 1 and
68
174
  # 4000, or the word "MAX", which corresponds to 2**30-1 UCS-2 characters.
69
175
  #
@@ -73,329 +179,436 @@ module ::ArJdbc
73
179
  # MSSQL Server 2000 is skipped here because I don't know how it will behave.
74
180
  #
75
181
  # See: http://msdn.microsoft.com/en-us/library/ms186939.aspx
76
- if type.to_s == 'string' and limit == 1073741823 and sqlserver_version != "2000"
182
+ if type_s == 'string' && limit == 1073741823 && ! sqlserver_2000?
77
183
  'NVARCHAR(MAX)'
78
- elsif %w( boolean date datetime ).include?(type.to_s)
79
- super(type) # cannot specify limit/precision/scale with these types
184
+ elsif NO_LIMIT_TYPES.include?(type_s)
185
+ super(type)
186
+ elsif type_s == 'integer' || type_s == 'int'
187
+ if limit.nil? || limit == 4
188
+ 'int'
189
+ elsif limit == 2
190
+ 'smallint'
191
+ elsif limit == 1
192
+ 'tinyint'
193
+ else
194
+ 'bigint'
195
+ end
196
+ elsif type_s == 'uniqueidentifier'
197
+ type_s
80
198
  else
81
199
  super
82
200
  end
83
201
  end
84
202
 
85
- module Column
86
- attr_accessor :identity, :is_special
87
-
88
- def simplified_type(field_type)
89
- case field_type
90
- when /int|bigint|smallint|tinyint/i then :integer
91
- when /numeric/i then (@scale.nil? || @scale == 0) ? :integer : :decimal
92
- when /float|double|decimal|money|real|smallmoney/i then :decimal
93
- when /datetime|smalldatetime/i then :datetime
94
- when /timestamp/i then :timestamp
95
- when /time/i then :time
96
- when /date/i then :date
97
- when /text|ntext|xml/i then :text
98
- when /binary|image|varbinary/i then :binary
99
- when /char|nchar|nvarchar|string|varchar/i then (@limit == 1073741823 ? (@limit = nil; :text) : :string)
100
- when /bit/i then :boolean
101
- when /uniqueidentifier/i then :string
102
- end
103
- end
104
-
105
- def default_value(value)
106
- return $1 if value =~ /^\(N?'(.*)'\)$/
107
- value
108
- end
109
-
110
- def type_cast(value)
111
- return nil if value.nil? || value == "(null)" || value == "(NULL)"
112
- case type
113
- when :integer then value.to_i rescue unquote(value).to_i rescue value ? 1 : 0
114
- when :primary_key then value == true || value == false ? value == true ? 1 : 0 : value.to_i
115
- when :decimal then self.class.value_to_decimal(unquote(value))
116
- when :datetime then cast_to_datetime(value)
117
- when :timestamp then cast_to_time(value)
118
- when :time then cast_to_time(value)
119
- when :date then cast_to_date(value)
120
- when :boolean then value == true or (value =~ /^t(rue)?$/i) == 0 or unquote(value)=="1"
121
- when :binary then unquote value
122
- else value
123
- end
124
- end
203
+ # @override
204
+ def quote(value, column = nil)
205
+ return value.quoted_id if value.respond_to?(:quoted_id)
206
+ return value if sql_literal?(value)
125
207
 
126
- def extract_limit(sql_type)
127
- case sql_type
128
- when /text|ntext|xml|binary|image|varbinary|bit/
129
- nil
208
+ case value
209
+ # SQL Server 2000 doesn't let you insert an integer into a NVARCHAR
210
+ when String, ActiveSupport::Multibyte::Chars, Integer
211
+ value = value.to_s
212
+ column_type = column && column.type
213
+ if column_type == :binary
214
+ if update_lob_value?(value, column)
215
+ BLOB_VALUE_MARKER
216
+ else
217
+ "'#{quote_string(column.class.string_to_binary(value))}'" # ' (for ruby-mode)
218
+ end
219
+ elsif column_type == :integer
220
+ value.to_i.to_s
221
+ elsif column_type == :float
222
+ value.to_f.to_s
223
+ elsif ! column.respond_to?(:is_utf8?) || column.is_utf8?
224
+ "N'#{quote_string(value)}'" # ' (for ruby-mode)
130
225
  else
131
226
  super
132
227
  end
228
+ when Date, Time
229
+ if column && column.type == :time
230
+ "'#{quoted_time(value)}'"
231
+ else
232
+ "'#{quoted_date(value)}'"
233
+ end
234
+ when TrueClass then '1'
235
+ when FalseClass then '0'
236
+ else super
133
237
  end
238
+ end
134
239
 
135
- def is_utf8?
136
- sql_type =~ /nvarchar|ntext|nchar/i
240
+ # @override
241
+ def quoted_date(value)
242
+ if value.respond_to?(:usec)
243
+ "#{super}.#{sprintf("%03d", value.usec / 1000)}"
244
+ else
245
+ super
137
246
  end
247
+ end
138
248
 
139
- def unquote(value)
140
- value.to_s.sub(/\A\([\(\']?/, "").sub(/[\'\)]?\)\Z/, "")
249
+ # @private
250
+ def quoted_time(value)
251
+ if value.acts_like?(:time)
252
+ tz_value = get_time(value)
253
+ usec = value.respond_to?(:usec) ? ( value.usec / 1000 ) : 0
254
+ sprintf("%02d:%02d:%02d.%03d", tz_value.hour, tz_value.min, tz_value.sec, usec)
255
+ else
256
+ quoted_date(value)
141
257
  end
258
+ end
142
259
 
143
- def cast_to_time(value)
144
- return value if value.is_a?(Time)
145
- time_array = ParseDate.parsedate(value)
146
- return nil if !time_array.any?
147
- time_array[0] ||= 2000
148
- time_array[1] ||= 1
149
- time_array[2] ||= 1
150
- return Time.send(ActiveRecord::Base.default_timezone, *time_array) rescue nil
151
-
152
- # Try DateTime instead - the date may be outside the time period support by Time.
153
- DateTime.new(*time_array[0..5]) rescue nil
154
- end
260
+ # @deprecated no longer used
261
+ # @private
262
+ def quoted_datetime(value)
263
+ quoted_date(value)
264
+ end
155
265
 
156
- def cast_to_date(value)
157
- return value if value.is_a?(Date)
158
- return Date.parse(value) rescue nil
266
+ # @deprecated no longer used
267
+ # @private
268
+ def quoted_full_iso8601(value)
269
+ if value.acts_like?(:time)
270
+ value.is_a?(Date) ?
271
+ get_time(value).to_time.xmlschema.to(18) :
272
+ get_time(value).iso8601(7).to(22)
273
+ else
274
+ quoted_date(value)
159
275
  end
276
+ end
160
277
 
161
- def cast_to_datetime(value)
162
- if value.is_a?(Time)
163
- if value.year != 0 and value.month != 0 and value.day != 0
164
- return value
165
- else
166
- return Time.mktime(2000, 1, 1, value.hour, value.min, value.sec) rescue nil
167
- end
168
- end
169
- if value.is_a?(DateTime)
170
- begin
171
- # Attempt to convert back to a Time, but it could fail for dates significantly in the past/future.
172
- return Time.mktime(value.year, value.mon, value.day, value.hour, value.min, value.sec)
173
- rescue ArgumentError
174
- return value
175
- end
176
- end
278
+ def quote_table_name(name)
279
+ quote_column_name(name)
280
+ end
177
281
 
178
- return cast_to_time(value) if value.is_a?(Date) or value.is_a?(String) rescue nil
282
+ def quote_column_name(name)
283
+ name = name.to_s.split('.')
284
+ name.map! { |n| quote_name_part(n) } # "[#{name}]"
285
+ name.join('.')
286
+ end
179
287
 
180
- return value.is_a?(Date) ? value : nil
181
- end
288
+ def quote_database_name(name)
289
+ quote_name_part(name.to_s)
290
+ end
182
291
 
183
- # These methods will only allow the adapter to insert binary data with a length of 7K or less
184
- # because of a SQL Server statement length policy.
185
- def self.string_to_binary(value)
186
- ''
292
+ # Does not quote function default values for UUID columns
293
+ def quote_default_value(value, column)
294
+ if column.type == :uuid && value =~ /\(\)/
295
+ value
296
+ else
297
+ quote(value)
187
298
  end
188
-
189
299
  end
190
300
 
191
- def quote(value, column = nil)
192
- return value.quoted_id if value.respond_to?(:quoted_id)
301
+ ADAPTER_NAME = 'MSSQL'.freeze
193
302
 
194
- case value
195
- # SQL Server 2000 doesn't let you insert an integer into a NVARCHAR
196
- # column, so we include Integer here.
197
- when String, ActiveSupport::Multibyte::Chars, Integer
198
- value = value.to_s
199
- if column && column.type == :binary
200
- "'#{quote_string(ArJdbc::MsSQL::Column.string_to_binary(value))}'" # ' (for ruby-mode)
201
- elsif column && [:integer, :float].include?(column.type)
202
- value = column.type == :integer ? value.to_i : value.to_f
203
- value.to_s
204
- elsif !column.respond_to?(:is_utf8?) || column.is_utf8?
205
- "N'#{quote_string(value)}'" # ' (for ruby-mode)
206
- else
207
- super
303
+ def adapter_name
304
+ ADAPTER_NAME
305
+ end
306
+
307
+ def change_order_direction(order)
308
+ asc, desc = /\bASC\b/i, /\bDESC\b/i
309
+ order.split(",").collect do |fragment|
310
+ case fragment
311
+ when desc then fragment.gsub(desc, "ASC")
312
+ when asc then fragment.gsub(asc, "DESC")
313
+ else "#{fragment.split(',').join(' DESC,')} DESC"
208
314
  end
209
- when TrueClass then '1'
210
- when FalseClass then '0'
211
- else super
212
- end
315
+ end.join(",")
213
316
  end
214
317
 
215
- def quote_string(string)
216
- string.gsub(/\'/, "''")
318
+ # @override
319
+ def supports_ddl_transactions?; true end
320
+
321
+ # @override
322
+ def supports_views?; true end
323
+
324
+ def tables(schema = current_schema)
325
+ @connection.tables(nil, schema)
217
326
  end
218
327
 
219
- def quote_table_name(name)
220
- name
328
+ # NOTE: Dynamic Name Resolution - SQL Server 2000 vs. 2005
329
+ #
330
+ # A query such as "select * from table1" in SQL Server 2000 goes through
331
+ # a set of steps to resolve and validate the object references before
332
+ # execution.
333
+ # The search first looks at the identity of the connection executing
334
+ # the query.
335
+ #
336
+ # However SQL Server 2005 provides a mechanism to allow finer control over
337
+ # name resolution to the administrators. By manipulating the value of the
338
+ # default_schema_name columns in the sys.database_principals.
339
+ #
340
+ # http://blogs.msdn.com/b/mssqlisv/archive/2007/03/23/upgrading-to-sql-server-2005-and-default-schema-setting.aspx
341
+
342
+ # Returns the default schema (to be used for table resolution) used for the {#current_user}.
343
+ def default_schema
344
+ return current_user if sqlserver_2000?
345
+ @default_schema ||=
346
+ @connection.execute_query_raw(
347
+ "SELECT default_schema_name FROM sys.database_principals WHERE name = CURRENT_USER"
348
+ ).first['default_schema_name']
221
349
  end
350
+ alias_method :current_schema, :default_schema
222
351
 
223
- def quote_column_name(name)
224
- "[#{name}]"
352
+ # Allows for changing of the default schema (to be used during unqualified
353
+ # table name resolution).
354
+ # @note This is not supported on SQL Server 2000 !
355
+ def default_schema=(default_schema) # :nodoc:
356
+ raise "changing DEFAULT_SCHEMA only supported on SQLServer 2005+" if sqlserver_2000?
357
+ execute("ALTER #{current_user} WITH DEFAULT_SCHEMA=#{default_schema}")
358
+ @default_schema = nil if defined?(@default_schema)
225
359
  end
360
+ alias_method :current_schema=, :default_schema=
226
361
 
227
- def quoted_true
228
- quote true
362
+ # `SELECT CURRENT_USER`
363
+ def current_user
364
+ @current_user ||= @connection.execute_query_raw("SELECT CURRENT_USER").first['']
229
365
  end
230
366
 
231
- def quoted_false
232
- quote false
367
+ def charset
368
+ select_value "SELECT SERVERPROPERTY('SqlCharSetName')"
233
369
  end
234
370
 
235
- def adapter_name #:nodoc:
236
- 'MsSQL'
371
+ def collation
372
+ select_value "SELECT SERVERPROPERTY('Collation')"
237
373
  end
238
374
 
239
- def change_order_direction(order)
240
- order.split(",").collect do |fragment|
241
- case fragment
242
- when /\bDESC\b/i then fragment.gsub(/\bDESC\b/i, "ASC")
243
- when /\bASC\b/i then fragment.gsub(/\bASC\b/i, "DESC")
244
- else String.new(fragment).split(',').join(' DESC,') + ' DESC'
245
- end
246
- end.join(",")
375
+ def current_database
376
+ select_value 'SELECT DB_NAME()'
247
377
  end
248
378
 
249
- def supports_ddl_transactions?
250
- true
379
+ def use_database(database = nil)
380
+ database ||= config[:database]
381
+ execute "USE #{quote_database_name(database)}" unless database.blank?
251
382
  end
252
383
 
253
- def recreate_database(name)
384
+ # @private
385
+ def recreate_database(name, options = {})
254
386
  drop_database(name)
255
- create_database(name)
387
+ create_database(name, options)
388
+ end
389
+
390
+ # @private
391
+ def recreate_database!(database = nil)
392
+ current_db = current_database
393
+ database ||= current_db
394
+ use_database('master') if this_db = ( database.to_s == current_db )
395
+ drop_database(database)
396
+ create_database(database)
397
+ ensure
398
+ use_database(current_db) if this_db
256
399
  end
257
400
 
258
401
  def drop_database(name)
259
- execute "USE master"
260
- execute "DROP DATABASE #{name}"
402
+ current_db = current_database
403
+ use_database('master') if current_db.to_s == name
404
+ execute "DROP DATABASE #{quote_database_name(name)}"
405
+ end
406
+
407
+ def create_database(name, options = {})
408
+ execute "CREATE DATABASE #{quote_database_name(name)}"
261
409
  end
262
410
 
263
- def create_database(name)
264
- execute "CREATE DATABASE #{name}"
265
- execute "USE #{name}"
411
+ def database_exists?(name)
412
+ select_value "SELECT name FROM sys.databases WHERE name = '#{name}'"
266
413
  end
267
414
 
268
- def rename_table(name, new_name)
269
- clear_cached_table(name)
270
- execute "EXEC sp_rename '#{name}', '#{new_name}'"
415
+ # @override
416
+ def rename_table(table_name, new_table_name)
417
+ clear_cached_table(table_name)
418
+ execute "EXEC sp_rename '#{table_name}', '#{new_table_name}'"
271
419
  end
272
420
 
273
421
  # Adds a new column to the named table.
274
- # See TableDefinition#column for details of the options you can use.
422
+ # @override
275
423
  def add_column(table_name, column_name, type, options = {})
276
424
  clear_cached_table(table_name)
277
- add_column_sql = "ALTER TABLE #{table_name} ADD #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
425
+ 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])}"
278
426
  add_column_options!(add_column_sql, options)
279
427
  # TODO: Add support to mimic date columns, using constraints to mark them as such in the database
280
428
  # 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
281
429
  execute(add_column_sql)
282
430
  end
283
431
 
284
- def rename_column(table, column, new_column_name)
285
- clear_cached_table(table)
286
- execute "EXEC sp_rename '#{table}.#{column}', '#{new_column_name}'"
432
+ # @override
433
+ def rename_column(table_name, column_name, new_column_name)
434
+ clear_cached_table(table_name)
435
+ execute "EXEC sp_rename '#{table_name}.#{column_name}', '#{new_column_name}', 'COLUMN'"
436
+ end
437
+
438
+ # SELECT DISTINCT clause for a given set of columns and a given ORDER BY clause.
439
+ # MSSQL requires the ORDER BY columns in the select list for distinct queries.
440
+ def distinct(columns, order_by)
441
+ "DISTINCT #{columns_for_distinct(columns, order_by)}"
287
442
  end
288
443
 
289
- def change_column(table_name, column_name, type, options = {}) #:nodoc:
290
- clear_cached_table(table_name)
444
+ def columns_for_distinct(columns, orders)
445
+ return columns if orders.blank?
446
+
447
+ # construct a clean list of column names from the ORDER BY clause,
448
+ # removing any ASC/DESC modifiers
449
+ order_columns = [ orders ]; order_columns.flatten! # AR 3.x vs 4.x
450
+ order_columns.map! do |column|
451
+ column = column.to_sql unless column.is_a?(String) # handle AREL node
452
+ column.split(',').collect!{ |s| s.split.first }
453
+ end.flatten!
454
+ order_columns.reject!(&:blank?)
455
+ order_columns = order_columns.zip(0...order_columns.size).to_a
456
+ order_columns = order_columns.map{ |s, i| "#{s}" }
457
+
458
+ columns = [ columns ]; columns.flatten!
459
+ columns.push( *order_columns ).join(', ')
460
+ # return a DISTINCT clause that's distinct on the columns we want but
461
+ # includes all the required columns for the ORDER BY to work properly
462
+ end
463
+
464
+ # @override
465
+ def change_column(table_name, column_name, type, options = {})
466
+ column = columns(table_name).find { |c| c.name.to_s == column_name.to_s }
467
+
468
+ indexes = EMPTY_ARRAY
469
+ if options_include_default?(options) || (column && column.type != type.to_sym)
470
+ remove_default_constraint(table_name, column_name)
471
+ indexes = indexes(table_name).select{ |index| index.columns.include?(column_name.to_s) }
472
+ remove_indexes(table_name, column_name)
473
+ end
474
+
475
+ if ! options[:null].nil? && options[:null] == false && ! options[:default].nil?
476
+ execute "UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote_default_value(options[:default], column)} WHERE #{quote_column_name(column_name)} IS NULL"
477
+ clear_cached_table(table_name)
478
+ end
291
479
  change_column_type(table_name, column_name, type, options)
292
480
  change_column_default(table_name, column_name, options[:default]) if options_include_default?(options)
293
- end
294
481
 
295
- def change_column_type(table_name, column_name, type, options = {}) #:nodoc:
296
- clear_cached_table(table_name)
297
- sql = "ALTER TABLE #{table_name} ALTER COLUMN #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
298
- if options.has_key?(:null)
299
- sql += (options[:null] ? " NULL" : " NOT NULL")
482
+ indexes.each do |index| # add any removed indexes back
483
+ index_columns = index.columns.map { |c| quote_column_name(c) }.join(', ')
484
+ execute "CREATE INDEX #{quote_table_name(index.name)} ON #{quote_table_name(table_name)} (#{index_columns})"
300
485
  end
301
- execute(sql)
302
486
  end
303
487
 
304
- def change_column_default(table_name, column_name, default) #:nodoc:
488
+ def change_column_type(table_name, column_name, type, options = {})
489
+ 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])}"
490
+ sql << (options[:null] ? " NULL" : " NOT NULL") if options.has_key?(:null)
491
+ result = execute(sql)
305
492
  clear_cached_table(table_name)
493
+ result
494
+ end
495
+
496
+ def change_column_default(table_name, column_name, default)
306
497
  remove_default_constraint(table_name, column_name)
307
498
  unless default.nil?
308
- execute "ALTER TABLE #{table_name} ADD CONSTRAINT DF_#{table_name}_#{column_name} DEFAULT #{quote(default)} FOR #{quote_column_name(column_name)}"
499
+ column = columns(table_name).find { |c| c.name.to_s == column_name.to_s }
500
+ result = execute "ALTER TABLE #{quote_table_name(table_name)} ADD CONSTRAINT DF_#{table_name}_#{column_name} DEFAULT #{quote_default_value(default, column)} FOR #{quote_column_name(column_name)}"
501
+ clear_cached_table(table_name)
502
+ result
309
503
  end
310
504
  end
311
505
 
312
- def remove_column(table_name, column_name)
506
+ def remove_columns(table_name, *column_names)
507
+ raise ArgumentError.new("You must specify at least one column name. Example: remove_column(:people, :first_name)") if column_names.empty?
508
+ # remove_columns(:posts, :foo, :bar) old syntax : remove_columns(:posts, [:foo, :bar])
313
509
  clear_cached_table(table_name)
510
+
511
+ column_names = column_names.flatten
512
+ return do_remove_column(table_name, column_names.first) if column_names.size == 1
513
+ column_names.each { |column_name| do_remove_column(table_name, column_name) }
514
+ end
515
+
516
+ def do_remove_column(table_name, column_name)
314
517
  remove_check_constraints(table_name, column_name)
315
518
  remove_default_constraint(table_name, column_name)
316
- execute "ALTER TABLE #{table_name} DROP COLUMN [#{column_name}]"
519
+ remove_indexes(table_name, column_name) unless sqlserver_2000?
520
+ execute "ALTER TABLE #{quote_table_name(table_name)} DROP COLUMN #{quote_column_name(column_name)}"
521
+ end
522
+ private :do_remove_column
523
+
524
+ if ActiveRecord::VERSION::MAJOR >= 4
525
+
526
+ # @override
527
+ def remove_column(table_name, column_name, type = nil, options = {})
528
+ remove_columns(table_name, column_name)
529
+ end
530
+
531
+ else
532
+
533
+ def remove_column(table_name, *column_names); remove_columns(table_name, *column_names) end
534
+
317
535
  end
318
536
 
319
537
  def remove_default_constraint(table_name, column_name)
320
538
  clear_cached_table(table_name)
321
- defaults = select "select def.name from sysobjects def, syscolumns col, sysobjects tab where col.cdefault = def.id and col.name = '#{column_name}' and tab.name = '#{table_name}' and col.id = tab.id"
322
- defaults.each {|constraint|
323
- execute "ALTER TABLE #{table_name} DROP CONSTRAINT #{constraint["name"]}"
324
- }
539
+ if sqlserver_2000?
540
+ # NOTE: since SQLServer 2005 these are provided as sys.sysobjects etc.
541
+ # but only due backwards-compatibility views and should be avoided ...
542
+ defaults = select_values "SELECT d.name" <<
543
+ " FROM sysobjects d, syscolumns c, sysobjects t" <<
544
+ " WHERE c.cdefault = d.id AND c.name = '#{column_name}'" <<
545
+ " AND t.name = '#{table_name}' AND c.id = t.id"
546
+ else
547
+ defaults = select_values "SELECT d.name FROM sys.tables t" <<
548
+ " JOIN sys.default_constraints d ON d.parent_object_id = t.object_id" <<
549
+ " JOIN sys.columns c ON c.object_id = t.object_id AND c.column_id = d.parent_column_id" <<
550
+ " WHERE t.name = '#{table_name}' AND c.name = '#{column_name}'"
551
+ end
552
+ defaults.each do |def_name|
553
+ execute "ALTER TABLE #{table_name} DROP CONSTRAINT #{def_name}"
554
+ end
325
555
  end
326
556
 
327
557
  def remove_check_constraints(table_name, column_name)
328
558
  clear_cached_table(table_name)
329
- # TODO remove all constraints in single method
330
- constraints = select "SELECT CONSTRAINT_NAME FROM INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE where TABLE_NAME = '#{table_name}' and COLUMN_NAME = '#{column_name}'"
331
- constraints.each do |constraint|
332
- execute "ALTER TABLE #{table_name} DROP CONSTRAINT #{constraint["CONSTRAINT_NAME"]}"
559
+ constraints = select_values "SELECT constraint_name" <<
560
+ " FROM information_schema.constraint_column_usage" <<
561
+ " WHERE table_name = '#{table_name}' AND column_name = '#{column_name}'"
562
+ constraints.each do |constraint_name|
563
+ execute "ALTER TABLE #{table_name} DROP CONSTRAINT #{constraint_name}"
564
+ end
565
+ end
566
+
567
+ def remove_indexes(table_name, column_name)
568
+ indexes = self.indexes(table_name)
569
+ indexes.select{ |index| index.columns.include?(column_name.to_s) }.each do |index|
570
+ remove_index(table_name, { :name => index.name })
333
571
  end
334
572
  end
335
573
 
336
574
  def remove_index(table_name, options = {})
337
- execute "DROP INDEX #{table_name}.#{index_name(table_name, options)}"
575
+ execute "DROP INDEX #{quote_table_name(table_name)}.#{index_name(table_name, options)}"
338
576
  end
339
577
 
340
- def columns(table_name, name = nil)
341
- # It's possible for table_name to be an empty string, or nil, if something attempts to issue SQL
342
- # which doesn't involve a table. IE. "SELECT 1" or "SELECT * from someFunction()".
343
- return [] if table_name.blank?
344
- table_name = table_name.to_s if table_name.is_a?(Symbol)
578
+ # @private
579
+ SKIP_COLUMNS_TABLE_NAMES_RE = /^information_schema\./i
345
580
 
346
- # Remove []'s from around the table name, valid in a select statement, but not when matching metadata.
347
- table_name = table_name.gsub(/[\[\]]/, '')
581
+ # @private
582
+ EMPTY_ARRAY = [].freeze
348
583
 
349
- return [] if table_name =~ /^information_schema\./i
350
- @table_columns = {} unless @table_columns
351
- unless @table_columns[table_name]
352
- @table_columns[table_name] = super
353
- @table_columns[table_name].each do |col|
354
- col.identity = true if col.sql_type =~ /identity/i
355
- col.is_special = true if col.sql_type =~ /text|ntext|image|xml/i
356
- end
357
- end
358
- @table_columns[table_name]
359
- end
584
+ def columns(table_name, name = nil, default = EMPTY_ARRAY)
585
+ # It's possible for table_name to be an empty string, or nil, if something
586
+ # attempts to issue SQL which doesn't involve a table.
587
+ # IE. "SELECT 1" or "SELECT * FROM someFunction()".
588
+ return default if table_name.blank?
360
589
 
361
- def _execute(sql, name = nil)
362
- # Match the start of the sql to determine appropriate behaviour. Be aware of
363
- # multi-line sql which might begin with 'create stored_proc' and contain 'insert into ...' lines.
364
- # Possible improvements include ignoring comment blocks prior to the first statement.
365
- if sql.lstrip =~ /\Ainsert/i
366
- if query_requires_identity_insert?(sql)
367
- table_name = get_table_name(sql)
368
- with_identity_insert_enabled(table_name) do
369
- id = @connection.execute_insert(sql)
370
- end
371
- else
372
- @connection.execute_insert(sql)
373
- end
374
- elsif sql.lstrip =~ /\A(create|exec)/i
375
- @connection.execute_update(sql)
376
- elsif sql.lstrip =~ /\A\(?\s*(select|show)/i
377
- repair_special_columns(sql)
378
- @connection.execute_query(sql)
379
- else
380
- @connection.execute_update(sql)
590
+ table_name = unquote_table_name(table_name)
591
+
592
+ return default if table_name =~ SKIP_COLUMNS_TABLE_NAMES_RE
593
+
594
+ unless columns = ( @table_columns ||= {} )[table_name]
595
+ @table_columns[table_name] = columns = super(table_name, name)
381
596
  end
597
+ columns
382
598
  end
383
599
 
384
- def select(sql, name = nil)
385
- log(sql, name) do
386
- @connection.execute_query(sql)
387
- end
600
+ def clear_cached_table(table_name)
601
+ ( @table_columns ||= {} ).delete(table_name.to_s)
388
602
  end
389
603
 
390
- #SELECT .. FOR UPDATE is not supported on Microsoft SQL Server
391
- def add_lock!(sql, options)
392
- sql
604
+ def reset_column_information
605
+ @table_columns = nil if defined? @table_columns
393
606
  end
394
607
 
395
608
  # Turns IDENTITY_INSERT ON for table during execution of the block
396
609
  # N.B. This sets the state of IDENTITY_INSERT to OFF after the
397
610
  # block has been executed without regard to its previous state
398
- def with_identity_insert_enabled(table_name, &block)
611
+ def with_identity_insert_enabled(table_name)
399
612
  set_identity_insert(table_name, true)
400
613
  yield
401
614
  ensure
@@ -405,68 +618,187 @@ module ::ArJdbc
405
618
  def set_identity_insert(table_name, enable = true)
406
619
  execute "SET IDENTITY_INSERT #{table_name} #{enable ? 'ON' : 'OFF'}"
407
620
  rescue Exception => e
408
- raise ActiveRecord::ActiveRecordError, "IDENTITY_INSERT could not be turned #{enable ? 'ON' : 'OFF'} for table #{table_name}"
621
+ raise ActiveRecord::ActiveRecordError, "IDENTITY_INSERT could not be turned" +
622
+ " #{enable ? 'ON' : 'OFF'} for table #{table_name} due : #{e.inspect}"
623
+ end
624
+
625
+ def disable_referential_integrity
626
+ execute "EXEC sp_MSforeachtable 'ALTER TABLE ? NOCHECK CONSTRAINT ALL'"
627
+ yield
628
+ ensure
629
+ execute "EXEC sp_MSforeachtable 'ALTER TABLE ? CHECK CONSTRAINT ALL'"
409
630
  end
410
631
 
411
- def identity_column(table_name)
412
- columns(table_name).each do |col|
413
- return col.name if col.identity
632
+ # @private
633
+ # @see ArJdbc::MSSQL::LimitHelpers
634
+ def determine_order_clause(sql)
635
+ return $1 if sql =~ /ORDER BY (.*)$/i
636
+ columns = self.columns(table_name = get_table_name(sql))
637
+ primary_column = columns.find { |column| column.primary? || column.identity? }
638
+ unless primary_column # look for an id column and return it,
639
+ # without changing case, to cover DBs with a case-sensitive collation :
640
+ primary_column = columns.find { |column| column.name =~ /^id$/i }
641
+ raise "no columns for table: #{table_name} (SQL query: ' #{sql} ')" if columns.empty?
414
642
  end
415
- return nil
643
+ # NOTE: if still no PK column simply get something for ORDER BY ...
644
+ "#{quote_table_name(table_name)}.#{quote_column_name((primary_column || columns.first).name)}"
416
645
  end
417
646
 
418
- def query_requires_identity_insert?(sql)
419
- table_name = get_table_name(sql)
420
- id_column = identity_column(table_name)
421
- if sql.strip =~ /insert into [^ ]+ ?\((.+?)\)/i
422
- insert_columns = $1.split(/, */).map(&method(:unquote_column_name))
423
- return table_name if insert_columns.include?(id_column)
647
+ def truncate(table_name, name = nil)
648
+ execute "TRUNCATE TABLE #{quote_table_name(table_name)}", name
649
+ end
650
+
651
+ # Support for executing a stored procedure.
652
+ def exec_proc(proc_name, *variables)
653
+ vars =
654
+ if variables.any? && variables.first.is_a?(Hash)
655
+ variables.first.map { |k, v| "@#{k} = #{quote(v)}" }
656
+ else
657
+ variables.map { |v| quote(v) }
658
+ end.join(', ')
659
+ sql = "EXEC #{proc_name} #{vars}".strip
660
+ log(sql, 'Execute Procedure') do
661
+ result = @connection.execute_query_raw(sql)
662
+ result.map! do |row|
663
+ row = row.is_a?(Hash) ? row.with_indifferent_access : row
664
+ yield(row) if block_given?
665
+ row
666
+ end
667
+ result
424
668
  end
425
669
  end
670
+ alias_method :execute_procedure, :exec_proc # AR-SQLServer-Adapter naming
426
671
 
427
- def unquote_column_name(name)
428
- if name =~ /^\[.*\]$/
429
- name[1..-2]
672
+ # @override
673
+ def exec_query(sql, name = 'SQL', binds = [])
674
+ # NOTE: we allow to execute SQL as requested returning a results.
675
+ # e.g. this allows to use SQLServer's EXEC with a result set ...
676
+ sql = to_sql(sql, binds) if sql.respond_to?(:to_sql)
677
+
678
+ sql = repair_special_columns(sql)
679
+ if prepared_statements?
680
+ log(sql, name, binds) { @connection.execute_query(sql, binds) }
430
681
  else
431
- name
682
+ log(sql, name) { @connection.execute_query(sql) }
432
683
  end
433
684
  end
434
685
 
435
- def get_special_columns(table_name)
436
- special = []
437
- columns(table_name).each do |col|
438
- special << col.name if col.is_special
686
+ # @override
687
+ def exec_query_raw(sql, name = 'SQL', binds = [], &block)
688
+ sql = to_sql(sql, binds) if sql.respond_to?(:to_sql)
689
+
690
+ sql = repair_special_columns(sql)
691
+ if prepared_statements?
692
+ log(sql, name, binds) { @connection.execute_query_raw(sql, binds, &block) }
693
+ else
694
+ log(sql, name) { @connection.execute_query_raw(sql, &block) }
439
695
  end
440
- special
441
696
  end
442
697
 
443
- def repair_special_columns(sql)
444
- special_cols = get_special_columns(get_table_name(sql))
445
- for col in special_cols.to_a
446
- sql.gsub!(Regexp.new(" #{col.to_s} = "), " #{col.to_s} LIKE ")
447
- sql.gsub!(/ORDER BY #{col.to_s}/i, '')
698
+ # @override
699
+ def release_savepoint(name = current_savepoint_name(false))
700
+ if @connection.jtds_driver?
701
+ @connection.release_savepoint(name)
702
+ else # MS invented it's "own" way
703
+ @connection.rollback_savepoint(name)
448
704
  end
449
- sql
450
705
  end
451
706
 
452
- def determine_order_clause(sql)
453
- return $1 if sql =~ /ORDER BY (.*)$/
707
+ private
708
+
709
+ def _execute(sql, name = nil)
710
+ # Match the start of the SQL to determine appropriate behavior.
711
+ # Be aware of multi-line SQL which might begin with 'create stored_proc'
712
+ # and contain 'insert into ...' lines.
713
+ # NOTE: ignoring comment blocks prior to the first statement ?!
714
+ if self.class.insert?(sql)
715
+ if id_insert_table_name = identity_insert_table_name(sql)
716
+ with_identity_insert_enabled(id_insert_table_name) do
717
+ @connection.execute_insert(sql)
718
+ end
719
+ else
720
+ @connection.execute_insert(sql)
721
+ end
722
+ elsif self.class.select?(sql)
723
+ @connection.execute_query_raw repair_special_columns(sql)
724
+ else # create | exec
725
+ @connection.execute_update(sql)
726
+ end
727
+ end
728
+
729
+ def identity_insert_table_name(sql)
454
730
  table_name = get_table_name(sql)
455
- "#{table_name}.#{determine_primary_key(table_name)}"
731
+ id_column = identity_column_name(table_name)
732
+ if id_column && sql.strip =~ /INSERT INTO [^ ]+ ?\((.+?)\)/i
733
+ insert_columns = $1.split(/, */).map(&method(:unquote_column_name))
734
+ return table_name if insert_columns.include?(id_column)
735
+ end
736
+ end
737
+
738
+ def identity_column_name(table_name)
739
+ for column in columns(table_name)
740
+ return column.name if column.identity
741
+ end
742
+ nil
456
743
  end
457
744
 
458
- def determine_primary_key(table_name)
459
- primary_key = columns(table_name).detect { |column| column.primary || column.identity }
460
- return primary_key.name if primary_key
461
- # Look for an id column. Return it, without changing case, to cover dbs with a case-sensitive collation.
462
- columns(table_name).each { |column| return column.name if column.name =~ /^id$/i }
463
- # Give up and provide something which is going to crash almost certainly
464
- columns(table_name)[0].name
745
+ def repair_special_columns(sql)
746
+ qualified_table_name = get_table_name(sql, true)
747
+ if special_columns = special_column_names(qualified_table_name)
748
+ return sql if special_columns.empty?
749
+ special_columns = special_columns.sort { |n1, n2| n2.size <=> n1.size }
750
+ for column in special_columns
751
+ sql.gsub!(/\s?\[?#{column}\]?\s?=\s?/, " [#{column}] LIKE ")
752
+ sql.gsub!(/ORDER BY \[?#{column}([^\.\w]|$)\]?/i, '') # NOTE: a bit stupid
753
+ end
754
+ end
755
+ sql
465
756
  end
466
757
 
467
- def clear_cached_table(name)
468
- (@table_columns ||= {}).delete(name.to_s)
758
+ def special_column_names(qualified_table_name)
759
+ columns = self.columns(qualified_table_name, nil, nil)
760
+ return columns if ! columns || columns.empty?
761
+ special = []
762
+ columns.each { |column| special << column.name if column.special? }
763
+ special
469
764
  end
765
+
766
+ def sqlserver_2000?
767
+ sqlserver_version <= '2000'
768
+ end
769
+
470
770
  end
471
771
  end
472
772
 
773
+ require 'arjdbc/util/quoted_cache'
774
+
775
+ module ActiveRecord::ConnectionAdapters
776
+
777
+ class MSSQLAdapter < JdbcAdapter
778
+ include ::ArJdbc::MSSQL
779
+ include ::ArJdbc::Util::QuotedCache
780
+
781
+ def initialize(*args)
782
+ ::ArJdbc::MSSQL.initialize!
783
+
784
+ super # configure_connection happens in super
785
+
786
+ setup_limit_offset!
787
+ end
788
+
789
+ def arel_visitor # :nodoc:
790
+ ( config && config[:sqlserver_version].to_s == '2000' ) ?
791
+ ::Arel::Visitors::SQLServer2000.new(self) :
792
+ ::Arel::Visitors::SQLServer.new(self)
793
+ end
794
+
795
+ def self.cs_equality_operator; ::ArJdbc::MSSQL.cs_equality_operator end
796
+ def self.cs_equality_operator=(operator); ::ArJdbc::MSSQL.cs_equality_operator = operator end
797
+
798
+ end
799
+
800
+ class MSSQLColumn < JdbcColumn
801
+ include ::ArJdbc::MSSQL::Column
802
+ end
803
+
804
+ end