2 module ActiveRecordExtensions
3 def hsqldb_connection(config)
4 config[:url] ||= "jdbc:hsqldb:#{config[:database]}"
5 config[:driver] ||= "org.hsqldb.jdbcDriver"
6 embedded_driver(config)
9 def h2_connection(config)
10 config[:url] ||= "jdbc:h2:#{config[:database]}"
11 config[:driver] ||= "org.h2.Driver"
12 embedded_driver(config)
17 def self.column_selector
18 [/hsqldb|\.h2\./i, lambda {|cfg,col| col.extend(::JdbcSpec::HSQLDB::Column)}]
21 def self.adapter_selector
22 [/hsqldb|\.h2\./i, lambda do |cfg,adapt|
23 adapt.extend(::JdbcSpec::HSQLDB)
24 def adapt.h2_adapter; true; end if cfg[:driver] =~ /\.h2\./
30 return nil if value.nil? || value =~ /^\s*null\s*$/i
32 when :string then value
33 when :integer then defined?(value.to_i) ? value.to_i : (value ? 1 : 0)
34 when :primary_key then defined?(value.to_i) ? value.to_i : (value ? 1 : 0)
35 when :float then value.to_f
36 when :datetime then cast_to_date_or_time(value)
37 when :timestamp then cast_to_time(value)
38 when :binary then value.scan(/[0-9A-Fa-f]{2}/).collect {|v| v.to_i(16)}.pack("C*")
39 when :time then cast_to_time(value)
43 def cast_to_date_or_time(value)
44 return value if value.is_a? Date
45 return nil if value.blank?
46 guess_date_or_time((value.is_a? Time) ? value : cast_to_time(value))
49 def cast_to_time(value)
50 return value if value.is_a? Time
51 time_array = ParseDate.parsedate value
52 time_array[0] ||= 2000; time_array[1] ||= 1; time_array[2] ||= 1;
53 Time.send(ActiveRecord::Base.default_timezone, *time_array) rescue nil
56 def guess_date_or_time(value)
57 (value.hour == 0 and value.min == 0 and value.sec == 0) ?
58 Date.new(value.year, value.month, value.day) : value
63 def simplified_type(field_type)
72 # Override of ActiveRecord::ConnectionAdapters::Column
73 def extract_limit(sql_type)
74 # HSQLDB appears to return "LONGVARCHAR(0)" for :text columns, which
75 # for AR purposes should be interpreted as "no limit"
76 return nil if sql_type =~ /\(0\)/
82 tp[:primary_key] = "INTEGER GENERATED BY DEFAULT AS IDENTITY(START WITH 0) PRIMARY KEY"
83 tp[:integer][:limit] = nil
84 tp[:boolean][:limit] = nil
85 # set text and float limits so we don't see odd scales tacked on
87 tp[:text][:limit] = nil
88 tp[:float][:limit] = 17
89 tp[:string][:limit] = 255
90 tp[:datetime] = { :name => "DATETIME" }
91 tp[:timestamp] = { :name => "DATETIME" }
92 tp[:time] = { :name => "DATETIME" }
93 tp[:date] = { :name => "DATETIME" }
97 def quote(value, column = nil) # :nodoc:
98 return value.quoted_id if value.respond_to?(:quoted_id)
102 if respond_to?(:h2_adapter) && value.empty?
104 elsif column && column.type == :binary
105 "'#{quote_string(value).unpack("C*").collect {|v| v.to_s(16)}.join}'"
107 "'#{quote_string(value)}'"
113 def quote_string(str)
125 def add_column(table_name, column_name, type, options = {})
126 if option_not_null = options[:null] == false
127 option_not_null = options.delete(:null)
129 add_column_sql = "ALTER TABLE #{quote_table_name(table_name)} ADD #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
130 add_column_options!(add_column_sql, options)
131 execute(add_column_sql)
133 alter_column_sql = "ALTER TABLE #{quote_table_name(table_name)} ALTER #{quote_column_name(column_name)} NOT NULL"
137 def change_column(table_name, column_name, type, options = {}) #:nodoc:
138 execute "ALTER TABLE #{table_name} ALTER COLUMN #{column_name} #{type_to_sql(type, options[:limit])}"
141 def change_column_default(table_name, column_name, default) #:nodoc:
142 execute "ALTER TABLE #{table_name} ALTER COLUMN #{column_name} SET DEFAULT #{quote(default)}"
145 def rename_column(table_name, column_name, new_column_name) #:nodoc:
146 execute "ALTER TABLE #{table_name} ALTER COLUMN #{column_name} RENAME TO #{new_column_name}"
149 def rename_table(name, new_name)
150 execute "ALTER TABLE #{name} RENAME TO #{new_name}"
153 def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
155 @connection.execute_update(sql)
157 table = sql.split(" ", 4)[2]
158 id_value || last_insert_id(table, nil)
161 def last_insert_id(table, sequence_name)
162 Integer(select_value("SELECT IDENTITY() FROM #{table}"))
165 # Override normal #_execute: See Rubyforge #11567
166 def _execute(sql, name = nil)
167 if ::ActiveRecord::ConnectionAdapters::JdbcConnection::select?(sql)
168 @connection.execute_query(sql)
169 elsif ::ActiveRecord::ConnectionAdapters::JdbcConnection::insert?(sql)
172 @connection.execute_update(sql)
176 def add_limit_offset!(sql, options) #:nodoc:
177 offset = options[:offset] || 0
179 if limit = options[:limit]
180 sql.replace "select limit #{offset} #{limit} #{bef}"
182 sql.replace "select limit #{offset} 0 #{bef}"
186 # override to filter out system tables that otherwise end
187 # up in db/schema.rb during migrations. JdbcConnection#tables
188 # now takes an optional block filter so we can screen out
189 # rows corresponding to system tables. HSQLDB names its
190 # system tables SYSTEM.*, but H2 seems to name them without
191 # any kind of convention
193 @connection.tables.select {|row| row.to_s !~ /^system_/i }
196 def remove_index(table_name, options = {})
197 execute "DROP INDEX #{quote_column_name(index_name(table_name, options))}"