📄 dblib.c
字号:
TDS_MUTEX_UNLOCK(&dblib_mutex); dblib_release_tds_ctx(count);}static const char *prdbresults_state(int retcode){ static char unknown[12]="oops: "; switch(retcode) { case _DB_RES_INIT: return "_DB_RES_INIT"; case _DB_RES_RESULTSET_EMPTY: return "_DB_RES_RESULTSET_EMPTY"; case _DB_RES_RESULTSET_ROWS: return "_DB_RES_RESULTSET_ROWS"; case _DB_RES_NEXT_RESULT: return "_DB_RES_NEXT_RESULT"; case _DB_RES_NO_MORE_RESULTS: return "_DB_RES_NO_MORE_RESULTS"; case _DB_RES_SUCCEED: return "_DB_RES_SUCCEED"; default: sprintf(unknown + 6, "%u ??", retcode); } return unknown;}static const char *prdbretcode(int retcode){ static char unknown[12]="oops: "; switch(retcode) { case REG_ROW: return "REG_ROW/MORE_ROWS"; case NO_MORE_ROWS: return "NO_MORE_ROWS"; case BUF_FULL: return "BUF_FULL"; case NO_MORE_RESULTS: return "NO_MORE_RESULTS"; case SUCCEED: return "SUCCEED"; case FAIL: return "FAIL"; default: sprintf(unknown + 6, "%u ??", retcode); } return unknown;}static const char *prretcode(int retcode){ static char unknown[12]="oops"; switch(retcode) { case TDS_SUCCEED: return "TDS_SUCCEED"; case TDS_FAIL: return "TDS_FAIL"; case TDS_NO_MORE_RESULTS: return "TDS_NO_MORE_RESULTS"; case TDS_CANCELLED: return "TDS_CANCELLED"; default: sprintf(unknown, "%u ??", retcode); } return unknown;}static const char *prresult_type(int result_type){ static char unknown[12]="oops"; switch(result_type) { case TDS_ROW_RESULT: return "TDS_ROW_RESULT"; case TDS_PARAM_RESULT: return "TDS_PARAM_RESULT"; case TDS_STATUS_RESULT: return "TDS_STATUS_RESULT"; case TDS_MSG_RESULT: return "TDS_MSG_RESULT"; case TDS_COMPUTE_RESULT: return "TDS_COMPUTE_RESULT"; case TDS_CMD_DONE: return "TDS_CMD_DONE"; case TDS_CMD_SUCCEED: return "TDS_CMD_SUCCEED"; case TDS_CMD_FAIL: return "TDS_CMD_FAIL"; case TDS_ROWFMT_RESULT: return "TDS_ROWFMT_RESULT"; case TDS_COMPUTEFMT_RESULT: return "TDS_COMPUTEFMT_RESULT"; case TDS_DESCRIBE_RESULT: return "TDS_DESCRIBE_RESULT"; case TDS_DONE_RESULT: return "TDS_DONE_RESULT"; case TDS_DONEPROC_RESULT: return "TDS_DONEPROC_RESULT"; case TDS_DONEINPROC_RESULT: return "TDS_DONEINPROC_RESULT"; case TDS_OTHERS_RESULT: return "TDS_OTHERS_RESULT"; default: sprintf(unknown, "%u ??", result_type); } return unknown;}/** * \ingroup dblib_core * \brief Set up query results. * * \param dbproc contains all information needed by db-lib to manage communications with the server. * \retval SUCCEED Some results are available. * \retval FAIL query was not processed successfully by the server * \retval NO_MORE_RESULTS query produced no results. * * \remarks Call dbresults() after calling dbsqlexec() or dbsqlok(), or dbrpcsend() returns SUCCEED. Unless * one of them fails, dbresults will return either SUCCEED or NO_MORE_RESULTS. * * The meaning of \em results is very specific and not very intuitive. Results are created by either * - a SELECT statement * - a stored procedure * * When dbresults returns SUCCEED, therefore, it indicates the server processed the query successfully and * that one or more of these is present: * - metadata -- dbnumcols() returns 1 or more * - data -- dbnextrow() returns SUCCEED * - return status -- dbhasretstat() returns TRUE * - output parameters -- dbnumrets() returns 1 or more * * If none of the above are present, dbresults() returns NO_MORE_RESULTS. * * SUCCEED does not imply that DBROWS() will return TRUE or even that dbnumcols() will return nonzero. * A general algorithm for reading results will call dbresults() until it return NO_MORE_RESULTS (or FAIL). * An application should check for all the above kinds of results within the dbresults() loop. * * \sa dbsqlexec(), dbsqlok(), dbrpcsend(), dbcancel(), DBROWS(), dbnextrow(), dbnumcols(), dbhasretstat(), dbretstatus(), dbnumrets() */RETCODEdbresults(DBPROCESS * dbproc){ RETCODE erc = _dbresults(dbproc); tdsdump_log(TDS_DBG_FUNC, "dbresults returning %d (%s)\n", erc, prdbretcode(erc)); return erc;}static RETCODE_dbresults(DBPROCESS * dbproc){ RETCODE retcode = FAIL; TDSSOCKET *tds; int result_type; int done_flags; tdsdump_log(TDS_DBG_FUNC, "dbresults(%p)\n", dbproc); CHECK_DBPROC(); DBPERROR_RETURN(IS_TDSDEAD(dbproc->tds_socket), SYBEDDNE); tds = dbproc->tds_socket; if (IS_TDSDEAD(tds)) return FAIL; tdsdump_log(TDS_DBG_FUNC, "dbresults: dbresults_state is %d (%s)\n", dbproc->dbresults_state, prdbresults_state(dbproc->dbresults_state)); switch ( dbproc->dbresults_state ) { case _DB_RES_SUCCEED: dbproc->dbresults_state = _DB_RES_NEXT_RESULT; return SUCCEED; break; case _DB_RES_RESULTSET_ROWS: dbperror(dbproc, SYBERPND, 0); /* dbresults called while rows outstanding.... */ return FAIL; break; case _DB_RES_NO_MORE_RESULTS: return NO_MORE_RESULTS; break; default: break; } for (;;) { retcode = tds_process_tokens(tds, &result_type, &done_flags, TDS_TOKEN_RESULTS); tdsdump_log(TDS_DBG_FUNC, "dbresults() tds_process_tokens returned %d (%s),\n\t\t\tresult_type %s\n", retcode, prretcode(retcode), prresult_type(result_type)); switch (retcode) { case TDS_SUCCEED: switch (result_type) { case TDS_ROWFMT_RESULT: buffer_free(&dbproc->row_buf); buffer_alloc(dbproc); dbproc->dbresults_state = _DB_RES_RESULTSET_EMPTY; break; case TDS_COMPUTEFMT_RESULT: break; case TDS_ROW_RESULT: case TDS_COMPUTE_RESULT: dbproc->dbresults_state = _DB_RES_RESULTSET_ROWS; return SUCCEED; break; case TDS_DONE_RESULT: case TDS_DONEPROC_RESULT: tdsdump_log(TDS_DBG_FUNC, "dbresults(): dbresults_state is %d (%s)\n", dbproc->dbresults_state, prdbresults_state(dbproc->dbresults_state)); /* A done token signifies the end of a logical command. * There are three possibilities: * 1. Simple command with no result set, i.e. update, delete, insert * 2. Command with result set but no rows * 3. Command with result set and rows */ switch (dbproc->dbresults_state) { case _DB_RES_INIT: case _DB_RES_NEXT_RESULT: dbproc->dbresults_state = _DB_RES_NEXT_RESULT; if (done_flags & TDS_DONE_ERROR) return FAIL; break; case _DB_RES_RESULTSET_EMPTY: case _DB_RES_RESULTSET_ROWS: dbproc->dbresults_state = _DB_RES_NEXT_RESULT; return SUCCEED; break; default: assert(0); break; } case TDS_DONEINPROC_RESULT: /* * Return SUCCEED on a command within a stored procedure * only if the command returned a result set. */ switch (dbproc->dbresults_state) { case _DB_RES_INIT: case _DB_RES_NEXT_RESULT: dbproc->dbresults_state = _DB_RES_NEXT_RESULT; break; case _DB_RES_RESULTSET_EMPTY : case _DB_RES_RESULTSET_ROWS : dbproc->dbresults_state = _DB_RES_NEXT_RESULT; return SUCCEED; break; } break; case TDS_STATUS_RESULT: case TDS_MSG_RESULT: case TDS_DESCRIBE_RESULT: case TDS_PARAM_RESULT: default: break; } break; case TDS_NO_MORE_RESULTS: dbproc->dbresults_state = _DB_RES_NO_MORE_RESULTS; return NO_MORE_RESULTS; break; case TDS_CANCELLED: case TDS_FAIL: dbproc->dbresults_state = _DB_RES_INIT; return FAIL; break; default: tdsdump_log(TDS_DBG_FUNC, "dbresults() does not recognize return code from process_result_tokens\n"); assert(0); return FAIL; break; } }}/** * \ingroup dblib_core * \brief Return number of regular columns in a result set. * * \param dbproc contains all information needed by db-lib to manage communications with the server. * \sa dbcollen(), dbcolname(), dbnumalts(). */intdbnumcols(DBPROCESS * dbproc){ tdsdump_log(TDS_DBG_FUNC, "dbnumcols(%p)\n", dbproc); CHECK_PARAMETER(dbproc, SYBENULL, 0); if (dbproc && dbproc->tds_socket && dbproc->tds_socket->res_info) return dbproc->tds_socket->res_info->num_cols; return 0;}/** * \ingroup dblib_core * \brief Return name of a regular result column. * * \param dbproc contains all information needed by db-lib to manage communications with the server. * \param column Nth in the result set, starting with 1. * \return pointer to ASCII null-terminated string, the name of the column. * \retval NULL \a column is not in range. * \sa dbcollen(), dbcoltype(), dbdata(), dbdatlen(), dbnumcols(). * \bug Relies on ASCII column names, post iconv conversion. * Will not work as described for UTF-8 or UCS-2 clients. * But maybe it shouldn't. */char *dbcolname(DBPROCESS * dbproc, int column){ TDSCOLUMN *colinfo; tdsdump_log(TDS_DBG_FUNC, "dbcolname(%p, %d)\n", dbproc, column); colinfo = dbcolptr(dbproc, column); if (!colinfo) return NULL; assert(colinfo->column_name[colinfo->column_namelen] == 0); return colinfo->column_name;}/** * \ingroup dblib_core * \brief Read a row from the row buffer. * * When row buffering is enabled (DBBUFFER option is on), the client can use dbgetrow() to re-read a row previously fetched * with dbnextrow(). The effect is to move the row pointer -- analogous to fseek() -- back to \a row. * Calls to dbnextrow() read from \a row + 1 until the buffer is exhausted, at which point it resumes * its normal behavior, except that as each row is fetched from the server, it is added to the row * buffer (in addition to being returned to the client). When the buffer is filled, dbnextrow() returns * \c FAIL until the buffer is at least partially emptied with dbclrbuf(). * \param dbproc contains all information needed by db-lib to manage communications with the server. * \param row Nth row to read, starting with 1. * \retval REG_ROW returned row is a regular row. * \returns computeid when returned row is a compute row. * \retval NO_MORE_ROWS no such row in the row buffer. Current row is unchanged. * \retval FAIL unsuccessful; row buffer may be full. * \sa dbaltbind(), dbbind(), dbclrbuf(), DBCURROW(), DBFIRSTROW(), DBLASTROW(), dbnextrow(), dbsetrow(). */RETCODEdbgetrow(DBPROCESS * dbproc, DBINT row){ RETCODE result = FAIL; const int idx = buffer_row2idx(&dbproc->row_buf, row); tdsdump_log(TDS_DBG_FUNC, "dbgetrow(%p, %d)\n", dbproc, row); CHECK_DBPROC(); DBPERROR_RETURN(IS_TDSDEAD(dbproc->tds_socket), SYBEDDNE); if (-1 == idx) return NO_MORE_ROWS; dbproc->row_buf.current = idx; buffer_transfer_bound_data(&dbproc->row_buf, TDS_ROW_RESULT, 0, dbproc, idx); result = REG_ROW; return result;}/** * \ingroup dblib_core * \brief Define substitution values to be used when binding null values. * * \param dbproc contains all information needed by db-lib to manage communications with the server. * \param bindtype type of binding to which the substitute value will apply. * \param bindlen size of the substitute value you are supplying, in bytes. * Ignored except for CHARBIND and BINARYBIND. * \param bindval pointer to a buffer containing the substitute value. * \retval SUCCEED query was processed without errors. * \retval FAIL query was not processed * \sa dbaltbind(), dbbind(), dbconvert(), dbnullbind(). */RETCODEdbsetnull(DBPROCESS * dbproc, int bindtype, int bindlen, BYTE *bindval){ BYTE *pval; tdsdump_log(TDS_DBG_FUNC, "dbsetnull(%p, %d, %d, %p)\n", dbproc, bindtype, bindlen, bindval); CHECK_DBPROC(); DBPERROR_RETURN(IS_TDSDEAD(dbproc->tds_socket), SYBEDDNE); CHECK_PARAMETER(bindval, SYBENBVP, FAIL); switch (bindtype) { case DATETIMEBIND: case DECIMALBIND: case FLT8BIND: case INTBIND: case MONEYBIND: case NUMERICBIND: case REALBIND: case SMALLBIND: case SMALLDATETIMEBIND: case SMALLMONEYBIND: case TINYBIND: bindlen = default_null_representations[bindtype].len; break; case CHARBIND: case BINARYBIND: CHECK_PARAMETER(bindlen >= 0, SYBEBBL, FAIL); break; case NTBSTRINGBIND: bindlen = strlen((char *) bindval); break; case STRINGBIND: bindlen = strlen((char *) bindval); break; case VARYBINBIND: bindlen = ((TDS_VARBINARY*) bindval)->len; break; case VARYCHARBIND: bindlen = ((TDS_VARCHAR*) bindval)->len; break;#if 0 case SENSITIVITYBIND: case BOUNDARYBIND:#endif default: dbperror(dbproc, SYBEBTYP, 0); return FAIL; } if ((pval = malloc(bindlen)) == NULL) { dbperror(dbproc, SYBEMEM, errno); return FAIL; } /* free any prior allocation */ if (dbproc->nullreps[bindtype].bindval != default_null_representations[bindtype].bindval) free((BYTE*)dbproc->nullreps[bindtype].bindval); memcpy(pval, bindval, bindlen); dbproc->nullreps[bindtype].bindval = pval; dbproc->nullreps[bindtype].len = bindlen; tdsdump_dump_buf(TDS_DBG_NETWORK, "null representation set ", pval, bindlen); return SUCCEED;}/** * \ingroup dblib_core * \brief Make a buffered row "current" without fetching it into bound variables. * * \param dbproc contains all information needed by db-lib to manage communications with the server. * \retval MORE_ROWS row found * \retval NO_MORE_ROWS row not found * \retval FAIL \a dbproc is dead or not enabled * \sa dbaltbind(), dbbind(), dbcanquery(), dbclrbuf(), dbgetrow(), dbnextrow(), dbprrow(). */RETCODEdbsetrow(DBPROCESS * dbproc, DBINT row){ const int idx = buffer_row2idx(&dbproc->row_buf, row); tdsdump_log(TDS_DBG_FUNC, "dbsetrow(%p, %d)\n", dbproc, row); CHECK_DBPROC(); DBPERROR_RETURN(IS_TDSDEAD(dbproc->tds_socket), SYBEDDNE); if (-1 == idx) return NO_MORE_ROWS; dbproc->row_buf.current = idx; /* FIXME: should determine REG_ROW or compute_id; */ return REG_ROW; }/** * \ingroup dblib_core * \brief Read result row into the row buffer and into any bound host variables. * * \param dbproc contains all information needed by db-lib to manage communications with the server. * \retval REG_ROW regular row has been read. * \returns computeid when a compute row is read. * \retval BUF_FULL reading next row would cause the buffer to be exceeded (and buffering is turned on). * No row was read from the server * \sa dbaltbind(), dbbind(), dbcanquery(), dbclrbuf(), dbgetrow(), dbprrow(), dbsetrow(). */RETCODEdbnextrow(DBPROCESS * dbproc){ TDSRESULTINFO *resinfo; TDSSOCKET *tds; RETCODE result = FAIL; TDS_INT res_type; TDS_INT computeid; int idx; /* row buffer index. Unless DBUFFER is on, idx will always be 0. */ tdsdump_log(TDS_DBG_FUNC, "dbnextrow(%p)\n", dbproc); CHECK_DBPROC(); DBPERROR_RETURN(IS_TDSDEAD(dbproc->tds_socket), SYBEDDNE); tds = dbproc->tds_socket; if (IS_TDSDEAD(tds)) { dbperror(dbproc, SYBEDDNE, 0); return FAIL; } resinfo = tds->res_info;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -