1 /*
2  * Purpose: Test remote procedure calls
3  * Functions:
4  */
5 
6 #include "common.h"
7 
8 #include <common/test_assert.h>
9 
10 static char software_version[] = "$Id: rpc.c 487511 2015-12-17 20:11:40Z ucko $";
11 static void *no_unused_var_warn[] = { software_version, no_unused_var_warn };
12 
13 static const char procedure_sql[] =
14 		"CREATE PROCEDURE %s \n"
15 			"  @null_input varchar(30) OUTPUT \n"
16 			", @first_type varchar(30) OUTPUT \n"
17 			", @nullout int OUTPUT\n"
18 			", @nrows int OUTPUT \n"
19 			", @c varchar(20)\n"
20 		"AS \n"
21 		"BEGIN \n"
22 			"select @null_input = max(convert(varchar(30), name)) from systypes \n"
23 			"select @first_type = min(convert(varchar(30), name)) from systypes \n"
24 			/* #1 empty result set: */
25 			"select name from sysobjects where 0=1\n"
26 			/* #2 3 rows: */
27 			"select distinct convert(varchar(30), name) as 'type'  from systypes \n"
28 				"where name in ('int', 'char', 'text') \n"
29 			"select @nrows = @@rowcount \n"
30 			/* #3 many rows: */
31 			"select distinct convert(varchar(30), name) as name  from systypes \n"
32 			"return 42 \n"
33 		"END \n";
34 
35 
36 static void
init_proc(const char * name)37 init_proc(const char *name)
38 {
39 	static char cmd[4096];
40 
41 	if (name[0] != '#') {
42 		printf("Dropping procedure %s\n", name);
43 		sprintf(cmd, "if exists (select 1 from sysobjects where name = '%s' and type = 'P') "
44 				"DROP PROCEDURE %s", name, name);
45 		CHKExecDirect(T(cmd), SQL_NTS, "SI");
46 	}
47 
48 	printf("Creating procedure %s\n", name);
49 	sprintf(cmd, procedure_sql, name);
50 
51 	/* create procedure. Fails if wrong permission or not MSSQL */
52 	CHKExecDirect(T(cmd), SQL_NTS, "SI");
53 }
54 
55 static void
Test(const char * name)56 Test(const char *name)
57 {
58 	int iresults=0, data_errors=0;
59 	int ipar=0;
60 	HSTMT odbc_stmt = SQL_NULL_HSTMT;
61 	char call_cmd[128];
62 	struct Argument {
63                 SQLSMALLINT       InputOutputType;  /* fParamType */
64                 SQLSMALLINT       ValueType;        /* fCType */
65                 SQLSMALLINT       ParameterType;    /* fSqlType */
66                 SQLUINTEGER       ColumnSize;       /* cbColDef */
67                 SQLSMALLINT       DecimalDigits;    /* ibScale */
68                 SQLPOINTER        ParameterValuePtr;/* rgbValue */
69                 SQLINTEGER        BufferLength;     /* cbValueMax */
70                 SQLLEN            ind;              /* pcbValue */
71 	};
72 	struct Argument args[] = {
73 		/* InputOutputType 	  ValueType   ParamType    ColumnSize
74 								    | DecimalDigits
75 								    |  | ParameterValuePtr
76 								    |  |  |  BufferLength
77 								    |  |  |   |	 ind */
78 		{ SQL_PARAM_OUTPUT,       SQL_C_LONG, SQL_INTEGER,  0, 0, 0,  4, 3 }, /* return status */
79 		{ SQL_PARAM_INPUT_OUTPUT, SQL_C_CHAR, SQL_VARCHAR, 30, 0, 0, 30, SQL_NULL_DATA },
80 										      /* @null_input varchar(30) OUTPUT */
81 		{ SQL_PARAM_INPUT_OUTPUT, SQL_C_CHAR, SQL_VARCHAR, 30, 0, 0, 30, 3 }, /* @first_type varchar(30) OUTPUT */
82 		{ SQL_PARAM_INPUT_OUTPUT, SQL_C_LONG, SQL_INTEGER,  0, 0, 0,  4, 4 }, /* @nullout int OUTPUT\ */
83 		{ SQL_PARAM_INPUT_OUTPUT, SQL_C_LONG, SQL_INTEGER,  0, 0, 0,  4, 4 }, /* @nrows int OUTPUT */
84 		{ SQL_PARAM_INPUT,        SQL_C_CHAR, SQL_VARCHAR, 20, 0, 0, 20, 3 }  /* @c varchar(20) */
85 	};
86 
87 
88 	printf("executing SQLAllocStmt\n");
89 	CHKAllocStmt(&odbc_stmt, "S");
90 
91 	for( ipar=0; ipar < sizeof(args)/sizeof(args[0]); ipar++ ) {
92 		printf("executing SQLBindParameter for parameter %d\n", 1+ipar);
93 		if( args[ipar].BufferLength > 0 ) {
94 			args[ipar].ParameterValuePtr = (SQLPOINTER) ODBC_GET(args[ipar].BufferLength);
95 			assert(args[ipar].ParameterValuePtr != NULL);
96 			memset(args[ipar].ParameterValuePtr, 0, args[ipar].BufferLength);
97 			memset(args[ipar].ParameterValuePtr, 'a', args[ipar].BufferLength - 1);
98 		}
99 		CHKBindParameter	( 1+ipar
100 					, args[ipar].InputOutputType
101 					, args[ipar].ValueType
102 					, args[ipar].ParameterType
103 					, args[ipar].ColumnSize
104 					, args[ipar].DecimalDigits
105 					, args[ipar].ParameterValuePtr
106 					, args[ipar].BufferLength
107 					, &args[ipar].ind
108 					, "S"
109 					);
110 	}
111 
112 	sprintf(call_cmd, "{?=call %s(?,?,?,?,?)}", name );
113 	printf("executing SQLPrepare: %s\n", call_cmd);
114 	CHKPrepare(T(call_cmd), SQL_NTS, "S");
115 
116 	printf("executing SQLExecute\n");
117 	CHKExecute("SI");
118 
119 	do {
120 		static const char dashes[] = "------------------------------";
121 		int nrows;
122 		SQLSMALLINT  icol, ncols;
123 		SQLTCHAR     name[256] = {0};
124 		SQLSMALLINT  namelen;
125 		SQLSMALLINT  type;
126 		SQLSMALLINT  scale;
127 		SQLSMALLINT  nullable;
128 
129 		printf("executing SQLNumResultCols for result set %d\n", ++iresults);
130 		CHKNumResultCols(&ncols, "S");
131 
132 		printf("executing SQLDescribeCol for %d column%c\n", ncols, (ncols == 1? ' ' : 's'));
133 		printf("%-5.5s %-15.15s %5.5s %5.5s %5.5s %8.8s\n", "col", "name", "type", "size", "scale", "nullable");
134 		printf("%-5.5s %-15.15s %5.5s %5.5s %5.5s %8.8s\n", dashes, dashes, dashes, dashes, dashes, dashes);
135 
136 		for (icol=ncols; icol > 0; icol--) {
137 			SQLULEN size;
138 			CHKDescribeCol(icol, name, ODBC_VECTOR_SIZE(name),
139 				       &namelen, &type, &size, &scale, &nullable, "S");
140 			printf("%-5d %-15s %5d %5ld %5d %8c\n", icol, C(name), type, (long int)size, scale, (nullable? 'Y' : 'N'));
141 		}
142 
143 		printf("executing SQLFetch...\n");
144 		printf("\t%-30s\n\t%s\n", C(name), dashes);
145 		for (nrows=0; CHKFetch("SNo") == SQL_SUCCESS; nrows++) {
146             const SQLUSMALLINT icol = 1;
147 			char buf[60];
148 			SQLLEN len;
149 			CHKGetData( icol
150 					, SQL_C_CHAR	/* fCType */
151 					, buf		/* rgbValue */
152 					, sizeof(buf)	/* cbValueMax */
153 	                		, &len		/* pcbValue */
154 					, "SI"
155 					);
156 			printf("\t%-30s\t(%2d bytes)\n", buf, (int) len);
157 		}
158 		printf("done.\n");
159 
160 		switch (iresults) {
161 		case 1:
162 			printf("0 rows expected, %d found\n", nrows);
163 			data_errors += (nrows == 0)? 0 : 1;
164 			break;;
165 		case 2:
166 			printf("3 rows expected, %d found\n", nrows);
167 			data_errors += (nrows == 3)? 0 : 1;
168 			break;;
169 		case 3:
170 			printf("at least 15 rows expected, %d found\n", nrows);
171 			data_errors += (nrows > 15)? 0 : 1;
172 			break;;
173 		}
174 
175 		printf("executing SQLMoreResults...\n");
176 	} while (CHKMoreResults("SNo") == SQL_SUCCESS);
177 	printf("done.\n");
178 
179 	for( ipar=0; ipar < sizeof(args)/sizeof(args[0]); ipar++ ) {
180 		if (args[ipar].InputOutputType == SQL_PARAM_INPUT)
181 			continue;
182 		printf("bound data for parameter %d is %ld bytes: ", 1+ipar, (long int)args[ipar].ind);
183 		switch( args[ipar].ValueType ) {
184 		case SQL_C_LONG:
185 			printf("%d.\n", (int)(*(SQLINTEGER *)args[ipar].ParameterValuePtr));
186 			break;
187 		case SQL_C_CHAR:
188 			printf("'%s'.\n", (char*)args[ipar].ParameterValuePtr);
189 			break;
190 		default:
191 			printf("type unsupported in this test\n");
192 			assert(0);
193 			break;
194 		}
195 	}
196 
197 	printf("executing SQLFreeStmt\n");
198 	CHKFreeStmt(SQL_DROP, "S");
199 	odbc_stmt = SQL_NULL_HSTMT;
200 
201 	ODBC_FREE();
202 
203 	if (data_errors) {
204 		fprintf(stderr, "%d errors found in expected row count\n", data_errors);
205 		exit(1);
206 	}
207 }
208 
209 int
main(int argc,char * argv[])210 main(int argc, char *argv[])
211 {
212 	const char proc_name[] = "freetds_odbc_rpc_test";
213 	char drop_proc[256] = "DROP PROCEDURE ";
214 
215 	strcat(drop_proc, proc_name);
216 
217 	printf("connecting\n");
218 	odbc_connect();
219 
220 	init_proc(proc_name);
221 
222 	printf("running test\n");
223 	Test(proc_name);
224 
225 	printf("dropping procedure\n");
226 	odbc_command(drop_proc);
227 
228 	odbc_disconnect();
229 
230 	printf("Done.\n");
231 	ODBC_FREE();
232 	return 0;
233 }
234 
235