diff --git a/appveyor.yml b/appveyor.yml index 83fce717..15a2962d 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -30,7 +30,7 @@ environment: SQL_INSTANCE: SQL2019 PHP_VC: vs16 PHP_MAJOR_VER: 8.0 - PHP_MINOR_VER: 0beta4 + PHP_MINOR_VER: 0rc1 PHP_EXE_PATH: Release THREAD: nts platform: x86 diff --git a/source/shared/core_stmt.cpp b/source/shared/core_stmt.cpp index ad33e572..4da01dfb 100644 --- a/source/shared/core_stmt.cpp +++ b/source/shared/core_stmt.cpp @@ -1938,7 +1938,11 @@ void core_get_field_common( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT field_i throw core::CoreException(); } - stream = php_stream_open_wrapper( "sqlsrv://sqlncli10", "r", 0, NULL ); + // For a sqlsrv stream, only REPORT_ERRORS may be used. For "mode", the 'b' option + // is ignored on POSIX systems, which treat text and binary files the same. Yet, the + // 'b' option might be important in other systems. + // For details check https://www.php.net/manual/en/internals2.ze1.streams.php + stream = php_stream_open_wrapper("sqlsrv://sqlncli10", "rb", REPORT_ERRORS, NULL); CHECK_CUSTOM_ERROR( !stream, stmt, SQLSRV_ERROR_STREAM_CREATE ) { throw core::CoreException(); diff --git a/source/shared/core_stream.cpp b/source/shared/core_stream.cpp index 72b22b61..05f4224d 100644 --- a/source/shared/core_stream.cpp +++ b/source/shared/core_stream.cpp @@ -238,9 +238,12 @@ static php_stream* sqlsrv_stream_opener( _In_opt_ php_stream_wrapper* wrapper, _ ss = static_cast( sqlsrv_malloc( sizeof( sqlsrv_stream ))); memset( ss, 0, sizeof( sqlsrv_stream )); - // check for valid options - if( options != REPORT_ERRORS ) { - php_stream_wrapper_log_error( wrapper, options, "Invalid option: no options except REPORT_ERRORS may be specified with a sqlsrv stream" ); + // The function core_get_field_common() is changed to pass REPORT_ERRORS for + // php_stream_open_wrapper(). Whether the error flag is toggled or cleared, + // the argument "options" will be zero. + // For details check this pull request: https://github.com/php/php-src/pull/6190 + if (options != 0) { + php_stream_wrapper_log_error(wrapper, options, "Invalid option: no options except REPORT_ERRORS may be specified with a sqlsrv stream"); return NULL; } diff --git a/source/sqlsrv/init.cpp b/source/sqlsrv/init.cpp index cd2220a2..4cc02597 100644 --- a/source/sqlsrv/init.cpp +++ b/source/sqlsrv/init.cpp @@ -98,6 +98,8 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX( sqlsrv_fetch_arginfo, 0, 0, 1 ) ZEND_ARG_INFO( 0, stmt ) + ZEND_ARG_INFO( 0, row ) + ZEND_ARG_INFO( 0, offset ) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX( sqlsrv_fetch_array_arginfo, 0, 0, 1 ) diff --git a/test/functional/pdo_sqlsrv/pdo_connection_quote.phpt b/test/functional/pdo_sqlsrv/pdo_connection_quote.phpt index d3865d0a..26b39fcd 100644 --- a/test/functional/pdo_sqlsrv/pdo_connection_quote.phpt +++ b/test/functional/pdo_sqlsrv/pdo_connection_quote.phpt @@ -19,11 +19,23 @@ try { $output3 = $conn->quote("The quick brown fox jumps over the lazy dog0123456789"); var_dump($output3); - $stmt = $conn->query(""); - if ($stmt != false) { - echo("Empty query was expected to fail!\n"); + if (PHP_MAJOR_VERSION < 8) { + $stmt = $conn->query(""); + if ($stmt != false) { + echo("Empty query was expected to fail!\n"); + } + unset($stmt); + } else { + try { + $stmt = $conn->query(""); + echo("Empty query was expected to fail!\n"); + } catch (ValueError $ve) { + $error = '*PDO::query(): Argument #1 ($query) cannot be empty'; + if (!fnmatch($error, $ve->getMessage())) { + var_dump($ve->getMessage()); + } + } } - unset($stmt); $stmt1 = $conn->prepare($output2); $result = $stmt1->execute(); diff --git a/test/functional/pdo_sqlsrv/pdo_fetch_column_twice.phpt b/test/functional/pdo_sqlsrv/pdo_fetch_column_twice.phpt index 7e2ec194..692b737b 100644 --- a/test/functional/pdo_sqlsrv/pdo_fetch_column_twice.phpt +++ b/test/functional/pdo_sqlsrv/pdo_fetch_column_twice.phpt @@ -88,21 +88,42 @@ function fetchColumnOutOfBound1($conn, $tableName, $col) echo "Error message unexpected in fetchColumnOutOfBound1\n"; var_dump($e->getMessage()); } + } catch (ValueError $ve) { + $error = '*Column index must be greater than or equal to 0'; + if (!fnmatch($error, $ve->getMessage())) { + echo "Error message unexpected in fetchColumnOutOfBound1\n"; + var_dump($ve->getMessage()); + } } } function fetchColumnOutOfBound2($conn, $tableName, $col) { + $error = '*Invalid column index'; try { $tsql = "SELECT * FROM $tableName"; $stmt = $conn->query($tsql, PDO::FETCH_COLUMN, $col); $result = $stmt->fetch(); unset($stmt); - } catch (PDOException $e) { - var_dump($e->getMessage()); + } catch (Error $e) { + if (!fnmatch($error, $e->getMessage())) { + var_dump($e->getMessage()); + } + } catch (ValueError $ve) { + if (!fnmatch($error, $ve->getMessage())) { + var_dump($ve->getMessage()); + } } } +// When testing with PHP 8.0 some test cases throw ValueError instead of exceptions or warnings. +// Thus implement a custom warning handler such that with PHP 7.x the warning would be handled +// to throw an Error (ValueError not available). +function warningHandler($errno, $errstr) +{ + throw new Error($errstr); +} + try { $conn = connect(); $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); @@ -135,7 +156,9 @@ try { // Change to warning mode $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING); + set_error_handler("warningHandler", E_WARNING); fetchColumnOutOfBound2($conn, $tableName, $numCols + 1); + restore_error_handler(); dropTable($conn, $tableName); unset($conn); @@ -144,8 +167,5 @@ try { var_dump($e); } ?> ---EXPECTREGEX-- -Warning: PDOStatement::fetch\(\): SQLSTATE\[HY000\]: General error: Invalid column index in .+(\/|\\)pdo_fetch_column_twice.php on line [0-9]+ - -Warning: PDOStatement::fetch\(\): SQLSTATE\[HY000\]: General error in .+(\/|\\)pdo_fetch_column_twice.php on line [0-9]+ +--EXPECT-- Done diff --git a/test/functional/pdo_sqlsrv/pdostatement_getColumnMeta.phpt b/test/functional/pdo_sqlsrv/pdostatement_getColumnMeta.phpt index 40805dc2..6493bf94 100644 --- a/test/functional/pdo_sqlsrv/pdostatement_getColumnMeta.phpt +++ b/test/functional/pdo_sqlsrv/pdostatement_getColumnMeta.phpt @@ -49,22 +49,43 @@ function fetchBoth($conn, $tbname) unset($meta["sqlsrv:decl_type"]); var_dump($meta); - // Test invalid arguments, set error mode to silent to reduce the amount of error messages generated + // Test invalid arguments, set error mode to silent to reduce the number of error messages generated $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT); - // Test negative column number, ignore the error messages - $meta = $stmt->getColumnMeta(-1); - var_dump($meta); + // Test negative column number + try { + $meta = $stmt->getColumnMeta(-1); + echo "Expect getColumnMeta to fail with -1\n"; + } catch (Error $e) { + if (PHP_MAJOR_VERSION == 8) { + $error = '*PDOStatement::getColumnMeta(): Argument #1 ($index) must be greater than or equal to 0*'; + } else { + $error = '*Invalid column reference: column number must be non-negative*'; + } + if (!fnmatch($error, $e->getMessage())) { + echo "Unexpected error:"; + var_dump($e->getMessage()); + } + } // Test non-existent column number $meta = $stmt->getColumnMeta(10); var_dump($meta); } +// When testing with PHP 8.0 the negative test case throws an Error instead of a warning. +// Implement a custom warning handler such that with PHP 7.x the warning would be handled +// to throw an Error. +function warningHandler($errno, $errstr) +{ + throw new Error($errstr); +} + try { $db = connect(); $tbname = "PDO_MainTypes"; createAndInsertTableMainTypes($db, $tbname); + set_error_handler("warningHandler", E_WARNING); fetchBoth($db, $tbname); } catch (PDOException $e) { var_dump($e); @@ -73,7 +94,7 @@ try { ?> ---EXPECTF-- +--EXPECT-- array(8) { ["flags"]=> @@ -217,7 +238,4 @@ array(7) { ["precision"]=> int(0) } - -Warning: PDOStatement::getColumnMeta(): SQLSTATE[42P10]: Invalid column reference: column number must be non-negative in %s on line %x -bool(false) bool(false) diff --git a/test/functional/pdo_sqlsrv/pdostatement_getColumnMeta_unicode_col_name.phpt b/test/functional/pdo_sqlsrv/pdostatement_getColumnMeta_unicode_col_name.phpt index e7075ee4..2aba3fa9 100644 --- a/test/functional/pdo_sqlsrv/pdostatement_getColumnMeta_unicode_col_name.phpt +++ b/test/functional/pdo_sqlsrv/pdostatement_getColumnMeta_unicode_col_name.phpt @@ -57,15 +57,35 @@ function fetchBoth($conn, $tbname) // Test invalid arguments, set error mode to silent to reduce the amount of error messages generated $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT); - // Test negative column number, ignore the error messages - $meta = $stmt->getColumnMeta(-1); - var_dump($meta); + // Test negative column number + try { + $meta = $stmt->getColumnMeta(-1); + echo "Expect getColumnMeta to fail with -1\n"; + } catch (Error $e) { + if (PHP_MAJOR_VERSION == 8) { + $error = '*PDOStatement::getColumnMeta(): Argument #1 ($index) must be greater than or equal to 0*'; + } else { + $error = '*Invalid column reference: column number must be non-negative*'; + } + if (!fnmatch($error, $e->getMessage())) { + echo "Unexpected error:"; + var_dump($e->getMessage()); + } + } // Test non-existent column number $meta = $stmt->getColumnMeta(10); var_dump($meta); } +// When testing with PHP 8.0 the negative test case throws an Error instead of a warning. +// Implement a custom warning handler such that with PHP 7.x the warning would be handled +// to throw an Error. +function warningHandler($errno, $errstr) +{ + throw new Error($errstr); +} + function createAndInsertTableUnicode($conn, $tbname) { try { @@ -106,13 +126,14 @@ try { $db = connect(); $tbname = "PDO_MainTypes"; createAndInsertTableUnicode($db, $tbname); + set_error_handler("warningHandler", E_WARNING); fetchBoth($db, $tbname); } catch (PDOException $e) { var_dump($e); exit; } ?> ---EXPECTF-- +--EXPECT-- array(8) { ["flags"]=> @@ -256,7 +277,4 @@ array(7) { ["precision"]=> int(0) } - -Warning: PDOStatement::getColumnMeta(): SQLSTATE[42P10]: Invalid column reference: column number must be non-negative in %s on line %x -bool(false) bool(false) \ No newline at end of file