# File lib/arjdbc/sqlite3/adapter.rb, line 375 def primary_key(table_name) column = table_structure(table_name).find { |field| field['pk'].to_i == 1 } column && column['name'] end
module ArJdbc::SQLite3
Constants
- ADAPTER_NAME
- NATIVE_DATABASE_TYPES
Public Class Methods
@see ActiveRecord::ConnectionAdapters::Jdbc::ArelSupport
# File lib/arjdbc/sqlite3/adapter.rb, line 100 def self.arel_visitor_type(config = nil) ::Arel::Visitors::SQLite end
@see ActiveRecord::ConnectionAdapters::JdbcColumn#column_types
# File lib/arjdbc/sqlite3/adapter.rb, line 18 def self.column_selector [ /sqlite/i, lambda { |config, column| column.extend(Column) } ] end
@see ActiveRecord::ConnectionAdapters::JdbcAdapter#jdbc_connection_class
# File lib/arjdbc/sqlite3/adapter.rb, line 11 def self.jdbc_connection_class ::ActiveRecord::ConnectionAdapters::SQLite3JdbcConnection end
Public Instance Methods
# File lib/arjdbc/sqlite3/adapter.rb, line 112 def adapter_name ADAPTER_NAME end
# File lib/arjdbc/sqlite3/adapter.rb, line 429 def add_column(table_name, column_name, type, options = {}) if supports_add_column? && valid_alter_table_options( type, options ) super(table_name, column_name, type, options) else alter_table(table_name) do |definition| definition.column(column_name, type, options) end end end
@private
# File lib/arjdbc/sqlite3/adapter.rb, line 506 def add_lock!(sql, options) sql # SELECT ... FOR UPDATE is redundant since the table is locked end
Returns 62. SQLite supports index names up to 64 characters. The rest is used by Rails internally to perform temporary rename operations. @return [Fixnum]
# File lib/arjdbc/sqlite3/adapter.rb, line 281 def allowed_index_name_length index_name_length - 2 end
# File lib/arjdbc/sqlite3/adapter.rb, line 483 def change_column(table_name, column_name, type, options = {}) alter_table(table_name) do |definition| include_default = options_include_default?(options) definition[column_name].instance_eval do self.type = type self.limit = options[:limit] if options.include?(:limit) self.default = options[:default] if include_default self.null = options[:null] if options.include?(:null) self.precision = options[:precision] if options.include?(:precision) self.scale = options[:scale] if options.include?(:scale) end end end
# File lib/arjdbc/sqlite3/adapter.rb, line 474 def change_column_null(table_name, column_name, null, default = nil) unless null || default.nil? execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL") end alter_table(table_name) do |definition| definition[column_name].null = null end end
@override
# File lib/arjdbc/sqlite3/adapter.rb, line 360 def columns(table_name, name = nil) column = jdbc_column_class pass_cast_type = respond_to?(:lookup_cast_type) table_structure(table_name).map do |field| sql_type = field['type'] if pass_cast_type cast_type = lookup_cast_type(sql_type) column.new(field['name'], field['dflt_value'], cast_type, sql_type, field['notnull'] == 0) else column.new(field['name'], field['dflt_value'], sql_type, field['notnull'] == 0) end end end
@private
# File lib/arjdbc/sqlite3/adapter.rb, line 307 def create_database(name = nil, options = {}) end
@override
# File lib/arjdbc/sqlite3/adapter.rb, line 286 def create_savepoint(name = current_savepoint_name(true)) log("SAVEPOINT #{name}", 'Savepoint') { super } end
# File lib/arjdbc/sqlite3/adapter.rb, line 142 def default_primary_key_type if supports_autoincrement? 'integer PRIMARY KEY AUTOINCREMENT NOT NULL' else 'integer PRIMARY KEY NOT NULL' end end
@private
# File lib/arjdbc/sqlite3/adapter.rb, line 311 def drop_database(name = nil) tables.each { |table| drop_table(table) } end
# File lib/arjdbc/sqlite3/adapter.rb, line 510 def empty_insert_statement_value # inherited (default) on 3.2 : "VALUES(DEFAULT)" # inherited (default) on 4.0 : "DEFAULT VALUES" # re-defined in native adapter on 3.2 "VALUES(NULL)" # on 4.0 no longer re-defined (thus inherits default) "DEFAULT VALUES" end
# File lib/arjdbc/sqlite3/adapter.rb, line 518 def encoding select_value 'PRAGMA encoding' end
@note Does not support prepared statements for INSERT statements. @override
# File lib/arjdbc/sqlite3/adapter.rb, line 344 def exec_insert(sql, name, binds, pk = nil, sequence_name = nil) # NOTE: since SQLite JDBC does not support executeUpdate but only # statement.execute we can not support prepared statements here : execute(sql, name, binds) end
NOTE: do not override indexes without testing support for 3.7.2 & 3.8.7 ! @override
# File lib/arjdbc/sqlite3/adapter.rb, line 382 def indexes(table_name, name = nil) # on JDBC 3.7 we'll simply do super since it can not handle "PRAGMA index_info" return @connection.indexes(table_name, name) if sqlite_version < '3.8' # super name ||= 'SCHEMA' exec_query_raw("PRAGMA index_list(#{quote_table_name(table_name)})", name).map do |row| index_name = row['name'] sql = "SELECT sql FROM sqlite_master" sql << " WHERE name=#{quote(index_name)} AND type='index'" sql << " UNION ALL " sql << "SELECT sql FROM sqlite_temp_master" sql << " WHERE name=#{quote(index_name)} AND type='index'" where = nil exec_query_raw(sql, name) do |index_sql| match = /\sWHERE\s+(.+)$/i.match(index_sql) where = match[1] if match end begin columns = exec_query_raw("PRAGMA index_info('#{index_name}')", name).map { |col| col['name'] } rescue => e # NOTE: JDBC <= 3.8.7 bug work-around : if e.message && e.message.index('[SQLITE_ERROR] SQL error or missing database') columns = [] end raise e end new_index_definition(table_name, index_name, row['unique'] != 0, columns, nil, nil, where) end end
@note We have an extra binds argument at the end due AR-2.3 support. @override
# File lib/arjdbc/sqlite3/adapter.rb, line 337 def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = []) result = execute(sql, name, binds) id_value || last_inserted_id(result) end
# File lib/arjdbc/sqlite3/adapter.rb, line 15 def jdbc_column_class; ::ActiveRecord::ConnectionAdapters::SQLite3Column end
# File lib/arjdbc/sqlite3/adapter.rb, line 522 def last_insert_id @connection.last_insert_rowid end
@override
# File lib/arjdbc/sqlite3/adapter.rb, line 136 def native_database_types types = NATIVE_DATABASE_TYPES.dup types[:primary_key] = default_primary_key_type types end
@override
@override
# File lib/arjdbc/sqlite3/adapter.rb, line 221 def quote(value, column = nil) return value if sql_literal?(value) if value.kind_of?(String) column_type = column && column.type if column_type == :binary "x'#{value.unpack("H*")[0]}'" else super end else super end end
@override
# File lib/arjdbc/sqlite3/adapter.rb, line 241 def quote_column_name(name) %Q("#{name.to_s.gsub('"', '""')}") # "' kludge for emacs font-lock end
# File lib/arjdbc/sqlite3/adapter.rb, line 236 def quote_table_name_for_assignment(table, attr) quote_column_name(attr) end
Quote date/time values for use in SQL input. Includes microseconds if the value is a Time responding to usec. @override
# File lib/arjdbc/sqlite3/adapter.rb, line 248 def quoted_date(value) if value.acts_like?(:time) && value.respond_to?(:usec) "#{super}.#{sprintf("%06d", value.usec)}" else super end end
@private
# File lib/arjdbc/sqlite3/adapter.rb, line 301 def recreate_database(name = nil, options = {}) drop_database(name) create_database(name, options) end
@override
# File lib/arjdbc/sqlite3/adapter.rb, line 296 def release_savepoint(name = current_savepoint_name(false)) log("RELEASE SAVEPOINT #{name}", 'Savepoint') { super } end
@private
# File lib/arjdbc/sqlite3/adapter.rb, line 442 def remove_column(table_name, column_name, type = nil, options = {}) alter_table(table_name) do |definition| definition.remove_column column_name end end
@override
# File lib/arjdbc/sqlite3/adapter.rb, line 413 def remove_index!(table_name, index_name) execute "DROP INDEX #{quote_column_name(index_name)}" end
# File lib/arjdbc/sqlite3/adapter.rb, line 497 def rename_column(table_name, column_name, new_column_name) unless columns(table_name).detect{|c| c.name == column_name.to_s } raise ActiveRecord::ActiveRecordError, "Missing column #{table_name}.#{column_name}" end alter_table(table_name, :rename => {column_name.to_s => new_column_name.to_s}) rename_column_indexes(table_name, column_name, new_column_name) if respond_to?(:rename_column_indexes) # AR-4.0 SchemaStatements end
@override
# File lib/arjdbc/sqlite3/adapter.rb, line 418 def rename_table(table_name, new_name) execute "ALTER TABLE #{quote_table_name(table_name)} RENAME TO #{quote_table_name(new_name)}" rename_table_indexes(table_name, new_name) if respond_to?(:rename_table_indexes) # AR-4.0 SchemaStatements end
@override
# File lib/arjdbc/sqlite3/adapter.rb, line 291 def rollback_to_savepoint(name = current_savepoint_name(true)) log("ROLLBACK TO SAVEPOINT #{name}", 'Savepoint') { super } end
# File lib/arjdbc/sqlite3/adapter.rb, line 315 def select(sql, name = nil, binds = []) result = super # AR::Result (4.0) or Array (<= 3.2) if result.respond_to?(:columns) # 4.0 result.columns.map! do |key| # [ [ 'id', ... ] key.is_a?(String) ? key.sub(/^"?\w+"?\./, '') : key end else result.map! do |row| # [ { 'id' => ... }, {...} ] record = {} row.each_key do |key| if key.is_a?(String) record[key.sub(/^"?\w+"?\./, '')] = row[key] end end record end end result end
@override
# File lib/arjdbc/sqlite3/adapter.rb, line 166 def supports_add_column? true end
@override
# File lib/arjdbc/sqlite3/adapter.rb, line 176 def supports_autoincrement? true end
@override
# File lib/arjdbc/sqlite3/adapter.rb, line 171 def supports_count_distinct? true end
@override
# File lib/arjdbc/sqlite3/adapter.rb, line 151 def supports_ddl_transactions? true end
@override
# File lib/arjdbc/sqlite3/adapter.rb, line 206 def supports_index_sort_order? true end
@override
# File lib/arjdbc/sqlite3/adapter.rb, line 181 def supports_migrations? true end
@override
# File lib/arjdbc/sqlite3/adapter.rb, line 161 def supports_partial_index? sqlite_version >= '3.8.0' end
@override
# File lib/arjdbc/sqlite3/adapter.rb, line 186 def supports_primary_key? true end
@override
# File lib/arjdbc/sqlite3/adapter.rb, line 156 def supports_savepoints? sqlite_version >= '3.6.8' end
@override
# File lib/arjdbc/sqlite3/adapter.rb, line 211 def supports_views? true end
@override
# File lib/arjdbc/sqlite3/adapter.rb, line 269 def table_exists?(table_name) table_name && tables(nil, table_name).any? end
# File lib/arjdbc/sqlite3/adapter.rb, line 350 def table_structure(table_name) sql = "PRAGMA table_info(#{quote_table_name(table_name)})" log(sql, 'SCHEMA') { @connection.execute_query_raw(sql) } rescue ActiveRecord::JDBCError => error e = ActiveRecord::StatementInvalid.new("Could not find table '#{table_name}'") e.set_backtrace error.backtrace raise e end
@override
# File lib/arjdbc/sqlite3/adapter.rb, line 257 def tables(name = nil, table_name = nil) sql = "SELECT name FROM sqlite_master WHERE type = 'table'" if table_name sql << " AND name = #{quote_table_name(table_name)}" else sql << " AND NOT name = 'sqlite_sequence'" end select_rows(sql, name).map { |row| row[0] } end
# File lib/arjdbc/sqlite3/adapter.rb, line 273 def truncate_fake(table_name, name = nil) execute "DELETE FROM #{quote_table_name(table_name)}; VACUUM", name end
SQLite has an additional restriction on the ALTER TABLE statement. @see www.sqlite.org/lang_altertable.html
# File lib/arjdbc/sqlite3/adapter.rb, line 425 def valid_alter_table_options( type, options) type.to_sym != :primary_key end
Protected Instance Methods
# File lib/arjdbc/sqlite3/adapter.rb, line 528 def last_inserted_id(result) super || last_insert_id # NOTE: #last_insert_id call should not be needed end
# File lib/arjdbc/sqlite3/adapter.rb, line 532 def translate_exception(exception, message) if msg = exception.message # SQLite 3.8.2 returns a newly formatted error message: # UNIQUE constraint failed: *table_name*.*column_name* # Older versions of SQLite return: # column *column_name* is not unique if msg.index('UNIQUE constraint failed: ') || msg =~ /column(s)? .* (is|are) not unique/ return ::ActiveRecord::RecordNotUnique.new(message, exception) end end super end
Private Instance Methods
# File lib/arjdbc/sqlite3/adapter.rb, line 215 def sqlite_version @sqlite_version ||= Version.new(select_value('SELECT sqlite_version(*)')) end