kb-activerecord-jdbc-adapter 0.9.7.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.
- data/History.txt +296 -0
- data/LICENSE.txt +21 -0
- data/Manifest.txt +139 -0
- data/README.txt +219 -0
- data/Rakefile +10 -0
- data/lib/active_record/connection_adapters/cachedb_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/derby_adapter.rb +13 -0
- data/lib/active_record/connection_adapters/h2_adapter.rb +13 -0
- data/lib/active_record/connection_adapters/hsqldb_adapter.rb +13 -0
- data/lib/active_record/connection_adapters/informix_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/jdbc_adapter.rb +661 -0
- data/lib/active_record/connection_adapters/jdbc_adapter_spec.rb +26 -0
- data/lib/active_record/connection_adapters/jndi_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/mssql_adapter.rb +13 -0
- data/lib/active_record/connection_adapters/mysql_adapter.rb +13 -0
- data/lib/active_record/connection_adapters/oracle_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +13 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +13 -0
- data/lib/activerecord-jdbc-adapter.rb +6 -0
- data/lib/arel/engines/sql/compilers/db2_compiler.rb +9 -0
- data/lib/arel/engines/sql/compilers/derby_compiler.rb +6 -0
- data/lib/arel/engines/sql/compilers/h2_compiler.rb +6 -0
- data/lib/arel/engines/sql/compilers/hsqldb_compiler.rb +6 -0
- data/lib/arel/engines/sql/compilers/jdbc_compiler.rb +6 -0
- data/lib/generators/jdbc/jdbc_generator.rb +9 -0
- data/lib/jdbc_adapter.rb +27 -0
- data/lib/jdbc_adapter/jdbc.rake +122 -0
- data/lib/jdbc_adapter/jdbc_adapter_internal.jar +0 -0
- data/lib/jdbc_adapter/jdbc_cachedb.rb +33 -0
- data/lib/jdbc_adapter/jdbc_db2.rb +222 -0
- data/lib/jdbc_adapter/jdbc_derby.rb +426 -0
- data/lib/jdbc_adapter/jdbc_firebird.rb +109 -0
- data/lib/jdbc_adapter/jdbc_hsqldb.rb +221 -0
- data/lib/jdbc_adapter/jdbc_informix.rb +147 -0
- data/lib/jdbc_adapter/jdbc_mimer.rb +145 -0
- data/lib/jdbc_adapter/jdbc_mssql.rb +468 -0
- data/lib/jdbc_adapter/jdbc_mysql.rb +260 -0
- data/lib/jdbc_adapter/jdbc_oracle.rb +397 -0
- data/lib/jdbc_adapter/jdbc_postgre.rb +531 -0
- data/lib/jdbc_adapter/jdbc_sqlite3.rb +386 -0
- data/lib/jdbc_adapter/jdbc_sybase.rb +50 -0
- data/lib/jdbc_adapter/missing_functionality_helper.rb +87 -0
- data/lib/jdbc_adapter/railtie.rb +9 -0
- data/lib/jdbc_adapter/rake_tasks.rb +10 -0
- data/lib/jdbc_adapter/tsql_helper.rb +69 -0
- data/lib/jdbc_adapter/version.rb +5 -0
- data/lib/pg.rb +4 -0
- data/rails_generators/jdbc_generator.rb +15 -0
- data/rails_generators/templates/config/initializers/jdbc.rb +7 -0
- data/rails_generators/templates/lib/tasks/jdbc.rake +8 -0
- data/rakelib/compile.rake +23 -0
- data/rakelib/package.rake +91 -0
- data/rakelib/rails.rake +41 -0
- data/rakelib/test.rake +78 -0
- data/src/java/jdbc_adapter/JdbcAdapterInternalService.java +53 -0
- data/src/java/jdbc_adapter/JdbcConnectionFactory.java +36 -0
- data/src/java/jdbc_adapter/JdbcDerbySpec.java +293 -0
- data/src/java/jdbc_adapter/JdbcMySQLSpec.java +134 -0
- data/src/java/jdbc_adapter/MssqlRubyJdbcConnection.java +71 -0
- data/src/java/jdbc_adapter/PostgresRubyJdbcConnection.java +55 -0
- data/src/java/jdbc_adapter/RubyJdbcConnection.java +1176 -0
- data/src/java/jdbc_adapter/SQLBlock.java +27 -0
- data/src/java/jdbc_adapter/Sqlite3RubyJdbcConnection.java +41 -0
- data/test/abstract_db_create.rb +107 -0
- data/test/activerecord/connection_adapters/type_conversion_test.rb +31 -0
- data/test/activerecord/connections/native_jdbc_mysql/connection.rb +25 -0
- data/test/cachedb_simple_test.rb +6 -0
- data/test/db/cachedb.rb +9 -0
- data/test/db/db2.rb +9 -0
- data/test/db/derby.rb +14 -0
- data/test/db/h2.rb +11 -0
- data/test/db/hsqldb.rb +12 -0
- data/test/db/informix.rb +11 -0
- data/test/db/jdbc.rb +11 -0
- data/test/db/jndi_config.rb +30 -0
- data/test/db/logger.rb +3 -0
- data/test/db/mssql.rb +9 -0
- data/test/db/mysql.rb +10 -0
- data/test/db/oracle.rb +34 -0
- data/test/db/postgres.rb +9 -0
- data/test/db/sqlite3.rb +15 -0
- data/test/db2_simple_test.rb +10 -0
- data/test/derby_migration_test.rb +21 -0
- data/test/derby_multibyte_test.rb +12 -0
- data/test/derby_simple_test.rb +21 -0
- data/test/generic_jdbc_connection_test.rb +9 -0
- data/test/h2_simple_test.rb +6 -0
- data/test/has_many_through.rb +79 -0
- data/test/helper.rb +5 -0
- data/test/hsqldb_simple_test.rb +6 -0
- data/test/informix_simple_test.rb +48 -0
- data/test/jdbc_adapter/jdbc_db2_test.rb +26 -0
- data/test/jdbc_adapter/jdbc_sybase_test.rb +33 -0
- data/test/jdbc_common.rb +25 -0
- data/test/jndi_callbacks_test.rb +38 -0
- data/test/jndi_test.rb +35 -0
- data/test/manualTestDatabase.rb +191 -0
- data/test/minirunit.rb +109 -0
- data/test/minirunit/testConnect.rb +14 -0
- data/test/minirunit/testH2.rb +73 -0
- data/test/minirunit/testHsqldb.rb +73 -0
- data/test/minirunit/testLoadActiveRecord.rb +3 -0
- data/test/minirunit/testMysql.rb +83 -0
- data/test/minirunit/testRawSelect.rb +24 -0
- data/test/models/add_not_null_column_to_table.rb +12 -0
- data/test/models/auto_id.rb +18 -0
- data/test/models/data_types.rb +28 -0
- data/test/models/entry.rb +23 -0
- data/test/models/mixed_case.rb +20 -0
- data/test/models/reserved_word.rb +18 -0
- data/test/models/string_id.rb +18 -0
- data/test/models/validates_uniqueness_of_string.rb +19 -0
- data/test/mssql_db_create_test.rb +26 -0
- data/test/mssql_identity_insert_test.rb +19 -0
- data/test/mssql_legacy_types_test.rb +58 -0
- data/test/mssql_limit_offset_test.rb +108 -0
- data/test/mssql_multibyte_test.rb +18 -0
- data/test/mssql_simple_test.rb +49 -0
- data/test/mysql_db_create_test.rb +25 -0
- data/test/mysql_info_test.rb +62 -0
- data/test/mysql_multibyte_test.rb +10 -0
- data/test/mysql_nonstandard_primary_key_test.rb +42 -0
- data/test/mysql_simple_test.rb +32 -0
- data/test/oracle_simple_test.rb +54 -0
- data/test/pick_rails_version.rb +3 -0
- data/test/postgres_db_create_test.rb +21 -0
- data/test/postgres_mixed_case_test.rb +19 -0
- data/test/postgres_nonseq_pkey_test.rb +40 -0
- data/test/postgres_reserved_test.rb +22 -0
- data/test/postgres_schema_search_path_test.rb +46 -0
- data/test/postgres_simple_test.rb +13 -0
- data/test/simple.rb +494 -0
- data/test/sqlite3_simple_test.rb +233 -0
- data/test/sybase_jtds_simple_test.rb +6 -0
- metadata +230 -0
|
@@ -0,0 +1,468 @@
|
|
|
1
|
+
require 'jdbc_adapter/tsql_helper'
|
|
2
|
+
|
|
3
|
+
module ::JdbcSpec
|
|
4
|
+
|
|
5
|
+
module ActiveRecordExtensions
|
|
6
|
+
|
|
7
|
+
def mssql_connection(config)
|
|
8
|
+
require "active_record/connection_adapters/mssql_adapter"
|
|
9
|
+
config[:host] ||= "localhost"
|
|
10
|
+
config[:port] ||= 1433
|
|
11
|
+
config[:url] ||= "jdbc:jtds:sqlserver://#{config[:host]}:#{config[:port]}/#{config[:database]}"
|
|
12
|
+
config[:driver] ||= "net.sourceforge.jtds.jdbc.Driver"
|
|
13
|
+
embedded_driver(config)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
module MsSQL
|
|
19
|
+
|
|
20
|
+
include TSqlMethods
|
|
21
|
+
|
|
22
|
+
def self.extended(mod)
|
|
23
|
+
unless @lob_callback_added
|
|
24
|
+
ActiveRecord::Base.class_eval do
|
|
25
|
+
def after_save_with_mssql_lob
|
|
26
|
+
self.class.columns.select { |c| c.sql_type =~ /image/i }.each do |c|
|
|
27
|
+
value = self[c.name]
|
|
28
|
+
value = value.to_yaml if unserializable_attribute?(c.name, c)
|
|
29
|
+
next if value.nil? || (value == '')
|
|
30
|
+
|
|
31
|
+
connection.write_large_object(c.type == :binary, c.name, self.class.table_name, self.class.primary_key, quote_value(id), value)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
ActiveRecord::Base.after_save :after_save_with_mssql_lob
|
|
37
|
+
@lob_callback_added = true
|
|
38
|
+
end
|
|
39
|
+
mod.add_version_specific_add_limit_offset
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def self.adapter_matcher(name, *)
|
|
43
|
+
name =~ /sqlserver|tds/i ? self : false
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def self.column_selector
|
|
47
|
+
[/sqlserver|tds/i, lambda {|cfg,col| col.extend(::JdbcSpec::MsSQL::Column)}]
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def self.jdbc_connection_class
|
|
51
|
+
::ActiveRecord::ConnectionAdapters::MssqlJdbcConnection
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def sqlserver_version
|
|
55
|
+
@sqlserver_version ||= select_value("select @@version")[/Microsoft SQL Server\s+(\d{4})/, 1]
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def add_version_specific_add_limit_offset
|
|
59
|
+
if sqlserver_version == "2000"
|
|
60
|
+
extend SqlServer2000LimitOffset
|
|
61
|
+
else
|
|
62
|
+
extend SqlServerLimitOffset
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def modify_types(tp) #:nodoc:
|
|
67
|
+
super(tp)
|
|
68
|
+
tp[:string] = {:name => "NVARCHAR", :limit => 255}
|
|
69
|
+
if sqlserver_version == "2000"
|
|
70
|
+
tp[:text] = {:name => "NTEXT"}
|
|
71
|
+
else
|
|
72
|
+
tp[:text] = {:name => "NVARCHAR(MAX)"}
|
|
73
|
+
end
|
|
74
|
+
tp
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
module Column
|
|
78
|
+
attr_accessor :identity, :is_special
|
|
79
|
+
|
|
80
|
+
def simplified_type(field_type)
|
|
81
|
+
case field_type
|
|
82
|
+
when /int|bigint|smallint|tinyint/i then :integer
|
|
83
|
+
when /numeric/i then (@scale.nil? || @scale == 0) ? :integer : :decimal
|
|
84
|
+
when /float|double|decimal|money|real|smallmoney/i then :decimal
|
|
85
|
+
when /datetime|smalldatetime/i then :datetime
|
|
86
|
+
when /timestamp/i then :timestamp
|
|
87
|
+
when /time/i then :time
|
|
88
|
+
when /date/i then :date
|
|
89
|
+
when /text|ntext/i then :text
|
|
90
|
+
when /binary|image|varbinary/i then :binary
|
|
91
|
+
when /char|nchar|nvarchar|string|varchar/i then :string
|
|
92
|
+
when /bit/i then :boolean
|
|
93
|
+
when /uniqueidentifier/i then :string
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def default_value(value)
|
|
98
|
+
return $1 if value =~ /^\(N?'(.*)'\)$/
|
|
99
|
+
value
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def type_cast(value)
|
|
103
|
+
return nil if value.nil? || value == "(null)" || value == "(NULL)"
|
|
104
|
+
case type
|
|
105
|
+
when :integer then unquote(value).to_i rescue value ? 1 : 0
|
|
106
|
+
when :primary_key then value == true || value == false ? value == true ? 1 : 0 : value.to_i
|
|
107
|
+
when :decimal then self.class.value_to_decimal(unquote(value))
|
|
108
|
+
when :datetime then cast_to_datetime(value)
|
|
109
|
+
when :timestamp then cast_to_time(value)
|
|
110
|
+
when :time then cast_to_time(value)
|
|
111
|
+
when :date then cast_to_date(value)
|
|
112
|
+
when :boolean then value == true or (value =~ /^t(rue)?$/i) == 0 or unquote(value)=="1"
|
|
113
|
+
when :binary then unquote value
|
|
114
|
+
else value
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def is_utf8?
|
|
120
|
+
sql_type =~ /nvarchar|ntext|nchar/i
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def unquote(value)
|
|
124
|
+
value.to_s.sub(/\A\([\(\']?/, "").sub(/[\'\)]?\)\Z/, "")
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def cast_to_time(value)
|
|
128
|
+
return value if value.is_a?(Time)
|
|
129
|
+
time_array = ParseDate.parsedate(value)
|
|
130
|
+
time_array[0] ||= 2000
|
|
131
|
+
time_array[1] ||= 1
|
|
132
|
+
time_array[2] ||= 1
|
|
133
|
+
Time.send(ActiveRecord::Base.default_timezone, *time_array) rescue nil
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def cast_to_date(value)
|
|
137
|
+
return value if value.is_a?(Date)
|
|
138
|
+
return Date.parse(value) rescue nil
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def cast_to_datetime(value)
|
|
142
|
+
if value.is_a?(Time)
|
|
143
|
+
if value.year != 0 and value.month != 0 and value.day != 0
|
|
144
|
+
return value
|
|
145
|
+
else
|
|
146
|
+
return Time.mktime(2000, 1, 1, value.hour, value.min, value.sec) rescue nil
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
return cast_to_time(value) if value.is_a?(Date) or value.is_a?(String) rescue nil
|
|
150
|
+
value
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
# These methods will only allow the adapter to insert binary data with a length of 7K or less
|
|
154
|
+
# because of a SQL Server statement length policy.
|
|
155
|
+
def self.string_to_binary(value)
|
|
156
|
+
''
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def quote(value, column = nil)
|
|
162
|
+
return value.quoted_id if value.respond_to?(:quoted_id)
|
|
163
|
+
|
|
164
|
+
case value
|
|
165
|
+
when String, ActiveSupport::Multibyte::Chars
|
|
166
|
+
value = value.to_s
|
|
167
|
+
if column && column.type == :binary
|
|
168
|
+
"'#{quote_string(JdbcSpec::MsSQL::Column.string_to_binary(value))}'" # ' (for ruby-mode)
|
|
169
|
+
elsif column && [:integer, :float].include?(column.type)
|
|
170
|
+
value = column.type == :integer ? value.to_i : value.to_f
|
|
171
|
+
value.to_s
|
|
172
|
+
elsif !column.respond_to?(:is_utf8?) || column.is_utf8?
|
|
173
|
+
"N'#{quote_string(value)}'" # ' (for ruby-mode)
|
|
174
|
+
else
|
|
175
|
+
super
|
|
176
|
+
end
|
|
177
|
+
when TrueClass then '1'
|
|
178
|
+
when FalseClass then '0'
|
|
179
|
+
else super
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def quote_string(string)
|
|
184
|
+
string.gsub(/\'/, "''")
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def quote_table_name(name)
|
|
188
|
+
name
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
def quote_column_name(name)
|
|
192
|
+
"[#{name}]"
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
def quoted_true
|
|
196
|
+
quote true
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
def quoted_false
|
|
200
|
+
quote false
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
module SqlServer2000LimitOffset
|
|
204
|
+
def add_limit_offset!(sql, options)
|
|
205
|
+
limit = options[:limit]
|
|
206
|
+
if limit
|
|
207
|
+
offset = (options[:offset] || 0).to_i
|
|
208
|
+
start_row = offset + 1
|
|
209
|
+
end_row = offset + limit.to_i
|
|
210
|
+
order = (options[:order] || determine_order_clause(sql))
|
|
211
|
+
sql.sub!(/ ORDER BY.*$/i, '')
|
|
212
|
+
find_select = /\b(SELECT(?:\s+DISTINCT)?)\b(.*)/i
|
|
213
|
+
whole, select, rest_of_query = find_select.match(sql).to_a
|
|
214
|
+
if (start_row == 1) && (end_row ==1)
|
|
215
|
+
new_sql = "#{select} TOP 1 #{rest_of_query}"
|
|
216
|
+
sql.replace(new_sql)
|
|
217
|
+
else
|
|
218
|
+
#UGLY
|
|
219
|
+
#KLUDGY?
|
|
220
|
+
#removing out stuff before the FROM...
|
|
221
|
+
rest = rest_of_query[/FROM/i=~ rest_of_query.. -1]
|
|
222
|
+
#need the table name for avoiding amiguity
|
|
223
|
+
table_name = get_table_name(sql)
|
|
224
|
+
#I am not sure this will cover all bases. but all the tests pass
|
|
225
|
+
new_order = "#{order}, #{table_name}.id" if order.index("#{table_name}.id").nil?
|
|
226
|
+
new_order ||= order
|
|
227
|
+
new_sql = "#{select} TOP #{limit} #{rest_of_query} WHERE #{table_name}.id NOT IN (#{select} TOP #{offset} #{table_name}.id #{rest} ORDER BY #{new_order}) ORDER BY #{order} "
|
|
228
|
+
sql.replace(new_sql)
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
module SqlServerLimitOffset
|
|
235
|
+
def add_limit_offset!(sql, options)
|
|
236
|
+
limit = options[:limit]
|
|
237
|
+
if limit
|
|
238
|
+
offset = (options[:offset] || 0).to_i
|
|
239
|
+
start_row = offset + 1
|
|
240
|
+
end_row = offset + limit.to_i
|
|
241
|
+
order = (options[:order] || determine_order_clause(sql))
|
|
242
|
+
sql.sub!(/ ORDER BY.*$/i, '')
|
|
243
|
+
find_select = /\b(SELECT(?:\s+DISTINCT)?)\b(.*)/i
|
|
244
|
+
whole, select, rest_of_query = find_select.match(sql).to_a
|
|
245
|
+
new_sql = "#{select} t.* FROM (SELECT ROW_NUMBER() OVER(ORDER BY #{order}) AS row_num, #{rest_of_query}"
|
|
246
|
+
new_sql << ") AS t WHERE t.row_num BETWEEN #{start_row.to_s} AND #{end_row.to_s}"
|
|
247
|
+
sql.replace(new_sql)
|
|
248
|
+
end
|
|
249
|
+
end
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
def change_order_direction(order)
|
|
253
|
+
order.split(",").collect do |fragment|
|
|
254
|
+
case fragment
|
|
255
|
+
when /\bDESC\b/i then fragment.gsub(/\bDESC\b/i, "ASC")
|
|
256
|
+
when /\bASC\b/i then fragment.gsub(/\bASC\b/i, "DESC")
|
|
257
|
+
else String.new(fragment).split(',').join(' DESC,') + ' DESC'
|
|
258
|
+
end
|
|
259
|
+
end.join(",")
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
def supports_ddl_transactions?
|
|
263
|
+
true
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
def recreate_database(name)
|
|
267
|
+
drop_database(name)
|
|
268
|
+
create_database(name)
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
def drop_database(name)
|
|
272
|
+
execute "USE master"
|
|
273
|
+
execute "DROP DATABASE #{name}"
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
def create_database(name)
|
|
277
|
+
execute "CREATE DATABASE #{name}"
|
|
278
|
+
execute "USE #{name}"
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
def rename_table(name, new_name)
|
|
282
|
+
execute "EXEC sp_rename '#{name}', '#{new_name}'"
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
# Adds a new column to the named table.
|
|
286
|
+
# See TableDefinition#column for details of the options you can use.
|
|
287
|
+
def add_column(table_name, column_name, type, options = {})
|
|
288
|
+
add_column_sql = "ALTER TABLE #{table_name} ADD #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
|
|
289
|
+
add_column_options!(add_column_sql, options)
|
|
290
|
+
# TODO: Add support to mimic date columns, using constraints to mark them as such in the database
|
|
291
|
+
# 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
|
|
292
|
+
execute(add_column_sql)
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
def rename_column(table, column, new_column_name)
|
|
296
|
+
execute "EXEC sp_rename '#{table}.#{column}', '#{new_column_name}'"
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
def change_column(table_name, column_name, type, options = {}) #:nodoc:
|
|
300
|
+
change_column_type(table_name, column_name, type, options)
|
|
301
|
+
change_column_default(table_name, column_name, options[:default]) if options_include_default?(options)
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
def change_column_type(table_name, column_name, type, options = {}) #:nodoc:
|
|
305
|
+
sql = "ALTER TABLE #{table_name} ALTER COLUMN #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
|
|
306
|
+
if options.has_key?(:null)
|
|
307
|
+
sql += (options[:null] ? " NULL" : " NOT NULL")
|
|
308
|
+
end
|
|
309
|
+
execute(sql)
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
def change_column_default(table_name, column_name, default) #:nodoc:
|
|
313
|
+
remove_default_constraint(table_name, column_name)
|
|
314
|
+
unless default.nil?
|
|
315
|
+
execute "ALTER TABLE #{table_name} ADD CONSTRAINT DF_#{table_name}_#{column_name} DEFAULT #{quote(default)} FOR #{quote_column_name(column_name)}"
|
|
316
|
+
end
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
def remove_column(table_name, column_name)
|
|
320
|
+
remove_check_constraints(table_name, column_name)
|
|
321
|
+
remove_default_constraint(table_name, column_name)
|
|
322
|
+
execute "ALTER TABLE #{table_name} DROP COLUMN [#{column_name}]"
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
def remove_default_constraint(table_name, column_name)
|
|
326
|
+
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"
|
|
327
|
+
defaults.each {|constraint|
|
|
328
|
+
execute "ALTER TABLE #{table_name} DROP CONSTRAINT #{constraint["name"]}"
|
|
329
|
+
}
|
|
330
|
+
end
|
|
331
|
+
|
|
332
|
+
def remove_check_constraints(table_name, column_name)
|
|
333
|
+
# TODO remove all constraints in single method
|
|
334
|
+
constraints = select "SELECT CONSTRAINT_NAME FROM INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE where TABLE_NAME = '#{table_name}' and COLUMN_NAME = '#{column_name}'"
|
|
335
|
+
constraints.each do |constraint|
|
|
336
|
+
execute "ALTER TABLE #{table_name} DROP CONSTRAINT #{constraint["CONSTRAINT_NAME"]}"
|
|
337
|
+
end
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
def remove_index(table_name, options = {})
|
|
341
|
+
execute "DROP INDEX #{table_name}.#{index_name(table_name, options)}"
|
|
342
|
+
end
|
|
343
|
+
|
|
344
|
+
def columns(table_name, name = nil)
|
|
345
|
+
return [] if table_name =~ /^information_schema\./i
|
|
346
|
+
cc = super
|
|
347
|
+
cc.each do |col|
|
|
348
|
+
col.identity = true if col.sql_type =~ /identity/i
|
|
349
|
+
col.is_special = true if col.sql_type =~ /text|ntext|image/i
|
|
350
|
+
end
|
|
351
|
+
cc
|
|
352
|
+
end
|
|
353
|
+
|
|
354
|
+
def _execute(sql, name = nil)
|
|
355
|
+
if sql.lstrip =~ /^insert/i
|
|
356
|
+
if query_requires_identity_insert?(sql)
|
|
357
|
+
table_name = get_table_name(sql)
|
|
358
|
+
with_identity_insert_enabled(table_name) do
|
|
359
|
+
id = @connection.execute_insert(sql)
|
|
360
|
+
end
|
|
361
|
+
else
|
|
362
|
+
@connection.execute_insert(sql)
|
|
363
|
+
end
|
|
364
|
+
elsif sql.lstrip =~ /^(create|exec)/i
|
|
365
|
+
@connection.execute_update(sql)
|
|
366
|
+
elsif sql.lstrip =~ /^\(?\s*(select|show)/i
|
|
367
|
+
repair_special_columns(sql)
|
|
368
|
+
@connection.execute_query(sql)
|
|
369
|
+
else
|
|
370
|
+
@connection.execute_update(sql)
|
|
371
|
+
end
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
#SELECT .. FOR UPDATE is not supported on Microsoft SQL Server
|
|
375
|
+
def add_lock!(sql, options)
|
|
376
|
+
sql
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
private
|
|
380
|
+
|
|
381
|
+
# Turns IDENTITY_INSERT ON for table during execution of the block
|
|
382
|
+
# N.B. This sets the state of IDENTITY_INSERT to OFF after the
|
|
383
|
+
# block has been executed without regard to its previous state
|
|
384
|
+
def with_identity_insert_enabled(table_name, &block)
|
|
385
|
+
set_identity_insert(table_name, true)
|
|
386
|
+
yield
|
|
387
|
+
ensure
|
|
388
|
+
set_identity_insert(table_name, false)
|
|
389
|
+
end
|
|
390
|
+
|
|
391
|
+
def set_identity_insert(table_name, enable = true)
|
|
392
|
+
execute "SET IDENTITY_INSERT #{table_name} #{enable ? 'ON' : 'OFF'}"
|
|
393
|
+
rescue Exception => e
|
|
394
|
+
raise ActiveRecord::ActiveRecordError, "IDENTITY_INSERT could not be turned #{enable ? 'ON' : 'OFF'} for table #{table_name}"
|
|
395
|
+
end
|
|
396
|
+
|
|
397
|
+
def get_table_name(sql)
|
|
398
|
+
if sql =~ /^\s*insert\s+into\s+([^\(\s,]+)\s*|^\s*update\s+([^\(\s,]+)\s*/i
|
|
399
|
+
$1
|
|
400
|
+
elsif sql =~ /from\s+([^\(\s,]+)\s*/i
|
|
401
|
+
$1
|
|
402
|
+
else
|
|
403
|
+
nil
|
|
404
|
+
end
|
|
405
|
+
end
|
|
406
|
+
|
|
407
|
+
def identity_column(table_name)
|
|
408
|
+
@table_columns = {} unless @table_columns
|
|
409
|
+
@table_columns[table_name] = columns(table_name) if @table_columns[table_name] == nil
|
|
410
|
+
@table_columns[table_name].each do |col|
|
|
411
|
+
return col.name if col.identity
|
|
412
|
+
end
|
|
413
|
+
|
|
414
|
+
return nil
|
|
415
|
+
end
|
|
416
|
+
|
|
417
|
+
def query_requires_identity_insert?(sql)
|
|
418
|
+
table_name = get_table_name(sql)
|
|
419
|
+
id_column = identity_column(table_name)
|
|
420
|
+
if sql.strip =~ /insert into [^ ]+ ?\((.+?)\)/i
|
|
421
|
+
insert_columns = $1.split(/, */).map(&method(:unquote_column_name))
|
|
422
|
+
return table_name if insert_columns.include?(id_column)
|
|
423
|
+
end
|
|
424
|
+
end
|
|
425
|
+
|
|
426
|
+
def unquote_column_name(name)
|
|
427
|
+
if name =~ /^\[.*\]$/
|
|
428
|
+
name[1..-2]
|
|
429
|
+
else
|
|
430
|
+
name
|
|
431
|
+
end
|
|
432
|
+
end
|
|
433
|
+
|
|
434
|
+
def get_special_columns(table_name)
|
|
435
|
+
special = []
|
|
436
|
+
@table_columns ||= {}
|
|
437
|
+
@table_columns[table_name] ||= columns(table_name)
|
|
438
|
+
@table_columns[table_name].each do |col|
|
|
439
|
+
special << col.name if col.is_special
|
|
440
|
+
end
|
|
441
|
+
special
|
|
442
|
+
end
|
|
443
|
+
|
|
444
|
+
def repair_special_columns(sql)
|
|
445
|
+
special_cols = get_special_columns(get_table_name(sql))
|
|
446
|
+
for col in special_cols.to_a
|
|
447
|
+
sql.gsub!(Regexp.new(" #{col.to_s} = "), " #{col.to_s} LIKE ")
|
|
448
|
+
sql.gsub!(/ORDER BY #{col.to_s}/i, '')
|
|
449
|
+
end
|
|
450
|
+
sql
|
|
451
|
+
end
|
|
452
|
+
|
|
453
|
+
def determine_order_clause(sql)
|
|
454
|
+
return $1 if sql =~ /ORDER BY (.*)$/
|
|
455
|
+
sql =~ /FROM +(\w+?)\b/ || raise("can't determine table name")
|
|
456
|
+
table_name = $1
|
|
457
|
+
"#{table_name}.#{determine_primary_key(table_name)}"
|
|
458
|
+
end
|
|
459
|
+
|
|
460
|
+
def determine_primary_key(table_name)
|
|
461
|
+
primary_key = columns(table_name).detect { |column| column.primary || column.identity }
|
|
462
|
+
primary_key ? primary_key.name : "id"
|
|
463
|
+
end
|
|
464
|
+
|
|
465
|
+
end
|
|
466
|
+
|
|
467
|
+
end
|
|
468
|
+
|