26
26
#include "php_pdo_odbc.h"
27
27
#include "php_pdo_odbc_int.h"
28
28
29
+ /* Buffer size; bigger columns than this become a "long column" */
30
+ #define LONG_COLUMN_BUFFER_SIZE (ZEND_MM_PAGE_SIZE- ZSTR_MAX_OVERHEAD)
31
+
29
32
enum pdo_odbc_conv_result {
30
33
PDO_ODBC_CONV_NOT_REQUIRED ,
31
34
PDO_ODBC_CONV_OK ,
@@ -615,7 +618,7 @@ static int odbc_stmt_describe(pdo_stmt_t *stmt, int colno)
615
618
/* tell ODBC to put it straight into our buffer, but only if it
616
619
* isn't "long" data, and only if we haven't already bound a long
617
620
* column. */
618
- if (colsize < 256 && !S -> going_long ) {
621
+ if (colsize < LONG_COLUMN_BUFFER_SIZE && !S -> going_long ) {
619
622
S -> cols [colno ].data = emalloc (colsize + 1 );
620
623
S -> cols [colno ].is_long = 0 ;
621
624
@@ -631,7 +634,7 @@ static int odbc_stmt_describe(pdo_stmt_t *stmt, int colno)
631
634
} else {
632
635
/* allocate a smaller buffer to keep around for smaller
633
636
* "long" columns */
634
- S -> cols [colno ].data = emalloc (256 );
637
+ S -> cols [colno ].data = emalloc (LONG_COLUMN_BUFFER_SIZE );
635
638
S -> going_long = 1 ;
636
639
S -> cols [colno ].is_long = 1 ;
637
640
}
@@ -657,37 +660,57 @@ static int odbc_stmt_get_col(pdo_stmt_t *stmt, int colno, zval *result, enum pdo
657
660
RETCODE rc ;
658
661
659
662
/* fetch it into C->data, which is allocated with a length
660
- * of 256 bytes; if there is more to be had, we then allocate
663
+ * of the page size minus zend_string overhead (LONG_COLUMN_BUFFER_SIZE);
664
+ * if there is more to be had, we then allocate
661
665
* bigger buffer for the caller to free */
662
666
663
667
rc = SQLGetData (S -> stmt , colno + 1 , C -> is_unicode ? SQL_C_BINARY : SQL_C_CHAR , C -> data ,
664
- 256 , & C -> fetched_len );
668
+ LONG_COLUMN_BUFFER_SIZE , & C -> fetched_len );
665
669
orig_fetched_len = C -> fetched_len ;
666
670
667
- if (rc == SQL_SUCCESS && C -> fetched_len < 256 ) {
671
+ if (rc == SQL_SUCCESS && C -> fetched_len < LONG_COLUMN_BUFFER_SIZE ) {
668
672
/* all the data fit into our little buffer;
669
673
* jump down to the generic bound data case */
670
674
goto in_data ;
671
675
}
672
676
673
677
if (rc == SQL_SUCCESS_WITH_INFO || rc == SQL_SUCCESS ) {
674
- /* this is a 'long column'
675
-
676
- read the column in 255 byte blocks until the end of the column is reached, reassembling those blocks
677
- in order into the output buffer; 255 bytes are an optimistic assumption, since the driver may assert
678
- more or less NUL bytes at the end; we cater to that later, if actual length information is available
679
-
680
- this loop has to work whether or not SQLGetData() provides the total column length.
681
- calling SQLDescribeCol() or other, specifically to get the column length, then doing a single read
682
- for that size would be slower except maybe for extremely long columns.*/
683
- char * buf2 = emalloc (256 );
684
- zend_string * str = zend_string_init (C -> data , 256 , 0 );
685
- size_t used = 255 ; /* not 256; the driver NUL terminated the buffer */
678
+ /*
679
+ * This is a long column.
680
+ *
681
+ * Try to get as much as we can at once. If the
682
+ * driver somehow has more for us, get more. We'll
683
+ * assemble it into one big buffer at the end.
684
+ *
685
+ * N.B. with n and n+1 mentioned in the comments:
686
+ * n is the size returned without null terminator.
687
+ *
688
+ * The extension previously tried getting it in 256
689
+ * byte blocks, but this could have created trouble
690
+ * with some drivers.
691
+ *
692
+ * However, depending on the driver, fetched_len may
693
+ * not contain the number of bytes and SQL_NO_TOTAL
694
+ * may be passed.
695
+ * The behavior in this case is the same as before,
696
+ * dividing the data into blocks. However, it has been
697
+ * changed from 256 byte to LONG_COLUMN_BUFFER_SIZE.
698
+ */
699
+ ssize_t to_fetch_len ;
700
+ if (orig_fetched_len == SQL_NO_TOTAL ) {
701
+ to_fetch_len = C -> datalen > (LONG_COLUMN_BUFFER_SIZE - 1 ) ? (LONG_COLUMN_BUFFER_SIZE - 1 ) : C -> datalen ;
702
+ } else {
703
+ to_fetch_len = orig_fetched_len ;
704
+ }
705
+ ssize_t to_fetch_byte = to_fetch_len + 1 ;
706
+ char * buf2 = emalloc (to_fetch_byte );
707
+ zend_string * str = zend_string_init (C -> data , to_fetch_byte , 0 );
708
+ size_t used = to_fetch_len ;
686
709
687
710
do {
688
711
C -> fetched_len = 0 ;
689
- /* read block. 256 bytes => 255 bytes are actually read, the last 1 is NULL */
690
- rc = SQLGetData (S -> stmt , colno + 1 , C -> is_unicode ? SQL_C_BINARY : SQL_C_CHAR , buf2 , 256 , & C -> fetched_len );
712
+ /* read block. n + 1 bytes => n bytes are actually read, the last 1 is NULL */
713
+ rc = SQLGetData (S -> stmt , colno + 1 , C -> is_unicode ? SQL_C_BINARY : SQL_C_CHAR , buf2 , to_fetch_byte , & C -> fetched_len );
691
714
692
715
/* adjust `used` in case we have proper length info from the driver */
693
716
if (orig_fetched_len >= 0 && C -> fetched_len >= 0 ) {
@@ -698,13 +721,13 @@ static int odbc_stmt_get_col(pdo_stmt_t *stmt, int colno, zval *result, enum pdo
698
721
}
699
722
700
723
/* resize output buffer and reassemble block */
701
- if (rc == SQL_SUCCESS_WITH_INFO || (rc == SQL_SUCCESS && C -> fetched_len > 255 )) {
724
+ if (rc == SQL_SUCCESS_WITH_INFO || (rc == SQL_SUCCESS && C -> fetched_len > to_fetch_len )) {
702
725
/* point 5, in section "Retrieving Data with SQLGetData" in http://msdn.microsoft.com/en-us/library/windows/desktop/ms715441(v=vs.85).aspx
703
- states that if SQL_SUCCESS_WITH_INFO, fetched_len will be > 255 (greater than buf2's size)
704
- (if a driver fails to follow that and wrote less than 255 bytes to buf2, this will AV or read garbage into buf) */
705
- str = zend_string_realloc (str , used + 256 , 0 );
706
- memcpy (ZSTR_VAL (str ) + used , buf2 , 256 );
707
- used = used + 255 ;
726
+ states that if SQL_SUCCESS_WITH_INFO, fetched_len will be > n (greater than buf2's size)
727
+ (if a driver fails to follow that and wrote less than n bytes to buf2, this will AV or read garbage into buf) */
728
+ str = zend_string_realloc (str , used + to_fetch_byte , 0 );
729
+ memcpy (ZSTR_VAL (str ) + used , buf2 , to_fetch_byte );
730
+ used = used + to_fetch_len ;
708
731
} else if (rc == SQL_SUCCESS ) {
709
732
str = zend_string_realloc (str , used + C -> fetched_len , 0 );
710
733
memcpy (ZSTR_VAL (str ) + used , buf2 , C -> fetched_len );
0 commit comments