Re: [GENERAL] Fwd: SPI_palloc problem - Mailing list pgsql-general

From Aron Widforss
Subject Re: [GENERAL] Fwd: SPI_palloc problem
Date
Msg-id 1509362997.1151368.1155403288.6B1EBA58@webmail.messagingengine.com
Whole thread Raw
In response to [GENERAL] Fwd: SPI_palloc problem  (Aron Widforss <pgsql-general@antarkt.is>)
List pgsql-general
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

pgsql-general by date:

Previous
From: Alexander Farber
Date:
Subject: [GENERAL] Comparing epoch to timestamp
Next
From: Alexander Farber
Date:
Subject: Re: [GENERAL] Comparing epoch to timestamp