C Extension woes

Started by Tim Hawesover 17 years ago7 messages
#1Tim Hawes
thawes@novadine.com

Hello all,

I am trying to write an extension in C that returns a simple environment
variable. The code compiles without any complaint or warning, and it
loads fine into the database, however, when I run the function, I get
disconnected from the server.

Here is my C code:

#include <postgres.h>
#include <fmgr.h>
PG_MODULE_MAGIC;

#include <stdio.h>
#include <stdlib.h>

PG_FUNCTION_INFO_V1(pl_masterkey);

text * pl_masterkey(PG_FUNCTION_ARGS)
{
char *e_var = getenv("PGMASTERKEY");
size_t length = VARSIZE(e_var) - VARHDRSZ;

text * mkey = (text *) palloc(length);
VARATT_SIZEP(mkey) = length;
memcpy(VARDATA(mkey), e_var, length);

return mkey;
}

And here is the SQL I use to create the function in PostgreSQL:

CREATE FUNCTION pl_masterkey() RETURNS text
AS 'pl_masterkey', 'pl_masterkey'
LANGUAGE C STRICT;

And the results:

select pl_masterkey();
server closed the connection unexpectedly
This probably means the server terminated abnormally
before or while processing the request.
The connection to the server was lost. Attempting reset: Failed.
!>

Thanks ahead of time for any and all help.

#2Jan Urbański
j.urbanski@students.mimuw.edu.pl
In reply to: Tim Hawes (#1)
Re: C Extension woes

Tim Hawes wrote:

Hello all,

I am trying to write an extension in C that returns a simple environment
variable. The code compiles without any complaint or warning, and it
loads fine into the database, however, when I run the function, I get
disconnected from the server.

Here is my C code:

#include <postgres.h>
#include <fmgr.h>
PG_MODULE_MAGIC;

#include <stdio.h>
#include <stdlib.h>

PG_FUNCTION_INFO_V1(pl_masterkey);

text * pl_masterkey(PG_FUNCTION_ARGS)
{
char *e_var = getenv("PGMASTERKEY");
size_t length = VARSIZE(e_var) - VARHDRSZ;

text * mkey = (text *) palloc(length);
VARATT_SIZEP(mkey) = length;
memcpy(VARDATA(mkey), e_var, length);

return mkey;
}

Oh, you confused a lot of things.
You need something like

Datum pl_masterkey(PG_FUNCTION_ARGS) {
char *e_var = getenv("PGMASTERKEY");
PG_RETURN_TEXT_P(cstring_to_text(e_var));
}

You don't need to mess with anything varlena-related (liek VARSIZE),
it's all handled for you.
Also, read up on how to declare user-defined C functions in Postgres
(they always need to return Datum).

Cheers,
Jan

--
Jan Urbanski
GPG key ID: E583D7D2

ouden estin

#3Andrew Chernow
ac@esilo.com
In reply to: Tim Hawes (#1)
Re: C Extension woes

Tim Hawes wrote:

text * pl_masterkey(PG_FUNCTION_ARGS)
{
char *e_var = getenv("PGMASTERKEY");
size_t length = VARSIZE(e_var) - VARHDRSZ;

The VARSIZE macro is for variable length structures, like a text or
bytea which contains a length and data member. You are using this macro
on a regular C string "e_var". Try this instead:

size_t length = e_var != NULL ? strlen(e_var) : 0;

--
Andrew Chernow
eSilo, LLC
every bit counts
http://www.esilo.com/

#4Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andrew Chernow (#3)
Re: C Extension woes

Andrew Chernow <ac@esilo.com> writes:

Tim Hawes wrote:

char *e_var = getenv("PGMASTERKEY");
size_t length = VARSIZE(e_var) - VARHDRSZ;

The VARSIZE macro is for variable length structures, like a text or
bytea which contains a length and data member. You are using this macro
on a regular C string "e_var".

The code on the output side is wrong too: if the useful data length is
"length", then the palloc has to ask for length + VARHDRSZ, and that's
also what's got to be put into the length word.

regards, tom lane

#5Tim Hawes
thawes@novadine.com
In reply to: Andrew Chernow (#3)
Re: C Extension woes

Thank you for your replies, however, it still is not working, see below...

Andrew Chernow wrote:

Tim Hawes wrote:

text * pl_masterkey(PG_FUNCTION_ARGS)
{
char *e_var = getenv("PGMASTERKEY");
size_t length = VARSIZE(e_var) - VARHDRSZ;

The VARSIZE macro is for variable length structures, like a text or
bytea which contains a length and data member. You are using this
macro on a regular C string "e_var". Try this instead:

size_t length = e_var != NULL ? strlen(e_var) : 0;

@Jan:
It appears the cstring_to_text function is unique to the latest
PostgreSQL code. I do not have a def for that for PostgreSQL 8.2, and
currently I am stuck working with that version. I changed the return
value to Datum, and I had previously only copied from the examples at:
http://www.postgresql.org/docs/8.2/interactive/xfunc-c.html

@Andrew:
here is my new code:
Datum pl_masterkey(PG_FUNCTION_ARGS)
{
char *e_var = getenv("PGMASTERKEY");
size_t length = e_var != NULL ? strlen(e_var) : 0;

text * mkey = (text *) palloc(length);
VARATT_SIZEP(mkey) = length;
memcpy(VARDATA(mkey), e_var, length);

PG_RETURN_TEXT_P(mkey);
}
now gets:
select pl_masterkey();
ERROR: invalid memory alloc request size 4294967293

#6Jan Urbański
j.urbanski@students.mimuw.edu.pl
In reply to: Tim Hawes (#5)
Re: C Extension woes

Tim Hawes wrote:

@Jan:
It appears the cstring_to_text function is unique to the latest
PostgreSQL code. I do not have a def for that for PostgreSQL 8.2, and

Oh, I'm sorry, I forgot about that. cstring_to_text has been added only
recently (it's not even it 8.3, silly me).

Datum pl_masterkey(PG_FUNCTION_ARGS)
{
char *e_var = getenv("PGMASTERKEY");
size_t length = e_var != NULL ? strlen(e_var) : 0;

text * mkey = (text *) palloc(length);
VARATT_SIZEP(mkey) = length;
memcpy(VARDATA(mkey), e_var, length);

PG_RETURN_TEXT_P(mkey);
}

You forgot to palloc space for the varlena header. You need an extra
VARHDRSZ bytes for the header, in addition to what is needed for your data.
Try something like this:

Datum
pl_masterkey(PG_FUNCTION_ARGS)
{
char *e_var;
size_t length;
text *mkey;

e_var = getenv("PGMASTERKEY");
length = e_var ? strlen(e_var) : 0;
mkey = (text *) palloc(VARHDRSZ + length);

VARATT_SIZEP(mkey) = VARHDRSZ + length;
memcpy(VARDATA(mkey), e_var, length);

PG_RETURN_TEXT_P(mkey);
}

--
Jan Urbanski
GPG key ID: E583D7D2

ouden estin

#7Tim Hawes
thawes@novadine.com
In reply to: Jan Urbański (#6)
Re: C Extension woes

Ok, that worked!
Thank you very much, Jan and others who gave their input.
I did see Tom's input for the VARHDRSZ and tried that, but forgot to add
that again
when I called VARATT_SIZEP

Jan Urbański wrote:

Show quoted text

Tim Hawes wrote:

@Jan:
It appears the cstring_to_text function is unique to the latest
PostgreSQL code. I do not have a def for that for PostgreSQL 8.2, and

Oh, I'm sorry, I forgot about that. cstring_to_text has been added
only recently (it's not even it 8.3, silly me).

Datum pl_masterkey(PG_FUNCTION_ARGS)
{
char *e_var = getenv("PGMASTERKEY");
size_t length = e_var != NULL ? strlen(e_var) : 0;

text * mkey = (text *) palloc(length);
VARATT_SIZEP(mkey) = length;
memcpy(VARDATA(mkey), e_var, length);

PG_RETURN_TEXT_P(mkey);
}

You forgot to palloc space for the varlena header. You need an extra
VARHDRSZ bytes for the header, in addition to what is needed for your
data.
Try something like this:

Datum
pl_masterkey(PG_FUNCTION_ARGS)
{
char *e_var;
size_t length;
text *mkey;

e_var = getenv("PGMASTERKEY");
length = e_var ? strlen(e_var) : 0;
mkey = (text *) palloc(VARHDRSZ + length);

VARATT_SIZEP(mkey) = VARHDRSZ + length;
memcpy(VARDATA(mkey), e_var, length);

PG_RETURN_TEXT_P(mkey);
}