【问题标题】:PHP/PostgreSQL: check if a prepared statement already existsPHP/PostgreSQL:检查准备好的语句是否已经存在
【发布时间】:2010-11-14 15:33:30
【问题描述】:

我将准备好的语句创建为:

pg_prepare('stm_name', 'SELECT ...');

今天,我在两次声明同名的prepared statement时遇到了一个问题(两次调用错误的函数):

Warning: pg_prepare() [function.pg-prepare]: Query failed: ERROR: prepared statement "insert_av" already exists in xxx on line 221

那么,作为问题的标题,有一种方法可以检查是否已经存在具有相同标签的prepare语句,以防万一,覆盖它?

我知道这个错误是我的错误造成的,只需在代码开头声明准备好的语句即可解决,但我想知道是否有解决方案可以更好地控制它们。

编辑:

在Milen回答之后,很简单地检查准备好的语句是否已经在使用,只需在数据库中查询表pg_prepared_statements:

try{
    $qrParamExist = pg_query_params("SELECT name FROM pg_prepared_statements WHERE name = $1", array($prepared_statement_name));
    if($qrParamExist){
        if(pg_num_rows($qrParamExist) != 0){
            echo 'parametized statement already created';
        }else{
            echo 'parametized statement not present';
        }
    }else{
        throw new Exception('Unable to query the database.');
    }
}catch(Exception $e){
    echo $e->getMessage();
}

但是,我认为这不是一个好的解决方案,因为我每次都要查询数据库。

好的,通常准备好的语句在脚本的开头声明,然后就可以重用,但是,我有一个连接良好的类,我不喜欢声明 10 个准备好的语句,而我只使用其中的 3 个.

所以,我想我会使用一个简单的 PHP 数组来跟踪我创建的语句,然后使用 isset() 函数检查它是否存在或需要创建:

try{
    $prepare = pg_prepare('my_stmt_name', "SELECT ...");
    if($prepare){
        $this->rayPrepared['my_stmt_name'] = true;
    }else{
        throw new Exception('Prepared statement failed.');
    }
}catch(Exception $e){
    echo $e->getMessage();
}

【问题讨论】:

  • 有时需要使用 EXECUTE 来修复“与 OID ###### 的关系不存在”错误。
  • 你能告诉我如何得到警告信息吗?当我没有收到此警告消息时,我花了将近一个小时来了解我的代码失败的原因。谢谢
  • @EnsomHodder 在 pg_preparepg_query 的结果失败时尝试使用 error_reporting(E_ALL);pg_last_error(即返回 false)

标签: php postgresql prepared-statement


【解决方案1】:

一种方式(希望有人指出更简单的一种方式):

<?
$prepared_statement_name = 'activity1';
$mydbname = '...';

$conn = pg_connect("host=... port=... dbname=... user=... password=...");

$result = pg_query_params($conn, 'SELECT name FROM pg_prepared_statements WHERE name = $1', array($prepared_statement_name));

if (pg_num_rows($result) == 0) {
    $result = pg_prepare($conn, $prepared_statement_name, 'SELECT * FROM pg_stat_activity WHERE datname =  $1');
}

$result = pg_execute($conn, $prepared_statement_name, array($mydbname));
while($row = pg_fetch_row($result)) {
    var_dump($row);
}

【讨论】:

    【解决方案2】:

    没有在 php 中尝试过,但如果这在您的应用程序中可行(如果您只需要在一个地方使用该语句并且不必通过名称再次“获取”它),您可以尝试准备一个未命名的语句. http://www.postgresql.org/docs/8.4/interactive/libpq-exec.html 说:

    PQprepare
    ...
    stmtName 可以是 "" 来创建一个未命名的语句,在这种情况下,任何预先存在的未命名语句都会被自动替换;否则,如果当前会话中已经定义了语句名称,则会出错。
    php_pg 使用 PQprepare,因此这可能对您有用。

    【讨论】:

    • 对不起,我有很多不同的准备好的状态..可以使用未命名的。
    • 是的,“当前会话”。我发现“当前会话”并不意味着“连接”。遗憾的是,没有一种可靠的方法让 API 告诉我们需要知道的内容,而无需进行查询。
    【解决方案3】:

    你为什么要使用准备好的语句?只有当您多次使用相同的语句时,它们才会提供性能优势。

    【讨论】:

    • 首先,它们提供了更高的防注入安全性......那么,我在哪里说我只使用它们一次? ;)
    • 性能优势很好,但对 sql 注入的帮助更好。
    • 致反对者:准备好的语句不提供更多的安全性。参数化语句可以。这些有不同的功能。 @peufeu 提到了一个非常有效的观点。请停止仅仅出于无知而说服人们使用 PS。
    • @Frunsi 确实如此。安全性是通过使用使安全选项成为最简单和最明显的选项的 API 来实现的。
    • @Schalton 不,postgres 为参数化语句提供了一个绝对安全的接口。 php 函数是 pg_query_params()。它基本上是一条消息中的准备+执行,与执行两个单独的操作相比,它更快并且导致更少的数据库负载。它还可以产生更好的计划,因为实际数据用于查询计划,而准备好的语句不是这种情况。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-05-09
    • 1970-01-01
    • 2014-12-16
    • 2022-12-31
    • 2011-11-28
    • 1970-01-01
    相关资源
    最近更新 更多