Drop support for max_length in mysqli_fetch_fields()

Retain the field, but always populate it with zero. This was
already the case for PS without length updating.

max_length has nothing lost in the field metadata -- it is a
property of the specific result set, and requires scanning the
whole result set to compute. PHP itself never uses max_length
with mysqlnd, it is only exposed in the raw mysqli API.

Keeping it for just that purpose is not worthwhile given the costs
involved. People who actually need this for some reason can easily
calculate it themselves, while making it obvious that the
calculation requires a full result set scan.
This commit is contained in:
Nikita Popov 2020-12-14 15:41:56 +01:00
parent 95a4e1ea3b
commit 890e4caf0b
10 changed files with 20 additions and 294 deletions

View File

@ -19,6 +19,13 @@ PHP 8.1 UPGRADE NOTES
1. Backward Incompatible Changes
========================================
- MySQLi:
. mysqli_fetch_fields() and mysqli_fetch_field_direct() will now always return
zero for max_length. You can compute this information by iterating over the
result set and taking the maximum length. This is what PHP was doing
internally previously.
. The MYSQLI_STMT_ATTR_UPDATE_MAX_LENGTH option no longer has an effect.
- Standard:
. version_compare() no longer accepts undocumented operator abbreviations.

View File

@ -1116,7 +1116,7 @@ static void php_add_field_properties(zval *value, const MYSQL_FIELD *field)
*/
add_property_string(value, "catalog", "def");
add_property_long(value, "max_length", field->max_length);
add_property_long(value, "max_length", 0);
add_property_long(value, "length", field->length);
add_property_long(value, "charsetnr", field->charsetnr);
add_property_long(value, "flags", field->flags);

View File

@ -35,10 +35,6 @@ require_once('skipifconnectfailure.inc');
printf("[004] Expecting charset %s/%d got %d\n",
$charsetInfo->charset, $charsetInfo->number, $tmp->charsetnr);
}
if ($tmp->length != $charsetInfo->max_length) {
printf("[005] Expecting length %d got %d\n",
$charsetInfo->max_length, $tmp->max_length);
}
if ($tmp->db != $db) {
printf("011] Expecting database '%s' got '%s'\n",
$db, $tmp->db);
@ -97,7 +93,7 @@ object(stdClass)#%d (13) {
["catalog"]=>
string(%d) "%s"
["max_length"]=>
int(1)
int(0)
["length"]=>
int(11)
["charsetnr"]=>
@ -159,7 +155,7 @@ object(stdClass)#%d (13) {
["catalog"]=>
string(%d) "%s"
["max_length"]=>
int(1)
int(0)
["length"]=>
int(11)
["charsetnr"]=>

View File

@ -42,10 +42,6 @@ require_once('skipifconnectfailure.inc');
printf("[005] Expecting charset %s/%d got %d\n",
$charsetInfo->charset, $charsetInfo->number, $tmp->charsetnr);
}
if ($tmp->length != $charsetInfo->max_length) {
printf("[006] Expecting length %d got %d\n",
$charsetInfo->max_length, $tmp->max_length);
}
if ($tmp->db != $db) {
printf("[007] Expecting database '%s' got '%s'\n",
$db, $tmp->db);
@ -86,7 +82,7 @@ object(stdClass)#%d (13) {
["catalog"]=>
string(%d) "%s"
["max_length"]=>
int(1)
int(0)
["length"]=>
int(11)
["charsetnr"]=>

View File

@ -35,11 +35,6 @@ require_once('skipifconnectfailure.inc');
$charsetInfo->charset,
$charsetInfo->number, $field->charsetnr);
}
if ($field->length != $charsetInfo->max_length) {
printf("[005] Expecting length %d got %d\n",
$charsetInfo->max_length,
$field->max_length);
}
break;
}
}
@ -76,7 +71,7 @@ object(stdClass)#%d (13) {
["catalog"]=>
string(%d) "%s"
["max_length"]=>
int(1)
int(0)
["length"]=>
int(11)
["charsetnr"]=>
@ -104,7 +99,7 @@ object(stdClass)#%d (13) {
["catalog"]=>
string(%d) "%s"
["max_length"]=>
int(1)
int(0)
["length"]=>
int(%d)
["charsetnr"]=>

View File

@ -61,7 +61,7 @@ require_once("connect.inc");
$res->close();
$stmt->close();
// expecting max_length to _be_ set
// MYSQLI_STMT_ATTR_UPDATE_MAX_LENGTH is no longer supported, expect no change in behavior.
$stmt = mysqli_stmt_init($link);
$stmt->prepare("SELECT label FROM test");
var_dump($stmt->attr_set(MYSQLI_STMT_ATTR_UPDATE_MAX_LENGTH, 1));
@ -75,8 +75,8 @@ require_once("connect.inc");
$max_lengths = array();
foreach ($fields as $k => $meta) {
$max_lengths[$meta->name] = $meta->max_length;
if ($meta->max_length === 0)
printf("[008] max_length should be set (!= 0), got %s for field %s\n", $meta->max_length, $meta->name);
if ($meta->max_length !== 0)
printf("[008] max_length should be not set (= 0), got %s for field %s\n", $meta->max_length, $meta->name);
}
$res->close();
$stmt->close();

View File

@ -766,22 +766,6 @@ mysqlnd_stmt_fetch_row_buffered(MYSQLND_RES * result, void * param, const unsign
if (PASS != rc) {
DBG_RETURN(FAIL);
}
result->stored_data->initialized_rows++;
if (stmt->update_max_length) {
for (i = 0; i < result->field_count; i++) {
/*
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(current_row[i]) == IS_STRING) {
zend_ulong len = Z_STRLEN(current_row[i]);
if (meta->fields[i].max_length < len) {
meta->fields[i].max_length = len;
}
}
}
}
}
for (i = 0; i < result->field_count; i++) {
@ -820,7 +804,6 @@ mysqlnd_stmt_fetch_row_unbuffered(MYSQLND_RES * result, void * param, const unsi
MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
MYSQLND_PACKET_ROW * row_packet;
MYSQLND_CONN_DATA * conn = result->conn;
const MYSQLND_RES_METADATA * const meta = result->meta;
void *checkpoint;
DBG_ENTER("mysqlnd_stmt_fetch_row_unbuffered");
@ -873,11 +856,6 @@ mysqlnd_stmt_fetch_row_unbuffered(MYSQLND_RES * result, void * param, const unsi
zval *resultzv = &stmt->result_bind[i].zv;
if (stmt->result_bind[i].bound == TRUE) {
zval *data = &result->unbuf->last_row_data[i];
if (Z_TYPE_P(data) == IS_STRING && (meta->fields[i].max_length < (zend_ulong) Z_STRLEN_P(data))){
meta->fields[i].max_length = Z_STRLEN_P(data);
}
ZEND_TRY_ASSIGN_VALUE_EX(resultzv, data, 0);
/* copied data, thus also the ownership. Thus null data */
ZVAL_NULL(data);
@ -1026,7 +1004,6 @@ mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES * result, void * param, const unsigned
UPSERT_STATUS_RESET(stmt->upsert_status);
if (PASS == (ret = PACKET_READ(conn, row_packet)) && !row_packet->eof) {
const MYSQLND_RES_METADATA * const meta = result->meta;
unsigned int i, field_count = result->field_count;
if (stmt->result_bind) {
@ -1055,11 +1032,6 @@ mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES * result, void * param, const unsigned
Z_TYPE_P(data), Z_REFCOUNTED(stmt->result_bind[i].zv)?
Z_REFCOUNT(stmt->result_bind[i].zv) : 0);
if (Z_TYPE_P(data) == IS_STRING &&
(meta->fields[i].max_length < (zend_ulong) Z_STRLEN_P(data))) {
meta->fields[i].max_length = Z_STRLEN_P(data);
}
ZEND_TRY_ASSIGN_VALUE_EX(resultzv, data, 0);
/* copied data, thus also the ownership. Thus null data */
ZVAL_NULL(data);
@ -1744,13 +1716,6 @@ MYSQLND_METHOD(mysqlnd_stmt, result_metadata)(MYSQLND_STMT * const s)
DBG_RETURN(NULL);
}
if (stmt->update_max_length && stmt->result->stored_data) {
/* stored result, we have to update the max_length before we clone the meta data :( */
stmt->result->stored_data->m.initialize_result_set_rest(stmt->result->stored_data,
stmt->result->meta,
conn->stats,
conn->options->int_and_float_native);
}
/*
TODO: This implementation is kind of a hack,
find a better way to do it. In different functions I have put

View File

@ -27,115 +27,6 @@
#include "mysqlnd_debug.h"
#include "mysqlnd_ext_plugin.h"
/* {{{ mysqlnd_result_buffered_zval::initialize_result_set_rest */
static enum_func_status
MYSQLND_METHOD(mysqlnd_result_buffered_zval, initialize_result_set_rest)(MYSQLND_RES_BUFFERED * const result,
MYSQLND_RES_METADATA * const meta,
MYSQLND_STATS * stats,
zend_bool int_and_float_native)
{
enum_func_status ret = PASS;
const unsigned int field_count = meta->field_count;
const uint64_t row_count = result->row_count;
zval *data_begin = ((MYSQLND_RES_BUFFERED_ZVAL *) result)->data;
zval *data_cursor = data_begin;
DBG_ENTER("mysqlnd_result_buffered_zval::initialize_result_set_rest");
if (!data_cursor || row_count == result->initialized_rows) {
DBG_RETURN(ret);
}
while ((data_cursor - data_begin) < (int)(row_count * field_count)) {
if (Z_ISUNDEF(data_cursor[0])) {
unsigned int i;
const size_t current_row_num = (data_cursor - data_begin) / field_count;
enum_func_status rc = result->m.row_decoder(&result->row_buffers[current_row_num],
data_cursor,
field_count,
meta->fields,
int_and_float_native,
stats);
if (rc != PASS) {
ret = FAIL;
break;
}
++result->initialized_rows;
for (i = 0; i < field_count; ++i) {
/*
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(data_cursor[i]) == IS_STRING) {
const size_t len = Z_STRLEN(data_cursor[i]);
if (meta->fields[i].max_length < len) {
meta->fields[i].max_length = len;
}
}
}
}
data_cursor += field_count;
}
DBG_RETURN(ret);
}
/* }}} */
/* {{{ mysqlnd_result_buffered_c::initialize_result_set_rest */
static enum_func_status
MYSQLND_METHOD(mysqlnd_result_buffered_c, initialize_result_set_rest)(MYSQLND_RES_BUFFERED * const result,
MYSQLND_RES_METADATA * const meta,
MYSQLND_STATS * stats,
const zend_bool int_and_float_native)
{
unsigned int row, field;
enum_func_status ret = PASS;
const unsigned int field_count = meta->field_count;
const uint64_t row_count = result->row_count;
enum_func_status rc;
DBG_ENTER("mysqlnd_result_buffered_c::initialize_result_set_rest");
if (result->initialized_rows < row_count) {
zend_uchar *initialized = ((MYSQLND_RES_BUFFERED_C *) result)->initialized;
zval *current_row = mnd_emalloc(field_count * sizeof(zval));
for (row = 0; row < result->row_count; row++) {
/* (row / 8) & the_bit_for_row*/
if (ZEND_BIT_TEST(initialized, row)) {
continue;
}
rc = result->m.row_decoder(&result->row_buffers[row], current_row, field_count, meta->fields, int_and_float_native, stats);
if (rc != PASS) {
ret = FAIL;
break;
}
result->initialized_rows++;
initialized[row >> 3] |= (1 << (row & 7));
for (field = 0; field < field_count; field++) {
/*
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(current_row[field]) == IS_STRING) {
const size_t len = Z_STRLEN(current_row[field]);
if (meta->fields[field].max_length < len) {
meta->fields[field].max_length = len;
}
}
zval_ptr_dtor_nogc(&current_row[field]);
}
}
mnd_efree(current_row);
}
DBG_RETURN(ret);
}
/* }}} */
/* {{{ mysqlnd_result_unbuffered::free_last_data */
static void
MYSQLND_METHOD(mysqlnd_result_unbuffered, free_last_data)(MYSQLND_RES_UNBUFFERED * unbuf, MYSQLND_STATS * const global_stats)
@ -219,8 +110,6 @@ static void
MYSQLND_METHOD(mysqlnd_result_buffered_c, free_result)(MYSQLND_RES_BUFFERED_C * const set)
{
DBG_ENTER("mysqlnd_result_buffered_c::free_result");
mnd_efree(set->initialized);
set->initialized = NULL;
DBG_VOID_RETURN;
}
/* }}} */
@ -690,10 +579,6 @@ MYSQLND_METHOD(mysqlnd_result_unbuffered, fetch_row_c)(MYSQLND_RES * result, voi
if (lengths) {
lengths[i] = len;
}
if (field->max_length < len) {
field->max_length = len;
}
}
}
result->unbuf->row_count++;
@ -825,10 +710,6 @@ MYSQLND_METHOD(mysqlnd_result_unbuffered, fetch_row)(MYSQLND_RES * result, void
if (lengths) {
lengths[i] = len;
}
if (field->max_length < len) {
field->max_length = len;
}
}
}
}
@ -944,20 +825,6 @@ MYSQLND_METHOD(mysqlnd_result_buffered, fetch_row_c)(MYSQLND_RES * result, void
if (rc != PASS) {
DBG_RETURN(FAIL);
}
++set->initialized_rows;
for (i = 0; i < field_count; ++i) {
/*
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(current_row[i]) == IS_STRING) {
const size_t len = Z_STRLEN(current_row[i]);
if (meta->fields[i].max_length < len) {
meta->fields[i].max_length = len;
}
}
}
}
/* BEGIN difference between normal normal fetch and _c */
@ -1029,20 +896,6 @@ MYSQLND_METHOD(mysqlnd_result_buffered_zval, fetch_row)(MYSQLND_RES * result, vo
if (rc != PASS) {
DBG_RETURN(FAIL);
}
++set->initialized_rows;
for (i = 0; i < field_count; ++i) {
/*
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(current_row[i]) == IS_STRING) {
const size_t len = Z_STRLEN(current_row[i]);
if (meta->fields[i].max_length < len) {
meta->fields[i].max_length = len;
}
}
}
}
for (i = 0; i < field_count; ++i) {
@ -1117,25 +970,6 @@ MYSQLND_METHOD(mysqlnd_result_buffered_c, fetch_row)(MYSQLND_RES * result, void
if (rc != PASS) {
DBG_RETURN(FAIL);
}
if (!ZEND_BIT_TEST(set->initialized, set->current_row)) {
set->initialized[set->current_row >> 3] |= (1 << (set->current_row & 7)); /* mark initialized */
++set->initialized_rows;
for (i = 0; i < field_count; ++i) {
/*
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(current_row[i]) == IS_STRING) {
const size_t len = Z_STRLEN(current_row[i]);
if (meta->fields[i].max_length < len) {
meta->fields[i].max_length = len;
}
}
}
}
for (i = 0; i < field_count; ++i) {
zval * data = &current_row[i];
@ -1393,7 +1227,6 @@ MYSQLND_METHOD(mysqlnd_res, store_result)(MYSQLND_RES * result,
} else if (flags & MYSQLND_STORE_COPY) {
MYSQLND_RES_BUFFERED_C * set = (MYSQLND_RES_BUFFERED_C *) result->stored_data;
set->current_row = 0;
set->initialized = mnd_ecalloc((unsigned int) ((set->row_count / 8) + 1), sizeof(zend_uchar)); /* +1 for safety */
}
}
@ -1547,28 +1380,6 @@ MYSQLND_METHOD(mysqlnd_res, fetch_field)(MYSQLND_RES * const result)
DBG_ENTER("mysqlnd_res::fetch_field");
do {
if (result->meta) {
/*
We optimize the result set, so we don't convert all the data from raw buffer format to
zval arrays during store. In the case someone doesn't read all the lines this will
save time. However, when a metadata call is done, we need to calculate max_length.
We don't have control whether max_length will be used, unfortunately. Otherwise we
could have been able to skip that step.
Well, if the mysqli API switches from returning stdClass to class like mysqli_field_metadata,
then we can have max_length as dynamic property, which will be calculated during runtime and
not during mysqli_fetch_field() time.
*/
if (result->stored_data && (result->stored_data->initialized_rows < result->stored_data->row_count)) {
const MYSQLND_CONN_DATA * const conn = result->conn;
DBG_INF_FMT("We have decode the whole result set to be able to satisfy this meta request");
/* we have to initialize the rest to get the updated max length */
if (PASS != result->stored_data->m.initialize_result_set_rest(result->stored_data,
result->meta,
conn->stats,
conn->options->int_and_float_native))
{
break;
}
}
DBG_RETURN(result->meta->m->fetch_field(result->meta));
}
} while (0);
@ -1584,28 +1395,6 @@ MYSQLND_METHOD(mysqlnd_res, fetch_field_direct)(MYSQLND_RES * const result, cons
DBG_ENTER("mysqlnd_res::fetch_field_direct");
do {
if (result->meta) {
/*
We optimize the result set, so we don't convert all the data from raw buffer format to
zval arrays during store. In the case someone doesn't read all the lines this will
save time. However, when a metadata call is done, we need to calculate max_length.
We don't have control whether max_length will be used, unfortunately. Otherwise we
could have been able to skip that step.
Well, if the mysqli API switches from returning stdClass to class like mysqli_field_metadata,
then we can have max_length as dynamic property, which will be calculated during runtime and
not during mysqli_fetch_field_direct() time.
*/
if (result->stored_data && (result->stored_data->initialized_rows < result->stored_data->row_count)) {
const MYSQLND_CONN_DATA * const conn = result->conn;
DBG_INF_FMT("We have decode the whole result set to be able to satisfy this meta request");
/* we have to initialized the rest to get the updated max length */
if (PASS != result->stored_data->m.initialize_result_set_rest(result->stored_data,
result->meta,
conn->stats,
conn->options->int_and_float_native))
{
break;
}
}
DBG_RETURN(result->meta->m->fetch_field_direct(result->meta, fieldnr));
}
} while (0);
@ -1622,17 +1411,6 @@ MYSQLND_METHOD(mysqlnd_res, fetch_fields)(MYSQLND_RES * const result)
DBG_ENTER("mysqlnd_res::fetch_fields");
do {
if (result->meta) {
if (result->stored_data && (result->stored_data->initialized_rows < result->stored_data->row_count)) {
const MYSQLND_CONN_DATA * const conn = result->conn;
/* we have to initialize the rest to get the updated max length */
if (PASS != result->stored_data->m.initialize_result_set_rest(result->stored_data,
result->meta,
conn->stats,
conn->options->int_and_float_native))
{
break;
}
}
DBG_RETURN(result->meta->m->fetch_fields(result->meta));
}
} while (0);
@ -1799,7 +1577,6 @@ MYSQLND_CLASS_METHODS_START(mysqlnd_result_buffered)
MYSQLND_METHOD(mysqlnd_result_buffered, num_rows),
NULL, /* fetch_lengths */
NULL, /* data_seek */
NULL, /* initialize_result_set_rest */
MYSQLND_METHOD(mysqlnd_result_buffered, free_result)
MYSQLND_CLASS_METHODS_END;
@ -1903,7 +1680,6 @@ mysqlnd_result_buffered_zval_init(MYSQLND_RES * result, const unsigned int field
ret->m.fetch_row = MYSQLND_METHOD(mysqlnd_result_buffered_zval, fetch_row);
ret->m.fetch_lengths = MYSQLND_METHOD(mysqlnd_result_buffered_zval, fetch_lengths);
ret->m.data_seek = MYSQLND_METHOD(mysqlnd_result_buffered_zval, data_seek);
ret->m.initialize_result_set_rest = MYSQLND_METHOD(mysqlnd_result_buffered_zval, initialize_result_set_rest);
DBG_RETURN(ret);
}
/* }}} */
@ -1942,7 +1718,6 @@ mysqlnd_result_buffered_c_init(MYSQLND_RES * result, const unsigned int field_co
ret->m.fetch_row = MYSQLND_METHOD(mysqlnd_result_buffered_c, fetch_row);
ret->m.fetch_lengths = MYSQLND_METHOD(mysqlnd_result_buffered_c, fetch_lengths);
ret->m.data_seek = MYSQLND_METHOD(mysqlnd_result_buffered_c, data_seek);
ret->m.initialize_result_set_rest = MYSQLND_METHOD(mysqlnd_result_buffered_c, initialize_result_set_rest);
DBG_RETURN(ret);
}

View File

@ -215,9 +215,8 @@ MYSQLND_METHOD(mysqlnd_res_meta, fetch_field)(MYSQLND_RES_METADATA * const meta)
DBG_INF("no more fields");
DBG_RETURN(NULL);
}
DBG_INF_FMT("name=%s max_length=%u",
meta->fields[meta->current_field].name? meta->fields[meta->current_field].name:"",
meta->fields[meta->current_field].max_length);
DBG_INF_FMT("name=%s",
meta->fields[meta->current_field].name? meta->fields[meta->current_field].name:"");
DBG_RETURN(&meta->fields[meta->current_field++]);
}
/* }}} */
@ -229,9 +228,8 @@ MYSQLND_METHOD(mysqlnd_res_meta, fetch_field_direct)(const MYSQLND_RES_METADATA
{
DBG_ENTER("mysqlnd_res_meta::fetch_field_direct");
DBG_INF_FMT("fieldnr=%u", fieldnr);
DBG_INF_FMT("name=%s max_length=%u",
meta->fields[meta->current_field].name? meta->fields[meta->current_field].name:"",
meta->fields[meta->current_field].max_length);
DBG_INF_FMT("name=%s",
meta->fields[meta->current_field].name? meta->fields[meta->current_field].name:"");
DBG_RETURN(&meta->fields[fieldnr]);
}
/* }}} */

View File

@ -89,7 +89,6 @@ typedef struct st_mysqlnd_field
const char *catalog; /* Catalog for table */
char *def; /* Default value */
zend_ulong length; /* Width of column (create length) */
zend_ulong max_length; /* Max width for selected set */
unsigned int name_length;
unsigned int org_name_length;
unsigned int table_length;
@ -718,8 +717,6 @@ MYSQLND_CLASS_METHODS_TYPE(mysqlnd_result_unbuffered)
};
typedef uint64_t (*func_mysqlnd_result_buffered__num_rows)(const MYSQLND_RES_BUFFERED * const result);
typedef enum_func_status (*func_mysqlnd_result_buffered__initialize_result_set_rest)(MYSQLND_RES_BUFFERED * const result, MYSQLND_RES_METADATA * const meta,
MYSQLND_STATS * stats, const zend_bool int_and_float_native);
typedef const size_t * (*func_mysqlnd_result_buffered__fetch_lengths)(const MYSQLND_RES_BUFFERED * const result);
typedef enum_func_status (*func_mysqlnd_result_buffered__data_seek)(MYSQLND_RES_BUFFERED * const result, const uint64_t row);
typedef void (*func_mysqlnd_result_buffered__free_result)(MYSQLND_RES_BUFFERED * const result);
@ -731,7 +728,6 @@ MYSQLND_CLASS_METHODS_TYPE(mysqlnd_result_buffered)
func_mysqlnd_result_buffered__num_rows num_rows;
func_mysqlnd_result_buffered__fetch_lengths fetch_lengths;
func_mysqlnd_result_buffered__data_seek data_seek;
func_mysqlnd_result_buffered__initialize_result_set_rest initialize_result_set_rest;
func_mysqlnd_result_buffered__free_result free_result;
};
@ -1185,7 +1181,6 @@ struct st_mysqlnd_result_metadata
#define def_mysqlnd_buffered_result_parent \
MYSQLND_ROW_BUFFER *row_buffers; \
uint64_t row_count; \
uint64_t initialized_rows; \
\
/* Column lengths of current row - both buffered and unbuffered. For buffered results it duplicates the data found in **data */ \
size_t *lengths; \
@ -1224,7 +1219,6 @@ struct st_mysqlnd_buffered_result_c
{
def_mysqlnd_buffered_result_parent;
zend_uchar *initialized; /* every row is a single bit */
uint64_t current_row;
};