SQL 错误消息: "???????????????? "@tid"??" SQL 语句: "INSERT INTO members ([tid], [name], [age]) VALUES (@tid,@name,@age)" SQL 错误代码: "7335941".
下面是TableDataGateway里面的部分代码
list($holders, $values) = $this->dbo->getPlaceholder($row, $this->fields);
$holders = implode(',', $holders);
$fields = $this->dbo->qfields(array_keys($values));
$sql = "INSERT INTO {$this->qtableName} ({$fields}) VALUES ({$holders})";
// 插入数据
if (!$this->dbo->Execute($sql, $values, true))
getPlaceholder 就是对SQL语句进行组装,函数定义在Abstract.php里面, 如下
/**
* 根据驱动的参数占位符样式,返回包含参数占位符及有效数据的数组
*
* @param array $inputarr
* @param array $fields
*
* @return array
*/
00803 function getPlaceholder(& $inputarr, $fields = null)
00804 {
00805 $holders = array();
00806 $values = array();
00807 if (is_array($fields)) {
00808 $fields = array_change_key_case(array_flip($fields), CASE_LOWER);
00809 foreach (array_keys($inputarr) as $key) {
00810 if (!isset($fields[strtolower($key)])) { continue; }
00811 if ($this->PARAM_STYLE == DBO_PARAM_QM) {
00812 $holders[] = $this->PARAM_STYLE;
00813 } else {
00814 $holders[] = $this->PARAM_STYLE . $key;
00815 }
00816 $values[$key] =& $inputarr[$key];
00817 }
00818 } else {
00819 foreach (array_keys($inputarr) as $key) {
00820 if ($this->PARAM_STYLE == DBO_PARAM_QM) {
00821 $holders[] = $this->PARAM_STYLE;
00822 } else {
00823 $holders[] = $this->PARAM_STYLE . $key;
00824 }
00825 $values[$key] =& $inputarr[$key];
00826 }
00827 }
00828 return array($holders, $values);
00829 }
比如我们需要插入一行数据, 这个函数辅助组装SQL语句,
原始数据是个KEY数组,getPlaceholder 传递参数 $inputarr 就是我们需要插入的值,参数$fields 就是字段名
函数返回的holders是判断PARAM_STYLE的定义 ,比如DBO_PARAM_QM ,
返回值一个是占位符数组 holders, 还有就是一个key型数组, 也就是需要插入的数据数组。
这个只是准备工作, 有了这两个数组后在TableDataGateway文件里面组装临时SQL语句,
$sql = "INSERT INTO {$this->qtableName} ({$fields}) VALUES ({$holders})";
然后用数据库访问对象来调用自身类下的执行语句,
$this->dbo->Execute($sql, $values, true)
当然dbo所属的类里面还需要对这个临时sql语句进行最终解析, 比如这里是这个dbo对象是mssql类
于是找到mssql.php里面的execute函数
function execute($sql, $inputarr = null, $throw = true)
函数头里面的inputarr可以看到是上面的values, 也就是已经整理过的需要插入的数据数组
而传递过来的$sql就是上面的临时的包含占位符的临时SQL语句,
函数里面,这个地方语句
if (is_array($inputarr)) {
$sql = $this->bind($sql, $inputarr);
}
$this->bind($sql, $inputarr); 就是把占位符替换成具体的值
比如
INSERT INTO [members] ([tid], [name], [age]) VALUES (@tid,@name,@age)
替换成 INSERT INTO [members] ([tid], [name], [age]) VALUES (123,abc,12)
结果发现这个bind函数只有父类中有定义, 在Abstract.php文件中:
function bind($sql, & $inputarr)
{
$arr = explode('?', $sql);
$sql = array_shift($arr);
foreach ($inputarr as $value) {
if (isset($arr[0])) {
$sql .= $this->qstr($value) . array_shift($arr);
}
}
return $sql;
}
可见这个函数里面只对占位符'?'进行处理,而在实际调用的时候,mssql类下面占位符定义的是DBO_PARAM_AT_NAMED(at名称的形式)
比如上面的传参的我们打印出临时SQL语句查看占位符是VALUES (@tid,@name,@age)这样的形式,
于是bind函数没有起到应有的作用, SQL语句解析没有完成
引起这个问题的原因就是占位符
var $PARAM_STYLE = DBO_PARAM_AT_NAMED;
var $PARAM_STYLE = DBO_PARAM_QM;
尝试在mssql.php文件中把占位符直接改成问号形式, 而不要at名称的形式
这个改完发现生成的SQL成了这种形式
INSERT INTO [members] ([tid], [name], [age]) VALUES (32,weiwei,97)
还是报错, 因为数据类型问题 ,缺引号, 如果全加引号,或给weiwei加上引号就能正确执行,
可以看到上面的bind函数里面的$this->qstr 就是起这个加引号的作用,
而mssql类下面的qstr实际运行的效果是没有加上引号,对比下看看吧,
mssql.php文件里面的:
function qstr($value)
{
if (is_int($value)) { return $value; }
if (is_bool($value)) { return $value ? $this->TRUE_VALUE : $this->FALSE_VALUE; }
if (is_null($value)) { return $this->NULL_VALUE; }
return str_replace("'", "''", $value);
}
mysql.php文件里面的
function qstr($value)
{
if (is_int($value) || is_float($value)) { return $value; }
if (is_bool($value)) { return $value ? $this->TRUE_VALUE : $this->FALSE_VALUE; }
if (is_null($value)) { return $this->NULL_VALUE; }
return "'" . mysql_real_escape_string($value, $this->conn) . "'";
}
最后的一句返回
return "'" . mysql_real_escape_string($value, $this->conn) . "'";
就是加上引号,而且需要实现一个类似mysql_real_escape_string的功能来保证数据安全,
简单测试下的话 就直接这样返回吧
return "'" . $value . "'";
这种形式倒是能正常运转了
后来发现原文件中已经有实现一个escap string的函数, 于是 return "'" . mssql_real_escape_string($value) . "'";
阅读(515) | 评论(0) | 转发(0) |