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 : #undef NDEBUG
25 : #include <config.h>
26 :
27 : #include <stdio.h>
28 : #include <stdlib.h>
29 : #include <assert.h>
30 :
31 : #if HAVE_STRING_H
32 : #include <string.h>
33 : #endif /* HAVE_STRING_H */
34 :
35 : #ifdef HAVE_UNISTD_H
36 : #include <unistd.h>
37 : #endif
38 :
39 : #ifdef _WIN32
40 : #include <process.h>
41 : #define EXE_SUFFIX ".exe"
42 : #define SDIR_SEPARATOR "\\"
43 : #else
44 : #define EXE_SUFFIX ""
45 : #define SDIR_SEPARATOR "/"
46 : #endif
47 :
48 : #include <freetds/bool.h>
49 : #include <freetds/macros.h>
50 :
51 : static char USER[512];
52 : static char SERVER[512];
53 : static char PASSWORD[512];
54 : static char DATABASE[512];
55 :
56 : /* content of output file, from command executed */
57 : static char *output;
58 :
59 : static bool
60 8 : read_login_info(void)
61 : {
62 8 : FILE *in = NULL;
63 : char line[512];
64 : char *s1, *s2;
65 :
66 8 : s1 = getenv("TDSPWDFILE");
67 8 : if (s1 && s1[0])
68 0 : in = fopen(s1, "r");
69 0 : if (!in)
70 8 : in = fopen("../../../PWD", "r");
71 8 : if (!in) {
72 0 : fprintf(stderr, "Can not open PWD file\n\n");
73 0 : return false;
74 : }
75 :
76 152 : while (fgets(line, sizeof(line), in)) {
77 144 : s1 = strtok(line, "=");
78 144 : s2 = strtok(NULL, "\n");
79 144 : if (!s1 || !s2) {
80 86 : continue;
81 : }
82 58 : if (!strcmp(s1, "UID")) {
83 8 : strcpy(USER, s2);
84 50 : } else if (!strcmp(s1, "SRV")) {
85 8 : strcpy(SERVER, s2);
86 42 : } else if (!strcmp(s1, "PWD")) {
87 8 : strcpy(PASSWORD, s2);
88 34 : } else if (!strcmp(s1, "DB")) {
89 8 : strcpy(DATABASE, s2);
90 : }
91 : }
92 8 : fclose(in);
93 8 : return true;
94 : }
95 :
96 : static void
97 0 : no_space(void)
98 : {
99 0 : fprintf(stderr, "No space left on buffer\n");
100 0 : exit(1);
101 : }
102 :
103 : static void
104 24 : normalize_spaces(char *s)
105 : {
106 : char *p, *dest, prev;
107 :
108 : /* replace all tabs with spaces */
109 10534 : for (p = s; *p; ++p)
110 10510 : if (*p == '\t')
111 148 : *p = ' ';
112 :
113 : /* replace duplicate spaces with a single space */
114 : prev = 'x';
115 10510 : for (dest = s, p = s; *p; ++p) {
116 10510 : if (prev == ' ' && *p == ' ')
117 612 : continue;
118 9898 : *dest++ = prev = *p;
119 : }
120 24 : *dest = 0;
121 24 : }
122 :
123 : /* read a file and output on a stream */
124 : static void
125 24 : cat(const char *fn, FILE *out)
126 : {
127 : char line[1024];
128 24 : FILE *f = fopen(fn, "r");
129 24 : assert(f);
130 364 : while (fgets(line, sizeof(line), f)) {
131 340 : fputs(" ", out);
132 340 : fputs(line, out);
133 : }
134 24 : fclose(f);
135 24 : }
136 :
137 : /* read a text file into memory, return it as a string */
138 : static char *
139 144 : read_file(const char *fn)
140 : {
141 : long pos;
142 : char *buf;
143 : size_t readed;
144 :
145 144 : FILE *f = fopen(fn, "r");
146 144 : assert(f);
147 144 : assert(fseek(f, 0, SEEK_END) == 0);
148 144 : pos = ftell(f);
149 144 : assert(pos >= 0);
150 144 : assert(fseek(f, 0, SEEK_SET) == 0);
151 144 : buf = malloc(pos + 10); /* allocate some more space */
152 144 : assert(buf);
153 144 : readed = fread(buf, 1, pos+1, f);
154 144 : assert(readed <= pos);
155 144 : assert(feof(f));
156 144 : fclose(f);
157 144 : buf[readed] = 0;
158 144 : return buf;
159 : }
160 :
161 : #define CHECK(n) do {\
162 : if (dest + (n) > dest_end) \
163 : no_space(); \
164 : } while(0)
165 :
166 : static char *
167 600 : quote_arg(char *dest, char *dest_end, const char *arg)
168 : {
169 : #ifndef _WIN32
170 600 : CHECK(1);
171 600 : *dest++ = '\'';
172 5516 : for (; *arg; ++arg) {
173 4916 : if (*arg == '\'') {
174 0 : CHECK(3);
175 0 : strcpy(dest, "'\\'");
176 0 : dest += 3;
177 : }
178 4916 : CHECK(1);
179 4916 : *dest++ = *arg;
180 : }
181 600 : CHECK(1);
182 600 : *dest++ = '\'';
183 : #else
184 : CHECK(1);
185 : *dest++ = '\"';
186 : for (; *arg; ++arg) {
187 : if (*arg == '\\' || *arg == '\"') {
188 : CHECK(1);
189 : *dest++ = '\\';
190 : }
191 : CHECK(1);
192 : *dest++ = *arg;
193 : }
194 : CHECK(1);
195 : *dest++ = '\"';
196 : #endif
197 600 : return dest;
198 : }
199 :
200 : static char *
201 744 : add_string(char *dest, char *const dest_end, const char *str)
202 : {
203 744 : size_t len = strlen(str);
204 744 : CHECK(len);
205 744 : memcpy(dest, str, len);
206 744 : return dest + len;
207 : }
208 :
209 : #undef CHECK
210 :
211 : static char *
212 144 : add_server(char *dest, char *const dest_end)
213 : {
214 144 : dest = add_string(dest, dest_end, " -S ");
215 144 : dest = quote_arg(dest, dest_end, SERVER);
216 144 : dest = add_string(dest, dest_end, " -U ");
217 144 : dest = quote_arg(dest, dest_end, USER);
218 144 : dest = add_string(dest, dest_end, " -P ");
219 144 : dest = quote_arg(dest, dest_end, PASSWORD);
220 144 : if (DATABASE[0]) {
221 144 : dest = add_string(dest, dest_end, " -D ");
222 144 : dest = quote_arg(dest, dest_end, DATABASE);
223 : }
224 144 : return dest;
225 : }
226 :
227 : static void
228 16 : cleanup(void)
229 : {
230 16 : unlink("empty");
231 16 : unlink("output");
232 16 : unlink("input");
233 16 : TDS_ZERO_FREE(output);
234 16 : }
235 :
236 : static void
237 120 : tsql(const char *input_data)
238 : {
239 : char cmd[2048];
240 120 : char *const end = cmd + sizeof(cmd) - 1;
241 : char *p;
242 : FILE *f;
243 :
244 120 : f = fopen("input", "w");
245 120 : assert(f);
246 120 : fputs(input_data, f);
247 120 : fclose(f);
248 :
249 120 : strcpy(cmd, ".." SDIR_SEPARATOR "tsql" EXE_SUFFIX " -o q");
250 120 : p = strchr(cmd, 0);
251 120 : p = add_server(p, end);
252 120 : p = add_string(p, end, "<input >output");
253 120 : *p = 0;
254 120 : printf("Executing: %s\n", cmd);
255 120 : if (system(cmd) != 0) {
256 0 : printf("Output is:\n");
257 0 : cat("output", stdout);
258 0 : fprintf(stderr, "Failed command\n");
259 0 : exit(1);
260 : }
261 120 : TDS_ZERO_FREE(output);
262 120 : output = read_file("output");
263 120 : }
264 :
265 : static void
266 24 : defncopy(const char *object_name)
267 : {
268 : char cmd[2048];
269 24 : char *const end = cmd + sizeof(cmd) - 1;
270 : char *p;
271 : FILE *f;
272 :
273 : // empty input
274 24 : f = fopen("input", "w");
275 24 : assert(f);
276 24 : fclose(f);
277 :
278 24 : strcpy(cmd, ".." SDIR_SEPARATOR "defncopy" EXE_SUFFIX);
279 24 : p = strchr(cmd, 0);
280 24 : p = add_server(p, end);
281 24 : p = add_string(p, end, " ");
282 24 : p = quote_arg(p, end, object_name);
283 24 : p = add_string(p, end, "<input >output");
284 24 : *p = 0;
285 24 : printf("Executing: %s\n", cmd);
286 24 : if (system(cmd) != 0) {
287 0 : printf("Output is:\n");
288 0 : cat("output", stdout);
289 0 : fprintf(stderr, "Failed command\n");
290 0 : exit(1);
291 : }
292 24 : TDS_ZERO_FREE(output);
293 24 : output = read_file("output");
294 24 : }
295 :
296 : /* table with a column name that is also a keyword, should be quoted */
297 : static void
298 8 : test_keyword(void)
299 : {
300 : const char *sql;
301 : static const char clean[] =
302 : "IF OBJECT_ID('dbo.table_with_column_named_key') IS NOT NULL DROP TABLE dbo.table_with_column_named_key\n";
303 :
304 8 : tsql(clean);
305 8 : tsql(
306 : "IF OBJECT_ID('dbo.table_with_column_named_key') IS NOT NULL DROP TABLE dbo.table_with_column_named_key\n"
307 : "GO\n"
308 : "CREATE TABLE dbo.table_with_column_named_key\n"
309 : "(\n"
310 : " [key] nvarchar(4000) NOT NULL\n"
311 : ")\n");
312 8 : defncopy("dbo.table_with_column_named_key");
313 8 : cat("output", stdout);
314 8 : normalize_spaces(output);
315 8 : sql =
316 : "CREATE TABLE [dbo].[table_with_column_named_key]\n"
317 : " ( [key] nvarchar(4000) NOT NULL\n"
318 : " )\n"
319 : "GO";
320 8 : if (strstr(output, sql) == NULL) {
321 0 : fprintf(stderr, "Expected SQL string not found\n");
322 0 : exit(1);
323 : }
324 8 : tsql(clean);
325 8 : tsql(sql);
326 8 : tsql(clean);
327 8 : }
328 :
329 : /* table with an index with a space inside */
330 : static void
331 8 : test_index_name_with_space(void)
332 : {
333 : const char *sql;
334 : static const char clean[] =
335 : "IF OBJECT_ID('dbo.tblReportPeriod') IS NOT NULL DROP TABLE dbo.tblReportPeriod\n";
336 :
337 8 : tsql(clean);
338 8 : tsql(
339 : "CREATE TABLE dbo.tblReportPeriod\n"
340 : " ( RecordID int NOT NULL\n"
341 : " , FromDate nvarchar(40) NULL\n"
342 : " , ToDate nvarchar(40) NULL\n"
343 : " )\n"
344 : "CREATE nonclustered INDEX [From Date] on dbo.tblReportPeriod(FromDate)\n");
345 8 : defncopy("dbo.tblReportPeriod");
346 8 : cat("output", stdout);
347 8 : normalize_spaces(output);
348 8 : sql =
349 : "CREATE TABLE [dbo].[tblReportPeriod]\n"
350 : " ( [RecordID] int NOT NULL\n"
351 : " , [FromDate] nvarchar(40) NULL\n"
352 : " , [ToDate] nvarchar(40) NULL\n"
353 : " )\n"
354 : "GO\n"
355 : "\n"
356 : "CREATE nonclustered INDEX [From Date] on [dbo].[tblReportPeriod]([FromDate])";
357 8 : if (strstr(output, sql) == NULL) {
358 0 : fprintf(stderr, "Expected SQL string not found\n");
359 0 : exit(1);
360 : }
361 8 : tsql(clean);
362 8 : tsql(sql);
363 8 : tsql(clean);
364 8 : }
365 :
366 : /* table with an index with a space inside */
367 : static void
368 8 : test_weird_index_names(void)
369 : {
370 : const char *sql, *sql_sybase;
371 : static const char clean[] =
372 : "IF OBJECT_ID('dbo.tblReportPeriod2') IS NOT NULL DROP TABLE dbo.tblReportPeriod2\n";
373 :
374 8 : tsql(clean);
375 8 : tsql(
376 : "CREATE TABLE dbo.tblReportPeriod2\n"
377 : " ( RecordID int NOT NULL\n"
378 : " , [To, ] nvarchar(40) NULL\n"
379 : " , [To] nvarchar(40) NULL\n"
380 : " , [To, , ] nvarchar(40) NULL\n"
381 : " )\n"
382 : "CREATE nonclustered INDEX [From Date] on dbo.tblReportPeriod2([To, ],[To, , ])\n");
383 8 : defncopy("dbo.tblReportPeriod2");
384 8 : cat("output", stdout);
385 8 : normalize_spaces(output);
386 8 : sql =
387 : "CREATE TABLE [dbo].[tblReportPeriod2]\n"
388 : " ( [RecordID] int NOT NULL\n"
389 : " , [To, ] nvarchar(40) NULL\n"
390 : " , [To] nvarchar(40) NULL\n"
391 : " , [To, , ] nvarchar(40) NULL\n"
392 : " )\n"
393 : "GO\n"
394 : "\n"
395 : "CREATE nonclustered INDEX [From Date] on [dbo].[tblReportPeriod2]([To, ],[To, , ])";
396 : /* Sybase remove spaces at the end */
397 8 : sql_sybase =
398 : "CREATE TABLE [dbo].[tblReportPeriod2]\n"
399 : " ( [RecordID] int NOT NULL\n"
400 : " , [To,] nvarchar(40) NULL\n"
401 : " , [To] nvarchar(40) NULL\n"
402 : " , [To, ,] nvarchar(40) NULL\n"
403 : " )\n"
404 : "GO\n"
405 : "\n"
406 : "CREATE nonclustered INDEX [From Date] on [dbo].[tblReportPeriod2]([To,],[To, ,])";
407 8 : if (strstr(output, sql) == NULL && strstr(output, sql_sybase) == NULL) {
408 0 : fprintf(stderr, "Expected SQL string not found\n");
409 0 : exit(1);
410 : }
411 8 : tsql(clean);
412 8 : tsql(sql);
413 8 : tsql(clean);
414 8 : }
415 :
416 8 : int main(void)
417 : {
418 : FILE *f;
419 :
420 8 : cleanup();
421 :
422 8 : if (!read_login_info())
423 : return 1;
424 :
425 8 : f = fopen("empty", "w");
426 8 : if (f)
427 8 : fclose(f);
428 :
429 8 : test_keyword();
430 8 : test_index_name_with_space();
431 8 : test_weird_index_names();
432 :
433 8 : cleanup();
434 8 : return 0;
435 : }
|