Thread: Modifying WHERE input conditions for a VIEW

Modifying WHERE input conditions for a VIEW

From
Tony Holmes
Date:
Hi,

I'm stuck with some libraries that severely limit how queries are formed
and sent to postgresql and decided to use views and rules to solve my
problem. However, I've bumped my head on an issue.

I have 2 tables that need to be selected from, depending on the input
WHERE condition.

Here is their very simplified schema:

table foo_users (
  uid serial,
  username character varying(16),    -- No @domainname.com
  password character varying(16),
  active boolean default 't'
);

table domain_users (
  uid serial,
  username character varying(255),    -- Includes @domainname.com
  password character varying(16),
  active boolean default 't'
)

Now, I'm dealing with pgsql commands (from an imap system FYI) that always
appends @domain.com to the username for the where clause:

  SELECT username, password FROM xxxx WHERE username='user@domain.com';


What I want to accomplish is, if the WHERE clause matches @foo.com
I want to select from foo_users, removing @foo.com, otherwise, return
the data from domain_users.

I have experimented with views, rules and looking all over the place
and am stuck!

How can I accomplish this?

Thanks in advance for you assistance!

--
Tony Holmes

Founder and Senior Systems Architect
Crosswinds Internet Communications Inc.


Re: Modifying WHERE input conditions for a VIEW

From
Tom Lane
Date:
Tony Holmes <tony@crosswinds.net> writes:
> I have 2 tables that need to be selected from, depending on the input
> WHERE condition.

> Here is their very simplified schema:

> table foo_users (
>   uid serial,
>   username character varying(16),    -- No @domainname.com
>   password character varying(16),
>   active boolean default 't'
> );

> table domain_users (
>   uid serial,
>   username character varying(255),    -- Includes @domainname.com
>   password character varying(16),
>   active boolean default 't'
> )

> What I want to accomplish is, if the WHERE clause matches @foo.com
> I want to select from foo_users, removing @foo.com, otherwise, return
> the data from domain_users.

Um, are there really only two tables?  Or are you using "foo" to imply
that you have a bunch of domains xxx each with its own xxx_users table?

You could imagine using a view like

    CREATE VIEW all_users as
        SELECT uid, username || '@foo.com', password, active
        FROM foo_users
        UNION ALL
        SELECT uid, username, password, active
        FROM domain_users

and then selecting from this view.  It won't be real efficient though,
and it won't scale conveniently if there are many per-domain tables
(especially if they come and go).

My advice is to change the schema.  Get rid of foo_users and store all
the users in the single domain_users table, with @domainname.com for
everyone.  It's false economy to avoid storing the @domainname.com, at
least if your primary traffic is queries that include it.

            regards, tom lane


Re: Modifying WHERE input conditions for a VIEW

From
Tony Holmes
Date:
On +Mar 30, Tom Lane wrote:
> > What I want to accomplish is, if the WHERE clause matches @foo.com
> > I want to select from foo_users, removing @foo.com, otherwise, return
> > the data from domain_users.
>
> Um, are there really only two tables?  Or are you using "foo" to imply
> that you have a bunch of domains xxx each with its own xxx_users table?

Yes, there is only 2. I am dealing with legacy issues here.

@foo.com is a "large" community site with domains "overlayed" (that best
describes it). In the case of email we are using virtual delivery to give
more email addresses for individual domains.

I now forsee the benefit of a single table, but moving the old foo.com
domain is painful at best. (foo.com has 1.5 million users and a lot of
special case code).

The new domain_users table will be all domains and users that are newly
registered.

> You could imagine using a view like
>
>     CREATE VIEW all_users as
>         SELECT uid, username || '@foo.com', password, active
>         FROM foo_users
>         UNION ALL
>         SELECT uid, username, password, active
>         FROM domain_users
>
> and then selecting from this view.  It won't be real efficient though,
> and it won't scale conveniently if there are many per-domain tables
> (especially if they come and go).

Yes, this works and is quite slow :/

Speed is a bit of an issue since it will be dealing with email.

One approach I had tried (my SQL background isn't the strongest) is
creation of a view (which works) that returns the data required
from the table in the format the mailserver wants it. That works
wonderfully.

I then attempted to use a SELECT rule on that view. It looked like
this (the view was imap_lookup):

CREATE RULE imap_lookup AS
    ON SELECT TO imap_lookup
    WHERE strpos(username, '@foo.com') DO INSTEAD
        SELECT uid, username, password, active
        FROM foo_users
        WHERE username=rtrim(username,'@foo.com');

Thus, redirecting only the queries for @foo.com to the correct
table.

I kept running into "ON SELECT rule may not use OLD".

> My advice is to change the schema.  Get rid of foo_users and store all
> the users in the single domain_users table, with @domainname.com for
> everyone.  It's false economy to avoid storing the @domainname.com, at
> least if your primary traffic is queries that include it.

I agree, but I'm kinda bound to what we have at the moment. Changing it
is in the plans, but more long term.

--
Tony Holmes

Founder and Senior Systems Architect
Crosswinds Internet Communications Inc.


Re: Modifying WHERE input conditions for a VIEW

From
Tom Lane
Date:
Tony Holmes <tony@crosswinds.net> writes:
> I then attempted to use a SELECT rule on that view. It looked like
> this (the view was imap_lookup):

> CREATE RULE imap_lookup AS
>     ON SELECT TO imap_lookup
>     WHERE strpos(username, '@foo.com') DO INSTEAD

This isn't going to work; we do not support WHERE conditions in ON
SELECT rules at all.  (There is no difference between ON SELECT rules
and views, actually.)

>> My advice is to change the schema.

> I agree, but I'm kinda bound to what we have at the moment. Changing it
> is in the plans, but more long term.

You might be able to make it work efficiently with a functional index.
To meet the syntactic limitations of functional indexes, you'd need to
define a function "addfoo(text) returns text" that returns the input
with "@foo.com" appended.  (Be sure to mark it strict and immutable.)
Now you can create an index on addfoo(username) for foo_users, and
then your view becomes

    CREATE VIEW all_users as
        SELECT uid, addfoo(username) AS username, password, active
        FROM foo_users
        UNION ALL
        SELECT uid, username, password, active
        FROM domain_users

I haven't tested, but I believe 7.3 will optimize this fairly decently
when the query is "SELECT ... FROM all_users WHERE username = something'.

            regards, tom lane


Re: Modifying WHERE input conditions for a VIEW

From
Tony Holmes
Date:
On +Mar 30, Tom Lane wrote:
>
> You might be able to make it work efficiently with a functional index.
> To meet the syntactic limitations of functional indexes, you'd need to
> define a function "addfoo(text) returns text" that returns the input
> with "@foo.com" appended.  (Be sure to mark it strict and immutable.)
> Now you can create an index on addfoo(username) for foo_users, and
> then your view becomes
>
>     CREATE VIEW all_users as
>         SELECT uid, addfoo(username) AS username, password, active
>         FROM foo_users
>         UNION ALL
>         SELECT uid, username, password, active
>         FROM domain_users
>
> I haven't tested, but I believe 7.3 will optimize this fairly decently
> when the query is "SELECT ... FROM all_users WHERE username = something'.

For not testing this solution works amazingly well! :)

There is 3 orders of magnitude improvement in query speed, making it
suitable for our needs! Now on to the task of fixing our schema and
data.

Thank you Tom. This kind of assistance is exactly what makes PostgreSQL
great! (well, on top of being a superior product, IMHO).

--
Tony Holmes

Founder and Senior Systems Architect
Crosswinds Internet Communications Inc.


Export content of a DB

From
Ben Clewett
Date:
Is it possible to export an entire DB in a form where it can be
re-imported?  For each table in a DB, output to a line-sequential file?

I have found that:

echo "select 'insert into $table values', * from $table |
    psql -F " " -A -t $database >> outfile.txt

Does at least give a file of sql statements.  But the strings are not
quoted or escaped.  So multi-word fields, and field''''s with quotes
mess up.

For this I also need a list of tables in a DB.  I can use:

for $table in read `echo "\dt" | psql -F " " -A -t rr | awk '{ print $2 }'`
do
    (as above)
done

But it's getting a little weigted!  I can't help fealing there must be
some clever option I am missing for dumping a DB in a form designed for
re-import, without all this work!


Thanks in advance if anybody can give me a hand...

Ben