summaryrefslogtreecommitdiff
path: root/ext/mysqlnd
diff options
context:
space:
mode:
authorAndrey Hristov <andrey@php.net>2011-08-04 09:51:26 +0000
committerAndrey Hristov <andrey@php.net>2011-08-04 09:51:26 +0000
commitc9e7716cfd137b7c9ec2980273b3bbd4bc743e1f (patch)
tree078196a1b05032c38ed91c487438f32f3a10dc10 /ext/mysqlnd
parentd8f08192effdd786e00dfc4da8ee762b066382b6 (diff)
downloadphp-git-c9e7716cfd137b7c9ec2980273b3bbd4bc743e1f.tar.gz
Add mysqli_error_list() that returns an array with errors. Typically only
one and just one for libmysql. mysqlnd can return generate more than one error during its work and with mysqli_error() only the last error is being reported. In the array returned by mysqli_error_list() / $mysqli->error_list, all errors will be found. The list is reset when the next command is executed
Diffstat (limited to 'ext/mysqlnd')
-rw-r--r--ext/mysqlnd/mysqlnd.c28
-rw-r--r--ext/mysqlnd/mysqlnd_auth.c5
-rw-r--r--ext/mysqlnd/mysqlnd_loaddata.c14
-rw-r--r--ext/mysqlnd/mysqlnd_priv.h28
-rw-r--r--ext/mysqlnd/mysqlnd_ps.c49
-rw-r--r--ext/mysqlnd/mysqlnd_result.c10
-rw-r--r--ext/mysqlnd/mysqlnd_result_meta.c2
-rw-r--r--ext/mysqlnd/mysqlnd_structs.h9
8 files changed, 118 insertions, 27 deletions
diff --git a/ext/mysqlnd/mysqlnd.c b/ext/mysqlnd/mysqlnd.c
index e02fd79099..e850862c88 100644
--- a/ext/mysqlnd/mysqlnd.c
+++ b/ext/mysqlnd/mysqlnd.c
@@ -69,6 +69,21 @@ static struct st_mysqlnd_conn_methods *mysqlnd_conn_methods;
static struct st_mysqlnd_plugin_core mysqlnd_plugin_core;
+/* {{{ mysqlnd_error_list_pdtor */
+static void
+mysqlnd_error_list_pdtor(void * pDest)
+{
+ MYSQLND_ERROR_LIST_ELEMENT * element = (MYSQLND_ERROR_LIST_ELEMENT *) pDest;
+ TSRMLS_FETCH();
+ DBG_ENTER("mysqlnd_error_list_pdtor");
+ if (element->error) {
+ mnd_pefree(element->error, TRUE);
+ }
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
/* {{{ mysqlnd_library_end */
PHPAPI void mysqlnd_library_end(TSRMLS_D)
{
@@ -178,6 +193,11 @@ MYSQLND_METHOD(mysqlnd_conn, free_contents)(MYSQLND * conn TSRMLS_DC)
mnd_pefree(conn->last_message, pers);
conn->last_message = NULL;
}
+ if (conn->error_info.error_list) {
+ zend_llist_clean(conn->error_info.error_list);
+ mnd_pefree(conn->error_info.error_list, pers);
+ conn->error_info.error_list = NULL;
+ }
conn->charset = NULL;
conn->greet_charset = NULL;
@@ -2492,6 +2512,14 @@ PHPAPI MYSQLND * _mysqlnd_init(zend_bool persistent TSRMLS_DC)
ret = NULL;
}
+ ret->error_info.error_list = mnd_pecalloc(1, sizeof(zend_llist), persistent);
+ if (!ret->error_info.error_list) {
+ ret->m->dtor(ret TSRMLS_CC);
+ ret = NULL;
+ } else {
+ zend_llist_init(ret->error_info.error_list, sizeof(MYSQLND_ERROR_LIST_ELEMENT), (llist_dtor_func_t)mysqlnd_error_list_pdtor, persistent);
+ }
+
DBG_RETURN(ret);
}
/* }}} */
diff --git a/ext/mysqlnd/mysqlnd_auth.c b/ext/mysqlnd/mysqlnd_auth.c
index 1bb08013c2..1a56e8ef0a 100644
--- a/ext/mysqlnd/mysqlnd_auth.c
+++ b/ext/mysqlnd/mysqlnd_auth.c
@@ -137,8 +137,7 @@ mysqlnd_auth_handshake(MYSQLND * conn,
strlcpy(conn->error_info.sqlstate, auth_resp_packet->sqlstate, sizeof(conn->error_info.sqlstate));
DBG_ERR_FMT("ERROR:%u [SQLSTATE:%s] %s", auth_resp_packet->error_no, auth_resp_packet->sqlstate, auth_resp_packet->error);
}
- conn->error_info.error_no = auth_resp_packet->error_no;
- strlcpy(conn->error_info.error, auth_resp_packet->error, sizeof(conn->error_info.error));
+ SET_CLIENT_ERROR(conn->error_info, auth_resp_packet->error_no, UNKNOWN_SQLSTATE, auth_resp_packet->error);
}
goto end;
}
@@ -235,7 +234,7 @@ mysqlnd_auth_change_user(MYSQLND * const conn,
}
ret = PACKET_READ(chg_user_resp, conn);
- conn->error_info = chg_user_resp->error_info;
+ COPY_CLIENT_ERROR(conn->error_info, chg_user_resp->error_info);
if (0xFE == chg_user_resp->response_code) {
ret = FAIL;
diff --git a/ext/mysqlnd/mysqlnd_loaddata.c b/ext/mysqlnd/mysqlnd_loaddata.c
index e7803c8169..db7a9e7201 100644
--- a/ext/mysqlnd/mysqlnd_loaddata.c
+++ b/ext/mysqlnd/mysqlnd_loaddata.c
@@ -181,11 +181,12 @@ mysqlnd_handle_local_infile(MYSQLND *conn, const char *filename, zend_bool *is_w
/* init handler: allocate read buffer and open file */
if (infile.local_infile_init(&info, (char *)filename, conn->infile.userdata TSRMLS_CC)) {
+ char tmp_buf[sizeof(conn->error_info.error)];
+ int tmp_error_no;
*is_warning = TRUE;
/* error occured */
- strcpy(conn->error_info.sqlstate, UNKNOWN_SQLSTATE);
- conn->error_info.error_no =
- infile.local_infile_error(info, conn->error_info.error, sizeof(conn->error_info.error) TSRMLS_CC);
+ tmp_error_no = infile.local_infile_error(info, tmp_buf, sizeof(tmp_buf) TSRMLS_CC);
+ SET_CLIENT_ERROR(conn->error_info, tmp_error_no, UNKNOWN_SQLSTATE, tmp_buf);
/* write empty packet to server */
ret = conn->net->m.send(conn, empty_packet, 0 TSRMLS_CC);
goto infile_error;
@@ -208,11 +209,12 @@ mysqlnd_handle_local_infile(MYSQLND *conn, const char *filename, zend_bool *is_w
/* error during read occured */
if (bufsize < 0) {
+ char tmp_buf[sizeof(conn->error_info.error)];
+ int tmp_error_no;
*is_warning = TRUE;
DBG_ERR_FMT("Bufsize < 0, warning, %d %s %s", CR_SERVER_LOST, UNKNOWN_SQLSTATE, lost_conn);
- strcpy(conn->error_info.sqlstate, UNKNOWN_SQLSTATE);
- conn->error_info.error_no =
- infile.local_infile_error(info, conn->error_info.error, sizeof(conn->error_info.error) TSRMLS_CC);
+ tmp_error_no = infile.local_infile_error(info, tmp_buf, sizeof(tmp_buf) TSRMLS_CC);
+ SET_CLIENT_ERROR(conn->error_info, tmp_error_no, UNKNOWN_SQLSTATE, tmp_buf);
goto infile_error;
}
diff --git a/ext/mysqlnd/mysqlnd_priv.h b/ext/mysqlnd/mysqlnd_priv.h
index 49ec127800..5bd688a285 100644
--- a/ext/mysqlnd/mysqlnd_priv.h
+++ b/ext/mysqlnd/mysqlnd_priv.h
@@ -134,15 +134,41 @@
(error_info).error_no = 0; \
(error_info).error[0] = '\0'; \
strlcpy((error_info).sqlstate, "00000", sizeof((error_info).sqlstate)); \
+ if ((error_info).error_list) { \
+ zend_llist_clean((error_info).error_list); \
+ } \
}
+
#define SET_CLIENT_ERROR(error_info, a, b, c) \
- { \
+{ \
+ if (0 == (a)) { \
+ SET_EMPTY_ERROR((error_info)); \
+ } else { \
(error_info).error_no = (a); \
strlcpy((error_info).sqlstate, (b), sizeof((error_info).sqlstate)); \
strlcpy((error_info).error, (c), sizeof((error_info).error)); \
+ if ((error_info).error_list) {\
+ MYSQLND_ERROR_LIST_ELEMENT error_for_the_list = {0}; \
+ \
+ error_for_the_list.error_no = (a); \
+ strlcpy(error_for_the_list.sqlstate, (b), sizeof(error_for_the_list.sqlstate)); \
+ error_for_the_list.error = mnd_pestrdup((c), TRUE); \
+ if (error_for_the_list.error) { \
+ DBG_INF_FMT("adding error [%s] to the list", error_for_the_list.error); \
+ zend_llist_add_element((error_info).error_list, &error_for_the_list); \
+ } \
+ } \
+ } \
+}
+
+
+#define COPY_CLIENT_ERROR(error_info_to, error_info_from) \
+ { \
+ SET_CLIENT_ERROR((error_info_to), (error_info_from).error_no, (error_info_from).sqlstate, (error_info_from).error); \
}
+
#define SET_OOM_ERROR(error_info) SET_CLIENT_ERROR((error_info), CR_OUT_OF_MEMORY, UNKNOWN_SQLSTATE, mysqlnd_out_of_memory)
diff --git a/ext/mysqlnd/mysqlnd_ps.c b/ext/mysqlnd/mysqlnd_ps.c
index 0930987094..e4688c1564 100644
--- a/ext/mysqlnd/mysqlnd_ps.c
+++ b/ext/mysqlnd/mysqlnd_ps.c
@@ -52,6 +52,22 @@ enum_func_status mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES *result, void *param,
static void mysqlnd_stmt_separate_result_bind(MYSQLND_STMT * const stmt TSRMLS_DC);
static void mysqlnd_stmt_separate_one_result_bind(MYSQLND_STMT * const stmt, unsigned int param_no TSRMLS_DC);
+
+/* {{{ mysqlnd_ps_error_list_pdtor */
+static void
+mysqlnd_ps_error_list_pdtor(void * pDest)
+{
+ MYSQLND_ERROR_LIST_ELEMENT * element = (MYSQLND_ERROR_LIST_ELEMENT *) pDest;
+ TSRMLS_FETCH();
+ DBG_ENTER("mysqlnd_ps_error_list_pdtor");
+ if (element->error) {
+ mnd_pefree(element->error, TRUE);
+ }
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
/* {{{ mysqlnd_stmt::store_result */
static MYSQLND_RES *
MYSQLND_METHOD(mysqlnd_stmt, store_result)(MYSQLND_STMT * const s TSRMLS_DC)
@@ -110,7 +126,7 @@ MYSQLND_METHOD(mysqlnd_stmt, store_result)(MYSQLND_STMT * const s TSRMLS_DC)
stmt->state = MYSQLND_STMT_USE_OR_STORE_CALLED;
} else {
- conn->error_info = result->stored_data->error_info;
+ COPY_CLIENT_ERROR(conn->error_info, result->stored_data->error_info);
stmt->result->m.free_result_contents(stmt->result TSRMLS_CC);
mnd_efree(stmt->result);
stmt->result = NULL;
@@ -177,7 +193,7 @@ MYSQLND_METHOD(mysqlnd_stmt, get_result)(MYSQLND_STMT * const s TSRMLS_DC)
stmt->state = MYSQLND_STMT_PREPARED;
result->type = MYSQLND_RES_PS_BUF;
} else {
- stmt->error_info = conn->error_info;
+ COPY_CLIENT_ERROR(stmt->error_info, conn->error_info);
stmt->state = MYSQLND_STMT_PREPARED;
break;
}
@@ -300,7 +316,8 @@ mysqlnd_stmt_read_prepare_response(MYSQLND_STMT * s TSRMLS_DC)
}
if (0xFF == prepare_resp->error_code) {
- stmt->error_info = stmt->conn->error_info = prepare_resp->error_info;
+ COPY_CLIENT_ERROR(stmt->error_info, prepare_resp->error_info);
+ COPY_CLIENT_ERROR(stmt->conn->error_info, prepare_resp->error_info);
goto done;
}
ret = PASS;
@@ -484,7 +501,7 @@ mysqlnd_stmt_execute_parse_response(MYSQLND_STMT * const s TSRMLS_DC)
ret = mysqlnd_query_read_result_set_header(stmt->conn, s TSRMLS_CC);
if (ret == FAIL) {
- stmt->error_info = conn->error_info;
+ COPY_CLIENT_ERROR(stmt->error_info, conn->error_info);
stmt->upsert_status.affected_rows = conn->upsert_status.affected_rows;
if (CONN_GET_STATE(conn) == CONN_QUIT_SENT) {
/* close the statement here, the connection has been closed */
@@ -685,7 +702,7 @@ MYSQLND_METHOD(mysqlnd_stmt, execute)(MYSQLND_STMT * const s TSRMLS_DC)
}
if (ret == FAIL) {
- stmt->error_info = conn->error_info;
+ COPY_CLIENT_ERROR(stmt->error_info, conn->error_info);
DBG_INF("FAIL");
DBG_RETURN(FAIL);
}
@@ -902,8 +919,8 @@ mysqlnd_stmt_fetch_row_unbuffered(MYSQLND_RES *result, void *param, unsigned int
*fetched_anything = TRUE;
} else if (ret == FAIL) {
if (row_packet->error_info.error_no) {
- stmt->conn->error_info = row_packet->error_info;
- stmt->error_info = row_packet->error_info;
+ COPY_CLIENT_ERROR(stmt->conn->error_info, row_packet->error_info);
+ COPY_CLIENT_ERROR(stmt->error_info, row_packet->error_info);
}
CONN_SET_STATE(result->conn, CONN_READY);
result->unbuf->eof_reached = TRUE; /* so next time we won't get an error */
@@ -1014,7 +1031,7 @@ mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES *result, void *param, unsigned int fla
if (FAIL == stmt->conn->m->simple_command(stmt->conn, COM_STMT_FETCH, buf, sizeof(buf),
PROT_LAST /* we will handle the response packet*/,
FALSE, TRUE TSRMLS_CC)) {
- stmt->error_info = stmt->conn->error_info;
+ COPY_CLIENT_ERROR(stmt->error_info, stmt->conn->error_info);
DBG_RETURN(FAIL);
}
@@ -1218,7 +1235,7 @@ MYSQLND_METHOD(mysqlnd_stmt, reset)(MYSQLND_STMT * const s TSRMLS_DC)
FAIL == (ret = conn->m->simple_command(conn, COM_STMT_RESET, cmd_buf,
sizeof(cmd_buf), PROT_OK_PACKET,
FALSE, TRUE TSRMLS_CC))) {
- stmt->error_info = conn->error_info;
+ COPY_CLIENT_ERROR(stmt->error_info, conn->error_info);
}
stmt->upsert_status = conn->upsert_status;
@@ -1337,7 +1354,7 @@ MYSQLND_METHOD(mysqlnd_stmt, send_long_data)(MYSQLND_STMT * const s, unsigned in
ret = conn->m->simple_command(conn, cmd, cmd_buf, packet_len, PROT_LAST , FALSE, TRUE TSRMLS_CC);
mnd_efree(cmd_buf);
if (FAIL == ret) {
- stmt->error_info = conn->error_info;
+ COPY_CLIENT_ERROR(stmt->error_info, conn->error_info);
}
} else {
ret = FAIL;
@@ -2117,6 +2134,11 @@ MYSQLND_METHOD(mysqlnd_stmt, free_stmt_content)(MYSQLND_STMT * const s TSRMLS_DC
stmt->result->m.free_result_internal(stmt->result TSRMLS_CC);
stmt->result = NULL;
}
+ if (stmt->error_info.error_list) {
+ zend_llist_clean(stmt->error_info.error_list);
+ mnd_pefree(stmt->error_info.error_list, s->persistent);
+ stmt->error_info.error_list = NULL;
+ }
DBG_VOID_RETURN;
}
@@ -2174,7 +2196,7 @@ MYSQLND_METHOD_PRIVATE(mysqlnd_stmt, net_close)(MYSQLND_STMT * const s, zend_boo
FAIL == conn->m->simple_command(conn, COM_STMT_CLOSE, cmd_buf, sizeof(cmd_buf),
PROT_LAST /* COM_STMT_CLOSE doesn't send an OK packet*/,
FALSE, TRUE TSRMLS_CC)) {
- stmt->error_info = conn->error_info;
+ COPY_CLIENT_ERROR(stmt->error_info, conn->error_info);
DBG_RETURN(FAIL);
}
}
@@ -2375,6 +2397,11 @@ MYSQLND_STMT * _mysqlnd_stmt_init(MYSQLND * const conn TSRMLS_DC)
or normal query result will close it then.
*/
stmt->conn = conn->m->get_reference(conn TSRMLS_CC);
+ stmt->error_info.error_list = mnd_pecalloc(1, sizeof(zend_llist), ret->persistent);
+ if (!stmt->error_info.error_list) {
+ break;
+ }
+ zend_llist_init(stmt->error_info.error_list, sizeof(MYSQLND_ERROR_LIST_ELEMENT), (llist_dtor_func_t) mysqlnd_ps_error_list_pdtor, conn->persistent);
DBG_RETURN(ret);
} while (0);
diff --git a/ext/mysqlnd/mysqlnd_result.c b/ext/mysqlnd/mysqlnd_result.c
index f1c6332788..6262793803 100644
--- a/ext/mysqlnd/mysqlnd_result.c
+++ b/ext/mysqlnd/mysqlnd_result.c
@@ -400,7 +400,7 @@ mysqlnd_query_read_result_set_header(MYSQLND *conn, MYSQLND_STMT * s TSRMLS_DC)
This will copy the error code and the messages, as they
are buffers in the struct
*/
- conn->error_info = rset_header->error_info;
+ COPY_CLIENT_ERROR(conn->error_info, rset_header->error_info);
ret = FAIL;
DBG_ERR_FMT("error=%s", rset_header->error_info.error);
/* Return back from CONN_QUERY_SENT */
@@ -705,7 +705,7 @@ mysqlnd_fetch_row_unbuffered_c(MYSQLND_RES * result TSRMLS_DC)
}
} else if (ret == FAIL) {
if (row_packet->error_info.error_no) {
- result->conn->error_info = row_packet->error_info;
+ COPY_CLIENT_ERROR(result->conn->error_info, row_packet->error_info);
DBG_ERR_FMT("errorno=%u error=%s", row_packet->error_info.error_no, row_packet->error_info.error);
}
CONN_SET_STATE(result->conn, CONN_READY);
@@ -840,7 +840,7 @@ mysqlnd_fetch_row_unbuffered(MYSQLND_RES * result, void *param, unsigned int fla
result->unbuf->row_count++;
} else if (ret == FAIL) {
if (row_packet->error_info.error_no) {
- result->conn->error_info = row_packet->error_info;
+ COPY_CLIENT_ERROR(result->conn->error_info, row_packet->error_info);
DBG_ERR_FMT("errorno=%u error=%s", row_packet->error_info.error_no, row_packet->error_info.error);
}
CONN_SET_STATE(result->conn, CONN_READY);
@@ -1228,7 +1228,7 @@ MYSQLND_METHOD(mysqlnd_res, store_result_fetch_data)(MYSQLND * const conn, MYSQL
}
if (ret == FAIL) {
- set->error_info = row_packet->error_info;
+ COPY_CLIENT_ERROR(set->error_info, row_packet->error_info);
} else {
/* Position at the first row */
set->data_cursor = set->data;
@@ -1277,7 +1277,7 @@ MYSQLND_METHOD(mysqlnd_res, store_result)(MYSQLND_RES * result,
ret = result->m.store_result_fetch_data(conn, result, result->meta, ps_protocol TSRMLS_CC);
if (FAIL == ret) {
if (result->stored_data) {
- conn->error_info = result->stored_data->error_info;
+ COPY_CLIENT_ERROR(conn->error_info, result->stored_data->error_info);
} else {
SET_OOM_ERROR(conn->error_info);
}
diff --git a/ext/mysqlnd/mysqlnd_result_meta.c b/ext/mysqlnd/mysqlnd_result_meta.c
index fbef4daf8c..51588016a4 100644
--- a/ext/mysqlnd/mysqlnd_result_meta.c
+++ b/ext/mysqlnd/mysqlnd_result_meta.c
@@ -170,7 +170,7 @@ MYSQLND_METHOD(mysqlnd_res_meta, read_metadata)(MYSQLND_RES_METADATA * const met
DBG_RETURN(FAIL);
}
if (field_packet->error_info.error_no) {
- conn->error_info = field_packet->error_info;
+ COPY_CLIENT_ERROR(conn->error_info, field_packet->error_info);
/* Return back from CONN_QUERY_SENT */
PACKET_FREE(field_packet);
DBG_RETURN(FAIL);
diff --git a/ext/mysqlnd/mysqlnd_structs.h b/ext/mysqlnd/mysqlnd_structs.h
index edea2e895c..94af6ced24 100644
--- a/ext/mysqlnd/mysqlnd_structs.h
+++ b/ext/mysqlnd/mysqlnd_structs.h
@@ -102,9 +102,18 @@ typedef struct st_mysqlnd_error_info
char error[MYSQLND_ERRMSG_SIZE+1];
char sqlstate[MYSQLND_SQLSTATE_LENGTH + 1];
unsigned int error_no;
+ zend_llist * error_list;
} MYSQLND_ERROR_INFO;
+typedef struct st_mysqlnd_error_list_element
+{
+ char * error;
+ char sqlstate[MYSQLND_SQLSTATE_LENGTH + 1];
+ unsigned int error_no;
+} MYSQLND_ERROR_LIST_ELEMENT;
+
+
typedef struct st_mysqlnd_infile_info
{
php_stream *fd;