From 1d6c303da5804a638efadf35b994bb997d9d114f Mon Sep 17 00:00:00 2001 From: TheWitness Date: Thu, 15 Oct 2026 09:29:02 -0400 Subject: [PATCH] Fix fatal PRIMARY KEY (`Array`) on fresh install from array primary key --- includes/database.php | 24 +++++++++++++++++++++-- tests/Integration/PluginLifecycleTest.php | 19 ++++++++++++++++++ 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/includes/database.php b/includes/database.php index 47bbb58..3893290 100644 --- a/includes/database.php +++ b/includes/database.php @@ -257,6 +257,26 @@ function webseer_table_map(): array { ]; } +/** + * 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 webseer_create_definition(array $data): array { + if (isset($data['primary']) && is_array($data['primary'])) { + $data['primary'] = implode('`,`', $data['primary']); + } + + return $data; +} + /** * Creates all of this plugin's database tables through Cacti's tracked * plugin table API. Called from plugin_webseer_install() during @@ -267,7 +287,7 @@ function webseer_table_map(): array { */ function plugin_webseer_setup_table() { foreach (webseer_table_map() as $table => $data) { - api_plugin_db_table_create('webseer', $table, $data); + api_plugin_db_table_create('webseer', $table, webseer_create_definition($data)); } } @@ -295,7 +315,7 @@ function webseer_upgrade_tables(): bool { if (db_table_exists($table)) { db_update_table($table, $data); } else { - api_plugin_db_table_create('webseer', $table, $data); + api_plugin_db_table_create('webseer', $table, webseer_create_definition($data)); } } diff --git a/tests/Integration/PluginLifecycleTest.php b/tests/Integration/PluginLifecycleTest.php index 0035598..f20e715 100644 --- a/tests/Integration/PluginLifecycleTest.php +++ b/tests/Integration/PluginLifecycleTest.php @@ -81,6 +81,25 @@ } }); +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 shared definitions keep it. + plugin_webseer_install(); + + $creates = array_filter($GLOBALS['__test_db_calls'], fn ($call) => $call['fn'] === 'api_plugin_db_table_create'); + + expect($creates)->not->toBeEmpty(); + + foreach ($creates as $call) { + // params = [$plugin, $data] + expect($call['params'][1]['primary'])->toBeString(); + } + + expect(webseer_servers_table_data()['primary'])->toBeArray(); +}); + it('drops every plugin table on uninstall', function () { plugin_webseer_uninstall();