LCOV - code coverage report
Current view: top level - src/apps/unittests - defncopy.c (source / functions) Hit Total Coverage
Test: FreeTDS coverage Lines: 148 168 88.1 %
Date: 2025-07-05 11:39:58 Functions: 13 14 92.9 %

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

Generated by: LCOV version 1.13