From c0c7eb1c8d0022252349e0f66bb8288af09978e4 Mon Sep 17 00:00:00 2001 From: Carlos Proensa Date: Tue, 5 Feb 2019 00:49:19 +0100 Subject: [PATCH] Fix mssql concurrent insert id The implementation of db_insert_id() in mssql relied on IDENT_CURRENT making it not thread safe. Modify it to use ADOdb native implementation, based on SCOPE_IDENTITY() which is session independent. Fixes: #25442 --- core/database_api.php | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/core/database_api.php b/core/database_api.php index 034fb813db..d581b66d93 100644 --- a/core/database_api.php +++ b/core/database_api.php @@ -451,32 +451,41 @@ function db_result( $p_result, $p_row_index = 0, $p_col_index = 0 ) { } /** - * return the last inserted id for a specific database table + * Return the last inserted ID after a insert statement. + * Warning: this function must be used immediately after the insert statement + * + * This relies on ADOdb to get the entity id when this functionality is available + * for the specific driver, and it makes sense in our model. + * Natively supported: + * - mysqli, using: mysqli_insert_id(connection). + * - mssqlnative, using SCOPE_IDENTITY(). + * Not natively supported: + * - pgsql, oracle, using the underlying sequence for the table. + * + * Since the table is needed for those drivers where a sequence is used, the + * $p_table parameter is mandatory to ensure portability. + * Warning: $p_table is not expected to be a different table than the one used + * for the previous insert. Note that it's not even used by some drivers. + * * @param string $p_table A valid database table name. * @param string $p_field A valid field name (default 'id'). * @return integer last successful insert id */ -function db_insert_id( $p_table = null, $p_field = 'id' ) { +function db_insert_id( $p_table, $p_field = 'id' ) { global $g_db, $g_db_functional_type; - if( isset( $p_table ) ) { - switch( $g_db_functional_type ) { + switch( $g_db_functional_type ) { case DB_TYPE_ORACLE: $t_query = 'SELECT seq_' . $p_table . '.CURRVAL FROM DUAL'; break; case DB_TYPE_PGSQL: $t_query = 'SELECT currval(\'' . $p_table . '_' . $p_field . '_seq\')'; break; - case DB_TYPE_MSSQL: - $t_query = 'SELECT IDENT_CURRENT(\'' . $p_table . '\')'; - break; - } - if( isset( $t_query ) ) { - $t_result = db_query( $t_query ); - return (int)db_result( $t_result ); - } + default: + return $g_db->Insert_ID(); } - return $g_db->Insert_ID(); + $t_result = db_query( $t_query ); + return (int)db_result( $t_result ); } /**