PostgreSQL C扩展函数:表作为参数和结果。

huangapple go评论58阅读模式
英文:

PostgreSQL C Extension function: Table as argument and as Result

问题

我需要编写一个针对PostgreSQL的扩展函数,具体要求如下:

  1. 参数为一个包含3列double类型的表格
  2. 结果为一个包含3列double类型的表格

请注意,函数的模式理想情况下应该是材料化模式(materialize mode),因为函数将通过转换整个输入表格的数据生成一个新表格。

我找到了关于将结果作为集合(多行)的相关信息。但是,测试函数时,结果并不是按列显示的,我只得到一个包含(val,val,val)的记录。

请问是否有人能够为我提供相关信息,最好是一个具有这种基本类型的简单示例函数?

谢谢!在PostgreSQL文档中,我尝试查找相关信息,但是并不清晰,特别是在涉及表格作为输入参数和以材料化模式返回时。

英文:

I need to write an extension function in C for PostgreSQL that:

  1. Has as argument a table of 3 columns of type double
  2. Has as result a table of 3 columns of type double

Note that the mode should ideally be in materialize mode, since the function will generate a new table by transforming the data on the entire input table.

I did find information related to having the result as sets (multiple rows). But the test of the function does not result into the values as columns - I just get a record with (val,val,val).

Can anyone please direct me to information or preferably a simple example function that has this basic typology?

Thank you!

I tried to find information in the PostgreSQL documentation but it is not clear, specially when talking about tables as input arguments and returns in materialize mode.

答案1

得分: 1

以下是您要求的翻译部分:

根据LaurenzAlbe的建议,我创建了类似以下的东西。

请注意,我没有确切找到如何将表作为函数的输入接收的方法。这里的想法是将其封装成一个SQL函数,以便它可以创建一个临时表,填充这个临时表,然后将这个临时表的名称传递给C函数。

对于我的应用程序来说,这已经足够了。但如果有其他人愿意帮助,我很乐意讨论和改进。

谢谢!

接下来,要将此扩展链接到您的PostgreSQL中,您可以使用以下代码:

删除函数(如果存在):

DROP FUNCTION IF EXISTS public.add_table(oid);

创建或替换函数:

CREATE OR REPLACE FUNCTION public.add_table(
    oid,
    OUT x real,
    OUT y real,
    OUT z real)
    RETURNS SETOF record 
    LANGUAGE 'c'
    COST 1
    IMMUTABLE STRICT PARALLEL UNSAFE
    ROWS 1000
AS '...路径.../<dll名称>.dll', 'add_table'
;

ALTER FUNCTION public.add_table(oid)
    OWNER TO postgres;

要调用该函数:

-- 表包含3个实数列
SELECT * FROM add_table('test_table'::regclass);

这将返回一个所有值都增加了的表。

英文:

Following the suggestion from LaurenzAlbe, I created something like the following.

Note that I did not exactly find how to receive a table as input for a function. The idea here would be to wrap this into a SQL function so that it would create a temp table, fill this temp table, and then pass the name of this temp table to the C function.

For my application, this is enough. But I would love to debate this and improve if anyone else would be willing to help.

Thanks,


#include &quot;postgres.h&quot;
#include &lt;string.h&gt;
#include &quot;fmgr.h&quot;
#include &quot;utils/geo_decls.h&quot;
#include &quot;funcapi.h&quot;
#include &quot;executor/executor.h&quot;
#include &quot;utils/array.h&quot;
#include &quot;utils/builtins.h&quot;
#include &quot;utils/tuplestore.h&quot;
#include &quot;catalog/pg_type.h&quot;
#include &quot;executor/spi.h&quot;
#include &quot;access/heapam.h&quot;
#include &quot;access/htup_details.h&quot;
#include &quot;miscadmin.h&quot;

#ifdef PG_MODULE_MAGIC
PG_MODULE_MAGIC;
#endif

PGDLLEXPORT Datum add_table(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1(add_table);

Datum add_table(PG_FUNCTION_ARGS) {

	ReturnSetInfo* rsinfo = (ReturnSetInfo*)fcinfo-&gt;resultinfo;

	/* check to see if caller supports us returning a tuplestore */
	if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
		ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg(&quot;set-valued function called in context that cannot accept a set&quot;)));
	if (!(rsinfo-&gt;allowedModes &amp; SFRM_Materialize))
		ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg(&quot;materialize mode required, but it is not allowed in this context&quot;)));


	/* The name of the table must be given as string &#39;tablename&#39;::regclass */
	char* inputTableOid = PG_GETARG_OID(0);

	/* Executes the select statement to get input table */
	StringInfoData commandBuffer;
	initStringInfo(&amp;commandBuffer);
	appendStringInfo(&amp;commandBuffer, &quot;SELECT * FROM %s&quot;, get_rel_name(inputTableOid));

	/* Starts SPI */
	int connRes = SPI_connect();
	if (connRes != SPI_OK_CONNECT) {
		elog(ERROR, &quot;SPI_connect failed: %d&quot;, connRes);
	}
	int selectRes = SPI_exec(commandBuffer.data, 0);
	if (selectRes != SPI_OK_SELECT) {
		elog(ERROR, &quot;SELECT failed: %d&quot;, selectRes);
	}

	/* Saves the select results */
	SPITupleTable* inputTupTable = SPI_tuptable; // The pointer to the input table - copies the global var that is filled by SPI_exec
	int inputNRows = SPI_processed; // The actual number of returned rows - copies the global var that is filled by SPI_exec
	TupleDesc inputTupleDesc = inputTupTable-&gt;tupdesc; // The TupleDesc of the input table
	int natts = inputTupleDesc-&gt;natts; // The number of attributes in the input tuple (cols)

	/* The tupdesc and tuplestore must be created in ecxt_per_query_memory */
	MemoryContext oldcontext = MemoryContextSwitchTo(rsinfo-&gt;econtext-&gt;ecxt_per_query_memory);

	/* Makes the output TupleDesc */
	TupleDesc tupdesc = CreateTemplateTupleDesc(3);
	TupleDescInitEntry(tupdesc, (AttrNumber)1, &quot;x&quot;, FLOAT4OID, -1, 0);
	TupleDescInitEntry(tupdesc, (AttrNumber)2, &quot;y&quot;, FLOAT4OID, -1, 0);
	TupleDescInitEntry(tupdesc, (AttrNumber)3, &quot;z&quot;, FLOAT4OID, -1, 0);
	tupdesc = BlessTupleDesc(tupdesc);

	/* Checks if random access is allowed */
	bool randomAccess = (rsinfo-&gt;allowedModes &amp; SFRM_Materialize_Random) != 0;
	/* Starts the tuplestore */
	Tuplestorestate* tupstore = tuplestore_begin_heap(randomAccess, false, work_mem);

	/* Set the output */
	rsinfo-&gt;returnMode = SFRM_Materialize;
	rsinfo-&gt;setResult = tupstore;
	rsinfo-&gt;setDesc = tupdesc;

	/* Returns to the old context */
	MemoryContextSwitchTo(oldcontext);


	/* Fills the output table */
	for (size_t i = 0; i &lt; inputNRows; i++) {
		/* Getting the values of the current row */
		HeapTuple inputTuple = inputTupTable-&gt;vals[i]; // Gets the current input tuple (row)

		Datum* inputValues = palloc(natts * sizeof(Datum));
		bool* inputNulls = palloc(natts * sizeof(bool));
		heap_deform_tuple(inputTuple, inputTupleDesc, inputValues, inputNulls); // Gets the value of the current row

		/* Adds 1 to each column of the input row */
		Datum* outputValues = palloc(natts * sizeof(Datum));
		for (size_t j = 0; j &lt; natts; j++) {
			outputValues[j] = Float4GetDatum(DatumGetFloat4(inputValues[j]) + 1);
		}

		/* Builds the output tuple (row) */
		HeapTuple outputTuple = heap_form_tuple(tupdesc, outputValues, inputNulls);
		/* Puts in the output tuplestore */
		tuplestore_puttuple(tupstore, outputTuple);
	}

	return (Datum)0;

}

Then, to add link this extension into your postgresql, you use the following code:

-- FUNCTION: public.add_table(oid)

DROP FUNCTION IF EXISTS public.add_table(oid);

CREATE OR REPLACE FUNCTION public.add_table(
	oid,
	OUT x real,
	OUT y real,
	OUT z real)
    RETURNS SETOF record 
    LANGUAGE &#39;c&#39;
    COST 1
    IMMUTABLE STRICT PARALLEL UNSAFE
    ROWS 1000

AS &#39;...Path.../&lt;dllName&gt;.dll&#39;, &#39;add_table&#39;
;

ALTER FUNCTION public.add_table(oid)
    OWNER TO postgres;

To call the function:

-- The table contains 3 real columns
select * from add_table(&#39;test_table&#39;::regclass);

This will return the table where all values are incremented.

huangapple
  • 本文由 发表于 2023年4月19日 23:18:50
  • 转载请务必保留本文链接:https://go.coder-hub.com/76056209.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定