module ArJdbc::SQLite3

Constants

ADAPTER_NAME
NATIVE_DATABASE_TYPES

Public Class Methods

arel_visitor_type(config = nil) click to toggle source

@see ActiveRecord::ConnectionAdapters::Jdbc::ArelSupport

# File lib/arjdbc/sqlite3/adapter.rb, line 100
def self.arel_visitor_type(config = nil)
  ::Arel::Visitors::SQLite
end
column_selector() click to toggle source

@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
jdbc_connection_class() click to toggle source

@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

adapter_name() click to toggle source
# File lib/arjdbc/sqlite3/adapter.rb, line 112
def adapter_name
  ADAPTER_NAME
end
add_column(table_name, column_name, type, options = {}) click to toggle source
Calls superclass method
# 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
add_lock!(sql, options) click to toggle source

@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
allowed_index_name_length() click to toggle source

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
change_column(table_name, column_name, type, options = {}) click to toggle source
# 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
change_column_null(table_name, column_name, null, default = nil) click to toggle source
# 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
columns(table_name, name = nil) click to toggle source

@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
create_database(name = nil, options = {}) click to toggle source

@private

# File lib/arjdbc/sqlite3/adapter.rb, line 307
def create_database(name = nil, options = {})
end
create_savepoint(name = current_savepoint_name(true)) click to toggle source

@override

Calls superclass method
# File lib/arjdbc/sqlite3/adapter.rb, line 286
def create_savepoint(name = current_savepoint_name(true))
  log("SAVEPOINT #{name}", 'Savepoint') { super }
end
default_primary_key_type() click to toggle source
# 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
drop_database(name = nil) click to toggle source

@private

# File lib/arjdbc/sqlite3/adapter.rb, line 311
def drop_database(name = nil)
  tables.each { |table| drop_table(table) }
end
empty_insert_statement_value() click to toggle source
# 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
encoding() click to toggle source
# File lib/arjdbc/sqlite3/adapter.rb, line 518
def encoding
  select_value 'PRAGMA encoding'
end
exec_insert(sql, name, binds, pk = nil, sequence_name = nil) click to toggle source

@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
indexes(table_name, name = nil) click to toggle source

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
insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = []) click to toggle source

@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
jdbc_column_class() click to toggle source
# File lib/arjdbc/sqlite3/adapter.rb, line 15
def jdbc_column_class; ::ActiveRecord::ConnectionAdapters::SQLite3Column end
last_insert_id() click to toggle source
# File lib/arjdbc/sqlite3/adapter.rb, line 522
def last_insert_id
  @connection.last_insert_rowid
end
native_database_types() click to toggle source

@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
primary_key(table_name) click to toggle source

@override

# 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
quote(value, column = nil) click to toggle source

@override

Calls superclass method
# 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
quote_column_name(name) click to toggle source

@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
quote_table_name_for_assignment(table, attr) click to toggle source
# File lib/arjdbc/sqlite3/adapter.rb, line 236
def quote_table_name_for_assignment(table, attr)
  quote_column_name(attr)
end
quoted_date(value) click to toggle source

Quote date/time values for use in SQL input. Includes microseconds if the value is a Time responding to usec. @override

Calls superclass method
# 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
recreate_database(name = nil, options = {}) click to toggle source

@private

# File lib/arjdbc/sqlite3/adapter.rb, line 301
def recreate_database(name = nil, options = {})
  drop_database(name)
  create_database(name, options)
end
release_savepoint(name = current_savepoint_name(false)) click to toggle source

@override

Calls superclass method
# File lib/arjdbc/sqlite3/adapter.rb, line 296
def release_savepoint(name = current_savepoint_name(false))
  log("RELEASE SAVEPOINT #{name}", 'Savepoint') { super }
end
remove_column(table_name, column_name, type = nil, options = {}) click to toggle source

@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
Also aliased as: remove_columns
remove_columns(table_name, column_name, type = nil, options = {})
Alias for: remove_column
remove_index!(table_name, index_name) click to toggle source

@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
rename_column(table_name, column_name, new_column_name) click to toggle source
# 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
rename_table(table_name, new_name) click to toggle source

@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
rollback_to_savepoint(name = current_savepoint_name(true)) click to toggle source

@override

Calls superclass method
# 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
select(sql, name = nil, binds = []) click to toggle source
Calls superclass method
# 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
supports_add_column?() click to toggle source

@override

# File lib/arjdbc/sqlite3/adapter.rb, line 166
def supports_add_column?
  true
end
supports_autoincrement?() click to toggle source

@override

# File lib/arjdbc/sqlite3/adapter.rb, line 176
def supports_autoincrement?
  true
end
supports_count_distinct?() click to toggle source

@override

# File lib/arjdbc/sqlite3/adapter.rb, line 171
def supports_count_distinct?
  true
end
supports_ddl_transactions?() click to toggle source

@override

# File lib/arjdbc/sqlite3/adapter.rb, line 151
def supports_ddl_transactions?
  true
end
supports_index_sort_order?() click to toggle source

@override

# File lib/arjdbc/sqlite3/adapter.rb, line 206
def supports_index_sort_order?
  true
end
supports_migrations?() click to toggle source

@override

# File lib/arjdbc/sqlite3/adapter.rb, line 181
def supports_migrations?
  true
end
supports_partial_index?() click to toggle source

@override

# File lib/arjdbc/sqlite3/adapter.rb, line 161
def supports_partial_index?
  sqlite_version >= '3.8.0'
end
supports_primary_key?() click to toggle source

@override

# File lib/arjdbc/sqlite3/adapter.rb, line 186
def supports_primary_key?
  true
end
supports_savepoints?() click to toggle source

@override

# File lib/arjdbc/sqlite3/adapter.rb, line 156
def supports_savepoints?
  sqlite_version >= '3.6.8'
end
supports_views?() click to toggle source

@override

# File lib/arjdbc/sqlite3/adapter.rb, line 211
def supports_views?
  true
end
table_exists?(table_name) click to toggle source

@override

# File lib/arjdbc/sqlite3/adapter.rb, line 269
def table_exists?(table_name)
  table_name && tables(nil, table_name).any?
end
table_structure(table_name) click to toggle source
# 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
tables(name = nil, table_name = nil) click to toggle source

@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
truncate_fake(table_name, name = nil) click to toggle source
# 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
valid_alter_table_options( type, options) click to toggle source

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

last_inserted_id(result) click to toggle source
Calls superclass method
# 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
translate_exception(exception, message) click to toggle source
Calls superclass method
# 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

sqlite_version() click to toggle source
# File lib/arjdbc/sqlite3/adapter.rb, line 215
def sqlite_version
  @sqlite_version ||= Version.new(select_value('SELECT sqlite_version(*)'))
end