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.
Files changed (121) hide show
  1. data/History.txt +248 -0
  2. data/LICENSE.txt +21 -0
  3. data/Manifest.txt +125 -0
  4. data/README.txt +218 -0
  5. data/Rakefile +10 -0
  6. data/lib/active_record/connection_adapters/cachedb_adapter.rb +1 -0
  7. data/lib/active_record/connection_adapters/derby_adapter.rb +13 -0
  8. data/lib/active_record/connection_adapters/h2_adapter.rb +13 -0
  9. data/lib/active_record/connection_adapters/hsqldb_adapter.rb +13 -0
  10. data/lib/active_record/connection_adapters/informix_adapter.rb +1 -0
  11. data/lib/active_record/connection_adapters/jdbc_adapter.rb +640 -0
  12. data/lib/active_record/connection_adapters/jdbc_adapter_spec.rb +26 -0
  13. data/lib/active_record/connection_adapters/jndi_adapter.rb +1 -0
  14. data/lib/active_record/connection_adapters/mysql_adapter.rb +13 -0
  15. data/lib/active_record/connection_adapters/oracle_adapter.rb +1 -0
  16. data/lib/active_record/connection_adapters/postgresql_adapter.rb +13 -0
  17. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +13 -0
  18. data/lib/generators/jdbc/jdbc_generator.rb +9 -0
  19. data/lib/jdbc_adapter.rb +27 -0
  20. data/lib/jdbc_adapter/jdbc.rake +121 -0
  21. data/lib/jdbc_adapter/jdbc_adapter_internal.jar +0 -0
  22. data/lib/jdbc_adapter/jdbc_cachedb.rb +33 -0
  23. data/lib/jdbc_adapter/jdbc_db2.rb +203 -0
  24. data/lib/jdbc_adapter/jdbc_derby.rb +430 -0
  25. data/lib/jdbc_adapter/jdbc_firebird.rb +109 -0
  26. data/lib/jdbc_adapter/jdbc_hsqldb.rb +218 -0
  27. data/lib/jdbc_adapter/jdbc_informix.rb +147 -0
  28. data/lib/jdbc_adapter/jdbc_mimer.rb +141 -0
  29. data/lib/jdbc_adapter/jdbc_mssql.rb +337 -0
  30. data/lib/jdbc_adapter/jdbc_mysql.rb +236 -0
  31. data/lib/jdbc_adapter/jdbc_oracle.rb +377 -0
  32. data/lib/jdbc_adapter/jdbc_postgre.rb +498 -0
  33. data/lib/jdbc_adapter/jdbc_sqlite3.rb +384 -0
  34. data/lib/jdbc_adapter/jdbc_sybase.rb +50 -0
  35. data/lib/jdbc_adapter/missing_functionality_helper.rb +87 -0
  36. data/lib/jdbc_adapter/rake_tasks.rb +10 -0
  37. data/lib/jdbc_adapter/tsql_helper.rb +60 -0
  38. data/lib/jdbc_adapter/version.rb +5 -0
  39. data/lib/pg.rb +4 -0
  40. data/rails_generators/jdbc_generator.rb +15 -0
  41. data/rails_generators/templates/config/initializers/jdbc.rb +7 -0
  42. data/rails_generators/templates/lib/tasks/jdbc.rake +8 -0
  43. data/rakelib/compile.rake +23 -0
  44. data/rakelib/package.rake +90 -0
  45. data/rakelib/rails.rake +41 -0
  46. data/rakelib/test.rake +76 -0
  47. data/src/java/jdbc_adapter/JdbcAdapterInternalService.java +53 -0
  48. data/src/java/jdbc_adapter/JdbcConnectionFactory.java +36 -0
  49. data/src/java/jdbc_adapter/JdbcDerbySpec.java +293 -0
  50. data/src/java/jdbc_adapter/JdbcMySQLSpec.java +134 -0
  51. data/src/java/jdbc_adapter/MssqlRubyJdbcConnection.java +71 -0
  52. data/src/java/jdbc_adapter/PostgresRubyJdbcConnection.java +55 -0
  53. data/src/java/jdbc_adapter/RubyJdbcConnection.java +1162 -0
  54. data/src/java/jdbc_adapter/SQLBlock.java +27 -0
  55. data/src/java/jdbc_adapter/Sqlite3RubyJdbcConnection.java +41 -0
  56. data/test/abstract_db_create.rb +107 -0
  57. data/test/activerecord/connection_adapters/type_conversion_test.rb +31 -0
  58. data/test/activerecord/connections/native_jdbc_mysql/connection.rb +25 -0
  59. data/test/cachedb_simple_test.rb +6 -0
  60. data/test/db/cachedb.rb +9 -0
  61. data/test/db/db2.rb +9 -0
  62. data/test/db/derby.rb +14 -0
  63. data/test/db/h2.rb +11 -0
  64. data/test/db/hsqldb.rb +12 -0
  65. data/test/db/informix.rb +11 -0
  66. data/test/db/jdbc.rb +11 -0
  67. data/test/db/jndi_config.rb +30 -0
  68. data/test/db/logger.rb +3 -0
  69. data/test/db/mssql.rb +9 -0
  70. data/test/db/mysql.rb +10 -0
  71. data/test/db/oracle.rb +34 -0
  72. data/test/db/postgres.rb +9 -0
  73. data/test/db/sqlite3.rb +15 -0
  74. data/test/db2_simple_test.rb +10 -0
  75. data/test/derby_migration_test.rb +21 -0
  76. data/test/derby_multibyte_test.rb +12 -0
  77. data/test/derby_simple_test.rb +21 -0
  78. data/test/generic_jdbc_connection_test.rb +9 -0
  79. data/test/h2_simple_test.rb +6 -0
  80. data/test/has_many_through.rb +79 -0
  81. data/test/helper.rb +5 -0
  82. data/test/hsqldb_simple_test.rb +6 -0
  83. data/test/informix_simple_test.rb +48 -0
  84. data/test/jdbc_adapter/jdbc_db2_test.rb +26 -0
  85. data/test/jdbc_adapter/jdbc_sybase_test.rb +33 -0
  86. data/test/jdbc_common.rb +25 -0
  87. data/test/jndi_callbacks_test.rb +38 -0
  88. data/test/jndi_test.rb +35 -0
  89. data/test/manualTestDatabase.rb +191 -0
  90. data/test/minirunit.rb +109 -0
  91. data/test/minirunit/testConnect.rb +14 -0
  92. data/test/minirunit/testH2.rb +73 -0
  93. data/test/minirunit/testHsqldb.rb +73 -0
  94. data/test/minirunit/testLoadActiveRecord.rb +3 -0
  95. data/test/minirunit/testMysql.rb +83 -0
  96. data/test/minirunit/testRawSelect.rb +24 -0
  97. data/test/models/add_not_null_column_to_table.rb +12 -0
  98. data/test/models/auto_id.rb +18 -0
  99. data/test/models/data_types.rb +28 -0
  100. data/test/models/entry.rb +23 -0
  101. data/test/models/mixed_case.rb +20 -0
  102. data/test/models/reserved_word.rb +18 -0
  103. data/test/models/string_id.rb +18 -0
  104. data/test/models/validates_uniqueness_of_string.rb +19 -0
  105. data/test/mssql_simple_test.rb +6 -0
  106. data/test/mysql_db_create_test.rb +25 -0
  107. data/test/mysql_multibyte_test.rb +10 -0
  108. data/test/mysql_nonstandard_primary_key_test.rb +42 -0
  109. data/test/mysql_simple_test.rb +32 -0
  110. data/test/oracle_simple_test.rb +29 -0
  111. data/test/pick_rails_version.rb +3 -0
  112. data/test/postgres_db_create_test.rb +21 -0
  113. data/test/postgres_mixed_case_test.rb +19 -0
  114. data/test/postgres_nonseq_pkey_test.rb +40 -0
  115. data/test/postgres_reserved_test.rb +22 -0
  116. data/test/postgres_schema_search_path_test.rb +46 -0
  117. data/test/postgres_simple_test.rb +13 -0
  118. data/test/simple.rb +475 -0
  119. data/test/sqlite3_simple_test.rb +233 -0
  120. data/test/sybase_jtds_simple_test.rb +6 -0
  121. 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