sqlite_adapter.rb 5.2 KB
Newer Older
D
Initial  
David Heinemeier Hansson 已提交
1
# sqlite_adapter.rb
2 3
# author: Luke Holden <lholden@cablelan.net>
# updated for SQLite3: Jamis Buck <jamis_buck@byu.edu>
D
Initial  
David Heinemeier Hansson 已提交
4 5 6 7 8

require 'active_record/connection_adapters/abstract_adapter'

module ActiveRecord
  class Base
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
    class << self
      # sqlite3 adapter reuses sqlite_connection.
      def sqlite3_connection(config) # :nodoc:
        parse_config!(config)

        unless self.class.const_defined?(:SQLite3)
          require_library_or_gem(config[:adapter])
        end

        db = SQLite3::Database.new(
          config[:dbfile],
          :results_as_hash => true,
          :type_translation => false
        )
        ConnectionAdapters::SQLiteAdapter.new(db, logger)
      end

      # Establishes a connection to the database that's used by all Active Record objects
      def sqlite_connection(config) # :nodoc:
        parse_config!(config)

        unless self.class.const_defined?(:SQLite)
          require_library_or_gem(config[:adapter])

          db = SQLite::Database.new(config[:dbfile], 0)
          db.show_datatypes   = "ON" if !defined? SQLite::Version
          db.results_as_hash  = true if defined? SQLite::Version
          db.type_translation = false

          # "Downgrade" deprecated sqlite API
          if SQLite.const_defined?(:Version)
            ConnectionAdapters::SQLiteAdapter.new(db, logger)
          else
            ConnectionAdapters::DeprecatedSQLiteAdapter.new(db, logger)
          end
        end
D
Initial  
David Heinemeier Hansson 已提交
45 46
      end

47 48 49 50 51 52
      private
        def parse_config!(config)
          # Require dbfile.
          unless config.has_key?(:dbfile)
            raise ArgumentError, "No database file specified. Missing argument: dbfile"
          end
D
Initial  
David Heinemeier Hansson 已提交
53

54 55 56 57 58
          # Allow database path relative to RAILS_ROOT.
          if Object.const_defined?(:RAILS_ROOT)
            config[:dbfile] = File.expand_path(config[:dbfile], RAILS_ROOT)
          end
        end
D
Initial  
David Heinemeier Hansson 已提交
59 60 61
    end
  end

62 63
  module ConnectionAdapters #:nodoc:
    class SQLiteColumn < Column #:nodoc:
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
      def string_to_binary(value)
        value.gsub(/(\0|\%)/) do
          case $1
            when "\0" then "%00"
            when "%" then "%25"
          end
        end                
      end
      
      def binary_to_string(value)
        value.gsub(/(%00|%25)/) do
          case $1
            when "%00" then "\0"
            when "%25" then "%"
          end
        end                
      end
    end
82

83 84 85 86 87 88 89
    # 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
    # from http://rubyforge.org/projects/sqlite-ruby/).
    #
    # Options:
    #
    # * <tt>:dbfile</tt> -- Path to the database file.
    class SQLiteAdapter < AbstractAdapter
90 91 92
      def native_database_types
        {
          :primary_key => "INTEGER PRIMARY KEY NOT NULL",
93 94 95 96 97 98 99 100 101 102
          :string      => { :name => "varchar", :limit => 255 },
          :text        => { :name => "text" },
          :integer     => { :name => "integer" },
          :float       => { :name => "float" },
          :datetime    => { :name => "datetime" },
          :timestamp   => { :name => "datetime" },
          :time        => { :name => "datetime" },
          :date        => { :name => "date" },
          :binary      => { :name => "blob" },
          :boolean     => { :name => "integer" }
103 104 105
        }
      end

106
      def execute(sql, name = nil)
107
        #log(sql, name, @connection) { |connection| connection.execute(sql) }
108
        log(sql, name) { @connection.execute(sql) }
D
Initial  
David Heinemeier Hansson 已提交
109 110
      end

111 112 113
      def update(sql, name = nil)
        execute(sql, name)
        @connection.changes
D
Initial  
David Heinemeier Hansson 已提交
114 115
      end

116 117 118 119
      def delete(sql, name = nil)
        sql += " WHERE 1=1" unless sql =~ /WHERE/i
        execute(sql, name)
        @connection.changes
D
Initial  
David Heinemeier Hansson 已提交
120 121 122 123
      end

      def insert(sql, name = nil, pk = nil, id_value = nil)
        execute(sql, name = nil)
124
        id_value || @connection.last_insert_row_id
D
Initial  
David Heinemeier Hansson 已提交
125 126
      end

127 128 129 130
      def select_all(sql, name = nil)
        execute(sql, name).map do |row|
          record = {}
          row.each_key do |key|
131 132 133
            if key.is_a?(String)
              record[key.sub(/^\w+\./, '')] = row[key]
            end
D
Initial  
David Heinemeier Hansson 已提交
134
          end
135
          record
D
Initial  
David Heinemeier Hansson 已提交
136 137 138
        end
      end

139 140 141
      def select_one(sql, name = nil)
        result = select_all(sql, name)
        result.nil? ? nil : result.first
142
      end
143 144 145 146 147 148 149 150 151


      def begin_db_transaction()    @connection.transaction end
      def commit_db_transaction()   @connection.commit      end
      def rollback_db_transaction() @connection.rollback    end


      def tables
        execute('.table').map { |table| Table.new(table) }
152
      end
D
Initial  
David Heinemeier Hansson 已提交
153

154 155 156 157 158
      def columns(table_name, name = nil)
        table_structure(table_name).map { |field|
          SQLiteColumn.new(field['name'], field['dflt_value'], field['type'])
        }
      end
D
Initial  
David Heinemeier Hansson 已提交
159 160

      def quote_string(s)
161
        @connection.class.quote(s)
D
Initial  
David Heinemeier Hansson 已提交
162
      end
163

D
Initial  
David Heinemeier Hansson 已提交
164
      def quote_column_name(name)
165
        "'#{name}'"
D
Initial  
David Heinemeier Hansson 已提交
166 167
      end

168 169 170 171
      def adapter_name()
        'SQLite'
      end

172

173
      protected
D
Initial  
David Heinemeier Hansson 已提交
174
        def table_structure(table_name)
175
          execute "PRAGMA table_info(#{table_name})"
D
Initial  
David Heinemeier Hansson 已提交
176 177
        end
    end
178 179 180 181 182 183 184

    class DeprecatedSQLiteAdapter < SQLiteAdapter # :nodoc:
      def insert(sql, name = nil, pk = nil, id_value = nil)
        execute(sql, name = nil)
        id_value || @connection.last_insert_rowid
      end
    end
D
Initial  
David Heinemeier Hansson 已提交
185
  end
186
end