int pdo_sqlsrv_db_handle_factory()

in source/pdo_sqlsrv/pdo_dbh.cpp [570:661]


int pdo_sqlsrv_db_handle_factory( _Inout_ pdo_dbh_t *dbh, _In_opt_ zval *driver_options) 
{
    PDO_LOG_DBH_ENTRY;

    hash_auto_ptr pdo_conn_options_ht;
    pdo_error_mode prev_err_mode = dbh->error_mode;

    // must be done in all cases so that even a failed connection can query the
    // object for errors.
    dbh->methods = &pdo_sqlsrv_dbh_methods;
    dbh->driver_data = NULL;
    zval* temp_server_z = NULL;
    sqlsrv_malloc_auto_ptr<conn_string_parser> dsn_parser;
    zval server_z;
    ZVAL_UNDEF( &server_z );

    try {
 
    // no matter what the error mode, we want exceptions thrown if the connection fails
    // to happen (per the PDO spec)
    dbh->error_mode = PDO_ERRMODE_EXCEPTION;

    g_pdo_henv_cp->set_driver( dbh );
    g_pdo_henv_ncp->set_driver( dbh );

    CHECK_CUSTOM_ERROR( driver_options && Z_TYPE_P( driver_options ) != IS_ARRAY, *g_pdo_henv_cp, SQLSRV_ERROR_CONN_OPTS_WRONG_TYPE ) {
        throw core::CoreException();
    }
	// throws PDOException if the ATTR_PERSISTENT is in connection options
	CHECK_CUSTOM_ERROR( dbh->is_persistent, *g_pdo_henv_cp, PDO_SQLSRV_ERROR_UNSUPPORTED_DBH_ATTR ) {
		dbh->refcount--;
		throw pdo::PDOException();
	}
	
    // Initialize the options array to be passed to the core layer
    ALLOC_HASHTABLE( pdo_conn_options_ht );

    core::sqlsrv_zend_hash_init( *g_pdo_henv_cp, pdo_conn_options_ht, 10 /* # of buckets */, 
                                 ZVAL_PTR_DTOR, 0 /*persistent*/ );

    // Either of g_pdo_henv_cp or g_pdo_henv_ncp can be used to propogate the error.
    dsn_parser = new ( sqlsrv_malloc( sizeof( conn_string_parser ))) conn_string_parser( *g_pdo_henv_cp, dbh->data_source, 
                                                                                          static_cast<int>( dbh->data_source_len ), pdo_conn_options_ht );
    dsn_parser->parse_conn_string();
    
    // Extract the server name
    temp_server_z = zend_hash_index_find( pdo_conn_options_ht, PDO_CONN_OPTION_SERVER );

    CHECK_CUSTOM_ERROR(( temp_server_z == NULL ), g_pdo_henv_cp, PDO_SQLSRV_ERROR_SERVER_NOT_SPECIFIED ) {
        
        throw pdo::PDOException();
    }

    server_z = *temp_server_z;

    // Add a reference to the option value since we are deleting it from the hashtable
    zval_add_ref( &server_z );
    zend_hash_index_del( pdo_conn_options_ht, PDO_CONN_OPTION_SERVER );

    sqlsrv_conn* conn = core_sqlsrv_connect( *g_pdo_henv_cp, *g_pdo_henv_ncp, core::allocate_conn<pdo_sqlsrv_dbh>, Z_STRVAL( server_z ), 
                                             dbh->username, dbh->password, pdo_conn_options_ht, pdo_sqlsrv_handle_dbh_error, 
                                             PDO_CONN_OPTS, dbh, "pdo_sqlsrv_db_handle_factory" );

    // Free the string in server_z after being used
    zend_string_release( Z_STR( server_z ));
                                
    SQLSRV_ASSERT( conn != NULL, "Invalid connection returned.  Exception should have been thrown." );

    // set the driver_data and methods to complete creation of the PDO object
    dbh->driver_data = conn;
    dbh->error_mode = prev_err_mode;    // reset the error mode
    dbh->alloc_own_columns = 1;         // we do our own memory management for columns
    dbh->native_case = PDO_CASE_NATURAL;// SQL Server supports mixed case types

    }
    catch( core::CoreException& ) {
    
        if ( Z_TYPE( server_z ) == IS_STRING ) {
            zend_string_release( Z_STR( server_z ));
        }
        dbh->error_mode = prev_err_mode;    // reset the error mode
        g_pdo_henv_cp->last_error().reset();    // reset the last error; callee will check if last_error exist before freeing it and setting it to NULL
        
        return 0;
    }
    catch( ... ) {

        DIE( "pdo_sqlsrv_db_handle_factory: Unknown exception caught" );
    }

    return 1;
}