Use lo_lseek64(lo_tell64) instead of lo_lseek(lo_tell) if the server's version >...
authorHiroshi Inoue <h-inoue@dream.email.ne.jp>
Sat, 7 Jan 2017 09:24:57 +0000 (18:24 +0900)
committerHiroshi Inoue <h-inoue@dream.email.ne.jp>
Thu, 12 Jan 2017 03:44:30 +0000 (12:44 +0900)
bind.c
bind.h
connection.c
convert.c
lobj.c
lobj.h
psqlodbc.h
results.c
statement.c

diff --git a/bind.c b/bind.c
index bbbb8f68d21ee47eb5ae7707b51d224ea9046dbc..0417bddf603a9a3c5c6a7af545a4b01c98f2a4d1 100644 (file)
--- a/bind.c
+++ b/bind.c
@@ -239,7 +239,7 @@ inolog("Bind column 0 is type %d not of type SQL_C_BOOKMARK", fCType);
    icol--;
 
    /* Reset for SQLGetData */
-   gdata_info->gdata[icol].data_left = -1;
+   GETDATA_RESET(gdata_info->gdata[icol]);
 
    if (rgbValue == NULL)
    {
@@ -834,7 +834,7 @@ inolog("GDATA_unbind_cols freeall=%d allocated=%d gdata=%p", freeall, self->allo
        self->fdata.ttlbuf = NULL;
    }
    self->fdata.ttlbuflen = self->fdata.ttlbufused = 0;
-   self->fdata.data_left = -1;
+   GETDATA_RESET(self->fdata);
    for (lf = 1; lf <= self->allocated; lf++)
        reset_a_getdata_info(self, lf);
    if (freeall)
@@ -848,7 +848,7 @@ inolog("GDATA_unbind_cols freeall=%d allocated=%d gdata=%p", freeall, self->allo
 
 void GetDataInfoInitialize(GetDataInfo *gdata_info)
 {
-   gdata_info->fdata.data_left = -1;
+   GETDATA_RESET(gdata_info->fdata);
    gdata_info->fdata.ttlbuf = NULL;
    gdata_info->fdata.ttlbuflen = gdata_info->fdata.ttlbufused = 0;
    gdata_info->allocated = 0;
@@ -863,10 +863,9 @@ create_empty_gdata(int num_columns)
    new_gdata = (GetDataClass *) malloc(num_columns * sizeof(GetDataClass));
    if (!new_gdata)
        return NULL;
-
    for (i = 0; i < num_columns; i++)
    {
-       new_gdata[i].data_left = -1;
+       GETDATA_RESET(new_gdata[i]);
        new_gdata[i].ttlbuf = NULL;
        new_gdata[i].ttlbuflen = 0;
        new_gdata[i].ttlbufused = 0;
@@ -946,7 +945,7 @@ void    reset_a_getdata_info(GetDataInfo *gdata_info, int icol)
    }
    gdata_info->gdata[icol].ttlbuflen =
    gdata_info->gdata[icol].ttlbufused = 0;
-   gdata_info->gdata[icol].data_left = -1;
+   GETDATA_RESET(gdata_info->gdata[icol]);
 }
 
 void PutDataInfoInitialize(PutDataInfo *pdata_info)
diff --git a/bind.h b/bind.h
index a9154d7d43c3b0dd167f64053d7ef428b9017eef..ff597cbfecd0b0823f980d421c2bcf3e926e5a61 100644 (file)
--- a/bind.h
+++ b/bind.h
@@ -30,14 +30,22 @@ struct BindInfoClass_
    /* area for work variables */
    char    dummy_data;     /* currently not used */
 };
+
+/* struct for SQLGetData */
 typedef struct
 {
+   /* for BLOBs which don't hold the data */
+   struct GetBlobDataClass {
+       Int8    data_left64;    /* amount of large object data
+                      left to read before conversion */
+   } blob;
+   /* for non-BLOBs which hold the data in ttlbuf after conversion */
    char    *ttlbuf;        /* to save the large result */
    SQLLEN  ttlbuflen;      /* the buffer length */
    SQLLEN  ttlbufused;     /* used length of the buffer */
-   SQLLEN  data_left;      /* amount of data left to read
-                    * (SQLGetData) */
+   SQLLEN  data_left;      /* amount of data left to read */
 }  GetDataClass;
+#define GETDATA_RESET(gdc) ((gdc).blob.data_left64 = (gdc).data_left = -1)
 
 /*
  * ParameterInfoClass -- stores information about a bound parameter
index 109c01274cbb02811199c4150659e99285a9d397..f1389497f1f92b994f0aabbcb5a3bf43203600ca 100644 (file)
@@ -2053,6 +2053,37 @@ static const char *func_param_str[MAX_SEND_FUNC_ARGS + 1] =
    "($1, $2, $3)"
 };
 
+static
+Int8 odbc_hton64(Int8 h64)
+{
+   union {
+       Int8    n64;
+       UInt4   i32[2];
+   } u;
+
+   u.i32[0] = htonl((UInt4) (h64 >> 32));
+   u.i32[1] = htonl((UInt4) h64);
+
+   return u.n64;
+}
+
+static
+Int8 odbc_ntoh64(Int8 n64)
+{
+   union {
+       Int8    h64;
+       UInt4   i32[2];
+   } u;
+   Int8 result;
+
+   u.h64 = n64;
+   result = ntohl(u.i32[0]);
+   result <<= 32;
+   result |= ntohl(u.i32[1]);
+
+   return result;
+}
+
 
 int
 CC_send_function(ConnectionClass *self, const char *fn_name, void *result_buf, int *actual_result_len, int result_is_int, LO_ARG *args, int nargs)
@@ -2067,6 +2098,7 @@ CC_send_function(ConnectionClass *self, const char *fn_name, void *result_buf, i
    int         paramLengths[MAX_SEND_FUNC_ARGS];
    int         paramFormats[MAX_SEND_FUNC_ARGS];
    Int4        intParamBufs[MAX_SEND_FUNC_ARGS];
+   Int8        int8ParamBufs[MAX_SEND_FUNC_ARGS];
 
    mylog("send_function(): conn=%p, fn_name=%s, result_is_int=%d, nargs=%d\n", self, fn_name, result_is_int, nargs);
 
@@ -2078,10 +2110,17 @@ CC_send_function(ConnectionClass *self, const char *fn_name, void *result_buf, i
             func_param_str[nargs]);
    for (i = 0; i < nargs; ++i)
    {
-       mylog("  arg[%d]: len = %d, isint = %d, integer = %d, ptr = %p\n", i, args[i].len, args[i].isint, args[i].u.integer, args[i].u.ptr);
-
+       mylog("  arg[%d]: len = %d, isint = %d, integer = " FORMATI64 ", ptr = %p\n", i, args[i].len, args[i].isint, args[i].isint == 2 ? args[i].u.integer64 : args[i].u.integer, args[i].u.ptr);
        /* integers are sent as binary, others as text */
-       if (args[i].isint)
+       if (args[i].isint == 2)
+       {
+           paramTypes[i] = PG_TYPE_INT8;
+           int8ParamBufs[i] = odbc_hton64(args[i].u.integer64);
+           paramValues[i] = (char *) &int8ParamBufs[i];
+           paramLengths[i] = 8;
+           paramFormats[i] = 1;
+       }
+       else if (args[i].isint)
        {
            paramTypes[i] = PG_TYPE_INT4;
            intParamBufs[i] = htonl(args[i].u.integer);
@@ -2123,7 +2162,15 @@ CC_send_function(ConnectionClass *self, const char *fn_name, void *result_buf, i
    if (*actual_result_len > 0)
    {
        char *value = PQgetvalue(pgres, 0, 0);
-       if (result_is_int)
+       if (result_is_int == 2)
+       {
+           Int8 int8val;
+           memcpy(&int8val, value, sizeof(Int8));
+           int8val = odbc_ntoh64(int8val);
+           memcpy(result_buf, &int8val, sizeof(Int8));
+mylog("int8 result=" FORMATI64 "\n", int8val);
+       }
+       else if (result_is_int)
        {
            Int4 int4val;
            memcpy(&int4val, value, sizeof(Int4));
index fef6abf52bedf3a222335fa1decc18291ab0fb39..90a57b3ded59471c88a87e0da61356765fe8e916 100644 (file)
--- a/convert.c
+++ b/convert.c
@@ -218,16 +218,10 @@ static SQLLEN pg_bin2whex(const char *src, SQLWCHAR *dst, SQLLEN length);
 #ifdef WIN32
 #define    ATOI64(val) _strtoi64(val, NULL, 10)
 #define    ATOI64U(val)    _strtoui64(val, NULL, 10)
-#define    FORMATI64   "%I64d"
-#define    FORMATI64U  "%I64u"
 #elif  (SIZEOF_LONG == 8)
 #define    ATOI64(val) strtol(val, NULL, 10)
 #define    ATOI64U(val)    strtoul(val, NULL, 10)
-#define    FORMATI64   "%ld"
-#define    FORMATI64U  "%lu"
 #else
-#define    FORMATI64   "%lld"
-#define    FORMATI64U  "%llu"
 #if    defined(HAVE_STRTOLL)
 #define    ATOI64(val) strtoll(val, NULL, 10)
 #define    ATOI64U(val)    strtoull(val, NULL, 10)
@@ -5801,10 +5795,10 @@ convert_lo(StatementClass *stmt, const void *value, SQLSMALLINT fCType, PTR rgbV
 {
    CSTR    func = "convert_lo";
    OID         oid;
-   int         retval,
-               result;
-   SQLLEN  left = -1;
-   GetDataClass *gdata = NULL;
+   int         result;
+   Int8            retval;
+   Int8        left64 = -1;
+   struct GetBlobDataClass *gdata_blob = NULL;
    ConnectionClass *conn = SC_get_conn(stmt);
    ConnInfo   *ci = &(conn->connInfo);
    GetDataInfo *gdata_info = SC_get_GDTI(stmt);
@@ -5832,8 +5826,8 @@ convert_lo(StatementClass *stmt, const void *value, SQLSMALLINT fCType, PTR rgbV
    /* If using SQLGetData, then current_col will be set */
    if (stmt->current_col >= 0)
    {
-       gdata = &gdata_info->gdata[stmt->current_col];
-       left = gdata->data_left;
+       gdata_blob = &(gdata_info->gdata[stmt->current_col].blob);
+       left64 = gdata_blob->data_left64;
    }
 
    /*
@@ -5841,7 +5835,7 @@ convert_lo(StatementClass *stmt, const void *value, SQLSMALLINT fCType, PTR rgbV
     * for reading
     */
 
-   if (!gdata || gdata->data_left == -1)
+   if (!gdata_blob || gdata_blob->data_left64 == -1)
    {
        /* begin transaction if needed */
        if (!CC_is_in_trans(conn))
@@ -5861,20 +5855,20 @@ convert_lo(StatementClass *stmt, const void *value, SQLSMALLINT fCType, PTR rgbV
        }
 
        /* Get the size */
-       retval = odbc_lo_lseek(conn, stmt->lobj_fd, 0L, SEEK_END);
+       retval = odbc_lo_lseek64(conn, stmt->lobj_fd, 0L, SEEK_END);
        if (retval >= 0)
        {
-           left = odbc_lo_tell(conn, stmt->lobj_fd);
-           if (gdata)
-               gdata->data_left = left;
+           left64 = odbc_lo_tell64(conn, stmt->lobj_fd);
+           if (gdata_blob)
+               gdata_blob->data_left64 = left64;
 
            /* return to beginning */
-           odbc_lo_lseek(conn, stmt->lobj_fd, 0L, SEEK_SET);
+           odbc_lo_lseek64(conn, stmt->lobj_fd, 0L, SEEK_SET);
        }
    }
-   else if (left == 0)
+   else if (left64 == 0)
        return COPY_NO_DATA_FOUND;
-   mylog("lo data left = %d\n", left);
+   mylog("lo data left = " FORMATI64 "\n", left64);
 
    if (stmt->lobj_fd < 0)
    {
@@ -5885,7 +5879,7 @@ convert_lo(StatementClass *stmt, const void *value, SQLSMALLINT fCType, PTR rgbV
    if (0 >= cbValueMax)
        retval = 0;
    else
-       retval = odbc_lo_read(conn, stmt->lobj_fd, (char *) rgbValue, (Int4) (factor > 1 ? (cbValueMax - 1) / factor : cbValueMax));
+       retval = (Int8) odbc_lo_read(conn, stmt->lobj_fd, (char *) rgbValue, (Int4) (factor > 1 ? (cbValueMax - 1) / factor : cbValueMax));
    if (retval < 0)
    {
        odbc_lo_close(conn, stmt->lobj_fd);
@@ -5908,18 +5902,21 @@ convert_lo(StatementClass *stmt, const void *value, SQLSMALLINT fCType, PTR rgbV
 
    if (factor > 1)
        pg_bin2hex((char *) rgbValue, (char *) rgbValue, retval);
-   if (retval < left)
+   if (retval < left64)
        result = COPY_RESULT_TRUNCATED;
    else
        result = COPY_OK;
 
    if (pcbValue)
-       *pcbValue = left < 0 ? SQL_NO_TOTAL : left * factor;
+   {
+       Int8    leftbytes = left64 * factor;
+       *pcbValue = left64 < 0 ? SQL_NO_TOTAL : (leftbytes == (SQLLEN) leftbytes ? leftbytes : /* exceeds SQLLEN limit */ SQL_NO_TOTAL);
+   }
 
-   if (gdata && gdata->data_left > 0)
-       gdata->data_left -= retval;
+   if (gdata_blob && gdata_blob->data_left64 > 0)
+       gdata_blob->data_left64 -= retval;
 
-   if (!gdata || gdata->data_left == 0)
+   if (!gdata_blob || gdata_blob->data_left64 == 0)
    {
        odbc_lo_close(conn, stmt->lobj_fd);
 
diff --git a/lobj.c b/lobj.c
index a6c6fd430a361eb52584a13a975dfd1d50f0bf4a..7dfd943dc5076eb934f2a628af98708fe65148a7 100644 (file)
--- a/lobj.c
+++ b/lobj.c
@@ -52,7 +52,7 @@ odbc_lo_open(ConnectionClass *conn, int lobjId, int mode)
    if (!CC_send_function(conn, "lo_open", &fd, &result_len, 1, argv, 2))
        return -1;
 
-   if (fd >= 0 && odbc_lo_lseek(conn, fd, 0L, SEEK_SET) < 0)
+   if (fd >= 0 && odbc_lo_lseek64(conn, fd, 0L, SEEK_SET) < 0)
        return -1;
 
    return fd;
@@ -142,7 +142,7 @@ odbc_lo_lseek(ConnectionClass *conn, int fd, int offset, Int4 whence)
    argv[2].len = 4;
    argv[2].u.integer = whence;
 
-   /* XXX: Should we use lo_lseek64? */
+   /* We use lo_lseek64 */
    if (!CC_send_function(conn, "lo_lseek", &retval, &result_len, 1, argv, 3))
        return -1;
    else
@@ -160,9 +160,68 @@ odbc_lo_tell(ConnectionClass *conn, int fd)
    argv[0].len = 4;
    argv[0].u.integer = fd;
 
-   /* XXX: Should we use lo_tell64? */
+   /* We use lo_tell64 */
    if (!CC_send_function(conn, "lo_tell", &retval, &result_len, 1, argv, 1))
        return -1;
    else
        return retval;
 }
+
+Int8
+odbc_lo_lseek64(ConnectionClass *conn, int fd, Int8 offset, Int4 whence)
+{
+   LO_ARG      argv[3];
+   Int8        retval;
+   Int4        result_len;
+
+   if (PG_VERSION_LT(conn, 9.3))
+   {
+       Int4    offset32;
+
+       offset32 = (Int4) offset;
+       if (offset != (Int8) offset32)
+       {
+           CC_set_error(conn, CONN_VALUE_OUT_OF_RANGE, "large object lseek64 is unavailable for the server", __FUNCTION__);
+           return -1;
+       }
+       return (Int8) odbc_lo_lseek(conn, fd, offset32, whence);
+   }
+
+   argv[0].isint = 1;
+   argv[0].len = 4;
+   argv[0].u.integer = fd;
+
+   argv[1].isint = 2;
+   argv[1].len = sizeof(offset);
+   argv[1].u.integer64 = offset;
+
+   argv[2].isint = 1;
+   argv[2].len = 4;
+   argv[2].u.integer = whence;
+
+   if (!CC_send_function(conn, "lo_lseek64", &retval, &result_len, 2, argv, 3))
+       return -1;
+   else
+       return retval;
+}
+
+
+Int8
+odbc_lo_tell64(ConnectionClass *conn, int fd)
+{
+   LO_ARG      argv[1];
+   Int8        retval;
+   Int4        result_len;
+
+   if (PG_VERSION_LT(conn, 9.3))
+       return (Int8) odbc_lo_tell(conn, fd);
+
+   argv[0].isint = 1;
+   argv[0].len = 4;
+   argv[0].u.integer = fd;
+
+   if (!CC_send_function(conn, "lo_tell64", &retval, &result_len, 2, argv, 1))
+       return -1;
+   else
+       return retval;
+}
diff --git a/lobj.h b/lobj.h
index d40d55bcf64dd0ac4aa793a31a24edac5838f0ca..1428c52387267292871a1ebe81ae01fd36684598 100644 (file)
--- a/lobj.h
+++ b/lobj.h
@@ -20,6 +20,7 @@ struct lo_arg
    {
        int         integer;
        char       *ptr;
+       Int8    integer64;
    }           u;
 };
 
@@ -34,4 +35,6 @@ Int4      odbc_lo_write(ConnectionClass *conn, int fd, char *buf, Int4 len);
 Int4       odbc_lo_lseek(ConnectionClass *conn, int fd, int offset, Int4 len);
 Int4       odbc_lo_tell(ConnectionClass *conn, int fd);
 
+Int8       odbc_lo_lseek64(ConnectionClass *conn, int fd, Int8 offset, Int4 len);
+Int8       odbc_lo_tell64(ConnectionClass *conn, int fd);
 #endif
index 38686cc9f6221dec10c6e3e2184bfd914690207b..bd92d842cfb208bff2ae084ff2ce3525f06e473b 100644 (file)
@@ -125,6 +125,7 @@ extern "C" {
 #define UInt4 unsigned int
 #define Int2 short
 #define UInt2 unsigned short
+typedef SQLBIGINT Int8;
 typedef    UInt4   OID;
 
 #ifndef SQL_TRUE
@@ -138,50 +139,66 @@ typedef   UInt4   OID;
 #ifndef    SSIZE_T_DEFINED
 #define    ssize_t SSIZE_T
 #define    SSIZE_T_DEFINED
-#endif
+#endif /* SSIZE_T */
 #define    FORMAT_SIZE_T   "%Iu"   /* size_t */
 #define    FORMAT_SSIZE_T  "%Id"   /* ssize_t */
 #define    FORMAT_INTEGER  "%ld"   /* SQLINTEGER */
 #define    FORMAT_UINTEGER "%lu"   /* SQLUINTEGER */
+#define    FORMATI64   "%I64d" /* SQLBIGINT */
+#define    FORMATI64U  "%I64u" /* SQLUBIGINT */
 #ifdef _WIN64
 #define    FORMAT_LEN  "%I64d" /* SQLLEN */
 #define    FORMAT_ULEN "%I64u" /* SQLULEN */
-#else
+#else /* _WIN64 */
 #define    FORMAT_LEN  "%ld"   /* SQLLEN */
 #define    FORMAT_ULEN "%lu"   /* SQLULEN */
 #endif /* _WIN64 */
-#else
+#else /* WIN32 */
 #define    FORMAT_SIZE_T   "%zu"   /* size_t */
 #define    FORMAT_SSIZE_T  "%zd"   /* ssize_t */
 #ifndef    HAVE_SSIZE_T
 typedef    long    ssize_t
 #endif /* HAVE_SSIZE_T */
 
-#if (SIZEOF_VOID_P == SIZEOF_LONG)
+#if (SIZEOF_VOID_P == SIZEOF_LONG) /* ILP32 or LP64 */
 typedef    long    LONG_PTR;
 typedef    unsigned long   ULONG_PTR;
-#elif defined (HAVE_LONG_LONG)
+#elif defined (HAVE_LONG_LONG) /* LLP64 */
 typedef    long long LONG_PTR;
 typedef    unsigned long long ULONG_PTR;
-#else
+#else /* SIZEOF_VOID_P */
 #error appropriate long pointer type not found
 #endif /* SIZEOF_VOID_P */
-#if (SIZEOF_LONG == 8)
+#if (SIZEOF_LONG == 8) /* LP64 */
 #define    FORMAT_INTEGER  "%d"    /* SQLINTEGER */
 #define    FORMAT_UINTEGER "%u"    /* SQLUINTEGER */
+#define    FORMATI64   "%ld"   /* SQLBIGINT */
+#define    FORMATI64U  "%lu"   /* SQLUBIGINT */
 #if defined(WITH_UNIXODBC) && defined(BUILD_LEGACY_64_BIT_MODE)
 #define FORMAT_LEN "%d"    /* SQLLEN */
 #define FORMAT_ULEN    "%u"    /* SQLULEN */
-#else
+#else /* WITH_UNIXODBC */
 #define FORMAT_LEN "%ld"   /* SQLLEN */
 #define FORMAT_ULEN    "%lu"   /* SQLULEN */
 #endif /* WITH_UNIXODBC */
-#else
-#define    FORMAT_LEN  "%ld"   /* SQLLEN */
-#define    FORMAT_ULEN "%lu"   /* SQLULEN */
+#else /* SIZEOF_LONG */
 #define    FORMAT_INTEGER  "%ld"   /* SQLINTEGER */
 #define    FORMAT_UINTEGER "%lu"   /* SQLUINTEGER */
+#if defined(HAVE_LONG_LONG)
+#define    FORMATI64   "%lld"  /* SQLBIGINT */
+#define    FORMATI64U  "%llu"  /* SQLUBIGINT */
+#if (SIZEOF_VOID_P == 8) /* LLP64 */
+#define    FORMAT_LEN  "%lld"  /* SQLLEN */
+#define    FORMAT_ULEN "%llu"  /* SQLULEN */
+#else /* SIZEOF_VOID_P ILP32 */
+#define    FORMAT_LEN  "%ld"   /* SQLLEN */
+#define    FORMAT_ULEN "%lu"   /* SQLULEN */
 #endif /* SIZEOF_VOID_P */
+#else /* HAVE_LONG_LONG */
+#define    FORMAT_LEN  "%ld"   /* SQLLEN */
+#define    FORMAT_ULEN "%lu"   /* SQLULEN */
+#endif /* HAVE_LONG_LONG */
+#endif /* SIZEOF_LONG */
 #endif /* WIN32 */
 #define    CAST_PTR(type, ptr) (type)((LONG_PTR)(ptr))
 #define    CAST_UPTR(type, ptr)    (type)((ULONG_PTR)(ptr))
index ea31240a8df2166453f4af3ebd4e911386a4ca18..35ab8c4de42dab4bd5b0077e79aa956edbb832b3 100644 (file)
--- a/results.c
+++ b/results.c
@@ -4728,7 +4728,7 @@ mylog("num_cols=%d gdatainfo=%d\n", QR_NumPublicResultCols(s.res), gdata_allocat
    if (gdata)
    {
        for (i = 0; i < gdata_allocated; i++)
-           gdata[i].data_left = -1;
+           GETDATA_RESET(gdata[i]);
    }
    conn = SC_get_conn(s.stmt);
    switch (s.fOption)
index 661963f80434c6f14b174301a69bdb488c30690f..5f94b63bf850648c32070819b1a4b5e2acc8cb81 100644 (file)
@@ -1691,7 +1691,7 @@ inolog("%s: stmt=%p ommitted++\n", func, self);
        mylog("fetch: cols=%d, lf=%d, opts = %p, opts->bindings = %p, buffer[] = %p\n", num_cols, lf, opts, opts->bindings, opts->bindings[lf].buffer);
 
        /* reset for SQLGetData */
-       gdata->gdata[lf].data_left = -1;
+       GETDATA_RESET(gdata->gdata[lf]);
 
        if (NULL == opts->bindings)
            continue;