/* * Copyright (c) 2016 Tada AB and other contributors, as listed below. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the The BSD 3-Clause License * which accompanies this distribution, and is available at * http://opensource.org/licenses/BSD-3-Clause * * Contributors: * Chapman Flack */ package org.postgresql.pljava.example.annotation; import java.math.BigDecimal; import java.sql.Date; import java.sql.Time; import java.sql.Timestamp; import java.net.URL; import java.net.MalformedURLException; import java.io.InputStream; import java.io.IOException; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.StringReader; import java.nio.ByteBuffer; import java.nio.CharBuffer; import static java.nio.charset.StandardCharsets.UTF_8; import java.nio.charset.CharacterCodingException; import java.nio.charset.CharsetDecoder; import java.nio.charset.CharsetEncoder; import java.util.Arrays; import java.util.Scanner; import java.sql.SQLData; import java.sql.SQLDataException; import java.sql.SQLException; import java.sql.SQLInput; import java.sql.SQLOutput; import org.postgresql.pljava.annotation.Function; import org.postgresql.pljava.annotation.BaseUDT; import org.postgresql.pljava.annotation.SQLAction; import static org.postgresql.pljava.annotation.Function.Effects.IMMUTABLE; import static org.postgresql.pljava.annotation.Function.OnNullInput.RETURNS_NULL; /** * A special user-defined type simply to exercise the I/O routines. *<p> * There is only one 'value' of this type. Its text representation is an empty * string. Its binary representation is a sequence of fixed values in all the * supported JDBC data types, which it writes on output, and reads/verifies on * input. */ @SQLAction(requires={"udtscalariotest type", "udtscalariotest boot fn"}, install={ "SELECT javatest.udtscalariotest()", // force class to resolve "SELECT CAST('' AS javatest.udtscalariotest)" // test send/recv }) @BaseUDT(schema="javatest", provides="udtscalariotest type") public class UDTScalarIOTest implements SQLData { private String m_typeName; private static BigDecimal s_bigdec = new BigDecimal( "11111111111111111111111111111111111.22222222222222222222222222222222"); private static String s_gedicht = "Dû bist mîn, ich bin dîn:\n" + "des solt dû gewis sîn;\n" + "dû bist beslozzen in mînem herzen,\n" + "verlorn ist daz slüzzelîn:\n" + "dû muost och immer darinne sîn."; private static byte[] s_utfgedicht; private static boolean s_bool = true; private static byte s_byte = 42; private static Date s_date = Date.valueOf("2004-01-07"); private static double s_double = Math.PI; private static float s_float = (float)Math.E; private static int s_int = 42424242; private static long s_long = 4242424242424242L; private static short s_short = 4242; private static Time s_time = Time.valueOf("06:33:24"); private static Timestamp s_timestamp = Timestamp.valueOf("2004-01-07 06:33:24"); private static URL s_url; static { try { s_gedicht = s_gedicht + s_gedicht + s_gedicht; // x3 s_gedicht = s_gedicht + s_gedicht + s_gedicht; // x9 ByteBuffer bb = UTF_8.newEncoder().encode( CharBuffer.wrap(s_gedicht)); s_utfgedicht = new byte[bb.limit()]; bb.get(s_utfgedicht); s_url = new URL("http://tada.github.io/pljava/"); } catch ( CharacterCodingException e ) { throw new RuntimeException(e); } catch ( MalformedURLException e ) { throw new RuntimeException(e); } } @Function(effects=IMMUTABLE, onNullInput=RETURNS_NULL) public static UDTScalarIOTest parse(String input, String typeName) throws SQLException { if ( ! "".equals(input) ) throw new SQLDataException( "The only valid text value for UDTScalarIOTest is ''", "22P02"); UDTScalarIOTest instance = new UDTScalarIOTest(); instance.m_typeName = typeName; return instance; } @Function(effects=IMMUTABLE, onNullInput=RETURNS_NULL) @Override public String toString() { return ""; } @Override public String getSQLTypeName() { return m_typeName; } public UDTScalarIOTest() { } @Function(effects=IMMUTABLE, onNullInput=RETURNS_NULL) @Override public void writeSQL(SQLOutput stream) throws SQLException { stream.writeBigDecimal(s_bigdec); stream.writeBinaryStream(new ByteArrayInputStream(s_utfgedicht)); stream.writeBoolean(s_bool); stream.writeByte(s_byte); stream.writeBytes(s_utfgedicht); stream.writeCharacterStream(new StringReader(s_gedicht)); stream.writeDate(s_date); stream.writeDouble(s_double); stream.writeFloat(s_float); stream.writeInt(s_int); stream.writeLong(s_long); stream.writeShort(s_short); stream.writeString(s_gedicht); stream.writeTime(s_time); stream.writeTimestamp(s_timestamp); stream.writeURL(s_url); } @Function(effects=IMMUTABLE, onNullInput=RETURNS_NULL) @Override public void readSQL(SQLInput stream, String typeName) throws SQLException { m_typeName = typeName; if ( ! s_bigdec.equals(stream.readBigDecimal()) ) throw new SQLException("BigDecimal mismatch"); ByteArrayOutputStream baos = new ByteArrayOutputStream(); InputStream is = stream.readBinaryStream(); try { for ( int b ; -1 != (b = is.read()) ; ) baos.write(b); } catch ( IOException e ) { throw new SQLException("Reading binary stream", "58030", e); } if ( ! Arrays.equals(s_utfgedicht, baos.toByteArray()) ) throw new SQLException("binaryStream mismatch"); if ( s_bool != stream.readBoolean() ) throw new SQLException("boolean mismatch"); if ( s_byte != stream.readByte() ) throw new SQLException("byte mismatch"); if ( ! Arrays.equals(s_utfgedicht, stream.readBytes()) ) throw new SQLException("bytes mismatch"); String charstream = new Scanner(stream.readCharacterStream()) .useDelimiter("\\A").next(); if ( ! s_gedicht.equals(charstream) ) throw new SQLException("characterStream mismatch"); if ( ! s_date.equals(stream.readDate()) ) throw new SQLException("date mismatch"); if ( s_double != stream.readDouble() ) throw new SQLException("double mismatch"); if ( s_float != stream.readFloat() ) throw new SQLException("float mismatch"); if ( s_int != stream.readInt() ) throw new SQLException("int mismatch"); if ( s_long != stream.readLong() ) throw new SQLException("long mismatch"); if ( s_short != stream.readShort() ) throw new SQLException("short mismatch"); if ( ! s_gedicht.equals(stream.readString()) ) throw new SQLException("string mismatch"); if ( ! s_time.equals(stream.readTime()) ) throw new SQLException("time mismatch"); if ( ! s_timestamp.equals(stream.readTimestamp()) ) throw new SQLException("timestamp mismatch"); if ( ! s_url.equals(stream.readURL()) ) throw new SQLException("url mismatch"); } /** * A no-op function that forces the UDTScalarIOTest class to be loaded. * This is only necessary because the deployment-descriptor install * actions contain a query making use of this type, and PostgreSQL does * not expect type in/out/send/recv functions to need an updated * snapshot, so it will try to find this class in the snapshot from * before the jar was installed, and fail. By providing this function, * which defaults to volatile so it gets an updated snapshot, and * calling it first, the class will be found and loaded; once it is * loaded, the user-defined type operations are able to find it. * <p> * Again, this is only an issue when trying to make use of the newly * loaded UDT from right within the deployment descriptor for the jar. */ @Function(schema="javatest", provides="udtscalariotest boot fn") public static void udtscalariotest() { } }