[This message refers to my previous post '[GENERAL] return value of a version-1
C function']
Ooops, I didn't read the docs very carefully! I solved the problem, by
declaring cod as a proper structure and then returning it with
PG_RETURN_BPCHAR_P(cod):
BpChar *cod = (BpChar *)SPI_palloc(VARHDRSZ + 8);
char c[9];
...
/* c = generated code (8 chars) + '\0' */
...
cod->vl_len = VARHDRSZ + 8;
memmove(VARDATA(cod), c, 8);
PG_RETURN_BPCHAR_P(cod);
}
Now I have three more questions!
1) SPI_connect() and SPI_finish() mark a new memory context; since I must
return cod I need to SPI_palloc() it in the upper Executor context. The
problem is: how can I free the SPI_palloc()'ed memory? I can't SPI_pfree() it
before PG_RETURN_BPCHAR_P(cod), conversely nothing after
PG_RETURN_BPCHAR_P(cod) would be executed. As stated in section 22.3 of the
PostgreSQL Programmer's Guide 'SPI has no ability to automatically free
allocations in the upper Executor context!', hence the question: how do I cope
with this situation?
2) If I use the VARSIZE(__PTR) macro (defined in postgres.h) to set the size of
cod, the compiler will complain about an invalid lvalue. This is not valid:
VARSIZE(cod) = VARHDRSZ + 8;
Can I safely use:
cod->vl_len = VARHDRSZ + 8;
or is this incorrect?
3) The following variable is an identifier (Name):
Name tablename = PG_GETARG_NAME(0);
If I want to retrieve the string contained in the union pointed to by
tablename I should use the macro NameStr(name) defined in c.h:
(void)strlcat(query, NameStr(fieldname), sizeof(query));
but again the compiler will complain with the error "request for member `data'
in something not a structure or union". Name is a pointer to NameData
union:
typedef union nameData
{
chardata[NAMEDATALEN];
int alignmentDummy;
} NameData;
typedef NameData *Name;
NameStr is defined as:
#define NameStr(name) ((name).data)
but name is a pointer, so shouldn't NameStr be defined as:
#define NameStr(name) ((name)->data)
? Is it correct to use: tablename->data?
I attached the C code of the function. I'm not a C/PostgreSQL guru, so ANY
suggestion is welcome. Thanks in advance for your help.
Francesco Casadei
#include
#include
#include
#include
#include "postgres.h"
#include "fmgr.h"
#include "executor/spi.h"
char cod[8];/* codice di otto lettere */
PG_FUNCTION_INFO_V1(genera_codice);
Datum genera_codice(PG_FUNCTION_ARGS) {
int i;
char n; /* numero pseudo-casuale */
char c[9];/* codice di otto lettere + NULL */
char query[1024]; /* testo della query per selezionare il codice */
Name tablename = PG_GETARG_NAME(0);
Name fieldname = PG_GETARG_NAME(1);
elog(DEBUG, "tablename: %s", tablename->data);
elog(DEBUG, "fieldname: %s", fieldname->data);
/* Connette la procedura all'SPI manager */
if (SPI_connect() != SPI_OK_CONNECT)
PG_RETURN_NULL();
do {
/* Genera il codice di otto lettere */
for (i = 0; i < 8; i++) {
srandomdev();
n = (char)(random() % 62);/* Numero pseudo-casuale compreso tra 0 e 61 */
if (n <= 25) {
/* 0 <= n <= 25: lettera tra 'A' e 'Z' (alfabeto inglese) */
cod[i] = n + 'A';
c[i] = cod[i];
} else if (n <= 51) {
/* 26 <= n <= 51: lettera tra 'a' e 'z' (alfabeto inglese) */
cod[i] = (n - 26) + 'a';
c[i] = cod[i];
} else {
/* 52 <= n <= 61: cifra tra '0' e '9' */
cod[i] = (n - 52) + '0';
c[i] = cod[i];
}
}
c[i] = '\0';
elog(DEBUG, "Generated code: %s", c);
/*
* Prepara il testo della query che verifica se nella base di dati
* e' gia' presente il codice appena generato
*/
bzero(query, sizeof(query));
(void)strlcat(query, "SELECT ", sizeof(query));
(void)strlcat(query, fieldname->data, sizeof(query));
(void)strlcat(query, " FROM ", sizeof(query));
(void)strlcat(query, tablename->data, sizeof(query));
(void)strlcat(query, " WHERE ", sizeof(query));
(void)strlcat(query, fieldname->data, sizeof(query));
(void)strlcat(query, " = '", sizeof(query));
(void)strlcat(query, c, sizeof(query));
(void)strlcat(query, "';", sizeof(query));
elog(DEBUG, "Query text: %s", query);
/* Esegue la query per cercare il codice generato */
if (SPI