PHP SQLite - 准备好的语句行为异常?

PHP SQLite - prepared statement misbehaves?

我有以下 SQLite table

CREATE TABLE keywords
(
 id INTEGER PRIMARY KEY,
 lang INTEGER NOT NULL,
 kwd TEXT NOT NULL,
 count INTEGER NOT NULL DEFAULT 0,
 locs TEXT NOT NULL DEFAULT '{}'
);

CREATE UNIQUE INDEX  kwd ON keywords(lang,kwd);

在 PHP 中工作 我通常需要在此 table 中插入关键字,或者如果关键字已存在则更新行数。举个例子

$langs = array(0,1,2,3,4,5);
$kwds = array('noel,canard,foie gras','','','','','');

我现在这些数据运行通过下面的代码

 $len = count($langs);
 $klen = count($kwds);
 $klen = ($klen < $len)?$klen:$len;

 $sqlite = new SQLite3('/path/to/keywords.sqlite');
 $iStmt = $sqlite->prepare("INSERT OR IGNORE INTO keywords (lang,kwd) 
 VALUES(:lang,:kwd)");
 $sStmt = $sqlite->prepare("SELECT rowid FROM keywords WHERE lang = :lang 
 AND kwd = :kwd");

 if (!$iStmt || !$sStmt) return;

 for($i=0;$i < $klen;$i++)
 {
  $keywords = $kwds[$i];
  if (0 === strlen($keywords)) continue;
  $lang = intval($langs[$i]);


  $keywords = explode(',',$keywords);
  for($j=0;$j < count($keywords);$j++)
  {
   $keyword = $keywords[$j];
   if (0 === strlen($keyword)) continue;

   $iStmt->bindValue(':kwd',$keyword,SQLITE3_TEXT);
   $iStmt->bindValue(':lang',$lang,SQLITE3_INTEGER);
   $sStmt->bindValue(':lang',$lang,SQLITE3_INTEGER);
   $sStmt->bindValue(':kwd',$keyword,SQLITE3_TEXT);

   trigger_error($keyword);
   $iStmt->execute();
   $sqlite->exec("UPDATE keywords SET count = count + 1 WHERE lang = 
   '{$lang}' AND kwd = '{$keyword}';");

   $rslt = $sStmt->execute();
   trigger_error($sqlite->lastErrorMsg());
   trigger_error(json_encode($rslt->fetchArray()));
  }
 }

生成以下 trigger_error 输出

Keyword: noel Last error: not an error SELECT Result: {"0":1,"id":1}

Keyword: canard Last Error: not an error
SELECT Reult:false

Keyword:foiegras Last Error: not an error SELECT Result: false

从 SQLite 命令行中,我看到 table 中存在三个行条目并且正确,id/rowid 列分别设置为 1、2 和 3。 lastErrorMsg 不报告错误,但三个 $rslt->fetchArray() 语句中的两个返回 false 而不是具有 rowid/id 属性的数组。那么我在这里做错了什么?


我对此进行了更多调查,发现了根本原因。在我的原始代码中,第一个 SQLite3::execute - $iStmt-execute() - 的结果没有被分配给任何东西。我没有看到获取和解释该结果的任何特殊原因。当我将该行代码更改为读取 $rslt = $iStmt->execute() 时,我得到了预期的结果 - 正确报告了插入的三行中的 rowid/id

就好像 PHP SQLite3 扩展在内部缓冲了 SQLiteStatement::execute 函数调用的结果。当我在 运行ning 这样的语句中跳过作业时,$sStmt->execute() 实际上是在获取先前的结果。这是我在不知道 PHP SQLite3 扩展的内部工作原理的情况下的解释。也许更了解扩展的人想发表评论。

trigger_error(json_encode($rslt->fetchArray())); 之后添加 $rslt = NONE; 并显示正确的结果。

FetchArray 只能调用一次,并且 php 无法检测到变量已更改。我还尝试将 bindValue 更改为 bindParam 并将其移动到循环之前,但这与主要问题无关。

我认为除非 php 中存在错误,否则我的解决方案应该不会起作用。我对这门语言太陌生了,无法对这种观点充满信心,并希望帮助验证它。 好吧,这不是错误,而是违反了最小意外原则。该对象仍然存在于内存中,因此没有完成它或重置变量,就不会触发获取数组。