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