mirror of
https://github.com/opensourcepos/opensourcepos.git
synced 2026-10-28 11:08:53 -04:00
feat(db): add MySQL 8.4 support (#4048)
Add MySQL 8.4 compatibility, including a utf8mb4 conversion migration, hardened foreign key handling, and expanded CI database coverage. Migrations: - Add Mysql84Utf8mb4Conversion migration using sqlscripts/3.4.3_mysql84_utf8mb4_conversion.sql via executeScript() - Drop ospos_people composite index before conversion, since its prefix length exceeds the 3072-byte key limit under utf8mb4 - Add assertNoCollationCollisions() pre-flight check to fail fast on unique values that collide under utf8mb4_unicode_520_ci, instead of hitting "Duplicate entry" partway through ALTER TABLE - Wrap the conversion in try/catch so foreign keys are recreated on failure; log restore errors separately so they don't mask the original exception - Throw RuntimeException if re-adding the people index fails - Remove the re-add of the sales_items_taxes FK in down(), which MySQL 8.4 rejects without a unique key - Revert edits to historical migrations; fix FK handling and table names migration_helper: - Add shared dropAllForeignKeyConstraints() and recreateForeignKeyConstraints(); table/column now optional, dropping all FKs in the schema when omitted - Match CONSTRAINT_SCHEMA when joining KEY_COLUMN_USAGE to REFERENTIAL_CONSTRAINTS to avoid cross-schema name collisions - Rebuild FK lookup with the query builder and bound values instead of interpolated SQL CI/tests: - Add mysql:5.7, mariadb:10.6 and mariadb:10.11 to PHPUnit matrix - Use master instead of latest image tag; improve CI robustness - Mock table() in FailedQueryConnectionTrait so query builder chains work against the failing connection Signed-off-by: objecttothis <17935339+objecttothis@users.noreply.github.com> Co-authored-by: aliworksx08 <57456290+aliworksx08@users.noreply.github.com> Co-authored-by: jekkos <jeroen.peelaerts@gmail.com> Co-authored-by: objecttothis <17935339+objecttothis@users.noreply.github.com> Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
12 files changed
+269
-57
No files matched your search
@@ -28,6 +28,7 @@ app.allowedHostnames=''
|
||||
#--------------------------------------------------------------------
|
||||
# DATABASE
|
||||
#--------------------------------------------------------------------
|
||||
# MySQL 5.7, 8.0, 8.4 (LTS) and MariaDB 10.x supported. MySQL 8.4 requires mysqlnd (default in PHP 8.1+).
|
||||
|
||||
database.default.hostname='localhost'
|
||||
database.default.database='ospos'
|
||||
|
||||
@@ -27,7 +27,7 @@ permissions:
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: PHP ${{ matrix.php-version }} Tests
|
||||
name: PHP ${{ matrix.php-version }} / ${{ matrix.db-image }} Tests
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
strategy:
|
||||
@@ -37,6 +37,13 @@ jobs:
|
||||
- '8.2'
|
||||
- '8.3'
|
||||
- '8.4'
|
||||
db-image:
|
||||
- 'mariadb:10.5'
|
||||
- 'mariadb:10.6'
|
||||
- 'mariadb:10.11'
|
||||
- 'mysql:5.7'
|
||||
- 'mysql:8.0'
|
||||
- 'mysql:8.4'
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
@@ -68,7 +75,7 @@ jobs:
|
||||
- name: Install npm dependencies
|
||||
run: npm install
|
||||
|
||||
- name: Start MariaDB
|
||||
- name: Start database
|
||||
run: |
|
||||
docker run -d --name mysql \
|
||||
-e MYSQL_ROOT_PASSWORD=root \
|
||||
@@ -76,13 +83,19 @@ jobs:
|
||||
-e MYSQL_USER=admin \
|
||||
-e MYSQL_PASSWORD=pointofsale \
|
||||
-p 3306:3306 \
|
||||
mariadb:10.5
|
||||
# Wait for MariaDB to be ready
|
||||
${{ matrix.db-image }}
|
||||
# Wait for database to be ready (max 60 attempts)
|
||||
retries=0
|
||||
until docker exec mysql mysqladmin ping -h 127.0.0.1 -u root -proot --silent; do
|
||||
echo "Waiting for MariaDB..."
|
||||
retries=$((retries + 1))
|
||||
if [ $retries -ge 60 ]; then
|
||||
echo "Database failed to start after 60 attempts"
|
||||
exit 1
|
||||
fi
|
||||
echo "Waiting for database... (attempt $retries/60)"
|
||||
sleep 2
|
||||
done
|
||||
echo "MariaDB is ready!"
|
||||
echo "Database is ready!"
|
||||
|
||||
# Grant admin the CREATE/DROP privileges it needs to drop and
|
||||
# recreate the test database at the start of each test class.
|
||||
@@ -140,14 +153,18 @@ jobs:
|
||||
MYSQL_HOST_NAME: 127.0.0.1
|
||||
run: composer test -- --no-coverage --log-junit test-results/junit.xml
|
||||
|
||||
- name: Set artifact name
|
||||
if: always()
|
||||
run: echo "DB_LABEL=$(echo '${{ matrix.db-image }}' | tr ':' '-')" >> $GITHUB_ENV
|
||||
|
||||
- name: Upload test results
|
||||
uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
with:
|
||||
name: test-results-php-${{ matrix.php-version }}
|
||||
name: test-results-php-${{ matrix.php-version }}-${{ env.DB_LABEL }}
|
||||
path: test-results/
|
||||
retention-days: 30
|
||||
|
||||
- name: Stop MariaDB
|
||||
- name: Stop database
|
||||
if: always()
|
||||
run: docker stop mysql && docker rm mysql
|
||||
@@ -59,8 +59,5 @@ class Migration_fix_keys_for_db_upgrade extends Migration
|
||||
$this->db->query('ALTER TABLE ' . $this->db->prefixTable('sales_items_taxes') . ' DROP FOREIGN KEY ospos_sales_items_taxes_ibfk_1');
|
||||
}
|
||||
|
||||
$this->db->query('ALTER TABLE ' . $this->db->prefixTable('sales_items_taxes')
|
||||
. ' ADD CONSTRAINT ospos_sales_items_taxes_ibfk_1 FOREIGN KEY (sale_id) '
|
||||
. ' REFERENCES ' . $this->db->prefixTable('sales_items') . ' (sale_id)');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
|
||||
namespace App\Database\Migrations;
|
||||
|
||||
use CodeIgniter\Database\Migration;
|
||||
use RuntimeException;
|
||||
|
||||
class Migration_Mysql84Utf8mb4Conversion extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
helper('migration');
|
||||
|
||||
$this->assertNoCollationCollisions();
|
||||
|
||||
$foreignKeys = dropAllForeignKeyConstraints();
|
||||
|
||||
try {
|
||||
if (indexExists('people', 'first_name')) {
|
||||
$this->db->query('ALTER TABLE ' . $this->db->prefixTable('people') . ' DROP INDEX first_name');
|
||||
}
|
||||
|
||||
$script = APPPATH . 'Database/Migrations/sqlscripts/3.4.3_mysql84_utf8mb4_conversion.sql';
|
||||
if (!executeScript($script)) {
|
||||
throw new RuntimeException('Failed to execute utf8mb4 conversion migration: ' . $script);
|
||||
}
|
||||
|
||||
if (!$this->db->query('ALTER TABLE ' . $this->db->prefixTable('people')
|
||||
. ' ADD INDEX(`first_name`(191), `last_name`(191), `email`(191), `phone_number`(191))')) {
|
||||
throw new RuntimeException('Failed to add composite index on people table');
|
||||
}
|
||||
} catch (\Throwable $exception) {
|
||||
log_message('error', 'utf8mb4 conversion failed; recovering dropped foreign keys: '
|
||||
. json_encode($foreignKeys));
|
||||
|
||||
try {
|
||||
recreateForeignKeyConstraints($foreignKeys);
|
||||
} catch (\Throwable $restoreError) {
|
||||
log_message('error', 'Foreign key recovery failed: ' . $restoreError->getMessage());
|
||||
}
|
||||
|
||||
throw $exception;
|
||||
}
|
||||
|
||||
recreateForeignKeyConstraints($foreignKeys);
|
||||
}
|
||||
|
||||
// Intentionally irreversible: converting back to utf8 could cause data loss for utf8mb4 characters.
|
||||
public function down(): void
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* utf8mb4_unicode_520_ci treats invisible/ignorable Unicode characters (e.g. the LTR
|
||||
* mark U+200E) as equal weight to nothing, unlike utf8_general_ci. Values that were
|
||||
* previously distinct under a UNIQUE constraint can collide once converted, which aborts
|
||||
* the ALTER TABLE mid-script. Detect those collisions up front and fail with an actionable
|
||||
* message rather than the raw "Duplicate entry" error partway through the conversion.
|
||||
*/
|
||||
private function assertNoCollationCollisions(): void
|
||||
{
|
||||
$uniqueColumns = $this->db->query("
|
||||
SELECT DISTINCT s.TABLE_NAME, s.COLUMN_NAME
|
||||
FROM information_schema.STATISTICS s
|
||||
JOIN information_schema.COLUMNS c
|
||||
ON c.TABLE_SCHEMA = s.TABLE_SCHEMA
|
||||
AND c.TABLE_NAME = s.TABLE_NAME
|
||||
AND c.COLUMN_NAME = s.COLUMN_NAME
|
||||
WHERE s.TABLE_SCHEMA = DATABASE()
|
||||
AND s.NON_UNIQUE = 0
|
||||
AND s.TABLE_NAME LIKE '" . $this->db->getPrefix() . "%'
|
||||
AND c.DATA_TYPE IN ('char', 'varchar', 'text', 'tinytext', 'mediumtext', 'longtext')
|
||||
AND (
|
||||
SELECT COUNT(*) FROM information_schema.STATISTICS s2
|
||||
WHERE s2.TABLE_SCHEMA = s.TABLE_SCHEMA
|
||||
AND s2.TABLE_NAME = s.TABLE_NAME
|
||||
AND s2.INDEX_NAME = s.INDEX_NAME
|
||||
) = 1
|
||||
")->getResultArray();
|
||||
|
||||
$problems = [];
|
||||
foreach ($uniqueColumns as $uniqueColumn) {
|
||||
$table = $uniqueColumn['TABLE_NAME'];
|
||||
$column = $uniqueColumn['COLUMN_NAME'];
|
||||
|
||||
$collisions = $this->db->query("
|
||||
SELECT CONVERT(`$column` USING utf8mb4) COLLATE utf8mb4_unicode_520_ci AS value,
|
||||
COUNT(*) AS count
|
||||
FROM `$table`
|
||||
WHERE `$column` IS NOT NULL
|
||||
GROUP BY CONVERT(`$column` USING utf8mb4) COLLATE utf8mb4_unicode_520_ci
|
||||
HAVING count > 1
|
||||
")->getResultArray();
|
||||
|
||||
if ($collisions) {
|
||||
$problems[] = "$table.$column (" . count($collisions) . ' colliding value(s))';
|
||||
}
|
||||
}
|
||||
|
||||
if ($problems) {
|
||||
throw new RuntimeException(
|
||||
'Cannot convert to utf8mb4: the following unique column(s) contain values that '
|
||||
. 'collide under utf8mb4_unicode_520_ci (often due to invisible Unicode characters '
|
||||
. "that this collation treats as equivalent):\n - " . implode("\n - ", $problems)
|
||||
. "\nClean up or merge the colliding rows before retrying this migration."
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
-- Convert all tables to utf8mb4 for MySQL 8.4 compatibility
|
||||
-- Foreign keys are dropped and recreated in the PHP migration to avoid collation mismatches
|
||||
ALTER TABLE ospos_app_config CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci;
|
||||
ALTER TABLE ospos_attribute_definitions CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci;
|
||||
ALTER TABLE ospos_attribute_links CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci;
|
||||
ALTER TABLE ospos_attribute_values CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci;
|
||||
ALTER TABLE ospos_cash_up CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci;
|
||||
ALTER TABLE ospos_customers CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci;
|
||||
ALTER TABLE ospos_customers_packages CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci;
|
||||
ALTER TABLE ospos_customers_points CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci;
|
||||
ALTER TABLE ospos_dinner_tables CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci;
|
||||
ALTER TABLE ospos_employees CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci;
|
||||
ALTER TABLE ospos_expenses CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci;
|
||||
ALTER TABLE ospos_expense_categories CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci;
|
||||
ALTER TABLE ospos_giftcards CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci;
|
||||
ALTER TABLE ospos_grants CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci;
|
||||
ALTER TABLE ospos_inventory CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci;
|
||||
ALTER TABLE ospos_items CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci;
|
||||
ALTER TABLE ospos_items_taxes CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci;
|
||||
ALTER TABLE ospos_item_kits CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci;
|
||||
ALTER TABLE ospos_item_kit_items CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci;
|
||||
ALTER TABLE ospos_item_quantities CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci;
|
||||
ALTER TABLE ospos_migrations CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci;
|
||||
ALTER TABLE ospos_modules CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci;
|
||||
ALTER TABLE ospos_people CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci;
|
||||
ALTER TABLE ospos_permissions CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci;
|
||||
ALTER TABLE ospos_receivings CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci;
|
||||
ALTER TABLE ospos_receivings_items CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci;
|
||||
ALTER TABLE ospos_sales CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci;
|
||||
ALTER TABLE ospos_sales_items CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci;
|
||||
ALTER TABLE ospos_sales_items_taxes CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci;
|
||||
ALTER TABLE ospos_sales_payments CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci;
|
||||
ALTER TABLE ospos_sales_reward_points CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci;
|
||||
ALTER TABLE ospos_sales_taxes CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci;
|
||||
ALTER TABLE ospos_sessions CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci;
|
||||
ALTER TABLE ospos_stock_locations CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci;
|
||||
ALTER TABLE ospos_suppliers CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci;
|
||||
ALTER TABLE ospos_tax_categories CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci;
|
||||
ALTER TABLE ospos_tax_codes CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci;
|
||||
ALTER TABLE ospos_tax_jurisdictions CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci;
|
||||
ALTER TABLE ospos_tax_rates CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci;
|
||||
@@ -118,40 +118,64 @@ function createPrimaryKey(string $table, string $index): void {
|
||||
}
|
||||
|
||||
/**
|
||||
* Drops all foreign key constraints that reference the provided table and column.
|
||||
* Drops foreign key constraints that reference the provided table and column.
|
||||
* When $table and $column are omitted, drops all foreign key constraints in the schema.
|
||||
*
|
||||
* A failed metadata query is treated as an error (thrown) rather than mistaken
|
||||
* for "no constraints found", so callers never proceed with schema changes on a
|
||||
* wrong assumption.
|
||||
*
|
||||
* @param string $table
|
||||
* @param string $column
|
||||
* @param string|null $table
|
||||
* @param string|null $column
|
||||
* @param BaseConnection|null $db Connection to use (defaults to the default connection).
|
||||
*
|
||||
* @return array containing the deleted constraints in case they need to be recreated after.
|
||||
*
|
||||
* @throws DatabaseException If the metadata query fails.
|
||||
*/
|
||||
function dropAllForeignKeyConstraints(string $table, string $column, ?BaseConnection $db = null): array {
|
||||
function dropAllForeignKeyConstraints(?string $table = null, ?string $column = null, ?BaseConnection $db = null): array {
|
||||
$db ??= Database::connect();
|
||||
$result = $db->query("
|
||||
SELECT DISTINCT
|
||||
kcu.CONSTRAINT_NAME,
|
||||
kcu.TABLE_NAME,
|
||||
kcu.COLUMN_NAME,
|
||||
kcu.REFERENCED_TABLE_NAME,
|
||||
kcu.REFERENCED_COLUMN_NAME,
|
||||
rc.DELETE_RULE,
|
||||
rc.UPDATE_RULE
|
||||
FROM information_schema.KEY_COLUMN_USAGE kcu
|
||||
LEFT JOIN information_schema.REFERENTIAL_CONSTRAINTS rc
|
||||
ON kcu.CONSTRAINT_NAME = rc.CONSTRAINT_NAME
|
||||
AND kcu.TABLE_NAME = rc.TABLE_NAME
|
||||
WHERE kcu.TABLE_SCHEMA = DATABASE()
|
||||
AND ((kcu.REFERENCED_TABLE_NAME = '" . $db->getPrefix() . "$table' AND kcu.REFERENCED_COLUMN_NAME = '$column')
|
||||
OR (kcu.TABLE_NAME = '" . $db->getPrefix() . "$table' AND kcu.COLUMN_NAME = '$column'))
|
||||
AND rc.CONSTRAINT_NAME IS NOT NULL
|
||||
");
|
||||
$prefixedTable = $table !== null ? $db->getPrefix() . $table : null;
|
||||
$prefix = overridePrefix();
|
||||
|
||||
$builder = $db->table('information_schema.KEY_COLUMN_USAGE kcu');
|
||||
$builder->distinct();
|
||||
$builder->select('kcu.CONSTRAINT_NAME, kcu.TABLE_NAME, kcu.COLUMN_NAME, kcu.REFERENCED_TABLE_NAME, kcu.REFERENCED_COLUMN_NAME, kcu.ORDINAL_POSITION, rc.DELETE_RULE, rc.UPDATE_RULE');
|
||||
$builder->join(
|
||||
'information_schema.REFERENTIAL_CONSTRAINTS rc',
|
||||
'kcu.CONSTRAINT_SCHEMA = rc.CONSTRAINT_SCHEMA AND kcu.CONSTRAINT_NAME = rc.CONSTRAINT_NAME AND kcu.TABLE_NAME = rc.TABLE_NAME',
|
||||
'left'
|
||||
);
|
||||
|
||||
if ($table !== null && $column !== null) {
|
||||
$scopedBuilder = $db->table('information_schema.KEY_COLUMN_USAGE scoped');
|
||||
$scopedBuilder->distinct();
|
||||
$scopedBuilder->select('scoped.CONSTRAINT_NAME, scoped.TABLE_NAME');
|
||||
$scopedBuilder->where('scoped.TABLE_SCHEMA', $db->database);
|
||||
$scopedBuilder->groupStart();
|
||||
$scopedBuilder->where('scoped.REFERENCED_TABLE_NAME', $prefixedTable);
|
||||
$scopedBuilder->where('scoped.REFERENCED_COLUMN_NAME', $column);
|
||||
$scopedBuilder->groupEnd();
|
||||
$scopedBuilder->orGroupStart();
|
||||
$scopedBuilder->where('scoped.TABLE_NAME', $prefixedTable);
|
||||
$scopedBuilder->where('scoped.COLUMN_NAME', $column);
|
||||
$scopedBuilder->groupEnd();
|
||||
|
||||
$builder->join(
|
||||
'(' . $scopedBuilder->getCompiledSelect() . ') matched',
|
||||
'matched.CONSTRAINT_NAME = kcu.CONSTRAINT_NAME AND matched.TABLE_NAME = kcu.TABLE_NAME',
|
||||
'inner',
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
$builder->where('kcu.TABLE_SCHEMA', $db->database);
|
||||
$builder->where('rc.CONSTRAINT_NAME IS NOT NULL', null, false);
|
||||
$builder->orderBy('kcu.CONSTRAINT_NAME');
|
||||
$builder->orderBy('kcu.ORDINAL_POSITION');
|
||||
|
||||
$result = $builder->get();
|
||||
overridePrefix($prefix);
|
||||
|
||||
if ($result === false) {
|
||||
$detail = 'unknown error';
|
||||
@@ -164,26 +188,46 @@ function dropAllForeignKeyConstraints(string $table, string $column, ?BaseConnec
|
||||
// A disconnected or mocked connection may not support error(); keep the default.
|
||||
}
|
||||
|
||||
throw new DatabaseException(sprintf(
|
||||
'Failed to look up foreign key constraints referencing table "%s" column "%s": %s',
|
||||
$table, $column, $detail
|
||||
));
|
||||
$scope = $table !== null && $column !== null
|
||||
? sprintf('referencing table "%s" column "%s"', $table, $column)
|
||||
: 'in the schema';
|
||||
|
||||
throw new DatabaseException(sprintf('Failed to look up foreign key constraints %s: %s', $scope, $detail));
|
||||
}
|
||||
|
||||
$deletedConstraints = [];
|
||||
|
||||
foreach ($result->getResultArray() as $constraint) {
|
||||
$deletedConstraints[] = [
|
||||
'constraintName' => $constraint['CONSTRAINT_NAME'],
|
||||
'tableName' => str_replace($db->DBPrefix, '', $constraint['TABLE_NAME']),
|
||||
'columnName' => $constraint['COLUMN_NAME'],
|
||||
'referencedTable' => str_replace($db->DBPrefix, '', $constraint['REFERENCED_TABLE_NAME']),
|
||||
'referencedColumn' => $constraint['REFERENCED_COLUMN_NAME'],
|
||||
'onDelete' => $constraint['DELETE_RULE'],
|
||||
'onUpdate' => $constraint['UPDATE_RULE'],
|
||||
];
|
||||
$key = $constraint['TABLE_NAME'] . '.' . $constraint['CONSTRAINT_NAME'];
|
||||
|
||||
if (!isset($deletedConstraints[$key])) {
|
||||
$deletedConstraints[$key] = [
|
||||
'constraintName' => $constraint['CONSTRAINT_NAME'],
|
||||
'tableName' => str_replace($db->DBPrefix, '', $constraint['TABLE_NAME']),
|
||||
'columnName' => [],
|
||||
'referencedTable' => str_replace($db->DBPrefix, '', $constraint['REFERENCED_TABLE_NAME']),
|
||||
'referencedColumn' => [],
|
||||
'onDelete' => $constraint['DELETE_RULE'],
|
||||
'onUpdate' => $constraint['UPDATE_RULE'],
|
||||
'seenPositions' => [],
|
||||
];
|
||||
}
|
||||
|
||||
$position = $constraint['ORDINAL_POSITION'];
|
||||
if (!isset($deletedConstraints[$key]['seenPositions'][$position])) {
|
||||
$deletedConstraints[$key]['seenPositions'][$position] = true;
|
||||
$deletedConstraints[$key]['columnName'][] = $constraint['COLUMN_NAME'];
|
||||
$deletedConstraints[$key]['referencedColumn'][] = $constraint['REFERENCED_COLUMN_NAME'];
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($deletedConstraints as &$constraint) {
|
||||
unset($constraint['seenPositions']);
|
||||
}
|
||||
unset($constraint);
|
||||
|
||||
$deletedConstraints = array_values($deletedConstraints);
|
||||
|
||||
if ($deletedConstraints) {
|
||||
$forge = Database::forge();
|
||||
foreach ($deletedConstraints as $foreignKey) {
|
||||
|
||||
@@ -150,7 +150,7 @@ class Summary_payments extends Summary_report
|
||||
LEFT OUTER JOIN ' . $this->db->prefixTable('sales_taxes') . ' AS sales_taxes
|
||||
ON sales.sale_id = sales_taxes.sale_id
|
||||
WHERE ' . $where . ' AND sales_taxes.tax_type = \'1\'
|
||||
GROUP BY sale_id
|
||||
GROUP BY sales.sale_id
|
||||
)'
|
||||
);
|
||||
|
||||
@@ -164,7 +164,7 @@ class Summary_payments extends Summary_report
|
||||
. 'ON sales.sale_id = sales_items.sale_id '
|
||||
. 'LEFT OUTER JOIN ' . $this->db->prefixTable('sumpay_taxes_temp') . ' AS sumpay_taxes '
|
||||
. 'ON sales.sale_id = sumpay_taxes.sale_id '
|
||||
. 'WHERE ' . $where . ' GROUP BY sale_id
|
||||
. 'WHERE ' . $where . ' GROUP BY sales.sale_id
|
||||
)'
|
||||
);
|
||||
|
||||
@@ -184,7 +184,7 @@ class Summary_payments extends Summary_report
|
||||
LEFT OUTER JOIN ' . $this->db->prefixTable('sales_payments') . ' AS sales_payments
|
||||
ON sales.sale_id = sales_payments.sale_id
|
||||
WHERE ' . $where . '
|
||||
GROUP BY sale_id
|
||||
GROUP BY sales.sale_id
|
||||
)'
|
||||
);
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ abstract class Summary_report extends Report
|
||||
INNER JOIN ' . $this->db->prefixTable('sales_items') . ' AS sales_items
|
||||
ON sales_items.sale_id = sales_items_taxes.sale_id AND sales_items.line = sales_items_taxes.line
|
||||
WHERE ' . $where . '
|
||||
GROUP BY sale_id, item_id, line
|
||||
GROUP BY sales_items_taxes.sale_id, sales_items_taxes.item_id, sales_items_taxes.line
|
||||
)'
|
||||
);
|
||||
|
||||
@@ -76,7 +76,7 @@ abstract class Summary_report extends Report
|
||||
INNER JOIN ' . $this->db->prefixTable('sales') . ' AS sales
|
||||
ON sales.sale_id = payments.sale_id
|
||||
WHERE ' . $where . '
|
||||
GROUP BY sale_id
|
||||
GROUP BY payments.sale_id
|
||||
)'
|
||||
);
|
||||
|
||||
|
||||
+2
-2
@@ -1088,7 +1088,7 @@ class Sale extends Model
|
||||
INNER JOIN ' . $this->db->prefixTable('sales_items') . ' AS sales_items
|
||||
ON sales_items.sale_id = sales_items_taxes.sale_id AND sales_items.line = sales_items_taxes.line
|
||||
WHERE ' . $where . '
|
||||
GROUP BY sale_id, item_id, line
|
||||
GROUP BY sales_items_taxes.sale_id, sales_items_taxes.item_id, sales_items_taxes.line
|
||||
)';
|
||||
|
||||
$this->db->query($sql);
|
||||
@@ -1174,7 +1174,7 @@ class Sale extends Model
|
||||
LEFT OUTER JOIN ' . $this->db->prefixTable('sales_items_taxes_temp') . ' AS sales_items_taxes
|
||||
ON sales_items.sale_id = sales_items_taxes.sale_id AND sales_items.item_id = sales_items_taxes.item_id AND sales_items.line = sales_items_taxes.line
|
||||
WHERE ' . $where . '
|
||||
GROUP BY sale_id, item_id, line
|
||||
GROUP BY sales.sale_id, items.item_id, sales_items.line, sales_items.discount_type
|
||||
)';
|
||||
|
||||
$this->db->query($sql);
|
||||
|
||||
@@ -12,7 +12,7 @@ networks:
|
||||
|
||||
services:
|
||||
mysql:
|
||||
image: mariadb:10.5
|
||||
image: ${OSPOS_DB_IMAGE:-mariadb:10.5}
|
||||
container_name: mysql
|
||||
restart: always
|
||||
expose:
|
||||
|
||||
@@ -12,7 +12,7 @@ networks:
|
||||
|
||||
services:
|
||||
mysql:
|
||||
image: mariadb:10.5
|
||||
image: ${OSPOS_DB_IMAGE:-mariadb:10.5}
|
||||
container_name: mysql
|
||||
restart: always
|
||||
expose:
|
||||
|
||||
@@ -9,14 +9,17 @@ trait FailedQueryConnectionTrait
|
||||
{
|
||||
protected function failingConnection(): BaseConnection
|
||||
{
|
||||
$driverClass = get_class(\Config\Database::connect($this->DBGroup));
|
||||
$realConnection = \Config\Database::connect($this->DBGroup);
|
||||
$driverClass = get_class($realConnection);
|
||||
$builderClass = str_replace('Connection', 'Builder', $driverClass);
|
||||
|
||||
$db = $this->getMockBuilder($driverClass)
|
||||
->disableOriginalConstructor()
|
||||
->onlyMethods(['query', 'error'])
|
||||
->onlyMethods(['query', 'error', 'table'])
|
||||
->getMock();
|
||||
$db->method('query')->willReturn(false);
|
||||
$db->method('error')->willReturn(['code' => 1, 'message' => 'simulated query failure']);
|
||||
$db->method('table')->willReturnCallback(static fn (string $tableName) => new $builderClass($tableName, $db));
|
||||
|
||||
return $db;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user