/* Ugly ---> Remove this */ txn_info *init_txn_info(char *database_name, TransactionId gxid) { database_info *database; txn_info *cur_txn_info; if ((database = find_database_info(database_name)) == NULL) return NULL; if (database->head_txn_info == NULL) { database->head_txn_info = database->last_txn_info = (txn_info *)malloc(sizeof(txn_info)); if (database->head_txn_info == NULL) return NULL; memset(database->head_txn_info, 0, sizeof(txn_info)); return database->head_txn_info; } for(cur_txn_info = database->head_txn_info; cur_txn_info; cur_txn_info = cur_txn_info->next) { if (cur_txn_info->gxid == gxid) return(cur_txn_info); } cur_txn_info->next = database->last_txn_info = (txn_info *)malloc(sizeof(txn_info)); if (cur_txn_info->next == NULL) return(NULL); memset(cur_txn_info->next, 0, sizeof(txn_info)); if ((cur_txn_info->next->txn_stat = (TXN_STATUS *)malloc(sizeof(TXN_STATUS) * pgxc_clean_node_count)) == NULL) return(NULL); memset(cur_txn_info->next->txn_stat, 0, sizeof(TXN_STATUS) * pgxc_clean_node_count); return cur_txn_info->next; }
database_info *add_database_info(char *database_name) { database_info *rv; if ((rv = find_database_info(database_name)) != NULL) return rv; /* Already in the list */ rv = malloc(sizeof(database_info)); if (rv == NULL) return NULL; rv->next = NULL; rv->database_name = strdup(database_name); if (rv->database_name == NULL) { free(rv); return NULL; } rv->head_txn_info = NULL; rv->last_txn_info = NULL; if (head_database_info == NULL) { head_database_info = last_database_info = rv; return rv; } else { last_database_info->next = rv; last_database_info = rv; return rv; } }
static txn_info * make_txn_info(char *dbname, TransactionId gxid, char *xid, char *owner) { database_info *dbinfo; txn_info *txn; if ((dbinfo = find_database_info(dbname)) == NULL) dbinfo = add_database_info(dbname); txn = (txn_info *)malloc(sizeof(txn_info)); if (txn == NULL) return NULL; memset(txn, 0, sizeof(txn_info)); txn->gxid = gxid; txn->xid = strdup(xid); if (txn->xid == NULL) { free(txn); return NULL; } txn->owner = strdup(owner); if (txn->owner == NULL) { free(txn); return NULL; } if (dbinfo->head_txn_info == NULL) { dbinfo->head_txn_info = dbinfo->last_txn_info = txn; } else { dbinfo->last_txn_info->next = txn; dbinfo->last_txn_info = txn; } txn->txn_stat = (TXN_STATUS *)malloc(sizeof(TXN_STATUS) * pgxc_clean_node_count); if (txn->txn_stat == NULL) return(NULL); memset(txn->txn_stat, 0, sizeof(TXN_STATUS) * pgxc_clean_node_count); return txn; }
/* * * Main * */ int main(int argc, char *argv[]) { /* Should setup pglocale when it is supported by XC core */ if (argc > 1) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { usage(); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) { showVersion(); exit(0); } } parse_pgxc_clean_options(argc, argv); /* * Check missing arguments */ if (clean_all_databases == false && head_database_names == NULL) { fprintf(stderr, "%s: you must specify -a or -d option.\n", progname); exit(1); } /* * Arrange my environment */ if (output_filename) { /* Prepare output filename */ outf = fopen(output_filename, "w"); if (outf == NULL) { fprintf(stderr, "%s: Cannot ope output file %s (%s)\n", progname, output_filename, strerror(errno)); exit(1); } errf = outf; } else { outf = stdout; errf = stderr; } if (coordinator_host == NULL) { /* Default Coordinator host */ if ((coordinator_host = getenv("PGHOST")) == NULL) coordinator_host = "localhost"; } if (coordinator_port == -1) { /* Default Coordinator port */ char *pgport; if ((pgport = getenv("PGPORT")) == NULL) coordinator_port = DEF_PGPORT; /* pg_config.h */ else coordinator_port = atoi(pgport); } if (username == NULL) strcpy(password_prompt, "Password: "******"Password for user %s: ", username); if (try_password_opt == TRI_YES) password = simple_prompt(password_prompt, 100, false); if (verbose_opt) { /* Print environments */ fprintf(outf, "%s (%s): Cleanup outstanding 2PCs.\n", progname, PG_VERSION); /* Target databaess */ fprintf(outf, "Target databases:"); if (clean_all_databases) fprintf(outf, "(ALL)\n"); else { database_names *cur_name; for(cur_name = head_database_names; cur_name; cur_name = cur_name->next) fprintf(outf, " %s", cur_name->database_name); fprintf(outf, "\n"); } /* Username to use */ fprintf(outf, "Username: %s\n", username ? username : "******"); /* Status opt */ fprintf(outf, "Status opt: %s\n", status_opt ? "on" : "off"); /* No-dlean opt */ fprintf(outf, "no-clean: %s\n", no_clean_opt ? "on" : "off"); } /* Connect to XC server */ if (verbose_opt) { fprintf(outf, "%s: connecting to database \"%s\", host: \"%s\", port: %d\n", progname, clean_all_databases ? "postgres" : head_database_names->database_name, coordinator_host, coordinator_port); } coord_conn = loginDatabase(coordinator_host, coordinator_port, username, password, clean_all_databases ? "postgres" : head_database_names->database_name, progname, "auto", password_prompt); if (verbose_opt) { fprintf(outf, "%s: connected successfully\n", progname); } /* * Get my nodename (connected Coordinator) */ getMyNodename(coord_conn); if (verbose_opt) { fprintf(outf, "%s: Connected to the node \"%s\"\n", progname, my_nodename); } /* * Get available databases * * pgxc_clean assumes that all the database are available from the connecting Coordinator. * Some (expert) DBA can create a database local to subset of the node by EXECUTE DIRECT. * In this case, DBA may have to clean outstanding 2PC transactions manually or clean * 2PC transactions by connecting pgxc_clean to different Coordinators. * * If such node-subset database is found to be used widely, pgxc_clean may need * an extension to deal with this case. */ getDatabaseList(coord_conn); if (verbose_opt) { database_info *cur_database; fprintf(outf, "%s: Databases visible from the node \"%s\": ", progname, my_nodename); if (head_database_info) { for (cur_database = head_database_info; cur_database; cur_database = cur_database->next) { fprintf(outf, " \"%s\"", cur_database->database_name); } fputc('\n', outf); } } /* * Get list of Coordinators * * As in the case of database, we clean transactions in visible nodes from the * connecting Coordinator. DBA can also setup different node configuration * at different Coordinators. In this case, DBA should be careful to choose * appropriate Coordinator to clean up transactions. */ getNodeList(coord_conn); if (verbose_opt) { int ii; fprintf(outf, "%s: Node list visible from the node \"%s\"\n", progname, my_nodename); for (ii = 0; ii < pgxc_clean_node_count; ii++) { fprintf(outf, "Name: %s, host: %s, port: %d, type: %s\n", pgxc_clean_node_info[ii].node_name, pgxc_clean_node_info[ii].host, pgxc_clean_node_info[ii].port, pgxc_clean_node_info[ii].type == NODE_TYPE_COORD ? "coordinator" : "datanode"); } } /* * Get list of prepared statement */ getPreparedTxnList(coord_conn); /* * Check if there're any 2PC candidate to recover */ if (!check2PCExists()) { fprintf(errf, "%s: There's no prepared 2PC in this cluster. Exiting.\n", progname); exit(0); } /* * Check status of each prepared transaction. To do this, look into * nodes where the transaction is not recorded as "prepared". * Possible status are unknown (prepare has not been issued), committed or * aborted. */ getTxnInfoOnOtherNodesAll(coord_conn); if (verbose_opt) { /* Print all the prepared transaction list */ database_info *cur_db; fprintf(outf, "%s: 2PC transaction list.\n", progname); for (cur_db = head_database_info; cur_db; cur_db = cur_db->next) { txn_info *txn; fprintf(outf, "Database: \"%s\":\n", cur_db->database_name); for (txn = cur_db->head_txn_info; txn; txn = txn->next) { int ii; fprintf(outf, " gxid: %d, xid: \"%s\", owner: %s\n", txn->gxid, txn->xid, txn->owner); for (ii = 0; ii < pgxc_clean_node_count; ii++) { fprintf(outf, " node: %s, status: %s\n", pgxc_clean_node_info[ii].node_name, str_txn_stat(txn->txn_stat[ii])); } } } } /* * Then disconnect from the database. * I need to login to specified databases which 2PC is issued for. Again, we assume * that all the prepare is issued against the same database in each node, which * current Coordinator does and there seems to be no way to violate this assumption. */ if (verbose_opt) { fprintf(outf, "%s: disconnecting\n", progname); } PQfinish(coord_conn); /* * If --no-clean option is specified, we exit here. */ if (no_clean_opt) { fprintf(outf, "--no-clean opt is specified. Exiting.\n"); exit(0); } /* * Recover 2PC for specified databases */ if (clean_all_databases) { database_info *cur_database_info; for(cur_database_info = head_database_info; cur_database_info; cur_database_info = cur_database_info->next) { recover2PCForDatabase(cur_database_info); } } else { database_info *cur_database_info; database_names *cur_database_name; for(cur_database_name = head_database_names; cur_database_name; cur_database_name = cur_database_name->next) { cur_database_info = find_database_info(cur_database_name->database_name); if (cur_database_info) { recover2PCForDatabase(cur_database_info); } } } exit(0); }