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

Generated by: LCOV version 1.13