Skip to content

Commit

Permalink
Merge pull request #77 from WebFiori/dev
Browse files Browse the repository at this point in the history
Fix to a Bug in Transactions + Added Documentation
  • Loading branch information
usernane authored Aug 13, 2023
2 parents b7602ff + 650ffc9 commit 488ecb2
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 22 deletions.
10 changes: 6 additions & 4 deletions tests/webfiori/database/tests/mssql/MSSQLQueryBuilderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -276,10 +276,12 @@ public function testTransaction02(int $userId) {
'age' => 32
])->where('id', $uId)->execute();

$db->table('users')->insert([
'first-name' => 'Ibrahim',
'last-name' => 'BinAlshikh',
])->execute();
$db->transaction(function (Database $db) {
$db->table('users')->insert([
'first-name' => 'Ibrahim',
'last-name' => 'BinAlshikh',
])->execute();
});
}, [$userId]);
} catch (DatabaseException $ex) {
$this->assertEquals("515 - [Microsoft][ODBC Driver 17 for SQL Server][SQL Server]Cannot insert the value NULL into column 'age', table 'testing_db.dbo.users'; column does not allow nulls. INSERT fails.", $ex->getMessage());
Expand Down
14 changes: 8 additions & 6 deletions tests/webfiori/database/tests/mysql/MySQLQueryBuilderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -1881,20 +1881,22 @@ public function testTransaction02(int $userId) {
'age' => 32
])->where('id', $uId)->execute();

$db->table('users')->insert([
'first-name' => 'Ibrahim',
'last-name' => 'BinAlshikh',
])->execute();
$db->transaction(function (Database $db) {
$db->table('users')->insert([
'first-name' => 'Ibrahim',
'last-name' => 'BinAlshikh',
])->execute();
});
}, [$userId]);
} catch (DatabaseException $ex) {
$this->assertEquals("1364 - Field 'age' doesn't have a default value", $ex->getMessage());
$this->assertEquals(1364, $ex->getCode());
$user = $schema->table('users')->select()->where('id', $userId)->execute()->getRows()[0];
$this->assertEquals([
'id' => $userId,
'first_name' => 'Ibrahim',
'first_name' => 'Ali',
'last_name' => 'BinAlshikh',
'age' => 30
'age' => 32
], $user);
}

Expand Down
11 changes: 8 additions & 3 deletions webfiori/database/Database.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,13 @@
*/
namespace webfiori\database;

use Exception;
use webfiori\database\mssql\MSSQLConnection;
use webfiori\database\mssql\MSSQLQuery;
use webfiori\database\mssql\MSSQLTable;
use webfiori\database\mysql\MySQLConnection;
use webfiori\database\mysql\MySQLQuery;
use webfiori\database\mysql\MySQLTable;
/**
* A class which is used to represent the structure of the database
* (database schema).
Expand Down Expand Up @@ -85,6 +88,8 @@ public function __construct(ConnectionInfo $connectionInfo = null) {
/**
* Start SQL transaction.
*
* This will disable auto-commit.
*
* @param callable $transaction A function that holds the logic of the transaction.
* The function must return true or null for success. If false is
* returned, it means the transaction failed and will be rolled back.
Expand Down Expand Up @@ -115,7 +120,7 @@ public function transaction(callable $transaction, array $transactionArgs = [])
$conn->rollBack($name);
return false;
}
} catch (DatabaseException $ex) {
} catch (Exception $ex) {
$conn->rollBack($name);
throw new DatabaseException($ex->getMessage(), $ex->getCode(), $ex->getSQLQuery(), $ex);
}
Expand Down Expand Up @@ -233,9 +238,9 @@ public function createBlueprint(string $name) : Table {
$dbType = $this->getConnection()->getConnectionInfo()->getDatabaseType();

if ($dbType == 'mysql') {
$blueprint = new mysql\MySQLTable($name);
$blueprint = new MySQLTable($name);
} else if ($dbType == 'mssql') {
$blueprint = new mssql\MSSQLTable($name);
$blueprint = new MSSQLTable($name);
}
$this->addTable($blueprint);

Expand Down
53 changes: 44 additions & 9 deletions webfiori/database/mssql/MSSQLConnection.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@
*
* @author Ibrahim
*
* @version 1.0
*/
class MSSQLConnection extends Connection {
private $link;
private $sqlState;
private $isTransactionStarted;
/**
* Creates new instance of the class.
*
Expand All @@ -37,15 +37,14 @@ class MSSQLConnection extends Connection {
* @throws DatabaseException If the connection to the database fails, the method
* will throw an exception.
*
* @since 1.0
*/
public function __construct(ConnectionInfo $connInfo) {
parent::__construct($connInfo);
$this->isTransactionStarted = false;
}
/**
* Close database connection.
*
* @since 1.0
*/
public function __destruct() {
sqlsrv_close($this->link);
Expand All @@ -56,7 +55,6 @@ public function __destruct() {
* @return bool If the connection was established, the method will return
* true. If the attempt to connect fails, the method will return false.
*
* @since 1.0
*/
public function connect() : bool {
if (!function_exists('sqlsrv_connect')) {
Expand Down Expand Up @@ -103,7 +101,6 @@ public function connect() : bool {
*
* @return string|null
*
* @since 1.0
*/
public function getSQLState() {
return $this->sqlState;
Expand All @@ -117,7 +114,6 @@ public function getSQLState() {
* @return bool If the query successfully executed, the method will return
* true. Other than that, the method will return false.
*
* @since 1.0
*/
public function runQuery(AbstractQuery $query = null) {
$this->addToExecuted($query->getQuery());
Expand Down Expand Up @@ -223,31 +219,70 @@ private function setSqlErr() {
$this->setErrCode($lastErr['code']);
}
}

/**
* Starts SQL server transaction.
*
* Note that calling this method multiple times will have no effect on number
* of created transactions.
*
* @param string|null $name This parameter is ignored.
*
* @throws DatabaseException If the method was not able to start the transaction.
*/
public function beginTransaction(string $name = null) {
if ($this->isTransactionStarted) {
return;
}
$r = sqlsrv_begin_transaction($this->link);

if (!$r) {
$this->setSqlErr();
throw new DatabaseException($this->getSQLState().' - '.$this->getLastErrMessage());
}
$this->isTransactionStarted = true;
}

/**
* Commit transaction changes to database.
*
* Note that if no transaction is started, calling this method will have
* no effect.
*
* @param string|null $name This parameter is ignored.
*
* @throws DatabaseException If the method was not able to commit the transaction.
*/
public function commit(string $name = null) {
if (!$this->isTransactionStarted) {
return;
}
$r = sqlsrv_commit($this->link);

if (!$r) {
$this->setSqlErr();
throw new DatabaseException($this->getSQLState().' - '.$this->getLastErrMessage());
}
$this->isTransactionStarted = false;
}

/**
* Roll back a transaction.
*
* Note that if no transaction is started, calling this method will have
* no effect.
*
* @param string|null $name This parameter is ignored.
*
* @throws DatabaseException If the method was not able to rollback the transaction.
*/
public function rollBack(string $name = null) {
if (!$this->isTransactionStarted) {
return;
}
$r = sqlsrv_rollback($this->link);

if (!$r) {
$this->setSqlErr();
throw new DatabaseException($this->getSQLState().' - '.$this->getLastErrMessage());
}
$this->isTransactionStarted = false;
}
}

0 comments on commit 488ecb2

Please sign in to comment.