bcp.c

来自「在Linux/Unix下面访问WINDOWS SQLSERVER 的ODBC驱动」· C语言 代码 · 共 2,086 行 · 第 1/5 页

C
2,086
字号
/* FreeTDS - Library of routines accessing Sybase and Microsoft databases * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005  Brian Bruns * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */#if HAVE_CONFIG_H#include <config.h>#endif#include <stdarg.h>#include <stdio.h>#include <assert.h>#if HAVE_STRING_H#include <string.h>#endif /* HAVE_STRING_H */#if HAVE_STDLIB_H#include <stdlib.h>#endif /* HAVE_STDLIB_H */#if HAVE_UNISTD_H#include <unistd.h>#endif /* HAVE_UNISTD_H */#ifdef WIN32#include <io.h>#endif#include "tds.h"#include "tdsiconv.h"#include "tdsconvert.h"#include "replacements.h"#include "sybfront.h"#include "sybdb.h"#include "syberror.h"#include "dblib.h"#ifdef DMALLOC#include <dmalloc.h>#endif#define HOST_COL_CONV_ERROR 1#define HOST_COL_NULL_ERROR 2typedef enum { BCP_REC_NOFETCH_DATA = 0, BCP_REC_FETCH_DATA = 1 } BEHAVIOUR;#ifndef MAX#define MAX(a,b) ( (a) > (b) ? (a) : (b) )#endiftypedef struct _pbcb{	char *pb;	int cb;	unsigned int from_malloc;}TDS_PBCB;TDS_RCSID(var, "$Id: bcp.c,v 1.171 2008/01/08 15:38:31 jklowden Exp $");#ifdef HAVE_FSEEKOtypedef off_t offset_type;#elif defined(WIN32)/* win32 version */typedef __int64 offset_type;#define fseeko(f,o,w) (_lseeki64(fileno(f),o,w) == -1)#define ftello(f) _telli64(fileno(f))#else/* use old version */#define fseeko(f,o,w) fseek(f,o,w)#define ftello(f) ftell(f)typedef long offset_type;#endifstatic RETCODE _bcp_send_bcp_record(DBPROCESS * dbproc, BEHAVIOUR behaviour);static RETCODE _bcp_build_bulk_insert_stmt(TDSSOCKET *, TDS_PBCB *, TDSCOLUMN *, int);static void _bcp_free_storage(DBPROCESS * dbproc);static void _bcp_free_columns(DBPROCESS * dbproc);static RETCODE _bcp_get_col_data(DBPROCESS * dbproc, TDSCOLUMN *bindcol);static RETCODE _bcp_send_colmetadata(DBPROCESS *);static RETCODE _bcp_start_copy_in(DBPROCESS *);static RETCODE _bcp_start_new_batch(DBPROCESS *);static int rtrim(char *, int);static offset_type _bcp_measure_terminated_field(FILE * hostfile, BYTE * terminator, int term_len);static RETCODE _bcp_read_hostfile(DBPROCESS * dbproc, FILE * hostfile, int *row_error);static int _bcp_readfmt_colinfo(DBPROCESS * dbproc, char *buf, BCP_HOSTCOLINFO * ci);static RETCODE _bcp_get_term_var(BYTE * pdata, BYTE * term, int term_len);static void bcp_row_free(TDSRESULTINFO* result, unsigned char *row);/**  * \ingroup dblib_bcp * \brief Prepare for bulk copy operation on a table * * \param dbproc contains all information needed by db-lib to manage communications with the server. * \param tblname the name of the table receiving or providing the data. * \param hfile the data file opposite the table, if any.   * \param errfile the "error file" captures messages and, if errors are encountered,  * 	copies of any rows that could not be written to the table. * \param direction one of  *		- \b DB_IN writing to the table *		- \b DB_OUT writing to the host file * 		. * \remarks bcp_init() sets the host file data format and acquires the table metadata. *	It is called before the other bulk copy functions.  * * 	When writing to a table, bcp can use as its data source a data file (\a hfile),  * 	or program data in an application's variables.  In the latter case, call bcp_bind()  *	to associate your data with the appropriate table column.   * \return SUCCEED or FAIL. * \sa 	BCP_SETL(), bcp_bind(), bcp_done(), bcp_exec() */RETCODEbcp_init(DBPROCESS * dbproc, const char *tblname, const char *hfile, const char *errfile, int direction){	TDSRESULTINFO *resinfo;	TDSRESULTINFO *bindinfo;	TDSCOLUMN *curcol;	TDS_INT result_type;	int i, rc;	tdsdump_log(TDS_DBG_FUNC, "bcp_init(%p, %s, %s, %s, %d)\n", 			dbproc, tblname? tblname:"NULL", hfile? hfile:"NULL", errfile? errfile:"NULL", direction);	CHECK_DBPROC();	DBPERROR_RETURN(IS_TDSDEAD(dbproc->tds_socket), SYBEDDNE);	CHECK_NULP(tblname, "bcp_init", 2, FAIL);	/* Free previously allocated storage in dbproc & initialise flags, etc. */		_bcp_free_storage(dbproc);	/* 	 * Validate other parameters 	 */	if (dbproc->tds_socket->major_version < 5) {		dbperror(dbproc, SYBETDSVER, 0);		return (FAIL);	}	if (tblname == NULL) {		dbperror(dbproc, SYBEBCITBNM, 0);		return (FAIL);	}	if (strlen(tblname) > 92 && !IS_TDS7_PLUS(dbproc->tds_socket)) {	/* 30.30.30 */		dbperror(dbproc, SYBEBCITBLEN, 0);		return (FAIL);	}	if (direction != DB_IN && direction != DB_OUT && direction != DB_QUERYOUT) {		dbperror(dbproc, SYBEBDIO, 0);		return (FAIL);	}	if (hfile != NULL) {		dbproc->hostfileinfo = calloc(1, sizeof(BCP_HOSTFILEINFO));		if (dbproc->hostfileinfo == NULL)			goto memory_error;		if ((dbproc->hostfileinfo->hostfile = strdup(hfile)) == NULL)			goto memory_error;		if (errfile != NULL)			if ((dbproc->hostfileinfo->errorfile = strdup(errfile)) == NULL)				goto memory_error;	} else {		dbproc->hostfileinfo = NULL;	}	/* Allocate storage */		dbproc->bcpinfo = calloc(1, sizeof(DB_BCPINFO));	if (dbproc->bcpinfo == NULL)		goto memory_error;	if ((dbproc->bcpinfo->tablename = strdup(tblname)) == NULL)		goto memory_error;	dbproc->bcpinfo->direction = direction;	dbproc->bcpinfo->xfer_init  = 0;	dbproc->bcpinfo->var_cols   = 0;	dbproc->bcpinfo->bind_count = 0;	if (direction == DB_IN) {		TDSSOCKET *tds = dbproc->tds_socket;		if (tds_submit_queryf(tds, "SET FMTONLY ON select * from %s SET FMTONLY OFF", 								dbproc->bcpinfo->tablename) == TDS_FAIL) {			/* Attempt to use Bulk Copy with a non-existent Server table (might be why ...) */			dbperror(dbproc, SYBEBCNT, 0);			return FAIL;		}			/* TODO check what happen if table is not present, cleanup on error */		while ((rc = tds_process_tokens(tds, &result_type, NULL, TDS_TOKEN_RESULTS))		       == TDS_SUCCEED) {		}		if (rc != TDS_NO_MORE_RESULTS) {			return FAIL;		}			if (!tds->res_info) {			return FAIL;		}			/* TODO check what happen if table is not present, cleanup on error */		resinfo = tds->res_info;		if ((bindinfo = tds_alloc_results(resinfo->num_cols)) == NULL)			goto memory_error;			bindinfo->row_size = resinfo->row_size;			dbproc->bcpinfo->bindinfo = bindinfo;		dbproc->bcpinfo->bind_count = 0;			/* Copy the column metadata */		for (i = 0; i < bindinfo->num_cols; i++) {				curcol = bindinfo->columns[i];						/*			 * TODO use memcpy ??			 * curcol and resinfo->columns[i] are both TDSCOLUMN.  			 * Why not "curcol = resinfo->columns[i];"?  Because the rest of TDSCOLUMN (below column_timestamp)			 * isn't being used.  Perhaps this "upper" part of TDSCOLUMN should be a substructure.			 * Or, see if the "lower" part is unused (and zeroed out) at this point, and just do one assignment.			 */			curcol->column_type = resinfo->columns[i]->column_type;			curcol->column_usertype = resinfo->columns[i]->column_usertype;			curcol->column_flags = resinfo->columns[i]->column_flags;			curcol->column_size = resinfo->columns[i]->column_size;			curcol->column_varint_size = resinfo->columns[i]->column_varint_size;			curcol->column_prec = resinfo->columns[i]->column_prec;			curcol->column_scale = resinfo->columns[i]->column_scale;			curcol->column_namelen = resinfo->columns[i]->column_namelen;			curcol->on_server.column_type = resinfo->columns[i]->on_server.column_type;			curcol->on_server.column_size = resinfo->columns[i]->on_server.column_size;			curcol->char_conv = resinfo->columns[i]->char_conv;			memcpy(curcol->column_name, resinfo->columns[i]->column_name, resinfo->columns[i]->column_namelen);			TDS_ZERO_FREE(curcol->table_column_name);			if (resinfo->columns[i]->table_column_name)				curcol->table_column_name = strdup(resinfo->columns[i]->table_column_name);			curcol->column_nullable = resinfo->columns[i]->column_nullable;			curcol->column_identity = resinfo->columns[i]->column_identity;			curcol->column_timestamp = resinfo->columns[i]->column_timestamp;						memcpy(curcol->column_collation, resinfo->columns[i]->column_collation, 5);						if (is_numeric_type(curcol->column_type)) {				curcol->bcp_column_data = tds_alloc_bcp_column_data(sizeof(TDS_NUMERIC));				((TDS_NUMERIC *) curcol->bcp_column_data->data)->precision = curcol->column_prec;				((TDS_NUMERIC *) curcol->bcp_column_data->data)->scale = curcol->column_scale;			} else {				curcol->bcp_column_data = 					tds_alloc_bcp_column_data(MAX(curcol->column_size,curcol->on_server.column_size));			}		}		bindinfo->current_row = malloc(bindinfo->row_size);		if (!bindinfo->current_row)			goto memory_error;		bindinfo->row_free = bcp_row_free;		return SUCCEED;	}	return SUCCEED;memory_error:	_bcp_free_storage(dbproc);	dbperror(dbproc, SYBEMEM, ENOMEM);	return FAIL;}/**  * \ingroup dblib_bcp * \brief Set the length of a host variable to be written to a table. *  * \param dbproc contains all information needed by db-lib to manage communications with the server. * \param varlen size of the variable, in bytes, or * 	- \b 0 indicating NULL *	- \b -1 indicating size is determined by the prefix or terminator.   *		(If both a prefix and a terminator are present, bcp is supposed to use the smaller of the  *		 two.  This feature might or might not actually work.) * \param table_column the number of the column in the table (starting with 1, not zero). *  * \return SUCCEED or FAIL. * \sa 	bcp_bind(), bcp_colptr(), bcp_sendrow()  */RETCODEbcp_collen(DBPROCESS * dbproc, DBINT varlen, int table_column){	TDSCOLUMN *bcpcol;	tdsdump_log(TDS_DBG_FUNC, "bcp_collen(%p, %d, %d)\n", dbproc, varlen, table_column);		CHECK_DBPROC();	DBPERROR_RETURN(IS_TDSDEAD(dbproc->tds_socket), SYBEDDNE);	CHECK_PARAMETER(dbproc->bcpinfo, SYBEBCPI, FAIL);		/* not initialized */	DBPERROR_RETURN(dbproc->bcpinfo->direction != DB_IN, SYBEBCPN)	/* not inbound */	DBPERROR_RETURN(dbproc->hostfileinfo != NULL, SYBEBCPI)		/* cannot have data file */	CHECK_PARAMETER(0 < table_column && 			    table_column <= dbproc->bcpinfo->bindinfo->num_cols, SYBECNOR, FAIL);		bcpcol = dbproc->bcpinfo->bindinfo->columns[table_column - 1];	DBPERROR_RETURN(varlen == 0  && !bcpcol->column_nullable, SYBEBCNN); /* non-nullable column cannot receive a NULL */	bcpcol->column_bindlen = varlen;	return SUCCEED;}/**  * \ingroup dblib_bcp * \brief Indicate how many columns are to be found in the datafile. *  * \param dbproc contains all information needed by db-lib to manage communications with the server. * \param host_colcount count of columns in the datafile, irrespective of how many you intend to use.  * \remarks This function describes the file as it is, not how it will be used.   *  * \return SUCCEED or FAIL.  It's hard to see how it could fail.   * \sa 	bcp_colfmt() 	 */RETCODEbcp_columns(DBPROCESS * dbproc, int host_colcount){	int i;	tdsdump_log(TDS_DBG_FUNC, "bcp_columns(%p, %d)\n", dbproc, host_colcount);	CHECK_DBPROC();	DBPERROR_RETURN(IS_TDSDEAD(dbproc->tds_socket), SYBEDDNE);	CHECK_PARAMETER(dbproc->bcpinfo, SYBEBCPI, FAIL);	CHECK_PARAMETER(dbproc->hostfileinfo, SYBEBIVI, FAIL);	if (host_colcount < 1) {		dbperror(dbproc, SYBEBCFO, 0);		return FAIL;	}	_bcp_free_columns(dbproc);	dbproc->hostfileinfo->host_columns = calloc(host_colcount, sizeof(BCP_HOSTCOLINFO *));	if (dbproc->hostfileinfo->host_columns == NULL) {		dbperror(dbproc, SYBEMEM, ENOMEM);		return FAIL;	}	dbproc->hostfileinfo->host_colcount = host_colcount;	for (i = 0; i < host_colcount; i++) {		dbproc->hostfileinfo->host_columns[i] = calloc(1, sizeof(BCP_HOSTCOLINFO));		if (dbproc->hostfileinfo->host_columns[i] == NULL) {			dbproc->hostfileinfo->host_colcount = i;			_bcp_free_columns(dbproc);			dbperror(dbproc, SYBEMEM, ENOMEM);			return FAIL;		}	}	return SUCCEED;}/**  * \ingroup dblib_bcp * \brief Specify the format of a datafile prior to writing to a table. *  * \param dbproc contains all information needed by db-lib to manage communications with the server. * \param host_colnum datafile column number (starting with 1, not zero). * \param host_type dataype token describing the data type in \a host_colnum.  E.g. SYBCHAR for character data. * \param host_prefixlen size of the prefix in the datafile column, if any.  For delimited files: zero.   *			May be 0, 1, 2, or 4 bytes.  The prefix will be read as an integer (not a character string) from the  * 			data file, and will be interpreted the data size of that column, in bytes.   * \param host_collen maximum size of datafile column, exclusive of any prefix/terminator.  Just the data, ma'am.   *		Special values: *			- \b 0 indicates NULL.   *			- \b -1 for fixed-length non-null datatypes *			- \b -1 for variable-length datatypes (e.g. SYBCHAR) where the length is established  *				by a prefix/terminator.   * \param host_term the sequence of characters that will serve as a column terminator (delimiter) in the datafile.   * 			Often a tab character, but can be any string of any length.  Zero indicates no terminator.   * 			Special characters: *				- \b '\\0' terminator is an ASCII NUL. *				- \b '\\t' terminator is an ASCII TAB. *				- \b '\\n' terminator is an ASCII NL. * \param host_termlen the length of \a host_term, in bytes.  * \param table_colnum Nth column, starting at 1, in the table that maps to \a host_colnum.   * 	If there is a column in the datafile that does not map to a table column, set \a table_colnum to zero.   * *\remarks  bcp_colfmt() is called once for each column in the datafile, as specified with bcp_columns().   * In so doing, you describe to FreeTDS how to parse each line of your datafile, and where to send each field.   * * When a prefix or terminator is used with variable-length data, \a host_collen may have one of three values: *		- \b positive indicating the maximum number of bytes to be used * 		- \b 0 indicating NULL *		- \b -1 indicating no maximum; all data, as described by the prefix/terminator will be used.   *		. * \return SUCCEED or FAIL. * \sa 	bcp_batch(), bcp_bind(), bcp_colfmt_ps(), bcp_collen(), bcp_colptr(), bcp_columns(), *	bcp_control(), bcp_done(), bcp_exec(), bcp_init(), bcp_sendrow

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?