File: _kicore_fetch.c

package info (click to toggle)
python-kinterbasdb 3.1-1
  • links: PTS
  • area: main
  • in suites: sarge
  • size: 1,044 kB
  • ctags: 1,157
  • sloc: ansic: 6,879; python: 2,517; makefile: 77
file content (144 lines) | stat: -rw-r--r-- 5,432 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
/* KInterbasDB Python Package - Implementation of Record Fetch
**
** Version 3.1
**
** The following contributors hold Copyright (C) over their respective
** portions of code (see license.txt for details):
**
** [Original Author (maintained through version 2.0-0.3.1):]
**   1998-2001 [alex]  Alexander Kuznetsov   <alexan@users.sourceforge.net>
** [Maintainers (after version 2.0-0.3.1):]
**   2001-2002 [maz]   Marek Isalski         <kinterbasdb@maz.nu>
**   2002-2004 [dsr]   David Rushby          <woodsplitter@rocketmail.com>
** [Contributors:]
**   2001      [eac]   Evgeny A. Cherkashin  <eugeneai@icc.ru>
**   2001-2002 [janez] Janez Jere            <janez.jere@void.si>
*/


/****************** "PRIVATE" DECLARATIONS:BEGIN *******************/

/* Technically, isc_info_sql_stmt_exec_procedure can also return a result set,
** but that statement type is handled in a separate clause of pyob_fetch. */
#define COULD_STATEMENT_RETURN_RESULT_SET(statement_type) \
    (   statement_type == isc_info_sql_stmt_select \
     || statement_type == isc_info_sql_stmt_select_for_upd \
    )

/****************** "PRIVATE" DECLARATIONS:END *******************/


static PyObject *pyob_fetch( PyObject *self, PyObject *args ) {
  CursorObject *cursor;
  PyObject *row;
  int statement_type;

  if ( !PyArg_ParseTuple(args, "O!", &CursorType, &cursor ) ) {
    return NULL;
  }
  CUR_REQUIRE_OPEN(cursor);

  /* If the result set has been exhausted and the cursor hasn't executed a
  ** fresh statement since, return None rather than raising an error.  This
  ** behavior is required by the Python DB API. */
  if (cursor->last_fetch_status == 100) {
    RETURN_PY_NONE;
  }

  /* 2003.01.26:  The statement type is now cached by the cursor; it need not
  ** be recomputed every time. */
  statement_type = cursor->statement_type;
  /* 2003.02.17:  Raise exception if a fetch is attempted before a statement
  ** has been executed. */
  if (statement_type == NULL_STATEMENT_TYPE) {
    raise_exception( ProgrammingError,
        "Cannot fetch from this cursor because it has not executed a statement."
      );
    goto FETCH_ERROR_CLEANUP;
  }

  debug_printf2(
      "\n[in pyob_fetch] STATEMENT TYPE: %d, # of output params: %d\n",
      statement_type, cursor->out_sqlda->sqld
    );

  /* If the cursor's statement type is isc_info_sql_stmt_exec_procedure,
  ** then use pseudo-fetch (for reasons explained in the comments in
  ** function pyob_execute regarding the difference between isc_dsql_execute
  ** and isc_dsql_execute2). */
  if (statement_type == isc_info_sql_stmt_exec_procedure) {
    if (cursor->exec_proc_results != NULL) {
      row = cursor->exec_proc_results;
      debug_printf("[in pyob_execute] returning the single cached"
        " EXECUTE PROCEDURE result row\n");
      /* Don't need to change reference count of exec_proc_results because
      ** we're passing reference ownership to the caller of this function. */
      cursor->exec_proc_results = NULL;
      return row;
    } else {
      debug_printf("[in pyob_execute] returning None because cached"
        " EXECUTE PROCEDURE results already used\n");
      RETURN_PY_NONE;
    }
  } else if ( !COULD_STATEMENT_RETURN_RESULT_SET(statement_type) ) {
    /* If the last executed statement couldn't possibly return a result set,
    ** the client programmer should not be asking for one.
    ** It is imperative that this situation be detected before the
    ** isc_dsql_fetch call below.  Otherwise, isc_dsql_fetch will claim to
    ** have succeeded without really having done so, and will leave the
    ** cursor statement handle in an invalid state that causes the program
    ** to freeze when isc_dsql_free_st4tement is called (usually by object
    ** destructors or other cleanup code). */
    const char *baseErrMsg = "Attempt to fetch row of results from a statement"
      " that does not produce a result set.  That statement was:  ";
    int baseErrMsg_len = strlen(baseErrMsg);

    int entireErrorMessage_len = baseErrMsg_len + PyString_Size(cursor->previous_sql);
    char *entireErrorMessage = (char *) kimem_main_malloc( sizeof(char) * (entireErrorMessage_len + 1) );
    strncpy(entireErrorMessage, baseErrMsg, baseErrMsg_len);
    strncpy(entireErrorMessage + baseErrMsg_len, PyString_AsString(cursor->previous_sql),
      PyString_Size(cursor->previous_sql));
    entireErrorMessage[entireErrorMessage_len] = '\0'; /* Add NULL terminator. */

    raise_exception( ProgrammingError, entireErrorMessage );
    kimem_main_free(entireErrorMessage);

    goto FETCH_ERROR_CLEANUP;
  }

  ENTER_DB
  cursor->last_fetch_status = isc_dsql_fetch( cursor->status_vector,
      &cursor->stmt_handle,
      cursor->connection->dialect,
      cursor->out_sqlda
    );
  LEAVE_DB

  /* isc_dsql_fetch return value meanings:
  **     0         -> success
  **   100         -> result set exhausted
  ** anything else -> error
  */
  switch (cursor->last_fetch_status) {
  case 0:
    row = XSQLDA2Tuple(cursor, cursor->out_sqlda);
    if (row == NULL) {
      goto FETCH_ERROR_CLEANUP;
    }
    return row;

  case 100:
    RETURN_PY_NONE;
  }
  /* default: error */

  raise_sql_exception( ProgrammingError, "fetch.isc_dsql_fetch: ",
      cursor->status_vector
    );
  /* Deliberately fall through into FETCH_ERROR_CLEANUP. */

FETCH_ERROR_CLEANUP:
  close_cursor_with_error(cursor); /* 2003.02.17c: */

  return NULL;
} /* pyob_fetch */