Annotation of embedaddon/php/ext/mysqlnd/mysqlnd_ps.c, revision 1.1.1.1

1.1       misho       1: /*
                      2:   +----------------------------------------------------------------------+
                      3:   | PHP Version 5                                                        |
                      4:   +----------------------------------------------------------------------+
                      5:   | Copyright (c) 2006-2012 The PHP Group                                |
                      6:   +----------------------------------------------------------------------+
                      7:   | This source file is subject to version 3.01 of the PHP license,      |
                      8:   | that is bundled with this package in the file LICENSE, and is        |
                      9:   | available through the world-wide-web at the following url:           |
                     10:   | http://www.php.net/license/3_01.txt                                  |
                     11:   | If you did not receive a copy of the PHP license and are unable to   |
                     12:   | obtain it through the world-wide-web, please send a note to          |
                     13:   | license@php.net so we can mail you a copy immediately.               |
                     14:   +----------------------------------------------------------------------+
                     15:   | Authors: Georg Richter <georg@mysql.com>                             |
                     16:   |          Andrey Hristov <andrey@mysql.com>                           |
                     17:   |          Ulf Wendel <uwendel@mysql.com>                              |
                     18:   +----------------------------------------------------------------------+
                     19: */
                     20: 
                     21: /* $Id: mysqlnd_ps.c 321634 2012-01-01 13:15:04Z felipe $ */
                     22: #include "php.h"
                     23: #include "mysqlnd.h"
                     24: #include "mysqlnd_wireprotocol.h"
                     25: #include "mysqlnd_priv.h"
                     26: #include "mysqlnd_result.h"
                     27: #include "mysqlnd_result_meta.h"
                     28: #include "mysqlnd_statistics.h"
                     29: #include "mysqlnd_debug.h"
                     30: #include "mysqlnd_block_alloc.h"
                     31: 
                     32: 
                     33: #define MYSQLND_SILENT
                     34: 
                     35: 
                     36: const char * const mysqlnd_not_bound_as_blob = "Can't send long data for non-string/non-binary data types";
                     37: const char * const mysqlnd_stmt_not_prepared = "Statement not prepared";
                     38: 
                     39: static struct st_mysqlnd_stmt_methods *mysqlnd_stmt_methods;
                     40: 
                     41: /* Exported by mysqlnd_ps_codec.c */
                     42: enum_func_status mysqlnd_stmt_execute_generate_request(MYSQLND_STMT * const s, zend_uchar ** request, size_t *request_len, zend_bool * free_buffer TSRMLS_DC);
                     43: 
                     44: enum_func_status mysqlnd_stmt_fetch_row_buffered(MYSQLND_RES *result, void *param,
                     45:                                                                                                unsigned int flags,
                     46:                                                                                                zend_bool *fetched_anything TSRMLS_DC);
                     47: 
                     48: enum_func_status mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES *result, void *param,
                     49:                                                                                           unsigned int flags,
                     50:                                                                                           zend_bool *fetched_anything TSRMLS_DC);
                     51: 
                     52: static void mysqlnd_stmt_separate_result_bind(MYSQLND_STMT * const stmt TSRMLS_DC);
                     53: static void mysqlnd_stmt_separate_one_result_bind(MYSQLND_STMT * const stmt, unsigned int param_no TSRMLS_DC);
                     54: 
                     55: /* {{{ mysqlnd_stmt::store_result */
                     56: static MYSQLND_RES *
                     57: MYSQLND_METHOD(mysqlnd_stmt, store_result)(MYSQLND_STMT * const s TSRMLS_DC)
                     58: {
                     59:        MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
                     60:        enum_func_status ret;
                     61:        MYSQLND * conn;
                     62:        MYSQLND_RES * result;
                     63:        zend_bool to_cache = FALSE;
                     64: 
                     65:        DBG_ENTER("mysqlnd_stmt::store_result");
                     66:        if (!stmt || !stmt->conn || !stmt->result) {
                     67:                DBG_RETURN(NULL);
                     68:        }
                     69:        DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
                     70: 
                     71:        conn = stmt->conn;
                     72: 
                     73:        /* be compliant with libmysql - NULL will turn */
                     74:        if (!stmt->field_count) {
                     75:                DBG_RETURN(NULL);
                     76:        }
                     77: 
                     78:        if (stmt->cursor_exists) {
                     79:                /* Silently convert buffered to unbuffered, for now */
                     80:                DBG_RETURN(s->m->use_result(s TSRMLS_CC));
                     81:        }
                     82: 
                     83:        /* Nothing to store for UPSERT/LOAD DATA*/
                     84:        if (CONN_GET_STATE(conn) != CONN_FETCHING_DATA ||
                     85:                stmt->state != MYSQLND_STMT_WAITING_USE_OR_STORE)
                     86:        {
                     87:                SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC,
                     88:                                                 UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
                     89:                DBG_RETURN(NULL);
                     90:        }
                     91: 
                     92:        stmt->default_rset_handler = s->m->store_result;
                     93: 
                     94:        SET_EMPTY_ERROR(stmt->error_info);
                     95:        SET_EMPTY_ERROR(stmt->conn->error_info);
                     96:        MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_PS_BUFFERED_SETS);
                     97: 
                     98:        result = stmt->result;
                     99:        result->type                    = MYSQLND_RES_PS_BUF;
                    100:        result->m.fetch_row             = mysqlnd_stmt_fetch_row_buffered;
                    101:        result->m.fetch_lengths = NULL;/* makes no sense */
                    102: 
                    103:        result->result_set_memory_pool = mysqlnd_mempool_create(MYSQLND_G(mempool_default_size) TSRMLS_CC);
                    104: 
                    105:        ret = result->m.store_result_fetch_data(conn, result, result->meta, TRUE, to_cache TSRMLS_CC);
                    106: 
                    107:        if (PASS == ret) {
                    108:                /* libmysql API docs say it should be so for SELECT statements */
                    109:                stmt->upsert_status.affected_rows = stmt->result->stored_data->row_count;
                    110: 
                    111:                stmt->state = MYSQLND_STMT_USE_OR_STORE_CALLED;
                    112:        } else {
                    113:                conn->error_info = result->stored_data->error_info;
                    114:                stmt->result->m.free_result_contents(stmt->result TSRMLS_CC);
                    115:                mnd_efree(stmt->result);
                    116:                stmt->result = NULL;
                    117:                stmt->state = MYSQLND_STMT_PREPARED;
                    118:        }
                    119: 
                    120:        DBG_RETURN(result);
                    121: }
                    122: /* }}} */
                    123: 
                    124: 
                    125: /* {{{ mysqlnd_stmt::get_result */
                    126: static MYSQLND_RES *
                    127: MYSQLND_METHOD(mysqlnd_stmt, get_result)(MYSQLND_STMT * const s TSRMLS_DC)
                    128: {
                    129:        MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
                    130:        MYSQLND * conn;
                    131:        MYSQLND_RES *result;
                    132: 
                    133:        DBG_ENTER("mysqlnd_stmt::get_result");
                    134:        if (!stmt || !stmt->conn || !stmt->result) {
                    135:                DBG_RETURN(NULL);
                    136:        }
                    137:        DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
                    138: 
                    139:        conn = stmt->conn;
                    140: 
                    141:        /* be compliant with libmysql - NULL will turn */
                    142:        if (!stmt->field_count) {
                    143:                DBG_RETURN(NULL);
                    144:        }
                    145: 
                    146:        if (stmt->cursor_exists) {
                    147:                /* Silently convert buffered to unbuffered, for now */
                    148:                DBG_RETURN(s->m->use_result(s TSRMLS_CC));
                    149:        }
                    150: 
                    151:        /* Nothing to store for UPSERT/LOAD DATA*/
                    152:        if (CONN_GET_STATE(conn) != CONN_FETCHING_DATA || stmt->state != MYSQLND_STMT_WAITING_USE_OR_STORE) {
                    153:                SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC,
                    154:                                                 UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
                    155:                DBG_RETURN(NULL);
                    156:        }
                    157: 
                    158:        SET_EMPTY_ERROR(stmt->error_info);
                    159:        SET_EMPTY_ERROR(stmt->conn->error_info);
                    160:        MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_BUFFERED_SETS);
                    161: 
                    162:        do {
                    163:                result = conn->m->result_init(stmt->result->field_count, stmt->persistent TSRMLS_CC);
                    164:                if (!result) {
                    165:                        SET_OOM_ERROR(stmt->conn->error_info);
                    166:                        break;
                    167:                }
                    168: 
                    169:                result->meta = stmt->result->meta->m->clone_metadata(stmt->result->meta, FALSE TSRMLS_CC);
                    170:                if (!result->meta) {
                    171:                        SET_OOM_ERROR(stmt->conn->error_info);
                    172:                        break;
                    173:                }
                    174: 
                    175:                if ((result = result->m.store_result(result, conn, TRUE TSRMLS_CC))) {
                    176:                        stmt->upsert_status.affected_rows = result->stored_data->row_count;
                    177:                        stmt->state = MYSQLND_STMT_PREPARED;
                    178:                        result->type = MYSQLND_RES_PS_BUF;
                    179:                } else {
                    180:                        stmt->error_info = conn->error_info;
                    181:                        stmt->state = MYSQLND_STMT_PREPARED;
                    182:                        break;
                    183:                }
                    184:                DBG_RETURN(result);
                    185:        } while (0);
                    186: 
                    187:        if (result) {
                    188:                result->m.free_result(result, TRUE TSRMLS_CC);
                    189:        }
                    190:        DBG_RETURN(NULL);
                    191: }
                    192: /* }}} */
                    193: 
                    194: 
                    195: /* {{{ mysqlnd_stmt::more_results */
                    196: static zend_bool
                    197: MYSQLND_METHOD(mysqlnd_stmt, more_results)(const MYSQLND_STMT * s TSRMLS_DC)
                    198: {
                    199:        MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
                    200:        DBG_ENTER("mysqlnd_stmt::more_results");
                    201:        /* (conn->state == CONN_NEXT_RESULT_PENDING) too */
                    202:        DBG_RETURN((stmt && stmt->conn && (stmt->conn->upsert_status.server_status &
                    203:                                                           SERVER_MORE_RESULTS_EXISTS))?
                    204:                                                                        TRUE:
                    205:                                                                        FALSE);
                    206: }
                    207: /* }}} */
                    208: 
                    209: 
                    210: /* {{{ mysqlnd_stmt::next_result */
                    211: static enum_func_status
                    212: MYSQLND_METHOD(mysqlnd_stmt, next_result)(MYSQLND_STMT * s TSRMLS_DC)
                    213: {
                    214:        MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
                    215:        MYSQLND * conn;
                    216: 
                    217:        DBG_ENTER("mysqlnd_stmt::next_result");
                    218:        if (!stmt || !stmt->conn || !stmt->result) {
                    219:                DBG_RETURN(FAIL);
                    220:        }
                    221:        conn = stmt->conn;
                    222:        DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
                    223: 
                    224:        if (CONN_GET_STATE(conn) != CONN_NEXT_RESULT_PENDING || !(conn->upsert_status.server_status & SERVER_MORE_RESULTS_EXISTS)) {
                    225:                DBG_RETURN(FAIL);
                    226:        }
                    227: 
                    228:        DBG_INF_FMT("server_status=%u cursor=%u", stmt->upsert_status.server_status, stmt->upsert_status.server_status & SERVER_STATUS_CURSOR_EXISTS);
                    229:        DBG_INF_FMT("server_status=%u cursor=%u", conn->upsert_status.server_status, conn->upsert_status.server_status & SERVER_STATUS_CURSOR_EXISTS);
                    230: 
                    231:        /* Free space for next result */
                    232:        s->m->free_stmt_content(s TSRMLS_CC);
                    233:        {
                    234:                enum_func_status ret = s->m->parse_execute_response(s TSRMLS_CC);
                    235:                DBG_RETURN(ret);
                    236:        }
                    237: }
                    238: /* }}} */
                    239: 
                    240: 
                    241: /* {{{ mysqlnd_stmt_skip_metadata */
                    242: static enum_func_status
                    243: mysqlnd_stmt_skip_metadata(MYSQLND_STMT * s TSRMLS_DC)
                    244: {
                    245:        MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
                    246:        /* Follows parameter metadata, we have just to skip it, as libmysql does */
                    247:        unsigned int i = 0;
                    248:        enum_func_status ret = FAIL;
                    249:        MYSQLND_PACKET_RES_FIELD * field_packet;
                    250: 
                    251:        DBG_ENTER("mysqlnd_stmt_skip_metadata");
                    252:        if (!stmt || !stmt->conn || !stmt->conn->protocol) {
                    253:                DBG_RETURN(FAIL);
                    254:        }
                    255:        DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
                    256: 
                    257:        field_packet = stmt->conn->protocol->m.get_result_field_packet(stmt->conn->protocol, FALSE TSRMLS_CC);
                    258:        if (!field_packet) {
                    259:                SET_OOM_ERROR(stmt->error_info);
                    260:                SET_OOM_ERROR(stmt->conn->error_info);
                    261:        } else {
                    262:                ret = PASS;
                    263:                field_packet->skip_parsing = TRUE;
                    264:                for (;i < stmt->param_count; i++) {
                    265:                        if (FAIL == PACKET_READ(field_packet, stmt->conn)) {
                    266:                                ret = FAIL;
                    267:                                break;
                    268:                        }
                    269:                }
                    270:                PACKET_FREE(field_packet);
                    271:        }
                    272: 
                    273:        DBG_RETURN(ret);
                    274: }
                    275: /* }}} */
                    276: 
                    277: 
                    278: /* {{{ mysqlnd_stmt_read_prepare_response */
                    279: static enum_func_status
                    280: mysqlnd_stmt_read_prepare_response(MYSQLND_STMT * s TSRMLS_DC)
                    281: {
                    282:        MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
                    283:        MYSQLND_PACKET_PREPARE_RESPONSE * prepare_resp;
                    284:        enum_func_status ret = FAIL;
                    285: 
                    286:        DBG_ENTER("mysqlnd_stmt_read_prepare_response");
                    287:        if (!stmt || !stmt->conn || !stmt->conn->protocol) {
                    288:                DBG_RETURN(FAIL);
                    289:        }
                    290:        DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
                    291: 
                    292:        prepare_resp = stmt->conn->protocol->m.get_prepare_response_packet(stmt->conn->protocol, FALSE TSRMLS_CC);
                    293:        if (!prepare_resp) {
                    294:                SET_OOM_ERROR(stmt->error_info);
                    295:                SET_OOM_ERROR(stmt->conn->error_info);
                    296:                goto done;
                    297:        }
                    298: 
                    299:        if (FAIL == PACKET_READ(prepare_resp, stmt->conn)) {
                    300:                goto done;
                    301:        }
                    302: 
                    303:        if (0xFF == prepare_resp->error_code) {
                    304:                stmt->error_info = stmt->conn->error_info = prepare_resp->error_info;
                    305:                goto done;
                    306:        }
                    307:        ret = PASS;
                    308:        stmt->stmt_id = prepare_resp->stmt_id;
                    309:        stmt->warning_count = stmt->conn->upsert_status.warning_count = prepare_resp->warning_count;
                    310:        stmt->field_count = stmt->conn->field_count = prepare_resp->field_count;
                    311:        stmt->param_count = prepare_resp->param_count;
                    312: done:
                    313:        PACKET_FREE(prepare_resp);
                    314: 
                    315:        DBG_RETURN(ret);
                    316: }
                    317: /* }}} */
                    318: 
                    319: 
                    320: /* {{{ mysqlnd_stmt_prepare_read_eof */
                    321: static enum_func_status
                    322: mysqlnd_stmt_prepare_read_eof(MYSQLND_STMT * s TSRMLS_DC)
                    323: {
                    324:        MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
                    325:        MYSQLND_PACKET_EOF * fields_eof;
                    326:        enum_func_status ret = FAIL;
                    327: 
                    328:        DBG_ENTER("mysqlnd_stmt_prepare_read_eof");
                    329:        if (!stmt || !stmt->conn || !stmt->conn->protocol) {
                    330:                DBG_RETURN(FAIL);
                    331:        }
                    332:        DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
                    333: 
                    334:        fields_eof = stmt->conn->protocol->m.get_eof_packet(stmt->conn->protocol, FALSE TSRMLS_CC);
                    335:        if (!fields_eof) {
                    336:                SET_OOM_ERROR(stmt->error_info);
                    337:                SET_OOM_ERROR(stmt->conn->error_info);
                    338:        } else {
                    339:                if (FAIL == (ret = PACKET_READ(fields_eof, stmt->conn))) {
                    340:                        if (stmt->result) {
                    341:                                stmt->result->m.free_result_contents(stmt->result TSRMLS_CC);
                    342:                                mnd_efree(stmt->result);
                    343:                                memset(stmt, 0, sizeof(MYSQLND_STMT_DATA));
                    344:                                stmt->state = MYSQLND_STMT_INITTED;
                    345:                        }
                    346:                } else {
                    347:                        stmt->upsert_status.server_status = fields_eof->server_status;
                    348:                        stmt->upsert_status.warning_count = fields_eof->warning_count;
                    349:                        stmt->state = MYSQLND_STMT_PREPARED;
                    350:                }
                    351:                PACKET_FREE(fields_eof);
                    352:        }
                    353: 
                    354:        DBG_RETURN(ret);
                    355: }
                    356: /* }}} */
                    357: 
                    358: 
                    359: /* {{{ mysqlnd_stmt::prepare */
                    360: static enum_func_status
                    361: MYSQLND_METHOD(mysqlnd_stmt, prepare)(MYSQLND_STMT * const s, const char * const query, unsigned int query_len TSRMLS_DC)
                    362: {
                    363:        MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
                    364:        MYSQLND_STMT * s_to_prepare = s;
                    365:        MYSQLND_STMT_DATA * stmt_to_prepare = stmt;
                    366: 
                    367:        DBG_ENTER("mysqlnd_stmt::prepare");
                    368:        if (!stmt || !stmt->conn) {
                    369:                DBG_RETURN(FAIL);
                    370:        }
                    371:        DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
                    372: 
                    373:        SET_ERROR_AFF_ROWS(stmt);
                    374:        SET_ERROR_AFF_ROWS(stmt->conn);
                    375: 
                    376:        SET_EMPTY_ERROR(stmt->error_info);
                    377:        SET_EMPTY_ERROR(stmt->conn->error_info);
                    378: 
                    379:        if (stmt->state > MYSQLND_STMT_INITTED) {
                    380:                /* See if we have to clean the wire */
                    381:                if (stmt->state == MYSQLND_STMT_WAITING_USE_OR_STORE) {
                    382:                        /* Do implicit use_result and then flush the result */
                    383:                        stmt->default_rset_handler = s->m->use_result;
                    384:                        stmt->default_rset_handler(s TSRMLS_CC);
                    385:                }
                    386:                /* No 'else' here please :) */
                    387:                if (stmt->state > MYSQLND_STMT_WAITING_USE_OR_STORE && stmt->result) {
                    388:                        stmt->result->m.skip_result(stmt->result TSRMLS_CC);
                    389:                }
                    390:                /*
                    391:                  Create a new test statement, which we will prepare, but if anything
                    392:                  fails, we will scrap it.
                    393:                */
                    394:                s_to_prepare = stmt->conn->m->stmt_init(stmt->conn TSRMLS_CC);
                    395:                if (!s_to_prepare) {
                    396:                        goto fail;
                    397:                }
                    398:                stmt_to_prepare = s_to_prepare->data;
                    399:        }
                    400: 
                    401:        if (FAIL == stmt_to_prepare->conn->m->simple_command(stmt_to_prepare->conn, COM_STMT_PREPARE, query, query_len, PROT_LAST, FALSE, TRUE TSRMLS_CC) ||
                    402:                FAIL == mysqlnd_stmt_read_prepare_response(s_to_prepare TSRMLS_CC))
                    403:        {
                    404:                goto fail;
                    405:        }
                    406: 
                    407:        if (stmt_to_prepare->param_count) {
                    408:                if (FAIL == mysqlnd_stmt_skip_metadata(s_to_prepare TSRMLS_CC) ||
                    409:                        FAIL == mysqlnd_stmt_prepare_read_eof(s_to_prepare TSRMLS_CC))
                    410:                {
                    411:                        goto fail;
                    412:                }
                    413:        }
                    414: 
                    415:        /*
                    416:          Read metadata only if there is actual result set.
                    417:          Beware that SHOW statements bypass the PS framework and thus they send
                    418:          no metadata at prepare.
                    419:        */
                    420:        if (stmt_to_prepare->field_count) {
                    421:                MYSQLND_RES * result = stmt->conn->m->result_init(stmt_to_prepare->field_count, stmt_to_prepare->persistent TSRMLS_CC);
                    422:                if (!result) {
                    423:                        SET_OOM_ERROR(stmt->conn->error_info);
                    424:                        goto fail;
                    425:                }
                    426:                /* Allocate the result now as it is needed for the reading of metadata */
                    427:                stmt_to_prepare->result = result; 
                    428: 
                    429:                result->conn = stmt_to_prepare->conn->m->get_reference(stmt_to_prepare->conn TSRMLS_CC);
                    430: 
                    431:                result->type = MYSQLND_RES_PS_BUF;
                    432: 
                    433:                if (FAIL == result->m.read_result_metadata(result, stmt_to_prepare->conn TSRMLS_CC) ||
                    434:                        FAIL == mysqlnd_stmt_prepare_read_eof(s_to_prepare TSRMLS_CC))
                    435:                {
                    436:                        goto fail;
                    437:                }
                    438:        }
                    439: 
                    440:        if (stmt_to_prepare != stmt) {
                    441:                /* swap */
                    442:                size_t real_size = sizeof(MYSQLND_STMT) + mysqlnd_plugin_count() * sizeof(void *);
                    443:                char * tmp_swap = mnd_malloc(real_size);
                    444:                memcpy(tmp_swap, s, real_size);
                    445:                memcpy(s, s_to_prepare, real_size);
                    446:                memcpy(s_to_prepare, tmp_swap, real_size);
                    447:                mnd_free(tmp_swap);
                    448:                {
                    449:                        MYSQLND_STMT_DATA * tmp_swap_data = stmt_to_prepare;
                    450:                        stmt_to_prepare = stmt;
                    451:                        stmt = tmp_swap_data;
                    452:                }
                    453:                s_to_prepare->m->dtor(s_to_prepare, TRUE TSRMLS_CC);
                    454:        }
                    455:        stmt->state = MYSQLND_STMT_PREPARED;
                    456:        DBG_INF("PASS");
                    457:        DBG_RETURN(PASS);
                    458: 
                    459: fail:
                    460:        if (stmt_to_prepare != stmt && s_to_prepare) {
                    461:                s_to_prepare->m->dtor(s_to_prepare, TRUE TSRMLS_CC);
                    462:        }
                    463:        stmt->state = MYSQLND_STMT_INITTED;
                    464: 
                    465:        DBG_INF("FAIL");
                    466:        DBG_RETURN(FAIL);
                    467: }
                    468: /* }}} */
                    469: 
                    470: 
                    471: /* {{{ mysqlnd_stmt_execute_parse_response */
                    472: static enum_func_status
                    473: mysqlnd_stmt_execute_parse_response(MYSQLND_STMT * const s TSRMLS_DC)
                    474: {
                    475:        MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
                    476:        enum_func_status ret;
                    477:        MYSQLND * conn;
                    478: 
                    479:        DBG_ENTER("mysqlnd_stmt_execute_parse_response");
                    480:        if (!stmt || !stmt->conn) {
                    481:                DBG_RETURN(FAIL);
                    482:        }
                    483:        conn = stmt->conn;
                    484:        CONN_SET_STATE(conn, CONN_QUERY_SENT);
                    485: 
                    486:        ret = mysqlnd_query_read_result_set_header(stmt->conn, s TSRMLS_CC);
                    487:        if (ret == FAIL) {
                    488:                stmt->error_info = conn->error_info;
                    489:                stmt->upsert_status.affected_rows = conn->upsert_status.affected_rows;
                    490:                if (CONN_GET_STATE(conn) == CONN_QUIT_SENT) {
                    491:                        /* close the statement here, the connection has been closed */
                    492:                }
                    493:                stmt->state = MYSQLND_STMT_PREPARED;
                    494:                stmt->send_types_to_server = 1;
                    495:        } else {
                    496:                /*
                    497:                  stmt->send_types_to_server has already been set to 0 in
                    498:                  mysqlnd_stmt_execute_generate_request / mysqlnd_stmt_execute_store_params
                    499:                  In case there is a situation in which binding was done for integer and the
                    500:                  value is > LONG_MAX or < LONG_MIN, there is string conversion and we have
                    501:                  to resend the types. Next execution will also need to resend the type.
                    502:                */
                    503:                SET_EMPTY_ERROR(stmt->error_info);
                    504:                SET_EMPTY_ERROR(stmt->conn->error_info);
                    505:                stmt->upsert_status = conn->upsert_status;
                    506:                stmt->state = MYSQLND_STMT_EXECUTED;
                    507:                if (conn->last_query_type == QUERY_UPSERT || conn->last_query_type == QUERY_LOAD_LOCAL) {
                    508:                        DBG_INF("PASS");
                    509:                        DBG_RETURN(PASS);
                    510:                }
                    511: 
                    512:                stmt->result->type = MYSQLND_RES_PS_BUF;
                    513:                if (!stmt->result->conn) {
                    514:                        /*
                    515:                          For SHOW we don't create (bypasses PS in server)
                    516:                          a result set at prepare and thus a connection was missing
                    517:                        */
                    518:                        stmt->result->conn = stmt->conn->m->get_reference(stmt->conn TSRMLS_CC);
                    519:                }
                    520: 
                    521:                /* Update stmt->field_count as SHOW sets it to 0 at prepare */
                    522:                stmt->field_count = stmt->result->field_count = conn->field_count;
                    523:                stmt->result->lengths = NULL;
                    524:                if (stmt->field_count) {
                    525:                        stmt->state = MYSQLND_STMT_WAITING_USE_OR_STORE;
                    526:                        /*
                    527:                          We need to set this because the user might not call
                    528:                          use_result() or store_result() and we should be able to scrap the
                    529:                          data on the line, if he just decides to close the statement.
                    530:                        */
                    531:                        DBG_INF_FMT("server_status=%u cursor=%u", stmt->upsert_status.server_status,
                    532:                                                stmt->upsert_status.server_status & SERVER_STATUS_CURSOR_EXISTS);
                    533: 
                    534:                        if (stmt->upsert_status.server_status & SERVER_STATUS_CURSOR_EXISTS) {
                    535:                                DBG_INF("cursor exists");
                    536:                                stmt->cursor_exists = TRUE;
                    537:                                CONN_SET_STATE(conn, CONN_READY);
                    538:                                /* Only cursor read */
                    539:                                stmt->default_rset_handler = s->m->use_result;
                    540:                                DBG_INF("use_result");
                    541:                        } else if (stmt->flags & CURSOR_TYPE_READ_ONLY) {
                    542:                                DBG_INF("asked for cursor but got none");
                    543:                                /*
                    544:                                  We have asked for CURSOR but got no cursor, because the condition
                    545:                                  above is not fulfilled. Then...
                    546: 
                    547:                                  This is a single-row result set, a result set with no rows, EXPLAIN,
                    548:                                  SHOW VARIABLES, or some other command which either a) bypasses the
                    549:                                  cursors framework in the server and writes rows directly to the
                    550:                                  network or b) is more efficient if all (few) result set rows are
                    551:                                  precached on client and server's resources are freed.
                    552:                                */
                    553:                                /* preferred is buffered read */
                    554:                                stmt->default_rset_handler = s->m->store_result;
                    555:                                DBG_INF("store_result");
                    556:                        } else {
                    557:                                DBG_INF("no cursor");
                    558:                                /* preferred is unbuffered read */
                    559:                                stmt->default_rset_handler = s->m->use_result;
                    560:                                DBG_INF("use_result");
                    561:                        }
                    562:                }
                    563:        }
                    564: #ifndef MYSQLND_DONT_SKIP_OUT_PARAMS_RESULTSET
                    565:        if (stmt->upsert_status.server_status & SERVER_PS_OUT_PARAMS) {
                    566:                s->m->free_stmt_content(s TSRMLS_CC);
                    567:                DBG_INF("PS OUT Variable RSet, skipping");
                    568:                /* OUT params result set. Skip for now to retain compatibility */
                    569:                ret = mysqlnd_stmt_execute_parse_response(s TSRMLS_CC);
                    570:        }
                    571: #endif
                    572: 
                    573:        DBG_INF(ret == PASS? "PASS":"FAIL");
                    574:        DBG_RETURN(ret);
                    575: }
                    576: /* }}} */
                    577: 
                    578: 
                    579: /* {{{ mysqlnd_stmt::execute */
                    580: static enum_func_status
                    581: MYSQLND_METHOD(mysqlnd_stmt, execute)(MYSQLND_STMT * const s TSRMLS_DC)
                    582: {
                    583:        MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
                    584:        enum_func_status ret;
                    585:        MYSQLND *       conn;
                    586:        zend_uchar *request = NULL;
                    587:        size_t          request_len;
                    588:        zend_bool       free_request;
                    589: 
                    590:        DBG_ENTER("mysqlnd_stmt::execute");
                    591:        if (!stmt || !stmt->conn) {
                    592:                DBG_RETURN(FAIL);
                    593:        }
                    594:        conn = stmt->conn;
                    595:        DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
                    596: 
                    597:        SET_ERROR_AFF_ROWS(stmt);
                    598:        SET_ERROR_AFF_ROWS(stmt->conn);
                    599: 
                    600:        if (stmt->result && stmt->state >= MYSQLND_STMT_PREPARED && stmt->field_count) {
                    601:                /*
                    602:                  We don need to copy the data from the buffers which we will clean.
                    603:                  Because it has already been copied. See
                    604:                  #ifndef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF
                    605:                */
                    606: #ifdef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF
                    607:                if (stmt->result_bind &&
                    608:                        stmt->result_zvals_separated_once == TRUE && 
                    609:                        stmt->state >= MYSQLND_STMT_USER_FETCHING)
                    610:                {
                    611:                        /*
                    612:                          We need to copy the data from the buffers which we will clean.
                    613:                          The bound variables point to them only if the user has started
                    614:                          to fetch data (MYSQLND_STMT_USER_FETCHING).
                    615:                          We need to check 'result_zvals_separated_once' or we will leak
                    616:                          in the following scenario
                    617:                          prepare("select 1 from dual");
                    618:                          execute();
                    619:                          fetch(); <-- no binding, but that's not a problem
                    620:                          bind_result();
                    621:                          execute(); <-- here we will leak because we separate without need
                    622:                        */
                    623:                        unsigned int i;
                    624:                        for (i = 0; i < stmt->field_count; i++) {
                    625:                                if (stmt->result_bind[i].bound == TRUE) {
                    626:                                        zval_copy_ctor(stmt->result_bind[i].zv);
                    627:                                }
                    628:                        }
                    629:                }
                    630: #endif
                    631: 
                    632:                /*
                    633:                  If right after execute() we have to call the appropriate
                    634:                  use_result() or store_result() and clean.
                    635:                */
                    636:                if (stmt->state == MYSQLND_STMT_WAITING_USE_OR_STORE) {
                    637:                        DBG_INF("fetching result set header");
                    638:                        /* Do implicit use_result and then flush the result */
                    639:                        stmt->default_rset_handler = s->m->use_result;
                    640:                        stmt->default_rset_handler(s TSRMLS_CC);
                    641:                }
                    642: 
                    643:                if (stmt->state > MYSQLND_STMT_WAITING_USE_OR_STORE) {
                    644:                        DBG_INF("skipping result");
                    645:                        /* Flush if anything is left and unbuffered set */
                    646:                        stmt->result->m.skip_result(stmt->result TSRMLS_CC);
                    647:                }
                    648: 
                    649:                if (stmt->state > MYSQLND_STMT_PREPARED) {
                    650:                        /* As the buffers have been freed, we should go back to PREPARED */
                    651:                        stmt->state = MYSQLND_STMT_PREPARED;
                    652:                }
                    653: 
                    654:                /*
                    655:                  Executed, but the user hasn't started to fetch
                    656:                  This will clean also the metadata, but after the EXECUTE call we will
                    657:                  have it again.
                    658:                */
                    659:                stmt->result->m.free_result_buffers(stmt->result TSRMLS_CC);
                    660:        } else if (stmt->state < MYSQLND_STMT_PREPARED) {
                    661:                /* Only initted - error */
                    662:                SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE,
                    663:                                                 mysqlnd_out_of_sync);
                    664:                SET_STMT_ERROR(stmt, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
                    665:                DBG_INF("FAIL");
                    666:                DBG_RETURN(FAIL);
                    667:        }
                    668: 
                    669:        if (stmt->param_count) {
                    670:                unsigned int i, not_bound = 0;
                    671:                if (!stmt->param_bind) {
                    672:                        SET_STMT_ERROR(stmt, CR_PARAMS_NOT_BOUND, UNKNOWN_SQLSTATE,
                    673:                                                         "No data supplied for parameters in prepared statement");
                    674:                        DBG_INF("FAIL");
                    675:                        DBG_RETURN(FAIL);
                    676:                }
                    677:                for (i = 0; i < stmt->param_count; i++) {
                    678:                        if (stmt->param_bind[i].zv == NULL) {
                    679:                                not_bound++;
                    680:                        }
                    681:                }
                    682:                if (not_bound) {
                    683:                        char * msg;
                    684:                        spprintf(&msg, 0, "No data supplied for %u parameter%s in prepared statement",
                    685:                                         not_bound, not_bound>1 ?"s":"");
                    686:                        SET_STMT_ERROR(stmt, CR_PARAMS_NOT_BOUND, UNKNOWN_SQLSTATE, msg);
                    687:                        if (msg) {
                    688:                                efree(msg); /* allocated by spprintf */
                    689:                        }
                    690:                        DBG_INF("FAIL");
                    691:                        DBG_RETURN(FAIL);
                    692:                }
                    693:        }
                    694:        ret = s->m->generate_execute_request(s, &request, &request_len, &free_request TSRMLS_CC);
                    695:        if (ret == PASS) {
                    696:                /* support for buffer types should be added here ! */
                    697:                ret = stmt->conn->m->simple_command(stmt->conn, COM_STMT_EXECUTE, (char *)request, request_len,
                    698:                                                                                        PROT_LAST /* we will handle the response packet*/,
                    699:                                                                                        FALSE, FALSE TSRMLS_CC);
                    700:        } else {
                    701:                SET_STMT_ERROR(stmt, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, "Couldn't generate the request. Possibly OOM.");
                    702:        }
                    703: 
                    704:        if (free_request) {
                    705:                mnd_efree(request);
                    706:        }
                    707: 
                    708:        if (ret == FAIL) {
                    709:                stmt->error_info = conn->error_info;
                    710:                DBG_INF("FAIL");
                    711:                DBG_RETURN(FAIL);
                    712:        }
                    713:        stmt->execute_count++;
                    714: 
                    715:        ret = s->m->parse_execute_response(s TSRMLS_CC);
                    716: 
                    717:        DBG_INF_FMT("server_status=%u cursor=%u", stmt->upsert_status.server_status, stmt->upsert_status.server_status & SERVER_STATUS_CURSOR_EXISTS);
                    718: 
                    719:        if (ret == PASS && conn->last_query_type == QUERY_UPSERT && stmt->upsert_status.affected_rows) {
                    720:                MYSQLND_INC_CONN_STATISTIC_W_VALUE(conn->stats, STAT_ROWS_AFFECTED_PS, stmt->upsert_status.affected_rows);
                    721:        }
                    722:        DBG_RETURN(ret);
                    723: }
                    724: /* }}} */
                    725: 
                    726: 
                    727: /* {{{ mysqlnd_stmt_fetch_row_buffered */
                    728: enum_func_status
                    729: mysqlnd_stmt_fetch_row_buffered(MYSQLND_RES *result, void *param, unsigned int flags, zend_bool *fetched_anything TSRMLS_DC)
                    730: {
                    731:        MYSQLND_STMT * s = (MYSQLND_STMT *) param;
                    732:        MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
                    733:        MYSQLND_RES_BUFFERED *set = result->stored_data;
                    734:        unsigned int field_count = result->meta->field_count;
                    735: 
                    736:        DBG_ENTER("mysqlnd_stmt_fetch_row_buffered");
                    737:        *fetched_anything = FALSE;
                    738:        DBG_INF_FMT("stmt=%lu", stmt != NULL ? stmt->stmt_id : 0L);
                    739: 
                    740:        /* If we haven't read everything */
                    741:        if (set->data_cursor &&
                    742:                (set->data_cursor - set->data) < (set->row_count * field_count))
                    743:        {
                    744:                /* The user could have skipped binding - don't crash*/
                    745:                if (stmt->result_bind) {
                    746:                        unsigned int i;
                    747:                        MYSQLND_RES_METADATA * meta = result->meta;
                    748:                        zval **current_row = set->data_cursor;
                    749: 
                    750:                        if (NULL == current_row[0]) {
                    751:                                uint64_t row_num = (set->data_cursor - set->data) / field_count;
                    752:                                enum_func_status rc = result->m.row_decoder(set->row_buffers[row_num],
                    753:                                                                                                current_row,
                    754:                                                                                                meta->field_count,
                    755:                                                                                                meta->fields,
                    756:                                                                                                result->stored_data->persistent,
                    757:                                                                                                result->conn->options.numeric_and_datetime_as_unicode,
                    758:                                                                                                result->conn->options.int_and_float_native,
                    759:                                                                                                result->conn->stats TSRMLS_CC);
                    760:                                if (PASS != rc) {
                    761:                                        DBG_RETURN(FAIL);
                    762:                                }
                    763:                                set->initialized_rows++;
                    764:                                if (stmt->update_max_length) {
                    765:                                        for (i = 0; i < result->field_count; i++) {
                    766:                                                /*
                    767:                                                  NULL fields are 0 length, 0 is not more than 0
                    768:                                                  String of zero size, definitely can't be the next max_length.
                    769:                                                  Thus for NULL and zero-length we are quite efficient.
                    770:                                                */
                    771:                                                if (Z_TYPE_P(current_row[i]) >= IS_STRING) {
                    772:                                                        unsigned long len = Z_STRLEN_P(current_row[i]);
                    773:                                                        if (meta->fields[i].max_length < len) {
                    774:                                                                meta->fields[i].max_length = len;
                    775:                                                        }
                    776:                                                }
                    777:                                        }
                    778:                                }
                    779:                        }
                    780: 
                    781:                        for (i = 0; i < result->field_count; i++) {
                    782:                                /* Clean what we copied last time */
                    783: #ifndef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF
                    784:                                if (stmt->result_bind[i].zv) {
                    785:                                        zval_dtor(stmt->result_bind[i].zv);
                    786:                                }
                    787: #endif
                    788:                                /* copy the type */
                    789:                                if (stmt->result_bind[i].bound == TRUE) {
                    790:                                        DBG_INF_FMT("i=%u type=%u", i, Z_TYPE_P(current_row[i]));
                    791:                                        if (Z_TYPE_P(current_row[i]) != IS_NULL) {
                    792:                                                /*
                    793:                                                  Copy the value.
                    794:                                                  Pre-condition is that the zvals in the result_bind buffer
                    795:                                                  have been  ZVAL_NULL()-ed or to another simple type
                    796:                                                  (int, double, bool but not string). Because of the reference
                    797:                                                  counting the user can't delete the strings the variables point to.
                    798:                                                */
                    799: 
                    800:                                                Z_TYPE_P(stmt->result_bind[i].zv) = Z_TYPE_P(current_row[i]);
                    801:                                                stmt->result_bind[i].zv->value = current_row[i]->value;
                    802: #ifndef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF
                    803:                                                zval_copy_ctor(stmt->result_bind[i].zv);
                    804: #endif
                    805:                                        } else {
                    806:                                                ZVAL_NULL(stmt->result_bind[i].zv);
                    807:                                        }
                    808:                                }
                    809:                        }
                    810:                }
                    811:                set->data_cursor += field_count;
                    812:                *fetched_anything = TRUE;
                    813:                /* buffered result sets don't have a connection */
                    814:                MYSQLND_INC_GLOBAL_STATISTIC(STAT_ROWS_FETCHED_FROM_CLIENT_PS_BUF);
                    815:                DBG_INF("row fetched");
                    816:        } else {
                    817:                set->data_cursor = NULL;
                    818:                DBG_INF("no more data");
                    819:        }
                    820:        DBG_INF("PASS");
                    821:        DBG_RETURN(PASS);
                    822: }
                    823: /* }}} */
                    824: 
                    825: 
                    826: /* {{{ mysqlnd_stmt_fetch_row_unbuffered */
                    827: static enum_func_status
                    828: mysqlnd_stmt_fetch_row_unbuffered(MYSQLND_RES *result, void *param, unsigned int flags, zend_bool *fetched_anything TSRMLS_DC)
                    829: {
                    830:        enum_func_status ret;
                    831:        MYSQLND_STMT * s = (MYSQLND_STMT *) param;
                    832:        MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
                    833:        MYSQLND_PACKET_ROW * row_packet;
                    834: 
                    835:        DBG_ENTER("mysqlnd_stmt_fetch_row_unbuffered");
                    836: 
                    837:        *fetched_anything = FALSE;
                    838: 
                    839:        if (result->unbuf->eof_reached) {
                    840:                /* No more rows obviously */
                    841:                DBG_INF("eof reached");
                    842:                DBG_RETURN(PASS);
                    843:        }
                    844:        if (CONN_GET_STATE(result->conn) != CONN_FETCHING_DATA) {
                    845:                SET_CLIENT_ERROR(result->conn->error_info, CR_COMMANDS_OUT_OF_SYNC,
                    846:                                                 UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
                    847:                DBG_ERR("command out of sync");
                    848:                DBG_RETURN(FAIL);
                    849:        }
                    850:        if (!(row_packet = result->row_packet)) {
                    851:                DBG_RETURN(FAIL);
                    852:        }
                    853: 
                    854:        /* Let the row packet fill our buffer and skip additional malloc + memcpy */
                    855:        row_packet->skip_extraction = stmt && stmt->result_bind? FALSE:TRUE;
                    856: 
                    857:        /*
                    858:          If we skip rows (stmt == NULL || stmt->result_bind == NULL) we have to
                    859:          result->m.unbuffered_free_last_data() before it. The function returns always true.
                    860:        */
                    861:        if (PASS == (ret = PACKET_READ(row_packet, result->conn)) && !row_packet->eof) {
                    862:                unsigned int i, field_count = result->field_count;
                    863: 
                    864:                if (!row_packet->skip_extraction) {
                    865:                        result->m.unbuffered_free_last_data(result TSRMLS_CC);
                    866: 
                    867:                        DBG_INF("extracting data");
                    868:                        result->unbuf->last_row_data = row_packet->fields;
                    869:                        result->unbuf->last_row_buffer = row_packet->row_buffer;
                    870:                        row_packet->fields = NULL;
                    871:                        row_packet->row_buffer = NULL;
                    872: 
                    873:                        if (PASS != result->m.row_decoder(result->unbuf->last_row_buffer,
                    874:                                                                        result->unbuf->last_row_data,
                    875:                                                                        row_packet->field_count,
                    876:                                                                        row_packet->fields_metadata,
                    877:                                                                        FALSE,
                    878:                                                                        result->conn->options.numeric_and_datetime_as_unicode,
                    879:                                                                        result->conn->options.int_and_float_native,
                    880:                                                                        result->conn->stats TSRMLS_CC))
                    881:                        {
                    882:                                DBG_RETURN(FAIL);
                    883:                        }
                    884: 
                    885:                        for (i = 0; i < field_count; i++) {
                    886:                                if (stmt->result_bind[i].bound == TRUE) {
                    887:                                        zval *data = result->unbuf->last_row_data[i];
                    888:                                        /*
                    889:                                          stmt->result_bind[i].zv has been already destructed
                    890:                                          in result->m.unbuffered_free_last_data()
                    891:                                        */
                    892: #ifndef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF
                    893:                                        zval_dtor(stmt->result_bind[i].zv);
                    894: #endif
                    895:                                        if (IS_NULL != (Z_TYPE_P(stmt->result_bind[i].zv) = Z_TYPE_P(data)) ) {
                    896:                                                if (
                    897:                                                        (Z_TYPE_P(data) == IS_STRING
                    898: #if MYSQLND_UNICODE
                    899:                                                        || Z_TYPE_P(data) == IS_UNICODE
                    900: #endif
                    901:                                                        )
                    902:                                                         && (result->meta->fields[i].max_length < (unsigned long) Z_STRLEN_P(data)))
                    903:                                                {
                    904:                                                        result->meta->fields[i].max_length = Z_STRLEN_P(data);
                    905:                                                }
                    906:                                                stmt->result_bind[i].zv->value = data->value;
                    907:                                                /* copied data, thus also the ownership. Thus null data */
                    908:                                                ZVAL_NULL(data);
                    909:                                        }
                    910:                                }
                    911:                        }
                    912:                        MYSQLND_INC_CONN_STATISTIC(stmt->conn->stats, STAT_ROWS_FETCHED_FROM_CLIENT_PS_UNBUF);
                    913:                } else {
                    914:                        DBG_INF("skipping extraction");
                    915:                        /*
                    916:                          Data has been allocated and usually result->m.unbuffered_free_last_data()
                    917:                          frees it but we can't call this function as it will cause problems with
                    918:                          the bound variables. Thus we need to do part of what it does or Zend will
                    919:                          report leaks.
                    920:                        */
                    921:                        row_packet->row_buffer->free_chunk(row_packet->row_buffer TSRMLS_CC);
                    922:                        row_packet->row_buffer = NULL;
                    923:                }
                    924: 
                    925:                result->unbuf->row_count++;
                    926:                *fetched_anything = TRUE;
                    927:        } else if (ret == FAIL) {
                    928:                if (row_packet->error_info.error_no) {
                    929:                        stmt->conn->error_info = row_packet->error_info; 
                    930:                        stmt->error_info = row_packet->error_info; 
                    931:                }
                    932:                CONN_SET_STATE(result->conn, CONN_READY);
                    933:                result->unbuf->eof_reached = TRUE; /* so next time we won't get an error */
                    934:        } else if (row_packet->eof) {
                    935:                DBG_INF("EOF");
                    936:                /* Mark the connection as usable again */
                    937:                result->unbuf->eof_reached = TRUE;
                    938:                result->conn->upsert_status.warning_count = row_packet->warning_count;
                    939:                result->conn->upsert_status.server_status = row_packet->server_status;
                    940:                /*
                    941:                  result->row_packet will be cleaned when
                    942:                  destroying the result object
                    943:                */
                    944:                if (result->conn->upsert_status.server_status & SERVER_MORE_RESULTS_EXISTS) {
                    945:                        CONN_SET_STATE(result->conn, CONN_NEXT_RESULT_PENDING);
                    946:                } else {
                    947:                        CONN_SET_STATE(result->conn, CONN_READY);
                    948:                }
                    949:        }
                    950: 
                    951:        DBG_INF_FMT("ret=%s fetched_anything=%u", ret == PASS? "PASS":"FAIL", *fetched_anything);
                    952:        DBG_RETURN(ret);
                    953: }
                    954: /* }}} */
                    955: 
                    956: 
                    957: /* {{{ mysqlnd_stmt::use_result */
                    958: static MYSQLND_RES *
                    959: MYSQLND_METHOD(mysqlnd_stmt, use_result)(MYSQLND_STMT * s TSRMLS_DC)
                    960: {
                    961:        MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
                    962:        MYSQLND_RES *result;
                    963:        MYSQLND * conn;
                    964: 
                    965:        DBG_ENTER("mysqlnd_stmt::use_result");
                    966:        if (!stmt || !stmt->conn || !stmt->result) {
                    967:                DBG_RETURN(NULL);
                    968:        }
                    969:        DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
                    970: 
                    971:        conn = stmt->conn;
                    972: 
                    973:        if (!stmt->field_count ||
                    974:                (!stmt->cursor_exists && CONN_GET_STATE(conn) != CONN_FETCHING_DATA) ||
                    975:                (stmt->cursor_exists && CONN_GET_STATE(conn) != CONN_READY) ||
                    976:                (stmt->state != MYSQLND_STMT_WAITING_USE_OR_STORE))
                    977:        {
                    978:                SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC,
                    979:                                                 UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
                    980:                DBG_ERR("command out of sync");
                    981:                DBG_RETURN(NULL);
                    982:        }
                    983: 
                    984:        SET_EMPTY_ERROR(stmt->error_info);
                    985: 
                    986:        MYSQLND_INC_CONN_STATISTIC(stmt->conn->stats, STAT_PS_UNBUFFERED_SETS);
                    987:        result = stmt->result;
                    988: 
                    989:        DBG_INF_FMT("%scursor exists", stmt->cursor_exists? "":"no ");
                    990:        result->m.use_result(stmt->result, TRUE TSRMLS_CC);
                    991:        result->m.fetch_row     = stmt->cursor_exists? mysqlnd_fetch_stmt_row_cursor:
                    992:                                                                                           mysqlnd_stmt_fetch_row_unbuffered;
                    993:        stmt->state = MYSQLND_STMT_USE_OR_STORE_CALLED;
                    994: 
                    995:        DBG_INF_FMT("%p", result);
                    996:        DBG_RETURN(result);
                    997: }
                    998: /* }}} */
                    999: 
                   1000: 
                   1001: #define STMT_ID_LENGTH 4
                   1002: 
                   1003: /* {{{ mysqlnd_fetch_row_cursor */
                   1004: enum_func_status
                   1005: mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES *result, void *param, unsigned int flags, zend_bool *fetched_anything TSRMLS_DC)
                   1006: {
                   1007:        enum_func_status ret;
                   1008:        MYSQLND_STMT * s = (MYSQLND_STMT *) param;
                   1009:        MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
                   1010:        zend_uchar buf[STMT_ID_LENGTH /* statement id */ + 4 /* number of rows to fetch */];
                   1011:        MYSQLND_PACKET_ROW * row_packet;
                   1012: 
                   1013:        DBG_ENTER("mysqlnd_fetch_stmt_row_cursor");
                   1014: 
                   1015:        if (!stmt || !stmt->conn || !result || !result->conn || !result->unbuf) {
                   1016:                DBG_ERR("no statement");
                   1017:                DBG_RETURN(FAIL);
                   1018:        }
                   1019: 
                   1020:        DBG_INF_FMT("stmt=%lu flags=%u", stmt->stmt_id, flags);
                   1021: 
                   1022:        if (stmt->state < MYSQLND_STMT_USER_FETCHING) {
                   1023:                /* Only initted - error */
                   1024:                SET_CLIENT_ERROR(stmt->conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE,
                   1025:                                                mysqlnd_out_of_sync);
                   1026:                DBG_ERR("command out of sync");
                   1027:                DBG_RETURN(FAIL);
                   1028:        }
                   1029:        if (!(row_packet = result->row_packet)) {
                   1030:                DBG_RETURN(FAIL);
                   1031:        }
                   1032: 
                   1033:        SET_EMPTY_ERROR(stmt->error_info);
                   1034:        SET_EMPTY_ERROR(stmt->conn->error_info);
                   1035: 
                   1036:        int4store(buf, stmt->stmt_id);
                   1037:        int4store(buf + STMT_ID_LENGTH, 1); /* for now fetch only one row */
                   1038: 
                   1039:        if (FAIL == stmt->conn->m->simple_command(stmt->conn, COM_STMT_FETCH, (char *)buf, sizeof(buf),
                   1040:                                                                                          PROT_LAST /* we will handle the response packet*/,
                   1041:                                                                                          FALSE, TRUE TSRMLS_CC)) {
                   1042:                stmt->error_info = stmt->conn->error_info;
                   1043:                DBG_RETURN(FAIL);
                   1044:        }
                   1045: 
                   1046:        row_packet->skip_extraction = stmt->result_bind? FALSE:TRUE;
                   1047: 
                   1048:        if (PASS == (ret = PACKET_READ(row_packet, result->conn)) && !row_packet->eof) {
                   1049:                unsigned int i, field_count = result->field_count;
                   1050: 
                   1051:                DBG_INF_FMT("skip_extraction=%u", row_packet->skip_extraction); 
                   1052:                if (!row_packet->skip_extraction) {
                   1053:                        result->m.unbuffered_free_last_data(result TSRMLS_CC);
                   1054: 
                   1055:                        DBG_INF("extracting data");
                   1056:                        result->unbuf->last_row_data = row_packet->fields;
                   1057:                        result->unbuf->last_row_buffer = row_packet->row_buffer;
                   1058:                        row_packet->fields = NULL;
                   1059:                        row_packet->row_buffer = NULL;
                   1060: 
                   1061:                        if (PASS != result->m.row_decoder(result->unbuf->last_row_buffer,
                   1062:                                                                          result->unbuf->last_row_data,
                   1063:                                                                          row_packet->field_count,
                   1064:                                                                          row_packet->fields_metadata,
                   1065:                                                                          FALSE,
                   1066:                                                                          result->conn->options.numeric_and_datetime_as_unicode,
                   1067:                                                                          result->conn->options.int_and_float_native,
                   1068:                                                                          result->conn->stats TSRMLS_CC))
                   1069:                        {
                   1070:                                DBG_RETURN(FAIL);                                                 
                   1071:                        }
                   1072: 
                   1073:                        /* If no result bind, do nothing. We consumed the data */
                   1074:                        for (i = 0; i < field_count; i++) {
                   1075:                                if (stmt->result_bind[i].bound == TRUE) {
                   1076:                                        zval *data = result->unbuf->last_row_data[i];
                   1077:                                        /*
                   1078:                                          stmt->result_bind[i].zv has been already destructed
                   1079:                                          in result->m.unbuffered_free_last_data()
                   1080:                                        */
                   1081: #ifndef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF
                   1082:                                        zval_dtor(stmt->result_bind[i].zv);
                   1083: #endif
                   1084:                                        DBG_INF_FMT("i=%u bound_var=%p type=%u refc=%u", i, stmt->result_bind[i].zv,
                   1085:                                                                Z_TYPE_P(data), Z_REFCOUNT_P(stmt->result_bind[i].zv));
                   1086:                                        if (IS_NULL != (Z_TYPE_P(stmt->result_bind[i].zv) = Z_TYPE_P(data))) {
                   1087:                                                if ((Z_TYPE_P(data) == IS_STRING
                   1088: #if MYSQLND_UNICODE
                   1089:                                                        || Z_TYPE_P(data) == IS_UNICODE
                   1090: #endif
                   1091:                                                        )
                   1092:                                                         && (result->meta->fields[i].max_length < (unsigned long) Z_STRLEN_P(data)))
                   1093:                                                {
                   1094:                                                        result->meta->fields[i].max_length = Z_STRLEN_P(data);
                   1095:                                                }
                   1096:                                                stmt->result_bind[i].zv->value = data->value;
                   1097:                                                /* copied data, thus also the ownership. Thus null data */
                   1098:                                                ZVAL_NULL(data);
                   1099:                                        }
                   1100:                                }
                   1101:                        }
                   1102:                } else {
                   1103:                        DBG_INF("skipping extraction");
                   1104:                        /*
                   1105:                          Data has been allocated and usually result->m.unbuffered_free_last_data()
                   1106:                          frees it but we can't call this function as it will cause problems with
                   1107:                          the bound variables. Thus we need to do part of what it does or Zend will
                   1108:                          report leaks.
                   1109:                        */
                   1110:                        row_packet->row_buffer->free_chunk(row_packet->row_buffer TSRMLS_CC);
                   1111:                        row_packet->row_buffer = NULL;
                   1112:                }
                   1113:                /* We asked for one row, the next one should be EOF, eat it */
                   1114:                ret = PACKET_READ(row_packet, result->conn);
                   1115:                if (row_packet->row_buffer) {
                   1116:                        row_packet->row_buffer->free_chunk(row_packet->row_buffer TSRMLS_CC);
                   1117:                        row_packet->row_buffer = NULL;
                   1118:                }
                   1119:                MYSQLND_INC_CONN_STATISTIC(stmt->conn->stats, STAT_ROWS_FETCHED_FROM_CLIENT_PS_CURSOR);
                   1120: 
                   1121:                result->unbuf->row_count++;
                   1122:                *fetched_anything = TRUE;
                   1123:        } else {
                   1124:                *fetched_anything = FALSE;
                   1125: 
                   1126:                stmt->upsert_status.warning_count =
                   1127:                        stmt->conn->upsert_status.warning_count =
                   1128:                                row_packet->warning_count;
                   1129: 
                   1130:                stmt->upsert_status.server_status = 
                   1131:                        stmt->conn->upsert_status.server_status =
                   1132:                                row_packet->server_status;
                   1133: 
                   1134:                result->unbuf->eof_reached = row_packet->eof;
                   1135:        }
                   1136:        stmt->upsert_status.warning_count =
                   1137:                stmt->conn->upsert_status.warning_count =
                   1138:                        row_packet->warning_count;
                   1139:        stmt->upsert_status.server_status = 
                   1140:                stmt->conn->upsert_status.server_status =
                   1141:                        row_packet->server_status;
                   1142: 
                   1143:        DBG_INF_FMT("ret=%s fetched=%u server_status=%u warnings=%u eof=%u",
                   1144:                                ret == PASS? "PASS":"FAIL", *fetched_anything,
                   1145:                                row_packet->server_status, row_packet->warning_count,
                   1146:                                result->unbuf->eof_reached);
                   1147:        DBG_RETURN(ret);
                   1148: }
                   1149: /* }}} */
                   1150: 
                   1151: 
                   1152: /* {{{ mysqlnd_stmt::fetch */
                   1153: static enum_func_status
                   1154: MYSQLND_METHOD(mysqlnd_stmt, fetch)(MYSQLND_STMT * const s, zend_bool * const fetched_anything TSRMLS_DC)
                   1155: {
                   1156:        MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
                   1157:        enum_func_status ret;
                   1158:        DBG_ENTER("mysqlnd_stmt::fetch");
                   1159:        if (!stmt || !stmt->conn) {
                   1160:                DBG_RETURN(FAIL);
                   1161:        }
                   1162:        DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
                   1163: 
                   1164:        if (!stmt->result ||
                   1165:                stmt->state < MYSQLND_STMT_WAITING_USE_OR_STORE) {
                   1166:                SET_STMT_ERROR(stmt, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
                   1167: 
                   1168:                DBG_ERR("command out of sync");
                   1169:                DBG_RETURN(FAIL);
                   1170:        } else if (stmt->state == MYSQLND_STMT_WAITING_USE_OR_STORE) {
                   1171:                /* Execute only once. We have to free the previous contents of user's bound vars */
                   1172: 
                   1173:                stmt->default_rset_handler(s TSRMLS_CC);
                   1174:        }
                   1175:        stmt->state = MYSQLND_STMT_USER_FETCHING;
                   1176: 
                   1177:        SET_EMPTY_ERROR(stmt->error_info);
                   1178:        SET_EMPTY_ERROR(stmt->conn->error_info);
                   1179: 
                   1180:        DBG_INF_FMT("result_bind=%p separated_once=%u", stmt->result_bind, stmt->result_zvals_separated_once);
                   1181:        /*
                   1182:          The user might have not bound any variables for result.
                   1183:          Do the binding once she does it.
                   1184:        */
                   1185:        if (stmt->result_bind && !stmt->result_zvals_separated_once) {
                   1186:                unsigned int i;
                   1187:                /*
                   1188:                  mysqlnd_stmt_store_result() has been called free the bind
                   1189:                  variables to prevent leaking of their previous content.
                   1190:                */
                   1191:                for (i = 0; i < stmt->result->field_count; i++) {
                   1192:                        if (stmt->result_bind[i].bound == TRUE) {
                   1193:                                zval_dtor(stmt->result_bind[i].zv);
                   1194:                                ZVAL_NULL(stmt->result_bind[i].zv);
                   1195:                        }
                   1196:                }
                   1197:                stmt->result_zvals_separated_once = TRUE;
                   1198:        }
                   1199: 
                   1200:        ret = stmt->result->m.fetch_row(stmt->result, (void*)s, 0, fetched_anything TSRMLS_CC);
                   1201:        DBG_RETURN(ret);
                   1202: }
                   1203: /* }}} */
                   1204: 
                   1205: 
                   1206: /* {{{ mysqlnd_stmt::reset */
                   1207: static enum_func_status
                   1208: MYSQLND_METHOD(mysqlnd_stmt, reset)(MYSQLND_STMT * const s TSRMLS_DC)
                   1209: {
                   1210:        MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
                   1211:        enum_func_status ret = PASS;
                   1212:        zend_uchar cmd_buf[STMT_ID_LENGTH /* statement id */];
                   1213: 
                   1214:        DBG_ENTER("mysqlnd_stmt::reset");
                   1215:        if (!stmt || !stmt->conn) {
                   1216:                DBG_RETURN(FAIL);
                   1217:        }
                   1218:        DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
                   1219: 
                   1220:        SET_EMPTY_ERROR(stmt->error_info);
                   1221:        SET_EMPTY_ERROR(stmt->conn->error_info);
                   1222: 
                   1223:        if (stmt->stmt_id) {
                   1224:                MYSQLND * conn = stmt->conn;
                   1225:                if (stmt->param_bind) {
                   1226:                        unsigned int i;
                   1227:                        DBG_INF("resetting long data");
                   1228:                        /* Reset Long Data */
                   1229:                        for (i = 0; i < stmt->param_count; i++) {
                   1230:                                if (stmt->param_bind[i].flags & MYSQLND_PARAM_BIND_BLOB_USED) {
                   1231:                                        stmt->param_bind[i].flags &= ~MYSQLND_PARAM_BIND_BLOB_USED;
                   1232:                                }
                   1233:                        }
                   1234:                }
                   1235: 
                   1236:                /*
                   1237:                  If the user decided to close the statement right after execute()
                   1238:                  We have to call the appropriate use_result() or store_result() and
                   1239:                  clean.
                   1240:                */
                   1241:                do {
                   1242:                        if (stmt->state == MYSQLND_STMT_WAITING_USE_OR_STORE) {
                   1243:                                DBG_INF("fetching result set header");
                   1244:                                stmt->default_rset_handler(s TSRMLS_CC);
                   1245:                                stmt->state = MYSQLND_STMT_USER_FETCHING;
                   1246:                        }
                   1247: 
                   1248:                        if (stmt->result) {
                   1249:                                DBG_INF("skipping result");
                   1250:                                stmt->result->m.skip_result(stmt->result TSRMLS_CC);
                   1251:                        }
                   1252:                } while (mysqlnd_stmt_more_results(s) && mysqlnd_stmt_next_result(s) == PASS);
                   1253: 
                   1254:                /*
                   1255:                  Don't free now, let the result be usable. When the stmt will again be
                   1256:                  executed then the result set will be cleaned, the bound variables will
                   1257:                  be separated before that.
                   1258:                */
                   1259: 
                   1260:                int4store(cmd_buf, stmt->stmt_id);
                   1261:                if (CONN_GET_STATE(conn) == CONN_READY &&
                   1262:                        FAIL == (ret = conn->m->simple_command(conn, COM_STMT_RESET, (char *)cmd_buf,
                   1263:                                                                                                  sizeof(cmd_buf), PROT_OK_PACKET,
                   1264:                                                                                                  FALSE, TRUE TSRMLS_CC))) {
                   1265:                        stmt->error_info = conn->error_info;
                   1266:                }
                   1267:                stmt->upsert_status = conn->upsert_status;
                   1268: 
                   1269:                stmt->state = MYSQLND_STMT_PREPARED;
                   1270:        }
                   1271:        DBG_INF(ret == PASS? "PASS":"FAIL");
                   1272:        DBG_RETURN(ret);
                   1273: }
                   1274: /* }}} */
                   1275: 
                   1276: 
                   1277: /* {{{ mysqlnd_stmt::send_long_data */
                   1278: static enum_func_status
                   1279: MYSQLND_METHOD(mysqlnd_stmt, send_long_data)(MYSQLND_STMT * const s, unsigned int param_no,
                   1280:                                                                                         const char * const data, unsigned long length TSRMLS_DC)
                   1281: {
                   1282:        MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
                   1283:        enum_func_status ret = FAIL;
                   1284:        MYSQLND * conn;
                   1285:        zend_uchar *cmd_buf;
                   1286:        enum php_mysqlnd_server_command cmd = COM_STMT_SEND_LONG_DATA;
                   1287: 
                   1288:        DBG_ENTER("mysqlnd_stmt::send_long_data");
                   1289:        if (!stmt || !stmt->conn) {
                   1290:                DBG_RETURN(FAIL);
                   1291:        }
                   1292:        DBG_INF_FMT("stmt=%lu param_no=%u data_len=%lu", stmt->stmt_id, param_no, length);
                   1293: 
                   1294:        conn = stmt->conn;
                   1295: 
                   1296:        SET_EMPTY_ERROR(stmt->error_info);
                   1297:        SET_EMPTY_ERROR(stmt->conn->error_info);
                   1298: 
                   1299:        if (stmt->state < MYSQLND_STMT_PREPARED) {
                   1300:                SET_STMT_ERROR(stmt, CR_NO_PREPARE_STMT, UNKNOWN_SQLSTATE, mysqlnd_stmt_not_prepared);
                   1301:                DBG_ERR("not prepared");
                   1302:                DBG_RETURN(FAIL);
                   1303:        }
                   1304:        if (!stmt->param_bind) {
                   1305:                SET_STMT_ERROR(stmt, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
                   1306:                DBG_ERR("command out of sync");
                   1307:                DBG_RETURN(FAIL);
                   1308:        }
                   1309: 
                   1310:        if (param_no >= stmt->param_count) {
                   1311:                SET_STMT_ERROR(stmt, CR_INVALID_PARAMETER_NO, UNKNOWN_SQLSTATE, "Invalid parameter number");
                   1312:                DBG_ERR("invalid param_no");
                   1313:                DBG_RETURN(FAIL);
                   1314:        }
                   1315:        if (stmt->param_bind[param_no].type != MYSQL_TYPE_LONG_BLOB) {
                   1316:                SET_STMT_ERROR(stmt, CR_INVALID_BUFFER_USE, UNKNOWN_SQLSTATE, mysqlnd_not_bound_as_blob);
                   1317:                DBG_ERR("param_no is not of a blob type");
                   1318:                DBG_RETURN(FAIL);
                   1319:        }
                   1320: 
                   1321:        /*
                   1322:          XXX:  Unfortunately we have to allocate additional buffer to be able the
                   1323:                        additional data, which is like a header inside the payload.
                   1324:                        This should be optimised, but it will be a pervasive change, so
                   1325:                        conn->m->simple_command() will accept not a buffer, but actually MYSQLND_STRING*
                   1326:                        terminated by NULL, to send. If the strings are not big, we can collapse them
                   1327:                        on the buffer every connection has, but otherwise we will just send them
                   1328:                        one by one to the wire.
                   1329:        */
                   1330: 
                   1331:        if (CONN_GET_STATE(conn) == CONN_READY) {
                   1332:                size_t packet_len;
                   1333:                cmd_buf = mnd_emalloc(packet_len = STMT_ID_LENGTH + 2 + length);
                   1334:                if (cmd_buf) {
                   1335:                        stmt->param_bind[param_no].flags |= MYSQLND_PARAM_BIND_BLOB_USED;
                   1336: 
                   1337:                        int4store(cmd_buf, stmt->stmt_id);
                   1338:                        int2store(cmd_buf + STMT_ID_LENGTH, param_no);
                   1339:                        memcpy(cmd_buf + STMT_ID_LENGTH + 2, data, length);
                   1340: 
                   1341:                        /* COM_STMT_SEND_LONG_DATA doesn't send an OK packet*/
                   1342:                        ret = conn->m->simple_command(conn, cmd, (char *)cmd_buf, packet_len, PROT_LAST , FALSE, TRUE TSRMLS_CC);
                   1343:                        mnd_efree(cmd_buf);
                   1344:                        if (FAIL == ret) {
                   1345:                                stmt->error_info = conn->error_info;
                   1346:                        }
                   1347:                } else {
                   1348:                        ret = FAIL;
                   1349:                        SET_OOM_ERROR(stmt->error_info);
                   1350:                        SET_OOM_ERROR(conn->error_info);
                   1351:                }
                   1352:                /*
                   1353:                  Cover protocol error: COM_STMT_SEND_LONG_DATA was designed to be quick and not
                   1354:                  sent response packets. According to documentation the only way to get an error
                   1355:                  is to have out-of-memory on the server-side. However, that's not true, as if
                   1356:                  max_allowed_packet_size is smaller than the chunk being sent to the server, the
                   1357:                  latter will complain with an error message. However, normally we don't expect
                   1358:                  an error message, thus we continue. When sending the next command, which expects
                   1359:                  response we will read the unexpected data and error message will look weird.
                   1360:                  Therefore we do non-blocking read to clean the line, if there is a need.
                   1361:                  Nevertheless, there is a built-in protection when sending a command packet, that
                   1362:                  checks if the line is clear - useful for debug purposes and to be switched off
                   1363:                  in release builds.
                   1364: 
                   1365:                  Maybe we can make it automatic by checking what's the value of
                   1366:                  max_allowed_packet_size on the server and resending the data.
                   1367:                */
                   1368: #ifdef MYSQLND_DO_WIRE_CHECK_BEFORE_COMMAND
                   1369: #if HAVE_USLEEP && !defined(PHP_WIN32)
                   1370:                usleep(120000);
                   1371: #endif
                   1372:                if ((packet_len = conn->net->m.consume_uneaten_data(conn->net, cmd TSRMLS_CC))) {
                   1373:                        php_error_docref(NULL TSRMLS_CC, E_WARNING, "There was an error "
                   1374:                                                         "while sending long data. Probably max_allowed_packet_size "
                   1375:                                                         "is smaller than the data. You have to increase it or send "
                   1376:                                                         "smaller chunks of data. Answer was "MYSQLND_SZ_T_SPEC" bytes long.", packet_len);
                   1377:                        SET_STMT_ERROR(stmt, CR_CONNECTION_ERROR, UNKNOWN_SQLSTATE,
                   1378:                                                        "Server responded to COM_STMT_SEND_LONG_DATA.");
                   1379:                        ret = FAIL;
                   1380:                }
                   1381: #endif
                   1382:        }
                   1383: 
                   1384:        DBG_INF(ret == PASS? "PASS":"FAIL");
                   1385:        DBG_RETURN(ret);
                   1386: }
                   1387: /* }}} */
                   1388: 
                   1389: 
                   1390: /* {{{ mysqlnd_stmt::bind_parameters */
                   1391: static enum_func_status
                   1392: MYSQLND_METHOD(mysqlnd_stmt, bind_parameters)(MYSQLND_STMT * const s, MYSQLND_PARAM_BIND * const param_bind TSRMLS_DC)
                   1393: {
                   1394:        MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
                   1395:        DBG_ENTER("mysqlnd_stmt::bind_param");
                   1396:        if (!stmt || !stmt->conn) {
                   1397:                DBG_RETURN(FAIL);
                   1398:        }
                   1399:        DBG_INF_FMT("stmt=%lu param_count=%u", stmt->stmt_id, stmt->param_count);
                   1400: 
                   1401:        if (stmt->state < MYSQLND_STMT_PREPARED) {
                   1402:                SET_STMT_ERROR(stmt, CR_NO_PREPARE_STMT, UNKNOWN_SQLSTATE, mysqlnd_stmt_not_prepared);
                   1403:                DBG_ERR("not prepared");
                   1404:                if (param_bind) {
                   1405:                        s->m->free_parameter_bind(s, param_bind TSRMLS_CC);
                   1406:                }
                   1407:                DBG_RETURN(FAIL);
                   1408:        }
                   1409: 
                   1410:        SET_EMPTY_ERROR(stmt->error_info);
                   1411:        SET_EMPTY_ERROR(stmt->conn->error_info);
                   1412: 
                   1413:        if (stmt->param_count) {
                   1414:                unsigned int i = 0;
                   1415: 
                   1416:                if (!param_bind) {
                   1417:                        SET_STMT_ERROR(stmt, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, "Re-binding (still) not supported");
                   1418:                        DBG_ERR("Re-binding (still) not supported");
                   1419:                        DBG_RETURN(FAIL);
                   1420:                } else if (stmt->param_bind) {
                   1421:                        DBG_INF("Binding");
                   1422:                        /*
                   1423:                          There is already result bound.
                   1424:                          Forbid for now re-binding!!
                   1425:                        */
                   1426:                        for (i = 0; i < stmt->param_count; i++) {
                   1427:                                /*
                   1428:                                  We may have the last reference, then call zval_ptr_dtor()
                   1429:                                  or we may leak memory.
                   1430:                                  Switching from bind_one_parameter to bind_parameters may result in zv being NULL
                   1431:                                */
                   1432:                                if (stmt->param_bind[i].zv) {
                   1433:                                        zval_ptr_dtor(&stmt->param_bind[i].zv);
                   1434:                                }
                   1435:                        }
                   1436:                        if (stmt->param_bind != param_bind) {
                   1437:                                s->m->free_parameter_bind(s, stmt->param_bind TSRMLS_CC);
                   1438:                        }
                   1439:                }
                   1440: 
                   1441:                stmt->param_bind = param_bind;
                   1442:                for (i = 0; i < stmt->param_count; i++) {
                   1443:                        /* The client will use stmt_send_long_data */
                   1444:                        DBG_INF_FMT("%u is of type %u", i, stmt->param_bind[i].type);
                   1445:                        /* Prevent from freeing */
                   1446:                        /* Don't update is_ref, or we will leak during conversion */
                   1447:                        Z_ADDREF_P(stmt->param_bind[i].zv);
                   1448:                        stmt->param_bind[i].flags = 0;
                   1449:                        if (stmt->param_bind[i].type == MYSQL_TYPE_LONG_BLOB) {
                   1450:                                stmt->param_bind[i].flags &= ~MYSQLND_PARAM_BIND_BLOB_USED;
                   1451:                        }
                   1452:                }
                   1453:                stmt->send_types_to_server = 1;
                   1454:        }
                   1455:        DBG_INF("PASS");
                   1456:        DBG_RETURN(PASS);
                   1457: }
                   1458: /* }}} */
                   1459: 
                   1460: 
                   1461: /* {{{ mysqlnd_stmt::bind_one_parameter */
                   1462: static enum_func_status
                   1463: MYSQLND_METHOD(mysqlnd_stmt, bind_one_parameter)(MYSQLND_STMT * const s, unsigned int param_no,
                   1464:                                                                                                 zval * const zv, zend_uchar type TSRMLS_DC)
                   1465: {
                   1466:        MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
                   1467:        DBG_ENTER("mysqlnd_stmt::bind_one_parameter");
                   1468:        if (!stmt || !stmt->conn) {
                   1469:                DBG_RETURN(FAIL);
                   1470:        }
                   1471:        DBG_INF_FMT("stmt=%lu param_no=%u param_count=%u type=%u",
                   1472:                                stmt->stmt_id, param_no, stmt->param_count, type);
                   1473: 
                   1474:        if (stmt->state < MYSQLND_STMT_PREPARED) {
                   1475:                SET_STMT_ERROR(stmt, CR_NO_PREPARE_STMT, UNKNOWN_SQLSTATE, mysqlnd_stmt_not_prepared);
                   1476:                DBG_ERR("not prepared");
                   1477:                DBG_RETURN(FAIL);
                   1478:        }
                   1479: 
                   1480:        if (param_no >= stmt->param_count) {
                   1481:                SET_STMT_ERROR(stmt, CR_INVALID_PARAMETER_NO, UNKNOWN_SQLSTATE, "Invalid parameter number");
                   1482:                DBG_ERR("invalid param_no");
                   1483:                DBG_RETURN(FAIL);
                   1484:        }
                   1485:        SET_EMPTY_ERROR(stmt->error_info);
                   1486:        SET_EMPTY_ERROR(stmt->conn->error_info);
                   1487: 
                   1488:        if (stmt->param_count) {
                   1489:                if (!stmt->param_bind) {
                   1490:                        stmt->param_bind = mnd_ecalloc(stmt->param_count, sizeof(MYSQLND_PARAM_BIND));
                   1491:                        if (!stmt->param_bind) {
                   1492:                                DBG_RETURN(FAIL);
                   1493:                        }
                   1494:                }
                   1495: 
                   1496:                /* Prevent from freeing */
                   1497:                /* Don't update is_ref, or we will leak during conversion */
                   1498:                Z_ADDREF_P(zv);
                   1499:                DBG_INF("Binding");
                   1500:                /* Release what we had, if we had */
                   1501:                if (stmt->param_bind[param_no].zv) {
                   1502:                        zval_ptr_dtor(&stmt->param_bind[param_no].zv);
                   1503:                }
                   1504:                if (type == MYSQL_TYPE_LONG_BLOB) {
                   1505:                        /* The client will use stmt_send_long_data */
                   1506:                        stmt->param_bind[param_no].flags &= ~MYSQLND_PARAM_BIND_BLOB_USED;
                   1507:                }
                   1508:                stmt->param_bind[param_no].zv = zv;
                   1509:                stmt->param_bind[param_no].type = type;
                   1510: 
                   1511:                stmt->send_types_to_server = 1;
                   1512:        }
                   1513:        DBG_INF("PASS");
                   1514:        DBG_RETURN(PASS);
                   1515: }
                   1516: /* }}} */
                   1517: 
                   1518: 
                   1519: /* {{{ mysqlnd_stmt::refresh_bind_param */
                   1520: static enum_func_status
                   1521: MYSQLND_METHOD(mysqlnd_stmt, refresh_bind_param)(MYSQLND_STMT * const s TSRMLS_DC)
                   1522: {
                   1523:        MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
                   1524:        DBG_ENTER("mysqlnd_stmt::refresh_bind_param");
                   1525:        if (!stmt || !stmt->conn) {
                   1526:                DBG_RETURN(FAIL);
                   1527:        }
                   1528:        DBG_INF_FMT("stmt=%lu param_count=%u", stmt->stmt_id, stmt->param_count);
                   1529: 
                   1530:        if (stmt->state < MYSQLND_STMT_PREPARED) {
                   1531:                SET_STMT_ERROR(stmt, CR_NO_PREPARE_STMT, UNKNOWN_SQLSTATE, mysqlnd_stmt_not_prepared);
                   1532:                DBG_ERR("not prepared");
                   1533:                DBG_RETURN(FAIL);
                   1534:        }
                   1535: 
                   1536:        SET_EMPTY_ERROR(stmt->error_info);
                   1537:        SET_EMPTY_ERROR(stmt->conn->error_info);
                   1538: 
                   1539:        if (stmt->param_count) {
                   1540:                stmt->send_types_to_server = 1;
                   1541:        }
                   1542:        DBG_INF("PASS");
                   1543:        DBG_RETURN(PASS);
                   1544: }
                   1545: /* }}} */
                   1546: 
                   1547: 
                   1548: /* {{{ mysqlnd_stmt::bind_result */
                   1549: static enum_func_status
                   1550: MYSQLND_METHOD(mysqlnd_stmt, bind_result)(MYSQLND_STMT * const s,
                   1551:                                                                                  MYSQLND_RESULT_BIND * const result_bind TSRMLS_DC)
                   1552: {
                   1553:        MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
                   1554:        DBG_ENTER("mysqlnd_stmt::bind_result");
                   1555:        if (!stmt || !stmt->conn) {
                   1556:                DBG_RETURN(FAIL);
                   1557:        }
                   1558:        DBG_INF_FMT("stmt=%lu field_count=%u", stmt->stmt_id, stmt->field_count);
                   1559: 
                   1560:        if (stmt->state < MYSQLND_STMT_PREPARED) {
                   1561:                SET_STMT_ERROR(stmt, CR_NO_PREPARE_STMT, UNKNOWN_SQLSTATE, mysqlnd_stmt_not_prepared);
                   1562:                if (result_bind) {
                   1563:                        s->m->free_result_bind(s, result_bind TSRMLS_CC);
                   1564:                }
                   1565:                DBG_ERR("not prepared");
                   1566:                DBG_RETURN(FAIL);
                   1567:        }
                   1568: 
                   1569:        SET_EMPTY_ERROR(stmt->error_info);
                   1570:        SET_EMPTY_ERROR(stmt->conn->error_info);
                   1571: 
                   1572:        if (stmt->field_count) {
                   1573:                unsigned int i = 0;
                   1574: 
                   1575:                if (!result_bind) {
                   1576:                        DBG_ERR("no result bind passed");
                   1577:                        DBG_RETURN(FAIL);
                   1578:                }
                   1579: 
                   1580:                mysqlnd_stmt_separate_result_bind(s TSRMLS_CC);
                   1581:                stmt->result_zvals_separated_once = FALSE;
                   1582:                stmt->result_bind = result_bind;
                   1583:                for (i = 0; i < stmt->field_count; i++) {
                   1584:                        /* Prevent from freeing */
                   1585:                        Z_ADDREF_P(stmt->result_bind[i].zv);
                   1586:                        DBG_INF_FMT("ref of %p = %u", stmt->result_bind[i].zv, Z_REFCOUNT_P(stmt->result_bind[i].zv));
                   1587:                        /*
                   1588:                          Don't update is_ref !!! it's not our job
                   1589:                          Otherwise either 009.phpt or mysqli_stmt_bind_result.phpt
                   1590:                          will fail.
                   1591:                        */
                   1592:                        stmt->result_bind[i].bound = TRUE;
                   1593:                }
                   1594:        } else if (result_bind) {
                   1595:                s->m->free_result_bind(s, result_bind TSRMLS_CC);
                   1596:        }
                   1597:        DBG_INF("PASS");
                   1598:        DBG_RETURN(PASS);
                   1599: }
                   1600: /* }}} */
                   1601: 
                   1602: 
                   1603: /* {{{ mysqlnd_stmt::bind_result */
                   1604: static enum_func_status
                   1605: MYSQLND_METHOD(mysqlnd_stmt, bind_one_result)(MYSQLND_STMT * const s, unsigned int param_no TSRMLS_DC)
                   1606: {
                   1607:        MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
                   1608:        DBG_ENTER("mysqlnd_stmt::bind_result");
                   1609:        if (!stmt || !stmt->conn) {
                   1610:                DBG_RETURN(FAIL);
                   1611:        }
                   1612:        DBG_INF_FMT("stmt=%lu field_count=%u", stmt->stmt_id, stmt->field_count);
                   1613: 
                   1614:        if (stmt->state < MYSQLND_STMT_PREPARED) {
                   1615:                SET_STMT_ERROR(stmt, CR_NO_PREPARE_STMT, UNKNOWN_SQLSTATE, mysqlnd_stmt_not_prepared);
                   1616:                DBG_ERR("not prepared");
                   1617:                DBG_RETURN(FAIL);
                   1618:        }
                   1619: 
                   1620:        if (param_no >= stmt->field_count) {
                   1621:                SET_STMT_ERROR(stmt, CR_INVALID_PARAMETER_NO, UNKNOWN_SQLSTATE, "Invalid parameter number");
                   1622:                DBG_ERR("invalid param_no");
                   1623:                DBG_RETURN(FAIL);
                   1624:        }
                   1625: 
                   1626:        SET_EMPTY_ERROR(stmt->error_info);
                   1627:        SET_EMPTY_ERROR(stmt->conn->error_info);
                   1628: 
                   1629:        if (stmt->field_count) {
                   1630:                mysqlnd_stmt_separate_one_result_bind(s, param_no TSRMLS_CC);
                   1631:                /* Guaranteed is that stmt->result_bind is NULL */
                   1632:                if (!stmt->result_bind) {
                   1633:                        stmt->result_bind = mnd_ecalloc(stmt->field_count, sizeof(MYSQLND_RESULT_BIND));
                   1634:                } else {
                   1635:                        stmt->result_bind = mnd_erealloc(stmt->result_bind, stmt->field_count * sizeof(MYSQLND_RESULT_BIND));
                   1636:                }
                   1637:                if (!stmt->result_bind) {
                   1638:                        DBG_RETURN(FAIL);
                   1639:                }
                   1640:                ALLOC_INIT_ZVAL(stmt->result_bind[param_no].zv);
                   1641:                /*
                   1642:                  Don't update is_ref !!! it's not our job
                   1643:                  Otherwise either 009.phpt or mysqli_stmt_bind_result.phpt
                   1644:                  will fail.
                   1645:                */
                   1646:                stmt->result_bind[param_no].bound = TRUE;
                   1647:        }
                   1648:        DBG_INF("PASS");
                   1649:        DBG_RETURN(PASS);
                   1650: }
                   1651: /* }}} */
                   1652: 
                   1653: 
                   1654: /* {{{ mysqlnd_stmt::insert_id */
                   1655: static uint64_t
                   1656: MYSQLND_METHOD(mysqlnd_stmt, insert_id)(const MYSQLND_STMT * const s TSRMLS_DC)
                   1657: {
                   1658:        MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
                   1659:        return stmt? stmt->upsert_status.last_insert_id : 0;
                   1660: }
                   1661: /* }}} */
                   1662: 
                   1663: 
                   1664: /* {{{ mysqlnd_stmt::affected_rows */
                   1665: static uint64_t
                   1666: MYSQLND_METHOD(mysqlnd_stmt, affected_rows)(const MYSQLND_STMT * const s TSRMLS_DC)
                   1667: {
                   1668:        MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
                   1669:        return stmt? stmt->upsert_status.affected_rows : 0;
                   1670: }
                   1671: /* }}} */
                   1672: 
                   1673: 
                   1674: /* {{{ mysqlnd_stmt::num_rows */
                   1675: static uint64_t
                   1676: MYSQLND_METHOD(mysqlnd_stmt, num_rows)(const MYSQLND_STMT * const s TSRMLS_DC)
                   1677: {
                   1678:        MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
                   1679:        return stmt && stmt->result? mysqlnd_num_rows(stmt->result):0;
                   1680: }
                   1681: /* }}} */
                   1682: 
                   1683: 
                   1684: /* {{{ mysqlnd_stmt::warning_count */
                   1685: static unsigned int
                   1686: MYSQLND_METHOD(mysqlnd_stmt, warning_count)(const MYSQLND_STMT * const s TSRMLS_DC)
                   1687: {
                   1688:        MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
                   1689:        return stmt? stmt->upsert_status.warning_count : 0;
                   1690: }
                   1691: /* }}} */
                   1692: 
                   1693: 
                   1694: /* {{{ mysqlnd_stmt::server_status */
                   1695: static unsigned int
                   1696: MYSQLND_METHOD(mysqlnd_stmt, server_status)(const MYSQLND_STMT * const s TSRMLS_DC)
                   1697: {
                   1698:        MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
                   1699:        return stmt? stmt->upsert_status.server_status : 0;
                   1700: }
                   1701: /* }}} */
                   1702: 
                   1703: 
                   1704: /* {{{ mysqlnd_stmt::field_count */
                   1705: static unsigned int
                   1706: MYSQLND_METHOD(mysqlnd_stmt, field_count)(const MYSQLND_STMT * const s TSRMLS_DC)
                   1707: {
                   1708:        MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
                   1709:        return stmt? stmt->field_count : 0;
                   1710: }
                   1711: /* }}} */
                   1712: 
                   1713: 
                   1714: /* {{{ mysqlnd_stmt::param_count */
                   1715: static unsigned int
                   1716: MYSQLND_METHOD(mysqlnd_stmt, param_count)(const MYSQLND_STMT * const s TSRMLS_DC)
                   1717: {
                   1718:        MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
                   1719:        return stmt? stmt->param_count : 0;
                   1720: }
                   1721: /* }}} */
                   1722: 
                   1723: 
                   1724: /* {{{ mysqlnd_stmt::errno */
                   1725: static unsigned int
                   1726: MYSQLND_METHOD(mysqlnd_stmt, errno)(const MYSQLND_STMT * const s TSRMLS_DC)
                   1727: {
                   1728:        MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
                   1729:        return stmt? stmt->error_info.error_no : 0;
                   1730: }
                   1731: /* }}} */
                   1732: 
                   1733: 
                   1734: /* {{{ mysqlnd_stmt::error */
                   1735: static const char *
                   1736: MYSQLND_METHOD(mysqlnd_stmt, error)(const MYSQLND_STMT * const s TSRMLS_DC)
                   1737: {
                   1738:        MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
                   1739:        return stmt? stmt->error_info.error : 0;
                   1740: }
                   1741: /* }}} */
                   1742: 
                   1743: 
                   1744: /* {{{ mysqlnd_stmt::sqlstate */
                   1745: static const char *
                   1746: MYSQLND_METHOD(mysqlnd_stmt, sqlstate)(const MYSQLND_STMT * const s TSRMLS_DC)
                   1747: {
                   1748:        MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
                   1749:        return stmt && stmt->error_info.sqlstate[0] ? stmt->error_info.sqlstate:MYSQLND_SQLSTATE_NULL;
                   1750: }
                   1751: /* }}} */
                   1752: 
                   1753: 
                   1754: /* {{{ mysqlnd_stmt::data_seek */
                   1755: static enum_func_status
                   1756: MYSQLND_METHOD(mysqlnd_stmt, data_seek)(const MYSQLND_STMT * const s, uint64_t row TSRMLS_DC)
                   1757: {
                   1758:        MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
                   1759:        return stmt && stmt->result? stmt->result->m.seek_data(stmt->result, row TSRMLS_CC) : FAIL;
                   1760: }
                   1761: /* }}} */
                   1762: 
                   1763: 
                   1764: /* {{{ mysqlnd_stmt::param_metadata */
                   1765: static MYSQLND_RES *
                   1766: MYSQLND_METHOD(mysqlnd_stmt, param_metadata)(MYSQLND_STMT * const s TSRMLS_DC)
                   1767: {
                   1768:        MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
                   1769:        if (!stmt || !stmt->param_count) {
                   1770:                return NULL;
                   1771:        }
                   1772:        return NULL;
                   1773: }
                   1774: /* }}} */
                   1775: 
                   1776: 
                   1777: /* {{{ mysqlnd_stmt::result_metadata */
                   1778: static MYSQLND_RES *
                   1779: MYSQLND_METHOD(mysqlnd_stmt, result_metadata)(MYSQLND_STMT * const s TSRMLS_DC)
                   1780: {
                   1781:        MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
                   1782:        MYSQLND_RES *result;
                   1783: 
                   1784:        DBG_ENTER("mysqlnd_stmt::result_metadata");
                   1785:        if (!stmt) {
                   1786:                DBG_RETURN(NULL);
                   1787:        }
                   1788:        DBG_INF_FMT("stmt=%u field_count=%u", stmt->stmt_id, stmt->field_count);
                   1789: 
                   1790:        if (!stmt->field_count || !stmt->conn || !stmt->result || !stmt->result->meta) {
                   1791:                DBG_INF("NULL");
                   1792:                DBG_RETURN(NULL);
                   1793:        }
                   1794: 
                   1795:        if (stmt->update_max_length && stmt->result->stored_data) {
                   1796:                /* stored result, we have to update the max_length before we clone the meta data :( */
                   1797:                stmt->result->m.initialize_result_set_rest(stmt->result TSRMLS_CC);
                   1798:        }
                   1799:        /*
                   1800:          TODO: This implementation is kind of a hack,
                   1801:                        find a better way to do it. In different functions I have put
                   1802:                        fuses to check for result->m.fetch_row() being NULL. This should
                   1803:                        be handled in a better way.
                   1804: 
                   1805:          In the meantime we don't need a zval cache reference for this fake
                   1806:          result set, so we don't get one.
                   1807:        */
                   1808:        do {
                   1809:                result = stmt->conn->m->result_init(stmt->field_count, stmt->persistent TSRMLS_CC);
                   1810:                if (!result) {
                   1811:                        break;
                   1812:                }
                   1813:                result->type = MYSQLND_RES_NORMAL;
                   1814:                result->m.fetch_row = result->m.fetch_row_normal_unbuffered;
                   1815:                result->unbuf = mnd_ecalloc(1, sizeof(MYSQLND_RES_UNBUFFERED));
                   1816:                if (!result->unbuf) {
                   1817:                        break;
                   1818:                }
                   1819:                result->unbuf->eof_reached = TRUE;
                   1820:                result->meta = stmt->result->meta->m->clone_metadata(stmt->result->meta, FALSE TSRMLS_CC);
                   1821:                if (!result->meta) {
                   1822:                        break;
                   1823:                }
                   1824: 
                   1825:                DBG_INF_FMT("result=%p", result);
                   1826:                DBG_RETURN(result);
                   1827:        } while (0);
                   1828: 
                   1829:        SET_OOM_ERROR(stmt->conn->error_info);
                   1830:        if (result) {
                   1831:                result->m.free_result(result, TRUE TSRMLS_CC);
                   1832:        }
                   1833:        DBG_RETURN(NULL);
                   1834: }
                   1835: /* }}} */
                   1836: 
                   1837: 
                   1838: /* {{{ mysqlnd_stmt::attr_set */
                   1839: static enum_func_status
                   1840: MYSQLND_METHOD(mysqlnd_stmt, attr_set)(MYSQLND_STMT * const s,
                   1841:                                                                           enum mysqlnd_stmt_attr attr_type,
                   1842:                                                                           const void * const value TSRMLS_DC)
                   1843: {
                   1844:        MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
                   1845:        DBG_ENTER("mysqlnd_stmt::attr_set");
                   1846:        if (!stmt) {
                   1847:                DBG_RETURN(FAIL);
                   1848:        }
                   1849:        DBG_INF_FMT("stmt=%lu attr_type=%u", stmt->stmt_id, attr_type);
                   1850: 
                   1851:        switch (attr_type) {
                   1852:                case STMT_ATTR_UPDATE_MAX_LENGTH:{
                   1853:                        zend_uchar bval = *(zend_uchar *) value;
                   1854:                        /*
                   1855:                          XXX : libmysql uses my_bool, but mysqli uses ulong as storage on the stack
                   1856:                          and mysqlnd won't be used out of the scope of PHP -> use ulong.
                   1857:                        */
                   1858:                        stmt->update_max_length = bval? TRUE:FALSE;
                   1859:                        break;
                   1860:                }
                   1861:                case STMT_ATTR_CURSOR_TYPE: {
                   1862:                        unsigned int ival = *(unsigned int *) value;
                   1863:                        if (ival > (unsigned long) CURSOR_TYPE_READ_ONLY) {
                   1864:                                SET_STMT_ERROR(stmt, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE, "Not implemented");
                   1865:                                DBG_INF("FAIL");
                   1866:                                DBG_RETURN(FAIL);
                   1867:                        }
                   1868:                        stmt->flags = ival;
                   1869:                        break;
                   1870:                }
                   1871:                case STMT_ATTR_PREFETCH_ROWS: {
                   1872:                        unsigned int ival = *(unsigned int *) value;
                   1873:                        if (ival == 0) {
                   1874:                                ival = MYSQLND_DEFAULT_PREFETCH_ROWS;
                   1875:                        } else if (ival > 1) {
                   1876:                                SET_STMT_ERROR(stmt, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE, "Not implemented");
                   1877:                                DBG_INF("FAIL");
                   1878:                                DBG_RETURN(FAIL);
                   1879:                        }
                   1880:                        stmt->prefetch_rows = ival;
                   1881:                        break;
                   1882:                }
                   1883:                default:
                   1884:                        SET_STMT_ERROR(stmt, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE, "Not implemented");
                   1885:                        DBG_RETURN(FAIL);
                   1886:        }
                   1887:        DBG_INF("PASS");
                   1888:        DBG_RETURN(PASS);
                   1889: }
                   1890: /* }}} */
                   1891: 
                   1892: 
                   1893: /* {{{ mysqlnd_stmt::attr_get */
                   1894: static enum_func_status
                   1895: MYSQLND_METHOD(mysqlnd_stmt, attr_get)(const MYSQLND_STMT * const s,
                   1896:                                                                           enum mysqlnd_stmt_attr attr_type,
                   1897:                                                                           void * const value TSRMLS_DC)
                   1898: {
                   1899:        MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
                   1900:        DBG_ENTER("mysqlnd_stmt::attr_set");
                   1901:        if (!stmt) {
                   1902:                DBG_RETURN(FAIL);
                   1903:        }
                   1904:        DBG_INF_FMT("stmt=%lu attr_type=%u", stmt->stmt_id, attr_type);
                   1905: 
                   1906:        switch (attr_type) {
                   1907:                case STMT_ATTR_UPDATE_MAX_LENGTH:
                   1908:                        *(zend_bool *) value= stmt->update_max_length;
                   1909:                        break;
                   1910:                case STMT_ATTR_CURSOR_TYPE:
                   1911:                        *(unsigned long *) value= stmt->flags;
                   1912:                        break;
                   1913:                case STMT_ATTR_PREFETCH_ROWS:
                   1914:                        *(unsigned long *) value= stmt->prefetch_rows;
                   1915:                        break;
                   1916:                default:
                   1917:                        DBG_RETURN(FAIL);
                   1918:        }
                   1919:        DBG_INF_FMT("value=%lu", value);
                   1920:        DBG_RETURN(PASS);
                   1921: }
                   1922: /* }}} */
                   1923: 
                   1924: /* free_result() doesn't actually free stmt->result but only the buffers */
                   1925: /* {{{ mysqlnd_stmt::free_result */
                   1926: static enum_func_status
                   1927: MYSQLND_METHOD(mysqlnd_stmt, free_result)(MYSQLND_STMT * const s TSRMLS_DC)
                   1928: {
                   1929:        MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
                   1930:        DBG_ENTER("mysqlnd_stmt::free_result");
                   1931:        if (!stmt || !stmt->conn) {
                   1932:                DBG_RETURN(FAIL);
                   1933:        }
                   1934:        DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
                   1935: 
                   1936:        if (!stmt->result) {
                   1937:                DBG_INF("no result");
                   1938:                DBG_RETURN(PASS);
                   1939:        }
                   1940: 
                   1941:        /*
                   1942:          If right after execute() we have to call the appropriate
                   1943:          use_result() or store_result() and clean.
                   1944:        */
                   1945:        if (stmt->state == MYSQLND_STMT_WAITING_USE_OR_STORE) {
                   1946:                DBG_INF("fetching result set header");
                   1947:                /* Do implicit use_result and then flush the result */
                   1948:                stmt->default_rset_handler = s->m->use_result;
                   1949:                stmt->default_rset_handler(s TSRMLS_CC);
                   1950:        }
                   1951: 
                   1952:        if (stmt->state > MYSQLND_STMT_WAITING_USE_OR_STORE) {
                   1953:                DBG_INF("skipping result");
                   1954:                /* Flush if anything is left and unbuffered set */
                   1955:                stmt->result->m.skip_result(stmt->result TSRMLS_CC);
                   1956:                /*
                   1957:                  Separate the bound variables, which point to the result set, then
                   1958:                  destroy the set.
                   1959:                */
                   1960:                mysqlnd_stmt_separate_result_bind(s TSRMLS_CC);
                   1961: 
                   1962:                /* Now we can destroy the result set */
                   1963:                stmt->result->m.free_result_buffers(stmt->result TSRMLS_CC);
                   1964:        }
                   1965: 
                   1966:        if (stmt->state > MYSQLND_STMT_PREPARED) {
                   1967:                /* As the buffers have been freed, we should go back to PREPARED */
                   1968:                stmt->state = MYSQLND_STMT_PREPARED;
                   1969:        }
                   1970: 
                   1971:        /* Line is free! */
                   1972:        CONN_SET_STATE(stmt->conn, CONN_READY);
                   1973: 
                   1974:        DBG_RETURN(PASS);
                   1975: }
                   1976: /* }}} */
                   1977: 
                   1978: 
                   1979: /* {{{ mysqlnd_stmt_separate_result_bind */
                   1980: static void
                   1981: mysqlnd_stmt_separate_result_bind(MYSQLND_STMT * const s TSRMLS_DC)
                   1982: {
                   1983:        MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
                   1984:        unsigned int i;
                   1985: 
                   1986:        DBG_ENTER("mysqlnd_stmt_separate_result_bind");
                   1987:        if (!stmt) {
                   1988:                DBG_VOID_RETURN;
                   1989:        }
                   1990:        DBG_INF_FMT("stmt=%lu result_bind=%p field_count=%u",
                   1991:                                stmt->stmt_id, stmt->result_bind, stmt->field_count);
                   1992: 
                   1993:        if (!stmt->result_bind) {
                   1994:                DBG_VOID_RETURN;
                   1995:        }
                   1996: 
                   1997:        /*
                   1998:          Because only the bound variables can point to our internal buffers, then
                   1999:          separate or free only them. Free is possible because the user could have
                   2000:          lost reference.
                   2001:        */
                   2002:        for (i = 0; i < stmt->field_count; i++) {
                   2003:                /* Let's try with no cache */
                   2004:                if (stmt->result_bind[i].bound == TRUE) {
                   2005:                        DBG_INF_FMT("%u has refcount=%u", i, Z_REFCOUNT_P(stmt->result_bind[i].zv));
                   2006:                        /*
                   2007:                          We have to separate the actual zval value of the bound
                   2008:                          variable from our allocated zvals or we will face double-free
                   2009:                        */
                   2010:                        if (Z_REFCOUNT_P(stmt->result_bind[i].zv) > 1) {
                   2011: #ifdef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF
                   2012:                                zval_copy_ctor(stmt->result_bind[i].zv);
                   2013: #endif
                   2014:                                zval_ptr_dtor(&stmt->result_bind[i].zv);
                   2015:                        } else {
                   2016:                                /*
                   2017:                                  If it is a string, what is pointed will be freed
                   2018:                                  later in free_result(). We need to remove the variable to
                   2019:                                  which the user has lost reference.
                   2020:                                */
                   2021: #ifdef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF
                   2022:                                ZVAL_NULL(stmt->result_bind[i].zv);
                   2023: #endif
                   2024:                                zval_ptr_dtor(&stmt->result_bind[i].zv);
                   2025:                        }
                   2026:                }
                   2027:        }
                   2028:        s->m->free_result_bind(s, stmt->result_bind TSRMLS_CC);
                   2029:        stmt->result_bind = NULL;
                   2030: 
                   2031:        DBG_VOID_RETURN;
                   2032: }
                   2033: /* }}} */
                   2034: 
                   2035: 
                   2036: /* {{{ mysqlnd_stmt_separate_one_result_bind */
                   2037: static void
                   2038: mysqlnd_stmt_separate_one_result_bind(MYSQLND_STMT * const s, unsigned int param_no TSRMLS_DC)
                   2039: {
                   2040:        MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
                   2041:        DBG_ENTER("mysqlnd_stmt_separate_one_result_bind");
                   2042:        if (!stmt) {
                   2043:                DBG_VOID_RETURN;
                   2044:        }
                   2045:        DBG_INF_FMT("stmt=%lu result_bind=%p field_count=%u param_no=%u",
                   2046:                                stmt->stmt_id, stmt->result_bind, stmt->field_count, param_no);
                   2047: 
                   2048:        if (!stmt->result_bind) {
                   2049:                DBG_VOID_RETURN;
                   2050:        }
                   2051: 
                   2052:        /*
                   2053:          Because only the bound variables can point to our internal buffers, then
                   2054:          separate or free only them. Free is possible because the user could have
                   2055:          lost reference.
                   2056:        */
                   2057:        /* Let's try with no cache */
                   2058:        if (stmt->result_bind[param_no].bound == TRUE) {
                   2059:                DBG_INF_FMT("%u has refcount=%u", param_no, Z_REFCOUNT_P(stmt->result_bind[param_no].zv));
                   2060:                /*
                   2061:                  We have to separate the actual zval value of the bound
                   2062:                  variable from our allocated zvals or we will face double-free
                   2063:                */
                   2064:                if (Z_REFCOUNT_P(stmt->result_bind[param_no].zv) > 1) {
                   2065: #ifdef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF
                   2066:                        zval_copy_ctor(stmt->result_bind[param_no].zv);
                   2067: #endif
                   2068:                        zval_ptr_dtor(&stmt->result_bind[param_no].zv);
                   2069:                } else {
                   2070:                        /*
                   2071:                          If it is a string, what is pointed will be freed
                   2072:                          later in free_result(). We need to remove the variable to
                   2073:                          which the user has lost reference.
                   2074:                        */
                   2075: #ifdef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF
                   2076:                        ZVAL_NULL(stmt->result_bind[param_no].zv);
                   2077: #endif
                   2078:                        zval_ptr_dtor(&stmt->result_bind[param_no].zv);
                   2079:                }
                   2080:        }
                   2081: 
                   2082:        DBG_VOID_RETURN;
                   2083: }
                   2084: /* }}} */
                   2085: 
                   2086: 
                   2087: /* {{{ mysqlnd_stmt::free_stmt_content */
                   2088: static void
                   2089: MYSQLND_METHOD(mysqlnd_stmt, free_stmt_content)(MYSQLND_STMT * const s TSRMLS_DC)
                   2090: {
                   2091:        MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
                   2092:        DBG_ENTER("mysqlnd_stmt::free_stmt_content");
                   2093:        if (!stmt) {
                   2094:                DBG_VOID_RETURN;
                   2095:        }
                   2096:        DBG_INF_FMT("stmt=%lu param_bind=%p param_count=%u",
                   2097:                                stmt->stmt_id, stmt->param_bind, stmt->param_count);
                   2098: 
                   2099:        /* Destroy the input bind */
                   2100:        if (stmt->param_bind) {
                   2101:                unsigned int i;
                   2102:                /*
                   2103:                  Because only the bound variables can point to our internal buffers, then
                   2104:                  separate or free only them. Free is possible because the user could have
                   2105:                  lost reference.
                   2106:                */
                   2107:                for (i = 0; i < stmt->param_count; i++) {
                   2108:                        /*
                   2109:                          If bind_one_parameter was used, but not everything was
                   2110:                          bound and nothing was fetched, then some `zv` could be NULL
                   2111:                        */
                   2112:                        if (stmt->param_bind[i].zv) {
                   2113:                                zval_ptr_dtor(&stmt->param_bind[i].zv);
                   2114:                        }
                   2115:                }
                   2116:                s->m->free_parameter_bind(s, stmt->param_bind TSRMLS_CC);
                   2117:                stmt->param_bind = NULL;
                   2118:        }
                   2119: 
                   2120:        /*
                   2121:          First separate the bound variables, which point to the result set, then
                   2122:          destroy the set.
                   2123:        */
                   2124:        mysqlnd_stmt_separate_result_bind(s TSRMLS_CC);
                   2125:        /* Not every statement has a result set attached */
                   2126:        if (stmt->result) {
                   2127:                stmt->result->m.free_result_internal(stmt->result TSRMLS_CC);
                   2128:                stmt->result = NULL;
                   2129:        }
                   2130: 
                   2131:        DBG_VOID_RETURN;
                   2132: }
                   2133: /* }}} */
                   2134: 
                   2135: 
                   2136: /* {{{ mysqlnd_stmt::net_close */
                   2137: static enum_func_status
                   2138: MYSQLND_METHOD_PRIVATE(mysqlnd_stmt, net_close)(MYSQLND_STMT * const s, zend_bool implicit TSRMLS_DC)
                   2139: {
                   2140:        MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
                   2141:        MYSQLND * conn;
                   2142:        zend_uchar cmd_buf[STMT_ID_LENGTH /* statement id */];
                   2143:        enum_mysqlnd_collected_stats statistic = STAT_LAST;
                   2144: 
                   2145:        DBG_ENTER("mysqlnd_stmt::net_close");
                   2146:        if (!stmt || !stmt->conn) {
                   2147:                DBG_RETURN(FAIL);
                   2148:        }
                   2149:        DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
                   2150: 
                   2151:        conn = stmt->conn;
                   2152: 
                   2153:        SET_EMPTY_ERROR(stmt->error_info);
                   2154:        SET_EMPTY_ERROR(stmt->conn->error_info);
                   2155: 
                   2156:        /*
                   2157:          If the user decided to close the statement right after execute()
                   2158:          We have to call the appropriate use_result() or store_result() and
                   2159:          clean.
                   2160:        */
                   2161:        do {
                   2162:                DBG_INF_FMT("stmt->state=%u", stmt->state);
                   2163:                if (stmt->state == MYSQLND_STMT_WAITING_USE_OR_STORE) {
                   2164:                        DBG_INF("fetching result set header");
                   2165:                        stmt->default_rset_handler(s TSRMLS_CC);
                   2166:                        stmt->state = MYSQLND_STMT_USER_FETCHING;
                   2167:                }
                   2168: 
                   2169:                /* unbuffered set not fetched to the end ? Clean the line */
                   2170:                if (stmt->result) {
                   2171:                        DBG_INF("skipping result");
                   2172:                        stmt->result->m.skip_result(stmt->result TSRMLS_CC);
                   2173:                }
                   2174:        } while (mysqlnd_stmt_more_results(s) && mysqlnd_stmt_next_result(s) == PASS);
                   2175:        /*
                   2176:          After this point we are allowed to free the result set,
                   2177:          as we have cleaned the line
                   2178:        */
                   2179:        if (stmt->stmt_id) {
                   2180:                MYSQLND_INC_GLOBAL_STATISTIC(implicit == TRUE?  STAT_FREE_RESULT_IMPLICIT:
                   2181:                                                                                                                STAT_FREE_RESULT_EXPLICIT);
                   2182: 
                   2183:                int4store(cmd_buf, stmt->stmt_id);
                   2184:                if (CONN_GET_STATE(conn) == CONN_READY &&
                   2185:                        FAIL == conn->m->simple_command(conn, COM_STMT_CLOSE, (char *)cmd_buf, sizeof(cmd_buf),
                   2186:                                                                                   PROT_LAST /* COM_STMT_CLOSE doesn't send an OK packet*/,
                   2187:                                                                                   FALSE, TRUE TSRMLS_CC)) {
                   2188:                        stmt->error_info = conn->error_info;
                   2189:                        DBG_RETURN(FAIL);
                   2190:                }
                   2191:        }
                   2192:        switch (stmt->execute_count) {
                   2193:                case 0:
                   2194:                        statistic = STAT_PS_PREPARED_NEVER_EXECUTED;
                   2195:                        break;
                   2196:                case 1:
                   2197:                        statistic = STAT_PS_PREPARED_ONCE_USED;
                   2198:                        break;
                   2199:                default:
                   2200:                        break;
                   2201:        }
                   2202:        if (statistic != STAT_LAST) {
                   2203:                MYSQLND_INC_CONN_STATISTIC(conn->stats, statistic);
                   2204:        }
                   2205: 
                   2206:        if (stmt->execute_cmd_buffer.buffer) {
                   2207:                mnd_pefree(stmt->execute_cmd_buffer.buffer, stmt->persistent);
                   2208:                stmt->execute_cmd_buffer.buffer = NULL;
                   2209:        }
                   2210: 
                   2211:        s->m->free_stmt_content(s TSRMLS_CC);
                   2212: 
                   2213:        if (stmt->conn) {
                   2214:                stmt->conn->m->free_reference(stmt->conn TSRMLS_CC);
                   2215:                stmt->conn = NULL;
                   2216:        }
                   2217: 
                   2218:        DBG_RETURN(PASS);
                   2219: }
                   2220: /* }}} */
                   2221: 
                   2222: /* {{{ mysqlnd_stmt::dtor */
                   2223: static enum_func_status
                   2224: MYSQLND_METHOD(mysqlnd_stmt, dtor)(MYSQLND_STMT * const s, zend_bool implicit TSRMLS_DC)
                   2225: {
                   2226:        MYSQLND_STMT_DATA * stmt = (s != NULL) ? s->data:NULL;
                   2227:        enum_func_status ret = FAIL;
                   2228:        zend_bool persistent = (s != NULL) ? s->persistent : 0;
                   2229: 
                   2230:        DBG_ENTER("mysqlnd_stmt::dtor");
                   2231:        if (stmt) {
                   2232: 
                   2233:                DBG_INF_FMT("stmt=%p", stmt);
                   2234: 
                   2235:                MYSQLND_INC_GLOBAL_STATISTIC(implicit == TRUE?  STAT_STMT_CLOSE_IMPLICIT:
                   2236:                                                                                                                STAT_STMT_CLOSE_EXPLICIT);
                   2237: 
                   2238:                ret = s->m->net_close(s, implicit TSRMLS_CC);
                   2239:                mnd_pefree(stmt, persistent);
                   2240:        }
                   2241:        mnd_pefree(s, persistent);
                   2242: 
                   2243:        DBG_INF(ret == PASS? "PASS":"FAIL");
                   2244:        DBG_RETURN(ret);
                   2245: }
                   2246: /* }}} */
                   2247: 
                   2248: 
                   2249: /* {{{ mysqlnd_stmt::alloc_param_bind */
                   2250: static MYSQLND_PARAM_BIND *
                   2251: MYSQLND_METHOD(mysqlnd_stmt, alloc_param_bind)(MYSQLND_STMT * const s TSRMLS_DC)
                   2252: {
                   2253:        MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
                   2254:        DBG_ENTER("mysqlnd_stmt::alloc_param_bind");
                   2255:        if (!stmt) {
                   2256:                DBG_RETURN(NULL);
                   2257:        }
                   2258:        DBG_RETURN(mnd_pecalloc(stmt->param_count, sizeof(MYSQLND_PARAM_BIND), stmt->persistent));
                   2259: }
                   2260: /* }}} */
                   2261: 
                   2262: 
                   2263: /* {{{ mysqlnd_stmt::alloc_result_bind */
                   2264: static MYSQLND_RESULT_BIND *
                   2265: MYSQLND_METHOD(mysqlnd_stmt, alloc_result_bind)(MYSQLND_STMT * const s TSRMLS_DC)
                   2266: {
                   2267:        MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
                   2268:        DBG_ENTER("mysqlnd_stmt::alloc_result_bind");
                   2269:        if (!stmt) {
                   2270:                DBG_RETURN(NULL);
                   2271:        }
                   2272:        DBG_RETURN(mnd_pecalloc(stmt->field_count, sizeof(MYSQLND_RESULT_BIND), stmt->persistent));
                   2273: }
                   2274: /* }}} */
                   2275: 
                   2276: 
                   2277: /* {{{ param_bind::free_parameter_bind */
                   2278: PHPAPI void
                   2279: MYSQLND_METHOD(mysqlnd_stmt, free_parameter_bind)(MYSQLND_STMT * const s, MYSQLND_PARAM_BIND * param_bind TSRMLS_DC)
                   2280: {
                   2281:        MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
                   2282:        if (stmt) {
                   2283:                mnd_pefree(param_bind, stmt->persistent);
                   2284:        }
                   2285: }
                   2286: /* }}} */
                   2287: 
                   2288: 
                   2289: /* {{{ mysqlnd_stmt::free_result_bind */
                   2290: PHPAPI void
                   2291: MYSQLND_METHOD(mysqlnd_stmt, free_result_bind)(MYSQLND_STMT * const s, MYSQLND_RESULT_BIND * result_bind TSRMLS_DC)
                   2292: {
                   2293:        MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
                   2294:        if (stmt) {
                   2295:                mnd_pefree(result_bind, stmt->persistent);
                   2296:        }
                   2297: }
                   2298: /* }}} */
                   2299: 
                   2300: 
                   2301: 
                   2302: MYSQLND_CLASS_METHODS_START(mysqlnd_stmt)
                   2303:        MYSQLND_METHOD(mysqlnd_stmt, prepare),
                   2304:        MYSQLND_METHOD(mysqlnd_stmt, execute),
                   2305:        MYSQLND_METHOD(mysqlnd_stmt, use_result),
                   2306:        MYSQLND_METHOD(mysqlnd_stmt, store_result),
                   2307:        MYSQLND_METHOD(mysqlnd_stmt, get_result),
                   2308:        MYSQLND_METHOD(mysqlnd_stmt, more_results),
                   2309:        MYSQLND_METHOD(mysqlnd_stmt, next_result),
                   2310:        MYSQLND_METHOD(mysqlnd_stmt, free_result),
                   2311:        MYSQLND_METHOD(mysqlnd_stmt, data_seek),
                   2312:        MYSQLND_METHOD(mysqlnd_stmt, reset),
                   2313:        MYSQLND_METHOD_PRIVATE(mysqlnd_stmt, net_close),
                   2314:        MYSQLND_METHOD(mysqlnd_stmt, dtor),
                   2315: 
                   2316:        MYSQLND_METHOD(mysqlnd_stmt, fetch),
                   2317: 
                   2318:        MYSQLND_METHOD(mysqlnd_stmt, bind_parameters),
                   2319:        MYSQLND_METHOD(mysqlnd_stmt, bind_one_parameter),
                   2320:        MYSQLND_METHOD(mysqlnd_stmt, refresh_bind_param),
                   2321:        MYSQLND_METHOD(mysqlnd_stmt, bind_result),
                   2322:        MYSQLND_METHOD(mysqlnd_stmt, bind_one_result),
                   2323:        MYSQLND_METHOD(mysqlnd_stmt, send_long_data),
                   2324:        MYSQLND_METHOD(mysqlnd_stmt, param_metadata),
                   2325:        MYSQLND_METHOD(mysqlnd_stmt, result_metadata),
                   2326: 
                   2327:        MYSQLND_METHOD(mysqlnd_stmt, insert_id),
                   2328:        MYSQLND_METHOD(mysqlnd_stmt, affected_rows),
                   2329:        MYSQLND_METHOD(mysqlnd_stmt, num_rows),
                   2330: 
                   2331:        MYSQLND_METHOD(mysqlnd_stmt, param_count),
                   2332:        MYSQLND_METHOD(mysqlnd_stmt, field_count),
                   2333:        MYSQLND_METHOD(mysqlnd_stmt, warning_count),
                   2334: 
                   2335:        MYSQLND_METHOD(mysqlnd_stmt, errno),
                   2336:        MYSQLND_METHOD(mysqlnd_stmt, error),
                   2337:        MYSQLND_METHOD(mysqlnd_stmt, sqlstate),
                   2338: 
                   2339:        MYSQLND_METHOD(mysqlnd_stmt, attr_get),
                   2340:        MYSQLND_METHOD(mysqlnd_stmt, attr_set),
                   2341: 
                   2342: 
                   2343:        MYSQLND_METHOD(mysqlnd_stmt, alloc_param_bind),
                   2344:        MYSQLND_METHOD(mysqlnd_stmt, alloc_result_bind),
                   2345:        MYSQLND_METHOD(mysqlnd_stmt, free_parameter_bind),
                   2346:        MYSQLND_METHOD(mysqlnd_stmt, free_result_bind),
                   2347:        MYSQLND_METHOD(mysqlnd_stmt, server_status),
                   2348:        mysqlnd_stmt_execute_generate_request,
                   2349:        mysqlnd_stmt_execute_parse_response,
                   2350:        MYSQLND_METHOD(mysqlnd_stmt, free_stmt_content)
                   2351: MYSQLND_CLASS_METHODS_END;
                   2352: 
                   2353: 
                   2354: /* {{{ _mysqlnd_stmt_init */
                   2355: MYSQLND_STMT * _mysqlnd_stmt_init(MYSQLND * const conn TSRMLS_DC)
                   2356: {
                   2357:        size_t alloc_size = sizeof(MYSQLND_STMT) + mysqlnd_plugin_count() * sizeof(void *);
                   2358:        MYSQLND_STMT * ret = mnd_pecalloc(1, alloc_size, conn->persistent);
                   2359:        MYSQLND_STMT_DATA * stmt = NULL;
                   2360: 
                   2361:        DBG_ENTER("_mysqlnd_stmt_init");
                   2362:        do {
                   2363:                if (!ret) {
                   2364:                        break;
                   2365:                }
                   2366:                ret->m = mysqlnd_stmt_methods;
                   2367:                ret->persistent = conn->persistent;
                   2368: 
                   2369:                stmt = ret->data = mnd_pecalloc(1, sizeof(MYSQLND_STMT_DATA), conn->persistent);
                   2370:                DBG_INF_FMT("stmt=%p", stmt);
                   2371:                if (!stmt) {
                   2372:                        break;
                   2373:                }
                   2374:                stmt->persistent = conn->persistent;
                   2375:                stmt->state = MYSQLND_STMT_INITTED;
                   2376:                stmt->execute_cmd_buffer.length = 4096;
                   2377:                stmt->execute_cmd_buffer.buffer = mnd_pemalloc(stmt->execute_cmd_buffer.length, stmt->persistent);
                   2378:                if (!stmt->execute_cmd_buffer.buffer) {
                   2379:                        break;
                   2380:                }
                   2381: 
                   2382:                stmt->prefetch_rows = MYSQLND_DEFAULT_PREFETCH_ROWS;
                   2383:                /*
                   2384:                  Mark that we reference the connection, thus it won't be
                   2385:                  be destructed till there is open statements. The last statement
                   2386:                  or normal query result will close it then.
                   2387:                */
                   2388:                stmt->conn = conn->m->get_reference(conn TSRMLS_CC);
                   2389: 
                   2390:                DBG_RETURN(ret);
                   2391:        } while (0);
                   2392: 
                   2393:        SET_OOM_ERROR(conn->error_info);
                   2394:        if (ret) {
                   2395:                ret->m->dtor(ret, TRUE TSRMLS_CC);
                   2396:                ret = NULL;
                   2397:        }
                   2398:        DBG_RETURN(NULL);
                   2399: }
                   2400: /* }}} */
                   2401: 
                   2402: 
                   2403: /* {{{ _mysqlnd_plugin_get_plugin_stmt_data */
                   2404: PHPAPI void ** _mysqlnd_plugin_get_plugin_stmt_data(const MYSQLND_STMT * stmt, unsigned int plugin_id TSRMLS_DC)
                   2405: {
                   2406:        DBG_ENTER("_mysqlnd_plugin_get_plugin_stmt_data");
                   2407:        DBG_INF_FMT("plugin_id=%u", plugin_id);
                   2408:        if (!stmt || plugin_id >= mysqlnd_plugin_count()) {
                   2409:                return NULL;
                   2410:        }
                   2411:        DBG_RETURN((void *)((char *)stmt + sizeof(MYSQLND_STMT) + plugin_id * sizeof(void *)));
                   2412: }
                   2413: /* }}} */
                   2414: 
                   2415: 
                   2416: /* {{{ _mysqlnd_init_ps_subsystem */
                   2417: void _mysqlnd_init_ps_subsystem()
                   2418: {
                   2419:        mysqlnd_stmt_methods = &MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_stmt);
                   2420:        _mysqlnd_init_ps_fetch_subsystem();
                   2421: }
                   2422: /* }}} */
                   2423: 
                   2424: 
                   2425: /* {{{ mysqlnd_conn_get_methods */
                   2426: PHPAPI struct st_mysqlnd_stmt_methods * mysqlnd_stmt_get_methods()
                   2427: {
                   2428:        return mysqlnd_stmt_methods;
                   2429: }
                   2430: /* }}} */
                   2431: 
                   2432: 
                   2433: /* {{{ mysqlnd_conn_set_methods */
                   2434: PHPAPI void mysqlnd_stmt_set_methods(struct st_mysqlnd_stmt_methods *methods)
                   2435: {
                   2436:        mysqlnd_stmt_methods = methods;
                   2437: }
                   2438: /* }}} */
                   2439: 
                   2440: 
                   2441: /*
                   2442:  * Local variables:
                   2443:  * tab-width: 4
                   2444:  * c-basic-offset: 4
                   2445:  * End:
                   2446:  * vim600: noet sw=4 ts=4 fdm=marker
                   2447:  * vim<600: noet sw=4 ts=4
                   2448:  */

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>