SYSTEM238 / PROG1 / その他 / Oracelのデータをテーブル毎にダンプする

NOTES1 PROG1
Oracelのデータをテーブル毎にダンプする
Oracle7時代に作ったダンププログラム。「Cでこれくらいは作れるんですよ」と自己顕示のために公開。

概要

Oracle7全盛期の頃に作ったダンププログラムです。テーブル単位でデータをダンプします。
すでに賞味期限は切れてるけど、「Cでもこれくらいは作れるん(たん)ですよ」と宣伝するために公開します。

  • C言語 + Pro*C/C++で開発しているので軽快。
  • 間に余計なもの(ODBCとか)が入らないので勝手に精度変換されたりしない。
  • INSERT文やSQL*Loaderの制御パラメター付の出力ができる。
  • 限定的だけど検索条件やソート順を指定できる。
  • 必要最小限の機能。パラメターも少ないので操作が簡単。CUIなので一括実行が簡単にできる。

動作環境&設定

動作環境

  • Oracle7.X以降
  • Proc*C/C++がコンパイルできる環境
  • Unixまたはそれに準拠したCUI環境

設定

  • Proc*C/C++がコンパイルできる環境でコンパイルしてください。
  • 以下の環境変数を設定します。
    FKDMP_UID=ユーザID
    FKDMP_PWD=パスワード
    FKDMP_SID=システムID
    (環境変数で設定しなくても実行時にコマンド引数で設定可能です)

使用方法

fk_dump [-h] [-l] [-i] [-wXXXX] [-uXXXX] [-pXXXX] [-sXXXX] TableName
         -h : ヘッダー付で出力する *1
         -l : SQL*Loaderの制御パラメター付で出力する *1
         -i : INSERT文付で出力する *1
         -w : where句             (XXXXで条件を指定)
         -o : order by句          (XXXXで項目を指定)
         -u : OracleのユーザID  (XXXXで指定)
         -p : Oracleのパスワード  (XXXXで指定)
         -s : OracleのシステムID(XXXXで指定)
  TableName : ダンプするテーブル

*1:排他的オプション(同時に指定した場合は最後に指定したオプションが有効になる)

-h : ヘッダー付で出力する
  ・出力されたCSV形式データの先頭行にヘッダーが付加される。

-l : SQL*Loaderの制御パラメター付で出力する
  ・出力データにSQL*Loaderのパラメターが付加される。SQL*Loaderの入力データと
   して使用できる。

-i : INSERT文付で出力する
  ・出力データにINSERT文が付加される。SQL*Plusやその他のツールの入力データと
   して使用できる。

-w : where句
  ・出力データに条件を設定してレコードの絞り込みを行うときに使用する。

-o : order by句
  ・出力データに条件を設定してレコードの並び替えを行うときに使用する。

-u : OracleのユーザID
  ・環境変数に設定されたユーザID以外でログインする場合に使用する。

-p : Oracleのパスワード
  ・環境変数に設定されたパスワード以外でログインする場合に使用する。

-s : OracleのSID
  ・環境変数に設定されたSID以外でログインする場合に使用する。

TableName : ダンプするテーブル
  ・出力対象のテーブルを設定する。

使用例

前提条件

出力するテーブルTEST_TBL
テーブルの項目T_CLUM01〜05
出力ファイル(CSV)test_tbl.csv
出力ファイル(Insert)test_tbl.sql
出力ファイル(SQL*Loader)test_tbl.ctl
ユーザ名USER1
パスワードUSER1PAS
システムIDORA7T

使用例

・簡易ヘルプを出力する
  fk_dump[Enter]

  *パラメターを間違えた場合にも出力されます。

・テーブル内のデータをCSV形式で画面に出力する
  fk_dump TEST_TBL[Enter]

・テーブル内のデータをCSV形式でファイルに出力する
  fk_dump TEST_TBL > test_tbl.csv[Enter]

・環境変数で設定されたデフォルトのユーザ以外でデータを出力する
  fk_dump -uUSER1 -pUSER1PAS -sORA7T TEST_TBL[Enter]

  *ユーザ名、パスワード、システムIDはそれぞれ省略する事もできます。

・条件に合致したデータのみを出力する(簡単な検索)
  fk_dump -wT_CLUM01=001 TEST_TBL[Enter]

・条件に合致したデータのみを出力する(ちょっと複雑な検索)
  fk_dump -w"T_CLUM03 like '0%'" TEST_TBL[Enter]

  *条件の展開はシェルのルールに従います。上記はCシェルでの場合
  「T_CLUM01 like '0%'」と展開されます。

・レコードの並び替えを行う
  fk_dump -oT_CLUM01,T_CLUM02 TEST_TBL[Enter]

・ヘッダー付で出力する
  fk_dump -h TEST_TBL[Enter]

・INSERT文付でファイルに出力する
  fk_dump -i TEST_TBL > test_tbl.sql[Enter]

  *ロードする場合は出力されたデータをSQLの実行できる適当なツールで読込んで使用してください。
   例) SQL*Plusを起動後、@test_tbl[Enter]で実行する。

・SQL*Loaderの制御パラメター付でファイルに出力する
  fk_dump -l TEST_TBL > test_tbl.ctl

  *ロードする場合は以下のコマンドシンタックスで読込めます。
   例) sqlldr control=test_tbl.ctl log=test_tbl.log

出力されるデータについて

  • 本ツール内では以下のようなSQL文を発行してデータを取得しています。
    SELECT ''''||項目1||''''||','||項目2||','||''''項目3||'''' from テーブル名
  • 上記の場合、出力データは以下のようになります。
    'AAAA',100,'99-08-24'
    'BB',.234,'99-08-31' ←数値が「0.234」だと「.234」となる
    'bb',, ←NULL値は「,」が連続する。(文字列の場合は「''」となる)
  • 「'」で出力データを囲むのは以下のデータ形式です。それ以外は囲みません。
    ・型にCHARが含まれている。(例:CHAR、VARCHAR2)
    ・型にDATEが含まれている。(例:DATE)
    *これ以外にも「'」で囲む必要がある場合はソースの「enc_name()」関数に条件を追加してください。
  • 通常のCSVと違い文字列は「'」で囲んでいます。(通常は「"」) これはINSERT文で入力する際の文字列指定を優先したためです。
  • 日付の出力形式はOracleインストール時の標準形式になります。システム間でデータ移行する際は注意してください。
  • 日付フォーマットが違う場合は以下のようにしてフォーマット変換をかけます。
    ・INSERT文付の場合:TO_DATE()関数を出力データに追加する。(行が多いと大変)
    ・SQL*Loaderの場合:日付マスクを先頭部分の制御パラメターに追加する。
  • Insert文付の出力データには数値項目のNULL値には'NULL'がセットされています。
    'AAAA',100,'99-08-24'
    'BB',.234,'99-08-31' ←数値が「0.234」だと「.234」となる
    'bb',NULL,'' ←NULL値は「,NULL」となる。(文字列の場合は「''」)
  • SQL*Loader制御パラメター付の出力データは全ての項目が「''」で囲まれています。
    'AAAA','100','99-08-24'
    'BB','.234','99-08-31' ←数値が「0.234」だと「'.234'」となる
    'bb','','' ←NULL値は「''」となる。
  • 出力データの数値精度はSQL*Plus等で「Select * from テーブル名」として表示されたものと同じです。

ソース

/**
 * 指定したテーブルの内容をダンプするアプリケーション
 * Insert句付やLoaderのパラメターも出力できてとってもお得
 */
/*
  Oracleからの戻りデータ長が「ORA_REC_LEN」の限界を超えたときのデバッグはやっていない。
  取り敢えず「ORA_REC_LEN」を十分に取っていれば問題なしと判断
*/
#include <stdio.h>
#include <string.h>

EXEC SQL INCLUDE sqlca.h;

/* -- Define定義 -- */
#define ORA_COLUMN_LEN  30    /* テーブル名や項目の長さ */
#define ORA_NA_PS_LEN   16    /* OracleのID&PWD&SIDの長さ */
#define ORA_TYPE_LEN    10    /* 型(CHARなど)の名前の長さ */
#define ORA_REC_LEN     1000  /* レコード長 */
#define MAX_COLUMNS     100   /* テーブル内のカラム数 */
#define MAX_WHERE       250   /* Where句の条件長 */
#define MAX_ORDER       100   /* OrderBy句の条件長 */
#define ENV_LEN         30    /* 環境変数名の長さ */

#define ENV_HEAD     "FKDMP_"         /* 環境変数の接頭語 */
#define FILL_CHAR    "###__###__###"  /* 埋め文字 */
#define CSV_TERM     "||','||"        /* 区切り文字 */
#define CSV_F_ENC    "''''||"         /* 囲み文字(前) */
#define CSV_E_ENC    "||''''"         /* 囲み文字(後) */
#define F_HEAD       'H'              /* ヘッダー出力 */
#define F_CTRL       'C'              /* 制御ファイル付出力 */
#define F_INST       'A'              /* INSERT文付出力 */
#define F_CSV        ' '              /* CSV */
#define N_END        0                /* NORMAL END */
#define W_END        1                /* WARNING END */
#define A_END        -1               /* ABNORMAL END */
#define A_END_CA     "* ERR *"        /* ABNORMAL END(Char Array) */
#define ORA_NO_DATA  1403             /* 対象データなし */
/**
 * ・囲み文字が「"」でなく「'」になっているのはDBへのレコードInsertを優先したため。
 * ・埋め文字の文字列長は「ORA_COLUMN_LEN」や「ORA_NA_PS_LEN」よりも小さい事。
 */


/* -- 構造体宣言 -- */
struct TabClm{ /* カラム情報格納用 */
  char caName[ORA_COLUMN_LEN + 1];
  char caType[ORA_TYPE_LEN + 1];
};

struct OraLogin{ /* Oracleログイン情報 */
  char caUID[ORA_NA_PS_LEN +1];
  char caPWD[ORA_NA_PS_LEN +1];
  char caSID[ORA_NA_PS_LEN +1];
};

/* -- プロトタイプ宣言 -- */
static int    param_check(int argc, char **argv); /* パラメターチェック */
static int    db_connect(void);                   /* Oracle接続 */
static int    tbl_data_get(void);                 /* テーブル情報の取得 */
static void   header_output(void);                /* ヘッダー出力 */
static int    data_output(char);                  /* データ出力 */
static void   ctrl_output(void);                  /* Loaderパラメター */
static void   help_msg(void);                     /* Helpの表示 */
static char  *fkdp_getenv(char *pcaEnvName);      /* 環境変数の読込 */
static char  *enc_name(int piIDX, char pcFlg);    /* 項目名を " で囲む */
static int    set_null(char *pcaData);            /* 「NULL」をセット */

/* -- グローバル変数 -- */
char   gcFlg;                             /* 処理区分 */
char   gcaTableName[ORA_COLUMN_LEN + 1];  /* テーブル名称 */
char   gcaWhere[MAX_WHERE + 1];           /* Where条件 */
char   gcaOrder[MAX_ORDER + 1];           /* Order By条件 */
struct TabClm   gClm[MAX_COLUMNS];        /* カラム情報 */
struct OraLogin gOra;                     /* ログイン情報 */

/**
 * メイン
 */
int
main(int argc, char **argv)
{
  int  liRetCD;  /* リターンコード */
  
  /* -- 引数の解析とグローバル変数の初期化 -- */
  liRetCD = param_check(argc, argv);
  if (liRetCD != N_END) exit(liRetCD);
  
  /* -- Oracle接続 -- */
  liRetCD = db_connect();
  if (liRetCD != N_END) exit(liRetCD);
  
  /* -- テーブル情報の取得 -- */
  liRetCD = tbl_data_get();
  if (liRetCD != N_END) goto ABNORMAL_END;
  
  /* -- メインの処理 -- */
  switch (gcFlg){
    case F_HEAD:  /* テーブルヘッダー出力 */
      header_output();
      liRetCD = data_output(F_HEAD);
      break;
    case F_CTRL:  /* 制御ファイル出力 */
      ctrl_output();
      liRetCD = data_output(F_CTRL);
      break;
    case F_INST:  /* INSERT文出力 */
      liRetCD = data_output(F_INST);
      break;
    default:  /* データ出力(CSV) */
      liRetCD = data_output(F_CSV);
      if (liRetCD != N_END) goto ABNORMAL_END;
      break;
  } /* end switch */
  
  if (liRetCD != N_END) goto ABNORMAL_END;
  
  /* -- 終了 -- */
  EXEC SQL  COMMIT RELEASE;
  if(sqlca.sqlcode != 0){
    fprintf(stderr, "終了処理に失敗しました\n");
    goto ABNORMAL_END;
  }
  
  exit(liRetCD);
  
  
ABNORMAL_END:
  /**
   * 異常終了
   *   実際にはDBの内容は変更しないのでCOMMITだろうがROLLBACKだろうが関係無い
   */
  EXEC SQL  ROLLBACK RELEASE;
}


/**
 * パラメターのチェックとグローバル変数へのセット
 */
int
param_check(int argc, char **argv)
{
  /* -- 引数が何も設定されていない場合はヘルプを表示して終了 -- */
  if (argc == 1) {
    help_msg();
    return(W_END);
  }
  
  /* -- グローバル変数の初期化 -- */
  gcFlg = NULL;                                     /* 処理区分 */
  memset(gcaTableName, NULL, sizeof(gcaTableName)); /* テーブル名称 */
  memset(gcaWhere,     NULL, sizeof(gcaWhere));     /* Where条件 */
  memset(gcaOrder,     NULL, sizeof(gcaOrder));     /* Order条件 */
  strcpy(gClm[0].caName,  FILL_CHAR);               /* カラム情報(名) */
  strcpy(gOra.caUID,      FILL_CHAR);               /* Oracle ID */
  strcpy(gOra.caPWD,      FILL_CHAR);               /* Oracle Password */
  strcpy(gOra.caSID,      FILL_CHAR);               /* Oracle SID */
  
  /* -- 引数(オプション)の解析 -- */
  while ((argc > 1) && (argv[1][0] == '-')) {
    switch (argv[1][1]){  /* argv[1][1]がオプション文字 */
      case 'h':  /* ヘッダー付出力 */
        gcFlg = F_HEAD;
        break;
      case 'l':  /* 制御ファイル付出力 */
        gcFlg = F_CTRL;
        break;
      case 'i':  /* INSERT文付出力 */
        gcFlg = F_INST;
        break;
      case 'u':  /* OracleユーザID */
        strcpy(gOra.caUID, &argv[1][2]);
        break;
      case 'p':  /* Oracleパスワード */
        if(strlen(&argv[1][2]) > ORA_NA_PS_LEN){
          fprintf(stderr, "パスワードの長さが不正です。(%dByte以下)\n", ORA_NA_PS_LEN);
          return(A_END);
        }
        strcpy(gOra.caPWD, &argv[1][2]);
        break;
      case 's':  /* Oracle SID */
        if(strlen(&argv[1][2]) > ORA_NA_PS_LEN){
          fprintf(stderr, "SIDの長さが不正です。(%dByte以下)\n", ORA_NA_PS_LEN);
          return(A_END);
        }
        strcpy(gOra.caSID, &argv[1][2]);
        break;
      case 'w':  /* Where句 */
        if(strlen(&argv[1][2]) > MAX_WHERE){
          fprintf(stderr, "Where句の条件長が不正です。(%dByte以下)\n", MAX_WHERE);
          return(A_END);
        }
        strcpy(gcaWhere, &argv[1][2]);
        break;
      case 'o':  /* Order BY句 */
        if(strlen(&argv[1][2]) > MAX_ORDER){
          fprintf(stderr, "Order By句の条件長が不正です。(%dByte以下)\n", MAX_ORDER);
          return(A_END);
        }
        strcpy(gcaOrder, &argv[1][2]);
        break;
      default:  /* 引数ミス */
        fprintf(stderr, "パラメターが間違っています\n");
        help_msg();
        return(W_END);
    } /* end switch */
    
    argv++;
    argc--;
  } /* end while */
  
  /* -- 引数で設定されていない場合は環境変数から取得 -- */
  if (strcmp(gOra.caUID, FILL_CHAR) == 0){  /* UserID */
    strcpy(gOra.caUID, fkdp_getenv("UID"));
    if (strcmp(gOra.caUID, A_END_CA) == 0) return(A_END);
  }
  if (strcmp(gOra.caPWD, FILL_CHAR) == 0){  /* Password */
    strcpy(gOra.caPWD, fkdp_getenv("PWD"));
    if (strcmp(gOra.caPWD, A_END_CA) == 0) return(A_END);
  }
  if (strcmp(gOra.caSID, FILL_CHAR) == 0){  /* SID */
    strcpy(gOra.caSID, fkdp_getenv("SID"));
    if (strcmp(gOra.caSID, A_END_CA) == 0) return(A_END);
  }
  
  /* -- テーブル名の取得 -- */
  if (argc == 2) {
    strcpy(gcaTableName, argv[1]);
    return(N_END);
  } else {
    fprintf(stderr, "パラメターが間違っています\n");
    help_msg();
    return(W_END);
  }
}


/**
 * Oracle接続処理
 */
int
db_connect(void)
{
  /* -- ホスト変数定義 -- */
  EXEC SQL BEGIN DECLARE SECTION;
    VARCHAR  lvUID[ORA_NA_PS_LEN + 1];  /* ユーザID用 */
    VARCHAR  lvPWD[ORA_NA_PS_LEN + 1];  /* パスワード */
    VARCHAR  lvSID[ORA_NA_PS_LEN + 1];  /* システム識別子 */
  EXEC SQL END DECLARE SECTION;
  
  /* -- ログイン情報の設定 -- */
  memset(lvUID.arr, NULL, sizeof(lvUID.arr));  /* クリア */
  memset(lvPWD.arr, NULL, sizeof(lvPWD.arr));
  memset(lvSID.arr, NULL, sizeof(lvSID.arr));
  
  strcpy(lvUID.arr, gOra.caUID);  /* 設定(値) */
  strcpy(lvPWD.arr, gOra.caPWD);
  strcpy(lvSID.arr, gOra.caSID);
  lvUID.len = strlen(lvUID.arr);  /* 設定(長さ) */
  lvPWD.len = strlen(lvPWD.arr);
  lvSID.len = strlen(lvSID.arr);
  
  /* -- ログイン -- */
  EXEC SQL  CONNECT :lvUID  IDENTIFIED BY :lvPWD  USING :lvSID;
  if(sqlca.sqlcode != 0){
    fprintf(stderr, "Oracleのログインに失敗しました\n");
    fprintf(stderr, "  ->UID:%s、SID:%s\n", gOra.caUID, gOra.caSID);
    fprintf(stderr, "    CD(%d):%s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);
    return(A_END);
  }
  
  
  return(N_END);
}


/**
 * テーブル情報の取得
 */
int
tbl_data_get(void)
{
  int  liA = 0;  /* カウンタ */
  EXEC SQL BEGIN DECLARE SECTION;
    VARCHAR  lvName[ORA_COLUMN_LEN + 1];  /* カラム名 */
    VARCHAR  lvType[ORA_TYPE_LEN + 1];    /* 型 */
  EXEC SQL END DECLARE SECTION;
  
  /* -- カーソル定義〜オープン -- */
  EXEC SQL DECLARE lcrTabClm CURSOR FOR
    select COLUMN_NAME, DATA_TYPE from USER_TAB_COLUMNS
      where TABLE_NAME = upper(:gcaTableName);
  
  EXEC SQL OPEN lcrTabClm;
  if(sqlca.sqlcode != 0){
    fprintf(stderr, "テーブル情報の取得に失敗しました\n");
    fprintf(stderr, "  ->TableName:%s\n", gcaTableName);
    fprintf(stderr, "    CD(%d):%s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);
    return(A_END);
  }
  
  /* -- テーブル情報の読み出し -- */
  EXEC SQL FETCH lcrTabClm INTO :lvName, :lvType;
  if (sqlca.sqlcode != 0){
    fprintf(stderr, "テーブル情報の取得に失敗しました\n");
    fprintf(stderr, "  ->TableName:%s\n", gcaTableName);
    fprintf(stderr, "    コメント :ここでの「データがありません」はたぶん「テーブル名が不正」の事です。\n");
    fprintf(stderr, "    CD(%d):%s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);
    return(A_END);
  }
  
  while (sqlca.sqlcode == 0){
    lvName.arr[lvName.len] = NULL;  /* 行末処理 */
    lvType.arr[lvType.len] = NULL;
    strcpy(gClm[liA].caName, lvName.arr);  /* グローバル変数設定 */
    strcpy(gClm[liA].caType, lvType.arr);
    
    EXEC SQL FETCH lcrTabClm INTO :lvName, :lvType;
    liA++;
  }
  
  strcpy(gClm[liA].caName, FILL_CHAR);  /* End Of Record */
  
  /* -- カーソルクローズ -- */
  EXEC SQL CLOSE lcrTabClm;
  
  
  return(N_END);
}


/**
 * ヘッダー出力(-hオプション)
 */
void
header_output(void)
{
  int   liA = 0;  /* カウンタ */
  char  lcaHeader[(ORA_COLUMN_LEN + 2) * MAX_COLUMNS + 1]; /* ヘッダー文字列 */
  
  /* -- カンマ区切り文字列を生成〜出力 -- */
  strcpy(lcaHeader, "'");
  strcat(lcaHeader, gClm[liA].caName);
  strcat(lcaHeader, "'");
  liA++;
  
  while(strcmp(gClm[liA].caName, FILL_CHAR) != 0){
    strcat(lcaHeader, ",");
    strcat(lcaHeader, "'");
    strcat(lcaHeader, gClm[liA].caName);
    strcat(lcaHeader, "'");
    liA++;
  }
  
  printf("%s\n", lcaHeader);  /* 出力 */
}


/**
 * データ出力
 *   F_CSV :「,」と「"文字列 or 日付"」のCSV
 *   F_INST:上記に数値項目でNull値の時に「NULL」をセット
 *   F_CTRL:全ての項目を「""」で囲む
 *   F_HEAD:F_CSVと同じ
 */
int
data_output(char pFlg)
{
  int  liRetCD;  /* リターンコード */
  int  liA = 0;  /* カウンタ */
  
  /* SELECT文字列  */
  char lcaSQL[(ORA_COLUMN_LEN + 9) * MAX_COLUMNS + MAX_WHERE + 1];
  EXEC SQL BEGIN DECLARE SECTION;
    VARCHAR  lvData[ORA_REC_LEN + 1];  /* 出力データ */
  EXEC SQL END DECLARE SECTION;
  
  /*
   * select文字列の生成
   *   F_CTRL:全部の項目を「""」で囲む
   *   その他:文字、日付項目を「""」で囲む
   * 
   * 関数「enc_name()」は型やフラグを見て項目を「""」で囲むか、そのまま戻す関数
   */
  memset(lcaSQL, NULL, sizeof(lcaSQL));  /* クリア */
  
  strcpy(lcaSQL, "select ");             /* Select */
  strcat(lcaSQL, enc_name(liA, pFlg));   /* 最初の項目 */
  liA++;
  
  while(strcmp(gClm[liA].caName, FILL_CHAR) != 0){  /* 2番目以降の項目 */
    strcat(lcaSQL, CSV_TERM);
    strcat(lcaSQL, enc_name(liA, pFlg));
    liA++;
  }
  
  strcat(lcaSQL, " from ");      /* From */
  strcat(lcaSQL, gcaTableName);  /* テーブル名 */
  
  if(strlen(gcaWhere) > 0){      /* Where句(あったら設定) */
    strcat(lcaSQL, " where ");
    strcat(lcaSQL, gcaWhere);    /* 条件 */
  }
  
  if(strlen(gcaOrder) > 0){      /* Order句(あったら設定) */
    strcat(lcaSQL, " order by ");
    strcat(lcaSQL, gcaOrder);    /* 条件 */
  }
  
  /* -- 動的SQLの生成〜カーソル生成〜オープン -- */
  EXEC SQL PREPARE lpSQL FROM :lcaSQL;      /* 動的SQLの生成 */
  EXEC SQL DECLARE lcrSQL CURSOR FOR lpSQL; /* カーソル生成 */
  EXEC SQL OPEN    lcrSQL;                  /* オープン */
  if(sqlca.sqlcode != 0){
    fprintf(stderr, "カーソルのオープンに失敗しました\n");
    fprintf(stderr, "  ->TableName:%s\n", gcaTableName);
    fprintf(stderr, "    CD(%d):%s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);
    fprintf(stderr, "    SQL      :%s\n", lcaSQL);
    return(A_END);
  }
  
  /* -- データ出力 -- */
  while(1)
  {
    EXEC SQL FETCH lcrSQL INTO :lvData;  /* データの読込 */
    if(sqlca.sqlcode == ORA_NO_DATA){    /* データなし */
      break;
    }else if(sqlca.sqlcode != 0){        /* その他のエラー */
      fprintf(stderr, "データのリード中にエラーが発生しました\n");
      fprintf(stderr, "  ->TableName:%s\n", gcaTableName);
      fprintf(stderr, "    CD(%d):%s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);
      return(A_END);
    }
    
    if (lvData.len > ORA_REC_LEN){
      fprintf(stderr, "レコードの長さがバッファ長を超えました\n");
      fprintf(stderr, "  ->TableName:%s\n", gcaTableName);
      fprintf(stderr, "    RecLen(%d):BufLen(%d)\n", lvData.len, ORA_REC_LEN);
      return(A_END);
    }else{
      lvData.arr[lvData.len] = NULL;     /* 行末処理 */
      
      if (pFlg == F_INST){               /* 出力(INSERT付) */
        liRetCD = set_null(lvData.arr);  /* NULLデータ処理 */
        if (liRetCD != N_END) return(A_END);
        printf("INSERT INTO %s VALUES(%s);\n", gcaTableName, lvData.arr);
      }else{                             /* 出力(CSV) */
        printf("%s\n", lvData.arr);
      }
    }
  } /* End While */
  
  /* -- カーソルクローズ -- */
  EXEC SQL CLOSE lcrSQL;
  
  
  return(N_END);
}


/**
 * Loaderパラメターの生成
 */
void
ctrl_output(void)
{
  int    liA = 0;  /* カウンタ */

  /*
   * load data
   * infile *
   * replace
   * into table テーブル名
   * fields terminated by "," optionally enclosed by '\''
   * (
   */
  printf("load data\n");
  printf("infile *\n");
  printf("replace\n");
  printf("into table %s\n", gcaTableName);
  printf("fields terminated by \",\" optionally enclosed by '\\''\n");
  printf("(\n");
  
  /*
   *  項目名,
   *  項目名,
   *   :,
   */
  printf("  %s", gClm[liA].caName);
  liA++;
  while(strcmp(gClm[liA].caName, FILL_CHAR) != 0){
    printf(",\n");
    printf("  %s", gClm[liA].caName);
    liA++;
  }
  
  /*
   * )
   * begindata
   */
  printf("\n) \n");
  printf("begindata\n");
}


/**
 * ヘルプの出力:引数が間違ったときや何も設定されなかったときに出力する
 */
void
help_msg(void)
{
  fprintf(stderr, "使用法 : fk_dump [-h] [-l] [-i] [-wXXXX] [-uXXXX] [-pXXXX] [-sXXXX] TableName\n");
  fprintf(stderr, "    -h : ヘッダー付で出力する *1\n");
  fprintf(stderr, "    -l : SQL*Loaderの制御パラメター付で出力する *1\n");
  fprintf(stderr, "    -i : INSERT文付で出力する *1\n");
  fprintf(stderr, "    -w : where句           (XXXXで条件を指定)\n");
  fprintf(stderr, "    -o : Order By句        (XXXXで項目を指定)\n");
  fprintf(stderr, "    -u : OracleのユーザID  (XXXXで指定)\n");
  fprintf(stderr, "    -p : Oracleのパスワード(XXXXで指定)\n");
  fprintf(stderr, "    -s : OracleのSID       (XXXXで指定)\n");
  fprintf(stderr, "  TableName : ダンプするテーブル\n\n");
  fprintf(stderr, "    *1:排他的オプションです。(同時指定の場合は最後のオプションが有効です)\n\n");
  fprintf(stderr, "現在このツールは...\n");
  fprintf(stderr, "  名称(テーブル名や項目)の長さ  :%d Byte\n", ORA_COLUMN_LEN);
  fprintf(stderr, "  1テーブル中の項目数          :%d 項目\n", MAX_COLUMNS);
  fprintf(stderr, "  ユーザ名,パスワード,SIDの長さ :%d Byte\n", ORA_NA_PS_LEN);
  fprintf(stderr, "  1レコードの長さ(全項目長の和):%d Byte\n", ORA_REC_LEN);
  fprintf(stderr, "  Where句の長さ                 :%d Byte\n", MAX_WHERE);
  fprintf(stderr, "  Order By句の長さ              :%d Byte\n", MAX_ORDER);
  fprintf(stderr, "まで対応しています。これ以上になる場合はプログラムを修正する必要があります。\n\n");
}


/**
 * 環境変数取得処理
 */
char
*fkdp_getenv(char *pcaEnvName)
{
  char lcaEnvName[ENV_LEN +1];  /* 環境変数の名前 */
  char *lcpEnvValue;            /* 環境変数の値 */
  
  /* -- 環境変数名を生成 〜 対応する値を取得 -- */
  strcpy(lcaEnvName, ENV_HEAD);
  strcat(lcaEnvName, pcaEnvName);
  
  lcpEnvValue = getenv(lcaEnvName);
  if (lcpEnvValue == NULL){
    fprintf(stderr, "環境変数「%s」の取得に失敗しました\n", lcaEnvName);
    fprintf(stderr, "環境変数を設定するか起動引数を指定してください\n");
    return(A_END_CA);
  }
  
  return(lcpEnvValue);
}


/**
 * Select時の項目名を必要に応じて「"」で囲む
 */
char
*enc_name(int piIDX, char pcFlg)
{
  char  lcaColumn[ORA_COLUMN_LEN + (6 * 2)];  /* Plus分は前後の囲み文字 */
  
  memset(lcaColumn, NULL, sizeof(lcaColumn));  /* クリア */
  
  /*
   * 項目のタイプを判定
   *  ・タイプ文字列中に「CHAR」と「DATE」があれば囲む
   *  ・フラグが「F_CTRL」だったら囲む
   */
  if((strstr(gClm[piIDX].caType, "CHAR") != NULL) ||
     (strstr(gClm[piIDX].caType, "DATE") != NULL) ||
     (pcFlg                              == F_CTRL)){
    strcpy(lcaColumn, CSV_F_ENC);
    strcat(lcaColumn, gClm[piIDX].caName);  /* 囲んで戻す */
    strcat(lcaColumn, CSV_E_ENC);
  }else{
    strcpy(lcaColumn, gClm[piIDX].caName);  /* そのまま戻す */
  }
  
  return(lcaColumn);
}


/**
 * 「""」で囲まれていないNullデータに対し「NULL」文字列をセットしInsert時にエラーが出ないようにする
 */
int
set_null(char *pcaData)
{
  int  liLenP;                    /* 変換前文字列長 */
  int  liCtP = 0;                 /* 変換前文字列のポインタ */
  int  liCtL = 0;                 /* 変換後文字列のポインタ */
  char lcaData[ORA_REC_LEN + 1];  /* 変換後文字列 */
  
  /* -- 初期処理 -- */
  memset(lcaData, NULL, sizeof(lcaData));  /* クリア */
  liLenP = strlen(pcaData) - 1;            /* 文字列長 */
  
  /*
   * 先頭文字が「,」は「NULL,」にする
   * 途中の「,,」は「,NULL,」にする
   * 最後の「,\0」は「,NULL\0」にする
   */
  if(pcaData[0] == ','){  /* 先頭 */
    memcpy(lcaData, "NULL", 4);
    liCtL = liCtL + 4;
  }
  
  while(liLenP >= liCtP){  /* 途中 */
    if(memcmp((pcaData + liCtP), ",,", 2) == 0){
      memcpy((lcaData + liCtL), ",NULL", 5);
      liCtP++;
      liCtL = liCtL + 5;
    }else{
      memcpy((lcaData + liCtL), (pcaData + liCtP), 1);
      liCtL++;
      liCtP++;
    }
    
    if((liCtL + (liLenP - liCtP) + 4) > ORA_REC_LEN){
      fprintf(stderr, "レコードの長さがバッファ長を超えました\n");
      fprintf(stderr, "  ->RecLen(%d):BufLen(%d)\n", (liCtL + (liLenP - liCtP)), ORA_REC_LEN);
      return(A_END);
    }
  }
  
  if(pcaData[liCtP - 1] == ','){  /* 最後 */
    memcpy((lcaData + liCtL), "NULL", 4);
    liCtL = liCtL + 4;
  }
  
  /* -- 値のコピー -- */
  lcaData[liCtL] = NULL;
  strcpy(pcaData, lcaData);
  
  return(N_END);
}

このプログラムについて

派遣社員をやってた頃に作ったプログラムです。(ちゃんと業務時間外に作りましたよ)

プロジェクトメンバーに公開したら意外と好評で、検索条件やソート順はメンバーの要望によって機能追加しました。契約が切れて離職する頃には全社で使ってました。

そのうち「このツールでダンプしたテキストデータを加工してデータベースに書き戻す」なんていう強者が出てきたりして、「性能がでないのはこのツールが悪い!!」という斜め上の評価を貰ったりしたのも今となってはいい思い出です。

カラム指定が出来るように機能拡張するつもりだったのですが、その後はIBMのDB2だったり、オープンソース系のデータベースに移ったりでOracleを触る事が少なくなったので実現できませんでした。(本当はPro*C/C++の開発環境を作れなかった)

言語もこのプロジェクト以降はJavaだったりPHPだったりでCからは離れちゃったし。

このツールはもう過去の遺物ですね。完全に。

そうそう、過去の遺物といえばCOBOLで作った類似のツールもあったりします。某汎用機のデータベース用で二度と出くわすことのない環境なんですが、ソースはまだ持ってたりします。保管メディアが8インチと5インチのFDDなので読み出すことができませんが...