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-02-21 09:36:06 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         236 : tmp_malloc(size_t len)
     150             : {
     151         236 :         tmp_buf *tmp = malloc(sizeof(tmp_buf*) + len);
     152         236 :         if (!tmp) {
     153           0 :                 fprintf(stderr, "Out of memory\n");
     154           0 :                 exit(1);
     155             :         }
     156         236 :         tmp->next = tmp_list;
     157         236 :         tmp_list = tmp;
     158         236 :         return tmp->buf;
     159             : }
     160             : 
     161             : static void
     162             : tmp_free(void)
     163             : {
     164         462 :         while (tmp_list) {
     165         236 :                 tmp_buf *next = tmp_list->next;
     166         236 :                 free(tmp_list);
     167         236 :                 tmp_list = next;
     168             :         }
     169             : }
     170             : 
     171             : static size_t
     172             : count_chars(const char *s, char c)
     173             : {
     174         376 :         size_t num = 0;
     175             :         if (c != 0) {
     176         376 :                 --s;
     177         436 :                 while ((s = strchr(s + 1, c)) != NULL)
     178          40 :                         ++num;
     179             :         }
     180             :         return num;
     181             : }
     182             : 
     183             : static char *
     184             : sql_quote(char *dest, const char *src, char quote_char)
     185             : {
     186        2058 :         for (; *src; ++src) {
     187        2058 :                 if (*src == quote_char)
     188           0 :                         *dest++ = *src;
     189        2058 :                 *dest++ = *src;
     190             :         }
     191             :         return dest;
     192             : }
     193             : 
     194             : static const char *
     195         236 : quote_id(const char *id)
     196             : {
     197             :         size_t n, len;
     198             :         char *s, *p;
     199             : 
     200         236 :         n = count_chars(id, ']');
     201         236 :         len = 1 + strlen(id) + n + 1 + 1;
     202         236 :         p = s = tmp_malloc(len);
     203         236 :         *p++ = '[';
     204         236 :         p = sql_quote(p, id, ']');
     205         236 :         *p++ = ']';
     206         236 :         *p = 0;
     207         236 :         return s;
     208             : }
     209             : 
     210             : static const char *
     211         120 : quote_str(const char *str)
     212             : {
     213             :         size_t n, len;
     214             :         char *s, *p;
     215             : 
     216         120 :         n = count_chars(str, '\'');
     217         120 :         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          30 : main(int argc, char *argv[])
     239             : {
     240             :         LOGINREC *login;
     241             :         DBPROCESS *dbproc;
     242             :         PROCEDURE procedure;
     243             :         RETCODE erc;
     244             :         int i, nrows;
     245             : 
     246          30 :         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          30 :         erc = dbinit();
     259          30 :         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          30 :         memset(&options, 0, sizeof(options));
     267          30 :         login = get_login(argc, argv, &options); /* get command-line parameters and call dblogin() */
     268          30 :         assert(login != NULL);
     269             : 
     270             :         /* Install our error and message handlers */
     271          30 :         dberrhandle(err_handler);
     272          30 :         dbmsghandle(msg_handler);
     273             : 
     274             :         /*
     275             :          * Override stdin, stdout, and stderr, as required
     276             :          */
     277          30 :         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          30 :         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          30 :         if (options.database)
     293          30 :                 DBSETLDBNAME(login, options.database);
     294             : 
     295             :         /*
     296             :          * Connect to the server
     297             :          */
     298          30 :         dbproc = dbopen(login, options.servername);
     299          30 :         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          60 :         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          30 :                 parse_argument(argv[i], &procedure);
     325             : 
     326          30 :                 erc = dbfcmd(dbproc, query, quote_str(procedure.name), quote_str(procedure.owner),
     327          30 :                              (DBTDS(dbproc) == DBTDS_5_0) ? "c.colid2, ":"");
     328          30 :                 tmp_free();
     329             : 
     330             :                 /* Send the query to the server (we could use dbsqlexec(), instead) */
     331          30 :                 erc = dbsqlsend(dbproc);
     332          30 :                 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          30 :                 erc = dbsqlok(dbproc);
     339          30 :                 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          30 :                 nrows = print_results(dbproc);
     346             : 
     347          30 :                 if (0 == nrows) {
     348          30 :                         erc = dbfcmd(dbproc, query_table, quote_str(procedure.owner), quote_str(procedure.name));
     349          30 :                         tmp_free();
     350          30 :                         assert(SUCCEED == erc);
     351          30 :                         erc = dbsqlexec(dbproc);
     352          30 :                         if (erc == FAIL) {
     353           0 :                                 fprintf(stderr, "%s:%d: dbsqlexec() failed\n", options.appname, __LINE__);
     354           0 :                                 exit(1);
     355             :                         }
     356          30 :                         nrows = print_ddl(dbproc, &procedure);
     357             :                 }
     358             : 
     359          30 :                 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          30 : parse_argument(const char argument[], PROCEDURE* procedure)
     376             : {
     377          30 :         const char *s = strchr(argument, '.');
     378             : 
     379          30 :         if (s) {
     380          30 :                 size_t len = s - argument;
     381          30 :                 if (len > sizeof(procedure->owner) - 1)
     382           0 :                         len = sizeof(procedure->owner) - 1;
     383          30 :                 memcpy(procedure->owner, argument, len);
     384          30 :                 procedure->owner[len] = '\0';
     385             : 
     386          30 :                 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          30 : }
     392             : 
     393             : static char *
     394         140 : rtrim(char *s)
     395             : {
     396         140 :         char *p = strchr(s, '\0');
     397             : 
     398         140 :         while (--p >= s && *p == ' ')
     399             :                 ;
     400         140 :         *(p + 1) = '\0';
     401             : 
     402         140 :         return s;
     403             : }
     404             : 
     405             : static char *
     406         140 : ltrim(char *s)
     407             : {
     408         140 :         char *p = s;
     409             : 
     410         280 :         while (*p == ' ')
     411           0 :                 ++p;
     412         140 :         memmove(s, p, strlen(p) + 1);
     413             : 
     414         140 :         return s;
     415             : }
     416             : 
     417             : static bool
     418        4164 : is_in(const char *item, const char *list)
     419             : {
     420        4164 :         const size_t item_len = strlen(item);
     421        4384 :         for (;;) {
     422        8548 :                 size_t len = strlen(list);
     423        8548 :                 if (len == 0)
     424             :                         return false;
     425        5082 :                 if (len == item_len && strcasecmp(item, list) == 0)
     426             :                         return true;
     427        4384 :                 list += len + 1;
     428             :         }
     429             : }
     430             : 
     431             : static void
     432         100 : search_columns(DBPROCESS *dbproc, int *colmap, const char *const *colmap_names, int num_cols)
     433             : {
     434             :         int i;
     435             : 
     436         100 :         assert(dbproc && colmap && colmap_names);
     437         100 :         assert(num_cols > 0);
     438             : 
     439             :         /* Find the columns we need */
     440         540 :         for (i = 0; i < num_cols; ++i)
     441         540 :                 colmap[i] = -1;
     442         896 :         for (i = 1; i <= dbnumcols(dbproc); ++i) {
     443         896 :                 const char *name = dbcolname(dbproc, i);
     444             :                 int j;
     445             : 
     446        4232 :                 for (j = 0; j < num_cols; ++j) {
     447        3876 :                         if (is_in(name, colmap_names[j])) {
     448         540 :                                 colmap[j] = i;
     449         540 :                                 break;
     450             :                         }
     451             :                 }
     452             :         }
     453         540 :         for (i = 0; i < num_cols; ++i) {
     454         540 :                 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         100 : }
     460             : 
     461             : static int
     462          34 : find_column_name(const char *start, const DDL *columns, int num_columns)
     463             : {
     464          34 :         size_t start_len = strlen(start);
     465          34 :         size_t found_len = 0;
     466          34 :         int i, found = -1;
     467             : 
     468         158 :         for (i = 0; i < num_columns; ++i) {
     469         124 :                 const char *const name = columns[i].name;
     470         124 :                 const size_t name_len = strlen(name);
     471         124 :                 if (name_len <= start_len && name_len > found_len
     472          86 :                     && (start[name_len] == 0 || strncmp(start+name_len, ", ", 2) == 0)
     473          60 :                     && memcmp(name, start, name_len) == 0) {
     474          40 :                         found_len = name_len;
     475          40 :                         found = i;
     476             :                 }
     477             :         }
     478          34 :         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          20 : quote_index_keys(char *index_keys, const DDL *columns, int num_columns)
     488             : {
     489             :         size_t num_commas = count_chars(index_keys, ',');
     490          20 :         size_t num_quotes = count_chars(index_keys, ']');
     491          20 :         size_t max_len = strlen(index_keys) + num_quotes + (num_commas + 1) * 2 + 1;
     492          20 :         char *const new_index_keys = malloc(max_len);
     493             :         char *dest;
     494          20 :         bool first = true;
     495          20 :         assert(new_index_keys);
     496             :         dest = new_index_keys;
     497          50 :         while (*index_keys) {
     498          30 :                 int icol = find_column_name(index_keys, columns, num_columns);
     499             :                 /* Sybase put a space at the beginning, handle it */
     500          30 :                 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          30 :                 if (!first)
     506          10 :                         *dest++ = ',';
     507          30 :                 *dest++ = '[';
     508          30 :                 if (icol >= 0) {
     509             :                         /* found a column matching, use the name */
     510          60 :                         dest = sql_quote(dest, columns[icol].name, ']');
     511          30 :                         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          30 :                 *dest++ = ']';
     525          30 :                 if (strncmp(index_keys, ", ", 2) == 0)
     526          10 :                         index_keys += 2;
     527             :                 first = false;
     528             :         }
     529          20 :         *dest = 0;
     530          20 :         return new_index_keys;
     531             : }
     532             : 
     533             : static char *
     534          20 : 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          20 :         char *index_name, *index_description, *index_keys, *p, fprimary=0;
     543             :         char *tmp_str;
     544             :         DBINT datlen;
     545             :         int ret, i;
     546             : 
     547          20 :         assert(dbnumcols(dbproc) >=3 );      /* column had better be in range */
     548             : 
     549          20 :         search_columns(dbproc, colmap, colmap_names, TDS_VECTOR_SIZE(colmap));
     550             : 
     551             :         /* name */
     552          20 :         datlen = dbdatlen(dbproc, 1);
     553          20 :         index_name = (char *) tds_strndup(dbdata(dbproc, 1), datlen);
     554          20 :         assert(index_name);
     555             : 
     556             :         /* kind */
     557          20 :         i = colmap[1];
     558          20 :         datlen = dbdatlen(dbproc, i);
     559          20 :         index_description = (char *) tds_strndup(dbdata(dbproc, i), datlen);
     560          20 :         assert(index_description);
     561             : 
     562             :         /* columns */
     563          20 :         i = colmap[2];
     564          20 :         datlen = dbdatlen(dbproc, i);
     565          20 :         index_keys = (char *) tds_strndup(dbdata(dbproc, i), datlen);
     566          20 :         assert(index_keys);
     567             : 
     568          20 :         tmp_str = quote_index_keys(index_keys, columns, num_columns);
     569          20 :         free(index_keys);
     570          20 :         index_keys = tmp_str;
     571             : 
     572             :         /* fix up the index attributes; we're going to use the string verbatim (almost). */
     573          20 :         p = strstr(index_description, "located");
     574          20 :         if (p) {
     575          16 :                 *p = '\0'; /* we don't care where it's located */
     576             :         }
     577             :         /* Microsoft version: [non]clustered[, unique][, primary key] located on PRIMARY */
     578          20 :         p = strstr(index_description, "primary key");
     579          20 :         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          20 :                 char nonclustered[] = "nonclustered", unique[] = "unique";
     587          20 :                 char *pclustering = nonclustered;
     588          20 :                 if (NULL == strstr(index_description, pclustering)) {
     589           0 :                         pclustering += 3;
     590           0 :                         if (NULL == strstr(index_description, pclustering))
     591           0 :                                 *pclustering = '\0';
     592             :                 }
     593          20 :                 if (NULL == strstr(index_description, unique))
     594          20 :                         unique[0] = '\0';
     595          20 :                 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          20 :         tmp_str = create_index;
     599          20 :         create_index = create_index ? create_index : "";
     600          20 :         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          40 :                 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          40 :                         quote_id(procedure->owner), quote_id(procedure->name), index_keys);
     612             :         }
     613          20 :         assert(ret >= 0);
     614          20 :         free(tmp_str);
     615          20 :         tmp_free();
     616             : 
     617          20 :         free(index_name);
     618          20 :         free(index_description);
     619          20 :         free(index_keys);
     620             : 
     621          20 :         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          30 : 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          30 :         DDL *ddl = NULL;
     655          30 :         char *create_index = NULL;
     656             :         RETCODE erc;
     657             :         int iresultset, i;
     658          30 :         int maxnamelen = 0, nrows = 0;
     659             :         char **p_str;
     660          30 :         bool is_ms = false;
     661             : 
     662          30 :         assert(dbproc);
     663          30 :         assert(procedure);
     664             : 
     665             :         /* sp_help returns several result sets.  We want just the second one, for now */
     666         198 :         for (iresultset=1; (erc = dbresults(dbproc)) != NO_MORE_RESULTS; iresultset++) {
     667             :                 int row_code;
     668             : 
     669         198 :                 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         440 :                 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         242 :                         assert(row_code == REG_ROW);
     681             : 
     682             :                         /* Look for index data */
     683         242 :                         if (0 == strcmp("index_name", dbcolname(dbproc, 1))) {
     684          20 :                                 create_index = parse_index_row(dbproc, procedure, create_index, ddl, nrows);
     685         182 :                                 continue;
     686             :                         }
     687             : 
     688             :                         /* skip other resultsets that don't describe the table's columns */
     689         222 :                         if (0 != strcasecmp("Column_name", dbcolname(dbproc, 1)))
     690         142 :                                 continue;
     691             : 
     692             :                         /* Find the columns we need */
     693          80 :                         search_columns(dbproc, colmap, colmap_names, TDS_VECTOR_SIZE(colmap));
     694             : 
     695             :                         /* check if server is Microsoft */
     696         832 :                         for (i = 1; i <= dbnumcols(dbproc); ++i)
     697         816 :                                 if (strcasecmp(dbcolname(dbproc, i), "Collation") == 0) {
     698             :                                         is_ms = true;
     699             :                                         break;
     700             :                                 }
     701             : 
     702             :                         /* Make room for the next row */
     703          80 :                         p = (DDL *) realloc(ddl, ++nrows * sizeof(DDL));
     704          80 :                         if (p == NULL) {
     705           0 :                                 perror("error: insufficient memory for row DDL");
     706           0 :                                 assert(p !=  NULL);
     707             :                                 exit(1);
     708             :                         }
     709          80 :                         ddl = p;
     710             : 
     711             :                         /* take the address of each member, so we can loop through them */
     712          80 :                         coldesc[0] = &ddl[nrows-1].name;
     713          80 :                         coldesc[1] = &ddl[nrows-1].type;
     714          80 :                         coldesc[2] = &ddl[nrows-1].length;
     715          80 :                         coldesc[3] = &ddl[nrows-1].precision;
     716          80 :                         coldesc[4] = &ddl[nrows-1].scale;
     717          80 :                         coldesc[5] = &ddl[nrows-1].nullable;
     718             : 
     719         560 :                         for( i=0; i < sizeof(DDL)/sizeof(char*); i++) {
     720         480 :                                 const int col_index = colmap[i];
     721         480 :                                 const DBINT datlen = dbdatlen(dbproc, col_index);
     722         480 :                                 const int type = dbcoltype(dbproc, col_index);
     723             : 
     724         480 :                                 assert(datlen >= 0); /* column had better be in range */
     725             : 
     726         480 :                                 if (datlen == 0) {
     727          32 :                                         *coldesc[i] = NULL;
     728          32 :                                         continue;
     729             :                                 }
     730             : 
     731         448 :                                 if (type == SYBCHAR || type == SYBVARCHAR) {
     732         352 :                                         *coldesc[i] = (char *) tds_strndup(dbdata(dbproc, col_index), datlen);
     733         352 :                                         if (!*coldesc[i]) {
     734           0 :                                                 perror("error: insufficient memory for row detail");
     735           0 :                                                 exit(1);
     736             :                                         }
     737             :                                 } else {
     738             :                                         char buf[256];
     739          96 :                                         DBINT len = dbconvert(dbproc, type, dbdata(dbproc, col_index), datlen,
     740             :                                                 SYBVARCHAR, (BYTE *) buf, -1);
     741          96 :                                         if (len < 0) {
     742           0 :                                                 fprintf(stderr, "Error converting column to char");
     743           0 :                                                 exit(1);
     744             :                                         }
     745          96 :                                         *coldesc[i] = strdup(buf);
     746          96 :                                         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         448 :                                 if (i == 0)
     757          80 :                                         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          30 :         if (nrows == 0)
     767             :                 goto cleanup;
     768             : 
     769          30 :         printf("%sCREATE TABLE %s.%s\n", use_statement, quote_id(procedure->owner), quote_id(procedure->name));
     770             :         tmp_free();
     771          80 :         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          80 :                 char *type = NULL;
     782             :                 bool is_null;
     783             :                 int ret;
     784             : 
     785             :                 /* get size of decimal, numeric, char, and image types */
     786          80 :                 ret = 0;
     787          80 :                 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          80 :                 } else if (is_in(ddl[i].type, varytypenames)) {
     794          60 :                         ltrim(rtrim(ddl[i].length));
     795          60 :                         if (strcmp(ddl[i].length, "-1") == 0)
     796           0 :                                 ret = asprintf(&type, "%s(max)", ddl[i].type);
     797          60 :                         else if (is_ms && is_in(ddl[i].type, "nchar\0nvarchar\0"))
     798          96 :                                 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          60 :                 assert(ret >= 0);
     803             : 
     804          80 :                 ltrim(rtrim(ddl[i].nullable));
     805          80 :                 is_null = is_in(ddl[i].nullable, "1\0yes\0");
     806             : 
     807             :                 /*      {(|,} name type [NOT] NULL */
     808         160 :                 printf("\t%c %-*s %-15s %3s NULL\n", (i==0? '(' : ','), maxnamelen+2, quote_id(ddl[i].name),
     809          80 :                                                 (type? type : ddl[i].type), (is_null? "" : "NOT"));
     810          80 :                 tmp_free();
     811             : 
     812          80 :                 free(type);
     813             :         }
     814          30 :         printf("\t)\nGO\n\n");
     815             : 
     816             :         /* print the CREATE INDEX statements */
     817          30 :         if (create_index != NULL)
     818          20 :                 fputs(create_index, stdout);
     819             : 
     820          40 : cleanup:
     821          30 :         p_str = (char **) ddl;
     822         510 :         for (i=0; i < nrows * (sizeof(DDL)/sizeof(char*)); ++i)
     823         480 :                 free(p_str[i]);
     824          30 :         free(ddl);
     825          30 :         free(create_index);
     826          30 :         return nrows;
     827             : }
     828             : 
     829             : static int /* return count of SQL text rows */
     830          30 : print_results(DBPROCESS *dbproc)
     831             : {
     832             :         RETCODE erc;
     833             :         int row_code;
     834             :         int iresultset;
     835          30 :         int nrows=0;
     836          30 :         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          30 :         for (iresultset=1; (erc = dbresults(dbproc)) != NO_MORE_RESULTS; iresultset++) {
     849          30 :                 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          30 :                 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          30 : get_login(int argc, char *argv[], OPTIONS *options)
     949             : {
     950             :         LOGINREC *login;
     951             :         char *password;
     952             :         int ch;
     953          30 :         int fdomain = TRUE;
     954             : 
     955             :         extern char *optarg;
     956             :         extern int optind;
     957             : 
     958          30 :         assert(options && argv);
     959             : 
     960          30 :         options->appname = basename(argv[0]);
     961             : 
     962          30 :         login = dblogin();
     963             : 
     964          30 :         if (!login) {
     965           0 :                 fprintf(stderr, "%s: unable to allocate login structure\n", options->appname);
     966           0 :                 exit(1);
     967             :         }
     968             : 
     969          30 :         DBSETLAPP(login, options->appname);
     970             : 
     971          30 :         if (-1 != gethostname(options->hostname, sizeof(options->hostname))) {
     972          30 :                 DBSETLHOST(login, options->hostname);
     973             :         }
     974             : 
     975         150 :         while ((ch = getopt(argc, argv, "U:P:S:d:D:i:o:v")) != -1) {
     976         120 :                 switch (ch) {
     977          30 :                 case 'U':
     978          30 :                         DBSETLUSER(login, optarg);
     979          30 :                         fdomain = FALSE;
     980          30 :                         break;
     981          30 :                 case 'P':
     982          30 :                         password = tds_getpassarg(optarg);
     983          30 :                         if (!password) {
     984           0 :                                 fprintf(stderr, "Error getting password\n");
     985           0 :                                 exit(1);
     986             :                         }
     987          30 :                         DBSETLPWD(login, password);
     988             :                         memset(password, 0, strlen(password));
     989          30 :                         free(password);
     990          30 :                         password = NULL;
     991          30 :                         fdomain = FALSE;
     992          30 :                         break;
     993          30 :                 case 'S':
     994          30 :                         options->servername = strdup(optarg);
     995          30 :                         break;
     996          30 :                 case 'd':
     997             :                 case 'D':
     998          30 :                         options->database = strdup(optarg);
     999          30 :                         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          30 :         if (fdomain)
    1035           0 :                 DBSETLNETWORKAUTH(login, TRUE);
    1036             : #endif /* MicrosoftsDbLib */
    1037          30 :         if (!options->servername) {
    1038           0 :                 usage(options->appname);
    1039           0 :                 exit(1);
    1040             :         }
    1041             : 
    1042          30 :         options->optind = optind;
    1043             : 
    1044          30 :         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             :         } else {
    1061           0 :                 fprintf(stderr, "%s: DB-LIBRARY error:\n\t", options.appname);
    1062           0 :                 fprintf(stderr, "%s\n", dberrstr);
    1063             :         }
    1064             : 
    1065           0 :         return INT_CANCEL;
    1066             : }
    1067             : 
    1068             : static int
    1069             : #ifndef MicrosoftsDbLib
    1070         396 : msg_handler(DBPROCESS * dbproc TDS_UNUSED, DBINT msgno, int msgstate TDS_UNUSED, int severity TDS_UNUSED, char *msgtext,
    1071             :             char *srvname TDS_UNUSED, char *procname TDS_UNUSED, int line TDS_UNUSED)
    1072             : #else
    1073             : msg_handler(DBPROCESS * dbproc TDS_UNUSED, DBINT msgno, int msgstate TDS_UNUSED, int severity TDS_UNUSED, const char msgtext[],
    1074             :             const char srvname[] TDS_UNUSED, const char procname[] TDS_UNUSED, unsigned short int line TDS_UNUSED)
    1075             : #endif /* MicrosoftsDbLib */
    1076             : {
    1077             :         char *dbname, *endquote;
    1078             : 
    1079         396 :         switch (msgno) {
    1080          36 :         case 5701: /* Print "USE dbname" for "Changed database context to 'dbname'" */
    1081          36 :                 dbname = strchr(msgtext, '\'');
    1082          36 :                 if (!dbname)
    1083             :                         break;
    1084          36 :                 endquote = strchr(++dbname, '\'');
    1085          36 :                 if (!endquote)
    1086             :                         break;
    1087          36 :                 *endquote = '\0';
    1088          36 :                 sprintf(use_statement, "USE %s\nGO\n\n", quote_id(dbname));
    1089             :                 tmp_free();
    1090             :                 return 0;
    1091             : 
    1092             :         case 0: /* Ignore print messages */
    1093             :         case 5703:      /* Ignore "Changed language setting to <language>". */
    1094             :                 return 0;
    1095             : 
    1096             :         default:
    1097             :                 break;
    1098             :         }
    1099             : 
    1100             : #if 0
    1101             :         printf("Msg %ld, Severity %d, State %d\n", (long) msgno, severity, msgstate);
    1102             : 
    1103             :         if (strlen(srvname) > 0)
    1104             :                 printf("Server '%s', ", srvname);
    1105             :         if (strlen(procname) > 0)
    1106             :                 printf("Procedure '%s', ", procname);
    1107             :         if (line > 0)
    1108             :                 printf("Line %d", line);
    1109             : #endif
    1110           0 :         printf("\t/* %s */\n", msgtext);
    1111             : 
    1112          80 :         return 0;
    1113             : }

Generated by: LCOV version 1.13