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 *) tds_strndup(dbdata(dbproc, 1), datlen);
554 16 : assert(index_name);
555 :
556 : /* kind */
557 16 : i = colmap[1];
558 16 : datlen = dbdatlen(dbproc, i);
559 16 : index_description = (char *) tds_strndup(dbdata(dbproc, i), datlen);
560 16 : assert(index_description);
561 :
562 : /* columns */
563 16 : i = colmap[2];
564 16 : datlen = dbdatlen(dbproc, i);
565 16 : index_keys = (char *) tds_strndup(dbdata(dbproc, i), datlen);
566 16 : assert(index_keys);
567 :
568 16 : tmp_str = quote_index_keys(index_keys, columns, num_columns);
569 16 : free(index_keys);
570 16 : index_keys = tmp_str;
571 :
572 : /* fix up the index attributes; we're going to use the string verbatim (almost). */
573 16 : p = strstr(index_description, "located");
574 16 : if (p) {
575 12 : *p = '\0'; /* we don't care where it's located */
576 : }
577 : /* Microsoft version: [non]clustered[, unique][, primary key] located on PRIMARY */
578 16 : p = strstr(index_description, "primary key");
579 16 : if (p) {
580 0 : fprimary = 1;
581 0 : *p = '\0'; /* we don't care where it's located */
582 0 : if ((p = strchr(index_description, ',')) != NULL)
583 0 : *p = '\0'; /* we use only the first term (clustered/nonclustered) */
584 : } else {
585 : /* reorder "unique" and "clustered" */
586 16 : char nonclustered[] = "nonclustered", unique[] = "unique";
587 16 : char *pclustering = nonclustered;
588 16 : if (NULL == strstr(index_description, pclustering)) {
589 0 : pclustering += 3;
590 0 : if (NULL == strstr(index_description, pclustering))
591 0 : *pclustering = '\0';
592 : }
593 16 : if (NULL == strstr(index_description, unique))
594 16 : unique[0] = '\0';
595 16 : sprintf(index_description, "%s %s", unique, pclustering);
596 : }
597 : /* Put it to a temporary variable; we'll print it after the CREATE TABLE statement. */
598 16 : tmp_str = create_index;
599 16 : create_index = create_index ? create_index : "";
600 16 : if (fprimary) {
601 0 : ret = asprintf(&create_index,
602 : "%sALTER TABLE %s.%s ADD CONSTRAINT %s PRIMARY KEY %s (%s)\nGO\n\n",
603 : create_index,
604 0 : quote_id(procedure->owner), quote_id(procedure->name),
605 : quote_id(index_name), index_description, index_keys);
606 : } else {
607 32 : ret = asprintf(&create_index,
608 : "%sCREATE %s INDEX %s on %s.%s(%s)\nGO\n\n",
609 : create_index,
610 : index_description, quote_id(index_name),
611 32 : quote_id(procedure->owner), quote_id(procedure->name), index_keys);
612 : }
613 16 : assert(ret >= 0);
614 16 : free(tmp_str);
615 16 : tmp_free();
616 :
617 16 : free(index_name);
618 16 : free(index_description);
619 16 : free(index_keys);
620 :
621 16 : return create_index;
622 : }
623 :
624 : /*
625 : * Get the table information from sp_help, because it's easier to get the index information (eventually).
626 : * The column descriptions are in resultset #2, which is where we start.
627 : * As shown below, sp_help returns different columns for resultset #2, so we build a map.
628 : * Sybase Microsoft
629 : * ----------------- ----------------
630 : * 1. Column_name Column_name
631 : * 2. Type Type
632 : * 3. Computed
633 : * 4. Length Length
634 : * 5. Prec Prec
635 : * 6. Scale Scale
636 : * 7. Nulls Nullable
637 : * 8. Default_name TrimTrail
638 : * 9. Rule_name FixedLenNullIn
639 : * 10. Access_Rule_name Collation
640 : * 11. Identity
641 : */
642 : static int
643 24 : print_ddl(DBPROCESS *dbproc, PROCEDURE *procedure)
644 : {
645 : static const char *const colmap_names[6] = {
646 : "column_name\0",
647 : "type\0",
648 : "length\0",
649 : "prec\0",
650 : "scale\0",
651 : "nulls\0nullable\0",
652 : };
653 :
654 24 : DDL *ddl = NULL;
655 24 : char *create_index = NULL;
656 : RETCODE erc;
657 : int iresultset, i;
658 24 : int maxnamelen = 0, nrows = 0;
659 : char **p_str;
660 24 : bool is_ms = false;
661 :
662 24 : assert(dbproc);
663 24 : assert(procedure);
664 :
665 : /* sp_help returns several result sets. We want just the second one, for now */
666 164 : for (iresultset=1; (erc = dbresults(dbproc)) != NO_MORE_RESULTS; iresultset++) {
667 : int row_code;
668 :
669 164 : if (erc == FAIL) {
670 0 : fprintf(stderr, "%s:%d: dbresults(), result set %d failed\n", options.appname, __LINE__, iresultset);
671 0 : goto cleanup;
672 : }
673 :
674 : /* Get the data */
675 362 : while ((row_code = dbnextrow(dbproc)) != NO_MORE_ROWS) {
676 : DDL *p;
677 : char **coldesc[sizeof(DDL)/sizeof(char*)]; /* an array of pointers to the DDL elements */
678 : int colmap[TDS_VECTOR_SIZE(colmap_names)];
679 :
680 198 : assert(row_code == REG_ROW);
681 :
682 : /* Look for index data */
683 198 : if (0 == strcmp("index_name", dbcolname(dbproc, 1))) {
684 16 : create_index = parse_index_row(dbproc, procedure, create_index, ddl, nrows);
685 150 : continue;
686 : }
687 :
688 : /* skip other resultsets that don't describe the table's columns */
689 182 : if (0 != strcasecmp("Column_name", dbcolname(dbproc, 1)))
690 118 : continue;
691 :
692 : /* Find the columns we need */
693 64 : search_columns(dbproc, colmap, colmap_names, TDS_VECTOR_SIZE(colmap));
694 :
695 : /* check if server is Microsoft */
696 672 : for (i = 1; i <= dbnumcols(dbproc); ++i)
697 656 : if (strcasecmp(dbcolname(dbproc, i), "Collation") == 0) {
698 : is_ms = true;
699 : break;
700 : }
701 :
702 : /* Make room for the next row */
703 64 : p = (DDL *) realloc(ddl, ++nrows * sizeof(DDL));
704 64 : if (p == NULL) {
705 0 : perror("error: insufficient memory for row DDL");
706 0 : assert(p != NULL);
707 : exit(1);
708 : }
709 64 : ddl = p;
710 :
711 : /* take the address of each member, so we can loop through them */
712 64 : coldesc[0] = &ddl[nrows-1].name;
713 64 : coldesc[1] = &ddl[nrows-1].type;
714 64 : coldesc[2] = &ddl[nrows-1].length;
715 64 : coldesc[3] = &ddl[nrows-1].precision;
716 64 : coldesc[4] = &ddl[nrows-1].scale;
717 64 : coldesc[5] = &ddl[nrows-1].nullable;
718 :
719 448 : for( i=0; i < sizeof(DDL)/sizeof(char*); i++) {
720 384 : const int col_index = colmap[i];
721 384 : const DBINT datlen = dbdatlen(dbproc, col_index);
722 384 : const int type = dbcoltype(dbproc, col_index);
723 :
724 384 : assert(datlen >= 0); /* column had better be in range */
725 :
726 384 : if (datlen == 0) {
727 32 : *coldesc[i] = NULL;
728 32 : continue;
729 : }
730 :
731 352 : if (type == SYBCHAR || type == SYBVARCHAR) {
732 272 : *coldesc[i] = (char *) tds_strndup(dbdata(dbproc, col_index), datlen);
733 272 : if (!*coldesc[i]) {
734 0 : perror("error: insufficient memory for row detail");
735 0 : exit(1);
736 : }
737 : } else {
738 : char buf[256];
739 80 : DBINT len = dbconvert(dbproc, type, dbdata(dbproc, col_index), datlen,
740 : SYBVARCHAR, (BYTE *) buf, -1);
741 80 : if (len < 0) {
742 0 : fprintf(stderr, "Error converting column to char");
743 0 : exit(1);
744 : }
745 80 : *coldesc[i] = strdup(buf);
746 80 : if (!*coldesc[i]) {
747 0 : perror("error: insufficient memory for row detail");
748 0 : exit(1);
749 : }
750 : }
751 :
752 : /*
753 : * maxnamelen will determine how much room we allow for column names
754 : * in the CREATE TABLE statement
755 : */
756 352 : if (i == 0)
757 64 : maxnamelen = (maxnamelen > datlen)? maxnamelen : datlen;
758 : }
759 : } /* wend */
760 : }
761 :
762 : /*
763 : * We've collected the description for the columns in the 'ddl' array.
764 : * Now we'll print the CREATE TABLE statement in jkl's preferred format.
765 : */
766 24 : if (nrows == 0)
767 : goto cleanup;
768 :
769 24 : printf("%sCREATE TABLE %s.%s\n", use_statement, quote_id(procedure->owner), quote_id(procedure->name));
770 : tmp_free();
771 64 : for (i=0; i < nrows; i++) {
772 : static const char varytypenames[] = "char\0"
773 : "nchar\0"
774 : "varchar\0"
775 : "nvarchar\0"
776 : "unichar\0"
777 : "univarchar\0"
778 : "binary\0"
779 : "varbinary\0"
780 : ;
781 64 : char *type = NULL;
782 : bool is_null;
783 : int ret;
784 :
785 : /* get size of decimal, numeric, char, and image types */
786 64 : ret = 0;
787 64 : if (is_in(ddl[i].type, "decimal\0numeric\0")) {
788 0 : if (ddl[i].precision && 0 != strcasecmp("NULL", ddl[i].precision)) {
789 0 : rtrim(ddl[i].precision);
790 0 : rtrim(ddl[i].scale);
791 0 : ret = asprintf(&type, "%s(%s,%s)", ddl[i].type, ddl[i].precision, ddl[i].scale);
792 : }
793 64 : } else if (is_in(ddl[i].type, varytypenames)) {
794 48 : ltrim(rtrim(ddl[i].length));
795 48 : if (strcmp(ddl[i].length, "-1") == 0)
796 0 : ret = asprintf(&type, "%s(max)", ddl[i].type);
797 48 : else if (is_ms && is_in(ddl[i].type, "nchar\0nvarchar\0"))
798 72 : ret = asprintf(&type, "%s(%d)", ddl[i].type, atoi(ddl[i].length)/2);
799 : else
800 12 : ret = asprintf(&type, "%s(%s)", ddl[i].type, ddl[i].length);
801 : }
802 48 : assert(ret >= 0);
803 :
804 64 : ltrim(rtrim(ddl[i].nullable));
805 64 : is_null = is_in(ddl[i].nullable, "1\0yes\0");
806 :
807 : /* {(|,} name type [NOT] NULL */
808 128 : printf("\t%c %-*s %-15s %3s NULL\n", (i==0? '(' : ','), maxnamelen+2, quote_id(ddl[i].name),
809 64 : (type? type : ddl[i].type), (is_null? "" : "NOT"));
810 64 : tmp_free();
811 :
812 64 : free(type);
813 : }
814 24 : printf("\t)\nGO\n\n");
815 :
816 : /* print the CREATE INDEX statements */
817 24 : if (create_index != NULL)
818 16 : fputs(create_index, stdout);
819 :
820 32 : cleanup:
821 24 : p_str = (char **) ddl;
822 408 : for (i=0; i < nrows * (sizeof(DDL)/sizeof(char*)); ++i)
823 384 : free(p_str[i]);
824 24 : free(ddl);
825 24 : free(create_index);
826 24 : return nrows;
827 : }
828 :
829 : static int /* return count of SQL text rows */
830 24 : print_results(DBPROCESS *dbproc)
831 : {
832 : RETCODE erc;
833 : int row_code;
834 : int iresultset;
835 24 : int nrows=0;
836 24 : int prior_procedure_number=1;
837 :
838 : /* bound variables */
839 : enum column_id { ctext=1, number=2 };
840 : char sql_text[16002];
841 : int sql_text_status;
842 : int procedure_number; /* for create proc abc;2 */
843 : int procedure_number_status;
844 :
845 : /*
846 : * Set up each result set with dbresults()
847 : */
848 24 : for (iresultset=1; (erc = dbresults(dbproc)) != NO_MORE_RESULTS; iresultset++) {
849 24 : if (erc == FAIL) {
850 0 : fprintf(stderr, "%s:%d: dbresults(), result set %d failed\n", options.appname, __LINE__, iresultset);
851 0 : return -1;
852 : }
853 :
854 24 : if (SUCCEED != DBROWS(dbproc)) {
855 : return 0;
856 : }
857 :
858 : /*
859 : * Bind the columns to our variables.
860 : */
861 0 : if (sizeof(sql_text) <= dbcollen(dbproc, ctext) ) {
862 0 : assert(sizeof(sql_text) > dbcollen(dbproc, ctext));
863 : return 0;
864 : }
865 0 : erc = dbbind(dbproc, ctext, STRINGBIND, 0, (BYTE *) sql_text);
866 0 : if (erc == FAIL) {
867 0 : fprintf(stderr, "%s:%d: dbbind(), column %d failed\n", options.appname, __LINE__, ctext);
868 0 : return -1;
869 : }
870 0 : erc = dbnullbind(dbproc, ctext, &sql_text_status);
871 0 : if (erc == FAIL) {
872 0 : fprintf(stderr, "%s:%d: dbnullbind(), column %d failed\n", options.appname, __LINE__, ctext);
873 0 : return -1;
874 : }
875 :
876 0 : erc = dbbind(dbproc, number, INTBIND, -1, (BYTE *) &procedure_number);
877 0 : if (erc == FAIL) {
878 0 : fprintf(stderr, "%s:%d: dbbind(), column %d failed\n", options.appname, __LINE__, number);
879 0 : return -1;
880 : }
881 0 : erc = dbnullbind(dbproc, number, &procedure_number_status);
882 0 : if (erc == FAIL) {
883 0 : fprintf(stderr, "%s:%d: dbnullbind(), column %d failed\n", options.appname, __LINE__, number);
884 0 : return -1;
885 : }
886 :
887 : /*
888 : * Print the data to stdout.
889 : */
890 0 : printf("%s", use_statement);
891 0 : for (;(row_code = dbnextrow(dbproc)) != NO_MORE_ROWS; nrows++, prior_procedure_number = procedure_number) {
892 0 : switch (row_code) {
893 0 : case REG_ROW:
894 0 : if ( -1 == sql_text_status) {
895 0 : fprintf(stderr, "defncopy: error: unexpected NULL row in SQL text\n");
896 : } else {
897 0 : if (prior_procedure_number != procedure_number)
898 0 : printf("\nGO\n");
899 0 : printf("%s", sql_text);
900 : }
901 : break;
902 0 : case BUF_FULL:
903 : default:
904 0 : fprintf(stderr, "defncopy: error: expected REG_ROW (%d), got %d instead\n", REG_ROW, row_code);
905 0 : assert(row_code == REG_ROW);
906 : break;
907 : } /* row_code */
908 :
909 : } /* wend dbnextrow */
910 0 : printf("\nGO\n");
911 :
912 : } /* wend dbresults */
913 : return nrows;
914 : }
915 :
916 : static void
917 : usage(const char invoked_as[])
918 : {
919 0 : fprintf(stderr, "usage: %s \n"
920 : " [-U username] [-P password]\n"
921 : " [-S servername] [-D database]\n"
922 : " [-i input filename] [-o output filename]\n"
923 : " [owner.]object_name [[owner.]object_name...]\n"
924 : , invoked_as);
925 : /**
926 : defncopy Syntax Error
927 : Usage: defncopy
928 : [-v]
929 : [-X]
930 : -- [-a <display_charset>]
931 : -- [-I <interfaces_file>]
932 : -- [-J [<client_charset>]]
933 : -- [-K <keytab_file>]
934 : [-P <password>]
935 : -- [-R <remote_server_principal>]
936 : [-S [<server_name>]]
937 : [-U <user_name>]
938 : -- [-V <security_options>]
939 : -- [-Z <security_mechanism>]
940 : -- [-z <language>]
941 : { in <file_name> <database_name> |
942 : out <file_name> <database_name> [<owner>.]<object_name>
943 : [[<owner>.]<object_name>...] }
944 : **/
945 : }
946 :
947 : static LOGINREC *
948 24 : get_login(int argc, char *argv[], OPTIONS *options)
949 : {
950 : LOGINREC *login;
951 : char *password;
952 : int ch;
953 24 : int fdomain = TRUE;
954 :
955 : extern char *optarg;
956 : extern int optind;
957 :
958 24 : assert(options && argv);
959 :
960 24 : options->appname = basename(argv[0]);
961 :
962 24 : login = dblogin();
963 :
964 24 : if (!login) {
965 0 : fprintf(stderr, "%s: unable to allocate login structure\n", options->appname);
966 0 : exit(1);
967 : }
968 :
969 24 : DBSETLAPP(login, options->appname);
970 :
971 24 : if (-1 != gethostname(options->hostname, sizeof(options->hostname))) {
972 24 : DBSETLHOST(login, options->hostname);
973 : }
974 :
975 120 : while ((ch = getopt(argc, argv, "U:P:S:d:D:i:o:v")) != -1) {
976 96 : switch (ch) {
977 24 : case 'U':
978 24 : DBSETLUSER(login, optarg);
979 24 : fdomain = FALSE;
980 24 : break;
981 24 : case 'P':
982 24 : password = tds_getpassarg(optarg);
983 24 : if (!password) {
984 0 : fprintf(stderr, "Error getting password\n");
985 0 : exit(1);
986 : }
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-2011 James K. Lowden\n\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; either\n"
1012 : "version 2 of the License, or (at your option) any later version.\n\n"
1013 : "This library is distributed in the hope that it will be useful,\n"
1014 : "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
1015 : "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n"
1016 : "Library General Public License for more details.\n\n"
1017 : "You should have received a copy of the GNU General Public\n"
1018 : "License along with this library; if not, write to the\n"
1019 : "Free Software Foundation, Inc., 59 Temple Place - Suite 330,\n"
1020 : "Boston, MA 02111-1307, USA.\n");
1021 0 : exit(1);
1022 : break;
1023 0 : case '?':
1024 : default:
1025 0 : usage(options->appname);
1026 0 : exit(1);
1027 : }
1028 : }
1029 :
1030 : #if defined(MicrosoftsDbLib) && defined(_WIN32)
1031 : if (fdomain)
1032 : DBSETLSECURE(login);
1033 : #else
1034 24 : if (fdomain)
1035 0 : DBSETLNETWORKAUTH(login, TRUE);
1036 : #endif /* MicrosoftsDbLib */
1037 24 : if (!options->servername) {
1038 0 : usage(options->appname);
1039 0 : exit(1);
1040 : }
1041 :
1042 24 : options->optind = optind;
1043 :
1044 24 : return login;
1045 : }
1046 :
1047 : static int
1048 : #ifndef MicrosoftsDbLib
1049 0 : err_handler(DBPROCESS * dbproc TDS_UNUSED, int severity, int dberr, int oserr TDS_UNUSED,
1050 : char *dberrstr, char *oserrstr TDS_UNUSED)
1051 : #else
1052 : err_handler(DBPROCESS * dbproc TDS_UNUSED, int severity, int dberr, int oserr TDS_UNUSED,
1053 : const char dberrstr[], const char oserrstr[] TDS_UNUSED)
1054 : #endif /* MicrosoftsDbLib */
1055 : {
1056 :
1057 0 : if (dberr) {
1058 0 : fprintf(stderr, "%s: Msg %d, Level %d\n", options.appname, dberr, severity);
1059 0 : fprintf(stderr, "%s\n\n", dberrstr);
1060 : }
1061 :
1062 : else {
1063 0 : fprintf(stderr, "%s: DB-LIBRARY error:\n\t", options.appname);
1064 0 : fprintf(stderr, "%s\n", dberrstr);
1065 : }
1066 :
1067 0 : return INT_CANCEL;
1068 : }
1069 :
1070 : static int
1071 : #ifndef MicrosoftsDbLib
1072 316 : msg_handler(DBPROCESS * dbproc TDS_UNUSED, DBINT msgno, int msgstate TDS_UNUSED, int severity TDS_UNUSED, char *msgtext,
1073 : char *srvname TDS_UNUSED, char *procname TDS_UNUSED, int line TDS_UNUSED)
1074 : #else
1075 : msg_handler(DBPROCESS * dbproc TDS_UNUSED, DBINT msgno, int msgstate TDS_UNUSED, int severity TDS_UNUSED, const char msgtext[],
1076 : const char srvname[] TDS_UNUSED, const char procname[] TDS_UNUSED, unsigned short int line TDS_UNUSED)
1077 : #endif /* MicrosoftsDbLib */
1078 : {
1079 : char *dbname, *endquote;
1080 :
1081 316 : switch (msgno) {
1082 30 : case 5701: /* Print "USE dbname" for "Changed database context to 'dbname'" */
1083 30 : dbname = strchr(msgtext, '\'');
1084 30 : if (!dbname)
1085 : break;
1086 30 : endquote = strchr(++dbname, '\'');
1087 30 : if (!endquote)
1088 : break;
1089 30 : *endquote = '\0';
1090 30 : sprintf(use_statement, "USE %s\nGO\n\n", quote_id(dbname));
1091 : tmp_free();
1092 : return 0;
1093 :
1094 : case 0: /* Ignore print messages */
1095 : case 5703: /* Ignore "Changed language setting to <language>". */
1096 : return 0;
1097 :
1098 : default:
1099 : break;
1100 : }
1101 :
1102 : #if 0
1103 : printf("Msg %ld, Severity %d, State %d\n", (long) msgno, severity, msgstate);
1104 :
1105 : if (strlen(srvname) > 0)
1106 : printf("Server '%s', ", srvname);
1107 : if (strlen(procname) > 0)
1108 : printf("Procedure '%s', ", procname);
1109 : if (line > 0)
1110 : printf("Line %d", line);
1111 : #endif
1112 0 : printf("\t/* %s */\n", msgtext);
1113 :
1114 60 : return 0;
1115 : }
|