bcp.c

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

C
2,086
字号
			if (len > 0x7fffffffl || len < 0) {				*row_error = TRUE;				tdsdump_log(TDS_DBG_FUNC, "_bcp_measure_terminated_field returned -1!\n");				dbperror(dbproc, SYBEBCOR, 0);				return (FAIL);			}			collen = len;			if (collen == 0)				data_is_null = 1;			tdsdump_log(TDS_DBG_FUNC, "_bcp_measure_terminated_field returned %d\n", collen);			/* 			 * Allocate a column buffer guaranteed to be big enough hold the post-iconv data.			 */			file_len = collen;			if (bcpcol->char_conv) {				if (bcpcol->on_server.column_size > bcpcol->column_size)					collen = (collen * bcpcol->on_server.column_size) / bcpcol->column_size;				cd = bcpcol->char_conv->to_wire;				tdsdump_log(TDS_DBG_FUNC, "Adjusted collen is %d.\n", collen);			} else {				cd = (iconv_t) - 1;			}			coldata = calloc(1, 1 + collen);			if (coldata == NULL) {				*row_error = TRUE;				tdsdump_log(TDS_DBG_FUNC, "calloc returned NULL pointer!\n");				dbperror(dbproc, SYBEMEM, errno);				return (FAIL);			}			/* 			 * Read and convert the data			 */			col_bytes_left = collen;			/* TODO make tds_iconv_fread handle terminator directly to avoid fseek in _bcp_measure_terminated_field */			file_bytes_left = tds_iconv_fread(cd, hostfile, file_len, hostcol->term_len, coldata, &col_bytes_left);			collen -= col_bytes_left;			/* tdsdump_log(TDS_DBG_FUNC, "collen is %d after tds_iconv_fread()\n", collen); */			if (file_bytes_left != 0) {				tdsdump_log(TDS_DBG_FUNC, "col %d: %d of %d bytes unread\nfile_bytes_left != 0!\n", 							(i+1), file_bytes_left, collen);				*row_error = TRUE;				free(coldata);				dbperror(dbproc, SYBEBCOR, 0);				return FAIL;			}			/*			 * TODO:  			 *    Dates are a problem.  In theory, we should be able to read non-English dates, which			 *    would contain non-ASCII characters.  One might suppose we should convert date			 *    strings to ISO-8859-1 (or another canonical form) here, because tds_convert() can't be			 *    expected to deal with encodings. But instead date strings are read verbatim and 			 *    passed to tds_convert() without even waving to iconv().  For English dates, this works, 			 *    because English dates expressed as UTF-8 strings are indistinguishable from the ASCII.  			 */		} else {	/* unterminated field */#if 0			bcpcol = dbproc->bcpinfo->bindinfo->columns[hostcol->tab_colnum - 1];			if (collen == 0 || bcpcol->column_nullable) {				if (collen != 0) {					/* A fixed length type */					TDS_TINYINT len;					if (fread(&len, sizeof(len), 1, hostfile) != 1) {						if (i != 0)							dbperror(dbproc, SYBEBCRE, errno);						return (FAIL);					}					if (len < 0)						dbperror(dbproc, SYBEBCNL, errno);											/* TODO 255 for NULL ?? check it, perhaps 0 */					collen = len == 255 ? -1 : len;				} else {					TDS_SMALLINT len;					if (fread(&len, sizeof(len), 1, hostfile) != 1) {						if (i != 0)							dbperror(dbproc, SYBEBCRE, errno);						return (FAIL);					}					if (len < 0)						dbperror(dbproc, SYBEBCNL, errno);					collen = len;				}				/* TODO if collen < -1 error */				if (collen <= -1) {					collen = 0;					data_is_null = 1;				}				tdsdump_log(TDS_DBG_FUNC, "Length read from hostfile: collen is now %d, data_is_null is %d\n", 							collen, data_is_null);			}#endif			coldata = (TDS_CHAR *) calloc(1, 1 + collen);			if (coldata == NULL) {				*row_error = TRUE;				dbperror(dbproc, SYBEMEM, errno);				return (FAIL);			}			if (collen) {				/* 				 * Read and convert the data				 * TODO: Call tds_iconv_fread() instead of fread(3).  				 *       The columns should each have their iconv cd set, and noncharacter data				 *       should have -1 as the iconv cd, causing tds_iconv_fread() to not attempt				 * 	 any conversion.  We do not need a datatype switch here to decide what to do.  				 *	 As of 0.62, this *should* actually work.  All that remains is to change the				 *	 call and test it. 				 */				tdsdump_log(TDS_DBG_FUNC, "Reading %d bytes from hostfile.\n", collen);				if (fread(coldata, collen, 1, hostfile) != 1) {					free(coldata);					return _bcp_check_eof(dbproc, hostfile, i);				}			}		}		/*		 * If we read no bytes and we're at end of file AND this is the first column, 		 * then we've stumbled across the finish line.  Tell the caller we failed to read 		 * anything but encountered no error.		 */		if (i == 0 && collen == 0 && feof(hostfile)) {			free(coldata);			tdsdump_log(TDS_DBG_FUNC, "Normal end-of-file reached while loading bcp data file.\n");			return NO_MORE_ROWS;		}		/* 		 * At this point, however the field was read, however big it was, its address is coldata and its size is collen.		 */		tdsdump_log(TDS_DBG_FUNC, "Data read from hostfile: collen is now %d, data_is_null is %d\n", collen, data_is_null);		if (hostcol->tab_colnum) {			if (data_is_null) {				bcpcol->bcp_column_data->is_null = 1;				bcpcol->bcp_column_data->datalen = 0;			} else {				bcpcol->bcp_column_data->is_null = 0;				desttype = tds_get_conversion_type(bcpcol->column_type, bcpcol->column_size);				/* special hack for text columns */				if (bcpcol->column_size == 4096 && collen > bcpcol->column_size) { /* "4096" might not matter */					BYTE *oldbuffer = bcpcol->bcp_column_data->data;					switch (desttype) {					case SYBTEXT:					case SYBNTEXT:					case SYBIMAGE:					case SYBVARBINARY:					case XSYBVARBINARY:					case SYBLONGBINARY:	/* Reallocate enough space for the data from the file. */						bcpcol->column_size = 8 + collen;	/* room to breathe */						bcpcol->bcp_column_data->data = 							(BYTE *) realloc(bcpcol->bcp_column_data->data, bcpcol->column_size);						if (!bcpcol->bcp_column_data->data) {							dbperror(dbproc, SYBEMEM, errno);							free(oldbuffer);							free(coldata);							return FAIL;						}						break;					default:						break;					}				}				/* end special hack for text columns */				/*				 * FIXME bcpcol->bcp_column_data->data && bcpcol->column_size ??				 * It seems a buffer overflow waiting...				 */				bcpcol->bcp_column_data->datalen =					dbconvert(dbproc, hostcol->datatype, (const BYTE *) coldata, collen, desttype, 									bcpcol->bcp_column_data->data, bcpcol->column_size);				if (bcpcol->bcp_column_data->datalen == -1) {					hostcol->column_error = HOST_COL_CONV_ERROR;					*row_error = 1;					/* FIXME possible integer overflow if off_t is 64bit and long int 32bit */					tdsdump_log(TDS_DBG_FUNC, 						"_bcp_read_hostfile failed to convert %d bytes at offset 0x%lx in the data file.\n", 						    collen, (unsigned long int) ftello(hostfile) - collen);				}				/* trim trailing blanks from character data */				if (desttype == SYBCHAR || desttype == SYBVARCHAR) {					bcpcol->bcp_column_data->datalen = rtrim((char *) bcpcol->bcp_column_data->data,											  bcpcol->bcp_column_data->datalen);				}			}			if (!hostcol->column_error) {				if (bcpcol->bcp_column_data->datalen <= 0) {	/* Are we trying to insert a NULL ? */					if (!bcpcol->column_nullable) {						/* too bad if the column is not nullable */						hostcol->column_error = HOST_COL_NULL_ERROR;						*row_error = 1;						dbperror(dbproc, SYBEBCNN, 0);					}				}			}		}		free(coldata);	}	return MORE_ROWS;}/* * Look for the next terminator in a host data file, and return the data size.   * \return size of field, excluding the terminator.   * \remarks The current offset will be unchanged.  If an error was encountered, the returned size will be -1.   * 	The caller should check for that possibility, but the appropriate message should already have been emitted.   * 	The caller can then use tds_iconv_fread() to read-and-convert the file's data  *	into host format, or, if we're not dealing with a character column, just fread(3).   *//**  * \ingroup dblib_bcp_internal * \brief  * * \param hostfile  * \param terminator  * \param term_len  *  * \return SUCCEED or FAIL. * \sa 	BCP_SETL(), bcp_batch(), bcp_bind(), bcp_colfmt(), bcp_colfmt_ps(), bcp_collen(), bcp_colptr(), bcp_columns(), bcp_control(), bcp_done(), bcp_exec(), bcp_getl(), bcp_init(), bcp_moretext(), bcp_options(), bcp_readfmt(), bcp_sendrow() */static offset_type_bcp_measure_terminated_field(FILE * hostfile, BYTE * terminator, int term_len){	char *sample;	int errnum;	int sample_size, bytes_read = 0;	offset_type size;	const offset_type initial_offset = ftello(hostfile);	tdsdump_log(TDS_DBG_FUNC, "_bcp_measure_terminated_field(%p, %p, %d)\n", hostfile, terminator, term_len);	if ((sample = malloc(term_len)) == NULL) {		dbperror(NULL, SYBEMEM, errno);		return -1;	}	for (sample_size = 1; (bytes_read = fread(sample, sample_size, 1, hostfile)) != 0;) {		bytes_read *= sample_size;		/*		 * Check for terminator.		 */		/*		 * TODO use memchr for performance, 		 * optimize this strange loop - freddy77		 */		if (*sample == *terminator) {			if (sample_size == term_len) {				/*				 * If we read a whole terminator, compare the whole sequence and, if found, go home. 				 */				if (memcmp(sample, terminator, term_len) == 0) {					free(sample);					size = ftello(hostfile) - initial_offset;					if (size < 0 || 0 != fseeko(hostfile, initial_offset, SEEK_SET)) {						/* FIXME emit message */						return -1;					}					return size - term_len;				}				/* 				 * If we tried to read a terminator and found something else, then we read a 				 * terminator's worth of data.  Back up N-1 bytes, and revert to byte-at-a-time testing.				 */				if (sample_size > 1) { 					sample_size--;					if (0 != fseeko(hostfile, -sample_size, SEEK_CUR)) {						/* FIXME emit message */						return -1;					}				}				sample_size = 1;				continue;			} else {				/* 				 * Found start of terminator, but haven't read a full terminator's length yet.  				 * Back up, read a whole terminator, and try again.				 */				assert(bytes_read == 1);				ungetc(*sample, hostfile);				sample_size = term_len;				continue;			}			assert(0); /* should not arrive here */		}	}	free(sample);	/*	 * To get here, we ran out of memory, or encountered an error (or EOF) with the file.  	 * EOF is a surprise, because if we read a complete field with its terminator, 	 * we would have returned without attempting to read past end of file.  	 */	if (feof(hostfile)) {		errnum = errno;		if (initial_offset == ftello(hostfile)) {			return 0;		} else {			/* a cheat: we don't have dbproc, so pass zero */			dbperror(0, SYBEBEOF, errnum);		}	} else if (ferror(hostfile)) {		dbperror(0, SYBEBCRE, errno);	}	return -1;}/** * Add fixed size columns to the row *//**  * \ingroup dblib_bcp_internal * \brief  * * \param dbproc contains all information needed by db-lib to manage communications with the server. * \param behaviour  * \param rowbuffer  * \param start  *  * \return SUCCEED or FAIL. * \sa 	BCP_SETL(), bcp_batch(), bcp_bind(), bcp_colfmt(), bcp_colfmt_ps(), bcp_collen(), bcp_colptr(), bcp_columns(), bcp_control(), bcp_done(), bcp_exec(), bcp_getl(), bcp_init(), bcp_moretext(), bcp_options(), bcp_readfmt(), bcp_sendrow() */static int_bcp_add_fixed_columns(DBPROCESS * dbproc, BEHAVIOUR behaviour, BYTE * rowbuffer, int start){	TDS_NUMERIC *num;	int row_pos = start;	TDSCOLUMN *bcpcol;	int cpbytes;	int i, j;	assert(dbproc);	assert(rowbuffer);	tdsdump_log(TDS_DBG_FUNC, "_bcp_add_fixed_columns(%p, %d, %p, %d)\n", dbproc, behaviour, rowbuffer, start);	for (i = 0; i < dbproc->bcpinfo->bindinfo->num_cols; i++) {		bcpcol = dbproc->bcpinfo->bindinfo->columns[i];		if (!is_nullable_type(bcpcol->column_type) && !(bcpcol->column_nullable)) {			tdsdump_log(TDS_DBG_FUNC, "_bcp_add_fixed_columns column %d is a fixed column\n", i + 1);			if (behaviour == BCP_REC_FETCH_DATA) { 				if ((_bcp_get_col_data(dbproc, bcpcol)) != SUCCEED) {					tdsdump_log(TDS_DBG_INFO1, "bcp_get_colData (column %d) failed\n", i + 1);		 			return FAIL;				}			}			if (bcpcol->bcp_column_data->is_null) {				dbperror(dbproc, SYBEBCNN, 0);				return FAIL;			}			if (is_numeric_type(bcpcol->column_type)) {				num = (TDS_NUMERIC *) bcpcol->bcp_column_data->data;				cpbytes = tds_numeric_bytes_per_prec[num->precision];				memcpy(&rowbuffer[row_pos], num->array, cpbytes);			} else {				cpbytes = bcpcol->bcp_column_data->datalen > bcpcol->column_size ? 					  bcpcol->column_size : bcpcol->bcp_column_data->datalen;				memcpy(&rowbuffer[row_pos], bcpcol->bcp_column_data->data, cpbytes);				/* CHAR data may need padding out to the database length with blanks */				if (bcpcol->column_type == SYBCHAR && cpbytes < bcpcol->column_size) {					for (j = cpbytes; j <  bcpcol->column_size; j++)						rowbuffer[row_pos + j] = ' ';				}			}			row_pos += bcpcol->column_size;		}	}	return row_pos;}/* * Add variable size columns to the row *//**  * \ingroup dblib_bcp_internal * \brief  * * \param dbproc contains all information needed by db-lib to manage communications with the server. * \param behaviour Whether the data are already in the TDSCOLUMN or should by copied from the buffer bound by bcp_bind(). * \param rowbuffer The row image that will be sent to the server.  * \param start Where to begin copying data into the rowbuffer.  * \param pncols Address of output variable holding the count of columns added to the rowbuffer.   *  * \return length of (potentially modified) rowbuffer, or FAIL. * \sa 	_bcp_send_bcp_record(), _bcp_get_col_data */static int_bcp_add_variable_columns(DBPROCESS * dbproc, BEHAVIOUR behaviour, BYTE * rowbuffer, int start, int *pncols){

⌨️ 快捷键说明

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