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 + -
显示快捷键?