LCOV - code coverage report
Current view: top level - src/apps/unittests - defncopy.c (source / functions) Hit Total Coverage
Test: FreeTDS coverage Lines: 167 191 87.4 %
Date: 2025-01-18 12:13:41 Functions: 14 15 93.3 %

          Line data    Source code
       1             : /* FreeTDS - Library of routines accessing Sybase and Microsoft databases
       2             :  * Copyright (C) 2024  Frediano Ziglio
       3             :  *
       4             :  * This library is free software; you can redistribute it and/or
       5             :  * modify it under the terms of the GNU Library 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 Library 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             : /**
      21             :  * This tests execute some command using tsql and defncopy to check behaviour
      22             :  */
      23             : 
      24             : #undef NDEBUG
      25             : #include <config.h>
      26             : 
      27             : #include <stdio.h>
      28             : #include <stdlib.h>
      29             : #include <assert.h>
      30             : 
      31             : #if HAVE_STRING_H
      32             : #include <string.h>
      33             : #endif /* HAVE_STRING_H */
      34             : 
      35             : #ifdef HAVE_UNISTD_H
      36             : #include <unistd.h>
      37             : #endif
      38             : 
      39             : #ifdef _WIN32
      40             : #include <process.h>
      41             : #define EXE_SUFFIX ".exe"
      42             : #define SDIR_SEPARATOR "\\"
      43             : #else
      44             : #define EXE_SUFFIX ""
      45             : #define SDIR_SEPARATOR "/"
      46             : #endif
      47             : 
      48             : #include <freetds/bool.h>
      49             : #include <freetds/macros.h>
      50             : 
      51             : static char USER[512];
      52             : static char SERVER[512];
      53             : static char PASSWORD[512];
      54             : static char DATABASE[512];
      55             : 
      56             : /* content of output file, from command executed */
      57             : static char *output;
      58             : 
      59             : static bool
      60           8 : read_login_info(void)
      61             : {
      62           8 :         FILE *in = NULL;
      63             :         char line[512];
      64             :         char *s1, *s2;
      65             : 
      66           8 :         s1 = getenv("TDSPWDFILE");
      67           8 :         if (s1 && s1[0])
      68           0 :                 in = fopen(s1, "r");
      69           0 :         if (!in)
      70           8 :                 in = fopen("../../../PWD", "r");
      71           8 :         if (!in) {
      72           0 :                 fprintf(stderr, "Can not open PWD file\n\n");
      73           0 :                 return false;
      74             :         }
      75             : 
      76         152 :         while (fgets(line, sizeof(line), in)) {
      77         144 :                 s1 = strtok(line, "=");
      78         144 :                 s2 = strtok(NULL, "\n");
      79         144 :                 if (!s1 || !s2) {
      80          86 :                         continue;
      81             :                 }
      82          58 :                 if (!strcmp(s1, "UID")) {
      83           8 :                         strcpy(USER, s2);
      84          50 :                 } else if (!strcmp(s1, "SRV")) {
      85           8 :                         strcpy(SERVER, s2);
      86          42 :                 } else if (!strcmp(s1, "PWD")) {
      87           8 :                         strcpy(PASSWORD, s2);
      88          34 :                 } else if (!strcmp(s1, "DB")) {
      89           8 :                         strcpy(DATABASE, s2);
      90             :                 }
      91             :         }
      92           8 :         fclose(in);
      93           8 :         return true;
      94             : }
      95             : 
      96             : static void
      97           0 : no_space(void)
      98             : {
      99           0 :         fprintf(stderr, "No space left on buffer\n");
     100           0 :         exit(1);
     101             : }
     102             : 
     103             : static void
     104          24 : normalize_spaces(char *s)
     105             : {
     106             :         char *p, *dest, prev;
     107             : 
     108             :         /* replace all tabs with spaces */
     109       10534 :         for (p = s; *p; ++p)
     110       10510 :                 if (*p == '\t')
     111         148 :                         *p = ' ';
     112             : 
     113             :         /* replace duplicate spaces with a single space */
     114             :         prev = 'x';
     115       10510 :         for (dest = s, p = s; *p; ++p) {
     116       10510 :                 if (prev == ' ' && *p == ' ')
     117         612 :                         continue;
     118        9898 :                 *dest++ = prev = *p;
     119             :         }
     120          24 :         *dest = 0;
     121          24 : }
     122             : 
     123             : /* read a file and output on a stream */
     124             : static void
     125          24 : cat(const char *fn, FILE *out)
     126             : {
     127             :         char line[1024];
     128          24 :         FILE *f = fopen(fn, "r");
     129          24 :         assert(f);
     130         364 :         while (fgets(line, sizeof(line), f)) {
     131         340 :                 fputs("  ", out);
     132         340 :                 fputs(line, out);
     133             :         }
     134          24 :         fclose(f);
     135          24 : }
     136             : 
     137             : /* read a text file into memory, return it as a string */
     138             : static char *
     139         144 : read_file(const char *fn)
     140             : {
     141             :         long pos;
     142             :         char *buf;
     143             :         size_t readed;
     144             : 
     145         144 :         FILE *f = fopen(fn, "r");
     146         144 :         assert(f);
     147         144 :         assert(fseek(f, 0, SEEK_END) == 0);
     148         144 :         pos = ftell(f);
     149         144 :         assert(pos >= 0);
     150         144 :         assert(fseek(f, 0, SEEK_SET) == 0);
     151         144 :         buf = malloc(pos + 10); /* allocate some more space */
     152         144 :         assert(buf);
     153         144 :         readed = fread(buf, 1, pos+1, f);
     154         144 :         assert(readed <= pos);
     155         144 :         assert(feof(f));
     156         144 :         fclose(f);
     157         144 :         buf[readed] = 0;
     158         144 :         return buf;
     159             : }
     160             : 
     161             : #define CHECK(n) do {\
     162             :         if (dest + (n) > dest_end) \
     163             :                 no_space(); \
     164             : } while(0)
     165             : 
     166             : static char *
     167         600 : quote_arg(char *dest, char *dest_end, const char *arg)
     168             : {
     169             : #ifndef _WIN32
     170         600 :         CHECK(1);
     171         600 :         *dest++ = '\'';
     172        5516 :         for (; *arg; ++arg) {
     173        4916 :                 if (*arg == '\'') {
     174           0 :                         CHECK(3);
     175           0 :                         strcpy(dest, "'\\'");
     176           0 :                         dest += 3;
     177             :                 }
     178        4916 :                 CHECK(1);
     179        4916 :                 *dest++ = *arg;
     180             :         }
     181         600 :         CHECK(1);
     182         600 :         *dest++ = '\'';
     183             : #else
     184             :         CHECK(1);
     185             :         *dest++ = '\"';
     186             :         for (; *arg; ++arg) {
     187             :                 if (*arg == '\\' || *arg == '\"') {
     188             :                         CHECK(1);
     189             :                         *dest++ = '\\';
     190             :                 }
     191             :                 CHECK(1);
     192             :                 *dest++ = *arg;
     193             :         }
     194             :         CHECK(1);
     195             :         *dest++ = '\"';
     196             : #endif
     197         600 :         return dest;
     198             : }
     199             : 
     200             : static char *
     201         744 : add_string(char *dest, char *const dest_end, const char *str)
     202             : {
     203         744 :         size_t len = strlen(str);
     204         744 :         CHECK(len);
     205         744 :         memcpy(dest, str, len);
     206         744 :         return dest + len;
     207             : }
     208             : 
     209             : #undef CHECK
     210             : 
     211             : static char *
     212         144 : add_server(char *dest, char *const dest_end)
     213             : {
     214         144 :         dest = add_string(dest, dest_end, " -S ");
     215         144 :         dest = quote_arg(dest, dest_end, SERVER);
     216         144 :         dest = add_string(dest, dest_end, " -U ");
     217         144 :         dest = quote_arg(dest, dest_end, USER);
     218         144 :         dest = add_string(dest, dest_end, " -P ");
     219         144 :         dest = quote_arg(dest, dest_end, PASSWORD);
     220         144 :         if (DATABASE[0]) {
     221         144 :                 dest = add_string(dest, dest_end, " -D ");
     222         144 :                 dest = quote_arg(dest, dest_end, DATABASE);
     223             :         }
     224         144 :         return dest;
     225             : }
     226             : 
     227             : static void
     228          16 : cleanup(void)
     229             : {
     230          16 :         unlink("empty");
     231          16 :         unlink("output");
     232          16 :         unlink("input");
     233          16 :         TDS_ZERO_FREE(output);
     234          16 : }
     235             : 
     236             : static void
     237         120 : tsql(const char *input_data)
     238             : {
     239             :         char cmd[2048];
     240         120 :         char *const end = cmd + sizeof(cmd) - 1;
     241             :         char *p;
     242             :         FILE *f;
     243             : 
     244         120 :         f = fopen("input", "w");
     245         120 :         assert(f);
     246         120 :         fputs(input_data, f);
     247         120 :         fclose(f);
     248             : 
     249         120 :         strcpy(cmd, ".." SDIR_SEPARATOR "tsql" EXE_SUFFIX " -o q");
     250         120 :         p = strchr(cmd, 0);
     251         120 :         p = add_server(p, end);
     252         120 :         p = add_string(p, end, "<input >output");
     253         120 :         *p = 0;
     254         120 :         printf("Executing: %s\n", cmd);
     255         120 :         if (system(cmd) != 0) {
     256           0 :                 printf("Output is:\n");
     257           0 :                 cat("output", stdout);
     258           0 :                 fprintf(stderr, "Failed command\n");
     259           0 :                 exit(1);
     260             :         }
     261         120 :         TDS_ZERO_FREE(output);
     262         120 :         output = read_file("output");
     263         120 : }
     264             : 
     265             : static void
     266          24 : defncopy(const char *object_name)
     267             : {
     268             :         char cmd[2048];
     269          24 :         char *const end = cmd + sizeof(cmd) - 1;
     270             :         char *p;
     271             :         FILE *f;
     272             : 
     273             :         // empty input
     274          24 :         f = fopen("input", "w");
     275          24 :         assert(f);
     276          24 :         fclose(f);
     277             : 
     278          24 :         strcpy(cmd, ".." SDIR_SEPARATOR "defncopy" EXE_SUFFIX);
     279          24 :         p = strchr(cmd, 0);
     280          24 :         p = add_server(p, end);
     281          24 :         p = add_string(p, end, " ");
     282          24 :         p = quote_arg(p, end, object_name);
     283          24 :         p = add_string(p, end, "<input >output");
     284          24 :         *p = 0;
     285          24 :         printf("Executing: %s\n", cmd);
     286          24 :         if (system(cmd) != 0) {
     287           0 :                 printf("Output is:\n");
     288           0 :                 cat("output", stdout);
     289           0 :                 fprintf(stderr, "Failed command\n");
     290           0 :                 exit(1);
     291             :         }
     292          24 :         TDS_ZERO_FREE(output);
     293          24 :         output = read_file("output");
     294          24 : }
     295             : 
     296             : /* table with a column name that is also a keyword, should be quoted */
     297             : static void
     298           8 : test_keyword(void)
     299             : {
     300             :         const char *sql;
     301             :         static const char clean[] =
     302             :                 "IF OBJECT_ID('dbo.table_with_column_named_key') IS NOT NULL DROP TABLE dbo.table_with_column_named_key\n";
     303             : 
     304           8 :         tsql(clean);
     305           8 :         tsql(
     306             : "IF OBJECT_ID('dbo.table_with_column_named_key') IS NOT NULL DROP TABLE dbo.table_with_column_named_key\n"
     307             : "GO\n"
     308             : "CREATE TABLE dbo.table_with_column_named_key\n"
     309             : "(\n"
     310             : "  [key]        nvarchar(4000)  NOT NULL\n"
     311             : ")\n");
     312           8 :         defncopy("dbo.table_with_column_named_key");
     313           8 :         cat("output", stdout);
     314           8 :         normalize_spaces(output);
     315           8 :         sql =
     316             : "CREATE TABLE [dbo].[table_with_column_named_key]\n"
     317             : " ( [key] nvarchar(4000) NOT NULL\n"
     318             : " )\n"
     319             : "GO";
     320           8 :         if (strstr(output, sql) == NULL) {
     321           0 :                 fprintf(stderr, "Expected SQL string not found\n");
     322           0 :                 exit(1);
     323             :         }
     324           8 :         tsql(clean);
     325           8 :         tsql(sql);
     326           8 :         tsql(clean);
     327           8 : }
     328             : 
     329             : /* table with an index with a space inside */
     330             : static void
     331           8 : test_index_name_with_space(void)
     332             : {
     333             :         const char *sql;
     334             :         static const char clean[] =
     335             :                 "IF OBJECT_ID('dbo.tblReportPeriod') IS NOT NULL DROP TABLE dbo.tblReportPeriod\n";
     336             : 
     337           8 :         tsql(clean);
     338           8 :         tsql(
     339             : "CREATE TABLE dbo.tblReportPeriod\n"
     340             : "  ( RecordID   int             NOT NULL\n"
     341             : "  , FromDate   nvarchar(40)        NULL\n"
     342             : "  , ToDate     nvarchar(40)        NULL\n"
     343             : "  )\n"
     344             : "CREATE  nonclustered INDEX [From Date] on dbo.tblReportPeriod(FromDate)\n");
     345           8 :         defncopy("dbo.tblReportPeriod");
     346           8 :         cat("output", stdout);
     347           8 :         normalize_spaces(output);
     348           8 :         sql =
     349             : "CREATE TABLE [dbo].[tblReportPeriod]\n"
     350             : " ( [RecordID] int NOT NULL\n"
     351             : " , [FromDate] nvarchar(40) NULL\n"
     352             : " , [ToDate] nvarchar(40) NULL\n"
     353             : " )\n"
     354             : "GO\n"
     355             : "\n"
     356             : "CREATE nonclustered INDEX [From Date] on [dbo].[tblReportPeriod]([FromDate])";
     357           8 :         if (strstr(output, sql) == NULL) {
     358           0 :                 fprintf(stderr, "Expected SQL string not found\n");
     359           0 :                 exit(1);
     360             :         }
     361           8 :         tsql(clean);
     362           8 :         tsql(sql);
     363           8 :         tsql(clean);
     364           8 : }
     365             : 
     366             : /* table with an index with a space inside */
     367             : static void
     368           8 : test_weird_index_names(void)
     369             : {
     370             :         const char *sql, *sql_sybase;
     371             :         static const char clean[] =
     372             :                 "IF OBJECT_ID('dbo.tblReportPeriod2') IS NOT NULL DROP TABLE dbo.tblReportPeriod2\n";
     373             : 
     374           8 :         tsql(clean);
     375           8 :         tsql(
     376             : "CREATE TABLE dbo.tblReportPeriod2\n"
     377             : "  ( RecordID   int             NOT NULL\n"
     378             : "  , [To, ]   nvarchar(40)        NULL\n"
     379             : "  , [To]     nvarchar(40)        NULL\n"
     380             : "  , [To, , ]     nvarchar(40)        NULL\n"
     381             : "  )\n"
     382             : "CREATE  nonclustered INDEX [From Date] on dbo.tblReportPeriod2([To, ],[To, , ])\n");
     383           8 :         defncopy("dbo.tblReportPeriod2");
     384           8 :         cat("output", stdout);
     385           8 :         normalize_spaces(output);
     386           8 :         sql =
     387             : "CREATE TABLE [dbo].[tblReportPeriod2]\n"
     388             : " ( [RecordID] int NOT NULL\n"
     389             : " , [To, ] nvarchar(40) NULL\n"
     390             : " , [To] nvarchar(40) NULL\n"
     391             : " , [To, , ] nvarchar(40) NULL\n"
     392             : " )\n"
     393             : "GO\n"
     394             : "\n"
     395             : "CREATE nonclustered INDEX [From Date] on [dbo].[tblReportPeriod2]([To, ],[To, , ])";
     396             :         /* Sybase remove spaces at the end */
     397           8 :         sql_sybase =
     398             : "CREATE TABLE [dbo].[tblReportPeriod2]\n"
     399             : " ( [RecordID] int NOT NULL\n"
     400             : " , [To,] nvarchar(40) NULL\n"
     401             : " , [To] nvarchar(40) NULL\n"
     402             : " , [To, ,] nvarchar(40) NULL\n"
     403             : " )\n"
     404             : "GO\n"
     405             : "\n"
     406             : "CREATE nonclustered INDEX [From Date] on [dbo].[tblReportPeriod2]([To,],[To, ,])";
     407           8 :         if (strstr(output, sql) == NULL && strstr(output, sql_sybase) == NULL) {
     408           0 :                 fprintf(stderr, "Expected SQL string not found\n");
     409           0 :                 exit(1);
     410             :         }
     411           8 :         tsql(clean);
     412           8 :         tsql(sql);
     413           8 :         tsql(clean);
     414           8 : }
     415             : 
     416           8 : int main(void)
     417             : {
     418             :         FILE *f;
     419             : 
     420           8 :         cleanup();
     421             : 
     422           8 :         if (!read_login_info())
     423             :                 return 1;
     424             : 
     425           8 :         f = fopen("empty", "w");
     426           8 :         if (f)
     427           8 :                 fclose(f);
     428             : 
     429           8 :         test_keyword();
     430           8 :         test_index_name_with_space();
     431           8 :         test_weird_index_names();
     432             : 
     433           8 :         cleanup();
     434           8 :         return 0;
     435             : }

Generated by: LCOV version 1.13