LCOV - code coverage report
Current view: top level - src/dblib/unittests - common.c (source / functions) Hit Total Coverage
Test: FreeTDS coverage Lines: 87 143 60.8 %
Date: 2026-03-24 22:22:09 Functions: 8 8 100.0 %

          Line data    Source code
       1             : #include "common.h"
       2             : 
       3             : #if HAVE_UNISTD_H
       4             : #include <unistd.h>
       5             : #endif
       6             : 
       7             : #ifdef __VMS
       8             : #include <unixlib.h>
       9             : #endif
      10             : 
      11             : #if HAVE_LIBGEN_H
      12             : #include <libgen.h>
      13             : #endif /* HAVE_LIBGEN_H */
      14             : 
      15             : #if HAVE_SYS_PARAM_H
      16             : #include <sys/param.h>
      17             : #endif /* HAVE_SYS_PARAM_H */
      18             : 
      19             : #include <freetds/time.h>
      20             : #include <freetds/bool.h>
      21             : 
      22             : #if HAVE_SYS_RESOURCE_H
      23             : #include <sys/resource.h>
      24             : #endif /* HAVE_SYS_RESOURCE_H */
      25             : 
      26             : #include <freetds/replacements.h>
      27             : 
      28             : #if !defined(PATH_MAX)
      29             : #define PATH_MAX 256
      30             : #endif
      31             : 
      32             : static char sql_file[PATH_MAX];
      33             : static FILE* input_file = NULL;
      34             : 
      35             : static char *ARGV0 = NULL;
      36             : static char *ARGV0B = NULL;
      37             : static char *DIRNAME = NULL;
      38             : static const char *BASENAME = NULL;
      39             : 
      40             : #if HAVE_MALLOC_OPTIONS
      41             : extern const char *malloc_options;
      42             : #endif /* HAVE_MALLOC_OPTIONS */
      43             : 
      44             : void
      45         352 : set_malloc_options(void)
      46             : {
      47             : 
      48             : #if HAVE_MALLOC_OPTIONS
      49             :         /*
      50             :          * Options for malloc
      51             :          * A- all warnings are fatal
      52             :          * J- init memory to 0xD0
      53             :          * R- always move memory block on a realloc
      54             :          */
      55             :         malloc_options = "AJR";
      56             : #endif /* HAVE_MALLOC_OPTIONS */
      57         352 : }
      58             : 
      59             : #if defined(__MINGW32__) || defined(_MSC_VER)
      60             : static char *
      61             : tds_dirname(char* path)
      62             : {
      63             :         char *p, *p2;
      64             : 
      65             :         for (p = path + strlen(path); --p > path && (*p == '/' || *p == '\\');)
      66             :                 *p = '\0';
      67             : 
      68             :         p = strrchr(path, '/');
      69             :         if (!p)
      70             :                 p = path;
      71             :         p2 = strrchr(p, '\\');
      72             :         if (p2)
      73             :                 p = p2;
      74             :         if (p == path) {
      75             :                 if (*p == '/' || *p == '\\')
      76             :                         return "\\";
      77             :                 return ".";
      78             :         }
      79             :         *p = 0;
      80             :         return path;
      81             : }
      82             : #define dirname tds_dirname
      83             : 
      84             : #endif
      85             : 
      86             : static bool free_file_registered = false;
      87             : static void
      88         430 : free_file(void)
      89             : {
      90         430 :         if (input_file) {
      91         340 :                 fclose(input_file);
      92         340 :                 input_file = NULL;
      93             :         }
      94         430 :         if (ARGV0) {
      95         430 :                 DIRNAME = NULL;
      96         430 :                 BASENAME = NULL;
      97         430 :                 free(ARGV0);
      98         430 :                 ARGV0 = NULL;
      99         430 :                 free(ARGV0B);
     100         430 :                 ARGV0B = NULL;
     101             :         }
     102         430 : }
     103             : 
     104             : int
     105         442 : read_login_info(int argc, char **argv)
     106             : {
     107             :         int len;
     108             :         int ch;
     109             :         char *s1 TDS_UNUSED;
     110             :         char filename[PATH_MAX];
     111             :         static const char *PWD = DEFAULT_PWD_PATH;
     112         442 :         const char *final_filename = NULL;
     113             : 
     114             : #if defined(HAVE_SETRLIMIT) && defined(RLIMIT_STACK)
     115             : #define MAX_STACK (8*1024*1024)
     116             : 
     117             :         struct rlimit rlim;
     118             : 
     119         442 :         if (!getrlimit(RLIMIT_STACK, &rlim) && (rlim.rlim_cur == RLIM_INFINITY || rlim.rlim_cur > MAX_STACK)) {
     120           0 :                 rlim.rlim_cur = MAX_STACK;
     121           0 :                 setrlimit(RLIMIT_STACK, &rlim);
     122             :         }
     123             : #endif
     124             : 
     125         442 :         setbuf(stdout, NULL);
     126         442 :         setbuf(stderr, NULL);
     127             : 
     128         442 :         free(ARGV0);
     129         442 :         free(ARGV0B);
     130             : #ifdef __VMS
     131             :         {
     132             :                 /* basename expects unix format */
     133             :                 s1 = strrchr(argv[0], ';'); /* trim version; extension trimmed later */
     134             :                 if (s1) *s1 = 0;
     135             :                 const char *unixspec = decc$translate_vms(argv[0]);
     136             :                 ARGV0 = strdup(unixspec);
     137             :         }
     138             : #else
     139         442 :         ARGV0 = strdup(argv[0]);
     140             : #endif
     141         442 :         ARGV0B = strdup(ARGV0);
     142             : 
     143         442 :         BASENAME = basename(ARGV0B);
     144             : #if defined(_WIN32) || defined(__VMS)
     145             :         s1 = strrchr(BASENAME, '.');
     146             :         if (s1) *s1 = 0;
     147             : #endif
     148         442 :         DIRNAME = dirname(ARGV0);
     149             : 
     150             :         /* process command line options (handy for manual testing) */
     151         884 :         while ((ch = getopt(argc, (char**)argv, "U:P:S:D:f:v")) != -1) {
     152           0 :                 switch (ch) {
     153           0 :                 case 'U':
     154           0 :                         strlcpy(common_pwd.user, optarg, sizeof(common_pwd.user));
     155           0 :                         break;
     156           0 :                 case 'P':
     157           0 :                         strlcpy(common_pwd.password, optarg, sizeof(common_pwd.password));
     158           0 :                         break;
     159           0 :                 case 'S':
     160           0 :                         strlcpy(common_pwd.server, optarg, sizeof(common_pwd.server));
     161           0 :                         break;
     162           0 :                 case 'D':
     163           0 :                         strlcpy(common_pwd.database, optarg, sizeof(common_pwd.database));
     164           0 :                         break;
     165           0 :                 case 'f': /* override default PWD file */
     166           0 :                         PWD = strdup(optarg);
     167           0 :                         break;
     168           0 :                 case 'v':       /* doesn't normally do anything */
     169           0 :                         common_pwd.fverbose = true;
     170           0 :                         break;
     171           0 :                 case '?':
     172             :                 default:
     173           0 :                         fprintf(stderr, "usage:  %s \n"
     174             :                                         "        [-U username] [-P password]\n"
     175             :                                         "        [-S servername] [-D database]\n"
     176             :                                         "        [-i input filename] [-o output filename] "
     177             :                                         "[-e error filename]\n"
     178             :                                         , BASENAME);
     179           0 :                         exit(1);
     180             :                 }
     181             :         }
     182         442 :         strlcpy(filename, PWD, sizeof(filename));
     183         442 :         if (!(final_filename = try_read_login_info_base(&common_pwd, filename))
     184           0 :             && !(final_filename = try_read_login_info_base(&common_pwd, "PWD"))) {
     185           0 :                 sprintf(filename, "%s/%s", (DIRNAME) ? DIRNAME : ".", PWD);
     186           0 :                 final_filename = read_login_info_base(&common_pwd, filename);
     187             :         }
     188             : 
     189         442 :         if (!*common_pwd.server) {
     190           0 :                 fprintf(stderr, "no servername provided, quitting.\n");
     191           0 :                 exit(1);
     192             :         }
     193             : 
     194         442 :         printf("found %s.%s for %s in \"%s\"\n", common_pwd.server, common_pwd.database, common_pwd.user, final_filename);
     195             : 
     196             : #if 0
     197             :         dbrecftos(BASENAME);
     198             : #endif
     199         442 :         len = snprintf(sql_file, sizeof(sql_file), "%s/%s.sql", FREETDS_SRCDIR, BASENAME);
     200         442 :         assert(len >= 0 && len <= sizeof(sql_file));
     201             : 
     202         442 :         if (input_file)
     203          12 :                 fclose(input_file);
     204         442 :         if ((input_file = fopen(sql_file, "r")) == NULL) {
     205          80 :                 fflush(stdout);
     206          80 :                 fprintf(stderr, "could not open SQL input file \"%s\"\n", sql_file);
     207             :         }
     208             : 
     209         442 :         if (!free_file_registered)
     210         430 :                 atexit(free_file);
     211         442 :         free_file_registered = true;
     212             : 
     213         442 :         printf("SQL text will be read from %s\n", sql_file);
     214             : 
     215         442 :         return 0;
     216             : }
     217             : 
     218             : /*
     219             :  * Fill the command buffer from a file while echoing it to standard output.
     220             :  */
     221             : RETCODE
     222        6948 : sql_cmd(DBPROCESS *dbproc)
     223             : {
     224        6948 :         char line[2048], *p = line;
     225        6948 :         int i = 0;
     226        6948 :         RETCODE erc=SUCCEED;
     227             : 
     228        6948 :         if (!input_file) {
     229           0 :                 fprintf(stderr, "%s: error: SQL input file \"%s\" not opened\n", BASENAME, sql_file);
     230           0 :                 exit(1);
     231             :         }
     232             : 
     233       18400 :         while ((p = fgets(line, (int)sizeof(line), input_file)) != NULL && strcasecmp("go\n", p) != 0) {
     234       11452 :                 printf("\t%3d: %s", ++i, p);
     235       11452 :                 if ((erc = dbcmd(dbproc, p)) != SUCCEED) {
     236           0 :                         fprintf(stderr, "%s: error: could write \"%s\" to dbcmd()\n", BASENAME, p);
     237           0 :                         exit(1);
     238             :                 }
     239             :         }
     240             : 
     241        6948 :         if (ferror(input_file)) {
     242           0 :                 fprintf(stderr, "%s: error: could not read SQL input file \"%s\"\n", BASENAME, sql_file);
     243           0 :                 exit(1);
     244             :         }
     245             : 
     246        6948 :         return erc;
     247             : }
     248             : 
     249             : RETCODE
     250         240 : sql_rewind(void)
     251             : {
     252         240 :         if (!input_file)
     253             :                 return FAIL;
     254         240 :         rewind(input_file);
     255         240 :         return SUCCEED;
     256             : }
     257             : 
     258             : RETCODE
     259         200 : sql_reopen(const char *fn)
     260             : {
     261         200 :         int len = snprintf(sql_file, sizeof(sql_file), "%s/%s.sql", FREETDS_SRCDIR, fn);
     262         200 :         assert(len >= 0 && len <= sizeof(sql_file));
     263             : 
     264         200 :         if (input_file)
     265         200 :                 fclose(input_file);
     266         200 :         if ((input_file = fopen(sql_file, "r")) == NULL) {
     267          10 :                 sql_file[0] = 0;
     268          10 :                 return FAIL;
     269             :         }
     270             :         return SUCCEED;
     271             : }
     272             : 
     273             : int
     274        1630 : syb_msg_handler(DBPROCESS * dbproc, DBINT msgno, int msgstate, int severity, char *msgtext, char *srvname, char *procname, int line)
     275             : {
     276             :         int *pexpected_msgno;
     277             : 
     278             :         /*
     279             :          * Check for "database changed", or "language changed" messages from
     280             :          * the client.  If we get one of these, then we need to pull the
     281             :          * name of the database or charset from the message and set the
     282             :          * appropriate variable.
     283             :          */
     284        3260 :         if (msgno == 5701 ||    /* database context change */
     285        1704 :             msgno == 5703 ||    /* language changed */
     286             :             msgno == 5704) {    /* charset changed */
     287             : 
     288             :                 /* fprintf( stderr, "msgno = %d: %s\n", msgno, msgtext ) ; */
     289             :                 return 0;
     290             :         }
     291             : 
     292             :         /*
     293             :          * If the user data indicates this is an expected error message (because we're testing the
     294             :          * error propogation, say) then indicate this message was anticipated.
     295             :          */
     296          74 :         if (dbproc != NULL) {
     297          74 :                 pexpected_msgno = (int *) dbgetuserdata(dbproc);
     298          74 :                 if (pexpected_msgno && *pexpected_msgno == msgno) {
     299          46 :                         printf("OK: anticipated message arrived: %d %s\n", (int) msgno, msgtext);
     300          46 :                         *pexpected_msgno = 0;
     301          46 :                         return 0;
     302             :                 }
     303             :         }
     304             :         /*
     305             :          * If the severity is something other than 0 or the msg number is
     306             :          * 0 (user informational messages).
     307             :          */
     308          28 :         fflush(stdout);
     309          28 :         if (severity >= 0 || msgno == 0) {
     310             :                 /*
     311             :                  * If the message was something other than informational, and
     312             :                  * the severity was greater than 0, then print information to
     313             :                  * stderr with a little pre-amble information.
     314             :                  */
     315          28 :                 if (msgno > 0 && severity > 0) {
     316           0 :                         fprintf(stderr, "Msg %d, Level %d, State %d\n", (int) msgno, (int) severity, (int) msgstate);
     317           0 :                         fprintf(stderr, "Server '%s'", srvname);
     318           0 :                         if (procname != NULL && *procname != '\0')
     319           0 :                                 fprintf(stderr, ", Procedure '%s'", procname);
     320           0 :                         if (line > 0)
     321           0 :                                 fprintf(stderr, ", Line %d", line);
     322           0 :                         fprintf(stderr, "\n");
     323           0 :                         fprintf(stderr, "%s\n", msgtext);
     324           0 :                         fflush(stderr);
     325             :                 } else {
     326             :                         /*
     327             :                          * Otherwise, it is just an informational (e.g. print) message
     328             :                          * from the server, so send it to stdout.
     329             :                          */
     330          28 :                         printf("%s\n", msgtext);
     331          28 :                         fflush(stdout);
     332          28 :                         severity = 0;
     333             :                 }
     334             :         }
     335             : 
     336           0 :         if (severity) {
     337           0 :                 fprintf(stderr, "exit: no unanticipated messages allowed in unit tests\n");
     338           0 :                 exit(EXIT_FAILURE);
     339             :         }
     340             :         return 0;
     341             : }
     342             : 
     343             : int
     344         184 : syb_err_handler(DBPROCESS * dbproc, int severity, int dberr, int oserr, char *dberrstr, char *oserrstr)
     345             : {
     346             :         int *pexpected_dberr;
     347             : 
     348             :         /*
     349             :          * For server messages, cancel the query and rely on the
     350             :          * message handler to spew the appropriate error messages out.
     351             :          */
     352         184 :         if (dberr == SYBESMSG)
     353             :                 return INT_CANCEL;
     354             : 
     355             :         /*
     356             :          * If the user data indicates this is an expected error message (because we're testing the
     357             :          * error propogation, say) then indicate this message was anticipated.
     358             :          */
     359         120 :         if (dbproc != NULL) {
     360         120 :                 pexpected_dberr = (int *) dbgetuserdata(dbproc);
     361         120 :                 if (pexpected_dberr && *pexpected_dberr == dberr) {
     362         120 :                         printf("OK: anticipated error %d (%s) arrived\n", dberr, dberrstr);
     363         120 :                         *pexpected_dberr = 0;
     364         120 :                         return INT_CANCEL;
     365             :                 }
     366             :         }
     367             : 
     368           0 :         fflush(stdout);
     369           0 :         fprintf(stderr,
     370             :                 "DB-LIBRARY error (dberr %d (severity %d): \"%s\"; oserr %d: \"%s\")\n",
     371             :                 dberr, severity, dberrstr ? dberrstr : "(null)", oserr, oserrstr ? oserrstr : "(null)");
     372           0 :         fflush(stderr);
     373             : 
     374             :         /*
     375             :          * If the dbprocess is dead or the dbproc is a NULL pointer and
     376             :          * we are not in the middle of logging in, then we need to exit.
     377             :          * We can't do anything from here on out anyway.
     378             :          * It's OK to end up here in response to a dbconvert() that
     379             :          * resulted in overflow, so don't exit in that case.
     380             :          */
     381           0 :         if ((dbproc == NULL) || DBDEAD(dbproc)) {
     382           0 :                 if (dberr != SYBECOFL) {
     383           0 :                         exit(255);
     384             :                 }
     385             :         }
     386             : 
     387           0 :         if (severity) {
     388           0 :                 fprintf(stderr, "error: no unanticipated errors allowed in unit tests\n");
     389           0 :                 exit(EXIT_FAILURE);
     390             :         }
     391             : 
     392             :         return INT_CANCEL;
     393             : }

Generated by: LCOV version 1.13