@@ -37,6 +37,36 @@ enum_func_status mysqlnd_stmt_execute_batch_generate_request(MYSQLND_STMT * cons
37
37
38
38
static void mysqlnd_stmt_separate_result_bind (MYSQLND_STMT * const stmt );
39
39
40
+ static enum_func_status mysqlnd_stmt_send_cursor_fetch_command (
41
+ const MYSQLND_STMT_DATA * stmt , unsigned max_rows )
42
+ {
43
+ MYSQLND_CONN_DATA * conn = stmt -> conn ;
44
+ zend_uchar buf [MYSQLND_STMT_ID_LENGTH /* statement id */ + 4 /* number of rows to fetch */ ];
45
+ const MYSQLND_CSTRING payload = {(const char * ) buf , sizeof (buf )};
46
+
47
+ int4store (buf , stmt -> stmt_id );
48
+ int4store (buf + MYSQLND_STMT_ID_LENGTH , max_rows );
49
+
50
+ if (conn -> command -> stmt_fetch (conn , payload ) == FAIL ) {
51
+ COPY_CLIENT_ERROR (stmt -> error_info , * conn -> error_info );
52
+ return FAIL ;
53
+ }
54
+ return PASS ;
55
+ }
56
+
57
+ static zend_bool mysqlnd_stmt_check_state (const MYSQLND_STMT_DATA * stmt )
58
+ {
59
+ const MYSQLND_CONN_DATA * conn = stmt -> conn ;
60
+ if (stmt -> state != MYSQLND_STMT_WAITING_USE_OR_STORE ) {
61
+ return 0 ;
62
+ }
63
+ if (stmt -> cursor_exists ) {
64
+ return GET_CONNECTION_STATE (& conn -> state ) == CONN_READY ;
65
+ } else {
66
+ return GET_CONNECTION_STATE (& conn -> state ) == CONN_FETCHING_DATA ;
67
+ }
68
+ }
69
+
40
70
/* {{{ mysqlnd_stmt::store_result */
41
71
static MYSQLND_RES *
42
72
MYSQLND_METHOD (mysqlnd_stmt , store_result )(MYSQLND_STMT * const s )
@@ -57,14 +87,8 @@ MYSQLND_METHOD(mysqlnd_stmt, store_result)(MYSQLND_STMT * const s)
57
87
DBG_RETURN (NULL );
58
88
}
59
89
60
- if (stmt -> cursor_exists ) {
61
- /* Silently convert buffered to unbuffered, for now */
62
- DBG_RETURN (s -> m -> use_result (s ));
63
- }
64
-
65
90
/* Nothing to store for UPSERT/LOAD DATA*/
66
- if (GET_CONNECTION_STATE (& conn -> state ) != CONN_FETCHING_DATA || stmt -> state != MYSQLND_STMT_WAITING_USE_OR_STORE )
67
- {
91
+ if (!mysqlnd_stmt_check_state (stmt )) {
68
92
SET_CLIENT_ERROR (conn -> error_info , CR_COMMANDS_OUT_OF_SYNC , UNKNOWN_SQLSTATE , mysqlnd_out_of_sync );
69
93
DBG_RETURN (NULL );
70
94
}
@@ -75,6 +99,12 @@ MYSQLND_METHOD(mysqlnd_stmt, store_result)(MYSQLND_STMT * const s)
75
99
SET_EMPTY_ERROR (conn -> error_info );
76
100
MYSQLND_INC_CONN_STATISTIC (conn -> stats , STAT_PS_BUFFERED_SETS );
77
101
102
+ if (stmt -> cursor_exists ) {
103
+ if (mysqlnd_stmt_send_cursor_fetch_command (stmt , -1 ) == FAIL ) {
104
+ DBG_RETURN (NULL );
105
+ }
106
+ }
107
+
78
108
result = stmt -> result ;
79
109
result -> type = MYSQLND_RES_PS_BUF ;
80
110
/* result->m.row_decoder = php_mysqlnd_rowp_read_binary_protocol; */
@@ -127,19 +157,8 @@ MYSQLND_METHOD(mysqlnd_stmt, get_result)(MYSQLND_STMT * const s)
127
157
DBG_RETURN (NULL );
128
158
}
129
159
130
- if (stmt -> cursor_exists ) {
131
- /* Prepared statement cursors are not supported as of yet */
132
- char * msg ;
133
- mnd_sprintf (& msg , 0 , "%s() cannot be used with cursors" , get_active_function_name ());
134
- SET_CLIENT_ERROR (stmt -> error_info , CR_NOT_IMPLEMENTED , UNKNOWN_SQLSTATE , msg );
135
- if (msg ) {
136
- mnd_sprintf_free (msg );
137
- }
138
- DBG_RETURN (NULL );
139
- }
140
-
141
160
/* Nothing to store for UPSERT/LOAD DATA*/
142
- if (GET_CONNECTION_STATE ( & conn -> state ) != CONN_FETCHING_DATA || stmt -> state != MYSQLND_STMT_WAITING_USE_OR_STORE ) {
161
+ if (! mysqlnd_stmt_check_state ( stmt ) ) {
143
162
SET_CLIENT_ERROR (stmt -> error_info , CR_COMMANDS_OUT_OF_SYNC , UNKNOWN_SQLSTATE , mysqlnd_out_of_sync );
144
163
DBG_RETURN (NULL );
145
164
}
@@ -148,6 +167,12 @@ MYSQLND_METHOD(mysqlnd_stmt, get_result)(MYSQLND_STMT * const s)
148
167
SET_EMPTY_ERROR (conn -> error_info );
149
168
MYSQLND_INC_CONN_STATISTIC (conn -> stats , STAT_BUFFERED_SETS );
150
169
170
+ if (stmt -> cursor_exists ) {
171
+ if (mysqlnd_stmt_send_cursor_fetch_command (stmt , -1 ) == FAIL ) {
172
+ DBG_RETURN (NULL );
173
+ }
174
+ }
175
+
151
176
do {
152
177
result = conn -> m -> result_init (stmt -> result -> field_count );
153
178
if (!result ) {
@@ -551,28 +576,30 @@ mysqlnd_stmt_execute_parse_response(MYSQLND_STMT * const s, enum_mysqlnd_parse_e
551
576
DBG_INF_FMT ("server_status=%u cursor=%u" , UPSERT_STATUS_GET_SERVER_STATUS (stmt -> upsert_status ),
552
577
UPSERT_STATUS_GET_SERVER_STATUS (stmt -> upsert_status ) & SERVER_STATUS_CURSOR_EXISTS );
553
578
554
- if (UPSERT_STATUS_GET_SERVER_STATUS (stmt -> upsert_status ) & SERVER_STATUS_CURSOR_EXISTS ) {
555
- DBG_INF ("cursor exists" );
556
- stmt -> cursor_exists = TRUE;
557
- SET_CONNECTION_STATE (& conn -> state , CONN_READY );
558
- /* Only cursor read */
559
- stmt -> default_rset_handler = s -> m -> use_result ;
560
- DBG_INF ("use_result" );
561
- } else if (stmt -> flags & CURSOR_TYPE_READ_ONLY ) {
562
- DBG_INF ("asked for cursor but got none" );
563
- /*
564
- We have asked for CURSOR but got no cursor, because the condition
565
- above is not fulfilled. Then...
566
-
567
- This is a single-row result set, a result set with no rows, EXPLAIN,
568
- SHOW VARIABLES, or some other command which either a) bypasses the
569
- cursors framework in the server and writes rows directly to the
570
- network or b) is more efficient if all (few) result set rows are
571
- precached on client and server's resources are freed.
572
- */
573
- /* preferred is buffered read */
574
- stmt -> default_rset_handler = s -> m -> store_result ;
575
- DBG_INF ("store_result" );
579
+ if (stmt -> flags & CURSOR_TYPE_READ_ONLY ) {
580
+ if (UPSERT_STATUS_GET_SERVER_STATUS (stmt -> upsert_status ) & SERVER_STATUS_CURSOR_EXISTS ) {
581
+ DBG_INF ("cursor exists" );
582
+ stmt -> cursor_exists = TRUE;
583
+ SET_CONNECTION_STATE (& conn -> state , CONN_READY );
584
+ /* Only cursor read */
585
+ stmt -> default_rset_handler = s -> m -> use_result ;
586
+ DBG_INF ("use_result" );
587
+ } else {
588
+ DBG_INF ("asked for cursor but got none" );
589
+ /*
590
+ We have asked for CURSOR but got no cursor, because the condition
591
+ above is not fulfilled. Then...
592
+
593
+ This is a single-row result set, a result set with no rows, EXPLAIN,
594
+ SHOW VARIABLES, or some other command which either a) bypasses the
595
+ cursors framework in the server and writes rows directly to the
596
+ network or b) is more efficient if all (few) result set rows are
597
+ precached on client and server's resources are freed.
598
+ */
599
+ /* preferred is buffered read */
600
+ stmt -> default_rset_handler = s -> m -> store_result ;
601
+ DBG_INF ("store_result" );
602
+ }
576
603
} else {
577
604
DBG_INF ("no cursor" );
578
605
/* preferred is unbuffered read */
@@ -718,11 +745,7 @@ MYSQLND_METHOD(mysqlnd_stmt, use_result)(MYSQLND_STMT * s)
718
745
}
719
746
DBG_INF_FMT ("stmt=%lu" , stmt -> stmt_id );
720
747
721
- if (!stmt -> field_count ||
722
- (!stmt -> cursor_exists && GET_CONNECTION_STATE (& conn -> state ) != CONN_FETCHING_DATA ) ||
723
- (stmt -> cursor_exists && GET_CONNECTION_STATE (& conn -> state ) != CONN_READY ) ||
724
- (stmt -> state != MYSQLND_STMT_WAITING_USE_OR_STORE ))
725
- {
748
+ if (!stmt -> field_count || !mysqlnd_stmt_check_state (stmt )) {
726
749
SET_CLIENT_ERROR (conn -> error_info , CR_COMMANDS_OUT_OF_SYNC , UNKNOWN_SQLSTATE , mysqlnd_out_of_sync );
727
750
DBG_ERR ("command out of sync" );
728
751
DBG_RETURN (NULL );
@@ -752,7 +775,6 @@ mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES * result, zval **row_ptr, const unsign
752
775
enum_func_status ret ;
753
776
MYSQLND_STMT_DATA * stmt = result -> unbuf -> stmt ;
754
777
MYSQLND_CONN_DATA * conn = stmt -> conn ;
755
- zend_uchar buf [MYSQLND_STMT_ID_LENGTH /* statement id */ + 4 /* number of rows to fetch */ ];
756
778
MYSQLND_PACKET_ROW * row_packet ;
757
779
void * checkpoint ;
758
780
@@ -777,18 +799,9 @@ mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES * result, zval **row_ptr, const unsign
777
799
SET_EMPTY_ERROR (stmt -> error_info );
778
800
SET_EMPTY_ERROR (conn -> error_info );
779
801
780
- int4store (buf , stmt -> stmt_id );
781
- int4store (buf + MYSQLND_STMT_ID_LENGTH , 1 ); /* for now fetch only one row */
782
-
783
- {
784
- const MYSQLND_CSTRING payload = {(const char * ) buf , sizeof (buf )};
785
-
786
- ret = conn -> command -> stmt_fetch (conn , payload );
787
- if (ret == FAIL ) {
788
- COPY_CLIENT_ERROR (stmt -> error_info , * conn -> error_info );
789
- DBG_RETURN (FAIL );
790
- }
791
-
802
+ /* for now fetch only one row */
803
+ if (mysqlnd_stmt_send_cursor_fetch_command (stmt , 1 ) == FAIL ) {
804
+ DBG_RETURN (FAIL );
792
805
}
793
806
794
807
checkpoint = result -> memory_pool -> checkpoint ;
0 commit comments