From fa4c0e8c4cc45ed4e547a99cc1ce9377250bd2f9 Mon Sep 17 00:00:00 2001 From: v-dareck Date: Tue, 31 Jan 2017 18:10:37 -0800 Subject: [PATCH] Merge remaining PHP-7.0-Linux changes into merged codebase. --- source/pdo_sqlsrv/config.m4 | 2 + source/pdo_sqlsrv/pdo_dbh.cpp | 15 +++- source/shared/core_conn.cpp | 81 ++++++++++++++---- source/shared/core_results.cpp | 8 +- source/shared/core_stmt.cpp | 42 +++++++--- source/shared/core_stream.cpp | 6 ++ source/shared/localizationimpl.cpp | 4 + source/sqlsrv/config.m4 | 1 + source/sqlsrv/php_sqlsrv.h | 4 +- .../pdo_014_integer_custom_formats.phpt | 56 +++++++++++++ ...do_015_integer_custom_formats_pooling.phpt | 61 ++++++++++++++ test/pdo_sqlsrv/pdo_016.phpt | 52 ++++++++++++ test/pdo_sqlsrv/pdo_017.phpt | 60 ++++++++++++++ test/pdo_sqlsrv/pdo_018_next_result_set.phpt | 60 ++++++++++++++ .../pdo_019_next_result_set_pooling.phpt | 65 +++++++++++++++ .../pdo_sqlsrv/pdo_020_bind_params_array.phpt | 58 +++++++++++++ test/pdo_sqlsrv/pdo_021_extended_ascii.phpt | 52 ++++++++++++ test/pdo_sqlsrv/pdo_022_xml_bind_value.phpt | 79 ++++++++++++++++++ .../pdo_sqlsrv/pdo_040_error_information.phpt | 45 ++++++++++ ...prepare_execute_fetch_pooling_default.phpt | 73 ++++++++++++++++ ...prepare_execute_fetch_pooling_enabled.phpt | 73 ++++++++++++++++ ...repare_execute_fetch_pooling_disabled.phpt | 73 ++++++++++++++++ test/sqlsrv/srv_011_temporary_table.phpt | 32 +++++++ test/sqlsrv/srv_013_sqlsrv_get_field.phpt | 48 +++++++++++ .../srv_028_data_conversion_nvarchar.phpt | 55 ++++++++++++ ..._029_data_conversion_varchar_datetime.phpt | 47 +++++++++++ .../srv_034_field_metadata_unicode.phpt | 68 +++++++++++++++ test/sqlsrv/srv_036_transaction_commit.phpt | 83 +++++++++++++++++++ test/sqlsrv/srv_037_transaction_rollback.phpt | 82 ++++++++++++++++++ test/sqlsrv/srv_047_stream_nvarchar.phpt | 55 ++++++++++++ test/sqlsrv/srv_048_stream_nvarchar.phpt | 42 ++++++++++ test/sqlsrv/srv_049_stream_nvarchar_utf8.phpt | 43 ++++++++++ .../srv_050_error_conversion_varchar_int.phpt | 46 ++++++++++ .../srv_051_error_conversion_nchar.phpt | 47 +++++++++++ test/sqlsrv/srv_052_mars.phpt | 40 +++++++++ .../srv_053_mars_disabled_error_checks.phpt | 56 +++++++++++++ 36 files changed, 1679 insertions(+), 35 deletions(-) create mode 100644 test/pdo_sqlsrv/pdo_014_integer_custom_formats.phpt create mode 100644 test/pdo_sqlsrv/pdo_015_integer_custom_formats_pooling.phpt create mode 100644 test/pdo_sqlsrv/pdo_016.phpt create mode 100644 test/pdo_sqlsrv/pdo_017.phpt create mode 100644 test/pdo_sqlsrv/pdo_018_next_result_set.phpt create mode 100644 test/pdo_sqlsrv/pdo_019_next_result_set_pooling.phpt create mode 100644 test/pdo_sqlsrv/pdo_020_bind_params_array.phpt create mode 100644 test/pdo_sqlsrv/pdo_021_extended_ascii.phpt create mode 100644 test/pdo_sqlsrv/pdo_022_xml_bind_value.phpt create mode 100644 test/pdo_sqlsrv/pdo_040_error_information.phpt create mode 100644 test/pdo_sqlsrv/pdo_060_prepare_execute_fetch_pooling_default.phpt create mode 100644 test/pdo_sqlsrv/pdo_061_prepare_execute_fetch_pooling_enabled.phpt create mode 100644 test/pdo_sqlsrv/pdo_062_prepare_execute_fetch_pooling_disabled.phpt create mode 100644 test/sqlsrv/srv_011_temporary_table.phpt create mode 100644 test/sqlsrv/srv_013_sqlsrv_get_field.phpt create mode 100644 test/sqlsrv/srv_028_data_conversion_nvarchar.phpt create mode 100644 test/sqlsrv/srv_029_data_conversion_varchar_datetime.phpt create mode 100644 test/sqlsrv/srv_034_field_metadata_unicode.phpt create mode 100644 test/sqlsrv/srv_036_transaction_commit.phpt create mode 100644 test/sqlsrv/srv_037_transaction_rollback.phpt create mode 100644 test/sqlsrv/srv_047_stream_nvarchar.phpt create mode 100644 test/sqlsrv/srv_048_stream_nvarchar.phpt create mode 100644 test/sqlsrv/srv_049_stream_nvarchar_utf8.phpt create mode 100644 test/sqlsrv/srv_050_error_conversion_varchar_int.phpt create mode 100644 test/sqlsrv/srv_051_error_conversion_nchar.phpt create mode 100644 test/sqlsrv/srv_052_mars.phpt create mode 100644 test/sqlsrv/srv_053_mars_disabled_error_checks.phpt diff --git a/source/pdo_sqlsrv/config.m4 b/source/pdo_sqlsrv/config.m4 index fc205938..2b310f92 100644 --- a/source/pdo_sqlsrv/config.m4 +++ b/source/pdo_sqlsrv/config.m4 @@ -57,6 +57,7 @@ if test "$PHP_PDO_SQLSRV" != "no"; then PHP_REQUIRE_CXX() PHP_ADD_LIBRARY(stdc++, 1, PDO_SQLSRV_SHARED_LIBADD) PHP_ADD_LIBRARY(odbc, 1, PDO_SQLSRV_SHARED_LIBADD) + PHP_ADD_LIBRARY(odbcinst, 1, PDO_SQLSRV_SHARED_LIBADD) AC_DEFINE(HAVE_PDO_SQLSRV, 1, [ ]) PHP_ADD_INCLUDE([$pdo_sqlsrv_inc_path]) PHP_NEW_EXTENSION(pdo_sqlsrv, $pdo_sqlsrv_src_class $shared_src_class, $ext_shared,,-I$pdo_cv_inc_path -std=c++11) @@ -64,3 +65,4 @@ if test "$PHP_PDO_SQLSRV" != "no"; then PHP_ADD_EXTENSION_DEP(pdo_sqlsrv, pdo) PHP_ADD_BUILD_DIR([$ext_builddir/shared], 1) fi + diff --git a/source/pdo_sqlsrv/pdo_dbh.cpp b/source/pdo_sqlsrv/pdo_dbh.cpp index c1c63a22..b9e3d882 100644 --- a/source/pdo_sqlsrv/pdo_dbh.cpp +++ b/source/pdo_sqlsrv/pdo_dbh.cpp @@ -1166,8 +1166,19 @@ char * pdo_sqlsrv_dbh_last_id( pdo_dbh_t *dbh, const char *name, _Out_ size_t* l NULL /*valid_stmt_opts*/, pdo_sqlsrv_handle_stmt_error, &temp_stmt TSRMLS_CC ); driver_stmt->set_func( __FUNCTION__ ); - // execute the last insert id query - core::SQLExecDirect( driver_stmt, last_insert_id_query TSRMLS_CC ); + + sqlsrv_malloc_auto_ptr wsql_string; + unsigned int wsql_len; + wsql_string = utf16_string_from_mbcs_string( SQLSRV_ENCODING_CHAR, reinterpret_cast( last_insert_id_query ), + strlen(last_insert_id_query), &wsql_len ); + + CHECK_CUSTOM_ERROR( wsql_string == 0, driver_stmt, SQLSRV_ERROR_QUERY_STRING_ENCODING_TRANSLATE, + get_last_error_message() ) { + throw core::CoreException(); + } + + // execute the last insert id query + core::SQLExecDirectW( driver_stmt, wsql_string TSRMLS_CC ); core::SQLFetchScroll( driver_stmt, SQL_FETCH_NEXT, 0 TSRMLS_CC ); SQLRETURN r = core::SQLGetData( driver_stmt, 1, SQL_C_CHAR, id_str, LAST_INSERT_ID_BUFF_LEN, diff --git a/source/shared/core_conn.cpp b/source/shared/core_conn.cpp index e16032ce..d4801590 100644 --- a/source/shared/core_conn.cpp +++ b/source/shared/core_conn.cpp @@ -32,7 +32,8 @@ #ifndef _WIN32 #include -#endif // !_WIN32 +#include +#endif // *** internal variables and constants *** @@ -101,13 +102,30 @@ sqlsrv_conn* core_sqlsrv_connect( sqlsrv_context& henv_cp, sqlsrv_context& henv_ sqlsrv_malloc_auto_ptr wconn_string; unsigned int wconn_len = 0; +#ifndef __linux__ + sqlsrv_context* henv = &henv_cp; // by default use the connection pooling henv +#else + sqlsrv_context* henv = &henv_ncp; // by default do not use the connection pooling henv +#endif + try { + // Due to the limitations on connection pooling in unixODBC 2.3.1 driver manager, we do not consider + // the connection string attributes to set (enable/disable) connection pooling. + // Instead, MSPHPSQL connection pooling is set according to the ODBCINST.INI file in [ODBC] section. + +#ifdef __linux__ + char pooling_string[ 128 ] = {0}; + SQLGetPrivateProfileString( "ODBC", "Pooling", "0", pooling_string, sizeof( pooling_string ), "ODBCINST.INI" ); - sqlsrv_context* henv = &henv_cp; // by default use the connection pooling henv - - // check the connection pooling setting to determine which henv to use to allocate the connection handle - // we do this earlier because we have to allocate the connection handle prior to setting attributes on - // it in build_connection_string_and_set_conn_attr. + if ( pooling_string[ 0 ] == '1' || toupper( pooling_string[ 0 ] ) == 'Y' || + ( toupper( pooling_string[ 0 ] ) == 'O' && toupper( pooling_string[ 1 ] ) == 'N' )) + { + henv = &henv_cp; + } +#else + // check the connection pooling setting to determine which henv to use to allocate the connection handle + // we do this earlier because we have to allocate the connection handle prior to setting attributes on + // it in build_connection_string_and_set_conn_attr. if( options_ht && zend_hash_num_elements( options_ht ) > 0 ) { @@ -120,6 +138,7 @@ sqlsrv_conn* core_sqlsrv_connect( sqlsrv_context& henv_cp, sqlsrv_context& henv_ } } } +#endif SQLHANDLE temp_conn_h; core::SQLAllocHandle( SQL_HANDLE_DBC, *henv, &temp_conn_h TSRMLS_CC ); @@ -143,7 +162,25 @@ sqlsrv_conn* core_sqlsrv_connect( sqlsrv_context& henv_cp, sqlsrv_context& henv_ } SQLSMALLINT output_conn_size; - r = SQLDriverConnectW( conn->handle(), NULL, reinterpret_cast(wconn_string.get()), static_cast( wconn_len ), NULL, 0, &output_conn_size, SQL_DRIVER_NOPROMPT ); +#ifdef __linux__ + // unixODBC 2.3.1 requires a non-wide SQLDriverConnect call while pooling enabled. + // connection handle has been allocated using henv_cp, means pooling enabled in a PHP script + if ( henv == &henv_cp ) + { + r = SQLDriverConnect( conn->handle(), NULL, (SQLCHAR*)conn_str.c_str(), + SQL_NTS, NULL, 0, &output_conn_size, SQL_DRIVER_NOPROMPT ); + } + else + { + r = SQLDriverConnectW( conn->handle(), NULL, reinterpret_cast( wconn_string.get() ), + static_cast( wconn_len ), NULL, + 0, &output_conn_size, SQL_DRIVER_NOPROMPT ); + } +#else + r = SQLDriverConnectW( conn->handle(), NULL, reinterpret_cast( wconn_string.get() ), + static_cast( wconn_len ), NULL, + 0, &output_conn_size, SQL_DRIVER_NOPROMPT ); +#endif // clear the connection string from memory to remove sensitive data (such as a password). memset( const_cast(conn_str.c_str()), 0, conn_str.size()); @@ -177,8 +214,18 @@ sqlsrv_conn* core_sqlsrv_connect( sqlsrv_context& henv_cp, sqlsrv_context& henv_ // determine the version of the server we're connected to. The server version is left in the // connection upon return. - determine_server_version( conn TSRMLS_CC ); - + // + // unixODBC 2.3.1: + // SQLGetInfo works when r = SQL_SUCCESS_WITH_INFO (non-pooled connection) + // but fails if the connection is using a pool, i.e. r= SQL_SUCCESS. + // Thus, in Linux, we don't call determine_server_version() for a connection that uses pool. +#ifdef __linux__ + if ( r == SQL_SUCCESS_WITH_INFO ) { +#endif + determine_server_version( conn TSRMLS_CC ); +#ifdef __linux__ + } +#endif } catch( std::bad_alloc& ) { memset( const_cast( conn_str.c_str()), 0, conn_str.size() ); @@ -406,12 +453,13 @@ void core_sqlsrv_get_server_info( sqlsrv_conn* conn, _Out_ zval *server_info TSR sqlsrv_malloc_auto_ptr buffer; SQLSMALLINT buffer_len = 0; - // initialize the array - core::sqlsrv_array_init( *conn, server_info TSRMLS_CC ); - // Get the database name buffer = static_cast( sqlsrv_malloc( INFO_BUFFER_LEN )); core::SQLGetInfo( conn, SQL_DATABASE_NAME, buffer, INFO_BUFFER_LEN, &buffer_len TSRMLS_CC ); + + // initialize the array + core::sqlsrv_array_init( *conn, server_info TSRMLS_CC ); + core::sqlsrv_add_assoc_string( *conn, server_info, "CurrentDatabase", buffer, 0 /*duplicate*/ TSRMLS_CC ); buffer.transferred(); @@ -444,13 +492,14 @@ void core_sqlsrv_get_client_info( sqlsrv_conn* conn, _Out_ zval *client_info TSR sqlsrv_malloc_auto_ptr buffer; SQLSMALLINT buffer_len = 0; - - // initialize the array - core::sqlsrv_array_init( *conn, client_info TSRMLS_CC ); - + // Get the ODBC driver's dll name buffer = static_cast( sqlsrv_malloc( INFO_BUFFER_LEN )); core::SQLGetInfo( conn, SQL_DRIVER_NAME, buffer, INFO_BUFFER_LEN, &buffer_len TSRMLS_CC ); + + // initialize the array + core::sqlsrv_array_init( *conn, client_info TSRMLS_CC ); + #ifndef _WIN32 core::sqlsrv_add_assoc_string( *conn, client_info, "DriverName", buffer, 0 /*duplicate*/ TSRMLS_CC ); #else diff --git a/source/shared/core_results.cpp b/source/shared/core_results.cpp index f70cd8c6..1f1593a2 100644 --- a/source/shared/core_results.cpp +++ b/source/shared/core_results.cpp @@ -481,8 +481,8 @@ sqlsrv_buffered_result_set::sqlsrv_buffered_result_set( sqlsrv_stmt* stmt TSRMLS // get the meta data and calculate the size of a row buffer SQLULEN offset = null_bytes; for( SQLSMALLINT i = 0; i < col_count; ++i ) { - - core::SQLDescribeCol( stmt, i + 1, NULL, 0, NULL, &meta[i].type, &meta[i].length, &meta[i].scale, NULL TSRMLS_CC ); + + core::SQLDescribeColW( stmt, i + 1, NULL, 0, NULL, &meta[i].type, &meta[i].length, &meta[i].scale, NULL TSRMLS_CC ); offset = align_to( offset ); meta[i].offset = offset; @@ -494,7 +494,7 @@ sqlsrv_buffered_result_set::sqlsrv_buffered_result_set( sqlsrv_stmt* stmt TSRMLS case SQL_DECIMAL: case SQL_GUID: case SQL_NUMERIC: - core::SQLColAttribute( stmt, i + 1, SQL_DESC_DISPLAY_SIZE, NULL, 0, NULL, + core::SQLColAttributeW( stmt, i + 1, SQL_DESC_DISPLAY_SIZE, NULL, 0, NULL, reinterpret_cast( &meta[i].length ) TSRMLS_CC ); meta[i].length += sizeof( char ) + sizeof( SQLULEN ); // null terminator space offset += meta[i].length; @@ -560,7 +560,7 @@ sqlsrv_buffered_result_set::sqlsrv_buffered_result_set( sqlsrv_stmt* stmt TSRMLS case SQL_SS_TIME2: case SQL_SS_TIMESTAMPOFFSET: case SQL_TYPE_TIMESTAMP: - core::SQLColAttribute( stmt, i + 1, SQL_DESC_DISPLAY_SIZE, NULL, 0, NULL, + core::SQLColAttributeW( stmt, i + 1, SQL_DESC_DISPLAY_SIZE, NULL, 0, NULL, reinterpret_cast( &meta[i].length ) TSRMLS_CC ); meta[i].length += sizeof(char) + sizeof( SQLULEN ); // null terminator space offset += meta[i].length; diff --git a/source/shared/core_stmt.cpp b/source/shared/core_stmt.cpp index 95ce194e..b2a0b034 100644 --- a/source/shared/core_stmt.cpp +++ b/source/shared/core_stmt.cpp @@ -812,10 +812,10 @@ field_meta_data* core_sqlsrv_field_metadata( sqlsrv_stmt* stmt, SQLSMALLINT coln SQLLEN field_name_len = 0; meta_data = new ( sqlsrv_malloc( sizeof( field_meta_data ))) field_meta_data(); - field_name_temp = static_cast( sqlsrv_malloc( SS_MAXCOLNAMELEN * 2 + 1 )); + field_name_temp = static_cast( sqlsrv_malloc( ( SS_MAXCOLNAMELEN + 1 ) * sizeof( SQLWCHAR ) )); SQLSRV_ENCODING encoding = ( (stmt->encoding() == SQLSRV_ENCODING_DEFAULT ) ? stmt->conn->encoding() : stmt->encoding()); try{ - core::SQLDescribeColW( stmt, colno + 1, field_name_temp, SS_MAXCOLNAMELEN * 2 + 1 , &field_len_temp, + core::SQLDescribeColW( stmt, colno + 1, field_name_temp, SS_MAXCOLNAMELEN, &field_len_temp, &( meta_data->field_type ), & ( meta_data->field_size ), & ( meta_data->field_scale ), &( meta_data->field_is_nullable ) TSRMLS_CC ); } @@ -938,10 +938,10 @@ void core_sqlsrv_get_field( sqlsrv_stmt* stmt, SQLUSMALLINT field_index, sqlsrv_ if( sqlsrv_php_type.typeinfo.type == SQLSRV_PHPTYPE_INVALID ) { // Get the SQL type of the field. - core::SQLColAttribute( stmt, field_index + 1, SQL_DESC_CONCISE_TYPE, NULL, 0, NULL, &sql_field_type TSRMLS_CC ); + core::SQLColAttributeW( stmt, field_index + 1, SQL_DESC_CONCISE_TYPE, NULL, 0, NULL, &sql_field_type TSRMLS_CC ); // Get the length of the field. - core::SQLColAttribute( stmt, field_index + 1, SQL_DESC_LENGTH, NULL, 0, NULL, &sql_field_len TSRMLS_CC ); + core::SQLColAttributeW( stmt, field_index + 1, SQL_DESC_LENGTH, NULL, 0, NULL, &sql_field_len TSRMLS_CC ); // Get the corresponding php type from the sql type. sqlsrv_php_type = stmt->sql_type_to_php_type( static_cast( sql_field_type ), static_cast( sql_field_len ), prefer_string ); @@ -1413,7 +1413,8 @@ void calc_string_size( sqlsrv_stmt* stmt, SQLUSMALLINT field_index, SQLLEN sql_t case SQL_SS_TIME2: case SQL_SS_TIMESTAMPOFFSET: { - core::SQLColAttribute( stmt, field_index + 1, SQL_DESC_DISPLAY_SIZE, NULL, 0, NULL, &size TSRMLS_CC ); + // unixODBC 2.3.1 requires wide calls to support pooling + core::SQLColAttributeW( stmt, field_index + 1, SQL_DESC_DISPLAY_SIZE, NULL, 0, NULL, &size TSRMLS_CC ); break; } @@ -1422,7 +1423,8 @@ void calc_string_size( sqlsrv_stmt* stmt, SQLUSMALLINT field_index, SQLLEN sql_t case SQL_WCHAR: case SQL_WVARCHAR: { - core::SQLColAttribute( stmt, field_index + 1, SQL_DESC_OCTET_LENGTH, NULL, 0, NULL, &size TSRMLS_CC ); + // unixODBC 2.3.1 requires wide calls to support pooling + core::SQLColAttributeW( stmt, field_index + 1, SQL_DESC_OCTET_LENGTH, NULL, 0, NULL, &size TSRMLS_CC ); break; } @@ -1614,7 +1616,7 @@ void core_get_field_common( _Inout_ sqlsrv_stmt* stmt, SQLUSMALLINT field_index, sqlsrv_stream* ss = NULL; SQLLEN sql_type; - SQLRETURN r = SQLColAttribute( stmt->handle(), field_index + 1, SQL_DESC_TYPE, NULL, 0, NULL, &sql_type ); + SQLRETURN r = SQLColAttributeW( stmt->handle(), field_index + 1, SQL_DESC_TYPE, NULL, 0, NULL, &sql_type ); CHECK_SQL_ERROR_OR_WARNING( r, stmt ) { throw core::CoreException(); } @@ -2112,8 +2114,8 @@ void get_field_as_string( sqlsrv_stmt* stmt, SQLUSMALLINT field_index, sqlsrv_ph break; } - // Get the SQL type of the field. - core::SQLColAttribute( stmt, field_index + 1, SQL_DESC_CONCISE_TYPE, NULL, 0, NULL, &sql_field_type TSRMLS_CC ); + // Get the SQL type of the field. unixODBC 2.3.1 requires wide calls to support pooling + core::SQLColAttributeW( stmt, field_index + 1, SQL_DESC_CONCISE_TYPE, NULL, 0, NULL, &sql_field_type TSRMLS_CC ); // Calculate the field size. calc_string_size( stmt, field_index, sql_field_type, sql_display_size TSRMLS_CC ); @@ -2123,6 +2125,8 @@ void get_field_as_string( sqlsrv_stmt* stmt, SQLUSMALLINT field_index, sqlsrv_ph sql_display_size == INT_MAX >> 1 || sql_display_size == UINT_MAX - 1 ) { field_len_temp = INITIAL_FIELD_STRING_LEN; + SQLLEN initiallen = field_len_temp + extra; + field_value_temp = static_cast( sqlsrv_malloc( field_len_temp + extra + 1 )); @@ -2146,7 +2150,13 @@ void get_field_as_string( sqlsrv_stmt* stmt, SQLUSMALLINT field_index, sqlsrv_ph stmt->current_results->get_diag_field( 1, SQL_DIAG_SQLSTATE, state, SQL_SQLSTATE_BUFSIZE, &len TSRMLS_CC ); - if( is_truncated_warning( state )) { + // with Linux connection pooling may not get a truncated warning back but the actual field_len_temp + // can be greater than the initallen value. +#ifdef __linux__ + if( is_truncated_warning( state ) || initiallen < field_len_temp) { +#else + if( is_truncated_warning( state ) ) { +#endif SQLLEN dummy_field_len; @@ -2287,7 +2297,17 @@ void get_field_as_string( sqlsrv_stmt* stmt, SQLUSMALLINT field_index, sqlsrv_ph // runtime checks to see if a string is null terminated and issues a warning about it if running in debug mode. // SQL_C_BINARY fields don't return a NULL terminator, so we allocate an extra byte on each field and use the ternary // operator to set add 1 to fill the null terminator - field_value_temp[field_len_temp] = '\0'; + + // with unixODBC connection pooling sometimes field_len_temp can be SQL_NO_DATA. + // In that cause do not set null terminator and set length to 0. + if ( field_len_temp > 0 ) + { + field_value_temp[field_len_temp] = '\0'; + } + else + { + *field_len = 0; + } } catch( core::CoreException& ) { diff --git a/source/shared/core_stream.cpp b/source/shared/core_stream.cpp index 0444201f..6af33750 100644 --- a/source/shared/core_stream.cpp +++ b/source/shared/core_stream.cpp @@ -116,7 +116,13 @@ size_t sqlsrv_stream_read( php_stream* stream, _Out_writes_bytes_(count) char* b "did not occur." ); } + // with unixODBC connection pooling enabled the truncated state may not be returned so check the actual length read + // with buffer length. + #ifdef __linux__ + if( is_truncated_warning( state ) || count < read) { + #else if( is_truncated_warning( state ) ) { + #endif switch( c_type ) { // As per SQLGetData documentation, if the length of character data exceeds the BufferLength, diff --git a/source/shared/localizationimpl.cpp b/source/shared/localizationimpl.cpp index ae39803a..eb66d34a 100644 --- a/source/shared/localizationimpl.cpp +++ b/source/shared/localizationimpl.cpp @@ -47,10 +47,14 @@ struct cp_iconv // Array of CodePage-to-IConvEncoding mappings // First few elements are most commonly used +// CodePage 2 corresponds to binary. If the attribute PDO::SQLSRV_ENCODING_BINARY +// is set, GetIndex() above hits the assert(false) directive unless we include +// CodePage 2 below and assign an empty string to it. const cp_iconv cp_iconv::g_cp_iconv[] = { { 65001, "UTF-8" }, { 1200, "UTF-16LE" }, { 3, "UTF-8" }, + { 2, "" }, { 1252, "CP1252//TRANSLIT" }, { 850, "CP850//TRANSLIT" }, { 437, "CP437//TRANSLIT" }, diff --git a/source/sqlsrv/config.m4 b/source/sqlsrv/config.m4 index 231868a1..255470d4 100644 --- a/source/sqlsrv/config.m4 +++ b/source/sqlsrv/config.m4 @@ -33,6 +33,7 @@ if test "$PHP_SQLSRV" != "no"; then PHP_REQUIRE_CXX() PHP_ADD_LIBRARY(stdc++, 1, SQLSRV_SHARED_LIBADD) PHP_ADD_LIBRARY(odbc, 1, SQLSRV_SHARED_LIBADD) + PHP_ADD_LIBRARY(odbcinst, 1, SQLSRV_SHARED_LIBADD) PHP_SUBST(SQLSRV_SHARED_LIBADD) AC_DEFINE(HAVE_SQLSRV, 1, [ ]) PHP_ADD_INCLUDE([$sqlsrv_inc_path]) diff --git a/source/sqlsrv/php_sqlsrv.h b/source/sqlsrv/php_sqlsrv.h index 1aadb95b..043a108b 100644 --- a/source/sqlsrv/php_sqlsrv.h +++ b/source/sqlsrv/php_sqlsrv.h @@ -603,8 +603,6 @@ inline H* process_params( INTERNAL_FUNCTION_PARAMETERS, char const* param_spec, } h->set_func( calling_func ); - - return h; } catch( core::CoreException& ) { @@ -615,6 +613,8 @@ inline H* process_params( INTERNAL_FUNCTION_PARAMETERS, char const* param_spec, DIE( "%1!s!: Unknown exception caught in process_params.", calling_func ); } + + return h; } #endif /* PHP_SQLSRV_H */ diff --git a/test/pdo_sqlsrv/pdo_014_integer_custom_formats.phpt b/test/pdo_sqlsrv/pdo_014_integer_custom_formats.phpt new file mode 100644 index 00000000..8a65f1ae --- /dev/null +++ b/test/pdo_sqlsrv/pdo_014_integer_custom_formats.phpt @@ -0,0 +1,56 @@ +--TEST-- +Number MAX_INT to string with custom formats +--SKIPIF-- +--FILE-- +query("CREATE DATABASE $dbName") ?: die(); + +// Create table +$query = "CREATE TABLE $tableName (col1 INT)"; +$stmt = $conn->query($query); + +// Query number with custom format +$query ="SELECT FORMAT($sample,'#,0.00')"; +$stmt = $conn->query($query); +$data = $stmt->fetchColumn(); +var_dump ($data); + +// Insert data using bind parameters +$query = "INSERT INTO $tableName VALUES(:p0)"; +$stmt = $conn->prepare($query); +$stmt->bindValue(':p0', $sample, PDO::PARAM_INT); +$stmt->execute(); + +// Fetching. Prepare with client buffered cursor +$query = "SELECT TOP 1 FORMAT(col1,'#,0.00 EUR') FROM $tableName"; +$stmt = $conn->prepare($query, array(PDO::ATTR_CURSOR => PDO::CURSOR_SCROLL, + PDO::SQLSRV_ATTR_CURSOR_SCROLL_TYPE => PDO::SQLSRV_CURSOR_BUFFERED)); +$stmt->execute(); +$value = $stmt->fetchColumn(); +var_dump ($value); + +// DROP database +$conn->query("DROP DATABASE ". $dbName) ?: die(); + +//Free the statement and connection +$stmt = null; +$conn = null; + +print "Done"; +?> + +--EXPECT-- +int(2147483647) +string(16) "2,147,483,647.00" +string(20) "2,147,483,647.00 EUR" +Done diff --git a/test/pdo_sqlsrv/pdo_015_integer_custom_formats_pooling.phpt b/test/pdo_sqlsrv/pdo_015_integer_custom_formats_pooling.phpt new file mode 100644 index 00000000..1cbe304c --- /dev/null +++ b/test/pdo_sqlsrv/pdo_015_integer_custom_formats_pooling.phpt @@ -0,0 +1,61 @@ +--TEST-- +Number MAX_INT to string with custom formats, see pdo_014. Pooling enabled. +--SKIPIF-- +--FILE-- +query("select 1"); +$conn0 = null; + +/* Connect */ +$conn = new PDO("sqlsrv:server=$serverName;ConnectionPooling=$pooling", $username, $password); + +// Create database +$conn->query("CREATE DATABASE $dbName") ?: die(); + +// Create table +$query = "CREATE TABLE $tableName (col1 INT)"; +$stmt = $conn->query($query); + +// Query number with custom format +$query ="SELECT FORMAT($sample,'#,0.00')"; +$stmt = $conn->query($query); +$data = $stmt->fetchColumn(); +var_dump ($data); + +// Insert data using bind parameters +$query = "INSERT INTO $tableName VALUES(:p0)"; +$stmt = $conn->prepare($query); +$stmt->bindValue(':p0', $sample, PDO::PARAM_INT); +$stmt->execute(); + +// Fetching. Prepare with client buffered cursor +$query = "SELECT TOP 1 FORMAT(col1,'#,0.00 EUR') FROM $tableName"; +$stmt = $conn->prepare($query, array(PDO::ATTR_CURSOR => PDO::CURSOR_SCROLL, + PDO::SQLSRV_ATTR_CURSOR_SCROLL_TYPE => PDO::SQLSRV_CURSOR_BUFFERED)); +$stmt->execute(); +$value = $stmt->fetchColumn(); +var_dump ($value); + +// DROP database +$conn->query("DROP DATABASE ". $dbName) ?: die(); + +//Free the statement and connection +$stmt = null; +$conn = null; + +print "Done"; +?> + +--EXPECT-- +string(16) "2,147,483,647.00" +string(20) "2,147,483,647.00 EUR" +Done diff --git a/test/pdo_sqlsrv/pdo_016.phpt b/test/pdo_sqlsrv/pdo_016.phpt new file mode 100644 index 00000000..33441cdb --- /dev/null +++ b/test/pdo_sqlsrv/pdo_016.phpt @@ -0,0 +1,52 @@ +--TEST-- +Bind integer parameters; allow fetch numeric types. +--SKIPIF-- +--FILE-- +query("CREATE DATABASE ". $dbName) ?: die(); + +// Create table +$sql = "CREATE TABLE $tableName (c1 INT, c2 INT)"; +$stmt = $conn->query($sql); + +// Insert data using bind parameters +$sql = "INSERT INTO $tableName VALUES (:num1, :num2)"; +$stmt = $conn->prepare($sql); +$stmt->bindParam(':num1', $sample[0], PDO::PARAM_INT); +$stmt->bindParam(':num2', $sample[1], PDO::PARAM_INT); +$stmt->execute(); + +// Fetch, get data +$sql = "SELECT * FROM $tableName"; +$stmt = $conn->query($sql); +$row = $stmt->fetch(PDO::FETCH_NUM); +var_dump ($row); + +// DROP database +$conn->query("DROP DATABASE ". $dbName) ?: die(); + +// Close connection +$stmt = null; +$conn = null; + +print "Done"; +?> + +--EXPECT-- +array(2) { + [0]=> + int(-2147483648) + [1]=> + int(2147483647) +} +Done diff --git a/test/pdo_sqlsrv/pdo_017.phpt b/test/pdo_sqlsrv/pdo_017.phpt new file mode 100644 index 00000000..082f31d8 --- /dev/null +++ b/test/pdo_sqlsrv/pdo_017.phpt @@ -0,0 +1,60 @@ +--TEST-- +Fetch string with new line and tab characters +--SKIPIF-- +--FILE-- +query("CREATE DATABASE ". $dbName) ?: die(); + +// Create table +$sql = "CREATE TABLE ".$tableName. + " (c1 VARCHAR(32), c2 CHAR(32), c3 NVARCHAR(32), c4 NCHAR(32))"; +$stmt = $conn->query($sql); + +// Bind parameters and insert data +$sql = "INSERT INTO $tableName VALUES (:val1, :val2, :val3, :val4)"; +$value = "I USE\nMSPHPSQL\tDRIVERS WITH PHP7"; +$stmt = $conn->prepare($sql); +$stmt->bindParam(':val1', $value); +$stmt->bindParam(':val2', $value); +$stmt->bindParam(':val3', $value); +$stmt->bindParam(':val4', $value); +$stmt->execute(); + +// Get data +$sql = "SELECT UPPER(c1) AS VARCHAR, UPPER(c2) AS CHAR, + UPPER(c3) AS NVARCHAR, UPPER(c4) AS NCHAR FROM $tableName"; +$stmt = $conn->query($sql); +$row = $stmt->fetch(PDO::FETCH_ASSOC); +var_dump($row); + +// DROP database +$conn->query("DROP DATABASE ". $dbName) ?: die(); + +// Close connection +$stmt=null; +$conn=null; +print "Done" +?> + +--EXPECT-- +array(4) { + ["VARCHAR"]=> + string(32) "I USE +MSPHPSQL DRIVERS WITH PHP7" + ["CHAR"]=> + string(32) "I USE +MSPHPSQL DRIVERS WITH PHP7" + ["NVARCHAR"]=> + string(32) "I USE +MSPHPSQL DRIVERS WITH PHP7" + ["NCHAR"]=> + string(32) "I USE +MSPHPSQL DRIVERS WITH PHP7" +} +Done diff --git a/test/pdo_sqlsrv/pdo_018_next_result_set.phpt b/test/pdo_sqlsrv/pdo_018_next_result_set.phpt new file mode 100644 index 00000000..647681c3 --- /dev/null +++ b/test/pdo_sqlsrv/pdo_018_next_result_set.phpt @@ -0,0 +1,60 @@ +--TEST-- +Moves the cursor to the next result set +--SKIPIF-- +--FILE-- +query("CREATE DATABASE ". $dbName) ?: die(); + +// Create table +$sql = "CREATE TABLE $tableName (c1 INT, c2 VARCHAR(40))"; +$stmt = $conn->query($sql); + +// Insert data using bind parameters +$sql = "INSERT INTO $tableName VALUES (?,?)"; +for($t=200; $t<220; $t++) { + $stmt = $conn->prepare($sql); + $stmt->bindParam(1, $t); + $ts = sha1($t); + $stmt->bindParam(2, $ts); + $stmt->execute(); +} + +// Fetch, get data and move the cursor to the next result set +$sql = "SELECT * from $tableName WHERE c1 = '204' OR c1 = '210'; + SELECT Top 3 * FROM $tableName ORDER BY c1 DESC"; +$stmt = $conn->query($sql); +$data1 = $stmt->fetchAll(PDO::FETCH_ASSOC); +$stmt->nextRowset(); +$data2 = $stmt->fetchAll(PDO::FETCH_NUM); + +// Array: FETCH_ASSOC +foreach ($data1 as $a) +echo $a['c1'] . "|" . $a['c2'] . "\n"; + +// Array: FETCH_NUM +foreach ($data2 as $a) +echo $a[0] . "|" . $a[1] . "\n"; + +// DROP database +$conn->query("DROP DATABASE ". $dbName) ?: die(); + +// Close connection +$stmt = null; +$conn = null; + +print "Done"; +?> + +--EXPECT-- +204|1cc641954099c249e0e4ef0402da3fd0364d95f0 +210|135debd4837026bf06c7bfc5d1e0c6a31611af1d +219|c0ba17c23a26ff8c314478bc69f30963a6e4a754 +218|3d5bdf107de596ce77e8ce48a61b585f52bbb61d +217|49e3d046636e06b2d82ee046db8e6eb9a2e11e16 +Done diff --git a/test/pdo_sqlsrv/pdo_019_next_result_set_pooling.phpt b/test/pdo_sqlsrv/pdo_019_next_result_set_pooling.phpt new file mode 100644 index 00000000..9e570159 --- /dev/null +++ b/test/pdo_sqlsrv/pdo_019_next_result_set_pooling.phpt @@ -0,0 +1,65 @@ +--TEST-- +Moves the cursor to the next result set with pooling enabled +--SKIPIF-- +--FILE-- +query("SELECT 1"); +$conn0 = null; + +// Connect +$conn = new PDO("sqlsrv:server=$serverName;ConnectionPooling=1", $username, $password); + +// CREATE database +$conn->query("CREATE DATABASE ". $dbName) ?: die(); + +// Create table +$sql = "CREATE TABLE $tableName (c1 INT, c2 XML)"; +$stmt = $conn->query($sql); + +// Insert data using bind parameters +$sql = "INSERT INTO $tableName VALUES (?,?)"; +for($t=200; $t<220; $t++) { + $stmt = $conn->prepare($sql); + $stmt->bindParam(1, $t); + $ts = substr(sha1($t),0,5); + $stmt->bindParam(2, $ts); + $stmt->execute(); +} + +// Fetch, get data and move the cursor to the next result set +$sql = "SELECT * from $tableName WHERE c1 = '204' OR c1 = '210'; + SELECT Top 3 * FROM $tableName ORDER BY c1 DESC"; +$stmt = $conn->query($sql); +$data1 = $stmt->fetchAll(PDO::FETCH_ASSOC); +$stmt->nextRowset(); +$data2 = $stmt->fetchAll(PDO::FETCH_NUM); + +// Array: FETCH_ASSOC +foreach ($data1 as $a) +echo $a['c1']."|".$a['c2']."\n"; + +// Array: FETCH_NUM +foreach ($data2 as $a) +echo $a[0] . "|".$a[1]."\n"; + +// DROP database +$conn->query("DROP DATABASE ". $dbName) ?: die(); + +// Close connection +$stmt = null; +$conn = null; + +print "Done"; +?> + +--EXPECT-- +204|1cc64 +210|135de +219|c0ba1 +218|3d5bd +217|49e3d +Done diff --git a/test/pdo_sqlsrv/pdo_020_bind_params_array.phpt b/test/pdo_sqlsrv/pdo_020_bind_params_array.phpt new file mode 100644 index 00000000..05ea05a0 --- /dev/null +++ b/test/pdo_sqlsrv/pdo_020_bind_params_array.phpt @@ -0,0 +1,58 @@ +--TEST-- +Bind parameters using an array +--SKIPIF-- +--FILE-- +query("CREATE DATABASE ". $dbName) ?: die(); + +// Create table +$sql = "CREATE TABLE $tableName (ID TINYINT, SID CHAR(5))"; +$stmt = $conn->query($sql); + +// Insert data using bind parameters +$sql = "INSERT INTO $tableName VALUES (?,?)"; +for($t=100; $t<103; $t++) { + $stmt = $conn->prepare($sql); + $ts = substr(sha1($t),0,5); + $params = array($t,$ts); + $stmt->execute($params); +} + +// Query, but do not fetch +$sql = "SELECT * from $tableName"; +$stmt = $conn->query($sql); + +// Insert duplicate row, ID = 100 +$t = 100; +$sql = "INSERT INTO $tableName VALUES (?,?)"; +$stmt1 = $conn->prepare($sql); +$ts = substr(sha1($t),0,5); +$params = array($t,$ts); +$stmt1->execute($params); + +// Fetch. The result set should not contain duplicates +$data = $stmt->fetchAll(); +foreach ($data as $a) +echo $a['ID'] . "|" . $a['SID'] . "\n"; + +// DROP database +$conn->query("DROP DATABASE ". $dbName) ?: die(); + +// Close connection +$stmt = null; +$conn = null; + +print "Done"; +?> + +--EXPECT-- +100|310b8 +101|dbc0f +102|c8306 +Done diff --git a/test/pdo_sqlsrv/pdo_021_extended_ascii.phpt b/test/pdo_sqlsrv/pdo_021_extended_ascii.phpt new file mode 100644 index 00000000..6158c1b6 --- /dev/null +++ b/test/pdo_sqlsrv/pdo_021_extended_ascii.phpt @@ -0,0 +1,52 @@ +--TEST-- +Bind parameters VARCHAR(n) extended ASCII +--SKIPIF-- +--FILE-- +query("CREATE DATABASE ". $dbName) ?: die(); + +// Create table +$sql = "CREATE TABLE $tableName (code CHAR(2), city VARCHAR(32))"; +$stmt = $conn->query($sql); + +// Insert data using bind parameters +$sql = "INSERT INTO $tableName VALUES (?,?)"; + +// First row +$stmt = $conn->prepare($sql); +$params = array("FI","Järvenpää"); +$stmt->execute($params); + +// Second row +$params = array("DE","München"); +$stmt->execute($params); + +// Query, fetch +$sql = "SELECT * from $tableName"; +$stmt = $conn->query($sql); +$data = $stmt->fetchAll(); + +// Print out +foreach ($data as $a) +echo $a[0] . "|" . $a[1] . "\n"; + +// DROP database +$conn->query("DROP DATABASE ". $dbName) ?: die(); + +// Close connection +$stmt = null; +$conn = null; + +print "Done"; +?> + +--EXPECT-- +FI|Järvenpää +DE|München +Done diff --git a/test/pdo_sqlsrv/pdo_022_xml_bind_value.phpt b/test/pdo_sqlsrv/pdo_022_xml_bind_value.phpt new file mode 100644 index 00000000..6b1164a6 --- /dev/null +++ b/test/pdo_sqlsrv/pdo_022_xml_bind_value.phpt @@ -0,0 +1,79 @@ +--TEST-- +Unicode XML message using bindValue() +--SKIPIF-- +--FILE-- +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + +// Create database +$conn->query("CREATE DATABASE ". $dbName) ?: die(); + +// Create table +$sql = "CREATE TABLE $tableName (ID INT PRIMARY KEY NOT NULL IDENTITY, XMLMessage XML)"; +$stmt = $conn->query($sql); + +// XML samples +$xml1 = ' + + APP_PoP_银河 + Το Παρίσι (γαλλικά: Paris, ΔΦΑ [paˈʁi]), γνωστό και ως η Πόλη του φωτός (Ville lumière), από τότε που εφοδιάστηκαν οι κύριες λεωφόροι του με φανούς γκαζιού το 1828, είναι η πρωτεύουσα της Γαλλίας και της περιφέρειας Ιλ ντε Φρανς (Île-de-France) και μία από τις ιστορικότερες πόλεις της Ευρώπης. +'; + +$xml2 = ' + + NULL + +'; + +// Insert data +try +{ + $stmt = $conn->prepare("INSERT INTO $tableName (XMLMessage) VALUES (:msg)"); + $stmt->bindValue(':msg', $xml1); + $stmt->execute(); + + $stmt = $conn->prepare("INSERT INTO $tableName (XMLMessage) VALUES (?)"); + $stmt->bindValue(1, $xml2); + $stmt->execute(); +} +catch (PDOException $ex) { + echo "Error: " . $ex->getMessage(); +} + +// Get data +$stmt = $conn->query("select * from $tableName"); +$row = $stmt->fetchAll(PDO::FETCH_ASSOC); +var_dump($row); + +// Drop database +$conn->query("DROP DATABASE ". $dbName) ?: die(); + +// Close connection +$stmt=null; +$conn=null; + +print "Done" +?> + +--EXPECT-- +array(2) { + [0]=> + array(2) { + ["ID"]=> + string(1) "1" + ["XMLMessage"]=> + string(553) "APP_PoP_银河Το Παρίσι (γαλλικά: Paris, ΔΦΑ [paˈʁi]), γνωστό και ως η Πόλη του φωτός (Ville lumière), από τότε που εφοδιάστηκαν οι κύριες λεωφόροι του με φανούς γκαζιού το 1828, είναι η πρωτεύουσα της Γαλλίας και της περιφέρειας Ιλ ντε Φρανς (Île-de-France) και μία από τις ιστορικότερες πόλεις της Ευρώπης." + } + [1]=> + array(2) { + ["ID"]=> + string(1) "2" + ["XMLMessage"]=> + string(43) "NULL" + } +} +Done diff --git a/test/pdo_sqlsrv/pdo_040_error_information.phpt b/test/pdo_sqlsrv/pdo_040_error_information.phpt new file mode 100644 index 00000000..a9c83410 --- /dev/null +++ b/test/pdo_sqlsrv/pdo_040_error_information.phpt @@ -0,0 +1,45 @@ +--TEST-- +Retrieve error information; supplied values does not match table definition +--SKIPIF-- +--FILE-- +query("CREATE DATABASE ". $dbName) ?: die(); + +// Create table +$sql = "CREATE TABLE $tableName (code INT)"; +$stmt = $conn->query($sql); + +// Insert data using bind parameters +// Number of supplied values does not match table definition +$sql = "INSERT INTO $tableName VALUES (?,?)"; +$stmt = $conn->prepare($sql); +$params = array(2010,"London"); + +// SQL statement has an error, which is then reported +$stmt->execute($params); +print_r($stmt->errorInfo()); + +// DROP database +$conn->query("DROP DATABASE ". $dbName) ?: die(); + +// Close connection +$stmt = null; +$conn = null; + +print "Done"; +?> + +--EXPECT-- +Array +( + [0] => 21S01 + [1] => 213 + [2] => [Microsoft][ODBC Driver 13 for SQL Server][SQL Server]Column name or number of supplied values does not match table definition. +) +Done diff --git a/test/pdo_sqlsrv/pdo_060_prepare_execute_fetch_pooling_default.phpt b/test/pdo_sqlsrv/pdo_060_prepare_execute_fetch_pooling_default.phpt new file mode 100644 index 00000000..ee2e66cb --- /dev/null +++ b/test/pdo_sqlsrv/pdo_060_prepare_execute_fetch_pooling_default.phpt @@ -0,0 +1,73 @@ +--TEST-- +Prepare, execute statement and fetch with pooling unset (default) +--SKIPIF-- +--FILE-- +query("CREATE DATABASE ". $dbName) ?: die(); + +// Create table +$sql = "CREATE TABLE $tableName (Столица NVARCHAR(32), year INT)"; +$stmt = $conn->query($sql); + +// Insert data +$sql = "INSERT INTO ".$tableName." VALUES (?,?)"; +$stmt = $conn->prepare($sql); +$stmt->execute(array("Лондон",2012)); + +// Get data +$stmt = $conn->query("select * from $tableName"); +$row = $stmt->fetch(PDO::FETCH_ASSOC); +var_dump($row); +$conn = null; + +// Create a new pool +$conn0 = new PDO( "sqlsrv:server=$serverName;", + $username, $password); +$conn0 = null; + +// Connection can use an existing pool +$conn = new PDO( "sqlsrv:server=$serverName;", + $username, $password); + +// Get data +$stmt = $conn->query("select * from $tableName"); +$row = $stmt->fetch(PDO::FETCH_ASSOC); +var_dump($row); + +// DROP database +$conn->query("DROP DATABASE ". $dbName) ?: die(); + +// Close connection +$stmt=null; +$conn=null; +print "Done" +?> +--EXPECT-- +array(2) { + ["Столица"]=> + string(12) "Лондон" + ["year"]=> + int(2012) +} +array(2) { + ["Столица"]=> + string(12) "Лондон" + ["year"]=> + string(4) "2012" +} +Done diff --git a/test/pdo_sqlsrv/pdo_061_prepare_execute_fetch_pooling_enabled.phpt b/test/pdo_sqlsrv/pdo_061_prepare_execute_fetch_pooling_enabled.phpt new file mode 100644 index 00000000..0c46ff87 --- /dev/null +++ b/test/pdo_sqlsrv/pdo_061_prepare_execute_fetch_pooling_enabled.phpt @@ -0,0 +1,73 @@ +--TEST-- +Prepare, execute statement and fetch with pooling enabled +--SKIPIF-- +--FILE-- +query("CREATE DATABASE ". $dbName) ?: die(); + +// Create table +$sql = "CREATE TABLE $tableName (Столица NVARCHAR(32), year INT)"; +$stmt = $conn->query($sql); + +// Insert data +$sql = "INSERT INTO ".$tableName." VALUES (?,?)"; +$stmt = $conn->prepare($sql); +$stmt->execute(array("Лондон",2012)); + +// Get data +$stmt = $conn->query("select * from $tableName"); +$row = $stmt->fetch(PDO::FETCH_ASSOC); +var_dump($row); +$conn = null; + +// Create a new pool +$conn0 = new PDO( "sqlsrv:server=$serverName;ConnectionPooling=1;", + $username, $password); +$conn0 = null; + +// Connection can use an existing pool +$conn = new PDO( "sqlsrv:server=$serverName;ConnectionPooling=1;", + $username, $password); + +// Get data +$stmt = $conn->query("select * from $tableName"); +$row = $stmt->fetch(PDO::FETCH_ASSOC); +var_dump($row); + +// DROP database +$conn->query("DROP DATABASE ". $dbName) ?: die(); + +// Close connection +$stmt=null; +$conn=null; +print "Done" +?> +--EXPECT-- +array(2) { + ["Столица"]=> + string(12) "Лондон" + ["year"]=> + int(2012) +} +array(2) { + ["Столица"]=> + string(12) "Лондон" + ["year"]=> + string(4) "2012" +} +Done diff --git a/test/pdo_sqlsrv/pdo_062_prepare_execute_fetch_pooling_disabled.phpt b/test/pdo_sqlsrv/pdo_062_prepare_execute_fetch_pooling_disabled.phpt new file mode 100644 index 00000000..dc30a772 --- /dev/null +++ b/test/pdo_sqlsrv/pdo_062_prepare_execute_fetch_pooling_disabled.phpt @@ -0,0 +1,73 @@ +--TEST-- +Prepare, execute statement and fetch with pooling disabled +--SKIPIF-- +--FILE-- +query("CREATE DATABASE ". $dbName) ?: die(); + +// Create table +$sql = "CREATE TABLE $tableName (Столица NVARCHAR(32), year INT)"; +$stmt = $conn->query($sql); + +// Insert data +$sql = "INSERT INTO ".$tableName." VALUES (?,?)"; +$stmt = $conn->prepare($sql); +$stmt->execute(array("Лондон",2012)); + +// Get data +$stmt = $conn->query("select * from $tableName"); +$row = $stmt->fetch(PDO::FETCH_ASSOC); +var_dump($row); +$conn = null; + +// Create a new pool +$conn0 = new PDO( "sqlsrv:server=$serverName;ConnectionPooling=0;", + $username, $password); +$conn0 = null; + +// Connection can use an existing pool +$conn = new PDO( "sqlsrv:server=$serverName;ConnectionPooling=0;", + $username, $password); + +// Get data +$stmt = $conn->query("select * from $tableName"); +$row = $stmt->fetch(PDO::FETCH_ASSOC); +var_dump($row); + +// DROP database +$conn->query("DROP DATABASE ". $dbName) ?: die(); + +// Close connection +$stmt=null; +$conn=null; +print "Done" +?> +--EXPECT-- +array(2) { + ["Столица"]=> + string(12) "Лондон" + ["year"]=> + int(2012) +} +array(2) { + ["Столица"]=> + string(12) "Лондон" + ["year"]=> + string(4) "2012" +} +Done diff --git a/test/sqlsrv/srv_011_temporary_table.phpt b/test/sqlsrv/srv_011_temporary_table.phpt new file mode 100644 index 00000000..fb89e7bf --- /dev/null +++ b/test/sqlsrv/srv_011_temporary_table.phpt @@ -0,0 +1,32 @@ +--TEST-- +Temporary table +--SKIPIF-- +--FILE-- + + +--EXPECT-- +string(11) "PHP7 SQLSRV" +Done diff --git a/test/sqlsrv/srv_013_sqlsrv_get_field.phpt b/test/sqlsrv/srv_013_sqlsrv_get_field.phpt new file mode 100644 index 00000000..935add7f --- /dev/null +++ b/test/sqlsrv/srv_013_sqlsrv_get_field.phpt @@ -0,0 +1,48 @@ +--TEST-- +sqlsrv_get_field() using SQLSRV_PHPTYPE_STREAM(SQLSRV_ENC_CHAR) +--SKIPIF-- +--FILE-- + + +--EXPECT-- +resource(9) of type (stream) +1998.1 +resource(10) of type (stream) +-2004.2436 +resource(11) of type (stream) +4.2 EUR +Done diff --git a/test/sqlsrv/srv_028_data_conversion_nvarchar.phpt b/test/sqlsrv/srv_028_data_conversion_nvarchar.phpt new file mode 100644 index 00000000..f12a8e54 --- /dev/null +++ b/test/sqlsrv/srv_028_data_conversion_nvarchar.phpt @@ -0,0 +1,55 @@ +--TEST-- +Data type precedence: conversion NVARCHAR(n) +--SKIPIF-- +--FILE-- +$username, "PWD"=>$password, "CharacterSet"=>"UTF-8"); +$conn = sqlsrv_connect($serverName, $connectionInfo) ?: die(); + +// Create database +sqlsrv_query($conn,"CREATE DATABASE ". $dbName) ?: die(); + +// Create table. Column names: passport +$sql = "CREATE TABLE $tableName (c1 NVARCHAR(8))"; +$stmt = sqlsrv_query($conn, $sql); + +// Insert data. The data type with the lower precedence +// is converted to the data type with the higher precedence +$sql = "INSERT INTO $tableName VALUES (3.1415),(-32),(null)"; +$stmt = sqlsrv_query($conn, $sql); + +// Insert more data +$sql = "INSERT INTO $tableName VALUES (''),('Galaxy'),('-- GO'),(N'银河系')"; +$stmt = sqlsrv_query($conn, $sql); + +// Read data from the table +$sql = "SELECT * FROM $tableName"; +$stmt = sqlsrv_query($conn, $sql); + +while($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_NUMERIC)) { + var_dump($row[0]); +} + +// DROP database +sqlsrv_query($conn,"DROP DATABASE ". $dbName); + +// Free statement and connection resources +sqlsrv_free_stmt($stmt); +sqlsrv_close($conn); + +print "Done" +?> + +--EXPECT-- +string(6) "3.1415" +string(8) "-32.0000" +NULL +string(0) "" +string(6) "Galaxy" +string(5) "-- GO" +string(9) "银河系" +Done diff --git a/test/sqlsrv/srv_029_data_conversion_varchar_datetime.phpt b/test/sqlsrv/srv_029_data_conversion_varchar_datetime.phpt new file mode 100644 index 00000000..a519f3f9 --- /dev/null +++ b/test/sqlsrv/srv_029_data_conversion_varchar_datetime.phpt @@ -0,0 +1,47 @@ +--TEST-- +Data type precedence: conversion VARCHAR(n) +--SKIPIF-- +--FILE-- + + +--EXPECT-- +string(19) "Jan 1 1900 12:00AM" +string(19) "Dec 18 1898 2:24PM" +string(19) "Sep 24 2017 9:36AM" +Done diff --git a/test/sqlsrv/srv_034_field_metadata_unicode.phpt b/test/sqlsrv/srv_034_field_metadata_unicode.phpt new file mode 100644 index 00000000..800c7f85 --- /dev/null +++ b/test/sqlsrv/srv_034_field_metadata_unicode.phpt @@ -0,0 +1,68 @@ +--TEST-- +Field metadata unicode +--SKIPIF-- +--FILE-- +"$username", "PWD"=>"$password", "CharacterSet"=>"UTF-8"); +$conn = sqlsrv_connect($serverName, $connectionInfo) ?: die(); + +// Create database +sqlsrv_query($conn,"CREATE DATABASE ". $dbName) ?: die(); + +// Create table. Column names: passport +$sql = "CREATE TABLE $tableName (पासपोर्ट CHAR(2), پاسپورٹ VARCHAR(2), Διαβατήριο VARCHAR(MAX))"; +$stmt = sqlsrv_query($conn, $sql); + +// Prepare the statement +$sql = "SELECT * FROM $tableName"; +$stmt = sqlsrv_prepare($conn, $sql); + +// Get and display field metadata +foreach(sqlsrv_field_metadata($stmt) as $meta) +{ + print_r($meta); +} + +// DROP database +sqlsrv_query($conn,"DROP DATABASE ". $dbName); + +// Free statement and connection resources +sqlsrv_free_stmt($stmt); +sqlsrv_close($conn); + +print "Done" +?> + +--EXPECT-- +Array +( + [Name] => पासपोर्ट + [Type] => 1 + [Size] => 2 + [Precision] => + [Scale] => + [Nullable] => 1 +) +Array +( + [Name] => پاسپورٹ + [Type] => 12 + [Size] => 2 + [Precision] => + [Scale] => + [Nullable] => 1 +) +Array +( + [Name] => Διαβατήριο + [Type] => 12 + [Size] => 0 + [Precision] => + [Scale] => + [Nullable] => 1 +) +Done diff --git a/test/sqlsrv/srv_036_transaction_commit.phpt b/test/sqlsrv/srv_036_transaction_commit.phpt new file mode 100644 index 00000000..55b6de22 --- /dev/null +++ b/test/sqlsrv/srv_036_transaction_commit.phpt @@ -0,0 +1,83 @@ +--TEST-- +Transaction operations: commit successful transactions +--SKIPIF-- +--FILE-- += 0))"; +$stmt = sqlsrv_query($conn, $sql); + + +// Set initial data +$sql = "INSERT INTO $tableName VALUES ('ID1','12','5'),('ID102','20','1')"; +$stmt = sqlsrv_query($conn, $sql) ?: die(print_r(sqlsrv_errors(), true)); + +//Initiate transaction +sqlsrv_begin_transaction($conn) ?: die(print_r( sqlsrv_errors(), true)); + +// Update parameters +$count = 4; +$groupId = "ID1"; +$params = array($count, $groupId); + +// Update Accepted column +$sql = "UPDATE $tableName SET Accepted = (Accepted + ?) WHERE GroupId = ?"; +$stmt1 = sqlsrv_query( $conn, $sql, $params) ?: die(print_r(sqlsrv_errors(), true)); + +// Update Tentative column +$sql = "UPDATE $tableName SET Tentative = (Tentative - ?) WHERE GroupId = ?"; +$stmt2 = sqlsrv_query($conn, $sql, $params); + +// Commit the transactions +if ($stmt1 && $stmt2) +{ + sqlsrv_commit($conn); +} +else +{ + echo "\nERROR: $stmt1 and $stmt2 should be valid\n"; + sqlsrv_rollback($conn); + echo "\nTransactions were rolled back.\n"; +} + +PrintContent($conn); + +// DROP database +$stmt = sqlsrv_query($conn,"DROP DATABASE ". $dbName); + +sqlsrv_free_stmt($stmt); +sqlsrv_close($conn); +print "Done" +?> + +--EXPECT-- +Array +( + [GroupId] => ID1 + [Accepted] => 16 + [Tentative] => 1 +) +Done + diff --git a/test/sqlsrv/srv_037_transaction_rollback.phpt b/test/sqlsrv/srv_037_transaction_rollback.phpt new file mode 100644 index 00000000..7988c73d --- /dev/null +++ b/test/sqlsrv/srv_037_transaction_rollback.phpt @@ -0,0 +1,82 @@ +--TEST-- +Transaction operations: rolled-back transactions +--SKIPIF-- +--FILE-- += 0))"; +$stmt = sqlsrv_query($conn, $sql); + + +// Set initial data +$sql = "INSERT INTO $tableName VALUES ('ID1','12','5'),('ID102','20','1')"; +$stmt = sqlsrv_query($conn, $sql) ?: die(print_r(sqlsrv_errors(), true)); + +//Initiate transaction +sqlsrv_begin_transaction($conn) ?: die(print_r( sqlsrv_errors(), true)); + +// Update parameters +$count = 8; +$groupId = "ID1"; +$params = array($count, $groupId); + +// Update Accepted column +$sql = "UPDATE $tableName SET Accepted = (Accepted + ?) WHERE GroupId = ?"; +$stmt1 = sqlsrv_query( $conn, $sql, $params) ?: die(print_r(sqlsrv_errors(), true)); + +// Update Tentative column +// This statement returns FALSE because Tentative column should be non-negative +$sql = "UPDATE $tableName SET Tentative = (Tentative - ?) WHERE GroupId = ?"; +$stmt2 = sqlsrv_query($conn, $sql, $params); + +// Commit the transactions +if ($stmt1 && $stmt2) +{ + echo "\nERROR: $stmt2 should be bool(false)\n"; +} +else +{ + sqlsrv_rollback($conn); +} + +PrintContent($conn); + +// DROP database +$stmt = sqlsrv_query($conn,"DROP DATABASE ". $dbName); + +sqlsrv_free_stmt($stmt); +sqlsrv_close($conn); +print "Done" +?> + +--EXPECT-- +Array +( + [GroupId] => ID1 + [Accepted] => 12 + [Tentative] => 5 +) +Done + diff --git a/test/sqlsrv/srv_047_stream_nvarchar.phpt b/test/sqlsrv/srv_047_stream_nvarchar.phpt new file mode 100644 index 00000000..13d14f04 --- /dev/null +++ b/test/sqlsrv/srv_047_stream_nvarchar.phpt @@ -0,0 +1,55 @@ +--TEST-- +Streaming nvarchar(max) unicode (Russian) with CharacterSet=utf-8 +--SKIPIF-- +--FILE-- +$username, "PWD"=>$password, "CharacterSet"=>"utf-8"); +$conn = sqlsrv_connect($serverName, $connectionInfo); +if( !$conn ) { die( print_r( sqlsrv_errors(), true)); } + +// Create table +$sql = "CREATE TABLE #Table (c1 NVARCHAR(max))"; +$stmt = sqlsrv_query($conn, $sql); +if( !$stmt ) { die( print_r( sqlsrv_errors(), true)); } + +// Insert data, 4538 characters +$data = "Первые публикации об объектно-ориентированных базах данных появились в середине 80-х годов. Поддержка сложных объектов. В системе должна быть предусмотрена возможность создания составных объектов за счет применения конструкторов составных объектов. Необходимо, чтобы конструкторы объектов были ортогональны, то есть любой конструктор можно было применять к любому объекту. Поддержка индивидуальности объектов. Все объекты должны иметь уникальный идентификатор, который не зависит от значений их атрибутов. Поддержка инкапсуляции. Корректная инкапсуляция достигается за счет того, что программисты обладают правом доступа только к спецификации интерфейса методов, а данные и реализация методов скрыты внутри объектов. Поддержка типов и классов. Требуется, чтобы в ООБД поддерживалась хотя бы одна концепция различия между типами и классами. (Термин «тип» более соответствует понятию абстрактного типа данных. В языках программирования переменная объявляется с указанием её типа. Компилятор может использовать эту информацию для проверки выполняемых с переменной операций на совместимость с её типом, что позволяет гарантировать корректность программного обеспечения. С другой стороны класс является неким шаблоном для создания объектов и предоставляет методы, которые могут применяться к этим объектам. Таким образом, понятие «класс» в большей степени относится ко времени исполнения, чем ко времени компиляции.) Поддержка наследования типов и классов от их предков. Подтип, или подкласс, должен наследовать атрибуты и методы от его супертипа, или суперкласса, соответственно. Перегрузка в сочетании с полным связыванием. Методы должны применяться к объектам разных типов. Реализация метода должна зависеть от типа объектов, к которым данный метод применяется. Для обеспечения этой функциональности связывание имен методов в системе не должно выполняться до времени выполнения программы. Вычислительная полнота. Язык манипулирования данными должен быть языком программирования общего назначения. Набор типов данных должен быть расширяемым. Пользователь должен иметь средства создания новых типов данных на основе набора предопределенных системных типов. Более того, между способами использования системных и пользовательских типов данных не должно быть никаких различий.Первые публикации об объектно-ориентированных базах данных появились в середине 80-х годов. Поддержка сложных объектов. В системе должна быть предусмотрена возможность создания составных объектов за счет применения конструкторов составных объектов. Необходимо, чтобы конструкторы объектов были ортогональны, то есть любой конструктор можно было применять к любому объекту. Поддержка индивидуальности объектов. Все объекты должны иметь уникальный идентификатор, который не зависит от значений их атрибутов. Поддержка инкапсуляции. Корректная инкапсуляция достигается за счет того, что программисты обладают правом доступа только к спецификации интерфейса методов, а данные и реализация методов скрыты внутри объектов. Поддержка типов и классов. Требуется, чтобы в ООБД поддерживалась хотя бы одна концепция различия между типами и классами. (Термин «тип» более соответствует понятию абстрактного типа данных. В языках программирования переменная объявляется с указанием её типа. Компилятор может использовать эту информацию для проверки выполняемых с переменной операций на совместимость с её типом, что позволяет гарантировать корректность программного обеспечения. С другой стороны класс является неким шаблоном для создания объектов и предоставляет методы, которые могут применяться к этим объектам. Таким образом, понятие «класс» в большей степени относится ко времени исполнения, чем ко времени компиляции.) Поддержка наследования типов и классов от их предков. Подтип, или подкласс, должен наследовать атрибуты и методы от его супертипа, или суперкласса, соответственно. Перегрузка в сочетании с полным связыванием. Методы должны применяться к объектам разных типов. Реализация метода должна зависеть от типа объектов, к которым данный метод применяется. Для обеспечения этой функциональности связывание имен методов в системе не должно выполняться до времени выполнения программы. Вычислительная полнота. Язык манипулирования данными должен быть языком программирования общего назначения. Набор типов данных должен быть расширяемым. Пользователь должен иметь средства создания новых типов данных на основе набора предопределенных системных типов. Более того, между способами использования системных и пользовательских типов данных не должно быть никаких различий."; +$sql = "INSERT INTO #Table VALUES (N'$data')"; +$stmt = sqlsrv_query($conn, $sql); +if( !$stmt ) { die( print_r( sqlsrv_errors(), true)); } + +// Query and fetch +$sql = "SELECT * FROM #Table"; +$stmt = sqlsrv_query($conn, $sql); +if( !$stmt ) { die( print_r( sqlsrv_errors(), true)); } +sqlsrv_fetch($stmt); + +// Get the data +$field = sqlsrv_get_field($stmt, 0, SQLSRV_PHPTYPE_STREAM('utf-8')); +$out = ""; +while(!feof($field)) +{ + $out .= fread($field, 512); +} + +// Output string length +var_dump(strlen($out)); + +// Compare output +echo ($out === $data) ? "True\n" : "False\n"; + +// Close connection +sqlsrv_free_stmt($stmt); +sqlsrv_close($conn); + +print "Done"; +?> + +--EXPECT-- +int(8408) +True +Done diff --git a/test/sqlsrv/srv_048_stream_nvarchar.phpt b/test/sqlsrv/srv_048_stream_nvarchar.phpt new file mode 100644 index 00000000..93e8972f --- /dev/null +++ b/test/sqlsrv/srv_048_stream_nvarchar.phpt @@ -0,0 +1,42 @@ +--TEST-- +Streaming nvarchar(max) with sqlsrv_fetch_array() +--SKIPIF-- +--FILE-- + + +--EXPECT-- +True +Done diff --git a/test/sqlsrv/srv_049_stream_nvarchar_utf8.phpt b/test/sqlsrv/srv_049_stream_nvarchar_utf8.phpt new file mode 100644 index 00000000..d245873a --- /dev/null +++ b/test/sqlsrv/srv_049_stream_nvarchar_utf8.phpt @@ -0,0 +1,43 @@ +--TEST-- +Streaming nvarchar(max) with sqlsrv_fetch_array() and CharacterSet=utf-8 +--SKIPIF-- +--FILE-- +$username, "PWD"=>$password, "CharacterSet"=>"utf-8"); +$conn = sqlsrv_connect($serverName, $connectionInfo); +if( !$conn ) { die( print_r( sqlsrv_errors(), true)); } + +// Create table +$sql = "CREATE TABLE #Table (c1 NVARCHAR(max))"; +$stmt = sqlsrv_query($conn, $sql); +if( !$stmt ) { die( print_r( sqlsrv_errors(), true)); } + +// Insert data +$data = "Первые публикации об объектно-ориентированных базах данных появились в середине 80-х годов. Поддержка сложных объектов. В системе должна быть предусмотрена возможность создания составных объектов за счет применения конструкторов составных объектов. Необходимо, чтобы конструкторы объектов были ортогональны, то есть любой конструктор можно было применять к любому объекту. Поддержка индивидуальности объектов. Все объекты должны иметь уникальный идентификатор, который не зависит от значений их атрибутов. Поддержка инкапсуляции. Корректная инкапсуляция достигается за счет того, что программисты обладают правом доступа только к спецификации интерфейса методов, а данные и реализация методов скрыты внутри объектов. Поддержка типов и классов. Требуется, чтобы в ООБД поддерживалась хотя бы одна концепция различия между типами и классами. (Термин «тип» более соответствует понятию абстрактного типа данных. В языках программирования переменная объявляется с указанием её типа. Компилятор может использовать эту информацию для проверки выполняемых с переменной операций на совместимость с её типом, что позволяет гарантировать корректность программного обеспечения. С другой стороны класс является неким шаблоном для создания объектов и предоставляет методы, которые могут применяться к этим объектам. Таким образом, понятие «класс» в большей степени относится ко времени исполнения, чем ко времени компиляции.) Поддержка наследования типов и классов от их предков. Подтип, или подкласс, должен наследовать атрибуты и методы от его супертипа, или суперкласса, соответственно. Перегрузка в сочетании с полным связыванием. Методы должны применяться к объектам разных типов. Реализация метода должна зависеть от типа объектов, к которым данный метод применяется. Для обеспечения этой функциональности связывание имен методов в системе не должно выполняться до времени выполнения программы. Вычислительная полнота. Язык манипулирования данными должен быть языком программирования общего назначения. Набор типов данных должен быть расширяемым. Пользователь должен иметь средства создания новых типов данных на основе набора предопределенных системных типов. Более того, между способами использования системных и пользовательских типов данных не должно быть никаких различий.Первые публикации об объектно-ориентированных базах данных появились в середине 80-х годов. Поддержка сложных объектов. В системе должна быть предусмотрена возможность создания составных объектов за счет применения конструкторов составных объектов. Необходимо, чтобы конструкторы объектов были ортогональны, то есть любой конструктор можно было применять к любому объекту. Поддержка индивидуальности объектов. Все объекты должны иметь уникальный идентификатор, который не зависит от значений их атрибутов. Поддержка инкапсуляции. Корректная инкапсуляция достигается за счет того, что программисты обладают правом доступа только к спецификации интерфейса методов, а данные и реализация методов скрыты внутри объектов. Поддержка типов и классов. Требуется, чтобы в ООБД поддерживалась хотя бы одна концепция различия между типами и классами. (Термин «тип» более соответствует понятию абстрактного типа данных. В языках программирования переменная объявляется с указанием её типа. Компилятор может использовать эту информацию для проверки выполняемых с переменной операций на совместимость с её типом, что позволяет гарантировать корректность программного обеспечения. С другой стороны класс является неким шаблоном для создания объектов и предоставляет методы, которые могут применяться к этим объектам. Таким образом, понятие «класс» в большей степени относится ко времени исполнения, чем ко времени компиляции.) Поддержка наследования типов и классов от их предков. Подтип, или подкласс, должен наследовать атрибуты и методы от его супертипа, или суперкласса, соответственно. Перегрузка в сочетании с полным связыванием. Методы должны применяться к объектам разных типов. Реализация метода должна зависеть от типа объектов, к которым данный метод применяется. Для обеспечения этой функциональности связывание имен методов в системе не должно выполняться до времени выполнения программы. Вычислительная полнота. Язык манипулирования данными должен быть языком программирования общего назначения. Набор типов данных должен быть расширяемым. Пользователь должен иметь средства создания новых типов данных на основе набора предопределенных системных типов. Более того, между способами использования системных и пользовательских типов данных не должно быть никаких различий."; +$sql = "INSERT INTO #Table VALUES (N'$data')"; +$stmt = sqlsrv_query($conn, $sql); +if( !$stmt ) { die( print_r( sqlsrv_errors(), true)); } + +// Fetch +$sql = "SELECT * FROM #Table"; +$stmt = sqlsrv_query($conn, $sql); +if( !$stmt ) { die( print_r( sqlsrv_errors(), true)); } + +// Compare output +$row = sqlsrv_fetch_array($stmt); +echo ($row['c1'] == $data) ? "True\n" : "False\n"; + +// Close connection +sqlsrv_free_stmt($stmt); +sqlsrv_close($conn); + +print "Done"; +?> + +--EXPECT-- +True +Done diff --git a/test/sqlsrv/srv_050_error_conversion_varchar_int.phpt b/test/sqlsrv/srv_050_error_conversion_varchar_int.phpt new file mode 100644 index 00000000..6ffd189e --- /dev/null +++ b/test/sqlsrv/srv_050_error_conversion_varchar_int.phpt @@ -0,0 +1,46 @@ +--TEST-- +Error checking for failed conversion: VARCHAR value 'null' to data type INT +--SKIPIF-- +--FILE-- + + +--EXPECT-- +Array +( + [0] => 22018 + [SQLSTATE] => 22018 + [1] => 245 + [code] => 245 + [2] => [Microsoft][ODBC Driver 13 for SQL Server][SQL Server]Conversion failed when converting the varchar value 'null' to data type int. + [message] => [Microsoft][ODBC Driver 13 for SQL Server][SQL Server]Conversion failed when converting the varchar value 'null' to data type int. +) +Done \ No newline at end of file diff --git a/test/sqlsrv/srv_051_error_conversion_nchar.phpt b/test/sqlsrv/srv_051_error_conversion_nchar.phpt new file mode 100644 index 00000000..46870645 --- /dev/null +++ b/test/sqlsrv/srv_051_error_conversion_nchar.phpt @@ -0,0 +1,47 @@ +--TEST-- +Error checking for failed explicit data type conversions NCHAR(2) +--SKIPIF-- +--FILE-- +$username, "PWD"=>$password, "CharacterSet"=>"UTF-8"); +$conn = sqlsrv_connect($serverName, $connectionInfo) ?: die(); + +// Create database +sqlsrv_query($conn,"CREATE DATABASE ". $dbName) ?: die(); + +// Create table. Column names: passport +$sql = "CREATE TABLE $tableName (c1 NCHAR(2))"; +$stmt = sqlsrv_query($conn, $sql); + +// Insert data. Invalid statement +$sql = "INSERT INTO $tableName VALUES (10),(N'银河')"; +$stmt = sqlsrv_query($conn, $sql); + +// Get extended error +$err = sqlsrv_errors(); +print_r($err[0]); + +// DROP database +sqlsrv_query($conn,"DROP DATABASE ". $dbName); + +// Free statement and connection resources +sqlsrv_close($conn); + +print "Done" +?> + +--EXPECT-- +Array +( + [0] => 22018 + [SQLSTATE] => 22018 + [1] => 245 + [code] => 245 + [2] => [Microsoft][ODBC Driver 13 for SQL Server][SQL Server]Conversion failed when converting the nvarchar value '银河' to data type int. + [message] => [Microsoft][ODBC Driver 13 for SQL Server][SQL Server]Conversion failed when converting the nvarchar value '银河' to data type int. +) +Done diff --git a/test/sqlsrv/srv_052_mars.phpt b/test/sqlsrv/srv_052_mars.phpt new file mode 100644 index 00000000..ee61f5c4 --- /dev/null +++ b/test/sqlsrv/srv_052_mars.phpt @@ -0,0 +1,40 @@ +--TEST-- +Enable multiple active result sets (MARS) +--SKIPIF-- +--FILE-- +$username, "PWD"=>$password, 'MultipleActiveResultSets' => true); +$conn = sqlsrv_connect($serverName, $connectionInfo) ?: die(); + +// Query +$stmt1 = sqlsrv_query( $conn, "SELECT 'ONE'" ) ?: die(print_r( sqlsrv_errors(), true )); +sqlsrv_fetch( $stmt1 ); + +// Query. Returns if multiple result sets are disabled +$stmt2 = sqlsrv_query( $conn, "SELECT 'TWO'" ) ?: die(print_r( sqlsrv_errors(), true )); +sqlsrv_fetch( $stmt2 ); + +// Print the data +$res = [ sqlsrv_get_field($stmt1, 0), sqlsrv_get_field($stmt2, 0) ]; +var_dump($res); + +// Free statement and connection resources +sqlsrv_free_stmt($stmt1); +sqlsrv_free_stmt($stmt2); +sqlsrv_close($conn); + +print "Done" +?> + +--EXPECT-- +array(2) { + [0]=> + string(3) "ONE" + [1]=> + string(3) "TWO" +} +Done diff --git a/test/sqlsrv/srv_053_mars_disabled_error_checks.phpt b/test/sqlsrv/srv_053_mars_disabled_error_checks.phpt new file mode 100644 index 00000000..0f03db7c --- /dev/null +++ b/test/sqlsrv/srv_053_mars_disabled_error_checks.phpt @@ -0,0 +1,56 @@ +--TEST-- +Error checking for multiple active result sets (MARS) disabled +--SKIPIF-- +--FILE-- +$username, "PWD"=>$password, 'MultipleActiveResultSets' => false); +$conn = sqlsrv_connect($serverName, $connectionInfo) ?: die(); + +// Query +$stmt1 = sqlsrv_query( $conn, "SELECT 'ONE'" ) ?: die(print_r( sqlsrv_errors(), true )); +sqlsrv_fetch( $stmt1 ); + +// Query. Returns if multiple result sets are disabled +$stmt2 = sqlsrv_query( $conn, "SELECT 'TWO'" ) ?: die(print_r( sqlsrv_errors(), true )); +sqlsrv_fetch( $stmt2 ); + +// Print the data +$res = [ sqlsrv_get_field($stmt1, 0), sqlsrv_get_field($stmt2, 0) ]; +var_dump($res); + +// Free statement and connection resources +sqlsrv_free_stmt($stmt1); +sqlsrv_free_stmt($stmt2); +sqlsrv_close($conn); + +print "Done" +?> + +--EXPECT-- +Array +( + [0] => Array + ( + [0] => IMSSP + [SQLSTATE] => IMSSP + [1] => -44 + [code] => -44 + [2] => The connection cannot process this operation because there is a statement with pending results. To make the connection available for other queries, either fetch all results or cancel or free the statement. For more information, see the product documentation about the MultipleActiveResultSets connection option. + [message] => The connection cannot process this operation because there is a statement with pending results. To make the connection available for other queries, either fetch all results or cancel or free the statement. For more information, see the product documentation about the MultipleActiveResultSets connection option. + ) + + [1] => Array + ( + [0] => HY000 + [SQLSTATE] => HY000 + [1] => 0 + [code] => 0 + [2] => [Microsoft][ODBC Driver 13 for SQL Server]Connection is busy with results for another command + [message] => [Microsoft][ODBC Driver 13 for SQL Server]Connection is busy with results for another command + ) + +)