From 868de4855bff412e5329e0642c4ad1a05517cd4f Mon Sep 17 00:00:00 2001 From: Christian Date: Mon, 3 Aug 2026 15:16:18 +0200 Subject: [PATCH] Performance: take target credential names and UUIDs from the iterator handle_get_targets looked up the name and the UUID of every credential of every target with credential_name() and credential_uuid(), one statement each. Over 1000 targets and six login types that is a large part of the 12196 statements get_targets issued, while only 156 ms of 1498 ms were spent actually executing SQL. The name columns already existed in TARGET_ITERATOR_COLUMNS for filtering and sorting but had no accessor. Add the UUID columns next to them and read both from the iterator row. The new columns are appended after "ips" because the accessors address columns by position, and they are not added to TARGET_ITERATOR_TRASH_COLUMNS, so the accessors are only valid on the non-trash iterator; gmp.c already branches on get.trash. Measured together with the prepared statement cache, get_targets with rows=-1 over 1000 targets goes from 1.493 s to 0.370 s. Co-Authored-By: Claude Opus 5 --- src/gmp.c | 42 +++++++++---- src/manage_sql_targets.c | 132 +++++++++++++++++++++++++++++++++++++++ src/manage_sql_targets.h | 64 +++++++++++++++++++ src/manage_targets.h | 39 ++++++++++++ 4 files changed, 265 insertions(+), 12 deletions(-) diff --git a/src/gmp.c b/src/gmp.c index 2157c14ce4..ccc8f0c53e 100644 --- a/src/gmp.c +++ b/src/gmp.c @@ -19436,8 +19436,14 @@ handle_get_targets (gmp_parser_t *gmp_parser, GError **error) { credential_t found; - ssh_name = credential_name (ssh_credential); - ssh_uuid = credential_uuid (ssh_credential); + /* The iterator owns its row buffer, so the values are + * copied. The free() calls at the end of the loop expect + * memory they own, as returned by credential_name() and + * credential_uuid(). */ + ssh_name + = g_strdup (target_iterator_ssh_credential_name (&targets)); + ssh_uuid + = g_strdup (target_iterator_ssh_credential_uuid (&targets)); if (find_credential_with_permission (ssh_uuid, &found, "get_credentials")) @@ -19466,8 +19472,10 @@ handle_get_targets (gmp_parser_t *gmp_parser, GError **error) { credential_t found; - smb_name = credential_name (smb_credential); - smb_uuid = credential_uuid (smb_credential); + smb_name + = g_strdup (target_iterator_smb_credential_name (&targets)); + smb_uuid + = g_strdup (target_iterator_smb_credential_uuid (&targets)); if (find_credential_with_permission (smb_uuid, &found, "get_credentials")) @@ -19498,8 +19506,10 @@ handle_get_targets (gmp_parser_t *gmp_parser, GError **error) { credential_t found; - esxi_name = credential_name (esxi_credential); - esxi_uuid = credential_uuid (esxi_credential); + esxi_name + = g_strdup (target_iterator_esxi_credential_name (&targets)); + esxi_uuid + = g_strdup (target_iterator_esxi_credential_uuid (&targets)); if (find_credential_with_permission (esxi_uuid, &found, "get_credentials")) @@ -19530,8 +19540,10 @@ handle_get_targets (gmp_parser_t *gmp_parser, GError **error) { credential_t found; - snmp_name = credential_name (snmp_credential); - snmp_uuid = credential_uuid (snmp_credential); + snmp_name + = g_strdup (target_iterator_snmp_credential_name (&targets)); + snmp_uuid + = g_strdup (target_iterator_snmp_credential_uuid (&targets)); if (find_credential_with_permission (snmp_uuid, &found, "get_credentials")) @@ -19562,8 +19574,12 @@ handle_get_targets (gmp_parser_t *gmp_parser, GError **error) { credential_t found; - ssh_elevate_name = credential_name (ssh_elevate_credential); - ssh_elevate_uuid = credential_uuid (ssh_elevate_credential); + ssh_elevate_name + = g_strdup (target_iterator_ssh_elevate_credential_name + (&targets)); + ssh_elevate_uuid + = g_strdup (target_iterator_ssh_elevate_credential_uuid + (&targets)); if (find_credential_with_permission (ssh_elevate_uuid, &found, "get_credentials")) @@ -19594,8 +19610,10 @@ handle_get_targets (gmp_parser_t *gmp_parser, GError **error) { credential_t found; - krb5_name = credential_name (krb5_credential); - krb5_uuid = credential_uuid (krb5_credential); + krb5_name + = g_strdup (target_iterator_krb5_credential_name (&targets)); + krb5_uuid + = g_strdup (target_iterator_krb5_credential_uuid (&targets)); if (find_credential_with_permission (krb5_uuid, &found, "get_credentials")) diff --git a/src/manage_sql_targets.c b/src/manage_sql_targets.c index c896dcb265..7a24bcc825 100644 --- a/src/manage_sql_targets.c +++ b/src/manage_sql_targets.c @@ -2073,6 +2073,138 @@ target_iterator_krb5_trash (iterator_t* iterator) DEF_ACCESS (target_iterator_allow_simultaneous_ips, GET_ITERATOR_COLUMN_COUNT + 22); +/* Credential names and UUIDs straight from the iterator. + * + * The name columns (23 to 28) already existed for filtering and sorting, + * they simply had no accessor. The UUID columns are 31 to 36; 29 and 30 + * are the "hosts" and "ips" filter columns that sit between them. + * + * Only valid on the non-trash iterator. TARGET_ITERATOR_TRASH_COLUMNS + * ends after index 22, so calling these on a trash iterator would read + * past the column list. Callers must branch on get.trash first, as + * gmp.c does. + */ + +/** + * @brief Get the SSH credential name from a target iterator. + * + * @param[in] iterator Iterator. + * + * @return Credential name, or NULL if iteration is complete. + */ +DEF_ACCESS (target_iterator_ssh_credential_name, + GET_ITERATOR_COLUMN_COUNT + 23); + +/** + * @brief Get the SMB credential name from a target iterator. + * + * @param[in] iterator Iterator. + * + * @return Credential name, or NULL if iteration is complete. + */ +DEF_ACCESS (target_iterator_smb_credential_name, + GET_ITERATOR_COLUMN_COUNT + 24); + +/** + * @brief Get the ESXi credential name from a target iterator. + * + * @param[in] iterator Iterator. + * + * @return Credential name, or NULL if iteration is complete. + */ +DEF_ACCESS (target_iterator_esxi_credential_name, + GET_ITERATOR_COLUMN_COUNT + 25); + +/** + * @brief Get the SNMP credential name from a target iterator. + * + * @param[in] iterator Iterator. + * + * @return Credential name, or NULL if iteration is complete. + */ +DEF_ACCESS (target_iterator_snmp_credential_name, + GET_ITERATOR_COLUMN_COUNT + 26); + +/** + * @brief Get the SSH elevate credential name from a target iterator. + * + * @param[in] iterator Iterator. + * + * @return Credential name, or NULL if iteration is complete. + */ +DEF_ACCESS (target_iterator_ssh_elevate_credential_name, + GET_ITERATOR_COLUMN_COUNT + 27); + +/** + * @brief Get the KRB5 credential name from a target iterator. + * + * @param[in] iterator Iterator. + * + * @return Credential name, or NULL if iteration is complete. + */ +DEF_ACCESS (target_iterator_krb5_credential_name, + GET_ITERATOR_COLUMN_COUNT + 28); + +/** + * @brief Get the SSH credential UUID from a target iterator. + * + * @param[in] iterator Iterator. + * + * @return Credential UUID, or NULL if iteration is complete. + */ +DEF_ACCESS (target_iterator_ssh_credential_uuid, + GET_ITERATOR_COLUMN_COUNT + 31); + +/** + * @brief Get the SMB credential UUID from a target iterator. + * + * @param[in] iterator Iterator. + * + * @return Credential UUID, or NULL if iteration is complete. + */ +DEF_ACCESS (target_iterator_smb_credential_uuid, + GET_ITERATOR_COLUMN_COUNT + 32); + +/** + * @brief Get the ESXi credential UUID from a target iterator. + * + * @param[in] iterator Iterator. + * + * @return Credential UUID, or NULL if iteration is complete. + */ +DEF_ACCESS (target_iterator_esxi_credential_uuid, + GET_ITERATOR_COLUMN_COUNT + 33); + +/** + * @brief Get the SNMP credential UUID from a target iterator. + * + * @param[in] iterator Iterator. + * + * @return Credential UUID, or NULL if iteration is complete. + */ +DEF_ACCESS (target_iterator_snmp_credential_uuid, + GET_ITERATOR_COLUMN_COUNT + 34); + +/** + * @brief Get the SSH elevate credential UUID from a target iterator. + * + * @param[in] iterator Iterator. + * + * @return Credential UUID, or NULL if iteration is complete. + */ +DEF_ACCESS (target_iterator_ssh_elevate_credential_uuid, + GET_ITERATOR_COLUMN_COUNT + 35); + +/** + * @brief Get the KRB5 credential UUID from a target iterator. + * + * @param[in] iterator Iterator. + * + * @return Credential UUID, or NULL if iteration is complete. + */ +DEF_ACCESS (target_iterator_krb5_credential_uuid, + GET_ITERATOR_COLUMN_COUNT + 36); + /** * @brief Initialise a target task iterator. * diff --git a/src/manage_sql_targets.h b/src/manage_sql_targets.h index ca1d6e517b..ba80737fbb 100644 --- a/src/manage_sql_targets.h +++ b/src/manage_sql_targets.h @@ -142,6 +142,70 @@ { "max_hosts (hosts, exclude_hosts)", \ "ips", \ KEYWORD_TYPE_INTEGER }, \ + /* The credential UUIDs. Appended after "ips" on purpose: the \ + * accessors address these columns by position, so inserting further \ + * up would silently shift every index behind it and the iterator \ + * would hand out a different credential's name than the target uses. \ + * \ + * Deliberately not in TARGET_ITERATOR_TRASH_COLUMNS, which is shorter \ + * and has no name columns either. The accessors are therefore only \ + * valid on the non-trash iterator; gmp.c branches on get.trash and \ + * uses trash_credential_uuid() there. \ + */ \ + { \ + "(SELECT uuid FROM credentials" \ + " WHERE credentials.id" \ + " = (SELECT credential FROM targets_login_data" \ + " WHERE target = targets.id" \ + " AND type = CAST ('ssh' AS text)))", \ + NULL, \ + KEYWORD_TYPE_STRING \ + }, \ + { \ + "(SELECT uuid FROM credentials" \ + " WHERE credentials.id" \ + " = (SELECT credential FROM targets_login_data" \ + " WHERE target = targets.id" \ + " AND type = CAST ('smb' AS text)))", \ + NULL, \ + KEYWORD_TYPE_STRING \ + }, \ + { \ + "(SELECT uuid FROM credentials" \ + " WHERE credentials.id" \ + " = (SELECT credential FROM targets_login_data" \ + " WHERE target = targets.id" \ + " AND type = CAST ('esxi' AS text)))", \ + NULL, \ + KEYWORD_TYPE_STRING \ + }, \ + { \ + "(SELECT uuid FROM credentials" \ + " WHERE credentials.id" \ + " = (SELECT credential FROM targets_login_data" \ + " WHERE target = targets.id" \ + " AND type = CAST ('snmp' AS text)))", \ + NULL, \ + KEYWORD_TYPE_STRING \ + }, \ + { \ + "(SELECT uuid FROM credentials" \ + " WHERE credentials.id" \ + " = (SELECT credential FROM targets_login_data" \ + " WHERE target = targets.id" \ + " AND type = CAST ('elevate' AS text)))", \ + NULL, \ + KEYWORD_TYPE_STRING \ + }, \ + { \ + "(SELECT uuid FROM credentials" \ + " WHERE credentials.id" \ + " = (SELECT credential FROM targets_login_data" \ + " WHERE target = targets.id" \ + " AND type = CAST ('krb5' AS text)))", \ + NULL, \ + KEYWORD_TYPE_STRING \ + }, \ { NULL, NULL, KEYWORD_TYPE_UNKNOWN } \ } diff --git a/src/manage_targets.h b/src/manage_targets.h index ce264d2f3d..383faedec8 100644 --- a/src/manage_targets.h +++ b/src/manage_targets.h @@ -144,6 +144,45 @@ target_iterator_ssh_elevate_credential (iterator_t *); int target_iterator_krb5_credential (iterator_t *); +/* Credential names and UUIDs from the iterator itself, so that get_targets + * does not need one statement per target and credential type. Only valid on + * the non-trash iterator - see the note in manage_sql_targets.c. */ +const char * +target_iterator_ssh_credential_name (iterator_t *); + +const char * +target_iterator_smb_credential_name (iterator_t *); + +const char * +target_iterator_esxi_credential_name (iterator_t *); + +const char * +target_iterator_snmp_credential_name (iterator_t *); + +const char * +target_iterator_ssh_elevate_credential_name (iterator_t *); + +const char * +target_iterator_krb5_credential_name (iterator_t *); + +const char * +target_iterator_ssh_credential_uuid (iterator_t *); + +const char * +target_iterator_smb_credential_uuid (iterator_t *); + +const char * +target_iterator_esxi_credential_uuid (iterator_t *); + +const char * +target_iterator_snmp_credential_uuid (iterator_t *); + +const char * +target_iterator_ssh_elevate_credential_uuid (iterator_t *); + +const char * +target_iterator_krb5_credential_uuid (iterator_t *); + int target_iterator_ssh_trash (iterator_t *);