📄 mysqlnd_ps.c
字号:
} } efree(stmt->param_bind); } stmt->param_bind = param_bind; for (i = 0; i < stmt->param_count; i++) { /* The client will use stmt_send_long_data */ if (stmt->param_bind[i].type != MYSQL_TYPE_LONG_BLOB) { /* Prevent from freeing */ ZVAL_ADDREF(stmt->param_bind[i].zv); /* Don't update is_ref, or we will leak during conversion */ } else { stmt->param_bind[i].zv = NULL; stmt->param_bind[i].flags &= ~MYSQLND_PARAM_BIND_BLOB_USED; } } stmt->send_types_to_server = 1; } return PASS;}/* }}} *//* {{{ _mysqlnd_stmt_bind_result */static enum_func_status_mysqlnd_stmt_bind_result(MYSQLND_STMT * const stmt, MYSQLND_RESULT_BIND * const result_bind){ int i = 0; if (stmt->state < MYSQLND_STMT_PREPARED) { SET_STMT_ERROR(stmt, CR_NO_PREPARE_STMT, UNKNOWN_SQLSTATE, mysqlnd_stmt_not_prepared); return FAIL; } if (stmt->field_count) { if (!result_bind) { return FAIL; } mysqlnd_stmt_separate_result_bind(stmt); stmt->result_bind = result_bind; for (i = 0; i < stmt->field_count; i++) { /* Prevent from freeing */ ZVAL_ADDREF(stmt->result_bind[i].zv); /* Update as ZVAL_ADDREF does not do it */ stmt->result_bind[i].zv->is_ref = 1; stmt->result_bind[i].bound = TRUE; } } return PASS;}/* }}} *//* {{{ _mysqlnd_stmt_insert_id */staticmynd_ulonglong _mysqlnd_stmt_insert_id(const MYSQLND_STMT * const stmt){ return stmt->upsert_status.last_insert_id;}/* }}} *//* {{{ _mysqlnd_stmt_affected_rows */staticmynd_ulonglong _mysqlnd_stmt_affected_rows(const MYSQLND_STMT * const stmt){ return stmt->upsert_status.affected_rows;}/* }}} *//* {{{ _mysqlnd_stmt_num_rows */staticmynd_ulonglong _mysqlnd_stmt_num_rows(const MYSQLND_STMT * const stmt){ return stmt->result? mysqlnd_num_rows(stmt->result):0;}/* }}} *//* {{{ _mysqlnd_stmt_warning_count */staticunsigned int _mysqlnd_stmt_warning_count(const MYSQLND_STMT * const stmt){ return stmt->upsert_status.warning_count;}/* }}} *//* {{{ _mysqlnd_stmt_field_count */staticunsigned int _mysqlnd_stmt_field_count(const MYSQLND_STMT * const stmt){ return stmt->field_count;}/* }}} *//* {{{ _mysqlnd_stmt_param_count */staticunsigned int _mysqlnd_stmt_param_count(const MYSQLND_STMT * const stmt){ return stmt->param_count;}/* }}} *//* {{{ _mysqlnd_stmt_errno */staticunsigned int _mysqlnd_stmt_errno(const MYSQLND_STMT * const stmt){ return stmt->error_info.error_no;}/* }}} *//* {{{ _mysqlnd_stmt_error */staticconst char * _mysqlnd_stmt_error(const MYSQLND_STMT * const stmt){ return stmt->error_info.error;}/* }}} *//* {{{ _mysqlnd_stmt_sqlstate */staticconst char * _mysqlnd_stmt_sqlstate(const MYSQLND_STMT * const stmt){ return stmt->error_info.sqlstate[0] ? stmt->error_info.sqlstate:MYSQLND_SQLSTATE_NULL;}/* }}} *//* {{{ _mysqlnd_stmt_data_seek */static enum_func_status_mysqlnd_stmt_data_seek(const MYSQLND_STMT * const stmt, mynd_ulonglong row){ return stmt->result? stmt->result->m.seek_data(stmt->result, row) : FAIL;}/* }}} *//* {{{ _mysqlnd_stmt_param_metadata */staticMYSQLND_RES * _mysqlnd_stmt_param_metadata(MYSQLND_STMT * const stmt){ if (!stmt->param_count) { return NULL; } return NULL;}/* }}} *//* {{{ _mysqlnd_stmt_param_metadata */staticMYSQLND_RES * _mysqlnd_stmt_result_metadata(MYSQLND_STMT * const stmt){ MYSQLND_RES *result; MYSQLND_ZVAL_PCACHE * cache; if (!stmt->field_count || !stmt->conn || !stmt->result) { return NULL; } /* TODO: This implementation is kind of a hack, find a better way to do it. In different functions I have put fuses to check for result->m.fetch_row() being NULL. This should be handled in a better way. */ cache = mysqlnd_palloc_get_cache_reference(stmt->conn->zval_cache); result = mysqlnd_result_init(stmt->field_count, cache); result->type = MYSQLND_RES_NORMAL; result->m.fetch_row = result->m.fetch_row_normal_unbuffered; result->unbuf = ecalloc(1, sizeof(MYSQLND_RES_UNBUFFERED)); result->unbuf->eof_reached = TRUE; result->meta = stmt->result->m.clone_metadata(stmt->result->meta, FALSE); return result;}/* }}} *//* {{{ _mysqlnd_stmt_attr_set */static enum_func_status_mysqlnd_stmt_attr_set(MYSQLND_STMT * const stmt, enum mysqlnd_stmt_attr attr_type, const void * const value){ unsigned long val = *(unsigned long *) value; switch (attr_type) { case STMT_ATTR_UPDATE_MAX_LENGTH: /* XXX : libmysql uses my_bool, but mysqli uses ulong as storage on the stack and mysqlnd won't be used out of the scope of PHP -> use ulong. */ stmt->update_max_length = val? TRUE:FALSE; break; case STMT_ATTR_CURSOR_TYPE: { if (val > (unsigned long) CURSOR_TYPE_READ_ONLY) { SET_STMT_ERROR(stmt, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE, "Not implemented"); return FAIL; } stmt->flags = val; break; } case STMT_ATTR_PREFETCH_ROWS: { if (val == 0) { val = MYSQLND_DEFAULT_PREFETCH_ROWS; } else if (val > 1) { SET_STMT_ERROR(stmt, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE, "Not implemented"); return FAIL; } stmt->prefetch_rows = val; break; } default: SET_STMT_ERROR(stmt, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE, "Not implemented"); return FAIL; } return PASS;}/* }}} *//* {{{ _mysqlnd_stmt_attr_get */static enum_func_status_mysqlnd_stmt_attr_get(MYSQLND_STMT * const stmt, enum mysqlnd_stmt_attr attr_type, void * const value){ switch (attr_type) { case STMT_ATTR_UPDATE_MAX_LENGTH: *(zend_bool *) value= stmt->update_max_length; break; case STMT_ATTR_CURSOR_TYPE: *(unsigned long *) value= stmt->flags; break; case STMT_ATTR_PREFETCH_ROWS: *(unsigned long *) value= stmt->prefetch_rows; break; default: return FAIL; } return PASS;}/* }}} *//* {{{ _mysqlnd_stmt_free_result */static enum_func_status_mysqlnd_stmt_free_result(MYSQLND_STMT * const stmt TSRMLS_DC){ if (!stmt->result) { return PASS; } if (stmt->state == MYSQLND_STMT_WAITING_USE_OR_STORE) { /* Do implicit use_result and then flush the result */ stmt->default_rset_handler = _mysqlnd_stmt_use_result; stmt->default_rset_handler(stmt TSRMLS_CC); } if (stmt->state > MYSQLND_STMT_WAITING_USE_OR_STORE) { /* Flush if anything is left and unbuffered set */ stmt->result->m.skip_result(stmt->result TSRMLS_CC); /* Separate the bound variables, which point to the result set, then destroy the set. */ mysqlnd_stmt_separate_result_bind(stmt); /* Now we can destroy the result set */ stmt->result->m.free_result_buffers(stmt->result TSRMLS_CC); } /* As the buffers have been freed, we should go back to PREPARED */ stmt->state = MYSQLND_STMT_PREPARED; /* Line is free! */ stmt->conn->state = CONN_READY; return PASS;}/* }}} *//* {{{ mysqlnd_stmt_separate_result_bind */void mysqlnd_stmt_separate_result_bind(MYSQLND_STMT * const stmt){ int i; if (!stmt->result_bind) { return; } /* Because only the bound variables can point to our internal buffers, then separate or free only them. Free is possible because the user could have lost reference. */ for (i = 0; i < stmt->field_count; i++) { /* Let's try with no cache */ if (stmt->result_bind[i].bound == TRUE) { /* We have to separate the actual zval value of the bound variable from our allocated zvals or we will face double-free */ if (ZVAL_REFCOUNT(stmt->result_bind[i].zv) > 1) { zval_copy_ctor(stmt->result_bind[i].zv); zval_ptr_dtor(&stmt->result_bind[i].zv); } else { /* If it is a string, what is pointed will be freed later in free_result(). We need to remove the variable to which the user has lost reference. */ ZVAL_NULL(stmt->result_bind[i].zv); zval_ptr_dtor(&stmt->result_bind[i].zv); } } } efree(stmt->result_bind); stmt->result_bind = NULL;}/* }}} *//* {{{ mysqlnd_internal_free_stmt_content */staticvoid mysqlnd_internal_free_stmt_content(MYSQLND_STMT *stmt TSRMLS_DC){ /* Destroy the input bind */ if (stmt->param_bind) { int i; /* Because only the bound variables can point to our internal buffers, then separate or free only them. Free is possible because the user could have lost reference. */ for (i = 0; i < stmt->param_count; i++) { /* For BLOBs zv is NULL */ if (stmt->param_bind[i].zv) { zval_ptr_dtor(&stmt->param_bind[i].zv); } } efree(stmt->param_bind); stmt->param_bind = NULL; } /* First separate the bound variables, which point to the result set, then destroy the set. */ mysqlnd_stmt_separate_result_bind(stmt); /* Not every statement has a result set attached */ if (stmt->result) { mysqlnd_internal_free_result(stmt->result TSRMLS_CC); stmt->result = NULL; } if (stmt->cmd_buffer.buffer) { efree(stmt->cmd_buffer.buffer); stmt->cmd_buffer.buffer = NULL; } if (stmt->conn) { stmt->conn->m->free_reference(stmt->conn TSRMLS_CC); stmt->conn = NULL; }}/* }}} *//* {{{ _mysqlnd_stmt_close */static enum_func_status_mysqlnd_stmt_close(MYSQLND_STMT * const stmt, zend_bool implicit TSRMLS_DC){ MYSQLND * conn = stmt->conn; zend_uchar cmd_buf[STMT_ID_LENGTH /* statement id */]; /* If the user decided to close the statement right after execute() We have to call the appropriate use_result() or store_result() and clean. */ if (stmt->state == MYSQLND_STMT_WAITING_USE_OR_STORE) { stmt->default_rset_handler(stmt TSRMLS_CC); stmt->state = MYSQLND_STMT_USER_FETCHING; } /* unbuffered set not fetched to the end ? Clean the line */ if (stmt->result) { stmt->result->m.skip_result(stmt->result TSRMLS_CC); } /* After this point we are allowed to free the result set, as we have cleaned the line */ if (stmt->stmt_id) { MYSQLND_INC_CONN_STATISTIC(NULL, implicit == TRUE? STAT_FREE_RESULT_IMPLICIT: STAT_FREE_RESULT_EXPLICIT); int4store(cmd_buf, stmt->stmt_id); if (conn->state == CONN_READY && FAIL == mysqlnd_simple_command(conn, COM_STMT_CLOSE, (char *)cmd_buf, sizeof(cmd_buf), PROT_LAST /* COM_STMT_CLOSE doesn't send an OK packet*/, FALSE TSRMLS_CC)) { stmt->error_info = conn->error_info; return FAIL; } } mysqlnd_internal_free_stmt_content(stmt TSRMLS_CC); return PASS;}/* }}} *//* {{{ _mysqlnd_stmt_close */static enum_func_status_mysqlnd_stmt_dtor(MYSQLND_STMT * const stmt, zend_bool implicit TSRMLS_DC){ enum_func_status ret; MYSQLND_INC_CONN_STATISTIC(NULL, implicit == TRUE? STAT_STMT_CLOSE_IMPLICIT: STAT_STMT_CLOSE_EXPLICIT); if (PASS == (ret = stmt->m->close(stmt, implicit TSRMLS_CC))) { efree(stmt); } return ret;}/* }}} */staticstruct st_mysqlnd_stmt_methods mysqlnd_stmt_methods = { _mysqlnd_stmt_prepare, _mysqlnd_stmt_execute, _mysqlnd_stmt_store_result, _mysqlnd_stmt_free_result, _mysqlnd_stmt_data_seek, _mysqlnd_stmt_reset, _mysqlnd_stmt_close, _mysqlnd_stmt_dtor, _mysqlnd_stmt_bind_param, _mysqlnd_stmt_bind_result, _mysqlnd_stmt_send_long_data, _mysqlnd_stmt_param_metadata, _mysqlnd_stmt_result_metadata, _mysqlnd_stmt_insert_id, _mysqlnd_stmt_affected_rows, _mysqlnd_stmt_num_rows, _mysqlnd_stmt_param_count, _mysqlnd_stmt_field_count, _mysqlnd_stmt_warning_count, _mysqlnd_stmt_errno, _mysqlnd_stmt_error, _mysqlnd_stmt_sqlstate, _mysqlnd_stmt_attr_get, _mysqlnd_stmt_attr_set, };/* {{{ _mysqlnd_stmt_init */MYSQLND_STMT * _mysqlnd_stmt_init(MYSQLND * const conn){ MYSQLND_STMT *stmt = ecalloc(1, sizeof(MYSQLND_STMT)); stmt->m = &mysqlnd_stmt_methods; stmt->state = MYSQLND_STMT_INITTED; stmt->cmd_buffer.length = 4096; stmt->cmd_buffer.buffer = emalloc(stmt->cmd_buffer.length); stmt->prefetch_rows = MYSQLND_DEFAULT_PREFETCH_ROWS; /* Mark that we reference the connection, thus it won't be be destructed till there is open statements. The last statement or normal query result will close it then. */ stmt->conn = conn->m->get_reference(conn); return stmt;}/* }}} */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -