Skip to content

docs: update DB transactions #6886

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
Dec 11, 2022
221 changes: 221 additions & 0 deletions tests/system/Database/Live/TransactionTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
<?php

/**
* This file is part of CodeIgniter 4 framework.
*
* (c) CodeIgniter Foundation <[email protected]>
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/

namespace CodeIgniter\Database\Live;

use CodeIgniter\Database\Exceptions\DatabaseException;
use CodeIgniter\Test\CIUnitTestCase;
use CodeIgniter\Test\DatabaseTestTrait;
use Config\Database;
use Tests\Support\Database\Seeds\CITestSeeder;

/**
* @group DatabaseLive
*
* @internal
*/
final class TransactionTest extends CIUnitTestCase
{
use DatabaseTestTrait;

protected $refresh = true;
protected $seed = CITestSeeder::class;

protected function setUp(): void
{
// Reset connection instance.
$this->db = Database::connect($this->DBGroup, false);

parent::setUp();
}

/**
* Sets $DBDebug to false.
*
* WARNING: this value will persist! take care to roll it back.
*/
protected function disableDBDebug(): void
{
$this->setPrivateProperty($this->db, 'DBDebug', false);
}

/**
* Sets $DBDebug to true.
*/
protected function enableDBDebug(): void
{
$this->setPrivateProperty($this->db, 'DBDebug', true);
}

public function testTransStartDBDebugTrue()
{
$builder = $this->db->table('job');
$e = null;

try {
$this->db->transStart();

$jobData = [
'name' => 'Grocery Sales',
'description' => 'Discount!',
];
$builder->insert($jobData);

// Duplicate entry '1' for key 'PRIMARY'
$jobData = [
'id' => 1,
'name' => 'Comedian',
'description' => 'Theres something in your teeth',
];
$builder->insert($jobData);

$this->db->transComplete();
} catch (DatabaseException $e) {
// Do nothing.
}

$this->assertInstanceOf(DatabaseException::class, $e);
$this->dontSeeInDatabase('job', ['name' => 'Grocery Sales']);
}

public function testTransStartDBDebugFalse()
{
$this->disableDBDebug();

$builder = $this->db->table('job');

$this->db->transStart();

$jobData = [
'name' => 'Grocery Sales',
'description' => 'Discount!',
];
$builder->insert($jobData);

$this->assertTrue($this->db->transStatus());

// Duplicate entry '1' for key 'PRIMARY'
$jobData = [
'id' => 1,
'name' => 'Comedian',
'description' => 'Theres something in your teeth',
];
$builder->insert($jobData);

$this->assertFalse($this->db->transStatus());

$this->db->transComplete();

$this->dontSeeInDatabase('job', ['name' => 'Grocery Sales']);

$this->enableDBDebug();
}

public function testTransStrictTrueAndDBDebugFalse()
{
$this->disableDBDebug();

$builder = $this->db->table('job');

// The first transaction group
$this->db->transStart();

$jobData = [
'name' => 'Grocery Sales',
'description' => 'Discount!',
];
$builder->insert($jobData);

$this->assertTrue($this->db->transStatus());

// Duplicate entry '1' for key 'PRIMARY'
$jobData = [
'id' => 1,
'name' => 'Comedian',
'description' => 'Theres something in your teeth',
];
$builder->insert($jobData);

$this->assertFalse($this->db->transStatus());

$this->db->transComplete();

$this->dontSeeInDatabase('job', ['name' => 'Grocery Sales']);

// The second transaction group
$this->db->transStart();

$jobData = [
'name' => 'Comedian',
'description' => 'Theres something in your teeth',
];
$builder->insert($jobData);

$this->assertFalse($this->db->transStatus());

$this->db->transComplete();

$this->dontSeeInDatabase('job', ['name' => 'Comedian']);

$this->enableDBDebug();
}

public function testTransStrictFalseAndDBDebugFalse()
{
$this->disableDBDebug();

$builder = $this->db->table('job');

$this->db->transStrict(false);

// The first transaction group
$this->db->transStart();

$jobData = [
'name' => 'Grocery Sales',
'description' => 'Discount!',
];
$builder->insert($jobData);

$this->assertTrue($this->db->transStatus());

// Duplicate entry '1' for key 'PRIMARY'
$jobData = [
'id' => 1,
'name' => 'Comedian',
'description' => 'Theres something in your teeth',
];
$builder->insert($jobData);

$this->assertFalse($this->db->transStatus());

$this->db->transComplete();

$this->dontSeeInDatabase('job', ['name' => 'Grocery Sales']);

// The second transaction group
$this->db->transStart();

$jobData = [
'name' => 'Comedian',
'description' => 'Theres something in your teeth',
];
$builder->insert($jobData);

$this->assertTrue($this->db->transStatus());

$this->db->transComplete();

$this->seeInDatabase('job', ['name' => 'Comedian']);

$this->enableDBDebug();
}
}
10 changes: 8 additions & 2 deletions user_guide_src/source/changelogs/v4.3.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,22 @@ Exceptions when Database Errors Occur

- The exceptions thrown by the database connection classes have been changed to ``CodeIgniter\Database\Exceptions\DatabaseException``. Previously, different database drivers threw different exception classes, but these have been unified into ``DatabaseException``.
- The exceptions thrown by the ``execute()`` method of Prepared Queries have been changed to ``DatabaseException``. Previously, different database drivers might throw different exception classes or did not throw exceptions, but these have been unified into ``DatabaseException``.
- ``DBDebug`` and ``CI_DEBUG``
- ``DBDebug`` and ``CI_DEBUG`` Changes

- To be consistent in behavior regardless of environments, ``Config\Database::$default['DBDebug']``
and ``Config\Database::$tests['DBDebug']`` has been changed to ``true`` by default. With these
settings, an exception is always thrown when a database error occurs.
settings, an exception is always thrown when a database error occurs. Previously, it is ``false``
**only in the production environment**.
- Now ``DatabaseException`` thrown in ``BaseBuilder`` is thrown if ``$DBDebug`` is true.
Previously, it is thrown if ``CI_DEBUG`` is true.
- The default value of ``BaseConnection::$DBDebug`` has been changed to ``true``.
- With these changes, ``DBDebug`` **now means whether or not to throw an exception when an error occurs**.
Although unrelated to debugging, the name has not been changed.
- When running transactions with ``DBDebug`` is ``true``, if a query error occurs, all the queries
will be rolled backed, and an exception will be thrown. :ref:`transactions-managing-errors` or
:ref:`transactions-manual-transactions` won't work. This is no different from previous versions,
but changing the ``DBDebug`` setting from the previous default value to ``true`` will change the
behavior in the production environment.
- Now when you delete without WHERE clause in ``Model``, ``DatabaseException`` is thrown even if
``CI_DEBUG`` is false. Previously, it is thrown if ``CI_DEBUG`` is true.

Expand Down
24 changes: 24 additions & 0 deletions user_guide_src/source/database/transactions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,21 @@ contrast, we've implemented a smart transaction system that does all
this for you automatically (you can also manage your transactions
manually if you choose to, but there's really no benefit).

.. note:: Since v4.3.0, ``DBDebug`` is true by default in all environments.
When ``DBDebug`` is true, if a query error occurs, all the queries
will be rolled backed, and a ``DatabaseException`` will be thrown.
In previous versions, ``DBDebug`` was false only in production environment,
and different database drivers might throw different exception classes.

Running Transactions
====================

To run your queries using transactions you will use the
``$this->db->transStart()`` and ``$this->db->transComplete()`` methods as
follows:

.. literalinclude:: transactions/008.php

.. literalinclude:: transactions/001.php

You can run as many queries as you want between the ``transStart()``/``transComplete()``
Expand All @@ -58,6 +66,14 @@ Strict Mode can be disabled as follows:

.. literalinclude:: transactions/002.php

.. note:: Since v4.3.0, ``DBDebug`` is true by default in all environments.
When ``DBDebug`` is true, if a query error occurs, all the queries
will be rolled backed, and a ``DatabaseException`` will be thrown.
In previous versions, ``DBDebug`` was false only in production environment,
and different database drivers might throw different exception classes.

.. _transactions-managing-errors:

Managing Errors
===============

Expand All @@ -69,6 +85,12 @@ If the ``DBDebug`` is false, you can manage your own errors like this:

.. literalinclude:: transactions/003.php

.. note:: Since v4.3.0, ``DBDebug`` is true by default in all environments.
When ``DBDebug`` is true, if a query error occurs, all the queries
will be rolled backed, and a ``DatabaseException`` will be thrown.
In previous versions, ``DBDebug`` was false only in production environment,
and different database drivers might throw different exception classes.

Disabling Transactions
======================

Expand All @@ -90,6 +112,8 @@ a valid result. To use test mode simply set the first parameter in the

.. literalinclude:: transactions/005.php

.. _transactions-manual-transactions:

Running Transactions Manually
=============================

Expand Down
2 changes: 2 additions & 0 deletions user_guide_src/source/database/transactions/001.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<?php

// When DBDebug in the Database Config is false.

$this->db->transStart();
$this->db->query('AN SQL QUERY...');
$this->db->query('ANOTHER QUERY...');
Expand Down
2 changes: 2 additions & 0 deletions user_guide_src/source/database/transactions/003.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<?php

// DBDebug in the Database Config must be false.

$this->db->transStart();
$this->db->query('AN SQL QUERY...');
$this->db->query('ANOTHER QUERY...');
Expand Down
2 changes: 2 additions & 0 deletions user_guide_src/source/database/transactions/006.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<?php

// DBDebug in the Database Config must be false.

$this->db->transBegin();

$this->db->query('AN SQL QUERY...');
Expand Down
15 changes: 15 additions & 0 deletions user_guide_src/source/database/transactions/008.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

// When DBDebug in the Database Config is true.

use CodeIgniter\Database\Exceptions\DatabaseException;

try {
$this->db->transStart();
$this->db->query('AN SQL QUERY...');
$this->db->query('ANOTHER QUERY...');
$this->db->query('AND YET ANOTHER QUERY...');
$this->db->transComplete();
} catch (DatabaseException $e) {
// Automatically rolled back already.
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still feel uncomfortable with this code.