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}