From 7d5392345ff0209018391191a9529b9069627ec6 Mon Sep 17 00:00:00 2001 From: TheWitness Date: Thu, 15 Oct 2026 09:27:35 -0400 Subject: [PATCH] Fix fatal PRIMARY KEY (`Array`) on fresh install from array primary key --- includes/database.php | 30 +++++++++++++++---- tests/Unit/MonitorSetupTableAndPollerTest.php | 21 +++++++++++++ 2 files changed, 46 insertions(+), 5 deletions(-) diff --git a/includes/database.php b/includes/database.php index 46fc57b..4b6f645 100644 --- a/includes/database.php +++ b/includes/database.php @@ -107,6 +107,26 @@ function monitor_dashboards_table_data(): array { return $data; } +/** + * Returns a copy of a table definition with an array-valued 'primary' + * flattened to the backtick-joined scalar form api_plugin_db_table_create() + * expects. Down to this plugin's Cacti 1.2.29 compat floor that API + * interpolates $data['primary'] straight into PRIMARY KEY (`...`), so an + * array would render as PRIMARY KEY (`Array`) and fail the create. The array + * form is kept in the *_table_data() definitions for db_update_table(). + * + * @param array $data The table definition (array primary). + * + * @return array The definition with a scalar primary key. + */ +function monitor_create_definition(array $data): array { + if (isset($data['primary']) && is_array($data['primary'])) { + $data['primary'] = implode('`,`', $data['primary']); + } + + return $data; +} + /** * Parse a legacy dashboard url string (monitor.php?a=b&c=d) into the current * properties structure, a JSON object of the dashboard's filter variables. @@ -189,10 +209,10 @@ function monitor_disown_dashboards_table() { * @return void */ function monitor_setup_table() { - api_plugin_db_table_create('monitor', 'plugin_monitor_notify_history', monitor_notify_history_table_data()); - api_plugin_db_table_create('monitor', 'plugin_monitor_reboot_history', monitor_reboot_history_table_data()); - api_plugin_db_table_create('monitor', 'plugin_monitor_uptime', monitor_uptime_table_data()); - api_plugin_db_table_create('monitor', 'plugin_monitor_dashboards', monitor_dashboards_table_data()); + api_plugin_db_table_create('monitor', 'plugin_monitor_notify_history', monitor_create_definition(monitor_notify_history_table_data())); + api_plugin_db_table_create('monitor', 'plugin_monitor_reboot_history', monitor_create_definition(monitor_reboot_history_table_data())); + api_plugin_db_table_create('monitor', 'plugin_monitor_uptime', monitor_create_definition(monitor_uptime_table_data())); + api_plugin_db_table_create('monitor', 'plugin_monitor_dashboards', monitor_create_definition(monitor_dashboards_table_data())); /* dashboards holds user data: create it but do not record plugin ownership, so uninstall preserves it. */ monitor_disown_dashboards_table(); @@ -242,7 +262,7 @@ function monitor_upgrade_tables() { if (db_table_exists($table)) { db_update_table($table, $table_data); } else { - api_plugin_db_table_create('monitor', $table, $table_data); + api_plugin_db_table_create('monitor', $table, monitor_create_definition($table_data)); } } diff --git a/tests/Unit/MonitorSetupTableAndPollerTest.php b/tests/Unit/MonitorSetupTableAndPollerTest.php index e135015..369ff35 100644 --- a/tests/Unit/MonitorSetupTableAndPollerTest.php +++ b/tests/Unit/MonitorSetupTableAndPollerTest.php @@ -48,6 +48,27 @@ } }); +it('hands api_plugin_db_table_create a scalar primary key while keeping the array form for db_update_table', function () { + // api_plugin_db_table_create() interpolates data['primary'] straight into + // PRIMARY KEY (`...`) down to the 1.2.29 compat floor, so an array would + // render as PRIMARY KEY (`Array`) and fail the create on a fresh install. + // db_update_table() needs the array form, so the canonical definitions keep it. + monitor_setup_table(); + + $creates = array_filter($GLOBALS['__test_db_calls'], function ($call) { + return $call['fn'] === 'api_plugin_db_table_create'; + }); + + expect($creates)->not->toBeEmpty(); + + foreach ($creates as $call) { + expect($call['data']['primary'])->toBeString(); + } + + expect(monitor_notify_history_table_data()['primary'])->toBeArray() + ->and(monitor_uptime_table_data()['primary'])->toBeArray(); +}); + it('does not keep plugin ownership of the user-owned dashboards table', function () { monitor_setup_table();