001/* 002 * Copyright (c) 2004-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 * Tada AB 011 * Chapman Flack 012 */ 013package org.postgresql.pljava.example.annotation; 014 015import java.math.BigDecimal; 016import java.sql.Date; 017import java.sql.ResultSet; 018import java.sql.SQLException; 019import java.sql.Time; 020import java.sql.Timestamp; 021import java.text.DateFormat; 022import java.text.SimpleDateFormat; 023import java.util.TimeZone; 024import java.util.logging.Logger; 025 026import org.postgresql.pljava.annotation.Function; 027import static org.postgresql.pljava.annotation.Function.Effects.IMMUTABLE; 028import org.postgresql.pljava.annotation.SQLAction; 029import org.postgresql.pljava.annotation.SQLType; 030 031/** 032 * Some methods used for testing parameter and return value coersion and 033 * resolution of overloaded methods. 034 *<p> 035 * About the {@code @SQLAction} here: the original, hand-crafted deployment 036 * descriptor declared <em>two</em> SQL functions both implemented by the same 037 * {@link #getTimestamp() getTimestamp} method here. Only one declaration can be 038 * automatically generated from a {@code @Function} annotation on the method 039 * itself. This {@code @SQLAction} takes care of the other declaration. 040 * Of course, there is now a burden on the author to get this declaration right 041 * and to keep it up to date if the method evolves, but at least it is here in 042 * the same file, rather than in a separate hand-maintained DDR file. 043 * @author Thomas Hallgren 044 */ 045@SQLAction(install = { 046 "CREATE OR REPLACE FUNCTION javatest.java_getTimestamptz()" + 047 " RETURNS timestamptz" + 048 " AS 'org.postgresql.pljava.example.annotation.Parameters.getTimestamp'" + 049 " LANGUAGE java" 050 }, 051 remove = "DROP FUNCTION javatest.java_getTimestamptz()" 052) 053public class Parameters { 054 public static double addNumbers(short a, int b, long c, BigDecimal d, 055 BigDecimal e, float f, double g) { 056 return d.doubleValue() + e.doubleValue() + a + b + c + f + g; 057 } 058 059 public static int addOne(int value) { 060 return value + 1; 061 } 062 063 @Function(schema = "javatest", name = "java_addOne", effects = IMMUTABLE) 064 public static int addOne(Integer value) { 065 return value.intValue() + 1; 066 } 067 068 public static int addOneLong(long value) { 069 return (int) value + 1; 070 } 071 072 @Function(schema = "javatest") 073 public static int countNulls(Integer[] intArray) throws SQLException { 074 int nullCount = 0; 075 int top = intArray.length; 076 for (int idx = 0; idx < top; ++idx) { 077 if (intArray[idx] == null) 078 nullCount++; 079 } 080 return nullCount; 081 } 082 083 @Function(schema = "javatest") 084 public static int countNulls(ResultSet input) throws SQLException { 085 int nullCount = 0; 086 int top = input.getMetaData().getColumnCount(); 087 for (int idx = 1; idx <= top; ++idx) { 088 input.getObject(idx); 089 if (input.wasNull()) 090 nullCount++; 091 } 092 return nullCount; 093 } 094 095 public static Date getDate() { 096 return new Date(System.currentTimeMillis()); 097 } 098 099 public static Time getTime() { 100 return new Time(System.currentTimeMillis()); 101 } 102 103 @Function(schema = "javatest", name = "java_getTimestamp") 104 public static Timestamp getTimestamp() { 105 return new Timestamp(System.currentTimeMillis()); 106 } 107 108 static void log(String msg) { 109 Logger.getAnonymousLogger().info(msg); 110 } 111 112 @Function(schema = "javatest", effects = IMMUTABLE) 113 public static Integer nullOnEven(int value) { 114 return (value % 2) == 0 ? null : value; 115 } 116 117 /* 118 * Declare parameter and return type as the PostgreSQL-specific "char" 119 * (the quoted one, not SQL CHAR) type ... that's how it was declared 120 * in the original hand-generated deployment descriptor. PL/Java's SQL 121 * generator would otherwise have emitted smallint by default for the 122 * Java byte type. 123 * 124 * Note that the SQL rules for quoted vs. regular identifiers are complex, 125 * and PL/Java has not yet precisely specified how the identifiers given in 126 * annotations are to be treated. A future release may lay down more precise 127 * rules, which may affect code supplying quoted identifiers like this. 128 */ 129 @Function(schema = "javatest", type = "\"char\"") 130 public static byte print(@SQLType("\"char\"") byte value) { 131 log("byte " + value); 132 return value; 133 } 134 135 @Function(schema = "javatest") 136 public static byte[] print(byte[] byteArray) { 137 StringBuffer buf = new StringBuffer(); 138 int top = byteArray.length; 139 buf.append("byte[] of size " + top); 140 if (top > 0) { 141 buf.append(" {"); 142 buf.append(byteArray[0]); 143 for (int idx = 1; idx < top; ++idx) { 144 buf.append(','); 145 buf.append(byteArray[idx]); 146 } 147 buf.append('}'); 148 } 149 log(buf.toString()); 150 return byteArray; 151 } 152 153 @Function(schema = "javatest") 154 public static void print(Date value) { 155 DateFormat p = DateFormat.getDateInstance(DateFormat.FULL); 156 log("Local Date is " + p.format(value)); 157 p.setTimeZone(TimeZone.getTimeZone("UTC")); 158 log("UTC Date is " + p.format(value)); 159 log("TZ = " + TimeZone.getDefault().getDisplayName()); 160 } 161 162 @Function(schema = "javatest") 163 public static double print(double value) { 164 log("double " + value); 165 return value; 166 } 167 168 @Function(schema = "javatest") 169 public static double[] print(double[] doubleArray) { 170 StringBuffer buf = new StringBuffer(); 171 int top = doubleArray.length; 172 buf.append("double[] of size " + top); 173 if (top > 0) { 174 buf.append(" {"); 175 buf.append(doubleArray[0]); 176 for (int idx = 1; idx < top; ++idx) { 177 buf.append(','); 178 buf.append(doubleArray[idx]); 179 } 180 buf.append('}'); 181 } 182 log(buf.toString()); 183 return doubleArray; 184 } 185 186 @Function(schema = "javatest") 187 public static float print(float value) { 188 log("float " + value); 189 return value; 190 } 191 192 @Function(schema = "javatest") 193 public static float[] print(float[] floatArray) { 194 StringBuffer buf = new StringBuffer(); 195 int top = floatArray.length; 196 buf.append("float[] of size " + top); 197 if (top > 0) { 198 buf.append(" {"); 199 buf.append(floatArray[0]); 200 for (int idx = 1; idx < top; ++idx) { 201 buf.append(','); 202 buf.append(floatArray[idx]); 203 } 204 buf.append('}'); 205 } 206 log(buf.toString()); 207 return floatArray; 208 } 209 210 @Function(schema = "javatest") 211 public static int print(int value) { 212 log("int " + value); 213 return value; 214 } 215 216 @Function(schema = "javatest") 217 public static int[] print(int[] intArray) { 218 StringBuffer buf = new StringBuffer(); 219 int top = intArray.length; 220 buf.append("int[] of size " + top); 221 if (top > 0) { 222 buf.append(" {"); 223 buf.append(intArray[0]); 224 for (int idx = 1; idx < top; ++idx) { 225 buf.append(','); 226 buf.append(intArray[idx]); 227 } 228 buf.append('}'); 229 } 230 log(buf.toString()); 231 return intArray; 232 } 233 234 @Function(schema = "javatest", name = "printObj") 235 public static Integer[] print(Integer[] intArray) { 236 StringBuffer buf = new StringBuffer(); 237 int top = intArray.length; 238 buf.append("Integer[] of size " + top); 239 if (top > 0) { 240 buf.append(" {"); 241 buf.append(intArray[0]); 242 for (int idx = 1; idx < top; ++idx) { 243 buf.append(','); 244 buf.append(intArray[idx]); 245 } 246 buf.append('}'); 247 } 248 log(buf.toString()); 249 return intArray; 250 } 251 252 @Function(schema = "javatest") 253 public static long print(long value) { 254 log("long " + value); 255 return value; 256 } 257 258 @Function(schema = "javatest") 259 public static long[] print(long[] longArray) { 260 StringBuffer buf = new StringBuffer(); 261 int top = longArray.length; 262 buf.append("long[] of size " + top); 263 if (top > 0) { 264 buf.append(" {"); 265 buf.append(longArray[0]); 266 for (int idx = 1; idx < top; ++idx) { 267 buf.append(','); 268 buf.append(longArray[idx]); 269 } 270 buf.append('}'); 271 } 272 log(buf.toString()); 273 return longArray; 274 } 275 276 @Function(schema = "javatest") 277 public static short print(short value) { 278 log("short " + value); 279 return value; 280 } 281 282 @Function(schema = "javatest") 283 public static short[] print(short[] shortArray) { 284 StringBuffer buf = new StringBuffer(); 285 int top = shortArray.length; 286 buf.append("short[] of size " + top); 287 if (top > 0) { 288 buf.append(" {"); 289 buf.append(shortArray[0]); 290 for (int idx = 1; idx < top; ++idx) { 291 buf.append(','); 292 buf.append(shortArray[idx]); 293 } 294 buf.append('}'); 295 } 296 log(buf.toString()); 297 return shortArray; 298 } 299 300 /* 301 * Declare the parameter type to be timetz in SQL, to match what the 302 * original hand-crafted deployment descriptor did. The SQL generator 303 * would otherwise assume time (without time zone). 304 */ 305 @Function(schema = "javatest") 306 public static void print(@SQLType("timetz") Time value) { 307 DateFormat p = new SimpleDateFormat("HH:mm:ss z Z"); 308 log("Local Time is " + p.format(value)); 309 p.setTimeZone(TimeZone.getTimeZone("UTC")); 310 log("UTC Time is " + p.format(value)); 311 log("TZ = " + TimeZone.getDefault().getDisplayName()); 312 } 313 314 /* 315 * Declare the parameter type to be timestamptz in SQL, to match what the 316 * original hand-crafted deployment descriptor did. The SQL generator 317 * would otherwise assume timestamp (without time zone). 318 */ 319 @Function(schema = "javatest") 320 public static void print(@SQLType("timestamptz") Timestamp value) { 321 DateFormat p = DateFormat.getDateTimeInstance(DateFormat.FULL, 322 DateFormat.FULL); 323 log("Local Timestamp is " + p.format(value)); 324 p.setTimeZone(TimeZone.getTimeZone("UTC")); 325 log("UTC Timestamp is " + p.format(value)); 326 log("TZ = " + TimeZone.getDefault().getDisplayName()); 327 } 328}