rails 3 SQLite3 Boolean false

Estou tentando inserir um valor booleano falso em uma tabela SQLite3, mas ele sempre insere um valor verdadeiro.

Aqui está minha migration:

class CreateUsers  false, :null => false end end def self.down drop_table :resources end end 

Quando tento inserir usando rails, ele produz o seguinte SQL:

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

O SQLite trata o ‘f’ como true, então ele insere o true em meu database. A consulta que quero gerar é:

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

O que estou fazendo de errado?

rails: 3.0.7

gem sqlite3: 1.3.3

O SQLite usa 1 para true e 0 para false :

O SQLite não possui uma class de armazenamento Booleana separada. Em vez disso, os valores booleanos são armazenados como inteiros 0 (falso) e 1 (verdadeiro).

Mas o SQLite também tem um sistema de tipos frouxo e lança automaticamente as coisas para que seu 'f' seja provavelmente interpretado como tendo uma veracidade de “verdadeiro” simplesmente porque não é zero.

Um pouco de escavação indica que você encontrou um bug no SQLiteAdapter do Rails 3.0.7. Em active_record/connection_adapters/abstract/quoting.rb , encontramos estes:

 def quoted_true "'t'" end def quoted_false "'f'" end 

Portanto, por padrão, o ActiveRecord assume que o database entende 't' e 'f' para colunas booleanas. O adaptador MySQL sobrescreve estes para trabalhar com sua implementação tinyint de colunas booleanas:

 QUOTED_TRUE, QUOTED_FALSE = '1'.freeze, '0'.freeze #... def quoted_true QUOTED_TRUE end def quoted_false QUOTED_FALSE end 

Mas o adaptador SQLite não fornece suas próprias implementações de quoted_true ou quoted_false modo a obter os padrões que não funcionam com os booleanos do SQLite.

Os 't' e 'f' booleanos funcionam no PostgreSQL, então talvez todos estejam usando o PostgreSQL com Rails 3 ou simplesmente não estão percebendo que suas consultas não estão funcionando corretamente.

Estou um pouco surpreso com isso e espero que alguém possa apontar onde eu errei, você não pode ser a primeira pessoa a usar uma coluna booleana no SQLite com Rails 3.

Tente monkey patching def quoted_true;'1';end e def quoted_false;'0';end em ActiveRecord::ConnectionAdapters::SQLiteAdapter (ou edite-os active_record/connection_adapters/sqlite_adapter.rb em active_record/connection_adapters/sqlite_adapter.rb ) e veja se você tem sensibilidade SQL

Eu encontrei isso também, aqui está como fazer o macacão:

 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 

Eu não entendo como ainda estou correndo com esse bug?

Você pode achar útil o seguinte trecho de código para adicionar compatibilidade com as colunas booleanas SQLite que estão realmente trabalhando no Rails 4 (também postado em 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 

Isto foi corrigido no master em 12 de julho de 2017. No entanto, não faz parte da última versão estável (5.1.4) . A versão mais recente em que foi corrigida é a v5.2.0.rc1 .

O comportamento pode ser definido via Rails.application.config.active_record.sqlite3.represent_boolean_as_integer (o padrão é true ).

Esta versão funciona no 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 

As constantes e .freeze são para desempenho, portanto, o ruby ​​não precisa regenerar essas cadeias de caracteres e o lixo as coleta em todas as chamadas.