From 69be63aa2e0ff331ad2031e5c991e6a703c7c47b Mon Sep 17 00:00:00 2001 From: Will Brown Date: Mon, 25 Nov 2024 00:36:40 -0500 Subject: [PATCH] Abstract out the last remaining SQL queries in timer_retrieve_feeds() --- feed_info.c | 65 ++++++++++++++++++++++++++++++++++++++++++++++++----- feed_info.h | 15 +++++++++++++ main.c | 54 +++++++++++--------------------------------- 3 files changed, 87 insertions(+), 47 deletions(-) diff --git a/feed_info.c b/feed_info.c index 0d78d87..f8d2551 100644 --- a/feed_info.c +++ b/feed_info.c @@ -23,10 +23,24 @@ static const char *ZBLOCK_FEED_INFO_ERRORS[] = { "Invalid arguments provided", "An error was encountered with the feed database", "The feed already exists", - "The feed does not exist" + "The feed does not exist", + "Finished getting feed info", + "Out of memory" }; static_assert(sizeof(ZBLOCK_FEED_INFO_ERRORS) / sizeof(*ZBLOCK_FEED_INFO_ERRORS) == ZBLOCK_FEED_INFO_ERRORCOUNT, "Not all feed info errors implemented"); +// free all information associated with a minimal feed info struct (does not assume the struct was allocated using malloc) +void zblock_feed_info_minimal_free(zblock_feed_info_minimal *feed_info) { + free(feed_info->last_pubDate); + free(feed_info->url); +} + +// free all information associated with a feed info struct (does not assume the struct was allocated using malloc) +void zblock_feed_info_free(zblock_feed_info *feed_info) { + free(feed_info->title); + zblock_feed_info_minimal_free((zblock_feed_info_minimal *) feed_info); +} + // returns a string about the result of a feed_info function const char *zblock_feed_info_strerror(zblock_feed_info_err error) { return error < 0 || error >= ZBLOCK_FEED_INFO_ERRORCOUNT ? "Unspecified error" : ZBLOCK_FEED_INFO_ERRORS[error]; @@ -43,6 +57,49 @@ time_t pubDate_to_time_t(char *s) { return timegm(&tm); } +// Begin retrieval of feed info objects. +zblock_feed_info_err zblock_feed_info_retrieve_list_begin(PGconn *conn) { + if (!conn) return ZBLOCK_FEED_INFO_INVALID_ARGS; + + if (!PQsendQueryParams( + conn, "SELECT url, last_pubDate, channel_id from feeds", + 0, NULL, NULL, NULL, NULL, 1 + )) { + return ZBLOCK_FEED_INFO_DBERROR; + } + PQsetSingleRowMode(conn); + return ZBLOCK_FEED_INFO_OK; +} + +// Retrieve the next feed list object. +// On error, no more objects can be retrieved and the returned object is invalid. +zblock_feed_info_err zblock_feed_info_retrieve_list_item(PGconn *conn, zblock_feed_info_minimal *feed_info) { + if (!conn || !feed_info) return ZBLOCK_FEED_INFO_INVALID_ARGS; + + PGresult *res = PQgetResult(conn); + if (PQresultStatus(res) != PGRES_SINGLE_TUPLE) { + if (PQresultStatus(res) != PGRES_TUPLES_OK) { + log_error("Unable to retrieve feeds: %s", PQresultErrorMessage(res)); + PQclear(res); + return ZBLOCK_FEED_INFO_DBERROR; + } else { + PQclear(res); + return ZBLOCK_FEED_INFO_FINISHED; + } + } + + feed_info->url = strdup(PQgetvalue(res, 0, 0)); + feed_info->last_pubDate = strdup(PQgetvalue(res, 0, 1)); + if (!feed_info->url || !feed_info->last_pubDate) { + PQclear(res); + return ZBLOCK_FEED_INFO_NOMEM; + } + feed_info->channel_id = be64toh(*(uint64_t *) PQgetvalue(res, 0, 2)); + + PQclear(res); + return ZBLOCK_FEED_INFO_OK; +} + // check if the feed currently exists. the result is in the exists pointer. zblock_feed_info_err zblock_feed_info_exists(PGconn *conn, const char *url, u64snowflake channel_id, int *exists) { if (!conn || !exists) return ZBLOCK_FEED_INFO_INVALID_ARGS; @@ -149,11 +206,7 @@ zblock_feed_info_err zblock_feed_info_update(PGconn *conn, zblock_feed_info_mini ); zblock_feed_info_err result = ZBLOCK_FEED_INFO_OK; - if (PQresultStatus(update_res) != PGRES_COMMAND_OK) { - log_error("Failed to update feed: %s", PQresultErrorMessage(update_res)); - result = ZBLOCK_FEED_INFO_DBERROR; - } - + if (PQresultStatus(update_res) != PGRES_COMMAND_OK) result = ZBLOCK_FEED_INFO_DBERROR; PQclear(update_res); return result; } diff --git a/feed_info.h b/feed_info.h index bd9de4f..bd0f850 100644 --- a/feed_info.h +++ b/feed_info.h @@ -27,15 +27,30 @@ typedef enum { ZBLOCK_FEED_INFO_DBERROR, ZBLOCK_FEED_INFO_EXISTS, ZBLOCK_FEED_INFO_NOT_EXIST, + ZBLOCK_FEED_INFO_FINISHED, + ZBLOCK_FEED_INFO_NOMEM, ZBLOCK_FEED_INFO_ERRORCOUNT } zblock_feed_info_err; +// free all information associated with a minimal feed info struct (does not assume the struct was allocated using malloc) +void zblock_feed_info_minimal_free(zblock_feed_info_minimal *feed_info); + +// free all information associated with a feed info struct (does not assume the struct was allocated using malloc) +void zblock_feed_info_free(zblock_feed_info *feed_info); + // maybe change the function signature so you can actually do error handling with the result? time_t pubDate_to_time_t(char *s); // returns a string about the result of a feed_info function const char *zblock_feed_info_strerror(zblock_feed_info_err error); +// Begin retrieval of feed info objects. +zblock_feed_info_err zblock_feed_info_retrieve_list_begin(PGconn *conn); + +// Retrieve the next feed list object. +// On error, no more objects can be retrieved and the returned object is invalid. +zblock_feed_info_err zblock_feed_info_retrieve_list_item(PGconn *conn, zblock_feed_info_minimal *feed_info); + // check if the feed currently exists. the result is in the exists pointer. zblock_feed_info_err zblock_feed_info_exists(PGconn *conn, const char *url, u64snowflake channel_id, int *exists); diff --git a/main.c b/main.c index 2d0d6b0..9059bae 100644 --- a/main.c +++ b/main.c @@ -70,64 +70,40 @@ static void timer_retrieve_feeds(struct discord *client, struct discord_timer *t return; } - if (!PQsendQueryParams( - database_conn, "SELECT url, last_pubDate, channel_id from feeds", - 0, NULL, NULL, NULL, NULL, 1 - )) { + // Begin retrieval of feed list objects. + if (zblock_feed_info_retrieve_list_begin(database_conn)) { log_error("Unable to retrieve feed list: %s", PQerrorMessage(database_conn)); + curl_multi_cleanup(multi); return; } - PQsetSingleRowMode(database_conn); // put running handles up here so we can start transfers now instead of later int running_handles, total_feeds = 0; // get all the required feed info to send messages - PGresult *database_res; - while ((database_res = PQgetResult(database_conn))) { - if (PQresultStatus(database_res) != PGRES_SINGLE_TUPLE) { - if (PQresultStatus(database_res) != PGRES_TUPLES_OK) { - log_error("Unable to retrieve feeds: %s", PQresultErrorMessage(database_res)); - } - goto db_loop_end; - } - + zblock_feed_info_minimal feed_info; + while (!zblock_feed_info_retrieve_list_item(database_conn, &feed_info)) { ++total_feeds; zblock_feed_buffer *feed_buffer = malloc(sizeof(*feed_buffer)); if (!feed_buffer) { log_error("Failure allocating feed buffer: %s", strerror(errno)); - goto db_loop_end; + continue; } - feed_buffer->info.url = strdup(PQgetvalue(database_res, 0, 0)); - if (!feed_buffer->info.url) { - log_error("Failure allocating feed buffer: %s", strerror(errno)); - free(feed_buffer); - goto db_loop_end; - } - feed_buffer->info.last_pubDate = strdup(PQgetvalue(database_res, 0, 1)); - if (!feed_buffer->info.url) { - log_error("Failure allocating feed buffer: %s", strerror(errno)); - free(feed_buffer->info.url); - free(feed_buffer); - goto db_loop_end; - } - feed_buffer->info.channel_id = be64toh(*(uint64_t *) PQgetvalue(database_res, 0, 2)); + feed_buffer->info = feed_info; feed_buffer->fp = open_memstream(&feed_buffer->buf, &feed_buffer->bufsize); if (!feed_buffer->fp) { log_error("Unable to retrieve feed: %s", strerror(errno)); - free(feed_buffer->info.last_pubDate); - free(feed_buffer->info.url); + zblock_feed_info_minimal_free(&feed_buffer->info); free(feed_buffer); - goto db_loop_end; + continue; } CURL *feed_handle = curl_easy_init(); if (!feed_handle) { fclose(feed_buffer->fp); free(feed_buffer->buf); - free(feed_buffer->info.last_pubDate); - free(feed_buffer->info.url); + zblock_feed_info_minimal_free(&feed_buffer->info); free(feed_buffer); - goto db_loop_end; + continue; } curl_easy_setopt(feed_handle, CURLOPT_URL, feed_buffer->info.url); @@ -139,15 +115,11 @@ static void timer_retrieve_feeds(struct discord *client, struct discord_timer *t curl_easy_cleanup(feed_handle); fclose(feed_buffer->fp); free(feed_buffer->buf); - free(feed_buffer->info.last_pubDate); - free(feed_buffer->info.url); + zblock_feed_info_minimal_free(&feed_buffer->info); free(feed_buffer); - goto db_loop_end; + continue; } curl_multi_perform(multi, &running_handles); - - db_loop_end: - PQclear(database_res); } int successful_feeds = 0;