Fwd: SPI_palloc problem

Started by Aron Widforssover 8 years ago2 messagesgeneral
Jump to latest
#1Aron Widforss
pgsql-general@antarkt.is

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

#2Aron Widforss
aron@antarkt.is
In reply to: Aron Widforss (#1)
Re: Fwd: SPI_palloc problem

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 negative in 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