LCOV - code coverage report
Current view: top level - src/dblib - dbpivot.c (source / functions) Hit Total Coverage
Test: FreeTDS coverage Lines: 6 472 1.3 %
Date: 2025-01-18 11:50:39 Functions: 1 29 3.4 %

          Line data    Source code
       1             : /* FreeTDS - Library of routines accessing Sybase and Microsoft databases
       2             :  * Copyright (C) 2011  James K. Lowden
       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             : #include <config.h>
      21             : 
      22             : #include <stdarg.h>
      23             : 
      24             : #include <freetds/time.h>
      25             : 
      26             : #include <assert.h>
      27             : #include <stdio.h>
      28             : 
      29             : #if HAVE_STDLIB_H
      30             : #include <stdlib.h>
      31             : #endif /* HAVE_STDLIB_H */
      32             : 
      33             : #if HAVE_STRING_H
      34             : #include <string.h>
      35             : #endif /* HAVE_STRING_H */
      36             : 
      37             : #if HAVE_UNISTD_H
      38             : #include <unistd.h>
      39             : #endif /* HAVE_UNISTD_H */
      40             : 
      41             : #if HAVE_ERRNO_H
      42             : # include <errno.h>
      43             : #endif /* HAVE_ERRNO_H */
      44             : 
      45             :  
      46             : #include <freetds/tds.h>
      47             : #include <freetds/thread.h>
      48             : #include <freetds/convert.h>
      49             : #include <freetds/utils/string.h>
      50             : #include <freetds/replacements.h>
      51             : #include <sybfront.h>
      52             : #include <sybdb.h>
      53             : #include <syberror.h>
      54             : #include <dblib.h>
      55             : 
      56             : #define TDS_FIND(k,b,c) tds_find(k, b, TDS_VECTOR_SIZE(b), sizeof(b[0]), c)
      57             : 
      58             : typedef bool (*compare_func)(const void *, const void *);
      59             : 
      60             : static void *
      61             : tds_find(const void *key, const void *base, size_t nelem, size_t width,
      62             :          compare_func compar)
      63             : {
      64             :         size_t n;
      65      175551 :         char *p = (char *) base;
      66             : 
      67      175551 :         for (n = nelem; n != 0; --n) {
      68           0 :                 if (compar(key, p))
      69             :                         return p;
      70           0 :                 p += width;
      71             :         }
      72             :         return NULL;
      73             : }
      74             : 
      75             : 
      76             : struct col_t
      77             : {
      78             :         size_t len;
      79             :         TDS_SERVER_TYPE type;
      80             :         int null_indicator;
      81             :         char *s;
      82             :         union {
      83             :                 DBTINYINT       ti;
      84             :                 DBSMALLINT      si;
      85             :                 DBINT           i;
      86             :                 DBREAL          r;
      87             :                 DBFLT8          f;
      88             :         } data;
      89             : };
      90             : 
      91             : static TDS_SERVER_TYPE infer_col_type(int sybtype);
      92             : 
      93             : static struct col_t *
      94           0 : col_init(struct col_t *pcol, int sybtype, int collen) 
      95             : {
      96           0 :         assert(pcol);
      97             :         
      98           0 :         pcol->type = infer_col_type(sybtype);
      99           0 :         if (pcol->type == TDS_INVALID_TYPE)
     100             :                 return NULL;
     101           0 :         pcol->len = collen;
     102           0 :         pcol->s = NULL;
     103             : 
     104           0 :         switch (sybtype) {
     105           0 :         case 0:
     106           0 :                 pcol->len = 0;
     107           0 :                 return NULL;
     108           0 :         case SYBDATETIME:
     109             :         case SYBDATETIME4:
     110             :         case SYBDATETIMN:
     111           0 :                 collen = 30;
     112             :                 /* fall through */
     113           0 :         case SYBCHAR:
     114             :         case SYBVARCHAR:
     115             :         case SYBTEXT:
     116             :         case SYBNTEXT:
     117           0 :                 pcol->len = collen;
     118           0 :                 if ((pcol->s = tds_new(char, 1+collen)) == NULL) {
     119             :                         return NULL;
     120             :                 }
     121             :                 break;
     122             :         }
     123             :         return pcol;
     124             : }
     125             : 
     126             : static void
     127           0 : col_free(struct col_t *p)
     128             : {
     129           0 :         free(p->s);
     130           0 :         memset(p, 0, sizeof(*p));
     131           0 : }
     132             : 
     133             : static bool
     134           0 : col_equal(const struct col_t *pc1, const struct col_t *pc2)
     135             : {
     136           0 :         assert( pc1 && pc2 );
     137           0 :         assert( pc1->type == pc2->type );
     138             :         
     139           0 :         switch (pc1->type) {
     140             :         
     141           0 :         case SYBCHAR:
     142             :         case SYBVARCHAR:
     143           0 :                 if( pc1->len != pc2->len)
     144             :                         return false;
     145           0 :                 return strncmp(pc1->s, pc2->s, pc1->len) == 0;
     146           0 :         case SYBINT1:
     147             :         case SYBUINT1:
     148             :         case SYBSINT1:
     149           0 :                 return pc1->data.ti == pc2->data.ti;
     150           0 :         case SYBINT2:
     151             :         case SYBUINT2:
     152           0 :                 return pc1->data.si == pc2->data.si;
     153           0 :         case SYBINT4:
     154             :         case SYBUINT4:
     155           0 :                 return pc1->data.i == pc2->data.i;
     156           0 :         case SYBFLT8:
     157           0 :                 return pc1->data.f == pc2->data.f;
     158           0 :         case SYBREAL:
     159           0 :                 return pc1->data.r == pc2->data.r;
     160             : 
     161             :         case SYBINTN:
     162             :         case SYBDATETIME:
     163             :         case SYBBIT:
     164             :         case SYBTEXT:
     165             :         case SYBNTEXT:
     166             :         case SYBIMAGE:
     167             :         case SYBMONEY4:
     168             :         case SYBMONEY:
     169             :         case SYBDATETIME4:
     170             :         case SYBBINARY:
     171             :         case SYBVOID:
     172             :         case SYBVARBINARY:
     173             :         case SYBBITN:
     174             :         case SYBNUMERIC:
     175             :         case SYBDECIMAL:
     176             :         case SYBFLTN:
     177             :         case SYBMONEYN:
     178             :         case SYBDATETIMN:
     179             :         case SYBMSTABLE:
     180             :         case SYBNVARCHAR:
     181             :         case SYBINT8:
     182             :         case XSYBCHAR:
     183             :         case XSYBVARCHAR:
     184             :         case XSYBNVARCHAR:
     185             :         case XSYBNCHAR:
     186             :         case XSYBVARBINARY:
     187             :         case XSYBBINARY:
     188             :         case SYBUNIQUE:
     189             :         case SYBVARIANT:
     190             :         case SYBMSUDT:
     191             :         case SYBMSXML:
     192             :         case SYBMSDATE:
     193             :         case SYBMSTIME:
     194             :         case SYBMSDATETIME2:
     195             :         case SYBMSDATETIMEOFFSET:
     196             :         case SYBLONGBINARY:
     197             :         case SYBUINT8:
     198             :         case SYBDATE:
     199             :         case SYBDATEN:
     200             :         case SYB5INT8:
     201             :         case SYBINTERVAL:
     202             :         case SYBTIME:
     203             :         case SYBTIMEN:
     204             :         case SYBUINTN:
     205             :         case SYBUNITEXT:
     206             :         case SYBXML:
     207             :         case SYB5BIGDATETIME:
     208             :         case SYB5BIGTIME:
     209             : 
     210           0 :                 assert( false && pc1->type );
     211             :                 break;
     212             :         }
     213             :         return false;
     214             : }
     215             : 
     216             : static void *
     217           0 : col_buffer(struct col_t *pcol) 
     218             : {
     219           0 :         switch (pcol->type) {
     220             :         
     221           0 :         case SYBCHAR:
     222             :         case SYBVARCHAR:
     223           0 :                 return pcol->s;
     224           0 :         case SYBINT1:
     225             :         case SYBUINT1:
     226             :         case SYBSINT1:
     227           0 :                 return &pcol->data.ti;
     228           0 :         case SYBINT2:
     229             :         case SYBUINT2:
     230           0 :                 return &pcol->data.si;
     231           0 :         case SYBINT4:
     232             :         case SYBUINT4:
     233           0 :                 return &pcol->data.i;
     234           0 :         case SYBFLT8:
     235           0 :                 return &pcol->data.f;
     236           0 :         case SYBREAL:
     237           0 :                 return &pcol->data.r;
     238             : 
     239             :         case SYBINTN:
     240             :         case SYBDATETIME:
     241             :         case SYBBIT:
     242             :         case SYBTEXT:
     243             :         case SYBNTEXT:
     244             :         case SYBIMAGE:
     245             :         case SYBMONEY4:
     246             :         case SYBMONEY:
     247             :         case SYBDATETIME4:
     248             :         case SYBBINARY:
     249             :         case SYBVOID:
     250             :         case SYBVARBINARY:
     251             :         case SYBBITN:
     252             :         case SYBNUMERIC:
     253             :         case SYBDECIMAL:
     254             :         case SYBFLTN:
     255             :         case SYBMONEYN:
     256             :         case SYBDATETIMN:
     257             :         case SYBMSTABLE:
     258             :         case SYBNVARCHAR:
     259             :         case SYBINT8:
     260             :         case XSYBCHAR:
     261             :         case XSYBVARCHAR:
     262             :         case XSYBNVARCHAR:
     263             :         case XSYBNCHAR:
     264             :         case XSYBVARBINARY:
     265             :         case XSYBBINARY:
     266             :         case SYBUNIQUE:
     267             :         case SYBVARIANT:
     268             :         case SYBMSUDT:
     269             :         case SYBMSXML:
     270             :         case SYBMSDATE:
     271             :         case SYBMSTIME:
     272             :         case SYBMSDATETIME2:
     273             :         case SYBMSDATETIMEOFFSET:
     274             :         case SYBLONGBINARY:
     275             :         case SYBUINT8:
     276             :         case SYBDATE:
     277             :         case SYBDATEN:
     278             :         case SYB5INT8:
     279             :         case SYBINTERVAL:
     280             :         case SYBTIME:
     281             :         case SYBTIMEN:
     282             :         case SYBUINTN:
     283             :         case SYBUNITEXT:
     284             :         case SYBXML:
     285             :         case SYB5BIGDATETIME:
     286             :         case SYB5BIGTIME:
     287           0 :                 assert( false && pcol->type );
     288             :                 break;
     289             :         }
     290             :         return NULL;
     291             : 
     292             : }
     293             : 
     294             : #if 0
     295             : static int
     296             : col_print(FILE* out, const struct col_t *pcol) 
     297             : {
     298             :         char *fmt;
     299             :         
     300             :         switch (pcol->type) {
     301             :         
     302             :         case SYBCHAR:
     303             :         case SYBVARCHAR:
     304             :                 return (int) fwrite(pcol->s, pcol->len, 1, out);
     305             :         case SYBINT1:
     306             :                 return fprintf(out, "%d", (int)pcol->ti);
     307             :         case SYBINT2:
     308             :                 return fprintf(out, "%d", (int)pcol->si);
     309             :         case SYBINT4:
     310             :                 return fprintf(out, "%d", (int)pcol->i);
     311             :         case SYBFLT8:
     312             :                 return fprintf(out, "%f",      pcol->f);
     313             :         case SYBREAL:
     314             :                 return fprintf(out, "%f", (double)pcol->r);
     315             : 
     316             :         case SYBINTN:
     317             :         case SYBDATETIME:
     318             :         case SYBBIT:
     319             :         case SYBTEXT:
     320             :         case SYBNTEXT:
     321             :         case SYBIMAGE:
     322             :         case SYBMONEY4:
     323             :         case SYBMONEY:
     324             :         case SYBDATETIME4:
     325             :         case SYBBINARY:
     326             :         case SYBVOID:
     327             :         case SYBVARBINARY:
     328             :         case SYBBITN:
     329             :         case SYBNUMERIC:
     330             :         case SYBDECIMAL:
     331             :         case SYBFLTN:
     332             :         case SYBMONEYN:
     333             :         case SYBDATETIMN:
     334             :                 assert( false && pcol->type );
     335             :                 break;
     336             :         }
     337             :         return false;
     338             : }
     339             : #endif
     340             : static struct col_t *
     341           0 : col_cpy(struct col_t *pdest, const struct col_t *psrc)
     342             : {
     343           0 :         assert( pdest && psrc );
     344           0 :         assert( psrc->len > 0 || psrc->null_indicator == -1);
     345             :         
     346           0 :         memcpy(pdest, psrc, sizeof(*pdest));
     347             :         
     348           0 :         if (psrc->s) {
     349             :                 assert(psrc->len >= 0);
     350           0 :                 if ((pdest->s = tds_new(char, psrc->len)) == NULL)
     351             :                         return NULL;
     352           0 :                 memcpy(pdest->s, psrc->s, psrc->len);
     353             :         }
     354             :         
     355           0 :         assert( pdest->len > 0 || pdest->null_indicator == -1);
     356             :         return pdest;
     357             : }
     358             : 
     359             : static bool
     360             : col_null( const struct col_t *pcol )
     361             : {
     362             :         assert(pcol);
     363           0 :         return pcol->null_indicator == -1;
     364             : } 
     365             : 
     366             : static char *
     367           0 : string_value(const struct col_t *pcol)
     368             : {
     369           0 :         char *output = NULL;
     370           0 :         int len = -1;
     371             : 
     372           0 :         switch (pcol->type) {
     373           0 :         case SYBCHAR:
     374             :         case SYBVARCHAR:
     375           0 :                 if ((output = tds_new0(char, 1 + pcol->len)) == NULL)
     376             :                         return NULL;
     377           0 :                 strncpy(output, pcol->s, pcol->len);
     378           0 :                 return output;
     379             :                 break;
     380           0 :         case SYBINT1:
     381           0 :                 len = asprintf(&output, "%d", (int)pcol->data.ti);
     382           0 :                 break;
     383           0 :         case SYBINT2:
     384           0 :                 len = asprintf(&output, "%d", (int)pcol->data.si);
     385           0 :                 break;
     386           0 :         case SYBINT4:
     387           0 :                 len = asprintf(&output, "%d", (int)pcol->data.i);
     388           0 :                 break;
     389           0 :         case SYBFLT8:
     390           0 :                 len = asprintf(&output, "%f", pcol->data.f);
     391           0 :                 break;
     392           0 :         case SYBREAL:
     393           0 :                 len = asprintf(&output, "%f", (double)pcol->data.r);
     394           0 :                 break;
     395             : 
     396             :         default:
     397             :         case SYBINTN:
     398             :         case SYBDATETIME:
     399             :         case SYBBIT:
     400             :         case SYBTEXT:
     401             :         case SYBNTEXT:
     402             :         case SYBIMAGE:
     403             :         case SYBMONEY4:
     404             :         case SYBMONEY:
     405             :         case SYBDATETIME4:
     406             :         case SYBBINARY:
     407             :         case SYBVOID:
     408             :         case SYBVARBINARY:
     409             :         case SYBBITN:
     410             :         case SYBNUMERIC:
     411             :         case SYBDECIMAL:
     412             :         case SYBFLTN:
     413             :         case SYBMONEYN:
     414             :         case SYBDATETIMN:
     415           0 :                 assert( false && pcol->type );
     416             :                 return NULL;
     417             :                 break;
     418             :         }
     419             : 
     420           0 :         return len >= 0? output : NULL;
     421             : }
     422             : 
     423             : static char *
     424           0 : join(int argc, char *argv[], const char sep[])
     425             : {
     426           0 :         size_t len = 0;
     427             :         char **p, *output;
     428             :                 
     429           0 :         for (p=argv; p < argv + argc; p++) {
     430           0 :                 len += strlen(*p);
     431             :         }
     432             :         
     433           0 :         len += 1 + argc * strlen(sep); /* allows one too many */ 
     434             :         
     435           0 :         output = tds_new0(char, len);
     436           0 :         if (!output)
     437             :                 return NULL;
     438             :         
     439           0 :         for (p=argv; p < argv + argc; p++) {
     440           0 :                 if (p != argv)
     441           0 :                         strcat(output, sep);
     442           0 :                 strcat(output, *p);
     443             :         }
     444             :         return output;
     445             : }
     446             : 
     447             : static TDS_SERVER_TYPE
     448           0 : infer_col_type(int sybtype) 
     449             : {
     450           0 :         switch (sybtype) {
     451             :         case SYBCHAR:
     452             :         case SYBVARCHAR:
     453             :         case SYBTEXT:
     454             :         case SYBNTEXT:
     455             :                 return SYBCHAR;
     456             :         case SYBDATETIME:
     457             :         case SYBDATETIME4:
     458             :         case SYBDATETIMN:
     459             :                 return SYBCHAR;
     460           0 :         case SYBINT1:
     461             :         case SYBBIT:
     462             :         case SYBBITN:
     463           0 :                 return SYBINT1;
     464           0 :         case SYBINT2:
     465           0 :                 return SYBINT2;
     466           0 :         case SYBINT4:
     467             :         case SYBINTN:
     468           0 :                 return SYBINT4;
     469           0 :         case SYBFLT8:
     470             :         case SYBMONEY4:
     471             :         case SYBMONEY:
     472             :         case SYBFLTN:
     473             :         case SYBMONEYN:
     474             :         case SYBNUMERIC:
     475             :         case SYBDECIMAL:
     476           0 :                 return SYBFLT8;
     477           0 :         case SYBREAL:
     478           0 :                 return SYBREAL;
     479             : 
     480             :         case SYBIMAGE:
     481             :         case SYBBINARY:
     482             :         case SYBVOID:
     483             :         case SYBVARBINARY:
     484           0 :                 assert( false && sybtype );
     485             :                 break;
     486             :         }
     487           0 :         return TDS_INVALID_TYPE;
     488             : }
     489             : 
     490             : static int
     491           0 : bind_type(int sybtype)
     492             : {
     493           0 :         switch (sybtype) {
     494             :         case SYBCHAR:
     495             :         case SYBVARCHAR:
     496             :         case SYBTEXT:
     497             :         case SYBNTEXT:
     498             :         case SYBDATETIME:
     499             :         case SYBDATETIME4:
     500             :         case SYBDATETIMN:
     501             :                 return NTBSTRINGBIND;
     502           0 :         case SYBINT1:
     503             :         case SYBBIT:
     504             :         case SYBBITN:
     505           0 :                 return TINYBIND;
     506           0 :         case SYBINT2:
     507           0 :                 return SMALLBIND;
     508           0 :         case SYBINT4:
     509             :         case SYBINTN:
     510           0 :                 return INTBIND;
     511           0 :         case SYBFLT8:
     512             :         case SYBMONEY4:
     513             :         case SYBMONEY:
     514             :         case SYBFLTN:
     515             :         case SYBMONEYN:
     516             :         case SYBNUMERIC:
     517             :         case SYBDECIMAL:
     518           0 :                 return FLT8BIND;
     519           0 :         case SYBREAL:
     520           0 :                 return REALBIND;
     521             : 
     522             :         case SYBIMAGE:
     523             :         case SYBBINARY:
     524             :         case SYBVOID:
     525             :         case SYBVARBINARY:
     526           0 :                 assert( false && sybtype );
     527             :                 break;
     528             :         }
     529           0 :         return 0;
     530             : }
     531             : 
     532             : typedef struct KEY_T
     533             : {
     534             :         int nkeys;
     535             :         struct col_t *keys;
     536             : } KEY_T;
     537             : 
     538             : static bool
     539           0 : key_equal(const KEY_T *a, const KEY_T *b)
     540             : {
     541             :         int i;
     542             :         
     543           0 :         assert(a && b);
     544           0 :         assert(a->keys && b->keys);
     545           0 :         assert(a->nkeys == b->nkeys);
     546             :         
     547           0 :         for (i=0; i < a->nkeys; i++) {
     548           0 :                 if (! col_equal(a->keys+i, b->keys+i))
     549             :                         return false;
     550             :         }
     551             :         return true;
     552             : }
     553             : 
     554             : 
     555             : static void
     556           0 : key_free(KEY_T *p)
     557             : {
     558           0 :         col_free(p->keys);
     559           0 :         free(p->keys);
     560           0 :         memset(p, 0, sizeof(*p));
     561           0 : }
     562             : 
     563             : static KEY_T *
     564           0 : key_cpy(KEY_T *pdest, const KEY_T *psrc)
     565             : {
     566             :         int i;
     567             :         
     568           0 :         assert( pdest && psrc );
     569             :         
     570           0 :         if ((pdest->keys = tds_new0(struct col_t, psrc->nkeys)) == NULL)
     571             :                 return NULL;
     572             : 
     573           0 :         pdest->nkeys = psrc->nkeys;
     574             :         
     575           0 :         for( i=0; i < psrc->nkeys; i++) {
     576           0 :                 if (NULL == col_cpy(pdest->keys+i, psrc->keys+i))
     577             :                         return NULL;
     578             :         }
     579             : 
     580             :         return pdest;
     581             : }
     582             : 
     583             : 
     584             : static char *
     585           0 : make_col_name(DBPROCESS *dbproc, const KEY_T *k)
     586             : {
     587             :         const struct col_t *pc;
     588             :         char **names, **s, *output;
     589             :         
     590           0 :         assert(k);
     591           0 :         assert(k->nkeys);
     592           0 :         assert(k->keys);
     593             :         
     594           0 :         s = names = tds_new0(char *, k->nkeys);
     595           0 :         if (!s) {
     596           0 :                 dbperror(dbproc, SYBEMEM, errno);
     597           0 :                 return NULL;
     598             :         }
     599           0 :         for(pc=k->keys; pc < k->keys + k->nkeys; pc++) {
     600           0 :                 *s++ = string_value(pc);
     601             :         }
     602             :         
     603           0 :         output = join(k->nkeys, names, "/");
     604             :         
     605           0 :         for(s=names; s < names + k->nkeys; s++) {
     606           0 :                 free(*s);
     607             :         }
     608           0 :         free(names);
     609             :         
     610           0 :         return output;
     611             : }
     612             :         
     613             : 
     614             : typedef struct agg_t
     615             : {
     616             :         KEY_T row_key, col_key;
     617             :         struct col_t value;
     618             : } AGG_T;
     619             : 
     620             : #if 0
     621             : static bool
     622             : agg_key_equal(const void *a, const void *b)
     623             : {
     624             :         int i;
     625             :         const AGG_T *p1 = a, *p2 = b;
     626             :         
     627             :         assert(p1 && p2);
     628             :         assert(p1->row_key.keys  && p2->row_key.keys);
     629             :         assert(p1->row_key.nkeys == p2->row_key.nkeys);
     630             :         
     631             :         for( i=0; i < p1->row_key.nkeys; i++ ) {
     632             :                 if (! col_equal(p1->row_key.keys+i, p2->row_key.keys+i))
     633             :                         return false;
     634             :         }
     635             : 
     636             :         return true;
     637             : }
     638             : #endif
     639             : 
     640             : static bool
     641           0 : agg_next(const AGG_T *p1, const AGG_T *p2)
     642             : {
     643             :         int i;
     644             : 
     645           0 :         assert(p1 && p2);
     646             :         
     647           0 :         if (p1->row_key.keys == NULL || p2->row_key.keys == NULL)
     648             :                 return false;
     649             :         
     650             :         assert(p1->row_key.keys  && p2->row_key.keys);
     651           0 :         assert(p1->row_key.nkeys == p2->row_key.nkeys);
     652             :         
     653           0 :         assert(p1->col_key.keys  && p2->col_key.keys);
     654           0 :         assert(p1->col_key.nkeys == p2->col_key.nkeys);
     655             :         
     656           0 :         for( i=0; i < p1->row_key.nkeys; i++ ) {
     657           0 :                 assert(p1->row_key.keys[i].type);
     658           0 :                 assert(p2->row_key.keys[i].type);
     659           0 :                 if (p1->row_key.keys[i].type != p2->row_key.keys[i].type)
     660             :                         return false;
     661             :         }
     662             : 
     663           0 :         for( i=0; i < p1->row_key.nkeys; i++ ) {
     664           0 :                 if (! col_equal(p1->row_key.keys+i, p2->row_key.keys+i))
     665             :                         return false;
     666             :         }
     667             : 
     668           0 :         for( i=0; i < p1->col_key.nkeys; i++ ) {
     669           0 :                 if (p1->col_key.keys[i].type != p2->col_key.keys[i].type)
     670             :                         return false;
     671             :         }
     672             : 
     673           0 :         for( i=0; i < p1->col_key.nkeys; i++ ) {
     674           0 :                 if (! col_equal(p1->col_key.keys+i, p2->col_key.keys+i))
     675             :                         return false;
     676             :         }
     677             : 
     678             :         return true;
     679             : }
     680             : 
     681             : static void
     682           0 : agg_free(AGG_T *p)
     683             : {
     684           0 :         key_free(&p->row_key);
     685           0 :         key_free(&p->col_key);
     686           0 :         col_free(&p->value);
     687           0 : }
     688             : 
     689             : static bool
     690           0 : agg_equal(const AGG_T *p1, const AGG_T *p2)
     691             : {
     692             :         int i;
     693             :         
     694           0 :         assert(p1 && p2);
     695           0 :         assert(p1->row_key.keys && p1->col_key.keys);
     696           0 :         assert(p2->row_key.keys && p2->col_key.keys);
     697             : 
     698           0 :         assert(p1->row_key.nkeys == p2->row_key.nkeys);
     699           0 :         assert(p1->col_key.nkeys == p2->col_key.nkeys);
     700             :         
     701             :         /* todo: use key_equal */
     702           0 :         for( i=0; i < p1->row_key.nkeys; i++ ) {
     703           0 :                 if (! col_equal(p1->row_key.keys+i, p2->row_key.keys+i))
     704             :                         return false;
     705             :         }
     706           0 :         for( i=0; i < p1->col_key.nkeys; i++ ) {
     707           0 :                 if (! col_equal(p1->col_key.keys+i, p2->col_key.keys+i))
     708             :                         return false;
     709             :         }
     710             :         return true;
     711             : }
     712             : 
     713             : #undef TEST_MALLOC
     714             : #define TEST_MALLOC(dest,type) \
     715             :         {if (!(dest = (type*)calloc(1, sizeof(type)))) goto Cleanup;}
     716             : 
     717             : #undef TEST_CALLOC
     718             : #define TEST_CALLOC(dest,type,n) \
     719             :         {if (!(dest = (type*)calloc((n), sizeof(type)))) goto Cleanup;}
     720             : 
     721             : #define tds_alloc_column() ((TDSCOLUMN*) calloc(1, sizeof(TDSCOLUMN)))
     722             : 
     723             : static TDSRESULTINFO *
     724           0 : alloc_results(size_t num_cols)
     725             : {
     726             :         TDSRESULTINFO *res_info;
     727             :         TDSCOLUMN **ppcol;
     728             : 
     729           0 :         TEST_MALLOC(res_info, TDSRESULTINFO);
     730           0 :         res_info->ref_count = 1;
     731           0 :         TEST_CALLOC(res_info->columns, TDSCOLUMN *, num_cols);
     732             :         
     733           0 :         for (ppcol = res_info->columns; ppcol < res_info->columns + num_cols; ppcol++)
     734           0 :                 if ((*ppcol = tds_alloc_column()) == NULL)
     735             :                         goto Cleanup;
     736           0 :         res_info->num_cols = num_cols;
     737           0 :         res_info->row_size = 0;
     738           0 :         return res_info;
     739             : 
     740           0 :       Cleanup:
     741           0 :         tds_free_results(res_info);
     742           0 :         return NULL;
     743             : }
     744             : 
     745             : static TDSRET
     746           0 : set_result_column(TDSSOCKET * tds, TDSCOLUMN * curcol, const char name[], const struct col_t *pvalue)
     747             : {
     748           0 :         assert(curcol && pvalue);
     749           0 :         assert(name);
     750             : 
     751           0 :         curcol->column_usertype = pvalue->type;
     752           0 :         curcol->column_nullable = true;
     753           0 :         curcol->column_writeable = false;
     754           0 :         curcol->column_identity = false;
     755             : 
     756           0 :         tds_set_column_type(tds->conn, curcol, pvalue->type);     /* sets "cardinal" type */
     757             : 
     758           0 :         curcol->column_timestamp = (curcol->column_type == SYBBINARY && curcol->column_usertype == TDS_UT_TIMESTAMP);
     759             : 
     760             : #if 0
     761             :         curcol->funcs->get_info(tds, curcol);
     762             : #endif
     763           0 :         curcol->on_server.column_size = curcol->column_size;
     764             : 
     765           0 :         if (!tds_dstr_copy(&curcol->column_name, name))
     766             :                 return TDS_FAIL;
     767             : 
     768           0 :         tdsdump_log(TDS_DBG_INFO1, "tds7_get_data_info: \n"
     769             :                     "\tcolname = %s\n"
     770             :                     "\ttype = %d (%s)\n"
     771             :                     "\tserver's type = %d (%s)\n"
     772             :                     "\tcolumn_varint_size = %d\n"
     773             :                     "\tcolumn_size = %d (%d on server)\n",
     774           0 :                     tds_dstr_cstr(&curcol->column_name),
     775           0 :                     curcol->column_type, tds_prtype(curcol->column_type), 
     776           0 :                     curcol->on_server.column_type, tds_prtype(curcol->on_server.column_type), 
     777           0 :                     curcol->column_varint_size,
     778             :                     curcol->column_size, curcol->on_server.column_size);
     779             : 
     780             :         return TDS_SUCCESS;
     781             : }
     782             : 
     783             : struct metadata_t { KEY_T *pacross; char *name; struct col_t col; };
     784             : 
     785             : 
     786             : static bool
     787           0 : reinit_results(TDSSOCKET * tds, size_t num_cols, const struct metadata_t meta[])
     788             : {
     789             :         TDSRESULTINFO *info;
     790             :         int i;
     791             : 
     792           0 :         assert(tds);
     793           0 :         assert(num_cols);
     794           0 :         assert(meta);
     795             :         
     796           0 :         tds_free_all_results(tds);
     797           0 :         tds->rows_affected = TDS_NO_COUNT;
     798             : 
     799           0 :         if ((info = alloc_results(num_cols)) == NULL)
     800             :                 return false;
     801             : 
     802           0 :         tds_set_current_results(tds, info);
     803           0 :         if (tds->cur_cursor) {
     804           0 :                 tds_free_results(tds->cur_cursor->res_info);
     805           0 :                 tds->cur_cursor->res_info = info;
     806           0 :                 tdsdump_log(TDS_DBG_INFO1, "set current_results to cursor->res_info\n");
     807             :         } else {
     808           0 :                 tds->res_info = info;
     809           0 :                 tdsdump_log(TDS_DBG_INFO1, "set current_results (%u column%s) to tds->res_info\n", (unsigned) num_cols, (num_cols==1? "":"s"));
     810             :         }
     811             : 
     812           0 :         tdsdump_log(TDS_DBG_INFO1, "setting up %u columns\n", (unsigned) num_cols);
     813             :         
     814           0 :         for (i = 0; i < num_cols; i++) {
     815           0 :                 set_result_column(tds, info->columns[i], meta[i].name, &meta[i].col);
     816           0 :                 info->columns[i]->bcp_terminator = (char*) meta[i].pacross;       /* overload available pointer */
     817             :         }
     818             :                 
     819             :         if (num_cols > 0) {
     820             :                 static const char dashes[31] = "------------------------------";
     821           0 :                 tdsdump_log(TDS_DBG_INFO1, " %-20s %-15s %-15s %-7s\n", "name", "size/wsize", "type/wtype", "utype");
     822           0 :                 tdsdump_log(TDS_DBG_INFO1, " %-20s %15s %15s %7s\n", dashes+10, dashes+30-15, dashes+30-15, dashes+30-7);
     823             :         }
     824           0 :         for (i = 0; i < num_cols; i++) {
     825           0 :                 TDSCOLUMN *curcol = info->columns[i];
     826             : 
     827           0 :                 tdsdump_log(TDS_DBG_INFO1, " %-20s %7d/%-7d %7d/%-7d %7d\n", 
     828           0 :                                                 tds_dstr_cstr(&curcol->column_name),
     829             :                                                 curcol->column_size, curcol->on_server.column_size, 
     830           0 :                                                 curcol->column_type, curcol->on_server.column_type, 
     831             :                                                 curcol->column_usertype);
     832             :         }
     833             : 
     834             : #if 1
     835             :         /* all done now allocate a row for tds_process_row to use */
     836           0 :         if (TDS_FAILED(tds_alloc_row(info))) return false;
     837             : #endif
     838           0 :         return true;
     839             : }
     840             : 
     841             : typedef struct pivot_t
     842             : {
     843             :         DBPROCESS *dbproc;
     844             :         STATUS status;
     845             :         DB_RESULT_STATE dbresults_state;
     846             :         
     847             :         AGG_T *output;
     848             :         KEY_T *across;
     849             :         size_t nout, nacross;
     850             : } PIVOT_T;
     851             : 
     852             : static bool
     853           0 : pivot_key_equal(const PIVOT_T *a, const PIVOT_T *b)
     854             : {
     855           0 :         assert(a && b);
     856             :         
     857           0 :         return a->dbproc == b->dbproc;
     858             : }
     859             : 
     860             : static PIVOT_T *pivots = NULL;
     861             : static size_t npivots = 0;
     862             : 
     863             : PIVOT_T *
     864      175551 : dbrows_pivoted(DBPROCESS *dbproc)
     865             : {
     866             :         PIVOT_T P;
     867             : 
     868      175551 :         assert(dbproc);
     869      175551 :         P.dbproc = dbproc;
     870             :         
     871      351102 :         return (PIVOT_T *) tds_find(&P, pivots, npivots, sizeof(*pivots), (compare_func) pivot_key_equal);
     872             : }
     873             : 
     874             : STATUS
     875           0 : dbnextrow_pivoted(DBPROCESS *dbproc, PIVOT_T *pp)
     876             : {
     877             :         int i;
     878             :         AGG_T candidate, *pout;
     879             : 
     880           0 :         assert(pp);
     881           0 :         assert(dbproc && dbproc->tds_socket);
     882           0 :         assert(dbproc->tds_socket->res_info);
     883           0 :         assert(dbproc->tds_socket->res_info->columns || 0 == dbproc->tds_socket->res_info->num_cols);
     884             :         
     885           0 :         for (pout = pp->output; pout < pp->output + pp->nout; pout++) {
     886           0 :                 if (pout->row_key.keys != NULL)
     887             :                         break;
     888             :         }
     889             : 
     890           0 :         if (pout == pp->output + pp->nout) {
     891           0 :                 dbproc->dbresults_state = _DB_RES_NEXT_RESULT;
     892           0 :                 return NO_MORE_ROWS;
     893             :         }
     894             : 
     895           0 :         memset(&candidate, 0, sizeof(candidate));
     896           0 :         key_cpy(&candidate.row_key, &pout->row_key);
     897             :         
     898             :         /* "buffer_transfer_bound_data" */
     899           0 :         for (i = 0; i < dbproc->tds_socket->res_info->num_cols; i++) {
     900           0 :                 struct col_t *pval = NULL;
     901           0 :                 TDSCOLUMN *pcol = dbproc->tds_socket->res_info->columns[i];
     902           0 :                 assert(pcol);
     903             :                 
     904           0 :                 if (pcol->column_nullbind) {
     905           0 :                         if (pcol->column_cur_size < 0) {
     906           0 :                                 *(DBINT *)(pcol->column_nullbind) = -1;
     907             :                         } else {
     908           0 :                                 *(DBINT *)(pcol->column_nullbind) = 0;
     909             :                         }
     910             :                 }
     911           0 :                 if (!pcol->column_varaddr) {
     912           0 :                         tdsdump_log(TDS_DBG_ERROR, "no pcol->column_varaddr in col %d\n", i);
     913           0 :                         continue;
     914             :                 }
     915             : 
     916             :                 /* find column in output */
     917           0 :                 if (pcol->bcp_terminator == NULL) { /* not a cross-tab column */
     918           0 :                         pval = &candidate.row_key.keys[i];
     919             :                 } else {
     920             :                         AGG_T *pcan;
     921           0 :                         key_cpy(&candidate.col_key, (KEY_T *) pcol->bcp_terminator);
     922           0 :                         if ((pcan = tds_find(&candidate, pout, pp->output + pp->nout - pout, 
     923             :                                                 sizeof(*pp->output), (compare_func) agg_next)) != NULL) {
     924             :                                 /* flag this output as used */
     925           0 :                                 pout->row_key.keys = NULL;
     926           0 :                                 pval = &pcan->value;
     927             :                         }
     928             :                 }
     929             :                 
     930           0 :                 if (!pval || col_null(pval)) {  /* nothing in output for this x,y location */
     931           0 :                         dbgetnull(dbproc, pcol->column_bindtype, pcol->column_bindlen, (BYTE *) pcol->column_varaddr);
     932           0 :                         continue;
     933             :                 }
     934             :                 
     935             :                 assert(pval);
     936             :                 
     937           0 :                 pcol->column_size = pval->len;
     938           0 :                 pcol->column_data = col_buffer(pval);
     939             :                 
     940           0 :                 copy_data_to_host_var(  dbproc, 
     941             :                                         pval->type, 
     942           0 :                                         col_buffer(pval), 
     943             :                                         pval->len, 
     944           0 :                                         (BYTE *) pcol->column_varaddr,  
     945             :                                         pcol->column_bindlen,
     946           0 :                                         pcol->column_bindtype, 
     947           0 :                                         (DBINT*) pcol->column_nullbind
     948             :                                         );
     949             :         }
     950             : 
     951             :         return REG_ROW;
     952             : }
     953             : 
     954             : /** 
     955             :  * Pivot the rows, creating a new resultset
     956             :  *
     957             :  * Call dbpivot() immediately after dbresults().  It calls dbnextrow() as long as
     958             :  * it returns REG_ROW, transforming the results into a cross-tab report.  
     959             :  * dbpivot() modifies the metadata such that DB-Library can be used tranparently: 
     960             :  * retrieve the rows as usual with dbnumcols(), dbnextrow(), etc. 
     961             :  *
     962             :  * @dbproc, our old friend
     963             :  * @nkeys the number of left-edge columns to group by
     964             :  * @keys  an array of left-edge columns to group by
     965             :  * @ncols the number of top-edge columns to group by
     966             :  * @cols  an array of top-edge columns to group by
     967             :  * @func  the aggregation function to use
     968             :  * @val   the number of the column to which @func is applied
     969             :  *
     970             :  * @returns the return code from the final call to dbnextrow().  
     971             :  *  Success is normally indicated by NO_MORE_ROWS.  
     972             :  */
     973             : RETCODE
     974           0 : dbpivot(DBPROCESS *dbproc, int nkeys, int *keys, int ncols, int *cols, DBPIVOT_FUNC func, int val)
     975             : {
     976             :         enum { logalot = 1 };
     977             :         PIVOT_T P, *pp;
     978           0 :         AGG_T input, *pout = NULL;
     979             :         struct metadata_t *metadata, *pmeta;
     980           0 :         size_t i, nmeta = 0;
     981             : 
     982           0 :         tdsdump_log(TDS_DBG_FUNC, "dbpivot(%p, %d,%p, %d,%p, %p, %d)\n", dbproc, nkeys, keys, ncols, cols, func, val);
     983             :         if (logalot) {
     984           0 :                 char buffer[1024] = {'\0'}, *s = buffer;
     985             :                 static const char *const names[2] = { "\tkeys (down)", "\n\tcols (across)" };
     986           0 :                 int *p = keys, *pend = p + nkeys;
     987             :                 
     988           0 :                 for (i=0; i < 2; i++) {
     989           0 :                         const char *sep = "";
     990           0 :                         s += sprintf(s, "%s: ", names[i]);
     991           0 :                         for ( ; p < pend; p++) {
     992           0 :                                 s += sprintf(s, "%s%d", sep, *p);
     993           0 :                                 sep = ", ";
     994             :                         }
     995           0 :                         p = cols;
     996           0 :                         pend = p + ncols;
     997           0 :                         assert(s < buffer + sizeof(buffer));
     998             :                 }
     999           0 :                 tdsdump_log(TDS_DBG_FUNC, "%s\n", buffer);
    1000             :         }
    1001             :         
    1002           0 :         memset(&input,  0, sizeof(input));
    1003             :         
    1004           0 :         P.dbproc = dbproc;
    1005           0 :         if ((pp = tds_find(&P, pivots, npivots, sizeof(*pivots), (compare_func) pivot_key_equal)) == NULL ) {
    1006           0 :                 pp = TDS_RESIZE(pivots, 1 + npivots);
    1007           0 :                 if (!pp)
    1008             :                         return FAIL;
    1009           0 :                 pp += npivots++;
    1010             :         } else {
    1011           0 :                 agg_free(pp->output);
    1012           0 :                 key_free(pp->across);                
    1013             :         }
    1014           0 :         memset(pp, 0, sizeof(*pp));
    1015             : 
    1016           0 :         if ((input.row_key.keys = tds_new0(struct col_t, nkeys)) == NULL)
    1017             :                 return FAIL;
    1018           0 :         input.row_key.nkeys = nkeys;
    1019           0 :         for (i=0; i < nkeys; i++) {
    1020           0 :                 int type = dbcoltype(dbproc, keys[i]);
    1021           0 :                 int len = dbcollen(dbproc, keys[i]);
    1022           0 :                 assert(type && len);
    1023             :                 
    1024           0 :                 if (!col_init(input.row_key.keys+i, type, len))
    1025             :                         return FAIL;
    1026           0 :                 if (FAIL == dbbind(dbproc, keys[i], bind_type(type), input.row_key.keys[i].len, col_buffer(input.row_key.keys+i)))
    1027             :                         return FAIL;
    1028           0 :                 if (FAIL == dbnullbind(dbproc, keys[i], &input.row_key.keys[i].null_indicator))
    1029             :                         return FAIL;
    1030             :         }
    1031             :         
    1032           0 :         if ((input.col_key.keys = tds_new0(struct col_t, ncols)) == NULL)
    1033             :                 return FAIL;
    1034           0 :         input.col_key.nkeys = ncols;
    1035           0 :         for (i=0; i < ncols; i++) {
    1036           0 :                 int type = dbcoltype(dbproc, cols[i]);
    1037           0 :                 int len = dbcollen(dbproc, cols[i]);
    1038           0 :                 assert(type && len);
    1039             :                 
    1040           0 :                 if (!col_init(input.col_key.keys+i, type, len))
    1041             :                         return FAIL;
    1042           0 :                 if (FAIL == dbbind(dbproc, cols[i], bind_type(type), input.col_key.keys[i].len, col_buffer(input.col_key.keys+i)))
    1043             :                         return FAIL;
    1044           0 :                 if (FAIL == dbnullbind(dbproc, cols[i], &input.col_key.keys[i].null_indicator))
    1045             :                         return FAIL;
    1046             :         }
    1047             :         
    1048             :         /* value */ {
    1049           0 :                 int type = dbcoltype(dbproc, val);
    1050           0 :                 int len = dbcollen(dbproc, val);
    1051           0 :                 assert(type && len);
    1052             :                 
    1053           0 :                 if (!col_init(&input.value, type, len))
    1054             :                         return FAIL;
    1055           0 :                 if (FAIL == dbbind(dbproc, val, bind_type(type), input.value.len, col_buffer(&input.value)))
    1056             :                         return FAIL;
    1057           0 :                 if (FAIL == dbnullbind(dbproc, val, &input.value.null_indicator))
    1058             :                         return FAIL;
    1059             :         }
    1060             :         
    1061           0 :         while ((pp->status = dbnextrow(dbproc)) == REG_ROW) {
    1062             :                 /* add to unique list of crosstab columns */
    1063           0 :                 if (tds_find(&input.col_key, pp->across, pp->nacross, sizeof(*pp->across), (compare_func) key_equal) == NULL) {
    1064           0 :                         if (!TDS_RESIZE(pp->across, 1 + pp->nacross))
    1065             :                                 return FAIL;
    1066           0 :                         key_cpy(pp->across + pp->nacross, &input.col_key);
    1067             :                 }
    1068           0 :                 assert(pp->across);
    1069             :                 
    1070           0 :                 if ((pout = tds_find(&input, pp->output, pp->nout, sizeof(*pp->output), (compare_func) agg_equal)) == NULL ) {
    1071           0 :                         if (!TDS_RESIZE(pp->output, 1 + pp->nout))
    1072             :                                 return FAIL;
    1073           0 :                         pout = pp->output + pp->nout++;
    1074             : 
    1075             :                         
    1076           0 :                         if ((pout->row_key.keys = tds_new0(struct col_t, input.row_key.nkeys)) == NULL)
    1077             :                                 return FAIL;
    1078           0 :                         key_cpy(&pout->row_key, &input.row_key);
    1079             : 
    1080           0 :                         if ((pout->col_key.keys = tds_new0(struct col_t, input.col_key.nkeys)) == NULL)
    1081             :                                 return FAIL;
    1082           0 :                         key_cpy(&pout->col_key, &input.col_key);
    1083             : 
    1084           0 :                         if (!col_init(&pout->value, input.value.type, input.value.len))
    1085             :                                 return FAIL;
    1086             :                 }
    1087             :                 
    1088           0 :                 func(&pout->value, &input.value);
    1089             : 
    1090             :         }
    1091             : 
    1092             :         /* Mark this proc as pivoted, so that dbnextrow() sees it when the application calls it */
    1093           0 :         pp->dbproc = dbproc;
    1094           0 :         pp->dbresults_state = dbproc->dbresults_state;
    1095           0 :         dbproc->dbresults_state = pp->output < pout? _DB_RES_RESULTSET_ROWS : _DB_RES_RESULTSET_EMPTY;
    1096             :         
    1097             :         /*
    1098             :          * Initialize new metadata
    1099             :          */
    1100           0 :         nmeta = input.row_key.nkeys + pp->nacross;   
    1101           0 :         metadata = tds_new0(struct metadata_t, nmeta);
    1102           0 :         if (!metadata) {
    1103           0 :                 dbperror(dbproc, SYBEMEM, errno);
    1104           0 :                 return FAIL;
    1105             :         }
    1106           0 :         assert(pp->across || pp->nacross == 0);
    1107             :         
    1108             :         /* key columns are passed through as-is, verbatim */
    1109           0 :         for (i=0; i < input.row_key.nkeys; i++) {
    1110           0 :                 assert(i < nkeys);
    1111           0 :                 metadata[i].name = strdup(dbcolname(dbproc, keys[i]));
    1112           0 :                 metadata[i].pacross = NULL;
    1113           0 :                 col_cpy(&metadata[i].col, input.row_key.keys+i);
    1114             :         }
    1115             : 
    1116             :         /* pivoted columms are found in the "across" data */
    1117           0 :         for (i=0, pmeta = metadata + input.row_key.nkeys; i < pp->nacross; i++) {
    1118             :                 struct col_t col;
    1119           0 :                 if (!col_init(&col, SYBFLT8, sizeof(double)))
    1120           0 :                         return FAIL;
    1121           0 :                 assert(pmeta + i < metadata + nmeta);
    1122           0 :                 pmeta[i].name = make_col_name(dbproc, pp->across+i);
    1123           0 :                 if (!pmeta[i].name)
    1124             :                         return FAIL;
    1125           0 :                 assert(pp->across);
    1126           0 :                 pmeta[i].pacross = pp->across + i;
    1127           0 :                 col_cpy(&pmeta[i].col, pp->nout? &pp->output[0].value : &col);
    1128             :         }
    1129             : 
    1130           0 :         if (!reinit_results(dbproc->tds_socket, nmeta, metadata)) {
    1131             :                 return FAIL;
    1132             :         }
    1133             :         
    1134           0 :         return SUCCEED;
    1135             : }
    1136             : 
    1137             : /* 
    1138             :  * Aggregation functions 
    1139             :  */
    1140             : 
    1141             : void
    1142           0 : dbpivot_count (struct col_t *tgt, const struct col_t *src)
    1143             : {
    1144           0 :         assert( tgt && src);
    1145           0 :         assert (src->type);
    1146             :         
    1147           0 :         tgt->type = SYBINT4;
    1148             :         
    1149           0 :         if (! col_null(src))
    1150           0 :                 tgt->data.i++;
    1151           0 : }
    1152             : 
    1153             : void
    1154           0 : dbpivot_sum (struct col_t *tgt, const struct col_t *src)
    1155             : {
    1156           0 :         assert( tgt && src);
    1157           0 :         assert (src->type);
    1158             :         
    1159           0 :         tgt->type = src->type;
    1160             :         
    1161           0 :         if (col_null(src))
    1162             :                 return;
    1163             : 
    1164           0 :         switch (src->type) {
    1165           0 :         case SYBINT1:
    1166           0 :                 tgt->data.ti += src->data.ti;
    1167           0 :                 break;
    1168           0 :         case SYBINT2:
    1169           0 :                 tgt->data.si += src->data.si;
    1170           0 :                 break;
    1171           0 :         case SYBINT4:
    1172           0 :                 tgt->data.i += src->data.i;
    1173           0 :                 break;
    1174           0 :         case SYBFLT8:
    1175           0 :                 tgt->data.f += src->data.f;
    1176           0 :                 break;
    1177           0 :         case SYBREAL:
    1178           0 :                 tgt->data.r += src->data.r;
    1179           0 :                 break;
    1180             : 
    1181           0 :         case SYBCHAR:
    1182             :         case SYBVARCHAR:
    1183             :         case SYBINTN:
    1184             :         case SYBDATETIME:
    1185             :         case SYBBIT:
    1186             :         case SYBTEXT:
    1187             :         case SYBNTEXT:
    1188             :         case SYBIMAGE:
    1189             :         case SYBMONEY4:
    1190             :         case SYBMONEY:
    1191             :         case SYBDATETIME4:
    1192             :         case SYBBINARY:
    1193             :         case SYBVOID:
    1194             :         case SYBVARBINARY:
    1195             :         case SYBBITN:
    1196             :         case SYBNUMERIC:
    1197             :         case SYBDECIMAL:
    1198             :         case SYBFLTN:
    1199             :         case SYBMONEYN:
    1200             :         case SYBDATETIMN:
    1201             :         default:
    1202           0 :                 tdsdump_log(TDS_DBG_INFO1, "dbpivot_sum(): invalid operand %d\n", src->type);
    1203           0 :                 tgt->type = SYBINT4;
    1204           0 :                 tgt->data.i = 0;
    1205           0 :                 break;
    1206             :         }
    1207             : }
    1208             : 
    1209             : void
    1210           0 : dbpivot_min (struct col_t *tgt, const struct col_t *src)
    1211             : {
    1212           0 :         assert( tgt && src);
    1213           0 :         assert (src->type);
    1214             :         
    1215           0 :         tgt->type = src->type;
    1216             :         
    1217           0 :         if (col_null(src))
    1218             :                 return;
    1219             : 
    1220           0 :         switch (src->type) {
    1221           0 :         case SYBINT1:
    1222           0 :                 tgt->data.ti = tgt->data.ti < src->data.ti? tgt->data.ti : src->data.ti;
    1223           0 :                 break;
    1224           0 :         case SYBINT2:
    1225           0 :                 tgt->data.si = tgt->data.si < src->data.si? tgt->data.si : src->data.si;
    1226           0 :                 break;
    1227           0 :         case SYBINT4:
    1228           0 :                 tgt->data.i = tgt->data.i < src->data.i? tgt->data.i : src->data.i;
    1229           0 :                 break;
    1230           0 :         case SYBFLT8:
    1231           0 :                 tgt->data.f = tgt->data.f < src->data.f? tgt->data.f : src->data.f;
    1232           0 :                 break;
    1233           0 :         case SYBREAL:
    1234           0 :                 tgt->data.r = tgt->data.r < src->data.r? tgt->data.r : src->data.r;
    1235           0 :                 break;
    1236             : 
    1237           0 :         case SYBCHAR:
    1238             :         case SYBVARCHAR:
    1239             :         case SYBINTN:
    1240             :         case SYBDATETIME:
    1241             :         case SYBBIT:
    1242             :         case SYBTEXT:
    1243             :         case SYBNTEXT:
    1244             :         case SYBIMAGE:
    1245             :         case SYBMONEY4:
    1246             :         case SYBMONEY:
    1247             :         case SYBDATETIME4:
    1248             :         case SYBBINARY:
    1249             :         case SYBVOID:
    1250             :         case SYBVARBINARY:
    1251             :         case SYBBITN:
    1252             :         case SYBNUMERIC:
    1253             :         case SYBDECIMAL:
    1254             :         case SYBFLTN:
    1255             :         case SYBMONEYN:
    1256             :         case SYBDATETIMN:
    1257             :         default:
    1258           0 :                 tdsdump_log(TDS_DBG_INFO1, "dbpivot_sum(): invalid operand %d\n", src->type);
    1259           0 :                 tgt->type = SYBINT4;
    1260           0 :                 tgt->data.i = 0;
    1261           0 :                 break;
    1262             :         }
    1263             : }
    1264             : 
    1265             : void
    1266           0 : dbpivot_max (struct col_t *tgt, const struct col_t *src)
    1267             : {
    1268           0 :         assert( tgt && src);
    1269           0 :         assert (src->type);
    1270             :         
    1271           0 :         tgt->type = src->type;
    1272             :         
    1273           0 :         if (col_null(src))
    1274             :                 return;
    1275             : 
    1276           0 :         switch (src->type) {
    1277           0 :         case SYBINT1:
    1278           0 :                 tgt->data.ti = tgt->data.ti > src->data.ti? tgt->data.ti : src->data.ti;
    1279           0 :                 break;
    1280           0 :         case SYBINT2:
    1281           0 :                 tgt->data.si = tgt->data.si > src->data.si? tgt->data.si : src->data.si;
    1282           0 :                 break;
    1283           0 :         case SYBINT4:
    1284           0 :                 tgt->data.i = tgt->data.i > src->data.i? tgt->data.i : src->data.i;
    1285           0 :                 break;
    1286           0 :         case SYBFLT8:
    1287           0 :                 tgt->data.f = tgt->data.f > src->data.f? tgt->data.f : src->data.f;
    1288           0 :                 break;
    1289           0 :         case SYBREAL:
    1290           0 :                 tgt->data.r = tgt->data.r > src->data.r? tgt->data.r : src->data.r;
    1291           0 :                 break;
    1292             : 
    1293           0 :         case SYBCHAR:
    1294             :         case SYBVARCHAR:
    1295             :         case SYBINTN:
    1296             :         case SYBDATETIME:
    1297             :         case SYBBIT:
    1298             :         case SYBTEXT:
    1299             :         case SYBNTEXT:
    1300             :         case SYBIMAGE:
    1301             :         case SYBMONEY4:
    1302             :         case SYBMONEY:
    1303             :         case SYBDATETIME4:
    1304             :         case SYBBINARY:
    1305             :         case SYBVOID:
    1306             :         case SYBVARBINARY:
    1307             :         case SYBBITN:
    1308             :         case SYBNUMERIC:
    1309             :         case SYBDECIMAL:
    1310             :         case SYBFLTN:
    1311             :         case SYBMONEYN:
    1312             :         case SYBDATETIMN:
    1313             :         default:
    1314           0 :                 tdsdump_log(TDS_DBG_INFO1, "dbpivot_sum(): invalid operand %d\n", src->type);
    1315           0 :                 tgt->type = SYBINT4;
    1316           0 :                 tgt->data.i = 0;
    1317           0 :                 break;
    1318             :         }
    1319             : }
    1320             : 
    1321             : static const struct name_t {
    1322             :         char name[14];
    1323             :         DBPIVOT_FUNC func;
    1324             : } names[] = 
    1325             :         { { "count",  dbpivot_count }
    1326             :         , { "sum",    dbpivot_sum }
    1327             :         , { "min",    dbpivot_min }
    1328             :         , { "max",    dbpivot_max }
    1329             :         };
    1330             : 
    1331             : static bool
    1332           0 : name_equal( const struct name_t *n1, const struct name_t *n2 ) 
    1333             : {
    1334           0 :         assert(n1 && n2);
    1335           0 :         return strcmp(n1->name, n2->name) == 0;
    1336             : }
    1337             : 
    1338             : DBPIVOT_FUNC 
    1339           0 : dbpivot_lookup_name( const char name[] )
    1340             : {
    1341           0 :         struct name_t *n = TDS_FIND(name, names, (compare_func) name_equal);
    1342             :         
    1343           0 :         return n ? n->func : NULL;
    1344             : }

Generated by: LCOV version 1.13