Line data Source code
1 : /* FreeTDS - Library of routines accessing Sybase and Microsoft databases
2 : * Copyright (C) 2004-2011 James K. Lowden
3 : *
4 : * This program is free software; you can redistribute it and/or
5 : * modify it under the terms of the GNU 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 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 : #ifdef MicrosoftsDbLib
21 : #ifdef _WIN32
22 : # pragma warning( disable : 4142 )
23 : # include "win32.microsoft/have.h"
24 : # include "../../include/replacements.win32.hacked.h"
25 : int getopt(int argc, const char *argv[], char *optstring);
26 :
27 : # ifndef DBNTWIN32
28 : # define DBNTWIN32
29 :
30 : /*
31 : * As of Visual Studio .NET 2003, define WIN32_LEAN_AND_MEAN to avoid redefining LPCBYTE in sqlfront.h
32 : * Unless it was already defined, undefine it after windows.h has been included.
33 : * (windows.h includes a bunch of stuff needed by sqlfront.h. Bleh.)
34 : */
35 : # ifndef WIN32_LEAN_AND_MEAN
36 : # define WIN32_LEAN_AND_MEAN
37 : # define WIN32_LEAN_AND_MEAN_DEFINED_HERE
38 : # endif
39 : # include <freetds/windows.h>
40 : # ifdef WIN32_LEAN_AND_MEAN_DEFINED_HERE
41 : # undef WIN32_LEAN_AND_MEAN_DEFINED_HERE
42 : # undef WIN32_LEAN_AND_MEAN
43 : # endif
44 : # include <process.h>
45 : # include <sqlfront.h>
46 : # include <sqldb.h>
47 :
48 : #endif /* DBNTWIN32 */
49 : # include "win32.microsoft/syb2ms.h"
50 : #endif
51 : #endif /* MicrosoftsDbLib */
52 :
53 : #include <config.h>
54 :
55 : #include <stdio.h>
56 : #include <assert.h>
57 :
58 : #if HAVE_ERRNO_H
59 : #include <errno.h>
60 : #endif
61 :
62 : #if HAVE_UNISTD_H
63 : #include <unistd.h>
64 : #endif
65 :
66 : #if HAVE_STDLIB_H
67 : #include <stdlib.h>
68 : #endif
69 :
70 : #if HAVE_STRING_H
71 : #include <string.h>
72 : #endif
73 :
74 : #if HAVE_LIBGEN_H
75 : #include <libgen.h>
76 : #endif
77 :
78 : #if HAVE_LOCALE_H
79 : #include <locale.h>
80 : #endif
81 :
82 : #include <sybfront.h>
83 : #include <sybdb.h>
84 : #ifndef MicrosoftsDbLib
85 : #include <freetds/replacements.h>
86 : #else
87 :
88 : #ifndef _WIN32
89 : # include <freetds/replacements.h>
90 : #endif
91 : #endif /* MicrosoftsDbLib */
92 :
93 : #include <freetds/sysdep_private.h>
94 : #include <freetds/utils.h>
95 : #include <freetds/bool.h>
96 : #include <freetds/macros.h>
97 :
98 : #ifndef MicrosoftsDbLib
99 : static int err_handler(DBPROCESS * dbproc, int severity, int dberr, int oserr, char *dberrstr, char *oserrstr);
100 : static int msg_handler(DBPROCESS * dbproc, DBINT msgno, int msgstate, int severity, char *msgtext,
101 : char *srvname, char *procname, int line);
102 : #else
103 : static int err_handler(DBPROCESS * dbproc, int severity, int dberr, int oserr, const char dberrstr[], const char oserrstr[]);
104 : static int msg_handler(DBPROCESS * dbproc, DBINT msgno, int msgstate, int severity, const char msgtext[],
105 : const char srvname[], const char procname[], unsigned short int line);
106 : #endif /* MicrosoftsDbLib */
107 :
108 : typedef struct _options
109 : {
110 : int optind;
111 : char *servername,
112 : *database,
113 : *appname,
114 : hostname[128],
115 : *input_filename,
116 : *output_filename;
117 : } OPTIONS;
118 :
119 : typedef struct _procedure
120 : {
121 : char name[512], owner[512];
122 : } PROCEDURE;
123 :
124 : typedef struct DDL {
125 : char *name;
126 : char *type;
127 : char *length;
128 : char *precision;
129 : char *scale;
130 : char *nullable;
131 : } DDL;
132 :
133 : static int print_ddl(DBPROCESS *dbproc, PROCEDURE *procedure);
134 : static int print_results(DBPROCESS *dbproc);
135 : static LOGINREC* get_login(int argc, char *argv[], OPTIONS *poptions);
136 : static void parse_argument(const char argument[], PROCEDURE* procedure);
137 : static void usage(const char invoked_as[]);
138 : static char *rtrim(char *s);
139 : static char *ltrim(char *s);
140 :
141 : typedef struct tmp_buf {
142 : struct tmp_buf *next;
143 : char buf[1];
144 : } tmp_buf;
145 :
146 : static tmp_buf *tmp_list = NULL;
147 :
148 : static void*
149 190 : tmp_malloc(size_t len)
150 : {
151 190 : tmp_buf *tmp = malloc(sizeof(tmp_buf*) + len);
152 190 : if (!tmp) {
153 0 : fprintf(stderr, "Out of memory\n");
154 0 : exit(1);
155 : }
156 190 : tmp->next = tmp_list;
157 190 : tmp_list = tmp;
158 190 : return tmp->buf;
159 : }
160 :
161 : static void
162 : tmp_free(void)
163 : {
164 372 : while (tmp_list) {
165 190 : tmp_buf *next = tmp_list->next;
166 190 : free(tmp_list);
167 190 : tmp_list = next;
168 : }
169 : }
170 :
171 : static size_t
172 : count_chars(const char *s, char c)
173 : {
174 302 : size_t num = 0;
175 : if (c != 0) {
176 302 : --s;
177 350 : while ((s = strchr(s + 1, c)) != NULL)
178 32 : ++num;
179 : }
180 : return num;
181 : }
182 :
183 : static char *
184 : sql_quote(char *dest, const char *src, char quote_char)
185 : {
186 1652 : for (; *src; ++src) {
187 1652 : if (*src == quote_char)
188 0 : *dest++ = *src;
189 1652 : *dest++ = *src;
190 : }
191 : return dest;
192 : }
193 :
194 : static const char *
195 190 : quote_id(const char *id)
196 : {
197 : size_t n, len;
198 : char *s, *p;
199 :
200 190 : n = count_chars(id, ']');
201 190 : len = 1 + strlen(id) + n + 1 + 1;
202 190 : p = s = tmp_malloc(len);
203 190 : *p++ = '[';
204 190 : p = sql_quote(p, id, ']');
205 190 : *p++ = ']';
206 190 : *p = 0;
207 190 : return s;
208 : }
209 :
210 : static const char *
211 96 : quote_str(const char *str)
212 : {
213 : size_t n, len;
214 : char *s, *p;
215 :
216 96 : n = count_chars(str, '\'');
217 96 : if (!n)
218 : return str;
219 0 : len = strlen(str) + n + 1;
220 0 : s = tmp_malloc(len);
221 0 : p = sql_quote(s, str, '\'');
222 0 : *p = 0;
223 0 : return s;
224 : }
225 :
226 : /* global variables */
227 : static OPTIONS options;
228 : static char use_statement[512];
229 : /* end global variables */
230 :
231 :
232 : /**
233 : * The purpose of this program is to load or extract the text of a stored procedure.
234 : * This first cut does extract only.
235 : * TODO: support loading procedures onto the server.
236 : */
237 : int
238 24 : main(int argc, char *argv[])
239 : {
240 : LOGINREC *login;
241 : DBPROCESS *dbproc;
242 : PROCEDURE procedure;
243 : RETCODE erc;
244 : int i, nrows;
245 :
246 24 : setlocale(LC_ALL, "");
247 :
248 : #ifdef __VMS
249 : /* Convert VMS-style arguments to Unix-style */
250 : parse_vms_args(&argc, &argv);
251 : #endif
252 :
253 : /* Initialize db-lib */
254 : #if _WIN32 && defined(MicrosoftsDbLib)
255 : LPCSTR msg = dbinit();
256 : if (msg == NULL) {
257 : #else
258 24 : erc = dbinit();
259 24 : if (erc == FAIL) {
260 : #endif /* MicrosoftsDbLib */
261 0 : fprintf(stderr, "%s:%d: dbinit() failed\n", options.appname, __LINE__);
262 0 : exit(1);
263 : }
264 :
265 :
266 24 : memset(&options, 0, sizeof(options));
267 24 : login = get_login(argc, argv, &options); /* get command-line parameters and call dblogin() */
268 24 : assert(login != NULL);
269 :
270 : /* Install our error and message handlers */
271 24 : dberrhandle(err_handler);
272 24 : dbmsghandle(msg_handler);
273 :
274 : /*
275 : * Override stdin, stdout, and stderr, as required
276 : */
277 24 : if (options.input_filename) {
278 0 : if (freopen(options.input_filename, "rb", stdin) == NULL) {
279 0 : fprintf(stderr, "%s: unable to open %s: %s\n", options.appname, options.input_filename, strerror(errno));
280 0 : exit(1);
281 : }
282 : }
283 :
284 24 : if (options.output_filename) {
285 0 : if (freopen(options.output_filename, "wb", stdout) == NULL) {
286 0 : fprintf(stderr, "%s: unable to open %s: %s\n", options.appname, options.output_filename, strerror(errno));
287 0 : exit(1);
288 : }
289 : }
290 :
291 : /* Select the specified database, if any */
292 24 : if (options.database)
293 24 : DBSETLDBNAME(login, options.database);
294 :
295 : /*
296 : * Connect to the server
297 : */
298 24 : dbproc = dbopen(login, options.servername);
299 24 : if (!dbproc) {
300 0 : fprintf(stderr, "There was a problem connecting to the server.\n");
301 0 : exit(1);
302 : }
303 :
304 : /*
305 : * Read the procedure names and move their texts.
306 : */
307 48 : for (i=options.optind; i < argc; i++) {
308 : #ifndef MicrosoftsDbLib
309 : static const char query[] = " select c.text"
310 : #else
311 : static const char query[] = " select cast(c.text as text)"
312 : #endif /* MicrosoftsDbLib */
313 : ", number "
314 : " from syscomments c,"
315 : " sysobjects o"
316 : " where o.id = c.id"
317 : " and o.name = '%s'"
318 : " and o.uid = user_id('%s')"
319 : " and o.type not in ('U', 'S')" /* no user or system tables */
320 : " order by c.number, %sc.colid"
321 : ;
322 : static const char query_table[] = " execute sp_help '%s.%s' ";
323 :
324 24 : parse_argument(argv[i], &procedure);
325 :
326 24 : erc = dbfcmd(dbproc, query, quote_str(procedure.name), quote_str(procedure.owner),
327 24 : (DBTDS(dbproc) == DBTDS_5_0) ? "c.colid2, ":"");
328 24 : tmp_free();
329 :
330 : /* Send the query to the server (we could use dbsqlexec(), instead) */
331 24 : erc = dbsqlsend(dbproc);
332 24 : if (erc == FAIL) {
333 0 : fprintf(stderr, "%s:%d: dbsqlsend() failed\n", options.appname, __LINE__);
334 0 : exit(1);
335 : }
336 :
337 : /* Wait for it to execute */
338 24 : erc = dbsqlok(dbproc);
339 24 : if (erc == FAIL) {
340 0 : fprintf(stderr, "%s:%d: dbsqlok() failed\n", options.appname, __LINE__);
341 0 : exit(1);
342 : }
343 :
344 : /* Write the output */
345 24 : nrows = print_results(dbproc);
346 :
347 24 : if (0 == nrows) {
348 24 : erc = dbfcmd(dbproc, query_table, quote_str(procedure.owner), quote_str(procedure.name));
349 24 : tmp_free();
350 24 : assert(SUCCEED == erc);
351 24 : erc = dbsqlexec(dbproc);
352 24 : if (erc == FAIL) {
353 0 : fprintf(stderr, "%s:%d: dbsqlexec() failed\n", options.appname, __LINE__);
354 0 : exit(1);
355 : }
356 24 : nrows = print_ddl(dbproc, &procedure);
357 : }
358 :
359 24 : switch (nrows) {
360 : case -1:
361 : return 1;
362 0 : case 0:
363 0 : fprintf(stderr, "%s: error: %s.%s.%s.%s not found\n", options.appname,
364 : options.servername, options.database, procedure.owner, procedure.name);
365 0 : return 2;
366 : default:
367 : break;
368 : }
369 : }
370 :
371 : return 0;
372 : }
373 :
374 : static void
375 24 : parse_argument(const char argument[], PROCEDURE* procedure)
376 : {
377 24 : const char *s = strchr(argument, '.');
378 :
379 24 : if (s) {
380 24 : size_t len = s - argument;
381 24 : if (len > sizeof(procedure->owner) - 1)
382 0 : len = sizeof(procedure->owner) - 1;
383 24 : memcpy(procedure->owner, argument, len);
384 24 : procedure->owner[len] = '\0';
385 :
386 24 : strlcpy(procedure->name, s+1, sizeof(procedure->name));
387 : } else {
388 0 : strcpy(procedure->owner, "dbo");
389 0 : strlcpy(procedure->name, argument, sizeof(procedure->name));
390 : }
391 24 : }
392 :
393 : static char *
394 112 : rtrim(char *s)
395 : {
396 112 : char *p = strchr(s, '\0');
397 :
398 112 : while (--p >= s && *p == ' ')
399 : ;
400 112 : *(p + 1) = '\0';
401 :
402 112 : return s;
403 : }
404 :
405 : static char *
406 112 : ltrim(char *s)
407 : {
408 112 : char *p = s;
409 :
410 224 : while (*p == ' ')
411 0 : ++p;
412 112 : memmove(s, p, strlen(p) + 1);
413 :
414 112 : return s;
415 : }
416 :
417 : static bool
418 3360 : is_in(const char *item, const char *list)
419 : {
420 3360 : const size_t item_len = strlen(item);
421 3534 : for (;;) {
422 6894 : size_t len = strlen(list);
423 6894 : if (len == 0)
424 : return false;
425 4090 : if (len == item_len && strcasecmp(item, list) == 0)
426 : return true;
427 3534 : list += len + 1;
428 : }
429 : }
430 :
431 : static void
432 80 : search_columns(DBPROCESS *dbproc, int *colmap, const char *const *colmap_names, int num_cols)
433 : {
434 : int i;
435 :
436 80 : assert(dbproc && colmap && colmap_names);
437 80 : assert(num_cols > 0);
438 :
439 : /* Find the columns we need */
440 432 : for (i = 0; i < num_cols; ++i)
441 432 : colmap[i] = -1;
442 724 : for (i = 1; i <= dbnumcols(dbproc); ++i) {
443 724 : const char *name = dbcolname(dbproc, i);
444 : int j;
445 :
446 3424 : for (j = 0; j < num_cols; ++j) {
447 3132 : if (is_in(name, colmap_names[j])) {
448 432 : colmap[j] = i;
449 432 : break;
450 : }
451 : }
452 : }
453 432 : for (i = 0; i < num_cols; ++i) {
454 432 : if (colmap[i] == -1) {
455 0 : fprintf(stderr, "Expected column name %s not found\n", colmap_names[i]);
456 0 : exit(1);
457 : }
458 : }
459 80 : }
460 :
461 : static int
462 28 : find_column_name(const char *start, const DDL *columns, int num_columns)
463 : {
464 28 : size_t start_len = strlen(start);
465 28 : size_t found_len = 0;
466 28 : int i, found = -1;
467 :
468 130 : for (i = 0; i < num_columns; ++i) {
469 102 : const char *const name = columns[i].name;
470 102 : const size_t name_len = strlen(name);
471 102 : if (name_len <= start_len && name_len > found_len
472 72 : && (start[name_len] == 0 || strncmp(start+name_len, ", ", 2) == 0)
473 48 : && memcmp(name, start, name_len) == 0) {
474 32 : found_len = name_len;
475 32 : found = i;
476 : }
477 : }
478 28 : return found;
479 : }
480 :
481 : /* This function split and quote index keys.
482 : * Index keys are separate by a command and a space (", ") however the
483 : * separator (very unlikely but possible can contain that separator)
484 : * so we use search for column names taking the longer we can.
485 : */
486 : static char *
487 16 : quote_index_keys(char *index_keys, const DDL *columns, int num_columns)
488 : {
489 : size_t num_commas = count_chars(index_keys, ',');
490 16 : size_t num_quotes = count_chars(index_keys, ']');
491 16 : size_t max_len = strlen(index_keys) + num_quotes + (num_commas + 1) * 2 + 1;
492 16 : char *const new_index_keys = malloc(max_len);
493 : char *dest;
494 16 : bool first = true;
495 16 : assert(new_index_keys);
496 : dest = new_index_keys;
497 40 : while (*index_keys) {
498 24 : int icol = find_column_name(index_keys, columns, num_columns);
499 : /* Sybase put a space at the beginning, handle it */
500 24 : if (icol < 0 && index_keys[0] == ' ') {
501 4 : icol = find_column_name(index_keys + 1, columns, num_columns);
502 4 : if (icol >= 0)
503 4 : ++index_keys;
504 : }
505 24 : if (!first)
506 8 : *dest++ = ',';
507 24 : *dest++ = '[';
508 24 : if (icol >= 0) {
509 : /* found a column matching, use the name */
510 48 : dest = sql_quote(dest, columns[icol].name, ']');
511 24 : index_keys += strlen(columns[icol].name);
512 : } else {
513 : /* not found, fallback looking for terminator */
514 : char save;
515 0 : char *end = strstr(index_keys, ", ");
516 0 : if (!end)
517 0 : end = strchr(index_keys, 0);
518 0 : save = *end;
519 0 : *end = 0;
520 0 : dest = sql_quote(dest, index_keys, ']');
521 0 : *end = save;
522 0 : index_keys = end;
523 : }
524 24 : *dest++ = ']';
525 24 : if (strncmp(index_keys, ", ", 2) == 0)
526 8 : index_keys += 2;
527 : first = false;
528 : }
529 16 : *dest = 0;
530 16 : return new_index_keys;
531 : }
532 :
533 : static char *
534 16 : parse_index_row(DBPROCESS *dbproc, PROCEDURE *procedure, char *create_index, const DDL *columns, int num_columns)
535 : {
536 : static const char *const colmap_names[3] = {
537 : "index_name\0",
538 : "index_description\0",
539 : "index_keys\0",
540 : };
541 : int colmap[TDS_VECTOR_SIZE(colmap_names)];
542 16 : char *index_name, *index_description, *index_keys, *p, fprimary=0;
543 : char *tmp_str;
544 : DBINT datlen;
545 : int ret, i;
546 :
547 16 : assert(dbnumcols(dbproc) >=3 ); /* column had better be in range */
548 :
549 16 : search_columns(dbproc, colmap, colmap_names, TDS_VECTOR_SIZE(colmap));
550 :
551 : /* name */
552 16 : datlen = dbdatlen(dbproc, 1);
553 16 : index_name = (char *) calloc(1, 1 + datlen);
554 16 : assert(index_name);
555 16 : memcpy(index_name, dbdata(dbproc, 1), datlen);
556 :
557 : /* kind */
558 16 : i = colmap[1];
559 16 : datlen = dbdatlen(dbproc, i);
560 16 : index_description = (char *) calloc(1, 1 + datlen);
561 16 : assert(index_description);
562 16 : memcpy(index_description, dbdata(dbproc, i), datlen);
563 :
564 : /* columns */
565 16 : i = colmap[2];
566 16 : datlen = dbdatlen(dbproc, i);
567 16 : index_keys = (char *) calloc(1, 1 + datlen);
568 16 : assert(index_keys);
569 16 : memcpy(index_keys, dbdata(dbproc, i), datlen);
570 :
571 16 : tmp_str = quote_index_keys(index_keys, columns, num_columns);
572 16 : free(index_keys);
573 16 : index_keys = tmp_str;
574 :
575 : /* fix up the index attributes; we're going to use the string verbatim (almost). */
576 16 : p = strstr(index_description, "located");
577 16 : if (p) {
578 12 : *p = '\0'; /* we don't care where it's located */
579 : }
580 : /* Microsoft version: [non]clustered[, unique][, primary key] located on PRIMARY */
581 16 : p = strstr(index_description, "primary key");
582 16 : if (p) {
583 0 : fprimary = 1;
584 0 : *p = '\0'; /* we don't care where it's located */
585 0 : if ((p = strchr(index_description, ',')) != NULL)
586 0 : *p = '\0'; /* we use only the first term (clustered/nonclustered) */
587 : } else {
588 : /* reorder "unique" and "clustered" */
589 16 : char nonclustered[] = "nonclustered", unique[] = "unique";
590 16 : char *pclustering = nonclustered;
591 16 : if (NULL == strstr(index_description, pclustering)) {
592 0 : pclustering += 3;
593 0 : if (NULL == strstr(index_description, pclustering))
594 0 : *pclustering = '\0';
595 : }
596 16 : if (NULL == strstr(index_description, unique))
597 16 : unique[0] = '\0';
598 16 : sprintf(index_description, "%s %s", unique, pclustering);
599 : }
600 : /* Put it to a temporary variable; we'll print it after the CREATE TABLE statement. */
601 16 : tmp_str = create_index;
602 16 : create_index = create_index ? create_index : "";
603 16 : if (fprimary) {
604 0 : ret = asprintf(&create_index,
605 : "%sALTER TABLE %s.%s ADD CONSTRAINT %s PRIMARY KEY %s (%s)\nGO\n\n",
606 : create_index,
607 0 : quote_id(procedure->owner), quote_id(procedure->name),
608 : quote_id(index_name), index_description, index_keys);
609 : } else {
610 32 : ret = asprintf(&create_index,
611 : "%sCREATE %s INDEX %s on %s.%s(%s)\nGO\n\n",
612 : create_index,
613 : index_description, quote_id(index_name),
614 32 : quote_id(procedure->owner), quote_id(procedure->name), index_keys);
615 : }
616 16 : assert(ret >= 0);
617 16 : free(tmp_str);
618 16 : tmp_free();
619 :
620 16 : free(index_name);
621 16 : free(index_description);
622 16 : free(index_keys);
623 :
624 16 : return create_index;
625 : }
626 :
627 : /*
628 : * Get the table information from sp_help, because it's easier to get the index information (eventually).
629 : * The column descriptions are in resultset #2, which is where we start.
630 : * As shown below, sp_help returns different columns for resultset #2, so we build a map.
631 : * Sybase Microsoft
632 : * ----------------- ----------------
633 : * 1. Column_name Column_name
634 : * 2. Type Type
635 : * 3. Computed
636 : * 4. Length Length
637 : * 5. Prec Prec
638 : * 6. Scale Scale
639 : * 7. Nulls Nullable
640 : * 8. Default_name TrimTrail
641 : * 9. Rule_name FixedLenNullIn
642 : * 10. Access_Rule_name Collation
643 : * 11. Identity
644 : */
645 : static int
646 24 : print_ddl(DBPROCESS *dbproc, PROCEDURE *procedure)
647 : {
648 : static const char *const colmap_names[6] = {
649 : "column_name\0",
650 : "type\0",
651 : "length\0",
652 : "prec\0",
653 : "scale\0",
654 : "nulls\0nullable\0",
655 : };
656 :
657 24 : DDL *ddl = NULL;
658 24 : char *create_index = NULL;
659 : RETCODE erc;
660 : int iresultset, i;
661 24 : int maxnamelen = 0, nrows = 0;
662 : char **p_str;
663 24 : bool is_ms = false;
664 :
665 24 : assert(dbproc);
666 24 : assert(procedure);
667 :
668 : /* sp_help returns several result sets. We want just the second one, for now */
669 164 : for (iresultset=1; (erc = dbresults(dbproc)) != NO_MORE_RESULTS; iresultset++) {
670 : int row_code;
671 :
672 164 : if (erc == FAIL) {
673 0 : fprintf(stderr, "%s:%d: dbresults(), result set %d failed\n", options.appname, __LINE__, iresultset);
674 0 : goto cleanup;
675 : }
676 :
677 : /* Get the data */
678 362 : while ((row_code = dbnextrow(dbproc)) != NO_MORE_ROWS) {
679 : DDL *p;
680 : char **coldesc[sizeof(DDL)/sizeof(char*)]; /* an array of pointers to the DDL elements */
681 : int colmap[TDS_VECTOR_SIZE(colmap_names)];
682 :
683 198 : assert(row_code == REG_ROW);
684 :
685 : /* Look for index data */
686 198 : if (0 == strcmp("index_name", dbcolname(dbproc, 1))) {
687 16 : create_index = parse_index_row(dbproc, procedure, create_index, ddl, nrows);
688 150 : continue;
689 : }
690 :
691 : /* skip other resultsets that don't describe the table's columns */
692 182 : if (0 != strcasecmp("Column_name", dbcolname(dbproc, 1)))
693 118 : continue;
694 :
695 : /* Find the columns we need */
696 64 : search_columns(dbproc, colmap, colmap_names, TDS_VECTOR_SIZE(colmap));
697 :
698 : /* check if server is Microsoft */
699 672 : for (i = 1; i <= dbnumcols(dbproc); ++i)
700 656 : if (strcasecmp(dbcolname(dbproc, i), "Collation") == 0) {
701 : is_ms = true;
702 : break;
703 : }
704 :
705 : /* Make room for the next row */
706 64 : p = (DDL *) realloc(ddl, ++nrows * sizeof(DDL));
707 64 : if (p == NULL) {
708 0 : perror("error: insufficient memory for row DDL");
709 0 : assert(p != NULL);
710 : exit(1);
711 : }
712 64 : ddl = p;
713 :
714 : /* take the address of each member, so we can loop through them */
715 64 : coldesc[0] = &ddl[nrows-1].name;
716 64 : coldesc[1] = &ddl[nrows-1].type;
717 64 : coldesc[2] = &ddl[nrows-1].length;
718 64 : coldesc[3] = &ddl[nrows-1].precision;
719 64 : coldesc[4] = &ddl[nrows-1].scale;
720 64 : coldesc[5] = &ddl[nrows-1].nullable;
721 :
722 448 : for( i=0; i < sizeof(DDL)/sizeof(char*); i++) {
723 384 : const int col_index = colmap[i];
724 384 : const DBINT datlen = dbdatlen(dbproc, col_index);
725 384 : const int type = dbcoltype(dbproc, col_index);
726 :
727 384 : assert(datlen >= 0); /* column had better be in range */
728 :
729 384 : if (datlen == 0) {
730 32 : *coldesc[i] = NULL;
731 32 : continue;
732 : }
733 :
734 352 : if (type == SYBCHAR || type == SYBVARCHAR) {
735 272 : *coldesc[i] = (char *) calloc(1, 1 + datlen); /* calloc will null terminate */
736 272 : if (!*coldesc[i]) {
737 0 : perror("error: insufficient memory for row detail");
738 0 : exit(1);
739 : }
740 272 : memcpy(*coldesc[i], dbdata(dbproc, col_index), datlen);
741 : } else {
742 : char buf[256];
743 80 : DBINT len = dbconvert(dbproc, type, dbdata(dbproc, col_index), datlen,
744 : SYBVARCHAR, (BYTE *) buf, -1);
745 80 : if (len < 0) {
746 0 : fprintf(stderr, "Error converting column to char");
747 0 : exit(1);
748 : }
749 80 : *coldesc[i] = strdup(buf);
750 80 : if (!*coldesc[i]) {
751 0 : perror("error: insufficient memory for row detail");
752 0 : exit(1);
753 : }
754 : }
755 :
756 : /*
757 : * maxnamelen will determine how much room we allow for column names
758 : * in the CREATE TABLE statement
759 : */
760 352 : if (i == 0)
761 64 : maxnamelen = (maxnamelen > datlen)? maxnamelen : datlen;
762 : }
763 : } /* wend */
764 : }
765 :
766 : /*
767 : * We've collected the description for the columns in the 'ddl' array.
768 : * Now we'll print the CREATE TABLE statement in jkl's preferred format.
769 : */
770 24 : if (nrows == 0)
771 : goto cleanup;
772 :
773 24 : printf("%sCREATE TABLE %s.%s\n", use_statement, quote_id(procedure->owner), quote_id(procedure->name));
774 : tmp_free();
775 64 : for (i=0; i < nrows; i++) {
776 : static const char varytypenames[] = "char\0"
777 : "nchar\0"
778 : "varchar\0"
779 : "nvarchar\0"
780 : "unichar\0"
781 : "univarchar\0"
782 : "binary\0"
783 : "varbinary\0"
784 : ;
785 64 : char *type = NULL;
786 : bool is_null;
787 : int ret;
788 :
789 : /* get size of decimal, numeric, char, and image types */
790 64 : ret = 0;
791 64 : if (is_in(ddl[i].type, "decimal\0numeric\0")) {
792 0 : if (ddl[i].precision && 0 != strcasecmp("NULL", ddl[i].precision)) {
793 0 : rtrim(ddl[i].precision);
794 0 : rtrim(ddl[i].scale);
795 0 : ret = asprintf(&type, "%s(%s,%s)", ddl[i].type, ddl[i].precision, ddl[i].scale);
796 : }
797 64 : } else if (is_in(ddl[i].type, varytypenames)) {
798 48 : ltrim(rtrim(ddl[i].length));
799 48 : if (strcmp(ddl[i].length, "-1") == 0)
800 0 : ret = asprintf(&type, "%s(max)", ddl[i].type);
801 48 : else if (is_ms && is_in(ddl[i].type, "nchar\0nvarchar\0"))
802 72 : ret = asprintf(&type, "%s(%d)", ddl[i].type, atoi(ddl[i].length)/2);
803 : else
804 12 : ret = asprintf(&type, "%s(%s)", ddl[i].type, ddl[i].length);
805 : }
806 48 : assert(ret >= 0);
807 :
808 64 : ltrim(rtrim(ddl[i].nullable));
809 64 : is_null = is_in(ddl[i].nullable, "1\0yes\0");
810 :
811 : /* {(|,} name type [NOT] NULL */
812 128 : printf("\t%c %-*s %-15s %3s NULL\n", (i==0? '(' : ','), maxnamelen+2, quote_id(ddl[i].name),
813 64 : (type? type : ddl[i].type), (is_null? "" : "NOT"));
814 64 : tmp_free();
815 :
816 64 : free(type);
817 : }
818 24 : printf("\t)\nGO\n\n");
819 :
820 : /* print the CREATE INDEX statements */
821 24 : if (create_index != NULL)
822 16 : fputs(create_index, stdout);
823 :
824 32 : cleanup:
825 24 : p_str = (char **) ddl;
826 408 : for (i=0; i < nrows * (sizeof(DDL)/sizeof(char*)); ++i)
827 384 : free(p_str[i]);
828 24 : free(ddl);
829 24 : free(create_index);
830 24 : return nrows;
831 : }
832 :
833 : static int /* return count of SQL text rows */
834 24 : print_results(DBPROCESS *dbproc)
835 : {
836 : RETCODE erc;
837 : int row_code;
838 : int iresultset;
839 24 : int nrows=0;
840 24 : int prior_procedure_number=1;
841 :
842 : /* bound variables */
843 : enum column_id { ctext=1, number=2 };
844 : char sql_text[16002];
845 : int sql_text_status;
846 : int procedure_number; /* for create proc abc;2 */
847 : int procedure_number_status;
848 :
849 : /*
850 : * Set up each result set with dbresults()
851 : */
852 24 : for (iresultset=1; (erc = dbresults(dbproc)) != NO_MORE_RESULTS; iresultset++) {
853 24 : if (erc == FAIL) {
854 0 : fprintf(stderr, "%s:%d: dbresults(), result set %d failed\n", options.appname, __LINE__, iresultset);
855 0 : return -1;
856 : }
857 :
858 24 : if (SUCCEED != DBROWS(dbproc)) {
859 : return 0;
860 : }
861 :
862 : /*
863 : * Bind the columns to our variables.
864 : */
865 0 : if (sizeof(sql_text) <= dbcollen(dbproc, ctext) ) {
866 0 : assert(sizeof(sql_text) > dbcollen(dbproc, ctext));
867 : return 0;
868 : }
869 0 : erc = dbbind(dbproc, ctext, STRINGBIND, 0, (BYTE *) sql_text);
870 0 : if (erc == FAIL) {
871 0 : fprintf(stderr, "%s:%d: dbbind(), column %d failed\n", options.appname, __LINE__, ctext);
872 0 : return -1;
873 : }
874 0 : erc = dbnullbind(dbproc, ctext, &sql_text_status);
875 0 : if (erc == FAIL) {
876 0 : fprintf(stderr, "%s:%d: dbnullbind(), column %d failed\n", options.appname, __LINE__, ctext);
877 0 : return -1;
878 : }
879 :
880 0 : erc = dbbind(dbproc, number, INTBIND, -1, (BYTE *) &procedure_number);
881 0 : if (erc == FAIL) {
882 0 : fprintf(stderr, "%s:%d: dbbind(), column %d failed\n", options.appname, __LINE__, number);
883 0 : return -1;
884 : }
885 0 : erc = dbnullbind(dbproc, number, &procedure_number_status);
886 0 : if (erc == FAIL) {
887 0 : fprintf(stderr, "%s:%d: dbnullbind(), column %d failed\n", options.appname, __LINE__, number);
888 0 : return -1;
889 : }
890 :
891 : /*
892 : * Print the data to stdout.
893 : */
894 0 : printf("%s", use_statement);
895 0 : for (;(row_code = dbnextrow(dbproc)) != NO_MORE_ROWS; nrows++, prior_procedure_number = procedure_number) {
896 0 : switch (row_code) {
897 0 : case REG_ROW:
898 0 : if ( -1 == sql_text_status) {
899 0 : fprintf(stderr, "defncopy: error: unexpected NULL row in SQL text\n");
900 : } else {
901 0 : if (prior_procedure_number != procedure_number)
902 0 : printf("\nGO\n");
903 0 : printf("%s", sql_text);
904 : }
905 : break;
906 0 : case BUF_FULL:
907 : default:
908 0 : fprintf(stderr, "defncopy: error: expected REG_ROW (%d), got %d instead\n", REG_ROW, row_code);
909 0 : assert(row_code == REG_ROW);
910 : break;
911 : } /* row_code */
912 :
913 : } /* wend dbnextrow */
914 0 : printf("\nGO\n");
915 :
916 : } /* wend dbresults */
917 : return nrows;
918 : }
919 :
920 : static void
921 : usage(const char invoked_as[])
922 : {
923 0 : fprintf(stderr, "usage: %s \n"
924 : " [-U username] [-P password]\n"
925 : " [-S servername] [-D database]\n"
926 : " [-i input filename] [-o output filename]\n"
927 : " [owner.]object_name [[owner.]object_name...]\n"
928 : , invoked_as);
929 : /**
930 : defncopy Syntax Error
931 : Usage: defncopy
932 : [-v]
933 : [-X]
934 : -- [-a <display_charset>]
935 : -- [-I <interfaces_file>]
936 : -- [-J [<client_charset>]]
937 : -- [-K <keytab_file>]
938 : [-P <password>]
939 : -- [-R <remote_server_principal>]
940 : [-S [<server_name>]]
941 : [-U <user_name>]
942 : -- [-V <security_options>]
943 : -- [-Z <security_mechanism>]
944 : -- [-z <language>]
945 : { in <file_name> <database_name> |
946 : out <file_name> <database_name> [<owner>.]<object_name>
947 : [[<owner>.]<object_name>...] }
948 : **/
949 : }
950 :
951 : static LOGINREC *
952 24 : get_login(int argc, char *argv[], OPTIONS *options)
953 : {
954 : LOGINREC *login;
955 : char *password;
956 : int ch;
957 24 : int fdomain = TRUE;
958 :
959 : extern char *optarg;
960 : extern int optind;
961 :
962 24 : assert(options && argv);
963 :
964 24 : options->appname = basename(argv[0]);
965 :
966 24 : login = dblogin();
967 :
968 24 : if (!login) {
969 0 : fprintf(stderr, "%s: unable to allocate login structure\n", options->appname);
970 0 : exit(1);
971 : }
972 :
973 24 : DBSETLAPP(login, options->appname);
974 :
975 24 : if (-1 != gethostname(options->hostname, sizeof(options->hostname))) {
976 24 : DBSETLHOST(login, options->hostname);
977 : }
978 :
979 120 : while ((ch = getopt(argc, argv, "U:P:S:d:D:i:o:v")) != -1) {
980 96 : switch (ch) {
981 24 : case 'U':
982 24 : DBSETLUSER(login, optarg);
983 24 : fdomain = FALSE;
984 24 : break;
985 24 : case 'P':
986 24 : password = tds_getpassarg(optarg);
987 24 : DBSETLPWD(login, password);
988 : memset(password, 0, strlen(password));
989 24 : free(password);
990 24 : password = NULL;
991 24 : fdomain = FALSE;
992 24 : break;
993 24 : case 'S':
994 24 : options->servername = strdup(optarg);
995 24 : break;
996 24 : case 'd':
997 : case 'D':
998 24 : options->database = strdup(optarg);
999 24 : break;
1000 0 : case 'i':
1001 0 : options->input_filename = strdup(optarg);
1002 0 : break;
1003 0 : case 'o':
1004 0 : options->output_filename = strdup(optarg);
1005 0 : break;
1006 0 : case 'v':
1007 0 : printf("%s\n\n%s", argv[0],
1008 : "Copyright (C) 2004 James K. Lowden\n"
1009 : "This program is free software; you can redistribute it and/or\n"
1010 : "modify it under the terms of the GNU General Public\n"
1011 : "License as published by the Free Software Foundation\n");
1012 0 : exit(1);
1013 : break;
1014 0 : case '?':
1015 : default:
1016 0 : usage(options->appname);
1017 0 : exit(1);
1018 : }
1019 : }
1020 :
1021 : #if defined(MicrosoftsDbLib) && defined(_WIN32)
1022 : if (fdomain)
1023 : DBSETLSECURE(login);
1024 : #else
1025 24 : if (fdomain)
1026 0 : DBSETLNETWORKAUTH(login, TRUE);
1027 : #endif /* MicrosoftsDbLib */
1028 24 : if (!options->servername) {
1029 0 : usage(options->appname);
1030 0 : exit(1);
1031 : }
1032 :
1033 24 : options->optind = optind;
1034 :
1035 24 : return login;
1036 : }
1037 :
1038 : static int
1039 : #ifndef MicrosoftsDbLib
1040 0 : err_handler(DBPROCESS * dbproc, int severity, int dberr, int oserr, char *dberrstr, char *oserrstr)
1041 : #else
1042 : err_handler(DBPROCESS * dbproc, int severity, int dberr, int oserr, const char dberrstr[], const char oserrstr[])
1043 : #endif /* MicrosoftsDbLib */
1044 : {
1045 :
1046 0 : if (dberr) {
1047 0 : fprintf(stderr, "%s: Msg %d, Level %d\n", options.appname, dberr, severity);
1048 0 : fprintf(stderr, "%s\n\n", dberrstr);
1049 : }
1050 :
1051 : else {
1052 0 : fprintf(stderr, "%s: DB-LIBRARY error:\n\t", options.appname);
1053 0 : fprintf(stderr, "%s\n", dberrstr);
1054 : }
1055 :
1056 0 : return INT_CANCEL;
1057 : }
1058 :
1059 : static int
1060 : #ifndef MicrosoftsDbLib
1061 316 : msg_handler(DBPROCESS * dbproc, DBINT msgno, int msgstate, int severity, char *msgtext, char *srvname, char *procname, int line)
1062 : #else
1063 : msg_handler(DBPROCESS * dbproc, DBINT msgno, int msgstate, int severity, const char msgtext[],
1064 : const char srvname[], const char procname[], unsigned short int line)
1065 : #endif /* MicrosoftsDbLib */
1066 : {
1067 : char *dbname, *endquote;
1068 :
1069 316 : switch (msgno) {
1070 30 : case 5701: /* Print "USE dbname" for "Changed database context to 'dbname'" */
1071 30 : dbname = strchr(msgtext, '\'');
1072 30 : if (!dbname)
1073 : break;
1074 30 : endquote = strchr(++dbname, '\'');
1075 30 : if (!endquote)
1076 : break;
1077 30 : *endquote = '\0';
1078 30 : sprintf(use_statement, "USE %s\nGO\n\n", quote_id(dbname));
1079 : tmp_free();
1080 : return 0;
1081 :
1082 : case 0: /* Ignore print messages */
1083 : case 5703: /* Ignore "Changed language setting to <language>". */
1084 : return 0;
1085 :
1086 : default:
1087 : break;
1088 : }
1089 :
1090 : #if 0
1091 : printf("Msg %ld, Severity %d, State %d\n", (long) msgno, severity, msgstate);
1092 :
1093 : if (strlen(srvname) > 0)
1094 : printf("Server '%s', ", srvname);
1095 : if (strlen(procname) > 0)
1096 : printf("Procedure '%s', ", procname);
1097 : if (line > 0)
1098 : printf("Line %d", line);
1099 : #endif
1100 0 : printf("\t/* %s */\n", msgtext);
1101 :
1102 60 : return 0;
1103 : }
|