/* * Re-organize one table. */ static void reorg_one_table(const reorg_table *table, const char *orderby) { PGresult *res; const char *params[1]; int num; int i; int num_waiting = 0; char *vxid; char buffer[12]; StringInfoData sql; initStringInfo(&sql); elog(DEBUG2, "---- reorg_one_table ----"); elog(DEBUG2, "target_name : %s", table->target_name); elog(DEBUG2, "target_oid : %u", table->target_oid); elog(DEBUG2, "target_toast : %u", table->target_toast); elog(DEBUG2, "target_tidx : %u", table->target_tidx); elog(DEBUG2, "pkid : %u", table->pkid); elog(DEBUG2, "ckid : %u", table->ckid); elog(DEBUG2, "create_pktype : %s", table->create_pktype); elog(DEBUG2, "create_log : %s", table->create_log); elog(DEBUG2, "create_trigger : %s", table->create_trigger); elog(DEBUG2, "alter_table : %s", table->alter_table); elog(DEBUG2, "create_table : %s", table->create_table); elog(DEBUG2, "drop_columns : %s", table->drop_columns ? table->drop_columns : "(skipped)"); elog(DEBUG2, "delete_log : %s", table->delete_log); elog(DEBUG2, "lock_table : %s", table->lock_table); elog(DEBUG2, "sql_peek : %s", table->sql_peek); elog(DEBUG2, "sql_insert : %s", table->sql_insert); elog(DEBUG2, "sql_delete : %s", table->sql_delete); elog(DEBUG2, "sql_update : %s", table->sql_update); elog(DEBUG2, "sql_pop : %s", table->sql_pop); /* * 1. Setup workspaces and a trigger. */ elog(DEBUG2, "---- setup ----"); lock_exclusive(utoa(table->target_oid, buffer), table->lock_table); /* * Check z_reorg_trigger is the trigger executed at last so that * other before triggers cannot modify triggered tuples. */ params[0] = utoa(table->target_oid, buffer); res = execute("SELECT reorg.conflicted_triggers($1)", 1, params); if (PQntuples(res) > 0) ereport(ERROR, (errcode(E_PG_COMMAND), errmsg("trigger %s conflicted for %s", PQgetvalue(res, 0, 0), table->target_name))); PQclear(res); command(table->create_pktype, 0, NULL); command(table->create_log, 0, NULL); command(table->create_trigger, 0, NULL); command(table->alter_table, 0, NULL); printfStringInfo(&sql, "SELECT reorg.disable_autovacuum('reorg.log_%u')", table->target_oid); command(sql.data, 0, NULL); command("COMMIT", 0, NULL); /* * Register the table to be dropped on error. We use pktype as * an advisory lock. The registration should be done after * the first command succeeds. */ pgut_atexit_push(&reorg_cleanup, (void *) table); /* * 2. Copy tuples into temp table. */ elog(DEBUG2, "---- copy tuples ----"); command("BEGIN ISOLATION LEVEL SERIALIZABLE", 0, NULL); /* SET work_mem = maintenance_work_mem */ command("SELECT set_config('work_mem', current_setting('maintenance_work_mem'), true)", 0, NULL); if (orderby && !orderby[0]) command("SET LOCAL synchronize_seqscans = off", 0, NULL); res = execute(SQL_XID_SNAPSHOT, 0, NULL); vxid = strdup(PQgetvalue(res, 0, 0)); PQclear(res); command(table->delete_log, 0, NULL); command(table->create_table, 0, NULL); printfStringInfo(&sql, "SELECT reorg.disable_autovacuum('reorg.table_%u')", table->target_oid); if (table->drop_columns) command(table->drop_columns, 0, NULL); command(sql.data, 0, NULL); command("COMMIT", 0, NULL); /* * 3. Create indexes on temp table. */ elog(DEBUG2, "---- create indexes ----"); params[0] = utoa(table->target_oid, buffer); res = execute("SELECT indexrelid," " reorg.reorg_indexdef(indexrelid, indrelid)," " indisvalid," " pg_get_indexdef(indexrelid)" " FROM pg_index WHERE indrelid = $1", 1, params); num = PQntuples(res); for (i = 0; i < num; i++) { reorg_index index; int c = 0; const char *isvalid; const char *indexdef; index.target_oid = getoid(res, i, c++); index.create_index = getstr(res, i, c++); isvalid = getstr(res, i, c++); indexdef = getstr(res, i, c++); if (isvalid && isvalid[0] == 'f') { elog(WARNING, "skipping invalid index: %s", indexdef); continue; } elog(DEBUG2, "[%d]", i); elog(DEBUG2, "target_oid : %u", index.target_oid); elog(DEBUG2, "create_index : %s", index.create_index); /* * NOTE: If we want to create multiple indexes in parallel, * we need to call create_index in multiple connections. */ command(index.create_index, 0, NULL); } PQclear(res); /* * 4. Apply log to temp table until no tuples are left in the log * and all of the old transactions are finished. */ for (;;) { num = apply_log(table, APPLY_COUNT); if (num > 0) continue; /* there might be still some tuples, repeat. */ /* old transactions still alive ? */ params[0] = vxid; res = execute(SQL_XID_ALIVE, 1, params); num = PQntuples(res); if (num > 0) { /* Wait for old transactions. * Only display the message below when the number of * transactions we are waiting on changes (presumably, * num_waiting should only go down), so as not to * be too noisy. */ if (num != num_waiting) { elog(NOTICE, "Waiting for %d transactions to finish. First PID: %s", num, PQgetvalue(res, 0, 0)); num_waiting = num; } PQclear(res); sleep(1); continue; } else { /* All old transactions are finished; * go to next step. */ PQclear(res); break; } } /* * 5. Swap. */ elog(DEBUG2, "---- swap ----"); lock_exclusive(utoa(table->target_oid, buffer), table->lock_table); apply_log(table, 0); params[0] = utoa(table->target_oid, buffer); command("SELECT reorg.reorg_swap($1)", 1, params); command("COMMIT", 0, NULL); /* * 6. Drop. */ elog(DEBUG2, "---- drop ----"); command("BEGIN ISOLATION LEVEL READ COMMITTED", 0, NULL); params[0] = utoa(table->target_oid, buffer); command("SELECT reorg.reorg_drop($1)", 1, params); command("COMMIT", 0, NULL); pgut_atexit_pop(&reorg_cleanup, (void *) table); free(vxid); /* * 7. Analyze. * Note that cleanup hook has been already uninstalled here because analyze * is not an important operation; No clean up even if failed. */ if (analyze) { elog(DEBUG2, "---- analyze ----"); command("BEGIN ISOLATION LEVEL READ COMMITTED", 0, NULL); printfStringInfo(&sql, "ANALYZE %s", table->target_name); command(sql.data, 0, NULL); command("COMMIT", 0, NULL); } termStringInfo(&sql); }
void apply_log() { apply_log(&m_feat); }