Example: How to design a new data type in PostgreSQL using Java

1 Complex.java

package com.example.proj;

import java.io.IOException;
import java.io.StreamTokenizer;
import java.io.StringReader;
import java.sql.SQLData;
import java.sql.SQLException;
import java.sql.SQLInput;
import java.sql.SQLOutput;
import java.util.logging.Logger;

import org.postgresql.pljava.annotation.Function;
import org.postgresql.pljava.annotation.SQLType;
import org.postgresql.pljava.annotation.BaseUDT;

import static org.postgresql.pljava.annotation.Function.Effects.IMMUTABLE;
import static
       org.postgresql.pljava.annotation.Function.OnNullInput.RETURNS_NULL;

@BaseUDT( name="complex",
         internalLength=16, alignment=BaseUDT.Alignment.DOUBLE)

public class Complex implements SQLData
{
   private double m_x;
   private double m_y;
   private String m_typeName;

   @Function(effects=IMMUTABLE, onNullInput=RETURNS_NULL)
   public static Complex parse(String input, String typeName)
   throws SQLException
   {
      try
      {
         StreamTokenizer tz = new StreamTokenizer(new StringReader(input));
         if(tz.nextToken() == '('
         && tz.nextToken() == StreamTokenizer.TT_NUMBER)
         {
            double x = tz.nval;
            if(tz.nextToken() == ','
            && tz.nextToken() == StreamTokenizer.TT_NUMBER)
            {
               double y = tz.nval;
               if(tz.nextToken() == ')')
               {
                  return new Complex(x, y, typeName);
               }
            }
         }
         throw new SQLException("Unable to parse complex from string \""
            + input + '"');
      }
      catch(IOException e)
      {
         throw new SQLException(e.getMessage());
      }
   }

   public Complex()
   {
   }

   public Complex(double x, double y, String typeName)
   {
      m_x = x;
      m_y = y;
      m_typeName = typeName;
   }

   @Override
   public String getSQLTypeName()
   {
      return m_typeName;
   }

   @Function(effects=IMMUTABLE, onNullInput=RETURNS_NULL)
   @Override
   public void readSQL(SQLInput stream, String typeName) throws SQLException
   {
      m_x = stream.readDouble();
      m_y = stream.readDouble();
      m_typeName = typeName;
   }

   @Function(effects=IMMUTABLE, onNullInput=RETURNS_NULL)
   @Override
   public void writeSQL(SQLOutput stream) throws SQLException
   {
      stream.writeDouble(m_x);
      stream.writeDouble(m_y);
   }

   @Function(effects=IMMUTABLE, onNullInput=RETURNS_NULL)
   @Override
   public String toString()
   {
      //s_logger.info(m_typeName + " toString");
      StringBuffer sb = new StringBuffer();
      sb.append('(');
      sb.append(m_x);
      sb.append(',');
      sb.append(m_y);
      sb.append(')');
      return sb.toString();
   }

    public static Complex parse1(Complex input, String typeName)
    { return new Complex(1,2,"3"); }

    public static Complex add(Complex left, Complex right)
    {
        return new Complex( left.m_x+right.m_x, left.m_y+right.m_y, left.m_typeName );     
    }

    public static boolean lessThan(Complex a, Complex b) {
        return compare(a,b) < 0;
    }


    public static boolean lessThanOrEquals(Complex a, Complex b) {
        return compare(a,b) <= 0;
    }


    public static boolean equals(Complex a, Complex b) {
        return compare(a,b) == 0;
    }


    public static boolean greaterThan(Complex a, Complex b) {
        return compare(a,b) > 0;
    }


    public static int compare( Complex left, Complex right ){
        double amag = left.m_x*left.m_x + left.m_y*left.m_y;
        double bmag = right.m_x*right.m_x + right.m_y*right.m_y;
        if (amag < bmag)
                return -1;
        if (amag > bmag)
                return 1;
        return 0;
    } 

}

2 Compile

mvn clean package

3 Load the jar

select sqlj.install_jar('file:/home/rudi/hhworkspace/mypljava/target/proj-0.0.1-SNAPSHOT.jar', 'proj', false);
select sqlj.set_classpath('public', 'proj');

4 Create the Complex Type

CREATE TYPE complex;

CREATE OR REPLACE FUNCTION complex_in(cstring, oid, integer)
        RETURNS complex
        LANGUAGE java IMMUTABLE
        RETURNS NULL ON NULL INPUT
        AS 'UDT[com.example.proj.Complex] INPUT';


CREATE OR REPLACE FUNCTION complex_out(complex)
        RETURNS cstring
        LANGUAGE java IMMUTABLE
        RETURNS NULL ON NULL INPUT
        AS 'UDT[com.example.proj.Complex] OUTPUT';

CREATE OR REPLACE FUNCTION complex_recv(internal, oid, integer)
        RETURNS complex
        LANGUAGE java IMMUTABLE
        RETURNS NULL ON NULL INPUT
        AS 'UDT[com.example.proj.Complex] RECEIVE';

CREATE OR REPLACE FUNCTION complex_send(complex)
        RETURNS bytea
        LANGUAGE java IMMUTABLE
        RETURNS NULL ON NULL INPUT
        AS 'UDT[com.example.proj.Complex] SEND';

CREATE TYPE complex (
        INPUT = complex_in,
        OUTPUT = complex_out,
        RECEIVE = complex_recv,
        SEND = complex_send,
        INTERNALLENGTH = 16,
        ALIGNMENT = DOUBLE,
        STORAGE = PLAIN
);

5 Test Complex

CREATE TABLE test_complex (
      a       complex,
      b       complex
);


INSERT INTO test_complex VALUES ('(1.0, 2.5)', '(4.2, 3.55 )');
INSERT INTO test_complex VALUES ('(33.0, 51.4)', '(100.42, 93.55)');

SELECT * FROM test_complex;

6 Support btree index

CREATE FUNCTION complex_cmp(Complex, Complex)
    RETURNS int
    AS 'com.example.proj.Complex.compare'
    LANGUAGE JAVA IMMUTABLE STRICT;

    CREATE FUNCTION complex_lt(Complex, Complex)
    RETURNS bool
    AS 'com.example.proj.Complex.lessThan'
    LANGUAGE JAVA IMMUTABLE STRICT;

CREATE FUNCTION complex_le(Complex, Complex)
    RETURNS bool
    AS 'com.example.proj.Complex.lessThanOrEquals'
    LANGUAGE JAVA IMMUTABLE STRICT;

CREATE FUNCTION complex_eq(Complex, Complex)
    RETURNS bool
    AS 'com.example.proj.Complex.equals'
    LANGUAGE JAVA IMMUTABLE STRICT;

CREATE FUNCTION complex_ge(Complex, Complex)
    RETURNS bool
    AS 'com.example.proj.Complex.greaterThanOrEquals'
    LANGUAGE JAVA IMMUTABLE STRICT;

CREATE FUNCTION complex_gt(Complex, Complex)
    RETURNS bool
    AS 'com.example.proj.Complex.greaterThan'
    LANGUAGE JAVA IMMUTABLE STRICT;

    CREATE OPERATOR < (
   leftarg = Complex, rightarg = Complex, procedure = complex_lt,
   commutator = > , negator = >= ,
   restrict = scalarltsel, join = scalarltjoinsel, merges
);

CREATE OPERATOR <= (
   leftarg = Complex, rightarg = Complex, procedure = complex_le,
   commutator = >= , negator = > , 
   restrict = scalarltsel, join = scalarltjoinsel, merges
);

CREATE OPERATOR = (
   leftarg = Complex, rightarg = Complex, procedure = complex_eq,
   commutator = = , negator = <>, hashes, merges
);

CREATE OPERATOR >= (
   leftarg = Complex, rightarg = Complex, procedure = complex_lt,
   commutator = <= , negator = < ,
   restrict = scalarltsel, join = scalarltjoinsel, merges
);

CREATE OPERATOR > (
   leftarg = Complex, rightarg = Complex, procedure = complex_le,
   commutator = <= , negator = < , 
   restrict = scalargtsel, join = scalargtjoinsel, merges
);



    -- btree join
CREATE OPERATOR CLASS complex_abs_ops
  DEFAULT FOR TYPE Complex USING btree AS
    OPERATOR        1       < ,
    OPERATOR        2       <= ,
    OPERATOR        3       = ,
    OPERATOR        4       >= ,
    OPERATOR        5       > ,
    FUNCTION        1       complex_cmp(Complex, Complex);

7 Test the index

CREATE INDEX test_cplx_ind ON test_complex
   USING btree(a complex_abs_ops);


SET enable_seqscan = OFF;


SELECT * from test_complex where a = '(56.0,-22.5)';
SELECT * from test_complex where a < '(56.0,-22.5)';
SELECT * from test_complex where a > '(56.0,-22.5)';
相關文章
相關標籤/搜索