in source/pdo_sqlsrv/pdo_dbh.cpp [713:872]
int pdo_sqlsrv_dbh_prepare(_Inout_ pdo_dbh_t *dbh, _In_reads_(sql_len) const char *sql,
_Inout_ size_t sql_len, _Inout_ pdo_stmt_t *stmt, _In_ zval *driver_options)
#else
bool pdo_sqlsrv_dbh_prepare(_Inout_ pdo_dbh_t *dbh, _In_ zend_string *sql_zstr, _Inout_ pdo_stmt_t *stmt, _In_ zval *driver_options)
#endif
{
PDO_RESET_DBH_ERROR;
PDO_VALIDATE_CONN;
PDO_LOG_DBH_ENTRY;
hash_auto_ptr pdo_stmt_options_ht;
sqlsrv_malloc_auto_ptr<char> sql_rewrite;
size_t sql_rewrite_len = 0;
sqlsrv_malloc_auto_ptr<pdo_sqlsrv_stmt> driver_stmt;
hash_auto_ptr placeholders;
sqlsrv_malloc_auto_ptr<sql_string_parser> sql_parser;
pdo_sqlsrv_dbh* driver_dbh = reinterpret_cast<pdo_sqlsrv_dbh*>( dbh->driver_data );
SQLSRV_ASSERT(( driver_dbh != NULL ), "pdo_sqlsrv_dbh_prepare: dbh->driver_data was null");
try {
// assign the methods for the statement object. This is necessary even if the
// statement fails so the user can retrieve the error information.
stmt->methods = &pdo_sqlsrv_stmt_methods;
// if not emulate_prepare, we support parameterized queries with ?, not names
stmt->supports_placeholders = (driver_dbh->emulate_prepare) ? PDO_PLACEHOLDER_NONE : PDO_PLACEHOLDER_POSITIONAL; // the statement options may override this later
// Initialize the options array to be passed to the core layer
ALLOC_HASHTABLE( pdo_stmt_options_ht );
core::sqlsrv_zend_hash_init( *driver_dbh , pdo_stmt_options_ht, 3 /* # 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.
validate_stmt_options( *driver_dbh, driver_options, pdo_stmt_options_ht );
driver_stmt = static_cast<pdo_sqlsrv_stmt*>( core_sqlsrv_create_stmt( driver_dbh, core::allocate_stmt<pdo_sqlsrv_stmt>,
pdo_stmt_options_ht, PDO_STMT_OPTS,
pdo_sqlsrv_handle_stmt_error, stmt ));
// if the user didn't set anything in the prepare options, then set the buffer limit
// to the value set on the connection.
if( driver_stmt->buffered_query_limit== sqlsrv_buffered_result_set::BUFFERED_QUERY_LIMIT_INVALID ) {
driver_stmt->buffered_query_limit = driver_dbh->client_buffer_max_size;
}
#if PHP_VERSION_ID >= 80100
zend_string* sql_rewrite_zstr = NULL;
const char* sql = ZSTR_VAL(sql_zstr);
size_t sql_len = ZSTR_LEN(sql_zstr);
#endif
// rewrite named parameters in the query to positional parameters if we aren't letting PDO do the
// parameter substitution for us
if( stmt->supports_placeholders != PDO_PLACEHOLDER_NONE ) {
// rewrite the query to map named parameters to positional parameters. We do this rather than use the ODBC named
// parameters for consistency with the PDO MySQL and PDO ODBC drivers.
#if PHP_VERSION_ID < 80100
int zr = pdo_parse_params( stmt, const_cast<char*>( sql ), sql_len, &sql_rewrite, &sql_rewrite_len );
CHECK_ZEND_ERROR(zr, driver_dbh, PDO_SQLSRV_ERROR_PARAM_PARSE) {
throw core::CoreException();
}
// if parameter substitution happened, use that query instead of the original
if (sql_rewrite != 0) {
sql = sql_rewrite;
sql_len = sql_rewrite_len;
}
#else
int zr = pdo_parse_params(stmt, sql_zstr, &sql_rewrite_zstr);
CHECK_ZEND_ERROR(zr, driver_dbh, PDO_SQLSRV_ERROR_PARAM_PARSE) {
throw core::CoreException();
}
// if parameter substitution happened, use that query instead of the original
if (sql_rewrite_zstr != NULL) {
sql = ZSTR_VAL(sql_rewrite_zstr);
sql_len = ZSTR_LEN(sql_rewrite_zstr);
}
#endif
}
if( !driver_stmt->direct_query && stmt->supports_placeholders != PDO_PLACEHOLDER_NONE ) {
core_sqlsrv_prepare(driver_stmt, sql, sql_len);
}
else if( driver_stmt->direct_query ) {
if( driver_stmt->direct_query_subst_string ) {
// we use efree rather than sqlsrv_free since sqlsrv_free may wrap another allocation scheme
// and we use estrdup below to allocate the new string, which uses emalloc
efree( reinterpret_cast<void*>( const_cast<char*>( driver_stmt->direct_query_subst_string )));
}
driver_stmt->direct_query_subst_string = estrdup( sql );
driver_stmt->direct_query_subst_string_len = sql_len;
}
#if PHP_VERSION_ID >= 80100
if (sql_rewrite_zstr != NULL) {
zend_string_release(sql_rewrite_zstr);
}
#endif
// else if stmt->support_placeholders == PDO_PLACEHOLDER_NONE means that stmt->active_query_string will be
// set to the substituted query
if ( stmt->supports_placeholders == PDO_PLACEHOLDER_NONE ) {
// parse placeholders in the sql query into the placeholders ht
ALLOC_HASHTABLE( placeholders );
core::sqlsrv_zend_hash_init(*driver_dbh, placeholders, 5, ZVAL_PTR_DTOR /* dtor */, 0 /* persistent */);
#if PHP_VERSION_ID < 80100
sql_parser = new (sqlsrv_malloc(sizeof(sql_string_parser))) sql_string_parser(*driver_dbh, stmt->query_string,
static_cast<int>(stmt->query_stringlen), placeholders);
#else
sql_parser = new (sqlsrv_malloc(sizeof(sql_string_parser))) sql_string_parser(*driver_dbh, ZSTR_VAL(stmt->query_string),
ZSTR_LEN(stmt->query_string), placeholders);
#endif
sql_parser->parse_sql_string();
driver_stmt->placeholders = placeholders;
placeholders.transferred();
}
stmt->driver_data = driver_stmt;
driver_stmt.transferred();
}
// everything is cleaned up by this point
// catch everything so the exception doesn't spill into the calling PDO code
catch( core::CoreException& ) {
if( driver_stmt ) {
driver_stmt->~pdo_sqlsrv_stmt();
}
// in the event that the statement caused an error that was copied to the connection, update the
// connection with the error's SQLSTATE.
if( driver_dbh->last_error() ) {
strcpy_s( dbh->error_code, sizeof( dbh->error_code ),
reinterpret_cast<const char*>( driver_dbh->last_error()->sqlstate ));
}
#if PHP_VERSION_ID < 80100
return 0;
#else
return false;
#endif
}
// catch any errant exception and die
catch(...) {
DIE( "pdo_sqlsrv_dbh_prepare: Unknown exception caught." );
}
#if PHP_VERSION_ID < 80100
return 1;
#else
return true;
#endif
}