PageRenderTime 26ms CodeModel.GetById 54ms RepoModel.GetById 1ms app.codeStats 0ms

/Support/vendor/rails/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb

https://github.com/drnic/pivotal-tracker-tmbundle
Ruby | 462 lines | 366 code | 75 blank | 21 comment | 38 complexity | 650d9997d05471e912bc41432b9523e9 MD5 | raw file
  1. require 'active_record/connection_adapters/abstract_adapter'
  2. require 'active_support/core_ext/kernel/requires'
  3. module ActiveRecord
  4. class Base
  5. class << self
  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)
  9. unless self.class.const_defined?(:SQLite)
  10. require_library_or_gem(config[:adapter])
  11. db = SQLite::Database.new(config[:database], 0)
  12. db.show_datatypes = "ON" if !defined? SQLite::Version
  13. db.results_as_hash = true if defined? SQLite::Version
  14. db.type_translation = false
  15. # "Downgrade" deprecated sqlite API
  16. if SQLite.const_defined?(:Version)
  17. ConnectionAdapters::SQLite2Adapter.new(db, logger, config)
  18. else
  19. ConnectionAdapters::DeprecatedSQLiteAdapter.new(db, logger, config)
  20. end
  21. end
  22. end
  23. private
  24. def parse_sqlite_config!(config)
  25. # Require database.
  26. unless config[:database]
  27. raise ArgumentError, "No database file specified. Missing argument: database"
  28. end
  29. # Allow database path relative to RAILS_ROOT, but only if
  30. # the database path is not the special path that tells
  31. # Sqlite to build a database only in memory.
  32. if Object.const_defined?(:RAILS_ROOT) && ':memory:' != config[:database]
  33. config[:database] = File.expand_path(config[:database], RAILS_ROOT)
  34. end
  35. end
  36. end
  37. end
  38. module ConnectionAdapters #:nodoc:
  39. class SQLiteColumn < Column #:nodoc:
  40. class << self
  41. def string_to_binary(value)
  42. value.gsub(/\0|\%/n) do |b|
  43. case b
  44. when "\0" then "%00"
  45. when "%" then "%25"
  46. end
  47. end
  48. end
  49. def binary_to_string(value)
  50. value.gsub(/%00|%25/n) do |b|
  51. case b
  52. when "%00" then "\0"
  53. when "%25" then "%"
  54. end
  55. end
  56. end
  57. end
  58. end
  59. # 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
  60. # from http://rubyforge.org/projects/sqlite-ruby/).
  61. #
  62. # Options:
  63. #
  64. # * <tt>:database</tt> - Path to the database file.
  65. class SQLiteAdapter < AbstractAdapter
  66. class Version
  67. include Comparable
  68. def initialize(version_string)
  69. @version = version_string.split('.').map(&:to_i)
  70. end
  71. def <=>(version_string)
  72. @version <=> version_string.split('.').map(&:to_i)
  73. end
  74. end
  75. def initialize(connection, logger, config)
  76. super(connection, logger)
  77. @config = config
  78. end
  79. def adapter_name #:nodoc:
  80. 'SQLite'
  81. end
  82. def supports_ddl_transactions?
  83. sqlite_version >= '2.0.0'
  84. end
  85. def supports_migrations? #:nodoc:
  86. true
  87. end
  88. def supports_primary_key? #:nodoc:
  89. true
  90. end
  91. def requires_reloading?
  92. true
  93. end
  94. def supports_add_column?
  95. sqlite_version >= '3.1.6'
  96. end
  97. def disconnect!
  98. super
  99. @connection.close rescue nil
  100. end
  101. def supports_count_distinct? #:nodoc:
  102. sqlite_version >= '3.2.6'
  103. end
  104. def supports_autoincrement? #:nodoc:
  105. sqlite_version >= '3.1.0'
  106. end
  107. def native_database_types #:nodoc:
  108. {
  109. :primary_key => default_primary_key_type,
  110. :string => { :name => "varchar", :limit => 255 },
  111. :text => { :name => "text" },
  112. :integer => { :name => "integer" },
  113. :float => { :name => "float" },
  114. :decimal => { :name => "decimal" },
  115. :datetime => { :name => "datetime" },
  116. :timestamp => { :name => "datetime" },
  117. :time => { :name => "time" },
  118. :date => { :name => "date" },
  119. :binary => { :name => "blob" },
  120. :boolean => { :name => "boolean" }
  121. }
  122. end
  123. # QUOTING ==================================================
  124. def quote_string(s) #:nodoc:
  125. @connection.class.quote(s)
  126. end
  127. def quote_column_name(name) #:nodoc:
  128. %Q("#{name}")
  129. end
  130. # Quote date/time values for use in SQL input. Includes microseconds
  131. # if the value is a Time responding to usec.
  132. def quoted_date(value) #:nodoc:
  133. if value.acts_like?(:time) && value.respond_to?(:usec)
  134. "#{super}.#{sprintf("%06d", value.usec)}"
  135. else
  136. super
  137. end
  138. end
  139. # DATABASE STATEMENTS ======================================
  140. def execute(sql, name = nil) #:nodoc:
  141. catch_schema_changes { log(sql, name) { @connection.execute(sql) } }
  142. end
  143. def update_sql(sql, name = nil) #:nodoc:
  144. super
  145. @connection.changes
  146. end
  147. def delete_sql(sql, name = nil) #:nodoc:
  148. sql += " WHERE 1=1" unless sql =~ /WHERE/i
  149. super sql, name
  150. end
  151. def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
  152. super || @connection.last_insert_row_id
  153. end
  154. def select_rows(sql, name = nil)
  155. execute(sql, name).map do |row|
  156. (0...(row.size / 2)).map { |i| row[i] }
  157. end
  158. end
  159. def begin_db_transaction #:nodoc:
  160. catch_schema_changes { @connection.transaction }
  161. end
  162. def commit_db_transaction #:nodoc:
  163. catch_schema_changes { @connection.commit }
  164. end
  165. def rollback_db_transaction #:nodoc:
  166. catch_schema_changes { @connection.rollback }
  167. end
  168. # SELECT ... FOR UPDATE is redundant since the table is locked.
  169. def add_lock!(sql, options) #:nodoc:
  170. sql
  171. end
  172. # SCHEMA STATEMENTS ========================================
  173. def tables(name = nil) #:nodoc:
  174. sql = <<-SQL
  175. SELECT name
  176. FROM sqlite_master
  177. WHERE type = 'table' AND NOT name = 'sqlite_sequence'
  178. SQL
  179. execute(sql, name).map do |row|
  180. row[0]
  181. end
  182. end
  183. def columns(table_name, name = nil) #:nodoc:
  184. table_structure(table_name).map do |field|
  185. SQLiteColumn.new(field['name'], field['dflt_value'], field['type'], field['notnull'] == "0")
  186. end
  187. end
  188. def indexes(table_name, name = nil) #:nodoc:
  189. execute("PRAGMA index_list(#{quote_table_name(table_name)})", name).map do |row|
  190. index = IndexDefinition.new(table_name, row['name'])
  191. index.unique = row['unique'] != '0'
  192. index.columns = execute("PRAGMA index_info('#{index.name}')").map { |col| col['name'] }
  193. index
  194. end
  195. end
  196. def primary_key(table_name) #:nodoc:
  197. column = table_structure(table_name).find {|field| field['pk'].to_i == 1}
  198. column ? column['name'] : nil
  199. end
  200. def remove_index(table_name, options={}) #:nodoc:
  201. execute "DROP INDEX #{quote_column_name(index_name(table_name, options))}"
  202. end
  203. def rename_table(name, new_name)
  204. execute "ALTER TABLE #{quote_table_name(name)} RENAME TO #{quote_table_name(new_name)}"
  205. end
  206. # See: http://www.sqlite.org/lang_altertable.html
  207. # SQLite has an additional restriction on the ALTER TABLE statement
  208. def valid_alter_table_options( type, options)
  209. type.to_sym != :primary_key
  210. end
  211. def add_column(table_name, column_name, type, options = {}) #:nodoc:
  212. if supports_add_column? && valid_alter_table_options( type, options )
  213. super(table_name, column_name, type, options)
  214. else
  215. alter_table(table_name) do |definition|
  216. definition.column(column_name, type, options)
  217. end
  218. end
  219. end
  220. def remove_column(table_name, *column_names) #:nodoc:
  221. column_names.flatten.each do |column_name|
  222. alter_table(table_name) do |definition|
  223. definition.columns.delete(definition[column_name])
  224. end
  225. end
  226. end
  227. alias :remove_columns :remove_column
  228. def change_column_default(table_name, column_name, default) #:nodoc:
  229. alter_table(table_name) do |definition|
  230. definition[column_name].default = default
  231. end
  232. end
  233. def change_column_null(table_name, column_name, null, default = nil)
  234. unless null || default.nil?
  235. execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
  236. end
  237. alter_table(table_name) do |definition|
  238. definition[column_name].null = null
  239. end
  240. end
  241. def change_column(table_name, column_name, type, options = {}) #:nodoc:
  242. alter_table(table_name) do |definition|
  243. include_default = options_include_default?(options)
  244. definition[column_name].instance_eval do
  245. self.type = type
  246. self.limit = options[:limit] if options.include?(:limit)
  247. self.default = options[:default] if include_default
  248. self.null = options[:null] if options.include?(:null)
  249. end
  250. end
  251. end
  252. def rename_column(table_name, column_name, new_column_name) #:nodoc:
  253. unless columns(table_name).detect{|c| c.name == column_name.to_s }
  254. raise ActiveRecord::ActiveRecordError, "Missing column #{table_name}.#{column_name}"
  255. end
  256. alter_table(table_name, :rename => {column_name.to_s => new_column_name.to_s})
  257. end
  258. def empty_insert_statement(table_name)
  259. "INSERT INTO #{table_name} VALUES(NULL)"
  260. end
  261. protected
  262. def select(sql, name = nil) #:nodoc:
  263. execute(sql, name).map do |row|
  264. record = {}
  265. row.each_key do |key|
  266. if key.is_a?(String)
  267. record[key.sub(/^"?\w+"?\./, '')] = row[key]
  268. end
  269. end
  270. record
  271. end
  272. end
  273. def table_structure(table_name)
  274. returning structure = execute("PRAGMA table_info(#{quote_table_name(table_name)})") do
  275. raise(ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'") if structure.empty?
  276. end
  277. end
  278. def alter_table(table_name, options = {}) #:nodoc:
  279. altered_table_name = "altered_#{table_name}"
  280. caller = lambda {|definition| yield definition if block_given?}
  281. transaction do
  282. move_table(table_name, altered_table_name,
  283. options.merge(:temporary => true))
  284. move_table(altered_table_name, table_name, &caller)
  285. end
  286. end
  287. def move_table(from, to, options = {}, &block) #:nodoc:
  288. copy_table(from, to, options, &block)
  289. drop_table(from)
  290. end
  291. def copy_table(from, to, options = {}) #:nodoc:
  292. options = options.merge(:id => (!columns(from).detect{|c| c.name == 'id'}.nil? && 'id' == primary_key(from).to_s))
  293. create_table(to, options) do |definition|
  294. @definition = definition
  295. columns(from).each do |column|
  296. column_name = options[:rename] ?
  297. (options[:rename][column.name] ||
  298. options[:rename][column.name.to_sym] ||
  299. column.name) : column.name
  300. @definition.column(column_name, column.type,
  301. :limit => column.limit, :default => column.default,
  302. :null => column.null)
  303. end
  304. @definition.primary_key(primary_key(from)) if primary_key(from)
  305. yield @definition if block_given?
  306. end
  307. copy_table_indexes(from, to, options[:rename] || {})
  308. copy_table_contents(from, to,
  309. @definition.columns.map {|column| column.name},
  310. options[:rename] || {})
  311. end
  312. def copy_table_indexes(from, to, rename = {}) #:nodoc:
  313. indexes(from).each do |index|
  314. name = index.name
  315. if to == "altered_#{from}"
  316. name = "temp_#{name}"
  317. elsif from == "altered_#{to}"
  318. name = name[5..-1]
  319. end
  320. to_column_names = columns(to).map(&:name)
  321. columns = index.columns.map {|c| rename[c] || c }.select do |column|
  322. to_column_names.include?(column)
  323. end
  324. unless columns.empty?
  325. # index name can't be the same
  326. opts = { :name => name.gsub(/_(#{from})_/, "_#{to}_") }
  327. opts[:unique] = true if index.unique
  328. add_index(to, columns, opts)
  329. end
  330. end
  331. end
  332. def copy_table_contents(from, to, columns, rename = {}) #:nodoc:
  333. column_mappings = Hash[*columns.map {|name| [name, name]}.flatten]
  334. rename.inject(column_mappings) {|map, a| map[a.last] = a.first; map}
  335. from_columns = columns(from).collect {|col| col.name}
  336. columns = columns.find_all{|col| from_columns.include?(column_mappings[col])}
  337. quoted_columns = columns.map { |col| quote_column_name(col) } * ','
  338. quoted_to = quote_table_name(to)
  339. @connection.execute "SELECT * FROM #{quote_table_name(from)}" do |row|
  340. sql = "INSERT INTO #{quoted_to} (#{quoted_columns}) VALUES ("
  341. sql << columns.map {|col| quote row[column_mappings[col]]} * ', '
  342. sql << ')'
  343. @connection.execute sql
  344. end
  345. end
  346. def catch_schema_changes
  347. return yield
  348. rescue ActiveRecord::StatementInvalid => exception
  349. if exception.message =~ /database schema has changed/
  350. reconnect!
  351. retry
  352. else
  353. raise
  354. end
  355. end
  356. def sqlite_version
  357. @sqlite_version ||= SQLiteAdapter::Version.new(select_value('select sqlite_version(*)'))
  358. end
  359. def default_primary_key_type
  360. if supports_autoincrement?
  361. 'INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL'.freeze
  362. else
  363. 'INTEGER PRIMARY KEY NOT NULL'.freeze
  364. end
  365. end
  366. def translate_exception(exception, message)
  367. case exception.message
  368. when /column(s)? .* (is|are) not unique/
  369. RecordNotUnique.new(message, exception)
  370. else
  371. super
  372. end
  373. end
  374. end
  375. class SQLite2Adapter < SQLiteAdapter # :nodoc:
  376. def rename_table(name, new_name)
  377. move_table(name, new_name)
  378. end
  379. end
  380. class DeprecatedSQLiteAdapter < SQLite2Adapter # :nodoc:
  381. def insert(sql, name = nil, pk = nil, id_value = nil)
  382. execute(sql, name = nil)
  383. id_value || @connection.last_insert_rowid
  384. end
  385. end
  386. end
  387. end