LCOV - code coverage report
Current view: top level - src/apps/unittests - defncopy.c (source / functions) Hit Total Coverage
Test: FreeTDS coverage Lines: 115 122 94.3 %
Date: 2026-11-07 23:58:45 Functions: 9 9 100.0 %

          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 "common.h"
      25             : 
      26             : #if HAVE_STRING_H
      27             : #include <string.h>
      28             : #endif /* HAVE_STRING_H */
      29             : 
      30             : #ifdef HAVE_UNISTD_H
      31             : #include <unistd.h>
      32             : #endif
      33             : 
      34             : #include <freetds/sysdep_private.h>
      35             : #include <freetds/replacements.h>
      36             : #include <freetds/utils.h>
      37             : 
      38             : /* Sybase mode using a different terminator */
      39             : #define TERMINATOR "GO"
      40             : #define TERMINATOR_SYBASE "/* ### DEFNCOPY: END OF DEFINITION */"
      41             : 
      42             : /* content of output file, from command executed */
      43             : static char *output;
      44             : 
      45             : /* Whether using sybase syntax */
      46             : static bool sybase_syntax;
      47             : static bool stream_io = true;
      48             : 
      49             : static bool
      50             : read_login_info(void)
      51             : {
      52          10 :         return read_login_info_base(&common_pwd, DEFAULT_PWD_PATH) != NULL;
      53             : }
      54             : 
      55             : static void
      56          20 : cleanup(void)
      57             : {
      58          20 :         unlink("empty");
      59          20 :         unlink(output_fn());
      60          20 :         unlink(input_fn());
      61          20 :         TDS_ZERO_FREE(output);
      62          20 : }
      63             : 
      64             : /* check expected output, ignoring whitespace diffs */
      65             : static bool
      66          80 : output_matches(char const *expect)
      67             : {
      68             :         bool result;
      69             :         char *tmp;
      70             : 
      71          80 :         if (!expect)
      72             :                 return false;
      73          80 :         normalize_spaces(output);
      74          80 :         tmp = xstrdup(expect);
      75          80 :         normalize_spaces(tmp);
      76          80 :         result = strstr(output, tmp) != NULL;
      77          80 :         free(tmp);
      78          80 :         return result;
      79             : }
      80             : 
      81             : static void
      82          80 : required_output(char const *expect)
      83             : {
      84          80 :         if (!output_matches(expect)) {
      85           0 :                 fprintf(stderr, "Expected SQL string not found\n");
      86           0 :                 exit(EXIT_FAILURE);
      87             :         }
      88          80 : }
      89             : 
      90             : static void
      91         120 : defncopy(const char *object_name)
      92             : {
      93         120 :         char cmd[2048] = { 0 };
      94         120 :         char *const end = cmd + sizeof(cmd) - 1;
      95             :         char *p;
      96         120 :         bool redirs = stream_io && !sybase_syntax;
      97             : 
      98         120 :         strcat(cmd, VMS_APP_EXE_INVOKE "defncopy" EXE_SUFFIX);
      99         120 :         p = strchr(cmd, 0);
     100             : 
     101         120 :         if (sybase_syntax) {
     102          50 :                 p = add_login(p, end);
     103          50 :                 p = add_string(p, end, object_name ? " out " : " in ");
     104          50 :                 p = add_string(p, end, object_name ? output_fn() : input_fn());
     105          50 :                 if (common_pwd.database[0]) {
     106          50 :                         p = add_string(p, end, " ");
     107          50 :                         p = quote_arg(p, end, common_pwd.database);
     108             :                 }
     109             :         } else {
     110          70 :                 p = add_server(p, end);
     111          70 :                 if (!stream_io) {
     112          20 :                         p = add_string(p, end, object_name ? " -o " : " -i ");
     113          20 :                         p = add_string(p, end, object_name ? output_fn() : input_fn());
     114             :                 }
     115             :         }
     116             : 
     117         120 :         if (object_name) {
     118          80 :                 p = add_string(p, end, " ");
     119          80 :                 p = quote_arg(p, end, object_name);
     120             :         }
     121             : 
     122             :         /* FreeTDS defncopy syntax supports both standard streams, and input/output files.
     123             :          */
     124         120 :         if (redirs) {
     125          50 :                 p = add_string(p, end, object_name ? " >" : " <");
     126          50 :                 p = add_string(p, end, object_name ? output_fn() : input_fn());
     127             :         }
     128         120 :         *p = 0;
     129             : 
     130         120 :         printf("Executing: %s\n", cmd);
     131         120 :         if (system(cmd) != 0) {
     132           0 :                 if (object_name) {
     133           0 :                         printf("Output is:\n");
     134           0 :                         cat(output_fn(), stdout);
     135             :                 }
     136           0 :                 fprintf(stderr, "Failed command\n");
     137           0 :                 exit(EXIT_FAILURE);
     138             :         }
     139             : 
     140         120 :         TDS_ZERO_FREE(output);
     141         120 :         if (object_name)
     142          80 :                 output = read_file(output_fn());
     143         120 : }
     144             : 
     145             : static void
     146          40 : defncopy_in(char const *sql)
     147             : {
     148             :         FILE *f;
     149             : 
     150          40 :         f = fopen(input_fn(), "w");
     151          40 :         assert(f);
     152          40 :         fputs(sql, f);
     153          40 :         fclose(f);
     154          40 :         defncopy(NULL);
     155          40 : }
     156             : 
     157             : /* table with a column name that is also a keyword, should be quoted */
     158             : static void
     159          20 : test_keyword(void)
     160             : {
     161             :         const char *sql;
     162             :         static const char clean[] =
     163             :                 "IF OBJECT_ID('dbo.table_with_column_named_key') IS NOT NULL DROP TABLE dbo.table_with_column_named_key\n";
     164             : 
     165          20 :         tsql(clean);
     166             : /* *INDENT-OFF* */
     167          20 :         tsql(
     168             : "IF OBJECT_ID('dbo.table_with_column_named_key') IS NOT NULL DROP TABLE dbo.table_with_column_named_key\n"
     169             : "GO\n"
     170             : "CREATE TABLE dbo.table_with_column_named_key\n"
     171             : "(\n"
     172             : "  [key]        nvarchar(4000)  NOT NULL\n"
     173             : ")\n");
     174             : /* *INDENT-ON* */
     175          20 :         defncopy("dbo.table_with_column_named_key");
     176          20 :         cat(output_fn(), stdout);
     177             : #define EXPECT_KEYWORD_OUT "CREATE TABLE [dbo].[table_with_column_named_key]\n ( [key] nvarchar(4000) NOT NULL\n )\n"
     178          20 :         sql = sybase_syntax ? EXPECT_KEYWORD_OUT TERMINATOR_SYBASE "\n" : EXPECT_KEYWORD_OUT TERMINATOR "\n";
     179             : 
     180          20 :         required_output(sql);
     181          20 :         tsql(clean);
     182             : 
     183             :         /* test defncopy input */
     184          20 :         defncopy_in(sql);
     185          20 :         unlink(output_fn());
     186          20 :         defncopy("dbo.table_with_column_named_key");
     187          20 :         required_output(sql);
     188          20 :         tsql(clean);
     189             : 
     190             :         /* test non-stream syntax */
     191          20 :         stream_io = false;
     192          20 :         defncopy_in(sql);
     193          20 :         unlink(output_fn());
     194          20 :         defncopy("dbo.table_with_column_named_key");
     195          20 :         required_output(sql);
     196          20 :         tsql(clean);
     197          20 :         stream_io = true;
     198             : 
     199          20 :         tsql(sql);
     200          20 :         tsql(clean);
     201          20 : }
     202             : 
     203             : /* table with an index with a space inside */
     204             : static void
     205          10 : test_index_name_with_space(void)
     206             : {
     207             :         const char *sql;
     208             :         static const char clean[] = "IF OBJECT_ID('dbo.tblReportPeriod') IS NOT NULL DROP TABLE dbo.tblReportPeriod\n";
     209             : 
     210          10 :         tsql(clean);
     211             : /* *INDENT-OFF* */
     212          10 :         tsql(
     213             : "CREATE TABLE dbo.tblReportPeriod\n"
     214             : "  ( RecordID   int             NOT NULL\n"
     215             : "  , FromDate   nvarchar(40)        NULL\n"
     216             : "  , ToDate     nvarchar(40)        NULL\n"
     217             : "  )\n"
     218             : "CREATE  nonclustered INDEX [From Date] on dbo.tblReportPeriod(FromDate)\n");
     219             : /* *INDENT-ON* */
     220          10 :         defncopy("dbo.tblReportPeriod");
     221          10 :         cat(output_fn(), stdout);
     222             : /* *INDENT-OFF* */
     223          10 :         sql =
     224             : "CREATE TABLE [dbo].[tblReportPeriod]\n"
     225             : " ( [RecordID] int NOT NULL\n"
     226             : " , [FromDate] nvarchar(40) NULL\n"
     227             : " , [ToDate] nvarchar(40) NULL\n"
     228             : " )\n"
     229             : "CREATE nonclustered INDEX [From Date] on [dbo].[tblReportPeriod]([FromDate]) "
     230             : TERMINATOR
     231             : ;
     232             : /* *INDENT-ON* */
     233          10 :         required_output(sql);
     234          10 :         tsql(clean);
     235          10 :         tsql(sql);
     236          10 :         tsql(clean);
     237          10 : }
     238             : 
     239             : /* table with an index with a space inside */
     240             : static void
     241          10 : test_weird_index_names(void)
     242             : {
     243             :         const char *sql;
     244             :         static const char clean[] = "IF OBJECT_ID('dbo.tblReportPeriod2') IS NOT NULL DROP TABLE dbo.tblReportPeriod2\n";
     245             : 
     246          10 :         tsql(clean);
     247             : /* *INDENT-OFF* */
     248          10 :         tsql(
     249             : "CREATE TABLE dbo.tblReportPeriod2\n"
     250             : "  ( RecordID   int             NOT NULL\n"
     251             : "  , [To, ]   nvarchar(40)        NULL\n"
     252             : "  , [To]     nvarchar(40)        NULL\n"
     253             : "  , [To, , ]     nvarchar(40)        NULL\n"
     254             : "  )\n"
     255             : "CREATE  nonclustered INDEX [From Date] on dbo.tblReportPeriod2([To, ],[To, , ])\n");
     256             : /* *INDENT-ON* */
     257          10 :         defncopy("dbo.tblReportPeriod2");
     258          10 :         cat(output_fn(), stdout);
     259             : 
     260             : /* *INDENT-OFF* */
     261          10 :         sql =
     262             : "CREATE TABLE [dbo].[tblReportPeriod2]\n"
     263             : " ( [RecordID] int NOT NULL\n"
     264             : " , [To, ] nvarchar(40) NULL\n"
     265             : " , [To] nvarchar(40) NULL\n"
     266             : " , [To, , ] nvarchar(40) NULL\n"
     267             : " )\n"
     268             : "CREATE nonclustered INDEX [From Date] on [dbo].[tblReportPeriod2]([To, ],[To, ,])\n"
     269             : TERMINATOR
     270             : ;
     271             : /* *INDENT-ON* */
     272          10 :         required_output(sql);
     273          10 :         tsql(clean);
     274          10 :         tsql(sql);
     275          10 :         tsql(clean);
     276          10 : }
     277             : 
     278          10 : TEST_MAIN()
     279             : {
     280             :         FILE *f;
     281             : 
     282          10 :         cleanup();
     283          10 :         update_path();
     284             : 
     285          10 :         if (!read_login_info())
     286             :                 return 1;
     287             : 
     288          10 :         f = fopen("empty", "w");
     289          10 :         if (f)
     290          10 :                 fclose(f);
     291             : 
     292          10 :         sybase_syntax = true;
     293          10 :         test_keyword();
     294          10 :         sybase_syntax = false;
     295          10 :         test_keyword();
     296          10 :         test_index_name_with_space();
     297          10 :         test_weird_index_names();
     298             : 
     299          10 :         cleanup();
     300          10 :         return 0;
     301             : }

Generated by: LCOV version 1.13