~ubuntu-branches/ubuntu/precise/myodbc/precise

« back to all changes in this revision

Viewing changes to cursor.c

  • Committer: Bazaar Package Importer
  • Author(s): Steve Langasek
  • Date: 2004-02-15 14:59:29 UTC
  • mfrom: (1.1.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20040215145929-wx1o3eobuhmmr2ft
Tags: 3.51.06-1
* New upstream release (closes: #149461).
* Add French debconf translations, thanks to Clément Stenac
  <zorglub@via.ecp.fr> (closes: #232839).

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* (C) Copyright MySQL AB
2
 
**
3
 
** CURSOR.C - This is the MyODBC sample driver code for implementing
4
 
** client side cursors
5
 
**
6
 
** With a non-keyset client-side cursor, the server sends the entire
7
 
** result set across the network to the client machine. The client
8
 
** machine provides and manages the temporary resources needed by
9
 
** the cursor and result set. The client-side application can browse
10
 
** through the entire result set to determine which rows it requires.
11
 
**
12
 
** Static and keyset-driven client-side cursors may place a significant
13
 
** load on your workstation if they include too many rows. While all of
14
 
** the cursor libraries are capable of building cursors with thousands
15
 
** of rows, applications designed to fetch such large rowsets may perform
16
 
** poorly. There are exceptions, of course. For some applications, a large
17
 
** client-side cursor may be perfectly appropriate and performance may not
18
 
** be an issue.
19
 
**
20
 
** One obvious benefit of the client-side cursor is quick response. After
21
 
** the result set has been downloaded to the client machine, browsing
22
 
** through the rows is very fast. Your application is generally more
23
 
** scalable with client-side cursors because the cursor's resource
24
 
** requirements are placed on each separate client and not on the server.
25
 
**
26
 
** @author : MySQL AB
27
 
** @date   : 2001-June-01
28
 
*/
29
 
 
30
 
#include "myodbc.h"
31
 
 
32
 
static const char *find_used_table(STMT *stmt);
33
 
 
34
 
void my_param_bind(STMT FAR *srcStmt, STMT FAR *dstStmt,
35
 
                   SQLUSMALLINT nSrcCol,SQLUSMALLINT nDstCol);
36
 
 
37
 
SQLRETURN my_if_pk_exits(STMT FAR *stmt);
38
 
SQLRETURN my_pk_param_bind(STMT FAR *stmtNew,STMT FAR *stmt,
39
 
                           SQLUSMALLINT irow,SQLUSMALLINT nColParam);
40
 
my_bool my_build_where_clause(STMT FAR *stmt,
41
 
                              DYNAMIC_STRING *dynQuery);
42
 
void my_set_cursor_data(STMT FAR *stmt);
43
 
SQLRETURN my_pos_add(STMT FAR *stmt, SQLUSMALLINT irow, 
44
 
                     DYNAMIC_STRING dynQuery);
45
 
/*
46
 
   SQLSetCursorName associates a cursor name with an active statement.
47
 
*/
48
 
 
49
 
SQLRETURN SQL_API SQLSetCursorName(SQLHSTMT  hstmt,
50
 
                                   SQLCHAR FAR *szCursor,
51
 
                                   SQLSMALLINT cbCursor)
52
 
{
53
 
  STMT FAR *stmt=(STMT FAR*) hstmt;
54
 
 
55
 
  DBUG_ENTER("SQLSetCursorName");
56
 
 
57
 
  /* if null string passed as the cursor name */
58
 
  if (!szCursor)
59
 
    DBUG_RETURN(set_stmt_error(stmt,"S1009",
60
 
                               "Invalid use of null pointer",0));
61
 
  
62
 
  if (cbCursor == SQL_NTS)
63
 
    cbCursor = (SQLSMALLINT) strlen(szCursor);
64
 
 
65
 
  /*
66
 
    This will be handled by the DM, but when the application is linked
67
 
    with the client lib(myodbc.lib), then driver need to through the error
68
 
  */
69
 
  if (cbCursor < 0) 
70
 
    DBUG_RETURN(set_stmt_error(stmt,"S1090",
71
 
                               "Invalid string or buffer length",0));
72
 
  /*
73
 
    Through an error, when trying to set the cursor in the middle of
74
 
    execution/ fetch
75
 
  */
76
 
  if (stmt->cursor_state == MYSQL_CURSOR_IN_EXECUTION ||
77
 
      stmt->cursor_state == MYSQL_CURSOR_NEED_DATA) 
78
 
    DBUG_RETURN(set_stmt_error(stmt,"24000","Invalid cursor state",0));
79
 
  
80
 
  /* validate for cursor names */
81
 
  if ( (cbCursor == 0) ||
82
 
       (my_casecmp(szCursor, "SQLCUR", 6) == 0)  ||
83
 
       (my_casecmp(szCursor, "SQL_CUR", 7) == 0)) 
84
 
    DBUG_RETURN(set_stmt_error(stmt,"34000","Invalid cursor name",0));
85
 
  
86
 
  stmt->cursor_state = MYSQL_CURSOR_DEFINED;
87
 
  /*
88
 
    if length is larger than the supported, then truncate and
89
 
    return warning
90
 
  */
91
 
  if (cbCursor > MYSQL_MAX_CURSOR_LEN) 
92
 
  {
93
 
    /* set the warning ...by truncating the cursor name to max length */
94
 
    strmake( stmt->cursor_name, szCursor, MYSQL_MAX_CURSOR_LEN);
95
 
    set_stmt_error(stmt,"01004","String data, right truncated",01004);
96
 
    DBUG_RETURN(SQL_SUCCESS_WITH_INFO);
97
 
  }
98
 
  strmake( stmt->cursor_name, szCursor, cbCursor);
99
 
  DBUG_RETURN(SQL_SUCCESS);
100
 
}
101
 
 
102
 
 
103
 
/*
104
 
   SQLGetCursorName returns the cursor name associated with a specified
105
 
   statement.
106
 
*/
107
 
 
108
 
SQLRETURN SQL_API SQLGetCursorName(SQLHSTMT hstmt,
109
 
                                   SQLCHAR FAR *szCursor,
110
 
                                   SQLSMALLINT cbCursorMax,
111
 
                                   SQLSMALLINT FAR *pcbCursor)
112
 
{
113
 
  STMT FAR   *stmt=(STMT FAR*) hstmt;
114
 
  SQLINTEGER nLength;
115
 
  SQLINTEGER nDummyLength;
116
 
  DBUG_ENTER("SQLGetCursorName");
117
 
 
118
 
  LINT_INIT(nLength);
119
 
  LINT_INIT(nDummyLength);
120
 
 
121
 
  /*
122
 
    If no prior cursor set by the application, as 2.x driver doesn't
123
 
    handle any default cursors, give an error
124
 
  */
125
 
  if (stmt->cursor_state == MYSQL_CURSOR_UNDEFINED)
126
 
    DBUG_RETURN(set_stmt_error(stmt,"S1015","No cursor name available",0));
127
 
 
128
 
  /* if invalid buffer length */
129
 
  if (cbCursorMax < 0)
130
 
    DBUG_RETURN(set_stmt_error(stmt,"S1090",
131
 
                               "Invalid string or buffer length",0));
132
 
 
133
 
  /* if the stmt is in the middle of execution... */
134
 
  if (stmt->cursor_state == MYSQL_CURSOR_NEED_DATA ) 
135
 
    DBUG_RETURN(set_stmt_error(stmt,"S1010","Function sequence error",0));
136
 
 
137
 
  if (pcbCursor == NULL)
138
 
    pcbCursor = (SQLSMALLINT *) &nDummyLength;
139
 
 
140
 
  *pcbCursor = strlen(stmt->cursor_name);
141
 
  if ( cbCursorMax )
142
 
    cbCursorMax -= sizeof(char); /* NULL termination */
143
 
 
144
 
  if ( szCursor && cbCursorMax > 0)
145
 
    strmake( szCursor, stmt->cursor_name, cbCursorMax);
146
 
 
147
 
  nLength = min( *pcbCursor , cbCursorMax );
148
 
  if ( nLength != *pcbCursor )
149
 
  {
150
 
    set_stmt_error(stmt,"01004","String data, right truncated",01004);
151
 
    DBUG_RETURN(SQL_SUCCESS_WITH_INFO);
152
 
  }
153
 
  DBUG_RETURN(SQL_SUCCESS);
154
 
}
155
 
 
156
 
 
157
 
/*
158
 
 SQLSetPos sets the cursor position in a rowset and allows an application
159
 
 to refresh data in the rowset or to update or delete data in the result
160
 
 set.
161
 
 
162
 
 As the server doesn't give the BEST_ROWID always..better to manage
163
 
 all the rows in the where clause to avoid duplicate data delete/update
164
 
*/
165
 
 
166
 
SQLRETURN SQL_API SQLSetPos(SQLHSTMT hstmt,
167
 
                            SQLUSMALLINT irow,
168
 
                            SQLUSMALLINT fOption,
169
 
                            SQLUSMALLINT fLock)
170
 
{
171
 
  STMT FAR  *stmt=(STMT FAR*) hstmt;
172
 
  RETCODE   sqlRet;
173
 
  DYNAMIC_STRING dynQuery;
174
 
  MYSQL_RES *result = stmt->result;
175
 
 
176
 
  DBUG_ENTER("SQLSetPos");
177
 
  DBUG_PRINT("enter",("irow, refresh: %d   Lock: %d",irow,fOption,fLock));
178
 
 
179
 
  /*
180
 
    if no result set, no meaning for SQLSetPos call, so just
181
 
    through an error
182
 
  */
183
 
  if (!result)
184
 
    DBUG_RETURN(set_stmt_error(stmt,"S1010",
185
 
                              "Function sequence error, no result set",0));
186
 
 
187
 
  /* if irow > maximum rows in the resultset */
188
 
  if ( fOption != SQL_ADD && irow > mysql_num_rows(result))
189
 
    DBUG_RETURN(set_stmt_error(stmt,"S1107","Row value out of range",0));
190
 
  
191
 
  if ( fLock > SQL_LOCK_UNLOCK)
192
 
    DBUG_RETURN(set_stmt_error(stmt,"S1009","Invalid argument value",0));
193
 
 
194
 
  switch(fOption) {
195
 
  case SQL_POSITION:
196
 
  {
197
 
    /* The RowNumber argument was 0, and the Operation argument was SQL_POSITION
198
 
       through an error as per the spec
199
 
    */
200
 
    if( irow == 0)
201
 
      DBUG_RETURN(set_stmt_error(stmt,"S1109","Invalid cursor position",0)); 
202
 
 
203
 
    irow--; sqlRet = SQL_SUCCESS;
204
 
    stmt->position_in_set=irow;
205
 
    stmt->current_row = irow;
206
 
    mysql_data_seek(result,irow);
207
 
    break;
208
 
  }/* END OF SQL_POSITION */
209
 
 
210
 
  case SQL_DELETE:
211
 
  {
212
 
    const char *table_name=find_used_table(stmt);
213
 
    
214
 
    if (!table_name)
215
 
      DBUG_RETURN(SQL_ERROR);
216
 
 
217
 
    if(irow && (stmt->current_row != (uint)irow-1))
218
 
      DBUG_RETURN(set_stmt_error(stmt,"S1109","Invalid cursor position",0));
219
 
                
220
 
    /*
221
 
      dynamic string, to build and store the SQL Query for
222
 
      DELETE statement
223
 
    */    
224
 
    if (init_dynamic_string(&dynQuery, "DELETE FROM ", 1024, 1024))
225
 
      DBUG_RETURN(set_stmt_error(stmt,"S1001","Not enough memory",4001));
226
 
   
227
 
    dynstr_append(&dynQuery,table_name);    
228
 
    sqlRet = my_pos_delete(stmt,irow,dynQuery);
229
 
    dynstr_free(&dynQuery);
230
 
    break;
231
 
  } /* END of CASE SQL_DELETE */
232
 
 
233
 
  case SQL_UPDATE:
234
 
  {
235
 
    MYSQL_FIELD *field, *end;
236
 
    const char  *table_name=find_used_table(stmt);
237
 
    
238
 
    if (!table_name)
239
 
      DBUG_RETURN(SQL_ERROR);
240
 
 
241
 
    if(irow && stmt->current_row != (uint)irow-1)
242
 
      DBUG_RETURN(set_stmt_error(stmt,"S1109","Invalid cursor position",0));
243
 
 
244
 
    /*
245
 
      dynamic string, to build and store the SQL Query for
246
 
      UPDATE statement
247
 
    */    
248
 
    if (init_dynamic_string(&dynQuery, "UPDATE ", 1024,1024))
249
 
      DBUG_RETURN(set_stmt_error(stmt,"S1001","Not enough memory",4001));
250
 
    
251
 
    dynstr_append(&dynQuery,table_name);
252
 
    dynstr_append_mem(&dynQuery," SET ",5);
253
 
 
254
 
    /*
255
 
      SET clause manipulation
256
 
 
257
 
      TODO:  as per spec: driver should retrieve the lengths of the data from
258
 
             the number-of-bytes buffers (the pcbValue argument in SQLBindCol).
259
 
             If the length of any column is SQL_IGNORE, the column is not 
260
 
             updated.
261
 
    */
262
 
    for (field=result->fields, end=field+ result->field_count;
263
 
         field < end ;
264
 
         field++)
265
 
    {
266
 
      if (field->table)
267
 
      {
268
 
        dynstr_append(&dynQuery,field->name);
269
 
        dynstr_append(&dynQuery,"=?,");
270
 
      }
271
 
    }
272
 
    /* Remove last ',' */    
273
 
    dynQuery.str[--dynQuery.length]=0;
274
 
    sqlRet = my_pos_update(stmt,irow,dynQuery,1);
275
 
    dynstr_free(&dynQuery);
276
 
    break;
277
 
  } /* END of CASE SQL_UPDATE */
278
 
 
279
 
  case SQL_REFRESH:
280
 
  {
281
 
    /* The driver positions the cursor on the row specified by irow and 
282
 
       refreshes data in the rowset buffers for that row.
283
 
 
284
 
       Note that, data in the buffers is refreshed but not refetched,
285
 
       the membership in the rowset is fixed.
286
 
       
287
 
       so it needs to refresh the specified row buffers, as this does't 
288
 
       need refetches the data
289
 
    */
290
 
    sqlRet = SQLExtendedFetch(hstmt,SQL_FETCH_ABSOLUTE,irow,
291
 
                              NULL,stmt->rgfRowStatus);
292
 
    break;
293
 
  }
294
 
  case SQL_ADD:
295
 
  {
296
 
    MYSQL_FIELD *field, *end;
297
 
    const char  *table_name=find_used_table(stmt);
298
 
    SQLUINTEGER index;
299
 
    
300
 
    if (!table_name)
301
 
      DBUG_RETURN(SQL_ERROR);
302
 
 
303
 
    if (init_dynamic_string(&dynQuery, "INSERT INTO ", 1024,1024))
304
 
      DBUG_RETURN(set_stmt_error(stmt,"S1001","Not enough memory",4001));
305
 
    
306
 
    dynstr_append(&dynQuery,table_name);
307
 
    dynstr_append_mem(&dynQuery,"(",1);
308
 
                
309
 
    /*
310
 
      INSERT column list
311
 
    */
312
 
    for (field=result->fields, end=field+ result->field_count;
313
 
         field < end ;
314
 
         field++)
315
 
    {
316
 
        dynstr_append(&dynQuery,field->name);
317
 
        dynstr_append(&dynQuery,",");
318
 
    }
319
 
    /* Remove last ',' */    
320
 
    dynQuery.length--;dynstr_append(&dynQuery,") VALUES (");
321
 
    for (index=0; index < result->field_count; index++)
322
 
      dynstr_append(&dynQuery,"?,");
323
 
   
324
 
    dynQuery.length--;dynstr_append(&dynQuery,")");
325
 
    sqlRet = my_pos_add(stmt,irow,dynQuery);
326
 
    dynstr_free(&dynQuery);
327
 
    break;
328
 
  }
329
 
  default:
330
 
  {
331
 
    DBUG_RETURN(set_stmt_error(stmt,"S1009","Invalid argument value",0));
332
 
  }
333
 
  }/* END OF switch */  
334
 
  DBUG_RETURN(sqlRet);
335
 
}
336
 
 
337
 
 
338
 
/*
339
 
   my_pos_delete deletes the positioned row
340
 
*/
341
 
 
342
 
SQLRETURN my_pos_delete(STMT FAR *stmt, SQLUSMALLINT irow, 
343
 
                        DYNAMIC_STRING dynQuery)
344
 
{
345
 
  SQLHSTMT     hstmtNew;
346
 
  SQLUSMALLINT ncol; 
347
 
  MYSQL_RES    *result = stmt->result;
348
 
  SQLRETURN    sqlRet;  
349
 
  STMT FAR     *stmtNew;
350
 
  my_bool      pk_exists; 
351
 
  
352
 
  /* if irow = 0, then DELETE all rows, else delete the current row */
353
 
  if (irow) 
354
 
    pk_exists = my_build_where_clause(stmt,&dynQuery);
355
 
  
356
 
  /*
357
 
    allocate new stmt handle, and execute the DELETE query in
358
 
    that stmt, without disturbing the original stmt
359
 
  */
360
 
 
361
 
  /* Make sure the cursor is still in the same position..
362
 
     Fix to MS Access making wrong calls
363
 
  */
364
 
  my_set_cursor_data(stmt);
365
 
 
366
 
  sqlRet = my_SQLAllocStmt(stmt->dbc, &hstmtNew);
367
 
  if(sqlRet != SQL_SUCCESS)
368
 
    return(sqlRet);
369
 
 
370
 
  stmtNew = (STMT FAR *)hstmtNew;
371
 
    
372
 
  sqlRet = my_SQLPrepare(stmtNew,dynQuery.str,SQL_NTS);
373
 
  if(sqlRet != SQL_SUCCESS)
374
 
    goto my_time_to_return_error; 
375
 
 
376
 
  /* if only the current row to be deleted, then supply param data */
377
 
  if (irow) 
378
 
  {
379
 
    /* check if PK column exists in the result set, else
380
 
       use the temporary result set to get the PK column data
381
 
    */
382
 
    if (pk_exists)
383
 
      my_pk_param_bind(stmtNew,stmt,irow,0);
384
 
    else
385
 
    {
386
 
      for (ncol = 0; ncol < result->field_count; ncol++)
387
 
      {
388
 
        if (result->fields[ncol].table)
389
 
          my_param_bind(stmtNew,stmt,ncol,ncol);
390
 
      }
391
 
      stmtNew->query = insert_params(stmtNew);
392
 
    }
393
 
  }
394
 
 
395
 
  /* now execute the real DELETE query... */
396
 
  DBUG_PRINT("SQL_DELETE:",("%s",stmtNew->query));  
397
 
  sqlRet = do_query(stmtNew,stmtNew->query);
398
 
  if( sqlRet == SQL_SUCCESS || sqlRet == SQL_SUCCESS_WITH_INFO )
399
 
  {
400
 
    /* if irow=0, server returns affected rows as 0, this is a bug, so
401
 
       manually do this from driver side, but change this when server
402
 
       supports it.
403
 
    */
404
 
    if(irow == 0)
405
 
    {
406
 
      stmt->affected_rows=result->row_count;
407
 
      stmt->dbc->mysql.affected_rows = result->row_count;
408
 
    }
409
 
    else
410
 
      stmt->affected_rows=mysql_affected_rows(&stmtNew->dbc->mysql);
411
 
    
412
 
    /* no rows updated/deleted, return warning */
413
 
    if( stmt->affected_rows == 0)
414
 
    {
415
 
      sqlRet = SQL_SUCCESS_WITH_INFO;
416
 
      set_stmt_error(stmt,"01S03","No rows updated/deleted",0);
417
 
    }
418
 
    /* if irow !=0 and stmt->affected_rows > 1, then return warning */
419
 
    else if( irow != 0 && stmt->affected_rows > 1)
420
 
    {
421
 
      sqlRet = SQL_SUCCESS_WITH_INFO;
422
 
      set_stmt_error(stmt,"01S04","More than one row updated/deleted",0);
423
 
    }
424
 
    else if( stmt->rgfRowStatus) /* update the status */
425
 
    {
426
 
      for(ncol = 0; ncol < stmt->affected_rows; ncol++)
427
 
        stmt->rgfRowStatus[ncol+stmt->current_row] = SQL_ROW_DELETED;
428
 
    }
429
 
  }
430
 
  else
431
 
  {
432
 
    DBUG_PRINT("error",("%s:%s",stmtNew->sqlstate,stmtNew->last_error));
433
 
    set_stmt_error(stmt,stmtNew->sqlstate,stmtNew->last_error,
434
 
                   stmt->last_errno);
435
 
  } 
436
 
my_time_to_return_error:
437
 
  my_SQLFreeStmt(hstmtNew,SQL_DROP);
438
 
  return(sqlRet);
439
 
}
440
 
 
441
 
 
442
 
/*
443
 
 my_pos_update updates the positioned row
444
 
*/
445
 
 
446
 
SQLRETURN my_pos_update(STMT FAR *stmt,
447
 
                        SQLUSMALLINT irow,
448
 
                        DYNAMIC_STRING dynQuery,
449
 
                        SQLUSMALLINT set_param_exists)
450
 
{
451
 
  SQLHSTMT     hstmtNew;
452
 
  SQLUSMALLINT ncol;
453
 
  MYSQL_RES    *result = stmt->result;
454
 
  SQLRETURN    sqlRet;
455
 
  STMT FAR     *stmtNew;
456
 
  my_bool      pk_exists; 
457
 
  
458
 
  /* if irow = 0, then UPDATE all rows, else update the current row */
459
 
  if (irow)
460
 
    pk_exists = my_build_where_clause(stmt,&dynQuery);
461
 
 
462
 
  /* Make sure the cursor is still in the same position..
463
 
     Fix to MS Access making wrong calls
464
 
  */
465
 
  my_set_cursor_data(stmt);
466
 
 
467
 
  /*  now...prepare to execute   */
468
 
  sqlRet = my_SQLAllocStmt(stmt->dbc, &hstmtNew);
469
 
  if(sqlRet != SQL_SUCCESS)
470
 
    return(sqlRet);
471
 
 
472
 
  stmtNew = (STMT FAR *)hstmtNew;
473
 
  sqlRet = my_SQLPrepare(stmtNew,dynQuery.str,SQL_NTS);
474
 
  if(sqlRet != SQL_SUCCESS)
475
 
    goto my_time_to_return_error;
476
 
 
477
 
  if (set_param_exists)
478
 
  {
479
 
    /*
480
 
      copy row buffers to param buffers to set the update data i.e.
481
 
      param for SET clause
482
 
    */    
483
 
    if(!stmt->bind){
484
 
      /* The argument fOption was SQL_ADD or SQL_UPDATE and no columns were
485
 
         bound with SQLBindCol.
486
 
      */
487
 
        sqlRet = SQL_ERROR;
488
 
        set_stmt_error(stmt,"21S02",
489
 
                       "Degree of derived table does not match column list",0);
490
 
        goto my_time_to_return_error;
491
 
    }
492
 
    for (ncol = 0; ncol < stmt->result->field_count; ncol++)
493
 
    {
494
 
      PARAM_BIND *param = dynamic_element(&stmtNew->params,ncol,PARAM_BIND*);
495
 
      ulong transfer_length,precision,display_size;
496
 
      MYSQL_FIELD *field = mysql_fetch_field_direct(result,ncol);
497
 
      BIND *bind = stmt->bind+ncol; 
498
 
 
499
 
      param->used= 1;
500
 
      param->SqlType = unireg_to_sql_datatype(stmt,field,0,
501
 
                                              &transfer_length,&precision,
502
 
                                              &display_size);
503
 
      param->CType = bind->fCType;
504
 
      param->buffer = (SQLCHAR *)bind->rgbValue;
505
 
      param->ValueMax = bind->cbValueMax;
506
 
      param->actual_len= bind->pcbValue;
507
 
      param->real_param_done = TRUE;
508
 
 
509
 
      set_dynamic(&stmtNew->params,(gptr)param,ncol);
510
 
    }
511
 
  } 
512
 
 
513
 
  /*  if only the current row to be updated, then supply param data */
514
 
  if (irow ) 
515
 
  {
516
 
    if (set_param_exists)
517
 
    {
518
 
      SQLUSMALLINT dstCol;
519
 
      if(pk_exists)
520
 
      {
521
 
        dstCol = result->field_count;
522
 
        my_pk_param_bind(stmtNew,stmt,irow,dstCol);
523
 
      }
524
 
      else
525
 
      {
526
 
        for (ncol = 0; ncol < result->field_count; ncol++)
527
 
        {
528
 
          dstCol = ncol+result->field_count;
529
 
          my_param_bind(stmtNew,stmt,ncol,dstCol);
530
 
        }
531
 
        stmtNew->query = insert_params(stmtNew);
532
 
      }
533
 
    }
534
 
    else
535
 
    {
536
 
      if(pk_exists)
537
 
        my_pk_param_bind(stmtNew,stmt,irow,0);
538
 
      else
539
 
      {
540
 
        for (ncol = 0; ncol < result->field_count; ncol++)
541
 
          my_param_bind(stmtNew,stmt,ncol,ncol);
542
 
        stmtNew->query = insert_params(stmtNew);
543
 
      }
544
 
    }
545
 
  }/* END of if (iRow) */
546
 
 
547
 
  /*  now execute the real UPDATE query...  */
548
 
  DBUG_PRINT("SQL_UPDATE:",("%s",stmtNew->query));    
549
 
  sqlRet = do_query(stmtNew,stmtNew->query);
550
 
  if( sqlRet == SQL_SUCCESS || sqlRet == SQL_SUCCESS_WITH_INFO )
551
 
  {
552
 
    stmt->affected_rows=mysql_affected_rows(&stmtNew->dbc->mysql);
553
 
 
554
 
    /* no rows updated/deleted, return warning */
555
 
    if( stmt->affected_rows == 0)
556
 
    {
557
 
      sqlRet = SQL_SUCCESS_WITH_INFO;
558
 
      set_stmt_error(stmt,"01S03","No rows updated/deleted",0);
559
 
    }
560
 
    /* if irow !=0 and stmt->affected_rows > 1, then return warning */
561
 
    else if( irow != 0 && stmt->affected_rows > 1)
562
 
    {
563
 
      sqlRet = SQL_SUCCESS_WITH_INFO;
564
 
      set_stmt_error(stmt,"01S04","More than one row updated/deleted",0);
565
 
    }
566
 
    else if( stmt->rgfRowStatus) /* update the status */
567
 
    {
568
 
      for(ncol = 0; ncol < stmt->affected_rows; ncol++)
569
 
        stmt->rgfRowStatus[ncol+stmt->current_row] = SQL_ROW_UPDATED;
570
 
    }
571
 
  }
572
 
  else 
573
 
  {
574
 
    DBUG_PRINT("error",("%s:%s",stmtNew->sqlstate,stmtNew->last_error));
575
 
    set_stmt_error(stmt,stmtNew->sqlstate,stmtNew->last_error,
576
 
                   stmt->last_errno);
577
 
  }    
578
 
 
579
 
my_time_to_return_error:
580
 
  my_SQLFreeStmt(hstmtNew,SQL_DROP); 
581
 
  return(sqlRet);
582
 
}
583
 
 
584
 
/*
585
 
 my_pos_add inserts a positioned row of data
586
 
*/
587
 
 
588
 
SQLRETURN my_pos_add(STMT FAR *stmt,
589
 
                     SQLUSMALLINT irow,
590
 
                     DYNAMIC_STRING dynQuery)
591
 
{
592
 
  SQLHSTMT     hstmtNew;
593
 
  SQLUSMALLINT ncol;
594
 
  MYSQL_RES    *result = stmt->result;
595
 
  SQLRETURN    sqlRet;
596
 
  STMT FAR     *stmtNew;
597
 
 
598
 
  /*  now...prepare to execute   */
599
 
  sqlRet = my_SQLAllocStmt(stmt->dbc, &hstmtNew);
600
 
  if(sqlRet != SQL_SUCCESS)
601
 
    return(sqlRet);
602
 
 
603
 
  stmtNew = (STMT FAR *)hstmtNew;
604
 
  sqlRet = my_SQLPrepare(stmtNew,dynQuery.str,SQL_NTS);
605
 
  if(sqlRet != SQL_SUCCESS)
606
 
    goto my_time_to_return_error;
607
 
 
608
 
  /*
609
 
    copy row buffers to param buffers to set the update data i.e.
610
 
    param for SET clause
611
 
  */    
612
 
  if(!stmt->bind){
613
 
    /* The argument fOption was SQL_ADD or SQL_UPDATE and no columns were
614
 
       bound with SQLBindCol.
615
 
    */
616
 
    sqlRet = SQL_ERROR;
617
 
    set_stmt_error(stmt,"21S02",
618
 
                   "Degree of derived table does not match column list",0);
619
 
    goto my_time_to_return_error;
620
 
  }
621
 
  for (ncol = 0; ncol < stmt->result->field_count; ncol++)
622
 
  {
623
 
    PARAM_BIND *param = dynamic_element(&stmtNew->params,ncol,PARAM_BIND*);
624
 
    ulong transfer_length,precision,display_size;
625
 
    MYSQL_FIELD *field = mysql_fetch_field_direct(result,ncol);
626
 
    BIND *bind = stmt->bind+ncol; 
627
 
 
628
 
    param->used= 1;
629
 
 
630
 
    param->SqlType = unireg_to_sql_datatype(stmt,field,0,
631
 
                                              &transfer_length,&precision,
632
 
                                              &display_size);
633
 
    param->CType = bind->fCType;
634
 
    param->buffer = (SQLCHAR *)bind->rgbValue;
635
 
    param->ValueMax = bind->cbValueMax;
636
 
    param->actual_len= bind->pcbValue;
637
 
    param->real_param_done = TRUE;
638
 
 
639
 
    set_dynamic(&stmtNew->params,(gptr)param,ncol);
640
 
  }
641
 
  /* This is a buggy code..it doesn't handle DATA_LEN_AT_EXEC...
642
 
     better to use my_SQLExecute, so that it will take care of it, but 
643
 
     it reduces the performance
644
 
  */
645
 
  stmtNew->query = insert_params(stmtNew);/* INSERT param DATA */
646
 
  /*  now execute the real INSERT query...  */
647
 
  DBUG_PRINT("SQL_ADD:",("%s",stmtNew->query));    
648
 
  sqlRet = do_query(stmtNew,stmtNew->query);
649
 
  if( sqlRet == SQL_SUCCESS || sqlRet == SQL_SUCCESS_WITH_INFO )
650
 
  {
651
 
    stmt->affected_rows=mysql_affected_rows(&stmtNew->dbc->mysql);
652
 
    if( stmt->rgfRowStatus) /* update the INSERT status */
653
 
       stmt->rgfRowStatus[stmt->current_row] = SQL_ROW_ADDED;
654
 
  }
655
 
  else 
656
 
  {
657
 
    DBUG_PRINT("error",("%s:%s",stmtNew->sqlstate,stmtNew->last_error));
658
 
    set_stmt_error(stmt,stmtNew->sqlstate,stmtNew->last_error,
659
 
                   stmt->last_errno);
660
 
  }    
661
 
 
662
 
my_time_to_return_error:
663
 
  my_SQLFreeStmt(hstmtNew,SQL_DROP); 
664
 
  return(sqlRet);
665
 
}
666
 
 
667
 
/*
668
 
 my_param_bind, does the param binding for the specific stmt by taking the
669
 
 data from the current row of the result set
670
 
*/
671
 
 
672
 
void my_param_bind(STMT FAR *dstStmt, STMT FAR *srcStmt,
673
 
                   SQLUSMALLINT nSrcCol, SQLUSMALLINT nDstCol)
674
 
{
675
 
  PARAM_BIND  *param;
676
 
  MYSQL_RES   *result = srcStmt->result;
677
 
  ulong       transfer_length,precision,display_size;
678
 
  MYSQL_FIELD *field = mysql_fetch_field_direct(result,nSrcCol);
679
 
  MYSQL_ROW   row_data = result->data_cursor->data + nSrcCol;
680
 
  
681
 
  param=dynamic_element(&dstStmt->params,nDstCol,PARAM_BIND*);
682
 
  param->used= 1;
683
 
  param->SqlType = unireg_to_sql_datatype(srcStmt,field,0,
684
 
        &transfer_length,&precision,
685
 
        &display_size);;
686
 
  param->CType = SQL_C_CHAR;
687
 
  param->buffer = (SQLCHAR *)*row_data;
688
 
  param->ValueMax = strlen(*row_data);
689
 
  param->actual_len = NULL;
690
 
  param->real_param_done = TRUE;
691
 
  set_dynamic(&dstStmt->params,(gptr)param,nDstCol);
692
 
}
693
 
 
694
 
/**
695
 
** checks whether the SQL statement contains WHERE CURRENT OF CURSOR
696
 
*/
697
 
 
698
 
my_bool check_if_positioned_cursor_exists(STMT FAR *stmt,STMT FAR **stmtNew)
699
 
{
700
 
  if (stmt->query && stmt->query_end)
701
 
  {
702
 
    char *szQueryEnd = stmt->query_end;
703
 
    char *szQuery = stmt->query;
704
 
    const char *szCursor = mystr_get_prev_token((const char **)&szQueryEnd,
705
 
                                                 stmt->query);
706
 
 
707
 
    if (!my_casecmp(mystr_get_prev_token((const char **)&szQueryEnd,
708
 
                                          stmt->query),"OF",2) &&
709
 
        !my_casecmp(mystr_get_prev_token((const char **)&szQueryEnd,
710
 
                                          stmt->query),"CURRENT",7) &&
711
 
        !my_casecmp(mystr_get_prev_token((const char **)&szQueryEnd,
712
 
                                          stmt->query),"WHERE",5))
713
 
    {
714
 
 
715
 
      /*
716
 
        Driver needs to setup the active stmt which has the cursor 
717
 
        specified in the query.
718
 
 
719
 
        Find out the cursor from all the stmts and make that as
720
 
        the active stmt..becuase this cursor is applicable
721
 
        for all stmts in the current DBC level
722
 
      */
723
 
      LIST *list_element,*next_element;
724
 
      DBC FAR *dbc=(DBC FAR*) stmt->dbc;
725
 
 
726
 
      for (list_element=dbc->statements ; list_element ;
727
 
           list_element=next_element)
728
 
      {
729
 
        next_element=list_element->next;
730
 
        *stmtNew = (HSTMT)list_element->data;
731
 
                                
732
 
        /* might have the cursor in the stmt without any resultset, so 
733
 
           avoid crashes, by keeping check on (*stmtNew)->result)
734
 
        */
735
 
        if (!my_strcasecmp((*stmtNew)->cursor_name,szCursor) &&
736
 
                           (*stmtNew)->result)
737
 
        {
738
 
          *szQueryEnd= '\0';
739
 
          return(TRUE);
740
 
        }
741
 
      }
742
 
      if (!list_element) 
743
 
      {
744
 
        char buff[100];
745
 
        strxmov(buff,"Cursor '",szCursor,"' does not exist",NullS);
746
 
        set_stmt_error(stmt,"3400",buff,ER_INVALID_CURSOR_NAME);
747
 
      }
748
 
      return(TRUE);
749
 
    }
750
 
  } /* END OF if (stmt->query && stmt->query_end) */
751
 
  return(FALSE);
752
 
753
 
 
754
 
/*
755
 
** checks whether the Primary Key column exists in the table
756
 
** if it exists, returns the PK column name
757
 
*/
758
 
 
759
 
SQLRETURN my_if_pk_exits(STMT FAR *stmt)
760
 
{
761
 
  char buff[100];
762
 
  MYSQL_ROW row;
763
 
  SQLHSTMT hStmtTemp;
764
 
  STMT FAR *stmtTemp;
765
 
  
766
 
  DBUG_ENTER("my_if_pk_exists");
767
 
 
768
 
  if(stmt->my_pk_validated)
769
 
    DBUG_RETURN(stmt->pk_count);
770
 
  
771
 
  if(my_SQLAllocStmt(stmt->dbc, &hStmtTemp) != SQL_SUCCESS)
772
 
      DBUG_RETURN(0);
773
 
 
774
 
  stmtTemp = (STMT FAR *)hStmtTemp;
775
 
 
776
 
  strxmov(buff,"show keys from ",stmt->result->fields->table,NullS);
777
 
  pthread_mutex_lock(&stmtTemp->dbc->lock);
778
 
  if( mysql_query(&stmtTemp->dbc->mysql,buff) ||
779
 
    !(stmtTemp->result=mysql_store_result(&stmtTemp->dbc->mysql)))
780
 
  {
781
 
    set_stmt_error(stmt,"S1000",mysql_error(&stmtTemp->dbc->mysql),
782
 
       mysql_errno(&stmtTemp->dbc->mysql));
783
 
    pthread_mutex_unlock(&stmtTemp->dbc->lock);
784
 
    my_SQLFreeStmt(hStmtTemp,SQL_DROP);
785
 
    DBUG_RETURN(0);
786
 
  }
787
 
  pthread_mutex_unlock(&stmtTemp->dbc->lock);
788
 
 
789
 
  /* right now take care of only PK's */
790
 
  while ((row = mysql_fetch_row(stmtTemp->result)) && 
791
 
      !(my_casecmp(row[2],"PRIMARY",7)) &&
792
 
       (stmt->pk_count < MY_MAX_PK_PARTS))
793
 
  {
794
 
    strmov(stmt->pk_col[stmt->pk_count++].name,row[4]);
795
 
  }
796
 
  stmt->my_pk_validated = TRUE;
797
 
  my_SQLFreeStmt(hStmtTemp,SQL_DROP);
798
 
  DBUG_RETURN(stmt->pk_count);
799
 
}
800
 
 
801
 
 
802
 
/*
803
 
** checks whether the Primary Key column exists in the result
804
 
** if it exists, takes that data and binds it,else does gets the 
805
 
** temporary resultset and takes the data
806
 
*/
807
 
 
808
 
SQLRETURN my_pk_param_bind(STMT FAR *stmtNew,STMT FAR *stmt,
809
 
                           SQLUSMALLINT irow,SQLUSMALLINT nColParam)
810
 
{
811
 
  MYSQL_RES   *result = stmt->result;
812
 
  MYSQL_FIELD *field;
813
 
  SQLUSMALLINT ncol;  
814
 
  SQLUSMALLINT index;
815
 
  DBUG_ENTER("my_pk_param_bind");
816
 
 
817
 
  /* check for any pk columns exists in the current resultset,
818
 
     if exists, take data from it and bind it
819
 
  */
820
 
  for (ncol = 0; ncol < result->field_count; ncol++)
821
 
  {
822
 
    field=result->fields+ncol;
823
 
    for (index=0; index < stmt->pk_count; index++)
824
 
    {
825
 
      if(!my_strcasecmp(stmt->pk_col[index].name,field->name))
826
 
      {     
827
 
        my_param_bind(stmtNew,stmt,index,(SQLUSMALLINT)(nColParam+index));
828
 
        stmt->pk_col[index].bind_done = TRUE; 
829
 
        break;
830
 
      }
831
 
    }
832
 
  }/* END OF for(ncol = 1 ; ncol <= stmt->result->field_count; ncol++)*/
833
 
  
834
 
  /* if PK doesn't exists in the current result set means, create a temp result
835
 
     set, and take the data from it.
836
 
  */
837
 
  {
838
 
    SQLHSTMT hStmtTemp;
839
 
    STMT FAR *stmtTemp; 
840
 
    DYNAMIC_STRING query;
841
 
    my_bool pk_not_in_set = FALSE;
842
 
    
843
 
    if (init_dynamic_string(&query, "SELECT ", 1024,1024))
844
 
      DBUG_RETURN(set_stmt_error(stmt,"S1001","Not enough memory",4001));
845
 
 
846
 
    for(index=0; index < stmt->pk_count; index++)
847
 
    {
848
 
      if(!stmt->pk_col[index].bind_done)
849
 
      {
850
 
        dynstr_append(&query,stmt->pk_col[index].name);   
851
 
        dynstr_append_mem(&query,",",1);
852
 
        pk_not_in_set = TRUE;
853
 
      }
854
 
    }
855
 
    /* if all the PK columns exists in the result set itself,
856
 
       return back, by inserting
857
 
    */
858
 
    if(!pk_not_in_set)
859
 
    {
860
 
      stmtNew->query = insert_params(stmtNew);
861
 
      dynstr_free(&query);
862
 
      DBUG_RETURN(0);
863
 
    }
864
 
    /* else get the data for other PK columns */
865
 
    query.length-=1;        /* Remove last  ', ' */
866
 
    dynstr_append(&query," FROM ");    
867
 
    dynstr_append(&query,stmt->table_name);    
868
 
 
869
 
    if(my_SQLAllocStmt(stmt->dbc, &hStmtTemp) != SQL_SUCCESS)
870
 
      DBUG_RETURN(0);
871
 
 
872
 
    stmtTemp = (STMT FAR *)hStmtTemp;
873
 
    pthread_mutex_lock(&stmtTemp->dbc->lock);
874
 
    if( mysql_query(&stmtTemp->dbc->mysql,query.str) ||
875
 
      !(stmtTemp->result=mysql_store_result(&stmtTemp->dbc->mysql)))
876
 
    {
877
 
      set_stmt_error(stmt,"S1000",mysql_error(&stmtTemp->dbc->mysql),
878
 
         mysql_errno(&stmtTemp->dbc->mysql));
879
 
      pthread_mutex_unlock(&stmtTemp->dbc->lock);
880
 
      my_SQLFreeStmt(hStmtTemp,SQL_DROP);
881
 
      DBUG_RETURN(0);
882
 
    }
883
 
    pthread_mutex_unlock(&stmtTemp->dbc->lock);       
884
 
 
885
 
    for(index=1;index <irow;index++)
886
 
      stmtTemp->result->data_cursor = stmtTemp->result->data_cursor->next;
887
 
 
888
 
    for(index=0; index < stmt->pk_count; index++)
889
 
    {
890
 
      if(!stmt->pk_col[index].bind_done)
891
 
      {
892
 
        my_param_bind(stmtNew,stmtTemp,0,(SQLUSMALLINT)(nColParam+index));
893
 
      }
894
 
    }    
895
 
    stmtNew->query = insert_params(stmtNew);
896
 
    my_SQLFreeStmt(hStmtTemp,SQL_DROP);
897
 
  } 
898
 
  DBUG_RETURN(1);
899
 
}
900
 
 
901
 
/**
902
 
* Building where search clause
903
 
**/
904
 
my_bool my_build_where_clause(STMT FAR *stmt,
905
 
                              DYNAMIC_STRING *dynQuery)
906
 
{
907
 
  MYSQL_RES *result = stmt->result;
908
 
  my_bool   pk_exists = FALSE;   
909
 
  uint      index;
910
 
 
911
 
  /* WHERE clause building.. */    
912
 
  dynstr_append(dynQuery," WHERE "); 
913
 
    
914
 
  /*  build the WHERE caluse... , if Primary Key exists
915
 
     take that column for searching, else use all columns
916
 
  */
917
 
  if (my_if_pk_exits(stmt))
918
 
  {
919
 
    for(index=0; index < stmt->pk_count; index++)
920
 
    {
921
 
      dynstr_append(dynQuery,stmt->pk_col[index].name);   
922
 
      dynstr_append_mem(dynQuery,"=? AND ",7);
923
 
    }
924
 
    pk_exists = TRUE;
925
 
  }
926
 
  else
927
 
  {
928
 
    MYSQL_FIELD *field, *end;
929
 
    for (field=result->fields, end=field+ result->field_count;
930
 
         field < end ; field++)
931
 
    {
932
 
      dynstr_append(dynQuery,field->name);   
933
 
      dynstr_append_mem(dynQuery,"=? AND ",7);
934
 
    }
935
 
  }
936
 
 
937
 
  dynQuery->length-=5;        /* Remove last  ' AND ' */
938
 
  dynstr_append(dynQuery," LIMIT 1");    
939
 
  return(pk_exists);
940
 
}
941
 
 
942
 
/*
943
 
** Returns the table used by this query.
944
 
** Ensures that all columns are from the same table
945
 
*/
946
 
 
947
 
static const char *find_used_table(STMT *stmt)
948
 
{
949
 
  MYSQL_FIELD  *field, *end;  
950
 
  const char *table_name;
951
 
  MYSQL_RES *result = stmt->result;
952
 
  DBUG_ENTER("find_used_table");
953
 
 
954
 
  if (stmt->table_name)
955
 
    DBUG_RETURN(stmt->table_name);    /* Return cached name */
956
 
 
957
 
  table_name=0;
958
 
  for (field=result->fields, end=field+ result->field_count;
959
 
       field < end ; field++)
960
 
  {
961
 
    if (field->table)
962
 
    {
963
 
      if (!table_name)
964
 
        table_name=field->table;
965
 
      if (strcmp(field->table, table_name))
966
 
      {
967
 
        set_stmt_error(stmt,"34000",
968
 
              "Can't modify a row from a statement that uses more than one table",
969
 
             ER_MULTIPLE_TABLE_IN_RESULT);
970
 
        DBUG_RETURN(NULL);
971
 
      }
972
 
    }
973
 
  }
974
 
  stmt->table_name=table_name;
975
 
  DBUG_RETURN(stmt->table_name);
976
 
}
977
 
 
978
 
/*
979
 
** position the data cursor to appropriate row
980
 
*/
981
 
void my_set_cursor_data(STMT FAR *stmt)
982
 
{
983
 
  SQLUINTEGER irow;
984
 
  MYSQL_RES *result=stmt->result;
985
 
  MYSQL_ROWS    *dcursor = result->data->data;
986
 
 
987
 
  if(!stmt->data_cursor)
988
 
  {
989
 
    for(irow=0; irow < stmt->current_row; irow++)
990
 
      dcursor = dcursor->next;
991
 
 
992
 
    stmt->data_cursor = TRUE;
993
 
    result->data_cursor = dcursor;   
994
 
  }
995
 
}
996
 
 
997