Thread: error handling

error handling

From
Verena Ruff
Date:
Hello,

I have a table with an UNIQUE constraint. Is it possible to have
something like a trigger (or error handler) which is called every time
a insert statement would break this constraint?  Or in some simple cases
that this record is just dropped silently, without reporting an error?

Tanks in advance,
Verena

Re: error handling

From
Terry Lee Tucker
Date:
On Thursday 27 April 2006 09:29 am, Verena Ruff <lists@triosolutions.at> thus
communicated:
--> Hello,
-->
--> I have a table with an UNIQUE constraint. Is it possible to have
--> something like a trigger (or error handler) which is called every time
--> a insert statement would break this constraint?  Or in some simple cases
--> that this record is just dropped silently, without reporting an error?
-->
--> Tanks in advance,
--> Verena
-->
--> ---------------------------(end of broadcast)---------------------------
--> TIP 6: explain analyze is your friend
-->
I believe that a UNIQUE constraint will stop the insert before any trigger
ever fires. Maybe someone else knows for sure.

Re: error handling

From
Oscar Rodriguez Fonseca
Date:
> --> I have a table with an UNIQUE constraint. Is it possible to have
> --> something like a trigger (or error handler) which is called every time
> --> a insert statement would break this constraint?  Or in some simple cases
> --> that this record is just dropped silently, without reporting an error?

> I believe that a UNIQUE constraint will stop the insert before any trigger
> ever fires. Maybe someone else knows for sure.

I do not know if there is an specific error handler built in postgresql (besides error-codes).

If you break the unique constraint an error will be thrown. A
workaround may be to launch a trigger BEFORE the insertion and do whatever is necessary within the trigger.

http://www.postgresql.org/docs/8.1/static/sql-createtrigger.html

--
Oscar

Re: error handling

From
Bruno Wolff III
Date:
On Thu, Apr 27, 2006 at 15:29:07 +0200,
  Verena Ruff <lists@triosolutions.at> wrote:
> Hello,
>
> I have a table with an UNIQUE constraint. Is it possible to have
> something like a trigger (or error handler) which is called every time
> a insert statement would break this constraint?  Or in some simple cases
> that this record is just dropped silently, without reporting an error?

Unique constraints are not deferrable in Postgres. It would be possible to
have a function do the insert and trap errors. I don't know if that will
help in your situation though.

Re: error handling

From
"Robert Wimmer"
Date:


>From: Bruno Wolff III <bruno@wolff.to>
>To: Verena Ruff <lists@triosolutions.at>
>CC: pgsql-novice@postgresql.org
>Subject: Re: [NOVICE] error handling
>Date: Thu, 27 Apr 2006 14:48:03 -0500
>
>On Thu, Apr 27, 2006 at 15:29:07 +0200,
>   Verena Ruff <lists@triosolutions.at> wrote:
> > Hello,
> >
> > I have a table with an UNIQUE constraint. Is it possible to have
> > something like a trigger (or error handler) which is called every time
> > a insert statement would break this constraint?  Or in some simple cases
> > that this record is just dropped silently, without reporting an error?
>
>Unique constraints are not deferrable in Postgres. It would be possible to
>have a function do the insert and trap errors. I don't know if that will
>help in your situation though.
>

In the following code i show two ways to handle errors in Postgres. The
first one is the simple version, the second one is very usefull in client
side programming.

besides : if there is some interest on this topic I could  write an article
about it ..

-------
-- error example
-------

DROP SCHEMA test CASCADE;
CREATE SCHEMA test;

CREATE TABLE test.ref(
  id INTEGER NOT NULL PRIMARY KEY
  );

CREATE TABLE test.test(
  id SERIAL NOT NULL PRIMARY KEY,
  name TEXT NOT NULL UNIQUE,
  ref_id INTEGER NOT NULL REFERENCES test.ref(id)
  );

CREATE OR REPLACE FUNCTION test.check_on_insert() RETURNS TRIGGER AS $$
BEGIN
  IF (NEW.name IS NULL) OR (CHAR_LENGTH(NEW.name) = 0) THEN  -- check the
not null constaint
    RAISE NOTICE 'name must not be null or empty';              -- only in the demo
version
    RETURN NULL;                                              -- don't insert
  END IF;

  IF EXISTS(SELECT * FROM test.test WHERE name = NEW.name) THEN  -- check
the unique constaint
    RAISE NOTICE 'value "%" not unique in table test',new.NAME;              -- it's
a demo
    RETURN NULL;
  END IF;

  IF NOT EXISTS(SELECT * FROM test.ref WHERE id = NEW.ref_id) THEN  -- check
foreign constaint
    RAISE NOTICE 'invalid ref_id "%"',NEW.ref_id;              -- it's a demo
    RETURN NULL;
  END IF;

  RETURN NEW;
END; $$
LANGUAGE plpgsql;

CREATE TRIGGER test_on_insert_trigger BEFORE INSERT ON test.test
FOR EACH ROW EXECUTE PROCEDURE test.check_on_insert();

INSERT INTO test.ref (id) VALUES(1);

INSERT INTO test.test(name,ref_id) VALUES('dummy',1);  -- will work
INSERT INTO test.test(name,ref_id) VALUES(NULL,1);
INSERT INTO test.test(name,ref_id) VALUES('',1);
INSERT INTO test.test(name,ref_id) VALUES('dummy',1);
INSERT INTO test.test(name,ref_id) VALUES ('joe',1);     -- will work
INSERT INTO test.test(name,ref_id) VALUES('mike',2);

SELECT * FROM test.test;

-- -----------------------
-- 'client friendly' version
-- -----------------------

DROP TRIGGER test_on_insert_trigger ON test.test;
DROP FUNCTION test.check_on_insert();

CREATE TABLE test.error(
  id SERIAL NOT NULL,
  message TEXT
  );

INSERT INTO test.error(id,message) VALUES(-1,'column "name" > value must not
be NULL or empty');
INSERT INTO test.error(id,message) VALUES(-2,'column "name" > value must be
UNIQUE');
INSERT INTO test.error(id,message) VALUES(-3,'column "ref_id" > invalid
reference');

-- check function

CREATE OR REPLACE FUNCTION test.client_check_on_insert(pname TEXT,pref_id
INTEGER) RETURNS INTEGER AS $$
BEGIN
  IF (pname IS NULL) OR (CHAR_LENGTH(pname) = 0) THEN  -- check the not null
constaint
    RETURN -1;
  END IF;

  IF EXISTS(SELECT * FROM test.test WHERE name = pname) THEN  -- check the
unique constaint
    RETURN -2;
  END IF;

  IF NOT EXISTS(SELECT * FROM test.ref WHERE id = pref_id) THEN  -- check
foreign constaint
    RETURN -3;
  END IF;

  RETURN 0;
END; $$
LANGUAGE plpgsql;

CREATE OR REPLACE FUNCTION test.check_on_insert() RETURNS TRIGGER AS $$
DECLARE ret INTEGER;
BEGIN
  ret := test.client_check_on_insert(NEW.name,NEW.ref_id);
  IF ret <> 0 THEN
    RAISE EXCEPTION '# %',ret;         -- mark these errors with "#"
  END IF;
  RETURN NEW;
END; $$
LANGUAGE plpgsql;

CREATE TRIGGER test_on_insert_trigger BEFORE INSERT ON test.test
FOR EACH ROW EXECUTE PROCEDURE test.check_on_insert();

INSERT INTO test.test(name,ref_id) VALUES('hans',1);  -- will work
INSERT INTO test.test(name,ref_id) VALUES(NULL,1);
INSERT INTO test.test(name,ref_id) VALUES('',1);
INSERT INTO test.test(name,ref_id) VALUES('joe',1);
INSERT INTO test.test(name,ref_id) VALUES('mike',2);

/*
you can now catch the error in a client program for example (java)

...
try {
  stmt.execute("INSERT INTO test.test(name,ref_id) VALUES('blabla',23)");
  }
catch (SQLException e) {
  if (e.SQLState.equals("P0001")) return parseError(e.SWLErrorMessage);
  .. otherwise
  }

*/



Re: error handling

From
Verena Ruff
Date:
Hello,

sorry for answering so late, ...
> I do not know if there is an specific error handler built in postgresql (besides error-codes).
>
> If you break the unique constraint an error will be thrown. A
> workaround may be to launch a trigger BEFORE the insertion and do whatever is necessary within the trigger.
>
This is possibillity I was thinking of, too. But if there are many
inserts and (probably) only a few of them would break the unique
constraint, there would be a great overhead. This was what I'd like to
avoid with a custom error handler.

Regards,
Verena



Re: error handling

From
Sean Davis
Date:


On 5/10/06 9:35 AM, "Verena Ruff" <lists@triosolutions.at> wrote:

> Hello,
>
> sorry for answering so late, ...
>> I do not know if there is an specific error handler built in postgresql
>> (besides error-codes).
>>
>> If you break the unique constraint an error will be thrown. A
>> workaround may be to launch a trigger BEFORE the insertion and do whatever is
>> necessary within the trigger.
>>
> This is possibillity I was thinking of, too. But if there are many
> inserts and (probably) only a few of them would break the unique
> constraint, there would be a great overhead. This was what I'd like to
> avoid with a custom error handler.

Probably the simplest way to do this is to load the data into a temporary
table without the unique constraint then use SQL to insert a "clean" version
into the new table.  Alternatively, you could use savepoints.  If an insert
fails, just rollback to that savepoint.  If it succeeds, commit that
savepoint.

Sean


Re: error handling

From
Verena Ruff
Date:
Hi,

Sean Davis schrieb:
> Probably the simplest way to do this is to load the data into a temporary
> table without the unique constraint then use SQL to insert a "clean" version
> into the new table.  Alternatively, you could use savepoints.  If an insert
> fails, just rollback to that savepoint.  If it succeeds, commit that
> savepoint.
>
But isn't that more overhead than using the trigger? And while inserting
into the clean table, I would have to test if the value is existing or
not. I don't see the advantage of this compared to the script which was
posted a few weeks ago.

Regards,
Verena

Re: error handling

From
Sean Davis
Date:


On 5/10/06 9:09 AM, "Verena Ruff" <lists@triosolutions.at> wrote:

> Hi,
>
> Sean Davis schrieb:
>> Probably the simplest way to do this is to load the data into a temporary
>> table without the unique constraint then use SQL to insert a "clean" version
>> into the new table.  Alternatively, you could use savepoints.  If an insert
>> fails, just rollback to that savepoint.  If it succeeds, commit that
>> savepoint.
>>
> But isn't that more overhead than using the trigger? And while inserting
> into the clean table, I would have to test if the value is existing or
> not.

If you are copying bulk data into the table only once, then cleaning the
data up front will not impact your actual use down the road.  If you are
saying that you will be inserting non-unique values and need to catch that,
a trigger is the better way to go.  As for testing if the value is existing
or not, you can avoid that by using SQL functions (like the postgresql
specific distinct on) to select from the temporary table only those values
that are unique.  See here in the docs:

http://www.postgresql.org/docs/8.1/interactive/sql-select.html#SQL-DISTINCT

> I don't see the advantage of this compared to the script which was
> posted a few weeks ago.

I should have pointed out that the solution depends on your needs.  If you
don't see an advantage, it is likely because there isn't one for your
particular needs, so feel free to use some other option.

Sean


Re: error handling

From
Verena Ruff
Date:
Hi,

Sean Davis schrieb:

>> If you are copying bulk data into the table only once, then cleaning the
>> data up front will not impact your actual use down the road.  If you are
>> saying that you will be inserting non-unique values and need to catch that,
>> a trigger is the better way to go.
This is what I need to do.
>> As for testing if the value is existing
>> or not, you can avoid that by using SQL functions (like the postgresql
>> specific distinct on) to select from the temporary table only those values
>> that are unique.  See here in the docs:
>>
>> http://www.postgresql.org/docs/8.1/interactive/sql-select.html#SQL-DISTINCT
>>
OK, I forgot about DISTINCT.
> I should have pointed out that the solution depends on your needs.  If you
> don't see an advantage, it is likely because there isn't one for your
> particular needs, so feel free to use some other option
Thanks for your hints. In my situation (many inserts and only a few
would break the unique clause) I think using a trigger is the way to get
a better performance.

Regards,
Verena



Re: error handling

From
Sean Davis
Date:


On 5/10/06 8:51 AM, "Verena Ruff" <lists@triosolutions.at> wrote:

> Hi,
>
> Sean Davis schrieb:
>
>>> If you are copying bulk data into the table only once, then cleaning the
>>> data up front will not impact your actual use down the road.  If you are
>>> saying that you will be inserting non-unique values and need to catch that,
>>> a trigger is the better way to go.
> This is what I need to do.
>>> As for testing if the value is existing
>>> or not, you can avoid that by using SQL functions (like the postgresql
>>> specific distinct on) to select from the temporary table only those values
>>> that are unique.  See here in the docs:
>>>
>>> http://www.postgresql.org/docs/8.1/interactive/sql-select.html#SQL-DISTINCT
>>>
> OK, I forgot about DISTINCT.
>> I should have pointed out that the solution depends on your needs.  If you
>> don't see an advantage, it is likely because there isn't one for your
>> particular needs, so feel free to use some other option
> Thanks for your hints. In my situation (many inserts and only a few
> would break the unique clause) I think using a trigger is the way to get
> a better performance.

Just keep in mind that the trigger runs on EVERY insert, even those for
which the unique clause is not violated.  If that is the behavior you need,
then use the trigger.  However, if you know that after you have clean data
in the table, you will not be inserting "duplicates" (I think this is the
typical case), then a trigger may not be the way to go.

Sean


Re: error handling

From
Verena Ruff
Date:
Sean Davis schrieb:

> Just keep in mind that the trigger runs on EVERY insert, even those for
> which the unique clause is not violated.  If that is the behavior you need,
> then use the trigger.  However, if you know that after you have clean data
> in the table, you will not be inserting "duplicates" (I think this is the
> typical case), then a trigger may not be the way to go.
>
These were my concerns in the first place I was thinking about using
triggers. Maybe I haven't really understood your suggestion right. When
would the temporary table be created? Isn't it neccessary to create it
on every insert, too? With wich statement could I do this without using
a trigger which is fired on every insert?

Thanks for your patience.

Regards,
Verena



Re: error handling

From
Sean Davis
Date:
Verena Ruff wrote:
> Sean Davis schrieb:
>
>> Just keep in mind that the trigger runs on EVERY insert, even those for
>> which the unique clause is not violated.  If that is the behavior you
>> need,
>> then use the trigger.  However, if you know that after you have clean
>> data
>> in the table, you will not be inserting "duplicates" (I think this is the
>> typical case), then a trigger may not be the way to go.
>>
>
> These were my concerns in the first place I was thinking about using
> triggers. Maybe I haven't really understood your suggestion right. When
> would the temporary table be created? Isn't it neccessary to create it
> on every insert, too? With wich statement could I do this without using
> a trigger which is fired on every insert?
>
> Thanks for your patience.

No problem.  I was thinking that you had a bunch of data that you wanted
to load ONCE, clean up, and then you would NOT  be inserting duplicated
values.  If you are going to be inserting duplicates potentially with
every insert, I think that a trigger is the only way to go on the DB
side of things.  Of course, you could do things on the client side, as
well (do a lookup, find nothing--do insert, find something--do nothing
or do update).

Sean

Re: error handling

From
Verena Ruff
Date:
Hi,

Sean Davis schrieb:
> No problem.  I was thinking that you had a bunch of data that you
> wanted to load ONCE, clean up, and then you would NOT  be inserting
> duplicated values.  If you are going to be inserting duplicates
> potentially with every insert, I think that a trigger is the only way
> to go on the DB side of things.  Of course, you could do things on the
> client side, as well (do a lookup, find nothing--do insert, find
> something--do nothing or do update).

Thanks for your opinion. Then I'll go with a trigger.

Regards,
Verena