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