Enable SQLBulkOperations in declare/fetch mode using long (>=12bytes) bookmarks.
authorHiroshi Inoue <h-inoue@dream.email.ne.jp>
Mon, 23 May 2016 05:52:50 +0000 (14:52 +0900)
committerHiroshi Inoue <h-inoue@dream.email.ne.jp>
Sat, 21 Jan 2017 08:15:01 +0000 (17:15 +0900)
Formerly the driver offered very simple bookmark support -- it is just
the current row number. Now the driver offers more verbose bookmarks which
contain KeySet informations (CTID (+ OID)). Though they consume 12bytes
(row number + CTID) or 16bytes (row number + CTID + OID), they are useful
in declare/fetch mode.

connection.c
options.c
pgapi30.c
qresult.h
results.c
statement.c
statement.h
test/src/bulkoperations-test.c
tuple.h

index f1389497f1f92b994f0aabbcb5a3bf43203600ca..b65d338de39243bff76de210b6e8e0ff01779ac0 100644 (file)
@@ -1025,7 +1025,7 @@ CC_connect(ConnectionClass *self, char *salt_para)
        if (ci->drivers.lie || !ci->drivers.use_declarefetch)
            ci->updatable_cursors |= (ALLOW_STATIC_CURSORS | ALLOW_KEYSET_DRIVEN_CURSORS | ALLOW_BULK_OPERATIONS | SENSE_SELF_OPERATIONS);
        else
-           ci->updatable_cursors |= (ALLOW_STATIC_CURSORS | SENSE_SELF_OPERATIONS);
+           ci->updatable_cursors |= (ALLOW_STATIC_CURSORS | ALLOW_BULK_OPERATIONS | SENSE_SELF_OPERATIONS);
    }
 
    if (CC_get_errornumber(self) > 0)
index fa4244df57743cbbbed5ac194958c5421a094e9f..3c03e0ecd5217e03f95443113854ae938414d297 100644 (file)
--- a/options.c
+++ b/options.c
@@ -647,7 +647,7 @@ PGAPI_GetStmtOption(HSTMT hstmt,
    QResultClass *res;
    SQLLEN      ridx;
    SQLINTEGER  len = sizeof(SQLINTEGER);
-   UInt4       bookmark;
+   Int4        bookmark;
 
    mylog("%s: entering...\n", func);
 
@@ -700,7 +700,7 @@ PGAPI_GetStmtOption(HSTMT hstmt,
                return SQL_ERROR;
            }
 
-           bookmark = (UInt4) SC_get_bookmark(stmt);
+           bookmark = SC_make_int4_bookmark(stmt->currTuple);
            memcpy(pvParam, &bookmark, sizeof(UInt4));
 
            break;
index b8620f15054fd4e1bbb27eee14dad981b7c0ebe7..8c045c8be69b0191c4a3517c86b83fd8050cad5b 100644 (file)
--- a/pgapi30.c
+++ b/pgapi30.c
@@ -1956,6 +1956,7 @@ RETCODE   bulk_ope_callback(RETCODE retcode, void *para)
    QResultClass    *res;
    IRDFields   *irdflds;
    BindInfoClass   *bookmark;
+   PG_BM       pg_bm;
 
    if (s->need_data_callback)
    {
@@ -1971,13 +1972,14 @@ RETCODE bulk_ope_callback(RETCODE retcode, void *para)
    bookmark = s->opts->bookmark;
    offset = s->opts->row_offset_ptr ? *(s->opts->row_offset_ptr) : 0;
    bind_size = s->opts->bind_size;
+   res = SC_get_Curres(s->stmt);
    for (; SQL_ERROR != ret && s->idx < s->opts->size_of_rowset; s->idx++)
    {
        if (SQL_ADD != s->operation)
        {
-           UInt4 tmp;
-           memcpy(&tmp, CALC_BOOKMARK_ADDR(bookmark, offset, bind_size, s->idx), sizeof(UInt4));
-           global_idx = SC_resolve_bookmark(tmp);
+           pg_bm = SC_Resolve_bookmark(s->opts, s->idx);
+           QR_get_last_bookmark(res, s->idx, &pg_bm.keys);
+           global_idx = pg_bm.index;
        }
        /* Note opts->row_operation_ptr is ignored */
        switch (s->operation)
@@ -1986,10 +1988,10 @@ RETCODE bulk_ope_callback(RETCODE retcode, void *para)
                ret = SC_pos_add(s->stmt, (UWORD) s->idx);
                break;
            case SQL_UPDATE_BY_BOOKMARK:
-               ret = SC_pos_update(s->stmt, (UWORD) s->idx, global_idx);
+               ret = SC_pos_update(s->stmt, (UWORD) s->idx, global_idx, &(pg_bm.keys));
                break;
            case SQL_DELETE_BY_BOOKMARK:
-               ret = SC_pos_delete(s->stmt, (UWORD) s->idx, global_idx);
+               ret = SC_pos_delete(s->stmt, (UWORD) s->idx, global_idx, &(pg_bm.keys));
                break;
        }
        if (SQL_NEED_DATA == ret)
@@ -2015,7 +2017,7 @@ RETCODE   bulk_ope_callback(RETCODE retcode, void *para)
    if (irdflds->rowsFetched)
        *(irdflds->rowsFetched) = s->processed;
 
-   if (res = SC_get_Curres(s->stmt), res)
+   if (res)
        res->recent_processed_row_count = s->stmt->diag_row_count = s->processed;
    return ret;
 }
index b5d878b2a05b70ed75feaf2d0dbaa5466538401f..f336713fcf220635f977ce5844b76b29be0480b3 100644 (file)
--- a/qresult.h
+++ b/qresult.h
@@ -244,6 +244,7 @@ void        QR_set_position(QResultClass *self, SQLLEN pos);
 void       QR_set_cursor(QResultClass *self, const char *name);
 SQLLEN     getNthValid(const QResultClass *self, SQLLEN sta, UWORD orientation, SQLULEN nth, SQLLEN *nearest);
 SQLLEN     QR_move_cursor_to_last(QResultClass *self, StatementClass *stmt);
+BOOL       QR_get_last_bookmark(const QResultClass *self, Int4 index, KeySet *keyset);
 
 #define QR_MALLOC_return_with_error(t, tp, s, a, m, r) \
 do { \
index 386a424148749a74cfc1f52a55a5c82af372a171..71309e42246dd939da867a5dd79e39f8cd1958ab 100644 (file)
--- a/results.c
+++ b/results.c
@@ -1028,13 +1028,13 @@ inolog("currT=%d base=%d rowset=%d\n", stmt->currTuple, QR_get_rowstart_in_cache
        {
            if (SQL_C_BOOKMARK == target_type || sizeof(UInt4) <= cbValueMax)
            {
-               UInt4 bookmark = (UInt4) SC_get_bookmark(stmt);
+               Int4 bookmark = SC_make_int4_bookmark(stmt->currTuple);
                contents_get = TRUE;
-               memcpy(rgbValue, &bookmark, sizeof(UInt4));
+               memcpy(rgbValue, &bookmark, sizeof(bookmark));
            }
        }
        if (pcbValue)
-           *pcbValue = sizeof(UInt4);
+           *pcbValue = sizeof(Int4);
 
        if (contents_get)
            result = SQL_SUCCESS;
@@ -1673,7 +1673,7 @@ inolog("num_tuples=%d\n", num_tuples);
            break;
 
        case SQL_FETCH_BOOKMARK:
-           bidx = SC_resolve_bookmark(irow);
+           bidx = SC_resolve_int4_bookmark(irow);
 
            if (bidx < 0)
            {
@@ -1994,6 +1994,7 @@ static void getTid(const QResultClass *res, SQLLEN index, UInt4 *blocknum, UInt2
 }
 static void KeySetSet(const TupleField *tuple, int num_fields, int num_key_fields, KeySet *keyset)
 {
+   keyset->status = 0;
    sscanf(tuple[num_fields - num_key_fields].value, "(%u,%hu)",
            &keyset->blocknum, &keyset->offset);
    if (num_key_fields > 1)
@@ -2317,7 +2318,7 @@ inolog("!!Commit Added=%d(%d)\n", QR_get_num_total_read(res) + i, i);
 }
 
 static int
-AddDeleted(QResultClass *res, SQLULEN index, KeySet *keyset)
+AddDeleted(QResultClass *res, SQLULEN index, const KeySet *keyset)
 {
    int i;
    Int2    dl_count, new_alloc;
@@ -2488,12 +2489,12 @@ enlargeUpdated(QResultClass *res, Int4 number, const StatementClass *stmt)
 }
 
 static void
-AddUpdated(StatementClass *stmt, SQLLEN index)
+AddUpdated(StatementClass *stmt, SQLLEN index, const KeySet *keyset, const TupleField *tuple_updated)
 {
    QResultClass    *res;
    SQLLEN  *updated;
-   KeySet  *updated_keyset, *keyset;
-   TupleField  *updated_tuples = NULL, *tuple_updated,  *tuple;
+   KeySet  *updated_keyset;
+   TupleField  *updated_tuples = NULL,  *tuple;
    SQLLEN  kres_ridx;
    UInt2   up_count;
    BOOL    is_in_trans;
@@ -2505,18 +2506,14 @@ AddUpdated(StatementClass *stmt, SQLLEN index)
 inolog("AddUpdated index=%d\n", index);
    if (!stmt)  return;
    if (res = SC_get_Curres(stmt), !res)    return;
-   if (!res->keyset)       return;
-   kres_ridx = GIdx2KResIdx(index, stmt, res);
-   if (kres_ridx < 0 || kres_ridx >= res->num_cached_keys)
-       return;
-   keyset = res->keyset + kres_ridx;
-   if (0 != (keyset->status & CURS_SELF_ADDING))
-       AddRollback(stmt, res, index, res->keyset + kres_ridx, SQL_REFRESH);
+   if (!keyset)    return;
    if (!QR_get_cursor(res))    return;
    up_count = res->up_count;
    if (up_count > 0 && 0 == res->up_alloc) return;
    num_fields = res->num_fields;
-   tuple_updated = res->backend_tuples + kres_ridx * num_fields;
+   kres_ridx = GIdx2KResIdx(index, stmt, res);
+   if (kres_ridx >= 0 && kres_ridx < res->num_cached_keys)
+       tuple_updated = res->backend_tuples + kres_ridx * num_fields;
    if (!tuple_updated)
        return;
    upd_idx = -1;
@@ -2773,6 +2770,7 @@ UndoRollback(StatementClass *stmt, QResultClass *res, BOOL partial)
 
        for (i = 0, doubtp = 0; i < res->rb_count; i++)
        {
+           keys.status = 0;
            keys.blocknum = rollback[i].blocknum;
            keys.offset = rollback[i].offset;
            keys.oid = rollback[i].oid;
@@ -2857,10 +2855,12 @@ inolog("UndoRollback %d(%d)\n", i, rollback[i].option);
            if (SQL_ADD == rollback[i].option)
                RemoveAdded(res, index);
            RemoveDeleted(res, index);
+           keys.status = 0;
            keys.blocknum = rollback[i].blocknum;
            keys.offset = rollback[i].offset;
            keys.oid = rollback[i].oid;
-           RemoveUpdatedAfterTheKey(res, index, &keys);
+           /* RemoveUpdatedAfterTheKey(res, index, &keys); is no longer needed? */
+           RemoveUpdated(res, index);
        }
        status = 0;
        kres_is_valid = FALSE;
@@ -3063,21 +3063,51 @@ cleanup:
    return qres;
 }
 
+BOOL QR_get_last_bookmark(const QResultClass *res, Int4 index, KeySet *keyset)
+{
+   int i;
+
+   if (res->dl_count > 0)
+   {
+       for (i = 0; i < res->dl_count; i++)
+       {
+           if (res->deleted[i] == index)
+           {
+               *keyset = res->deleted_keyset[i];
+               return TRUE;
+           }
+           if (res->deleted[i] > index)
+               break;
+       }
+   }
+   if (res->up_count > 0)
+   {
+       for (i = res->up_count - 1; i >= 0; i--)
+       {
+           if (res->updated[i] == index)
+           {
+               *keyset = res->updated_keyset[i];
+               return TRUE;
+           }
+       }
+   }
+   return FALSE;
+}
+
 static RETCODE
 SC_pos_reload_with_key(StatementClass *stmt, SQLULEN global_ridx, UInt2 *count, Int4 logKind, const KeySet *keyset)
 {
-   CSTR        func = "SC_pos_reload";
+   CSTR        func = "SC_pos_reload_with_key";
    int     res_cols;
-   UInt2       offset;
    UInt2       rcnt;
-   SQLLEN      res_ridx, kres_ridx;
+   SQLLEN      kres_ridx;
    OID     oidint;
-   UInt4       blocknum;
    QResultClass    *res, *qres;
    IRDFields   *irdflds = SC_get_IRDF(stmt);
    RETCODE     ret = SQL_ERROR;
    char        tidval[32];
    BOOL        use_ctid = TRUE;
+   BOOL        idx_exist = TRUE;
 
    mylog("positioned load fi=%p ti=%p\n", irdflds->fi, stmt->ti);
    rcnt = 0;
@@ -3088,21 +3118,20 @@ SC_pos_reload_with_key(StatementClass *stmt, SQLULEN global_ridx, UInt2 *count,
        SC_set_error(stmt, STMT_INVALID_CURSOR_STATE_ERROR, "Null statement result in SC_pos_reload.", func);
        return SQL_ERROR;
    }
-   res_ridx = GIdx2CacheIdx(global_ridx, stmt, res);
-   if (res_ridx < 0 || res_ridx >= QR_get_num_cached_tuples(res))
-   {
-       SC_set_error(stmt, STMT_ROW_OUT_OF_RANGE, "the target rows is out of the rowset", func);
-       return SQL_ERROR;
-   }
+
    kres_ridx = GIdx2KResIdx(global_ridx, stmt, res);
    if (kres_ridx < 0 || kres_ridx >= res->num_cached_keys)
    {
-       SC_set_error(stmt, STMT_ROW_OUT_OF_RANGE, "the target rows is out of the rowset", func);
-       return SQL_ERROR;
+       if (NULL == keyset || keyset->offset == 0)
+       {
+           SC_set_error(stmt, STMT_ROW_OUT_OF_RANGE, "the target keys are out of the rowset", func);
+           return SQL_ERROR;
+       }
+       idx_exist = FALSE;
    }
    else if (0 != (res->keyset[kres_ridx].status & CURS_SELF_ADDING))
    {
-       if (NULL == keyset)
+       if (NULL == keyset || keyset->offset == 0)
        {
            use_ctid = FALSE;
            mylog("The tuple is currently being added and can't use ctid\n");
@@ -3118,18 +3147,19 @@ SC_pos_reload_with_key(StatementClass *stmt, SQLULEN global_ridx, UInt2 *count,
        return SQL_ERROR;
    }
    /* old oid & tid */
-   if (!(oidint = getOid(res, kres_ridx)))
+   if (idx_exist)
    {
-       if (!strcmp(SAFE_NAME(stmt->ti[0]->bestitem), OID_NAME))
+       if (!(oidint = getOid(res, kres_ridx)))
        {
-           SC_set_error(stmt, STMT_ROW_VERSION_CHANGED, "the row was already deleted ?", func);
-           return SQL_SUCCESS_WITH_INFO;
+           if (!strcmp(SAFE_NAME(stmt->ti[0]->bestitem), OID_NAME))
+           {
+               SC_set_error(stmt, STMT_ROW_VERSION_CHANGED, "the row was already deleted ?", func);
+               return SQL_SUCCESS_WITH_INFO;
+           }
        }
    }
-   getTid(res, kres_ridx, &blocknum, &offset);
-   snprintf(tidval, sizeof(tidval), "(%u, %u)", blocknum, offset);
    res_cols = getNumResultCols(res);
-   if (keyset)
+   if (keyset) /* after or update */
    {
        char tid[32];
 
@@ -3138,25 +3168,42 @@ SC_pos_reload_with_key(StatementClass *stmt, SQLULEN global_ridx, UInt2 *count,
        qres = positioned_load(stmt, 0, &oidint, tid);
    }
    else
-       qres = positioned_load(stmt, use_ctid ? LATEST_TUPLE_LOAD : 0, &oidint, use_ctid ? tidval : NULL);
+   {
+       if (use_ctid)
+           qres = positioned_load(stmt, LATEST_TUPLE_LOAD, &oidint, tidval);
+       else
+           qres = positioned_load(stmt, 0, &oidint, NULL);
+       keyset = res->keyset + kres_ridx;
+   }
    if (!QR_command_maybe_successful(qres))
    {
        ret = SQL_ERROR;
        SC_replace_error_with_res(stmt, STMT_ERROR_TAKEN_FROM_BACKEND, "positioned_load failed", qres, TRUE);
    }
-   else
+   else if (rcnt = (UInt2) QR_get_num_cached_tuples(qres), rcnt == 1)
    {
-       TupleField *tuple_old, *tuple_new;
        ConnectionClass *conn = SC_get_conn(stmt);
+       SQLLEN      res_ridx;
 
-       rcnt = (UInt2) QR_get_num_cached_tuples(qres);
-       tuple_old = res->backend_tuples + res->num_fields * res_ridx;
-       if (0 != logKind && CC_is_in_trans(conn))
-           AddRollback(stmt, res, global_ridx, res->keyset + kres_ridx, logKind);
-       if (rcnt == 1)
+       switch (logKind)
+       {
+           case 0:
+           case SQL_FETCH_BY_BOOKMARK:
+               break;
+           case SQL_UPDATE:
+               AddUpdated(stmt, global_ridx, keyset, qres->tupleField);
+               break;
+           default:
+               AddRollback(stmt, res, global_ridx, keyset, logKind);
+       }
+       res_ridx = GIdx2CacheIdx(global_ridx, stmt, res);
+       if (res_ridx >= 0 && res_ridx < QR_get_num_cached_tuples(res))
        {
+           TupleField *tuple_old, *tuple_new;
            int effective_fields = res_cols;
 
+           tuple_old = res->backend_tuples + res->num_fields * res_ridx;
+
            QR_set_position(qres, 0);
            tuple_new = qres->tupleField;
            if (SQL_CURSOR_KEYSET_DRIVEN == stmt->options.cursor_type &&
@@ -3167,9 +3214,15 @@ SC_pos_reload_with_key(StatementClass *stmt, SQLULEN global_ridx, UInt2 *count,
            ret = SQL_SUCCESS;
        }
        else
+           ret = SQL_SUCCESS;
+   }
+   else
+   {
+       SC_set_error(stmt, STMT_ROW_VERSION_CHANGED, "the content was deleted after last fetch", func);
+       ret = SQL_SUCCESS_WITH_INFO;
+       AddRollback(stmt, res, global_ridx, keyset, logKind);
+       if (idx_exist)
        {
-           SC_set_error(stmt, STMT_ROW_VERSION_CHANGED, "the content was deleted after last fetch", func);
-           ret = SQL_SUCCESS_WITH_INFO;
            if (stmt->options.cursor_type == SQL_CURSOR_KEYSET_DRIVEN)
            {
                res->keyset[kres_ridx].status |= SQL_ROW_DELETED;
@@ -3722,8 +3775,7 @@ QR_get_rowstart_in_cache(res), SC_get_rowset_start(stmt), stmt->options.cursor_t
 }
 
 static RETCODE SQL_API
-irow_update(RETCODE ret, StatementClass *stmt, StatementClass *ustmt,
-           SQLSETPOSIROW irow, SQLULEN global_ridx)
+irow_update(RETCODE ret, StatementClass *stmt, StatementClass *ustmt, SQLULEN global_ridx, const KeySet *old_keyset)
 {
    CSTR    func = "irow_update";
 
@@ -3744,11 +3796,11 @@ irow_update(RETCODE ret, StatementClass *stmt, StatementClass *ustmt,
                    1 == QR_get_num_cached_tuples(tres))
                {
                    KeySetSet(tres->backend_tuples, QR_NumResultCols(tres), QR_NumResultCols(tres), &keys);
-                   keyset = &keys;
+                   if (SQL_SUCCEEDED(ret = SC_pos_reload_with_key(stmt, global_ridx, (UInt2 *) 0, SQL_UPDATE, &keys)))
+                       AddRollback(stmt, SC_get_Curres(stmt), global_ridx, old_keyset, SQL_UPDATE);
                }
-               ret = SC_pos_reload_with_key(stmt, global_ridx, (UInt2 *) 0, SQL_UPDATE, keyset);
-               if (SQL_ERROR != ret)
-                   AddUpdated(stmt, global_ridx);
+               else
+                   ret = SQL_ERROR;
            }
            else if (updcnt == 0)
            {
@@ -3779,6 +3831,7 @@ typedef struct
    IRDFields   *irdflds;
    SQLSETPOSIROW       irow;
    SQLULEN     global_ridx;
+   KeySet      old_keyset;
 }  pup_cdata;
 static RETCODE
 pos_update_callback(RETCODE retcode, void *para)
@@ -3787,11 +3840,12 @@ pos_update_callback(RETCODE retcode, void *para)
    RETCODE ret = retcode;
    pup_cdata *s = (pup_cdata *) para;
    SQLLEN  kres_ridx;
+   BOOL    idx_exist = TRUE;
 
    if (s->updyes)
    {
        mylog("pos_update_callback in\n");
-       ret = irow_update(ret, s->stmt, s->qstmt, s->irow, s->global_ridx);
+       ret = irow_update(ret, s->stmt, s->qstmt, s->global_ridx, &s->old_keyset);
 inolog("irow_update ret=%d,%d\n", ret, SC_get_errornumber(s->qstmt));
        if (ret != SQL_SUCCESS)
            SC_error_copy(s->stmt, s->qstmt, TRUE);
@@ -3802,11 +3856,9 @@ inolog("irow_update ret=%d,%d\n", ret, SC_get_errornumber(s->qstmt));
    kres_ridx = GIdx2KResIdx(s->global_ridx, s->stmt, s->res);
    if (kres_ridx < 0 || kres_ridx >= s->res->num_cached_keys)
    {
-       SC_set_error(s->stmt, STMT_ROW_OUT_OF_RANGE, "the target rows is out of the rowset", func);
-inolog("gidx=%d num_keys=%d kresidx=%d\n", s->global_ridx, s->res->num_cached_keys, kres_ridx);
-       return SQL_ERROR;
+       idx_exist = FALSE;
    }
-   if (SQL_SUCCESS == ret && s->res->keyset)
+   if (SQL_SUCCESS == ret && s->res->keyset && idx_exist)
    {
        ConnectionClass *conn = SC_get_conn(s->stmt);
 
@@ -3833,7 +3885,7 @@ inolog("gidx=%d num_keys=%d kresidx=%d\n", s->global_ridx, s->res->num_cached_ke
 }
 RETCODE
 SC_pos_update(StatementClass *stmt,
-             SQLSETPOSIROW irow, SQLULEN global_ridx)
+         SQLSETPOSIROW irow, SQLULEN global_ridx, const KeySet *keyset)
 {
    CSTR    func = "SC_pos_update";
    int         i,
@@ -3853,6 +3905,7 @@ SC_pos_update(StatementClass *stmt,
    SQLLEN  offset;
    SQLLEN  *used, kres_ridx;
    Int4    bind_size = opts->bind_size;
+   BOOL    idx_exist = TRUE;
 
    s.stmt = stmt;
    s.irow = irow;
@@ -3876,18 +3929,33 @@ SC_pos_update(StatementClass *stmt,
    kres_ridx = GIdx2KResIdx(s.global_ridx, s.stmt, s.res);
    if (kres_ridx < 0 || kres_ridx >= s.res->num_cached_keys)
    {
-       SC_set_error(s.stmt, STMT_ROW_OUT_OF_RANGE, "the target rows is out of the rowset", func);
-       return SQL_ERROR;
+       if (NULL == keyset || keyset->offset == 0)
+       {
+           SC_set_error(s.stmt, STMT_ROW_OUT_OF_RANGE, "the target keys are out of the rowset", func);
+           return SQL_ERROR;
+       }
+       idx_exist = FALSE;
    }
-   if (!(oid = getOid(s.res, kres_ridx)))
+   if (idx_exist)
    {
-       if (!strcmp(SAFE_NAME(stmt->ti[0]->bestitem), OID_NAME))
+       if (!(oid = getOid(s.res, kres_ridx)))
        {
-           SC_set_error(stmt, STMT_ROW_VERSION_CHANGED, "the row was already deleted ?", func);
-           return SQL_ERROR;
+           if (!strcmp(SAFE_NAME(stmt->ti[0]->bestitem), OID_NAME))
+           {
+               SC_set_error(stmt, STMT_ROW_VERSION_CHANGED, "the row was already deleted ?", func);
+               return SQL_ERROR;
+           }
        }
+       getTid(s.res, kres_ridx, &blocknum, &pgoffset);
+       s.old_keyset = s.res->keyset[kres_ridx];
+   }
+   else
+   {
+       oid = keyset->oid;
+       blocknum = keyset->blocknum;
+       pgoffset = keyset->offset;
+       s.old_keyset = *keyset;
    }
-   getTid(s.res, kres_ridx, &blocknum, &pgoffset);
 
    ti = s.stmt->ti[0];
 
@@ -4021,7 +4089,7 @@ SC_pos_update(StatementClass *stmt,
 }
 RETCODE
 SC_pos_delete(StatementClass *stmt,
-             SQLSETPOSIROW irow, SQLULEN global_ridx)
+         SQLSETPOSIROW irow, SQLULEN global_ridx, const KeySet *keyset)
 {
    CSTR    func = "SC_pos_update";
    UWORD       offset;
@@ -4036,6 +4104,7 @@ SC_pos_delete(StatementClass *stmt,
    TABLE_INFO  *ti;
    const char  *bestitem;
    const char  *bestqual;
+   BOOL        idx_exist = TRUE;
 
    mylog("POS DELETE ti=%p\n", stmt->ti);
    if (!(res = SC_get_Curres(stmt)))
@@ -4054,22 +4123,35 @@ SC_pos_delete(StatementClass *stmt,
    kres_ridx = GIdx2KResIdx(global_ridx, stmt, res);
    if (kres_ridx < 0 || kres_ridx >= res->num_cached_keys)
    {
-       SC_set_error(stmt, STMT_ROW_OUT_OF_RANGE, "the target rows is out of the rowset", func);
-       return SQL_ERROR;
+       if (NULL == keyset || keyset->offset == 0)
+       {
+           SC_set_error(stmt, STMT_ROW_OUT_OF_RANGE, "the target keys are out of the rowset", func);
+           return SQL_ERROR;
+       }
+       idx_exist = FALSE;
    }
    ti = stmt->ti[0];
    bestitem = GET_NAME(ti->bestitem);
-   if (!(oid = getOid(res, kres_ridx)))
+   bestqual = GET_NAME(ti->bestqual);
+   if (idx_exist)
    {
-       if (bestitem && !strcmp(bestitem, OID_NAME))
+       if (!(oid = getOid(res, kres_ridx)))
        {
-           SC_set_error(stmt, STMT_ROW_VERSION_CHANGED, "the row was already deleted ?", func);
-           return SQL_ERROR;
+           if (bestitem && !strcmp(bestitem, OID_NAME))
+           {
+               SC_set_error(stmt, STMT_ROW_VERSION_CHANGED, "the row was already deleted ?", func);
+               return SQL_ERROR;
+           }
        }
+       getTid(res, kres_ridx, &blocknum, &offset);
+       keyset = res->keyset + kres_ridx;
+   }
+   else
+   {
+       oid = keyset->oid;
+       blocknum = keyset->blocknum;
+       offset = keyset->offset;
    }
-   bestqual = GET_NAME(ti->bestqual);
-   getTid(res, kres_ridx, &blocknum, &offset);
-   /*sprintf(dltstr, "delete from \"%s\" where ctid = '%s' and oid = %s",*/
    snprintf(dltstr, sizeof(dltstr),
             "delete from %s where ctid = '(%u, %u)'",
             ti_quote(stmt, oid), blocknum, offset);
@@ -4096,7 +4178,7 @@ SC_pos_delete(StatementClass *stmt,
        {
            if (dltcnt == 1)
            {
-               RETCODE tret = SC_pos_reload(stmt, global_ridx, (UInt2 *) 0, SQL_DELETE);
+               RETCODE tret = SC_pos_reload_with_key(stmt, global_ridx, (UInt2 *) 0, SQL_DELETE, keyset);
                if (!SQL_SUCCEEDED(tret))
                    ret = tret;
            }
@@ -4104,7 +4186,7 @@ SC_pos_delete(StatementClass *stmt,
            {
                SC_set_error(stmt, STMT_ROW_VERSION_CHANGED, "the content was changed before deletion", func);
                ret = SQL_ERROR;
-               if (stmt->options.cursor_type == SQL_CURSOR_KEYSET_DRIVEN)
+               if (idx_exist && stmt->options.cursor_type == SQL_CURSOR_KEYSET_DRIVEN)
                    SC_pos_reload(stmt, global_ridx, (UInt2 *) 0, 0);
            }
            else
@@ -4129,9 +4211,10 @@ SC_pos_delete(StatementClass *stmt,
    }
    if (qres)
        QR_Destructor(qres);
-   if (SQL_SUCCESS == ret && res->keyset)
+   if (SQL_SUCCESS == ret && keyset)
+       AddDeleted(res, global_ridx, keyset);
+   if (SQL_SUCCESS == ret && keyset && idx_exist)
    {
-       AddDeleted(res, global_ridx, res->keyset + kres_ridx);
        res->keyset[kres_ridx].status &= (~KEYSET_INFO_PUBLIC);
        if (CC_is_in_trans(conn))
        {
@@ -4204,21 +4287,8 @@ irow_insert(RETCODE ret, StatementClass *stmt, StatementClass *istmt,
            bookmark = opts->bookmark;
            if (bookmark && bookmark->buffer)
            {
-               char    buf[32];
-               SQLULEN offset = opts->row_offset_ptr ? *opts->row_offset_ptr : 0;
-
-               snprintf(buf, sizeof(buf), FORMAT_LEN, SC_make_bookmark(addpos));
                SC_set_current_col(stmt, -1);
-               copy_and_convert_field(stmt,
-                                      PG_TYPE_INT4,
-                                      PG_UNSPECIFIED,
-                                      buf,
-                                      bookmark->returntype,
-                                      0,
-                                      bookmark->buffer + offset,
-                                      bookmark->buflen,
-                                      LENADDR_SHIFT(bookmark->used, offset),
-                                      LENADDR_SHIFT(bookmark->used, offset));
+               SC_Create_bookmark(stmt, bookmark, stmt->bind_row, addpos, &keys);
            }
        }
        else
@@ -4598,10 +4668,10 @@ RETCODE spos_callback(RETCODE retcode, void *para)
            switch (s->fOption)
            {
                case SQL_UPDATE:
-                   ret = SC_pos_update(s->stmt, s->nrow, global_ridx);
+                   ret = SC_pos_update(s->stmt, s->nrow, global_ridx, NULL);
                    break;
                case SQL_DELETE:
-                   ret = SC_pos_delete(s->stmt, s->nrow, global_ridx);
+                   ret = SC_pos_delete(s->stmt, s->nrow, global_ridx, NULL);
                    break;
                case SQL_ADD:
                    ret = SC_pos_add(s->stmt, s->nrow);
@@ -4888,28 +4958,43 @@ SC_fetch_by_bookmark(StatementClass *stmt)
    SC_MALLOC_gexit_with_error(tidbuf, char, size_of_rowset * tidbuflen, stmt, "Couldn't allocate memory for tidbuf bind.", (ret = SQL_ERROR));
    for (i = 0; i < size_of_rowset; i++)
    {
+       PG_BM   pg_bm;
        SQLLEN  bidx;
-       UInt4 tmp;
+       BOOL    idx_exist = FALSE;
 
-       memcpy(&tmp, CALC_BOOKMARK_ADDR(opts->bookmark, (opts->row_offset_ptr ? *(opts->row_offset_ptr) : 0), opts->bind_size, i), sizeof(UInt4));
-       bidx = SC_resolve_bookmark(tmp);
+       pg_bm = SC_Resolve_bookmark(opts, i);
+       bidx = pg_bm.index;
 
 mylog("i=%d bidx=%d cached=%d\n", i, bidx, res->num_cached_keys);
        kres_ridx = GIdx2KResIdx(bidx, stmt, res);
        if (kres_ridx < 0 || kres_ridx >= res->num_cached_keys)
        {
-           SC_set_error(stmt, STMT_ROW_OUT_OF_RANGE, "the target rows is out of the rowset", __FUNCTION__);
-           goto cleanup;
+           if (pg_bm.keys.offset > 0)
+           {
+               QR_get_last_bookmark(res, bidx, &pg_bm.keys);
+               blocknum = pg_bm.keys.blocknum;
+               offset = pg_bm.keys.offset;
+               oidint = pg_bm.keys.oid;
+           }
+           else
+           {
+               SC_set_error(stmt, STMT_ROW_OUT_OF_RANGE, "the target rows is out of the rowset", __FUNCTION__);
+               goto cleanup;
+           }
        }
-       if (!(oidint = getOid(res, kres_ridx)))
+       else
        {
-           if (!strcmp(SAFE_NAME(stmt->ti[0]->bestitem), OID_NAME))
+           if (!(oidint = getOid(res, kres_ridx)))
            {
-               SC_set_error(stmt, STMT_ROW_VERSION_CHANGED, "the row was already deleted ?", __FUNCTION__);
+               if (!strcmp(SAFE_NAME(stmt->ti[0]->bestitem), OID_NAME))
+               {
+                   SC_set_error(stmt, STMT_ROW_VERSION_CHANGED, "the row was already deleted ?", __FUNCTION__);
+               }
            }
+           getTid(res, kres_ridx, &blocknum, &offset);
        }
-       getTid(res, kres_ridx, &blocknum, &offset);
        snprintf(tidbuf + i * tidbuflen, tidbuflen, "(%u,%u)", blocknum, offset);
+       mylog("!!!! tidbuf=%s\n", tidbuf + i * tidbuflen);
    }
    if (!SQL_SUCCEEDED(PGAPI_BindParameter(hstmt, 1, SQL_PARAM_INPUT,
            SQL_C_CHAR, SQL_CHAR, tidbuflen, 0,
index 5f94b63bf850648c32070819b1a4b5e2acc8cb81..616c9eae9750f53d42a14353cea7f2d9313d23cb 100644 (file)
@@ -1544,17 +1544,6 @@ SC_get_time(StatementClass *stmt)
        stmt->stmt_time = time(NULL);
    return stmt->stmt_time;
 }
-/*
- * Currently, the driver offers very simple bookmark support -- it is
- * just the current row number.  But it could be more sophisticated
- * someday, such as mapping a key to a 32 bit value
- */
-SQLULEN
-SC_get_bookmark(StatementClass *self)
-{
-   return SC_make_bookmark(self->currTuple);
-}
-
 
 RETCODE
 SC_fetch(StatementClass *self)
@@ -1574,6 +1563,7 @@ SC_fetch(StatementClass *self)
    ColumnInfoClass *coli;
    BindInfoClass   *bookmark;
    BOOL        useCursor;
+   KeySet      *keyset = NULL;
 
    /* TupleField *tupleField; */
 
@@ -1648,6 +1638,7 @@ inolog("SC_ pstatus[%d]=%hx fetch_count=" FORMAT_LEN "\n", kres_ridx, pstatus, s
                    return result;
                pstatus &= ~CURS_NEEDS_REREAD;
            }
+           keyset =  res->keyset + kres_ridx;
        }
    }
 
@@ -1667,15 +1658,8 @@ inolog("%s: stmt=%p ommitted++\n", func, self);
     */
    if ((bookmark = opts->bookmark) && bookmark->buffer)
    {
-       char        buf[32];
-       SQLLEN  offset = opts->row_offset_ptr ? *opts->row_offset_ptr : 0;
-
-       sprintf(buf, FORMAT_ULEN, SC_get_bookmark(self));
        SC_set_current_col(self, -1);
-       result = copy_and_convert_field(self, 0, PG_UNSPECIFIED, buf,
-            SQL_C_ULONG, 0, bookmark->buffer + offset, 0,
-           LENADDR_SHIFT(bookmark->used, offset),
-           LENADDR_SHIFT(bookmark->used, offset));
+       SC_Create_bookmark(self, bookmark, self->bind_row, self->currTuple, keyset);
    }
 
    if (self->options.retrieve_data == SQL_RD_OFF)      /* data isn't required */
@@ -2974,3 +2958,79 @@ SC_set_errorinfo(StatementClass *self, QResultClass *res)
            break;
    }
 }
+
+/*
+ * Before 9.6, the driver offered very simple bookmark support -- it is just
+ * the current row number.
+ * Now the driver offers more verbose bookmarks which contain KeySet informations
+ * (CTID (+ OID)). Though they consume 12bytes(row number + CTID) or 16bytes
+ * (row number + CTID + OID), they are useful in declare/fetch mode.
+ */
+
+PG_BM  SC_Resolve_bookmark(const ARDFields *opts, Int4 idx)
+{
+   BindInfoClass   *bookmark;
+   SQLLEN      *used;
+   SQLULEN     offset;
+   SQLUINTEGER bind_size;
+   size_t  cpylen = sizeof(Int4);
+   PG_BM   pg_bm;
+
+   bookmark = opts->bookmark;
+   offset = opts->row_offset_ptr ? *(opts->row_offset_ptr) : 0;
+   bind_size = opts->bind_size;
+   memset(&pg_bm, 0, sizeof(pg_bm));
+   if (used = bookmark->used, used != NULL)
+   {
+       used = LENADDR_SHIFT(used, offset);
+       if (bind_size > 0)
+           used = LENADDR_SHIFT(used, idx * bind_size);
+       else
+           used = LENADDR_SHIFT(used, idx * sizeof(SQLLEN));
+       if (*used >= sizeof(pg_bm))
+           cpylen = sizeof(pg_bm);
+       else if (*used >= 12)
+           cpylen = 12;
+       mylog("%s used=%d cpylen=%d\n", __FUNCTION__, *used, cpylen);
+   }
+   memcpy(&pg_bm, CALC_BOOKMARK_ADDR(bookmark, offset, bind_size, idx), cpylen);
+mylog("%s index=%d block=%d off=%d\n", __FUNCTION__, pg_bm.index, pg_bm.keys.blocknum, pg_bm.keys.offset);
+   pg_bm.index = SC_resolve_int4_bookmark(pg_bm.index);
+
+   return pg_bm;
+}
+
+int    SC_Create_bookmark(StatementClass *self, BindInfoClass *bookmark, Int4 bind_row, Int4 currTuple, const KeySet *keyset)
+{
+   ARDFields   *opts = SC_get_ARDF(self);
+   SQLUINTEGER bind_size = opts->bind_size;
+   SQLULEN     offset = opts->row_offset_ptr ? *opts->row_offset_ptr : 0;
+   size_t      cvtlen = sizeof(Int4);
+   PG_BM       pg_bm;
+
+mylog("%s type=%d buflen=%d buf=%p\n", __FUNCTION__, bookmark->returntype, bookmark->buflen, bookmark->buffer);
+   memset(&pg_bm, 0, sizeof(pg_bm));
+   if (SQL_C_BOOKMARK == bookmark->returntype)
+       ;
+   else if (bookmark->buflen >= sizeof(pg_bm))
+       cvtlen = sizeof(pg_bm);
+   else if (bookmark->buflen >= 12)
+       cvtlen = 12;
+   pg_bm.index = SC_make_int4_bookmark(currTuple);
+   if (keyset)
+       pg_bm.keys  = *keyset;
+   memcpy(CALC_BOOKMARK_ADDR(bookmark, offset, bind_size, bind_row), &pg_bm, cvtlen);
+   if (bookmark->used)
+   {
+       SQLLEN *used = LENADDR_SHIFT(bookmark->used, offset);
+
+       if (bind_size > 0)
+           used = LENADDR_SHIFT(used, bind_row * bind_size);
+       else
+           used = LENADDR_SHIFT(used, bind_row * sizeof(SQLLEN));
+       *used = cvtlen;
+   }
+mylog("%s cvtlen=%d ix(bl,of)=%d(%d,%d)\n", __FUNCTION__, cvtlen, pg_bm.index, pg_bm.keys.blocknum, pg_bm.keys.offset);
+
+   return COPY_OK;
+}
index a8a21429c49c17f18452a40df8a65741326d7880..803b20fa5e3b65c688a18075ea166327cf2c181a 100644 (file)
@@ -15,6 +15,7 @@
 #include "pgtypes.h"
 #include "bind.h"
 #include "descriptor.h"
+#include "tuple.h"
 
 #if defined (POSIX_MULTITHREAD_SUPPORT)
 #include <pthread.h>
@@ -508,13 +509,16 @@ RETCODE       SC_fetch(StatementClass *self);
 void       SC_free_params(StatementClass *self, char option);
 void       SC_log_error(const char *func, const char *desc, const StatementClass *self);
 time_t     SC_get_time(StatementClass *self);
-SQLULEN        SC_get_bookmark(StatementClass *self);
+SQLLEN     SC_get_int4_bookmark(StatementClass *self);
 RETCODE        SC_pos_reload(StatementClass *self, SQLULEN index, UInt2 *, Int4);
-RETCODE        SC_pos_update(StatementClass *self, SQLSETPOSIROW irow, SQLULEN index);
-RETCODE        SC_pos_delete(StatementClass *self, SQLSETPOSIROW irow, SQLULEN index);
+RETCODE        SC_pos_update(StatementClass *self, SQLSETPOSIROW irow, SQLULEN index, const KeySet *keyset);
+RETCODE        SC_pos_delete(StatementClass *self, SQLSETPOSIROW irow, SQLULEN index, const KeySet *keyset);
 RETCODE        SC_pos_refresh(StatementClass *self, SQLSETPOSIROW irow, SQLULEN index);
+RETCODE        SC_pos_fetch(StatementClass *self, const PG_BM *pg_bm);
 RETCODE        SC_pos_add(StatementClass *self, SQLSETPOSIROW irow);
 RETCODE        SC_fetch_by_bookmark(StatementClass *self);
+int        SC_Create_bookmark(StatementClass *stmt, BindInfoClass *bookmark, Int4 row_pos, Int4 currTuple, const KeySet *keyset);
+PG_BM      SC_Resolve_bookmark(const ARDFields *opts, Int4 idx);
 int        SC_set_current_col(StatementClass *self, int col);
 void       SC_setInsertedTable(StatementClass *, RETCODE);
 void       SC_scanQueryAndCountParams(const char *, const ConnectionClass *,
@@ -551,6 +555,6 @@ QResultClass *ParseAndDescribeWithLibpq(StatementClass *stmt, const char *plan_n
 #define    KResIdx2GIdx(ridx, stmt, res)   (ridx - (res)->key_base + (stmt)->rowset_start)
 
 #define    BOOKMARK_SHIFT  1
-#define    SC_make_bookmark(b) ((b < 0) ? (b) : (b + BOOKMARK_SHIFT))
-#define    SC_resolve_bookmark(b)  ((b < 0) ? (b) : (b - BOOKMARK_SHIFT))
+#define    SC_make_int4_bookmark(b)    ((b < 0) ? (b) : (b + BOOKMARK_SHIFT))
+#define    SC_resolve_int4_bookmark(b) ((b < 0) ? (b) : (b - BOOKMARK_SHIFT))
 #endif /* __STATEMENT_H__ */
index 9b89ddec2072728ad9fa8933fd910b54e1897254..9ef032a8b6535cf95bab95e3494ffc0e37d2024a 100644 (file)
@@ -31,7 +31,7 @@ printCurrentRow(HSTMT hstmt)
    printf("\n");
 }
 
-#define    BOOKMARK_SIZE   10
+#define    BOOKMARK_SIZE   14
 
 int main(int argc, char **argv)
 {
diff --git a/tuple.h b/tuple.h
index 782a77c4de03c6ad0080a6f7efe6e6a3d65b42a9..22c48f5faea7c4740916547d09d14bf334600d07 100644 (file)
--- a/tuple.h
+++ b/tuple.h
@@ -65,4 +65,9 @@ void      set_tuplefield_int4(TupleField *tuple_field, Int4 value);
 SQLLEN ClearCachedRows(TupleField *tuple, int num_fields, SQLLEN num_rows);
 SQLLEN ReplaceCachedRows(TupleField *otuple, const TupleField *ituple, int num_fields, SQLLEN num_rows);
 
+typedef struct _PG_BM_ {
+   Int4    index;
+   KeySet  keys;
+} PG_BM;
+
 #endif