LCOV - code coverage report
Current view: top level - src/apps - defncopy.c (source / functions) Hit Total Coverage
Test: FreeTDS coverage Lines: 302 428 70.6 %
Date: 2025-01-18 11:50:39 Functions: 16 17 94.1 %

          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             : }

Generated by: LCOV version 1.13