php - texto - ''PDOException'' con el mensaje ''SQLSTATE[22001]: datos de cadena, derecha truncada: 0
string strip_tags (2)
NOTA: reduje este problema a PDO específicamente porque puedo preparar y ejecutar sentencias con éxito utilizando las funciones odbc_* .
¿Por qué no puedo vincular este parámetro a la declaración preparada PDO?
Esto funciona:
$mssqldriver = ''ODBC Driver 13 for SQL Server'';
$pdoDB = new PDO("odbc:Driver=$mssqldriver;Server=$hostname;Database=$dbname", $username, $password);
$pdoDB->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
$sql = "SELECT ''value'' AS col where ''this'' = ''this''";
$stmt = $pdoDB->prepare($sql);
$params = [];
$stmt->execute($params);
print_r($stmt->fetch());
Array ( [col] => value [0] => value )
No funciona:
$sql = "SELECT ''value'' AS col where ''this'' = ?";
$stmt = $pdoDB->prepare($sql);
$params = [''this''];
$stmt->execute($params);
print_r($stmt->fetch());
El servidor web ejecuta PHP 5.5.9 en Linux Ubuntu 14.04 con el controlador ODBC 13 para SQL Server y se conecta a Microsoft SQL Server 2012 en Windows Server 2012
Aquí está el error completo:
Fatal error: Uncaught exception ''PDOException'' with message ''SQLSTATE[22001]: String data, right truncated: 0 [Microsoft][ODBC Driver 13 for SQL Server] String data, right truncation (SQLExecute[0] at /build/buildd/php5-5.5.9+dfsg/ext/pdo_odbc/odbc_stmt.c:254)'' in /var/www/scratch.php:46 Stack trace: #0 /var/www/scratch.php(46): PDOStatement->execute(Array) #1 {main} thrown in /var/www/scratch.php on line 46
También he intentado configurar:
$pdoDB->setAttribute( PDO::ATTR_EMULATE_PREPARES, true );
Y usando parámetros nombrados:
$sql = "SELECT ''value'' AS col where ''this'' = :myVal";
$stmt = $pdoDB->prepare($sql);
$params = [''myVal'' => ''this''];
$stmt->execute($params);
print_r($stmt->fetch());
Incluso con un colon explícito:
$params = ['':myVal'' => ''this''];
También intenté usar bindParam
como se demuestra en esta respuesta :
$sql = "SELECT ''value'' AS col where ''this'' = ?";
$stmt = $pdoDB->prepare($sql);
$param = ''this'';
$stmt->bindParam(1, $param);
$stmt->execute();
print_r($stmt->fetch());
Además de con parámetros nombrados:
$sql = "SELECT ''value'' AS col where ''this'' = :myVal";
$stmt = $pdoDB->prepare($sql);
$param = ''this'';
$stmt->bindParam('':myVal'', $param, PDO::PARAM_STR);
$stmt->execute();
print_r($stmt->fetch());
Si intento establecer explícitamente la longitud:
$stmt->bindParam('':myVal'', $param, PDO::PARAM_STR, 4);
Recibo un error extra:
Fatal error: Uncaught exception ''PDOException'' with message ''SQLSTATE[42000]: Syntax error or access violation: 102 [Microsoft][ODBC Driver 13 for SQL Server][SQL Server] Incorrect syntax near ''OUTPUT''.
Y sí, todo esto es un ejemplo trivializado sin tablas para que pueda reproducirlo fácilmente, pero para estar seguro, lo he intentado con una tabla real.
CREATE TABLE myTable (
id INT IDENTITY PRIMARY KEY,
val NVARCHAR(255)
);
INSERT INTO myTable (val) VALUES (''hello world'');
Trabajos:
$sql = "SELECT * FROM myTable WHERE val = ''hello world''";
$stmt = $pdoDB->prepare($sql);
$params = [];
$stmt->execute($params);
print_r($stmt->fetch());
Array ( [id] => 1 [0] => 1 [val] => hello world [1] => hello world )
No funciona:
$sql = "SELECT * FROM myTable WHERE val = ?";
$stmt = $pdoDB->prepare($sql);
$params = [''hello world''];
$stmt->execute($params);
print_r($stmt->fetch());
Todas las rutas conducen al mismo error:
Datos de cadena, truncados a la derecha
Desafortunadamente,
Es un problema de incompatibilidad de 64 bits #61777 ( #61777 , #64824 ) y sin dudas está en una compilación de 64 bits que no le permite enlazar parámetros.
Por suerte,
Tiene un parche que se incluyó por primera vez en la versión 5.6:
Este error también se menciona en #61777 y aún está presente en la última versión estable de la rama 5.5. Veo que ya existen dos tickets para este problema, y solo estoy enviando estos cambios a través de github como un recordatorio de que este es un problema grave para cualquiera que use
PDO_ODBC
en las compilaciones de x64.
¿Qué hay de malo en el envío de tu PDO_ODBC
?
Al mirar uno de esos parches recomendados:
diff --git a/ext/pdo_odbc/odbc_stmt.c b/ext/pdo_odbc/odbc_stmt.c
index 8b0ccf3..1d275cd 100644
--- a/ext/pdo_odbc/odbc_stmt.c
+++ b/ext/pdo_odbc/odbc_stmt.c
@@ -551,7 +551,7 @@ static int odbc_stmt_describe(pdo_stmt_t *stmt, int colno TSRMLS_DC)
struct pdo_column_data *col = &stmt->columns[colno];
RETCODE rc;
SWORD colnamelen;
- SDWORD colsize;
+ SQLULEN colsize;
SQLLEN displaysize;
Vemos que lo único que ha cambiado es SDWORD
( SDWORD
16 bits con signo) que se sustituye con un nuevo tipo de ODBC, SQLULEN
que es de 64 bits en una aplicación ODBC de 64 bits y 32 bits en una aplicación ODBC de 32 bits .
Creo que committer no estaba al tanto del tipo de datos de colsize
, ya que en la siguiente línea SQLLEN
está definida correctamente.
¿Qué debería hacer ahora?
- Actualizar a la versión de PHP> = 5.6
-
odbc_*
con las funcionesodbc_*
como solución de trabajo. - Compila un PHP v5.5.9 con parches proporcionados.
- Cree su propio contenedor PDO según lo recomendado por @GordonM
Probablemente esto no sea lo que quiere escuchar, pero tiene todas las características de un error en el controlador ODBC de PHP para PDO (que no se utiliza mucho ya que los programadores de PHP tienden a favorecer bases de datos de código abierto como MySQL / SQLite / Postgres sobre ofertas comerciales) , o en el controlador subyacente del servidor SQL (que tiene poco soporte en Linux, por razones similares), aunque si odbc_ * funciona, probablemente no sea el controlador subyacente.
Si intenta realizar exactamente las mismas tareas, excepto el uso de "sqlite::memory:"
como el DSN, todos sus ejemplos funcionan. Esto hace que sea muy poco probable que estés haciendo algo incorrecto (a menos que el servidor MS tenga una sintaxis SQL realmente extraña y extraña de la que no tengo conocimiento). Sus ejemplos funcionan bien para mí con SQLite cuando ATTR_EMULATE_PREPARES
está habilitado y deshabilitado.
Todo lo que creo que puedes hacer de manera realista es presentar un informe de error y esperar a que alguien lo recoja. Sin embargo, puede que tengas una larga espera.
En cuanto a las soluciones prácticas para su problema, sus opciones son a) cambiar a un DBMS compatible con PHP ob) recurrir a la construcción de cadenas SQL en lugar de declaraciones preparadas y estar preparado para aceptar la carga de evitar ataques de inyección SQL usted mismo. ¡Esto debería ser considerado como el último recurso! El uso de PDO::quote() puede ayudar, pero también me aseguraré de que sus datos también estén completamente validados. Sé que no es una solución ideal, pero si debe usar MS SQL y no puede esperar a que el equipo PHP lo solucione, entonces realmente no veo que tenga muchas opciones.
O hay una opción c) que es utilizar las funciones odbc_ * en su lugar, si funcionan. Por supuesto, si desea utilizar el estilo de OOP, tendrá que implementar su propia clase que ajuste las funciones de odbc de procedimiento en los métodos de OO, lo que podría ser mucho trabajo.
EDIT : encontré otra pregunta sobre donde el asker parece tener un problema similar. Su solución fue deshacerse del controlador de MS "oficial" a favor de FreeTDS. Esto podría ser algo así como un tiro en la oscuridad, pero podría valer la pena intentarlo.