ファイヤープロジェクト
問い合わせ結果からデータを取得する
2004-02-29T19:00+09:00   matsu
PGresultから各種データを取得,表示するプログラムを作成してみた.
libpqで問い合わせを行ない,結果からデータを取り出す処理の概要を改めて整理してみる.
  1. PQexecの返り値であるPGresultを使用してPQresultStatusを呼ぶ.
  2. PQresultStatusの値がPGRES_TUPLES_OKだった場合,PGresultのアクセッサを使用して各種情報を取り出す.
  3. PQclearでPGresultを開放する.
本頁では,PGresultアクセッサのいくつかを使用するサンプルを作成し,問い合わせ結果からデータを取り出す方法について記述する.
PGresultのアクセッサを使用するサンプルを以下に示す.
このサンプルは前頁のサンプルを一部追加/修正したものである.変更点は以下のとおり.
  1. テーブルhogeのスキーマを変更.
      char *sql = "create table hoge (id int primary key, value int, txt TEXT)";
    
  2. 上にともないINSERT,SELECT文を変更.INSERTは以下のとおり.
          sprintf (sql, "insert into hoge values (%d, %d, 'RANDOM%d')", i,
    	       myRandom (), myRandom);
    
    SELECTは以下のとおり.
      sprintf (sql, "select * from hoge "
    	   "where value = (SELECT max(value) from hoge) "
    	   "OR value = (SELECT min(value) from hoge) ");
    
  3. SELECT回数は1回とし,それにともないコマンドライン引数を変更.
  4. selectRecords関数内でPGresultからタプル数を取得して表示していた部分を以下の関数呼び出しに変更.
    static void showResultInfo (PGresult * result);
    
本頁で記述するのは最後のshowResultInfo関数での処理である.ではコンパイル.
gcc  -Wall -I/usr/include/postgresql/ -lpq query2.c
そして実行.
$> ./a.out 1024 "host=dbserver user=matsu password=hogefuga dbname=firstdb" 
insert_count : 1024 conninfo : host=dbserver user=matsu password=hogefuga dbname=firstdb
### start connect 0 0 ###
PQconnectdb OK
### end connect 0 0 ###
### start createTable 0 0 ###
NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index 'hoge_pkey' for table 'hoge'
### end createTable 1 1 ###
### insert start 1 0 ###
insert completed (1024/1024)
### end insert 7 6 ###
### start select 7 0 ###
##########
SQL command status [SELECT]
##########
tuple has 3 fields
0       id      4
1       value   4
2       txt     -1
##########
result has 2 tuples
77(2)   2146608383(10)  RANDOM134518388(15)     
962(3)  -2127616400(11) RANDOM134518388(15)     
### end select 7 0 ###
### start dropTable 7 0 ###
### end dropTable 7 0 ###
上の出力の内,showResultInfo内での出力は「start select」と「end select」の間である.
サンプルでは該当するPGresultのコマンドステータスを取得している.これには以下の関数を使用する.
char *PQcmdStatus(PGresult *res);
サンプルではスキーマ情報を取得している.以下が該当部分である.
  /* スキーマの表示 */
  fprintf (stdout, "##########\n");
  /* フィールド数 :/
  fieldNum = PQnfields (result);
  fprintf (stdout, "tuple has %d fields\n", fieldNum);
  /* フィールドインデクス,フィールド名/サイズの表示 */
  for (i = 0; i < fieldNum; i++)
    {
      fprintf (stdout, "%d\t%s\t%d\n",
	       PQfnumber (result, PQfname (result, i)), PQfname (result, i),
	       PQfsize (result, i));
    }
まず,これは完全なテーブルスキーマを得られるものではないことを注意しておく.あくまでPGresultに入っているデータである.すなわち発行したSQL文(大抵はおそらくSELECT文のターゲットリスト)に指定したものだけが入っている.サンプルではターゲットリストを*としているので,完全なテーブルスキーマが得られる.サンプルではまず以下の関数でスキーマのフィールド数を取得する.
int PQnfields(PGresult *res);
この値を使用して0から順に各フィールド情報を取得する.以下の関数は指定したPGresultの指定したインデクス番号のフィールド名を返す.
char *PQfname(PGresult *res, int field_index);
PQfnumberはPQfnameとは逆に,指定したPGresultの指定した名前のフィールドの番号を返す(※).
int PQfnumber(PGresult *res, char* field_name);
PGresult内ではフィールドがおそらく配列か何かに格納されていて,ここで言う番号とはその要素番号である.PQnfieldsが返すフィールド数は要素数ということになる.フィールドのサイズは以下の関数で取得する.
int PQfsize(PGresult *res, int field_index);
これはサーバにおけるデータのバイナリ表現のバイト数である.可変長の場合は-1が返る.サンプルの実行結果でもtxtフィールドのサイズは-1と出力された.
※ サンプルでは関数の機能からして自明の処理になっている.
PGresultに格納されているタプル数は以下の関数で取得する.
int PQntuples(PGresult *res);
サンプルではタプルがバイナリデータかどうかをチェックして,バイナリデータなら固定文字列を出力するようにした.タプルがバイナリデータを含むかどうかには以下の関数を使用する.
int PQbinaryTuples(PGresult *res);
1ならバイナリデータを含み,2ならASCIIデータである.基本的にタプルのデータはバイナリデータを含まず,含む場合というのはBINARYカーソルからデータを取り出す問い合わせだけらしい.タプルデータはタプル毎かつフィールド毎に取得する.タプル番号とフィールド番号を指定する必要がある.フィールド番号に関しては前節とおなじ要領で指定する.タプル番号はPQntuplesで取得した値をフィールドの場合と同様に使用する.サンプルではすべてのタプルの全てのフィールドを処理するためにfor文をネストしている.
      for (i = 0; i < tupleNum; i++)
	{
	  for (j = 0; j < fieldNum; j++)
	    {
指定したタプルとフィールドの値の取得には以下の関数を使用する.
char* PQgetvalue(PGresult *res, int tup_num, int field_num);
返り値は大抵NULLエンドの文字列であるが,PQbinaryTuplesの返り値が1の場合はサーバ内のバイナリフォーマットになっている.自分で正しい型にキャストする必要がある.指定したタプルとフィールドの長さを取得するには以下の関数を使用する.
int PQgetlength(PGresult *res, int tup_num, int field_num);
サンプル実行結果から察するに,PQgetvalueで返される文字列の長さであって,PQfsizeとは異なるようだ.
matsu(C)
Since 2002
Mail to matsu