/** * \ingroup odbc_bcp * \brief Bind a program host variable to a database column * * \param dbc ODBC database connection object * \param varaddr address of host variable * \param prefixlen length of any prefix found at the beginning of \a varaddr, in bytes. * Use zero for fixed-length datatypes. * \param varlen bytes of data in \a varaddr. Zero for NULL, -1 for fixed-length datatypes. * \param terminator byte sequence that marks the end of the data in \a varaddr * \param termlen length of \a terminator * \param vartype datatype of the host variable * \param table_column Nth column, starting at 1, in the table. * * \remarks The order of operation is: * - bcp_init() with \a hfile == NULL and \a direction == DB_IN. * - bcp_bind(), once per column you want to write to * - bcp_batch(), optionally, to commit a set of rows * - bcp_done() * * \sa odbc_bcp_batch(), odbc_bcp_done(), odbc_bcp_sendrow() */ void odbc_bcp_bind(TDS_DBC *dbc, const BYTE * varaddr, int prefixlen, int varlen, const BYTE * terminator, int termlen, int vartype, int table_column) { TDSCOLUMN *colinfo; tdsdump_log(TDS_DBG_FUNC, "bcp_bind(%p, %p, %d, %d -- %p, %d, %d, %d)\n", dbc, varaddr, prefixlen, varlen, terminator, termlen, vartype, table_column); if (!dbc->bcpinfo) ODBCBCP_ERROR_RETURN("HY010"); if (dbc->bcpinfo->direction != BCP_DIRECTION_IN) ODBCBCP_ERROR_RETURN("HY010"); if (varlen < -1 && varlen != SQL_VARLEN_DATA) ODBCBCP_ERROR_RETURN("HY009"); if (prefixlen != 0 && prefixlen != 1 && prefixlen != 2 && prefixlen != 4 && prefixlen != 8) ODBCBCP_ERROR_RETURN("HY009"); if (prefixlen == 0 && varlen == SQL_VARLEN_DATA && termlen == -1 && !is_fixed_type(vartype)) { tdsdump_log(TDS_DBG_FUNC, "bcp_bind(): non-fixed type %d requires prefix or terminator\n", vartype); ODBCBCP_ERROR_RETURN("HY009"); } if (table_column <= 0 || table_column > dbc->bcpinfo->bindinfo->num_cols) ODBCBCP_ERROR_RETURN("HY009"); if (varaddr == NULL && (prefixlen != 0 || termlen != 0)) ODBCBCP_ERROR_RETURN("HY009"); colinfo = dbc->bcpinfo->bindinfo->columns[table_column - 1]; /* If varaddr is NULL and varlen greater than 0, the table column type must be SYBTEXT or SYBIMAGE and the program variable type must be SYBTEXT, SYBCHAR, SYBIMAGE or SYBBINARY */ if (varaddr == NULL && varlen >= 0) { int fOK = (colinfo->column_type == SYBTEXT || colinfo->column_type == SYBIMAGE) && (vartype == SYBTEXT || vartype == SYBCHAR || vartype == SYBIMAGE || vartype == SYBBINARY ); if( !fOK ) { tdsdump_log(TDS_DBG_FUNC, "bcp_bind: SYBEBCBNTYP: column=%d and vartype=%d (should fail?)\n", colinfo->column_type, vartype); ODBCBCP_ERROR_RETURN("HY009"); } } colinfo->column_varaddr = (char *)varaddr; colinfo->column_bindtype = vartype; colinfo->column_bindlen = varlen; TDS_ZERO_FREE(colinfo->bcp_terminator); colinfo->bcp_term_len = 0; if (termlen) { if ((colinfo->bcp_terminator = (TDS_CHAR*) malloc(termlen)) == NULL) ODBCBCP_ERROR_RETURN("HY001"); memcpy(colinfo->bcp_terminator, terminator, termlen); colinfo->bcp_term_len = termlen; } }
static void _odbc_blob_free(TDSCOLUMN *col) { if (!col->column_data) return; TDS_ZERO_FREE(col->column_data); }
void odbc_bcp_free_storage(TDS_DBC *dbc) { tdsdump_log(TDS_DBG_FUNC, "_bcp_free_storage(%p)\n", dbc); assert(dbc); tds_free_bcpinfo(dbc->bcpinfo); dbc->bcpinfo = NULL; TDS_ZERO_FREE(dbc->bcphint); }
/** * Fetch character data the wire. * Output is NOT null terminated. * If \a char_conv is not NULL, convert data accordingly. * \param tds state information for the socket and the TDS protocol * \param row_buffer destination buffer in current_row. Can't be NULL * \param wire_size size to read from wire (in bytes) * \param curcol column information * \return TDS_SUCCEED or TDS_FAIL (probably memory error on text data) * \todo put a TDSICONV structure in every TDSCOLUMN */ int tds_get_char_data(TDSSOCKET * tds, char *row_buffer, size_t wire_size, TDSCOLUMN * curcol) { size_t in_left; TDSBLOB *blob = NULL; char *dest = row_buffer; if (is_blob_col(curcol)) { blob = (TDSBLOB *) row_buffer; dest = blob->textvalue; } /* * dest is usually a column buffer, allocated when the column's metadata are processed * and reused for each row. * For blobs, dest is blob->textvalue, and can be reallocated or freed * TODO: reallocate if blob and no space */ /* silly case, empty string */ if (wire_size == 0) { curcol->column_cur_size = 0; if (blob) TDS_ZERO_FREE(blob->textvalue); return TDS_SUCCEED; } if (curcol->char_conv) { /* * TODO The conversion should be selected from curcol and tds version * TDS8/single -> use curcol collation * TDS7/single -> use server single byte * TDS7+/unicode -> use server (always unicode) * TDS5/4.2 -> use server * TDS5/UTF-8 -> use server * TDS5/UTF-16 -> use UTF-16 */ in_left = blob ? curcol->column_cur_size : curcol->column_size; curcol->column_cur_size = read_and_convert(tds, curcol->char_conv, &wire_size, &dest, &in_left); if (wire_size > 0) { tdsdump_log(TDS_DBG_NETWORK, "error: tds_get_char_data: discarded %u on wire while reading %d into client. \n", (unsigned int) wire_size, curcol->column_cur_size); return TDS_FAIL; } } else { curcol->column_cur_size = (TDS_INT)wire_size; if (tds_get_n(tds, dest, (int)wire_size) == NULL) { tdsdump_log(TDS_DBG_NETWORK, "error: tds_get_char_data: failed to read %u from wire. \n", (unsigned int) wire_size); return TDS_FAIL; } } return TDS_SUCCEED; }
SQLRETURN desc_free_records(TDS_DESC * desc) { int i; if (desc->records) { for (i = 0; i < desc->header.sql_desc_count; i++) desc_free_record(&desc->records[i]); TDS_ZERO_FREE(desc->records); } desc->header.sql_desc_count = 0; return SQL_SUCCESS; }
/** * Set the full name of interface file * @param interf file name */ TDSRET tds_set_interfaces_file_loc(const char *interf) { /* Free it if already set */ if (interf_file != NULL) TDS_ZERO_FREE(interf_file); /* If no filename passed, leave it NULL */ if ((interf == NULL) || (interf[0] == '\0')) { return TDS_SUCCESS; } /* Set to new value */ if ((interf_file = strdup(interf)) == NULL) { return TDS_FAIL; } return TDS_SUCCESS; }
void odbc_errs_reset(struct _sql_errors *errs) { int i; if (errs->errs) { for (i = 0; i < errs->num_errors; ++i) { /* TODO see flags */ free((char *) errs->errs[i].msg); free(errs->errs[i].server); } TDS_ZERO_FREE(errs->errs); errs->num_errors = 0; } errs->lastrc = SQL_SUCCESS; errs->ranked = 0; assert(errs->num_errors == 0); }
/** * \brief Call the client library's error handler (for library-generated errors only) * * The client library error handler may return: * TDS_INT_CANCEL -- Return TDS_FAIL to the calling function. For TDSETIME, closes the connection first. * TDS_INT_CONTINUE -- For TDSETIME only, retry the network read/write operation. Else invalid. * TDS_INT_TIMEOUT -- For TDSETIME only, send a TDSCANCEL packet. Else invalid. * * These are Sybase semantics, but they serve all purposes. * The application tells the library to quit, fail, retry, or attempt to cancel. In the event of a network timeout, * a failed operation necessarily means the connection becomes unusable, because no cancellation dialog was * concluded with the server. * * It is the client library's duty to call the error handler installed by the application, if any, and to interpret the * installed handler's return code. It may return to this function one of the above codes only. This function will not * check the return code because there's nothing that can be done here except abort. It is merely passed to the * calling function, which will (we hope) DTRT. * * \param tds_ctx points to a TDSCONTEXT structure * \param tds the connection structure, may be NULL if not connected * \param msgno an enumerated libtds msgno, cf. tds.h * \param errnum the OS errno, if it matters, else zero * * \returns client library function's return code */ int tdserror (const TDSCONTEXT * tds_ctx, TDSSOCKET * tds, int msgno, int errnum) { #if 0 static const char int_exit_text[] = "FreeTDS: libtds: exiting because client error handler returned %d for msgno %d\n"; static const char int_invalid_text[] = "%s (%d) received from client library error handler for nontimeout for error %d." " Treating as INT_EXIT\n"; #endif const TDS_ERROR_MESSAGE *err; TDSMESSAGE msg; int rc = TDS_INT_CANCEL; tdsdump_log(TDS_DBG_FUNC, "tdserror(%p, %p, %d, %d)\n", tds_ctx, tds, msgno, errnum); /* look up the error message */ for (err = tds_error_messages; err->msgno; ++err) { if (err->msgno == msgno) break; } CHECK_CONTEXT_EXTRA(tds_ctx); if (tds) CHECK_TDS_EXTRA(tds); if (tds_ctx && tds_ctx->err_handler) { memset(&msg, 0, sizeof(TDSMESSAGE)); msg.msgno = msgno; msg.severity = err->severity; msg.state = -1; msg.server = "OpenClient"; msg.line_number = -1; msg.message = (TDS_CHAR*) err->msgtext; msg.sql_state = tds_alloc_client_sqlstate(msg.msgno); msg.oserr = errnum; /* * Call client library handler. * The client library must return a valid code. It is not checked again here. */ rc = tds_ctx->err_handler(tds_ctx, tds, &msg); tdsdump_log(TDS_DBG_FUNC, "tdserror: client library returned %s(%d)\n", retname(rc), rc); TDS_ZERO_FREE(msg.sql_state); } else { const static char msg[] = "tdserror: client library not called because either " "tds_ctx (%p) or tds_ctx->err_handler is NULL\n"; tdsdump_log(TDS_DBG_FUNC, msg, tds_ctx); } assert(msgno == TDSETIME || rc != TDS_INT_TIMEOUT); /* client library should prevent */ assert(msgno == TDSETIME || rc != TDS_INT_CONTINUE); /* client library should prevent */ if (msgno != TDSETIME && rc != TDS_INT_CANCEL) { tdsdump_log(TDS_DBG_SEVERE, "exit: %s(%d) valid only for TDSETIME\n", retname(rc), rc); rc = TDS_INT_CANCEL; } if (rc == TDS_INT_TIMEOUT) { tds_send_cancel(tds); rc = TDS_INT_CONTINUE; } tdsdump_log(TDS_DBG_FUNC, "tdserror: returning %s(%d)\n", retname(rc), rc); return rc; }
/** * Prepare the query to be sent to server to request BCP information * \tds * \param bcpinfo BCP information */ static TDSRET tds_bcp_start_insert_stmt(TDSSOCKET * tds, TDSBCPINFO * bcpinfo) { char *query; if (IS_TDS7_PLUS(tds->conn)) { int i, firstcol, erc; char *hint; TDSCOLUMN *bcpcol; TDSPBCB colclause; char clause_buffer[4096] = { 0 }; colclause.pb = clause_buffer; colclause.cb = sizeof(clause_buffer); colclause.from_malloc = 0; /* TODO avoid asprintf, use always malloc-ed buffer */ firstcol = 1; for (i = 0; i < bcpinfo->bindinfo->num_cols; i++) { bcpcol = bcpinfo->bindinfo->columns[i]; if (bcpcol->column_timestamp || !tds_bcp_is_bound(bcpinfo, bcpcol)) continue; if (!bcpinfo->identity_insert_on && bcpcol->column_identity) continue; tds7_build_bulk_insert_stmt(tds, &colclause, bcpcol, firstcol); firstcol = 0; } if (bcpinfo->hint) { if (asprintf(&hint, " with (%s)", bcpinfo->hint) < 0) hint = NULL; } else { hint = strdup(""); } if (!hint) { if (colclause.from_malloc) TDS_ZERO_FREE(colclause.pb); return TDS_FAIL; } erc = asprintf(&query, "insert bulk %s (%s)%s", bcpinfo->tablename, colclause.pb, hint); free(hint); if (colclause.from_malloc) TDS_ZERO_FREE(colclause.pb); /* just for good measure; not used beyond this point */ if (erc < 0) return TDS_FAIL; } else { /* NOTE: if we use "with nodescribe" for following inserts server do not send describe */ if (asprintf(&query, "insert bulk %s", bcpinfo->tablename) < 0) return TDS_FAIL; } /* save the statement for later... */ bcpinfo->insert_stmt = query; return TDS_SUCCESS; }