2012-05-10 21 views
21

Estoy usando un campo de bit (1) para almacenar valores booleanos y escribir en la tabla usando declaraciones preparadas con PDO.PDOstatement (MySQL): insertando el valor 0 en un campo de bit (1) da como resultado 1 escrito en la tabla

Esta es la tabla de prueba:

CREATE TABLE IF NOT EXISTS `test` (
    `SomeText` varchar(255) NOT NULL, 
    `TestBool` bit(1) NOT NULL DEFAULT b'0' 
) ENGINE=MEMORY DEFAULT CHARSET=latin1; 

Este es el código de prueba:

$pdo = new PDO("connection string etc") ; 
$statement = $pdo->prepare('INSERT INTO `test` (SomeText,TestBool) VALUES (?,?)') ; 
$statement->execute(array("TEST",0)) ; 

Ejecución de código que me da una fila con valor 1 bajo TestBool. Y lo mismo con bindValue() y bindParm(). También probé marcadores de posición con nombre (en lugar de?) Con el mismo resultado.

Luego probé:

$statement = $pdo->prepare('INSERT INTO `test` (SomeText,TestBool) VALUES ("TEST",0)') ; 
$statement->execute() ; 

que funcionaba correctamente (TestBool tiene valor 0). Perforar el SQL directamente en MySQL también funciona.

Tenga en cuenta que la inserción de 1 siempre funciona.

Entonces, ¿por qué los marcadores de posición no pueden insertar el valor 0? (¿y cómo lo hago realmente?)

+0

Ya está usando PDO, eso está bien. ¿Por qué no aprovechar la función de marcadores de posición nombrados de PDO? Vea un tutorial: http://www.phpeveryday.com/articles/PDO-Positional-and-Named-Placeholders-P551.html –

+0

A los fines de esta pregunta, lo probé y no hace ninguna diferencia. Para una consulta general, ¿hay alguna ventaja de usarlo de otra manera que la conveniencia? (Esto es parte de un DAL, así se generará de todos modos) – Peter

+0

Sí, no necesita recordar el orden de sus variables. Aumenta la abstracción de su código. –

Respuesta

26

La columna BIT es un tipo binario en mysql (aunque está documentado como tipo numérico, eso no es precisamente cierto) y aconsejo evitarlo debido a problemas con las bibliotecas del cliente (lo que prueba el problema PDO). Se ahorrará muchos problemas si modifica el tipo de columna en TINYINT (1)

TINYINT (1) consumirá por supuesto un byte de almacenamiento completo para cada fila, pero de acuerdo con los documentos de mysql BIT (1) hará también.

de: http://dev.mysql.com/doc/refman/5.1/en/storage-requirements.html

requisito de almacenamiento de bits es: aproximadamente (M + 7)/8 bytes que sugiere que la columna BIT (M) también se byte alineado.

También encontré esto: https://bugs.php.net/bug.php?id=50757

por lo que podría comprobar si siguiente código funciona como se espera:

$pdo = new PDO("connection string etc") ; 
$statement = $pdo->prepare('INSERT INTO `test` (SomeText,TestBool) VALUES (:someText,:testBool)') ; 
$statement->bindValue(':someText', "TEST"); 
$statement->bindValue(':testBool', 0, PDO::PARAM_INT); 
$statement->execute(); 

También puede probar con diferentes sugerencias de tipo que PARAM_INT, aún incluso si lo hacen trabajo. Aconsejo cambiar a TINYINT.

+3

Eso funcionó. A propósito, acabo de descubrir que usar true/false (en lugar de 1/0) también parece funcionar para mi caso. – Peter

+1

Tenga en cuenta que cuando vincula una cadena como un valor, incluso si usa 'PDO :: PARAM_INT', no funcionará como se espera. Además de 'PDO :: PARAM_INT', también necesita convertir la cadena en un entero:' $ statement-> bindValue (': testBool', (int) $ _ POST ['cero'], PDO :: PARAM_INT); 'o sino el valor siempre será 1. – Mike

5

pdo de forma predeterminada no utiliza declaraciones preparadas para el controlador mysql, sino que las emula creando sql dinámico detrás de las escenas para usted. El sql enviado a mysql termina siendo un solo 0 citado como '0', que mysql interpreta como una cadena, no como un número.

$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); 

Debería funcionar ahora, y también utilizará declaraciones realmente preparadas.

+1

Lo probé y todavía no funciona. ¿Es posible que el controlador mysql simplemente no admita las declaraciones nativas preparadas? Estoy usando php 5.4.0 si eso hace la diferencia. – Peter

1

podría intentar esto sin parámetro

if($_POST['bool'] == 1) 
{ 
$bool = "b'1'"; 
} 
else 
{ 
$bool = "b'0'"; 
} 
$statement = $pdo->prepare("INSERT INTO `test` (SomeText,TestBool) VALUES (?,$bool)") ; 
$statement->execute(array("TEST")) ; 

y no hay problema de seguridad

3

Debido prepare añade ' a su parámetro, sólo tiene que añadir b antes del nombre del parámetro

$statement = $pdo->prepare('INSERT INTO `test` (SomeText,TestBool) VALUES (?, b?)'); 
$statement->execute(array("TEST", 1 /* or TRUE */)); 

Nota: puede usar 1, 0 o TRUE, FALSE.

Cuestiones relacionadas