1 require 'active_record/connection_adapters/abstract_adapter'
6 # Establishes a connection to the database that's used by all Active Record objects
7 def sqlite_connection(config
) # :nodoc:
8 parse_sqlite_config
!(config
)
10 unless self.class.const_defined
?(:SQLite)
11 require_library_or_gem(config
[:adapter])
13 db
= SQLite
::Database.new(config
[:database], 0)
14 db
.show_datatypes
= "ON" if !defined? SQLite
::Version
15 db
.results_as_hash
= true if defined? SQLite
::Version
16 db
.type_translation
= false
18 # "Downgrade" deprecated sqlite API
19 if SQLite
.const_defined
?(:Version)
20 ConnectionAdapters
::SQLite2Adapter.new(db
, logger
, config
)
22 ConnectionAdapters
::DeprecatedSQLiteAdapter.new(db
, logger
, config
)
28 def parse_sqlite_config
!(config
)
29 config
[:database] ||= config
[:dbfile]
31 unless config
[:database]
32 raise ArgumentError
, "No database file specified. Missing argument: database"
35 # Allow database path relative to RAILS_ROOT, but only if
36 # the database path is not the special path that tells
37 # Sqlite to build a database only in memory.
38 if Object
.const_defined
?(:RAILS_ROOT) && ':memory:' != config
[:database]
39 config
[:database] = File
.expand_path(config
[:database], RAILS_ROOT
)
45 module ConnectionAdapters
#:nodoc:
46 class SQLiteColumn
< Column
#:nodoc:
48 def string_to_binary(value
)
49 value
.gsub(/\0|\%/n
) do |b
|
57 def binary_to_string(value
)
58 value
.gsub(/%00|%25/n
) do |b
|
68 # The SQLite adapter works with both the 2.x and 3.x series of SQLite with the sqlite-ruby drivers (available both as gems and
69 # from http://rubyforge.org/projects/sqlite-ruby/).
73 # * <tt>:database</tt> - Path to the database file.
74 class SQLiteAdapter
< AbstractAdapter
78 def initialize(version_string
)
79 @version = version_string
.split('.').map(&:to_i)
82 def <=>(version_string
)
83 @version <=> version_string
.split('.').map(&:to_i)
87 def initialize(connection
, logger
, config
)
88 super(connection
, logger
)
92 def adapter_name
#:nodoc:
96 def supports_ddl_transactions
?
97 sqlite_version
>= '2.0.0'
100 def supports_migrations
? #:nodoc:
104 def requires_reloading
?
108 def supports_add_column
?
109 sqlite_version
>= '3.1.6'
114 @connection.close
rescue nil
117 def supports_count_distinct
? #:nodoc:
118 sqlite_version
>= '3.2.6'
121 def supports_autoincrement
? #:nodoc:
122 sqlite_version
>= '3.1.0'
125 def native_database_types
#:nodoc:
127 :primary_key => default_primary_key_type
,
128 :string => { :name => "varchar", :limit => 255 },
129 :text => { :name => "text" },
130 :integer => { :name => "integer" },
131 :float => { :name => "float" },
132 :decimal => { :name => "decimal" },
133 :datetime => { :name => "datetime" },
134 :timestamp => { :name => "datetime" },
135 :time => { :name => "time" },
136 :date => { :name => "date" },
137 :binary => { :name => "blob" },
138 :boolean => { :name => "boolean" }
143 # QUOTING ==================================================
145 def quote_string(s
) #:nodoc:
146 @connection.class.quote(s
)
149 def quote_column_name(name
) #:nodoc:
154 # DATABASE STATEMENTS ======================================
156 def execute(sql, name = nil) #:nodoc:
157 catch_schema_changes { log(sql, name) { @connection.execute(sql) } }
160 def update_sql(sql, name = nil) #:nodoc:
165 def delete_sql(sql, name = nil) #:nodoc:
166 sql += " WHERE 1=1" unless sql =~ /WHERE/i
170 def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
171 super || @connection.last_insert_row_id
174 def select_rows(sql, name = nil)
175 execute(sql, name).map do |row|
176 (0...(row.size / 2)).map { |i| row[i] }
180 def begin_db_transaction #:nodoc:
181 catch_schema_changes { @connection.transaction }
184 def commit_db_transaction #:nodoc:
185 catch_schema_changes { @connection.commit }
188 def rollback_db_transaction #:nodoc:
189 catch_schema_changes { @connection.rollback }
192 # SELECT ... FOR UPDATE is redundant since the table is locked.
193 def add_lock!(sql, options) #:nodoc:
198 # SCHEMA STATEMENTS ========================================
200 def tables(name = nil) #:nodoc:
204 WHERE type = 'table' AND NOT name = 'sqlite_sequence'
207 execute(sql
, name
).map
do |row
|
212 def columns(table_name
, name
= nil) #:nodoc:
213 table_structure(table_name
).map
do |field
|
214 SQLiteColumn
.new(field
['name'], field
['dflt_value'], field
['type'], field
['notnull'] == "0")
218 def indexes(table_name
, name
= nil) #:nodoc:
219 execute("PRAGMA index_list(#{quote_table_name(table_name)})", name
).map
do |row
|
220 index
= IndexDefinition
.new(table_name
, row
['name'])
221 index
.unique
= row
['unique'] != '0'
222 index
.columns
= execute("PRAGMA index_info('#{index.name}')").map
{ |col
| col
['name'] }
227 def primary_key(table_name
) #:nodoc:
228 column
= table_structure(table_name
).find
{|field
| field
['pk'].to_i
== 1}
229 column
? column
['name'] : nil
232 def remove_index(table_name
, options
={}) #:nodoc:
233 execute
"DROP INDEX #{quote_column_name(index_name(table_name, options))}"
236 def rename_table(name
, new_name
)
237 execute
"ALTER TABLE #{name} RENAME TO #{new_name}"
240 # See: http://www.sqlite.org/lang_altertable.html
241 # SQLite has an additional restriction on the ALTER TABLE statement
242 def valid_alter_table_options( type
, options
)
243 type
.to_sym
!= :primary_key
246 def add_column(table_name
, column_name
, type
, options
= {}) #:nodoc:
247 if supports_add_column
? && valid_alter_table_options( type
, options
)
248 super(table_name
, column_name
, type
, options
)
250 alter_table(table_name
) do |definition
|
251 definition
.column(column_name
, type
, options
)
256 def remove_column(table_name
, *column_names
) #:nodoc:
257 column_names
.flatten
.each
do |column_name
|
258 alter_table(table_name
) do |definition
|
259 definition
.columns
.delete(definition
[column_name
])
263 alias :remove_columns :remove_column
265 def change_column_default(table_name
, column_name
, default
) #:nodoc:
266 alter_table(table_name
) do |definition
|
267 definition
[column_name
].default
= default
271 def change_column_null(table_name
, column_name
, null
, default
= nil)
272 unless null
|| default
.nil?
273 execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
275 alter_table(table_name
) do |definition
|
276 definition
[column_name
].null
= null
280 def change_column(table_name
, column_name
, type
, options
= {}) #:nodoc:
281 alter_table(table_name
) do |definition
|
282 include_default
= options_include_default
?(options
)
283 definition
[column_name
].instance_eval
do
285 self.limit
= options
[:limit] if options
.include?(:limit)
286 self.default
= options
[:default] if include_default
287 self.null
= options
[:null] if options
.include?(:null)
292 def rename_column(table_name
, column_name
, new_column_name
) #:nodoc:
293 unless columns(table_name
).detect
{|c
| c
.name
== column_name
.to_s
}
294 raise ActiveRecord
::ActiveRecordError, "Missing column #{table_name}.#{column_name}"
296 alter_table(table_name
, :rename => {column_name
.to_s
=> new_column_name
.to_s
})
299 def empty_insert_statement(table_name
)
300 "INSERT INTO #{table_name} VALUES(NULL)"
304 def select(sql
, name
= nil) #:nodoc:
305 execute(sql
, name
).map
do |row
|
307 row
.each_key
do |key
|
309 record
[key
.sub(/^"?\w+"?\./, '')] = row
[key
]
316 def table_structure(table_name
)
317 returning structure
= execute("PRAGMA table_info(#{quote_table_name(table_name)})") do
318 raise(ActiveRecord
::StatementInvalid, "Could not find table '#{table_name}'") if structure
.empty
?
322 def alter_table(table_name
, options
= {}) #:nodoc:
323 altered_table_name
= "altered_#{table_name}"
324 caller
= lambda
{|definition
| yield definition
if block_given
?}
327 move_table(table_name
, altered_table_name
,
328 options
.merge(:temporary => true))
329 move_table(altered_table_name
, table_name
, &caller
)
333 def move_table(from
, to
, options
= {}, &block
) #:nodoc:
334 copy_table(from
, to
, options
, &block
)
338 def copy_table(from
, to
, options
= {}) #:nodoc:
339 options
= options
.merge(:id => (!columns(from
).detect
{|c
| c
.name
== 'id'}.nil? && 'id' == primary_key(from
).to_s
))
340 create_table(to
, options
) do |definition
|
341 @definition = definition
342 columns(from
).each
do |column
|
343 column_name
= options
[:rename] ?
344 (options
[:rename][column
.name
] ||
345 options
[:rename][column
.name
.to_sym
] ||
346 column
.name
) : column
.name
348 @definition.column(column_name
, column
.type
,
349 :limit => column
.limit
, :default => column
.default
,
350 :null => column
.null
)
352 @definition.primary_key(primary_key(from
)) if primary_key(from
)
353 yield @definition if block_given
?
356 copy_table_indexes(from
, to
, options
[:rename] || {})
357 copy_table_contents(from
, to
,
358 @definition.columns
.map
{|column
| column
.name
},
359 options
[:rename] || {})
362 def copy_table_indexes(from
, to
, rename
= {}) #:nodoc:
363 indexes(from
).each
do |index
|
365 if to
== "altered_#{from}"
366 name
= "temp_#{name}"
367 elsif from
== "altered_#{to}"
371 to_column_names
= columns(to
).map(&:name)
372 columns
= index
.columns
.map
{|c
| rename
[c
] || c
}.select
do |column
|
373 to_column_names
.include?(column
)
376 unless columns
.empty
?
377 # index name can't be the same
378 opts
= { :name => name
.gsub(/_(#{from})_/, "_#{to}_") }
379 opts
[:unique] = true if index
.unique
380 add_index(to
, columns
, opts
)
385 def copy_table_contents(from
, to
, columns
, rename
= {}) #:nodoc:
386 column_mappings
= Hash
[*columns
.map
{|name
| [name
, name
]}.flatten
]
387 rename
.inject(column_mappings
) {|map
, a
| map
[a
.last
] = a
.first
; map
}
388 from_columns
= columns(from
).collect
{|col
| col
.name
}
389 columns
= columns
.find_all
{|col
| from_columns
.include?(column_mappings
[col
])}
390 quoted_columns
= columns
.map
{ |col
| quote_column_name(col
) } * ','
392 quoted_to
= quote_table_name(to
)
393 @connection.execute
"SELECT * FROM #{quote_table_name(from)}" do |row
|
394 sql
= "INSERT INTO #{quoted_to} (#{quoted_columns}) VALUES ("
395 sql
<< columns
.map
{|col
| quote row
[column_mappings
[col
]]} * ', '
397 @connection.execute sql
401 def catch_schema_changes
403 rescue ActiveRecord
::StatementInvalid => exception
404 if exception
.message
=~
/database schema has changed/
413 @sqlite_version ||= SQLiteAdapter
::Version.new(select_value('select sqlite_version(*)'))
416 def default_primary_key_type
417 if supports_autoincrement
?
418 'INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL'.freeze
420 'INTEGER PRIMARY KEY NOT NULL'.freeze
425 class SQLite2Adapter
< SQLiteAdapter
# :nodoc:
426 def rename_table(name
, new_name
)
427 move_table(name
, new_name
)
431 class DeprecatedSQLiteAdapter
< SQLite2Adapter
# :nodoc:
432 def insert(sql
, name
= nil, pk
= nil, id_value
= nil)
433 execute(sql
, name
= nil)
434 id_value
|| @connection.last_insert_rowid