001/*
002 * Copyright (c) 2016-2020 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 *   Chapman Flack
011 */
012package org.postgresql.pljava.example.annotation;
013
014import java.math.BigDecimal;
015import java.sql.Date;
016import java.sql.Time;
017import java.sql.Timestamp;
018import java.net.URL;
019import java.net.MalformedURLException;
020
021import java.io.InputStream;
022import java.io.IOException;
023import java.io.ByteArrayInputStream;
024import java.io.ByteArrayOutputStream;
025import java.io.StringReader;
026import java.nio.ByteBuffer;
027import java.nio.CharBuffer;
028import static java.nio.charset.StandardCharsets.UTF_8;
029import java.nio.charset.CharacterCodingException;
030import java.nio.charset.CharsetDecoder;
031import java.nio.charset.CharsetEncoder;
032import java.util.Arrays;
033import java.util.Scanner;
034
035import java.sql.SQLData;
036import java.sql.SQLDataException;
037import java.sql.SQLException;
038import java.sql.SQLInput;
039import java.sql.SQLOutput;
040
041import org.postgresql.pljava.annotation.Function;
042import org.postgresql.pljava.annotation.BaseUDT;
043import org.postgresql.pljava.annotation.SQLAction;
044
045import static org.postgresql.pljava.annotation.Function.Effects.IMMUTABLE;
046import static
047    org.postgresql.pljava.annotation.Function.OnNullInput.RETURNS_NULL;
048
049/**
050 * A special user-defined type simply to exercise the I/O routines.
051 *<p>
052 * There is only one 'value' of this type. Its text representation is an empty
053 * string. Its binary representation is a sequence of fixed values in all the
054 * supported JDBC data types, which it writes on output, and reads/verifies on
055 * input.
056 */
057@SQLAction(requires= { "udtscalariotest type" },
058    install = {
059        "SELECT CAST('' AS javatest.udtscalariotest)" // test send/recv
060    })
061@BaseUDT(schema="javatest", provides="udtscalariotest type")
062public class UDTScalarIOTest implements SQLData
063{
064
065    private String m_typeName;
066
067    private static BigDecimal s_bigdec = new BigDecimal(
068        "11111111111111111111111111111111111.22222222222222222222222222222222");
069
070    private static String s_gedicht =
071"Dû bist mîn, ich bin dîn:\n" +
072"des solt dû gewis sîn;\n" +
073"dû bist beslozzen in mînem herzen,\n" +
074"verlorn ist daz slüzzelîn:\n" +
075"dû muost och immer darinne sîn.";
076    private static byte[] s_utfgedicht;
077
078    private static boolean s_bool = true;
079    private static byte s_byte = 42;
080    private static Date s_date = Date.valueOf("2004-01-07");
081    private static double s_double = Math.PI;
082    private static float s_float = (float)Math.E;
083    private static int s_int = 42424242;
084    private static long s_long = 4242424242424242L;
085    private static short s_short = 4242;
086    private static Time s_time = Time.valueOf("06:33:24");
087    private static Timestamp s_timestamp =
088        Timestamp.valueOf("2004-01-07 06:33:24");
089    private static URL s_url;
090
091    static
092    {
093        try
094        {
095            s_gedicht = s_gedicht + s_gedicht + s_gedicht; // x3
096            s_gedicht = s_gedicht + s_gedicht + s_gedicht; // x9
097
098            ByteBuffer bb = UTF_8.newEncoder().encode(
099                CharBuffer.wrap(s_gedicht));
100            s_utfgedicht = new byte[bb.limit()];
101            bb.get(s_utfgedicht);
102
103            s_url = new URL("http://tada.github.io/pljava/");
104        }
105        catch ( CharacterCodingException | MalformedURLException e )
106        {
107            throw new RuntimeException(e);
108        }
109    }
110
111    @Function(effects=IMMUTABLE, onNullInput=RETURNS_NULL)
112    public static UDTScalarIOTest parse(String input, String typeName)
113            throws SQLException
114    {
115        if ( ! "".equals(input) )
116            throw new SQLDataException(
117                "The only valid text value for UDTScalarIOTest is ''", "22P02");
118        UDTScalarIOTest instance = new UDTScalarIOTest();
119        instance.m_typeName = typeName;
120        return instance;
121    }
122
123    @Function(effects=IMMUTABLE, onNullInput=RETURNS_NULL)
124    @Override
125    public String toString()
126    {
127        return "";
128    }
129
130    @Override
131    public String getSQLTypeName()
132    {
133        return m_typeName;
134    }
135
136    public UDTScalarIOTest()
137    {
138    }
139
140    @Function(effects=IMMUTABLE, onNullInput=RETURNS_NULL)
141    @Override
142    public void writeSQL(SQLOutput stream) throws SQLException
143    {
144        stream.writeBigDecimal(s_bigdec);
145        stream.writeBinaryStream(new ByteArrayInputStream(s_utfgedicht));
146        stream.writeBoolean(s_bool);
147        stream.writeByte(s_byte);
148        stream.writeBytes(s_utfgedicht);
149        stream.writeCharacterStream(new StringReader(s_gedicht));
150        stream.writeDate(s_date);
151        stream.writeDouble(s_double);
152        stream.writeFloat(s_float);
153        stream.writeInt(s_int);
154        stream.writeLong(s_long);
155        stream.writeShort(s_short);
156        stream.writeString(s_gedicht);
157        stream.writeTime(s_time);
158        stream.writeTimestamp(s_timestamp);
159        stream.writeURL(s_url);
160    }
161
162    @Function(effects=IMMUTABLE, onNullInput=RETURNS_NULL)
163    @Override
164    public void readSQL(SQLInput stream, String typeName) throws SQLException
165    {
166        m_typeName = typeName;
167
168        if ( ! s_bigdec.equals(stream.readBigDecimal()) )
169            throw new SQLException("BigDecimal mismatch");
170        ByteArrayOutputStream baos = new ByteArrayOutputStream();
171        InputStream is = stream.readBinaryStream();
172        try
173        {
174            for ( int b ; -1 != (b = is.read()) ; )
175                baos.write(b);
176        }
177        catch ( IOException e )
178        {
179            throw new SQLException("Reading binary stream",
180                "58030", e);
181        }
182        if ( ! Arrays.equals(s_utfgedicht, baos.toByteArray()) )
183            throw new SQLException("binaryStream mismatch");
184        if ( s_bool != stream.readBoolean() )
185            throw new SQLException("boolean mismatch");
186        if ( s_byte != stream.readByte() )
187            throw new SQLException("byte mismatch");
188        if ( ! Arrays.equals(s_utfgedicht, stream.readBytes()) )
189            throw new SQLException("bytes mismatch");
190        String charstream = new Scanner(stream.readCharacterStream())
191            .useDelimiter("\\A").next();
192        if ( ! s_gedicht.equals(charstream) )
193            throw new SQLException("characterStream mismatch");
194        if ( ! s_date.equals(stream.readDate()) )
195            throw new SQLException("date mismatch");
196        if ( s_double != stream.readDouble() )
197            throw new SQLException("double mismatch");
198        if ( s_float != stream.readFloat() )
199            throw new SQLException("float mismatch");
200        if ( s_int != stream.readInt() )
201            throw new SQLException("int mismatch");
202        if ( s_long != stream.readLong() )
203            throw new SQLException("long mismatch");
204        if ( s_short != stream.readShort() )
205            throw new SQLException("short mismatch");
206        if ( ! s_gedicht.equals(stream.readString()) )
207            throw new SQLException("string mismatch");
208        if ( ! s_time.equals(stream.readTime()) )
209            throw new SQLException("time mismatch");
210        if ( ! s_timestamp.equals(stream.readTimestamp()) )
211            throw new SQLException("timestamp mismatch");
212        if ( ! s_url.equals(stream.readURL()) )
213            throw new SQLException("url mismatch");
214    }
215}