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 10 : read_login_info(void)
61 : {
62 10 : FILE *in = NULL;
63 : char line[512];
64 : char *s1, *s2;
65 :
66 10 : s1 = getenv("TDSPWDFILE");
67 10 : if (s1 && s1[0])
68 0 : in = fopen(s1, "r");
69 0 : if (!in)
70 10 : in = fopen("../../../PWD", "r");
71 10 : if (!in) {
72 0 : fprintf(stderr, "Can not open PWD file\n\n");
73 0 : return false;
74 : }
75 :
76 198 : while (fgets(line, sizeof(line), in)) {
77 188 : s1 = strtok(line, "=");
78 188 : s2 = strtok(NULL, "\n");
79 188 : if (!s1 || !s2) {
80 114 : continue;
81 : }
82 74 : if (!strcmp(s1, "UID")) {
83 10 : strcpy(USER, s2);
84 64 : } else if (!strcmp(s1, "SRV")) {
85 10 : strcpy(SERVER, s2);
86 54 : } else if (!strcmp(s1, "PWD")) {
87 10 : strcpy(PASSWORD, s2);
88 44 : } else if (!strcmp(s1, "DB")) {
89 10 : strcpy(DATABASE, s2);
90 : }
91 : }
92 10 : fclose(in);
93 10 : 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 30 : normalize_spaces(char *s)
105 : {
106 : char *p, *dest, prev;
107 :
108 : /* replace all tabs with spaces */
109 13926 : for (p = s; *p; ++p)
110 13896 : if (*p == '\t')
111 190 : *p = ' ';
112 :
113 : /* replace duplicate spaces with a single space */
114 : prev = 'x';
115 13896 : for (dest = s, p = s; *p; ++p) {
116 13896 : if (prev == ' ' && *p == ' ')
117 764 : continue;
118 13132 : *dest++ = prev = *p;
119 : }
120 30 : *dest = 0;
121 30 : }
122 :
123 : /* read a file and output on a stream */
124 : static void
125 30 : cat(const char *fn, FILE *out)
126 : {
127 : char line[1024];
128 30 : FILE *f = fopen(fn, "r");
129 30 : assert(f);
130 460 : while (fgets(line, sizeof(line), f)) {
131 430 : fputs(" ", out);
132 430 : fputs(line, out);
133 : }
134 30 : fclose(f);
135 30 : }
136 :
137 : /* read a text file into memory, return it as a string */
138 : static char *
139 180 : read_file(const char *fn)
140 : {
141 : long pos;
142 : char *buf;
143 : size_t readed;
144 :
145 180 : FILE *f = fopen(fn, "r");
146 180 : assert(f);
147 180 : assert(fseek(f, 0, SEEK_END) == 0);
148 180 : pos = ftell(f);
149 180 : assert(pos >= 0);
150 180 : assert(fseek(f, 0, SEEK_SET) == 0);
151 180 : buf = malloc(pos + 10); /* allocate some more space */
152 180 : assert(buf);
153 180 : readed = fread(buf, 1, pos+1, f);
154 180 : assert(readed <= pos);
155 180 : assert(feof(f));
156 180 : fclose(f);
157 180 : buf[readed] = 0;
158 180 : 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 750 : quote_arg(char *dest, char *dest_end, const char *arg)
168 : {
169 : #ifndef _WIN32
170 750 : CHECK(1);
171 750 : *dest++ = '\'';
172 6778 : for (; *arg; ++arg) {
173 6028 : if (*arg == '\'') {
174 0 : CHECK(3);
175 0 : strcpy(dest, "'\\'");
176 0 : dest += 3;
177 : }
178 6028 : CHECK(1);
179 6028 : *dest++ = *arg;
180 : }
181 750 : CHECK(1);
182 750 : *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 750 : return dest;
198 : }
199 :
200 : static char *
201 930 : add_string(char *dest, char *const dest_end, const char *str)
202 : {
203 930 : size_t len = strlen(str);
204 930 : CHECK(len);
205 930 : memcpy(dest, str, len);
206 930 : return dest + len;
207 : }
208 :
209 : #undef CHECK
210 :
211 : static char *
212 180 : add_server(char *dest, char *const dest_end)
213 : {
214 180 : dest = add_string(dest, dest_end, " -S ");
215 180 : dest = quote_arg(dest, dest_end, SERVER);
216 180 : dest = add_string(dest, dest_end, " -U ");
217 180 : dest = quote_arg(dest, dest_end, USER);
218 180 : dest = add_string(dest, dest_end, " -P ");
219 180 : dest = quote_arg(dest, dest_end, PASSWORD);
220 180 : if (DATABASE[0]) {
221 180 : dest = add_string(dest, dest_end, " -D ");
222 180 : dest = quote_arg(dest, dest_end, DATABASE);
223 : }
224 180 : return dest;
225 : }
226 :
227 : static void
228 20 : cleanup(void)
229 : {
230 20 : unlink("empty");
231 20 : unlink("output");
232 20 : unlink("input");
233 20 : TDS_ZERO_FREE(output);
234 20 : }
235 :
236 : static void
237 150 : tsql(const char *input_data)
238 : {
239 : char cmd[2048];
240 150 : char *const end = cmd + sizeof(cmd) - 1;
241 : char *p;
242 : FILE *f;
243 :
244 150 : f = fopen("input", "w");
245 150 : assert(f);
246 150 : fputs(input_data, f);
247 150 : fclose(f);
248 :
249 150 : strcpy(cmd, ".." SDIR_SEPARATOR "tsql" EXE_SUFFIX " -o q");
250 150 : p = strchr(cmd, 0);
251 150 : p = add_server(p, end);
252 150 : p = add_string(p, end, "<input >output");
253 150 : *p = 0;
254 150 : printf("Executing: %s\n", cmd);
255 150 : 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 150 : TDS_ZERO_FREE(output);
262 150 : output = read_file("output");
263 150 : }
264 :
265 : static void
266 30 : defncopy(const char *object_name)
267 : {
268 : char cmd[2048];
269 30 : char *const end = cmd + sizeof(cmd) - 1;
270 : char *p;
271 : FILE *f;
272 :
273 : // empty input
274 30 : f = fopen("input", "w");
275 30 : assert(f);
276 30 : fclose(f);
277 :
278 30 : strcpy(cmd, ".." SDIR_SEPARATOR "defncopy" EXE_SUFFIX);
279 30 : p = strchr(cmd, 0);
280 30 : p = add_server(p, end);
281 30 : p = add_string(p, end, " ");
282 30 : p = quote_arg(p, end, object_name);
283 30 : p = add_string(p, end, "<input >output");
284 30 : *p = 0;
285 30 : printf("Executing: %s\n", cmd);
286 30 : 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 30 : TDS_ZERO_FREE(output);
293 30 : output = read_file("output");
294 30 : }
295 :
296 : /* table with a column name that is also a keyword, should be quoted */
297 : static void
298 10 : 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 10 : tsql(clean);
305 10 : 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 10 : defncopy("dbo.table_with_column_named_key");
313 10 : cat("output", stdout);
314 10 : normalize_spaces(output);
315 10 : sql =
316 : "CREATE TABLE [dbo].[table_with_column_named_key]\n"
317 : " ( [key] nvarchar(4000) NOT NULL\n"
318 : " )\n"
319 : "GO";
320 10 : if (strstr(output, sql) == NULL) {
321 0 : fprintf(stderr, "Expected SQL string not found\n");
322 0 : exit(1);
323 : }
324 10 : tsql(clean);
325 10 : tsql(sql);
326 10 : tsql(clean);
327 10 : }
328 :
329 : /* table with an index with a space inside */
330 : static void
331 10 : 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 10 : tsql(clean);
338 10 : 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 10 : defncopy("dbo.tblReportPeriod");
346 10 : cat("output", stdout);
347 10 : normalize_spaces(output);
348 10 : 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 10 : if (strstr(output, sql) == NULL) {
358 0 : fprintf(stderr, "Expected SQL string not found\n");
359 0 : exit(1);
360 : }
361 10 : tsql(clean);
362 10 : tsql(sql);
363 10 : tsql(clean);
364 10 : }
365 :
366 : /* table with an index with a space inside */
367 : static void
368 10 : 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 10 : tsql(clean);
375 10 : 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 10 : defncopy("dbo.tblReportPeriod2");
384 10 : cat("output", stdout);
385 10 : normalize_spaces(output);
386 10 : 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 10 : 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 10 : 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 10 : tsql(clean);
412 10 : tsql(sql);
413 10 : tsql(clean);
414 10 : }
415 :
416 10 : int main(void)
417 : {
418 : FILE *f;
419 :
420 10 : cleanup();
421 :
422 10 : if (!read_login_info())
423 : return 1;
424 :
425 10 : f = fopen("empty", "w");
426 10 : if (f)
427 10 : fclose(f);
428 :
429 10 : test_keyword();
430 10 : test_index_name_with_space();
431 10 : test_weird_index_names();
432 :
433 10 : cleanup();
434 10 : return 0;
435 : }
|