Rails 3 SQLite3 Boolean false - PullRequest
       5

Rails 3 SQLite3 Boolean false

9 голосов
/ 16 мая 2011

Я пытаюсь вставить ложное логическое значение в таблицу SQLite3, но оно всегда вставляет истинное значение.

Вот моя миграция:

class CreateUsers < ActiveRecord::Migration
  def self.up
    create_table :users do |t|
      t.column :name, :string
      t.column :active, :boolean, :default => false, :null => false
    end
  end

  def self.down
    drop_table :resources
  end
end

Когда я пытаюсь вставить, используя рельсы, он выдает следующий SQL:

INSERT INTO "users" ("name", "active") VALUES ('test', 'f')

SQLite рассматривает 'f' как true, поэтому он вставляет true в мою базу данных. Запрос, который я хочу сгенерировать:

INSERT INTO "users" ("name", "active") VALUES ('test', false)

Что я делаю не так?

рельсы: 3,0,7

драгоценный камень sqlite3: 1.3.3

Ответы [ 5 ]

21 голосов
/ 16 мая 2011

SQLite использует 1 для true и 0 для false :

SQLite не имеет отдельного класса логического хранилища. Вместо этого логические значения хранятся в виде целых чисел 0 (ложь) и 1 (истина).

Но SQLite также имеет свободную систему типов и автоматически преобразует данные, поэтому ваш 'f', вероятно, интерпретируется как имеющий истинность "true" просто потому, что он не равен нулю.

Небольшое копание указывает на то, что вы обнаружили ошибку в Rails 3.0.7 SQLiteAdapter. В active_record/connection_adapters/abstract/quoting.rb мы находим это:

def quoted_true
  "'t'"
end

def quoted_false
  "'f'"
end

Таким образом, по умолчанию ActiveRecord предполагает, что база данных понимает 't' и 'f' для логических столбцов. Адаптер MySQL переопределяет их для работы с tinyint реализацией логических столбцов:

QUOTED_TRUE, QUOTED_FALSE = '1'.freeze, '0'.freeze

#...

def quoted_true
  QUOTED_TRUE
end

def quoted_false
  QUOTED_FALSE
end

Но адаптер SQLite не предоставляет свои собственные реализации quoted_true или quoted_false, поэтому он получает значения по умолчанию, которые не работают с логическими значениями SQLite.

Булевы 't' и 'f' работают в PostgreSQL, поэтому, возможно, все используют PostgreSQL с Rails 3, или они просто не замечают, что их запросы не работают должным образом.

Я немного удивлен этим, и, надеюсь, кто-то может указать, где я ошибся, вы не можете быть первым, кто использует логический столбец в SQLite с Rails 3.

Попробуйте обезьянько патчить def quoted_true;'1';end и def quoted_false;'0';end в ActiveRecord::ConnectionAdapters::SQLiteAdapter (или временно отредактируйте их в active_record/connection_adapters/sqlite_adapter.rb) и посмотрите, получите ли вы разумный SQL.

4 голосов
/ 03 декабря 2013

Я тоже столкнулся с этим, вот как обезьяна патч:

require 'active_record/connection_adapters/sqlite_adapter'
module ActiveRecord
  module ConnectionAdapters
    class SQLite3Adapter < SQLiteAdapter
      def quoted_true; '1' end
      def quoted_false; '0' end
    end
  end
end

Я не понимаю, как я все еще сталкиваюсь с этой ошибкой ??

3 голосов
/ 06 марта 2014

Может оказаться полезным следующий фрагмент кода для добавления совместимости с логическими столбцами SQLite, фактически работающими в Rails 4 (также размещен по адресу https://gist.github.com/ajoman/9391708):

# config/initializers/sqlite3_adapter_patch.rb

module ActiveRecord
  module ConnectionAdapters
    class SQLite3Adapter < AbstractAdapter
      QUOTED_TRUE, QUOTED_FALSE = "'t'", "'f'"

      def quoted_true
        QUOTED_TRUE
      end

      def quoted_false
        QUOTED_FALSE
      end
    end
  end
end
2 голосов
/ 01 февраля 2018

Это было исправлено на мастере 12 июля 2017 года. Однако оно не является частью последней стабильной версии (5.1.4) . Самым последним выпуском, в котором он исправлен, является v5.2.0.rc1 .

Поведение можно установить с помощью Rails.application.config.active_record.sqlite3.represent_boolean_as_integer ( по умолчанию true).

0 голосов
/ 23 декабря 2014

Эта версия работает в Rails 4.1.

require 'active_record/connection_adapters/sqlite_adapter'

module ActiveRecord::ConnectionAdapters::SQLite3Adapter
  QUOTED_TRUE, QUOTED_FALSE = 't'.freeze, 'f'.freeze

  def quoted_true; QUOTED_TRUE end
  def quoted_false; QUOTED_FALSE end
end

Константы и .freeze предназначены для производительности, поэтому ruby ​​не нужно восстанавливать эти строки и собирать их мусором при каждом вызове.

...