Yessica Brinkmann escribió:
> Buenos días,
> Quisiera hacer una consulta al grupo por favor.
> Alguien ha usado SPI_connect() para conectarse a Postgresql desde un
> programa?
> Saben por si acaso en qué casos da error?

Hmm, si estás invocando tu función desde otra función que ya hace
SPI_connect, entonces tienes que hacer SPI_push antes de invocar el
SPI_connect, y un SPI_pop después del SPI_finish.  Así creas un nuevo
"contexto" para que SPI pueda procesar limpiamente tu consulta.  Me
parece que el ejemplo claro es que get_distinct está llamando a
get_columnnames sin un SPI_push.

Consejo aparte: no necesitas el snprintf con strlen etc; eso es lo que
appendStringInfo() puede hacer por ti en forma más sencilla.

(Para mostrar código fuente recomiendo ponerlo en un archivo adjunto en
vez de copiar/pegar al mail, porque el programa de mail lo va a
reformatear y queda ilegible.  Para poder leer tu código tuve que
pasarlo por pgindent ... va de vuelta como archivo adjunto.)

Saludos

-- 
Álvaro Herrera                https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
/**
 * save_advice
 * for every candidate insert an entry into IND_ADV_TABL
 */
static void
save_advice(List *candidates)
{
	StringInfoData query;		/* string for Query */
	StringInfoData cols;		/* string for Columns */
	Oid			advise_oid;
	ListCell   *cell;

	elog(DEBUG3, "IND ADV: save_advice: ENTER");

	Assert(list_length(candidates) != 0);

	/*
	 * Minimal check: check that IND_ADV_TABL is at least visible to us. There
	 * are a lot more checks we should do in order to not let the INSERT fail,
	 * like permissions, datatype mis-match, etc., but we leave those checks
	 * upto the executor.
	 */

	/* find a relation named IND_ADV_TABL on the search path */
	advise_oid = RelnameGetRelid(IND_ADV_TABL);

	if (advise_oid != InvalidOid)
	{
#if 1
		Relation	advise_rel = relation_open(advise_oid, AccessShareLock);

		if (advise_rel->rd_rel->relkind != RELKIND_RELATION
			&& advise_rel->rd_rel->relkind != RELKIND_VIEW)
		{
			relation_close(advise_rel, AccessShareLock);

			/* FIXME: add errdetail() and/or errcontext() calls here. */
			ereport(ERROR,
					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
					 errmsg(IND_ADV_ERROR_NTV)));
		}

		relation_close(advise_rel, AccessShareLock);
#else
		/*
		 * heap_open() makes sure that the oid does not represent an INDEX or
		 * a COMPOSITE type, else it will raise an ERROR, which is exactly
		 * what we want. The comments above heap_open() ask the caller not to
		 * assume any storage since the returned relation may be a VIEW; but
		 * we don't mind, since the user may have defined some rules on it to
		 * make the INSERTs work smoothly! If not, we leave it upto the
		 * executor to raise ERROR.
		 */
		PG_TRY();
		{
			heap_close(heap_open(advise_oid, AccessShareLock), AccessShareLock);
		}
		PG_CATCH();
		{
			errmsg(IND_ADV_ERROR_NTV);
			PG_RE_THROW();
		}
		PG_END_TRY();
#endif
	}
	else
	{
		/* FIXME: add errdetail() and/or errcontext() calls here. */
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_TABLE),
				 errmsg(IND_ADV_ERROR_NE)));
	}

	initStringInfo(&query);
	initStringInfo(&cols);

	foreach(cell, candidates)
	{
		int			i;
		IndexCandidate *idxcd = (IndexCandidate *) lfirst(cell);

		if (!idxcd->idxused)
			continue;

		/*
		 * pfree() the memory allocated for the previous candidate. FIXME:
		 * Avoid meddling with the internals of a StringInfo, and try to use
		 * an API.
		 */
		if (cols.len > 0)
		{
			pfree(cols.data);
			cols.data = NULL;
		}

		initStringInfo(&cols);

		for (i = 0; i < idxcd->ncols; ++i)
			appendStringInfo(&cols, "%s%d", (i > 0 ? "," : ""), idxcd->varattno[i]);

		/* FIXME: Mention the column names explicitly after the table name. */
		appendStringInfo(&query, "insert into \"" IND_ADV_TABL "\" values"
						 "( %d, array[%s], %f, %d, %d, now());",
						 idxcd->reloid,
						 cols.data,
						 idxcd->benefit,
						 idxcd->pages * BLCKSZ / 1024,	/* in KBs */
						 MyProcPid);
	}							/* foreach cell in candidates */

	if (query.len > 0)			/* if we generated any SQL */
	{
		if (SPI_connect() == SPI_OK_CONNECT)
		{
			if (SPI_execute(query.data, false, 0) != SPI_OK_INSERT)
				elog(WARNING, "IND ADV: SPI_execute failed while saving advice.");

			if (SPI_finish() != SPI_OK_FINISH)
				elog(WARNING, "IND ADV: SPI_finish failed while saving advice.");
		}
		else
			elog(WARNING, "IND ADV: SPI_connect failed while saving advice.");
	}

	/* TODO: Propose to -hackers to introduce API to free a StringInfoData . */
	if (query.len > 0)
		pfree(query.data);

	if (cols.len > 0)
		pfree(cols.data);

	elog(DEBUG3, "IND ADV: save_advice: EXIT");
}


/**
 * get_distinct
 * for every candidate get an entry into IndexCandidate
 */
static void
get_ndistinct(List *candidates)
{
	int			proc;
	StringInfoData query;		/* string for Query */
	StringInfoData cols;		/* string for Columns */
	Oid			advise_oid;
	ListCell   *cell;

	SPITupleTable *SPI_tuptable;

	TupleDesc	tupdesc;


	elog(DEBUG3, "IND ADV: get_distinct: ENTER");

	Assert(list_length(candidates) != 0);

	/*
	 * Minimal check: check that IND_ADV_TABL is at least visible to us. There
	 * are a lot more checks we should do in order to not let the INSERT fail,
	 * like permissions, datatype mis-match, etc., but we leave those checks
	 * upto the executor.
	 */

	/* find a relation named IND_ADV_TABL on the search path */
	advise_oid = RelnameGetRelid(IND_ADV_TABL);

	if (advise_oid != InvalidOid)
	{
#if 1
		Relation	advise_rel = relation_open(advise_oid, AccessShareLock);

		if (advise_rel->rd_rel->relkind != RELKIND_RELATION
			&& advise_rel->rd_rel->relkind != RELKIND_VIEW)
		{
			relation_close(advise_rel, AccessShareLock);

			/* FIXME: add errdetail() and/or errcontext() calls here. */
			ereport(ERROR,
					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
					 errmsg(IND_ADV_ERROR_NTV)));
		}

		relation_close(advise_rel, AccessShareLock);
#else
		/*
		 * heap_open() makes sure that the oid does not represent an INDEX or
		 * a COMPOSITE type, else it will raise an ERROR, which is exactly
		 * what we want. The comments above heap_open() ask the caller not to
		 * assume any storage since the returned relation may be a VIEW; but
		 * we don't mind, since the user may have defined some rules on it to
		 * make the INSERTs work smoothly! If not, we leave it upto the
		 * executor to raise ERROR.
		 */
		PG_TRY();
		{
			heap_close(heap_open(advise_oid, AccessShareLock), AccessShareLock);
		}
		PG_CATCH();
		{
			errmsg(IND_ADV_ERROR_NTV);
			PG_RE_THROW();
		}
		PG_END_TRY();
#endif
	}
	else
	{
		/* FIXME: add errdetail() and/or errcontext() calls here. */
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_TABLE),
				 errmsg(IND_ADV_ERROR_NE)));
	}

	initStringInfo(&query);
	initStringInfo(&cols);

	foreach(cell, candidates)	/* foreach cell in candidates */
	{
		int			i;
		IndexCandidate *idxcd = (IndexCandidate *) lfirst(cell);

		get_columnnames(idxcd);

		if (!idxcd->idxused)
			continue;

		/*
		 * pfree() the memory allocated for the previous candidate. FIXME:
		 * Avoid meddling with the internals of a StringInfo, and try to use
		 * an API.
		 */
		if (cols.len > 0)
		{
			pfree(cols.data);
			cols.data = NULL;
		}


		appendStringInfo(&query, "select n_distintic from pg_stats where ");

		for (i = 0; i < idxcd->ncols; ++i)
		{
			appendStringInfo(&cols, "attname=%s%d", (i > 0 ? "OR" : ""),
							 idxcd->varattnombres[i]);
		}						/* foreach col in varattno */


		/* FIXME: Mention the column names explicitly after the table name. */
		appendStringInfo(&query, "%s;", cols.data);

		elog(INFO, "QUERY:%s", query.data);
		elog(INFO, "LONGITUD:%d", query.len);


		if (query.len > 0)		/* if we generated any SQL */
		{
			if (SPI_connect() == SPI_OK_CONNECT)
			{
				if (SPI_execute(query.data, true, 0) != SPI_OK_SELECT)
					elog(WARNING, "IND ADV: SPI_execute failed while select.");
				else			/* SPI_OK_SELECT */
				{

					proc = SPI_processed;
					tupdesc = SPI_tuptable->tupdesc;
					SPITupleTable *tuptable = SPI_tuptable;
					char		buf[8192];
					int			i,
								j;

					for (j = 0; j < proc; j++)
					{
						/* cada fila */
						HeapTuple	tuple = tuptable->vals[j];

						for (i = 0, buf[0] = 0; i < tupdesc->natts; i++)
						{
							/* cada columna de cada fila */
							char	   *data;

							data = SPI_getvalue(tuple, tupdesc, i);

							/*
							 * falta imprimir data y guardar el dato
							 * correspondiente en la estructura del índice
							 */
							snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "%s %s", data, (i == tupdesc->natts) ? "" : "|");
						}

						elog(INFO, "EXECQ:%s", buf);
						strcpy(idxcd->ndistinct, buf);
					}
				}

				if (SPI_finish() != SPI_OK_FINISH)
					elog(WARNING, "IND ADV: SPI_finish failed while select.");
			}
			else				/* SPI_connect() != SPI_OK_CONNECT */
				elog(WARNING, "IND ADV: SPI_connect failed while select.");
		}

		if (query.len > 0)
			pfree(query.data);
	}							/* foreach cell in candidates */


	elog(DEBUG3, "IND ADV: select: EXIT");
}


static void
get_columnnames(IndexCandidate * idx)
{
	int			proc;
	StringInfoData query;		/* string for Query */
	StringInfoData cols;		/* string for Columns */
	Oid			advise_oid;
	ListCell   *cell;

	SPITupleTable *SPI_tuptable;

	TupleDesc	tupdesc;


	elog(DEBUG3, "IND ADV: get_column_names: ENTER");

	initStringInfo(&query);
	initStringInfo(&cols);

	int			i;
	IndexCandidate *idxcd = idx;

	/*
	 * pfree() the memory allocated for the previous candidate. FIXME: Avoid
	 * meddling with the internals of a StringInfo, and try to use an API.
	 */
	if (cols.len > 0)
	{
		pfree(cols.data);
		cols.data = NULL;
	}							/* IF col.len>0 */


	appendStringInfo(&query, "select a.attname from pg_class c,pg_attribute a where c.oid=%d AND a.attrelid = c.oid AND ", idxcd->reloid);

	for (i = 0; i < idxcd->ncols; ++i)
	{

		appendStringInfo(&cols, "a.attnum=%s%d", (i > 0 ? "AND" : ""),
						 idxcd->varattno[i]);


	}							/* foreach col in varattno */


	/* FIXME: Mention the column names explicitly after the table name. */
	appendStringInfo(&query, "%s;", cols.data);

	elog(INFO, "QUERY:%s", query.data);
	elog(INFO, "LONGITUD:%d", query.len);

	if (query.len > 0)			/* if we generated any SQL */
	{
		if (SPI_connect() == SPI_OK_CONNECT)
		{
			if (SPI_execute(query.data, true, 0) != SPI_OK_SELECT)
				elog(WARNING, "IND ADV: SPI_execute failed while select.");
			else				/* ok */
			{
				proc = SPI_processed;
				tupdesc = SPI_tuptable->tupdesc;
				SPITupleTable *tuptable = SPI_tuptable;
				char		buf[8192];
				int			i,
							j;

				for (j = 0; j < proc; j++)
				{
					/* cada fila */
					HeapTuple	tuple = tuptable->vals[j];

					for (i = 0, buf[0] = 0; i < tupdesc->natts; i++)
					{
						/* cada columna de cada fila */
						char	   *data;

						data = SPI_getvalue(tuple, tupdesc, i);
						idxcd->varattnombres[i] = data;

					}			/* (i=0,buf[0]=0;i<tupdesc->natts;i++) */
				}				/* (j=0;j<proc;j++) */
			}					/* else ok */

			if (SPI_finish() != SPI_OK_FINISH)
				elog(WARNING, "IND ADV: SPI_finish failed while select.");

		}						/* if( SPI_connect() == SPI_OK_CONNECT ) */
		else
			elog(WARNING, "IND ADV: SPI_connect failed while select.");
	}							/* if( query.len > 0 ) */

	/* TODO: Propose to -hackers to introduce API to free a StringInfoData . */
	if (query.len > 0)
		pfree(query.data);

	elog(DEBUG3, "IND ADV: select: EXIT");
}

Reply via email to