pg_send_query って何だ？

# pg_send_query って何だ？

## 概要
PHP で PostgreSQL を使用したい場合、多くの人が PostgreSQL 関数を利用するだろう。[PHPマニュアルのPostgreSQL関数](https://www.php.net/manual/ja/ref.pgsql.php)を見ると、`pg_send_` から始まる関数がいくつかあり使い方が気になったので調べてみた。

ちなみに自分は普段 `pg_query_params`, `pg_fetch_assoc` くらいしか使っていなかった。

## pg_send_* 関数について

`pg_send_*` という名前の関数は現在、`pg_send_execute`, `pg_send_prepare`, `pg_send_query`, `pg_send_query_params` の4種類が存在する。

それぞれの関数名の似ている `pg_execute`, `pg_prepare`, `pg_query`, `pg_query_params` と似た構造をしていて引数が同じだが返値が異なる。 `pg_send_*` 関数の返値は、実行が成功したら `true`, 失敗したら `0` になる。

`pg_send_*` 関数は、実行時に結果を待たずに終了する。実行結果(`PgSql\Result`オブジェクト)を取得するには `pg_get_result` 関数を使用する。結果を取得せずに連続でSQL実行したいのであれば、連続で `pg_send_*` を実行すれば時間短縮になるのだろう。

`pg_send_*` 関数を実行した場合、SQLエラーが起きても `pg_get_result` 関数で `PgSql\Result` オブジェクトを取得できる為、`pg_result_error`, `pg_result_error_field` 関数を使用してより詳細なエラー情報を取得できる。(`pg_query`, `pg_query_params` 関数ではエラー時に `PgSql\Result` を返さないし、`pg_get_result` 関数でも取得できない)

[PHP: pg_send_query - Manual](https://www.php.net/manual/ja/function.pg-send-query.php)

## エラー時の動作

`pg_send_*` 関数は実行結果を待たないため、エラー時(不正なSQL実行時など)の動作が変化する。

`pg_query_params` では、以下のようにWarningがでて、返値は `false` になる。

```php
$r = pg_query_params($conn, "SELEEEEECT * FROM hoge", array());
//=> false
```

	Warning: pg_query_params(): Query failed: ERROR:  syntax error at or near "SEEEELECT"
	LINE 1: SELEEEEECT * FROM hoge

`pg_send_query_params` 関数では Warning も何もなく、返値も `true` が返る。

```php
$r = pg_send_query_params($conn, "SELEEEEECT * FROM hoge", array());
//=> true
```

エラーを取得するには、`pg_get_result($conn)` で `PgSql\Result` オブジェクトを取得してから`pg_result_error($result)` でエラーメッセージがあるか確認するか、`pg_last_error($conn)` でエラーメッセージを確認する。

```php
	$r = pg_send_query_params($conn, "SELEEEEECT * FROM hoge", array());
	//=> true
	$result = pg_get_result($conn);
	if (($errmsg = pg_result_error($result))) {
		echo "ErrorMessage: {$errmsg}\n";
	}
```

`pg_result_error_field($result, $field_code)` 関数でより詳細なエラー情報を取得できるが、`pg_last_error` 関数や `pg_result_error` 関数で取得するエラーメッセージで充分だし読みやすいので、普段は使わないでもよいだろう。

`pg_send_*` ではなく `pg_query_params` 等でエラーが発生した場合は、`pg_get_result` 関数で `PgSql\Result` を取得できない。よって、`PgSql\Result` が必要な `pg_result_error` 関数や `pg_result_error_field` 関数は使えないので、`pg_last_error` 関数を使用する必要がある。


## DB状態の確認

`pg_connection_status`, `pg_connection_busy`, `pg_transaction_status` と `pg_result_status` 等はDB/SQL実行結果の状態を確認する関数である。これらを `pg_send_query_params` , `pg_query_params` 関数の前後に実行し、どのようにステータス変化するかを確認した。

簡単にまとめると、

* `pg_query_params` 関数の前後では、`pg_connection_status`, `pg_connection_busy`, `pg_transaction_status` 関数の返値は変化しなかった
* `pg_connection_status` 関数は常に `PGSQL_CONNECTION_OK` を返した
* `pg_connection_busy` 関数の返値は細かく `true`/`false` が切り替わっているようだ。`pg_send_*` 関数の実行後 `pg_get_result` 実行前までや、SQLエラー発生後しばらくは `true` だった
* トランザクション中はもちろんだが、`pg_send_*` 関数実行直後も `pg_transaction_status` 関数の返値が変化することがわかった
* `pg_result_status` 関数はその名の通り、`PgSql\Result` オブジェクトの状態を返してくれた

試したコード例と各状態を以下に列挙する。


### コード例1 - `pg_query_params` で SELECT文
```php
// 実行前(A)
$result = pg_query_params($conn, "SELECT * FROM hoge", array());
// 実行後(B)
```

||(A)|(B)|
|:--|:--|:--|
|pg_connection_busy|false|false|
|pg_connection_status|PGSQL_CONNECTION_OK|PGSQL_CONNECTION_OK|
|pg_transaction_status|PGSQL_TRANSACTION_IDLE|PGSQL_TRANSACTION_IDLE|
|$result||`PgSql\Result`オブジェクト|
|pg_result_status||PGSQL_TUPLES_OK|


### コード例2 - pg_send_query_params で SELECT文
```php
// pg_send_* 実行前(A)
$retval = pg_send_query_params($conn, "SELECT * FROM hoge", array());
// pg_send_* 実行後(B)
$result = pg_get_result($conn);
// pg_get_result 実行後(C)
```
||(A)|(B)|(C)|
|:--|:--|:--|:--|
|pg_connection_busy|false|**true**|false|
|pg_connection_status|PGSQL_CONNECTION_OK|PGSQL_CONNECTION_OK|PGSQL_CONNECTION_OK|
|pg_transaction_status|PGSQL_TRANSACTION_IDLE|**PGSQL_TRANSACTION_ACTIVE**|PGSQL_TRANSACTION_IDLE|
|$retval||true||
|$result|||`PgSql\Result`オブジェクト|
|pg_result_status|||PGSQL_TUPLES_OK|

コード例1との違いは、`pg_send_*` の直後に `pg_connection_busy` が `true` となり、`pg_transaction_status` が `PGSQL_TRANSACTION_ACTIVE` となる点。`pg_get_result` の実行後はコード例1と同じになる。



### コード例3 - pg_query_params で INSERT/UPDATE/DELETE文
```php
// 実行前(A)
$result = pg_query_params($conn, "INSERT hoge (col1) VALUES(\$1)", array('val1'));
// 実行後(B)
```
||(A)|(B)|
|:--|:--|:--|
|pg_connection_busy|false|false|
|pg_connection_status|PGSQL_CONNECTION_OK|PGSQL_CONNECTION_OK|
|pg_transaction_status|PGSQL_TRANSACTION_IDLE|PGSQL_TRANSACTION_IDLE|
|$result||`PgSql\Result`オブジェクト|
|pg_result_status||**PGSQL_COMMAND_OK**|

`pg_result_status` が `PGSQL_COMMAND_OK` に変わっただけで、他はコード例1と同じ。UPDATE,DELETE文にしても同様


### コード例4 - pg_send_query_params で INSERT/UPDATE/DELETE文
```php
// pg_send_* 実行前(A)
$retval = pg_send_query_params($conn, "INSERT hoge (col1) VALUES(\$1)", array('val1'));
// pg_send_* 実行後(B)
$result = pg_get_result($conn);
// pg_get_result 実行後(C)
```

||(A)|(B)|(C)|
|:--|:--|:--|:--|
|pg_connection_busy|false|true|false|
|pg_connection_status|PGSQL_CONNECTION_OK|PGSQL_CONNECTION_OK|PGSQL_CONNECTION_OK|
|pg_transaction_status|PGSQL_TRANSACTION_IDLE|PGSQL_TRANSACTION_ACTIVE|PGSQL_TRANSACTION_IDLE|
|$retval||true||
|$result|||`PgSql\Result`オブジェクト|
|pg_result_status||**PGSQL_COMMAND_OK**|

`pg_result_status` が `PGSQL_COMMAND_OK` に変わっただけで、他はコード例2と同じ。UPDATE,DELETE文にしても同様


### コード例5: pg_query_params でトランザクション中にSELECT文
```php
pg_query($conn, "BEGIN");
// 実行前(A)
$result = pg_query_params($conn, "SELECT * FROM hoge", array());
// 実行後(B)
pg_query($conn, "END");
```

||(A)|(B)|
|:--|:--|:--|
|pg_connection_busy|false|false|
|pg_connection_status|PGSQL_CONNECTION_OK|PGSQL_CONNECTION_OK|
|pg_transaction_status|**PGSQL_TRANSACTION_INTRANS**|**PGSQL_TRANSACTION_INTRANS**|
|$result||`PgSql\Result`オブジェクト|
|pg_result_status||PGSQL_TUPLES_OK|

コード例1とほぼ同じだが、`pg_transaction_status` の結果が `PGSQL_TRANSACTION_IDLE` ではなく `PGSQL_TRANSACTION_INTRANS` になっている


### コード例6 - pg_send_query_params でトランザクション中にSELECT文
```php
pg_query($conn, "BEGIN");
// pg_send_* 実行前(A)
$retval = pg_send_query_params($conn, "SELECT * FROM hoge", array());
// pg_send_* 実行後(B)
$result = pg_get_result($conn);
// pg_get_result 実行後(C)
pg_query($conn, "END");
```
||(A)|(B)|(C)|
|:--|:--|:--|:--|
|pg_connection_busy|false|true|false|
|pg_connection_status|PGSQL_CONNECTION_OK|PGSQL_CONNECTION_OK|PGSQL_CONNECTION_OK|
|pg_transaction_status|**PGSQL_TRANSACTION_INTRANS**|PGSQL_TRANSACTION_ACTIVE|**PGSQL_TRANSACTION_INTRANS**|
|$retval||true||
|$result|||`PgSql\Result`オブジェクト|
|pg_result_status|||PGSQL_TUPLES_OK|

コード例2とほぼ同じだが、`pg_transaction_status`の結果が `PGSQL_TRANSACTION_IDLE` ではなく `PGSQL_TRANSACTION_INTRANS` になっている


### コード例7 - pg_query_params でSQLエラー発生
```php
// 実行前(A)
$result = pg_query_params($conn, "SELEEEECT * FROM hoge", array());
// 実行後(B)
```

||(A)|(B)|
|:--|:--|:--|
|pg_connection_busy|false|false|
|pg_connection_status|PGSQL_CONNECTION_OK|PGSQL_CONNECTION_OK|
|pg_transaction_status|PGSQL_TRANSACTION_IDLE|PGSQL_TRANSACTION_IDLE|
|$result||**false**|
|pg_result_status||**(実行不可)**|

SQLエラーが発生すると、`pg_query_params` が `false` を返す為、`PgSql\Result` は取得できない。
エラー内容は `pg_last_error($conn)` からエラーメッセージを取得し判断するしかないか。


### コード例8 - pg_send_query_params でSQLエラー発生
```php
// pg_send_* 実行前(A)
$retval = pg_send_query_params($conn, "SELEEEEECT * FROM hoge", array());
// pg_send_* 実行後(B)
$result = pg_get_result($conn);
// pg_get_result 実行後(C)
```	

||(A)|(B)|(C)|(C')|
|:--|:--|:--|:--|:--|
|pg_connection_busy|false|**true**|false|**true**|
|pg_connection_status|PGSQL_CONNECTION_OK|PGSQL_CONNECTION_OK|PGSQL_CONNECTION_OK|PGSQL_CONNECTION_OK|
|pg_transaction_status|PGSQL_TRANSACTION_IDLE|PGSQL_TRANSACTION_ACTIVE|**PGSQL_TRANSACTION_IDLE**|**PGSQL_TRANSACTION_ACTIVE**|
|$retval||true|||
|$result|||`PgSql\Result`オブジェクト|`PgSql\Result`オブジェクト|
|pg_result_status|||**PGSQL_FATAL_ERROR**|**PGSQL_FATAL_ERROR**|

SQLエラーが発生しても、`pg_send_query_params` は `true` を返し、`pg_get_result($conn)`で `PgSql\Result` が取得可能。`pg_result_status($result)` とすると、`PGSQL_FATAL_ERROR` を返し、エラーが起きていたことがわかる。
エラー内容は `pg_last_error($conn)` からエラーメッセージを取得してもよいし、`PgSql\Result` オブジェクトから `pg_result_error($result)` 関数 や `pg_result_error_field($result, $field_code)` 関数でもエラー情報を取得できる。
細かい点だがSQLエラー時は `pg_get_result` 実行直後も `pg_connection_busy` 関数が `true` を返し、`pg_transaction_status` 関数が `PGSQL_TRANSACTION_ACTIVE` を返す **こともある** ので注意(C')。


### コード例9: pg_query_params でトランザクション中にSQLエラー発生
```php
pg_query($conn, "BEGIN");
// 実行前(A)
$result = pg_query_params($conn, "SELEEEEECT * FROM hoge", array());
// 実行後(B)
pg_query($conn, "END");
```

||(A)|(B)|
|:--|:--|:--|
|pg_connection_busy|false|false|
|pg_connection_status|PGSQL_CONNECTION_OK|PGSQL_CONNECTION_OK|
|pg_transaction_status|PGSQL_TRANSACTION_INTRANS|**PGSQL_TRANSACTION_INERROR**|
|$result||false|
|pg_result_status||(実行不可)|

SQLエラー発生後はトランザクションが終了する(END や ROLLBACK を実行する)まで、`pg_transaction_status` 関数は `PGSQL_TRANSACTION_INERROR` を返す。


### コード例10 - pg_send_query_params でトランザクション中にSQLエラー発生
```php
pg_query($conn, "BEGIN");
// pg_send_* 実行前(A)
$retval = pg_send_query_params($conn, "SELEEEEECT * FROM hoge", array());
// pg_send_* 実行後(B)
$result = pg_get_result($conn);
// pg_get_result 実行後(C)
pg_query($conn, "END");
```

||(A)|(B)|(C)|(C')|
|:--|:--|:--|:--|:--|
|pg_connection_busy|false|true|false|**true**|
|pg_connection_status|PGSQL_CONNECTION_OK|PGSQL_CONNECTION_OK|PGSQL_CONNECTION_OK|PGSQL_CONNECTION_OK|
|pg_transaction_status|**PGSQL_TRANSACTION_INTRANS**|PGSQL_TRANSACTION_ACTIVE|**PGSQL_TRANSACTION_INERROR**|PGSQL_TRANSACTION_ACTIVE|
|$retval||true|||
|$result|||`PgSql\Result`オブジェクト|`PgSql\Result`オブジェクト|
|pg_result_status|||PGSQL_FATAL_ERROR|PGSQL_FATAL_ERROR|

コード例8とほぼ同じ。トランザクション中なので `pg_transaction_status` の返値が異なる
コード例8と同じく、SQLエラー時は `pg_get_result` 実行後も `pg_connection_busy` 関数が `true` を返し、`pg_transaction_status` 関数が `PGSQL_TRANSACTION_ACTIVE` を返すこともあるので注意(C')。


## まとめ
`pg_send_*` 関数を使用すれば、より詳細なエラー情報を取得し、返答が不要なSQLを連続実行できるといった利点がある。ただ、手間の割にメリットは薄いように思われるので通常は `pg_query_params` 等で充分だろう。

SQL連続実行にしても、順にPostgreSQLにコマンドを投げて実行することには変わりないので、トータルの処理時間はそこまでかわらないはず。

ただ、自分が知らないだけで他の利点が有るかもしれないので、もし知っていたら教えていただきたい。