Re: [0.95rc2] Preliminary test results

Frediano Ziglio <[email protected]>
Newsgroups gmane.comp.db.tds.freetds
Message-ID <CAHt6W4cEVE-qFLRLkAr=9jpthW6Q3Z7MA03xyNkxtM0AfHSL4A@mail.gmail.com>
2015-05-12 21:07 GMT+01:00 Sergio NNX <[email protected]>:
>> I start to suspect this (and the other rpc test) could be due to some changes in the protocol for mssql 2008 r2.
>> I'll need a dump to confirm it.
>
> dblib/rpc test dump
>
> About to logon
> expected error 20201: "Packet size of -1 not supported -- size of 0 used instead!"
> About to open localhost.tempdb
> Check if server support long identifiers
>       1: SELECT 'x' AS this_is_a_really_really_long_identifier
> trying to create a temporary stored procedure
> Creating procedure #t0022
>       1:
>       2: CREATE PROCEDURE #t0022
>       3:   @null_input varchar(30) OUTPUT
>       4: , @first_type varchar(30) OUTPUT
>       5: , @nullout int OUTPUT
>       6: , @varchar_tds7_out varchar(8000) OUTPUT
>       7: , @nvarchar_tds7_out nvarchar(4000) OUTPUT
>       8: , @nrows int OUTPUT
>       9: , @c_this_name_is_way_more_than_thirty_characters_charlie varchar(20)
>      10: , @nv nvarchar(20) = N'hello'
>      11: AS
>      12: BEGIN
>      13: if @null_input is not NULL begin
>      14:     select 'error: should be NULL' as status, @null_input as 'null_input'
>      15:     return -42
>      16: end else begin
>      17:     print 'Good: @null_input is NULL'
>      18: end
>      19: if @c_this_name_is_way_more_than_thirty_characters_charlie is not NULL begin
>      20:     select 'error: should be NULL' as status, @c_this_name_is_way_more_than_thirty_characters_charlie as '@c_this_name_is_way_more_than_thirty_characters_charlie'
>      21:     return -42
>      22: end else begin
>      23:     print 'Good: @c_this_name_is_way_more_than_thirty_characters_charlie is NULL'
>      24: end
>      25: select @null_input = max(convert(varchar(30), name)) from systypes
>      26: select @first_type = min(convert(varchar(30), name)) from systypes
>      27: select name from sysobjects where 0=1
>      28: select distinct convert(varchar(30), name) as 'type'  from systypes
>      29: where name in ('int', 'char', 'text')
>      30: select @nrows = @@rowcount
>      31: set @varchar_tds7_out = replicate(char(247), 8000)
>      32: set @nvarchar_tds7_out = replicate(nchar(246), 4000)
>      33: select distinct @nv as '@nv', convert(varchar(30), name) as name  from sysobjects where type = 'S'
>      34: select      @null_input as 'null_input'
>      35:     , @first_type as 'first_type'
>      36:     , @nullout as 'nullout'
>      37:     , @nrows as 'nrows'
>      38:     , @c_this_name_is_way_more_than_thirty_characters_charlie as 'c'
>      39:     , @nv as 'nv'
>      40:     into #parameters
>      41: select * from #parameters
>      42: return 42
>      43: END
>      44:
> Created procedure #t0022
> executing dbrpcinit
> executing dbrpcparam for @null_input
> executing dbrpcparam for @first_type
> executing dbrpcparam for @nullout
> executing dbrpcparam for @varchar_tds7_out
> executing dbrpcparam for @nvarchar_tds7_out
> executing dbrpcparam for @nrows
> executing dbrpcparam for @c_this_name_is_way_more_than_thirty_characters_charlie
> executing dbrpcparam for @nv
> executing dbrpcsend
> executing dbsqlok
> Good: @null_input is NULL
> Good: @c_this_name_is_way_more_than_thirty_characters_charlie is NULL
> fetching results
> fetched resultset 1 successfully:
> column 1 (name) is 1 wide, buffer initialized to 'one'
> name
> row count 0
> fetched resultset 2 successfully:
> column 1 (type) is 1 wide, buffer initialized to 'one'
> type
> char
> int
> text
> row count 3
> fetched resultset 3 successfully:
> column 1 (@nv) is 1 wide, buffer initialized to 'one'
> column 2 (name) is 1 wide, buffer initialized to 'two'
> @nv name
> row count 0
> fetched resultset 4 successfully:
> column 1 (null_input) is 1 wide, buffer initialized to 'one'
> column 2 (first_type) is 1 wide, buffer initialized to 'two'
> column 3 (nullout) is 1 wide, buffer initialized to 'three'
> column 4 (nrows) is 1 wide, buffer initialized to 'four'
> column 5 (c) is 1 wide, buffer initialized to 'five'
> null_input first_type nullout nrows c
> xml bigint   3
> row count 1
> retrieving return status...
> 42
> retrieving output parameters...
> param name                  type length  data
> ----  -------------------   ----  ----- ------------------------------
> 1     @null_input             39      3  xml
> 2     @first_type             39      6  bigint
> 3     @nullout                56      0
> 4     @varchar_tds7_out       39   8000  ÷÷÷÷÷÷÷÷÷÷...
> 5     @nvarchar_tds7_out      39   4000  öööööööööö...
> 6     @nrows                  56      4  3
> Good: Got 6 output parameters and 1 return status of 42.
> Expected an empty resultset got 2.
> FAIL rpc.exe (exit status: 1)
>

This is the output, to get the dump define TDSDUMP to a filename and
send the file, under cmd.exe is something like

set TDSDUMP=dump.txt

while in bash (if you use mingw or cygwin or Linux) is

export TDSDUMP=dump.txt

>
>>> freeclose test output
>>>
>>> [...]
>>> Error finding last socket opened
>>> FAIL freeclose.exe (exit status: 1)
>>>
>
>> Can you try the attached version?
>
> It didn't work. Same error message as before.
> I'd like to understand that test. What's a typical result for this function: odbc_find_last_socket(void)?
> What's a common (or default) value for ip remote addr and remote port? Show me an example.
>
> Thanks for your time.
>
> Sergio.
>

This function is quite an hack function. It try to discover the last
socket connection opened. So it try to scan a lot of possible handles
(numerically 4096 starting from 4, so 4, 8, 12, 16 ... etc) and call
some function to check if they are really sockets (getpeername and
getsockname). I also recently changed to discard pair of socketpair
(two sockets attached together).

I attached a new version, should handle IPv6 addresses (unfortunately
sizeof(struct sockaddr) < sizeof(struct sockaddr_in6) on Windows).

Frediano

_______________________________________________
FreeTDS mailing list
[email protected]
http://lists.ibiblio.org/mailman/listinfo/freetds
freeclose.c (text/x-csrc, 11.2 KB)
#include "common.h"

#if HAVE_UNISTD_H
#include <unistd.h>
#endif /* HAVE_UNISTD_H */

#include <freetds/time.h>

#if HAVE_ERRNO_H
#include <errno.h>
#endif /* HAVE_ERRNO_H */

#if HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif /* HAVE_SYS_SOCKET_H */

#if HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif /* HAVE_SYS_STAT_H */

#if HAVE_SYS_IOCTL_H
#include <sys/ioctl.h>
#endif /* HAVE_SYS_IOCTL_H */

#if HAVE_SYS_WAIT_H
#include <sys/wait.h>
#endif /* HAVE_SYS_WAIT_H */

#if HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif /* HAVE_NETINET_IN_H */

#if (!defined(TDS_NO_THREADSAFE) && HAVE_ALARM && HAVE_FSTAT && defined(S_IFSOCK)) || defined(_WIN32)

#include <ctype.h>

#include <freetds/tds.h>
#include <freetds/thread.h>

/* this crazy test test that we do not send too much prepare ... */

static tds_mutex mtx;

typedef union {
	struct sockaddr sa;
	struct sockaddr_in sin;
} long_sockaddr;

#ifdef _WIN32
static TDS_SYS_SOCKET
odbc_find_last_socket(void)
{
	typedef struct {
		TDS_SYS_SOCKET sock;
		int local_port;
		int remote_port;
	} sock_info;
	sock_info found[8];
	unsigned num_found = 0, n;
	int i;

	for (i = 4; i <= (4096*4); i += 4) {
		long_sockaddr remote_addr, local_addr;
		struct sockaddr_in *in;
		socklen_t remote_addr_len, local_addr_len;
		sock_info *info;

		/* check if is a socket */
		remote_addr_len = sizeof(remote_addr);
		if (tds_getpeername((TDS_SYS_SOCKET) i, &remote_addr, &remote_addr_len))
			continue;
		local_addr_len = sizeof(local_addr);
		if (tds_getsockname((TDS_SYS_SOCKET) i, &local_addr, &local_addr_len))
			continue;

		/* save in the array */
		if (num_found >= 8) {
			memmove(found, found+1, sizeof(found) - sizeof(found[0]));
			num_found = 7;
		}
		info = &found[num_found++];
		info->sock = (TDS_SYS_SOCKET) i;
		info->local_port = -1;
		info->remote_port = -1;

		/* now check if is a socketpair */
		in = &remote_addr.sin;
		if (in->sin_family != AF_INET)
			continue;
		if (in->sin_addr.s_addr != htonl(INADDR_LOOPBACK))
			continue;
		info->remote_port = ntohs(in->sin_port);
		in = &local_addr.sin;
		if (in->sin_family != AF_INET)
			continue;
		if (in->sin_addr.s_addr != htonl(INADDR_LOOPBACK))
			continue;
		info->local_port = ntohs(in->sin_port);
		for (n = 0; n < num_found - 1; ++n) {
			if (found[n].remote_port != info->local_port
			    || found[n].local_port != info->remote_port)
				continue;
			--num_found;
			memmove(found+n, found+n+1, num_found-n-1);
			--num_found;
			break;
		}
	}

	/* return last */
	if (num_found == 0)
		return INVALID_SOCKET;
	return found[num_found-1].sock;
}
#endif

static long_sockaddr remote_addr;
static socklen_t remote_addr_len;

static TDS_SYS_SOCKET fake_sock;

static tds_thread fake_thread;
#ifdef _WIN32
#define alarm(n) do { ; } while(0)
#endif
static TDS_THREAD_PROC_DECLARE(fake_thread_proc, arg);

static int
init_fake_server(int ip_port)
{
	struct sockaddr_in sin;
	TDS_SYS_SOCKET s;

	memset(&sin, 0, sizeof(sin));
	sin.sin_addr.s_addr = INADDR_ANY;
	sin.sin_port = htons((short) ip_port);
	sin.sin_family = AF_INET;

	if (TDS_IS_SOCKET_INVALID(s = socket(AF_INET, SOCK_STREAM, 0))) {
		perror("socket");
		exit(1);
	}
	if (bind(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
		perror("bind");
		CLOSESOCKET(s);
		return 1;
	}
	listen(s, 5);
	if (tds_thread_create(&fake_thread, fake_thread_proc, int2ptr(s)) != 0) {
		perror("tds_thread_create");
		exit(1);
	}
	return 0;
}

static void
write_all(TDS_SYS_SOCKET s, const void *buf, size_t len)
{
	int res, l;
	fd_set fds_write;

	for (; len > 0;) {
		FD_ZERO(&fds_write);
		FD_SET(s, &fds_write);

		res = select(s + 1, NULL, &fds_write, NULL, NULL);
		if (res <= 0) {
			if (errno == EINTR)
				continue;
			perror("select");
			exit(1);
		}

		l = WRITESOCKET(s, buf, len);
		if (l <= 0) {
			perror("write socket");
			exit(1);
		}
		buf = ((const char *) buf) + l;
		len -= l;
	}
}

static unsigned int inserts = 0;
static char insert_buf[256] = "";

static void
count_insert(const char* buf, size_t len)
{
	static const char search[] = "insert into";
	static unsigned char prev = 'x';
	char *p;
	unsigned char c;
	size_t insert_len;

	/* add to buffer */
	for (p = strchr(insert_buf, 0); len > 0; --len, ++buf) {
		c = (unsigned char) buf[0];
		if (prev == 0 || c != 0)
			*p++ = (c >= ' ' && c < 128) ? tolower(c) : '_';
		prev = c;
	}
	*p = 0;

	/* check it */
	while ((p=strstr(insert_buf, search)) != NULL) {
		tds_mutex_lock(&mtx);
		++inserts;
		tds_mutex_unlock(&mtx);
		/* do not find again */
		p[0] = '_';
	}

	/* avoid buffer too long */
	insert_len = strlen(insert_buf);
	if (insert_len > sizeof(search)) {
		p = insert_buf + insert_len - sizeof(search);
		memmove(insert_buf, p, strlen(p) + 1);
	}
}

static unsigned int round_trips = 0;
static enum { sending, receiving } flow = sending;

static TDS_THREAD_PROC_DECLARE(fake_thread_proc, arg)
{
	TDS_SYS_SOCKET s = ptr2int(arg), server_sock;
	socklen_t sock_len;
	int len;
	char buf[128];
	struct sockaddr_in sin;
	fd_set fds_read, fds_write, fds_error;
	TDS_SYS_SOCKET max_fd = 0;

	memset(&sin, 0, sizeof(sin));
	sock_len = sizeof(sin);
	alarm(30);
	if ((fake_sock = tds_accept(s, (struct sockaddr *) &sin, &sock_len)) < 0) {
		perror("accept");
		exit(1);
	}
	CLOSESOCKET(s);

	if (TDS_IS_SOCKET_INVALID(server_sock = socket(AF_INET, SOCK_STREAM, 0))) {
		perror("socket");
		exit(1);
	}

	if (connect(server_sock, &remote_addr, remote_addr_len)) {
		perror("connect");
		exit(1);
	}
	alarm(0);

	if (fake_sock > max_fd) max_fd = fake_sock;
	if (server_sock > max_fd) max_fd = server_sock;

	for (;;) {
		int res;

		FD_ZERO(&fds_read);
		FD_SET(fake_sock, &fds_read);
		FD_SET(server_sock, &fds_read);

		FD_ZERO(&fds_write);

		FD_ZERO(&fds_error);
		FD_SET(fake_sock, &fds_error);
		FD_SET(server_sock, &fds_error);

		alarm(30);
		res = select(max_fd + 1, &fds_read, &fds_write, &fds_error, NULL);
		alarm(0);
		if (res < 0) {
			if (sock_errno == TDSSOCK_EINTR)
				continue;
			perror("select");
			exit(1);
		}

		if (FD_ISSET(fake_sock, &fds_error) || FD_ISSET(server_sock, &fds_error)) {
			fprintf(stderr, "error in select\n");
			exit(1);
		}

		/* just read and forward */
		if (FD_ISSET(fake_sock, &fds_read)) {
			if (flow != sending) {
				tds_mutex_lock(&mtx);
				++round_trips;
				tds_mutex_unlock(&mtx);
			}
			flow = sending;

			len = READSOCKET(fake_sock, buf, sizeof(buf));
			if (len == 0)
				break;
			if (len < 0 && sock_errno != TDSSOCK_EINPROGRESS)
				break;
			count_insert(buf, len);
			write_all(server_sock, buf, len);
		}

		if (FD_ISSET(server_sock, &fds_read)) {
			if (flow != receiving) {
				tds_mutex_lock(&mtx);
				++round_trips;
				tds_mutex_unlock(&mtx);
			}
			flow = receiving;

			len = READSOCKET(server_sock, buf, sizeof(buf));
			if (len == 0)
				break;
			if (len < 0 && sock_errno != TDSSOCK_EINPROGRESS)
				break;
			write_all(fake_sock, buf, len);
		}
	}
	CLOSESOCKET(fake_sock);
	CLOSESOCKET(server_sock);
	return NULL;
}

int
main(int argc, char **argv)
{
	SQLLEN sql_nts = SQL_NTS;
	const char *query;
	SQLINTEGER id = 0;
	char string[64];
	TDS_SYS_SOCKET last_socket;
	int port;
	const int num_inserts = 20;
	int is_freetds;

#ifdef _WIN32
	WSADATA wsaData;
	WSAStartup(MAKEWORD(1, 1), &wsaData);
#endif

	if (tds_mutex_init(&mtx))
		return 1;

	odbc_connect();

	/*
	 * this does not work if server is not connected with socket
	 * (ie ms driver connected locally)
	 */
	last_socket = odbc_find_last_socket();
	if (TDS_IS_SOCKET_INVALID(last_socket)) {
		fprintf(stderr, "Error finding last socket opened\n");
		return 1;
	}

	remote_addr_len = sizeof(remote_addr);
	if (tds_getpeername(last_socket, &remote_addr, &remote_addr_len)) {
		fprintf(stderr, "Unable to get remote address\n");
		return 1;
	}

	is_freetds = odbc_driver_is_freetds();
	odbc_disconnect();

	/* init fake server, behave like a proxy */
	for (port = 12340; port < 12350; ++port)
		if (!init_fake_server(port))
			break;
	if (port == 12350) {
		fprintf(stderr, "Cannot bind to a port\n");
		return 1;
	}
	printf("Fake server bound at port %d\n", port);

	/* override connections */
	if (is_freetds) {
		setenv("TDSHOST", "127.0.0.1", 1);
		sprintf(string, "%d", port);
		setenv("TDSPORT", string, 1);

		odbc_connect();
	} else {
		char tmp[2048];
		SQLSMALLINT len;

		CHKAllocEnv(&odbc_env, "S");
		CHKAllocConnect(&odbc_conn, "S");
		sprintf(tmp, "DRIVER={SQL Server};SERVER=127.0.0.1,%d;UID=%s;PWD=%s;DATABASE=%s;Network=DBMSSOCN;", port, odbc_user, odbc_password, odbc_database);
		printf("connection string: %s\n", tmp);
		CHKDriverConnect(NULL, T(tmp), SQL_NTS, (SQLTCHAR *) tmp, sizeof(tmp)/sizeof(SQLTCHAR), &len, SQL_DRIVER_NOPROMPT, "SI");
		CHKAllocStmt(&odbc_stmt, "S");
	}

	/* real test */
	odbc_command("CREATE TABLE #test(i int, c varchar(40))");

	odbc_reset_statement();

	/* do not take into account connection statistics */
	tds_mutex_lock(&mtx);
	round_trips = 0;
	inserts = 0;
	tds_mutex_unlock(&mtx);

	query = "insert into #test values (?, ?)";

	CHKBindParameter(1, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, sizeof(id), 0, &id, 0, &sql_nts, "SI");
	CHKBindParameter(2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_VARCHAR, sizeof(string), 0, string, 0, &sql_nts, "SI");

	CHKPrepare(T(query), SQL_NTS, "SI");
	tds_mutex_lock(&mtx);
	printf("%u round trips %u inserts\n", round_trips, inserts);
	tds_mutex_unlock(&mtx);

	for (id = 0; id < num_inserts; id++) {
		sprintf(string, "This is a test (%d)", (int) id);
		CHKExecute("SI");
		CHKFreeStmt(SQL_CLOSE, "S");
	}

	tds_mutex_lock(&mtx);
	printf("%u round trips %u inserts\n", round_trips, inserts);
	tds_mutex_unlock(&mtx);
	odbc_reset_statement();

	tds_mutex_lock(&mtx);
	if (inserts > 1 || round_trips > (unsigned) (num_inserts * 2 + 6)) {
		fprintf(stderr, "Too much round trips (%u) or insert (%u) !!!\n", round_trips, inserts);
		tds_mutex_unlock(&mtx);
		return 1;
	}
	printf("%u round trips %u inserts\n", round_trips, inserts);
	tds_mutex_unlock(&mtx);

#ifdef ENABLE_DEVELOPING
	/* check for SQL_RESET_PARAMS */
	tds_mutex_lock(&mtx);
	round_trips = 0;
	inserts = 0;
	tds_mutex_unlock(&mtx);

	CHKBindParameter(1, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, sizeof(id), 0, &id, 0, &sql_nts, "SI");
	CHKBindParameter(2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_VARCHAR, sizeof(string), 0, string, 0, &sql_nts, "SI");

	CHKPrepare((SQLCHAR *) query, SQL_NTS, "SI");
	tds_mutex_lock(&mtx);
	printf("%u round trips %u inserts\n", round_trips, inserts);
	tds_mutex_unlock(&mtx);

	for (id = 0; id < num_inserts; id++) {
		CHKBindParameter(1, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, sizeof(id), 0, &id, 0, &sql_nts, "SI");
		CHKBindParameter(2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_VARCHAR, sizeof(string), 0, string, 0, &sql_nts, "SI");

		sprintf(string, "This is a test (%d)", (int) id);
		CHKExecute("SI");
		CHKFreeStmt(SQL_RESET_PARAMS, "S");
	}

	tds_mutex_lock(&mtx);
	printf("%u round trips %u inserts\n", round_trips, inserts);
	tds_mutex_unlock(&mtx);
	odbc_reset_statement();

	tds_mutex_lock(&mtx);
	if (inserts > 1 || round_trips > num_inserts * 2 + 6) {
		fprintf(stderr, "Too much round trips (%u) or insert (%u) !!!\n", round_trips, inserts);
		tds_mutex_unlock(&mtx);
		return 1;
	}
	printf("%u round trips %u inserts\n", round_trips, inserts);
	tds_mutex_unlock(&mtx);
#endif

	odbc_disconnect();

	alarm(10);
	tds_thread_join(fake_thread, NULL);

	return 0;
}

#else
int
main(void)
{
        printf("Not possible for this platform.\n");
        return 0;
}
#endif
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.