chapter 10


PostgreSQL Extensions

The basic capabilities of a RDBMS are accessible using the SQL language. These capabilities include the basic data types, such and numeric, text, date, etc. and basic functions and operators, such as length, sqrt, = and like. It is possible to extend the capabilities of the database and of SQL by defining new data types and new functions. These integrate neatly into the syntax of SQL and allow the new data types and functions to be easily used in ways similar to the standard SQL data types and functions. The PostgreSQL RDBMS allows the use of various computer languages to create new functions, including a procedural language plpgsql native to PostgreSQL. The plpgsql language is analogous, but substantially different than the sqlplus language used in the Oracle RDBMS. Of course, it is possible to simply use SQL to define new data types and functions as well. In this chapter the focus is on PostgreSQL and the various languages available to extend its functionality.

Chapter 3 showed how SQL could be used to write a function to convert pressure data values expressed in atmospheres to kilopascals. Other functions were used in check constraints on a column containing CAS numbers. This chapter will show how new data types can be defined. This will require functions to define the method for input parsing and the method to output data values. There will also be functions to define operations on the new data types, enabling searches to be integrated easily with standard SQL syntax. [view wordle] [hide wordle]

Page  
109
Create Type conc As (val float, unit text);
110
Create Table assay1 (id integer, ki float, ki_unit text, ic50 float, ic50_unit text, ec50 float, ec50_unit text);
Create Table assay1 (id integer, ki conc, ic50 conc, ec50 conc);
Select ic50, ec50 From assay1 Where id = 47665;
Select (ic50).val as "ic50(nM)", (ec50).val||(ec50).unit as  ec50
   From assay1 where (ic50).val < 10 and (ic50).unit = 'nM';
111
Create Type range As (op Text, val Float);
Create Table rangetest (smiles text, ic50 range, name text); 
Select * from rangetest;
Create Function range_parse(text) Returns range As $$
   Select Case 
     When substring($1, 1, 1) = '<' Then ('<',substring($1, 2))::range
     When substring($1, 1, 1) = '>' Then ('>',substring($1, 2))::range
     When substring($1, 1, 1) = '=' Then ('=',substring($1, 2))::range
     Else ('=',substring($1, 1))::range
   End; 
$$ Language SQL;

Create Function range_text(range) Returns Text As $$ 
   Select Case 
     When ($1).q = '=' Then (($1).v)::Text 
     Else (($1).q||($1).v)::Text 
   End; 
$$ Language SQL; 
112
Insert Into rangetest (ic50) Values ( range_parse('<27.5') );
Select range_text(ic50) from rangetest;
Create Cast (range as text) With Function range_text(range) As Implicit;        
Select ic50::text from rangetest;
Create Function range_float(range) Returns Float As $$ 
   Select Case When ($1).q = '=' Then ($1).v End; 
$$ Language SQL; 
Create Cast (range as float) With Function range_float(range) As Implicit;          
Select ic50, ic50::text, ic50::float, sqrt(ic50) from rangetest; 
113
Select ic50::text, ic50::float, sqrt(ic50) from rangetest Where ic50 > 10;
Create Function range_cmp(range, range) Returns Integer As $$ 
   Select Case 
     When ($1).q = ($2).q And ($1).v = ($2).v Then 0 
     When ($1).q = '=' And ($2).q = '=' And ($1).v < ($2).v Then -1 
     When ($1).q = '=' And ($2).q = '=' And ($1).v > ($2).v Then 1 
     When ($1).v = ($2).v And ( 
         ( ($1).q = '<' And ($2).q = '=' ) Or 
         ( ($1).q = '<' And ($2).q = '>' ) Or 
         ( ($1).q = '=' And ($2).q = '>' )) Then -1 
     When ($1).v = ($2).v And ( 
         ( ($1).q = '>' And ($2).q = '=' ) Or 
         ( ($1).q = '>' And ($2).q = '<' ) Or 
         ( ($1).q = '=' And ($2).q = '<' )) Then 1 
     When ($1).v < ($2).v And ( 
         ( ($1).q = '<' And ($2).q = '=' ) Or 
         ( ($1).q = '<' And ($2).q = '>' ) Or 
         ( ($1).q = '=' And ($2).q = '>' )) Then -1 
     When ($1).v > ($2).v And ( 
         ( ($1).q = '>' And ($2).q = '=' ) Or 
         ( ($1).q = '=' And ($2).q = '<' ) Or 
         ( ($1).q = '>' And ($2).q = '<' )) Then 1 
     Else Null 
   End; 
$$ Language Sql; 
Create Function range_eq(range, range) Returns Boolean As $$ 
   Select ($1).q = ($2).q And ($1).v = ($2).v; 
$$ Language SQL; 
Create Function range_ne(range, range) Returns Boolean As $$ 
   Select ($1).q != ($2).q Or ($1).v != ($2).v; 
$$ Language SQL; 
Create Function range_lt(range, range) Returns Boolean As $$ 
   Select range_cmp($1, $2) = -1 
$$ Language SQL; 
Create Function range_le(range, range) Returns Boolean As $$ 
   Select range_cmp($1, $2) != 1 
$$ Language SQL;Create Function range_gt(range, range) Returns Boolean As $$ 
   Select range_cmp($1, $2) = 1 
$$ Language SQL; 
Create Function range_ge(range, range) Returns Boolean As $$ 
   Select range_cmp($1, $2) != -1 
$$ Language SQL; 
114
Create Operator = (Leftarg = range, Rightarg = range, 
  Procedure = range_eq, Commutator = =, Negator = !=); 
Create Operator != (Leftarg = range, Rightarg = range, 
  Procedure = range_ne, Commutator = !=, Negator = =); 
Create Operator < (Leftarg = range, Rightarg = range, 
  Procedure = range_lt, Commutator = >, Negator = >=); 
Create Operator > (Leftarg = range, Rightarg = range, 
  Procedure = range_gt, Commutator = <, Negator = <=); 
Create Operator >= (Leftarg = range, Rightarg = range, 
  Procedure = range_ge, Commutator = <=, Negator = <); 
Create Operator <= (Leftarg = range, Rightarg = range, 
  Procedure = range_le, Commutator = >=, Negator = >); 
Select ic50::text, ic50::float, sqrt(ic50) from rangetest
  Where ic50 > range_parse(10);
115
Create table ctest (smiles text, name text, coord float[][3]);
Insert Into coordtest (smiles,name,coord) Values 
   ('CN1C=C(C)C(=O)NC1=O','1-methylthymine',
    '{ 
     {-0.5223,-1.2374,0.2579}, 
     {-1.8677,-1.3917,0.1177}, 
     {-2.7245,-0.3893,-0.2291}, 
     {-2.2127,0.8622,-0.4679}, 
     {-0.8652,1.0783,-0.3284}, 
     {0.0270,0.0456,0.0406}, 
     {-3.9604,-0.6494,-0.3168}, 
     {0.1995,-2.2271,0.5751}, 
     {1.4782,0.3157,0.1674}, 
     {-3.0687,1.9631,-0.8370}
     }');
Select smiles, coord[1:2] From coordtest Where name='1-methylthymine';
       
116
Insert Into coordtest (smiles,name,coord) Values 
   ('CN1C=C(C)C(=O)NC1=O','1-methylthymine',
    '{ 
     {-0.0000,-0.8250}, 
     {0.7145,-0.4125}, 
     {0.7145,0.4125}, 
     {0.0000,0.8250}, 
     {-0.7145,0.4125}, 
     {-0.7145,-0.4125}, 
     {1.4289,0.8250}, 
     {-0.0000,-1.6500}, 
     {-1.4289,-0.8250}, 
     {0.0000,1.6500}
     }'); 
Select smiles, coord from coordtest where array_upper(coord,2) = 2;
Create Type mol (smiles Text, coord Float[][3]);
Create Table moltest (amol mol, name Text);
Insert Into moltest (amol,name) Values (
    'CN1C=C(C)C(=O)NC1=O',
    '{ 
     {-0.5223,-1.2374,0.2579}, 
     {-1.8677,-1.3917,0.1177}, 
     {-2.7245,-0.3893,-0.2291}, 
     {-2.2127,0.8622,-0.4679}, 
     {-0.8652,1.0783,-0.3284}, 
     {0.0270,0.0456,0.0406}, 
     {-3.9604,-0.6494,-0.3168}, 
     {0.1995,-2.2271,0.5751}, 
     {1.4782,0.3157,0.1674}, 
     {-3.0687,1.9631,-0.8370}
     }'),
     '1-methylthymine');
117
Create Function center(xmol mol) Returns float[3] As $$
  Declare
    centrum Float[3] := Array[0., 0., 0.];
    natoms Integer := array_upper((xmol).coord, 1);
    i Integer;
  Begin
    For i In 1 .. natoms Loop
      centrum[1] = centrum[1] + (xmol).coord[i][1];
      centrum[2] = centrum[2] + (xmol).coord[i][2];
      centrum[3] = centrum[3] + (xmol).coord[i][3];
    End Loop;
    centrum[1] = centrum[1] / natoms;
    centrum[2] = centrum[2] / natoms;
    centrum[3] = centrum[3] / natoms;
    Return centrum;
  End;
$$ Language plpgsql;
118
Select center(amol) From moltest Where name='1-methylthymine';
119
Create Or Replace Function MF(text) Returns Text As $EOPERL$ 
  use Chemistry::File::SMILES; 
  use Chemistry::File::Formula; 
  my ($s) = @_; 
  my $mol = Chemistry::Mol->parse($s, format => 'smiles'); 
  return $mol->print(format => formula); 
$EOPERL$ Language plperlu;
120
Create or Replace Function oe_smiles(Text, integer) 
  Returns Text As 'gnova', 'oe_anysmiles' 
  Language 'c' Immutable Strict; 
  Comment On Function oe_smiles(Text, integer) Is 'smiles to smiles of various types';