libpq-fe: how to determine unique collision ?

Started by Marc SCHAEFERover 25 years ago4 messagesgeneral
Jump to latest
#1Marc SCHAEFER
schaefer@alphanet.ch

Hi,

I am using libpq (with postgreSQL Version: 6.5.3-23 (Debian 2.2r2)). I
have the following questions which, I am afraid, are not answered in the
documentation:

- how do you detect the error `Can't insert tuple because duplicate' ?

- how do you get the OID of an insertion (the goal being to get
the value for the SERIAL type, in order to do something with it) ?

Thank you.

Example:
CREATE TABLE file (id SERIAL,
creation_time TIMESTAMP
NOT NULL DEFAULT CURRENT_TIMESTAMP,
last_access_time TIMESTAMP, /* read some data */
file_name VARCHAR(100) NOT NULL, /* see servers_defs.h */
client_name VARCHAR(30) NOT NULL, /* from auth */
server_name VARCHAR(30) NOT NULL, /* from auth */
UNIQUE(client_name, server_name, file_name),
UNIQUE(id), PRIMARY KEY(id));

#include "libpq-fe.h"

#define BUFFER_LENGTH 1024 /* Is boundary-checked */
BOOL db_create_file_tuple(db_context_t context,
const char *file_name,
const char *client_name,
const char *server_name,
db_error_t *error) {
char buffer[BUFFER_LENGTH];
BOOL result = FALSE;
PGconn *conn = (PGconn *) context;

*error = DB_ERROR_INTERNAL;

if (snprintf(buffer,
sizeof(buffer),
"INSERT INTO file (file_name, client_name, server_name)"
" VALUES('%s', '%s', '%s')",
file_name,
client_name,
server_name)
== -1) {
*error = DB_ERROR_WOULD_OVERFLOW;
}
else {
PGresult *res;

/* BUGS
* - Should protect or bind like in Perl.
*/

res = PQexec(conn, buffer);
if (res && (PQresultStatus(res) == PGRES_COMMAND_OK)) {
result = TRUE;
*error = DB_ERROR_NONE; /* not really useful, shouldn't be read */
}
else {
debug_log(DEBUG_LEVEL_ERROR,
"INSERT query failed: %s",
PQerrorMessage(conn));

/* BUGS
* - We are not sure this is it, but we can only guess
* for now.
*/
*error = DB_ERROR_FILE_OBJECT_EXISTS;
}

PQclear(res); /* avoid memory leaks */
}

return result;
}

#2Peter Eisentraut
peter_e@gmx.net
In reply to: Marc SCHAEFER (#1)
Re: libpq-fe: how to determine unique collision ?

Marc SCHAEFER writes:

- how do you detect the error `Can't insert tuple because duplicate' ?

if (strcmp(PQerrorMessage(conn), "ERROR: Can't insert tuple because duplicate") == 0) {
panic();
}

I know it's not ideal, but it's about the best you can do.

- how do you get the OID of an insertion

PQoidValue()

(the goal being to get
the value for the SERIAL type, in order to do something with it) ?

Serial values and oids are not related.

--
Peter Eisentraut peter_e@gmx.net http://yi.org/peter-e/

#3Marc SCHAEFER
schaefer@alphanet.ch
In reply to: Peter Eisentraut (#2)
Re: libpq-fe: how to determine unique collision ?

On Thu, 4 Jan 2001, Peter Eisentraut wrote:

- how do you get the OID of an insertion

PQoidValue()

Thanks.

(the goal being to get
the value for the SERIAL type, in order to do something with it) ?

Serial values and oids are not related.

No, that right, but do you have a better way of doing the following ? :)

INSERT INTO some_table(name, surname) VALUES('marc', 'moua');
-> creates OID 37492374
SELECT id FROM some_table WHERE oid = 37492374;

assuming

CREATE TABLE some_table(id SERIAL,
name VARCHAR(8),
surname VARCHAR(8),
UNIQUE(id), PRIMARY KEY(id));

What the above does is basically retrieving the id that was created.

Of course you could do:

BEGIN TRANSACTION
SELECT next_val('some_table_id_sequence');
INSERT INTO some_table(id, name, surname)
VALUES(current_val('some_table_id_sequence'), 'marc', 'moua');
END TRANSACTION

Which one do you recommend, are they really equivalent, and do you have a
better way ? (hint: name and surname are not unique).

thank you (and, PostgreSQL is great!).

#4Tom Lane
tgl@sss.pgh.pa.us
In reply to: Marc SCHAEFER (#3)
Re: libpq-fe: how to determine unique collision ?

Marc SCHAEFER <schaefer@alphanet.ch> writes:

No, that right, but do you have a better way of doing the following ? :)

INSERT INTO some_table(name, surname) VALUES('marc', 'moua');
-> creates OID 37492374
SELECT id FROM some_table WHERE oid = 37492374;

This select will get pretty slow as the table gets large, unless you
make an index on its OID. Which you could do, but why pay the price
of maintaining an index just for this?

Of course you could do:
BEGIN TRANSACTION
SELECT next_val('some_table_id_sequence');
INSERT INTO some_table(id, name, surname)
VALUES(current_val('some_table_id_sequence'), 'marc', 'moua');
END TRANSACTION

This is the Right Way To Do It. You do not need the transaction
(because currval() is backend-private anyway). I'd not bother with
currval() at all, but just do

SELECT nextval('some_table_id_sequence');

-- this returns 4242, say

INSERT INTO some_table(id, name, surname)
VALUES(4242, 'marc', 'moua');

Simple, reliable, fast.

regards, tom lane