Skip to content
This repository
Browse code

merged sqlite and sqlite3 adapters

  • Loading branch information...
commit 7572efc827c1e53beb621008eda819fa92feb653 1 parent 602000b
Andrey Deryabin authored April 27, 2012
545  activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
... ...
@@ -1,4 +1,6 @@
1  
-require 'active_record/connection_adapters/sqlite_adapter'
  1
+require 'active_record/connection_adapters/abstract_adapter'
  2
+require 'active_record/connection_adapters/statement_pool'
  3
+require 'arel/visitors/bind_visitor'
2 4
 
3 5
 gem 'sqlite3', '~> 1.3.5'
4 6
 require 'sqlite3'
@@ -35,16 +37,171 @@ def sqlite3_connection(config) # :nodoc:
35 37
   end
36 38
 
37 39
   module ConnectionAdapters #:nodoc:
38  
-    class SQLite3Adapter < SQLiteAdapter # :nodoc:
39  
-      def quote(value, column = nil)
40  
-        if value.kind_of?(String) && column && column.type == :binary && column.class.respond_to?(:string_to_binary)
41  
-          s = column.class.string_to_binary(value).unpack("H*")[0]
42  
-          "x'#{s}'"
43  
-        else
  40
+    class SQLiteColumn < Column #:nodoc:
  41
+      class <<  self
  42
+        def binary_to_string(value)
  43
+          if value.encoding != Encoding::ASCII_8BIT
  44
+            value = value.force_encoding(Encoding::ASCII_8BIT)
  45
+          end
  46
+          value
  47
+        end
  48
+      end
  49
+    end
  50
+
  51
+    # The SQLite adapter works with both the 2.x and 3.x series of SQLite with the sqlite-ruby
  52
+    # drivers (available both as gems and from http://rubyforge.org/projects/sqlite-ruby/).
  53
+    #
  54
+    # Options:
  55
+    #
  56
+    # * <tt>:database</tt> - Path to the database file.
  57
+    class SQLite3Adapter < AbstractAdapter
  58
+      class Version
  59
+        include Comparable
  60
+
  61
+        def initialize(version_string)
  62
+          @version = version_string.split('.').map { |v| v.to_i }
  63
+        end
  64
+
  65
+        def <=>(version_string)
  66
+          @version <=> version_string.split('.').map { |v| v.to_i }
  67
+        end
  68
+      end
  69
+
  70
+      class StatementPool < ConnectionAdapters::StatementPool
  71
+        def initialize(connection, max)
44 72
           super
  73
+          @cache = Hash.new { |h,pid| h[pid] = {} }
  74
+        end
  75
+
  76
+        def each(&block); cache.each(&block); end
  77
+        def key?(key);    cache.key?(key); end
  78
+        def [](key);      cache[key]; end
  79
+        def length;       cache.length; end
  80
+
  81
+        def []=(sql, key)
  82
+          while @max <= cache.size
  83
+            dealloc(cache.shift.last[:stmt])
  84
+          end
  85
+          cache[sql] = key
  86
+        end
  87
+
  88
+        def clear
  89
+          cache.values.each do |hash|
  90
+            dealloc hash[:stmt]
  91
+          end
  92
+          cache.clear
  93
+        end
  94
+
  95
+        private
  96
+        def cache
  97
+          @cache[$$]
  98
+        end
  99
+
  100
+        def dealloc(stmt)
  101
+          stmt.close unless stmt.closed?
45 102
         end
46 103
       end
47 104
 
  105
+      class BindSubstitution < Arel::Visitors::SQLite # :nodoc:
  106
+        include Arel::Visitors::BindVisitor
  107
+      end
  108
+
  109
+      def initialize(connection, logger, config)
  110
+        super(connection, logger)
  111
+        @statements = StatementPool.new(@connection,
  112
+                                        config.fetch(:statement_limit) { 1000 })
  113
+        @config = config
  114
+
  115
+        if config.fetch(:prepared_statements) { true }
  116
+          @visitor = Arel::Visitors::SQLite.new self
  117
+        else
  118
+          @visitor = BindSubstitution.new self
  119
+        end
  120
+      end
  121
+
  122
+      def adapter_name #:nodoc:
  123
+        'SQLite'
  124
+      end
  125
+
  126
+      # Returns true if SQLite version is '2.0.0' or greater, false otherwise.
  127
+      def supports_ddl_transactions?
  128
+        sqlite_version >= '2.0.0'
  129
+      end
  130
+
  131
+      # Returns true if SQLite version is '3.6.8' or greater, false otherwise.
  132
+      def supports_savepoints?
  133
+        sqlite_version >= '3.6.8'
  134
+      end
  135
+
  136
+      # Returns true, since this connection adapter supports prepared statement
  137
+      # caching.
  138
+      def supports_statement_cache?
  139
+        true
  140
+      end
  141
+
  142
+      # Returns true, since this connection adapter supports migrations.
  143
+      def supports_migrations? #:nodoc:
  144
+        true
  145
+      end
  146
+
  147
+      # Returns true.
  148
+      def supports_primary_key? #:nodoc:
  149
+        true
  150
+      end
  151
+
  152
+      def requires_reloading?
  153
+        true
  154
+      end
  155
+
  156
+      # Returns true if SQLite version is '3.1.6' or greater, false otherwise.
  157
+      def supports_add_column?
  158
+        sqlite_version >= '3.1.6'
  159
+      end
  160
+
  161
+      # Disconnects from the database if already connected. Otherwise, this
  162
+      # method does nothing.
  163
+      def disconnect!
  164
+        super
  165
+        clear_cache!
  166
+        @connection.close rescue nil
  167
+      end
  168
+
  169
+      # Clears the prepared statements cache.
  170
+      def clear_cache!
  171
+        @statements.clear
  172
+      end
  173
+
  174
+      # Returns true if SQLite version is '3.2.6' or greater, false otherwise.
  175
+      def supports_count_distinct? #:nodoc:
  176
+        sqlite_version >= '3.2.6'
  177
+      end
  178
+
  179
+      # Returns true if SQLite version is '3.1.0' or greater, false otherwise.
  180
+      def supports_autoincrement? #:nodoc:
  181
+        sqlite_version >= '3.1.0'
  182
+      end
  183
+
  184
+      def supports_index_sort_order?
  185
+        sqlite_version >= '3.3.0'
  186
+      end
  187
+
  188
+      def native_database_types #:nodoc:
  189
+        {
  190
+          :primary_key => default_primary_key_type,
  191
+          :string      => { :name => "varchar", :limit => 255 },
  192
+          :text        => { :name => "text" },
  193
+          :integer     => { :name => "integer" },
  194
+          :float       => { :name => "float" },
  195
+          :decimal     => { :name => "decimal" },
  196
+          :datetime    => { :name => "datetime" },
  197
+          :timestamp   => { :name => "datetime" },
  198
+          :time        => { :name => "time" },
  199
+          :date        => { :name => "date" },
  200
+          :binary      => { :name => "blob" },
  201
+          :boolean     => { :name => "boolean" }
  202
+        }
  203
+      end
  204
+
48 205
       # Returns the current database encoding format as a string, eg: 'UTF-8'
49 206
       def encoding
50 207
         @connection.encoding.to_s
@@ -55,6 +212,50 @@ def supports_explain?
55 212
         true
56 213
       end
57 214
 
  215
+
  216
+      # QUOTING ==================================================
  217
+
  218
+      def quote(value, column = nil)
  219
+        if value.kind_of?(String) && column && column.type == :binary && column.class.respond_to?(:string_to_binary)
  220
+          s = column.class.string_to_binary(value).unpack("H*")[0]
  221
+          "x'#{s}'"
  222
+        else
  223
+          super
  224
+        end
  225
+      end
  226
+
  227
+
  228
+      def quote_string(s) #:nodoc:
  229
+        @connection.class.quote(s)
  230
+      end
  231
+
  232
+      def quote_column_name(name) #:nodoc:
  233
+        %Q("#{name.to_s.gsub('"', '""')}")
  234
+      end
  235
+
  236
+      # Quote date/time values for use in SQL input. Includes microseconds
  237
+      # if the value is a Time responding to usec.
  238
+      def quoted_date(value) #:nodoc:
  239
+        if value.respond_to?(:usec)
  240
+          "#{super}.#{sprintf("%06d", value.usec)}"
  241
+        else
  242
+          super
  243
+        end
  244
+      end
  245
+
  246
+      def type_cast(value, column) # :nodoc:
  247
+        return value.to_f if BigDecimal === value
  248
+        return super unless String === value
  249
+        return super unless column && value
  250
+
  251
+        value = super
  252
+        if column.type == :string && value.encoding == Encoding::ASCII_8BIT
  253
+          logger.error "Binary data inserted for `string` type on column `#{column.name}`" if logger
  254
+          value.encode! 'utf-8'
  255
+        end
  256
+        value
  257
+      end
  258
+
58 259
       # DATABASE STATEMENTS ======================================
59 260
 
60 261
       def explain(arel, binds = [])
@@ -75,6 +276,336 @@ def pp(result) # :nodoc:
75 276
           end.join("\n") + "\n"
76 277
         end
77 278
       end
  279
+
  280
+      def exec_query(sql, name = nil, binds = [])
  281
+        log(sql, name, binds) do
  282
+
  283
+          # Don't cache statements without bind values
  284
+          if binds.empty?
  285
+            stmt    = @connection.prepare(sql)
  286
+            cols    = stmt.columns
  287
+            records = stmt.to_a
  288
+            stmt.close
  289
+            stmt = records
  290
+          else
  291
+            cache = @statements[sql] ||= {
  292
+              :stmt => @connection.prepare(sql)
  293
+            }
  294
+            stmt = cache[:stmt]
  295
+            cols = cache[:cols] ||= stmt.columns
  296
+            stmt.reset!
  297
+            stmt.bind_params binds.map { |col, val|
  298
+              type_cast(val, col)
  299
+            }
  300
+          end
  301
+
  302
+          ActiveRecord::Result.new(cols, stmt.to_a)
  303
+        end
  304
+      end
  305
+
  306
+      def exec_delete(sql, name = 'SQL', binds = [])
  307
+        exec_query(sql, name, binds)
  308
+        @connection.changes
  309
+      end
  310
+      alias :exec_update :exec_delete
  311
+
  312
+      def last_inserted_id(result)
  313
+        @connection.last_insert_row_id
  314
+      end
  315
+
  316
+      def execute(sql, name = nil) #:nodoc:
  317
+        log(sql, name) { @connection.execute(sql) }
  318
+      end
  319
+
  320
+      def update_sql(sql, name = nil) #:nodoc:
  321
+        super
  322
+        @connection.changes
  323
+      end
  324
+
  325
+      def delete_sql(sql, name = nil) #:nodoc:
  326
+        sql += " WHERE 1=1" unless sql =~ /WHERE/i
  327
+        super sql, name
  328
+      end
  329
+
  330
+      def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
  331
+        super
  332
+        id_value || @connection.last_insert_row_id
  333
+      end
  334
+      alias :create :insert_sql
  335
+
  336
+      def select_rows(sql, name = nil)
  337
+        exec_query(sql, name).rows
  338
+      end
  339
+
  340
+      def create_savepoint
  341
+        execute("SAVEPOINT #{current_savepoint_name}")
  342
+      end
  343
+
  344
+      def rollback_to_savepoint
  345
+        execute("ROLLBACK TO SAVEPOINT #{current_savepoint_name}")
  346
+      end
  347
+
  348
+      def release_savepoint
  349
+        execute("RELEASE SAVEPOINT #{current_savepoint_name}")
  350
+      end
  351
+
  352
+      def begin_db_transaction #:nodoc:
  353
+        log('begin transaction',nil) { @connection.transaction }
  354
+      end
  355
+
  356
+      def commit_db_transaction #:nodoc:
  357
+        log('commit transaction',nil) { @connection.commit }
  358
+      end
  359
+
  360
+      def rollback_db_transaction #:nodoc:
  361
+        log('rollback transaction',nil) { @connection.rollback }
  362
+      end
  363
+
  364
+      # SCHEMA STATEMENTS ========================================
  365
+
  366
+      def tables(name = 'SCHEMA', table_name = nil) #:nodoc:
  367
+        sql = <<-SQL
  368
+          SELECT name
  369
+          FROM sqlite_master
  370
+          WHERE type = 'table' AND NOT name = 'sqlite_sequence'
  371
+        SQL
  372
+        sql << " AND name = #{quote_table_name(table_name)}" if table_name
  373
+
  374
+        exec_query(sql, name).map do |row|
  375
+          row['name']
  376
+        end
  377
+      end
  378
+
  379
+      def table_exists?(name)
  380
+        name && tables('SCHEMA', name).any?
  381
+      end
  382
+
  383
+      # Returns an array of +SQLiteColumn+ objects for the table specified by +table_name+.
  384
+      def columns(table_name) #:nodoc:
  385
+        table_structure(table_name).map do |field|
  386
+          case field["dflt_value"]
  387
+          when /^null$/i
  388
+            field["dflt_value"] = nil
  389
+          when /^'(.*)'$/
  390
+            field["dflt_value"] = $1.gsub("''", "'")
  391
+          when /^"(.*)"$/
  392
+            field["dflt_value"] = $1.gsub('""', '"')
  393
+          end
  394
+
  395
+          SQLiteColumn.new(field['name'], field['dflt_value'], field['type'], field['notnull'].to_i == 0)
  396
+        end
  397
+      end
  398
+
  399
+      # Returns an array of indexes for the given table.
  400
+      def indexes(table_name, name = nil) #:nodoc:
  401
+        exec_query("PRAGMA index_list(#{quote_table_name(table_name)})", name).map do |row|
  402
+          IndexDefinition.new(
  403
+            table_name,
  404
+            row['name'],
  405
+            row['unique'] != 0,
  406
+            exec_query("PRAGMA index_info('#{row['name']}')").map { |col|
  407
+              col['name']
  408
+            })
  409
+        end
  410
+      end
  411
+
  412
+      def primary_key(table_name) #:nodoc:
  413
+        column = table_structure(table_name).find { |field|
  414
+          field['pk'] == 1
  415
+        }
  416
+        column && column['name']
  417
+      end
  418
+
  419
+      def remove_index!(table_name, index_name) #:nodoc:
  420
+        exec_query "DROP INDEX #{quote_column_name(index_name)}"
  421
+      end
  422
+
  423
+      # Renames a table.
  424
+      #
  425
+      # Example:
  426
+      #   rename_table('octopuses', 'octopi')
  427
+      def rename_table(name, new_name)
  428
+        exec_query "ALTER TABLE #{quote_table_name(name)} RENAME TO #{quote_table_name(new_name)}"
  429
+      end
  430
+
  431
+      # See: http://www.sqlite.org/lang_altertable.html
  432
+      # SQLite has an additional restriction on the ALTER TABLE statement
  433
+      def valid_alter_table_options( type, options)
  434
+        type.to_sym != :primary_key
  435
+      end
  436
+
  437
+      def add_column(table_name, column_name, type, options = {}) #:nodoc:
  438
+        if supports_add_column? && valid_alter_table_options( type, options )
  439
+          super(table_name, column_name, type, options)
  440
+        else
  441
+          alter_table(table_name) do |definition|
  442
+            definition.column(column_name, type, options)
  443
+          end
  444
+        end
  445
+      end
  446
+
  447
+      def remove_column(table_name, *column_names) #:nodoc:
  448
+        raise ArgumentError.new("You must specify at least one column name. Example: remove_column(:people, :first_name)") if column_names.empty?
  449
+        column_names.flatten.each do |column_name|
  450
+          alter_table(table_name) do |definition|
  451
+            definition.columns.delete(definition[column_name])
  452
+          end
  453
+        end
  454
+      end
  455
+      alias :remove_columns :remove_column
  456
+
  457
+      def change_column_default(table_name, column_name, default) #:nodoc:
  458
+        alter_table(table_name) do |definition|
  459
+          definition[column_name].default = default
  460
+        end
  461
+      end
  462
+
  463
+      def change_column_null(table_name, column_name, null, default = nil)
  464
+        unless null || default.nil?
  465
+          exec_query("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
  466
+        end
  467
+        alter_table(table_name) do |definition|
  468
+          definition[column_name].null = null
  469
+        end
  470
+      end
  471
+
  472
+      def change_column(table_name, column_name, type, options = {}) #:nodoc:
  473
+        alter_table(table_name) do |definition|
  474
+          include_default = options_include_default?(options)
  475
+          definition[column_name].instance_eval do
  476
+            self.type    = type
  477
+            self.limit   = options[:limit] if options.include?(:limit)
  478
+            self.default = options[:default] if include_default
  479
+            self.null    = options[:null] if options.include?(:null)
  480
+            self.precision = options[:precision] if options.include?(:precision)
  481
+            self.scale   = options[:scale] if options.include?(:scale)
  482
+          end
  483
+        end
  484
+      end
  485
+
  486
+      def rename_column(table_name, column_name, new_column_name) #:nodoc:
  487
+        unless columns(table_name).detect{|c| c.name == column_name.to_s }
  488
+          raise ActiveRecord::ActiveRecordError, "Missing column #{table_name}.#{column_name}"
  489
+        end
  490
+        alter_table(table_name, :rename => {column_name.to_s => new_column_name.to_s})
  491
+      end
  492
+
  493
+      def empty_insert_statement_value
  494
+        "VALUES(NULL)"
  495
+      end
  496
+
  497
+      protected
  498
+        def select(sql, name = nil, binds = []) #:nodoc:
  499
+          exec_query(sql, name, binds)
  500
+        end
  501
+
  502
+        def table_structure(table_name)
  503
+          structure = exec_query("PRAGMA table_info(#{quote_table_name(table_name)})", 'SCHEMA').to_hash
  504
+          raise(ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'") if structure.empty?
  505
+          structure
  506
+        end
  507
+
  508
+        def alter_table(table_name, options = {}) #:nodoc:
  509
+          altered_table_name = "altered_#{table_name}"
  510
+          caller = lambda {|definition| yield definition if block_given?}
  511
+
  512
+          transaction do
  513
+            move_table(table_name, altered_table_name,
  514
+              options.merge(:temporary => true))
  515
+            move_table(altered_table_name, table_name, &caller)
  516
+          end
  517
+        end
  518
+
  519
+        def move_table(from, to, options = {}, &block) #:nodoc:
  520
+          copy_table(from, to, options, &block)
  521
+          drop_table(from)
  522
+        end
  523
+
  524
+        def copy_table(from, to, options = {}) #:nodoc:
  525
+          options = options.merge(:id => (!columns(from).detect{|c| c.name == 'id'}.nil? && 'id' == primary_key(from).to_s))
  526
+          create_table(to, options) do |definition|
  527
+            @definition = definition
  528
+            columns(from).each do |column|
  529
+              column_name = options[:rename] ?
  530
+                (options[:rename][column.name] ||
  531
+                 options[:rename][column.name.to_sym] ||
  532
+                 column.name) : column.name
  533
+
  534
+              @definition.column(column_name, column.type,
  535
+                :limit => column.limit, :default => column.default,
  536
+                :precision => column.precision, :scale => column.scale,
  537
+                :null => column.null)
  538
+            end
  539
+            @definition.primary_key(primary_key(from)) if primary_key(from)
  540
+            yield @definition if block_given?
  541
+          end
  542
+
  543
+          copy_table_indexes(from, to, options[:rename] || {})
  544
+          copy_table_contents(from, to,
  545
+            @definition.columns.map {|column| column.name},
  546
+            options[:rename] || {})
  547
+        end
  548
+
  549
+        def copy_table_indexes(from, to, rename = {}) #:nodoc:
  550
+          indexes(from).each do |index|
  551
+            name = index.name
  552
+            if to == "altered_#{from}"
  553
+              name = "temp_#{name}"
  554
+            elsif from == "altered_#{to}"
  555
+              name = name[5..-1]
  556
+            end
  557
+
  558
+            to_column_names = columns(to).map { |c| c.name }
  559
+            columns = index.columns.map {|c| rename[c] || c }.select do |column|
  560
+              to_column_names.include?(column)
  561
+            end
  562
+
  563
+            unless columns.empty?
  564
+              # index name can't be the same
  565
+              opts = { :name => name.gsub(/_(#{from})_/, "_#{to}_") }
  566
+              opts[:unique] = true if index.unique
  567
+              add_index(to, columns, opts)
  568
+            end
  569
+          end
  570
+        end
  571
+
  572
+        def copy_table_contents(from, to, columns, rename = {}) #:nodoc:
  573
+          column_mappings = Hash[columns.map {|name| [name, name]}]
  574
+          rename.each { |a| column_mappings[a.last] = a.first }
  575
+          from_columns = columns(from).collect {|col| col.name}
  576
+          columns = columns.find_all{|col| from_columns.include?(column_mappings[col])}
  577
+          quoted_columns = columns.map { |col| quote_column_name(col) } * ','
  578
+
  579
+          quoted_to = quote_table_name(to)
  580
+          exec_query("SELECT * FROM #{quote_table_name(from)}").each do |row|
  581
+            sql = "INSERT INTO #{quoted_to} (#{quoted_columns}) VALUES ("
  582
+            sql << columns.map {|col| quote row[column_mappings[col]]} * ', '
  583
+            sql << ')'
  584
+            exec_query sql
  585
+          end
  586
+        end
  587
+
  588
+        def sqlite_version
  589
+          @sqlite_version ||= SQLite3Adapter::Version.new(select_value('select sqlite_version(*)'))
  590
+        end
  591
+
  592
+        def default_primary_key_type
  593
+          if supports_autoincrement?
  594
+            'INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL'
  595
+          else
  596
+            'INTEGER PRIMARY KEY NOT NULL'
  597
+          end
  598
+        end
  599
+
  600
+        def translate_exception(exception, message)
  601
+          case exception.message
  602
+          when /column(s)? .* (is|are) not unique/
  603
+            RecordNotUnique.new(message, exception)
  604
+          else
  605
+            super
  606
+          end
  607
+        end
  608
+
78 609
     end
79 610
   end
80 611
 end
539  activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
... ...
@@ -1,539 +0,0 @@
1  
-require 'active_record/connection_adapters/abstract_adapter'
2  
-require 'active_record/connection_adapters/statement_pool'
3  
-require 'arel/visitors/bind_visitor'
4  
-
5  
-module ActiveRecord
6  
-  module ConnectionAdapters #:nodoc:
7  
-    class SQLiteColumn < Column #:nodoc:
8  
-      class <<  self
9  
-        def binary_to_string(value)
10  
-          if value.encoding != Encoding::ASCII_8BIT
11  
-            value = value.force_encoding(Encoding::ASCII_8BIT)
12  
-          end
13  
-          value
14  
-        end
15  
-      end
16  
-    end
17  
-
18  
-    # The SQLite adapter works with both the 2.x and 3.x series of SQLite with the sqlite-ruby
19  
-    # drivers (available both as gems and from http://rubyforge.org/projects/sqlite-ruby/).
20  
-    #
21  
-    # Options:
22  
-    #
23  
-    # * <tt>:database</tt> - Path to the database file.
24  
-    class SQLiteAdapter < AbstractAdapter
25  
-      class Version
26  
-        include Comparable
27  
-
28  
-        def initialize(version_string)
29  
-          @version = version_string.split('.').map { |v| v.to_i }
30  
-        end
31  
-
32  
-        def <=>(version_string)
33  
-          @version <=> version_string.split('.').map { |v| v.to_i }
34  
-        end
35  
-      end
36  
-
37  
-      class StatementPool < ConnectionAdapters::StatementPool
38  
-        def initialize(connection, max)
39  
-          super
40  
-          @cache = Hash.new { |h,pid| h[pid] = {} }
41  
-        end
42  
-
43  
-        def each(&block); cache.each(&block); end
44  
-        def key?(key);    cache.key?(key); end
45  
-        def [](key);      cache[key]; end
46  
-        def length;       cache.length; end
47  
-
48  
-        def []=(sql, key)
49  
-          while @max <= cache.size
50  
-            dealloc(cache.shift.last[:stmt])
51  
-          end
52  
-          cache[sql] = key
53  
-        end
54  
-
55  
-        def clear
56  
-          cache.values.each do |hash|
57  
-            dealloc hash[:stmt]
58  
-          end
59  
-          cache.clear
60  
-        end
61  
-
62  
-        private
63  
-        def cache
64  
-          @cache[$$]
65  
-        end
66  
-
67  
-        def dealloc(stmt)
68  
-          stmt.close unless stmt.closed?
69  
-        end
70  
-      end
71  
-
72  
-      class BindSubstitution < Arel::Visitors::SQLite # :nodoc:
73  
-        include Arel::Visitors::BindVisitor
74  
-      end
75  
-
76  
-      def initialize(connection, logger, config)
77  
-        super(connection, logger)
78  
-        @statements = StatementPool.new(@connection,
79  
-                                        config.fetch(:statement_limit) { 1000 })
80  
-        @config = config
81  
-
82  
-        if config.fetch(:prepared_statements) { true }
83  
-          @visitor = Arel::Visitors::SQLite.new self
84  
-        else
85  
-          @visitor = BindSubstitution.new self
86  
-        end
87  
-      end
88  
-
89  
-      def adapter_name #:nodoc:
90  
-        'SQLite'
91  
-      end
92  
-
93  
-      # Returns true if SQLite version is '2.0.0' or greater, false otherwise.
94  
-      def supports_ddl_transactions?
95  
-        sqlite_version >= '2.0.0'
96  
-      end
97  
-
98  
-      # Returns true if SQLite version is '3.6.8' or greater, false otherwise.
99  
-      def supports_savepoints?
100  
-        sqlite_version >= '3.6.8'
101  
-      end
102  
-
103  
-      # Returns true, since this connection adapter supports prepared statement
104  
-      # caching.
105  
-      def supports_statement_cache?
106  
-        true
107  
-      end
108  
-
109  
-      # Returns true, since this connection adapter supports migrations.
110  
-      def supports_migrations? #:nodoc:
111  
-        true
112  
-      end
113  
-
114  
-      # Returns true.
115  
-      def supports_primary_key? #:nodoc:
116  
-        true
117  
-      end
118  
-
119  
-      def requires_reloading?
120  
-        true
121  
-      end
122  
-
123  
-      # Returns true if SQLite version is '3.1.6' or greater, false otherwise.
124  
-      def supports_add_column?
125  
-        sqlite_version >= '3.1.6'
126  
-      end
127  
-
128  
-      # Disconnects from the database if already connected. Otherwise, this
129  
-      # method does nothing.
130  
-      def disconnect!
131  
-        super
132  
-        clear_cache!
133  
-        @connection.close rescue nil
134  
-      end
135  
-
136  
-      # Clears the prepared statements cache.
137  
-      def clear_cache!
138  
-        @statements.clear
139  
-      end
140  
-
141  
-      # Returns true if SQLite version is '3.2.6' or greater, false otherwise.
142  
-      def supports_count_distinct? #:nodoc:
143  
-        sqlite_version >= '3.2.6'
144  
-      end
145  
-
146  
-      # Returns true if SQLite version is '3.1.0' or greater, false otherwise.
147  
-      def supports_autoincrement? #:nodoc:
148  
-        sqlite_version >= '3.1.0'
149  
-      end
150  
-
151  
-      def supports_index_sort_order?
152  
-        sqlite_version >= '3.3.0'
153  
-      end
154  
-
155  
-      def native_database_types #:nodoc:
156  
-        {
157  
-          :primary_key => default_primary_key_type,
158  
-          :string      => { :name => "varchar", :limit => 255 },
159  
-          :text        => { :name => "text" },
160  
-          :integer     => { :name => "integer" },
161  
-          :float       => { :name => "float" },
162  
-          :decimal     => { :name => "decimal" },
163  
-          :datetime    => { :name => "datetime" },
164  
-          :timestamp   => { :name => "datetime" },
165  
-          :time        => { :name => "time" },
166  
-          :date        => { :name => "date" },
167  
-          :binary      => { :name => "blob" },
168  
-          :boolean     => { :name => "boolean" }
169  
-        }
170  
-      end
171  
-
172  
-
173  
-      # QUOTING ==================================================
174  
-
175  
-      def quote_string(s) #:nodoc:
176  
-        @connection.class.quote(s)
177  
-      end
178  
-
179  
-      def quote_column_name(name) #:nodoc:
180  
-        %Q("#{name.to_s.gsub('"', '""')}")
181  
-      end
182  
-
183  
-      # Quote date/time values for use in SQL input. Includes microseconds
184  
-      # if the value is a Time responding to usec.
185  
-      def quoted_date(value) #:nodoc:
186  
-        if value.respond_to?(:usec)
187  
-          "#{super}.#{sprintf("%06d", value.usec)}"
188  
-        else
189  
-          super
190  
-        end
191  
-      end
192  
-
193  
-      def type_cast(value, column) # :nodoc:
194  
-        return value.to_f if BigDecimal === value
195  
-        return super unless String === value
196  
-        return super unless column && value
197  
-
198  
-        value = super
199  
-        if column.type == :string && value.encoding == Encoding::ASCII_8BIT
200  
-          logger.error "Binary data inserted for `string` type on column `#{column.name}`" if logger
201  
-          value.encode! 'utf-8'
202  
-        end
203  
-        value
204  
-      end
205  
-
206  
-      # DATABASE STATEMENTS ======================================
207  
-
208  
-      def exec_query(sql, name = nil, binds = [])
209  
-        log(sql, name, binds) do
210  
-
211  
-          # Don't cache statements without bind values
212  
-          if binds.empty?
213  
-            stmt    = @connection.prepare(sql)
214  
-            cols    = stmt.columns
215  
-            records = stmt.to_a
216  
-            stmt.close
217  
-            stmt = records
218  
-          else
219  
-            cache = @statements[sql] ||= {
220  
-              :stmt => @connection.prepare(sql)
221  
-            }
222  
-            stmt = cache[:stmt]
223  
-            cols = cache[:cols] ||= stmt.columns
224  
-            stmt.reset!
225  
-            stmt.bind_params binds.map { |col, val|
226  
-              type_cast(val, col)
227  
-            }
228  
-          end
229  
-
230  
-          ActiveRecord::Result.new(cols, stmt.to_a)
231  
-        end
232  
-      end
233  
-
234  
-      def exec_delete(sql, name = 'SQL', binds = [])
235  
-        exec_query(sql, name, binds)
236  
-        @connection.changes
237  
-      end
238  
-      alias :exec_update :exec_delete
239  
-
240  
-      def last_inserted_id(result)
241  
-        @connection.last_insert_row_id
242  
-      end
243  
-
244  
-      def execute(sql, name = nil) #:nodoc:
245  
-        log(sql, name) { @connection.execute(sql) }
246  
-      end
247  
-
248  
-      def update_sql(sql, name = nil) #:nodoc:
249  
-        super
250  
-        @connection.changes
251  
-      end
252  
-
253  
-      def delete_sql(sql, name = nil) #:nodoc:
254  
-        sql += " WHERE 1=1" unless sql =~ /WHERE/i
255  
-        super sql, name
256  
-      end
257  
-
258  
-      def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
259  
-        super
260  
-        id_value || @connection.last_insert_row_id
261  
-      end
262  
-      alias :create :insert_sql
263  
-
264  
-      def select_rows(sql, name = nil)
265  
-        exec_query(sql, name).rows
266  
-      end
267  
-
268  
-      def create_savepoint
269  
-        execute("SAVEPOINT #{current_savepoint_name}")
270  
-      end
271  
-
272  
-      def rollback_to_savepoint
273  
-        execute("ROLLBACK TO SAVEPOINT #{current_savepoint_name}")
274  
-      end
275  
-
276  
-      def release_savepoint
277  
-        execute("RELEASE SAVEPOINT #{current_savepoint_name}")
278  
-      end
279  
-
280  
-      def begin_db_transaction #:nodoc:
281  
-        log('begin transaction',nil) { @connection.transaction }
282  
-      end
283  
-
284  
-      def commit_db_transaction #:nodoc:
285  
-        log('commit transaction',nil) { @connection.commit }
286  
-      end
287  
-
288  
-      def rollback_db_transaction #:nodoc:
289  
-        log('rollback transaction',nil) { @connection.rollback }
290  
-      end
291  
-
292  
-      # SCHEMA STATEMENTS ========================================
293  
-
294  
-      def tables(name = 'SCHEMA', table_name = nil) #:nodoc:
295  
-        sql = <<-SQL
296  
-          SELECT name
297  
-          FROM sqlite_master
298  
-          WHERE type = 'table' AND NOT name = 'sqlite_sequence'
299  
-        SQL
300  
-        sql << " AND name = #{quote_table_name(table_name)}" if table_name
301  
-
302  
-        exec_query(sql, name).map do |row|
303  
-          row['name']
304  
-        end
305  
-      end
306  
-
307  
-      def table_exists?(name)
308  
-        name && tables('SCHEMA', name).any?
309  
-      end
310  
-
311  
-      # Returns an array of +SQLiteColumn+ objects for the table specified by +table_name+.
312  
-      def columns(table_name) #:nodoc:
313  
-        table_structure(table_name).map do |field|
314  
-          case field["dflt_value"]
315  
-          when /^null$/i
316  
-            field["dflt_value"] = nil
317  
-          when /^'(.*)'$/
318  
-            field["dflt_value"] = $1.gsub("''", "'")
319  
-          when /^"(.*)"$/
320  
-            field["dflt_value"] = $1.gsub('""', '"')
321  
-          end
322  
-
323  
-          SQLiteColumn.new(field['name'], field['dflt_value'], field['type'], field['notnull'].to_i == 0)
324  
-        end
325  
-      end
326  
-
327  
-      # Returns an array of indexes for the given table.
328  
-      def indexes(table_name, name = nil) #:nodoc:
329  
-        exec_query("PRAGMA index_list(#{quote_table_name(table_name)})", name).map do |row|
330  
-          IndexDefinition.new(
331  
-            table_name,
332  
-            row['name'],
333  
-            row['unique'] != 0,
334  
-            exec_query("PRAGMA index_info('#{row['name']}')").map { |col|
335  
-              col['name']
336  
-            })
337  
-        end
338  
-      end
339  
-
340  
-      def primary_key(table_name) #:nodoc:
341  
-        column = table_structure(table_name).find { |field|
342  
-          field['pk'] == 1
343  
-        }
344  
-        column && column['name']
345  
-      end
346  
-
347  
-      def remove_index!(table_name, index_name) #:nodoc:
348  
-        exec_query "DROP INDEX #{quote_column_name(index_name)}"
349  
-      end
350  
-
351  
-      # Renames a table.
352  
-      #
353  
-      # Example:
354  
-      #   rename_table('octopuses', 'octopi')
355  
-      def rename_table(name, new_name)
356  
-        exec_query "ALTER TABLE #{quote_table_name(name)} RENAME TO #{quote_table_name(new_name)}"
357  
-      end
358  
-
359  
-      # See: http://www.sqlite.org/lang_altertable.html
360  
-      # SQLite has an additional restriction on the ALTER TABLE statement
361  
-      def valid_alter_table_options( type, options)
362  
-        type.to_sym != :primary_key
363  
-      end
364  
-
365  
-      def add_column(table_name, column_name, type, options = {}) #:nodoc:
366  
-        if supports_add_column? && valid_alter_table_options( type, options )
367  
-          super(table_name, column_name, type, options)
368  
-        else
369  
-          alter_table(table_name) do |definition|
370  
-            definition.column(column_name, type, options)
371  
-          end
372  
-        end
373  
-      end
374  
-
375  
-      def remove_column(table_name, *column_names) #:nodoc:
376  
-        raise ArgumentError.new("You must specify at least one column name. Example: remove_column(:people, :first_name)") if column_names.empty?
377  
-        column_names.flatten.each do |column_name|
378  
-          alter_table(table_name) do |definition|
379  
-            definition.columns.delete(definition[column_name])
380  
-          end
381  
-        end
382  
-      end
383  
-      alias :remove_columns :remove_column
384  
-
385  
-      def change_column_default(table_name, column_name, default) #:nodoc:
386  
-        alter_table(table_name) do |definition|
387  
-          definition[column_name].default = default
388  
-        end
389  
-      end
390  
-
391  
-      def change_column_null(table_name, column_name, null, default = nil)
392  
-        unless null || default.nil?
393  
-          exec_query("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
394  
-        end
395  
-        alter_table(table_name) do |definition|
396  
-          definition[column_name].null = null
397  
-        end
398  
-      end
399  
-
400  
-      def change_column(table_name, column_name, type, options = {}) #:nodoc:
401  
-        alter_table(table_name) do |definition|
402  
-          include_default = options_include_default?(options)
403  
-          definition[column_name].instance_eval do
404  
-            self.type    = type
405  
-            self.limit   = options[:limit] if options.include?(:limit)
406  
-            self.default = options[:default] if include_default
407  
-            self.null    = options[:null] if options.include?(:null)
408  
-            self.precision = options[:precision] if options.include?(:precision)
409  
-            self.scale   = options[:scale] if options.include?(:scale)
410  
-          end
411  
-        end
412  
-      end
413  
-
414  
-      def rename_column(table_name, column_name, new_column_name) #:nodoc:
415  
-        unless columns(table_name).detect{|c| c.name == column_name.to_s }
416  
-          raise ActiveRecord::ActiveRecordError, "Missing column #{table_name}.#{column_name}"
417  
-        end
418  
-        alter_table(table_name, :rename => {column_name.to_s => new_column_name.to_s})
419  
-      end
420  
-
421  
-      def empty_insert_statement_value
422  
-        "VALUES(NULL)"
423  
-      end
424  
-
425  
-      protected
426  
-        def select(sql, name = nil, binds = []) #:nodoc:
427  
-          exec_query(sql, name, binds)
428  
-        end
429  
-
430  
-        def table_structure(table_name)
431  
-          structure = exec_query("PRAGMA table_info(#{quote_table_name(table_name)})", 'SCHEMA').to_hash
432  
-          raise(ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'") if structure.empty?
433  
-          structure
434  
-        end
435  
-
436  
-        def alter_table(table_name, options = {}) #:nodoc:
437  
-          altered_table_name = "altered_#{table_name}"
438  
-          caller = lambda {|definition| yield definition if block_given?}
439  
-
440  
-          transaction do
441  
-            move_table(table_name, altered_table_name,
442  
-              options.merge(:temporary => true))
443  
-            move_table(altered_table_name, table_name, &caller)
444  
-          end
445  
-        end
446  
-
447  
-        def move_table(from, to, options = {}, &block) #:nodoc:
448  
-          copy_table(from, to, options, &block)
449  
-          drop_table(from)
450  
-        end
451  
-
452  
-        def copy_table(from, to, options = {}) #:nodoc:
453  
-          options = options.merge(:id => (!columns(from).detect{|c| c.name == 'id'}.nil? && 'id' == primary_key(from).to_s))