Re: [SQL] Composite UNIQUE across two tables?

2008-03-07 Thread Bart Degryse
create a function that, given the siteid returns the sitegroupid
create a unique index on username and that function

>>> "Jamie Tufnell" <[EMAIL PROTECTED]> 2008-03-06 20:04 >>>
Hi,

I'm remodeling our authentication tables at the moment to accomodate
future changes.  I've hit a wall and thought I'd ask here for some
help.  Hopefully I'm just missing something simple.

I'll do my best to explain the domain..

We have users, sites, and groups of sites.

Usernames should be unique within a site group.
Each user is tied to one, and only one, site.
Each site belongs to exactly one group, and one group can contain
many sites. (some sites are actually groupless in reality, but at
one point in my design I figured it might make it easier to force
them to be in a group of 1 -- perhaps that's part of my problem?).

A user has implied permission to access any site in their group of
sites.

So far this is what I have:

CREATE TABLE site_groups (
id serial,
name varchar(120) not null,
PRIMARY KEY (id)
);

CREATE TABLE sites (
id serial,
site_group_id integer not null,
name varchar(120) not null,
PRIMARY KEY (id),
FOREIGN KEY (site_group_id) REFERENCES site_groups (id)
);

CREATE TABLE users (
id serial,
site_id integer not null,
username varchar(120) not null,
PRIMARY KEY (id),
FOREIGN KEY (site_id) REFERENCES sites (id)
);

The problem is the "Usernames should be unique within a site group."

I could include a site_group_id FK in the users table, and put a
UNIQUE(username, site_group_id), but it feels like I'm doing
something wrong here since the user's site_group_id is already implied
by the user's site_id.

When users login I get their username, password and site name.
Without a UNIQUE constraint on username+site_group_id, it's possible
I'll get more than one user id matching the same login information
which shouldn't happen.

I suppose I could write a trigger to enforce this uniqueness, but it
just feels like I'm probably doing something wrong elsewhere.

Any help/abuse greatly appreciated :-)

Cheers,
J.

--
Sent via pgsql-sql mailing list (pgsql-sql@postgresql.org)
To make changes to your subscription:
http://mail.postgresql.org/mj/mj_wwwusr?domain=postgresql.org&extra=pgsql-sql


Re: [SQL] Composite UNIQUE across two tables?

2008-03-07 Thread Bart Degryse
I haven't tested these two statements, but I'm using exactly this
concept on some tables myself.
My equivalent of your users table contains some 3,000,000 records.
My equivalent of your sites table contains some 150,000 records.
And it works fine...
 
CREATE OR REPLACE FUNCTION "fnc_idx_sitegroupid" (p_siteid
sites.id%TYPE) RETURNS site_groups.id%TYPE AS
$body$
DECLARE
  v_sitegroupid site_groups.id%TYPE ;
BEGIN
  SELECT site_group_id INTO v_sitegroupid FROM sites WHERE id =
p_siteid;
  RETURN v_sitegroupid;
END;
$body$
LANGUAGE 'plpgsql' VOLATILE RETURNS NULL ON NULL INPUT SECURITY
INVOKER;
 
CREATE UNIQUE INDEX "users_unq" ON "users"
  USING btree ("username", (fnc_idx_sitegroupid(site_id)));


>>> Sébastien Meudec <[EMAIL PROTECTED]> 2008-03-07 9:40 >>>
Hi Bart,
I'm following this topic with interest.
Could you describe me more how you design an unique index with both a
column
name and a function name by an example
Thx.
Sebastien


De : [EMAIL PROTECTED]
[mailto:[EMAIL PROTECTED] 
De la part de Bart Degryse
Envoyé : vendredi 7 mars 2008 08:46
À : Jamie Tufnell; pgsql-sql@postgresql.org 
Objet : Re: [SQL] Composite UNIQUE across two tables?

create a function that, given the siteid returns the sitegroupid
create a unique index on username and that function

>>> "Jamie Tufnell" <[EMAIL PROTECTED]> 2008-03-06 20:04 >>>
Hi,

I'm remodeling our authentication tables at the moment to accomodate
future changes.  I've hit a wall and thought I'd ask here for some
help.  Hopefully I'm just missing something simple.

I'll do my best to explain the domain..

We have users, sites, and groups of sites.

Usernames should be unique within a site group.
Each user is tied to one, and only one, site.
Each site belongs to exactly one group, and one group can contain
many sites. (some sites are actually groupless in reality, but at
one point in my design I figured it might make it easier to force
them to be in a group of 1 -- perhaps that's part of my problem?).

A user has implied permission to access any site in their group of
sites.

So far this is what I have:

CREATE TABLE site_groups (
id serial,
name varchar(120) not null,
PRIMARY KEY (id)
);

CREATE TABLE sites (
id serial,
site_group_id integer not null,
name varchar(120) not null,
PRIMARY KEY (id),
FOREIGN KEY (site_group_id) REFERENCES site_groups (id)
);

CREATE TABLE users (
id serial,
site_id integer not null,
username varchar(120) not null,
PRIMARY KEY (id),
FOREIGN KEY (site_id) REFERENCES sites (id)
);

The problem is the "Usernames should be unique within a site group."

I could include a site_group_id FK in the users table, and put a
UNIQUE(username, site_group_id), but it feels like I'm doing
something wrong here since the user's site_group_id is already implied
by the user's site_id.

When users login I get their username, password and site name.
Without a UNIQUE constraint on username+site_group_id, it's possible
I'll get more than one user id matching the same login information
which shouldn't happen.

I suppose I could write a trigger to enforce this uniqueness, but it
just feels like I'm probably doing something wrong elsewhere.

Any help/abuse greatly appreciated :-)

Cheers,
J.

--
Sent via pgsql-sql mailing list (pgsql-sql@postgresql.org)
To make changes to your subscription:
http://mail.postgresql.org/mj/mj_wwwusr?domain=postgresql.org&extra=pgsql-sq

l




Re: [SQL] Composite UNIQUE across two tables?

2008-03-07 Thread Sébastien Meudec
Hi Bart,
I'm following this topic with interest.
Could you describe me more how you design an unique index with both a column
name and a function name by an example
Thx.
Sebastien


De : [EMAIL PROTECTED] [mailto:[EMAIL PROTECTED]
De la part de Bart Degryse
Envoyé : vendredi 7 mars 2008 08:46
À : Jamie Tufnell; pgsql-sql@postgresql.org
Objet : Re: [SQL] Composite UNIQUE across two tables?

create a function that, given the siteid returns the sitegroupid
create a unique index on username and that function

>>> "Jamie Tufnell" <[EMAIL PROTECTED]> 2008-03-06 20:04 >>>
Hi,

I'm remodeling our authentication tables at the moment to accomodate
future changes.  I've hit a wall and thought I'd ask here for some
help.  Hopefully I'm just missing something simple.

I'll do my best to explain the domain..

We have users, sites, and groups of sites.

Usernames should be unique within a site group.
Each user is tied to one, and only one, site.
Each site belongs to exactly one group, and one group can contain
many sites. (some sites are actually groupless in reality, but at
one point in my design I figured it might make it easier to force
them to be in a group of 1 -- perhaps that's part of my problem?).

A user has implied permission to access any site in their group of
sites.

So far this is what I have:

CREATE TABLE site_groups (
    id serial,
    name varchar(120) not null,
    PRIMARY KEY (id)
);

CREATE TABLE sites (
    id serial,
    site_group_id integer not null,
    name varchar(120) not null,
    PRIMARY KEY (id),
    FOREIGN KEY (site_group_id) REFERENCES site_groups (id)
);

CREATE TABLE users (
    id serial,
    site_id integer not null,
    username varchar(120) not null,
    PRIMARY KEY (id),
    FOREIGN KEY (site_id) REFERENCES sites (id)
);

The problem is the "Usernames should be unique within a site group."

I could include a site_group_id FK in the users table, and put a
UNIQUE(username, site_group_id), but it feels like I'm doing
something wrong here since the user's site_group_id is already implied
by the user's site_id.

When users login I get their username, password and site name.
Without a UNIQUE constraint on username+site_group_id, it's possible
I'll get more than one user id matching the same login information
which shouldn't happen.

I suppose I could write a trigger to enforce this uniqueness, but it
just feels like I'm probably doing something wrong elsewhere.

Any help/abuse greatly appreciated :-)

Cheers,
J.

--
Sent via pgsql-sql mailing list (pgsql-sql@postgresql.org)
To make changes to your subscription:
http://mail.postgresql.org/mj/mj_wwwusr?domain=postgresql.org&extra=pgsql-sq
l



-- 
Sent via pgsql-sql mailing list (pgsql-sql@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-sql


[SQL] UPDATE .. FROM

2008-03-07 Thread Markus Bertheau
I'm kind of stuck as to why postgresql doesn't understand what I mean in the
following queries:

UPDATE tag_data td SET td.usage_counter = td.usage_counter + 1 FROM
tag_list_tag_data ltd WHERE ltd.tag_id = td.id AND ltd.id =  102483;
ERROR:  column "td" of relation "tag_data" does not exist
LINE 1: UPDATE tag_data td SET td.usage_counter = td.usage_counter +...
   ^

I tried without aliases:

UPDATE tag_data SET tag_data.usage_counter = tag_data.usage_counter + 1 FROM
tag_list_tag_data ltd WHERE ltd.tag_id = tag_data.id AND ltd.id =  102483;
ERROR:  column "tag_data" of relation "tag_data" does not exist
LINE 1: UPDATE tag_data SET tag_data.usage_counter = tag_data.usage_...
^

Without a table specifier the error is understandable:

UPDATE tag_data SET usage_counter = usage_counter + 1 FROM tag_list_tag_data
ltd WHERE ltd.tag_id = tag_data.id AND ltd.id =  102483;
ERROR:  column reference "usage_counter" is ambiguous
LINE 1: UPDATE tag_data SET usage_counter = usage_counter + 1 FROM t...
^

The non-FROM form works:

UPDATE tag_data SET usage_counter = usage_counter + 1 WHERE id IN (SELECT
tag_id FROM tag_list_tag_data WHERE id =  102483);
UPDATE 1

Table schemata:

# \d+ tag_data
   Table "public.tag_data"
Column |  Type  |
Modifiers   | Description
---++---+-
id| bigint | not null default
nextval('tag_data_id_seq'::regclass) |
ns_id | bigint | not
null  |
name  | character varying(128) | not
null  |
usage_counter | bigint | not null default
0|
Indexes:
"tag_data_pkey" PRIMARY KEY, btree (id)
"tag_data_ns_id_key" UNIQUE, btree (ns_id, name)

# \d+ tag_list_tag_data
   Table "public.tag_list_tag_data"
Column |  Type  |
Modifiers| Description
---+++-
id| bigint | not null default
nextval('tag_list_tag_data_id_seq'::regclass) |
list_id   | bigint | not
null   |
tag_id| bigint | not
null   |
usage_counter | bigint | not null default
0 |
Indexes:
"tag_list_tag_data_pkey" PRIMARY KEY, btree (id)
"tag_list_tag_data_list_id_key" UNIQUE, btree (list_id, tag_id)

This is 8.3.0.

Thanks

-- 
Markus Bertheau
Blog: http://www.bluetwanger.de/blog/


Re: [SQL] RETURN QUERY generates error

2008-03-07 Thread Yura Gal
> The error sounds suspiciously like what would happen if you tried to
>  use RETURN QUERY in a pre-8.3 version.
>
> regards, tom lane
>

Thanks a lot to all. Actually there were a mass of errors in my
function. Now I rewrite it so as it works. It would be much more
complicated to achieve my goal without your advices.

-- 
Sent via pgsql-sql mailing list (pgsql-sql@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-sql


Re: [SQL] UPDATE .. FROM

2008-03-07 Thread Tom Lane
"Markus Bertheau" <[EMAIL PROTECTED]> writes:
> I'm kind of stuck as to why postgresql doesn't understand what I mean in the
> following queries:

> UPDATE tag_data td SET td.usage_counter = td.usage_counter + 1 FROM
> tag_list_tag_data ltd WHERE ltd.tag_id = td.id AND ltd.id =  102483;
> ERROR:  column "td" of relation "tag_data" does not exist

You aren't supposed to specify a table name (nor alias) for a target
variable in a SET clause.  It's useless (since you can't update more
than one table) and it's ambiguous because of the possibility of
composite fields.  What you want is something like

UPDATE tag_data td SET usage_counter = td.usage_counter + 1 FROM ...

regards, tom lane

-- 
Sent via pgsql-sql mailing list (pgsql-sql@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-sql


Re: [SQL] Composite UNIQUE across two tables?

2008-03-07 Thread Jamie Tufnell
Hi Ray,

On 3/7/08, Ray Madigan <[EMAIL PROTECTED]> wrote:
> How I think about it.
>
> A user has access to a site and all of the sites within the site group that
> the site is in.
>
> if you reword your condition
>
> A user has access to all of the sites in a site group with a default defined
> by site_id. Then there is no problem having both variables in the table.

One field in the users table (site_id) implicitly ties the user to a
site_group_id.
One field in the users table (site_group_id) explicitly ties the user
to a site_group_id.

The problem I have (or had.. read below) was enforcing that those
site_group_id's are equal.

> Also, you have to trade off the cost of the table join to get the group_id
> in all of the queries as opposed to the extra integer required. My thought
> is that the extra Integer is small compared to the number of wueries that
> have to run and would then adopt the second wording of the constraint.
>
> Even if you come up with an alternative, composite key you will still have
> to deal with all of the table joins. The table joins isn't a big deal, but
> it is unnecessary.

When I asked about a composite FK in my previous message, I'd planned
to use it in addition to your solution (not in place of)... the idea
being to solve the problem mentioned above.

What I was thinking is something like this .. I'm interested to hear
your thoughts on this:

users table:
FOREIGN KEY (site_id, site_group_id) REFERENCES sites (id, site_group_id)
sites table:
UNIQUE (id, site_group_id)

Assuming that's going to work... then I could use your suggestion of
adding site_group_id to the users table. As you said, that will make
writing SELECTs a lot simpler.

How would I handle INSERTs / UPDATEs though without having to always
specify both fields?  I have a feeling it will require some functions
being called by triggers / default values.  That's all doable, but I
wonder if there's a way of expressing these relationships without
having to duplicate site_group_id.

Cheers,
J.

-- 
Sent via pgsql-sql mailing list (pgsql-sql@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-sql


Re: [SQL] Composite UNIQUE across two tables?

2008-03-07 Thread Jamie Tufnell
Hi Bart,

On 3/7/08, Bart Degryse <[EMAIL PROTECTED]> wrote:
> I haven't tested these two statements, but I'm using exactly this
> concept on some tables myself.
> My equivalent of your users table contains some 3,000,000 records.
> My equivalent of your sites table contains some 150,000 records.
> And it works fine...
>
> CREATE OR REPLACE FUNCTION "fnc_idx_sitegroupid" (p_siteid
> sites.id%TYPE) RETURNS site_groups.id%TYPE AS
> $body$
> DECLARE
> v_sitegroupid site_groups.id%TYPE ;
> BEGIN
> SELECT site_group_id INTO v_sitegroupid FROM sites WHERE id =
> p_siteid;
> RETURN v_sitegroupid;
> END;
> $body$
> LANGUAGE 'plpgsql' VOLATILE RETURNS NULL ON NULL INPUT SECURITY
> INVOKER;
>
> CREATE UNIQUE INDEX "users_unq" ON "users"
> USING btree ("username", (fnc_idx_sitegroupid(site_id)));

Thank you for your suggestion and example!  I really like this idea
but I haven't been able to get it to work.

When I try to create the index I get the following error:

ERROR:  functions in index expression must be marked IMMUTABLE

After consulting the docs
(http://www.postgresql.org/docs/8.2/static/sql-createfunction.html)
I get the impression I shouldn't declare this function IMMUTABLE since
it queries the database?  It seems to me it should be STABLE.

Out of curiosity, I declared it IMMUTABLE and it worked for the
purposes of my small, isolated test,.

Am I opening myself up to problems by doing this?

Cheers,
J.

-- 
Sent via pgsql-sql mailing list (pgsql-sql@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-sql


Re: [SQL] Composite UNIQUE across two tables?

2008-03-07 Thread Jamie Tufnell
Hi Jorge,

On 3/7/08, Jorge Godoy <[EMAIL PROTECTED]> wrote:
> Em Thursday 06 March 2008 22:10:14 Jamie Tufnell escreveu:
> > I'm not sure how I would enforce that the site_group_id
> > added to the users table would correspond correctly with
> > the site_id (as per the sites table).  Perhaps I would make a
> > composite foreign key?
>
> Or you manage site_group_id from a trigger. When the user creates / chooses
> a site_id and updates/inserts this information then you fill site_group_id
> with a trigger. This way you guarantee the correctness of this parameter.

Hmm.  So your advice would be to duplicate the
site_group_id field into the users table as well?

That looks to be the consensus.. so I'm not sure why it feels
like I'm doing something wrong :-)

Thanks for your input!

J.

-- 
Sent via pgsql-sql mailing list (pgsql-sql@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-sql


[SQL] plpythonu + os.spawnv

2008-03-07 Thread Markus Schatten
Dear all,

I'm not sure if I'm at the right place to ask my question so please excuse me 
if I'm not.

I'm trying to spawn a process from plpythonu using psql  8.1.11. The code is 
working fine from Python shell but if I put it into a plpythonu function it 
seems like the process isn't starting at all.

The code I'm using is (with additional comment on the important parts):

CREATE OR REPLACE FUNCTION top_inline_query( VARCHAR( 50 ), TEXT )
RETURNS TEXT
AS $$

import os
import re
import sets
import random

def f2vars( string ):
  vars_re = re.compile( r'[?][^-:,\]\[]*' )
  all_vars = vars_re.findall( string )
  set_vars = sets.Set( all_vars )
  for i in all_vars:
if i.startswith( '?_' ):
  try:
set_vars.remove( i )
  except:
pass
  
  return list( set_vars )

''' THE SPAWN FUNCTION '''
def run(program, *args):
return os.spawnv(os.P_WAIT, program, (program,) + args)


def f2payser( query, module, path ):
''' THE PROCESS TO BE RUN '''
florahome = '/path/to/flora2/runflora' #where runflora is a shell script 
that runs a shell-like environment (take a look at http://flora.sf.net)

  
  quote_re = re.compile( r"['][^']*[']" )
  quotes = quote_re.findall( query )
  
  for i in quotes:
cp = i.replace( ' ', r'\s' )
query = query.replace( i, cp )  
  
  query = query[ :-1 ].replace( ' ', '' )
  
  query_vars = f2vars( query )
  #print query_vars
  
  
  vars_print = ''
  for i in query_vars:
vars_print += ",%write('" + i + "=')@_io,%write(" + i + ")@_io,[EMAIL 
PROTECTED]"
  
  
  randfilename = path + str( random.random() )
  while os.path.exists( randfilename ):
randfilename = path + str( random.random() )
  
  '''THE QUERY STRING TO BE PASSED TO FLORA'''
  query_str = "-e \"['" + module + "'].\" -e \"%tell('" + randfilename 
+ "')@_io," + query + vars_print + ",[EMAIL PROTECTED]" -e \"_halt.\""
  
  plpy.info( florahome + ' ' + str( query_str ) )
  run(florahome, query_str)
  
  vars_re = re.compile( '([?].*)[=](.*)')
  
  result = {}
  results = []
  
  warning = False
  try:
file = open( randfilename, 'r' )
  except:
file = []

  for i in file:
val_pair = vars_re.findall( i )
if len( val_pair ) == 1:
  if not ( val_pair[ 0 ][ 1 ].startswith( '(' ) and val_pair[ 0 ][ 
1 ].endswith( ')' ) and val_pair[ 0 ][ 1 ].find( '?_' ) != -1 ):
result[ val_pair[ 0 ][ 0 ] ] = val_pair[ 0 ][ 1 ]
  else:
warning = True
elif val_pair == []:
  if result != {}:
if not warning:
  results.append( result )
  result = {}
warning = False

else:
  raise ValueError,'Syntax error!'
  try:
os.remove( randfilename )
  except:
pass
  
  res_copy = results[ : ]
  for i in res_copy:
results.remove( i )
if not i in results:
  results.append( i )
  return results

project = args[ 0 ]
query = args[ 1 ]

'''THE ORIGINAL QUERY COMMENTED OUT '''
#onto_query = 'SELECT top_export_flora2_ontology( \'%s\' ) AS ontology;' % 
project

#ontology = plpy.execute( onto_query )[ 0 ][ 'ontology' ]

ontology = 'markus:person[ name->markus, surename->schatten ].\n'


path = '/path/to/chmoded/directory/to/store/files/in'
randfilename = path + str( int( random.random() * 100 ) ) + '.flr'
while os.path.exists( randfilename ):
  randfilename = path + str( int( random.random() * 100 ) ) + '.flr'

onto_file = open( randfilename, 'w' )
onto_file.write( ontology )
onto_file.close()
os.chmod( randfilename, 0755 )

module = randfilename[ :-4 ]


results = f2payser( query, module, path )

'''CLEAN UP'''
os.remove( randfilename )

return str( results )

  $$
  LANGUAGE plpythonu;

The funny thing is that the same code (as said before) works fine when called 
from a Python environment so my question is (if I'm not missing something in 
the code) is there a difference in handling spawned processes in plpthonu 
and "normal" python?

The query passed to FLORA reads an ontology file queries it and writes the 
results to another file that is than read by the plpythonu function. I tried 
a lot of different approaches but none of them seems to be starting the FLORA 
process at all. It seem like plpythonu just runs over the spawn function 
doing nothing continuing the execution of the rest of the code.

Any help would be very appreciated because I'm running out of ideas ;)

Best regards,

-- 
Markus Schatten, MSc
Faculty of Organization and Informatics
Varaždin, Croatia
e-mail: [EMAIL PROTECTED]
http://www.foi.hr

-- 
Sent via pgsql-sql mailing list (pgsql-sql@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-sql