Re: [GENERAL] 88, CREATE FUNCTION ON TABLE - Mailing list pgsql-general

From Jose' Soares
Subject Re: [GENERAL] 88, CREATE FUNCTION ON TABLE
Date
Msg-id 368B3E5E.736FD65A@sferacarta.com
Whole thread Raw
In response to 88, CREATE FUNCTION ON TABLE  (Clark Evans <clark.evans@manhattanproject.com>)
List pgsql-general
Hi Clark,

Now that we have the PL/pgSQL procedural language (thanks to Jan Wieck)
you can do almost every thing you desire with PostgreSQL in a easy way.
For example; to emulate COBOL level 88, as you suggest you may create
the DECODE function (see attached file).
The other solution as you suggest is to have a function for every level
88
this is a better choice to have high performance... (see attached file)

-Jose'-

--COB

Clark Evans wrote:
>
> On several occasions I've heard my father, an old cobol hack, cry that
> none of the current databases nor programming languages have an "88".
> Since he's my dad I often ignored him, but finally, some 5 years later
> I now know what he was talking about.   In my last contract
> assignment, I was given the job of training / helping a bunch of
> COBOL programmers to convert their code to PL/SQL.
> Their code was "beautiful"... really.  It's unfortunate that they had
> to convert to Oracle PL/SQL, which is very much inferior.  Anyway,
> in this e-mail I describe exactly what an COBOL level 88 is, and
> suggest an improvement to PostgreSQL.
>
> When defining a data structure, much like a table description,
> a COBOl programmer describes the record by listing its members
> and providing a data type for each.  Here is psuto code:
>
> 01   customer
>      10  customer_id          pic9(10)
>      10  customer_name        picX(30)
>      10  customer_status      picX(01)
>          88 active-client       value "A" "a".
>          88 historical-client   value "H" "h".
>          88 invalid-client      value "I" "i".
>          88 potential-client    value "P" "p".
>          88 current-client      value "A" "a" "P" "p"
>      10  sales_rep            pic9(10)
>
> 000-process
>     Display "processing based on 88"
>     Evaluate
>        When active-client
>           ... do stuff ...
>        When historica-client
>           ... do other stuff ...
>        When current-client
>           ... do even more stuff ...
>
> First, I'm sure this is not valid COBOL, I'm only trying to
> demonstrate what an 88 is.  In logical terms, it is a
> predicate, which takes the current fields as parameters.
>
> To write our example above (in oracle),
>
> create table customer
> ( customer_id      number(10),
>   customer_name    varchar(30),
>   customer_status  char(1),
>   sales_rep        number(10)
> );
>
> And then to select all "current"
> clients we get something like this:
>
> select customer_id, customer_name, sales_rep
> from customer
> where customer_status in ('A','a','P'.'p');
>
> Then we can progress.... right?
>
> Well, this is fine on a small scale, but when you
> have 30+ tables with code schemes like this (as I've
> seen in many-a-companies production database)
> it gets to be a horrendous nightmare.
>
> At my assignment before the last assignment, I was
> dealing with this problem, but in a rather annoying way.
> For every table, I'd define a view.  The view would
> do the decode logic... like:
>
> create view customer_view as
> select client_id, client_name, sales_rep,
>    DECODE(client_status,'A',TRUE,'a',TRUE,FALSE) active_client,
>    DECODE(client_status,'H',TRUE,'h',TRUE,FALSE) historical_client,
>    DECODE(client_status,'I',TRUE,'i',TRUE,FALSE) invalid_client,
>    DECODE(client_status,'P',TRUE,'p',TRUE,FALSE) potential_client,
>    DECODE(client_status,'A',TRUE,'a',TRUE,'P',TRUE,'p',TRUE,FALSE)
> current_client
> from customer;
>
> so that you can do:
>
> select customer_id, customer_name, sales_rep
> from customer
> where current_client IS TRUE
>
> huge improvement from a maintenance standpoint.
> A few problems:
>   * It invalidates the indexes *cry* (_big_ problem)
>   * Having IS TRUE is unsightly and annoying.
>   * Using views doubles the number of relations
>   * etc.
>
> Once you get to 30+ tables, this solution,
> aside from the index problem, is very good, but
> it could be better.
>
> I was thinking......
>
> Why couldn't we define a function (sorry I dont' have PosgreSQL
> syntax down yet... so I'm reverting to Oracle PL/SQL) like so:
>
> CREATE FUNCTION is_active_client
> ON client
> IS
> BEGIN
>   RETURN ( client_staus IN ('A','a') );
> END;
>
> Or... better yet, use the "embedded" syntax like
> constraints...
>
> create table customer
> ( customer_id      number(10),
>   customer_name    varchar(30),
>   customer_status  char(1),
>   sales_rep        number(10),
>   --
>   is_active_client customer_status in ('A','a'),
>   etc.
> );
>
> Ok, the syntax needs help, but I hope the point makes sence,
> Then we can have queries like:
>
> select customer_id, customer_name, sales_rep
> from customer
> where is_active_client;
>
> Now!  That is much much more readable and maintainable.
> Also, the "language" for functions "on" a relation could
> be limited to that which will pass through the query
> optimizer, thus the above would merely be "expanded"
> to the appropriate back end sql... traditional "functions"
> have the problem that they typically disable indexes,
> are always called, etc.  These type of boolean functions
> or predicate, if you may, would only be evaluated when
> if needed...
>
> After writing this, I guess this is similar to adding
> a "method" to the table, although I think that
> what I'm proposing is far more limited in scope.
>
> You could have these things to "existence" checks, etc.
>
> aka
>
> CREATE FUNCTION has_line_items
> ON order
> IS
> BEGIN
>    RETURN exists (select 'x' from line_items where line_items.order_no =
> order.order_no);
> END;
>
> Hmm.  I guess what'm trying to do is allow the programmer
> who defines the tables to define the "chunks of logic" that
> make sense in a where clause for the application programmers.
>
> In this way, shielding application programmers from database
> logic changes and also providing self-documentation for the system.
> It would also allow end-users with a MS Query like tool to
> be much more efficient... instead of exposing 4! = 4*3*2*1 = 24 views
> with
> all the possible permutations of queries that an end user
> might want, you could expose the table and 4 of these "predicates".
> A visual tool could then help the manager build their query.
>
> I have implemented this in a corporate system using very large
> views (over 40 additional columns)...but it quickly became painful
> and unmanageable.  I feel that 40 "predicates" would be a
> completely different story...
>
> Advanced version of this feature would allow the function
> to be valid for more than one table, if every target table
> had the required columns.  In postgreSQL, a clear strategy
> would have to be developed for such a feature to work with
> inherited classes, and a nice polymorphism scheme created.
>
> But even if it does not go that far... the "basic" version
> is more than wonderful.
>
> Anyway... what do you all think?  Sorry for being so wordy.
>
> Best,
>
> Clark--Now that we have the PL/pgSQL procedural language (thanks to Jan Wieck)
--you can do almost every thing you desire with PostgreSQL in a easy way.
--For example; to emulate COBOL level 88, as you suggest you may create
--the DECODE function as in:

--COBOL level 88 using views and decode function------------------------------

drop function decode(text,text,bool);
create function decode(text,text,bool) returns bool as '
DECLARE
    stringa text;
    substringa text;
    code text;
    value text;
    i int2;
    z int2;
BEGIN
    stringa:= $2;
    loop
            i:= position(''='' in stringa);
        if i = 0 then
            return $3;
        end if;
            z:= position('','' in stringa);
        if z = 0 then
            z:= textlen(stringa)+1;
        end if;
        value:= substr(stringa,i + 1,z - i - 1);
        code:= substr(stringa,1,i - 1);
        if $1 = code then
            return value;
        else
            if z > 0 then
                stringa:= substr(stringa,z + 1);
            else
                return $3;
            end if;
        end if;
    end loop;
END;
' language 'plpgsql';


drop table customer, customer_view;
create table customer(
    customer_id    numeric,
    customer_name    varchar(30),
    customer_status    char(1),
    sales_rep    numeric
);
insert into customer values (1,'Pippo','A',20);
insert into customer values (2,'Pluto','H',50);
insert into customer values (3,'Topolino','a',10);
insert into customer values (4,'Paperino','P',30);

create view customer_view as
    select customer_id, customer_name, sales_rep,
        DECODE (customer_status,'A=TRUE,a=TRUE',FALSE) as active_client,
        DECODE (customer_status,'H=TRUE,h=TRUE',FALSE) as historical_client,
        DECODE (customer_status,'I=TRUE,i=TRUE',FALSE) as invalid_client,
        DECODE (customer_status,'P=TRUE,p=TRUE',FALSE) as potential_client,
        DECODE (customer_status,'A=TRUE,a=TRUE,P=TRUE,p=TRUE',FALSE) as current_client
           from customer;

select customer_id, customer_name, active_client, sales_rep
from customer_view
where active_client;

select customer_id, customer_name, active_client, sales_rep
from customer_view
where not active_client;

--The other solution as you suggest is to have a function for every level 88
--this is a better choice to have high performance...

--COBOL level 88 using functions-----------------------------------------------

drop function current_client(text);
create function current_client(text) returns bool as '
DECLARE
    status ALIAS for $1;
BEGIN
    RETURN status IN (''A'',''a'',''P'',''p'');
END;
' language 'plpgsql';

drop function active_client(text);
create function active_client(text) returns bool as '
DECLARE
    status ALIAS for $1;
BEGIN
    RETURN status IN (''A'',''a'');
END;
' language 'plpgsql';

select *  from customer
where active_client(customer_status);

select *  from customer
where not active_client(customer_status);

pgsql-general by date:

Previous
From: Charles Hornberger
Date:
Subject: date('now') returns 1 year from now? -- follow-up
Next
From: "David Hartwig"
Date:
Subject: Re: [GENERAL] Removing large objects from the db (Q at bottom)