LCOV - code coverage report
Current view: top level - sql/backends/monet5 - sql_bincopyconvert.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 277 300 92.3 %
Date: 2024-10-04 20:04:04 Functions: 24 24 100.0 %

          Line data    Source code
       1             : /*
       2             :  * SPDX-License-Identifier: MPL-2.0
       3             :  *
       4             :  * This Source Code Form is subject to the terms of the Mozilla Public
       5             :  * License, v. 2.0.  If a copy of the MPL was not distributed with this
       6             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
       7             :  *
       8             :  * Copyright 2024 MonetDB Foundation;
       9             :  * Copyright August 2008 - 2023 MonetDB B.V.;
      10             :  * Copyright 1997 - July 2008 CWI.
      11             :  */
      12             : 
      13             : #include "monetdb_config.h"
      14             : 
      15             : #include "copybinary.h"
      16             : #include "copybinary_support.h"
      17             : #include "sql_bincopyconvert.h"
      18             : #include "sql.h"
      19             : #include "gdk.h"
      20             : #include "mal_backend.h"
      21             : #include "mal_interpreter.h"
      22             : #include "mstring.h"
      23             : 
      24             : 
      25             : #define bailout(...) do { \
      26             :                 msg = createException(MAL, mal_operator, SQLSTATE(42000) __VA_ARGS__); \
      27             :                 goto end; \
      28             :         } while (0)
      29             : 
      30             : 
      31             : static str
      32           4 : validate_bit(void *dst_, size_t count, int width, const char *filename)
      33             : {
      34           4 :         (void)width;
      35           4 :         const unsigned char *data = dst_;
      36             : 
      37          10 :         for (size_t i = 0; i < count; i++) {
      38           6 :                 if (data[i] > 1 && data[i] != 0x80)
      39           0 :                         throw(SQL, "convert_bit", SQLSTATE(22003) "invalid boolean byte value %d in %s", data[i], filename);
      40             :         }
      41             :         return MAL_SUCCEED;
      42             : }
      43             : 
      44             : // width is only nonzero for DECIMAL types. For plain integer types it is 0.
      45             : #define VALIDATE_DECIMAL(TYP,NIL_VALUE) do { \
      46             :                 if (width) { \
      47             :                         TYP m = 1; \
      48             :                         for (int i = 0; i < width; i++) \
      49             :                                 m *= 10; \
      50             :                         TYP *dst = dst_; \
      51             :                         for (size_t i = 0; i < count; i++) { \
      52             :                                 if (dst[i] == NIL_VALUE) \
      53             :                                         continue; \
      54             :                                 if (dst[i] >= m || dst[i] <= -m) \
      55             :                                         throw(SQL, "convert", SQLSTATE(22003) "decimal out of range in %s", filename); \
      56             :                         } \
      57             :                 } \
      58             :     } while (0)
      59             : 
      60             : 
      61             : static str
      62           9 : byteswap_sht(void *dst_, void *src_, size_t count, bool byteswap)
      63             : {
      64           9 :         assert(byteswap); (void)byteswap; // otherwise, why call us?
      65             :         sht *dst = dst_;
      66             :         const sht *src = src_;
      67     4000015 :         for (size_t i = 0; i < count; i++)
      68     4000006 :                 *dst++ = copy_binary_byteswap16(*src++);
      69           9 :         return MAL_SUCCEED;
      70             : }
      71             : 
      72             : static str
      73          42 : validate_sht(void *dst_, size_t count, int width, const char *filename)
      74             : {
      75     3000087 :         VALIDATE_DECIMAL(sht, sht_nil);
      76             :         return MAL_SUCCEED;
      77             : }
      78             : 
      79             : static str
      80          41 : byteswap_int(void *dst_, void *src_, size_t count, bool byteswap)
      81             : {
      82          41 :         assert(byteswap); (void)byteswap; // otherwise, why call us?
      83             :         int *dst = dst_;
      84             :         const int *src = src_;
      85     8000071 :         for (size_t i = 0; i < count; i++)
      86     8000030 :                 *dst++ = copy_binary_byteswap32(*src++);
      87          41 :         return MAL_SUCCEED;
      88             : }
      89             : 
      90             : static str
      91         284 : validate_int(void *dst_, size_t count, int width, const char *filename)
      92             : {
      93     4000447 :         VALIDATE_DECIMAL(int, int_nil);
      94             :         return MAL_SUCCEED;
      95             : }
      96             : 
      97             : static str
      98          33 : byteswap_lng(void *dst_, void *src_, size_t count, bool byteswap)
      99             : {
     100          33 :         assert(byteswap); (void)byteswap; // otherwise, why call us?
     101             :         lng *dst = dst_;
     102             :         const lng *src = src_;
     103     4000039 :         for (size_t i = 0; i < count; i++)
     104     4000006 :                 *dst++ = copy_binary_byteswap64(*src++);
     105          33 :         return MAL_SUCCEED;
     106             : }
     107             : 
     108             : static str
     109         103 : validate_lng(void *dst_, size_t count, int width, const char *filename)
     110             : {
     111     4000433 :         VALIDATE_DECIMAL(lng, lng_nil);
     112             :         return MAL_SUCCEED;
     113             : }
     114             : 
     115             : #ifdef HAVE_HGE
     116             : static str
     117           1 : byteswap_hge(void *dst_, void *src_, size_t count, bool byteswap)
     118             : {
     119           1 :         assert(byteswap); (void)byteswap; // otherwise, why call us?
     120             :         hge *dst = dst_;
     121             :         const hge *src = src_;
     122           7 :         for (size_t i = 0; i < count; i++)
     123           6 :                 *dst++ = copy_binary_byteswap128(*src++);
     124           1 :         return MAL_SUCCEED;
     125             : }
     126             : 
     127             : static str
     128          94 : validate_hge(void *dst_, size_t count, int width, const char *filename)
     129             : {
     130     4001215 :         VALIDATE_DECIMAL(hge, hge_nil);
     131             :         return MAL_SUCCEED;
     132             : }
     133             : #endif
     134             : 
     135             : static str
     136          17 : byteswap_flt(void *dst_, void *src_, size_t count, bool byteswap)
     137             : {
     138             :         // Verify that size and alignment requirements of flt do not exceed int.
     139             :         // This is important because we use the int32 byteswap to byteswap the floats.
     140          17 :         assert(sizeof(uint32_t) == sizeof(flt));
     141          17 :         assert(sizeof(struct { char dummy; uint32_t ui; }) >= sizeof(struct { char dummy; flt f; }));
     142             : 
     143          17 :         assert(byteswap); (void)byteswap; // otherwise, why call us?
     144             :         int *dst = dst_;
     145             :         const int *src = src_;
     146     4000020 :         for (size_t i = 0; i < count; i++)
     147     4000003 :                 *dst++ = copy_binary_byteswap32(*src++);
     148          17 :         return MAL_SUCCEED;
     149             : }
     150             : 
     151             : static str
     152          33 : byteswap_dbl(void *dst_, void *src_, size_t count, bool byteswap)
     153             : {
     154             :         // Verify that size and alignment requirements of dbl do not exceed lng
     155             :         // This is important because we use the int64 byteswap to byteswap the doubles.
     156          33 :         assert(sizeof(uint64_t) == sizeof(dbl));
     157          33 :         assert(sizeof(struct { char dummy; uint64_t ui; }) >= sizeof(struct { char dummy; dbl f; }));
     158             : 
     159          33 :         assert(byteswap); (void)byteswap; // otherwise, why call us?
     160             :         lng *dst = dst_;
     161             :         const lng *src = src_;
     162     4000036 :         for (size_t i = 0; i < count; i++)
     163     4000003 :                 *dst++ = copy_binary_byteswap64(*src++);
     164          33 :         return MAL_SUCCEED;
     165             : }
     166             : 
     167             : 
     168             : static str
     169          10 : decode_date(void *dst_, void *src_, size_t count, bool byteswap)
     170             : {
     171          10 :         date *dst = dst_;
     172          10 :         copy_binary_date *src = src_;
     173             : 
     174     2000014 :         for (size_t i = 0; i < count; i++) {
     175     2000004 :                 copy_binary_date incoming;
     176     2000004 :                 if (!byteswap)
     177     2000002 :                         incoming = *src++;
     178             :                 else
     179           2 :                         incoming = copy_binary_byteswap_date(*src++);
     180     2000004 :                 date value = date_create(incoming.year, incoming.month, incoming.day);
     181     2000004 :                 *dst++ = value;
     182             :         }
     183          10 :         return MAL_SUCCEED;
     184             : }
     185             : 
     186             : static str
     187          10 : encode_date(void *dst_, void *src_, size_t count, bool byteswap)
     188             : {
     189          10 :         copy_binary_date *dst = dst_;
     190          10 :         date *src = src_;
     191     2000526 :         for (size_t i = 0; i < count; i++) {
     192     2000516 :                 date dt = *src++;
     193     2000516 :                 if (is_date_nil(dt)) {
     194      199527 :                         *dst++ = (copy_binary_date){
     195             :                                 .day = 0xFF,
     196             :                                 .month = 0xFF,
     197             :                                 .year = -1,
     198             :                         };
     199      199527 :                         continue;
     200             :                 }
     201     1800989 :                 int16_t year = date_year(dt);
     202     1800989 :                 if (byteswap)
     203           0 :                         year = copy_binary_byteswap16(year);
     204     1800989 :                 *dst++ = (copy_binary_date){
     205     1800989 :                         .day = date_day(dt),
     206     1800989 :                         .month = date_month(dt),
     207             :                         .year = year,
     208             :                 };
     209             :         }
     210          10 :         return MAL_SUCCEED;
     211             : }
     212             : 
     213             : static str
     214          16 : decode_time(void *dst_, void *src_, size_t count, bool byteswap)
     215             : {
     216          16 :         daytime *dst = dst_;
     217          16 :         copy_binary_time *src = src_;
     218             : 
     219     2000016 :         for (size_t i = 0; i < count; i++) {
     220     2000000 :                 copy_binary_time incoming;
     221     2000000 :                 if (!byteswap)
     222     2000000 :                         incoming = *src++;
     223             :                 else
     224           0 :                         incoming = copy_binary_byteswap_time(*src++);
     225     2000000 :                 daytime value = daytime_create(incoming.hours, incoming.minutes, incoming.seconds, incoming.ms);
     226     2000000 :                 *dst++ = value;
     227             :         }
     228          16 :         return MAL_SUCCEED;
     229             : }
     230             : 
     231             : static str
     232          16 : encode_time(void *dst_, void *src_, size_t count, bool byteswap)
     233             : {
     234          16 :         copy_binary_time *dst = dst_;
     235          16 :         daytime *src = src_;
     236     2000016 :         for (size_t i = 0; i < count; i++) {
     237     2000000 :                 daytime tm = *src++;
     238     2000000 :                 if (is_daytime_nil(tm)) {
     239      199402 :                         *dst++ = (copy_binary_time){
     240             :                                 .ms = 0xFFFFFFFF,
     241             :                                 .seconds = 0xFF,
     242             :                                 .minutes = 0xFF,
     243             :                                 .hours = 0xFF,
     244             :                                 .padding = 0xFF,
     245             :                         };
     246      199402 :                         continue;
     247             :                 }
     248     1800598 :                 uint32_t ms = daytime_usec(tm);
     249     1800598 :                 if (byteswap)
     250           0 :                         ms = copy_binary_byteswap32(ms);
     251     1800598 :                 *dst++ = (copy_binary_time){
     252             :                         .ms = ms,
     253     1800598 :                         .seconds = daytime_sec(tm),
     254     1800598 :                         .minutes = daytime_min(tm),
     255     1800598 :                         .hours = daytime_hour(tm),
     256             :                 };
     257             :         }
     258          16 :         return MAL_SUCCEED;
     259             : }
     260             : 
     261             : static str
     262          37 : decode_timestamp(void *dst_, void *src_, size_t count, bool byteswap)
     263             : {
     264          37 :         timestamp *dst = dst_;
     265          37 :         copy_binary_timestamp *src = src_;
     266             : 
     267     2000041 :         for (size_t i = 0; i < count; i++) {
     268     2000004 :                 copy_binary_timestamp incoming;
     269     2000004 :                 if (!byteswap)
     270     2000002 :                         incoming = *src++;
     271             :                 else
     272           2 :                         incoming = copy_binary_byteswap_timestamp(*src++);
     273     2000004 :                 date dt = date_create(incoming.date.year, incoming.date.month, incoming.date.day);
     274     2000004 :                 daytime tm = daytime_create(incoming.time.hours, incoming.time.minutes, incoming.time.seconds, incoming.time.ms);
     275     2000004 :                 timestamp value = timestamp_create(dt, tm);
     276     2000004 :                 *dst++ = value;
     277             :         }
     278          37 :         return MAL_SUCCEED;
     279             : }
     280             : 
     281             : 
     282             : static str
     283          24 : encode_timestamp(void *dst_, void *src_, size_t count, bool byteswap)
     284             : {
     285          24 :         copy_binary_timestamp *dst = dst_;
     286          24 :         timestamp *src = src_;
     287     2000024 :         for (size_t i = 0; i < count; i++) {
     288     2000000 :                 timestamp value = *src++;
     289     2000000 :                 if (is_timestamp_nil(value)) {
     290      199402 :                         *dst++ = (copy_binary_timestamp) {
     291             :                                 .time = {
     292             :                                         .ms = 0xFFFFFFFF,
     293             :                                         .seconds = 0xFF,
     294             :                                         .minutes = 0xFF,
     295             :                                         .hours = 0xFF,
     296             :                                         .padding = 0xFF,
     297             :                                 },
     298             :                                 .date = {
     299             :                                         .day = 0xFF,
     300             :                                         .month = 0xFF,
     301             :                                         .year = -1,
     302             :                                 }
     303             :                         };
     304      199402 :                         continue;
     305             :                 }
     306     1800598 :                 date dt = timestamp_date(value);
     307     1800598 :                 daytime tm = timestamp_daytime(value);
     308     1800598 :                 int16_t year = date_year(dt);
     309     1800598 :                 uint32_t ms = daytime_usec(tm);
     310     1800598 :                 if (byteswap) {
     311           0 :                         ms = copy_binary_byteswap32(ms);
     312           0 :                         year = copy_binary_byteswap16(year);
     313             :                 }
     314     1800598 :                 *dst++ = (copy_binary_timestamp) {
     315             :                         .time = {
     316             :                                 .ms = ms,
     317     1800598 :                                 .seconds = daytime_sec(tm),
     318     1800598 :                                 .minutes = daytime_min(tm),
     319     1800598 :                                 .hours = daytime_hour(tm),
     320             :                         },
     321             :                         .date = {
     322     1800598 :                                 .day = date_day(dt),
     323     1800598 :                                 .month = date_month(dt),
     324             :                                 .year = year,
     325             :                         },
     326             :                 };
     327             :         }
     328          24 :         return MAL_SUCCEED;
     329             : }
     330             : 
     331             : // Load NUL-terminated items from the stream and put them in the BAT.
     332             : static str
     333          22 : load_zero_terminated_text(BAT *bat, stream *s, int *eof_reached, int width, bool byteswap)
     334             : {
     335          22 :         (void)byteswap;
     336          22 :         const char mal_operator[] = "sql.importColumn";
     337          22 :         str msg = MAL_SUCCEED;
     338          22 :         bstream *bs = NULL;
     339          22 :         int tpe = BATttype(bat);
     340          22 :         void *buffer = NULL;
     341          22 :         size_t buffer_len = 0;
     342             : 
     343             :         // convert_and_validate_utf8() above counts on the following property to hold:
     344          22 :         assert(strNil((const char[2]){ 0x80, 0 }));
     345             : 
     346          22 :         bs = bstream_create(s, 1 << 20);
     347          22 :         if (bs == NULL) {
     348           0 :                 msg = createException(SQL, "sql", SQLSTATE(HY013) MAL_MALLOC_FAIL);
     349           0 :                 goto end;
     350             :         }
     351             : 
     352             :         // In the outer loop we refill the buffer until the stream ends.
     353             :         // In the inner loop we look for complete \0-terminated strings.
     354         846 :         while (1) {
     355         434 :                 ssize_t nread = bstream_next(bs);
     356         434 :                 if (nread < 0)
     357           0 :                         bailout("%s", mnstr_peek_error(s));
     358         434 :                 if (nread == 0)
     359             :                         break;
     360             : 
     361         417 :                 char *buf_start = &bs->buf[bs->pos];
     362         417 :                 char *buf_end = &bs->buf[bs->len];
     363         417 :                 char *start, *end;
     364    14247338 :                 for (start = buf_start; (end = memchr(start, '\0', buf_end - start)) != NULL; start = end + 1) {
     365    14246926 :                         char *value;
     366    14246926 :                         if (!checkUTF8(start)) {
     367           3 :                                 msg = createException(SQL, "load_zero_terminated_text", SQLSTATE(42000) "malformed utf-8 byte sequence");
     368           3 :                                 goto end;
     369             :                         }
     370    14246923 :                         if (tpe == TYPE_str) {
     371    12246927 :                                 if (width > 0 && !strNil(start)) {
     372     2000006 :                                         int w = UTF8_strlen(start);
     373     2000006 :                                         if (w > width) {
     374           1 :                                                 msg = createException(SQL, "sql.importColumn", "string too wide for column");
     375           1 :                                                 goto end;
     376             :                                         }
     377             :                                 }
     378             :                                 value = start;
     379             :                         } else {
     380     4000002 :                                 ssize_t n = ATOMfromstr(tpe, &buffer, &buffer_len, start, false);
     381     4000002 :                                 if (n <= 0) {
     382           1 :                                         msg = createException(SQL, "sql.importColumn", GDK_EXCEPTION);
     383           1 :                                         goto end;
     384             :                                 }
     385     4000001 :                                 value = buffer;
     386             :                         }
     387    14246921 :                         if (BUNappend(bat, value, false) != GDK_SUCCEED) {
     388           0 :                                 msg = createException(SQL, "sql.importColumn", GDK_EXCEPTION);
     389           0 :                                 goto end;
     390             :                         }
     391             :                 }
     392         412 :                 bs->pos = start - buf_start;
     393             :         }
     394             : 
     395             :         // It's an error to have date left after falling out of the outer loop
     396          17 :         if (bs->pos < bs->len)
     397           0 :                 bailout("unterminated string at end");
     398             : 
     399          17 : end:
     400          22 :         *eof_reached = 0;
     401          22 :         GDKfree(buffer);
     402          22 :         if (bs != NULL) {
     403          22 :                 *eof_reached = (int)bs->eof;
     404          22 :                 bs->s = NULL;
     405          22 :                 bstream_destroy(bs);
     406             :         }
     407          22 :         return msg;
     408             : }
     409             : 
     410             : static str
     411         596 : dump_zero_terminated_text(BAT *bat, stream *s, BUN start, BUN length, bool byteswap)
     412             : {
     413         596 :         (void)byteswap;
     414         596 :         const char mal_operator[] = "sql.export_bin_column";
     415         596 :         str msg = MAL_SUCCEED;
     416         596 :         int tpe = BATttype(bat);
     417         596 :         assert(ATOMstorage(tpe) == TYPE_str); (void)tpe;
     418         596 :         assert(mnstr_isbinary(s));
     419             : 
     420             : 
     421         596 :         BUN end = start + length;
     422         596 :         assert(end <= BATcount(bat));
     423         596 :         BATiter bi = bat_iterator(bat);
     424    10893837 :         for (BUN p = start; p < end; p++) {
     425    10893241 :                 const char *v = BUNtvar(bi, p);
     426    10893241 :                 if (mnstr_writeStr(s, v) < 0 || mnstr_writeBte(s, 0) < 0) {
     427    10893241 :                         bailout("%s", mnstr_peek_error(s));
     428             :                 }
     429             :         }
     430             : 
     431         596 : end:
     432         596 :         bat_iterator_end(&bi);
     433         596 :         return msg;
     434             : }
     435             : 
     436             : // Some streams, in particular the mapi upload stream, sometimes read fewer
     437             : // bytes than requested. This function wraps the read in a loop to force it to
     438             : // read the whole block
     439             : static ssize_t
     440     9996006 : read_exact(stream *s, void *buffer, size_t length)
     441             : {
     442     9996006 :         char *p = buffer;
     443             : 
     444    19992963 :         while (length > 0) {
     445     9996963 :                 ssize_t nread = mnstr_read(s, p, 1, length);
     446     9996963 :                 if (nread < 0) {
     447           0 :                         return nread;
     448     9996963 :                 } else if (nread == 0) {
     449             :                         break;
     450             :                 } else {
     451     9996957 :                         p += nread;
     452     9996957 :                         length -= nread;
     453             :                 }
     454             :         }
     455             : 
     456     9996006 :         return p - (char*)buffer;
     457             : }
     458             : 
     459             : // Read BLOBs.  Every blob is preceded by a 64bit header word indicating its length.
     460             : // NULLs are indicated by length==-1
     461             : static str
     462           6 : load_blob(BAT *bat, stream *s, int *eof_reached, int width, bool byteswap)
     463             : {
     464           6 :         (void)width;
     465           6 :         const char mal_operator[] = "sql.importColumn";
     466           6 :         str msg = MAL_SUCCEED;
     467           6 :         const blob *nil_value = ATOMnilptr(TYPE_blob);
     468           6 :         blob *buffer = NULL;
     469           6 :         size_t buffer_size = 0;
     470           6 :         union {
     471             :                 uint64_t length;
     472             :                 char bytes[8];
     473             :         } header;
     474             : 
     475           6 :         *eof_reached = 0;
     476             : 
     477             :         /* we know nothing about the ordering of the input data */
     478           6 :         bat->tsorted = false;
     479           6 :         bat->trevsorted = false;
     480           6 :         bat->tkey = false;
     481             :         /* keep tno* properties: if they're set they remain valid when
     482             :          * appending */
     483     6000006 :         while (1) {
     484     6000006 :                 const blob *value;
     485             :                 // Read the header
     486     6000006 :                 ssize_t nread = read_exact(s, header.bytes, 8);
     487     6000006 :                 if (nread < 0) {
     488           0 :                         bailout("%s", mnstr_peek_error(s));
     489     6000006 :                 } else if (nread == 0) {
     490           6 :                         *eof_reached = 1;
     491           6 :                         break;
     492     6000000 :                 } else if (nread < 8) {
     493           0 :                         bailout("incomplete blob at end of file");
     494             :                 }
     495     6000000 :                 if (byteswap) {
     496     2000000 :                         copy_binary_convert64(&header.length);
     497             :                 }
     498             : 
     499     6000000 :                 if (header.length == ~(uint64_t)0) {
     500     1999998 :                         value = nil_value;
     501     1999998 :                         bat->tnonil = false;
     502     1999998 :                         bat->tnil = true;
     503             :                 } else {
     504     4000002 :                         size_t length;
     505     4000002 :                         size_t needed;
     506             : 
     507     4000002 :                         if (header.length >= VAR_MAX) {
     508           0 :                                 bailout("blob too long");
     509             :                         }
     510     4000002 :                         length = (size_t) header.length;
     511             : 
     512             :                         // Reallocate the buffer
     513     4000002 :                         needed = sizeof(blob) + length;
     514     4000002 :                         if (buffer_size < needed) {
     515             :                                 // do not use GDKrealloc, no need to copy the old contents
     516           6 :                                 GDKfree(buffer);
     517           6 :                                 size_t allocate = needed;
     518           6 :                                 allocate += allocate / 16;   // add a little margin
     519             : #ifdef _MSC_VER
     520             : #pragma warning(suppress:4146)
     521             : #endif
     522           6 :                                 allocate += ((~allocate + 1) % 0x100000);   // round up to nearest MiB
     523           6 :                                 assert(allocate >= needed);
     524           6 :                                 buffer = GDKmalloc(allocate);
     525           6 :                                 if (!buffer) {
     526           0 :                                         msg = createException(SQL, "sql", SQLSTATE(HY013) MAL_MALLOC_FAIL);
     527           0 :                                         goto end;
     528             :                                 }
     529             :                                 buffer_size = allocate;
     530             :                         }
     531             : 
     532             :                         // Fill the buffer
     533     4000002 :                         buffer->nitems = length;
     534     4000002 :                         if (length > 0) {
     535     3996000 :                                 nread = read_exact(s, buffer->data, length);
     536     3996000 :                                 if (nread < 0) {
     537           0 :                                         bailout("%s", mnstr_peek_error(s));
     538     3996000 :                                 } else if ((size_t)nread < length) {
     539           0 :                                         bailout("Incomplete blob at end of file");
     540             :                                 }
     541             :                         }
     542             : 
     543             :                         value = buffer;
     544             :                 }
     545             : 
     546     6000000 :                 if (bunfastapp(bat, value) != GDK_SUCCEED) {
     547           0 :                                 msg = createException(SQL, mal_operator, GDK_EXCEPTION);
     548           0 :                                 goto end;
     549             :                 }
     550             :         }
     551             : 
     552           6 : end:
     553           6 :         GDKfree(buffer);
     554           6 :         return msg;
     555             : }
     556             : 
     557             : static str
     558           6 : dump_blob(BAT *bat, stream *s, BUN start, BUN length, bool byteswap)
     559             : {
     560           6 :         const char mal_operator[] = "sql.export_bin_column";
     561           6 :         str msg = MAL_SUCCEED;
     562           6 :         int tpe = BATttype(bat);
     563           6 :         assert(ATOMstorage(tpe) == TYPE_blob); (void)tpe;
     564           6 :         assert(mnstr_isbinary(s));
     565             : 
     566           6 :         BUN end = start + length;
     567           6 :         assert(end <= BATcount(bat));
     568           6 :         BATiter bi = bat_iterator(bat);
     569           6 :         uint64_t nil_header = ~(uint64_t)0;
     570     6000006 :         for (BUN p = start; p < end; p++) {
     571     6000000 :                 const blob *b = BUNtvar(bi, p);
     572     6000000 :                 uint64_t header = is_blob_nil(b) ? nil_header : (uint64_t)b->nitems;
     573     6000000 :                 if (byteswap)
     574     2000000 :                         copy_binary_convert64(&header);
     575     6000000 :                 if (mnstr_write(s, &header, 8, 1) != 1) {
     576           0 :                         bailout("%s", mnstr_peek_error(s));
     577             :                 }
     578     6000000 :                 if (!is_blob_nil(b) && mnstr_write(s, b->data,b->nitems, 1) != 1) {
     579     6000000 :                         bailout("%s", mnstr_peek_error(s));
     580             :                 }
     581             :         }
     582             : 
     583           6 : end:
     584           6 :         bat_iterator_end(&bi);
     585           6 :         return msg;
     586             : 
     587             : }
     588             : 
     589             : 
     590             : static struct type_record_t type_recs[] = {
     591             : 
     592             :         // no conversion, no byteswapping
     593             :         { "bte", "bte", .encoder_trivial=true, .decoder_trivial=true},
     594             :         { "uuid", "uuid", .encoder_trivial=true, .decoder_trivial=true},
     595             :         { "bit", "bit", .encoder_trivial=true, .decoder_trivial=true, .validate=validate_bit },
     596             : 
     597             :         // vanilla integer types
     598             :         { "sht", "sht", .trivial_if_no_byteswap=true, .decoder=byteswap_sht, .encoder=byteswap_sht, .validate=validate_sht },
     599             :         { "int", "int", .trivial_if_no_byteswap=true, .decoder=byteswap_int, .encoder=byteswap_int, .validate=validate_int },
     600             :         { "lng", "lng", .trivial_if_no_byteswap=true, .decoder=byteswap_lng, .encoder=byteswap_lng, .validate=validate_lng },
     601             :         { "flt", "flt", .trivial_if_no_byteswap=true, .decoder=byteswap_flt, .encoder=byteswap_flt},
     602             :         { "dbl", "dbl", .trivial_if_no_byteswap=true, .decoder=byteswap_dbl, .encoder=byteswap_dbl},
     603             : #ifdef HAVE_HGE
     604             :         { "hge", "hge", .trivial_if_no_byteswap=true, .decoder=byteswap_hge, .encoder=byteswap_hge, .validate=validate_hge },
     605             : #endif
     606             : 
     607             :         { "blob", "blob", .loader=load_blob, .dumper=dump_blob },
     608             : 
     609             :         // \0-terminated text records
     610             :         { "str", "str", .loader=load_zero_terminated_text, .dumper=dump_zero_terminated_text },
     611             :         { "url", "url", .loader=load_zero_terminated_text, .dumper=dump_zero_terminated_text },
     612             :         { "json", "json", .loader=load_zero_terminated_text, .dumper=dump_zero_terminated_text },
     613             : 
     614             :         // temporal types have record size different from the underlying gdk type
     615             :         { "date", "date", .decoder=decode_date, .encoder=encode_date, .record_size=sizeof(copy_binary_date), },
     616             :         { "daytime", "daytime", .decoder=decode_time, .encoder=encode_time, .record_size=sizeof(copy_binary_time), },
     617             :         { "timestamp", "timestamp", .decoder=decode_timestamp, .encoder=encode_timestamp, .record_size=sizeof(copy_binary_timestamp), },
     618             : };
     619             : 
     620             : 
     621             : type_record_t*
     622        3595 : find_type_rec(const char *name)
     623             : {
     624        3595 :         struct type_record_t *end = (struct type_record_t*)((char *)type_recs + sizeof(type_recs));
     625       21993 :         for (struct type_record_t *t = &type_recs[0]; t < end; t++)
     626       21993 :                 if (strcmp(t->method, name) == 0)
     627        3595 :                         return t;
     628             :         return NULL;
     629             : }
     630             : 
     631             : bool
     632        3229 : can_dump_binary_column(type_record_t *rec)
     633             : {
     634        3229 :         return rec->encoder_trivial || rec->dumper || rec->encoder;
     635             : }

Generated by: LCOV version 1.14