Thread: [GENERAL] Fwd: SPI_palloc problem

[GENERAL] Fwd: SPI_palloc problem

From
Aron Widforss
Date:
I mailed this from my main email address instead of the one I'm
subscribed to pgsql-general with. So, here goes.

----- Original message -----
From: Aron Widforss <aron@antarkt.is>
To: pgsql-general@postgresql.org
Subject: SPI_palloc problem
Date: Sun, 29 Oct 2017 19:35:30 +0100

Good evening,

I'm really new to PostgreSQL, and even C, but am trying to put together
a small pathfinding module to learn about it. Right now I'm just writing
a function to load my graph into a convenient data structure to use
later. I'm currently just loading the data of each vertice into my data
structure and then printing the loaded position out with elog(). It
works, but when I try to use SPI_palloc instead of palloc it disconnects
every time.

I suspect that my bug is really trivial, so maybe someone on this list
can just see what it is in the code and save me some time. :)

Regards,
Aron Widforss

Here is my version, example data and program:

pathfinding-hike=# SELECT version();                                                    version             

-----------------------------------------------------------------------------------------------------------------PostgreSQL
9.5.9on x86_64-pc-linux-gnu, compiled by gcc (Ubuntu5.4.0-6ubuntu1~16.04.4) 5.4.0 20160609, 64-bit
 
(1 row)

----

pathfinding-hike=# SELECT * FROM toc LIMIT 10;node |       pos        | connected  |    length     |     costs     |
lines   
 
------+------------------+------------+---------------+---------------+------------   1 | {268582,6528365} | {2}
|{297}         | {297}         |   {1}   2 | {268773,6528472} | {1}        | {297}         | {297}         |   {1}   3
|{269103,6534918} | {15}       | {676}         | {676}         |   {2}   4 | {269436,6534931} | {12}       | {263}
  | {263}         |   {4}   5 | {269521,6534450} | {11}       | {373}         | {373}         |   {6}   6 |
{269535,6536629}| {28,22,7}  | {894,1508,66} | {894,1508,66} |   {7,3,8}   7 | {269542,6536693} | {6,9,9}    |
{66,657,218} | {66,657,218}  |   {8,5,9}   8 | {269585,6535610} | {14}       | {70}          | {70}          |   {10}
9| {269631,6536886} | {7,7,36}   | {657,218,584} | {657,218,584} |   {5,9,11}  10 | {269642,6534754} | {12,20,11} |
{137,140,6}  | {137,140,6}   |  {12,13,14}
 
(10 rows)

----

#include "postgres.h"
#include "utils/array.h"
#include "utils/lsyscache.h"
#include "catalog/pg_type.h"
#include "executor/spi.h"
#include "utils/builtins.h"

int check_err(int code);
int check_err(int code)
{   if(code < 0) {       elog(ERROR, "%m");   }   return code;
}

int32_t * get_int_arr(HeapTuple tuple, TupleDesc rowdesc, int fnumber);
int32_t * get_int_arr(HeapTuple tuple, TupleDesc rowdesc, int fnumber)
{   Datum     raw_arr;   ArrayType *pg_arr;   Datum     *c_arr;   bool      isnull;   /* Parameters for data alignment
*/  Oid       eltype;   int16     typlen;   bool      typbyval;   char      typalign;   /* Info about unwrapped array
*/  int32_t   *int_arr;   int       arr_len;   int       arr_dim;
 
   /* Get array raw-format */   raw_arr = SPI_getbinval(tuple, rowdesc, fnumber, &isnull);   check_err(SPI_result);
if(isnull){       elog(ERROR, "Cannot deconstruct null array");   }
 
   /* Get array db-format */   pg_arr  = DatumGetArrayTypeP(raw_arr);   arr_dim = ARR_NDIM(pg_arr);   eltype  =
ARR_ELEMTYPE(pg_arr);  //TODO: More thorough type checking has to be done in plpgsql   if(eltype != INT4OID) {
elog(ERROR,"Array not of type Integer");   }
 
   /* Get array as C-array (length prepended to 1st element) */   get_typlenbyvalalign(eltype, &typlen, &typbyval,
&typalign);  if(arr_dim != 1) {       elog(ERROR, "Cannot interpret multidimensional arrays");   }
deconstruct_array(pg_arr,eltype, typlen, typbyval, typalign,                     &c_arr, NULL, &arr_len);   int_arr =
palloc((arr_len+1)*sizeof(int32_t));  for(int i = 0; i<arr_len; i++) {       int_arr[i+1] = DatumGetInt32(c_arr[i]);
}  int_arr[0] = arr_len+1;
 
   return int_arr;
}   
uint32_t * get_uint_arr(HeapTuple tuple, TupleDesc rowdesc, int
fnumber);
uint32_t * get_uint_arr(HeapTuple tuple, TupleDesc rowdesc, int fnumber)
{   int32_t  *int_arr;   int_arr = get_int_arr(tuple, rowdesc, fnumber);
   for(int i = 1; i < int_arr[0]; i++) {       if(int_arr[i] < 0) {           elog(ERROR, "Unsigned int stored as
negativein database");       }   }
 
   return (uint32_t *)int_arr;
}                
PG_MODULE_MAGIC;

PG_FUNCTION_INFO_V1(astarc);
Datum
astarc(PG_FUNCTION_ARGS)
{   int rows;
   typedef struct node node;   struct node {       int32_t *pos;       uint32_t *conn;       uint32_t *costs;
uint32_t*edges;   };
 
   struct node *graph;
   check_err(SPI_connect());   check_err(SPI_execute("SELECT * FROM toc", true, 10));   rows = SPI_processed;
   graph = palloc((rows + 1)*sizeof(node));   // Store size of array in first connected[]   graph[0].conn[0] = rows+1;
   if(rows > 0 && SPI_tuptable != NULL) {       TupleDesc tupdesc = SPI_tuptable->tupdesc;       SPITupleTable
*tuptable= SPI_tuptable;
 
       for (uint32_t i = 0; i < rows; i++) {           HeapTuple tuple = tuptable->vals[i];           graph[i].pos   =
get_int_arr(tuple,tupdesc, 2);           graph[i].conn  = get_uint_arr(tuple, tupdesc, 3);           graph[i].costs =
get_uint_arr(tuple,tupdesc, 5);           graph[i].edges = get_uint_arr(tuple, tupdesc, 6);
 
           elog(INFO, "x: %i, y: %i", graph[i].pos[1],           graph[i].pos[2]);       }   }
   if(rows == 0) {       elog(ERROR, "Graph not found");   }
   check_err(SPI_finish());
   PG_RETURN_TEXT_P(cstring_to_text("test"));
}


-- 
Sent via pgsql-general mailing list (pgsql-general@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-general

Re: [GENERAL] Fwd: SPI_palloc problem

From
Aron Widforss
Date:
On Sun, Oct 29, 2017, at 07:40 PM, Aron Widforss wrote:
> I mailed this from my main email address instead of the one I'm
> subscribed to pgsql-general with. So, here goes.
> 
> ----- Original message -----
> From: Aron Widforss <aron@antarkt.is>
> To: pgsql-general@postgresql.org
> Subject: SPI_palloc problem
> Date: Sun, 29 Oct 2017 19:35:30 +0100
> 
> Good evening,
> 
> I'm really new to PostgreSQL, and even C, but am trying to put together
> a small pathfinding module to learn about it. Right now I'm just writing
> a function to load my graph into a convenient data structure to use
> later. I'm currently just loading the data of each vertice into my data
> structure and then printing the loaded position out with elog(). It
> works, but when I try to use SPI_palloc instead of palloc it disconnects
> every time.
> 
> I suspect that my bug is really trivial, so maybe someone on this list
> can just see what it is in the code and save me some time. :)
> 
> Regards,
> Aron Widforss
> 
> Here is my version, example data and program:
> 
> pathfinding-hike=# SELECT version();
>                                                      version             
> -----------------------------------------------------------------------------------------------------------------
>  PostgreSQL 9.5.9 on x86_64-pc-linux-gnu, compiled by gcc (Ubuntu
>  5.4.0-6ubuntu1~16.04.4) 5.4.0 20160609, 64-bit
> (1 row)
> 
> ----
> 
> pathfinding-hike=# SELECT * FROM toc LIMIT 10;
>  node |       pos        | connected  |    length     |     costs     | 
>   lines    
> ------+------------------+------------+---------------+---------------+------------
>     1 | {268582,6528365} | {2}        | {297}         | {297}         |
>     {1}
>     2 | {268773,6528472} | {1}        | {297}         | {297}         |
>     {1}
>     3 | {269103,6534918} | {15}       | {676}         | {676}         |
>     {2}
>     4 | {269436,6534931} | {12}       | {263}         | {263}         |
>     {4}
>     5 | {269521,6534450} | {11}       | {373}         | {373}         |
>     {6}
>     6 | {269535,6536629} | {28,22,7}  | {894,1508,66} | {894,1508,66} |
>     {7,3,8}
>     7 | {269542,6536693} | {6,9,9}    | {66,657,218}  | {66,657,218}  |
>     {8,5,9}
>     8 | {269585,6535610} | {14}       | {70}          | {70}          |
>     {10}
>     9 | {269631,6536886} | {7,7,36}   | {657,218,584} | {657,218,584} |
>     {5,9,11}
>    10 | {269642,6534754} | {12,20,11} | {137,140,6}   | {137,140,6}   |
>    {12,13,14}
> (10 rows)
> 
> ----
> 
> #include "postgres.h"
> #include "utils/array.h"
> #include "utils/lsyscache.h"
> #include "catalog/pg_type.h"
> #include "executor/spi.h"
> #include "utils/builtins.h"
> 
> int check_err(int code);
> int check_err(int code)
> {
>     if(code < 0) {
>         elog(ERROR, "%m");
>     }
>     return code;
> }
> 
> int32_t * get_int_arr(HeapTuple tuple, TupleDesc rowdesc, int fnumber);
> int32_t * get_int_arr(HeapTuple tuple, TupleDesc rowdesc, int fnumber)
> {
>     Datum     raw_arr;
>     ArrayType *pg_arr;
>     Datum     *c_arr;
>     bool      isnull;
>     /* Parameters for data alignment */
>     Oid       eltype;
>     int16     typlen;
>     bool      typbyval;
>     char      typalign;
>     /* Info about unwrapped array */
>     int32_t   *int_arr;
>     int       arr_len;
>     int       arr_dim;
> 
>     /* Get array raw-format */
>     raw_arr = SPI_getbinval(tuple, rowdesc, fnumber, &isnull);
>     check_err(SPI_result);
>     if(isnull) {
>         elog(ERROR, "Cannot deconstruct null array");
>     }
> 
>     /* Get array db-format */
>     pg_arr  = DatumGetArrayTypeP(raw_arr);
>     arr_dim = ARR_NDIM(pg_arr);
>     eltype  = ARR_ELEMTYPE(pg_arr);
>     //TODO: More thorough type checking has to be done in plpgsql
>     if(eltype != INT4OID) {
>         elog(ERROR, "Array not of type Integer");
>     }
> 
>     /* Get array as C-array (length prepended to 1st element) */
>     get_typlenbyvalalign(eltype, &typlen, &typbyval, &typalign);
>     if(arr_dim != 1) {
>         elog(ERROR, "Cannot interpret multidimensional arrays");
>     }
>     deconstruct_array(pg_arr, eltype, typlen, typbyval, typalign,
>                       &c_arr, NULL, &arr_len);
>     int_arr = palloc((arr_len+1)*sizeof(int32_t));
>     for(int i = 0; i<arr_len; i++) {
>         int_arr[i+1] = DatumGetInt32(c_arr[i]);
>     }
>     int_arr[0] = arr_len+1;
> 
>     return int_arr;
> }
>     
> uint32_t * get_uint_arr(HeapTuple tuple, TupleDesc rowdesc, int
> fnumber);
> uint32_t * get_uint_arr(HeapTuple tuple, TupleDesc rowdesc, int fnumber)
> {
>     int32_t  *int_arr;
>     int_arr = get_int_arr(tuple, rowdesc, fnumber);
> 
>     for(int i = 1; i < int_arr[0]; i++) {
>         if(int_arr[i] < 0) {
>             elog(ERROR, "Unsigned int stored as negative in database");
>         }
>     }
> 
>     return (uint32_t *)int_arr;
> }
>                  
> PG_MODULE_MAGIC;
> 
> PG_FUNCTION_INFO_V1(astarc);
> Datum
> astarc(PG_FUNCTION_ARGS)
> {
>     int rows;
> 
>     typedef struct node node;
>     struct node {
>         int32_t *pos;
>         uint32_t *conn;
>         uint32_t *costs;
>         uint32_t *edges;
>     };
> 
>     struct node *graph;
> 
>     check_err(SPI_connect());
>     check_err(SPI_execute("SELECT * FROM toc", true, 10));
>     rows = SPI_processed;
> 
>     graph = palloc((rows + 1)*sizeof(node));
>     // Store size of array in first connected[]
>     graph[0].conn[0] = rows+1;
> 
>     if(rows > 0 && SPI_tuptable != NULL) {
>         TupleDesc tupdesc = SPI_tuptable->tupdesc;
>         SPITupleTable *tuptable = SPI_tuptable;
> 
>         for (uint32_t i = 0; i < rows; i++) {
>             HeapTuple tuple = tuptable->vals[i];
>             graph[i].pos   = get_int_arr(tuple, tupdesc, 2);
>             graph[i].conn  = get_uint_arr(tuple, tupdesc, 3);
>             graph[i].costs = get_uint_arr(tuple, tupdesc, 5);
>             graph[i].edges = get_uint_arr(tuple, tupdesc, 6);
> 
>             elog(INFO, "x: %i, y: %i", graph[i].pos[1],
>             graph[i].pos[2]);
>         }
>     }
> 
>     if(rows == 0) {
>         elog(ERROR, "Graph not found");
>     }
> 
>     check_err(SPI_finish());
> 
>     PG_RETURN_TEXT_P(cstring_to_text("test"));
> }
> 
> 
> -- 
> Sent via pgsql-general mailing list (pgsql-general@postgresql.org)
> To make changes to your subscription:
> http://www.postgresql.org/mailpref/pgsql-general

After some sleep I attacked my problem again and resolved it myself. I
had some of by one errors, but the main problem seemed to be that I did
not allocate space for the elements of my first node, where I stored the
size of the graph array. I though that allocation was implicit in the
struct declaration, but apparently not. For the sake of completeness
I'll post my working code below.

Regards,
Aron Widforss

----

#include "postgres.h"
#include "utils/array.h"
#include "utils/lsyscache.h"
#include "catalog/pg_type.h"
#include "executor/spi.h"
#include "utils/builtins.h"

typedef struct node {   int32_t *pos;   uint32_t *conn;   uint32_t *costs;   uint32_t *edges;
} node;

void delay(unsigned int secs);
int check_err(int code);
int32_t * get_int_arr(HeapTuple tuple, TupleDesc rowdesc, int fnumber);
uint32_t * get_uint_arr(HeapTuple tuple, TupleDesc rowdesc, int
fnumber);
node * load_graph(void);

PG_MODULE_MAGIC;

PG_FUNCTION_INFO_V1(astarc);
Datum
astarc(PG_FUNCTION_ARGS)
{   struct node *graph;   graph = load_graph();
   for(int i = 0; i < *(graph[0].pos); i++) {       elog(INFO, "x: %i, y: %i", graph[i].pos[1], graph[i].pos[2]);   }
elog(INFO,"%i", *(graph[0].pos));
 
   for(int i = 1; i < graph[0].pos[0]; i++) {       pfree(graph[i].pos);       pfree(graph[i].conn);
pfree(graph[i].costs);      pfree(graph[i].edges);   }   pfree(graph);
 
   PG_RETURN_TEXT_P(cstring_to_text("test"));
}

node * load_graph(void) {   int rows;   struct node *graph;
   check_err(SPI_connect());   check_err(SPI_execute("SELECT * FROM toc", true, 10));   rows = SPI_processed;
elog(INFO,"%i", rows);
 
   graph = SPI_palloc((rows + 1)*sizeof(node));   // Store size of array in first connected[]   graph[0].pos =
SPI_palloc(sizeof(int32_t));  graph[0].conn = SPI_palloc(sizeof(uint32_t));   graph[0].costs =
SPI_palloc(sizeof(uint32_t));  graph[0].edges = SPI_palloc(sizeof(uint32_t));   *(graph[0].pos) = rows+1;
*(graph[0].conn)= 0;   *(graph[0].costs) = 0;   *(graph[0].edges) = 0;
 
   if(rows > 0 && SPI_tuptable != NULL) {       TupleDesc tupdesc = SPI_tuptable->tupdesc;       SPITupleTable
*tuptable= SPI_tuptable;
 
       for (uint32_t i = 0; i < rows; i++) {           HeapTuple tuple = tuptable->vals[i];           graph[i+1].pos
=get_int_arr(tuple, tupdesc, 2);           graph[i+1].conn  = get_uint_arr(tuple, tupdesc, 3);
graph[i+1].costs= get_uint_arr(tuple, tupdesc, 5);           graph[i+1].edges = get_uint_arr(tuple, tupdesc, 6);
}  }
 
   if(rows == 0) {       elog(ERROR, "Graph not found");   }
   check_err(SPI_finish());
   return graph;
}

int check_err(int code)
{   if(code < 0) {       elog(ERROR, "%m");   }   return code;
}

int32_t * get_int_arr(HeapTuple tuple, TupleDesc rowdesc, int fnumber)
{   Datum     raw_arr;   ArrayType *pg_arr;   Datum     *c_arr;   bool      isnull;   /* Parameters for data alignment
*/  Oid       eltype;   int16     typlen;   bool      typbyval;   char      typalign;   /* Info about unwrapped array
*/  int32_t   *int_arr;   int       arr_len;   int       arr_dim;
 
   /* Get array raw-format */   raw_arr = SPI_getbinval(tuple, rowdesc, fnumber, &isnull);   check_err(SPI_result);
if(isnull){       elog(ERROR, "Cannot deconstruct null array");   }
 
   /* Get array db-format */   pg_arr  = DatumGetArrayTypeP(raw_arr);   arr_dim = ARR_NDIM(pg_arr);   eltype  =
ARR_ELEMTYPE(pg_arr);  //TODO: More thorough type checking has to be done in plpgsql   if(eltype != INT4OID) {
elog(ERROR,"Array not of type Integer");   }
 
   /* Get array as C-array (length prepended to 1st element) */   get_typlenbyvalalign(eltype, &typlen, &typbyval,
&typalign);  if(arr_dim != 1) {       elog(ERROR, "Cannot interpret multidimensional arrays");   }
deconstruct_array(pg_arr,eltype, typlen, typbyval, typalign,                     &c_arr, NULL, &arr_len);   int_arr =
SPI_palloc((arr_len+1)*sizeof(int32_t));  for(int i = 0; i<arr_len; i++) {       int_arr[i+1] =
DatumGetInt32(c_arr[i]);  }   int_arr[0] = arr_len+1;
 
   return int_arr;
}   
uint32_t * get_uint_arr(HeapTuple tuple, TupleDesc rowdesc, int fnumber)
{   int32_t  *int_arr;   int_arr = get_int_arr(tuple, rowdesc, fnumber);
   for(int i = 1; i < int_arr[0]; i++) {       if(int_arr[i] < 0) {           elog(ERROR, "Unsigned int stored as
negativein database");       }   }
 
   return (uint32_t *)int_arr;
}


-- 
Sent via pgsql-general mailing list (pgsql-general@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-general