Thread: C Programming with postgres.h - my function crashes the database backend
I've been trying to extend Postgres and create an enumerated type to represent gender, as a precursor to more complex enumerated types. I've created the C functions for input and output with the following code: - ---- gender.c ----- #include "server/postgres.h" #include <string.h> #include "server/fmgr.h" PG_FUNCTION_INFO_V1(enum_gender_in); Datum enum_gender_in(PG_FUNCTION_ARGS) { text *invalue = PG_GETARG_TEXT_P(0); if ( strcmp ( VARDATA(invalue), "Male" ) ) { /* VARDATA gets the data portion of a "varlena" struct, which istypedef'd to "text" */ PG_RETURN_INT32( 0 ); } PG_RETURN_INT32( 1 ); } PG_FUNCTION_INFO_V1(enum_gender_out); Datum enum_gender_out(PG_FUNCTION_ARGS) { int32 internal = PG_GETARG_INT32(0); text *outvalue; if ( internal == 0 ) { int32 male_struct_size = 5 * sizeof(char) + VARHDRSZ; /* Five characters ('Male\0') plus int32 */ outvalue = (text *) palloc ( male_struct_size ); VARATT_SIZEP(outvalue) = male_struct_size; memcpy(VARDATA(outvalue), "Male\0", 5); } else { int32 female_struct_size = 7 * sizeof(char) + VARHDRSZ; /* Five characters ('Female\0') plus int32 */ outvalue = (text *) palloc ( female_struct_size ); VARATT_SIZEP(outvalue) = female_struct_size; memcpy(VARDATA(outvalue), "Female\0", 5); } PG_RETURN_TEXT_P (outvalue); } - ---- end of gender.c ----- Once I've compiled this to a shared library file with no errors or warnings (even with -Wall), I can load these functions in psql without error, and create a table with the appropriate type: test=# CREATE FUNCTION enum_gender_in (cstring) RETURNS enum_gender IMMUTABLE STRICT AS '/home/alex/epic/gender.so' LANGUAGEC; NOTICE: ProcedureCreate: type enum_gender is not yet defined CREATE FUNCTION test=# CREATE FUNCTION enum_gender_out (enum_gender) RETURNS cstring IMMUTABLE STRICT AS '/home/alex/epic/gender.so' LANGUAGEC; NOTICE: Argument type "enum_gender" is only a shell CREATE FUNCTION test=# CREATE TYPE enum_gender ( test(# INPUT = enum_gender_in, test(# OUTPUT = enum_gender_out, test(# INTERNALLENGTH = 2, test(# PASSEDBYVALUE test(# ); CREATE TYPE test=# CREATE TABLE people ( test(# id serial, test(# name text, test(# gender enum_gender test(# ); NOTICE: CREATE TABLE will create implicit sequence 'people_id_seq' for SERIAL column 'people.id' CREATE TABLE So far so good. However, the real problem comes when I try to insert something into the table I've just created: test=# INSERT INTO people (name, gender) VALUES ('Alex', 'Male'); 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. !# I'm fairly sure there's a bug in my C code somewhere, but I can't spot it. Admittedly, my C is pretty rusty, and I've not been able to find any useful documentation for the internal C structures in postgres, other than the source code itself. Any links to HOWTOs or similar would also be appreciated. Can I request a feature enhancement of built-in enumerated types for Postgres? ;) Alex -- Mail: Alex Page <alex.page@cancer.org.uk> Real: Systems/Network Assistant, Epidemiology Unit, Oxford Tel: 01865 302 223 (external) / 223 (internal) PGP: 8868 21D7 3D35 DD77 9D06 BF0A 0746 2DE6 55EA 367E
Attachment
Re: C Programming with postgres.h - my function crashes the database backend
From
Alvaro Herrera
Date:
On Tue, Dec 02, 2003 at 05:56:45PM +0000, Alex Page wrote: > Datum enum_gender_in(PG_FUNCTION_ARGS) { > text *invalue = PG_GETARG_TEXT_P(0); > > if ( strcmp ( VARDATA(invalue), "Male" ) ) { /* VARDATA gets the data portion of a "varlena" struct, whichis typedef'd to "text" */ > PG_RETURN_INT32( 0 ); > } > PG_RETURN_INT32( 1 ); > } VARDATA is not 0-terminated, so you can't use strcmp on it. Maybe you should use memcmp instead. -- Alvaro Herrera (<alvherre[a]dcc.uchile.cl>) "Nunca se desea ardientemente lo que solo se desea por razón" (F. Alexandre)
> > Datum enum_gender_in(PG_FUNCTION_ARGS) { > text *invalue = PG_GETARG_TEXT_P(0); > > PG_FUNCTION_INFO_V1(enum_gender_out); > > Datum enum_gender_out(PG_FUNCTION_ARGS) { > PG_RETURN_TEXT_P (outvalue); > } IN function takes a C-string, not a text and OUT functions should return C-string, not a text. -- Teodor Sigaev E-mail: teodor@sigaev.ru
Alvaro Herrera wrote: > On Tue, Dec 02, 2003 at 05:56:45PM +0000, Alex Page wrote: > >> Datum enum_gender_in(PG_FUNCTION_ARGS) { >> text *invalue = PG_GETARG_TEXT_P(0); >> >> if ( strcmp ( VARDATA(invalue), "Male" ) ) { /* VARDATA gets the data portion of a "varlena" struct, whichis typedef'd to "text" */ >> PG_RETURN_INT32( 0 ); >> } >> PG_RETURN_INT32( 1 ); >> } > > VARDATA is not 0-terminated, so you can't use strcmp on it. Maybe you > should use memcmp instead. > First of all, the argument to a type input procedure is a nul terminated CString, not text. Alex, why don't you look at an existing datatype in backend/utils/adt? Jan -- #======================================================================# # It's easier to get forgiveness for being wrong than for being right. # # Let's break this rule - forgive me. # #================================================== JanWieck@Yahoo.com #
On Tue, Dec 02, 2003 at 01:54:06PM -0500, Jan Wieck wrote: > First of all, the argument to a type input procedure is a nul terminated > CString, not text. Oops! Thanks for that, I'll see if I can get this working now. > Alex, why don't you look at an existing datatype in backend/utils/adt? I don't seem to have this file in my Postgres installation. Is it available somewhere? Alex -- Mail: Alex Page <alex.page@cancer.org.uk> Real: Systems/Network Assistant, Epidemiology Unit, Oxford Tel: 01865 302 223 (external) / 223 (internal) PGP: 8868 21D7 3D35 DD77 9D06 BF0A 0746 2DE6 55EA 367E
Attachment
Alex Page wrote: > On Tue, Dec 02, 2003 at 01:54:06PM -0500, Jan Wieck wrote: > >> First of all, the argument to a type input procedure is a nul terminated >> CString, not text. > > Oops! Thanks for that, I'll see if I can get this working now. > >> Alex, why don't you look at an existing datatype in backend/utils/adt? > > I don't seem to have this file in my Postgres installation. Is it > available somewhere? It is the directory of the PostgreSQL source tree where all the builtin data types live and you can find it here: http://developer.postgresql.org/cvsweb.cgi/pgsql-server/src/backend/utils/adt/ Jan -- #======================================================================# # It's easier to get forgiveness for being wrong than for being right. # # Let's break this rule - forgive me. # #================================================== JanWieck@Yahoo.com #