activerecord-jdbc-alt-adapter 50.3.0-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (198) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +35 -0
  3. data/.travis.yml +100 -0
  4. data/.yardopts +4 -0
  5. data/CONTRIBUTING.md +50 -0
  6. data/Gemfile +92 -0
  7. data/History.md +1191 -0
  8. data/LICENSE.txt +26 -0
  9. data/README.md +240 -0
  10. data/RUNNING_TESTS.md +127 -0
  11. data/Rakefile +336 -0
  12. data/Rakefile.jdbc +20 -0
  13. data/activerecord-jdbc-adapter.gemspec +55 -0
  14. data/activerecord-jdbc-alt-adapter.gemspec +56 -0
  15. data/lib/active_record/connection_adapters/as400_adapter.rb +2 -0
  16. data/lib/active_record/connection_adapters/db2_adapter.rb +1 -0
  17. data/lib/active_record/connection_adapters/derby_adapter.rb +1 -0
  18. data/lib/active_record/connection_adapters/firebird_adapter.rb +1 -0
  19. data/lib/active_record/connection_adapters/h2_adapter.rb +1 -0
  20. data/lib/active_record/connection_adapters/hsqldb_adapter.rb +1 -0
  21. data/lib/active_record/connection_adapters/informix_adapter.rb +1 -0
  22. data/lib/active_record/connection_adapters/jdbc_adapter.rb +1 -0
  23. data/lib/active_record/connection_adapters/jndi_adapter.rb +1 -0
  24. data/lib/active_record/connection_adapters/mariadb_adapter.rb +1 -0
  25. data/lib/active_record/connection_adapters/mssql_adapter.rb +1 -0
  26. data/lib/active_record/connection_adapters/mysql2_adapter.rb +1 -0
  27. data/lib/active_record/connection_adapters/mysql_adapter.rb +1 -0
  28. data/lib/active_record/connection_adapters/postgresql_adapter.rb +1 -0
  29. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +1 -0
  30. data/lib/active_record/connection_adapters/sqlserver_adapter.rb +1 -0
  31. data/lib/activerecord-jdbc-adapter.rb +1 -0
  32. data/lib/arel/visitors/compat.rb +60 -0
  33. data/lib/arel/visitors/db2.rb +137 -0
  34. data/lib/arel/visitors/derby.rb +112 -0
  35. data/lib/arel/visitors/firebird.rb +79 -0
  36. data/lib/arel/visitors/h2.rb +25 -0
  37. data/lib/arel/visitors/hsqldb.rb +32 -0
  38. data/lib/arel/visitors/postgresql_jdbc.rb +6 -0
  39. data/lib/arel/visitors/sql_server.rb +225 -0
  40. data/lib/arel/visitors/sql_server/ng42.rb +294 -0
  41. data/lib/arel/visitors/sqlserver.rb +214 -0
  42. data/lib/arjdbc.rb +19 -0
  43. data/lib/arjdbc/abstract/connection_management.rb +35 -0
  44. data/lib/arjdbc/abstract/core.rb +74 -0
  45. data/lib/arjdbc/abstract/database_statements.rb +64 -0
  46. data/lib/arjdbc/abstract/statement_cache.rb +58 -0
  47. data/lib/arjdbc/abstract/transaction_support.rb +86 -0
  48. data/lib/arjdbc/db2.rb +4 -0
  49. data/lib/arjdbc/db2/adapter.rb +789 -0
  50. data/lib/arjdbc/db2/as400.rb +130 -0
  51. data/lib/arjdbc/db2/column.rb +167 -0
  52. data/lib/arjdbc/db2/connection_methods.rb +44 -0
  53. data/lib/arjdbc/derby.rb +3 -0
  54. data/lib/arjdbc/derby/active_record_patch.rb +13 -0
  55. data/lib/arjdbc/derby/adapter.rb +540 -0
  56. data/lib/arjdbc/derby/connection_methods.rb +20 -0
  57. data/lib/arjdbc/derby/schema_creation.rb +15 -0
  58. data/lib/arjdbc/discover.rb +104 -0
  59. data/lib/arjdbc/firebird.rb +4 -0
  60. data/lib/arjdbc/firebird/adapter.rb +434 -0
  61. data/lib/arjdbc/firebird/connection_methods.rb +23 -0
  62. data/lib/arjdbc/h2.rb +3 -0
  63. data/lib/arjdbc/h2/adapter.rb +303 -0
  64. data/lib/arjdbc/h2/connection_methods.rb +27 -0
  65. data/lib/arjdbc/hsqldb.rb +3 -0
  66. data/lib/arjdbc/hsqldb/adapter.rb +297 -0
  67. data/lib/arjdbc/hsqldb/connection_methods.rb +28 -0
  68. data/lib/arjdbc/hsqldb/explain_support.rb +35 -0
  69. data/lib/arjdbc/hsqldb/schema_creation.rb +11 -0
  70. data/lib/arjdbc/informix.rb +5 -0
  71. data/lib/arjdbc/informix/adapter.rb +162 -0
  72. data/lib/arjdbc/informix/connection_methods.rb +9 -0
  73. data/lib/arjdbc/jdbc.rb +59 -0
  74. data/lib/arjdbc/jdbc/adapter.rb +475 -0
  75. data/lib/arjdbc/jdbc/adapter_require.rb +46 -0
  76. data/lib/arjdbc/jdbc/base_ext.rb +15 -0
  77. data/lib/arjdbc/jdbc/callbacks.rb +53 -0
  78. data/lib/arjdbc/jdbc/column.rb +97 -0
  79. data/lib/arjdbc/jdbc/connection.rb +14 -0
  80. data/lib/arjdbc/jdbc/connection_methods.rb +37 -0
  81. data/lib/arjdbc/jdbc/error.rb +65 -0
  82. data/lib/arjdbc/jdbc/extension.rb +59 -0
  83. data/lib/arjdbc/jdbc/java.rb +13 -0
  84. data/lib/arjdbc/jdbc/railtie.rb +2 -0
  85. data/lib/arjdbc/jdbc/rake_tasks.rb +3 -0
  86. data/lib/arjdbc/jdbc/serialized_attributes_helper.rb +3 -0
  87. data/lib/arjdbc/jdbc/type_cast.rb +166 -0
  88. data/lib/arjdbc/jdbc/type_converter.rb +142 -0
  89. data/lib/arjdbc/mssql.rb +7 -0
  90. data/lib/arjdbc/mssql/adapter.rb +384 -0
  91. data/lib/arjdbc/mssql/column.rb +29 -0
  92. data/lib/arjdbc/mssql/connection_methods.rb +79 -0
  93. data/lib/arjdbc/mssql/database_statements.rb +134 -0
  94. data/lib/arjdbc/mssql/errors.rb +6 -0
  95. data/lib/arjdbc/mssql/explain_support.rb +129 -0
  96. data/lib/arjdbc/mssql/extensions.rb +36 -0
  97. data/lib/arjdbc/mssql/limit_helpers.rb +231 -0
  98. data/lib/arjdbc/mssql/lock_methods.rb +77 -0
  99. data/lib/arjdbc/mssql/old_adapter.rb +804 -0
  100. data/lib/arjdbc/mssql/old_column.rb +200 -0
  101. data/lib/arjdbc/mssql/quoting.rb +101 -0
  102. data/lib/arjdbc/mssql/schema_creation.rb +31 -0
  103. data/lib/arjdbc/mssql/schema_definitions.rb +74 -0
  104. data/lib/arjdbc/mssql/schema_statements.rb +329 -0
  105. data/lib/arjdbc/mssql/transaction.rb +69 -0
  106. data/lib/arjdbc/mssql/types.rb +52 -0
  107. data/lib/arjdbc/mssql/types/binary_types.rb +33 -0
  108. data/lib/arjdbc/mssql/types/date_and_time_types.rb +134 -0
  109. data/lib/arjdbc/mssql/types/deprecated_types.rb +40 -0
  110. data/lib/arjdbc/mssql/types/numeric_types.rb +71 -0
  111. data/lib/arjdbc/mssql/types/string_types.rb +56 -0
  112. data/lib/arjdbc/mssql/utils.rb +66 -0
  113. data/lib/arjdbc/mysql.rb +3 -0
  114. data/lib/arjdbc/mysql/adapter.rb +140 -0
  115. data/lib/arjdbc/mysql/connection_methods.rb +166 -0
  116. data/lib/arjdbc/oracle/adapter.rb +863 -0
  117. data/lib/arjdbc/postgresql.rb +3 -0
  118. data/lib/arjdbc/postgresql/adapter.rb +687 -0
  119. data/lib/arjdbc/postgresql/base/array_decoder.rb +26 -0
  120. data/lib/arjdbc/postgresql/base/array_encoder.rb +25 -0
  121. data/lib/arjdbc/postgresql/base/array_parser.rb +95 -0
  122. data/lib/arjdbc/postgresql/base/pgconn.rb +11 -0
  123. data/lib/arjdbc/postgresql/column.rb +51 -0
  124. data/lib/arjdbc/postgresql/connection_methods.rb +67 -0
  125. data/lib/arjdbc/postgresql/name.rb +24 -0
  126. data/lib/arjdbc/postgresql/oid_types.rb +266 -0
  127. data/lib/arjdbc/railtie.rb +11 -0
  128. data/lib/arjdbc/sqlite3.rb +3 -0
  129. data/lib/arjdbc/sqlite3/adapter.rb +678 -0
  130. data/lib/arjdbc/sqlite3/connection_methods.rb +59 -0
  131. data/lib/arjdbc/sybase.rb +2 -0
  132. data/lib/arjdbc/sybase/adapter.rb +47 -0
  133. data/lib/arjdbc/tasks.rb +13 -0
  134. data/lib/arjdbc/tasks/database_tasks.rb +31 -0
  135. data/lib/arjdbc/tasks/databases.rake +48 -0
  136. data/lib/arjdbc/tasks/db2_database_tasks.rb +104 -0
  137. data/lib/arjdbc/tasks/derby_database_tasks.rb +95 -0
  138. data/lib/arjdbc/tasks/h2_database_tasks.rb +31 -0
  139. data/lib/arjdbc/tasks/hsqldb_database_tasks.rb +70 -0
  140. data/lib/arjdbc/tasks/jdbc_database_tasks.rb +169 -0
  141. data/lib/arjdbc/tasks/mssql_database_tasks.rb +46 -0
  142. data/lib/arjdbc/util/quoted_cache.rb +60 -0
  143. data/lib/arjdbc/util/serialized_attributes.rb +98 -0
  144. data/lib/arjdbc/util/table_copier.rb +110 -0
  145. data/lib/arjdbc/version.rb +3 -0
  146. data/lib/generators/jdbc/USAGE +9 -0
  147. data/lib/generators/jdbc/jdbc_generator.rb +17 -0
  148. data/lib/jdbc_adapter.rb +2 -0
  149. data/lib/jdbc_adapter/rake_tasks.rb +4 -0
  150. data/lib/jdbc_adapter/version.rb +4 -0
  151. data/pom.xml +114 -0
  152. data/rails_generators/jdbc_generator.rb +15 -0
  153. data/rails_generators/templates/config/initializers/jdbc.rb +10 -0
  154. data/rails_generators/templates/lib/tasks/jdbc.rake +11 -0
  155. data/rakelib/01-tomcat.rake +51 -0
  156. data/rakelib/02-test.rake +132 -0
  157. data/rakelib/bundler_ext.rb +11 -0
  158. data/rakelib/db.rake +75 -0
  159. data/rakelib/rails.rake +223 -0
  160. data/src/java/arjdbc/ArJdbcModule.java +276 -0
  161. data/src/java/arjdbc/db2/DB2Module.java +76 -0
  162. data/src/java/arjdbc/db2/DB2RubyJdbcConnection.java +126 -0
  163. data/src/java/arjdbc/derby/DerbyModule.java +178 -0
  164. data/src/java/arjdbc/derby/DerbyRubyJdbcConnection.java +152 -0
  165. data/src/java/arjdbc/firebird/FirebirdRubyJdbcConnection.java +174 -0
  166. data/src/java/arjdbc/h2/H2Module.java +50 -0
  167. data/src/java/arjdbc/h2/H2RubyJdbcConnection.java +85 -0
  168. data/src/java/arjdbc/hsqldb/HSQLDBModule.java +73 -0
  169. data/src/java/arjdbc/informix/InformixRubyJdbcConnection.java +75 -0
  170. data/src/java/arjdbc/jdbc/AdapterJavaService.java +43 -0
  171. data/src/java/arjdbc/jdbc/Callable.java +44 -0
  172. data/src/java/arjdbc/jdbc/ConnectionFactory.java +45 -0
  173. data/src/java/arjdbc/jdbc/DataSourceConnectionFactory.java +156 -0
  174. data/src/java/arjdbc/jdbc/DriverConnectionFactory.java +63 -0
  175. data/src/java/arjdbc/jdbc/DriverWrapper.java +119 -0
  176. data/src/java/arjdbc/jdbc/JdbcResult.java +130 -0
  177. data/src/java/arjdbc/jdbc/RubyConnectionFactory.java +61 -0
  178. data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +3979 -0
  179. data/src/java/arjdbc/mssql/MSSQLModule.java +90 -0
  180. data/src/java/arjdbc/mssql/MSSQLRubyJdbcConnection.java +508 -0
  181. data/src/java/arjdbc/mysql/MySQLModule.java +152 -0
  182. data/src/java/arjdbc/mysql/MySQLRubyJdbcConnection.java +294 -0
  183. data/src/java/arjdbc/oracle/OracleModule.java +80 -0
  184. data/src/java/arjdbc/oracle/OracleRubyJdbcConnection.java +455 -0
  185. data/src/java/arjdbc/postgresql/ByteaUtils.java +157 -0
  186. data/src/java/arjdbc/postgresql/PgDateTimeUtils.java +52 -0
  187. data/src/java/arjdbc/postgresql/PostgreSQLModule.java +77 -0
  188. data/src/java/arjdbc/postgresql/PostgreSQLResult.java +192 -0
  189. data/src/java/arjdbc/postgresql/PostgreSQLRubyJdbcConnection.java +948 -0
  190. data/src/java/arjdbc/sqlite3/SQLite3Module.java +73 -0
  191. data/src/java/arjdbc/sqlite3/SQLite3RubyJdbcConnection.java +525 -0
  192. data/src/java/arjdbc/util/CallResultSet.java +826 -0
  193. data/src/java/arjdbc/util/DateTimeUtils.java +699 -0
  194. data/src/java/arjdbc/util/ObjectSupport.java +65 -0
  195. data/src/java/arjdbc/util/QuotingUtils.java +137 -0
  196. data/src/java/arjdbc/util/StringCache.java +63 -0
  197. data/src/java/arjdbc/util/StringHelper.java +145 -0
  198. metadata +269 -0
@@ -0,0 +1,200 @@
1
+ module ArJdbc
2
+ module MSSQL
3
+
4
+ # @see ActiveRecord::ConnectionAdapters::JdbcColumn#column_types
5
+ def self.column_selector
6
+ [ /sqlserver|tds|Microsoft SQL/i, lambda { |config, column| column.extend(Column) } ]
7
+ end
8
+
9
+ # @see ActiveRecord::ConnectionAdapters::JdbcColumn
10
+ module Column
11
+
12
+ def self.included(base)
13
+ # NOTE: assumes a standalone MSSQLColumn class
14
+ class << base; include Cast; end
15
+ end
16
+
17
+ include LockMethods unless AR42
18
+
19
+ # @override
20
+ def simplified_type(field_type)
21
+ case field_type
22
+ when /int|bigint|smallint|tinyint/i then :integer
23
+ when /numeric/i then (@scale.nil? || @scale == 0) ? :integer : :decimal
24
+ when /float|double|money|real|smallmoney/i then :decimal
25
+ when /datetime|smalldatetime/i then :datetime
26
+ when /timestamp/i then :timestamp
27
+ when /time/i then :time
28
+ when /date/i then :date
29
+ when /text|ntext|xml/i then :text
30
+ when /binary|image|varbinary/i then :binary
31
+ when /char|nchar|nvarchar|string|varchar/i then (@limit == 1073741823 ? (@limit = nil; :text) : :string)
32
+ when /bit/i then :boolean
33
+ when /uniqueidentifier/i then :string
34
+ else
35
+ super
36
+ end
37
+ end
38
+
39
+ # @override
40
+ def default_value(value)
41
+ return $1 if value =~ /^\(N?'(.*)'\)$/ || value =~ /^\(\(?(.*?)\)?\)$/
42
+ value
43
+ end
44
+
45
+ # @override
46
+ def type_cast(value)
47
+ return nil if value.nil?
48
+ case type
49
+ when :integer then ( value.is_a?(String) ? unquote(value) : (value || 0) ).to_i
50
+ when :primary_key then value.respond_to?(:to_i) ? value.to_i : ((value && 1) || 0)
51
+ when :decimal then self.class.value_to_decimal(unquote(value))
52
+ when :date then self.class.string_to_date(value)
53
+ when :datetime then self.class.string_to_time(value)
54
+ when :timestamp then self.class.string_to_time(value)
55
+ when :time then self.class.string_to_dummy_time(value)
56
+ when :boolean then value == true || (value =~ /^t(rue)?$/i) == 0 || unquote(value) == '1'
57
+ when :binary then unquote(value)
58
+ else value
59
+ end
60
+ end
61
+
62
+ # @override
63
+ def extract_limit(sql_type)
64
+ case sql_type
65
+ when /^smallint/i
66
+ 2
67
+ when /^int/i
68
+ 4
69
+ when /^bigint/i
70
+ 8
71
+ when /\(max\)/, /decimal/, /numeric/
72
+ nil
73
+ when /text|ntext|xml|binary|image|varbinary|bit/
74
+ nil
75
+ else
76
+ super
77
+ end
78
+ end
79
+
80
+ # #primary replacement that works on 4.2 as well
81
+ # #columns will set @primary even when on AR 4.2
82
+ def primary?; @primary end
83
+ alias_method :is_primary, :primary?
84
+
85
+ def identity?
86
+ !! sql_type.downcase.index('identity')
87
+ end
88
+ # @deprecated
89
+ alias_method :identity, :identity?
90
+ alias_method :is_identity, :identity?
91
+
92
+ # NOTE: these do not handle = equality as expected
93
+ # see {#repair_special_columns}
94
+ # (TEXT, NTEXT, and IMAGE data types are deprecated)
95
+ # @private
96
+ def special?
97
+ unless defined? @special # /text|ntext|image|xml/i
98
+ sql_type = @sql_type.downcase
99
+ @special = !! ( sql_type.index('text') || sql_type.index('image') || sql_type.index('xml') )
100
+ end
101
+ @special
102
+ end
103
+ # @deprecated
104
+ alias_method :special, :special?
105
+ alias_method :is_special, :special?
106
+
107
+ def is_utf8?
108
+ !!( sql_type =~ /nvarchar|ntext|nchar/i )
109
+ end
110
+
111
+ private
112
+
113
+ def unquote(value)
114
+ value.to_s.sub(/\A\([\(\']?/, "").sub(/[\'\)]?\)\Z/, "")
115
+ end
116
+
117
+ # @deprecated no longer used
118
+ def cast_to_time(value)
119
+ return value if value.is_a?(Time)
120
+ DateTime.parse(value).to_time rescue nil
121
+ end
122
+
123
+ # @deprecated no longer used
124
+ def cast_to_date(value)
125
+ return value if value.is_a?(Date)
126
+ return Date.parse(value) rescue nil
127
+ end
128
+
129
+ # @deprecated no longer used
130
+ def cast_to_datetime(value)
131
+ if value.is_a?(Time)
132
+ if value.year != 0 and value.month != 0 and value.day != 0
133
+ return value
134
+ else
135
+ return Time.mktime(2000, 1, 1, value.hour, value.min, value.sec) rescue nil
136
+ end
137
+ end
138
+ if value.is_a?(DateTime)
139
+ begin
140
+ # Attempt to convert back to a Time, but it could fail for dates significantly in the past/future.
141
+ return Time.mktime(value.year, value.mon, value.day, value.hour, value.min, value.sec)
142
+ rescue ArgumentError
143
+ return value
144
+ end
145
+ end
146
+
147
+ return cast_to_time(value) if value.is_a?(Date) or value.is_a?(String) rescue nil
148
+
149
+ return value.is_a?(Date) ? value : nil
150
+ end
151
+
152
+ module Cast
153
+
154
+ def string_to_date(value)
155
+ return value unless value.is_a?(String)
156
+ return nil if value.empty?
157
+
158
+ date = fast_string_to_date(value)
159
+ date ? date : Date.parse(value) rescue nil
160
+ end
161
+
162
+ def string_to_time(value)
163
+ return value unless value.is_a?(String)
164
+ return nil if value.empty?
165
+
166
+ fast_string_to_time(value) || DateTime.parse(value).to_time rescue nil
167
+ end
168
+
169
+ ISO_TIME = /\A(\d\d)\:(\d\d)\:(\d\d)(\.\d+)?\z/
170
+
171
+ def string_to_dummy_time(value)
172
+ return value unless value.is_a?(String)
173
+ return nil if value.empty?
174
+
175
+ if value =~ ISO_TIME # "12:34:56.1234560"
176
+ microsec = ($4.to_f * 1_000_000).round.to_i
177
+ new_time 2000, 1, 1, $1.to_i, $2.to_i, $3.to_i, microsec
178
+ else
179
+ super(value)
180
+ end
181
+ end
182
+
183
+ def string_to_binary(value)
184
+ # this will only allow the adapter to insert binary data with a length
185
+ # of 7K or less because of a SQL Server statement length policy ...
186
+ "0x#{value.unpack("H*")}" # "0x#{value.unpack("H*")[0]}"
187
+ end
188
+
189
+ def binary_to_string(value)
190
+ if value.respond_to?(:force_encoding) && value.encoding != Encoding::ASCII_8BIT
191
+ value = value.force_encoding(Encoding::ASCII_8BIT)
192
+ end
193
+ value =~ /[^[:xdigit:]]/ ? value : [value].pack('H*')
194
+ end
195
+
196
+ end
197
+
198
+ end
199
+ end
200
+ end
@@ -0,0 +1,101 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module MSSQL
4
+ module Quoting
5
+ # Quote date/time values for use in SQL input, includes microseconds
6
+ # with three digits only if the value is a Time responding to usec.
7
+ # The JDBC drivers does not work with 6 digits microseconds
8
+ def quoted_date(value)
9
+ if value.acts_like?(:time)
10
+ value = time_with_db_timezone(value)
11
+ end
12
+
13
+ result = value.to_s(:db)
14
+
15
+ if value.respond_to?(:usec) && value.usec > 0
16
+ "#{result}.#{sprintf("%06d", value.usec)}"
17
+ else
18
+ result
19
+ end
20
+ end
21
+
22
+ # Quotes strings for use in SQL input.
23
+ def quote_string(s)
24
+ s.to_s.gsub(/\'/, "''")
25
+ end
26
+
27
+ # Does not quote function default values for UUID columns
28
+ def quote_default_expression(value, column)
29
+ cast_type = lookup_cast_type(column.sql_type)
30
+ if cast_type.type == :uuid && value =~ /\(\)/
31
+ value
32
+ else
33
+ super
34
+ end
35
+ end
36
+
37
+ def quoted_true
38
+ 1
39
+ end
40
+
41
+ def quoted_false
42
+ 0
43
+ end
44
+
45
+ # @override
46
+ def quoted_time(value)
47
+ if value.acts_like?(:time)
48
+ tz_value = time_with_db_timezone(value)
49
+ usec = value.respond_to?(:usec) ? value.usec : 0
50
+ sprintf('%02d:%02d:%02d.%06d', tz_value.hour, tz_value.min, tz_value.sec, usec)
51
+ else
52
+ quoted_date(value)
53
+ end
54
+ end
55
+
56
+ # @private
57
+ # @see #quote in old adapter
58
+ BLOB_VALUE_MARKER = "''"
59
+
60
+ private
61
+
62
+ def time_with_db_timezone(value)
63
+ zone_conv_method = if ActiveRecord::Base.default_timezone == :utc
64
+ :getutc
65
+ else
66
+ :getlocal
67
+ end
68
+
69
+ if value.respond_to?(zone_conv_method)
70
+ value = value.send(zone_conv_method)
71
+ else
72
+ value
73
+ end
74
+ end
75
+
76
+ # @override
77
+ # FIXME: it need to be improved to handle other custom types.
78
+ # Also check if it's possible insert integer into a NVARCHAR
79
+ def _quote(value)
80
+ case value
81
+ when ActiveRecord::Type::Binary::Data
82
+ "0x#{value.hex}"
83
+ # when SomeOtherBinaryData then BLOB_VALUE_MARKER
84
+ # when SomeOtherData then "yyy"
85
+ when String, ActiveSupport::Multibyte::Chars
86
+ "N'#{quote_string(value)}'"
87
+ # when OnlyTimeType then "'#{quoted_time(value)}'"
88
+ when Date, Time
89
+ "'#{quoted_date(value)}'"
90
+ when TrueClass
91
+ quoted_true
92
+ when FalseClass
93
+ quoted_false
94
+ else
95
+ super
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,31 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module MSSQL
4
+ class SchemaCreation < AbstractAdapter::SchemaCreation
5
+ private
6
+
7
+ def visit_TableDefinition(o)
8
+ if o.as
9
+ table_name = quote_table_name(o.temporary ? "##{o.name}" : o.name)
10
+ projections, source = @conn.to_sql(o.as).match(%r{SELECT\s+(.*)?\s+FROM\s+(.*)?}).captures
11
+ select_into = "SELECT #{projections} INTO #{table_name} FROM #{source}"
12
+ else
13
+ o.instance_variable_set :@as, nil
14
+ super
15
+ end
16
+ end
17
+
18
+ # There is no RESTRICT in MSSQL but it has NO ACTION which behave
19
+ # same as RESTRICT, added this behave according rails api.
20
+ def action_sql(action, dependency)
21
+ case dependency
22
+ when :restrict then "ON #{action} NO ACTION"
23
+ else
24
+ super
25
+ end
26
+ end
27
+
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,74 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module MSSQL
4
+ module ColumnMethods
5
+ # datetime with seconds always zero (:00) and without fractional seconds
6
+ def smalldatetime(*args, **options)
7
+ args.each { |name| column(name, :smalldatetime, options) }
8
+ end
9
+
10
+ # this is the old sql server datetime type, the precision is as follow
11
+ # xx1, xx3, and xx7
12
+ def datetime_basic(*args, **options)
13
+ args.each { |name| column(name, :datetime_basic, options) }
14
+ end
15
+
16
+ def real(*args, **options)
17
+ args.each { |name| column(name, :real, options) }
18
+ end
19
+
20
+ def money(*args, **options)
21
+ args.each { |name| column(name, :money, options) }
22
+ end
23
+
24
+ def smallmoney(*args, **options)
25
+ args.each { |name| column(name, :smallmoney, options) }
26
+ end
27
+
28
+ def char(*args, **options)
29
+ args.each { |name| column(name, :char, options) }
30
+ end
31
+
32
+ def varchar(*args, **options)
33
+ args.each { |name| column(name, :varchar, options) }
34
+ end
35
+
36
+ def varchar_max(*args, **options)
37
+ args.each { |name| column(name, :varchar_max, options) }
38
+ end
39
+
40
+ def text_basic(*args, **options)
41
+ args.each { |name| column(name, :text_basic, options) }
42
+ end
43
+
44
+ def nchar(*args, **options)
45
+ args.each { |name| column(name, :nchar, options) }
46
+ end
47
+
48
+ def ntext(*args, **options)
49
+ args.each { |name| column(name, :ntext, options) }
50
+ end
51
+
52
+ def binary_basic(*args, **options)
53
+ args.each { |name| column(name, :binary_basic, options) }
54
+ end
55
+
56
+ def varbinary(*args, **options)
57
+ args.each { |name| column(name, :varbinary, options) }
58
+ end
59
+
60
+ def uuid(*args, **options)
61
+ args.each { |name| column(name, :uniqueidentifier, options) }
62
+ end
63
+ end
64
+
65
+ class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition
66
+ include ColumnMethods
67
+ end
68
+
69
+ class Table < ActiveRecord::ConnectionAdapters::Table
70
+ include ColumnMethods
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,329 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module MSSQL
4
+ module SchemaStatements
5
+
6
+ NATIVE_DATABASE_TYPES = {
7
+ # Logical Rails types to SQL Server types
8
+ primary_key: 'int NOT NULL IDENTITY(1,1) PRIMARY KEY',
9
+ integer: { name: 'int', limit: 4 },
10
+ boolean: { name: 'bit' },
11
+ decimal: { name: 'decimal' },
12
+ float: { name: 'float' },
13
+ date: { name: 'date' },
14
+ time: { name: 'time' },
15
+ datetime: { name: 'datetime2' },
16
+ string: { name: 'nvarchar', limit: 4000 },
17
+ text: { name: 'nvarchar(max)' },
18
+ binary: { name: 'varbinary(max)' },
19
+ # Other types or SQL Server specific
20
+ bigint: { name: 'bigint' },
21
+ smalldatetime: { name: 'smalldatetime' },
22
+ datetime_basic: { name: 'datetime' },
23
+ timestamp: { name: 'datetime' },
24
+ real: { name: 'real' },
25
+ money: { name: 'money' },
26
+ smallmoney: { name: 'smallmoney' },
27
+ char: { name: 'char' },
28
+ nchar: { name: 'nchar' },
29
+ varchar: { name: 'varchar', limit: 8000 },
30
+ varchar_max: { name: 'varchar(max)' },
31
+ uuid: { name: 'uniqueidentifier' },
32
+ binary_basic: { name: 'binary' },
33
+ varbinary: { name: 'varbinary', limit: 8000 },
34
+ # Deprecated SQL Server types
35
+ image: { name: 'image' },
36
+ ntext: { name: 'ntext' },
37
+ text_basic: { name: 'text' }
38
+ }.freeze
39
+
40
+ def native_database_types
41
+ NATIVE_DATABASE_TYPES
42
+ end
43
+
44
+ # Returns an array of table names defined in the database.
45
+ def tables(name = nil)
46
+ if name
47
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
48
+ Passing arguments to #tables is deprecated without replacement.
49
+ MSG
50
+ end
51
+
52
+ @connection.tables(nil, name)
53
+ end
54
+
55
+ # Returns an array of Column objects for the table specified by +table_name+.
56
+ # See the concrete implementation for details on the expected parameter values.
57
+ # NOTE: This is ready, all implemented in the java part of adapter,
58
+ # it uses MSSQLColumn, SqlTypeMetadata, etc.
59
+ def columns(table_name)
60
+ @connection.columns(table_name)
61
+ rescue => e
62
+ raise translate_exception_class(e, nil)
63
+ end
64
+
65
+ # Returns an array of view names defined in the database.
66
+ # (to be implemented)
67
+ def views
68
+ []
69
+ end
70
+
71
+ def table_exists?(table_name)
72
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
73
+ #table_exists? currently checks both tables and views.
74
+ This behavior is deprecated and will be changed with Rails 5.1 to only check tables.
75
+ Use #data_source_exists? instead.
76
+ MSG
77
+
78
+ tables.include?(table_name.to_s)
79
+ end
80
+
81
+ # Returns an array of indexes for the given table.
82
+ def indexes(table_name, name = nil)
83
+ @connection.indexes(table_name, name)
84
+ end
85
+
86
+ def primary_keys(table_name)
87
+ @connection.primary_keys(table_name)
88
+ end
89
+
90
+ def foreign_keys(table_name)
91
+ @connection.foreign_keys(table_name)
92
+ end
93
+
94
+ def charset
95
+ select_value "SELECT SqlCharSetName = CAST(SERVERPROPERTY('SqlCharSetName') AS NVARCHAR(128))"
96
+ end
97
+
98
+ def collation
99
+ select_value "SELECT Collation = CAST(SERVERPROPERTY('Collation') AS NVARCHAR(128))"
100
+ end
101
+
102
+ def current_database
103
+ select_value 'SELECT DB_NAME()'
104
+ end
105
+
106
+ def use_database(database = nil)
107
+ database ||= config[:database]
108
+ execute "USE #{quote_database_name(database)}" unless database.blank?
109
+ end
110
+
111
+ def drop_database(name)
112
+ current_db = current_database
113
+ use_database('master') if current_db.to_s == name
114
+ # Only SQL Server 2016 onwards:
115
+ # execute "DROP DATABASE IF EXISTS #{quote_database_name(name)}"
116
+ execute "IF EXISTS(SELECT name FROM sys.databases WHERE name='#{name}') DROP DATABASE #{quote_database_name(name)}"
117
+ end
118
+
119
+ def create_database(name, options = {})
120
+ execute "CREATE DATABASE #{quote_database_name(name)}"
121
+ end
122
+
123
+ def recreate_database(name, options = {})
124
+ drop_database(name)
125
+ create_database(name, options)
126
+ end
127
+
128
+ def remove_column(table_name, column_name, type = nil, options = {})
129
+ raise ArgumentError.new('You must specify at least one column name. Example: remove_column(:people, :first_name)') if column_name.is_a? Array
130
+ remove_check_constraints(table_name, column_name)
131
+ remove_default_constraint(table_name, column_name)
132
+ remove_indexes(table_name, column_name)
133
+ execute "ALTER TABLE #{quote_table_name(table_name)} DROP COLUMN #{quote_column_name(column_name)}"
134
+ end
135
+
136
+ def drop_table(table_name, options = {})
137
+ # mssql cannot recreate referenced table with force: :cascade
138
+ # https://docs.microsoft.com/en-us/sql/t-sql/statements/drop-table-transact-sql?view=sql-server-2017
139
+ if options[:force] == :cascade
140
+ execute_procedure(:sp_fkeys, pktable_name: table_name).each do |fkdata|
141
+ fktable = fkdata['FKTABLE_NAME']
142
+ fkcolmn = fkdata['FKCOLUMN_NAME']
143
+ pktable = fkdata['PKTABLE_NAME']
144
+ pkcolmn = fkdata['PKCOLUMN_NAME']
145
+ remove_foreign_key(fktable, name: fkdata['FK_NAME'])
146
+ execute("DELETE FROM #{quote_table_name(fktable)} WHERE #{quote_column_name(fkcolmn)} IN ( SELECT #{quote_column_name(pkcolmn)} FROM #{quote_table_name(pktable)} )")
147
+ end
148
+ end
149
+
150
+ if options[:if_exists] && @mssql_major_version < 13
151
+ # this is for sql server 2012 and 2014
152
+ execute "IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = #{quote(table_name)}) DROP TABLE #{quote_table_name(table_name)}"
153
+ else
154
+ # For sql server 2016 onwards
155
+ super
156
+ end
157
+ end
158
+
159
+ def rename_table(table_name, new_table_name)
160
+ execute "EXEC sp_rename '#{table_name}', '#{new_table_name}'"
161
+ rename_table_indexes(table_name, new_table_name)
162
+ end
163
+
164
+ # This is the same as the abstract method
165
+ def quote_table_name(name)
166
+ quote_column_name(name)
167
+ end
168
+
169
+ # This overrides the abstract method to be specific to SQL Server.
170
+ def quote_column_name(name)
171
+ name = name.to_s.split('.')
172
+ name.map! { |n| quote_name_part(n) } # "[#{name}]"
173
+ name.join('.')
174
+ end
175
+
176
+ def quote_database_name(name)
177
+ quote_name_part(name.to_s)
178
+ end
179
+
180
+ # @private these cannot specify a limit
181
+ NO_LIMIT_TYPES = %w(text binary boolean date)
182
+
183
+ def type_to_sql(type, limit = nil, precision = nil, scale = nil)
184
+ type_s = type.to_s
185
+ # MSSQL's NVARCHAR(n | max) column supports either a number between 1 and
186
+ # 4000, or the word "MAX", which corresponds to 2**30-1 UCS-2 characters.
187
+ #
188
+ # It does not accept NVARCHAR(1073741823) here, so we have to change it
189
+ # to NVARCHAR(MAX), even though they are logically equivalent.
190
+ #
191
+ # See: http://msdn.microsoft.com/en-us/library/ms186939.aspx
192
+ #
193
+ if type_s == 'string' && limit == 1073741823
194
+ 'NVARCHAR(MAX)'
195
+ elsif NO_LIMIT_TYPES.include?(type_s)
196
+ super(type)
197
+ elsif type_s == 'integer' || type_s == 'int'
198
+ if limit.nil? || limit == 4
199
+ 'int'
200
+ elsif limit == 2
201
+ 'smallint'
202
+ elsif limit == 1
203
+ 'tinyint'
204
+ else
205
+ 'bigint'
206
+ end
207
+ elsif type_s == 'uniqueidentifier'
208
+ type_s
209
+ else
210
+ super
211
+ end
212
+ end
213
+
214
+ # SQL Server requires the ORDER BY columns in the select
215
+ # list for distinct queries, and requires that the ORDER BY
216
+ # include the distinct column.
217
+ def columns_for_distinct(columns, orders) #:nodoc:
218
+ order_columns = orders.reject(&:blank?).map{ |s|
219
+ # Convert Arel node to string
220
+ s = s.to_sql unless s.is_a?(String)
221
+ # Remove any ASC/DESC modifiers
222
+ s.gsub(/\s+(?:ASC|DESC)\b/i, '')
223
+ .gsub(/\s+NULLS\s+(?:FIRST|LAST)\b/i, '')
224
+ }.reject(&:blank?).map.with_index { |column, i| "#{column} AS alias_#{i}" }
225
+
226
+ [super, *order_columns].join(', ')
227
+ end
228
+
229
+ def rename_column(table_name, column_name, new_column_name)
230
+ # The below line checks if column exists otherwise raise activerecord
231
+ # default exception for this case.
232
+ _column = column_for(table_name, column_name)
233
+
234
+ execute "EXEC sp_rename '#{table_name}.#{column_name}', '#{new_column_name}', 'COLUMN'"
235
+ rename_column_indexes(table_name, column_name, new_column_name)
236
+ end
237
+
238
+ def change_column_default(table_name, column_name, default_or_changes)
239
+ remove_default_constraint(table_name, column_name)
240
+
241
+ default = extract_new_default_value(default_or_changes)
242
+ unless default.nil?
243
+ column = columns(table_name).find { |c| c.name.to_s == column_name.to_s }
244
+ result = execute "ALTER TABLE #{quote_table_name(table_name)} ADD CONSTRAINT DF_#{table_name}_#{column_name} DEFAULT #{quote_default_expression(default, column)} FOR #{quote_column_name(column_name)}"
245
+ result
246
+ end
247
+ end
248
+
249
+ def change_column(table_name, column_name, type, options = {})
250
+ column = columns(table_name).find { |c| c.name.to_s == column_name.to_s }
251
+
252
+ indexes = []
253
+ if options_include_default?(options) || (column && column.type != type.to_sym)
254
+ remove_default_constraint(table_name, column_name)
255
+ indexes = indexes(table_name).select{ |index| index.columns.include?(column_name.to_s) }
256
+ remove_indexes(table_name, column_name)
257
+ end
258
+
259
+ if !options[:null].nil? && options[:null] == false && !options[:default].nil?
260
+ execute "UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote_default_expression(options[:default], column)} WHERE #{quote_column_name(column_name)} IS NULL"
261
+ end
262
+
263
+ change_column_type(table_name, column_name, type, options)
264
+ change_column_default(table_name, column_name, options[:default]) if options_include_default?(options)
265
+
266
+ # add any removed indexes back
267
+ indexes.each do |index|
268
+ index_columns = index.columns.map { |c| quote_column_name(c) }.join(', ')
269
+ execute "CREATE INDEX #{quote_table_name(index.name)} ON #{quote_table_name(table_name)} (#{index_columns})"
270
+ end
271
+ end
272
+
273
+ def change_column_null(table_name, column_name, null, default = nil)
274
+ column = column_for(table_name, column_name)
275
+ quoted_table = quote_table_name(table_name)
276
+ quoted_column = quote_column_name(column_name)
277
+ quoted_default = quote(default)
278
+ unless null || default.nil?
279
+ execute("UPDATE #{quoted_table} SET #{quoted_column}=#{quoted_default} WHERE #{quoted_column} IS NULL")
280
+ end
281
+ sql_alter = [
282
+ "ALTER TABLE #{quoted_table}",
283
+ "ALTER COLUMN #{quoted_column} #{type_to_sql column.type, column.limit, column.precision, column.scale}",
284
+ (' NOT NULL' unless null)
285
+ ]
286
+
287
+ execute(sql_alter.join(' '))
288
+ end
289
+
290
+ private
291
+
292
+ def change_column_type(table_name, column_name, type, options = {})
293
+ 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])}"
294
+ sql << (options[:null] ? " NULL" : " NOT NULL") if options.has_key?(:null)
295
+ result = execute(sql)
296
+ result
297
+ end
298
+
299
+ # Implements the quoting style for SQL Server
300
+ def quote_name_part(part)
301
+ part =~ /^\[.*\]$/ ? part : "[#{part.gsub(']', ']]')}]"
302
+ end
303
+
304
+ def remove_check_constraints(table_name, column_name)
305
+ constraints = select_values "SELECT CONSTRAINT_NAME FROM INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE where TABLE_NAME = '#{quote_string(table_name)}' and COLUMN_NAME = '#{quote_string(column_name)}'", 'SCHEMA'
306
+ constraints.each do |constraint|
307
+ execute "ALTER TABLE #{quote_table_name(table_name)} DROP CONSTRAINT #{quote_column_name(constraint)}"
308
+ end
309
+ end
310
+
311
+ def remove_default_constraint(table_name, column_name)
312
+ # If their are foreign keys in this table, we could still get back a 2D array, so flatten just in case.
313
+ execute_procedure(:sp_helpconstraint, table_name, 'nomsg').flatten.select do |row|
314
+ row['constraint_type'] == "DEFAULT on column #{column_name}"
315
+ end.each do |row|
316
+ execute "ALTER TABLE #{quote_table_name(table_name)} DROP CONSTRAINT #{row['constraint_name']}"
317
+ end
318
+ end
319
+
320
+ def remove_indexes(table_name, column_name)
321
+ indexes(table_name).select { |index| index.columns.include?(column_name.to_s) }.each do |index|
322
+ remove_index(table_name, name: index.name)
323
+ end
324
+ end
325
+
326
+ end
327
+ end
328
+ end
329
+ end