'PDOException' with message ' SQLSTATE[22001]: String data, right truncated: 0

NOTE: j'ai réduit ce problème à spécifiquement AOP parce que je suis capable de préparer et exécuter avec succès des déclarations en utilisant les fonctions odbc_* .

pourquoi ne puis-je pas lier ce paramètre à la déclaration préparée par L'AOP?

Cela fonctionne:

$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 )

ne fonctionne pas:

$sql = "SELECT 'value' AS col where 'this' = ?";
$stmt = $pdoDB->prepare($sql);
$params = ['this'];
$stmt->execute($params);
print_r($stmt->fetch());

le serveur web exécute PHP 5.5.9 sur Linux Ubuntu 14.04 avec pilote ODBC 13 pour le serveur SQL et la connexion à Microsoft SQL Server 2012 sur Windows Server 2012

Voici l'erreur complète:

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

j'ai aussi essayé le réglage:

$pdoDB->setAttribute( PDO::ATTR_EMULATE_PREPARES, true );

et en utilisant les paramètres nommés:

$sql = "SELECT 'value' AS col where 'this' = :myVal";
$stmt = $pdoDB->prepare($sql);
$params = ['myVal' => 'this'];
$stmt->execute($params);
print_r($stmt->fetch());

même avec un deux-points explicite:

$params = [':myVal' => 'this'];

j'ai également essayé juste en utilisant bindParam comme démontré dans cette réponse :

$sql = "SELECT 'value' AS col where 'this' = ?";
$stmt = $pdoDB->prepare($sql);
$param = 'this';
$stmt->bindParam(1, $param);
$stmt->execute();
print_r($stmt->fetch());

ainsi qu'avec les paramètres nommés:

$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 j'essaie de définir explicitement la longueur:

$stmt->bindParam(':myVal', $param, PDO::PARAM_STR, 4);

je reçois une erreur bonus:

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'.

Et oui, tout cela est banalisé exemple sans tables de sorte que vous pouvez facilement reproduire, mais juste pour être sûr, j'ai effectivement essayé avec une vraie table.

CREATE TABLE myTable (
    id INT IDENTITY PRIMARY KEY,
    val NVARCHAR(255)
);
INSERT INTO myTable (val) VALUES ('hello world');

:

$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 )

ne fonctionne pas:

$sql = "SELECT * FROM myTable WHERE val = ?";
$stmt = $pdoDB->prepare($sql);
$params = ['hello world'];
$stmt->execute($params);
print_r($stmt->fetch());

Tous les chemins mènent à la même erreur:

Chaîne de données, droit tronqué

23
demandé sur Community 2016-07-08 00:33:59

2 réponses

malheureusement,

c'est un PDO_ODBC problème d'incompatibilité 64 bits( #61777 , #64824 ) et sans aucun doute, vous êtes sur une construction 64 bits qui ne vous permet pas de lier les paramètres.

heureusement", 1519100920"

il a un patch qui a été inclus pour la première fois dans la version 5.6:

ce bug est aussi référencé dans #61777 et est toujours présent dans la dernière version stable de la branche 5.5. Je vois deux billets existe déjà pour ce problème, et je ne fais que soumettre ces modifications via github comme un rappel que c'est un grave problème pour ceux qui en utilisant PDO_ODBC sur les constructions x64.

Qu'est-ce qui ne va pas avec votre PHP expédié PDO_ODBC ?

En regardant l'un de ces patchs recommandés:

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;

nous voyons que la seule chose qui a changé est SDWORD (entier signé de 16 bits) qui est substitué avec le nouveau type ODBC SQLULEN qui est 64 bits dans une application ODBC 64 bits et 32 bits dans une application ODBC 32 bits .

je crois que committer n'était pas au courant du type de données colsize seulement puisque dans la ligne suivante SQLLEN est défini correctement.

Que dois-je faire maintenant?

  1. Mise à niveau de la version de PHP >= 5.6
  2. Bâton avec odbc_* fonctionne comme une solution de travail.
  3. compiler un PHP v5.5.9 avec patches fournis.
  4. Construisez votre propre emballage PDO comme recommandé par @GordonM
19
répondu revo 2016-07-20 22:06:04

ce n'est probablement pas ce que vous voulez entendre, mais cela a toutes les caractéristiques d'un bug dans le pilote ODBC de PHP pour PDO (qui n'est pas très utilisé car les programmeurs PHP ont tendance à préférer les bases de données open source comme MySQL/SQLite/Postgres aux offres commerciales), ou dans le pilote SQL server sous-jacent (qui est mal pris en charge par Linux, pour des raisons similaires), mais si odbc_* fonctionne alors ce n'est probablement pas le pilote sous-jacent.

Si vous essayez de faire exactement les mêmes tâches, sauf utiliser "sqlite::memory:" comme DSN, tous vos exemples fonctionnent. Il est donc très peu probable que vous fassiez quelque chose de mal (à moins que MS Server n'ait une syntaxe SQL vraiment bizarre et non conforme dont je ne suis pas au courant). Vos exemples fonctionnent très bien pour moi avec SQLite quand ATTR_EMULATE_PREPARES est à la fois activé et désactivé.

tout ce que je pense que vous pouvez faire de façon réaliste est faites un rapport de bogue et espérez que quelqu'un le ramasse. Vous pouvez être dans une longue attente.

quant aux solutions pratiques à votre problème, vos options sont soit a) passer à un SGBD que PHP soutient ou b) recourir à la construction de chaîne de caractères SQL au lieu de déclarations préparées et être prêt à accepter le fardeau d'éviter attaques D'injection SQL vous-même. Cela devrait être considéré comme un dernier recours! L'utilisation de AOP::quote () peut aider, mais je m'assurerais également que vos données sont validées très en détail ainsi. Je sais que c'est ce n'est pas une solution idéale, mais si vous devez utiliser MS SQL et que vous ne pouvez pas attendre que L'équipe PHP le corrige, Je ne vois pas vraiment que vous ayez le choix.

ou il y a l'option c) qui est d'utiliser les fonctions odbc_* à la place, si elles fonctionnent. Bien sûr, si vous voulez utiliser le style OOP alors vous devrez mettre en œuvre votre propre classe qui enveloppe les fonctions odbc procédurales dans les méthodes OO de sorte que cela pourrait potentiellement être beaucoup de travail.

EDIT : J'ai trouvé une autre question sur le débordement de la pile où l'asker semble avoir un problème similaire. Sa solution était de laisser tomber le conducteur MS "officiel" en faveur de FreeTDS. C'est peut-être un tir dans le noir, mais ça peut valoir le coup d'essayer.

4
répondu GordonM 2017-05-23 12:18:32