Skip to content

Commit 3bc5be1

Browse files
committed
Support Basic Prepared Statement
1 parent c3ca236 commit 3bc5be1

File tree

3 files changed

+51
-42
lines changed

3 files changed

+51
-42
lines changed

src/cursor.cpp

Lines changed: 29 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "getdata.h"
2525
#include "dbspecific.h"
2626
#include <datetime.h>
27+
#include "handle.h"
2728

2829
enum
2930
{
@@ -682,7 +683,6 @@ int GetDiagRecs(Cursor* cur)
682683
return 0;
683684
}
684685

685-
static bool first_prepare = false;
686686
static PyObject * prepare_statement(Cursor* cur, PyObject* pSql, PyObject* params, bool skip_first)
687687
{
688688
// Internal function to execute SQL, called by .prepareStatement
@@ -712,13 +712,14 @@ static PyObject * prepare_statement(Cursor* cur, PyObject* pSql, PyObject* param
712712
return 0;
713713
}
714714

715-
first_prepare = true;
716715
Py_INCREF(cur);
717-
return (PyObject*)cur;
716+
Handle *hndl = Handle_New(cur);
717+
hndl->hstmt = cur->hstmt;
718+
return (PyObject*)hndl;
718719
}
719720

720721

721-
static PyObject* executePreparedStatement(Cursor* cur, PyObject* params, bool skip_first)
722+
static PyObject* executePreparedStatement(Cursor* cur, Handle *hndl, PyObject* params, bool skip_first)
722723
{
723724

724725
if (params)
@@ -736,23 +737,16 @@ static PyObject* executePreparedStatement(Cursor* cur, PyObject* params, bool sk
736737

737738
FreeParameterData(cur);
738739

739-
SQLCancelHandle(SQL_HANDLE_STMT, cur->hstmt);
740-
/*if (first_prepare) {
741-
//SQLFreeStmt(cur->hstmt, SQL_CLOSE);
742-
//SQLFreeStmt(cur->hstmt, SQL_UNBIND);
743-
//SQLFreeStmt(cur->hstmt, SQL_RESET_PARAMS);
744-
first_prepare = false;
745-
} else {
746-
SQLCancelHandle(SQL_HANDLE_DBC, cur->hstmt);
747-
}*/
740+
SQLCancelHandle(SQL_HANDLE_STMT, hndl->hstmt);
741+
748742
const char* szLastFunction = "";
749743

750744
if (!Bind(cur, params, skip_first))
751745
return 0;
752746

753747
szLastFunction = "SQLExecute";
754748
Py_BEGIN_ALLOW_THREADS
755-
ret = SQLExecute(cur->hstmt);
749+
ret = SQLExecute(hndl->hstmt);
756750
Py_END_ALLOW_THREADS
757751

758752

@@ -768,7 +762,7 @@ static PyObject* executePreparedStatement(Cursor* cur, PyObject* params, bool sk
768762
{
769763
// We could try dropping through the while and if below, but if there is an error, we need to raise it before
770764
// FreeParameterData calls more ODBC functions.
771-
RaiseErrorFromHandle(cur->cnxn, "SQLExecDirectW", cur->cnxn->hdbc, cur->hstmt);
765+
RaiseErrorFromHandle(cur->cnxn, "SQLExecDirectW", cur->cnxn->hdbc, hndl->hstmt);
772766
FreeParameterData(cur);
773767
return 0;
774768
}
@@ -791,11 +785,11 @@ static PyObject* executePreparedStatement(Cursor* cur, PyObject* params, bool sk
791785
szLastFunction = "SQLParamData";
792786
ParamInfo* pInfo;
793787
Py_BEGIN_ALLOW_THREADS
794-
ret = SQLParamData(cur->hstmt, (SQLPOINTER*)&pInfo);
788+
ret = SQLParamData(hndl->hstmt, (SQLPOINTER*)&pInfo);
795789
Py_END_ALLOW_THREADS
796790

797791
if (ret != SQL_NEED_DATA && ret != SQL_NO_DATA && !SQL_SUCCEEDED(ret))
798-
return RaiseErrorFromHandle(cur->cnxn, "SQLParamData", cur->cnxn->hdbc, cur->hstmt);
792+
return RaiseErrorFromHandle(cur->cnxn, "SQLParamData", cur->cnxn->hdbc, hndl->hstmt);
799793

800794
TRACE("SQLParamData() --> %d\n", ret);
801795

@@ -830,7 +824,7 @@ static PyObject* executePreparedStatement(Cursor* cur, PyObject* params, bool sk
830824
ret = SQLPutData(cur->hstmt, (SQLPOINTER)&p[offset], remaining);
831825
Py_END_ALLOW_THREADS
832826
if (!SQL_SUCCEEDED(ret))
833-
return RaiseErrorFromHandle(cur->cnxn, "SQLPutData", cur->cnxn->hdbc, cur->hstmt);
827+
return RaiseErrorFromHandle(cur->cnxn, "SQLPutData", cur->cnxn->hdbc, hndl->hstmt);
834828
offset += remaining;
835829
}
836830
while (offset < cb);
@@ -881,19 +875,19 @@ static PyObject* executePreparedStatement(Cursor* cur, PyObject* params, bool sk
881875
hasTvpRows = 1;
882876
}
883877
Py_BEGIN_ALLOW_THREADS
884-
ret = SQLPutData(cur->hstmt, hasTvpRows ? (SQLPOINTER)1 : 0, hasTvpRows);
878+
ret = SQLPutData(hndl->hstmt, hasTvpRows ? (SQLPOINTER)1 : 0, hasTvpRows);
885879
Py_END_ALLOW_THREADS
886880
if (!SQL_SUCCEEDED(ret))
887-
return RaiseErrorFromHandle(cur->cnxn, "SQLPutData", cur->cnxn->hdbc, cur->hstmt);
881+
return RaiseErrorFromHandle(cur->cnxn, "SQLPutData", cur->cnxn->hdbc, hndl->hstmt);
888882
}
889883
else
890884
{
891885
// TVP column sent as DAE
892886
Py_BEGIN_ALLOW_THREADS
893-
ret = SQLPutData(cur->hstmt, pInfo->ParameterValuePtr, pInfo->BufferLength);
887+
ret = SQLPutData(hndl->hstmt, pInfo->ParameterValuePtr, pInfo->BufferLength);
894888
Py_END_ALLOW_THREADS
895889
if (!SQL_SUCCEEDED(ret))
896-
return RaiseErrorFromHandle(cur->cnxn, "SQLPutData", cur->cnxn->hdbc, cur->hstmt);
890+
return RaiseErrorFromHandle(cur->cnxn, "SQLPutData", cur->cnxn->hdbc, hndl->hstmt);
897891
}
898892
ret = SQL_NEED_DATA;
899893
}
@@ -910,29 +904,29 @@ static PyObject* executePreparedStatement(Cursor* cur, PyObject* params, bool sk
910904
}
911905

912906
if (!SQL_SUCCEEDED(ret))
913-
return RaiseErrorFromHandle(cur->cnxn, szLastFunction, cur->cnxn->hdbc, cur->hstmt);
907+
return RaiseErrorFromHandle(cur->cnxn, szLastFunction, cur->cnxn->hdbc, hndl->hstmt);
914908

915909
SQLLEN cRows = -1;
916910
Py_BEGIN_ALLOW_THREADS
917-
ret = SQLRowCount(cur->hstmt, &cRows);
911+
ret = SQLRowCount(hndl->hstmt, &cRows);
918912
Py_END_ALLOW_THREADS
919913
if (!SQL_SUCCEEDED(ret))
920-
return RaiseErrorFromHandle(cur->cnxn, "SQLRowCount", cur->cnxn->hdbc, cur->hstmt);
914+
return RaiseErrorFromHandle(cur->cnxn, "SQLRowCount", cur->cnxn->hdbc, hndl->hstmt);
921915

922916
cur->rowcount = (int)cRows;
923917

924918
TRACE("SQLRowCount: %d\n", cRows);
925919

926920
SQLSMALLINT cCols = 0;
927921
Py_BEGIN_ALLOW_THREADS
928-
ret = SQLNumResultCols(cur->hstmt, &cCols);
922+
ret = SQLNumResultCols(hndl->hstmt, &cCols);
929923
Py_END_ALLOW_THREADS
930924
if (!SQL_SUCCEEDED(ret))
931925
{
932926
// Note: The SQL Server driver sometimes returns HY007 here if multiple statements (separated by ;) were
933927
// submitted. This is not documented, but I've seen it with multiple successful inserts.
934928

935-
return RaiseErrorFromHandle(cur->cnxn, "SQLNumResultCols", cur->cnxn->hdbc, cur->hstmt);
929+
return RaiseErrorFromHandle(cur->cnxn, "SQLNumResultCols", cur->cnxn->hdbc, hndl->hstmt);
936930
}
937931

938932
TRACE("SQLNumResultCols: %d\n", cCols);
@@ -944,7 +938,7 @@ static PyObject* executePreparedStatement(Cursor* cur, PyObject* params, bool sk
944938
}
945939

946940
if (!SQL_SUCCEEDED(ret))
947-
return RaiseErrorFromHandle(cur->cnxn, "SQLRowCount", cur->cnxn->hdbc, cur->hstmt);
941+
return RaiseErrorFromHandle(cur->cnxn, "SQLRowCount", cur->cnxn->hdbc, hndl->hstmt);
948942

949943
if (cCols != 0)
950944
{
@@ -1329,18 +1323,17 @@ PyObject* Cursor_executePreparedStatement(PyObject* self, PyObject* args)
13291323

13301324
if (cParams < 0)
13311325
{
1332-
PyErr_SetString(PyExc_TypeError, "execute() takes at least 1 argument (0 given)");
1326+
PyErr_SetString(PyExc_TypeError, "executePreparedStatement() takes at least 1 argument (0 given)");
13331327
return 0;
13341328
}
1335-
/*
1336-
TODO: Leave the first argument slot open for the handle. Handle to be implemented later
1337-
PyObject* pSql = PyTuple_GET_ITEM(args, 0);
13381329

1339-
if (!PyUnicode_Check(pSql) && !PyUnicode_Check(pSql))
1330+
PyObject* hndl = PyTuple_GET_ITEM(args, 0);
1331+
1332+
if (!Handle_Check(hndl))
13401333
{
1341-
PyErr_SetString(PyExc_TypeError, "The first argument to execute must be a string or unicode query.");
1334+
PyErr_SetString(PyExc_TypeError, "The first argument to executePreparedStatement must be a query handle.");
13421335
return 0;
1343-
}*/
1336+
}
13441337

13451338
// Figure out if there were parameters and how they were passed. Our optional parameter passing complicates this slightly.
13461339

@@ -1350,7 +1343,6 @@ TODO: Leave the first argument slot open for the handle. Handle to be implemente
13501343
{
13511344
// There is a single argument and it is a sequence, so we must treat it as a sequence of parameters. (This is
13521345
// the normal Cursor.execute behavior.)
1353-
13541346
params = PyTuple_GET_ITEM(args, 1);
13551347
skip_first = false;
13561348
}
@@ -1362,7 +1354,7 @@ TODO: Leave the first argument slot open for the handle. Handle to be implemente
13621354

13631355
// Execute.
13641356

1365-
return executePreparedStatement(cursor, params, skip_first);
1357+
return executePreparedStatement(cursor, (Handle *)hndl, params, skip_first);
13661358
}
13671359

13681360
static char execute_doc[] =

src/handle.cpp

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
#include <datetime.h>
2727
#include "handle.h"
2828

29-
extern PyTypeObject HandleType;
29+
3030

3131
static char messages_doc[] =
3232
"This read-only attribute is a list of all the diagnostic messages in the\n" \
@@ -49,6 +49,9 @@ static PyMemberDef Cursor_members[] =
4949

5050

5151

52+
53+
54+
5255
static char enter_doc[] = "__enter__() -> self.";
5356
static PyObject* Handle_enter(PyObject* self, PyObject* args)
5457
{
@@ -57,7 +60,7 @@ static PyObject* Handle_enter(PyObject* self, PyObject* args)
5760
return self;
5861
}
5962

60-
static char exit_doc[] = "__exit__(*excinfo) -> None. Commits the connection if necessary..";
63+
static char exit_doc[] = "__exit__(*excinfo) -> None. XXX: Commits the connection if necessary..";
6164
static PyObject* Handle_exit(PyObject* self, PyObject* args)
6265
{
6366
//Cursor* cursor = Cursor_Validate(self, CURSOR_REQUIRE_OPEN | CURSOR_RAISE_ERROR);
@@ -68,6 +71,13 @@ static PyObject* Handle_exit(PyObject* self, PyObject* args)
6871
Py_RETURN_NONE;
6972
}
7073

74+
static void Handle_dealloc(Handle* hndl)
75+
{
76+
77+
//Py_XDECREF(cursor->inputsizes);
78+
PyObject_Del(hndl);
79+
}
80+
7181

7282
static PyMethodDef Handle_methods[] =
7383
{
@@ -76,9 +86,9 @@ static PyMethodDef Handle_methods[] =
7686
{0, 0, 0, 0}
7787
};
7888

79-
static char cursor_doc[] =
89+
static char handle_doc[] =
8090
"TODO";
81-
PyTypeObject CursorType =
91+
PyTypeObject HandleType =
8292
{
8393
PyVarObject_HEAD_INIT(0, 0)
8494
"pyodbc.Handle", // tp_name
@@ -142,7 +152,7 @@ Handle_New(Cursor* cur)
142152

143153
if (hndl)
144154
{
145-
Connection cnxn = cur->cnxn;
155+
Connection *cnxn = cur->cnxn;
146156
hndl->cur = cur;
147157
hndl->hstmt = SQL_NULL_HANDLE;
148158

src/handle.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,11 @@ struct Handle
2929
void Handle_init();
3030

3131
Handle* Handle_New(Cursor* cur);
32+
33+
extern PyTypeObject HandleType;
34+
35+
#define Handle_Check(op) PyObject_TypeCheck(op, &HandleType)
36+
#define Handle_CheckExact(op) (Py_TYPE(op) == &HandleType)
37+
38+
3239
#endif

0 commit comments

Comments
 (0)