001/*
002 * Copyright (c) 2004-2015 Tada AB and other contributors, as listed below.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the The BSD 3-Clause License
006 * which accompanies this distribution, and is available at
007 * http://opensource.org/licenses/BSD-3-Clause
008 *
009 * Contributors:
010 *   Tada AB
011 *   Chapman Flack
012 */
013package org.postgresql.pljava.example.annotation;
014
015import java.sql.Connection;
016import java.sql.DriverManager;
017import java.sql.PreparedStatement;
018import java.sql.ResultSet;
019import java.sql.SQLData;
020import java.sql.SQLException;
021import java.sql.SQLInput;
022import java.sql.SQLOutput;
023import java.util.logging.Logger;
024
025import org.postgresql.pljava.annotation.Function;
026import org.postgresql.pljava.annotation.MappedUDT;
027import org.postgresql.pljava.annotation.SQLAction;
028
029import static org.postgresql.pljava.annotation.Function.Effects.IMMUTABLE;
030import static
031    org.postgresql.pljava.annotation.Function.OnNullInput.RETURNS_NULL;
032
033/**
034 * Complex (re and im parts are doubles) implemented in Java as a mapped UDT.
035 */
036@SQLAction(requires={
037        "complextuple assertHasValues","complextuple setParameter"}, install={
038        "SELECT javatest.assertHasValues(" +
039        " CAST('(1,2)' AS javatest.complextuple), 1, 2)",
040        "SELECT javatest.setParameter()"
041    }
042)
043@MappedUDT(schema="javatest", name="complextuple",
044structure={
045    "x float8",
046    "y float8"
047})
048public class ComplexTuple implements SQLData {
049    private static Logger s_logger = Logger.getAnonymousLogger();
050
051    /**
052     * Return the same 'complextuple' passed in, logging its contents at
053     * level INFO.
054     * @param cpl any instance of this UDT
055     * @return the same instance passed in
056     */
057    @Function(schema="javatest", name="logcomplex",
058        effects=IMMUTABLE, onNullInput=RETURNS_NULL)
059    public static ComplexTuple logAndReturn(ComplexTuple cpl) {
060        s_logger.info(cpl.getSQLTypeName() + "(" + cpl.m_x + ", " + cpl.m_y
061                + ")");
062        return cpl;
063    }
064
065    /**
066     * Assert a 'complextuple' has given re and im values, to test that its
067     * representation in Java corresponds to what PostgreSQL sees.
068     * @param cpl an instance of this UDT
069     * @param re the 'real' value it should have
070     * @param im the 'imaginary' value it should have
071     * @throws SQLException if the values do not match
072     */
073    @Function(schema="javatest", provides="complextuple assertHasValues",
074        effects=IMMUTABLE, onNullInput=RETURNS_NULL)
075    public static void assertHasValues(ComplexTuple cpl, double re, double im)
076        throws SQLException
077    {
078        if ( cpl.m_x != re  ||  cpl.m_y != im )
079            throw new SQLException("assertHasValues fails");
080    }
081
082    /**
083     * Pass a 'complextuple' UDT as a parameter to a PreparedStatement
084     * that returns it, and verify that it makes the trip intact.
085     */
086    @Function(schema="javatest", provides="complextuple setParameter",
087        effects=IMMUTABLE, onNullInput=RETURNS_NULL)
088    public static void setParameter() throws SQLException
089    {
090        Connection c = DriverManager.getConnection("jdbc:default:connection");
091        PreparedStatement ps =
092            c.prepareStatement("SELECT CAST(? AS javatest.complextuple)");
093        ComplexTuple ct = new ComplexTuple();
094        ct.m_x = 1.5;
095        ct.m_y = 2.5;
096        ct.m_typeName = "javatest.complextuple";
097        ps.setObject(1, ct);
098        ResultSet rs = ps.executeQuery();
099        rs.next();
100        ct = (ComplexTuple)rs.getObject(1);
101        ps.close();
102        assertHasValues(ct, 1.5, 2.5);
103    }
104
105    private double m_x;
106    private double m_y;
107
108    private String m_typeName;
109
110    @Override
111    public String getSQLTypeName() {
112        return m_typeName;
113    }
114
115    @Override
116    public void readSQL(SQLInput stream, String typeName) throws SQLException {
117        m_typeName = typeName;
118        m_x = stream.readDouble();
119        m_y = stream.readDouble();
120        s_logger.info(typeName + " from SQLInput");
121    }
122
123    @Override
124    public void writeSQL(SQLOutput stream) throws SQLException {
125        stream.writeDouble(m_x);
126        stream.writeDouble(m_y);
127        s_logger.info(m_typeName + " to SQLOutput");
128    }
129}