插入参数超过30个字符时出错


Error during insert with parameter(s) over 30 characters

问题

当至少有一个参数超过30个字符时,使用带有PDO和ODBC驱动程序的准备好的语句执行插入会出现以下错误:

SQLSTATE[HY010]: Function sequence error: 0 
[unixODBC][Driver Manager]Function sequence error (SQLExecute[0] at /usr/src/builddir/ext/pdo_odbc/odbc_stmt.c:254)

插入<=的任何绑定字符串的工作长度为30个字符。

我对SELECT查询没有问题。

INSERTisqlsqlcmd一起使用不会产生错误,但如果列值超过30个字符,则会截断数据库中的列值。

这似乎是一个驱动程序问题。

关于是什么导致了这个问题,以及如何解决这个问题,有什么想法吗?

示例

下面是一个用PHP、isqlsqlcmd在我的系统上复制错误的最小示例。

使用的表(称为table(有三列:

  • colvarchar varchar(70)
  • colnvarchar nvarchar(40)
  • colnchar nchar(60)

产生错误的代码:

<?php
$dns = 'odbc:testdb';
$username = 'user';
$password = 'pass';
$pdo = new PDO($dns, $username, $password);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$sql = <<<'QUERY'
        INSERT INTO table
            (colvarchar, colnvarchar, colnchar)
        VALUES
            (CAST(:colvarchar AS varchar)                 
             CAST(:colnvarchar AS nvarchar),
             CAST(:colnchar AS nchar));
QUERY;
$prepStmt = $pdo->prepare($sql);
// Add one more characters to any of the following strings to cause the error
$prepStmt->bindValue('colvarchar', '012345678901234567890123456789');
$prepStmt->bindValue('colnvarchar', '012345678901234567890123456789');
$prepStmt->bindValue('colnchar', '012345678901234567890123456789');
$prepStmt->execute();
?>

向30个字符串中的任何一个添加额外字符都会导致以下错误:

PHP Fatal error:  Uncaught exception 'PDOException' with message 'SQLSTATE[HY010]: Function sequence error: 0 [unixODBC][Driver Manager]Function sequence error (SQLExecute[0] at /usr/src/builddir/ext/pdo_odbc/odbc_stmt.c:254)' in ...

从命令行使用isqlsqlcmd执行插入,但表上的选择显示字符串被截断为30个字符。

slqcmd:

sqlcmd-D-S testdb-U user-p pass-q"INSERT INTO表(colvarchar,colnvarchar、colnchar(值(CAST('012345678901234567890123456789xxx'AS varchar(,CAST('012345678901234567890123456789xxx'AS nvarchar(,CAST('012345678901234567890123456789xxx'AS nchar(">

(1行受影响(

isql:

isql testdb-U用户-p通过

SQL>INSERT INTO表(colvarchar,colnvarchar、colnchar(值(CAST('012345678901234567890123456789xxx'AS varchar(,CAST('012345678901234567890123456789xxx'AS nvarchar(,CAST('012345678901234567890123456789xxx'AS nchar(">

SQLRowCount返回1

结果:

SELECT colvarchar, colnvarchar, colnchar FROM table;
colvarchar | colnvarchar | colnchar
012345678901234567890123456789 | 012345678901234567890123456789 | 012345678901234567890123456789
012345678901234567890123456789 | 012345678901234567890123456789 | 012345678901234567890123456789

研究

这篇文章详细介绍了一个类似的问题,即插入不符合最大列宽、时间戳格式或列类型。

  • 时间戳格式已经过测试,并且确实有效,因为它少于30个字符
  • 已经检查了超过30个字符的测试字符串,以确保它们的长度小于列的最大宽度
  • 问题列的数据类型为varchar

系统设置

  • 操作系统:Debian喘息(64位(
  • 数据库:Microsoft SQL Server 2014
  • Web服务器:Apache 2.2.22(64位(
  • PHP 5.6.24与Zend线程安全(64位(
  • Microsoft ODBC驱动程序11.0.2270.0(Red Hat Linux((64位(
    • 根据这个博客和spiceworks创建了合适的环境来与Debian合作
  • unixODBC 2.3.0(64位(
    • linux上的MS ODBC驱动程序所需

更新:我认为这是一个unixODBC问题,因为当我使用FreeTDS和unixODBC时,出现了30个字符的截断(因为这个问题,它被更改为MS ODBC和unixODBC.(。不同的是,使用FreeTDS时没有错误消息;它像CCD_ 14和CCD_。

为了与MS ODBC 11兼容,将unixODBC版本从2.3.4更改为2.3.0,如下所示。问题仍然存在。

所有需要ODBC共享库的程序都链接到了2011年的版本。它们现在都链接到unixODBC中最新的共享库。问题仍然存在。

您CAST到NVARCHAR,并且NVARCHAR在SQL Server 中限制为30个字符

制作演员阵容如下:CAST(colvarchar AS varchar(70((CAST(colnvarchar AS nvarchar(40((CAST(colnchar AS nchar(60((

问题在于SQL CAST表达式。

来自MSDN-CAST&转换:

截断和舍入结果

当您转换字符或二进制表达式(char、nchar、,nvarchar、varchar、binary或varbinary(转换为不同的数据类型,数据可以被截断,仅部分显示,或者由于结果太短而无法显示而返回错误。

文档列出了保证不会被截断的特定例外情况。然而,它也没有详细说明将截断的长度数据。

指定数据类型长度以避免截断:

CAST (colvarchar  AS varchar(70))
CAST (colnvarchar AS nvarchar(40))
CAST (colnchar    AS nchar(60))