ResultSet

ResultSetクラス

ResultSet は DB操作の結果を保持するクラスである。 文オブジェクトが一つ所有しており、execute を行うと更新される。

ResultSet には結果を受け取るための多数の関数が定義されている:
class IResultSet: public nine::Sharable
{
   public:
      virtual Connection getConnection() = 0;
      virtual int getUpdateCount() = 0; // DDLなどカウントの無い場合は 0. エラーの場合は負。
      virtual void close() = 0;

      virtual bool next() = 0;
      virtual int getRow() = 0; // 1-base
      virtual int getColumnCount() = 0;

      virtual bool getBool(int index, bool* value) = 0; //BOOL
      virtual bool getUInt8(int index, uint8_t* value) = 0; //TINYINT
      virtual bool getInt16(int index, int16_t* value) = 0; //SMALLINT
      virtual bool getInt32(int index, int32_t* value) = 0; //INTEGER
      virtual bool getInt64(int index, int64_t* value) = 0; //BIGINT
      virtual bool getFloat32(int index, float* value) = 0; //REAL
      virtual bool getFloat64(int index, double* value) = 0; //DOUBLE
      virtual bool getString(int index,  dbi_string* value) = 0; //VARCHAR(n)
      virtual bool getDate(int index, Date* value) = 0; //DATE
      virtual bool getTime(int index, Time* value) = 0; //TIME
      virtual bool getTimestamp(int index, Timestamp* value) = 0; //TIMESTAMP

      virtual TypeWarning getLastWarning() const = 0;
      virtual bool isError() const = 0;
      virtual const char* getSqlState() const = 0;
      virtual int getDriverResultCode() const = 0;
      virtual const char* getDriverErrorMessage() const = 0;
};

select の結果は複数行から構成される。 複数行を扱うため、ResultSet は「現在行」という状態を持っている。 各結果取得関数は現在行に対して動作することになる。

現在の行番号を得るには getRow() 関数を使う。有効な行番号は 1 以降である。 初期状態は特別な 0 行目であり、この状態では値を取得することができない。

次の行へ移るには next() を使う。 戻り値が真ならば、次の行を利用可能である。

通常、プログラムは next() を繰り返しながら値を得る形になる:
  rs = stmt.executeQuery(...);
  while (rs->next()) {
    ...
  }

フィールド値取得

現在行のフィールドを得るには、各 getXXX(index, value) 関数を使う。 XXX の部分は型名であり、その型で値を受け取る。

要求型とフィールド型が異なっていた場合、可能ならば変換されるが、不可能ならばエラーとなる。 詳細は次節で説明する。

index パラメータには 1 から始まるフィールド番号を渡す。 value は各取得型のポインタであり、結果がこのポインタのオブジェクトへ格納される。
    rs = stmt.executeQuery("select now()");
    rs->next();
    nine::dbi::Timestamp ts;
    rs->getTimestamp(1, &ts);
    printf("%d-%d-%d %d:%d:%d\n", ts.getYear(), ts.getMonth(), ts.getDay(), ...);
特別な受け取り方として、getRawString() が定義されている。 これはどんな型でも、その値を文字列表現で受け取ることができる。
    nine::dbi::dbi_string s;
    rs->getRawString(1, &s);
    printf("%s\n", s.c_str());

結果を受け取る際の型変換

ResultSet の get 関数で値を受け取る場合に、データベース側の型とアプリケーション側の型が異なっていた場合、可能ならば変換される。

現在の仕様は次のようになっている。これは将来変更の可能性がある:
  • 型を次のような類に分ける:
    • 論理
    • 整数
    • 浮動小数点数
    • 時刻
    • 文字
    型類が異なる場合はエラーとする。例えば INT から REAL への変換はエラーである。
  • int64 と int32 のような、範囲部分集合の場合:
    • 範囲が拡大する方向への変換は成功
    • 範囲が縮小する方向へは
      • 値が範囲内に納まる場合は、成功
      • 値が範囲内に納まらない場合は失敗。W_OVERFLOW/W_UNDERFLOW 警告がセットされる。
  • date と timestamp のような、要素の追加・削減される場合:
    • 要素が増える方向への変換は成功。新要素の値はデフォルト値。
    • 範囲が縮小する方向への変換は成功。W_PROJECTION 警告がセットされる。
  • float と double のような、精度の変わる場合:
    • 精度が細かくなる方向への変換は成功。
    • 精度が粗くなる方向への変換は成功。W_PRECISION 警告がセットされる。
型変換時の警告は、getLastWarning() 関数で得ることができる。

エラーの取得

エラーを示すいくつかの関数が提供されている。

基本は isError() であり、これは execute の結果がエラーであるかどうかを返す。

例えば、
  rs = stmt.executeQuery("seleect * from tbl")
は、seleect という命令文が間違っているため、
  rs->isError()
は真になる。

エラーの内容は、getSqlState(), getDriverResultCode(), getDriverErrorMessage() で得られる。

getSqlState() は SQL92/SQL99 で定義された標準規格であり、各 DBMS でコード体系を使えるようになっている。ただし、具体的なエラーをどのコードに割り当てるかは各DBMS依存なので、詳細は各 DBMS のドキュメントを参照する必要がある。

DriverResultCode() は、DBMS 固有の結果コードを返し、DriverErrorMessage() は、DBMS 固有の結果メッセージを返す。