58
58
59
59
60
60
61
+ static void pgsql_stmt_cancel (pdo_pgsql_stmt * S )
62
+ {
63
+ if (S -> is_running_unbuffered ) {
64
+ PGcancel * cancel = PQgetCancel (S -> H -> server );
65
+ char errbuf [256 ];
66
+ PQcancel (cancel , errbuf , 256 );
67
+ PQfreeCancel (cancel );
68
+ S -> is_running_unbuffered = false;
69
+ }
70
+ }
71
+
61
72
static int pgsql_stmt_dtor (pdo_stmt_t * stmt )
62
73
{
63
74
pdo_pgsql_stmt * S = (pdo_pgsql_stmt * )stmt -> driver_data ;
@@ -108,6 +119,8 @@ static int pgsql_stmt_dtor(pdo_stmt_t *stmt)
108
119
S -> query = NULL ;
109
120
}
110
121
122
+ pgsql_stmt_cancel (S );
123
+
111
124
if (S -> cursor_name ) {
112
125
if (server_obj_usable ) {
113
126
pdo_pgsql_db_handle * H = S -> H ;
@@ -137,10 +150,12 @@ static int pgsql_stmt_execute(pdo_stmt_t *stmt)
137
150
pdo_pgsql_stmt * S = (pdo_pgsql_stmt * )stmt -> driver_data ;
138
151
pdo_pgsql_db_handle * H = S -> H ;
139
152
ExecStatusType status ;
153
+ int dispatch_result = 1 ;
140
154
141
155
bool in_trans = stmt -> dbh -> methods -> in_transaction (stmt -> dbh );
142
156
143
157
/* ensure that we free any previous unfetched results */
158
+ pgsql_stmt_cancel (S );
144
159
if (S -> result ) {
145
160
PQclear (S -> result );
146
161
S -> result = NULL ;
@@ -219,6 +234,16 @@ static int pgsql_stmt_execute(pdo_stmt_t *stmt)
219
234
}
220
235
}
221
236
}
237
+ if (S -> is_unbuffered ) {
238
+ dispatch_result = PQsendQueryPrepared (H -> server , S -> stmt_name ,
239
+ stmt -> bound_params ?
240
+ zend_hash_num_elements (stmt -> bound_params ) :
241
+ 0 ,
242
+ (const char * * )S -> param_values ,
243
+ S -> param_lengths ,
244
+ S -> param_formats ,
245
+ 0 );
246
+ } else {
222
247
S -> result = PQexecPrepared (H -> server , S -> stmt_name ,
223
248
stmt -> bound_params ?
224
249
zend_hash_num_elements (stmt -> bound_params ) :
@@ -227,22 +252,51 @@ static int pgsql_stmt_execute(pdo_stmt_t *stmt)
227
252
S -> param_lengths ,
228
253
S -> param_formats ,
229
254
0 );
255
+ }
230
256
} else if (stmt -> supports_placeholders == PDO_PLACEHOLDER_NAMED ) {
231
257
/* execute query with parameters */
258
+ if (S -> is_unbuffered ) {
259
+ dispatch_result = PQsendQueryParams (H -> server , ZSTR_VAL (S -> query ),
260
+ stmt -> bound_params ? zend_hash_num_elements (stmt -> bound_params ) : 0 ,
261
+ S -> param_types ,
262
+ (const char * * )S -> param_values ,
263
+ S -> param_lengths ,
264
+ S -> param_formats ,
265
+ 0 );
266
+ } else {
232
267
S -> result = PQexecParams (H -> server , ZSTR_VAL (S -> query ),
233
268
stmt -> bound_params ? zend_hash_num_elements (stmt -> bound_params ) : 0 ,
234
269
S -> param_types ,
235
270
(const char * * )S -> param_values ,
236
271
S -> param_lengths ,
237
272
S -> param_formats ,
238
273
0 );
274
+ }
239
275
} else {
240
276
/* execute plain query (with embedded parameters) */
277
+ if (S -> is_unbuffered ) {
278
+ dispatch_result = PQsendQuery (H -> server , ZSTR_VAL (stmt -> active_query_string ));
279
+ } else {
241
280
S -> result = PQexec (H -> server , ZSTR_VAL (stmt -> active_query_string ));
281
+ }
242
282
}
283
+
284
+ if (S -> is_unbuffered ) {
285
+ if (!dispatch_result ) {
286
+ pdo_pgsql_error_stmt (stmt , 0 , NULL );
287
+ return 0 ;
288
+ }
289
+ S -> is_running_unbuffered = true;
290
+ PQsetSingleRowMode (H -> server );
291
+ /* no matter if it returns 0: PQ then transparently fallbacks to full result fetching */
292
+
293
+ /* try a first fetch to at least have column names and so on */
294
+ S -> result = PQgetResult (S -> H -> server );
295
+ }
296
+
243
297
status = PQresultStatus (S -> result );
244
298
245
- if (status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK ) {
299
+ if (status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK && status != PGRES_SINGLE_TUPLE ) {
246
300
pdo_pgsql_error_stmt (stmt , status , pdo_pgsql_sqlstate (S -> result ));
247
301
return 0 ;
248
302
}
@@ -464,6 +518,35 @@ static int pgsql_stmt_fetch(pdo_stmt_t *stmt,
464
518
return 0 ;
465
519
}
466
520
} else {
521
+ if (S -> is_running_unbuffered && S -> current_row >= stmt -> row_count ) {
522
+ ExecStatusType status ;
523
+
524
+ /* @todo in unbuffered mode, PQ allows multiple queries to be passed:
525
+ * column_count should be recomputed on each iteration */
526
+
527
+ if (S -> result ) {
528
+ PQclear (S -> result );
529
+ S -> result = NULL ;
530
+ }
531
+
532
+ S -> result = PQgetResult (S -> H -> server );
533
+ status = PQresultStatus (S -> result );
534
+
535
+ if (status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK && status != PGRES_SINGLE_TUPLE ) {
536
+ pdo_pgsql_error_stmt (stmt , status , pdo_pgsql_sqlstate (S -> result ));
537
+ return 0 ;
538
+ }
539
+
540
+ stmt -> row_count = (zend_long )PQntuples (S -> result );
541
+ S -> current_row = 0 ;
542
+
543
+ if (!stmt -> row_count ) {
544
+ /* libpq requires looping until getResult returns null */
545
+ PQgetResult (S -> H -> server );
546
+ /* @todo receiving a result here is unexpected and should throw an error */
547
+ S -> is_running_unbuffered = false;
548
+ }
549
+ }
467
550
if (S -> current_row < stmt -> row_count ) {
468
551
S -> current_row ++ ;
469
552
return 1 ;
0 commit comments