Esempio n. 1
0
static void
parse_indexdef(IndexDef *stmt, Oid index, Oid table)
{
    char *sql = pg_get_indexdef_string(index);
    const char *idxname = get_quoted_relname(index);
    const char *tblname = get_relation_name(table);

    /* CREATE [UNIQUE] INDEX */
    stmt->create = sql;
    sql = skip_const(index, sql, "CREATE INDEX", "CREATE UNIQUE INDEX");
    /* index */
    stmt->index = sql;
    sql = skip_const(index, sql, idxname, NULL);
    /* ON */
    sql = skip_const(index, sql, "ON", NULL);
    /* table */
    stmt->table = sql;
    sql = skip_const(index, sql, tblname, NULL);
    /* USING */
    sql = skip_const(index, sql, "USING", NULL);
    /* type */
    stmt->type = sql;
    sql = skip_ident(index, sql);
    /* (columns) */
    if ((sql = strchr(sql, '(')) == NULL)
        parse_error(index);
    sql++;
    stmt->columns = sql;
    if ((sql = skip_until(index, sql, ')')) == NULL)
        parse_error(index);
    /* options */
    stmt->options = sql;
}
Esempio n. 2
0
/**
 * @fn      Datum reorg_drop(PG_FUNCTION_ARGS)
 * @brief   Delete temporarily objects.
 *
 * reorg_drop(oid, relname)
 *
 * @param	oid		Oid of target table.
 * @retval			None.
 */
Datum
reorg_drop(PG_FUNCTION_ARGS)
{
    Oid			oid = PG_GETARG_OID(0);
    const char *relname = get_quoted_relname(oid);
    const char *nspname = get_quoted_nspname(oid);

    /* authority check */
    must_be_superuser("reorg_drop");

    /* connect to SPI manager */
    reorg_init();

    /*
     * drop reorg trigger: We have already dropped the trigger in normal
     * cases, but it can be left on error.
     */
    execute_with_format(
        SPI_OK_UTILITY,
        "DROP TRIGGER IF EXISTS z_reorg_trigger ON %s.%s CASCADE",
        nspname, relname);

#if PG_VERSION_NUM < 80400
    /* delete autovacuum settings */
    execute_with_format(
        SPI_OK_DELETE,
        "DELETE FROM pg_catalog.pg_autovacuum v"
        " USING pg_class c, pg_namespace n"
        " WHERE relname IN ('log_%u', 'table_%u')"
        "   AND n.nspname = 'reorg'"
        "   AND c.relnamespace = n.oid"
        "   AND v.vacrelid = c.oid",
        oid, oid);
#endif

    /* drop log table */
    execute_with_format(
        SPI_OK_UTILITY,
        "DROP TABLE IF EXISTS reorg.log_%u CASCADE",
        oid);

    /* drop temp table */
    execute_with_format(
        SPI_OK_UTILITY,
        "DROP TABLE IF EXISTS reorg.table_%u CASCADE",
        oid);

    /* drop type for log table */
    execute_with_format(
        SPI_OK_UTILITY,
        "DROP TYPE IF EXISTS reorg.pk_%u CASCADE",
        oid);

    SPI_finish();

    PG_RETURN_VOID();
}
Esempio n. 3
0
/**
 * @fn      Datum reorg_swap(PG_FUNCTION_ARGS)
 * @brief   Swapping relfilenode of tables and relation ids of toast tables
 *          and toast indexes.
 *
 * reorg_swap(oid, relname)
 *
 * TODO: remove useless CommandCounterIncrement().
 *
 * @param	oid		Oid of table of target.
 * @retval			None.
 */
Datum
reorg_swap(PG_FUNCTION_ARGS)
{
    Oid				oid = PG_GETARG_OID(0);
    const char	   *relname = get_quoted_relname(oid);
    const char	   *nspname = get_quoted_nspname(oid);
    Oid 			argtypes[1] = { OIDOID };
    bool	 		nulls[1] = { 0 };
    Datum	 		values[1];
    SPITupleTable  *tuptable;
    TupleDesc		desc;
    HeapTuple		tuple;
    uint32			records;
    uint32			i;

    Oid				reltoastrelid1;
    Oid				reltoastidxid1;
    Oid				oid2;
    Oid				reltoastrelid2;
    Oid				reltoastidxid2;
    Oid				owner1;
    Oid				owner2;

    /* authority check */
    must_be_superuser("reorg_swap");

    /* connect to SPI manager */
    reorg_init();

    /* swap relfilenode and dependencies for tables. */
    values[0] = ObjectIdGetDatum(oid);
    execute_with_args(SPI_OK_SELECT,
                      "SELECT X.reltoastrelid, TX.reltoastidxid, X.relowner,"
                      "       Y.oid, Y.reltoastrelid, TY.reltoastidxid, Y.relowner"
                      "  FROM pg_catalog.pg_class X LEFT JOIN pg_catalog.pg_class TX"
                      "         ON X.reltoastrelid = TX.oid,"
                      "       pg_catalog.pg_class Y LEFT JOIN pg_catalog.pg_class TY"
                      "         ON Y.reltoastrelid = TY.oid"
                      " WHERE X.oid = $1"
                      "   AND Y.oid = ('reorg.table_' || X.oid)::regclass",
                      1, argtypes, values, nulls);

    tuptable = SPI_tuptable;
    desc = tuptable->tupdesc;
    records = SPI_processed;

    if (records == 0)
        elog(ERROR, "reorg_swap : no swap target");

    tuple = tuptable->vals[0];

    reltoastrelid1 = getoid(tuple, desc, 1);
    reltoastidxid1 = getoid(tuple, desc, 2);
    owner1 = getoid(tuple, desc, 3);
    oid2 = getoid(tuple, desc, 4);
    reltoastrelid2 = getoid(tuple, desc, 5);
    reltoastidxid2 = getoid(tuple, desc, 6);
    owner2 = getoid(tuple, desc, 7);

    /* change owner of new relation to original owner */
    if (owner1 != owner2)
    {
        ATExecChangeOwner(oid2, owner1, true, AccessExclusiveLock);
        CommandCounterIncrement();
    }

    /* swap tables. */
    swap_heap_or_index_files(oid, oid2);
    CommandCounterIncrement();

    /* swap indexes. */
    values[0] = ObjectIdGetDatum(oid);
    execute_with_args(SPI_OK_SELECT,
                      "SELECT X.oid, Y.oid"
                      "  FROM pg_catalog.pg_index I,"
                      "       pg_catalog.pg_class X,"
                      "       pg_catalog.pg_class Y"
                      " WHERE I.indrelid = $1"
                      "   AND I.indexrelid = X.oid"
                      "   AND I.indisvalid"
                      "   AND Y.oid = ('reorg.index_' || X.oid)::regclass",
                      1, argtypes, values, nulls);

    tuptable = SPI_tuptable;
    desc = tuptable->tupdesc;
    records = SPI_processed;

    for (i = 0; i < records; i++)
    {
        Oid		idx1, idx2;

        tuple = tuptable->vals[i];
        idx1 = getoid(tuple, desc, 1);
        idx2 = getoid(tuple, desc, 2);
        swap_heap_or_index_files(idx1, idx2);

        CommandCounterIncrement();
    }

    /* swap names for toast tables and toast indexes */
    if (reltoastrelid1 == InvalidOid)
    {
        if (reltoastidxid1 != InvalidOid ||
                reltoastrelid2 != InvalidOid ||
                reltoastidxid2 != InvalidOid)
            elog(ERROR, "reorg_swap : unexpected toast relations (T1=%u, I1=%u, T2=%u, I2=%u",
                 reltoastrelid1, reltoastidxid1, reltoastrelid2, reltoastidxid2);
        /* do nothing */
    }
    else if (reltoastrelid2 == InvalidOid)
    {
        char	name[NAMEDATALEN];

        if (reltoastidxid1 == InvalidOid ||
                reltoastidxid2 != InvalidOid)
            elog(ERROR, "reorg_swap : unexpected toast relations (T1=%u, I1=%u, T2=%u, I2=%u",
                 reltoastrelid1, reltoastidxid1, reltoastrelid2, reltoastidxid2);

        /* rename X to Y */
        snprintf(name, NAMEDATALEN, "pg_toast_%u", oid2);
        RENAME_REL(reltoastrelid1, name);
        snprintf(name, NAMEDATALEN, "pg_toast_%u_index", oid2);
        RENAME_REL(reltoastidxid1, name);
        CommandCounterIncrement();
    }
    else if (reltoastrelid1 != InvalidOid)
    {
        char	name[NAMEDATALEN];
        int		pid = getpid();

        /* rename X to TEMP */
        snprintf(name, NAMEDATALEN, "pg_toast_pid%d", pid);
        RENAME_REL(reltoastrelid1, name);
        snprintf(name, NAMEDATALEN, "pg_toast_pid%d_index", pid);
        RENAME_REL(reltoastidxid1, name);
        CommandCounterIncrement();

        /* rename Y to X */
        snprintf(name, NAMEDATALEN, "pg_toast_%u", oid);
        RENAME_REL(reltoastrelid2, name);
        snprintf(name, NAMEDATALEN, "pg_toast_%u_index", oid);
        RENAME_REL(reltoastidxid2, name);
        CommandCounterIncrement();

        /* rename TEMP to Y */
        snprintf(name, NAMEDATALEN, "pg_toast_%u", oid2);
        RENAME_REL(reltoastrelid1, name);
        snprintf(name, NAMEDATALEN, "pg_toast_%u_index", oid2);
        RENAME_REL(reltoastidxid1, name);
        CommandCounterIncrement();
    }

    /* drop reorg trigger */
    execute_with_format(
        SPI_OK_UTILITY,
        "DROP TRIGGER IF EXISTS z_reorg_trigger ON %s.%s CASCADE",
        nspname, relname);

    SPI_finish();

    PG_RETURN_VOID();
}
Esempio n. 4
0
/**
 * @fn      Datum repack_drop(PG_FUNCTION_ARGS)
 * @brief   Delete temporarily objects.
 *
 * repack_drop(oid, relname)
 *
 * @param	oid		Oid of target table.
 * @retval			None.
 */
Datum
repack_drop(PG_FUNCTION_ARGS)
{
	Oid			oid = PG_GETARG_OID(0);
	int			numobj = PG_GETARG_INT32(1);
	const char *relname = get_quoted_relname(oid);
	const char *nspname = get_quoted_nspname(oid);

	if (!(relname && nspname))
	{
		elog(ERROR, "table name not found for OID %u", oid);
		PG_RETURN_VOID();
	}

	/* authority check */
	must_be_superuser("repack_drop");

	/* connect to SPI manager */
	repack_init();

	/*
	 * To prevent concurrent lockers of the repack target table from causing
	 * deadlocks, take an exclusive lock on it. Consider that the following
	 * commands take exclusive lock on tables log_xxx and the target table
	 * itself when deleting the repack_trigger on it, while concurrent
	 * updaters require row exclusive lock on the target table and in
	 * addition, on the log_xxx table, because of the trigger.
	 *
	 * Consider how a deadlock could occur - if the DROP TABLE repack.log_%u
	 * gets a lock on log_%u table before a concurrent updater could get it
	 * but after the updater has obtained a lock on the target table, the
	 * subsequent DROP TRIGGER ... ON target-table would report a deadlock as
	 * it finds itself waiting for a lock on target-table held by the updater,
	 * which in turn, is waiting for lock on log_%u table.
	 *
	 * Fixes deadlock mentioned in the Github issue #55.
	 *
	 * Skip the lock if we are not going to do anything.
	 * Otherwise, if repack gets accidentally run twice for the same table
	 * at the same time, the second repack, in order to perform
	 * a pointless cleanup, has to wait until the first one completes.
	 * This adds an ACCESS EXCLUSIVE lock request into the queue
	 * making the table effectively inaccessible for any other backend.
	 */
	if (numobj > 0)
	{
		execute_with_format(
			SPI_OK_UTILITY,
			"LOCK TABLE %s.%s IN ACCESS EXCLUSIVE MODE",
			nspname, relname);
	}

	/* drop log table: must be done before dropping the pk type,
	 * since the log table is dependent on the pk type. (That's
	 * why we check numobj > 1 here.)
	 */
	if (numobj > 1)
	{
		execute_with_format(
			SPI_OK_UTILITY,
			"DROP TABLE IF EXISTS repack.log_%u CASCADE",
			oid);
		--numobj;
	}

	/* drop type for pk type */
	if (numobj > 0)
	{
		execute_with_format(
			SPI_OK_UTILITY,
			"DROP TYPE IF EXISTS repack.pk_%u",
			oid);
		--numobj;
	}

	/*
	 * drop repack trigger: We have already dropped the trigger in normal
	 * cases, but it can be left on error.
	 */
	if (numobj > 0)
	{
		execute_with_format(
			SPI_OK_UTILITY,
			"DROP TRIGGER IF EXISTS repack_trigger ON %s.%s CASCADE",
			nspname, relname);
		--numobj;
	}

	/* drop temp table */
	if (numobj > 0)
	{
		execute_with_format(
			SPI_OK_UTILITY,
			"DROP TABLE IF EXISTS repack.table_%u CASCADE",
			oid);
		--numobj;
	}

	SPI_finish();

	PG_RETURN_VOID();
}
Esempio n. 5
0
static void
parse_indexdef(IndexDef *stmt, Oid index, Oid table)
{
	char *sql = pg_get_indexdef_string(index);
	const char *idxname = get_quoted_relname(index);
	const char *tblname = get_relation_name(table);
	const char *limit = strchr(sql, '\0');

	/* CREATE [UNIQUE] INDEX */
	stmt->create = sql;
	sql = skip_const(index, sql, "CREATE INDEX", "CREATE UNIQUE INDEX");
	/* index */
	stmt->index = sql;
	sql = skip_const(index, sql, idxname, NULL);
	/* ON */
	sql = skip_const(index, sql, "ON", NULL);
	/* table */
	stmt->table = sql;
	sql = skip_const(index, sql, tblname, NULL);
	/* USING */
	sql = skip_const(index, sql, "USING", NULL);
	/* type */
	stmt->type = sql;
	sql = skip_ident(index, sql);
	/* (columns) */
	if ((sql = strchr(sql, '(')) == NULL)
		parse_error(index);
	sql++;
	stmt->columns = sql;
	if ((sql = skip_until(index, sql, ')')) == NULL)
		parse_error(index);

	/* options */
	stmt->options = sql;
	stmt->tablespace = NULL;
	stmt->where = NULL;

	/* Is there a tablespace? Note that apparently there is never, but
	 * if there was one it would appear here. */
	if (sql < limit && strstr(sql, "TABLESPACE"))
	{
		sql = skip_until_const(index, sql, "TABLESPACE");
		stmt->tablespace = sql;
		sql = skip_ident(index, sql);
	}

	/* Note: assuming WHERE is the only clause allowed after TABLESPACE */
	if (sql < limit && strstr(sql, "WHERE"))
	{
		sql = skip_until_const(index, sql, "WHERE");
		stmt->where = sql;
	}

	elog(DEBUG2, "indexdef.create  = %s", stmt->create);
	elog(DEBUG2, "indexdef.index   = %s", stmt->index);
	elog(DEBUG2, "indexdef.table   = %s", stmt->table);
	elog(DEBUG2, "indexdef.type    = %s", stmt->type);
	elog(DEBUG2, "indexdef.columns = %s", stmt->columns);
	elog(DEBUG2, "indexdef.options = %s", stmt->options);
	elog(DEBUG2, "indexdef.tspace  = %s", stmt->tablespace);
	elog(DEBUG2, "indexdef.where   = %s", stmt->where);
}