// Author: Robert Rusecki // Company: Kolmisoft // Year: 2020 // About: Script cache effective from of rates #define SCRIPT_VERSION "1.3" #define SCRIPT_NAME "m2_rates_effective_from_cache" #define error_checker(n) _error_checker(n, __func__) #define NO_PROCESS_LOCK 1 #include "m2_functions.c" #include "m2_rates_effective_from_cache.h" struct tm *current_time; char date_str[20] = ""; int tariff_id = 0; char *effective_from; tariff_t *tariff_head; char *prefix; int by_prefix = 0; int by_date = 0; int by_range = 0; char *range_start; char *range_end; int main(int argc, char *argv[]) { // starting sript m2_init("Starting rates effective from cache script\n"); set_current_time(); get_arguments_values(argc, argv); update_cache(); m2_log("Script completed\n"); return 0; } /* Get arguments from command line */ int get_arguments_values(int argc, char *argv[]) { check_arguments(argc, argv); if (strcmp(argv[1], "-d") == 0) { tariff_head = initialize_tariff(-1); effective_from = argv[2]; by_date = 1; m2_log("Rates will be updated by effective_from: %s\n", effective_from); } else if (strcmp(argv[1], "-t") == 0) { tariff_id = atoi(argv[2]); tariff_head = initialize_tariff(tariff_id); m2_log("Rates will be updated by tariff ID: %d\n", tariff_id); } else if (strcmp(argv[1], "-a") == 0) { m2_log("All Rates will be updated\n"); tariff_head = initialize_tariff(-1); m2_log("Getting Tariffs \n"); get_tariffs(); } else if (strcmp(argv[1], "-pt") == 0) { tariff_id = atoi(argv[3]); prefix = argv[2]; by_prefix = 1; tariff_head = initialize_tariff(tariff_id); m2_log("Rates will be updated by prefix: %s and tariff ID: %d\n", prefix, tariff_id); } else if (strcmp(argv[1], "-r") == 0) { range_start = argv[2]; range_end = argv[3]; by_range = 1; tariff_head = initialize_tariff(-1); m2_log("Rates will be updated in effective_from range: %s - %s\n", range_start, range_end); } else if (strcmp(argv[1], "-c") == 0) { int not_updated_count = check_effective_from(argv[2], argv[3]); if (not_updated_count > 0) { m2_log("All Rates will be updated\n"); tariff_head = initialize_tariff(-1); m2_log("Getting Tariffs \n"); get_tariffs(); } else { m2_log("Rates effectiveness cache are correct"); exit(0); } } else { m2_log("Bad argument"); exit(1); } return error_checker(0); } /* Update cache */ int update_cache() { m2_log("Updating Cache \n"); if (by_date) { get_active_rates_by_date(effective_from); update_tariffs_by_date(); } else if (by_range) { get_active_rates_by_range(); update_tariffs_by_date(); } else { update_currently_effective_to_zero(); update_rates_by_tariff(); } return error_checker(0); } int update_rates_by_tariff() { tariff_t *current_tariff = tariff_head; while(current_tariff != NULL && current_tariff->id != -1) { m2_log("UPDATING Tariff: %d \n", current_tariff->id); m2_log("Getting effective rates \n"); get_current_effective_rates_by_tariff(current_tariff); m2_log("Updating Tariff %d rates \n", current_tariff->id); update_current_effective_rates(current_tariff); m2_log("UPDATE of Tariff %d completed!\n", current_tariff->id); current_tariff = current_tariff->next; } return error_checker(0); } int update_tariffs_by_date() { tariff_t *current_tariff = tariff_head; m2_dbg_2("tariff_head -> id: %d\n", tariff_head->id); while(current_tariff != NULL && current_tariff->id != -1) { m2_log("UPDATING Tariff: %d \n", current_tariff->id); m2_log("Updating expired rates \n"); update_expired_rates(current_tariff); m2_log("Updating Tariff %d rates \n", current_tariff->id); update_current_effective_rates(current_tariff); m2_log("UPDATE of Tariff %d completed!\n", current_tariff->id); current_tariff = current_tariff->next; } return error_checker(0); } int update_expired_rates(tariff_t *tariff) { char *where_sql = (char*) malloc(sizeof(char)); rate_t *current_rate = tariff->rate; int n = 0; int total_count = 0; while (current_rate->next != NULL && current_rate->active == 1) { int digits = strlen(current_rate->prefix); int realloc_size = strlen(where_sql) + digits + 6; where_sql = (char *) realloc(where_sql, realloc_size); if (n == 0) { memset(where_sql, 0, realloc_size); sprintf(where_sql, "'%s'", current_rate->prefix); } else { char buffer[100] = ""; sprintf(buffer, "'%s'", current_rate->prefix); strcat(where_sql, ", "); strcat(where_sql, buffer); } n++; total_count++; if (n == UPDATE_BATCH_SIZE) { m2_dbg_1("UPDATING prefixes count: %d\n", n); update_batch_of_prefix(tariff->id, where_sql); free(where_sql); where_sql = NULL; where_sql = (char*) malloc(sizeof(char)); memset(where_sql, 0, sizeof(char)); m2_dbg_1("UPDATED Prefixes\n"); m2_dbg_1("Total updated prefixes: %d\n", total_count); n = 0; } current_rate = current_rate->next; } if (n > 0) { m2_dbg_1("UPDATING prefixes count: %d\n", n); update_batch_of_prefix(tariff->id, where_sql); free(where_sql); } return error_checker(0); } int update_batch_of_prefix(int id, char *prefixes) { char *query; char where[64] = ""; query = (char*) malloc(512 + strlen(prefixes)); if (by_date) { sprintf(where, "AND effective_from != '%s'", effective_from); } sprintf(query, "UPDATE rates SET currently_effective = 0 WHERE tariff_id = %d AND prefix IN (%s) %s", id, prefixes, where); if (m2_mysql_query(query)) { return error_checker(-1); } free(query); query = NULL; return error_checker(0); } int get_active_rates_by_range() { MYSQL_RES *result; MYSQL_ROW row; char query[1024] = ""; tariff_t *current_tariff = tariff_head; rate_t *current_rate = NULL; int tariff_id = 0; int first_tariff = 1; char prev_prefix[16] = ""; int prev_tariff_id = 0; char currently_effective_prefix[16] = ""; sprintf(query, "SELECT rates.id, " "rates.currently_effective, rates.tariff_id, rates.prefix " "FROM rates WHERE (effective_from BETWEEN '%s' AND '%s') " "ORDER BY rates.tariff_id, rates.prefix, rates.effective_from DESC", range_start, range_end); if (m2_mysql_query(query)) { return error_checker(-1); } result = mysql_store_result(&mysql); if (result) { while((row = mysql_fetch_row(result))) { m2_log("rate_id: %lld\n", atoll(row[0])); tariff_id = atoi(row[2]); int currently_effective = atoi(row[1]); if (currently_effective == 1 || (strcmp(currently_effective_prefix, row[3]) == 0 && tariff_id == prev_tariff_id)){ strcpy(currently_effective_prefix, row[3]); prev_tariff_id = tariff_id; continue; } if (current_tariff->id != tariff_id) { if (first_tariff == 0) current_tariff = current_tariff->next; first_tariff = 0; strcpy(prev_prefix, ""); current_tariff->id = tariff_id; current_tariff->next = initialize_tariff(-1); current_tariff->rate = initialize_rate(0); current_rate = current_tariff->rate; prev_tariff_id = tariff_id; } char current_prefix[16] = ""; if (row[3]) { strcpy(current_prefix, row[3]); } else { continue; } if (strcmp(prev_prefix, current_prefix) == 0) continue; strcpy(prev_prefix, current_prefix); current_rate->id = atoll(row[0]); m2_dbg_1("save_id: %lld\n", current_rate->id); current_rate->active = 1; strcpy(current_rate->prefix, current_prefix); current_rate->next = initialize_rate(0); current_rate = current_rate->next; } } return error_checker(0); } int get_active_rates_by_date(char *date) { MYSQL_RES *result; MYSQL_ROW row; char query[1024] = ""; tariff_t *current_tariff = tariff_head; int tariff_id = 0; rate_t *current_rate = NULL; int first_tariff = 1; sprintf(query, "SELECT id, tariff_id, prefix FROM rates WHERE effective_from = '%s' ORDER BY tariff_id", date); if (m2_mysql_query(query)) { return error_checker(-1); } result = mysql_store_result(&mysql); if (result) { while ((row = mysql_fetch_row(result))) { tariff_id = atoi(row[1]); if (current_tariff->id != tariff_id) { if (first_tariff == 0) { current_tariff = current_tariff->next; } first_tariff = 0; current_tariff->id = atoi(row[1]); current_tariff->next = initialize_tariff(-1); tariff_id = current_tariff->id; current_tariff->rate = initialize_rate(0); current_rate = current_tariff->rate; m2_dbg_2("Current Tariff ID: %d\n", current_tariff->id); } current_rate->id = atoll(row[0]); current_rate->active = 1; if (row[2]) strcpy(current_rate->prefix, row[2]); m2_dbg_2("current_rate->id: %lld\n", current_rate->id); current_rate->next = initialize_rate(0); current_rate = current_rate->next; } } return error_checker(0); } int get_tariffs() { MYSQL_RES *result; MYSQL_ROW row; char query[1024] = ""; tariff_t *current_tariff = tariff_head; sprintf(query, "SELECT tariff_id FROM rates GROUP BY tariff_id"); if (m2_mysql_query(query)) { return error_checker(-1); } result = mysql_store_result(&mysql); if (result) { while ((row = mysql_fetch_row(result))) { current_tariff->id = atoi(row[0]); current_tariff->next = initialize_tariff(-1); current_tariff = current_tariff->next; } } return error_checker(0); } int update_current_effective_rates(tariff_t *tariff) { char *where_sql = (char*) malloc(sizeof(char)); rate_t *current_rate = tariff->rate; int n = 0; while (current_rate->active == 1) { int digits = floor(log10(abs(current_rate->id))) + 1; where_sql = (char *) realloc(where_sql, sizeof(char) * (strlen(where_sql) + digits + 4)); if (n == 0) { sprintf(where_sql, "%lld", current_rate->id); } else { char buffer[100] = ""; sprintf(buffer, "%lld", current_rate->id); strcat(where_sql, ", "); strcat(where_sql, buffer); } n++; if (n == UPDATE_BATCH_SIZE) { m2_dbg_1("UPDATING IDS count: %d\n", n); update_batch_of_ids(where_sql); memset(where_sql, 0, strlen(where_sql)); free(where_sql); where_sql = NULL; where_sql = (char*) malloc(sizeof(char)); memset(where_sql, 0, sizeof(char)); m2_dbg_1("UPDATED IDS\n"); n = 0; } current_rate = current_rate->next; } if (n > 0) { m2_dbg_1("UPDATING IDS count: %d\n", n); update_batch_of_ids(where_sql); } free(where_sql); return error_checker(0); } int update_batch_of_ids(char *ids) { char *query; query = (char*) malloc((strlen(ids) + 128) * sizeof(char)); sprintf(query, "UPDATE rates SET currently_effective = 1 WHERE id IN (%s)", ids); if (m2_mysql_query(query)) { return error_checker(-1); } free(query); query = NULL; return error_checker(0); } int update_currently_effective_to_zero() { char query[1024] = ""; char where_sql[256] = ""; m2_log("Updating rates currently_effective to zero \n"); if (tariff_id > 0) { m2_dbg_1("Updating by one tariff ID: %d\n", tariff_id); sprintf(where_sql, "WHERE tariff_id = %d", tariff_id); if (by_prefix) { char buffer[100] = ""; m2_dbg_1("Updating by one prefix: %s\n", prefix); sprintf(buffer, " AND prefix = '%s'", prefix); strcat(where_sql, buffer); } } sprintf(query, "UPDATE rates SET currently_effective = 0 %s", where_sql); if (m2_mysql_query(query)) { return error_checker(-1); } return error_checker(0); } int get_current_effective_rates_by_tariff(tariff_t *tariff) { MYSQL_RES *result; MYSQL_ROW row; char query[1024] = ""; char where_sql[256] = ""; tariff->rate = initialize_rate(-1); rate_t *current_rate = tariff->rate; sprintf(where_sql, "AND tariff_id = %d", tariff->id); if (by_prefix) { char buffer[100] = ""; sprintf(buffer, " AND prefix = '%s' ", prefix); strcat(where_sql, buffer); } /* SELECT id FROM (SELECT rates.id, rates.prefix FROM rates LEFT JOIN destinations on rates.destination_id = destinations.id WHERE (effective_from < now() OR effective_from IS NULL) AND #{condition} ORDER BY rates.effective_from DESC, id DESC) AS tmp_table GROUP BY prefix */ sprintf(query, "SELECT id FROM (SELECT rates.id, rates.prefix FROM rates " "WHERE (effective_from < now() OR effective_from IS NULL) %s " "ORDER BY rates.effective_from DESC) AS tmp_table GROUP BY prefix", where_sql); m2_dbg_1("SELECT Query: %s \n", query); if (m2_mysql_query(query)) { return error_checker(-1); } result = mysql_store_result(&mysql); if (result) { while ((row = mysql_fetch_row(result))) { current_rate->id = atoll(row[0]); current_rate->active = 1; m2_dbg_2("current_rate->id: %lld\n", current_rate->id); current_rate->next = initialize_rate(0); current_rate = current_rate->next; } } //m2_log("head id : %d\n", head->id); return error_checker(0); } int check_effective_from(char *from, char *till) { MYSQL_RES *result; MYSQL_ROW row; char query[1024] = ""; int not_updated_count = 0; sprintf(query, "SELECT COUNT(*) FROM rates " "WHERE effective_from BETWEEN '%s' AND '%s' AND currently_effective = 0", from, till); if (m2_mysql_query(query)) { return error_checker(-1); } result = mysql_store_result(&mysql); if (result) { if ((row = mysql_fetch_row(result))) { not_updated_count = atoi(row[0]); } } if (not_updated_count > 0) { return error_checker(not_updated_count); } return error_checker(0); } //=========================================== /* Helper methods */ int _error_checker(int return_code, char const * caller_name) { if (return_code < 0) { char query[512] = ""; m2_log("Error in: %s\n", caller_name); sprintf(query, "INSERT IGNORE INTO conflines (name, value) SELECT 'rates_cache_error', 'Error in: %s'"\ " FROM dual WHERE NOT EXISTS (SELECT * FROM conflines WHERE name = 'rates_cache_error')", caller_name); m2_mysql_query(query); exit(return_code); } else { return return_code; } } int set_current_time() { time_t rawtime; time(&rawtime); current_time = localtime(&rawtime); strftime(date_str, sizeof(date_str), DATE_FORMAT, current_time); m2_log("Current time and date: %s\n", date_str); return error_checker(0); } tariff_t *initialize_tariff(int id) { tariff_t *tariff = (tariff_t *) malloc(sizeof(tariff_t)); tariff->id = id; tariff->next = NULL; tariff->rate = NULL; return tariff; } rate_t *initialize_rate(long long int id) { rate_t *rate = (rate_t *) malloc(sizeof(rate_t)); rate->id = id; rate->next = NULL; rate->active = 0; return rate; } int check_arguments(int argc, char *argv[]) { if (argc < 2) { goto error; } else if (strcmp(argv[1], "-pt") == 0 && argc != 4) { goto error; } else if ((strcmp(argv[1], "-a") != 0 || strcmp(argv[1], "-c") != 0 ) && argc < 3) { goto error; } else { m2_dbg_1("Argument option: %s\n", argv[1]); } return error_checker(0); error: m2_log("Use it with options: -d; -t; -a; -pt"); exit(1); }