Line data Source code
1 : /* FreeTDS - Library of routines accessing Sybase and Microsoft databases
2 : * Copyright (C) 2024 Frediano Ziglio
3 : *
4 : * This library is free software; you can redistribute it and/or
5 : * modify it under the terms of the GNU Library General Public
6 : * License as published by the Free Software Foundation; either
7 : * version 2 of the License, or (at your option) any later version.
8 : *
9 : * This library is distributed in the hope that it will be useful,
10 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 : * Library General Public License for more details.
13 : *
14 : * You should have received a copy of the GNU Library General Public
15 : * License along with this library; if not, write to the
16 : * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 : * Boston, MA 02111-1307, USA.
18 : */
19 :
20 : /**
21 : * This tests execute some command using tsql and defncopy to check behaviour
22 : */
23 :
24 : #include <freetds/utils/test_base.h>
25 :
26 : #include <stdio.h>
27 : #include <stdlib.h>
28 : #include <assert.h>
29 :
30 : #if HAVE_STRING_H
31 : #include <string.h>
32 : #endif /* HAVE_STRING_H */
33 :
34 : #ifdef HAVE_UNISTD_H
35 : #include <unistd.h>
36 : #endif
37 :
38 : #ifdef _WIN32
39 : #include <process.h>
40 : #define EXE_SUFFIX ".exe"
41 : #define SDIR_SEPARATOR "\\"
42 : #else
43 : #define EXE_SUFFIX ""
44 : #define SDIR_SEPARATOR "/"
45 : #endif
46 :
47 : #include <freetds/bool.h>
48 : #include <freetds/macros.h>
49 : #include <freetds/sysdep_private.h>
50 :
51 : /* content of output file, from command executed */
52 : static char *output;
53 :
54 : static bool
55 : read_login_info(void)
56 : {
57 10 : return read_login_info_base(&common_pwd, DEFAULT_PWD_PATH) != NULL;
58 : }
59 :
60 : static void
61 0 : no_space(void)
62 : {
63 0 : fprintf(stderr, "No space left on buffer\n");
64 0 : exit(1);
65 : }
66 :
67 : static void
68 30 : normalize_spaces(char *s)
69 : {
70 : char *p, *dest, prev;
71 :
72 : /* replace all tabs with spaces */
73 13926 : for (p = s; *p; ++p)
74 13896 : if (*p == '\t')
75 190 : *p = ' ';
76 :
77 : /* replace duplicate spaces with a single space */
78 : prev = 'x';
79 13896 : for (dest = s, p = s; *p; ++p) {
80 13896 : if (prev == ' ' && *p == ' ')
81 764 : continue;
82 13132 : *dest++ = prev = *p;
83 : }
84 30 : *dest = 0;
85 30 : }
86 :
87 : /* read a file and output on a stream */
88 : static void
89 30 : cat(const char *fn, FILE *out)
90 : {
91 : char line[1024];
92 30 : FILE *f = fopen(fn, "r");
93 30 : assert(f);
94 460 : while (fgets(line, sizeof(line), f)) {
95 430 : fputs(" ", out);
96 430 : fputs(line, out);
97 : }
98 30 : fclose(f);
99 30 : }
100 :
101 : /* read a text file into memory, return it as a string */
102 : static char *
103 180 : read_file(const char *fn)
104 : {
105 : long pos;
106 : char *buf;
107 : size_t readed, size;
108 :
109 180 : FILE *f = fopen(fn, "r");
110 180 : assert(f);
111 180 : assert(fseek(f, 0, SEEK_END) == 0);
112 180 : pos = ftell(f);
113 180 : assert(pos >= 0 && pos <= 0x1000000);
114 180 : size = (size_t) pos;
115 180 : assert(fseek(f, 0, SEEK_SET) == 0);
116 180 : buf = malloc(size + 10); /* allocate some more space */
117 180 : assert(buf);
118 180 : readed = fread(buf, 1, size + 1ul, f);
119 180 : assert(readed <= size);
120 180 : assert(feof(f));
121 180 : fclose(f);
122 180 : buf[readed] = 0;
123 180 : return buf;
124 : }
125 :
126 : #define CHECK(n) do {\
127 : if (dest + (n) > dest_end) \
128 : no_space(); \
129 : } while(0)
130 :
131 : static char *
132 750 : quote_arg(char *dest, char *dest_end, const char *arg)
133 : {
134 : #ifndef _WIN32
135 750 : CHECK(1);
136 750 : *dest++ = '\'';
137 6778 : for (; *arg; ++arg) {
138 6028 : if (*arg == '\'') {
139 0 : CHECK(3);
140 0 : strcpy(dest, "'\\'");
141 0 : dest += 3;
142 : }
143 6028 : CHECK(1);
144 6028 : *dest++ = *arg;
145 : }
146 750 : CHECK(1);
147 750 : *dest++ = '\'';
148 : #else
149 : CHECK(1);
150 : *dest++ = '\"';
151 : for (; *arg; ++arg) {
152 : if (*arg == '\\' || *arg == '\"') {
153 : CHECK(1);
154 : *dest++ = '\\';
155 : }
156 : CHECK(1);
157 : *dest++ = *arg;
158 : }
159 : CHECK(1);
160 : *dest++ = '\"';
161 : #endif
162 750 : return dest;
163 : }
164 :
165 : static char *
166 930 : add_string(char *dest, char *const dest_end, const char *str)
167 : {
168 930 : size_t len = strlen(str);
169 930 : CHECK(len);
170 930 : memcpy(dest, str, len);
171 930 : return dest + len;
172 : }
173 :
174 : #undef CHECK
175 :
176 : static char *
177 180 : add_server(char *dest, char *const dest_end)
178 : {
179 180 : dest = add_string(dest, dest_end, " -S ");
180 180 : dest = quote_arg(dest, dest_end, common_pwd.server);
181 180 : dest = add_string(dest, dest_end, " -U ");
182 180 : dest = quote_arg(dest, dest_end, common_pwd.user);
183 180 : dest = add_string(dest, dest_end, " -P ");
184 180 : dest = quote_arg(dest, dest_end, common_pwd.password);
185 180 : if (common_pwd.database[0]) {
186 180 : dest = add_string(dest, dest_end, " -D ");
187 180 : dest = quote_arg(dest, dest_end, common_pwd.database);
188 : }
189 180 : return dest;
190 : }
191 :
192 : static void
193 20 : cleanup(void)
194 : {
195 20 : unlink("empty");
196 20 : unlink("output");
197 20 : unlink("input");
198 20 : TDS_ZERO_FREE(output);
199 20 : }
200 :
201 : static void
202 150 : tsql(const char *input_data)
203 : {
204 : char cmd[2048];
205 150 : char *const end = cmd + sizeof(cmd) - 1;
206 : char *p;
207 : FILE *f;
208 :
209 150 : f = fopen("input", "w");
210 150 : assert(f);
211 150 : fputs(input_data, f);
212 150 : fclose(f);
213 :
214 150 : strcpy(cmd, ".." SDIR_SEPARATOR "tsql" EXE_SUFFIX " -o q");
215 150 : p = strchr(cmd, 0);
216 150 : p = add_server(p, end);
217 150 : p = add_string(p, end, "<input >output");
218 150 : *p = 0;
219 150 : printf("Executing: %s\n", cmd);
220 150 : if (system(cmd) != 0) {
221 0 : printf("Output is:\n");
222 0 : cat("output", stdout);
223 0 : fprintf(stderr, "Failed command\n");
224 0 : exit(1);
225 : }
226 150 : TDS_ZERO_FREE(output);
227 150 : output = read_file("output");
228 150 : }
229 :
230 : static void
231 30 : defncopy(const char *object_name)
232 : {
233 : char cmd[2048];
234 30 : char *const end = cmd + sizeof(cmd) - 1;
235 : char *p;
236 : FILE *f;
237 :
238 : // empty input
239 30 : f = fopen("input", "w");
240 30 : assert(f);
241 30 : fclose(f);
242 :
243 30 : strcpy(cmd, ".." SDIR_SEPARATOR "defncopy" EXE_SUFFIX);
244 30 : p = strchr(cmd, 0);
245 30 : p = add_server(p, end);
246 30 : p = add_string(p, end, " ");
247 30 : p = quote_arg(p, end, object_name);
248 30 : p = add_string(p, end, "<input >output");
249 30 : *p = 0;
250 30 : printf("Executing: %s\n", cmd);
251 30 : if (system(cmd) != 0) {
252 0 : printf("Output is:\n");
253 0 : cat("output", stdout);
254 0 : fprintf(stderr, "Failed command\n");
255 0 : exit(1);
256 : }
257 30 : TDS_ZERO_FREE(output);
258 30 : output = read_file("output");
259 30 : }
260 :
261 : /* table with a column name that is also a keyword, should be quoted */
262 : static void
263 10 : test_keyword(void)
264 : {
265 : const char *sql;
266 : static const char clean[] =
267 : "IF OBJECT_ID('dbo.table_with_column_named_key') IS NOT NULL DROP TABLE dbo.table_with_column_named_key\n";
268 :
269 10 : tsql(clean);
270 10 : tsql(
271 : "IF OBJECT_ID('dbo.table_with_column_named_key') IS NOT NULL DROP TABLE dbo.table_with_column_named_key\n"
272 : "GO\n"
273 : "CREATE TABLE dbo.table_with_column_named_key\n"
274 : "(\n"
275 : " [key] nvarchar(4000) NOT NULL\n"
276 : ")\n");
277 10 : defncopy("dbo.table_with_column_named_key");
278 10 : cat("output", stdout);
279 10 : normalize_spaces(output);
280 10 : sql =
281 : "CREATE TABLE [dbo].[table_with_column_named_key]\n"
282 : " ( [key] nvarchar(4000) NOT NULL\n"
283 : " )\n"
284 : "GO";
285 10 : if (strstr(output, sql) == NULL) {
286 0 : fprintf(stderr, "Expected SQL string not found\n");
287 0 : exit(1);
288 : }
289 10 : tsql(clean);
290 10 : tsql(sql);
291 10 : tsql(clean);
292 10 : }
293 :
294 : /* table with an index with a space inside */
295 : static void
296 10 : test_index_name_with_space(void)
297 : {
298 : const char *sql;
299 : static const char clean[] =
300 : "IF OBJECT_ID('dbo.tblReportPeriod') IS NOT NULL DROP TABLE dbo.tblReportPeriod\n";
301 :
302 10 : tsql(clean);
303 10 : tsql(
304 : "CREATE TABLE dbo.tblReportPeriod\n"
305 : " ( RecordID int NOT NULL\n"
306 : " , FromDate nvarchar(40) NULL\n"
307 : " , ToDate nvarchar(40) NULL\n"
308 : " )\n"
309 : "CREATE nonclustered INDEX [From Date] on dbo.tblReportPeriod(FromDate)\n");
310 10 : defncopy("dbo.tblReportPeriod");
311 10 : cat("output", stdout);
312 10 : normalize_spaces(output);
313 10 : sql =
314 : "CREATE TABLE [dbo].[tblReportPeriod]\n"
315 : " ( [RecordID] int NOT NULL\n"
316 : " , [FromDate] nvarchar(40) NULL\n"
317 : " , [ToDate] nvarchar(40) NULL\n"
318 : " )\n"
319 : "GO\n"
320 : "\n"
321 : "CREATE nonclustered INDEX [From Date] on [dbo].[tblReportPeriod]([FromDate])";
322 10 : if (strstr(output, sql) == NULL) {
323 0 : fprintf(stderr, "Expected SQL string not found\n");
324 0 : exit(1);
325 : }
326 10 : tsql(clean);
327 10 : tsql(sql);
328 10 : tsql(clean);
329 10 : }
330 :
331 : /* table with an index with a space inside */
332 : static void
333 10 : test_weird_index_names(void)
334 : {
335 : const char *sql, *sql_sybase;
336 : static const char clean[] =
337 : "IF OBJECT_ID('dbo.tblReportPeriod2') IS NOT NULL DROP TABLE dbo.tblReportPeriod2\n";
338 :
339 10 : tsql(clean);
340 10 : tsql(
341 : "CREATE TABLE dbo.tblReportPeriod2\n"
342 : " ( RecordID int NOT NULL\n"
343 : " , [To, ] nvarchar(40) NULL\n"
344 : " , [To] nvarchar(40) NULL\n"
345 : " , [To, , ] nvarchar(40) NULL\n"
346 : " )\n"
347 : "CREATE nonclustered INDEX [From Date] on dbo.tblReportPeriod2([To, ],[To, , ])\n");
348 10 : defncopy("dbo.tblReportPeriod2");
349 10 : cat("output", stdout);
350 10 : normalize_spaces(output);
351 10 : sql =
352 : "CREATE TABLE [dbo].[tblReportPeriod2]\n"
353 : " ( [RecordID] int NOT NULL\n"
354 : " , [To, ] nvarchar(40) NULL\n"
355 : " , [To] nvarchar(40) NULL\n"
356 : " , [To, , ] nvarchar(40) NULL\n"
357 : " )\n"
358 : "GO\n"
359 : "\n"
360 : "CREATE nonclustered INDEX [From Date] on [dbo].[tblReportPeriod2]([To, ],[To, , ])";
361 : /* Sybase remove spaces at the end */
362 10 : sql_sybase =
363 : "CREATE TABLE [dbo].[tblReportPeriod2]\n"
364 : " ( [RecordID] int NOT NULL\n"
365 : " , [To,] nvarchar(40) NULL\n"
366 : " , [To] nvarchar(40) NULL\n"
367 : " , [To, ,] nvarchar(40) NULL\n"
368 : " )\n"
369 : "GO\n"
370 : "\n"
371 : "CREATE nonclustered INDEX [From Date] on [dbo].[tblReportPeriod2]([To,],[To, ,])";
372 10 : if (strstr(output, sql) == NULL && strstr(output, sql_sybase) == NULL) {
373 0 : fprintf(stderr, "Expected SQL string not found\n");
374 0 : exit(1);
375 : }
376 10 : tsql(clean);
377 10 : tsql(sql);
378 10 : tsql(clean);
379 10 : }
380 :
381 10 : TEST_MAIN()
382 : {
383 : FILE *f;
384 :
385 10 : cleanup();
386 :
387 10 : if (!read_login_info())
388 : return 1;
389 :
390 10 : f = fopen("empty", "w");
391 10 : if (f)
392 10 : fclose(f);
393 :
394 10 : test_keyword();
395 10 : test_index_name_with_space();
396 10 : test_weird_index_names();
397 :
398 10 : cleanup();
399 10 : return 0;
400 : }
|