@@ -94,14 +94,10 @@ class Connection extends BaseConnection
9494 */
9595 protected function isUniqueConstraintViolation (int |string $ code , string $ message ): bool
9696 {
97- $ code = ( string ) $ code ;
97+ $ vendorCode = $ this -> getVendorErrorCode ( $ code) ;
9898
99- if (str_contains ($ code , '/ ' )) {
100- [$ sqlstate , $ vendorCode ] = explode ('/ ' , $ code , 2 );
101-
102- if ($ sqlstate === '23000 ' && in_array ((int ) $ vendorCode , [2627 , 2601 ], true )) {
103- return true ;
104- }
99+ if ($ vendorCode !== null && in_array ($ vendorCode , [2627 , 2601 ], true )) {
100+ return $ this ->hasSQLState ($ code , '23000 ' );
105101 }
106102
107103 $ errors = sqlsrv_errors (SQLSRV_ERR_ERRORS );
@@ -121,20 +117,55 @@ protected function isUniqueConstraintViolation(int|string $code, string $message
121117 return false ;
122118 }
123119
120+ /**
121+ * Checks whether the native database error represents a NOT NULL constraint violation.
122+ */
123+ protected function isNotNullConstraintViolation (int |string $ code , string $ message ): bool
124+ {
125+ return $ this ->getVendorErrorCode ($ code ) === 515
126+ && $ this ->hasSQLState ($ code , '23000 ' );
127+ }
128+
129+ /**
130+ * Checks whether the native database error represents a constraint violation.
131+ */
132+ protected function isConstraintViolation (int |string $ code , string $ message ): bool
133+ {
134+ return $ this ->getSQLState ($ code ) === '23000 '
135+ || ($ this ->getVendorErrorCode ($ code ) === 547 && $ this ->hasSQLState ($ code , '23000 ' ));
136+ }
137+
124138 /**
125139 * Checks whether the native database code represents a retryable transaction failure.
126140 */
127141 protected function isRetryableTransactionErrorCode (int |string $ code ): bool
142+ {
143+ $ vendorCode = $ this ->getVendorErrorCode ($ code );
144+
145+ return $ vendorCode !== null && in_array ($ vendorCode , [1205 , 3960 ], true );
146+ }
147+
148+ private function getVendorErrorCode (int |string $ code ): ?int
128149 {
129150 $ vendorCode = (string ) (is_string ($ code ) && str_contains ($ code , '/ ' )
130151 ? substr ($ code , strrpos ($ code , '/ ' ) + 1 )
131152 : $ code );
132153
133- if (preg_match ('/^\d+$/ ' , $ vendorCode ) !== 1 ) {
134- return false ;
135- }
154+ return preg_match ('/^\d+$/ ' , $ vendorCode ) === 1 ? (int ) $ vendorCode : null ;
155+ }
156+
157+ private function getSQLState (int |string $ code ): string
158+ {
159+ return is_string ($ code ) && str_contains ($ code , '/ ' )
160+ ? substr ($ code , 0 , strpos ($ code , '/ ' ))
161+ : (string ) $ code ;
162+ }
136163
137- return in_array ((int ) $ vendorCode , [1205 , 3960 ], true );
164+ private function hasSQLState (int |string $ code , string $ sqlstate ): bool
165+ {
166+ return ! is_string ($ code )
167+ || ! str_contains ($ code , '/ ' )
168+ || $ this ->getSQLState ($ code ) === $ sqlstate ;
138169 }
139170
140171 /**
0 commit comments