Example #1
0
/**
 * Fetch a string from the wire.
 * Output string is NOT null terminated.
 * If TDS version is 7 or 8 read unicode string and convert it.
 * This function should be use to read server default encoding strings like 
 * columns name, table names, etc, not for data (use tds_get_char_data instead)
 * @return bytes written to \a dest
 * @param tds  connection information
 * @param string_len length of string to read from wire 
 *        (in server characters, bytes for tds4-tds5, ucs2 for tds7+)
 * @param dest destination buffer, if NULL string is read and discarded
 * @param dest_size destination buffer size, in bytes
 */
int
tds_get_string(TDSSOCKET * tds, int string_len, char *dest, size_t dest_size)
{
	size_t wire_bytes;

	/*
	 * FIX: 02-Jun-2000 by Scott C. Gray (SCG)
	 * Bug to malloc(0) on some platforms.
	 */
	if (string_len == 0) {
		return 0;
	}

	assert(string_len >= 0 && dest_size >= 0);

	wire_bytes = IS_TDS7_PLUS(tds) ? string_len * 2 : string_len;

	if (IS_TDS7_PLUS(tds)) {
		if (dest == NULL) {
			tds_get_n(tds, NULL, (int)wire_bytes);
			return string_len;
		}

		return read_and_convert(tds, tds->char_convs[client2ucs2], &wire_bytes, &dest, &dest_size);
	} else {
		/* FIXME convert to client charset */
		assert(dest_size >= (size_t) string_len);
		tds_get_n(tds, dest, string_len);
		return string_len;
	}
}
Example #2
0
static void
unfinished_query_test(TDSSOCKET *tds)
{
	int char_len;
	char *buf, *p;
	int i, len;
	union {
		TDS_USMALLINT si;
		TDS_UINT i;
		TDS_INT8 i8;
		char buf[8];
	} conv;

	if (IS_TDS72_PLUS(tds->conn))
		return;

	tds_init_write_buf(tds);

	/* try to build an invalid (unfinished) query split in two packets */
	char_len = IS_TDS7_PLUS(tds->conn) ? 2 : 1;
	buf = calloc(1, tds->out_buf_max + 200);
	memset(buf, '-', tds->out_buf_max + 200);
	strcpy(buf + (tds->out_buf_max - 8) / char_len - strlen(select_query) + 1, select_query);
	memset(strchr(buf, 0), 0, 16);

	/* convert if needed */
	len = strlen(buf);
	for (i = len; --i >= 0; ) {
		char c = buf[i];
		buf[i * char_len + 0] = c;
		if (IS_TDS7_PLUS(tds->conn))
			buf[i * char_len + 1] = 0;
	}
	len *= char_len;

	/* send the query using tds_put_int8, non allineati */
	tds->out_flag = TDS_QUERY;
	if (tds_set_state(tds, TDS_WRITING) != TDS_WRITING)
		exit(1);
	p = buf;
	memcpy(conv.buf, p, 2);
	tds_put_smallint(tds, conv.si);
	p += 2;
	for (; p < buf + len; p += 8) {
		CHECK_TDS_EXTRA(tds);
		memcpy(conv.buf, p, 8);
		tds_put_int8(tds, conv.i8);
	}
	tds_flush_packet(tds);
	tds_set_state(tds, TDS_PENDING);

	/* check result was fine */
	if (TDS_FAILED(tds_process_simple_query(tds))) {
		fprintf(stderr, "Error in prepared query\n");
		exit(1);
	}
	free(buf);
}
Example #3
0
void
tds_send_login_ack(TDSSOCKET * tds, const char *progname)
{
	TDS_UINT ui, version;

	tds_put_byte(tds, TDS_LOGINACK_TOKEN);
	tds_put_smallint(tds, 10 + (IS_TDS7_PLUS(tds->conn)? 2 : 1) * strlen(progname));	/* length of message */
	if (IS_TDS50(tds->conn)) {
		tds_put_byte(tds, 5);
		version = 0x05000000u;
	} else {
		tds_put_byte(tds, 1);
		/* see src/tds/token.c */
		if (IS_TDS73_PLUS(tds->conn)) {
			version = 0x730B0003u;
		} else if (IS_TDS72_PLUS(tds->conn)) {
			version = 0x72090002u;
		} else if (IS_TDS71_PLUS(tds->conn)) {
			version = tds->conn->tds71rev1 ? 0x07010000u : 0x71000001u;
		} else {
			version = (TDS_MAJOR(tds->conn) << 24) | (TDS_MINOR(tds->conn) << 16);
		}
	}
	TDS_PUT_A4BE(&ui, version);
	tds_put_n(tds, &ui, 4);

	tds_put_byte(tds, strlen(progname));
	/* FIXME ucs2 */
	tds_put_string(tds, progname, strlen(progname));

	/* server version, always big endian */
	TDS_PUT_A4BE(&ui, tds->conn->product_version & 0x7fffffffu);
	tds_put_n(tds, &ui, 4);
}
void
tds_send_login_ack(TDSSOCKET * tds, const char *progname)
{
	tds_put_byte(tds, TDS_LOGINACK_TOKEN);
	tds_put_smallint(tds, 10 + (IS_TDS7_PLUS(tds->conn)? 2 : 1) * strlen(progname));	/* length of message */
	if (IS_TDS50(tds->conn)) {
		tds_put_byte(tds, 5);
		tds_put_byte(tds, 5);
		tds_put_byte(tds, 0);
	} else {
		tds_put_byte(tds, 1);
		tds_put_byte(tds, TDS_MAJOR(tds->conn));
		tds_put_byte(tds, TDS_MINOR(tds->conn));
	}
	tds_put_byte(tds, 0);	/* unknown */
	tds_put_byte(tds, 0);	/* unknown */
	tds_put_byte(tds, strlen(progname));
	/* FIXME ucs2 */
	tds_put_string(tds, progname, strlen(progname));
	/* server version, for mssql 1.0.1 */
	tds_put_byte(tds, 1);	/* unknown */
	tds_put_byte(tds, 0);	/* unknown */
	tds_put_byte(tds, 0);	/* unknown */
	tds_put_byte(tds, 1);	/* unknown */
}
Example #5
0
int
odbc_sql_to_server_type(TDSSOCKET * tds, int sql_type)
{

	switch (sql_type) {

	case SQL_CHAR:
		return SYBCHAR;
	case SQL_VARCHAR:
		return SYBVARCHAR;
	case SQL_LONGVARCHAR:
		return SYBTEXT;
	case SQL_DECIMAL:
		return SYBDECIMAL;
	case SQL_NUMERIC:
		return SYBNUMERIC;
	case SQL_GUID:
		if (IS_TDS7_PLUS(tds))
			return SYBUNIQUE;
		return 0;
	case SQL_BIT:
		return SYBBITN;
	case SQL_TINYINT:
		return SYBINT1;
	case SQL_SMALLINT:
		return SYBINT2;
	case SQL_INTEGER:
		return SYBINT4;
	case SQL_BIGINT:
		return SYBINT8;
	case SQL_REAL:
		return SYBREAL;
	case SQL_FLOAT:
	case SQL_DOUBLE:
		return SYBFLT8;
		/* ODBC version 2 */
	case SQL_DATE:
	case SQL_TIME:
	case SQL_TIMESTAMP:
		/* ODBC version 3 */
	case SQL_TYPE_DATE:
	case SQL_TYPE_TIME:
	case SQL_TYPE_TIMESTAMP:
		return SYBDATETIME;
	case SQL_BINARY:
		return SYBBINARY;
	case SQL_VARBINARY:
		return SYBVARBINARY;
	case SQL_LONGVARBINARY:
		return SYBIMAGE;
		/* TODO interval types */
	default:
		return 0;
	}
}
Example #6
0
static
#endif
int
tds_get_varint_size(TDSSOCKET * tds, int datatype)
{
	switch (datatype) {
	case SYBTEXT:
	case SYBNTEXT:
	case SYBIMAGE:
		return 4;
	case SYBVOID:
	case SYBINT1:
	case SYBBIT:
	case SYBINT2:
	case SYBINT4:
	case SYBINT8:
	case SYBDATETIME4:
	case SYBREAL:
	case SYBMONEY:
	case SYBDATETIME:
	case SYBFLT8:
	case SYBMONEY4:
	case SYBSINT1:
	case SYBUINT2:
	case SYBUINT4:
	case SYBUINT8:
		return 0;
	}

	if (IS_TDS7_PLUS(tds)) {
		switch (datatype) {
		/* TODO support this strange type */
		case SYBVARIANT:
			return 4;
		case XSYBCHAR:
		case XSYBNCHAR:
		case XSYBNVARCHAR:
		case XSYBVARCHAR:
		case XSYBBINARY:
		case XSYBVARBINARY:
			return 2;
		}
	} else if (IS_TDS50(tds)) {
		switch (datatype) {
		case SYBLONGBINARY:
		case SYBLONGCHAR:
			return 5;
		case SYB5INT8:
			return 0;
		}
	}
	return 1;
}
Example #7
0
void
tds_env_change(TDSSOCKET * tds, int type, const char *oldvalue, const char *newvalue)
{
	TDS_SMALLINT totsize;

	/* If oldvalue is NULL, treat it like "" */
	if (oldvalue == NULL)
		oldvalue = "";

	/*
	 * NOTE: I don't know why each type of environment value has a different
	 * format.  According to the TDS5 specifications, they should all use
	 * the same format.   The code for the TDS_ENV_CHARSET case *should*
	 * work for all environment values.  -- Steve Kirkendall
	 */

	switch (type) {
	case TDS_ENV_DATABASE:
	case TDS_ENV_LANG:
	case TDS_ENV_PACKSIZE:
	case TDS_ENV_CHARSET:
		tds_put_byte(tds, TDS_ENVCHANGE_TOKEN);
		/* totsize = type + newlen + newvalue + oldlen + oldvalue  */
		/* FIXME ucs2 */
		totsize = (IS_TDS7_PLUS(tds->conn) ? 2 : 1) * (strlen(oldvalue) + strlen(newvalue)) + 3;
		tds_put_smallint(tds, totsize);
		tds_put_byte(tds, type);
		tds_put_byte(tds, strlen(newvalue));
		/* FIXME this assume singlebyte -> ucs2 for mssql */
		tds_put_string(tds, newvalue, strlen(newvalue));
		tds_put_byte(tds, strlen(oldvalue));
		/* FIXME this assume singlebyte -> ucs2 for mssql */
		tds_put_string(tds, oldvalue, strlen(oldvalue));
		break;
	case TDS_ENV_LCID:
	case TDS_ENV_SQLCOLLATION:
#if 1
		tds_put_byte(tds, TDS_ENVCHANGE_TOKEN);
		/* totsize = type + len + oldvalue + len + newvalue */
		totsize = 3 + strlen(newvalue) + strlen(oldvalue);
		tds_put_smallint(tds, totsize);
		tds_put_byte(tds, type);
		tds_put_byte(tds, strlen(newvalue));
		tds_put_n(tds, newvalue, strlen(newvalue));
		tds_put_byte(tds, strlen(oldvalue));
		tds_put_n(tds, oldvalue, strlen(oldvalue));
		break;
#endif
	default:
		tdsdump_log(TDS_DBG_WARN, "tds_env_change() ignoring unsupported environment code #%d", type);
	}
}
Example #8
0
/**
 * \ingroup odbc_bcp
 * \brief Prepare for bulk copy operation on a table
 *
 * \param dbc ODBC database connection object
 * \param tblname the name of the table receiving or providing the data.
 * \param hfile the data file opposite the table, if any. NB: The current
 *              implementation does not support file I/O so this must be NULL
 * \param errfile the "error file" captures messages and, if errors are
 *              encountered. NB: The current implementation does not support
 *              file I/O so this must be NULL
 * \param direction one of
 *      - \b DB_IN writing to the table
 *      - \b DB_OUT writing to the host file (Not currently supported)
 *      .
 * \remarks bcp_init() sets the host file data format and acquires the table metadata.
 *	It is called before the other bulk copy functions.
 *
 *	The ODBC BCP functionality should be accessed via the inline functions in
 *	odbcss.h.
 *
 *	After calling this function, call bcp_bind() to associate your data with
 *	the appropriate table column.
 *
 * \sa	SQL_COPT_SS_BCP, odbc_bcp_bind(), odbc_bcp_done(), odbc_bcp_exec()
 */
void
odbc_bcp_init(TDS_DBC *dbc, const ODBC_CHAR *tblname, const ODBC_CHAR *hfile,
              const ODBC_CHAR *errfile, int direction _WIDE)
{
    /* TODO convert unicode for printing */
    tdsdump_log(TDS_DBG_FUNC, "bcp_init(%p, %s, %s, %s, %d)\n",
                dbc, tblname, hfile, errfile, direction);
    if (!tblname)
        ODBCBCP_ERROR_RETURN("HY009");

    /* Free previously allocated storage in dbproc & initialise flags, etc. */

    odbc_bcp_free_storage(dbc);

    /*
     * Validate other parameters
     */
    if (dbc->tds_socket->conn->tds_version < 0x500)
        ODBCBCP_ERROR_RETURN("HYC00");

    if (direction != BCP_DIRECTION_IN || hfile || errfile)
        ODBCBCP_ERROR_RETURN("HYC00");

    /* Allocate storage */

    dbc->bcpinfo = tds_alloc_bcpinfo();
    if (dbc->bcpinfo == NULL)
        ODBCBCP_ERROR_RETURN("HY001");

    if (!odbc_dstr_copy(dbc, &dbc->bcpinfo->tablename, SQL_NTS, tblname)) {
        odbc_bcp_free_storage(dbc);
        ODBCBCP_ERROR_RETURN("HY001");
    }

    if (tds_dstr_len(&dbc->bcpinfo->tablename) > 92 && !IS_TDS7_PLUS(dbc->tds_socket->conn)) { 	/* 30.30.30 */
        odbc_bcp_free_storage(dbc);
        ODBCBCP_ERROR_RETURN("HYC00");
    }

    dbc->bcpinfo->direction = direction;

    dbc->bcpinfo->xfer_init  = 0;
    dbc->bcpinfo->bind_count = 0;

    if (TDS_FAILED(tds_bcp_init(dbc->tds_socket, dbc->bcpinfo))) {
        /* TODO return proper error */
        /* Attempt to use Bulk Copy with a non-existent Server table (might be why ...) */
        ODBCBCP_ERROR_RETURN("HY000");
    }
}
Example #9
0
/**
 * Go looking for trouble.  Return NULL if the info is okay, or an error message
 * if something needs to change.
 */
static const char *
validate(DSNINFO * di)
{
	if (!SQLValidDSN(tds_dstr_cstr(&di->dsn)))
		return "Invalid DSN";
	if (!IS_TDS42(di->login) && !IS_TDS46(di->login)
	    && !IS_TDS50(di->login) && !IS_TDS7_PLUS(di->login))
		return "Bad Protocol version";
	if (tds_dstr_isempty(&di->login->server_name))
		return "Address is required";
	if (di->login->port < 1 || di->login->port > 65535)
		return "Bad port - Try 1433 or 4000";
	return NULL;
}
Example #10
0
int
main(int argc, char **argv)
{
	TDSLOGIN *login;
	int ret;
	int verbose = 0;

	/* use UTF-8 as our coding */
	strcpy(CHARSET, "UTF-8");

	ret = try_tds_login(&login, &tds, __FILE__, verbose);
	if (ret != TDS_SUCCESS) {
		fprintf(stderr, "try_tds_login() failed\n");
		return 1;
	}

	if (IS_TDS7_PLUS(tds->conn)) {
		char type[32];
		char buf[1024];
		int i, len;

		strcpy(buf, "aaa");
		len = 0;
		for (i = 0; strlen(buf) < 980 && len < 200; ++i) {
			char tmp[256];

			strcat(buf, japanese);
			len += strlen(to_utf8(japanese, tmp));
		}
		strings[sizeof(strings) / sizeof(strings[0]) - 5] = buf + 3;
		strings[sizeof(strings) / sizeof(strings[0]) - 4] = buf + 2;
		strings[sizeof(strings) / sizeof(strings[0]) - 3] = buf + 1;
		strings[sizeof(strings) / sizeof(strings[0]) - 2] = buf;

		test("NVARCHAR(500)", "NVARCHAR with large size");

		sprintf(type, "NVARCHAR(%d)", utf8_max_len);
		test(type, "NVARCHAR with sufficient size");

		test("NTEXT", "TEXT");

		/* TODO test parameters */
	}

	try_tds_logout(login, tds, verbose);
	return 0;
}
Example #11
0
/**
 * Fetch a string from the wire.
 * Output string is NOT null terminated.
 * If TDS version is 7 or 8 read unicode string and convert it.
 * This function should be use to read server default encoding strings like 
 * columns name, table names, etc, not for data (use tds_get_char_data instead)
 * @return bytes written to \a dest
 * @param tds  connection information
 * @param string_len length of string to read from wire 
 *        (in server characters, bytes for tds4-tds5, ucs2 for tds7+)
 * @param dest destination buffer, if NULL string is read and discarded
 * @param dest_size destination buffer size, in bytes
 */
size_t
tds_get_string(TDSSOCKET * tds, size_t string_len, char *dest, size_t dest_size)
{
	size_t wire_bytes = string_len;
	unsigned conv = client2server_chardata;

	if (IS_TDS7_PLUS(tds->conn)) {
		wire_bytes *= 2u;
		conv = client2ucs2;
	}

	if (dest == NULL) {
		tds_get_n(tds, NULL, wire_bytes);
		return string_len;
	}

	return read_and_convert(tds, tds->conn->char_convs[conv], &wire_bytes, dest, dest_size);
}
Example #12
0
int
main(int argc, char **argv)
{
	TDSLOGIN *login;
	int ret;
	int verbose = 0;

	/* use UTF-8 as our coding */
	strcpy(CHARSET, "UTF-8");

	ret = try_tds_login(&login, &tds, __FILE__, verbose);
	if (ret != TDS_SUCCESS) {
		fprintf(stderr, "try_tds_login() failed\n");
		return 1;
	}

	if (IS_TDS7_PLUS(tds->conn)) {
		char buf[129 * 8];
		int i;

		/* build a string of length 128 */
		strcpy(buf, "");
		for (i = 1; i <= 128; ++i) {
			sprintf(strchr(buf, 0), "&#x%04x;", 0x4000 + i);
		}

		/* do all test */
		for (i = 1;;) {
			printf("Testing len %d\n", i);
			test(buf + 8 * (128 - i));
			if (i == 128)
				break;
			++i;
			if (i > 12)
				i += 3;
			if (i >= 128)
				i = 128;
		}
	}

	try_tds_logout(login, tds, verbose);
	return 0;
}
Example #13
0
void
tds_send_msg(TDSSOCKET * tds, int msgno, int msgstate, int severity,
	     const char *msgtext, const char *srvname, const char *procname, int line)
{
	int msgsz;
	size_t len;

	tds_put_byte(tds, TDS_INFO_TOKEN);
	if (!procname)
		procname = "";
	len = strlen(procname);
	msgsz = 4		/* msg no    */
		+ 1		/* msg state */
		+ 1		/* severity  */
		/* FIXME ucs2 */
		+ 4 + (IS_TDS7_PLUS(tds->conn) ? 2 : 1) * (strlen(msgtext) + strlen(srvname) + len)
		+ (IS_TDS72_PLUS(tds->conn) ? 4 : 2);	/* line number */
	tds_put_smallint(tds, msgsz);
	tds_put_int(tds, msgno);
	tds_put_byte(tds, msgstate);
	tds_put_byte(tds, severity);
	tds_put_smallint(tds, strlen(msgtext));
	/* FIXME ucs2 */
	tds_put_string(tds, msgtext, strlen(msgtext));
	tds_put_byte(tds, strlen(srvname));
	/* FIXME ucs2 */
	tds_put_string(tds, srvname, strlen(srvname));
	if (len) {
		tds_put_byte(tds, len);
		/* FIXME ucs2 */
		tds_put_string(tds, procname, len);
	} else {
		tds_put_byte(tds, 0);
	}
	if (IS_TDS72_PLUS(tds->conn))
		tds_put_int(tds, line);
	else
		tds_put_smallint(tds, line);
}
Example #14
0
int
tds_append_cancel(TDSSOCKET *tds)
{
	unsigned char buf[8];
	TDSPACKET *packet;

	buf[0] = TDS_CANCEL;
	buf[1] = 1;
	TDS_PUT_A2BE(buf+2, 8);
	TDS_PUT_A4(buf+4, 0);
	if (IS_TDS7_PLUS(tds->conn) && !tds->login)
		buf[6] = 0x01;

	packet = tds_build_packet(tds, buf, 8);
	if (!packet)
		return TDS_FAIL;

	tds_mutex_lock(&tds->conn->list_mtx);
	tds_append_packet(&tds->conn->send_packets, packet);
	tds_mutex_unlock(&tds->conn->list_mtx);

	return TDS_SUCCESS;
}
Example #15
0
/**
 * Set type of column initializing all dependency
 * \param tds    state information for the socket and the TDS protocol
 * \param curcol column to set
 * \param type   type to set
 */
void
tds_set_param_type(TDSSOCKET * tds, TDSCOLUMN * curcol, TDS_SERVER_TYPE type)
{
	if (IS_TDS7_PLUS(tds)) {
		switch (type) {
		case SYBVARCHAR:
			type = XSYBVARCHAR;
			break;
		case SYBCHAR:
			type = XSYBCHAR;
			break;
		case SYBVARBINARY:
			type = XSYBVARBINARY;
			break;
		case SYBBINARY:
			type = XSYBBINARY;
			break;
			/* avoid warning on other types */
		default:
			break;
		}
	}
	tds_set_column_type(tds, curcol, type);

	if (is_collate_type(type)) {
		curcol->char_conv = tds->char_convs[is_unicode_type(type) ? client2ucs2 : client2server_chardata];
		memcpy(curcol->column_collation, tds->collation, sizeof(tds->collation));
	}

	/* special case, GUID, varint != 0 but only a size */
	/* TODO VARIANT, when supported */
	switch (type) {
	case SYBUNIQUE:
		curcol->on_server.column_size = curcol->column_size = sizeof(TDS_UNIQUE);
		break;
	case SYBBITN:
		curcol->on_server.column_size = curcol->column_size = sizeof(TDS_TINYINT);
		break;
	/* mssql 2005 don't like SYBINT4 as parameter closing connection  */
	case SYBINT1:
	case SYBINT2:
	case SYBINT4:
	case SYBINT8:
		curcol->on_server.column_type = SYBINTN;
		curcol->column_varint_size = 1;
		curcol->column_cur_size = -1;
		break;
	case SYBMONEY4:
	case SYBMONEY:
		curcol->on_server.column_type = SYBMONEYN;
		curcol->column_varint_size = 1;
		curcol->column_cur_size = -1;
		break;
	case SYBDATETIME:
	case SYBDATETIME4:
		curcol->on_server.column_type = SYBDATETIMN;
		curcol->column_varint_size = 1;
		curcol->column_cur_size = -1;
		break;
	case SYBFLT8:
	case SYBREAL:
		curcol->on_server.column_type = SYBFLTN;
		curcol->column_varint_size = 1;
		curcol->column_cur_size = -1;
		break;
	default:
		break;
	}
}
Example #16
0
static int
prepared_rpc(struct _hstmt *stmt, int compute_row)
{
	int nparam = stmt->params ? stmt->params->num_cols : 0;
	const char *p = stmt->prepared_pos - 1;
	TDSCONNECTION *conn = stmt->dbc->tds_socket->conn;

	for (;;) {
		TDSPARAMINFO *temp_params;
		TDSCOLUMN *curcol;
		TDS_SERVER_TYPE type;
		const char *start;

		while (TDS_ISSPACE(*++p));
		if (!*p)
			return SQL_SUCCESS;

		/* we have certainly a parameter */
		if (!(temp_params = tds_alloc_param_result(stmt->params))) {
			odbc_errs_add(&stmt->errs, "HY001", NULL);
			return SQL_ERROR;
		}
		stmt->params = temp_params;
		curcol = temp_params->columns[nparam];

		switch (*p) {
		case ',':
			if (IS_TDS7_PLUS(conn)) {
				tds_set_param_type(conn, curcol, SYBVOID);
				curcol->column_size = curcol->column_cur_size = 0;
			} else {
				/* TODO is there a better type ? */
				tds_set_param_type(conn, curcol, SYBINTN);
				curcol->column_size = curcol->on_server.column_size = 4;
				curcol->column_cur_size = -1;
			}
			if (compute_row)
				if (!tds_alloc_param_data(curcol)) {
					tds_free_param_result(temp_params);
					return SQL_ERROR;
				}
			--p;
			break;
		default:
			/* add next parameter to list */
			start = p;

			if (!(p = parse_const_param(p, &type))) {
				tds_free_param_result(temp_params);
				return SQL_ERROR;
			}
			tds_set_param_type(conn, curcol, type);
			switch (type) {
			case SYBVARCHAR:
				curcol->column_size = p - start;
				break;
			case SYBVARBINARY:
				curcol->column_size = (p - start) / 2 -1;
				break;
			default:
				assert(0);
			case SYBINT4:
			case SYBFLT8:
				curcol->column_cur_size = curcol->column_size;
				break;
			}
			curcol->on_server.column_size = curcol->column_size;
			/* TODO support other type other than VARCHAR, do not strip escape in prepare_call */
			if (compute_row) {
				char *dest;
				int len;
				CONV_RESULT cr;

				if (!tds_alloc_param_data(curcol)) {
					tds_free_param_result(temp_params);
					return SQL_ERROR;
				}
				dest = (char *) curcol->column_data;
				switch (type) {
				case SYBVARCHAR:
					if (*start != '\'') {
						memcpy(dest, start, p - start);
						curcol->column_cur_size = p - start;
					} else {
						++start;
						for (;;) {
							if (*start == '\'')
								++start;
							if (start >= p)
								break;
							*dest++ = *start++;
						}
						curcol->column_cur_size =
							dest - (char *) curcol->column_data;
					}
					break;
				case SYBVARBINARY:
					cr.cb.len = curcol->column_size;
					cr.cb.ib = dest;
					len = tds_convert(NULL, SYBVARCHAR, start, p - start, TDS_CONVERT_BINARY, &cr);
					if (len >= 0 && len <= curcol->column_size)
						curcol->column_cur_size = len;
					break;
				case SYBINT4:
					*((TDS_INT *) dest) = strtol(start, NULL, 10);
					break;
				case SYBFLT8:
					*((TDS_FLOAT *) dest) = strtod(start, NULL);
					break;
				default:
					break;
				}
			}
			--p;
			break;
		case '?':
			/* find binded parameter */
			if (stmt->param_num > stmt->apd->header.sql_desc_count
			    || stmt->param_num > stmt->ipd->header.sql_desc_count) {
				tds_free_param_result(temp_params);
				/* TODO set error */
				return SQL_ERROR;
			}

			switch (odbc_sql2tds
				(stmt, &stmt->ipd->records[stmt->param_num - 1], &stmt->apd->records[stmt->param_num - 1],
				 curcol, compute_row, stmt->apd, stmt->curr_param_row)) {
			case SQL_ERROR:
				return SQL_ERROR;
			case SQL_NEED_DATA:
				return SQL_NEED_DATA;
			}
			++stmt->param_num;
			break;
		}
		++nparam;

		while (TDS_ISSPACE(*++p));
		if (!*p || *p != ',')
			return SQL_SUCCESS;
		stmt->prepared_pos = (char *) p + 1;
	}
}
Example #17
0
int
main(int argc, char **argv)
{
	fprintf(stdout, "%s: Testing conversion from server\n", __FILE__);
	if (try_tds_login(&login, &tds, __FILE__, 0) != TDS_SUCCEED) {
		fprintf(stderr, "try_tds_login() failed\n");
		return 1;
	}

	/* bit */
	test("BIT", "0", NULL);
	test("BIT", "1", NULL);

	/* integers */
	test("TINYINT", "234", NULL);
	test("SMALLINT", "-31789", NULL);
	test("INT", "16909060", NULL);

	/* floating point */
	test("REAL", "1.23", NULL);
	test("FLOAT", "-49586.345", NULL);

	/* money */
	test("MONEY", "-123.3400", "-123.34");
	test("MONEY", "-123.3450", "-123.35");
	test("MONEY", "123.3450", "123.35");
	/* very long money, this test int64 operations too */
	test("MONEY", "123456789012345.67", NULL);
	/* test smaller money */
	test("MONEY", "-922337203685477.5808", "-922337203685477.58");
	test("SMALLMONEY", "89123.12", NULL);
	test("SMALLMONEY", "-123.3400", "-123.34");
	test("SMALLMONEY", "-123.3450", "-123.35");
	test("SMALLMONEY", "123.3450", "123.35");
	/* test smallest smallmoney */
	test("SMALLMONEY", "-214748.3648", "-214748.36");

	/* char */
	test("CHAR(10)", "pippo", "pippo     ");
	test("VARCHAR(20)", "pippo", NULL);
	test0("TEXT", "a", NULL, "foofoo", NULL, "try with a relatively long value, we hope for the best", NULL, NULL);

	/* binary */
	test("VARBINARY(6)", "foo", "666f6f");
	test("BINARY(6)", "foo", "666f6f000000");
	test0("IMAGE", "foo", "666f6f", "foofoofoofoo", "666f6f666f6f666f6f666f6f", NULL);

	/* numeric */
	test("NUMERIC(10,2)", "12765.76", NULL);
	test("NUMERIC(18,4)", "12765.761234", "12765.7612");

	/* date */
	free(test_context->locale->date_fmt);
	test_context->locale->date_fmt = tds_strdup("%Y-%m-%d %H:%M:%S");

	test("DATETIME", "2003-04-21 17:50:03", NULL);
	test("SMALLDATETIME", "2003-04-21 17:50:03", "2003-04-21 17:50:00");

	if (IS_TDS7_PLUS(tds)) {
		test("UNIQUEIDENTIFIER", "12345678-1234-A234-9876-543298765432", NULL);
		test("NVARCHAR(20)", "Excellent test", NULL);
		test("NCHAR(20)", "Excellent test", "Excellent test      ");
		test("NTEXT", "Excellent test", NULL);
	}

	try_tds_logout(login, tds, 0);
	return g_result;
}
Example #18
0
/**
 * Initialize BCP information.
 * Query structure of the table to server.
 * \tds
 * \param bcpinfo BCP information to initialize. Structure should be allocate
 *        and table name and direction should be already set.
 */
TDSRET
tds_bcp_init(TDSSOCKET *tds, TDSBCPINFO *bcpinfo)
{
	TDSRESULTINFO *resinfo;
	TDSRESULTINFO *bindinfo = NULL;
	TDSCOLUMN *curcol;
	TDS_INT result_type;
	int i;
	TDSRET rc;
	const char *fmt;

	/* FIXME don't leave state in processing state */

	/* TODO quote tablename if needed */
	if (bcpinfo->direction != TDS_BCP_QUERYOUT)
		fmt = "SET FMTONLY ON select * from %s SET FMTONLY OFF";
	else
		fmt = "SET FMTONLY ON %s SET FMTONLY OFF";

	if (TDS_FAILED(rc=tds_submit_queryf(tds, fmt, bcpinfo->tablename)))
		/* TODO return an error ?? */
		/* Attempt to use Bulk Copy with a non-existent Server table (might be why ...) */
		return rc;

	/* TODO possibly stop at ROWFMT and copy before going to idle */
	/* 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_SUCCESS)
		continue;
	if (TDS_FAILED(rc))
		return rc;

	/* copy the results info from the TDS socket */
	if (!tds->res_info)
		return TDS_FAIL;

	resinfo = tds->res_info;
	if ((bindinfo = tds_alloc_results(resinfo->num_cols)) == NULL) {
		rc = TDS_FAIL;
		goto cleanup;
	}

	bindinfo->row_size = resinfo->row_size;

	/* Copy the column metadata */
	rc = TDS_FAIL;
	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->funcs = resinfo->columns[i]->funcs;
		curcol->column_type = resinfo->columns[i]->column_type;
		curcol->column_usertype = resinfo->columns[i]->column_usertype;
		curcol->column_flags = resinfo->columns[i]->column_flags;
		if (curcol->column_varint_size == 0)
			curcol->column_cur_size = resinfo->columns[i]->column_cur_size;
		else
			curcol->column_cur_size = -1;
		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->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;
		if (!tds_dstr_dup(&curcol->column_name, &resinfo->columns[i]->column_name))
			goto cleanup;
		if (!tds_dstr_dup(&curcol->table_column_name, &resinfo->columns[i]->table_column_name))
			goto cleanup;
		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 if (bcpinfo->bind_count != 0 /* ctlib */
                   &&  is_blob_col(curcol)) {
            curcol->bcp_column_data = tds_alloc_bcp_column_data(0);
		} else {
			curcol->bcp_column_data = 
				tds_alloc_bcp_column_data(MAX(curcol->column_size,curcol->on_server.column_size));
		}
		if (!curcol->bcp_column_data)
			goto cleanup;
	}

	if (!IS_TDS7_PLUS(tds->conn)) {
		bindinfo->current_row = (unsigned char*) malloc(bindinfo->row_size);
		if (!bindinfo->current_row)
			goto cleanup;
		bindinfo->row_free = tds_bcp_row_free;
	}

	if (bcpinfo->identity_insert_on) {

		rc = tds_submit_queryf(tds, "set identity_insert %s on", bcpinfo->tablename);
		if (TDS_FAILED(rc))
			goto cleanup;

		/* TODO use tds_process_simple_query */
		while ((rc = tds_process_tokens(tds, &result_type, NULL, TDS_TOKEN_RESULTS))
			   == TDS_SUCCESS) {
		}
		if (rc != TDS_NO_MORE_RESULTS)
			goto cleanup;
	}

	bcpinfo->bindinfo = bindinfo;
	bcpinfo->bind_count = 0;
	return TDS_SUCCESS;

cleanup:
	tds_free_results(bindinfo);
	return rc;
}
Example #19
0
/**
 * Send one row of data to server
 * \tds
 * \param bcpinfo BCP information
 * \param get_col_data function to call to retrieve data to be sent
 * \param ignored function to call if we try to send NULL if not allowed (not used)
 * \param offset passed to get_col_data and null_error to specify the row to get
 * \return TDS_SUCCESS or TDS_FAIL.
 */
TDSRET
tds_bcp_send_record(TDSSOCKET *tds, TDSBCPINFO *bcpinfo, tds_bcp_get_col_data get_col_data, tds_bcp_null_error null_error, int offset)
{
	TDSCOLUMN  *bindcol;
    int i, start_col = bcpinfo->next_col;
	TDSRET rc;

    tdsdump_log(TDS_DBG_FUNC, "tds_bcp_send_bcp_record(%p, %p, %p, %p, %d)\n", tds, bcpinfo, get_col_data, null_error, offset);

	if (tds->out_flag != TDS_BULK || tds_set_state(tds, TDS_WRITING) != TDS_WRITING)
		return TDS_FAIL;

    if (start_col > 0) {
        bindcol = bcpinfo->bindinfo->columns[start_col - 1];
        *bindcol->column_lenbind
            = MIN((TDS_INT) bindcol->column_bindlen - bcpinfo->text_sent,
                  *bindcol->column_lenbind);
        tds_put_n(tds, bindcol->column_varaddr, *bindcol->column_lenbind);
        bcpinfo->text_sent += *bindcol->column_lenbind;
        if ((TDS_UINT) bcpinfo->text_sent < bindcol->column_bindlen) {
            return TDS_SUCCESS; /* That's all for now. */
        } else if (!IS_TDS7_PLUS(tds->conn)) {
            bcpinfo->blob_cols++;
        }
        bcpinfo->next_col  = 0;
        bcpinfo->text_sent = 0;
    }

	if (IS_TDS7_PLUS(tds->conn)) {

        if (start_col == 0) {
            tds_put_byte(tds, TDS_ROW_TOKEN);   /* 0xd1 */
        }
        for (i = start_col; i < bcpinfo->bindinfo->num_cols; i++) {
	
			TDS_INT save_size;
			unsigned char *save_data;
			TDSBLOB blob;
            int /* bool */ has_text = 0;

			bindcol = bcpinfo->bindinfo->columns[i];

			/*
			 * Don't send the (meta)data for timestamp columns or
			 * identity columns unless indentity_insert is enabled.
			 */

			if ((!bcpinfo->identity_insert_on && bindcol->column_identity) || 
                bindcol->column_timestamp ||
                !tds_bcp_is_bound(bcpinfo, bindcol)) {
				continue;
			}

			rc = get_col_data(bcpinfo, bindcol, offset);
            if (rc == TDS_FAIL) {
				tdsdump_log(TDS_DBG_INFO1, "get_col_data (column %d) failed\n", i + 1);
				goto cleanup;
            } else if (rc != TDS_SUCCESS) { /* CS_BLK_HAS_TEXT? */
                has_text = 1;
			}
			tdsdump_log(TDS_DBG_INFO1, "gotten column %d length %d null %d\n",
					i + 1, bindcol->bcp_column_data->datalen, bindcol->bcp_column_data->is_null);

			save_size = bindcol->column_cur_size;
			save_data = bindcol->column_data;
			assert(bindcol->column_data == NULL);
			if (bindcol->bcp_column_data->is_null) {
                if ( !bindcol->column_nullable
                     &&  !is_nullable_type(bindcol->on_server.column_type) ) {
                    return TDS_FAIL;
                }
				bindcol->column_cur_size = -1;
            } else if (has_text) {
                bindcol->column_cur_size = bindcol->bcp_column_data->datalen;
			} else if (is_blob_col(bindcol)) {
				bindcol->column_cur_size = bindcol->bcp_column_data->datalen;
				memset(&blob, 0, sizeof(blob));
				blob.textvalue = (TDS_CHAR *) bindcol->bcp_column_data->data;
				bindcol->column_data = (unsigned char *) &blob;
			} else {
				bindcol->column_cur_size = bindcol->bcp_column_data->datalen;
				bindcol->column_data = bindcol->bcp_column_data->data;
			}
			rc = bindcol->funcs->put_data(tds, bindcol, 1);
			bindcol->column_cur_size = save_size;
			bindcol->column_data = save_data;

			if (TDS_FAILED(rc))
				goto cleanup;
            else if (has_text) {
                bcpinfo->next_col = i + 1;
                /* bcpinfo->text_sent = 0; */
                break;
            }
		}
	}  /* IS_TDS7_PLUS */
	else {
      if (start_col == 0) {
		int row_pos;
		int row_sz_pos;
		int var_cols_written = 0;
		TDS_INT	 old_record_size = bcpinfo->bindinfo->row_size;
		unsigned char *record = bcpinfo->bindinfo->current_row;

		memset(record, '\0', old_record_size);	/* zero the rowbuffer */

		/*
		 * offset 0 = number of var columns
		 * offset 1 = row number.  zeroed (datasever assigns)
		 */
		row_pos = 2;

		rc = TDS_FAIL;
        if ((row_pos = tds_bcp_add_fixed_columns(bcpinfo, get_col_data, null_error, offset, record, row_pos)) < 0)
			goto cleanup;

		row_sz_pos = row_pos;

		/* potential variable columns to write */

        if ((row_pos = tds_bcp_add_variable_columns(bcpinfo, get_col_data, null_error, offset, record, row_pos, &var_cols_written)) < 0)
			goto cleanup;


		if (var_cols_written) {
			TDS_PUT_UA2(&record[row_sz_pos], row_pos);
			record[0] = var_cols_written;
		}

		tdsdump_log(TDS_DBG_INFO1, "old_record_size = %d new size = %d \n", old_record_size, row_pos);

		tds_put_smallint(tds, row_pos);
		tds_put_n(tds, record, row_pos);

		/* row is done, now handle any text/image data */

        bcpinfo->blob_cols = 0;
      }

        for (i = start_col; i < bcpinfo->bindinfo->num_cols; i++) {
			bindcol = bcpinfo->bindinfo->columns[i];
			if (is_blob_type(bindcol->column_type)) {
                /* Elide trailing NULLs */
                if (bindcol->bcp_column_data->is_null) {
                    int j;
                    for (j = i + 1;  j < bcpinfo->bindinfo->num_cols;  ++j) {
                        TDSCOLUMN *bindcol2 = bcpinfo->bindinfo->columns[j];
                        if (is_blob_type(bindcol2->column_type)
                            &&  !bindcol2->bcp_column_data->is_null) {
                            break;
                        }
                    }
                    if (j == bcpinfo->bindinfo->num_cols) {
                        i = j;
                        break;
                    }
                }

				rc = get_col_data(bcpinfo, bindcol, offset);
                if (rc == TDS_FAIL)
					goto cleanup;
				/* unknown but zero */
				tds_put_smallint(tds, 0);
                tds_put_byte(tds, (unsigned char) bindcol->column_type);
                tds_put_byte(tds, 0xff - bcpinfo->blob_cols);
				/*
				 * offset of txptr we stashed during variable
				 * column processing 
				 */
				tds_put_smallint(tds, bindcol->column_textpos);
				tds_put_int(tds, bindcol->bcp_column_data->datalen);
                if (rc != TDS_SUCCESS) { /* CS_BLK_HAS_TEXT? */
                    bcpinfo->next_col = i + 1;
                    /* bcpinfo->text_sent = 0; */
                    break;
                }
				tds_put_n(tds, bindcol->bcp_column_data->data, bindcol->bcp_column_data->datalen);
                bcpinfo->blob_cols++;

			}
		}
	}

    if (i == bcpinfo->bindinfo->num_cols) {
        tds_set_state(tds, TDS_SENDING);
        bcpinfo->next_col = 0;
    }
	return TDS_SUCCESS;

cleanup:
	tds_set_state(tds, TDS_SENDING);
	return rc;
}
Example #20
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;
}