Skip to content

Commit f48c0c7

Browse files
committed
Merge pull request mongodb#45 from derickr/PHP-320-gridfs-transaction
Fixed transaction issues with GridFS
2 parents 066624e + bc6e7c5 commit f48c0c7

File tree

4 files changed

+398
-60
lines changed

4 files changed

+398
-60
lines changed

gridfs.c

Lines changed: 181 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131
#include "mongo_types.h"
3232
#include "db.h"
3333

34+
#include "ext/standard/php_smart_str.h"
35+
3436
typedef struct {
3537
FILE *file;
3638
int fd; /* underlying file descriptor */
@@ -335,6 +337,57 @@ static void add_md5(zval *zfile, zval *zid, mongo_collection *c TSRMLS_DC) {
335337
}
336338
}
337339

340+
static cleanup_broken_insert(INTERNAL_FUNCTION_PARAMETERS, zval *zid TSRMLS_CC)
341+
{
342+
zval *chunks, *files, *criteria_chunks, *criteria_files;
343+
char *message = NULL;
344+
smart_str tmp_message = { 0 };
345+
zval *temp_return;
346+
347+
chunks = zend_read_property(mongo_ce_GridFS, getThis(), "chunks", strlen("chunks"), NOISY TSRMLS_CC);
348+
if (EG(exception)) {
349+
message = estrdup(Z_STRVAL_P(zend_read_property(mongo_ce_GridFSException, EG(exception), "message", strlen("message"), NOISY TSRMLS_CC)));
350+
zend_clear_exception(TSRMLS_C);
351+
}
352+
353+
MAKE_STD_ZVAL(criteria_files);
354+
array_init(criteria_files);
355+
zval_add_ref(&zid);
356+
add_assoc_zval(criteria_files, "_id", zid);
357+
358+
MAKE_STD_ZVAL(criteria_chunks);
359+
array_init(criteria_chunks);
360+
zval_add_ref(&zid);
361+
add_assoc_zval(criteria_chunks, "files_id", zid);
362+
363+
MAKE_STD_ZVAL(temp_return);
364+
ZVAL_NULL(temp_return);
365+
MONGO_METHOD1(MongoCollection, remove, temp_return, chunks, criteria_chunks);
366+
zval_ptr_dtor(&temp_return);
367+
368+
MAKE_STD_ZVAL(temp_return);
369+
ZVAL_NULL(temp_return);
370+
MONGO_METHOD1(MongoCollection, remove, temp_return, getThis(), criteria_files);
371+
zval_ptr_dtor(&temp_return);
372+
373+
zval_ptr_dtor(&criteria_files);
374+
zval_ptr_dtor(&criteria_chunks);
375+
376+
// create the message for the exception
377+
if (message) {
378+
smart_str_appends(&tmp_message, "Could not store file: ");
379+
smart_str_appends(&tmp_message, message);
380+
smart_str_0(&tmp_message);
381+
efree(message);
382+
} else {
383+
smart_str_appends(&tmp_message, "Could not store file for unknown reasons");
384+
smart_str_0(&tmp_message);
385+
}
386+
zend_throw_exception(mongo_ce_GridFSException, tmp_message.c, 0 TSRMLS_CC);
387+
smart_str_free(&tmp_message);
388+
RETVAL_FALSE;
389+
}
390+
338391
/*
339392
* Stores an array of bytes that may not have a filename,
340393
* such as data from a socket or stream.
@@ -348,6 +401,7 @@ PHP_METHOD(MongoGridFS, storeBytes) {
348401
char *bytes = 0;
349402
int bytes_len = 0, chunk_num = 0, chunk_size = 0, global_chunk_size = 0,
350403
pos = 0;
404+
int free_options = 0, revert = 0;
351405

352406
zval temp;
353407
zval *extra = 0, *zid = 0, *zfile = 0, *chunks = 0, *options = 0;
@@ -358,16 +412,9 @@ PHP_METHOD(MongoGridFS, storeBytes) {
358412
chunks = zend_read_property(mongo_ce_GridFS, getThis(), "chunks", strlen("chunks"), NOISY TSRMLS_CC);
359413
ensure_gridfs_index(&temp, chunks TSRMLS_CC);
360414

361-
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|az", &bytes, &bytes_len, &extra, &options) == FAILURE) {
362-
return;
363-
}
364-
365-
if (!options) {
366-
zval *opts;
367-
MAKE_STD_ZVAL(opts);
368-
array_init(opts);
369-
options = opts;
370-
}
415+
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|aa/", &bytes, &bytes_len, &extra, &options) == FAILURE) {
416+
return;
417+
}
371418

372419
// file array object
373420
MAKE_STD_ZVAL(zfile);
@@ -383,14 +430,30 @@ PHP_METHOD(MongoGridFS, storeBytes) {
383430
add_assoc_long(zfile, "length", bytes_len);
384431
}
385432

433+
// options
434+
if (!options) {
435+
zval *opts;
436+
MAKE_STD_ZVAL(opts);
437+
array_init(opts);
438+
options = opts;
439+
free_options = 1;
440+
}
441+
442+
// force safe mode
443+
add_assoc_long(options, "safe", 1);
444+
386445
// insert chunks
387446
while (pos < bytes_len) {
388447
chunk_size = bytes_len-pos >= global_chunk_size ? global_chunk_size : bytes_len-pos;
389448

390-
insert_chunk(chunks, zid, chunk_num, bytes+pos, chunk_size, options TSRMLS_CC);
391-
if (EG(exception)) {
392-
return;
393-
}
449+
if (insert_chunk(chunks, zid, chunk_num, bytes+pos, chunk_size, options TSRMLS_CC) == FAILURE) {
450+
revert = 1;
451+
goto cleanup_on_failure;
452+
}
453+
if (EG(exception)) {
454+
revert = 1;
455+
goto cleanup_on_failure;
456+
}
394457

395458
// increment counters
396459
pos += chunk_size;
@@ -402,11 +465,25 @@ PHP_METHOD(MongoGridFS, storeBytes) {
402465

403466
// insert file
404467
MONGO_METHOD2(MongoCollection, insert, &temp, getThis(), zfile, options);
405-
406-
zval_add_ref(&zid);
407-
zval_ptr_dtor(&zfile);
408-
409-
RETURN_ZVAL(zid, 1, 1);
468+
zval_dtor(&temp);
469+
if (EG(exception)) {
470+
revert = 1;
471+
}
472+
473+
cleanup_on_failure:
474+
if (!revert) {
475+
RETVAL_ZVAL(zid, 1, 1);
476+
} else {
477+
cleanup_broken_insert(INTERNAL_FUNCTION_PARAM_PASSTHRU, zid TSRMLS_CC);
478+
RETVAL_FALSE;
479+
}
480+
481+
zval_add_ref(&zid);
482+
zval_ptr_dtor(&zfile);
483+
484+
if (free_options) {
485+
zval_ptr_dtor(&options);
486+
}
410487
}
411488

412489
/* add extra fields required for files:
@@ -454,7 +531,7 @@ static int setup_file_fields(zval *zfile, char *filename, int size TSRMLS_DC) {
454531
* - buf
455532
*/
456533
static int insert_chunk(zval *chunks, zval *zid, int chunk_num, char *buf, int chunk_size, zval *options TSRMLS_DC) {
457-
zval temp;
534+
zval temp;
458535
zval *zchunk, *zbin;
459536

460537
// create chunk
@@ -480,6 +557,7 @@ static int insert_chunk(zval *chunks, zval *zid, int chunk_num, char *buf, int c
480557
else {
481558
MONGO_METHOD1(MongoCollection, insert, &temp, chunks, zchunk);
482559
}
560+
zval_dtor(&temp);
483561

484562
// increment counters
485563
zval_ptr_dtor(&zchunk); // zid->refcount = 1
@@ -496,6 +574,7 @@ PHP_METHOD(MongoGridFS, storeFile) {
496574
zval *fh, *extra = 0, *options = 0;
497575
char *filename = 0;
498576
int chunk_num = 0, global_chunk_size = 0, size = 0, pos = 0, fd = -1, safe = 0;
577+
int free_options = 0, revert = 0;
499578
FILE *fp = 0;
500579

501580
zval temp;
@@ -507,16 +586,9 @@ PHP_METHOD(MongoGridFS, storeFile) {
507586

508587
ensure_gridfs_index(&temp, chunks TSRMLS_CC);
509588

510-
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|az", &fh, &extra, &options) == FAILURE) {
511-
return;
512-
}
513-
514-
if (!options) {
515-
zval *opts;
516-
MAKE_STD_ZVAL(opts);
517-
array_init(opts);
518-
options = opts;
519-
}
589+
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|aa/", &fh, &extra, &options) == FAILURE) {
590+
return;
591+
}
520592

521593
if (Z_TYPE_P(fh) == IS_RESOURCE) {
522594
zend_rsrc_list_entry *le;
@@ -577,6 +649,18 @@ PHP_METHOD(MongoGridFS, storeFile) {
577649
// chunkSize
578650
global_chunk_size = get_chunk_size(zfile TSRMLS_CC);
579651

652+
// options
653+
if (!options) {
654+
zval *opts;
655+
MAKE_STD_ZVAL(opts);
656+
array_init(opts);
657+
options = opts;
658+
free_options = 1;
659+
}
660+
661+
// force safe mode
662+
add_assoc_long(options, "safe", 1);
663+
580664
// insert chunks
581665
while (pos < size || fp == 0) {
582666
int result = 0;
@@ -588,32 +672,41 @@ PHP_METHOD(MongoGridFS, storeFile) {
588672
if (fp) {
589673
if ((int)fread(buf, 1, chunk_size, fp) < chunk_size) {
590674
zend_throw_exception_ex(mongo_ce_GridFSException, 0 TSRMLS_CC, "error reading file %s", filename);
591-
return;
675+
revert = 1;
676+
efree(buf);
677+
goto cleanup_on_failure;
592678
}
593679
pos += chunk_size;
594680
if (insert_chunk(chunks, zid, chunk_num, buf, chunk_size, options TSRMLS_CC) == FAILURE) {
595-
break;
681+
revert = 1;
682+
efree(buf);
683+
goto cleanup_on_failure;
596684
}
597685
}
598686
else {
599687
result = read(fd, buf, chunk_size);
600688
if (result == -1) {
601689
zend_throw_exception_ex(mongo_ce_GridFSException, 0 TSRMLS_CC, "error reading filehandle");
602-
return;
690+
revert = 1;
691+
efree(buf);
692+
goto cleanup_on_failure;
603693
}
604694
pos += result;
605695
if (insert_chunk(chunks, zid, chunk_num, buf, result, options TSRMLS_CC) == FAILURE) {
606-
break;
696+
revert = 1;
697+
efree(buf);
698+
goto cleanup_on_failure;
607699
}
608700
}
609701

610-
if (safe && EG(exception)) {
611-
return;
612-
}
702+
efree(buf);
613703

614-
chunk_num++;
704+
if (safe && EG(exception)) {
705+
revert = 1;
706+
goto cleanup_on_failure;
707+
}
615708

616-
efree(buf);
709+
chunk_num++;
617710

618711
if (fp == 0 && result < chunk_size) {
619712
break;
@@ -626,8 +719,7 @@ PHP_METHOD(MongoGridFS, storeFile) {
626719
}
627720

628721
if (EG(exception)) {
629-
zval_ptr_dtor(&zfile);
630-
return;
722+
goto cleanup_on_failure;
631723
}
632724

633725
if (!fp) {
@@ -636,14 +728,39 @@ PHP_METHOD(MongoGridFS, storeFile) {
636728

637729
add_md5(zfile, zid, c TSRMLS_CC);
638730

639-
// insert file
640-
MONGO_METHOD2(MongoCollection, insert, &temp, getThis(), zfile, options);
641-
642-
// cleanup
643-
zval_add_ref(&zid);
644-
zval_ptr_dtor(&zfile);
645-
646-
RETURN_ZVAL(zid, 1, 1);
731+
// insert file
732+
if (!revert) {
733+
zval *temp_return;
734+
735+
Z_ADDREF_P(options);
736+
MAKE_STD_ZVAL(temp_return);
737+
ZVAL_NULL(temp_return);
738+
MONGO_METHOD2(MongoCollection, insert, temp_return, getThis(), zfile, options);
739+
zval_ptr_dtor(&temp_return);
740+
Z_DELREF_P(options);
741+
if (EG(exception)) {
742+
revert = 1;
743+
}
744+
}
745+
746+
if (!revert) {
747+
RETVAL_ZVAL(zid, 1, 1);
748+
}
749+
750+
cleanup_on_failure:
751+
// remove all inserted chunks and main file document
752+
if (revert) {
753+
cleanup_broken_insert(INTERNAL_FUNCTION_PARAM_PASSTHRU, zid TSRMLS_DC);
754+
RETVAL_FALSE;
755+
}
756+
757+
// cleanup
758+
zval_add_ref(&zid);
759+
zval_ptr_dtor(&zfile);
760+
761+
if (free_options) {
762+
zval_ptr_dtor(&options);
763+
}
647764
}
648765

649766
PHP_METHOD(MongoGridFS, findOne) {
@@ -826,21 +943,25 @@ PHP_METHOD(MongoGridFS, storeUpload) {
826943
* New GridFS API
827944
*/
828945

829-
PHP_METHOD(MongoGridFS, delete) {
830-
zval *id, *criteria;
946+
PHP_METHOD(MongoGridFS, delete)
947+
{
948+
zval *id, *criteria;
949+
char *str_id;
950+
size_t str_len;
831951

832-
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &id, mongo_ce_Id) == FAILURE) {
833-
return;
834-
}
952+
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &id) == FAILURE) {
953+
return;
954+
}
835955

836-
MAKE_STD_ZVAL(criteria);
837-
array_init(criteria);
838-
add_assoc_zval(criteria, "_id", id);
839-
zval_add_ref(&id);
956+
// Set up criteria array
957+
MAKE_STD_ZVAL(criteria);
958+
array_init(criteria);
959+
add_assoc_zval(criteria, "_id", id);
960+
zval_add_ref(&id);
840961

841-
MONGO_METHOD1(MongoGridFS, remove, return_value, getThis(), criteria);
962+
MONGO_METHOD1(MongoGridFS, remove, return_value, getThis(), criteria);
842963

843-
zval_ptr_dtor(&criteria);
964+
zval_ptr_dtor(&criteria);
844965
}
845966

846967
PHP_METHOD(MongoGridFS, get) {

0 commit comments

Comments
 (0)