Version 2.1 of Zild Database Library is available:
Download: http://www.tildeslash.com/libzdb/dist/libzdb-2.1.tar.gz MD5 checksum: 5abec56051f105cacb6dcec237a1d989 Change log: http://www.tildeslash.com/libzdb/dist/CHANGES.txt
This is a feature release which has been tested on the following platforms:
Darwin 9.1.0 [ppc/x86] Linux Ubuntu [x86]
Changes in this release:
Version 2.1 -----------
* API: Connection_ping is promoted to a public method. Clients can use this method to test if a Connection is alive.
* API: Connection_clear() is exposed as a public method. Normally it is not necessary to call this method, but in some situations, if you use PreparedStatement_executeQuery it is necessary to call this function to close a prepared statement explicit. Basically, if you see this SQLite error, "SQL statements in progress", call this function to close any previous opened statements before proceeding.
* Upon returning a Connection, the Connection Pool previously tried to commit a Connection if it was in a non-committed state, while now it rollback instead, which is assumed to be more correct.
* Improved retry when SQLite report database lock, which should reduce the chance to meet the infamous SQLite "database is locked" error
* Removed section 3 from "Tildeslash License Exception" so libzdb is licensed clear and permissive.
* Internal optimizing changes
Jan-Hendrik
Given the change in the license from gplv2-with-exceptions to gplv3 I've decided to start using libzdb in my dbmail project.
However, I've also filed an ITP bug for debian which means I intent to package libzdb for inclusion in debian and have it uploaded to debian.org's ftp servers.
From reading the EXCEPTIONS file I assume that would be acceptable for
you, but I'd appreciate your input and confirmation.
thanks,
Jan-Henrik Haukeland wrote:
Version 2.1 of Zild Database Library is available:
Download: http://www.tildeslash.com/libzdb/dist/libzdb-2.1.tar.gz MD5 checksum: 5abec56051f105cacb6dcec237a1d989 Change log: http://www.tildeslash.com/libzdb/dist/CHANGES.txt
This is a feature release which has been tested on the following platforms:
Darwin 9.1.0 [ppc/x86] Linux Ubuntu [x86]
Changes in this release:
Version 2.1
API: Connection_ping is promoted to a public method. Clients can use this method to test if a Connection is alive.
API: Connection_clear() is exposed as a public method. Normally it is not necessary to call this method, but in some situations, if you use PreparedStatement_executeQuery it is necessary to call this function to close a prepared statement explicit. Basically, if you see this SQLite error, "SQL statements in progress", call this function to close any previous opened statements before proceeding.
Upon returning a Connection, the Connection Pool previously tried to commit a Connection if it was in a non-committed state, while now it rollback instead, which is assumed to be more correct.
Improved retry when SQLite report database lock, which should reduce the chance to meet the infamous SQLite "database is locked" error
Removed section 3 from "Tildeslash License Exception" so libzdb is licensed clear and permissive.
Internal optimizing changes
-- To unsubscribe: http://www.tildeslash.com/mailman/listinfo/libzdb-general
On 20. feb.. 2008, at 21.58, Paul J Stevens wrote:
Given the change in the license from gplv2-with-exceptions to gplv3 I've decided to start using libzdb in my dbmail project.
Great, its really good to have more projects using libzdb. It can only make the library better. When implementing libzdb in dbmail, if you need any help or need any changes to libzdb do let me know.
However, I've also filed an ITP bug for debian which means I intent to package libzdb for inclusion in debian and have it uploaded to debian.org's ftp servers.
From reading the EXCEPTIONS file I assume that would be acceptable for you, but I'd appreciate your input and confirmation.
The exceptions to the GPL is only there to make distribution and usage of libzdb even more permissive, while still retaining some protection by the GPL, so I have absolutely no problem with that, and I appreciate the effort. We support debian and already have another package, monit, distributed with debian.
Good luck with using libzdb and let me know how it goes.
Jan-Henrik
Jan-Henrik,
Thanks for your reply.
One of the things that I'm still unsure about is string escaping on untrusted input values.
dbmail does a lot of 'INSERT INTO table (f1,f2,f3) VALUES (long, long, _escaped_string_)
where the escaped strings are always put through the proper string escaping calls in the client lib of the selected backend.
As I understand things, for inserts this can be solved by using prepared statements (which is part of what I really like about libzdb).
But how about:
SELECT * FROM table WHERE f1 LIKE '%somestring%';
where again, somestring is totally untrusted user input.
can I rewrite that code to do
SELECT * FROM table WHERE f1 LIKE ?
and bind the argument to somestring with the globbing char tacked onto both ends?
Anyway, it looks valid enough. Just havent gotten around to testing it. I'll be busy adapting the internal db-api to align with libzdb's, but it's totally straightforward. Just a lot of changes because dbmail uses/used num_rows() a lot, and because dbmail uses one single global connector pointer (yuk).
I've already packaged libzdb, just need to convince my dbmail debian sponsor to help me out on this package as well. As soon as it's uploaded to unstable I'll notify you.
deb http://debian.nfgd.net/debian unstable main
kind regards,
On 21. feb.. 2008, at 09.23, Paul J Stevens wrote:
One of the things that I'm still unsure about is string escaping on untrusted input values.
dbmail does a lot of 'INSERT INTO table (f1,f2,f3) VALUES (long, long, _escaped_string_)
where the escaped strings are always put through the proper string escaping calls in the client lib of the selected backend.
As I understand things, for inserts this can be solved by using prepared statements (which is part of what I really like about libzdb).
Yes, the right way to do this is to use a prepared statement. This has been the general case since prepared statement was invented. It is the only way to be really safe against sql injection. And you do not have to escape a string used in a prepared statement.
However, if you control the SQL string yourself you should use Connection_execute(Query) instead as it is more lightweight.
But how about:
SELECT * FROM table WHERE f1 LIKE '%somestring%';
where again, somestring is totally untrusted user input.
can I rewrite that code to do
SELECT * FROM table WHERE f1 LIKE ?
and bind the argument to somestring with the globbing char tacked onto both ends?
Yes, something like this,
PreparedStatement_T p = Connection_prepareStatement(con, "select * from table where f1 like ?;"); PreparedStatement_setString(p, 1, "%blabla%"); ResultSet_T result = PreparedStatement_executeQuery(p);
There is a minor quirk though with SQLite when using PreparedStatement_executeQuery(), if the Connection is used for further statements before it is returned to the pool, you must call Connection_clear() as stated in the latest change log.
* API: Connection_clear() is exposed as a public method. Normally it is not necessary to call this method, but in some situations, if you use PreparedStatement_executeQuery it is necessary to call this function to close a prepared statement explicit. Basically, if you see this SQLite error, "SQL statements in progress", call this function to close any previous opened statements before proceeding.
Further releases my refactor away this quirk.
Jan-Henrik Haukeland wrote:
There is a minor quirk though with SQLite when using PreparedStatement_executeQuery(), if the Connection is used for further statements before it is returned to the pool, you must call Connection_clear() as stated in the latest change log.
- API: Connection_clear() is exposed as a public method. Normally it is not necessary to call this method, but in some situations, if you use PreparedStatement_executeQuery it is necessary to call this function to close a prepared statement explicit. Basically, if you see this SQLite error, "SQL statements in progress", call this function to close any previous opened statements before proceeding.
I've been thinking about this. This implies that you can't use multiple statements within a single transaction (at least with sqlite). Is that correct? Because that would make code like below invalid, right?
PreparedStatement_T s1, s2; ResultSet_T r;
s1 = Connection_PrepareStatement("select data from table where id=?"); s2 = Connection_PrepareStatement("update table set data=? where id=?");
while (iterator) { PreparedStatement_setInt(s1, 1, 1000); r = PreparedStatement_executeQuery(s1); ... if (condition) { PreparedStatement_setInt(s2,1,1000); PreparedStatement_setString(s2,2,"new data"); PreparedStatement_execute(s2); ... } }
On 2. mars. 2008, at 22.09, Paul J Stevens wrote:
I've been thinking about this. This implies that you can't use multiple statements within a single transaction (at least with sqlite). Is that correct? Because that would make code like below invalid, right?
Not necessarily, though a fair chance that it will not work. The indicator is this SQLite error message "SQL statements in progress".
Jan-Henrik Haukeland wrote:
On 2. mars. 2008, at 22.09, Paul J Stevens wrote:
I've been thinking about this. This implies that you can't use multiple statements within a single transaction (at least with sqlite). Is that correct? Because that would make code like below invalid, right?
Not necessarily, though a fair chance that it will not work. The indicator is this SQLite error message "SQL statements in progress".
I am aware of that caveat in the docs. Is that a limitation of sqlite per se? Or rather how libzdb talks to sqlite?
The reason I'm asking is because I'm still trying to get a feel for libzdb's behaviour and implicit expectations. A few things I've noticed:
- I can't use Connection_PreparedStatement on a postgresql connection until I've issued a Connection_executeQuery. Doing so segfaults in libzdb. I've reported this one already, and I can easily work around this.
- I'm getting stack corruption and segfaults in certain hotspots in my code that use glib's GObject interface throught the GMime code. Now is GObject a known not-threadsafe interface, and putting mutexes around my calls solves the segfaults. But thing is: my code is (still) single-threaded (apart from the reaper thread). So apparently thread semantics apply even to single-threaded apps when using libzdb. If that is true, I need to start studying on threads pronto because I havent a clue as to how thread(un)safe my code is at the moment.
- Valgrind is generating massive amounts of 'invalid read' warnings in the ResultSet_getXXX code. Whether this results in segfaults is dependent on the backend. The mysql driver appears to segfault where the postgresql driver simply returns null values. I havent yet tested sqlite. I'm left wondering if this too is thread related.
However, since I only just finished the code changes that de-globalize the result and connection pointers (leaving only the pool pointer as a global) I havent had enough time yet to do some thorough backtracking.
keep you posted.
On 3. mars. 2008, at 09.44, Paul J Stevens wrote:
Jan-Henrik Haukeland wrote:
On 2. mars. 2008, at 22.09, Paul J Stevens wrote:
I've been thinking about this. This implies that you can't use multiple statements within a single transaction (at least with sqlite). Is that correct? Because that would make code like below invalid, right?
Not necessarily, though a fair chance that it will not work. The indicator is this SQLite error message "SQL statements in progress".
I am aware of that caveat in the docs. Is that a limitation of sqlite per se? Or rather how libzdb talks to sqlite?
I believe it is a combination. Using PreparedStatement_executeQuery() the prepared statement is reused in the ResultSet and not closed. One place where this is a problem is in an explicit transaction. Upon a commit or rollback, at least SQLite check for open statements and issue a "SQL statements in progress" error, unless Connection_clear() is called to explicit close the statement.
For Connection_execute() and Connection_executeQuery() which also use a statement internally, the statement is closed upon the next call to any of these methods and therefor does not have this problem. But for PreparedStatement_executeQuery() the same internal statement is reused, and since a prepared statement is longer lived [1] than a ResultSet it may be a problem. A better option than to reuse, is probably to create a new statement for the ResultSet when PreparedStatement_executeQuery() is called, though this need more research which I haven't had time to prioritize. Until then, a workaround is available by calling Connection_clear() at appropriate places to close prepared statements explicit.
The reason I'm asking is because I'm still trying to get a feel for libzdb's behaviour and implicit expectations. A few things I've noticed:
- I can't use Connection_PreparedStatement on a postgresql
connection until I've issued a Connection_executeQuery. Doing so segfaults in libzdb. I've reported this one already, and I can easily work around this.
Its on my TODO list and I hope to have it moved over to Martin's TODO list :)
- I'm getting stack corruption and segfaults in certain hotspots in
my code that use glib's GObject interface throught the GMime code. Now is GObject a known not-threadsafe interface, and putting mutexes around my calls solves the segfaults. But thing is: my code is (still) single-threaded (apart from the reaper thread). So apparently thread semantics apply even to single- threaded apps when using libzdb. If that is true, I need to start studying on threads pronto because I havent a clue as to how thread(un)safe my code is at the moment.
Linking with pthread makes your application threaded, per def, *but* as long as you only use one thread, i.e. the application main-thread you should not need to consider yourself with tread-safeness nor synchronizing shared resources. This is also the case if you run libzdb with a reaper thread since it does not affect any of your application data structures.
Thread-safe is one thing, reentrant is another and since I know you are using libevent, you may also want to check your event handlers. Even a single threaded application may have reentrancy problems - think signal handlers, and using libevent may introduce reentrancy problems in your code which may look like a thread bug. Maybe linking with pthread/libzdb exposed these bugs, but I don't think its the root cause.
- Valgrind is generating massive amounts of 'invalid read' warnings
in the ResultSet_getXXX code. Whether this results in segfaults is dependent on the backend. The mysql driver appears to segfault where the postgresql driver simply returns null values. I havent yet tested sqlite. I'm left wondering if this too is thread related.
Well, this is unexpected. Not that valgrind issues lots of warnings, but that you get SIGSEGV from using ResultSet_getXXX. Sounds like a Heisenbug (http://en.wikipedia.org/wiki/Heisenbug) someplace. Hopefully not in libzdb. One thing to check is PreparedStatement_setXXX. String or blob values set in a PreparedStatement_setXXX method are set by *reference* and if those references has disappeared somehow before doing a PreparedStatement_execute.. there will be Heisenbugs.
However, since I only just finished the code changes that de- globalize the result and connection pointers (leaving only the pool pointer as a global) I havent had enough time yet to do some thorough backtracking.
keep you posted.
Appreciated!
[1] A prepared statement is closed when its parent Connection is returned to the pool. Though for efficiency it should live longer. libzdb should/may in the future internalize and cache them. I have not verified this, but I hope the database backends already does this.
Jan-Henrik Haukeland wrote:
- I can't use Connection_PreparedStatement on a postgresql
connection until I've issued a Connection_executeQuery. Doing so segfaults in libzdb. I've reported this one already, and I can easily work around this.
Its on my TODO list and I hope to have it moved over to Martin's TODO list :)
Hi,
the problem should be fixed ... postgresql returned PGRES_EMPTY_QUERY for PQprepare() whereas libzdb expected PGRES_COMMAND_OK or PGRES_TUPLES_OK. I have checked the postgresql code and it seems that PGRES_EMPTY_QUERY is not error in this context - the prepared statement is processed fine. I have modified the code to check for result (C->res) of PQprepare() and accept the PGRES_EMPTY_QUERY.
Note that to prevent the uncaught exception like this:
--8<-- Uncaught exception SQLException raised in Connection_prepareStatement at src/db/Connection.c:294 --8<--
you should modify your code using TRY-CATCH-END_TRY this way:
--8<-- TRY { PreparedStatement_T p = Connection_prepareStatement(con, "SELECT * FROM dbmail_users WHERE userid=?;"); if (! p) printf("[%s]", Connection_getLastError(con)); assert(p); } CATCH(SQLException) { printf("\tResult: prepare statement failed -- %s\n", Connection_getLastError(con)); } END_TRY; --8<--
This way you will get the the error message printed out and the program won't crash.
The patch is in the cvs and attachment as well.
Cheers, Martin
Index: src/db/postgresql/PostgresqlConnection.c =================================================================== RCS file: /cvsroot/libzdb/src/db/postgresql/PostgresqlConnection.c,v retrieving revision 1.15 retrieving revision 1.17 diff -u -r1.15 -r1.17 --- src/db/postgresql/PostgresqlConnection.c 23 Feb 2008 19:57:28 -0000 1.15 +++ src/db/postgresql/PostgresqlConnection.c 3 Mar 2008 22:34:30 -0000 1.17 @@ -40,7 +40,7 @@ /** * Implementation of the Connection/Strategy interface for postgresql. * - * @version $Id: PostgresqlConnection.c,v 1.15 2008/02/23 19:57:28 hauk Exp $ + * @version $Id: PostgresqlConnection.c,v 1.17 2008/03/03 22:34:30 martinp Exp $ * @file */
@@ -249,9 +249,12 @@ /* Get the unique prepared statement ID */ name = Util_getString("%d", rand()); PQclear(C->res); - C->res = PQprepare(C->db, name, stmt, prm1, NULL); + C->res = PQprepare(C->db, name, stmt, 0, NULL); FREE(stmt); - if (C->lastError == PGRES_COMMAND_OK || C->lastError == PGRES_TUPLES_OK) { + if (C->res && + (C->lastError == PGRES_EMPTY_QUERY || + C->lastError == PGRES_COMMAND_OK || + C->lastError == PGRES_TUPLES_OK)) { return PreparedStatement_new(PostgresqlPreparedStatement_new(C->db, C->maxRows, name,
Martin,
your patch didn't quit apply to libzdb-2.1 but I'll test it and include it with the debian package. Is the cvs repo for libzdb publicly accessible?
you should modify your code using TRY-CATCH-END_TRY this way:
I know, and I'm working on that right now.
thanks for the fix.
I'm sorry, the patch was for cvs version ... there were c style changes since libzdb-2.1.
In the attachment is the new patch for libzdb-2.1
Martin
Paul J Stevens wrote:
Martin,
your patch didn't quit apply to libzdb-2.1 but I'll test it and include it with the debian package. Is the cvs repo for libzdb publicly accessible?
you should modify your code using TRY-CATCH-END_TRY this way:
I know, and I'm working on that right now.
thanks for the fix.
Index: src/db/postgresql/PostgresqlConnection.c =================================================================== RCS file: /cvsroot/libzdb/src/db/postgresql/PostgresqlConnection.c,v retrieving revision 1.13 retrieving revision 1.17 diff -u -r1.13 -r1.17 --- src/db/postgresql/PostgresqlConnection.c 3 Jan 2008 17:26:05 -0000 1.13 +++ src/db/postgresql/PostgresqlConnection.c 3 Mar 2008 22:34:30 -0000 1.17 @@ -40,7 +40,7 @@ /** * Implementation of the Connection/Strategy interface for postgresql. * - * @version $Id: PostgresqlConnection.c,v 1.13 2008/01/03 17:26:05 hauk Exp $ + * @version $Id: PostgresqlConnection.c,v 1.17 2008/03/03 22:34:30 martinp Exp $ * @file */
@@ -48,7 +48,7 @@ /* ----------------------------------------------------------- Definitions */
-const struct conop postgresqlconops= { +const struct conop postgresqlconops = { "postgresql", PostgresqlConnection_new, PostgresqlConnection_free, @@ -98,13 +98,13 @@ PGconn *db; assert(url); assert(error); - if(! (db= doConnect(url, error))) + if (! (db = doConnect(url, error))) return NULL; NEW(C); - C->db= db; - C->res= NULL; - C->url= url; - C->sb= StringBuffer_new(""); + C->db = db; + C->res = NULL; + C->url = url; + C->sb = StringBuffer_new(""); return C; }
@@ -120,13 +120,13 @@
void PostgresqlConnection_setQueryTimeout(T C, int ms) { assert(C); - C->timeout= ms; + C->timeout = ms; }
void PostgresqlConnection_setMaxRows(T C, int max) { assert(C); - C->maxRows= max; + C->maxRows = max; }
@@ -140,8 +140,8 @@ int PostgresqlConnection_beginTransaction(T C) { PGresult *res; assert(C); - res= PQexec(C->db, "BEGIN TRANSACTION;"); - C->lastError= PQresultStatus(res); + res = PQexec(C->db, "BEGIN TRANSACTION;"); + C->lastError = PQresultStatus(res); PQclear(res); return (C->lastError == PGRES_COMMAND_OK); } @@ -150,8 +150,8 @@ int PostgresqlConnection_commit(T C) { PGresult *res; assert(C); - res= PQexec(C->db, "COMMIT TRANSACTION;"); - C->lastError= PQresultStatus(res); + res = PQexec(C->db, "COMMIT TRANSACTION;"); + C->lastError = PQresultStatus(res); PQclear(res); return (C->lastError == PGRES_COMMAND_OK); } @@ -160,8 +160,8 @@ int PostgresqlConnection_rollback(T C) { PGresult *res; assert(C); - res= PQexec(C->db, "ROLLBACK TRANSACTION;"); - C->lastError= PQresultStatus(res); + res = PQexec(C->db, "ROLLBACK TRANSACTION;"); + C->lastError = PQresultStatus(res); PQclear(res); return (C->lastError == PGRES_COMMAND_OK); } @@ -174,7 +174,7 @@
long long int PostgresqlConnection_rowsChanged(T C) { - int e = FALSE; + int e = false; assert(C); return(Util_parseLLong(PQcmdTuples(C->res), &e) && !e); } @@ -185,8 +185,8 @@ StringBuffer_clear(C->sb); StringBuffer_vappend(C->sb, sql, ap); PQclear(C->res); - C->res= PQexec(C->db, StringBuffer_toString(C->sb)); - C->lastError= PQresultStatus(C->res); + C->res = PQexec(C->db, StringBuffer_toString(C->sb)); + C->lastError = PQresultStatus(C->res); return (C->lastError == PGRES_COMMAND_OK); }
@@ -196,10 +196,10 @@ StringBuffer_clear(C->sb); StringBuffer_vappend(C->sb, sql, ap); PQclear(C->res); - C->res= PQexec(C->db, StringBuffer_toString(C->sb)); - C->lastError= PQresultStatus(C->res); - if(C->lastError == PGRES_TUPLES_OK) { - return ResultSet_new(PostgresqlResultSet_new(C->res, C->maxRows, FALSE), + C->res = PQexec(C->db, StringBuffer_toString(C->sb)); + C->lastError = PQresultStatus(C->res); + if (C->lastError == PGRES_TUPLES_OK) { + return ResultSet_new(PostgresqlResultSet_new(C->res, C->maxRows, false), (Rop_T)&postgresqlrsetops); } return NULL; @@ -223,24 +223,24 @@ p = q = Util_strdup(sql); memset(index, 0, sizeof(index)); index[0] = (long)p; - while(prm1 < maxparam && (p= strchr(p, '?'))) { + while (prm1 < maxparam && (p = strchr(p, '?'))) { prm1++; *p = 0; p++; index[prm1] = (long)p; } - if(!prm1) { + if (!prm1) { stmt = Util_strdup(sql); - } else if(prm1 > maxparam) { + } else if (prm1 > maxparam) { DEBUG("Prepared statement limit is %d parameters\n", maxparam); FREE(q); return NULL; } else { len = strlen(sql) + prm1 * strlen(MAXPARAM + 1); stmt = CALLOC(1, len + 1); - while(prm2 <= prm1) { + while (prm2 <= prm1) { sprintf(stmt + strlen(stmt), "%s", (char *)index[prm2]); - if(prm2 < prm1) + if (prm2 < prm1) sprintf(stmt + strlen(stmt), "$%d", prm2 + 1); prm2++; } @@ -249,9 +249,12 @@ /* Get the unique prepared statement ID */ name = Util_getString("%d", rand()); PQclear(C->res); - C->res = PQprepare(C->db, name, stmt, prm1, NULL); + C->res = PQprepare(C->db, name, stmt, 0, NULL); FREE(stmt); - if(C->lastError == PGRES_COMMAND_OK || C->lastError == PGRES_TUPLES_OK) { + if (C->res && + (C->lastError == PGRES_EMPTY_QUERY || + C->lastError == PGRES_COMMAND_OK || + C->lastError == PGRES_TUPLES_OK)) { return PreparedStatement_new(PostgresqlPreparedStatement_new(C->db, C->maxRows, name, @@ -275,33 +278,33 @@
static PGconn *doConnect(URL_T url, char **error) { -#define ERROR(e) do {*error= Util_strdup(e); goto error;} while(0) +#define ERROR(e) do {*error = Util_strdup(e); goto error;} while (0) int port; - int ssl = FALSE; + int ssl = false; int connectTimeout = SQL_DEFAULT_TCP_TIMEOUT; const char *user, *password, *host, *database, *timeout; char *conninfo; PGconn *db = NULL; - if(!(user= URL_getUser(url))) - if(!(user= URL_getParameter(url, "user"))) + if (!(user = URL_getUser(url))) + if (!(user = URL_getParameter(url, "user"))) ERROR("no username specified in URL"); - if(!(password= URL_getPassword(url))) - if(!(password= URL_getParameter(url, "password"))) + if (!(password = URL_getPassword(url))) + if (!(password = URL_getParameter(url, "password"))) ERROR("no password specified in URL"); - if(!(host= URL_getHost(url))) + if (!(host = URL_getHost(url))) ERROR("no host specified in URL"); - if((port= URL_getPort(url))<=0) + if ((port = URL_getPort(url))<=0) ERROR("no port specified in URL"); - if(!(database= URL_getPath(url))) + if (!(database = URL_getPath(url))) ERROR("no database specified in URL"); else database++; - if(IS(URL_getParameter(url, "use-ssl"), "true")) - ssl= TRUE; - if((timeout= URL_getParameter(url, "connect-timeout"))) { - int e= FALSE; + if (IS(URL_getParameter(url, "use-ssl"), "true")) + ssl = true; + if ((timeout = URL_getParameter(url, "connect-timeout"))) { + int e = false; connectTimeout = Util_parseInt(timeout, &e); - if(connectTimeout<=0 || e) ERROR("invalid connect timeout value"); + if (connectTimeout<=0 || e) ERROR("invalid connect timeout value"); } conninfo = Util_getString(" host='%s'" " port=%d" @@ -321,8 +324,8 @@ ); db = PQconnectdb(conninfo); FREE(conninfo); - if(PQstatus(db) != CONNECTION_OK) { - *error= Util_getString("%s", PQerrorMessage(db)); + if (PQstatus(db) != CONNECTION_OK) { + *error = Util_getString("%s", PQerrorMessage(db)); goto error; } return db;