📄 mysqlnd.c
字号:
} /* Nothing to store for UPSERT/LOAD DATA */ if (conn->last_query_type != QUERY_SELECT || conn->state != CONN_FETCHING_DATA) { SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync); return NULL; } MYSQLND_INC_CONN_STATISTIC(&conn->stats, STAT_UNBUFFERED_SETS); result = conn->current_result; conn->current_result = NULL; result->type = MYSQLND_RES_NORMAL; result->m.fetch_row = result->m.fetch_row_normal_unbuffered; result->m.fetch_lengths = mysqlnd_fetch_lengths_unbuffered; result->unbuf = ecalloc(1, sizeof(MYSQLND_RES_UNBUFFERED)); result->conn = conn->m->get_reference(conn); /* Will be freed in the mysqlnd_internal_free_result_contents() called by the resource destructor. mysqlnd_fetch_row_unbuffered() expects this to be not NULL. */ PACKET_INIT(result->row_packet, PROT_ROW_PACKET, php_mysql_packet_row *); result->row_packet->field_count = result->field_count; result->row_packet->binary_protocol = FALSE; result->row_packet->fields_metadata = result->meta->fields; result->lengths = ecalloc(result->field_count, sizeof(unsigned long)); /* No multithreading issues as we don't share the connection :) */ return result;}/* }}} *//* {{{ mysqlnd_fetch_row_buffered */static enum_func_statusmysqlnd_fetch_row_buffered(MYSQLND_RES *result, void *param, unsigned int flags, zend_bool *fetched_anything TSRMLS_DC){ unsigned int i; zval *row = (zval *) param; /* If we haven't read everything */ if (result->data->data_cursor && (result->data->data_cursor - result->data->data) < result->data->row_count) { zval **current_row = *result->data->data_cursor; for (i = 0; i < result->field_count; i++) { zval *data = current_row[i]; /* Let us later know what to do with this zval. If ref_count > 1, we will just decrease it, otherwise free it. zval_ptr_dtor() make this very easy job. */ ZVAL_ADDREF(data); if ((flags & MYSQLND_FETCH_BOTH) == MYSQLND_FETCH_BOTH) { ZVAL_ADDREF(data); } if (flags & MYSQLND_FETCH_NUM) { zend_hash_next_index_insert(Z_ARRVAL_P(row), &data, sizeof(zval *), NULL); } if (flags & MYSQLND_FETCH_ASSOC) { /* zend_hash_quick_update needs length + trailing zero */ /* QQ: Error handling ? */ /* zend_hash_quick_update does not check, as add_assoc_zval_ex do, whether the index is a numeric and convert it to it. This however means constant hashing of the column name, which is not needed as it can be precomputed. */ if (result->meta->zend_hash_keys[i].is_numeric == FALSE) {#if PHP_MAJOR_VERSION >= 6 if (UG(unicode)) { zend_u_hash_quick_update(Z_ARRVAL_P(row), IS_UNICODE, result->meta->zend_hash_keys[i].ustr, result->meta->zend_hash_keys[i].ulen + 1, result->meta->zend_hash_keys[i].key, (void *) &data, sizeof(zval *), NULL); } else#endif { zend_hash_quick_update(Z_ARRVAL_P(row), result->meta->fields[i].name, result->meta->fields[i].name_length + 1, result->meta->zend_hash_keys[i].key, (void *) &data, sizeof(zval *), NULL); } } else { zend_hash_index_update(Z_ARRVAL_P(row), result->meta->zend_hash_keys[i].key, (void *) &data, sizeof(zval *), NULL); } } } result->data->data_cursor++; *fetched_anything = TRUE; } else { result->data->data_cursor = NULL; *fetched_anything = FALSE;#ifndef MYSQLND_SILENT php_printf("NO MORE DATA\n ");#endif } return PASS;}/* }}} */#define STORE_RESULT_PREALLOCATED_SET 32/* {{{ mysqlnd_store_result */staticMYSQLND_RES *_mysqlnd_store_result(MYSQLND * const conn TSRMLS_DC){ enum_func_status ret; unsigned int next_extend = STORE_RESULT_PREALLOCATED_SET, free_rows; php_mysql_packet_row row_packet; MYSQLND_RES *result; zend_bool to_cache = FALSE; if (!conn->current_result) { return NULL; } /* Nothing to store for UPSERT/LOAD DATA*/ if (conn->last_query_type != QUERY_SELECT || conn->state != CONN_FETCHING_DATA) { SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync); return NULL; } MYSQLND_INC_CONN_STATISTIC(&conn->stats, STAT_BUFFERED_SETS); result = conn->current_result; conn->current_result = NULL; result->type = MYSQLND_RES_NORMAL; result->m.fetch_row = result->m.fetch_row_normal_buffered; result->m.fetch_lengths = mysqlnd_fetch_lengths_buffered; conn->state = CONN_FETCHING_DATA; result->lengths = ecalloc(result->field_count, sizeof(unsigned long)); /* Create room for 'next_extend' rows */ result->conn = NULL; /* store result does not reference the connection */ result->data = pecalloc(1, sizeof(MYSQLND_RES_BUFFERED), to_cache); result->data->data = pemalloc(next_extend * sizeof(zval **), to_cache); result->data->row_buffers = pemalloc(next_extend * sizeof(zend_uchar *), to_cache); result->data->persistent = to_cache; result->data->qcache = to_cache? mysqlnd_qcache_get_cache_reference(conn->qcache):NULL; result->data->references = 1; free_rows = next_extend; PACKET_INIT_ALLOCA(row_packet, PROT_ROW_PACKET); row_packet.field_count = result->field_count; row_packet.binary_protocol = FALSE; row_packet.fields_metadata = result->meta->fields; /* Let the row packet fill our buffer and skip additional malloc + memcpy */ while (FAIL != (ret = PACKET_READ_ALLOCA(row_packet, conn)) && !row_packet.eof) { int i; zval **current_row; if (!free_rows) { unsigned long total_rows = free_rows = next_extend = next_extend * 5 / 3; /* extend with 33% */ total_rows += result->data->row_count; result->data->data = perealloc(result->data->data, total_rows * sizeof(zval **), to_cache); result->data->row_buffers = perealloc(result->data->row_buffers, total_rows * sizeof(zend_uchar *), to_cache); } free_rows--; current_row = result->data->data[result->data->row_count] = row_packet.fields; result->data->row_buffers[result->data->row_count] = row_packet.row_buffer; result->data->row_count++; /* So row_packet's destructor function won't efree() it */ row_packet.fields = NULL; row_packet.row_buffer = NULL; for (i = 0; i < row_packet.field_count; i++) { unsigned long len; /* NULL fields are 0 length, 0 is not more than 0 String of zero size, definitely can't be the next max_length. Thus for NULL and zero-length we are quite efficient. */ if (Z_TYPE_P(current_row[i]) != IS_NULL && (len = Z_STRLEN_P(current_row[i])) && result->meta->fields[i].max_length < len) { result->meta->fields[i].max_length = len; } } /* No need to FREE_ALLOCA as we can reuse the 'lengths' and 'fields' arrays. For lengths its absolutely safe. 'fields' is reused because the ownership of the strings has been transfered above. */ } MYSQLND_INC_CONN_STATISTIC_W_VALUE(&conn->stats, STAT_ROWS_FETCHED_FROM_CLIENT, result->data->row_count); /* Finally clean */ if (row_packet.eof) { conn->upsert_status.warning_count = row_packet.warning_count; conn->upsert_status.server_status = row_packet.server_status; } PACKET_FREE_ALLOCA(row_packet); /* save some memory */ if (free_rows) { result->data->data = perealloc(result->data->data, result->data->row_count * sizeof(zval **), to_cache); result->data->row_buffers = perealloc(result->data->row_buffers, result->data->row_count * sizeof(zend_uchar *), to_cache); } if (conn->upsert_status.server_status & SERVER_MORE_RESULTS_EXISTS) { conn->state = CONN_NEXT_RESULT_PENDING; } else { conn->state = CONN_READY; } if (PASS == ret) { /* Position at the first row */ result->data->data_cursor = result->data->data; /* libmysql's documentation says it should be so for SELECT statements */ conn->upsert_status.affected_rows = result->data->row_count; } else { mysqlnd_internal_free_result_contents(result TSRMLS_CC); efree(result); result = NULL; php_error_docref(NULL TSRMLS_CC, E_WARNING, "Pretty serious error"); } return result;}/* }}} *//* {{{ _mysqlnd_unbuffered_skip_result */static enum_func_status_mysqlnd_unbuffered_skip_result(MYSQLND_RES * const result TSRMLS_DC){ zend_bool fetched_anything; /* Unbuffered sets A PS could be prepared - there is metadata and thus a stmt->result but the fetch_row function isn't actually set (NULL), thus we have to skip these. */ if (!result->data && result->conn && result->unbuf && !result->unbuf->eof_reached && result->m.fetch_row) { /* We have to fetch all data to clean the line */ MYSQLND_INC_CONN_STATISTIC(&result->conn->stats, result->type == MYSQLND_RES_NORMAL? STAT_FLUSHED_NORMAL_SETS: STAT_FLUSHED_PS_SETS); while ((PASS == result->m.fetch_row(result, NULL, 0, &fetched_anything TSRMLS_CC)) && fetched_anything == TRUE) { /* do nothing */; } } return PASS;}/* }}} *//* {{{ _mysqlnd_free_result */static enum_func_status_mysqlnd_free_result(MYSQLND_RES *result, zend_bool implicit TSRMLS_DC){ result->m.skip_result(result TSRMLS_CC); MYSQLND_INC_CONN_STATISTIC(result->conn? &result->conn->stats : NULL, implicit == TRUE? STAT_FREE_RESULT_EXPLICIT: STAT_FREE_RESULT_IMPLICIT); mysqlnd_internal_free_result(result TSRMLS_CC); return PASS;}/* }}} *//* {{{ _mysqlnd_data_seek */static enum_func_status_mysqlnd_data_seek(MYSQLND_RES *result, mynd_ulonglong row){ if (!result->data) { return FAIL; } /* libmysql just moves to the end, it does traversing of a linked list */ if (row >= result->data->row_count) { result->data->data_cursor = NULL; } else { result->data->data_cursor = result->data->data + row; } return PASS;}/* }}} *//* {{{ mysqlnd_errno */staticunsigned int _mysqlnd_errno(const MYSQLND * const conn){ return conn->error_info.error_no;}/* }}} *//* {{{ mysqlnd_error */staticconst char * _mysqlnd_error(const MYSQLND * const conn){ return conn->error_info.error;}/* }}} *//* {{{ mysqlnd_sqlstate */staticconst char * _mysqlnd_sqlstate(const MYSQLND * const conn){ return conn->error_info.sqlstate[0] ? conn->error_info.sqlstate:MYSQLND_SQLSTATE_NULL;}/* }}} *//* {{{ _mysqlnd_real_escape_quotes */staticulong _mysqlnd_real_escape_quotes(const MYSQLND * const conn, char *newstr, const char *escapestr, int escapestr_len){ const char *newstr_s = newstr; const char *newstr_e = newstr + 2 * escapestr_len; const char *end = escapestr + escapestr_len; zend_bool escape_overflow = FALSE; for (;escapestr < end; escapestr++) { uint len; /* check unicode characters */ if (conn->charset->char_maxlen > 1 && conn->charset->mb_charlen(*escapestr) > 1) { len = conn->charset->mb_valid(escapestr, end); /* check possible overflow */ if ((newstr + len) > newstr_e) { escape_overflow = TRUE; break; } /* copy mb char without escaping it */ while (len--) { *newstr++ = *escapestr++; } escapestr--; continue; } if (*newstr == '\'') { if (newstr + 2 > newstr_e) { escape_overflow = TRUE; break; } *newstr++ = '\''; *newstr++ = '\''; } else { if (newstr + 1 > newstr_e) { escape_overflow = TRUE; break; } *newstr++= *escapestr; } } *newstr = '\0'; if (escape_overflow) { return (ulong)~0; } return (ulong)(newstr - newstr_s);}/* }}} *//* {{{ _mysqlnd_real_escape_slashes */staticulong _mysqlnd_real_escape_slashes(const MYSQLND * const conn, char *newstr, const char *escapestr, int escapestr_len){ const char *newstr_s = newstr; const char *newstr_e = newstr + 2 * escapestr_len; const char *end = escapestr + escapestr_len; zend_bool escape_overflow = FALSE; for (;escapestr < end; escapestr++) { uint len; char esc = '\0'; /* check unicode characters */ if (conn->charset->char_maxlen > 1 && conn->charset->mb_charlen(*escapestr) > 1) { len = conn->charset->mb_valid(escapestr, end); /* check possible overflow */ if ((newstr + len) > newstr_e) { escape_overflow = TRUE; break; } /* copy mb char without escaping it */ while (len--) { *newstr++ = *escapestr++; } escapestr--; continue; } if (conn->charset->char_maxlen > 1 && conn->charset->mb_charlen(*escapestr) > 1) { esc = *escapestr; } else { switch (*escapestr) { case 0: esc = '0'; break; case '\n': esc = 'n'; break; case '\r': esc = 'r'; break; case '\\': case '\'': case '"': esc = *escapestr; break; case '\032': esc = 'Z'; break; } } if (esc) { if (newstr + 2 > newstr_e) { escape_overflow = TRUE; break; } /* copy escaped character */ *newstr++ = '\\'; *newstr++ = esc; } else { if (newstr + 1 > newstr_e) { escape_overflow = TRUE; break; } /* copy non escaped character */ *newstr++ = *escapestr; } } *newstr = '\0'; if (escape_overflow) { return (ulong)~0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -