Exemplo n.º 1
0
/**
 * \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;
    }
}
Exemplo n.º 2
0
static void
_odbc_blob_free(TDSCOLUMN *col)
{
        if (!col->column_data)
                return;

        TDS_ZERO_FREE(col->column_data);
}
Exemplo n.º 3
0
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);
}
Exemplo n.º 4
0
/**
 * 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;
}
Exemplo n.º 5
0
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;
}
Exemplo n.º 6
0
/**
 * 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;
}
Exemplo n.º 7
0
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);
}
Exemplo n.º 8
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;
}
Exemplo n.º 9
0
/**
 * 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;
}