aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
blob: 4dc8d634b312d7dea02680b5bf55bcb881448665 (plain) (blame)
1
2
3
4
5
6
7
8
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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
# sqlite_adapter.rb
# author:   Luke Holden <lholden@cablelan.net>

require 'active_record/connection_adapters/abstract_adapter'

module ActiveRecord
  class Base
    # Establishes a connection to the database that's used by all Active Record objects
    def self.sqlite_connection(config) # :nodoc:
      require_library_or_gem('sqlite') unless self.class.const_defined?(:SQLite)
      symbolize_strings_in_hash(config)
      unless config.has_key?(:dbfile)
        raise ArgumentError, "No database file specified. Missing argument: dbfile"
      end
      
      config[:dbfile] = File.expand_path(config[:dbfile], RAILS_ROOT) if Object.const_defined?(:RAILS_ROOT)
      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

      ConnectionAdapters::SQLiteAdapter.new(db, logger)
    end
  end

  module ConnectionAdapters
    
    class SQLiteColumn < Column
      
      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
    class SQLiteAdapter < AbstractAdapter # :nodoc:
      def select_all(sql, name = nil)
        select(sql, name)
      end

      def select_one(sql, name = nil)
        result = select(sql, name)
        result.nil? ? nil : result.first
      end

      def columns(table_name, name = nil)
        table_structure(table_name).inject([]) do |columns, field| 
          columns << SQLiteColumn.new(field['name'], field['dflt_value'], field['type'])
          columns
        end
      end

      def insert(sql, name = nil, pk = nil, id_value = nil)
        execute(sql, name = nil)
        id_value || @connection.send( defined?( SQLite::Version ) ? :last_insert_row_id : :last_insert_rowid )
      end

      def execute(sql, name = nil)
        log(sql, name, @connection) do |connection|
          if defined?( SQLite::Version )
            case sql
              when "BEGIN" then connection.transaction
              when "COMMIT" then connection.commit
              when "ROLLBACK" then connection.rollback
              else connection.execute(sql)
            end
          else
            connection.execute( sql )
          end
        end
      end

      def update(sql, name = nil)
        execute(sql, name)
        @connection.changes
      end
      
      def delete(sql, name = nil)
        sql += " WHERE 1=1" unless sql =~ /WHERE/i
        execute(sql, name)
        @connection.changes
      end

      def begin_db_transaction()    execute "BEGIN" end
      def commit_db_transaction()   execute "COMMIT" end
      def rollback_db_transaction() execute "ROLLBACK" end

      def quote_string(s)
        SQLite::Database.quote(s)
      end
        
      def quote_column_name(name)
        return "'#{name}'"
      end

      private
        def select(sql, name = nil)
          results = nil
          log(sql, name, @connection) { |connection| results = connection.execute(sql) }

          rows = []

          results.each do |row|
            hash_only_row = {}
            row.each_key do |key|
              hash_only_row[key.sub(/\w+\./, "")] = row[key] unless key.class == Fixnum
            end
            rows << hash_only_row
          end

          return rows
        end

        def table_structure(table_name)
          sql = "PRAGMA table_info(#{table_name});"
          results = nil
          log(sql, nil, @connection) { |connection| results = connection.execute(sql) }
          return results
        end
    end
  end
end