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}