Thread: User-defined-type in C crashing PostgreSQL server: What am I doing wrong?

User-defined-type in C crashing PostgreSQL server: What am I doing wrong?

From
"J. Greg Davidson"
Date:
Hello,

My user-defined types are crashing the PostgreSQL server and I don't
understand why.  I've been trying to figure it out on my own for overr
a week.  I've cooked what I'm doing down to the essentials and I'm
asking for help.  Help: What am I doing wrong?

* Sections of this message:
** Overview with psql session
** SQL code
** C Code
** gdb backtraces
***  gdb backtrace after SELECT ARRAY[ pair_tag_id(3, 4) ];
***  gdb backtrace after SELECT x from pair_tag_id(3, 4) x;
***  gdb backtrace after SELECT ROW( pair_tag_id(3, 4) );
** Thanks for your help!

** Overview wiith psql session

Tested with: PostgreSQL-8.1.4 and  PostgreSQL-8.2beta3,
    installed from source with
    ./configure ... --enable-debug --enable-cassert
Platform: SuSE Linux 10.0 on i686 Dell Notebook Computer

I have a simple one-word datatype called a "pair", which consists of a
1-byte "tag" and a 3-byte "id".

-- At first, it seems to work:

pairs=# select pair_tag_id(3, 4);
       pair_tag_id
-------------------------
 <pair tag="3" id="4" />
(1 row)

pairs=# select tag_pair(pair_tag_id(3, 4));
 tag_pair
----------
        3
(1 row)

pairs=# select id_pair(pair_tag_id(3, 4));
 id_pair
---------
       4
(1 row)

-- But all three of these crash the server with Signal 11:

SELECT ARRAY[ pair_tag_id(3, 4) ];

SELECT x from pair_tag_id(3, 4) x;

SELECT ROW( pair_tag_id(3, 4) );

-- Note:    Nothing special about 3 and 4, all values I've tried behave
--    the same!

** SQL code

-- CREATE TYPE pair; -- PostgreSQL 8.2

CREATE OR REPLACE
FUNCTION pair_in(cstring) RETURNS pair
AS 'pair.so' LANGUAGE 'c' STRICT;

CREATE OR REPLACE
FUNCTION pair_out(pair) RETURNS cstring
AS 'pair.so' LANGUAGE 'c' STRICT;

CREATE TYPE pair (
  INTERNALLENGTH = 4,        -- 32-bits
  INPUT = pair_in,
  OUTPUT = pair_out
);

CREATE OR REPLACE
FUNCTION tag_pair(pair) RETURNS integer
AS 'pair.so' LANGUAGE 'c' STRICT;

CREATE OR REPLACE
FUNCTION id_pair(pair) RETURNS integer
AS 'pair.so' LANGUAGE 'c' STRICT;

CREATE OR REPLACE
FUNCTION pair_tag_id(integer, integer) RETURNS pair
AS 'pair.so' LANGUAGE 'c' STRICT;

** C Code

$ cc -fpic -I/Ubuntu/usr/local/pgsql-8.1.4/include/server -Wall   -c -o
pair.o pair.c
$ cc -shared -o pair.so pair.o
$ cat pair.c

#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <postgres.h>    /* general Postgres declarations */
#include <fmgr.h>        /* for argument/result macros */

#define PAIR_TAG_WIDTH 8
#define PAIR_MAX_TAG ( (1U << PAIR_TAG_WIDTH) - 1)
#define PAIR_MAX_ID ( (int) (INT_MAX >> PAIR_TAG_WIDTH) )
#define PAIR_MIN_ID ( -PAIR_MAX_ID )

typedef long pairs;            // a tag plus an ID
typedef unsigned tags;
#define F_TAG "tag: %u "        // Format string for tags
typedef long ids;
#define F_ID "id: %ld "        // Format string for ids

typedef char *StrPtr;        // '\0' terminated modifiable string
typedef const char *Str;        // '\0' terminated constant string

#ifdef PG_MODULE_MAGIC
PG_MODULE_MAGIC;
#endif

inline pairs TagPairId (tags tag, ids id) {
  return (id << PAIR_TAG_WIDTH) | tag;
}
inline tags TagPair(pairs v) {
  return v & ~(~0U <<  PAIR_TAG_WIDTH);
}
inline ids IdPair(pairs v) {
  return v >> PAIR_TAG_WIDTH;
}

#define FUNCTION_DEFINE(f)    \
    PG_FUNCTION_INFO_V1(f);    \
    Datum (f)(PG_FUNCTION_ARGS)

FUNCTION_DEFINE(pair_tag_id) {    // (tags, ids) -> pair
  tags tag = PG_GETARG_INT32(0);
  ids  id =  PG_GETARG_INT32(1);
  PG_RETURN_INT32( TagPairId( tag,  id) );
}

FUNCTION_DEFINE(tag_pair) {    // (pair) ->  tags
  PG_RETURN_INT32( TagPair( PG_GETARG_INT32(0) ) );
}

FUNCTION_DEFINE(id_pair) {    // (pair) -> ids
  PG_RETURN_INT32( IdPair( PG_GETARG_INT32(0) ) );
}

inline StrPtr NewStr( Str old ) {
  return strcpy( palloc(strlen(old)+1), old );
}

StrPtr PairToXMLStr(Str message, pairs p) {
  tags tag = TagPair(p);
  ids id = IdPair(p);
  Str msg = message ? message : "";
  Str format = message
    ? "<pair tag=\"%u\" id=\"%d\">%s</pair>"
    : "<pair tag=\"%u\" id=\"%d\" %s/>";
// I tried with both of the next two lines, just in case: same behavior
//  char buffer[strlen(format) + strlen(msg) + 2 * 20];    // gcc extension
  char * buffer = palloc(strlen(format) + strlen(msg) + 2 * 20);
  sprintf(buffer, format, tag, id, msg);
  return NewStr(buffer);
}

FUNCTION_DEFINE(pair_in) {        // cstring -> pair
  PG_RETURN_INT32(0);
}

FUNCTION_DEFINE(pair_out) {        // pair -> cstring
  PG_RETURN_CSTRING( PairToXMLStr( NULL, PG_GETARG_INT32(0) ) );
}


** gdb backtraces

# gdb bin/postgres data/core
GNU gdb 6.3
...
Core was generated by `postgres: greg pairs [local] SELECT
'.

Program terminated with signal 11, Segmentation fault.
...


***  gdb backtrace after SELECT ARRAY[ pair_tag_id(3, 4) ];
(gdb) backtrace
#0  0xb7d2a07d in memmove () from /lib/tls/libc.so.6
#1  0x00000403 in ?? ()
#2  0x081ed694 in ArrayCastAndSet (src=138452140, typlen=1027,
    typbyval=<value optimized out>, typalign=105 'i',
    dest=0x8409cac "\177\177\177\177~", '\177' <repeats 195 times>...)
    at arrayfuncs.c:3673
#3  0x081ed92a in CopyArrayEls (array=<value optimized out>,
values=0x8409ba4,
    nulls=0x8409bb8 "", nitems=1, typlen=4, typbyval=0 '\0',
typalign=105 'i',
    freedata=0 '\0') at arrayfuncs.c:872
#4  0x081edbde in construct_md_array (elems=0x8409ba4, nulls=0x8409bb8
"",
    ndims=1, dims=0xbfaaddc0, lbs=0xbfaadda8, elmtype=164377, elmlen=4,
    elmbyval=0 '\0', elmalign=105 'i') at arrayfuncs.c:2863
#5  0x0815ae43 in ExecEvalArray (astate=0x8407e58, econtext=0x8407dc0,
    isNull=0x84085cc "", isDone=0x84085e0) at execQual.c:2266
#6  0x08159b54 in ExecProject (projInfo=0x8408520, isDone=0xbfaade7c)
    at execQual.c:3986
#7  0x08167e83 in ExecResult (node=0x8407d34) at nodeResult.c:157
#8  0x08156673 in ExecProcNode (node=0x8407d34) at execProcnode.c:334
#9  0x08154dc0 in ExecutorRun (queryDesc=0x84077c0,
    direction=ForwardScanDirection, count=0) at execMain.c:1081
#10 0x081e3129 in PortalRunSelect (portal=0x83fecdc,
    forward=<value optimized out>, count=0, dest=0x83f299c) at
pquery.c:831
#11 0x081e441a in PortalRun (portal=0x83fecdc, count=2147483647,
---Type <return> to continue, or q <return> to quit---

***  gdb backtrace after SELECT x from pair_tag_id(3, 4) x;

(gdb) backtrace
#0  0xb7d2a58c in memcpy () from /lib/tls/libc.so.6
#1  0x0807b58f in heap_fill_tuple (tupleDesc=0x842053c,
values=0xbfaaddb8,
    isnull=0xbfaadb8c "", data=0x842053c "", infomask=0x8420538,
bit=0x0)
    at heaptuple.c:180
#2  0x0807c0e6 in heap_form_tuple (tupleDescriptor=0x841aeec,
    values=0xbfaaddb8, isnull=0xbfaadb8c "") at heaptuple.c:745
#3  0x0815aac9 in ExecMakeTableFunctionResult (funcexpr=0x841a930,
    econtext=0x841a718, expectedDesc=0x841a7fc, returnDesc=0x842053c)
    at execQual.c:1457
#4  0x08167c0c in FunctionNext (node=0x841a68c) at nodeFunctionscan.c:69
#5  0x0815c7d8 in ExecScan (node=0x841a68c, accessMtd=0x8167bb0
<FunctionNext>)
    at execScan.c:68
#6  0x08167824 in ExecFunctionScan (node=0x841a68c) at
nodeFunctionscan.c:110
#7  0x081566ec in ExecProcNode (node=0x841a68c) at execProcnode.c:371
#8  0x08154dc0 in ExecutorRun (queryDesc=0x8403450,
    direction=ForwardScanDirection, count=0) at execMain.c:1081
#9  0x081e3129 in PortalRunSelect (portal=0x83fa96c,
    forward=<value optimized out>, count=0, dest=0x83d96c4) at
pquery.c:831
#10 0x081e441a in PortalRun (portal=0x83fa96c, count=2147483647,
    dest=0x83d96c4, altdest=0x83d96c4, completionTag=0xbfaae0ba "")
    at pquery.c:684
#11 0x081dffaa in exec_simple_query (
    query_string=0x83d774c "SELECT x from pair_tag_id(3, 4) x;")
---Type <return> to continue, or q <return> to quit---

***  gdb backtrace after SELECT ROW( pair_tag_id(3, 4) );

(gdb) backtrace
#0  0xb7d2a58c in memcpy () from /lib/tls/libc.so.6
#1  0x0807b58f in heap_fill_tuple (tupleDesc=0x8411d84,
values=0x8411d2c,
    isnull=0x8411d40 "", data=0x8411d84 "", infomask=0x8411d80, bit=0x0)
    at heaptuple.c:180
#2  0x0807c0e6 in heap_form_tuple (tupleDescriptor=0x8403954,
    values=0x8411d2c, isnull=0x8411d40 "") at heaptuple.c:745
#3  0x08158a53 in ExecEvalRow (rstate=0x8403928, econtext=0x8403890,
    isNull=0x84041a8 "", isDone=0x84041bc) at execQual.c:2479
#4  0x08159b54 in ExecProject (projInfo=0x84040fc, isDone=0xbfaade7c)
    at execQual.c:3986
#5  0x08167e83 in ExecResult (node=0x8403804) at nodeResult.c:157
#6  0x08156673 in ExecProcNode (node=0x8403804) at execProcnode.c:334
#7  0x08154dc0 in ExecutorRun (queryDesc=0x8403290,
    direction=ForwardScanDirection, count=0) at execMain.c:1081
#8  0x081e3129 in PortalRunSelect (portal=0x840726c,
    forward=<value optimized out>, count=0, dest=0x8416454) at
pquery.c:831
#9  0x081e441a in PortalRun (portal=0x840726c, count=2147483647,
    dest=0x8416454, altdest=0x8416454, completionTag=0xbfaae0ba "")
    at pquery.c:684
#10 0x081dffaa in exec_simple_query (
    query_string=0x83d774c "SELECT ROW( pair_tag_id(3, 4) );")
    at postgres.c:939
#11 0x081e0f6e in PostgresMain (argc=4, argv=<value optimized out>,
---Type <return> to continue, or q <return> to quit---

** Thanks for your help!

_Greg

Re: User-defined-type in C crashing PostgreSQL server: What am I doing wrong?

From
Martijn van Oosterhout
Date:
On Sat, Nov 18, 2006 at 01:07:15PM -0800, J. Greg Davidson wrote:
> Hello,
>
> My user-defined types are crashing the PostgreSQL server and I don't
> understand why.  I've been trying to figure it out on my own for overr
> a week.  I've cooked what I'm doing down to the essentials and I'm
> asking for help.  Help: What am I doing wrong?

This may be a long shot but:

> CREATE TYPE pair (
>   INTERNALLENGTH = 4,        -- 32-bits
>   INPUT = pair_in,
>   OUTPUT = pair_out
> );

You're not specifying PASSEDBYVALUE, so I think postgres is assuming
you're returning a *pointer* to 4 bytes, so it's dying trying to copy
it.

Hope this helps,
--
Martijn van Oosterhout   <kleptog@svana.org>   http://svana.org/kleptog/
> From each according to his ability. To each according to his ability to litigate.

Attachment

Re: User-defined-type in C crashing PostgreSQL server:

From
"J. Greg Davidson"
Date:
Martijn was exactly right!  All of the examples I'd been following were
using pass-by-reference, the function call interface is oblivious and I
missed this very important part of the type definition.  Yea!

Big thanks,

_Greg

n Sat, 2006-11-18 at 23:15 +0100, Martijn van Oosterhout wrote:
> On Sat, Nov 18, 2006 at 01:07:15PM -0800, J. Greg Davidson wrote:
> > Hello,
> >
> > My user-defined types are crashing the PostgreSQL server and I don't
> > understand why.  I've been trying to figure it out on my own for overr
> > a week.  I've cooked what I'm doing down to the essentials and I'm
> > asking for help.  Help: What am I doing wrong?
>
> This may be a long shot but:
>
> > CREATE TYPE pair (
> >   INTERNALLENGTH = 4,        -- 32-bits
> >   INPUT = pair_in,
> >   OUTPUT = pair_out
> > );
>
> You're not specifying PASSEDBYVALUE, so I think postgres is assuming
> you're returning a *pointer* to 4 bytes, so it's dying trying to copy
> it.
>
> Hope this helps,