Received: from postgresql.org.org (webmail.postgresql.org [216.126.85.28]) by postgresql.org (8.11.3/8.11.1) with SMTP id f44J1iA00672 for ; Fri, 4 May 2001 15:01:44 -0400 (EDT) (envelope-from pgsql-interfaces-owner@postgresql.org) Received: from smtp01.pumatech.com (smtp01.pumatech.com [207.88.93.2]) by postgresql.org (8.11.3/8.11.1) with ESMTP id f423UBd61411 for ; Tue, 1 May 2001 23:30:12 -0400 (EDT) (envelope-from Kmillard@pumatech.com) Received: by smtp01.pumatech.com with Internet Mail Service (5.5.2653.19) id ; Tue, 1 May 2001 06:38:31 -0700 Message-ID: From: Keith Millard To: "'pgsql-interfaces@postgresql.org'" Subject: Postgres odbc driver bug Date: Tue, 1 May 2001 06:36:36 -0700 MIME-Version: 1.0 X-Mailer: Internet Mail Service (5.5.2653.19) Content-Type: multipart/mixed; boundary="----_=_NextPart_000_01C0D243.BE782300" X-Archive-Number: 200105/21 X-Sequence-Number: 1547 This message is in MIME format. Since your mail reader does not understand this format, some or all of this message may not be legible. ------_=_NextPart_000_01C0D243.BE782300 Content-Type: multipart/alternative; boundary="----_=_NextPart_001_01C0D243.BE782300" ------_=_NextPart_001_01C0D243.BE782300 Content-Type: text/plain; charset="iso-8859-1" We have found a reproducible problem with the Postgres ODBC driver. It has to do with the parameter replacement feature in the driver. Below is a piece of test code that shows the problem. We were able to fix the problem by making a change to execute.c in the ODBC driver code (attached). odbc driver version : Found in both 6.50 and 7.01.0004. postgresql database version : Found with both 7.0.2 and 7.0.3 versions of Postgres. Server Operating System: Windows2000 Client Operation System: Windows2000 Explanation of the odbc driver bug: /***************************************** * ERROR HAPPENS HERE: * If we set the buffer that we binded to column "text1" now, it will FAIL. * The SQLExecute in doQuery() will succeed (finding 0 rows), and the * SQLFetchScroll call will return SQL_NO_DATA_FOUND. * * This is because of a bug in the odbc driver. The call to SQLNumResultCols * will execute the query in the driver, but the buffer that is binded is * empty, so no rows will be found. This of course assumes that the table * doesn't contain any empty records for the "text1" column, which is correct * for our test. Then when we call SQLExcute, the driver will realize it has * already done the query work and will not do it again, even though our * buffer for parameter substitution has changed (next line of code below * this comment). *****************************************/ Zip file is attached. The code is currently set up to fail, but I describe in my comments that you can uncomment the code that sets the buffer before SQLNumResultCols to make things will work. Thanks for your attention, Keith Millard _____ Alex Shore.. Senior Software Engineer.. Pumatech.. tel 603-888-0666 x1017.. .. ashore@pumatech.com.. www.pumatech.com.. ------_=_NextPart_001_01C0D243.BE782300 Content-Type: text/html; charset="iso-8859-1" Content-Transfer-Encoding: quoted-printable

We h= ave found a reproducible problem with the Postgres ODBC driver.  It has to do with the parameter replacement feature in the driver.  Below is a piece of test code that shows the problem.  We were able to fix the problem by making a change to execute.c in the ODBC driver code (attached).=

 

odbc driver version : Found in both 6.50 and 7.01.0004.=

post= gresql database version : Found with both 7.0.2 and 7.0.3 versions of Postgres.

Serv= er Operating System: Windows2000

Clie= nt Operation System: Windows2000

 

Explanation of the odbc driver bug:

=  

=  

   /***********************= ******************
    * ERROR HAPPENS HERE:
    * If we set the buffer that we binded to column "text1" now, it will FAIL.
    * The SQLExecute in doQuery() will succeed (finding 0 ro= ws), and the
    * SQLFetchScroll call will return SQL_NO_DATA_FOUND.
    *
    * This is because of a bug in the odbc driver. The call = to SQLNumResultCols
    * will execute the query in the driver, but the buffer t= hat is binded is
    * empty, so no rows will be found.  This of course assumes that the table
    * doesn't contain any empty records for the "text1" column, which is correct
    * for our test.  Then when we call SQLExcute, the driver will realize it has
    * already done the query work and will not do it again, = even though our
    * buffer for parameter substitution has changed (next li= ne of code below
    * this comment).
    *****************************************/
=

=  

=  Zip file is attached.  The code is currently set up to fai= l, but I describe in my comments that you can uncomment the code that sets the buffer before SQLNumResultCols to make things will work.=

 

Than= ks for your attention,

Keith Millard

=  

=  


3D"Keith

3D"Alex
------_=_NextPart_001_01C0D243.BE782300-- ------_=_NextPart_000_01C0D243.BE782300 Content-Type: application/octet-stream; name="pgodbc_test.zip" Content-Transfer-Encoding: base64 Content-Disposition: attachment; filename="pgodbc_test.zip" UEsDBBQAAAAIAAlcmioKD05ezQkAABgiAAAUAAAAVGVzdFBvc3RHcmVzT0RC Qy5jcHC9GWtz4kbyc7Zq/0PHqfjA0Rp7L7cfNnGuMOA1dRgIknfvzrWlEtKA VREjopHs9T7++3X3zEgCP2AvIaoyhlZPT78fM60WeELl41TlbzKhRt3TzmG4 XMJr6IpZLIWC/FqAkHl2B8s0ljnM0oxhYSpVmggIlsskDoM8TuXh82etFv0B f/QhWEChYjmHAG4EUlDxYolL8mCKn5pQjDsgA4cA5yJDavhH1CORB3GiXjOh boArAiVABgsBJ7AXJOLDHr+6VCIrIQ4sA6Vu0ywCJDtNAvkbI3m8n12M5CMk qNeTwNB1h/SijTRIGfrNS5QwKRZSQSyZJc31CyQgPuTHexDISH9/iRtP0/ya mX/bnnTO2xMHEiGR6KsfjT6eP/sulmFSRAJ+vo1llN6qw+tf6lCVR3F6D5ah +taAYX63FPcXJ/F0Hfh7ch+CDDOQ+HrxAt4k6TRIFCJFbHAYe/7p5Zk/6A3h +OhoBT68vPA7o4ELL1fAKO6w57q+1/cGPdgjX5qjL0EaTUM27p7erbak23M7 k/5pzz/tD7tEEwBarUQEN0L7RFrkDkSp/FsOUgi0Zw55CkoItKwqBBE87w3f wty/FvIGFX30E0LImAihfTXE/XVw3h52ka25r/JFjuDh5WCAb8LrIDtA6FJ9 JPeqXpC6Zsjpg2w+f4aMIlEysVl9WsxmVzXlvP/JIvWHXu9Nb4JokpAGQq7j fSdkFM9oT2ONUZFBkccJzAoZUkihYSjoonSQzmPZaP5U+406qgF+LTDCar87 7L19OUsZyFscvPgzH5TzAIYYVa9BP4sglgwcF9kyVRreW88d6BCAysfcogjZ /sFE5EUmFS86IshmDrZ/DlpaL8Rig74E2Tx0QHsBfr+5et98/uwTbvsNvc1C bcVpmiYwdYswRG7RR/KsEKxM9ldgo/CPjDyubiWExTNoIPzbE3IG373sdDBK YH8fkVdh/ru+d+73h2ejJi/8xJ/4XOCuwVycph8a5J0O7JktYIbZUUSYeNbj z4GLU3/0L8MCccZq5WjAX18q5j00SwC/k9vURaj8aGci8BZfIULNADPMVaJ8 gVIgIMGUkaXF/JpSRMJxsS7roIJWlrLxs0tT4R5/2FbxrGE10Hx8O4Px9Dbf aBVU2/AeKxlGRwFHBhZvkVHZvHr5j1c6rzGYq2y0BsylqqMSnOiqPMgLZWMG 61m4vGtYuo4p3EYD5q2h5NQq8iqC3h/f7zXtTrOGrgQnJ0efP+sSgF+bn8wy 4uEEzdhOkjQ8x8qdiAZZVRcHHyuJQz9JlQbm7GuCpYM0NJVvT2resL//AHDd RSqFr+vCcuWKvCdv2nmeGSmYmbbnTXzqT/y3vYnbHw2dxk0aRwdNejl6y6/+ rtn23L+CzUeUh2w4hu19rfmdMFPGQ8VQJ5VShDqsoaH3dsAQaBRSxXOJvQMn +ab1KqMw5xG00jU34BkfLNUPVbDu1FGMzOQsq3IbEqXntC+9UWd0cdH3ShHw 3XhEXcmEfajC8EdnZ3UCu5bIAGsr76UiTs4mFz2YSKxOurEKtU4a1vtgt5YA rghnmRAPBwOsRMHmNZR9oMo2mxtQbKO4h4m5y8V/P9e7cwT88MNadeL8aJvV +D0VOsp1pbT4RCIRuYCr91DHLCtR1apuYT/TQ9TNZzuqFVPCSp8s3Twb8NRk fLDEuLQoYZbeOtivZcGdtUm9hZfilsPzqhpijAQLsVAib5TIDhw5tVHH1pHN 2c71LjxrYQf29Vixu+zr2lljcTc2GqNvRlbzFq5wUnu/UiIbXJMPmoxN3UFv 0Ot42GXPsnQBZgSGd+e9SQ94nkWK/7Rltm4TbRCkivMsUsWJJAdDm0ivKU7b bpyJJc7CDa0cx/BApHZUGXTA8wR1irM1To8ZzHmw1bxO0ZlFRu2hHd71bH+P c1o9DjLM/7nILP+1IKk9x4/Aifdxe9K+QKbHl94TWB2fTwoeR7BnCQ9jvPrx kRfo2a0W1qo0o3OUMF6gGqJ4HufqYfx6XajC6RHijco5mrUA2oyNvrhvInxH 4VI5QTc1R1RkZEw5s1TPObGc6/ylZ4C1ERl2NQdU+3z9LGDz5pdSQs5+5nwD f1JKrH6t9Aoueq/pKtmRXdsbTEbv/PZk0v6P7/b/i60uk9xpK/k1rLn+Wc/r nPe6/tibOPuc80vedlPb7yWC3gcRFrlNYX9Fd30m8vDaRWGTpEycRJB14Q97 /8ayc7zOyAnv2ZtMRpPPn+ug/vBte9Dvmqr1lFnWaA1Hfrfttf2z0eWwu0lj Hlcf9ZFmNG4YypEQ1DLDUo+0y5cYCpP0VsGMBMU++uTkl+8pEqiVabIbG+nu hVCdxhNxY5iqGpmqZq41N5TfaoVjc4tmq/5GZ/hjfW89IT3Y+wIXBveiPaC0 ChIX1FLfwbaPZvIA+jO4FYCNEadLWyWvg5zAU6yFaCYsmiaP2topqQeLESdO ErhNs98sOToR13k3y7AfB1kspkgwnUFGhmf8qeBUHNOZruRCXUUbhHSYY07U kF7ZSB4ayLZPS+ujZZqhWte3R8ePfPz/YOcyLBYToYoEx6xElWG4T3reXQ74 ertxwMN5ezzuDV2gJu71n2jSs3Z/YDUOHlKpWQhtVlpFYys6d0KijRkSp7ue I7Z20yndwZJaTXLa2EyiFhYr6ccyUfESK77ZEWGAczo5VoAizu0NDV84RFl8 I7JDZpy3QGnXLWsJ8u7CiEYUuEuw5DQlB3e4r0ziQisTv1lyYrHM7zBdpajP VZefpYWMDkFLgGyH6PgoQKBUseA7tiCv7pgq9xeKLkCw6c4D5CmQd3oLVBiG WKTKu7jVrtaB2+s4vCbOTCRaioRPIWdu3FBFEnHpw+iKLU3acGoasEYKkvij IC+5DkoNBgnCozu6qqlrkLIC25+XypSSG60M5iiIA+JGkIr5zJb4sdSMhonN pe3C0cGmKo/zgq5DaGtq6eWcPE6i1JDQlRKrNEITiQS7IUuOb5LCdLEQWGH+ vxyyKYMAlZAtLoyqlE0XJtgD4zxE9Ly7pXggr3e5XUecYZEk5BMljp4GkRv0 46G+0XxkEnRrc3OB2C5az6ac+gnCMZ8gnOhi8sDZgeW4ajIJaJlfgdpt1lBZ mFWYlWsFWs/EXaHCLJ4KpPj0HMYzhtVcE+Kn0FhFOIhU6tuOKCp1G0Qecax9 t8W3inxqQWlHWmGVvP0O2pm2FsCYpiyQAPUrzPjF8fv1IxqA+qGRRqh55cpx TKuFaULpm37dlnEnEJXHeA95A83l5AnwtCvoZ0uH0I+7aRA3z/0huRR288Lt B+b7i8gma+o3iecLf/LAS10rFEsGVL1u5eZsSn2EBwBlx/h4P/o/UEsDBBQA AAAIAA1cmioSaPj8GQQAADURAAAUAAAAVGVzdFBvc3RHcmVzT0RCQy5kc3Dt V21v4jgQ/l6p/2Euex/a1R6wZVXtleMkSAJCW15E6O11dRJynAG8DXFkOy39 9ztJCA2lpb1277QfViJi7Bk/fmbsPI7fQF9wJbWcGXDwGkMZowLPJIGQMFLy K3IDHREi/AYDtsSmNUFtRlKbrkI9dNq2RZ4R41dsjjC8iVA1//jw5+HBm33A XaQ4ZjCAdiLCIJvgHXSkWjIDf6HSQkZwWqnVUpy3b8EZwmA4AdfpTah5eJB2 T1rj7uRy5IL1WUT1EzhafTw9BltGWhLbVhyHgjNDQBbUVrX3tXo6zO50m/cT IP45goN+Mj88+KXvel6r68JkITTQL5IGGFyzUASwZFc4I7YVmEjwM/ImDYvX pUq0iOYw6Lc+ue9KUIlGikNwV7FUBvprFOByuWRRAOmjkqg0omRmaFCdwU7t K0THqjw86lImwFkEOkYuZreUAZfRTMwTlVUFbhYYpXNGG8Klwf4tBMQw86W8 l4zWEqh6QCPTjoJ4KCKqBa0c4Iot4xDPXpRDCr27tbZXxnoYmQZo4ae1XEjB UcOMyGynytRjtB6fcowhMo0WHPn0F6R5P7nRjp+FnCfzUtx887dxLqLi/Uw7 RuPhCFphKG9GqOwsewdjjAKMuKCi1DZBHufTdGD6NoNlbfWfS87CETOLzGGP Rk0eVnCFhwdjz24qntvEptcBsH49olU7tqDZfEYdc97ZTO2W58KFxmm/Y5eI bbqzAk3Pha+Y2uaehQwTEydm6ggF1h16OaIXGVRLDARJzL64CVNzXCPduXaZ 7Se1l88zqOywaDlOzo8WAKqRDOVcQvVzHardv6E6PIGqQ3umN6ifWJk5cNz2 RTe3p/Zw4A3P3XWr37Y9Mi8TS5uAzVaVBbU6DlR5MdF3m2OD6l20J+OWPcmh LwnukqcUtlKj/QTVkJT5Q+13qAabCYqgx/1tz66fNH3NUynON2QJN/MWCW08 253nvcEngiDputoZn/vgClWEYf2kEgo/FW+1NueBWFs3ItKxlGHWIDUMwvna w4JrFhdheoFhgUOv9Z3FElPAJyLIewOfFwFk8njd+LHYbDaLTnx9qw0uz/ha s6p0TCzoQDjr1T+eFmX9WdHvV1FSXvfcc5+vvsXB+WLtff+49hbY+5X3oajX 6O4doT1cnqTxbzR3mWvilx7pYnBfF6cv0N7uF9grwP/xhA/r7/QJ/Z3+1N8f Xy2CdJdviwZU48A3tzGeaYzNT1H+n8qcKvXAIaEuf6/nqpP35J/fz/hsfjL0 TtryWbpKJjFYnkwUx+xqrcsngIMzloRmSg4SSbB4HDd4g69WDcUbdOlrqIYM woagZxF/bfjMlLBLoCmkN7wY226z8s/OpY5QX3FReMUp9/jX570lcen2upVO 3pVVr2gUC5a3Ntetb1BLAQIyCxQAAAAIAAlcmioKD05ezQkAABgiAAAUAAAA AAAAAAEAIAC2gQAAAABUZXN0UG9zdEdyZXNPREJDLmNwcFBLAQIyCxQAAAAI AA1cmioSaPj8GQQAADURAAAUAAAAAAAAAAEAIAC2gf8JAABUZXN0UG9zdEdy ZXNPREJDLmRzcFBLBQYAAAAAAgACAIQAAABKDgAAAAA= ------_=_NextPart_000_01C0D243.BE782300 Content-Type: application/octet-stream; name="execute.c" Content-Transfer-Encoding: quoted-printable Content-Disposition: attachment; filename="execute.c" /* Module: execute.c * * Description: This module contains routines related to=20 * preparing and executing an SQL statement. * * Classes: n/a * * API functions: SQLPrepare, SQLExecute, SQLExecDirect, SQLTransact, * SQLCancel, SQLNativeSql, SQLParamData, SQLPutData * * Comments: See "notice.txt" for copyright and license information. * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "psqlodbc.h" #include #include #ifndef WIN32 #include "iodbc.h" #include "isqlext.h" #else #include #include #endif #include "connection.h" #include "statement.h" #include "qresult.h" #include "convert.h" #include "bind.h" #include "lobj.h" extern GLOBAL_VALUES globals; // Perform a Prepare on the SQL statement RETCODE SQL_API SQLPrepare(HSTMT hstmt, UCHAR FAR *szSqlStr, SDWORD cbSqlStr) { static char *func =3D "SQLPrepare"; StatementClass *self =3D (StatementClass *) hstmt; mylog( "%s: entering...\n", func); if ( ! self) { SC_log_error(func, "", NULL); return SQL_INVALID_HANDLE; } =20=20=20=20 /* According to the ODBC specs it is valid to call SQLPrepare mulitple tim= es. In that case, the bound SQL statement is replaced by the new one=20 */ switch(self->status) { case STMT_PREMATURE: mylog("**** SQLPrepare: STMT_PREMATURE, recycle\n"); SC_recycle_statement(self); /* recycle the statement, but do not remove p= arameter bindings */ break; case STMT_FINISHED: mylog("**** SQLPrepare: STMT_FINISHED, recycle\n"); SC_recycle_statement(self); /* recycle the statement, but do not remove p= arameter bindings */ break; case STMT_ALLOCATED: mylog("**** SQLPrepare: STMT_ALLOCATED, copy\n"); self->status =3D STMT_READY; break; case STMT_READY: mylog("**** SQLPrepare: STMT_READY, change SQL\n"); break; case STMT_EXECUTING: mylog("**** SQLPrepare: STMT_EXECUTING, error!\n"); self->errornumber =3D STMT_SEQUENCE_ERROR; self->errormsg =3D "SQLPrepare(): The handle does not point to a statemen= t that is ready to be executed"; SC_log_error(func, "", self); return SQL_ERROR; default: self->errornumber =3D STMT_INTERNAL_ERROR; self->errormsg =3D "An Internal Error has occured -- Unknown statement st= atus."; SC_log_error(func, "", self); return SQL_ERROR; } if (self->statement) free(self->statement); self->statement =3D make_string(szSqlStr, cbSqlStr, NULL); if ( ! self->statement) { self->errornumber =3D STMT_NO_MEMORY_ERROR; self->errormsg =3D "No memory available to store statement"; SC_log_error(func, "", self); return SQL_ERROR; } self->prepare =3D TRUE; self->statement_type =3D statement_type(self->statement); // Check if connection is onlyread (only selects are allowed) if ( CC_is_onlyread(self->hdbc) && STMT_UPDATE(self)) { self->errornumber =3D STMT_EXEC_ERROR; self->errormsg =3D "Connection is readonly, only select statements are al= lowed."; SC_log_error(func, "", self); return SQL_ERROR; } return SQL_SUCCESS; } // - - - - - - - - - // Performs the equivalent of SQLPrepare, followed by SQLExecute. RETCODE SQL_API SQLExecDirect( HSTMT hstmt, UCHAR FAR *szSqlStr, SDWORD cbSqlStr) { StatementClass *stmt =3D (StatementClass *) hstmt; RETCODE result; static char *func =3D "SQLExecDirect"; mylog( "%s: entering...\n", func); =20=20=20=20 if ( ! stmt) { SC_log_error(func, "", NULL); return SQL_INVALID_HANDLE; } if (stmt->statement) free(stmt->statement); // keep a copy of the un-parametrized statement, in case // they try to execute this statement again stmt->statement =3D make_string(szSqlStr, cbSqlStr, NULL); if ( ! stmt->statement) { stmt->errornumber =3D STMT_NO_MEMORY_ERROR; stmt->errormsg =3D "No memory available to store statement"; SC_log_error(func, "", stmt); return SQL_ERROR; } mylog("**** %s: hstmt=3D%u, statement=3D'%s'\n", func, hstmt, stmt->statem= ent); stmt->prepare =3D FALSE; // If an SQLPrepare was performed prior to this, but was left in=20 // the premature state because an error occurred prior to SQLExecute // then set the statement to finished so it can be recycled. if ( stmt->status =3D=3D STMT_PREMATURE ) stmt->status =3D STMT_FINISHED; stmt->statement_type =3D statement_type(stmt->statement); // Check if connection is onlyread (only selects are allowed) if ( CC_is_onlyread(stmt->hdbc) && STMT_UPDATE(stmt)) { stmt->errornumber =3D STMT_EXEC_ERROR; stmt->errormsg =3D "Connection is readonly, only select statements are al= lowed."; SC_log_error(func, "", stmt); return SQL_ERROR; } =09 mylog("%s: calling SQLExecute...\n", func); result =3D SQLExecute(hstmt); mylog("%s: returned %hd from SQLExecute\n", func, result); return result; } // Execute a prepared SQL statement RETCODE SQL_API SQLExecute( HSTMT hstmt) { static char *func=3D"SQLExecute"; StatementClass *stmt =3D (StatementClass *) hstmt; ConnectionClass *conn; int i, retval; mylog("%s: entering...\n", func); if ( ! stmt) { SC_log_error(func, "", NULL); mylog("%s: NULL statement so return SQL_INVALID_HANDLE\n", func); return SQL_INVALID_HANDLE; } /* If the statement is premature, it means we already executed it from an SQLPrepare/SQLDescribeCol type of scenario. So just return success. */ #if 0 //chuck if ( stmt->prepare && stmt->status =3D=3D STMT_PREMATURE) { stmt->status =3D STMT_FINISHED;=20=20=20=20=20=20=20 if (stmt->errormsg =3D=3D NULL) { mylog("%s: premature statement but return SQL_SUCCESS\n", func); return SQL_SUCCESS; } else { SC_log_error(func, "", stmt); mylog("%s: premature statement so return SQL_ERROR\n", func); return SQL_ERROR; } }=20=20 #endif mylog("%s: clear errors...\n", func); SC_clear_error(stmt); conn =3D SC_get_conn(stmt); if (conn->status =3D=3D CONN_EXECUTING) { stmt->errormsg =3D "Connection is already in use."; stmt->errornumber =3D STMT_SEQUENCE_ERROR; SC_log_error(func, "", stmt); mylog("%s: problem with connection\n", func); return SQL_ERROR; } if ( ! stmt->statement) { stmt->errornumber =3D STMT_NO_STMTSTRING; stmt->errormsg =3D "This handle does not have a SQL statement stored in i= t"; SC_log_error(func, "", stmt); mylog("%s: problem with handle\n", func); return SQL_ERROR; } /* If SQLExecute is being called again, recycle the statement. Note this should have been done by the application in a call to SQLFreeStmt(SQL_CLOSE) or SQLCancel. */ if (stmt->status =3D=3D STMT_FINISHED) { mylog("%s: recycling statement (should have been done by app)...\n", func= ); SC_recycle_statement(stmt); } /* Check if the statement is in the correct state */ //chuck if ((stmt->prepare && stmt->status !=3D STMT_READY) || //chuck (stmt->status !=3D STMT_ALLOCATED && stmt->status !=3D STMT_REA= DY)) { if ((stmt->prepare && (stmt->status !=3D STMT_READY && stmt->status !=3D= STMT_PREMATURE)) || //chuck (stmt->status !=3D STMT_ALLOCATED && stmt->status !=3D STMT_READY && = stmt->status !=3D STMT_PREMATURE)) { //chuck =09=09 stmt->errornumber =3D STMT_STATUS_ERROR; stmt->errormsg =3D "The handle does not point to a statement that is read= y to be executed"; SC_log_error(func, "", stmt); mylog("%s: problem with statement\n", func); return SQL_ERROR; } /* The bound parameters could have possibly changed since the last execute of this statement? Therefore check for params and re-copy. */ mylog("%s: Check for data at execution parameters\n", func); mylog("%s: stmt->parameters_allocated =3D %d\n", func,stmt->parameters_all= ocated); stmt->data_at_exec =3D -1; for (i =3D 0; i < stmt->parameters_allocated; i++) { /* Check for data at execution parameters */ mylog("%s: stmt->parameters[%d].data_at_exec =3D %d\n", func,i,stmt->par= ameters[i].data_at_exec); if ( stmt->parameters[i].data_at_exec =3D=3D TRUE) { if (stmt->data_at_exec < 0) stmt->data_at_exec =3D 1; else stmt->data_at_exec++; } } // If there are some data at execution parameters, return need data // SQLParamData and SQLPutData will be used to send params and execute the= statement. if (stmt->data_at_exec > 0) return SQL_NEED_DATA; mylog("%s: copying statement params: trans_status=3D%d, len=3D%d, stmt=3D'= %s'\n", func, conn->transact_status, strlen(stmt->statement), stmt->stateme= nt); // Create the statement with parameters substituted. retval =3D copy_statement_with_parameters(stmt); if( retval !=3D SQL_SUCCESS) /* error msg passed from above */ return retval; mylog(" stmt_with_params =3D '%s'\n", stmt->stmt_with_params); return SC_execute(stmt); } // - - - - - - - - - RETCODE SQL_API SQLTransact( HENV henv, HDBC hdbc, UWORD fType) { static char *func =3D "SQLTransact"; extern ConnectionClass *conns[]; ConnectionClass *conn; QResultClass *res; char ok, *stmt_string; int lf; mylog("entering %s: hdbc=3D%u, henv=3D%u\n", func, hdbc, henv); if (hdbc =3D=3D SQL_NULL_HDBC && henv =3D=3D SQL_NULL_HENV) { CC_log_error(func, "", NULL); return SQL_INVALID_HANDLE; } /* If hdbc is null and henv is valid, it means transact all connections on that henv.=20=20 */ if (hdbc =3D=3D SQL_NULL_HDBC && henv !=3D SQL_NULL_HENV) { for (lf=3D0; lf henv =3D=3D henv) if ( SQLTransact(henv, (HDBC) conn, fType) !=3D SQL_SUCCESS) return SQL_ERROR; } return SQL_SUCCESS;=20=20=20=20=20=20=20 } conn =3D (ConnectionClass *) hdbc; if (fType =3D=3D SQL_COMMIT) { stmt_string =3D "COMMIT"; } else if (fType =3D=3D SQL_ROLLBACK) { stmt_string =3D "ROLLBACK"; } else { conn->errornumber =3D CONN_INVALID_ARGUMENT_NO; conn->errormsg =3D"SQLTransact can only be called with SQL_COMMIT or SQL_= ROLLBACK as parameter"; CC_log_error(func, "", conn); return SQL_ERROR; }=20=20=20=20 /* If manual commit and in transaction, then proceed. */ if ( ! CC_is_in_autocommit(conn) && CC_is_in_trans(conn)) { mylog("SQLTransact: sending on conn %d '%s'\n", conn, stmt_string); res =3D CC_send_query(conn, stmt_string, NULL); CC_set_no_trans(conn); if ( ! res) { // error msg will be in the connection CC_log_error(func, "", conn); return SQL_ERROR; } ok =3D QR_command_successful(res);=20=20=20 QR_Destructor(res); if (!ok) { CC_log_error(func, "", conn); return SQL_ERROR; } }=20=20=20=20 return SQL_SUCCESS; } // - - - - - - - - - RETCODE SQL_API SQLCancel( HSTMT hstmt) // Statement to cancel. { static char *func=3D"SQLCancel"; StatementClass *stmt =3D (StatementClass *) hstmt; RETCODE result; #ifdef WIN32 HMODULE hmodule; FARPROC addr; #endif mylog( "%s: entering...\n", func); // Check if this can handle canceling in the middle of a SQLPutData? if ( ! stmt) { SC_log_error(func, "", NULL); return SQL_INVALID_HANDLE; } // Not in the middle of SQLParamData/SQLPutData so cancel like a close. if (stmt->data_at_exec < 0) { /* MAJOR HACK for Windows to reset the driver manager's cursor state: Because of what seems like a bug in the Odbc driver manager, SQLCancel does not act like a SQLFreeStmt(CLOSE), as many applications depend on this behavior. So, this=20 brute force method calls the driver manager's function on behalf of the application.=20=20 */ #ifdef WIN32 if (globals.cancel_as_freestmt) { hmodule =3D GetModuleHandle("ODBC32"); addr =3D GetProcAddress(hmodule, "SQLFreeStmt"); result =3D addr( (char *) (stmt->phstmt) - 96, SQL_CLOSE); } else { result =3D SQLFreeStmt( hstmt, SQL_CLOSE); } #else result =3D SQLFreeStmt( hstmt, SQL_CLOSE); #endif mylog("SQLCancel: SQLFreeStmt returned %d\n", result); SC_clear_error(hstmt); return SQL_SUCCESS; } // In the middle of SQLParamData/SQLPutData, so cancel that. // Note, any previous data-at-exec buffers will be freed in the recycle // if they call SQLExecDirect or SQLExecute again. stmt->data_at_exec =3D -1; stmt->current_exec_param =3D -1; stmt->put_data =3D FALSE; return SQL_SUCCESS; } // - - - - - - - - - // Returns the SQL string as modified by the driver. // Currently, just copy the input string without modification // observing buffer limits and truncation. RETCODE SQL_API SQLNativeSql( HDBC hdbc, UCHAR FAR *szSqlStrIn, SDWORD cbSqlStrIn, UCHAR FAR *szSqlStr, SDWORD cbSqlStrMax, SDWORD FAR *pcbSqlStr) { static char *func=3D"SQLNativeSql"; int len =3D 0; char *ptr; ConnectionClass *conn =3D (ConnectionClass *) hdbc; RETCODE result; mylog( "%s: entering...cbSqlStrIn=3D%d\n", func, cbSqlStrIn); ptr =3D (cbSqlStrIn =3D=3D 0) ? "" : make_string(szSqlStrIn, cbSqlStrIn, N= ULL); if ( ! ptr) { conn->errornumber =3D CONN_NO_MEMORY_ERROR; conn->errormsg =3D "No memory available to store native sql string"; CC_log_error(func, "", conn); return SQL_ERROR; } result =3D SQL_SUCCESS; len =3D strlen(ptr); if (szSqlStr) { strncpy_null(szSqlStr, ptr, cbSqlStrMax); if (len >=3D cbSqlStrMax) { result =3D SQL_SUCCESS_WITH_INFO; conn->errornumber =3D STMT_TRUNCATED; conn->errormsg =3D "The buffer was too small for the result."; } } if (pcbSqlStr) *pcbSqlStr =3D len; free(ptr); return result; } // - - - - - - - - - // Supplies parameter data at execution time. Used in conjuction = with // SQLPutData. RETCODE SQL_API SQLParamData( HSTMT hstmt, PTR FAR *prgbValue) { static char *func =3D "SQLParamData"; StatementClass *stmt =3D (StatementClass *) hstmt; int i, retval; mylog( "%s: entering...\n", func); if ( ! stmt) { SC_log_error(func, "", NULL); return SQL_INVALID_HANDLE; } mylog("%s: data_at_exec=3D%d, params_alloc=3D%d\n", func, stmt->data_at_ex= ec, stmt->parameters_allocated); if (stmt->data_at_exec < 0) { stmt->errornumber =3D STMT_SEQUENCE_ERROR; stmt->errormsg =3D "No execution-time parameters for this statement"; SC_log_error(func, "", stmt); return SQL_ERROR; } if (stmt->data_at_exec > stmt->parameters_allocated) { stmt->errornumber =3D STMT_SEQUENCE_ERROR; stmt->errormsg =3D "Too many execution-time parameters were present"; SC_log_error(func, "", stmt); return SQL_ERROR; } /* close the large object */ if ( stmt->lobj_fd >=3D 0) { lo_close(stmt->hdbc, stmt->lobj_fd); /* commit transaction if needed */ if (!globals.use_declarefetch && CC_is_in_autocommit(stmt->hdbc)) { QResultClass *res; char ok; res =3D CC_send_query(stmt->hdbc, "COMMIT", NULL); if (!res) { stmt->errormsg =3D "Could not commit (in-line) a transaction"; stmt->errornumber =3D STMT_EXEC_ERROR; SC_log_error(func, "", stmt); return SQL_ERROR; } ok =3D QR_command_successful(res); QR_Destructor(res); if (!ok) { stmt->errormsg =3D "Could not commit (in-line) a transaction"; stmt->errornumber =3D STMT_EXEC_ERROR; SC_log_error(func, "", stmt); return SQL_ERROR; } CC_set_no_trans(stmt->hdbc); } stmt->lobj_fd =3D -1; } /* Done, now copy the params and then execute the statement */ if (stmt->data_at_exec =3D=3D 0) { retval =3D copy_statement_with_parameters(stmt); if (retval !=3D SQL_SUCCESS) return retval; stmt->current_exec_param =3D -1; return SC_execute(stmt); } /* Set beginning param; if first time SQLParamData is called , start at 0. Otherwise, start at the last parameter + 1. */ i =3D stmt->current_exec_param >=3D 0 ? stmt->current_exec_param+1 : 0; /* At least 1 data at execution parameter, so Fill in the token value */ for ( ; i < stmt->parameters_allocated; i++) { if (stmt->parameters[i].data_at_exec =3D=3D TRUE) { stmt->data_at_exec--; stmt->current_exec_param =3D i; stmt->put_data =3D FALSE; *prgbValue =3D stmt->parameters[i].buffer; /* token */ break; } } return SQL_NEED_DATA; } // - - - - - - - - - // Supplies parameter data at execution time. Used in conjunction= with // SQLParamData. RETCODE SQL_API SQLPutData( HSTMT hstmt, PTR rgbValue, SDWORD cbValue) { static char *func =3D "SQLPutData"; StatementClass *stmt =3D (StatementClass *) hstmt; int old_pos, retval; ParameterInfoClass *current_param; char *buffer; mylog( "%s: entering...\n", func); if ( ! stmt) { SC_log_error(func, "", NULL); return SQL_INVALID_HANDLE; } =09 if (stmt->current_exec_param < 0) { stmt->errornumber =3D STMT_SEQUENCE_ERROR; stmt->errormsg =3D "Previous call was not SQLPutData or SQLParamData"; SC_log_error(func, "", stmt); return SQL_ERROR; } current_param =3D &(stmt->parameters[stmt->current_exec_param]); if ( ! stmt->put_data) { /* first call */ mylog("SQLPutData: (1) cbValue =3D %d\n", cbValue); stmt->put_data =3D TRUE; current_param->EXEC_used =3D (SDWORD *) malloc(sizeof(SDWORD)); if ( ! current_param->EXEC_used) { stmt->errornumber =3D STMT_NO_MEMORY_ERROR; stmt->errormsg =3D "Out of memory in SQLPutData (1)"; SC_log_error(func, "", stmt); return SQL_ERROR; } *current_param->EXEC_used =3D cbValue; if (cbValue =3D=3D SQL_NULL_DATA) return SQL_SUCCESS; /* Handle Long Var Binary with Large Objects */ if ( current_param->SQLType =3D=3D SQL_LONGVARBINARY) { /* begin transaction if needed */ if(!CC_is_in_trans(stmt->hdbc)) { QResultClass *res; char ok; res =3D CC_send_query(stmt->hdbc, "BEGIN", NULL); if (!res) { stmt->errormsg =3D "Could not begin (in-line) a transaction"; stmt->errornumber =3D STMT_EXEC_ERROR; SC_log_error(func, "", stmt); return SQL_ERROR; } ok =3D QR_command_successful(res); QR_Destructor(res); if (!ok) { stmt->errormsg =3D "Could not begin (in-line) a transaction"; stmt->errornumber =3D STMT_EXEC_ERROR; SC_log_error(func, "", stmt); return SQL_ERROR; } CC_set_in_trans(stmt->hdbc); } /* store the oid */ current_param->lobj_oid =3D lo_creat(stmt->hdbc, INV_READ | INV_WRITE); if (current_param->lobj_oid =3D=3D 0) { stmt->errornumber =3D STMT_EXEC_ERROR; stmt->errormsg =3D "Couldnt create large object."; SC_log_error(func, "", stmt); return SQL_ERROR; } /* major hack -- to allow convert to see somethings there */ /* have to modify convert to handle this better */ current_param->EXEC_buffer =3D (char *) ¤t_param->lobj_oid; /* store the fd */ stmt->lobj_fd =3D lo_open(stmt->hdbc, current_param->lobj_oid, INV_WRITE= ); if ( stmt->lobj_fd < 0) { stmt->errornumber =3D STMT_EXEC_ERROR; stmt->errormsg =3D "Couldnt open large object for writing."; SC_log_error(func, "", stmt); return SQL_ERROR; } retval =3D lo_write(stmt->hdbc, stmt->lobj_fd, rgbValue, cbValue); mylog("lo_write: cbValue=3D%d, wrote %d bytes\n", cbValue, retval); } else { /* for handling text fields and small binaries */ if (cbValue =3D=3D SQL_NTS) { current_param->EXEC_buffer =3D strdup(rgbValue); if ( ! current_param->EXEC_buffer) { stmt->errornumber =3D STMT_NO_MEMORY_ERROR; stmt->errormsg =3D "Out of memory in SQLPutData (2)"; SC_log_error(func, "", stmt); return SQL_ERROR; } } else { current_param->EXEC_buffer =3D malloc(cbValue + 1); if ( ! current_param->EXEC_buffer) { stmt->errornumber =3D STMT_NO_MEMORY_ERROR; stmt->errormsg =3D "Out of memory in SQLPutData (2)"; SC_log_error(func, "", stmt); return SQL_ERROR; } memcpy(current_param->EXEC_buffer, rgbValue, cbValue); current_param->EXEC_buffer[cbValue] =3D '\0'; } } } else { /* calling SQLPutData more than once */ mylog("SQLPutData: (>1) cbValue =3D %d\n", cbValue); if (current_param->SQLType =3D=3D SQL_LONGVARBINARY) { /* the large object fd is in EXEC_buffer */ retval =3D lo_write(stmt->hdbc, stmt->lobj_fd, rgbValue, cbValue); mylog("lo_write(2): cbValue =3D %d, wrote %d bytes\n", cbValue, retval); *current_param->EXEC_used +=3D cbValue; } else { buffer =3D current_param->EXEC_buffer; if (cbValue =3D=3D SQL_NTS) { buffer =3D realloc(buffer, strlen(buffer) + strlen(rgbValue) + 1); if ( ! buffer) { stmt->errornumber =3D STMT_NO_MEMORY_ERROR; stmt->errormsg =3D "Out of memory in SQLPutData (3)"; SC_log_error(func, "", stmt); return SQL_ERROR; } strcat(buffer, rgbValue); mylog(" cbValue =3D SQL_NTS: strlen(buffer) =3D %d\n", strlen(buf= fer)); *current_param->EXEC_used =3D cbValue; /* reassign buffer incase realloc moved it */ current_param->EXEC_buffer =3D buffer; } else if (cbValue > 0) { old_pos =3D *current_param->EXEC_used; *current_param->EXEC_used +=3D cbValue; mylog(" cbValue =3D %d, old_pos =3D %d, *used =3D %d\n", cbValue= , old_pos, *current_param->EXEC_used); /* dont lose the old pointer in case out of memory */ buffer =3D realloc(current_param->EXEC_buffer, *current_param->EXEC_use= d + 1); if ( ! buffer) { stmt->errornumber =3D STMT_NO_MEMORY_ERROR; stmt->errormsg =3D "Out of memory in SQLPutData (3)"; SC_log_error(func, "", stmt); return SQL_ERROR; } memcpy(&buffer[old_pos], rgbValue, cbValue); buffer[*current_param->EXEC_used] =3D '\0'; /* reassign buffer incase realloc moved it */ current_param->EXEC_buffer =3D buffer; =09=09=09=09 } else { SC_log_error(func, "bad cbValue", stmt); return SQL_ERROR; } } } return SQL_SUCCESS; } ------_=_NextPart_000_01C0D243.BE782300--