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