>From 7bfe31114c6ca02b3efa5f75f884423099ac370b Mon Sep 17 00:00:00 2001 From: Heikki Linnakangas Date: Fri, 15 Mar 2013 15:26:01 +0200 Subject: [PATCH 1/1] Eliminate looping through chain of results in performance critical codepaths. This patch eliminates O(n^2) behavior on the number of chained results in SC_execute. This makes it feasible to deal with large parameter arrays, for example. The idea is to track the owner statement of each result object, so that when we need to check if a result object is already in the chain of results of a statement, we just need to look at the owner field, not loop through the whole chain. Also, keep track of the last result in the chain in StatementClass, so that we don't need to iterate through it when appending to the end. --- qresult.c | 2 ++ qresult.h | 1 + statement.c | 72 +++++++++++++++++++++++++++++++++++++++++++++++------------ statement.h | 2 ++ 4 files changed, 63 insertions(+), 14 deletions(-) diff --git a/qresult.c b/qresult.c index 9ad0814..5bb17fc 100644 --- a/qresult.c +++ b/qresult.c @@ -163,6 +163,7 @@ QR_Constructor() rv->command = NULL; rv->notice = NULL; rv->conn = NULL; + rv->stmt = NULL; rv->next = NULL; rv->pstatus = 0; rv->count_backend_allocated = 0; @@ -1234,6 +1235,7 @@ inolog("id='%c' response_length=%d\n", id, response_length); CC_on_abort(conn, CONN_DEAD); return FALSE; } + self->next->stmt = self->stmt; QR_set_cache_size(self->next, self->cache_size); self = self->next; if (!QR_fetch_tuples(self, conn, NULL, LastMessageType)) diff --git a/qresult.h b/qresult.h index 3893bda..108f1c0 100644 --- a/qresult.h +++ b/qresult.h @@ -55,6 +55,7 @@ struct QResultClass_ ColumnInfoClass *fields; /* the Column information */ ConnectionClass *conn; /* the connection this result is using * (backend) */ + StatementClass *stmt; /* the statement this result belongs to. */ QResultClass *next; /* the following result class */ /* Stuff for declare/fetch tuples */ diff --git a/statement.c b/statement.c index 697d142..be21548 100644 --- a/statement.c +++ b/statement.c @@ -383,6 +383,7 @@ SC_Constructor(ConnectionClass *conn) rv->hdbc = conn; rv->phstmt = NULL; rv->result = NULL; + rv->lastres = NULL; rv->curres = NULL; rv->catalog_result = FALSE; rv->prepare = NON_PREPARE_STATEMENT; @@ -525,7 +526,7 @@ SC_Destructor(StatementClass *self) void SC_init_Result(StatementClass *self) { - self->result = self->curres = NULL; + self->result = self->lastres = self->curres = NULL; self->curr_param_result = 0; mylog("SC_init_Result(%x)", self); } @@ -537,9 +538,22 @@ SC_set_Result(StatementClass *self, QResultClass *res) { mylog("SC_set_Result(%x, %x)", self, res); QR_Destructor(self->result); - self->result = self->curres = res; + self->result = self->lastres = self->curres = res; if (NULL != res) + { + /* + * Set the owner of 'res', and all results chained + * to it. They all belong to this statement now. + */ + QResultClass *t; + for (t = res; t != NULL; t = t->next) + { + t->stmt = self; + /* also update lastres while we're at it */ + self->lastres = t; + } self->curr_param_result = 1; + } } } @@ -1573,6 +1587,28 @@ PGAPI_StmtError( SQLHSTMT hstmt, pcbErrorMsg, flag); } +/* + * Get the last result in the results chain. + */ +QResultClass * +SC_get_Lastres(StatementClass *self) +{ + QResultClass *res; + + res = self->lastres; + if (res == NULL) + res = NULL; + else + { + while (res->next != NULL) + res = res->next; + } + + /* update the cached value while we're at it */ + self->lastres = res; + return res; +} + time_t SC_get_time(StatementClass *stmt) { @@ -1978,7 +2014,7 @@ SC_execute(StatementClass *self) SC_set_error(self, STMT_EXEC_ERROR, "Execute request error", func); goto cleanup; } - for (res = SC_get_Result(self); NULL != res && NULL != res->next; res = res->next) ; + res = SC_get_Lastres(self); inolog("get_Result=%p %p %d\n", res, SC_get_Result(self), self->curr_param_result); if (!(res = SendSyncAndReceive(self, self->curr_param_result ? res : NULL, "bind_and_execute"))) { @@ -2193,20 +2229,28 @@ inolog("res->next=%p\n", tres); CC_abort(conn); #endif /* _LEGACY_MODE_ */ } - if (!SC_get_Result(self)) - SC_set_Result(self, res); - else - { - QResultClass *last; - for (last = SC_get_Result(self); NULL != last->next; last = last->next) + /* + * Add 'res' to the end of the result chain, if it's not in the chain + * already. + */ + if (res->stmt != self) + { + /* the result should not belong to any other statement */ + /* assert (res->stmt == NULL) */ + QResultClass *last = SC_get_Lastres(self); + if (last == NULL) + SC_set_Result(self, res); + else { - if (last == res) - break; - } - if (last != res) + QResultClass *t; + for (t = res; t != NULL; t = t->next) + t->stmt = self; + last->next = res; - self->curr_param_result = 1; + self->curr_param_result = 1; + res->stmt = self; + } } ipdopts = SC_get_IPDF(self); diff --git a/statement.h b/statement.h index 21785ae..110cf22 100644 --- a/statement.h +++ b/statement.h @@ -182,6 +182,7 @@ struct StatementClass_ * statement belongs to */ QResultClass *result; /* result of the current statement */ QResultClass *curres; /* the current result in the chain */ + QResultClass *lastres; /* last result in the result->next chain */ HSTMT FAR *phstmt; StatementOptions options; StatementOptions options_orig; @@ -290,6 +291,7 @@ void SC_set_Result(StatementClass *self, QResultClass *res); #define SC_get_Result(a) (a->result) #define SC_set_Curres(a, b) (a->curres = b) #define SC_get_Curres(a) (a->curres) +extern QResultClass *SC_get_Lastres(StatementClass *self); #define SC_get_ARD(a) (a->ard) #define SC_get_APD(a) (a->apd) #define SC_get_IRD(a) (a->ird) -- 1.7.10.4