activerecord-jdbc-adapter 0.9.3-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 +248 -0
- data/LICENSE.txt +21 -0
- data/Manifest.txt +125 -0
- data/README.txt +218 -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 +640 -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/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/generators/jdbc/jdbc_generator.rb +9 -0
- data/lib/jdbc_adapter.rb +27 -0
- data/lib/jdbc_adapter/jdbc.rake +121 -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 +203 -0
- data/lib/jdbc_adapter/jdbc_derby.rb +430 -0
- data/lib/jdbc_adapter/jdbc_firebird.rb +109 -0
- data/lib/jdbc_adapter/jdbc_hsqldb.rb +218 -0
- data/lib/jdbc_adapter/jdbc_informix.rb +147 -0
- data/lib/jdbc_adapter/jdbc_mimer.rb +141 -0
- data/lib/jdbc_adapter/jdbc_mssql.rb +337 -0
- data/lib/jdbc_adapter/jdbc_mysql.rb +236 -0
- data/lib/jdbc_adapter/jdbc_oracle.rb +377 -0
- data/lib/jdbc_adapter/jdbc_postgre.rb +498 -0
- data/lib/jdbc_adapter/jdbc_sqlite3.rb +384 -0
- data/lib/jdbc_adapter/jdbc_sybase.rb +50 -0
- data/lib/jdbc_adapter/missing_functionality_helper.rb +87 -0
- data/lib/jdbc_adapter/rake_tasks.rb +10 -0
- data/lib/jdbc_adapter/tsql_helper.rb +60 -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 +90 -0
- data/rakelib/rails.rake +41 -0
- data/rakelib/test.rake +76 -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 +1162 -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_simple_test.rb +6 -0
- data/test/mysql_db_create_test.rb +25 -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 +29 -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 +475 -0
- data/test/sqlite3_simple_test.rb +233 -0
- data/test/sybase_jtds_simple_test.rb +6 -0
- metadata +188 -0
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
require 'jdbc_adapter/tsql_helper'
|
|
2
|
+
|
|
3
|
+
module ::JdbcSpec
|
|
4
|
+
module MsSQL
|
|
5
|
+
include TSqlMethods
|
|
6
|
+
|
|
7
|
+
def self.extended(mod)
|
|
8
|
+
unless @lob_callback_added
|
|
9
|
+
ActiveRecord::Base.class_eval do
|
|
10
|
+
def after_save_with_mssql_lob
|
|
11
|
+
self.class.columns.select { |c| c.sql_type =~ /image/i }.each do |c|
|
|
12
|
+
value = self[c.name]
|
|
13
|
+
value = value.to_yaml if unserializable_attribute?(c.name, c)
|
|
14
|
+
next if value.nil? || (value == '')
|
|
15
|
+
|
|
16
|
+
connection.write_large_object(c.type == :binary, c.name, self.class.table_name, self.class.primary_key, quote_value(id), value)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
ActiveRecord::Base.after_save :after_save_with_mssql_lob
|
|
22
|
+
@lob_callback_added = true
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def self.adapter_matcher(name, *)
|
|
27
|
+
name =~ /sqlserver|tds/i ? self : false
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def self.column_selector
|
|
31
|
+
[/sqlserver|tds/i, lambda {|cfg,col| col.extend(::JdbcSpec::MsSQL::Column)}]
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def self.jdbc_connection_class
|
|
35
|
+
::ActiveRecord::ConnectionAdapters::MssqlJdbcConnection
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
module Column
|
|
39
|
+
attr_accessor :identity, :is_special
|
|
40
|
+
|
|
41
|
+
def simplified_type(field_type)
|
|
42
|
+
case field_type
|
|
43
|
+
when /int|bigint|smallint|tinyint/i then :integer
|
|
44
|
+
when /numeric/i then (@scale.nil? || @scale == 0) ? :integer : :decimal
|
|
45
|
+
when /float|double|decimal|money|real|smallmoney/i then :decimal
|
|
46
|
+
when /datetime|smalldatetime/i then :datetime
|
|
47
|
+
when /timestamp/i then :timestamp
|
|
48
|
+
when /time/i then :time
|
|
49
|
+
when /date/i then :date
|
|
50
|
+
when /text|ntext/i then :text
|
|
51
|
+
when /binary|image|varbinary/i then :binary
|
|
52
|
+
when /char|nchar|nvarchar|string|varchar/i then :string
|
|
53
|
+
when /bit/i then :boolean
|
|
54
|
+
when /uniqueidentifier/i then :string
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def type_cast(value)
|
|
59
|
+
return nil if value.nil? || value == "(null)" || value == "(NULL)"
|
|
60
|
+
case type
|
|
61
|
+
when :string then unquote_string value
|
|
62
|
+
when :integer then unquote(value).to_i rescue value ? 1 : 0
|
|
63
|
+
when :primary_key then value == true || value == false ? value == true ? 1 : 0 : value.to_i
|
|
64
|
+
when :decimal then self.class.value_to_decimal(unquote(value))
|
|
65
|
+
when :datetime then cast_to_datetime(value)
|
|
66
|
+
when :timestamp then cast_to_time(value)
|
|
67
|
+
when :time then cast_to_time(value)
|
|
68
|
+
when :date then cast_to_date(value)
|
|
69
|
+
when :boolean then value == true or (value =~ /^t(rue)?$/i) == 0 or unquote(value)=="1"
|
|
70
|
+
when :binary then unquote value
|
|
71
|
+
else value
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# JRUBY-2011: Match balanced quotes and parenthesis - 'text',('text') or (text)
|
|
76
|
+
def unquote_string(value)
|
|
77
|
+
value.to_s.sub(/^\((.*)\)$/,'\1').sub(/^'(.*)'$/,'\1')
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def unquote(value)
|
|
81
|
+
value.to_s.sub(/\A\([\(\']?/, "").sub(/[\'\)]?\)\Z/, "")
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def cast_to_time(value)
|
|
85
|
+
return value if value.is_a?(Time)
|
|
86
|
+
time_array = ParseDate.parsedate(value)
|
|
87
|
+
time_array[0] ||= 2000
|
|
88
|
+
time_array[1] ||= 1
|
|
89
|
+
time_array[2] ||= 1
|
|
90
|
+
Time.send(ActiveRecord::Base.default_timezone, *time_array) rescue nil
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def cast_to_date(value)
|
|
94
|
+
return value if value.is_a?(Date)
|
|
95
|
+
return Date.parse(value) rescue nil
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def cast_to_datetime(value)
|
|
99
|
+
if value.is_a?(Time)
|
|
100
|
+
if value.year != 0 and value.month != 0 and value.day != 0
|
|
101
|
+
return value
|
|
102
|
+
else
|
|
103
|
+
return Time.mktime(2000, 1, 1, value.hour, value.min, value.sec) rescue nil
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
return cast_to_time(value) if value.is_a?(Date) or value.is_a?(String) rescue nil
|
|
107
|
+
value
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# These methods will only allow the adapter to insert binary data with a length of 7K or less
|
|
111
|
+
# because of a SQL Server statement length policy.
|
|
112
|
+
def self.string_to_binary(value)
|
|
113
|
+
''
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def quote(value, column = nil)
|
|
118
|
+
return value.quoted_id if value.respond_to?(:quoted_id)
|
|
119
|
+
|
|
120
|
+
case value
|
|
121
|
+
when String, ActiveSupport::Multibyte::Chars
|
|
122
|
+
value = value.to_s
|
|
123
|
+
if column && column.type == :binary
|
|
124
|
+
"'#{quote_string(JdbcSpec::MsSQL::Column.string_to_binary(value))}'" # ' (for ruby-mode)
|
|
125
|
+
elsif column && [:integer, :float].include?(column.type)
|
|
126
|
+
value = column.type == :integer ? value.to_i : value.to_f
|
|
127
|
+
value.to_s
|
|
128
|
+
else
|
|
129
|
+
"N'#{quote_string(value)}'" # ' (for ruby-mode)
|
|
130
|
+
end
|
|
131
|
+
when TrueClass then '1'
|
|
132
|
+
when FalseClass then '0'
|
|
133
|
+
else super
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def quote_string(string)
|
|
138
|
+
string.gsub(/\'/, "''")
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def quote_table_name(name)
|
|
142
|
+
name
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def quote_column_name(name)
|
|
146
|
+
"[#{name}]"
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def quoted_true
|
|
150
|
+
quote true
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def quoted_false
|
|
154
|
+
quote false
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def change_order_direction(order)
|
|
158
|
+
order.split(",").collect {|fragment|
|
|
159
|
+
case fragment
|
|
160
|
+
when /\bDESC\b/i then fragment.gsub(/\bDESC\b/i, "ASC")
|
|
161
|
+
when /\bASC\b/i then fragment.gsub(/\bASC\b/i, "DESC")
|
|
162
|
+
else String.new(fragment).split(',').join(' DESC,') + ' DESC'
|
|
163
|
+
end
|
|
164
|
+
}.join(",")
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def recreate_database(name)
|
|
168
|
+
drop_database(name)
|
|
169
|
+
create_database(name)
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def drop_database(name)
|
|
173
|
+
execute "USE master"
|
|
174
|
+
execute "DROP DATABASE #{name}"
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def create_database(name)
|
|
178
|
+
execute "CREATE DATABASE #{name}"
|
|
179
|
+
execute "USE #{name}"
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def rename_table(name, new_name)
|
|
183
|
+
execute "EXEC sp_rename '#{name}', '#{new_name}'"
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
# Adds a new column to the named table.
|
|
187
|
+
# See TableDefinition#column for details of the options you can use.
|
|
188
|
+
def add_column(table_name, column_name, type, options = {})
|
|
189
|
+
add_column_sql = "ALTER TABLE #{table_name} ADD #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
|
|
190
|
+
add_column_options!(add_column_sql, options)
|
|
191
|
+
# TODO: Add support to mimic date columns, using constraints to mark them as such in the database
|
|
192
|
+
# 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
|
|
193
|
+
execute(add_column_sql)
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
def rename_column(table, column, new_column_name)
|
|
197
|
+
execute "EXEC sp_rename '#{table}.#{column}', '#{new_column_name}'"
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
def change_column(table_name, column_name, type, options = {}) #:nodoc:
|
|
201
|
+
sql_commands = ["ALTER TABLE #{table_name} ALTER COLUMN #{column_name} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"]
|
|
202
|
+
if options_include_default?(options)
|
|
203
|
+
remove_default_constraint(table_name, column_name)
|
|
204
|
+
sql_commands << "ALTER TABLE #{table_name} ADD CONSTRAINT DF_#{table_name}_#{column_name} DEFAULT #{quote(options[:default], options[:column])} FOR #{column_name}"
|
|
205
|
+
end
|
|
206
|
+
sql_commands.each {|c|
|
|
207
|
+
execute(c)
|
|
208
|
+
}
|
|
209
|
+
end
|
|
210
|
+
def change_column_default(table_name, column_name, default) #:nodoc:
|
|
211
|
+
remove_default_constraint(table_name, column_name)
|
|
212
|
+
execute "ALTER TABLE #{table_name} ADD CONSTRAINT DF_#{table_name}_#{column_name} DEFAULT #{quote(default, column_name)} FOR #{column_name}"
|
|
213
|
+
end
|
|
214
|
+
def remove_column(table_name, column_name)
|
|
215
|
+
remove_check_constraints(table_name, column_name)
|
|
216
|
+
remove_default_constraint(table_name, column_name)
|
|
217
|
+
execute "ALTER TABLE #{table_name} DROP COLUMN [#{column_name}]"
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
def remove_default_constraint(table_name, column_name)
|
|
221
|
+
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"
|
|
222
|
+
defaults.each {|constraint|
|
|
223
|
+
execute "ALTER TABLE #{table_name} DROP CONSTRAINT #{constraint["name"]}"
|
|
224
|
+
}
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
def remove_check_constraints(table_name, column_name)
|
|
228
|
+
# TODO remove all constraints in single method
|
|
229
|
+
constraints = select "SELECT CONSTRAINT_NAME FROM INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE where TABLE_NAME = '#{table_name}' and COLUMN_NAME = '#{column_name}'"
|
|
230
|
+
constraints.each do |constraint|
|
|
231
|
+
execute "ALTER TABLE #{table_name} DROP CONSTRAINT #{constraint["CONSTRAINT_NAME"]}"
|
|
232
|
+
end
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
def remove_index(table_name, options = {})
|
|
236
|
+
execute "DROP INDEX #{table_name}.#{index_name(table_name, options)}"
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
def columns(table_name, name = nil)
|
|
241
|
+
return [] if table_name =~ /^information_schema\./i
|
|
242
|
+
cc = super
|
|
243
|
+
cc.each do |col|
|
|
244
|
+
col.identity = true if col.sql_type =~ /identity/i
|
|
245
|
+
col.is_special = true if col.sql_type =~ /text|ntext|image/i
|
|
246
|
+
end
|
|
247
|
+
cc
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
def _execute(sql, name = nil)
|
|
251
|
+
if sql.lstrip =~ /^insert/i
|
|
252
|
+
if query_requires_identity_insert?(sql)
|
|
253
|
+
table_name = get_table_name(sql)
|
|
254
|
+
with_identity_insert_enabled(table_name) do
|
|
255
|
+
id = @connection.execute_insert(sql)
|
|
256
|
+
end
|
|
257
|
+
else
|
|
258
|
+
@connection.execute_insert(sql)
|
|
259
|
+
end
|
|
260
|
+
elsif sql.lstrip =~ /^\(?\s*(select|show)/i
|
|
261
|
+
repair_special_columns(sql)
|
|
262
|
+
@connection.execute_query(sql)
|
|
263
|
+
else
|
|
264
|
+
@connection.execute_update(sql)
|
|
265
|
+
end
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
#SELECT .. FOR UPDATE is not supported on Microsoft SQL Server
|
|
269
|
+
def add_lock!(sql, options)
|
|
270
|
+
sql
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
private
|
|
274
|
+
# Turns IDENTITY_INSERT ON for table during execution of the block
|
|
275
|
+
# N.B. This sets the state of IDENTITY_INSERT to OFF after the
|
|
276
|
+
# block has been executed without regard to its previous state
|
|
277
|
+
|
|
278
|
+
def with_identity_insert_enabled(table_name, &block)
|
|
279
|
+
set_identity_insert(table_name, true)
|
|
280
|
+
yield
|
|
281
|
+
ensure
|
|
282
|
+
set_identity_insert(table_name, false)
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
def set_identity_insert(table_name, enable = true)
|
|
286
|
+
execute "SET IDENTITY_INSERT #{table_name} #{enable ? 'ON' : 'OFF'}"
|
|
287
|
+
rescue Exception => e
|
|
288
|
+
raise ActiveRecord::ActiveRecordError, "IDENTITY_INSERT could not be turned #{enable ? 'ON' : 'OFF'} for table #{table_name}"
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
def get_table_name(sql)
|
|
292
|
+
if sql =~ /^\s*insert\s+into\s+([^\(\s,]+)\s*|^\s*update\s+([^\(\s,]+)\s*/i
|
|
293
|
+
$1
|
|
294
|
+
elsif sql =~ /from\s+([^\(\s,]+)\s*/i
|
|
295
|
+
$1
|
|
296
|
+
else
|
|
297
|
+
nil
|
|
298
|
+
end
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
def identity_column(table_name)
|
|
302
|
+
@table_columns = {} unless @table_columns
|
|
303
|
+
@table_columns[table_name] = columns(table_name) if @table_columns[table_name] == nil
|
|
304
|
+
@table_columns[table_name].each do |col|
|
|
305
|
+
return col.name if col.identity
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
return nil
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
def query_requires_identity_insert?(sql)
|
|
312
|
+
table_name = get_table_name(sql)
|
|
313
|
+
id_column = identity_column(table_name)
|
|
314
|
+
sql =~ /\[#{id_column}\]/ ? table_name : nil
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
def get_special_columns(table_name)
|
|
318
|
+
special = []
|
|
319
|
+
@table_columns ||= {}
|
|
320
|
+
@table_columns[table_name] ||= columns(table_name)
|
|
321
|
+
@table_columns[table_name].each do |col|
|
|
322
|
+
special << col.name if col.is_special
|
|
323
|
+
end
|
|
324
|
+
special
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
def repair_special_columns(sql)
|
|
328
|
+
special_cols = get_special_columns(get_table_name(sql))
|
|
329
|
+
for col in special_cols.to_a
|
|
330
|
+
sql.gsub!(Regexp.new(" #{col.to_s} = "), " #{col.to_s} LIKE ")
|
|
331
|
+
sql.gsub!(/ORDER BY #{col.to_s}/i, '')
|
|
332
|
+
end
|
|
333
|
+
sql
|
|
334
|
+
end
|
|
335
|
+
end
|
|
336
|
+
end
|
|
337
|
+
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
require 'active_record/connection_adapters/abstract/schema_definitions'
|
|
2
|
+
|
|
3
|
+
module ::JdbcSpec
|
|
4
|
+
# Don't need to load native mysql adapter
|
|
5
|
+
$LOADED_FEATURES << "active_record/connection_adapters/mysql_adapter.rb"
|
|
6
|
+
|
|
7
|
+
module ActiveRecordExtensions
|
|
8
|
+
add_method_to_remove_from_ar_base(:mysql_connection)
|
|
9
|
+
|
|
10
|
+
def mysql_connection(config)
|
|
11
|
+
require File.dirname(__FILE__) + "/../active_record/connection_adapters/mysql_adapter"
|
|
12
|
+
config[:port] ||= 3306
|
|
13
|
+
url_options = "zeroDateTimeBehavior=convertToNull&jdbcCompliantTruncation=false&useUnicode=true&characterEncoding="
|
|
14
|
+
url_options << (config[:encoding] || 'utf8')
|
|
15
|
+
if config[:url]
|
|
16
|
+
config[:url] = config[:url]['?'] ? "#{config[:url]}&#{url_options}" : "#{config[:url]}?#{url_options}"
|
|
17
|
+
else
|
|
18
|
+
config[:url] = "jdbc:mysql://#{config[:host]}:#{config[:port]}/#{config[:database]}?#{url_options}"
|
|
19
|
+
end
|
|
20
|
+
config[:driver] = "com.mysql.jdbc.Driver"
|
|
21
|
+
connection = jdbc_connection(config)
|
|
22
|
+
::JdbcSpec::MySQL.kill_cancel_timer(connection.raw_connection)
|
|
23
|
+
connection
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
module MySQL
|
|
28
|
+
def self.adapter_matcher(name, *)
|
|
29
|
+
name =~ /mysql/i ? self : false
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def self.column_selector
|
|
33
|
+
[/mysql/i, lambda {|cfg,col| col.extend(::JdbcSpec::MySQL::Column)}]
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def self.extended(adapter)
|
|
37
|
+
adapter.execute("SET SQL_AUTO_IS_NULL=0")
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
module Column
|
|
41
|
+
TYPES_ALLOWING_EMPTY_STRING_DEFAULT = Set.new([:binary, :string, :text])
|
|
42
|
+
|
|
43
|
+
def simplified_type(field_type)
|
|
44
|
+
return :boolean if field_type =~ /tinyint\(1\)|bit/i
|
|
45
|
+
return :string if field_type =~ /enum/i
|
|
46
|
+
super
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def init_column(name, default, *args)
|
|
50
|
+
@original_default = default
|
|
51
|
+
@default = nil if missing_default_forged_as_empty_string?
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# MySQL misreports NOT NULL column default when none is given.
|
|
55
|
+
# We can't detect this for columns which may have a legitimate ''
|
|
56
|
+
# default (string, text, binary) but we can for others (integer,
|
|
57
|
+
# datetime, boolean, and the rest).
|
|
58
|
+
#
|
|
59
|
+
# Test whether the column has default '', is not null, and is not
|
|
60
|
+
# a type allowing default ''.
|
|
61
|
+
def missing_default_forged_as_empty_string?
|
|
62
|
+
!null && @original_default == '' && !TYPES_ALLOWING_EMPTY_STRING_DEFAULT.include?(type)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def modify_types(tp)
|
|
67
|
+
tp[:primary_key] = "int(11) DEFAULT NULL auto_increment PRIMARY KEY"
|
|
68
|
+
tp[:decimal] = { :name => "decimal" }
|
|
69
|
+
tp[:timestamp] = { :name => "datetime" }
|
|
70
|
+
tp[:datetime][:limit] = nil
|
|
71
|
+
tp
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# QUOTING ==================================================
|
|
75
|
+
|
|
76
|
+
def quote(value, column = nil)
|
|
77
|
+
return value.quoted_id if value.respond_to?(:quoted_id)
|
|
78
|
+
|
|
79
|
+
if column && column.type == :primary_key
|
|
80
|
+
value.to_s
|
|
81
|
+
elsif column && String === value && column.type == :binary && column.class.respond_to?(:string_to_binary)
|
|
82
|
+
s = column.class.string_to_binary(value).unpack("H*")[0]
|
|
83
|
+
"x'#{s}'"
|
|
84
|
+
elsif BigDecimal === value
|
|
85
|
+
"'#{value.to_s("F")}'"
|
|
86
|
+
else
|
|
87
|
+
super
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def quoted_true
|
|
92
|
+
"1"
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def quoted_false
|
|
96
|
+
"0"
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def begin_db_transaction #:nodoc:
|
|
100
|
+
@connection.begin
|
|
101
|
+
rescue Exception
|
|
102
|
+
# Transactions aren't supported
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def commit_db_transaction #:nodoc:
|
|
106
|
+
@connection.commit
|
|
107
|
+
rescue Exception
|
|
108
|
+
# Transactions aren't supported
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def rollback_db_transaction #:nodoc:
|
|
112
|
+
@connection.rollback
|
|
113
|
+
rescue Exception
|
|
114
|
+
# Transactions aren't supported
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def disable_referential_integrity(&block) #:nodoc:
|
|
118
|
+
old = select_value("SELECT @@FOREIGN_KEY_CHECKS")
|
|
119
|
+
begin
|
|
120
|
+
update("SET FOREIGN_KEY_CHECKS = 0")
|
|
121
|
+
yield
|
|
122
|
+
ensure
|
|
123
|
+
update("SET FOREIGN_KEY_CHECKS = #{old}")
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# SCHEMA STATEMENTS ========================================
|
|
128
|
+
|
|
129
|
+
def structure_dump #:nodoc:
|
|
130
|
+
if supports_views?
|
|
131
|
+
sql = "SHOW FULL TABLES WHERE Table_type = 'BASE TABLE'"
|
|
132
|
+
else
|
|
133
|
+
sql = "SHOW TABLES"
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
select_all(sql).inject("") do |structure, table|
|
|
137
|
+
table.delete('Table_type')
|
|
138
|
+
|
|
139
|
+
hash = select_one("SHOW CREATE TABLE #{quote_table_name(table.to_a.first.last)}")
|
|
140
|
+
|
|
141
|
+
if(table = hash["Create Table"])
|
|
142
|
+
structure += table + ";\n\n"
|
|
143
|
+
elsif(view = hash["Create View"])
|
|
144
|
+
structure += view + ";\n\n"
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def recreate_database(name) #:nodoc:
|
|
150
|
+
drop_database(name)
|
|
151
|
+
create_database(name)
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def character_set(options) #:nodoc:
|
|
155
|
+
str = "CHARACTER SET `#{options[:charset] || 'utf8'}`"
|
|
156
|
+
str += " COLLATE `#{options[:collation]}`" if options[:collation]
|
|
157
|
+
str
|
|
158
|
+
end
|
|
159
|
+
private :character_set
|
|
160
|
+
|
|
161
|
+
def create_database(name, options = {}) #:nodoc:
|
|
162
|
+
execute "CREATE DATABASE `#{name}` DEFAULT #{character_set(options)}"
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def drop_database(name) #:nodoc:
|
|
166
|
+
execute "DROP DATABASE IF EXISTS `#{name}`"
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def current_database
|
|
170
|
+
select_one("SELECT DATABASE() as db")["db"]
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def create_table(name, options = {}) #:nodoc:
|
|
174
|
+
super(name, {:options => "ENGINE=InnoDB #{character_set(options)}"}.merge(options))
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def rename_table(name, new_name)
|
|
178
|
+
execute "RENAME TABLE #{quote_table_name(name)} TO #{quote_table_name(new_name)}"
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def change_column_default(table_name, column_name, default) #:nodoc:
|
|
182
|
+
current_type = select_one("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE '#{column_name}'")["Type"]
|
|
183
|
+
|
|
184
|
+
execute("ALTER TABLE #{quote_table_name(table_name)} CHANGE #{quote_column_name(column_name)} #{quote_column_name(column_name)} #{current_type} DEFAULT #{quote(default)}")
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def change_column(table_name, column_name, type, options = {}) #:nodoc:
|
|
188
|
+
unless options_include_default?(options)
|
|
189
|
+
if column = columns(table_name).find { |c| c.name == column_name.to_s }
|
|
190
|
+
options[:default] = column.default
|
|
191
|
+
else
|
|
192
|
+
raise "No such column: #{table_name}.#{column_name}"
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
change_column_sql = "ALTER TABLE #{quote_table_name(table_name)} CHANGE #{quote_column_name(column_name)} #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
|
|
197
|
+
add_column_options!(change_column_sql, options)
|
|
198
|
+
execute(change_column_sql)
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
def rename_column(table_name, column_name, new_column_name) #:nodoc:
|
|
202
|
+
cols = select_one("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE '#{column_name}'")
|
|
203
|
+
current_type = cols["Type"] || cols["COLUMN_TYPE"]
|
|
204
|
+
execute "ALTER TABLE #{quote_table_name(table_name)} CHANGE #{quote_table_name(column_name)} #{quote_column_name(new_column_name)} #{current_type}"
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
def add_limit_offset!(sql, options) #:nodoc:
|
|
208
|
+
if limit = options[:limit]
|
|
209
|
+
unless offset = options[:offset]
|
|
210
|
+
sql << " LIMIT #{limit}"
|
|
211
|
+
else
|
|
212
|
+
sql << " LIMIT #{offset}, #{limit}"
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
def show_variable(var)
|
|
218
|
+
res = execute("show variables like '#{var}'")
|
|
219
|
+
row = res.detect {|row| row["Variable_name"] == var }
|
|
220
|
+
row && row["Value"]
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
def charset
|
|
224
|
+
show_variable("character_set_database")
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
def collation
|
|
228
|
+
show_variable("collation_database")
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
private
|
|
232
|
+
def supports_views?
|
|
233
|
+
false
|
|
234
|
+
end
|
|
235
|
+
end
|
|
236
|
+
end
|