Scheduler

libpq による PostgreSQL へのアクセス

xgawk の特筆するべき拡張として libpq を用いた PostgreSQL へのアクセスがあります。 従来 awk はテキストのみの処理であるため、自前で CSV, TSV などのデータベースファイルを用意する必要があり、大規模なデータ処理には不向きでした。 libpq を介して PostgreSQL にアクセスすることにより、SQL を直接的に操作することも可能であり、データベースを扱える言語になりました。

ここでは WebCalendar から入力した OSC2008 Tokyo/Spring のスケジュールを PostgreSQL から読み取り表示させるスクリプトを紹介しています。

scheduler.awk

#! /usr/local/bin/xgawk -f

@load pgsql

BEGIN {

    # settings
    host     = "localhost";
    dbname   = "webcal";
    user     = "webcal";
    password = "XXXXXX";

    now_date = strftime("%Y%m%d");
    now_hour = strftime("%H");

    # main
    conn = pg_connect("host=" host " dbname=" dbname " user=" user \
                      " password=" password);
    comm = "select eu.cal_id, eu.cal_status, e.cal_time, e.cal_date, e.cal_name from webcal_entry_user eu, webcal_entry e where eu.cal_id=e.cal_id and eu.cal_status='A';"
    if (pg_sendquery(conn, comm) > 0) {
        while ((res = pg_getresult(conn)) != "") {
            process_result(res);
            numres++;
        }
    }
    pg_disconnect(conn);

    for (i in pg_contents) {
          split(pg_contents[i], a_pg_contents, /\|/);
          if (substr(a_pg_contents[3], 1, 2) == now_hour && \
              a_pg_contents[4] ~ now_date) {
              print a_pg_contents[5];
          }
    }
}

#---------------------------------------------------------------------
# process_result - get and perform PostgreSQL contents
function process_result(res,   nf, nr, col, row, rowdata) {
    if (res ~ /^ERROR/) {
        printf "error message: %s\n", ERRNO > "/dev/stderr";
        if (res ~ /^ERROR BADCONN/) {
            print "Attempting to reset the connection." > "/dev/stderr";
            if (pg_reset(conn) < 0) {
                printf "pg_reset failed: %s\n", ERRNO > "/dev/stderr";
                exit 1;
            }
        }
        return;
    }
    if (res ~ /^OK/) {
        return;
    }
    if (res ~ /^TUPLES /) {
        nf = pg_nfields(res);
        nr = pg_ntuples(res);
        for (row = 0; row < nr; row++) {
            for (col = 0; col < nf; col++) {
                if (col > 0) {
                    pg_contents[row] = pg_contents[row] "|";
                }
                pg_contents[row] = pg_contents[row] (pg_getisnull(res,row,col) ? "<NULL>" : pg_getvalue(res,row,col));
            }
            ##print pg_contents[row];
            ##printf "\n";
        }
        pg_clear(res);
        return;
    }
    if (res ~ /^COPY_OUT /) {
        row = 0;
        while ((rowdata = pg_getcopydata(conn)) != "") {
            printf "COPY OUT row %d = %s\n", row++, rowdata;
        }
        if (ERRNO != "") {
            printf "Warning: pg_getcopydata returned error: %s\n",
                ERRNO > "/dev/stderr";
        }
        printf "COPY OUT completed after %d rows, check next result for status\n", row;
        return;
    }
    if (res ~ /^COPY_IN /) {
        row = 0;
        while (1) {
            if (getline <= 0) {
                print "unexpected EOF during COPY IN" > "/dev/stderr";
                exit 2;
            }
            if ($0 == ":COPY EOF:") {
                break;
            }
            if (pg_putcopydata(conn, ($0"\n")) < 0) {
                printf "pg_putcopydata failed: %s\n", ERRNO > "/dev/stderr";
                return;
            }
            row++;
        }
        if (pg_putcopyend(conn) < 0) {
            printf "pg_putcopyend failed: %s\n", ERRNO > "/dev/stderr";
            return;
        }
        printf "COPY IN complete for %d rows, check next result for status\n", row;
        return;
    }
    printf "unknown result type: %s\n", res > "/dev/stderr";
}

実行結果

scheduler.jpg