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 "common.h"
25 :
26 : #if HAVE_STRING_H
27 : #include <string.h>
28 : #endif /* HAVE_STRING_H */
29 :
30 : #ifdef HAVE_UNISTD_H
31 : #include <unistd.h>
32 : #endif
33 :
34 : #include <freetds/sysdep_private.h>
35 : #include <freetds/replacements.h>
36 : #include <freetds/utils.h>
37 :
38 : /* Sybase mode using a different terminator */
39 : #define TERMINATOR "GO"
40 : #define TERMINATOR_SYBASE "/* ### DEFNCOPY: END OF DEFINITION */"
41 :
42 : /* content of output file, from command executed */
43 : static char *output;
44 :
45 : /* Whether using sybase syntax */
46 : static bool sybase_syntax;
47 : static bool stream_io = true;
48 :
49 : static bool
50 : read_login_info(void)
51 : {
52 10 : return read_login_info_base(&common_pwd, DEFAULT_PWD_PATH) != NULL;
53 : }
54 :
55 : static void
56 20 : cleanup(void)
57 : {
58 20 : unlink("empty");
59 20 : unlink(output_fn());
60 20 : unlink(input_fn());
61 20 : TDS_ZERO_FREE(output);
62 20 : }
63 :
64 : /* check expected output, ignoring whitespace diffs */
65 : static bool
66 80 : output_matches(char const *expect)
67 : {
68 : bool result;
69 : char *tmp;
70 :
71 80 : if (!expect)
72 : return false;
73 80 : normalize_spaces(output);
74 80 : tmp = xstrdup(expect);
75 80 : normalize_spaces(tmp);
76 80 : result = strstr(output, tmp) != NULL;
77 80 : free(tmp);
78 80 : return result;
79 : }
80 :
81 : static void
82 80 : required_output(char const *expect)
83 : {
84 80 : if (!output_matches(expect)) {
85 0 : fprintf(stderr, "Expected SQL string not found\n");
86 0 : exit(EXIT_FAILURE);
87 : }
88 80 : }
89 :
90 : static void
91 120 : defncopy(const char *object_name)
92 : {
93 120 : char cmd[2048] = { 0 };
94 120 : char *const end = cmd + sizeof(cmd) - 1;
95 : char *p;
96 120 : bool redirs = stream_io && !sybase_syntax;
97 :
98 120 : strcat(cmd, VMS_APP_EXE_INVOKE "defncopy" EXE_SUFFIX);
99 120 : p = strchr(cmd, 0);
100 :
101 120 : if (sybase_syntax) {
102 50 : p = add_login(p, end);
103 50 : p = add_string(p, end, object_name ? " out " : " in ");
104 50 : p = add_string(p, end, object_name ? output_fn() : input_fn());
105 50 : if (common_pwd.database[0]) {
106 50 : p = add_string(p, end, " ");
107 50 : p = quote_arg(p, end, common_pwd.database);
108 : }
109 : } else {
110 70 : p = add_server(p, end);
111 70 : if (!stream_io) {
112 20 : p = add_string(p, end, object_name ? " -o " : " -i ");
113 20 : p = add_string(p, end, object_name ? output_fn() : input_fn());
114 : }
115 : }
116 :
117 120 : if (object_name) {
118 80 : p = add_string(p, end, " ");
119 80 : p = quote_arg(p, end, object_name);
120 : }
121 :
122 : /* FreeTDS defncopy syntax supports both standard streams, and input/output files.
123 : */
124 120 : if (redirs) {
125 50 : p = add_string(p, end, object_name ? " >" : " <");
126 50 : p = add_string(p, end, object_name ? output_fn() : input_fn());
127 : }
128 120 : *p = 0;
129 :
130 120 : printf("Executing: %s\n", cmd);
131 120 : if (system(cmd) != 0) {
132 0 : if (object_name) {
133 0 : printf("Output is:\n");
134 0 : cat(output_fn(), stdout);
135 : }
136 0 : fprintf(stderr, "Failed command\n");
137 0 : exit(EXIT_FAILURE);
138 : }
139 :
140 120 : TDS_ZERO_FREE(output);
141 120 : if (object_name)
142 80 : output = read_file(output_fn());
143 120 : }
144 :
145 : static void
146 40 : defncopy_in(char const *sql)
147 : {
148 : FILE *f;
149 :
150 40 : f = fopen(input_fn(), "w");
151 40 : assert(f);
152 40 : fputs(sql, f);
153 40 : fclose(f);
154 40 : defncopy(NULL);
155 40 : }
156 :
157 : /* table with a column name that is also a keyword, should be quoted */
158 : static void
159 20 : test_keyword(void)
160 : {
161 : const char *sql;
162 : static const char clean[] =
163 : "IF OBJECT_ID('dbo.table_with_column_named_key') IS NOT NULL DROP TABLE dbo.table_with_column_named_key\n";
164 :
165 20 : tsql(clean);
166 : /* *INDENT-OFF* */
167 20 : tsql(
168 : "IF OBJECT_ID('dbo.table_with_column_named_key') IS NOT NULL DROP TABLE dbo.table_with_column_named_key\n"
169 : "GO\n"
170 : "CREATE TABLE dbo.table_with_column_named_key\n"
171 : "(\n"
172 : " [key] nvarchar(4000) NOT NULL\n"
173 : ")\n");
174 : /* *INDENT-ON* */
175 20 : defncopy("dbo.table_with_column_named_key");
176 20 : cat(output_fn(), stdout);
177 : #define EXPECT_KEYWORD_OUT "CREATE TABLE [dbo].[table_with_column_named_key]\n ( [key] nvarchar(4000) NOT NULL\n )\n"
178 20 : sql = sybase_syntax ? EXPECT_KEYWORD_OUT TERMINATOR_SYBASE "\n" : EXPECT_KEYWORD_OUT TERMINATOR "\n";
179 :
180 20 : required_output(sql);
181 20 : tsql(clean);
182 :
183 : /* test defncopy input */
184 20 : defncopy_in(sql);
185 20 : unlink(output_fn());
186 20 : defncopy("dbo.table_with_column_named_key");
187 20 : required_output(sql);
188 20 : tsql(clean);
189 :
190 : /* test non-stream syntax */
191 20 : stream_io = false;
192 20 : defncopy_in(sql);
193 20 : unlink(output_fn());
194 20 : defncopy("dbo.table_with_column_named_key");
195 20 : required_output(sql);
196 20 : tsql(clean);
197 20 : stream_io = true;
198 :
199 20 : tsql(sql);
200 20 : tsql(clean);
201 20 : }
202 :
203 : /* table with an index with a space inside */
204 : static void
205 10 : test_index_name_with_space(void)
206 : {
207 : const char *sql;
208 : static const char clean[] = "IF OBJECT_ID('dbo.tblReportPeriod') IS NOT NULL DROP TABLE dbo.tblReportPeriod\n";
209 :
210 10 : tsql(clean);
211 : /* *INDENT-OFF* */
212 10 : tsql(
213 : "CREATE TABLE dbo.tblReportPeriod\n"
214 : " ( RecordID int NOT NULL\n"
215 : " , FromDate nvarchar(40) NULL\n"
216 : " , ToDate nvarchar(40) NULL\n"
217 : " )\n"
218 : "CREATE nonclustered INDEX [From Date] on dbo.tblReportPeriod(FromDate)\n");
219 : /* *INDENT-ON* */
220 10 : defncopy("dbo.tblReportPeriod");
221 10 : cat(output_fn(), stdout);
222 : /* *INDENT-OFF* */
223 10 : sql =
224 : "CREATE TABLE [dbo].[tblReportPeriod]\n"
225 : " ( [RecordID] int NOT NULL\n"
226 : " , [FromDate] nvarchar(40) NULL\n"
227 : " , [ToDate] nvarchar(40) NULL\n"
228 : " )\n"
229 : "CREATE nonclustered INDEX [From Date] on [dbo].[tblReportPeriod]([FromDate]) "
230 : TERMINATOR
231 : ;
232 : /* *INDENT-ON* */
233 10 : required_output(sql);
234 10 : tsql(clean);
235 10 : tsql(sql);
236 10 : tsql(clean);
237 10 : }
238 :
239 : /* table with an index with a space inside */
240 : static void
241 10 : test_weird_index_names(void)
242 : {
243 : const char *sql;
244 : static const char clean[] = "IF OBJECT_ID('dbo.tblReportPeriod2') IS NOT NULL DROP TABLE dbo.tblReportPeriod2\n";
245 :
246 10 : tsql(clean);
247 : /* *INDENT-OFF* */
248 10 : tsql(
249 : "CREATE TABLE dbo.tblReportPeriod2\n"
250 : " ( RecordID int NOT NULL\n"
251 : " , [To, ] nvarchar(40) NULL\n"
252 : " , [To] nvarchar(40) NULL\n"
253 : " , [To, , ] nvarchar(40) NULL\n"
254 : " )\n"
255 : "CREATE nonclustered INDEX [From Date] on dbo.tblReportPeriod2([To, ],[To, , ])\n");
256 : /* *INDENT-ON* */
257 10 : defncopy("dbo.tblReportPeriod2");
258 10 : cat(output_fn(), stdout);
259 :
260 : /* *INDENT-OFF* */
261 10 : sql =
262 : "CREATE TABLE [dbo].[tblReportPeriod2]\n"
263 : " ( [RecordID] int NOT NULL\n"
264 : " , [To, ] nvarchar(40) NULL\n"
265 : " , [To] nvarchar(40) NULL\n"
266 : " , [To, , ] nvarchar(40) NULL\n"
267 : " )\n"
268 : "CREATE nonclustered INDEX [From Date] on [dbo].[tblReportPeriod2]([To, ],[To, ,])\n"
269 : TERMINATOR
270 : ;
271 : /* *INDENT-ON* */
272 10 : required_output(sql);
273 10 : tsql(clean);
274 10 : tsql(sql);
275 10 : tsql(clean);
276 10 : }
277 :
278 10 : TEST_MAIN()
279 : {
280 : FILE *f;
281 :
282 10 : cleanup();
283 10 : update_path();
284 :
285 10 : if (!read_login_info())
286 : return 1;
287 :
288 10 : f = fopen("empty", "w");
289 10 : if (f)
290 10 : fclose(f);
291 :
292 10 : sybase_syntax = true;
293 10 : test_keyword();
294 10 : sybase_syntax = false;
295 10 : test_keyword();
296 10 : test_index_name_with_space();
297 10 : test_weird_index_names();
298 :
299 10 : cleanup();
300 10 : return 0;
301 : }
|