2 module ActiveRecordExtensions
3 def sqlite3_connection(config)
4 config[:url] ||= "jdbc:sqlite:#{config[:database]}"
5 config[:driver] ||= "org.sqlite.JDBC"
6 jdbc_connection(config)
11 def self.column_selector
12 [/sqlite/i, lambda {|cfg,col| col.extend(::JdbcSpec::SQLite3::Column)}]
15 def self.adapter_selector
16 [/sqlite/i, lambda {|cfg,adapt| adapt.extend(::JdbcSpec::SQLite3)}]
22 def simplified_type(field_type)
24 when /^integer\(1\)$/i then :boolean
25 when /text/i then :string
26 when /int/i then :integer
27 when /real/i then @scale == 0 ? :integer : :decimal
28 when /date|time/i then :datetime
29 when /blob/i then :binary
33 def self.cast_to_date_or_time(value)
34 return value if value.is_a? Date
35 return nil if value.blank?
36 guess_date_or_time((value.is_a? Time) ? value : cast_to_time(value))
39 def self.cast_to_time(value)
40 return value if value.is_a? Time
41 Time.at(value) rescue nil
44 def self.guess_date_or_time(value)
45 (value.hour == 0 and value.min == 0 and value.sec == 0) ?
46 Date.new(value.year, value.month, value.day) : value
51 return nil if value.nil?
53 when :string then value
54 when :integer then defined?(value.to_i) ? value.to_i : (value ? 1 : 0)
55 when :primary_key then defined?(value.to_i) ? value.to_i : (value ? 1 : 0)
56 when :float then value.to_f
57 when :datetime then JdbcSpec::SQLite3::Column.cast_to_date_or_time(value)
58 when :time then JdbcSpec::SQLite3::Column.cast_to_time(value)
59 when :decimal then self.class.value_to_decimal(value)
60 when :boolean then self.class.value_to_boolean(value)
66 tp[:primary_key] = "INTEGER PRIMARY KEY AUTOINCREMENT"
67 tp[:float] = { :name => "REAL" }
68 tp[:decimal] = { :name => "REAL" }
69 tp[:datetime] = { :name => "INTEGER" }
70 tp[:timestamp] = { :name => "INTEGER" }
71 tp[:time] = { :name => "INTEGER" }
72 tp[:date] = { :name => "INTEGER" }
73 tp[:boolean] = { :name => "INTEGER", :limit => 1}
77 def quote(value, column = nil) # :nodoc:
78 return value.quoted_id if value.respond_to?(:quoted_id)
82 if column && column.type == :binary
83 "'#{quote_string(value).unpack("C*").collect {|v| v.to_s(16)}.join}'"
85 "'#{quote_string(value)}'"
103 def add_column(table_name, column_name, type, options = {})
104 if option_not_null = options[:null] == false
105 option_not_null = options.delete(:null)
107 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])}"
108 add_column_options!(add_column_sql, options)
109 execute(add_column_sql)
111 alter_column_sql = "ALTER TABLE #{quote_table_name(table_name)} ALTER #{quote_column_name(column_name)} NOT NULL"
115 def remove_column(table_name, column_name) #:nodoc:
116 cols = columns(table_name).collect {|col| col.name}
117 cols.delete(column_name)
118 cols = cols.join(', ')
119 table_backup = table_name + "_backup"
123 execute "CREATE TEMPORARY TABLE #{table_backup}(#{cols})"
124 insert "INSERT INTO #{table_backup} SELECT #{cols} FROM #{table_name}"
125 execute "DROP TABLE #{table_name}"
126 execute "CREATE TABLE #{table_name}(#{cols})"
127 insert "INSERT INTO #{table_name} SELECT #{cols} FROM #{table_backup}"
128 execute "DROP TABLE #{table_backup}"
133 def change_column(table_name, column_name, type, options = {}) #:nodoc:
134 execute "ALTER TABLE #{table_name} ALTER COLUMN #{column_name} #{type_to_sql(type, options[:limit])}"
137 def change_column_default(table_name, column_name, default) #:nodoc:
138 execute "ALTER TABLE #{table_name} ALTER COLUMN #{column_name} SET DEFAULT #{quote(default)}"
141 def rename_column(table_name, column_name, new_column_name) #:nodoc:
142 execute "ALTER TABLE #{table_name} ALTER COLUMN #{column_name} RENAME TO #{new_column_name}"
145 def rename_table(name, new_name)
146 execute "ALTER TABLE #{name} RENAME TO #{new_name}"
149 def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
151 @connection.execute_update(sql)
153 table = sql.split(" ", 4)[2]
154 id_value || last_insert_id(table, nil)
157 def last_insert_id(table, sequence_name)
158 Integer(select_value("SELECT SEQ FROM SQLITE_SEQUENCE WHERE NAME = '#{table}'"))
161 def add_limit_offset!(sql, options) #:nodoc:
163 sql << " LIMIT #{options[:limit]}"
164 sql << " OFFSET #{options[:offset]}" if options[:offset]
169 @connection.tables.select {|row| row.to_s !~ /^sqlite_/i }
172 def remove_index(table_name, options = {})
173 execute "DROP INDEX #{quote_column_name(index_name(table_name, options))}"
176 def indexes(table_name, name = nil)
177 result = select_rows("SELECT name, sql FROM sqlite_master WHERE tbl_name = '#{table_name}' AND type = 'index'", name)
179 result.collect do |row|
182 unique = (index_sql =~ /unique/i)
183 cols = index_sql.match(/\((.*)\)/)[1].gsub(/,/,' ').split
184 ::ActiveRecord::ConnectionAdapters::IndexDefinition.new(table_name, name, unique, cols)