84f8c0284ea7d11ba0e8111debee25609ffd4c71
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
)
22 ConnectionAdapters
::DeprecatedSQLiteAdapter.new(db
, logger
)
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
75 def adapter_name
#:nodoc:
79 def supports_migrations
? #:nodoc:
83 def requires_reloading
?
89 @connection.close
rescue nil
92 def supports_count_distinct
? #:nodoc:
93 sqlite_version
>= '3.2.6'
96 def supports_autoincrement
? #:nodoc:
97 sqlite_version
>= '3.1.0'
100 def native_database_types
#:nodoc:
102 :primary_key => default_primary_key_type
,
103 :string => { :name => "varchar", :limit => 255 },
104 :text => { :name => "text" },
105 :integer => { :name => "integer" },
106 :float => { :name => "float" },
107 :decimal => { :name => "decimal" },
108 :datetime => { :name => "datetime" },
109 :timestamp => { :name => "datetime" },
110 :time => { :name => "time" },
111 :date => { :name => "date" },
112 :binary => { :name => "blob" },
113 :boolean => { :name => "boolean" }
118 # QUOTING ==================================================
120 def quote_string(s
) #:nodoc:
121 @connection.class.quote(s
)
124 def quote_column_name(name
) #:nodoc:
129 # DATABASE STATEMENTS ======================================
131 def execute(sql, name = nil) #:nodoc:
132 catch_schema_changes { log(sql, name) { @connection.execute(sql) } }
135 def update_sql(sql, name = nil) #:nodoc:
140 def delete_sql(sql, name = nil) #:nodoc:
141 sql += " WHERE 1=1" unless sql =~ /WHERE/i
145 def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
146 super || @connection.last_insert_row_id
149 def select_rows(sql, name = nil)
150 execute(sql, name).map do |row|
151 (0...(row.size / 2)).map { |i| row[i] }
155 def begin_db_transaction #:nodoc:
156 catch_schema_changes { @connection.transaction }
159 def commit_db_transaction #:nodoc:
160 catch_schema_changes { @connection.commit }
163 def rollback_db_transaction #:nodoc:
164 catch_schema_changes { @connection.rollback }
168 # SELECT ... FOR UPDATE is redundant since the table is locked.
169 def add_lock!(sql, options) #:nodoc:
174 # SCHEMA STATEMENTS ========================================
176 def tables(name = nil) #:nodoc:
180 WHERE type = 'table' AND NOT name = 'sqlite_sequence'
183 execute(sql
, name
).map
do |row
|
188 def columns(table_name
, name
= nil) #:nodoc:
189 table_structure(table_name
).map
do |field
|
190 SQLiteColumn
.new(field
['name'], field
['dflt_value'], field
['type'], field
['notnull'] == "0")
194 def indexes(table_name
, name
= nil) #:nodoc:
195 execute("PRAGMA index_list(#{quote_table_name(table_name)})", name
).map
do |row
|
196 index
= IndexDefinition
.new(table_name
, row
['name'])
197 index
.unique
= row
['unique'] != '0'
198 index
.columns
= execute("PRAGMA index_info('#{index.name}')").map
{ |col
| col
['name'] }
203 def primary_key(table_name
) #:nodoc:
204 column
= table_structure(table_name
).find
{|field
| field
['pk'].to_i
== 1}
205 column
? column
['name'] : nil
208 def remove_index(table_name
, options
={}) #:nodoc:
209 execute
"DROP INDEX #{quote_column_name(index_name(table_name, options))}"
212 def rename_table(name
, new_name
)
213 execute
"ALTER TABLE #{name} RENAME TO #{new_name}"
216 def add_column(table_name
, column_name
, type
, options
= {}) #:nodoc:
217 if @connection.respond_to
?(:transaction_active?) && @connection.transaction_active
?
218 raise StatementInvalid
, 'Cannot add columns to a SQLite database while inside a transaction'
221 super(table_name
, column_name
, type
, options
)
222 # See last paragraph on http://www.sqlite.org/lang_altertable.html
226 def remove_column(table_name
, *column_names
) #:nodoc:
227 column_names
.flatten
.each
do |column_name
|
228 alter_table(table_name
) do |definition
|
229 definition
.columns
.delete(definition
[column_name
])
233 alias :remove_columns :remove_column
235 def change_column_default(table_name
, column_name
, default
) #:nodoc:
236 alter_table(table_name
) do |definition
|
237 definition
[column_name
].default
= default
241 def change_column_null(table_name
, column_name
, null
, default
= nil)
242 unless null
|| default
.nil?
243 execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
245 alter_table(table_name
) do |definition
|
246 definition
[column_name
].null
= null
250 def change_column(table_name
, column_name
, type
, options
= {}) #:nodoc:
251 alter_table(table_name
) do |definition
|
252 include_default
= options_include_default
?(options
)
253 definition
[column_name
].instance_eval
do
255 self.limit
= options
[:limit] if options
.include?(:limit)
256 self.default
= options
[:default] if include_default
257 self.null
= options
[:null] if options
.include?(:null)
262 def rename_column(table_name
, column_name
, new_column_name
) #:nodoc:
263 unless columns(table_name
).detect
{|c
| c
.name
== column_name
.to_s
}
264 raise ActiveRecord
::ActiveRecordError, "Missing column #{table_name}.#{column_name}"
266 alter_table(table_name
, :rename => {column_name
.to_s
=> new_column_name
.to_s
})
269 def empty_insert_statement(table_name
)
270 "INSERT INTO #{table_name} VALUES(NULL)"
274 def select(sql
, name
= nil) #:nodoc:
275 execute(sql
, name
).map
do |row
|
277 row
.each_key
do |key
|
279 record
[key
.sub(/^"?\w+"?\./, '')] = row
[key
]
286 def table_structure(table_name
)
287 returning structure
= execute("PRAGMA table_info(#{quote_table_name(table_name)})") do
288 raise(ActiveRecord
::StatementInvalid, "Could not find table '#{table_name}'") if structure
.empty
?
292 def alter_table(table_name
, options
= {}) #:nodoc:
293 altered_table_name
= "altered_#{table_name}"
294 caller
= lambda
{|definition
| yield definition
if block_given
?}
297 move_table(table_name
, altered_table_name
,
298 options
.merge(:temporary => true))
299 move_table(altered_table_name
, table_name
, &caller
)
303 def move_table(from
, to
, options
= {}, &block
) #:nodoc:
304 copy_table(from
, to
, options
, &block
)
308 def copy_table(from
, to
, options
= {}) #:nodoc:
309 options
= options
.merge(:id => !columns(from
).detect
{|c
| c
.name
== 'id'}.nil?)
310 create_table(to
, options
) do |definition
|
311 @definition = definition
312 columns(from
).each
do |column
|
313 column_name
= options
[:rename] ?
314 (options
[:rename][column
.name
] ||
315 options
[:rename][column
.name
.to_sym
] ||
316 column
.name
) : column
.name
318 @definition.column(column_name
, column
.type
,
319 :limit => column
.limit
, :default => column
.default
,
320 :null => column
.null
)
322 @definition.primary_key(primary_key(from
)) if primary_key(from
)
323 yield @definition if block_given
?
326 copy_table_indexes(from
, to
, options
[:rename] || {})
327 copy_table_contents(from
, to
,
328 @definition.columns
.map
{|column
| column
.name
},
329 options
[:rename] || {})
332 def copy_table_indexes(from
, to
, rename
= {}) #:nodoc:
333 indexes(from
).each
do |index
|
335 if to
== "altered_#{from}"
336 name
= "temp_#{name}"
337 elsif from
== "altered_#{to}"
341 to_column_names
= columns(to
).map(&:name)
342 columns
= index
.columns
.map
{|c
| rename
[c
] || c
}.select
do |column
|
343 to_column_names
.include?(column
)
346 unless columns
.empty
?
347 # index name can't be the same
348 opts
= { :name => name
.gsub(/_(#{from})_/, "_#{to}_") }
349 opts
[:unique] = true if index
.unique
350 add_index(to
, columns
, opts
)
355 def copy_table_contents(from
, to
, columns
, rename
= {}) #:nodoc:
356 column_mappings
= Hash
[*columns
.map
{|name
| [name
, name
]}.flatten
]
357 rename
.inject(column_mappings
) {|map
, a
| map
[a
.last
] = a
.first
; map
}
358 from_columns
= columns(from
).collect
{|col
| col
.name
}
359 columns
= columns
.find_all
{|col
| from_columns
.include?(column_mappings
[col
])}
360 quoted_columns
= columns
.map
{ |col
| quote_column_name(col
) } * ','
362 quoted_to
= quote_table_name(to
)
363 @connection.execute
"SELECT * FROM #{quote_table_name(from)}" do |row
|
364 sql
= "INSERT INTO #{quoted_to} (#{quoted_columns}) VALUES ("
365 sql
<< columns
.map
{|col
| quote row
[column_mappings
[col
]]} * ', '
367 @connection.execute sql
371 def catch_schema_changes
373 rescue ActiveRecord
::StatementInvalid => exception
374 if exception
.message
=~
/database schema has changed/
383 @sqlite_version ||= select_value('select sqlite_version(*)')
386 def default_primary_key_type
387 if supports_autoincrement
?
388 'INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL'.freeze
390 'INTEGER PRIMARY KEY NOT NULL'.freeze
395 class SQLite2Adapter
< SQLiteAdapter
# :nodoc:
396 def supports_count_distinct
? #:nodoc:
400 def rename_table(name
, new_name
)
401 move_table(name
, new_name
)
404 def add_column(table_name
, column_name
, type
, options
= {}) #:nodoc:
405 alter_table(table_name
) do |definition
|
406 definition
.column(column_name
, type
, options
)
411 class DeprecatedSQLiteAdapter
< SQLite2Adapter
# :nodoc:
412 def insert(sql
, name
= nil, pk
= nil, id_value
= nil)
413 execute(sql
, name
= nil)
414 id_value
|| @connection.last_insert_rowid