📄 statement.c
字号:
}/* Pre-execute a statement (SQLPrepare/SQLDescribeCol) */void SC_pre_execute(StatementClass *self){ mylog("SC_pre_execute: status = %d\n", self->status); if (self->status == STMT_READY) { mylog(" preprocess: status = READY\n"); SQLExecute(self); if (self->status == STMT_FINISHED) { mylog(" preprocess: after status = FINISHED, so set PREMATURE\n"); self->status = STMT_PREMATURE; } } }/* This is only called from SQLFreeStmt(SQL_UNBIND) */char SC_unbind_cols(StatementClass *self){Int2 lf; for(lf = 0; lf < self->bindings_allocated; lf++) { self->bindings[lf].data_left = -1; self->bindings[lf].buflen = 0; self->bindings[lf].buffer = NULL; self->bindings[lf].used = NULL; self->bindings[lf].returntype = SQL_C_CHAR; } self->bookmark.buffer = NULL; self->bookmark.used = NULL; return 1;}void SC_clear_error(StatementClass *self){ self->errornumber = 0; self->errormsg = NULL; self->errormsg_created = FALSE;}// This function creates an error msg which is the concatenation// of the result, statement, connection, and socket messages.char *SC_create_errormsg(StatementClass *self){QResultClass *res = self->result;ConnectionClass *conn = self->hdbc;int pos;static char msg[4096]; msg[0] = '\0'; if (res && res->message) strcpy(msg, res->message); else if (self->errormsg) strcpy(msg, self->errormsg); if (conn) { SocketClass *sock = conn->sock; if (conn->errormsg && conn->errormsg[0] != '\0') { pos = strlen(msg); sprintf(&msg[pos], ";\n%s", conn->errormsg); } if (sock && sock->errormsg && sock->errormsg[0] != '\0') { pos = strlen(msg); sprintf(&msg[pos], ";\n%s", sock->errormsg); } } return msg;}char SC_get_error(StatementClass *self, int *number, char **message){char rv; // Create a very informative errormsg if it hasn't been done yet. if ( ! self->errormsg_created) { self->errormsg = SC_create_errormsg(self); self->errormsg_created = TRUE; } if ( self->errornumber) { *number = self->errornumber; *message = self->errormsg; self->errormsg = NULL; } rv = (self->errornumber != 0); self->errornumber = 0; return rv;}/* 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*/unsigned longSC_get_bookmark(StatementClass *self){ return (self->currTuple + 1); }RETCODESC_fetch(StatementClass *self){static char *func = "SC_fetch";QResultClass *res = self->result;int retval, result;Int2 num_cols, lf;Oid type;char *value;ColumnInfoClass *ci;// TupleField *tupleField; self->last_fetch_count = 0; ci = QR_get_fields(res); /* the column info */ mylog("manual_result = %d, use_declarefetch = %d\n", self->manual_result, globals.use_declarefetch); if ( self->manual_result || ! globals.use_declarefetch) { if (self->currTuple >= QR_get_num_tuples(res) -1 || (self->options.maxRows > 0 && self->currTuple == self->options.maxRows - 1)) { /* if at the end of the tuples, return "no data found" and set the cursor past the end of the result set */ self->currTuple = QR_get_num_tuples(res); return SQL_NO_DATA_FOUND; } mylog("**** SQLFetch: manual_result\n"); (self->currTuple)++; } else { // read from the cache or the physical next tuple retval = QR_next_tuple(res); if (retval < 0) { mylog("**** SQLFetch: end_tuples\n"); return SQL_NO_DATA_FOUND; } else if (retval > 0) (self->currTuple)++; // all is well else { mylog("SQLFetch: error\n"); self->errornumber = STMT_EXEC_ERROR; self->errormsg = "Error fetching next row"; SC_log_error(func, "", self); return SQL_ERROR; } } num_cols = QR_NumResultCols(res); result = SQL_SUCCESS; self->last_fetch_count = 1; /* If the bookmark column was bound then return a bookmark. Since this is used with SQLExtendedFetch, and the rowset size may be greater than 1, and an application can use row or column wise binding, use the code in copy_and_convert_field() to handle that. */ if (self->bookmark.buffer) { char buf[32]; sprintf(buf, "%ld", SC_get_bookmark(self)); result = copy_and_convert_field(self, 0, buf, SQL_C_ULONG, self->bookmark.buffer, 0, self->bookmark.used); } for (lf=0; lf < num_cols; lf++) { mylog("fetch: cols=%d, lf=%d, self = %u, self->bindings = %u, buffer[] = %u\n", num_cols, lf, self, self->bindings, self->bindings[lf].buffer); /* reset for SQLGetData */ self->bindings[lf].data_left = -1; if (self->bindings[lf].buffer != NULL) { // this column has a binding // type = QR_get_field_type(res, lf); type = CI_get_oid(ci, lf); /* speed things up */ mylog("type = %d\n", type); if (self->manual_result) { value = QR_get_value_manual(res, self->currTuple, lf); mylog("manual_result\n"); } else if (globals.use_declarefetch) value = QR_get_value_backend(res, lf); else { value = QR_get_value_backend_row(res, self->currTuple, lf); } mylog("value = '%s'\n", (value==NULL)?"<NULL>":value); retval = copy_and_convert_field_bindinfo(self, type, value, lf); mylog("copy_and_convert: retval = %d\n", retval); switch(retval) { case COPY_OK: break; /* OK, do next bound column */ case COPY_UNSUPPORTED_TYPE: self->errormsg = "Received an unsupported type from Postgres."; self->errornumber = STMT_RESTRICTED_DATA_TYPE_ERROR; SC_log_error(func, "", self); result = SQL_ERROR; break; case COPY_UNSUPPORTED_CONVERSION: self->errormsg = "Couldn't handle the necessary data type conversion."; self->errornumber = STMT_RESTRICTED_DATA_TYPE_ERROR; SC_log_error(func, "", self); result = SQL_ERROR; break; case COPY_RESULT_TRUNCATED: self->errornumber = STMT_TRUNCATED; self->errormsg = "The buffer was too small for the result."; result = SQL_SUCCESS_WITH_INFO; break; case COPY_GENERAL_ERROR: /* error msg already filled in */ SC_log_error(func, "", self); result = SQL_ERROR; break; /* This would not be meaningful in SQLFetch. */ case COPY_NO_DATA_FOUND: break; default: self->errormsg = "Unrecognized return value from copy_and_convert_field."; self->errornumber = STMT_INTERNAL_ERROR; SC_log_error(func, "", self); result = SQL_ERROR; break; } } } return result;}RETCODE SC_execute(StatementClass *self){static char *func="SC_execute";ConnectionClass *conn;QResultClass *res;char ok, was_ok, was_nonfatal;Int2 oldstatus, numcols;QueryInfo qi; conn = SC_get_conn(self); /* Begin a transaction if one is not already in progress */ /* The reason is because we can't use declare/fetch cursors without starting a transaction first. */ if ( ! self->internal && ! CC_is_in_trans(conn) && (globals.use_declarefetch || STMT_UPDATE(self))) { mylog(" about to begin a transaction on statement = %u\n", self); res = CC_send_query(conn, "BEGIN", NULL); if ( ! res) { self->errormsg = "Could not begin a transaction"; self->errornumber = STMT_EXEC_ERROR; SC_log_error(func, "", self); return SQL_ERROR; } ok = QR_command_successful(res); mylog("SQLExecute: ok = %d, status = %d\n", ok, QR_get_status(res)); QR_Destructor(res); if (!ok) { self->errormsg = "Could not begin a transaction"; self->errornumber = STMT_EXEC_ERROR; SC_log_error(func, "", self); return SQL_ERROR; } else CC_set_in_trans(conn); } oldstatus = conn->status; conn->status = CONN_EXECUTING; self->status = STMT_EXECUTING; // If its a SELECT statement, use a cursor. // Note that the declare cursor has already been prepended to the statement // in copy_statement... if (self->statement_type == STMT_TYPE_SELECT) { char fetch[128]; mylog(" Sending SELECT statement on stmt=%u, cursor_name='%s'\n", self, self->cursor_name); /* send the declare/select */ self->result = CC_send_query(conn, self->stmt_with_params, NULL); if (globals.use_declarefetch && self->result != NULL) { QR_Destructor(self->result); /* That worked, so now send the fetch to start getting data back */ qi.result_in = NULL; qi.cursor = self->cursor_name; qi.row_size = globals.fetch_max; /* Most likely the rowset size will not be set by the application until after the statement is executed, so might as well use the cache size. The qr_next_tuple() function will correct for any discrepancies in sizes and adjust the cache accordingly. */ sprintf(fetch, "fetch %d in %s", qi.row_size, self->cursor_name); self->result = CC_send_query( conn, fetch, &qi); } mylog(" done sending the query:\n"); } else { // not a SELECT statement so don't use a cursor mylog(" its NOT a select statement: stmt=%u\n", self); self->result = CC_send_query(conn, self->stmt_with_params, NULL); // If we are in autocommit, we must send the commit. if ( ! self->internal && CC_is_in_autocommit(conn) && STMT_UPDATE(self)) { CC_send_query(conn, "COMMIT", NULL); CC_set_no_trans(conn); } } conn->status = oldstatus; self->status = STMT_FINISHED; /* Check the status of the result */ if (self->result) { was_ok = QR_command_successful(self->result); was_nonfatal = QR_command_nonfatal(self->result); if ( was_ok) self->errornumber = STMT_OK; else self->errornumber = was_nonfatal ? STMT_INFO_ONLY : STMT_ERROR_TAKEN_FROM_BACKEND; self->currTuple = -1; /* set cursor before the first tuple in the list */ self->current_col = -1; self->rowset_start = -1; /* see if the query did return any result columns */ numcols = QR_NumResultCols(self->result); /* now allocate the array to hold the binding info */ if (numcols > 0) { extend_bindings(self, numcols); if (self->bindings == NULL) { self->errornumber = STMT_NO_MEMORY_ERROR; self->errormsg = "Could not get enough free memory to store the binding information"; SC_log_error(func, "", self); return SQL_ERROR; } } } else { /* Bad Error -- The error message will be in the Connection */ if (self->statement_type == STMT_TYPE_CREATE) { self->errornumber = STMT_CREATE_TABLE_ERROR; self->errormsg = "Error creating the table"; /* This would allow the table to already exists, thus appending rows to it. BUT, if the table didn't have the same attributes, it would fail. return SQL_SUCCESS_WITH_INFO; */ } else { self->errornumber = STMT_EXEC_ERROR; self->errormsg = "Error while executing the query"; } if ( ! self->internal) CC_abort(conn); } if (self->errornumber == STMT_OK) return SQL_SUCCESS; else if (self->errornumber == STMT_INFO_ONLY) return SQL_SUCCESS_WITH_INFO; else { SC_log_error(func, "", self); return SQL_ERROR; }}voidSC_log_error(char *func, char *desc, StatementClass *self){ if (self) { qlog("STATEMENT ERROR: func=%s, desc='%s', errnum=%d, errmsg='%s'\n", func, desc, self->errornumber, self->errormsg); mylog("STATEMENT ERROR: func=%s, desc='%s', errnum=%d, errmsg='%s'\n", func, desc, self->errornumber, self->errormsg); qlog(" ------------------------------------------------------------\n"); qlog(" hdbc=%u, stmt=%u, result=%u\n", self->hdbc, self, self->result); qlog(" manual_result=%d, prepare=%d, internal=%d\n", self->manual_result, self->prepare, self->internal); qlog(" bindings=%u, bindings_allocated=%d\n", self->bindings, self->bindings_allocated); qlog(" parameters=%u, parameters_allocated=%d\n", self->parameters, self->parameters_allocated); qlog(" statement_type=%d, statement='%s'\n", self->statement_type, self->statement); qlog(" stmt_with_params='%s'\n", self->stmt_with_params); qlog(" data_at_exec=%d, current_exec_param=%d, put_data=%d\n", self->data_at_exec, self->current_exec_param, self->put_data); qlog(" currTuple=%d, current_col=%d, lobj_fd=%d\n", self->currTuple, self->current_col, self->lobj_fd); qlog(" maxRows=%d, rowset_size=%d, keyset_size=%d, cursor_type=%d, scroll_concurrency=%d\n", self->options.maxRows, self->options.rowset_size, self->options.keyset_size, self->options.cursor_type, self->options.scroll_concurrency); qlog(" cursor_name='%s'\n", self->cursor_name); qlog(" ----------------QResult Info -------------------------------\n"); if (self->result) { QResultClass *res = self->result; qlog(" fields=%u, manual_tuples=%u, backend_tuples=%u, tupleField=%d, conn=%u\n", res->fields, res->manual_tuples, res->backend_tuples, res->tupleField, res->conn); qlog(" fetch_count=%d, fcount=%d, num_fields=%d, cursor='%s'\n", res->fetch_count, res->fcount, res->num_fields, res->cursor); qlog(" message='%s', command='%s', notice='%s'\n", res->message, res->command, res->notice); qlog(" status=%d, inTuples=%d\n", res->status, res->inTuples); } // Log the connection error if there is one CC_log_error(func, desc, self->hdbc); } else qlog("INVALID STATEMENT HANDLE ERROR: func=%s, desc='%s'\n", func, desc);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -