1 #include "common.h"
2 
3 #include <common/test_assert.h>
4 
5 /* test binding with UTF-8 encoding */
6 
7 #ifndef _WIN32
8 static void init_connect(void);
9 
10 static void
init_connect(void)11 init_connect(void)
12 {
13 	CHKAllocEnv(&odbc_env, "S");
14 	SQLSetEnvAttr(odbc_env, SQL_ATTR_ODBC_VERSION, (SQLPOINTER) (SQL_OV_ODBC3), SQL_IS_UINTEGER);
15 	CHKAllocConnect(&odbc_conn, "S");
16 }
17 
18 static void
CheckNoRow(const char * query)19 CheckNoRow(const char *query)
20 {
21 	SQLRETURN rc;
22 
23 	rc = CHKExecDirect(T(query), SQL_NTS, "SINo");
24 	if (rc == SQL_NO_DATA)
25 		return;
26 
27 	do {
28 		SQLSMALLINT cols;
29 
30 		CHKNumResultCols(&cols, "S");
31 		if (cols != 0) {
32 			fprintf(stderr, "Data not expected here, query:\n\t%s\n", query);
33 			exit(1);
34 		}
35 	} while (CHKMoreResults("SNo") == SQL_SUCCESS);
36 }
37 
38 /* test table name, it contains two japanese characters */
39 static const char table_name[] = "mytab\xe7\x8e\x8b\xe9\xb4\xbb";
40 
41 static const char * const strings[] = {
42 	/* ascii */
43 	"aaa", "aaa",
44 	/* latin 1*/
45 	"abc\xc3\xa9\xc3\xa1\xc3\xb4", "abc\xc3\xa9\xc3\xae\xc3\xb4",
46 	/* Japanese... */
47 	"abc\xe7\x8e\x8b\xe9\xb4\xbb", "abc\xe7\x8e\x8b\xe9\xb4\xbb\xe5\x82\x91\xe7\x8e\x8b\xe9\xb4\xbb\xe5\x82\x91",
48 	NULL, NULL
49 };
50 
51 /* same strings in hex */
52 static const char * const strings_hex[] = {
53 	/* ascii */
54 	"0x610061006100", "0x610061006100",
55 	/* latin 1*/
56 	"0x610062006300e900e100f400", "0x610062006300e900ee00f400",
57 	/* Japanese... */
58 	"0x6100620063008b733b9d", "0x6100620063008b733b9d91508b733b9d9150",
59 	NULL, NULL
60 };
61 
62 static char tmp[2048];
63 
64 static void
TestBinding(int minimum)65 TestBinding(int minimum)
66 {
67 	const char * const*p;
68 	SQLINTEGER n;
69 	SQLLEN n_len;
70 
71 	sprintf(tmp, "DELETE FROM %s", table_name);
72 	odbc_command(tmp);
73 
74 	/* insert with SQLPrepare/SQLBindParameter/SQLExecute */
75 	sprintf(tmp, "INSERT INTO %s VALUES(?,?,?)", table_name);
76 	CHKPrepare(T(tmp), SQL_NTS, "S");
77 	CHKBindParameter(1, SQL_PARAM_INPUT, SQL_C_LONG,
78 		SQL_INTEGER, 0, 0, &n, 0, &n_len, "S");
79 	n_len = sizeof(n);
80 
81 	for (n = 1, p = strings; p[0] && p[1]; p += 2, ++n) {
82 		SQLLEN s1_len, s2_len;
83 		unsigned int len;
84 
85         len = minimum ? (((int) strlen(strings_hex[p-strings]) - 2) / 4) : 40;
86 		CHKBindParameter(2, SQL_PARAM_INPUT, SQL_C_CHAR,
87 			SQL_WCHAR, len, 0, (void *) p[0], 0, &s1_len, "S");
88         len = minimum ? (((int) strlen(strings_hex[p+1-strings]) - 2) / 4) : 40;
89 		/* FIXME this with SQL_VARCHAR produce wrong protocol data */
90 		CHKBindParameter(3, SQL_PARAM_INPUT, SQL_C_CHAR,
91 			SQL_WVARCHAR, len, 0, (void *) p[1], 0, &s2_len, "S");
92 		s1_len = strlen(p[0]);
93 		s2_len = strlen(p[1]);
94 		printf("insert #%d\n", (int) n);
95 		CHKExecute("S");
96 	}
97 
98 	/* check rows */
99 	for (n = 1, p = strings_hex; p[0] && p[1]; p += 2, ++n) {
100 		sprintf(tmp, "IF NOT EXISTS(SELECT * FROM %s WHERE k = %d AND c = %s AND vc = %s) SELECT 1", table_name, (int) n, p[0], p[1]);
101 		CheckNoRow(tmp);
102 	}
103 
104 	odbc_reset_statement();
105 }
106 
107 int
main(int argc,char * argv[])108 main(int argc, char *argv[])
109 {
110 	SQLSMALLINT len;
111 	const char * const*p;
112 	SQLINTEGER n;
113 
114 	if (odbc_read_login_info())
115 		exit(1);
116 
117 	/* connect string using DSN */
118 	init_connect();
119 	sprintf(tmp, "DSN=%s;UID=%s;PWD=%s;DATABASE=%s;ClientCharset=UTF-8;", odbc_server, odbc_user, odbc_password, odbc_database);
120 	CHKDriverConnect(NULL, T(tmp), SQL_NTS, (SQLTCHAR *) tmp, sizeof(tmp)/sizeof(SQLTCHAR), &len, SQL_DRIVER_NOPROMPT, "SI");
121 	if (!odbc_driver_is_freetds()) {
122 		odbc_disconnect();
123 		printf("Driver is not FreeTDS, exiting\n");
124 		odbc_test_skipped();
125 		return 0;
126 	}
127 
128 	if (!odbc_db_is_microsoft() || odbc_db_version_int() < 0x08000000u) {
129 		odbc_disconnect();
130 		printf("Test for MSSQL only\n");
131 		odbc_test_skipped();
132 		return 0;
133 	}
134 
135 	CHKAllocStmt(&odbc_stmt, "S");
136 
137 	/* create test table */
138 	sprintf(tmp, "IF OBJECT_ID(N'%s') IS NOT NULL DROP TABLE %s", table_name, table_name);
139 	odbc_command(tmp);
140 	sprintf(tmp, "CREATE TABLE %s (k int, c NCHAR(10), vc NVARCHAR(10))", table_name);
141 	odbc_command(tmp);
142 
143 	/* insert with INSERT statements */
144 	for (n = 1, p = strings; p[0] && p[1]; p += 2, ++n) {
145 		sprintf(tmp, "INSERT INTO %s VALUES (%d,N'%s',N'%s')", table_name, (int) n, p[0], p[1]);
146 		odbc_command(tmp);
147 	}
148 
149 	/* check rows */
150 	for (n = 1, p = strings_hex; p[0] && p[1]; p += 2, ++n) {
151 		sprintf(tmp, "IF NOT EXISTS(SELECT * FROM %s WHERE k = %d AND c = %s AND vc = %s) SELECT 1", table_name, (int) n, p[0], p[1]);
152 		CheckNoRow(tmp);
153 	}
154 
155 	TestBinding(0);
156 
157 	TestBinding(1);
158 
159 	/* cleanup */
160 	sprintf(tmp, "IF OBJECT_ID(N'%s') IS NOT NULL DROP TABLE %s", table_name, table_name);
161 	odbc_command(tmp);
162 
163 	odbc_disconnect();
164 	printf("Done.\n");
165 	return 0;
166 }
167 
168 #else
169 
170 int
main(void)171 main(void)
172 {
173 	/* on Windows SQLExecDirect is always converted to SQLExecDirectW by the DM */
174 	printf("Not possible for this platform.\n");
175 	odbc_test_skipped();
176 	return 0;
177 }
178 #endif
179