apr_dbd pgsql

apr-utilにDBDがあることに気づいたので試してみた。
Apache Portable Runtime Utility Library: DBD routines

標準では、PostgreSQL、SQLite2、SQLite3のドライバーが用意されている。
今回はPostgreSQLを少しだけ試した。


まずドライバーの取得が必須なので、その確認からした。

#include "apr_general.h"
#include "apr_file_io.h"
#include "apu.h"
#include "apr_dbd.h"

apr_file_t *apr_stdout = NULL;
apr_file_t *apr_stderr = NULL;

void initialize(apr_pool_t **pool)
{
  apr_initialize();
  apr_pool_create(pool, NULL);
  apr_file_open_stdout(&apr_stdout, *pool);
  apr_file_open_stderr(&apr_stderr, *pool);
  apr_dbd_init(*pool); // dbdの初期化が必要
}

#define DRIVER_PGSQL "pgsql" // ドライバ名

int main(void)
{
  apr_pool_t *pool;
  const char *name;
  const apr_dbd_driver_t *dbd_driver = NULL;
  apr_status_t result;
  
  initialize(&pool);
  result = apr_dbd_get_driver(pool, DRIVER_PGSQL, &dbd_driver); // ドライバー取得
  if (result != APR_SUCCESS) {
    char buf[128];
    apr_file_printf(apr_stderr, "apr_dbd_get_driver: %s\n",
                    apr_strerror(result, buf, sizeof(buf)));
    return -1;
  }
  name = apr_dbd_name(dbd_driver); // ドライバ名取得
  apr_file_printf(apr_stdout, "%s\n", name); // ドライバ名表示

  return 0;
}

実行。

[webmaster@localhost ujc]$ cat ~/bin/apr-opt 
#!/bin/sh
echo -I`apr-1-config --includedir` \
`apr-1-config --cppflags --cflags --link-ld` \
`apu-1-config --link-ld --libs`
[webmaster@localhost ujc]$ gcc pgtest1.c -o pgtest1 `apr-opt` 
[webmaster@localhost ujc]$ ./pgtest1 
pgsql
[webmaster@localhost ujc]$

使えていた。
謎なのは、PostgreSQLのドライバ名が"pgsql"だということをどうやって調べるのが正当な方法なのかよくわからないということ。今回はソースコードからひっぱってきたが、ドキュメントのどこかに一覧がないの? と思った。


しかしまあ使えればいいやーということで、SELECTを試してみた。
まずテスト用のテーブルを作った。


[webmaster@localhost ujc]$ psql -d test
Welcome to psql 8.1.3, the PostgreSQL interactive terminal.

Type: \copyright for distribution terms
\h for help with SQL commands
\? for help with psql commands
\g or terminate with semicolon to execute query
\q to quit

test=> create table test(no serial, name varchar(20));
NOTICE: CREATE TABLE will create implicit sequence "test_no_seq" for serial column "test.no"
CREATE TABLE
test=> \z
Access privileges for database "test"
Schema | Name | Type | Access privileges

                                                                                                        • -

public | test | table |
public | test_no_seq | sequence |
(2 rows)

test->
test=> insert into test(name) values('hoge');
INSERT 0 1
test=> insert into test(name) values('hage');
INSERT 0 1
test=> insert into test(name) values('fuga');
INSERT 0 1
test=> insert into test(name) values('piyo');
INSERT 0 1
test=> \q


そのテーブルにテキトウにデータを入れる。

そしてテストテーブルのすべてのデータを単純に表示するだけのコードは次のような感じ。

#include "apr_general.h"
#include "apr_file_io.h"
#include "apu.h"
#include "apr_dbd.h"

apr_file_t *apr_stdout = NULL;
apr_file_t *apr_stderr = NULL;

#define DRIVER_PGSQL "pgsql" // ドライバー名


// 接続セット
typedef struct {
  const apr_dbd_driver_t *driver;
  apr_dbd_t *handle;
  apr_pool_t *pool;
} dbd_ctx;

void initialize(apr_pool_t **pool);
void print_apr_error_message(apr_status_t status);
apr_status_t dbd_open(dbd_ctx *ctx, apr_pool_t *parent_pool,
                      const char *driver_name, const char *args);
int print_test_table(dbd_ctx * ctx);

// メイン!
int main(void)
{
  apr_pool_t *pool;
  apr_status_t result;
  dbd_ctx ctx;
  
  initialize(&pool);
  memset(&ctx, 0, sizeof(ctx));
  
  result = dbd_open(&ctx, pool, 
                    DRIVER_PGSQL, "dbname=test");
  if (result != APR_SUCCESS) {
    print_apr_error_message(result);
    return -1;
  }
  
  return print_test_table(&ctx);
}

// 初期化
void initialize(apr_pool_t **pool)
{
  apr_initialize();
  apr_pool_create(pool, NULL);
  apr_file_open_stdout(&apr_stdout, *pool);
  apr_file_open_stderr(&apr_stderr, *pool);
  // dbd
  apr_dbd_init(*pool);
}

// エラー表示
void print_apr_error_message(apr_status_t status)
{
  char buf[128];
  
  apr_file_printf(apr_stderr, "apr error: %s\n",
                  apr_strerror(status, buf, sizeof(buf)));
}
// エラー表示
void print_driver_error_message(const dbd_ctx *ctx,
                                int status)
{
  apr_file_printf(apr_stderr, "driver error: %s\n",
                  apr_dbd_error(ctx->driver, ctx->handle, status));
}

// 接続
apr_status_t dbd_open(dbd_ctx *ctx,
                      apr_pool_t *parent_pool,
                      const char *driver_name,
                      const char *args)
{
  apr_status_t result;
  
  result = apr_pool_create(&ctx->pool, parent_pool);
  if (result != APR_SUCCESS) {
    return result;
  }
  result = apr_dbd_get_driver(ctx->pool,
                              driver_name, &ctx->driver);
  if (result != APR_SUCCESS) {
    return result;
  }
  result = apr_dbd_open(ctx->driver,
                        ctx->pool, args, &ctx->handle);
  if (result != APR_SUCCESS) {
    return result;
  }
  
  return APR_SUCCESS;
}

// TESTテーブルを表示
int print_test_table(dbd_ctx *ctx)
{
  apr_pool_t *data_pool = NULL;
  apr_dbd_results_t *tableset = NULL;
  int result, rows, cols, x, y;
  
  apr_pool_create(&data_pool, ctx->pool);

  result = apr_dbd_select(ctx->driver, data_pool, ctx->handle,
                          &tableset,
                          "SELECT NO,NAME FROM TEST;",
                          1);
  if (result != 0) {
    print_driver_error_message(ctx, result);
    return -1;
  }
  rows = apr_dbd_num_tuples(ctx->driver, tableset);
  cols = apr_dbd_num_cols(ctx->driver, tableset);
  apr_file_printf(apr_stdout, "rows:%d, cols: %d\n",
                  rows,cols);
  
  for (y = 0; y < rows; ++y) {
    apr_dbd_row_t *row = NULL;
    
    result = apr_dbd_get_row(ctx->driver, data_pool,
                             tableset, &row, y);
    if (result != 0) {
      print_driver_error_message(ctx, result);
      apr_pool_destroy(data_pool);
      return -1;
    }
    if (row == NULL) {
      break;
    }
    for (x = 0; x < cols; ++x) {
      if (x != 0) {
        apr_file_printf(apr_stdout, ",");
      }
      apr_file_printf(apr_stdout, "%s",
                      apr_dbd_get_entry(ctx->driver, row, x));
    }
    apr_file_printf(apr_stdout, "\n");
  }
  apr_file_flush(apr_stdout);
  apr_pool_destroy(data_pool);

  return 0;
}

実行。

[webmaster@localhost ujc]$ gcc pgtest3.c -Wall -o pgtest3 `apr-opt`
[webmaster@localhost ujc]$ ./pgtest3
rows:4, cols: 2
1,hoge
2,hage
3,fuga
4,piyo
[webmaster@localhost ujc]$ 

C言語面倒くさすぎ、ワロタ\(^o^)/

libpqを直接使ったほうが簡単そうな気はしているけど、aprはメモリ管理が特殊なのでapr-dbdを使ったほうがいいのか……。共通インターフェースを使うメリットとしてドライバさえ差し替えれば別のDBMSが使えたりがありそうな気がしているものの、ドライバの実装に依存している箇所が多いような気がするので、案外使えないかなーとか微妙な感じ。
一度これを使って、ちょっと大きめ物を作ってみてまた評価してみようと思います。