Discussion:
Fast access to PostGIS from c/c++
Boehm, Andreas
2006-12-05 11:25:36 UTC
Permalink
Hi!

Evaluating the possibilities of using PostGIS instead of ArcSDE, I'm
looking for a fast solution to insert and read points and polygons to
PostGIS from my c++ code.

I have tried several approaches with libpqxx. The only way to insert
points is executing a statement with WNT like "INSERT INTO points VALUES
(1, GeometryFromText('POINT (10 20)', -1) )". Either the tabelwriter
functionality (internally using COPY) nor predefined statements ("INSERT
INTO points VALUES ($1, GeometryFromText('POINT ($2 $3)', -1)" do work.

I assume the only possibility to communicate with PostgreSQL / PostGIS
is using SQL and strings. Every number is converted into a string and
later - on the other side - back into a number. You have to convert
geometries via GeometryFromText() or AsText() or X().

There seems to be a binary interface for ArcSDE. So I wonder if PostGIS
would be fast enough. Could it make sense to convert geometries into WKB
by myself on client side?

Regards,
Andi
Mark Cave-Ayland
2006-12-05 12:09:32 UTC
Permalink
Post by Boehm, Andreas
Hi!
Evaluating the possibilities of using PostGIS instead of ArcSDE, I'm
looking for a fast solution to insert and read points and polygons to
PostGIS from my c++ code.
I have tried several approaches with libpqxx. The only way to insert
points is executing a statement with WNT like "INSERT INTO points VALUES
(1, GeometryFromText('POINT (10 20)', -1) )". Either the tabelwriter
functionality (internally using COPY) nor predefined statements ("INSERT
INTO points VALUES ($1, GeometryFromText('POINT ($2 $3)', -1)" do work.
Hi Andi,

AFAICT using prepared statements in this way won't work because
GeometryFromText() takes a string argument and you are trying to
substitute values within the string parameter, rather than the parameter
itself.

In theory, COPY should be fastest method of inserting geometries since
it requires that the client should already have converted the geometry
to EWKB format ready for insertion. Documentation on the (E)WKB formats
can be found within the PostGIS source download.

If all your geometries are of one type, e.g. points, the second fastest
method would probably be to use a prepared statement using the
MakePoint() function and put as many points as possible within a single
transaction, e.g.

PREPARE myplan(int, double, double) AS INSERT INTO points VALUES ($1,
MakePoint($2, $3));
BEGIN;
..
..
EXECUTE myplan(1, 20, 30);
..
..
COMMIT;

Since committing a transaction incurs an extra write to the transaction
log, then placing as many points (if not all) within the same
transaction will greatly speed your insertion speed.

Finally, don't forget your basic PostgreSQL tuning - by default
out-of-the-box PostgreSQL only uses 8Mb of memory, so you'll need to
alter the postgresql.conf parameters for
shared_buffers/effective_cache_size to suit the amount of RAM in your
server and restart PostgreSQL.


Kind regards,

Mark.
Chapman, Martin
2006-12-05 16:36:14 UTC
Permalink
Mark,

Just out of curiosity, you mention PostgreSQL tuning at the bottom of
your email. How will changing that setting help performance? What do
you set it at when you run your database.

Thanks,

Martin Chapman
SANZ Inc.
Software Developer
http://www.earthwhere.com
W - 303.495.6326
C - 303.898.0397

-----Original Message-----
From: postgis-users-***@postgis.refractions.net
[mailto:postgis-users-***@postgis.refractions.net] On Behalf Of Mark
Cave-Ayland
Sent: Tuesday, December 05, 2006 5:10 AM
To: PostGIS Users Discussion
Subject: Re: [postgis-users] Fast access to PostGIS from c/c++
Post by Boehm, Andreas
Hi!
Evaluating the possibilities of using PostGIS instead of ArcSDE, I'm
looking for a fast solution to insert and read points and polygons to
PostGIS from my c++ code.
I have tried several approaches with libpqxx. The only way to insert
points is executing a statement with WNT like "INSERT INTO points VALUES
(1, GeometryFromText('POINT (10 20)', -1) )". Either the tabelwriter
functionality (internally using COPY) nor predefined statements ("INSERT
INTO points VALUES ($1, GeometryFromText('POINT ($2 $3)', -1)" do work.
Hi Andi,

AFAICT using prepared statements in this way won't work because
GeometryFromText() takes a string argument and you are trying to
substitute values within the string parameter, rather than the parameter
itself.

In theory, COPY should be fastest method of inserting geometries since
it requires that the client should already have converted the geometry
to EWKB format ready for insertion. Documentation on the (E)WKB formats
can be found within the PostGIS source download.

If all your geometries are of one type, e.g. points, the second fastest
method would probably be to use a prepared statement using the
MakePoint() function and put as many points as possible within a single
transaction, e.g.

PREPARE myplan(int, double, double) AS INSERT INTO points VALUES ($1,
MakePoint($2, $3));
BEGIN;
..
..
EXECUTE myplan(1, 20, 30);
..
..
COMMIT;

Since committing a transaction incurs an extra write to the transaction
log, then placing as many points (if not all) within the same
transaction will greatly speed your insertion speed.

Finally, don't forget your basic PostgreSQL tuning - by default
out-of-the-box PostgreSQL only uses 8Mb of memory, so you'll need to
alter the postgresql.conf parameters for
shared_buffers/effective_cache_size to suit the amount of RAM in your
server and restart PostgreSQL.


Kind regards,

Mark.
Mark Cave-Ayland
2006-12-05 17:31:56 UTC
Permalink
Post by Chapman, Martin
Mark,
Just out of curiosity, you mention PostgreSQL tuning at the bottom of
your email. How will changing that setting help performance? What do
you set it at when you run your database.
Thanks,
Martin Chapman
SANZ Inc.
Software Developer
http://www.earthwhere.com
W - 303.495.6326
C - 303.898.0397
Hi Martin,

A lot :) I did post a very basic PostgreSQL/PostGIS tuning guide to the
list a while back
(http://postgis.refractions.net/pipermail/postgis-users/2006-March/011539.html) which should contain the basics.

Setting shared_buffers sets the amount of data PostgreSQL can cache
without spilling to disk, while effective_cache_size is used to
influence the planner by giving an idea of how much caching is performed
by the kernel.

You will see the biggest difference if you are using an application that
has high concurrency, large tables (into tens/hundreds of thousands of
rows) and large joins. For example, you could try issuing a "SELECT
COUNT(*) FROM mytable" a number of times and take the average before and
after changing the settings - you should see a noticeable difference. As
a rough guide, I would see pg_bench performance increase from ~ 70tps to
just under 400tps by using 40000 shared_buffers on a 12Gb Opteron
server.

Of course, things will vary depending upon your
storage/RAM/CPU/PostgreSQL version, so please feel free to post an
update if you find any of this information is out of date.


HTH,

Mark.
Chapman, Martin
2006-12-05 17:42:01 UTC
Permalink
Thanks a ton mark. I will check it out.

Martin Chapman
SANZ Inc.
Software Developer
http://www.earthwhere.com
W - 303.495.6326
C - 303.898.0397

-----Original Message-----
From: postgis-users-***@postgis.refractions.net
[mailto:postgis-users-***@postgis.refractions.net] On Behalf Of Mark
Cave-Ayland
Sent: Tuesday, December 05, 2006 10:32 AM
To: PostGIS Users Discussion
Subject: RE: [postgis-users] Fast access to PostGIS from c/c++
Post by Chapman, Martin
Mark,
Just out of curiosity, you mention PostgreSQL tuning at the bottom of
your email. How will changing that setting help performance? What do
you set it at when you run your database.
Thanks,
Martin Chapman
SANZ Inc.
Software Developer
http://www.earthwhere.com
W - 303.495.6326
C - 303.898.0397
Hi Martin,

A lot :) I did post a very basic PostgreSQL/PostGIS tuning guide to the
list a while back
(http://postgis.refractions.net/pipermail/postgis-users/2006-March/01153
9.html) which should contain the basics.

Setting shared_buffers sets the amount of data PostgreSQL can cache
without spilling to disk, while effective_cache_size is used to
influence the planner by giving an idea of how much caching is performed
by the kernel.

You will see the biggest difference if you are using an application that
has high concurrency, large tables (into tens/hundreds of thousands of
rows) and large joins. For example, you could try issuing a "SELECT
COUNT(*) FROM mytable" a number of times and take the average before and
after changing the settings - you should see a noticeable difference. As
a rough guide, I would see pg_bench performance increase from ~ 70tps to
just under 400tps by using 40000 shared_buffers on a 12Gb Opteron
server.

Of course, things will vary depending upon your
storage/RAM/CPU/PostgreSQL version, so please feel free to post an
update if you find any of this information is out of date.


HTH,

Mark.
Boehm, Andreas
2006-12-06 09:48:23 UTC
Permalink
Hi Mark,

thanks for your advice.
Post by Mark Cave-Ayland
If all your geometries are of one type, e.g. points, the
second fastest method would probably be to use a prepared
statement using the MakePoint() function [...]
Using libpqxx prepared functionality with MakePoint() works fine.
Unfortunately is slower than inserting with GeometryFromText().
Post by Mark Cave-Ayland
In theory, COPY should be fastest method of inserting
geometries since it requires that the client should already
have converted the geometry to EWKB format ready for
insertion.
I'll figure that out and post the results if somebody is interested in
it.

Regards
Andi
Mark Cave-Ayland
2006-12-06 11:07:48 UTC
Permalink
Post by Boehm, Andreas
Hi Mark,
thanks for your advice.
Post by Mark Cave-Ayland
If all your geometries are of one type, e.g. points, the
second fastest method would probably be to use a prepared
statement using the MakePoint() function [...]
Using libpqxx prepared functionality with MakePoint() works fine.
Unfortunately is slower than inserting with GeometryFromText().
Hi Andi,

*blinks* really?

Can you post your test cases for both MakePoint() and
GeometryFromText()? Over how many points are you testing? There are
several reasons why this shouldn't happen, including that all
GeometryFromText() input has to go through a bison-generated parser
which is not the case with MakePoint()...


Kind regards,

Mark.
Boehm, Andreas
2006-12-07 10:22:15 UTC
Permalink
Hi Mark,
Post by Mark Cave-Ayland
Can you post your test cases for both MakePoint() and
GeometryFromText()? Over how many points are you testing?
there are 3 different test cases
1) Inserting with executing "GeometryFromText()"
2) Inserting with executing "PREPARE insert_pt ..." and "EXECUTE" as you
have proposed
3) Inserting using libpqxx's prepared functionality

Case 1 and 2 are similar. No. 3 is the one witch takes about 60 % more
time.
I've made tests with 10,000, 100,000 and - for case 1 - with 1,000,000
points.
Until now I've left the database configuration untouched.

I'll post the complete source. Thanks a lot!
Andi


#include <iostream>
#include <time.h>
#include <assert.h>
#include <math.h>

#include <pqxx/connection>
#include <pqxx/transaction>
#include <pqxx/nontransaction>
#include <pqxx/tablewriter>
#include <pqxx/tablereader>

using namespace PGSTD;
using namespace pqxx;

// Table names
const string POINTS_TABLE = "points";

// Range and sizes
const int POINT_COUNT = 100000;
const int X_MAX = 10000;
const int Y_MAX = 10000;

// Forwards
void DropOldTable(connection &c, const string &tableName);
void CreateNewTable(connection &c, const string &tableName, const string
&geomType);
void CreateIndex(connection &c, const string &tableName);
clock_t PrintElapsedTime(clock_t start);

// Insertion
void InsertPointsFromTxt(connection &c)
{
cout << "Inserting " << to_string(POINT_COUNT)
<< " points via GeometryFromText()..." << endl;
clock_t start = clock();

// Insert POINT_COUNT points
work *t;
srand(42); // instead of "(int) time(NULL)" for
deterministically results
double x, y;
string execStr = "";
for (int i = 0; i < POINT_COUNT;)
{
// If string is to small, double the capacity
if (execStr.size() == execStr.capacity())
execStr.reserve(4 + execStr.capacity() * 2);

// RAND_MAX is 32767 only. For a huge number of points
use rand() twice
x = ((double) rand() + ((double) rand() / RAND_MAX))
/ (RAND_MAX + 1) * X_MAX;
y = ((double) rand() + ((double) rand() / RAND_MAX))
/ (RAND_MAX + 1) * Y_MAX;

// Add new Point
execStr += "INSERT INTO " + POINTS_TABLE + " VALUES ( "
+
to_string(i) + // Primary Key
", GeometryFromText('POINT (" +
to_string(x) + " " + to_string(y) + //
coordinates
")', -1) ); ";

// Exectute and commit every 10000 objects
if ((++i) % 10000 == 0)
{
t = new work(c, "insert_points");
t->exec(execStr);
t->commit();
delete t;
execStr = "";
cerr << ".";
}
}

// Execute the rest
if (execStr.length() > 0)
{
t = new work(c, "insert_points");
t->exec(execStr);
t->commit();
delete t;
}

cerr << "#"<<endl;
PrintElapsedTime(start);
}

void InsertPointsMakePtPrep(connection &c)
{
cout << "Inserting " << to_string(POINT_COUNT)
<< " points via GeometryMakePt() and PREPARE ..." <<
endl;
clock_t start = clock();

// Prepare plan
work *t = new work(c, "insert_points");
string execStr = "PREPARE insert_pt(int, float, float) AS INSERT
INTO " +
POINTS_TABLE + " VALUES ($1, MakePoint($2, $3))";
t->exec(execStr);
t->commit();
delete t;

// Insert POINT_COUNT points
srand(42); // instead of "(int) time(NULL)" for
deterministically results
double x, y;
execStr = "";
for (int i = 0; i < POINT_COUNT;)
{
// If string is to small, double the capacity
if (execStr.size() == execStr.capacity())
execStr.reserve(4 + execStr.capacity() * 2);

// RAND_MAX is 32767 only. For a huge number of points
use rand() twice
x = ((double) rand() + ((double) rand() / RAND_MAX))
/ (RAND_MAX + 1) * X_MAX;
y = ((double) rand() + ((double) rand() / RAND_MAX))
/ (RAND_MAX + 1) * Y_MAX;

// Add new Point
execStr += "EXECUTE insert_pt(" + to_string(i) + ","
+ to_string(x) + "," + to_string(y) + ");";

// Exectute and commit every 10000 objects
if ((++i) % 10000 == 0)
{
t = new work(c, "insert_points");
t->exec(execStr);
t->commit();
delete t;
execStr = "";
cerr << ".";
}
}

// Execute the rest, unprepare
t = new work(c, "insert_points");
t->exec(execStr + "DEALLOCATE insert_pt;");
t->commit();
delete t;

cerr << "#"<<endl;
PrintElapsedTime(start);
}

void InsertPointsMakePt(connection &c)
{
cout << "Inserting " << to_string(POINT_COUNT)
<< " points via MakePt() and libpqxx's prepared..." <<
endl;
clock_t start = clock();

// Prepare Statement
string prepNameWritePt = "InsertPoints";
string prepStr = "INSERT INTO " + POINTS_TABLE +
" VALUES ($1, MakePoint($2, $3))";
c.prepare(prepNameWritePt, prepStr)
("integer", pqxx::prepare::treat_direct)
("float", pqxx::prepare::treat_direct)
("float", pqxx::prepare::treat_direct);

// New transaction
work *t;
t = new work(c, "insert_points");

// Insert POINT_COUNT points
srand(42); // instead of "(int) time(NULL)" for
deterministically results
double x, y;
for (int i = 0; i < POINT_COUNT;)
{
// RAND_MAX is 32767 only. For a huge number of points
use rand() twice
x = ((double) rand() + ((double) rand() / RAND_MAX))
/ (RAND_MAX + 1) * X_MAX;
y = ((double) rand() + ((double) rand() / RAND_MAX))
/ (RAND_MAX + 1) * Y_MAX;
t->prepared(prepNameWritePt)(i)(x)(y).exec();

// every 10000 points start a new transaction
if (++i % 10000 == 0)
{
t->commit();
delete t;
t = new work(c, "insert_points");
cerr << ".";
}
}

// Commit, unprepare
t->commit();
delete t;
c.unprepare(prepNameWritePt);

cerr << "#"<<endl;
PrintElapsedTime(start);
}


// Main
int main(int argc, char *argv[])
{
try
{
// Set up a connection to the backend
connection c("host=geoserver port=5432 dbname=postgis\
user=postgres password=***");

// Creating Points using GeometryFromText()
// Drop old table, create new one, insert objects
cout << "*** Creating points using GeometryFromText ***"
<< endl;
DropOldTable(c, POINTS_TABLE);
CreateNewTable(c, POINTS_TABLE, "POINT");
CreateIndex(c, POINTS_TABLE);
InsertPointsFromTxt(c);

// Creating Points using InsertPointsMakePtPrep()
cout << "*** Creating points using
InsertPointsMakePtPrep ***" << endl;
DropOldTable(c, POINTS_TABLE);
CreateNewTable(c, POINTS_TABLE, "POINT");
CreateIndex(c, POINTS_TABLE);
InsertPointsMakePtPrep(c);

// Create points using MakePt()
cout << "*** Creating points using MakePt() ***" <<
endl;
DropOldTable(c, POINTS_TABLE);
CreateNewTable(c, POINTS_TABLE, "POINT");
CreateIndex(c, POINTS_TABLE);
InsertPointsMakePt(c);

cout << "*** End ***" << endl;
}
catch (const sql_error &e)
{
// If we're interested in the text of a failed query, we
can write
// separate exception handling code for this type of
exception
cerr << "SQL error: " << e.what() << endl
<< "Query was: '" << e.query() << "'" <<
endl;
return 1;
}
catch (const exception &e)
{
// All exceptions thrown by libpqxx are derived from
std::exception
cerr << "Exception: " << e.what() << endl;
return 2;
}
catch (...)
{
// This is really unexpected (see above)
cerr << "Unhandled exception" << endl;
return 100;
}

return 0;
}

// Helpers
void DropOldTable(connection &c, const string &tableName)
{
cout << "Dropping old table '" + tableName + "' ..." << endl;
clock_t start = clock();

try
{
// First create a separate transaction to drop old
table, if any.
// This mayfail if the table didn't previously exist.
work dropTrans(c, "drop_" + tableName);

// Using PostGIS delete statement instead of 'DROP
TABLE'.
// This will delete any geometry column before dropping
table.
dropTrans.exec("SELECT DropGeometryTable('" + tableName
+ "')");
dropTrans.commit();
}
catch (const undefined_table &e)
{
cout << "(Expected) Couldn't drop table: " << e.what()
<< endl
<< "Query was: " << e.query() << endl;
}
catch (const sql_error &e)
{
cerr << "Couldn't drop table: " << e.what() << endl
<< "Query was: " << e.query() << endl;
}

PrintElapsedTime(start);
}

void CreateNewTable(connection &c, const string &tableName,
const string &geomType)
{
cout << "Creating new table '" << tableName << "' ..." << endl;
clock_t start = clock();

// New transaction
work t(c, "create_" + tableName);

// Create table
t.exec("CREATE TABLE " + tableName + "(id integer PRIMARY KEY)");

// Add geometry column
t.exec("SELECT AddGeometryColumn('" + tableName +
"', 'geom', -1, '" + geomType + "', 2)");

// Commit
t.commit();

PrintElapsedTime(start);
}

void CreateIndex(connection &c, const string &tableName)
{
cout << "Creating index for '" << tableName << "' ..." << endl;
clock_t start = clock();

// Don't use a transaction, because CREATE INDEX could run into
a timeout.
// VACUUM cann't run in a transaction block, either.
// Ergo use nontransaction.
nontransaction t(c, "index_" + tableName);

t.exec("CREATE INDEX " + tableName + "_index ON " + tableName +
" USING GIST ( geom GIST_GEOMETRY_OPS )");
t.exec("VACUUM ANALYZE " + tableName + " (geom)");

// Commit
t.commit();

PrintElapsedTime(start);
}

clock_t PrintElapsedTime(clock_t start)
{
clock_t end = clock();
double durationClock = (double)(end - start) / CLOCKS_PER_SEC;
cout << "... elapsed time: " << durationClock << " s." << endl;
return end;
}
Mark Cave-Ayland
2006-12-07 11:29:06 UTC
Permalink
Post by Boehm, Andreas
Hi Mark,
Post by Mark Cave-Ayland
Can you post your test cases for both MakePoint() and
GeometryFromText()? Over how many points are you testing?
there are 3 different test cases
1) Inserting with executing "GeometryFromText()"
2) Inserting with executing "PREPARE insert_pt ..." and "EXECUTE" as you
have proposed
3) Inserting using libpqxx's prepared functionality
Case 1 and 2 are similar. No. 3 is the one witch takes about 60 % more
time.
I've made tests with 10,000, 100,000 and - for case 1 - with 1,000,000
points.
Until now I've left the database configuration untouched.
I'll post the complete source. Thanks a lot!
Andi
Hi Andi,

IANAC++P (I Am Not A C++ Programmer) but the following tips should help:


1) You mention that you haven't changed the database configuration. The
best thing you can do is set shared_buffers/effective_cache_size as
mentioned in my previous email in this thread to Martin - otherwise
PostgreSQL *will* swap excessively to disk. This is the single most
important thing you can do to obtain better performance.

2) In InsertPointsFromTxt() and InsertPointsMakePt(), you are building
all 10,000 points into a single string and submitting. I suspect you
would be better off submitting each individual execStr with t->exec()
rather than concatenating all 10,000 into one long string.

3) Don't create any indexes on the table/VACUUM ANALYZE until after you
have inserted all your points, i.e. insert the points as soon as you
have created the table. Otherwise you waste I/O manipulating the index
rather than inserting your data.

4) As a note, rand() can be slow on some platforms.

5) Instead of executing your SQL statements directly over the network,
try and append them into one large text file. You can then run this into
psql using the -f parameter on the server itself to determine whether
any speed problems are related to network latency.


As a rough guide to performance you should be seeing, for me it was
possible to get over 10,000 points per second inserted into a table on
old 1.1GHz PIII hardware with a single 36GB SCSI disk executing
pre-generated text files with psql -f.

Finally, make sure you are using the latest and greatest
PostgreSQL/PostGIS - at the very minimum you should be aiming for
PostgreSQL 8.1 and PostGIS 1.1, preferably the latest releases of each
to ensure you have all the latest bugfixes. There is often a speed gain
of between 10-20% just by upgrading to the latest PostgreSQL version.


HTH,

Mark.
Markus Schaber
2006-12-08 16:40:04 UTC
Permalink
Hi, Andreas,
Post by Boehm, Andreas
I have tried several approaches with libpqxx. The only way to insert
points is executing a statement with WNT like "INSERT INTO points VALUES
(1, GeometryFromText('POINT (10 20)', -1) )". Either the tabelwriter
functionality (internally using COPY) nor predefined statements ("INSERT
INTO points VALUES ($1, GeometryFromText('POINT ($2 $3)', -1)" do work.
Why does the Tablewriter not work? Can you give us the code example, and
the error message? We've used COPY to fill lots of geometry tables over
here.

And for predefined statements, the following ones should work:

- INSERT INTO points VALUES($1, GeometryFromText('POINT('||$1||'
'||$2||')', -1)

- INSERT INTO points VALUES($1, makePoint($2, $3))

- INSERT INTO points VALUES($1, $2)
and giving the 'POINT(0 0)' representation as String in $2. (or use hexWKB).


I'd expect the last one to be the fastest, because the insert does not
contain any functions, which allows PostgreSQL to shortcut some code pathes.
Post by Boehm, Andreas
There seems to be a binary interface for ArcSDE. So I wonder if PostGIS
would be fast enough. Could it make sense to convert geometries into WKB
by myself on client side?
Yes. I don't know the exact libpqxx interface for TableWriter and
prepared Statements, but you can always send WKB to PostgreSQL when you
select binary parameter transfer, and hexed WBK when you selected text
parameter transfer (which is what pg_dump and psql do).

HTH,
Markus
--
Markus Schaber | Logical Tracking&Tracing International AG
Dipl. Inf. | Software Development GIS

Fight against software patents in Europe! www.ffii.org
www.nosoftwarepatents.org
Boehm, Andreas
2006-12-12 13:02:30 UTC
Permalink
Hi Markus,

sorry to be slow in replying. I got distracted by other issues
yesterday.
Post by Markus Schaber
Why does the Tablewriter not work? Can you give us the code
example, and the error message? We've used COPY to fill lots
of geometry tables over here.
Here is my code to insert points with tablewriter:

void InsertPointsTableWriter(connection &c)
{
work *t = new work(c, "insert_points");
tablewriter *w = new tablewriter(*t, POINTS_TABLE);
vector<string> myData;
myData.resize(2);
srand(42);
double x, y;
for (int i = 0; i < POINT_COUNT;)
{
x = ((double) rand() + ((double) rand() / RAND_MAX))
/ (RAND_MAX + 1) * X_MAX;
y = ((double) rand() + ((double) rand() / RAND_MAX))
/ (RAND_MAX + 1) * Y_MAX;

myData[0] = to_string(i);
myData[1] = "GeometryFromText('POINT(" + to_string(x) + " "
+ to_string(y) + ")', -1)";
w->insert(myData);

// every 10000 points complete writing, start a new transaction
if (++i % 10000 == 0)
{
w->complete();
delete w;
t->commit();
delete t;
t = new work(c, "insert_points");
w = new tablewriter(*t, POINTS_TABLE);
cerr << ".";
}
}

// Complete writing
w->complete();
delete w;
t->commit();
delete t;
cerr << "#";
}

The error message is:
SQL error: ERROR: parse error - invalid geometry
CONTEXT: COPY points, line 1, column geom:
"GeometryFromText('POINT(122.072
9172.53)', -1)"
Query was: '[END COPY]'

Running "INSERT INTO points VALUES (1, GeometryFromText('POINT(122.072
9172.53)', -1));" in pgAdmin was sucsessful.

Regards,
Andi
Markus Schaber
2006-12-12 13:44:21 UTC
Permalink
Hi, Andreas,
Post by Boehm, Andreas
myData[1] = "GeometryFromText('POINT(" + to_string(x) + " "
+ to_string(y) + ")', -1)";
[...]
Post by Boehm, Andreas
SQL error: ERROR: parse error - invalid geometry
"GeometryFromText('POINT(122.072
9172.53)', -1)"
The problem here is that you insert the GeometryFromText call as String
parameter.

The PostgreSQL serser prepares the COPY command semantically equal to
the following INSERT statement: "INSERT INTO points VALUES ($1,$2)"
and $2 is the text "GeometryFromText('POINT(122.072 9172.53)', -1)"
which is not a valid geometry representation.

PostgreSQL tries to parse the contents of a COPY datastream directly
into the data columns, it does not handle function calls like
GeometryFromText inside of a copy stream.

Try the following change in your code:
myData[1] = "POINT(" + to_string(x) + " " + to_string(y) + ")";
Post by Boehm, Andreas
Running "INSERT INTO points VALUES (1, GeometryFromText('POINT(122.072
9172.53)', -1));" in pgAdmin was sucsessful.
Yes, that's what expected, but not equivalent to what you did above,
which equals to:

"INSERT INTO points VALUES (1, 'GeometryFromText(''POINT(122.072
9172.53)'', -1)' );"


HTH,
Markus

Loading...