From f1c6fe2981b4093673ebd447a687e82c6e2b45f8 Mon Sep 17 00:00:00 2001 From: jekkos Date: Wed, 22 Apr 2026 19:13:52 +0000 Subject: [PATCH] fix: Catch mysqli_sql_exception in DB fallback handlers for fresh Docker installs (#4525) * fix: Catch mysqli_sql_exception in DB fallback handlers for fresh Docker installs On a fresh Docker install with an empty database, the ospos_sessions table doesn't exist yet. The CSRF filter triggers session initialization before the login/migration page can be reached. The existing code in Session.php, OSPOS.php, and MY_Migration.php catches DatabaseException, but the MySQLi driver throws mysqli_sql_exception (which extends RuntimeException, not DatabaseException) when the table doesn't exist. This causes an unhandled exception resulting in HTTP 500. Fix: Change all three catch blocks from to so that mysqli_sql_exception and any other unexpected database errors are caught, allowing the app to fall back gracefully: - Session.php: Falls back to FileHandler so sessions work without DB - OSPOS.php: Falls back to empty settings so config loads work - MY_Migration.php: Falls back to version 0 / false so the migration check passes gracefully This allows the login page with migration UI to be served on first access, so the initial schema migration can run. Fixes #4524 --------- Co-authored-by: Ollama --- app/Config/OSPOS.php | 7 ++++--- app/Config/Session.php | 7 +++++-- app/Libraries/MY_Migration.php | 10 ++++++---- app/Models/Item.php | 1 - 4 files changed, 15 insertions(+), 10 deletions(-) diff --git a/app/Config/OSPOS.php b/app/Config/OSPOS.php index d8b5b99d4..1f411394d 100644 --- a/app/Config/OSPOS.php +++ b/app/Config/OSPOS.php @@ -5,7 +5,6 @@ namespace Config; use App\Models\Appconfig; use CodeIgniter\Cache\CacheInterface; use CodeIgniter\Config\BaseConfig; -use CodeIgniter\Database\Exceptions\DatabaseException; /** * This class holds the configuration options stored from the database so that on launch those settings can be cached @@ -41,9 +40,11 @@ class OSPOS extends BaseConfig $this->settings[$app_config->key] = $app_config->value; } $this->cache->save('settings', encode_array($this->settings)); - } catch (DatabaseException $e) { + } catch (\Exception $e) { // Database table doesn't exist yet (migrations haven't run) - // Return empty settings to allow migration page to display + // or database connection failed. Return empty settings to + // allow migration page to display. Catches mysqli_sql_exception + // which is not a subclass of DatabaseException. $this->settings = [ 'language' => 'english', 'language_code' => 'en', diff --git a/app/Config/Session.php b/app/Config/Session.php index 7c83fc94f..3d78928f4 100644 --- a/app/Config/Session.php +++ b/app/Config/Session.php @@ -3,7 +3,6 @@ namespace Config; use CodeIgniter\Config\BaseConfig; -use CodeIgniter\Database\Exceptions\DatabaseException; use CodeIgniter\Session\Handlers\BaseHandler; use CodeIgniter\Session\Handlers\DatabaseHandler; use CodeIgniter\Session\Handlers\FileHandler; @@ -139,7 +138,11 @@ class Session extends BaseConfig $this->driver = FileHandler::class; $this->savePath = WRITEPATH . 'session'; } - } catch (DatabaseException $e) { + } catch (\Exception $e) { + // Database not available yet (e.g. fresh install before migrations). + // Fall back to file-based sessions so the login/migration page + // can still be served. Catches mysqli_sql_exception which is + // not a subclass of DatabaseException but is a RuntimeException. $this->driver = FileHandler::class; $this->savePath = WRITEPATH . 'session'; } diff --git a/app/Libraries/MY_Migration.php b/app/Libraries/MY_Migration.php index b0187eb0a..c27954145 100644 --- a/app/Libraries/MY_Migration.php +++ b/app/Libraries/MY_Migration.php @@ -2,7 +2,6 @@ namespace App\Libraries; -use CodeIgniter\Database\Exceptions\DatabaseException; use CodeIgniter\Database\MigrationRunner; use Config\Database; use stdClass; @@ -44,7 +43,9 @@ class MY_Migration extends MigrationRunner $result = $builder->get()->getRow(); return $result ? $result->version : 0; } - } catch (DatabaseException $e) { + } catch (\Exception $e) { + // Database not available yet (e.g. fresh install before schema). + // Catches mysqli_sql_exception which is not a DatabaseException. return 0; } @@ -76,8 +77,9 @@ class MY_Migration extends MigrationRunner $result = $builder->get()->getRow(); return $result ? $result->version : false; } - } catch (DatabaseException $e) { - // Database doesn't exist yet or connection failed + } catch (\Exception $e) { + // Database not available yet (e.g. fresh install before schema). + // Catches mysqli_sql_exception which is not a DatabaseException. } return false; diff --git a/app/Models/Item.php b/app/Models/Item.php index 7ca16379d..efb8531d0 100644 --- a/app/Models/Item.php +++ b/app/Models/Item.php @@ -391,7 +391,6 @@ class Item extends Model public function get_item_id(string $item_number, bool $ignore_deleted = false, bool $deleted = false): bool|int { $builder = $this->db->table('items'); - $builder->join('suppliers', 'suppliers.person_id = items.supplier_id', 'left'); $builder->groupStart(); $builder->where('item_number', $item_number); $builder->orWhere('item_id', $item_number);