Merge pull request #592 from yitam/nextResult
Fix to GitHub issues 574 and 580
This commit is contained in:
commit
04f50341bc
|
@ -45,7 +45,7 @@ namespace {
|
||||||
|
|
||||||
// *** internal constants ***
|
// *** internal constants ***
|
||||||
|
|
||||||
const int INITIAL_FIELD_STRING_LEN = 256; // base allocation size when retrieving a string field
|
const int INITIAL_LOB_FIELD_LEN = 2048; // base allocation size when retrieving a LOB field
|
||||||
|
|
||||||
// *** internal functions ***
|
// *** internal functions ***
|
||||||
|
|
||||||
|
@ -1520,9 +1520,9 @@ SQLPOINTER read_lob_field( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT field_in
|
||||||
}
|
}
|
||||||
|
|
||||||
SQLLEN already_read = 0;
|
SQLLEN already_read = 0;
|
||||||
SQLLEN to_read = INITIAL_FIELD_STRING_LEN;
|
SQLLEN to_read = INITIAL_LOB_FIELD_LEN;
|
||||||
sqlsrv_malloc_auto_ptr<char> buffer;
|
sqlsrv_malloc_auto_ptr<char> buffer;
|
||||||
buffer = static_cast<char*>( sqlsrv_malloc( INITIAL_FIELD_STRING_LEN + extra + sizeof( SQLULEN )));
|
buffer = static_cast<char*>( sqlsrv_malloc( INITIAL_LOB_FIELD_LEN + extra + sizeof( SQLULEN )));
|
||||||
SQLRETURN r = SQL_SUCCESS;
|
SQLRETURN r = SQL_SUCCESS;
|
||||||
SQLCHAR state[ SQL_SQLSTATE_BUFSIZE ];
|
SQLCHAR state[ SQL_SQLSTATE_BUFSIZE ];
|
||||||
SQLLEN last_field_len = 0;
|
SQLLEN last_field_len = 0;
|
||||||
|
|
|
@ -1357,30 +1357,31 @@ struct sqlsrv_stmt : public sqlsrv_context {
|
||||||
bool past_fetch_end; // Core_sqlsrv_fetch sets this field when the statement goes beyond the last row
|
bool past_fetch_end; // Core_sqlsrv_fetch sets this field when the statement goes beyond the last row
|
||||||
sqlsrv_result_set* current_results; // Current result set
|
sqlsrv_result_set* current_results; // Current result set
|
||||||
SQLULEN cursor_type; // Type of cursor for the current result set
|
SQLULEN cursor_type; // Type of cursor for the current result set
|
||||||
int fwd_row_index; // fwd_row_index is the current row index, SQL_CURSOR_FORWARD_ONLY
|
int fwd_row_index; // fwd_row_index is the current row index, SQL_CURSOR_FORWARD_ONLY
|
||||||
|
int curr_result_set; // the current active result set, 0 by default but will be incremented by core_sqlsrv_next_result
|
||||||
bool has_rows; // Has_rows is set if there are actual rows in the row set
|
bool has_rows; // Has_rows is set if there are actual rows in the row set
|
||||||
bool fetch_called; // Used by core_sqlsrv_get_field to return an informative error if fetch not yet called
|
bool fetch_called; // Used by core_sqlsrv_get_field to return an informative error if fetch not yet called
|
||||||
int last_field_index; // last field retrieved by core_sqlsrv_get_field
|
int last_field_index; // last field retrieved by core_sqlsrv_get_field
|
||||||
bool past_next_result_end; // core_sqlsrv_next_result sets this to true when the statement goes beyond the
|
bool past_next_result_end; // core_sqlsrv_next_result sets this to true when the statement goes beyond the
|
||||||
// last results
|
// last results
|
||||||
unsigned long query_timeout; // maximum allowed statement execution time
|
unsigned long query_timeout; // maximum allowed statement execution time
|
||||||
zend_long buffered_query_limit; // maximum allowed memory for a buffered query (measured in KB)
|
zend_long buffered_query_limit; // maximum allowed memory for a buffered query (measured in KB)
|
||||||
|
|
||||||
// holds output pointers for SQLBindParameter
|
// holds output pointers for SQLBindParameter
|
||||||
// We use a deque because it 1) provides the at/[] access in constant time, and 2) grows dynamically without moving
|
// We use a deque because it 1) provides the at/[] access in constant time, and 2) grows dynamically without moving
|
||||||
// memory, which is important because we pass the pointer to an element of the deque to SQLBindParameter to hold
|
// memory, which is important because we pass the pointer to an element of the deque to SQLBindParameter to hold
|
||||||
std::deque<SQLLEN> param_ind_ptrs; // output pointers for lengths for calls to SQLBindParameter
|
std::deque<SQLLEN> param_ind_ptrs; // output pointers for lengths for calls to SQLBindParameter
|
||||||
zval param_input_strings; // hold all UTF-16 input strings that aren't managed by PHP
|
zval param_input_strings; // hold all UTF-16 input strings that aren't managed by PHP
|
||||||
zval output_params; // hold all the output parameters
|
zval output_params; // hold all the output parameters
|
||||||
zval param_streams; // track which streams to send data to the server
|
zval param_streams; // track which streams to send data to the server
|
||||||
zval param_datetime_buffers; // datetime strings to be converted back to DateTime objects
|
zval param_datetime_buffers; // datetime strings to be converted back to DateTime objects
|
||||||
bool send_streams_at_exec; // send all stream data right after execution before returning
|
bool send_streams_at_exec; // send all stream data right after execution before returning
|
||||||
sqlsrv_stream current_stream; // current stream sending data to the server as an input parameter
|
sqlsrv_stream current_stream; // current stream sending data to the server as an input parameter
|
||||||
unsigned int current_stream_read; // # of bytes read so far. (if we read an empty PHP stream, we send an empty string
|
unsigned int current_stream_read; // # of bytes read so far. (if we read an empty PHP stream, we send an empty string
|
||||||
// to the server)
|
// to the server)
|
||||||
zval field_cache; // cache for a single row of fields, to allow multiple and out of order retrievals
|
zval field_cache; // cache for a single row of fields, to allow multiple and out of order retrievals
|
||||||
zval col_cache; // Used by get_field_as_string not to call SQLColAttribute() after every fetch.
|
zval col_cache; // Used by get_field_as_string not to call SQLColAttribute() after every fetch.
|
||||||
zval active_stream; // the currently active stream reading data from the database
|
zval active_stream; // the currently active stream reading data from the database
|
||||||
|
|
||||||
sqlsrv_stmt( _In_ sqlsrv_conn* c, _In_ SQLHANDLE handle, _In_ error_callback e, _In_opt_ void* drv TSRMLS_DC );
|
sqlsrv_stmt( _In_ sqlsrv_conn* c, _In_ SQLHANDLE handle, _In_ error_callback e, _In_opt_ void* drv TSRMLS_DC );
|
||||||
virtual ~sqlsrv_stmt( void );
|
virtual ~sqlsrv_stmt( void );
|
||||||
|
|
|
@ -60,7 +60,8 @@ struct col_cache {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const int INITIAL_FIELD_STRING_LEN = 256; // base allocation size when retrieving a string field
|
const int INITIAL_FIELD_STRING_LEN = 2048; // base allocation size when retrieving a string field
|
||||||
|
const int INITIAL_AE_FIELD_STRING_LEN = 8000; // base allocation size when retrieving a string field when AE is enabled
|
||||||
|
|
||||||
// UTF-8 tags for byte length of characters, used by streams to make sure we don't clip a character in between reads
|
// UTF-8 tags for byte length of characters, used by streams to make sure we don't clip a character in between reads
|
||||||
const unsigned int UTF8_MIDBYTE_MASK = 0xc0;
|
const unsigned int UTF8_MIDBYTE_MASK = 0xc0;
|
||||||
|
@ -135,6 +136,7 @@ sqlsrv_stmt::sqlsrv_stmt( _In_ sqlsrv_conn* c, _In_ SQLHANDLE handle, _In_ error
|
||||||
current_results( NULL ),
|
current_results( NULL ),
|
||||||
cursor_type( SQL_CURSOR_FORWARD_ONLY ),
|
cursor_type( SQL_CURSOR_FORWARD_ONLY ),
|
||||||
fwd_row_index( -1 ),
|
fwd_row_index( -1 ),
|
||||||
|
curr_result_set( 0 ),
|
||||||
has_rows( false ),
|
has_rows( false ),
|
||||||
fetch_called( false ),
|
fetch_called( false ),
|
||||||
last_field_index( -1 ),
|
last_field_index( -1 ),
|
||||||
|
@ -1138,9 +1140,13 @@ void core_sqlsrv_next_result( _Inout_ sqlsrv_stmt* stmt TSRMLS_DC, _In_ bool fin
|
||||||
|
|
||||||
// mark we are past the end of all results
|
// mark we are past the end of all results
|
||||||
stmt->past_next_result_end = true;
|
stmt->past_next_result_end = true;
|
||||||
|
|
||||||
|
// reset the current active result set
|
||||||
|
stmt->curr_result_set = 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
stmt->curr_result_set++;
|
||||||
stmt->new_result_set( TSRMLS_C );
|
stmt->new_result_set( TSRMLS_C );
|
||||||
}
|
}
|
||||||
catch( core::CoreException& e ) {
|
catch( core::CoreException& e ) {
|
||||||
|
@ -2236,6 +2242,7 @@ void get_field_as_string( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT field_ind
|
||||||
SQLLEN field_len_temp = 0;
|
SQLLEN field_len_temp = 0;
|
||||||
SQLLEN sql_display_size = 0;
|
SQLLEN sql_display_size = 0;
|
||||||
char* field_value_temp = NULL;
|
char* field_value_temp = NULL;
|
||||||
|
unsigned int intial_field_len = INITIAL_FIELD_STRING_LEN;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
|
@ -2262,6 +2269,11 @@ void get_field_as_string( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT field_ind
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if( stmt->conn->ce_option.enabled ) {
|
||||||
|
// when AE is enabled, increase the intial field len
|
||||||
|
intial_field_len = INITIAL_AE_FIELD_STRING_LEN;
|
||||||
|
}
|
||||||
|
|
||||||
col_cache* cached = NULL;
|
col_cache* cached = NULL;
|
||||||
if ( NULL != ( cached = static_cast< col_cache* >( zend_hash_index_find_ptr( Z_ARRVAL( stmt->col_cache ), static_cast< zend_ulong >( field_index ))))) {
|
if ( NULL != ( cached = static_cast< col_cache* >( zend_hash_index_find_ptr( Z_ARRVAL( stmt->col_cache ), static_cast< zend_ulong >( field_index ))))) {
|
||||||
sql_field_type = cached->sql_type;
|
sql_field_type = cached->sql_type;
|
||||||
|
@ -2282,7 +2294,7 @@ void get_field_as_string( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT field_ind
|
||||||
if( sql_display_size == 0 || sql_display_size == INT_MAX ||
|
if( sql_display_size == 0 || sql_display_size == INT_MAX ||
|
||||||
sql_display_size == INT_MAX >> 1 || sql_display_size == UINT_MAX - 1 ) {
|
sql_display_size == INT_MAX >> 1 || sql_display_size == UINT_MAX - 1 ) {
|
||||||
|
|
||||||
field_len_temp = INITIAL_FIELD_STRING_LEN;
|
field_len_temp = intial_field_len;
|
||||||
|
|
||||||
SQLLEN initiallen = field_len_temp + extra;
|
SQLLEN initiallen = field_len_temp + extra;
|
||||||
|
|
||||||
|
@ -2323,7 +2335,7 @@ void get_field_as_string( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT field_ind
|
||||||
if( field_len_temp == SQL_NO_TOTAL ) {
|
if( field_len_temp == SQL_NO_TOTAL ) {
|
||||||
|
|
||||||
// reset the field_len_temp
|
// reset the field_len_temp
|
||||||
field_len_temp = INITIAL_FIELD_STRING_LEN;
|
field_len_temp = intial_field_len;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
SQLLEN initial_field_len = field_len_temp;
|
SQLLEN initial_field_len = field_len_temp;
|
||||||
|
@ -2378,14 +2390,14 @@ void get_field_as_string( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT field_ind
|
||||||
&dummy_field_len, false /*handle_warning*/ TSRMLS_CC );
|
&dummy_field_len, false /*handle_warning*/ TSRMLS_CC );
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// We have already recieved INITIAL_FIELD_STRING_LEN size data.
|
// We have already recieved intial_field_len size data.
|
||||||
field_len_temp -= INITIAL_FIELD_STRING_LEN;
|
field_len_temp -= intial_field_len;
|
||||||
|
|
||||||
// Get the rest of the data.
|
// Get the rest of the data.
|
||||||
r = stmt->current_results->get_data( field_index + 1, c_type, field_value_temp + INITIAL_FIELD_STRING_LEN,
|
r = stmt->current_results->get_data( field_index + 1, c_type, field_value_temp + intial_field_len,
|
||||||
field_len_temp + extra, &dummy_field_len,
|
field_len_temp + extra, &dummy_field_len,
|
||||||
true /*handle_warning*/ TSRMLS_CC );
|
true /*handle_warning*/ TSRMLS_CC );
|
||||||
field_len_temp += INITIAL_FIELD_STRING_LEN;
|
field_len_temp += intial_field_len;
|
||||||
}
|
}
|
||||||
|
|
||||||
if( dummy_field_len == SQL_NULL_DATA ) {
|
if( dummy_field_len == SQL_NULL_DATA ) {
|
||||||
|
@ -2655,6 +2667,12 @@ bool reset_ae_stream_cursor( _Inout_ sqlsrv_stmt* stmt ) {
|
||||||
// close and reopen the cursor
|
// close and reopen the cursor
|
||||||
core::SQLCloseCursor(stmt->current_results->odbc);
|
core::SQLCloseCursor(stmt->current_results->odbc);
|
||||||
core::SQLExecute(stmt);
|
core::SQLExecute(stmt);
|
||||||
|
|
||||||
|
// advance to the previous active result set
|
||||||
|
for (int j = 0; j < stmt->curr_result_set; j++) {
|
||||||
|
core::SQLMoreResults(stmt);
|
||||||
|
}
|
||||||
|
|
||||||
// FETCH_NEXT until the cursor reaches the row that it was at
|
// FETCH_NEXT until the cursor reaches the row that it was at
|
||||||
for (int i = 0; i <= stmt->fwd_row_index; i++) {
|
for (int i = 0; i <= stmt->fwd_row_index; i++) {
|
||||||
core::SQLFetchScroll(stmt->current_results->odbc, SQL_FETCH_NEXT, 0);
|
core::SQLFetchScroll(stmt->current_results->odbc, SQL_FETCH_NEXT, 0);
|
||||||
|
|
170
test/functional/pdo_sqlsrv/pdo_574_next_rowset.phpt
Normal file
170
test/functional/pdo_sqlsrv/pdo_574_next_rowset.phpt
Normal file
|
@ -0,0 +1,170 @@
|
||||||
|
--TEST--
|
||||||
|
GitHub issue 574 - Fetch Next Result Test
|
||||||
|
--DESCRIPTION--
|
||||||
|
Verifies the functionality of PDOStatement nextRowset
|
||||||
|
--ENV--
|
||||||
|
PHPT_EXEC=true
|
||||||
|
--SKIPIF--
|
||||||
|
<?php require('skipif_mid-refactor.inc'); ?>
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
require_once("MsCommon_mid-refactor.inc");
|
||||||
|
|
||||||
|
try {
|
||||||
|
$conn = connect();
|
||||||
|
$tableName = 'test574';
|
||||||
|
$tableName1 = 'test574_1';
|
||||||
|
|
||||||
|
// create two tables with max fields
|
||||||
|
$columns = array(new ColumnMeta('varchar(max)', 'col1'));
|
||||||
|
createTable($conn, $tableName, $columns);
|
||||||
|
|
||||||
|
$columns = array(new ColumnMeta('varchar(max)', 'col1'));
|
||||||
|
createTable($conn, $tableName1, $columns);
|
||||||
|
|
||||||
|
// insert one row to each table
|
||||||
|
$phrase = str_repeat('This is a test ', 25000);
|
||||||
|
$stmt = insertRow($conn, $tableName, array('col1' => $phrase));
|
||||||
|
unset($stmt);
|
||||||
|
|
||||||
|
$phrase1 = str_repeat('This is indeed very long ', 30000);
|
||||||
|
$stmt = insertRow($conn, $tableName1, array('col1' => $phrase1));
|
||||||
|
unset($stmt);
|
||||||
|
|
||||||
|
// run queries in a batch
|
||||||
|
$stmt = $conn->prepare("SELECT * FROM [$tableName]; SELECT artist FROM [cd_info]; SELECT * FROM [$tableName1]");
|
||||||
|
$stmt->execute();
|
||||||
|
|
||||||
|
// fetch from $tableName
|
||||||
|
$row = $stmt->fetch(PDO::FETCH_NUM);
|
||||||
|
if ($row) {
|
||||||
|
if ($row[0] === $phrase) {
|
||||||
|
echo(substr($row[0], 0, 15)) . PHP_EOL;
|
||||||
|
} else {
|
||||||
|
echo "Incorrect value substr($row[0], 0, 1000)...!" . PHP_EOL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fetch from cd_info
|
||||||
|
echo "1. next result? ";
|
||||||
|
$next = $stmt->nextRowset();
|
||||||
|
var_dump($next);
|
||||||
|
|
||||||
|
$row = $stmt->fetch(PDO::FETCH_NUM);
|
||||||
|
if ($row) {
|
||||||
|
echo $row[0] . PHP_EOL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// fetch from $tableName1
|
||||||
|
echo "2. next result? ";
|
||||||
|
$next = $stmt->nextRowset();
|
||||||
|
var_dump($next);
|
||||||
|
|
||||||
|
$row = $stmt->fetch(PDO::FETCH_NUM);
|
||||||
|
if ($row) {
|
||||||
|
if ($row[0] === $phrase1) {
|
||||||
|
echo(substr($row[0], 0, 25)) . PHP_EOL;
|
||||||
|
} else {
|
||||||
|
echo "Incorrect value substr($row[0], 0, 1000)...!" . PHP_EOL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// should be no more next results, first returns false second throws an exception
|
||||||
|
echo "3. next result? ";
|
||||||
|
$next = $stmt->nextRowset();
|
||||||
|
var_dump($next);
|
||||||
|
|
||||||
|
$row = $stmt->fetch(PDO::FETCH_NUM);
|
||||||
|
if ($row) {
|
||||||
|
fatalError("This is unexpected!\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "4. next result? " . PHP_EOL;
|
||||||
|
try {
|
||||||
|
$next = $stmt->nextRowset();
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
echo $e->getMessage() . PHP_EOL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// run queries in a batch again, different order this time
|
||||||
|
$stmt = $conn->prepare("SELECT * FROM [$tableName1]; SELECT * FROM [$tableName]; SELECT artist FROM [cd_info]");
|
||||||
|
$stmt->execute();
|
||||||
|
|
||||||
|
// skip the first two queries
|
||||||
|
$stmt->nextRowset();
|
||||||
|
$stmt->nextRowset();
|
||||||
|
|
||||||
|
// fetch from cd_info
|
||||||
|
$row = $stmt->fetch(PDO::FETCH_NUM);
|
||||||
|
if ($row) {
|
||||||
|
echo $row[0] . PHP_EOL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// re-execute the statement, should return to the first query in the batch
|
||||||
|
$stmt->execute();
|
||||||
|
|
||||||
|
// fetch from $tableName1
|
||||||
|
$row = $stmt->fetch(PDO::FETCH_NUM);
|
||||||
|
if ($row) {
|
||||||
|
if ($row[0] === $phrase1) {
|
||||||
|
echo(substr($row[0], 0, 25)) . PHP_EOL;
|
||||||
|
} else {
|
||||||
|
echo "Incorrect value substr($row[0], 0, 1000)...!" . PHP_EOL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unset($stmt);
|
||||||
|
|
||||||
|
// execute a simple query, no more batch
|
||||||
|
$stmt = $conn->query("SELECT * FROM [$tableName]");
|
||||||
|
|
||||||
|
// fetch from $tableName
|
||||||
|
$row = $stmt->fetch(PDO::FETCH_NUM);
|
||||||
|
if ($row) {
|
||||||
|
if ($row[0] === $phrase) {
|
||||||
|
echo(substr($row[0], 0, 15)) . PHP_EOL;
|
||||||
|
} else {
|
||||||
|
echo "Incorrect value substr($row[0], 0, 1000)...!" . PHP_EOL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// should be no more next results, first returns false second throws an exception
|
||||||
|
echo "5. next result? ";
|
||||||
|
$next = $stmt->nextRowset();
|
||||||
|
var_dump($next);
|
||||||
|
|
||||||
|
echo "6. next result? " . PHP_EOL;
|
||||||
|
try {
|
||||||
|
$next = $stmt->nextRowset();
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
echo $e->getMessage() . PHP_EOL;
|
||||||
|
}
|
||||||
|
|
||||||
|
dropTable($conn, $tableName);
|
||||||
|
dropTable($conn, $tableName1);
|
||||||
|
|
||||||
|
unset($stmt);
|
||||||
|
unset($conn);
|
||||||
|
|
||||||
|
echo "Done\n";
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
echo $e->getMessage();
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
|
||||||
|
--EXPECT--
|
||||||
|
This is a test
|
||||||
|
1. next result? bool(true)
|
||||||
|
Led Zeppelin
|
||||||
|
2. next result? bool(true)
|
||||||
|
This is indeed very long
|
||||||
|
3. next result? bool(false)
|
||||||
|
4. next result?
|
||||||
|
SQLSTATE[IMSSP]: There are no more results returned by the query.
|
||||||
|
Led Zeppelin
|
||||||
|
This is indeed very long
|
||||||
|
This is a test
|
||||||
|
5. next result? bool(false)
|
||||||
|
6. next result?
|
||||||
|
SQLSTATE[IMSSP]: There are no more results returned by the query.
|
||||||
|
Done
|
202
test/functional/sqlsrv/sqlsrv_574_next_result.phpt
Normal file
202
test/functional/sqlsrv/sqlsrv_574_next_result.phpt
Normal file
|
@ -0,0 +1,202 @@
|
||||||
|
--TEST--
|
||||||
|
GitHub issue 574 - Fetch Next Result Test
|
||||||
|
--DESCRIPTION--
|
||||||
|
Verifies the functionality of sqlsrv_next_result
|
||||||
|
--ENV--
|
||||||
|
PHPT_EXEC=true
|
||||||
|
--SKIPIF--
|
||||||
|
<?php require('skipif_versions_old.inc'); ?>
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
require_once('MsCommon.inc');
|
||||||
|
|
||||||
|
$conn = AE\connect();
|
||||||
|
$tableName = 'test574';
|
||||||
|
$tableName1 = 'test574_1';
|
||||||
|
|
||||||
|
// create two tables with max fields
|
||||||
|
$columns = array(new AE\ColumnMeta('varchar(max)', 'col1'));
|
||||||
|
$stmt = AE\createTable($conn, $tableName, $columns);
|
||||||
|
if (!$stmt) {
|
||||||
|
fatalError("Failed to create table for the test\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
$columns = array(new AE\ColumnMeta('varchar(max)', 'col1'));
|
||||||
|
$stmt = AE\createTable($conn, $tableName1, $columns);
|
||||||
|
if (!$stmt) {
|
||||||
|
fatalError("Failed to create table for the test\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// insert one row to each table
|
||||||
|
$sql = "insert into $tableName (col1) VALUES (?)";
|
||||||
|
$phrase = str_repeat('This is a test ', 25000);
|
||||||
|
|
||||||
|
$stmt = sqlsrv_prepare($conn, $sql, array($phrase));
|
||||||
|
if ($stmt) {
|
||||||
|
$r = sqlsrv_execute($stmt);
|
||||||
|
if (!$r) {
|
||||||
|
print_r(sqlsrv_errors());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$phrase1 = str_repeat('This is indeed very long ', 30000);
|
||||||
|
$sql = "insert into $tableName1 (col1) VALUES (?)";
|
||||||
|
$stmt = sqlsrv_prepare($conn, $sql, array($phrase1));
|
||||||
|
if ($stmt) {
|
||||||
|
$r = sqlsrv_execute($stmt);
|
||||||
|
if (!$r) {
|
||||||
|
print_r(sqlsrv_errors());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// run queries in a batch
|
||||||
|
$stmt = sqlsrv_prepare($conn, "SELECT * FROM [$tableName]; SELECT artist FROM [cd_info]; SELECT * FROM [$tableName1]");
|
||||||
|
if ($stmt) {
|
||||||
|
$r = sqlsrv_execute($stmt);
|
||||||
|
if (!$r) {
|
||||||
|
print_r(sqlsrv_errors());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fetch from $tableName
|
||||||
|
$row = sqlsrv_fetch($stmt);
|
||||||
|
if ($row) {
|
||||||
|
$fld = sqlsrv_get_field($stmt, 0, SQLSRV_PHPTYPE_STRING(SQLSRV_ENC_CHAR));
|
||||||
|
if ($fld === $phrase) {
|
||||||
|
echo(substr($fld, 0, 15)) . PHP_EOL;
|
||||||
|
} else {
|
||||||
|
echo "Incorrect value substr($fld, 0, 1000)...!" . PHP_EOL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fetch from cd_info
|
||||||
|
echo "1. next result? ";
|
||||||
|
$next = sqlsrv_next_result($stmt);
|
||||||
|
var_dump($next);
|
||||||
|
|
||||||
|
$row = sqlsrv_fetch($stmt);
|
||||||
|
if ($row) {
|
||||||
|
$fld = sqlsrv_get_field($stmt, 0, SQLSRV_PHPTYPE_STRING(SQLSRV_ENC_CHAR));
|
||||||
|
echo $fld . PHP_EOL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// fetch from $tableName1
|
||||||
|
echo "2. next result? ";
|
||||||
|
$next = sqlsrv_next_result($stmt);
|
||||||
|
var_dump($next);
|
||||||
|
|
||||||
|
$row = sqlsrv_fetch($stmt);
|
||||||
|
if ($row) {
|
||||||
|
$fld = sqlsrv_get_field($stmt, 0, SQLSRV_PHPTYPE_STRING(SQLSRV_ENC_CHAR));
|
||||||
|
if ($fld === $phrase1) {
|
||||||
|
echo(substr($fld, 0, 25)) . PHP_EOL;
|
||||||
|
} else {
|
||||||
|
echo "Incorrect value substr($fld, 0, 1000)...!" . PHP_EOL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// should be no more next results, first returns NULL second returns false
|
||||||
|
echo "3. next result? ";
|
||||||
|
$next = sqlsrv_next_result($stmt);
|
||||||
|
var_dump($next);
|
||||||
|
|
||||||
|
$row = sqlsrv_fetch($stmt);
|
||||||
|
if ($row) {
|
||||||
|
fatalError("This is unexpected!\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "4. next result? ";
|
||||||
|
$next = sqlsrv_next_result($stmt);
|
||||||
|
var_dump($next);
|
||||||
|
sqlsrv_free_stmt($stmt);
|
||||||
|
|
||||||
|
// run queries in a batch again, different order this time
|
||||||
|
$stmt = sqlsrv_prepare($conn, "SELECT * FROM [$tableName1]; SELECT * FROM [$tableName]; SELECT artist FROM [cd_info]");
|
||||||
|
if ($stmt) {
|
||||||
|
$r = sqlsrv_execute($stmt);
|
||||||
|
if (!$r) {
|
||||||
|
print_r(sqlsrv_errors());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// skip the first two queries
|
||||||
|
sqlsrv_next_result($stmt);
|
||||||
|
sqlsrv_next_result($stmt);
|
||||||
|
|
||||||
|
// fetch from cd_info
|
||||||
|
$row = sqlsrv_fetch($stmt);
|
||||||
|
if ($row) {
|
||||||
|
$fld = sqlsrv_get_field($stmt, 0, SQLSRV_PHPTYPE_STRING(SQLSRV_ENC_CHAR));
|
||||||
|
echo $fld . PHP_EOL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// re-execute the statement, should return to the first query in the batch
|
||||||
|
$r = sqlsrv_execute($stmt);
|
||||||
|
if (!$r) {
|
||||||
|
print_r(sqlsrv_errors());
|
||||||
|
}
|
||||||
|
|
||||||
|
// fetch from $tableName1
|
||||||
|
$row = sqlsrv_fetch($stmt);
|
||||||
|
if ($row) {
|
||||||
|
$fld = sqlsrv_get_field($stmt, 0, SQLSRV_PHPTYPE_STRING(SQLSRV_ENC_CHAR));
|
||||||
|
if ($fld === $phrase1) {
|
||||||
|
echo(substr($fld, 0, 25)) . PHP_EOL;
|
||||||
|
} else {
|
||||||
|
echo "Incorrect value substr($fld, 0, 1000)...!" . PHP_EOL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sqlsrv_free_stmt($stmt);
|
||||||
|
|
||||||
|
// execute a simple query, no more batch
|
||||||
|
$stmt = sqlsrv_prepare($conn, "SELECT * FROM [$tableName]");
|
||||||
|
if ($stmt) {
|
||||||
|
$r = sqlsrv_execute($stmt);
|
||||||
|
if (!$r) {
|
||||||
|
print_r(sqlsrv_errors());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fetch from $tableName
|
||||||
|
$row = sqlsrv_fetch($stmt);
|
||||||
|
if ($row) {
|
||||||
|
$fld = sqlsrv_get_field($stmt, 0, SQLSRV_PHPTYPE_STRING(SQLSRV_ENC_CHAR));
|
||||||
|
if ($fld === $phrase) {
|
||||||
|
echo(substr($fld, 0, 15)) . PHP_EOL;
|
||||||
|
} else {
|
||||||
|
echo "Incorrect value substr($fld, 0, 1000)...!" . PHP_EOL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// should be no more next results, first returns NULL second returns false
|
||||||
|
echo "5. next result? ";
|
||||||
|
$next = sqlsrv_next_result($stmt);
|
||||||
|
var_dump($next);
|
||||||
|
|
||||||
|
echo "6. next result? ";
|
||||||
|
$next = sqlsrv_next_result($stmt);
|
||||||
|
var_dump($next);
|
||||||
|
|
||||||
|
dropTable($conn, $tableName);
|
||||||
|
dropTable($conn, $tableName1);
|
||||||
|
|
||||||
|
sqlsrv_free_stmt($stmt);
|
||||||
|
sqlsrv_close($conn);
|
||||||
|
|
||||||
|
echo "Done\n";
|
||||||
|
?>
|
||||||
|
|
||||||
|
--EXPECT--
|
||||||
|
This is a test
|
||||||
|
1. next result? bool(true)
|
||||||
|
Led Zeppelin
|
||||||
|
2. next result? bool(true)
|
||||||
|
This is indeed very long
|
||||||
|
3. next result? NULL
|
||||||
|
4. next result? bool(false)
|
||||||
|
Led Zeppelin
|
||||||
|
This is indeed very long
|
||||||
|
This is a test
|
||||||
|
5. next result? NULL
|
||||||
|
6. next result? bool(false)
|
||||||
|
Done
|
Loading…
Reference in a new issue