Fix SQL_DATA_AT_EXEC processing for large objects.
authorHeikki Linnakangas <heikki.linnakangas@iki.fi>
Mon, 21 Dec 2015 12:01:57 +0000 (14:01 +0200)
committerHeikki Linnakangas <heikki.linnakangas@iki.fi>
Mon, 21 Dec 2015 12:22:03 +0000 (14:22 +0200)
Hiroshi Inoue identified another bug with large objects, with
ByteaAsLongVarBinary=1, but I'll deal with that separately.

Reported by Vadym Krevs.

convert.c
test/expected/large-object-data-at-exec.out [new file with mode: 0644]
test/src/large-object-data-at-exec-test.c [new file with mode: 0644]
test/tests

index dd7672071dd7ff83a03968696e14b58221417c21..2297e6a2ee42aece307ab113ceda4248a495eafe 100644 (file)
--- a/convert.c
+++ b/convert.c
@@ -4118,14 +4118,11 @@ inolog("ipara=%p paramType=%d %d proc_return=%d\n", ipara, ipara ? ipara->paramT
        }
        else if (!handling_large_object)
        {
-           CVT_APPEND_CHAR(qb, '?');
-           return SQL_SUCCESS;
+           /* shouldn't happen */
+           qb->errormsg = "unexpected NULL parameter value";
+           qb->errornumber = STMT_EXEC_ERROR;
+           return SQL_ERROR;
        }
-
-       /* shouldn't happen */
-       qb->errormsg = "unexpected NULL parameter value";
-       qb->errornumber = STMT_EXEC_ERROR;
-       return SQL_ERROR;
    }
 
    /* replace DEFAULT with something we can use */
diff --git a/test/expected/large-object-data-at-exec.out b/test/expected/large-object-data-at-exec.out
new file mode 100644 (file)
index 0000000..9cd6ed9
--- /dev/null
@@ -0,0 +1,8 @@
+connected
+inserting large object with len 8...
+reading it back...
+hex: 0102030405060708
+inserting large object with len 100...
+reading it back...
+hex: 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F60616263
+disconnecting
diff --git a/test/src/large-object-data-at-exec-test.c b/test/src/large-object-data-at-exec-test.c
new file mode 100644 (file)
index 0000000..6ef09de
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+ * Test data-at-execution, for a large object.
+ */
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "common.h"
+
+static void
+printhex(unsigned char *b, int len)
+{
+   int i;
+
+   printf("hex: ");
+   for (i = 0; i < len; i++)
+       printf("%02X", b[i]);
+}
+
+/*
+ * Insert a large object to table, using a data-at-exec param. Then read it
+ * back and print.
+ */
+static void
+do_test(HSTMT hstmt, int testno, int lobByteSize, char *lobData)
+{
+   char        sql[200];
+   int         rc;
+   SQLLEN      cbParam1;
+   char        *buf;
+   SQLLEN      ind;
+
+   /**** Insert a Large Object */
+   printf("inserting large object with len %d...\n", lobByteSize);
+   snprintf(sql, sizeof(sql),
+            "INSERT INTO lo_test_tab VALUES (%d, ?)", testno);
+   rc = SQLPrepare(hstmt, (SQLCHAR *) sql, SQL_NTS);
+   CHECK_STMT_RESULT(rc, "SQLPrepare failed", hstmt);
+
+   /* bind LO param as a data at execution parameter */
+   cbParam1 = SQL_DATA_AT_EXEC;
+   rc = SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT,
+                         SQL_C_BINARY, /* value type */
+                         SQL_LONGVARBINARY,    /* param type */
+                         0,            /* column size */
+                         0,            /* dec digits */
+                         lobData,      /* param value ptr */
+                         0,            /* buffer len */
+                         &cbParam1     /* StrLen_or_IndPtr */);
+   CHECK_STMT_RESULT(rc, "SQLBindParameter failed", hstmt);
+
+   /* Execute */
+   rc = SQLExecute(hstmt);
+
+   /* Check for data-at-execute parameters */
+   if (SQL_NEED_DATA == rc)
+   {
+       SQLLEN lobChunkSize = 4096;
+       SQLPOINTER pParamId = 0;
+       const char *what = 0;
+
+       /* Get the parameter that needs data */
+       what = "SQLParamData failed (first call)";
+       rc = SQLParamData(hstmt, &pParamId);
+       int error = SQL_SUCCESS != rc && SQL_SUCCESS_WITH_INFO != rc && SQL_NEED_DATA != rc;
+       if (SQL_NEED_DATA == rc)
+       {
+           /* Send parameter data in chunks */
+           what = "SQLPutData failed (next chunk)";
+           while (!error && lobByteSize > lobChunkSize)
+           {
+               rc = SQLPutData(hstmt, (SQLPOINTER)pParamId, lobChunkSize);
+               lobByteSize -= lobChunkSize;
+               error = SQL_SUCCESS != rc && SQL_SUCCESS_WITH_INFO != rc && SQL_NEED_DATA != rc;
+           }
+
+           /* Send final chunk */
+           if (!error)
+           {
+               what = "SQLPutData failed (final chunk)";
+               rc = SQLPutData(hstmt, (SQLPOINTER)pParamId, lobByteSize);
+               error = SQL_SUCCESS != rc && SQL_SUCCESS_WITH_INFO != rc && SQL_NEED_DATA != rc;
+           }
+
+           /* Make final call */
+           if (!error)
+           {
+               what = "SQLParamData failed (final call)";
+               rc = SQLParamData(hstmt, &pParamId);
+               error = SQL_SUCCESS != rc && SQL_SUCCESS_WITH_INFO != rc;
+           }
+       }
+       CHECK_STMT_RESULT(rc, (char*)what, hstmt);
+   }
+   else
+   {
+       print_diag("SQLExecute didn't return SQL_NEED_DATA as expected",
+                  SQL_HANDLE_STMT, hstmt);
+       exit(1);
+   }
+
+   rc = SQLFreeStmt(hstmt, SQL_CLOSE);
+   CHECK_STMT_RESULT(rc, "SQLFreeStmt failed", hstmt);
+
+   /**** Read it back ****/
+   printf("reading it back...\n");
+
+   snprintf(sql, sizeof(sql),
+            "SELECT id, large_data FROM lo_test_tab WHERE id = %d", testno);
+   SQLExecDirect(hstmt, (SQLCHAR *) sql, SQL_NTS);
+   rc = SQLFetch(hstmt);
+   CHECK_STMT_RESULT(rc, "SQLFetch failed", hstmt);
+
+   buf = malloc(lobByteSize * 2);
+   rc = SQLGetData(hstmt, 2, SQL_C_BINARY, buf, lobByteSize * 2, &ind);
+   CHECK_STMT_RESULT(rc, "SQLGetData failed", hstmt);
+
+   printhex(buf, ind);
+   printf("\n");
+
+   free(buf);
+
+   rc = SQLFreeStmt(hstmt, SQL_CLOSE);
+   CHECK_STMT_RESULT(rc, "SQLFreeStmt failed", hstmt);
+}
+
+int
+main(int argc, char **argv)
+{
+   int         rc;
+   HSTMT       hstmt = SQL_NULL_HSTMT;
+   char        param1[8] = { 1, 2, 3, 4, 5, 6, 7, 8 };
+   char        param2[100];
+   int         i;
+
+   test_connect();
+
+   rc = SQLAllocHandle(SQL_HANDLE_STMT, conn, &hstmt);
+   if (!SQL_SUCCEEDED(rc))
+   {
+       print_diag("failed to allocate stmt handle", SQL_HANDLE_DBC, conn);
+       exit(1);
+   }
+
+   do_test(hstmt, 101, sizeof(param1), param1);
+
+   for (i = 0; i < sizeof(param2); i++)
+   {
+       param2[i] = i % 200;
+   }
+   do_test(hstmt, 102, sizeof(param2), param2);
+
+   /* Clean up */
+   test_disconnect();
+
+   return 0;
+}
index eb5fbe91e539bdeaabf1ab8feb2df8f40dc249be..1909983bc60a62ed798f59ce6f24ff2dd9d57b88 100644 (file)
@@ -48,4 +48,5 @@ TESTBINS = src/connect-test \
    src/diagnostic-test \
    src/numeric-test \
    src/large-object-test \
+   src/large-object-data-at-exec-test \
    src/odbc-escapes-test