Re: [SQL] Referential integrity (foreign keys) across multiple tables

2006-07-24 Thread Richard Jones
On Sun, Jul 23, 2006 at 01:32:37PM -0500, Bruno Wolff III wrote:
> On Sat, Jul 22, 2006 at 14:32:57 +0100,
>   Richard Jones <[EMAIL PROTECTED]> wrote:
> > 
> > Now I want to add a column to page_contents, say called link_name,
> > which is going to reference the pages.url column for the particular
> > host that this page belongs to.
> 
> What are you trying to accomplish by this?

Data integrity.

> The information is available by doing a join. If you are trying to
> simplify things for applications, you can probably do it with a view
> or rules depending on whether you want to have an updatable view. If
> you are denormalizing for performance and want constraints to
> maintain consistancy, then you probably want to push the hostid down
> to page_contents as well as the url. These could both be set with a
> trigger. (I think a rule could be used as well.)

So if I get this right, I should use a trigger to ensure that the old
code causes the hostid field to be set in page_contents?

Rich.

-- 
Richard Jones, CTO Merjis Ltd.
Merjis - web marketing and technology - http://merjis.com
Team Notepad - intranets and extranets for business - http://team-notepad.com

---(end of broadcast)---
TIP 6: explain analyze is your friend


Re: [SQL] Rows with exclusive lock

2006-07-24 Thread Martin Marques

On Sun, 23 Jul 2006, Alvaro Herrera wrote:


Martin Marques escribió:


After the SELECT FOR UPDATE other transactions can still see the locked
rows. I want a read/write lock, so no one can access does rows.


SELECT FOR UPDATE acquires an exclusive lock, but other transactions
must try to acquire a lock on the rows as well, or they won't be locked.
You can try using SELECT FOR SHARE (new as of 8.1) if you want some
transactions to hold shared (read) locks.


Sorry for not getting it clear the first time.

What I want is something like "LOCK table IN ACCESS EXCLUSIVE MODE", but 
at row level.



IOW, SELECT FOR UPDATE blocks other SELECTs FOR UPDATE and SELECTs FOR
SHARE, but it does not block plain SELECT.


So, this is not posible. :-(

--
 21:50:04 up 2 days,  9:07,  0 users,  load average: 0.92, 0.37, 0.18
-
Lic. Martín Marqués |   SELECT 'mmarques' ||
Centro de Telemática|   '@' || 'unl.edu.ar';
Universidad Nacional|   DBA, Programador,
del Litoral |   Administrador
-
---(end of broadcast)---
TIP 2: Don't 'kill -9' the postmaster


[SQL] Primary key constraint violation without error?

2006-07-24 Thread Mario Splivalo
Actually it's not violation, but I get no error message on violating
it...

The story is like this. I have few tables from where I extract
messageIds for particular users. Then, last 100 messages for each user I
transfer to spare table, to do something with that. That spare table has
strange behaviour.

I have two functions. First one extract last 100 messageIds for
particular user, and second one finds all the users, and then inserts
last 100 messageIds for particular user. 

The table definition is like this:


CREATE TABLE mes_del
(
  "messageId" int4 NOT NULL,
  CONSTRAINT pk PRIMARY KEY ("messageId")
) 
WITHOUT OIDS;

And the two functions are like this:
-- this function returns last 100 messageIds for particular user
CREATE OR REPLACE FUNCTION punibrisitablica(int4)
  RETURNS SETOF mes_del AS
$BODY$SELECT
messages.id as messagesId
FROM
users
JOIN phone_numbers
ON users.id = phone_numbers.user_id
JOIN messages
ON messages.phone_number = phone_numbers.phone_number
where
users.id = $1
order by
messages.time desc
limit 100;$BODY$
  LANGUAGE 'sql' VOLATILE;


-- this function goes trough all the users and inserts messageIds
-- to table mes_del
CREATE OR REPLACE FUNCTION punimessages()
  RETURNS bool AS
$BODY$

declare
userId users%ROWTYPE;

begin

truncate table mes_del;

FOR userId IN
SELECT users.id FROM users ORDER BY users.id DESC limit 5
LOOP
INSERT INTO mes_del SELECT * FROM puniBrisiTablica(userId.id);
RAISE NOTICE 'Ubacili smo za usera %.', userId.id;
END LOOP;

return true;
end;
$BODY$
  LANGUAGE 'plpgsql' VOLATILE;

I appologize for the line breaks, but Evolution is a bit stupid email
client...

In second function there is LIMIT 5, because there are 4 users in my
database, and it's going to be easier to explain like this.

Now, there is sequence of the commands I run trough psql:

First, I truncate the table mes_del:

l_netsms=# truncate table mes_del;
TRUNCATE TABLE

Then I run function punimessages() for filling the messageIds to the
mes_del table:

l_netsms=# select punimessages();
NOTICE:  Ubacili smo za usera 4162.
NOTICE:  Ubacili smo za usera 4161.
NOTICE:  Ubacili smo za usera 4160.
NOTICE:  Ubacili smo za usera 4159.
NOTICE:  Ubacili smo za usera 4158.
 punimessages
--
 t
(1 row)

l_netsms=# select count(*) from mes_del;
 count
---
60
(1 row)

There are 60 messages for those five users. 

Now I run the function again:

l_netsms=# select punimessages();
NOTICE:  Ubacili smo za usera 4162.
NOTICE:  Ubacili smo za usera 4161.
NOTICE:  Ubacili smo za usera 4160.
NOTICE:  Ubacili smo za usera 4159.
NOTICE:  Ubacili smo za usera 4158.
 punimessages
--
 t
(1 row)

Shouldn't I get errors that I'm violating primary key constraint when
INSERTing again same data?

l_netsms=# select count(*) from mes_del;
 count
---
60
(1 row)

l_netsms=#

If I execute INSERT statement from the second function, I get the error:

l_netsms=# INSERT INTO mes_del SELECT * FROM puniBrisiTablica(4158);
ERROR:  duplicate key violates unique constraint "pk"
l_netsms=#

This is expected, I'm just unsure why ain't I receiving that error when
running punimessages() function?

Postgres is 8.1.2 running on linux 2.6.17.

Mike
-- 
Mario Splivalo
Mob-Art
[EMAIL PROTECTED]

"I can do it quick, I can do it cheap, I can do it well. Pick any two."



---(end of broadcast)---
TIP 9: In versions below 8.0, the planner will ignore your desire to
   choose an index scan if your joining column's datatypes do not
   match


Re: [SQL] Primary key constraint violation without error?

2006-07-24 Thread Richard Huxton

Mario Splivalo wrote:

Actually it's not violation, but I get no error message on violating
it...




CREATE OR REPLACE FUNCTION punimessages()
  RETURNS bool AS
$BODY$

declare
userId users%ROWTYPE;

begin

truncate table mes_del;

^^^

This at the start of your inserts is why you're not seeing an error :-)

--
  Richard Huxton
  Archonet Ltd

---(end of broadcast)---
TIP 6: explain analyze is your friend


Re: [SQL] Primary key constraint violation without error?

2006-07-24 Thread Michael Fuhr
On Mon, Jul 24, 2006 at 02:12:39PM +0200, Mario Splivalo wrote:
> Now I run the function again:
> 
> l_netsms=# select punimessages();
> NOTICE:  Ubacili smo za usera 4162.
> NOTICE:  Ubacili smo za usera 4161.
> NOTICE:  Ubacili smo za usera 4160.
> NOTICE:  Ubacili smo za usera 4159.
> NOTICE:  Ubacili smo za usera 4158.
>  punimessages
> --
>  t
> (1 row)
> 
> Shouldn't I get errors that I'm violating primary key constraint when
> INSERTing again same data?

No, because punimessages() has a truncate statement that empties
the table each time the function is called.  Should that truncate
statement be there?

-- 
Michael Fuhr

---(end of broadcast)---
TIP 9: In versions below 8.0, the planner will ignore your desire to
   choose an index scan if your joining column's datatypes do not
   match


Re: [SQL] System catalog table privileges

2006-07-24 Thread Aaron Bono
On 7/21/06, Hilary Forbes <[EMAIL PROTECTED]> wrote:

Aaron
Thanks for this one - I had actually wondered about doing that but the
trouble is that they say that they need up to the minute reports not
"as of last night".  Indeed, I do have another app where I
do just that because I find that reports indexes/requirements are very
different to transactional type requirements.  However, you have
made me make up my mind to see if I can persuade them to work on data
that is a day old. I have heard "I need up to the minute data" a lot but have NEVER seen it to be true.  I guess if you are trading stocks on the stock market and need to buy and sell immediately as the prices change then you would have a reason but almost always business users think they need things now when they don't.
You could also look at the cost/benefit: if they bring the database down, how much would it cost the business?  If they are working on day old data, how much would it cost?  Get the user to write down and justify their numbers.  This will show to you and the user whether it is really necessary to report off of the live data.
Good luck!==   Aaron Bono   Aranya Software Technologies, Inc.   http://www.aranya.com
==


Re: [SQL] Referential integrity (foreign keys) across multiple tables

2006-07-24 Thread Aaron Bono
On 7/22/06, Richard Jones <[EMAIL PROTECTED]> wrote:
Simplified schema:  create table hosts (id serial primary key,hostname text not null  );  create table pages (id serial primary key,hostid int not null references hosts (id),
url text not null,unique (hostid, url)  );  create table page_contents (pageid int not null references pages (id),section text not null  );(There are many hosts, many pages per host, and many page_contents
sections per page).Now I want to add a column to page_contents, say called link_name,which is going to reference the pages.url column for the particularhost that this page belongs to.Something like:
  alter table page_contents add link_name text;  alter table page_contentsadd constraint foo foreign key (p.hostid, link_name)references pages (hostid, url)where p.id
 = pageid; The easiest, and not necessarily elegant, way to acomplish this is to create linkid rather than link_name and make it a foreign key to pages.id.  Then add a trigger that checks to make sure the pages you link to from page_contents to pages is for the same host.  If not, raise an exception.
Another option is to do this:  create table page_contents (   hostid int not null,   url text not null,   linkhostid int,   linkurl text,    section text not null,   foreign key (hostid, url) references pages (hostid, url),
   foreign key (linkhostid, linkurl) references pages (hostid, url) );Or if you really want to restructure things: create table hosts (    id serial primary key,    hostname text not null
  );  create table pages (    id serial primary key,    url text not null,    unique (url)  );  create table page_contents (    pageid int not null references pages (id),   hostsid int not null references hosts (id),
   linkpageid int references pages(id),    section text not null  );That should give you some options to play with.As a side comment, you should also name your primary key columns more meaningfully.  Use 
hosts.hostsid and pages.pagesid, not hosts.id and pages.id.  When you begin writing large queries, the use of the column name id all over the place will make your queries more prone to error, harder to read and harder to write.
==   Aaron Bono   Aranya Software Technologies, Inc.   http://www.aranya.com==


Re: [SQL] Referential integrity (foreign keys) across multiple tables

2006-07-24 Thread Bruno Wolff III
On Mon, Jul 24, 2006 at 09:59:07 +0100,
  Richard Jones <[EMAIL PROTECTED]> wrote:
> On Sun, Jul 23, 2006 at 01:32:37PM -0500, Bruno Wolff III wrote:
> > On Sat, Jul 22, 2006 at 14:32:57 +0100,
> >   Richard Jones <[EMAIL PROTECTED]> wrote:
> > > 
> > > Now I want to add a column to page_contents, say called link_name,
> > > which is going to reference the pages.url column for the particular
> > > host that this page belongs to.
> > 
> > What are you trying to accomplish by this?
> 
> Data integrity.

This doesn't make sense in isolation. If that is all you are trying to do,
then you don't need to do anything to the database design as the information
is already there. The application just needs to do a join when querying the
data.

> > The information is available by doing a join. If you are trying to
> > simplify things for applications, you can probably do it with a view
> > or rules depending on whether you want to have an updatable view. If
> > you are denormalizing for performance and want constraints to
> > maintain consistancy, then you probably want to push the hostid down
> > to page_contents as well as the url. These could both be set with a
> > trigger. (I think a rule could be used as well.)
> 
> So if I get this right, I should use a trigger to ensure that the old
> code causes the hostid field to be set in page_contents?

No unless you are trying to do something else in addition to maintaining
data integrity.

---(end of broadcast)---
TIP 2: Don't 'kill -9' the postmaster


Re: [SQL] Referential integrity (foreign keys) across multiple tables

2006-07-24 Thread Richard Jones
On Mon, Jul 24, 2006 at 12:51:48PM -0500, Bruno Wolff III wrote:
> On Mon, Jul 24, 2006 at 09:59:07 +0100,
>   Richard Jones <[EMAIL PROTECTED]> wrote:
> > On Sun, Jul 23, 2006 at 01:32:37PM -0500, Bruno Wolff III wrote:
> > > On Sat, Jul 22, 2006 at 14:32:57 +0100,
> > >   Richard Jones <[EMAIL PROTECTED]> wrote:
> > > > 
> > > > Now I want to add a column to page_contents, say called link_name,
> > > > which is going to reference the pages.url column for the particular
> > > > host that this page belongs to.
> > > 
> > > What are you trying to accomplish by this?
> > 
> > Data integrity.
> 
> This doesn't make sense in isolation. If that is all you are trying to do,
> then you don't need to do anything to the database design as the information
> is already there. The application just needs to do a join when querying the
> data.

I'm not sure what this means.  By "data integrity" I just meant that I
don't want applications to create page_contents.link_name fields which
could point to non-existent URLs.  (A URL consists of a particular
hostid and url, since you're not allowed to have one host pointing to
pages on another, and this is where the requirement for a foreign key
which spans two tables comes from).  Perhaps I meant "data
consistency"?  Anyway without some sort of check, be it a reference or
a trigger -- assuming a trigger is possible -- then an application
might create such a bad link.

Rich.

-- 
Richard Jones, CTO Merjis Ltd.
Merjis - web marketing and technology - http://merjis.com
Team Notepad - intranets and extranets for business - http://team-notepad.com

---(end of broadcast)---
TIP 4: Have you searched our list archives?

   http://archives.postgresql.org


Re: [SQL] Referential integrity (foreign keys) across multiple tables

2006-07-24 Thread Richard Jones
On Mon, Jul 24, 2006 at 12:26:15PM -0500, Aaron Bono wrote:
> On 7/22/06, Richard Jones <[EMAIL PROTECTED]> wrote:
> >  create table hosts (
> >id serial primary key,
> >hostname text not null
> >  );
> >
> >  create table pages (
> >id serial primary key,
> >hostid int not null references hosts (id),
> >url text not null,
> >unique (hostid, url)
> >  );
> >
> >  create table page_contents (
> >pageid int not null references pages (id),
> >section text not null
> >  );
> >
> >(There are many hosts, many pages per host, and many page_contents
> >sections per page).
> >
> >Now I want to add a column to page_contents, say called link_name,
> >which is going to reference the pages.url column for the particular
> >host that this page belongs to.
> >
> >Something like:
> >
> >  alter table page_contents add link_name text;
> >  alter table page_contents
> >add constraint foo foreign key (p.hostid, link_name)
> >references pages (hostid, url)
> >where p.id = pageid;
> 
> The easiest, and not necessarily elegant, way to acomplish this is to create
> linkid rather than link_name and make it a foreign key to pages.id.

Unfortunately this isn't possible :-(  My schema above is simplified a
little too far.  In the real schema we keep old versions of pages
around in the pages table (we move the 'url' field to a 'url_deleted'
field so that the unique (hostid, url) isn't violated by the new
version of the page).  This means that the pageid can be updated, so
link_name must store a url, not a pageid.

> Then add a trigger that checks to make sure the pages you link to
> from page_contents to pages is for the same host.  If not, raise an
> exception.

I think though that this suggestion is right.  I'm not sure what
difference it makes if it's link_name or linkid, but it looks like
I'll have to write a trigger for this.  It doesn't seem like there's a
way using just ordinary foreign keys.

Rich.

-- 
Richard Jones, CTO Merjis Ltd.
Merjis - web marketing and technology - http://merjis.com
Team Notepad - intranets and extranets for business - http://team-notepad.com

---(end of broadcast)---
TIP 3: Have you checked our extensive FAQ?

   http://www.postgresql.org/docs/faq


Re: [SQL] Referential integrity (foreign keys) across multiple tables

2006-07-24 Thread Richard Huxton

Richard Jones wrote:

Simplified schema:

  create table hosts (
id serial primary key,
hostname text not null
  );

  create table pages (
id serial primary key,
hostid int not null references hosts (id),
url text not null,
unique (hostid, url)
  );

  create table page_contents (
pageid int not null references pages (id),
section text not null
  );

(There are many hosts, many pages per host, and many page_contents
sections per page).

Now I want to add a column to page_contents, say called link_name,
which is going to reference the pages.url column for the particular
host that this page belongs to.


Ah! It's only from reading later messages that I realise you're trying 
to ensure that a link in page_contents can only reference pages on the 
same host as itself.



Something like:

  alter table page_contents add link_name text;
  alter table page_contents
add constraint foo foreign key (p.hostid, link_name)
references pages (hostid, url)
where p.id = pageid;

Obviously that second statement isn't going to compile.

I don't want to add the hostid column to page_contents table because I
have a lot of old code accessing the database which would be hard to
change (the old code would no longer be able to insert page_contents
rows).


I'm sure you know, but for the benefit of the list the problem here is 
that the surrogate primary-key on "pages" has concealed valuable 
information (i.e. the hostid) from the "page_contents" table. 
Unfortunate that you can't update the application [* see rant below]



Is this possible somehow?  Perhaps by adding a second table?  Do I
have to use triggers, and if so is that as robust as referential
integrity?


Write triggers to enforce the host dependency. Just make a table of all 
the columns involved on all three tables and decide what (if anything) 
should happen when a value is inserted/updated/deleted. Write your 
triggers then write a short test script. Oh, test it with concurrent 
transactions too - just to make sure it's doing what you'd expect.


The only reason hand-built triggers would be less reliable than built-in 
foreign-keys is because they'll have had less testing.


The other alternative would be to add the hostid column to page_contents 
and use a BEFORE trigger to set it based on the pageid the application 
inserts. Then you can have a straightforward foreign-key. If your 
application can't cope with having a column added to the table you might 
have to mask this with a view.


[*minor rant follows:
Why is it that when changes to the database schema break applications 
because the column-names are hard-wired, it is the RDBMS that is at 
fault? I'm prepared to bet good money (up to the value of say, a whole 
pound) that the primary key is just being used anonymously in the 
application and that if the database interface had been able to refer to 
it as "column(s) referencing pages primary-key" then Richard wouldn't 
have a problem to post about.
And it's relational databases that aren't "agile" enough for the modern 
world, not most of your programming languages.

]

--
  Richard Huxton
  Archonet Ltd

---(end of broadcast)---
TIP 4: Have you searched our list archives?

  http://archives.postgresql.org


Re: [SQL] CREATE TABLE AS inside of a function

2006-07-24 Thread Kevin Nikiforuk
So, I've changed my code as Erik suggested:
 
CREATE OR REPLACE FUNCTION rgio() RETURNS INTEGER as $$
DECLARE
lv RECORD;

BEGIN
FOR lv IN SELECT DISTINCT rg
FROM ldevrg
LOOP
 
EXECUTE 'CREATE TABLE rgio_' || $lv || ' AS
SELECT ldev
FROM ldevrg
WHERE rg=' || quote_literal($lv) || ';'
 
END LOOP;
RETURN 1;
END;
$$ LANGUAGE plpgsql;

And I get:
 
psql:rgio.sql:32: ERROR:  syntax error at or near "$" at character 33
QUERY:  SELECT  'CREATE TABLE rgio_' || $ $1  || ' AS
SELECT ldev
FROM ldevrg
WHERE rg=' || quote_literal($ $2 ) || ';' END LOOP
CONTEXT:  SQL statement in PL/PgSQL function "rgio" near line 23
psql:rgio.sql:32: LINE 1: SELECT  'CREATE TABLE rgio_' || $ $1  || ' AS
psql:rgio.sql:32:   
 ^




From: [EMAIL PROTECTED] on behalf of Erik Jones
Sent: Fri 7/21/2006 3:04 PM
To: Rodrigo De Leon
Cc: pgsql-sql@postgresql.org
Subject: Re: [SQL] CREATE TABLE AS inside of a function



Rodrigo De Leon wrote:
> On 7/21/06, Kevin Nikiforuk <[EMAIL PROTECTED]> wrote:
>> So now that I've got my loops working, on to my next newbie
>> question.  I've created my function and in it, I want to loop through
>> the results of a select and for each value of my loop counter, I want
>> to create a new table, but I can't figure out how to use a variable
>> in the name of the new table, see below.
>>
>> CREATE OR REPLACE FUNCTION rgio() RETURNS INTEGER as $$
>> DECLARE
>> lv RECORD;
>>
>> BEGIN
>> FOR lv IN SELECT DISTINCT rg FROM ldevrg LOOP
>> CREATE TABLE rgio_$lv AS
>> SELECT ldev
>> FROM ldevrg
>> WHERE rg='$lv';
>> END LOOP;
>> RETURN 1;
>> END;
>> $$ LANGUAGE plpgsql;
>>
>> Thanks,
>> Kevin
>
> See:
> http://www.postgresql.org/docs/8.1/static/plpgsql-statements.html#PLPGSQL-STATEMENTS-EXECUTING-DYN
>
Also, I really recommend enough that you read chapters 32. Extending
SQL, 33. Triggers, and 36. Pl/pgSQL in their entirety.  In fact, to keep
up with the linking to them for you:

http://www.postgresql.org/docs/8.1/interactive/extend.html
http://www.postgresql.org/docs/8.1/interactive/triggers.html
http://www.postgresql.org/docs/8.1/interactive/plpgsql.html

And, so that I don't feel like I'm becoming one of those rtfm jerks I
always complain about:  what you need to do is place your CREATE TABLE
statement in an EXECUTE directive like so (inside the the FOR body):

EXECUTE 'CREATE TABLE rgio_' || $lv || ' AS
SELECT ldev
FROM ldevrg
WHERE rg=\'' || $lv || '\';' -- this line could have also been: 
WHERE rg=' || quote_literal($lv) || ';'

EXECUTE takes a query in a string to execute and you  need to use string
concatenation to build the string if you're using variables from the
function in the query.  Pl/pgSQL doesn't  have any variable substitution
inside of strings (like in double quoted string in PHP) which is why you
need to use the concatenation bit.

--
erik jones <[EMAIL PROTECTED]>
software development
emma(r)


---(end of broadcast)---
TIP 1: if posting/reading through Usenet, please send an appropriate
   subscribe-nomail command to [EMAIL PROTECTED] so that your
   message can get through to the mailing list cleanly



---(end of broadcast)---
TIP 2: Don't 'kill -9' the postmaster


Re: [SQL] Referential integrity (foreign keys) across multiple tables

2006-07-24 Thread Bruno Wolff III
On Mon, Jul 24, 2006 at 18:53:20 +0100,
  Richard Jones <[EMAIL PROTECTED]> wrote:
> 
> I'm not sure what this means.  By "data integrity" I just meant that I
> don't want applications to create page_contents.link_name fields which
> could point to non-existent URLs.  (A URL consists of a particular
> hostid and url, since you're not allowed to have one host pointing to
> pages on another, and this is where the requirement for a foreign key
> which spans two tables comes from).  Perhaps I meant "data
> consistency"?  Anyway without some sort of check, be it a reference or
> a trigger -- assuming a trigger is possible -- then an application
> might create such a bad link.

I think part of the problem might have been over simplification of the
problem. In your example there was no reason to create that new column since
the information was available by doing a join between two tables and this
would prevent a problem with data being out of sync.

I see from another message in this thread that there is really more to
this example. It may be that you still don't need the new column, but I
haven't examined the new example carefully to say one way or the other.

---(end of broadcast)---
TIP 6: explain analyze is your friend


Re: [SQL] CREATE TABLE AS inside of a function

2006-07-24 Thread Stephan Szabo
On Mon, 24 Jul 2006, Kevin Nikiforuk wrote:

> So, I've changed my code as Erik suggested:
>
> CREATE OR REPLACE FUNCTION rgio() RETURNS INTEGER as $$
> DECLARE
> lv RECORD;
>
> BEGIN
> FOR lv IN SELECT DISTINCT rg
> FROM ldevrg
> LOOP
>
> EXECUTE 'CREATE TABLE rgio_' || $lv || ' AS
> SELECT ldev
> FROM ldevrg
> WHERE rg=' || quote_literal($lv) || ';'


I think you want something like lv.rg (no special punctuation) rather than
$lv in the above.

---(end of broadcast)---
TIP 4: Have you searched our list archives?

   http://archives.postgresql.org