KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > mckoi > database > InternalFunctionFactory


1 /**
2  * com.mckoi.database.InternalFunctionFactory 19 Sep 2000
3  *
4  * Mckoi SQL Database ( http://www.mckoi.com/database )
5  * Copyright (C) 2000, 2001, 2002 Diehl and Associates, Inc.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * Version 2 as published by the Free Software Foundation.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License Version 2 for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * Version 2 along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19  *
20  * Change Log:
21  *
22  *
23  */

24
25 package com.mckoi.database;
26
27 import java.lang.reflect.*;
28 import java.math.BigDecimal JavaDoc;
29 import java.util.ArrayList JavaDoc;
30 import java.util.HashMap JavaDoc;
31 import java.util.Date JavaDoc;
32 import java.util.Calendar JavaDoc;
33 import java.util.Comparator JavaDoc;
34 import java.util.Arrays JavaDoc;
35 import java.util.Locale JavaDoc;
36 import java.text.*;
37 import java.io.InputStream JavaDoc;
38 import java.io.IOException JavaDoc;
39 import com.mckoi.util.Cache;
40 import com.mckoi.database.global.SQLTypes;
41 import com.mckoi.database.global.CastHelper;
42 import com.mckoi.database.global.ByteLongObject;
43 import com.mckoi.database.global.BlobAccessor;
44 import com.mckoi.database.global.StringObject;
45 import com.mckoi.database.global.StringAccessor;
46 import com.mckoi.database.global.ObjectTranslator;
47 import com.mckoi.database.jdbc.SQLQuery;
48 import com.mckoi.util.BigNumber;
49
50 /**
51  * A FunctionFactory for all internal SQL functions (including aggregate,
52  * mathematical, string functions). This FunctionFactory is registered with
53  * the DatabaseSystem during initialization.
54  *
55  * @author Tobias Downer
56  */

57
58 final class InternalFunctionFactory extends FunctionFactory {
59
60   /**
61    * Registers the function classes with this factory.
62    */

63   public void init() {
64
65     // Parses a date/time/timestamp string
66
addFunction("dateob", DateObFunction.class);
67     addFunction("timeob", TimeObFunction.class);
68     addFunction("timestampob", TimeStampObFunction.class);
69     addFunction("dateformat", DateFormatFunction.class);
70
71     // Casting functions
72
addFunction("tonumber", ToNumberFunction.class);
73     addFunction("sql_cast", SQLCastFunction.class);
74     // String functions
75
addFunction("lower", LowerFunction.class);
76     addFunction("upper", UpperFunction.class);
77     addFunction("concat", ConcatFunction.class);
78     addFunction("length", LengthFunction.class);
79     addFunction("substring", SubstringFunction.class);
80     addFunction("sql_trim", SQLTrimFunction.class);
81     addFunction("ltrim", LTrimFunction.class);
82     addFunction("rtrim", RTrimFunction.class);
83     // Security
84
addFunction("user", UserFunction.class);
85     addFunction("privgroups", PrivGroupsFunction.class);
86     // Aggregate
87
addFunction("count", CountFunction.class, FunctionInfo.AGGREGATE);
88     addFunction("distinct_count",
89                 DistinctCountFunction.class, FunctionInfo.AGGREGATE);
90     addFunction("avg", AvgFunction.class, FunctionInfo.AGGREGATE);
91     addFunction("sum", SumFunction.class, FunctionInfo.AGGREGATE);
92     addFunction("min", MinFunction.class, FunctionInfo.AGGREGATE);
93     addFunction("max", MaxFunction.class, FunctionInfo.AGGREGATE);
94     addFunction("aggor", AggOrFunction.class, FunctionInfo.AGGREGATE);
95     // Mathematical
96
addFunction("abs", AbsFunction.class);
97     addFunction("sign", SignFunction.class);
98     addFunction("mod", ModFunction.class);
99     addFunction("round", RoundFunction.class);
100     addFunction("pow", PowFunction.class);
101     addFunction("sqrt", SqrtFunction.class);
102     // Sequence operations
103
addFunction("uniquekey",
104                 UniqueKeyFunction.class, FunctionInfo.STATE_BASED);
105     addFunction("nextval",
106                 NextValFunction.class, FunctionInfo.STATE_BASED);
107     addFunction("currval",
108                 CurrValFunction.class, FunctionInfo.STATE_BASED);
109     addFunction("setval",
110                 SetValFunction.class, FunctionInfo.STATE_BASED);
111     // Misc
112
addFunction("hextobinary", HexToBinaryFunction.class);
113     addFunction("binarytohex", BinaryToHexFunction.class);
114     // Lists
115
addFunction("least", LeastFunction.class);
116     addFunction("greatest", GreatestFunction.class);
117     // Branch
118
addFunction("if", IfFunction.class);
119     addFunction("coalesce", CoalesceFunction.class);
120
121     // Object instantiation (Internal)
122
addFunction("_new_JavaObject", JavaObjectInstantiation2.class);
123
124     // Internal functions
125
addFunction("i_frule_convert", ForeignRuleConvert.class);
126     addFunction("i_sql_type", SQLTypeString.class);
127     addFunction("i_view_data", ViewDataConvert.class);
128     addFunction("i_privilege_string", PrivilegeString.class);
129     
130   }
131
132
133   // ---------- The internal functions ----------
134

135   // ---------- Grouping functions ----------
136

137   private static class CountFunction extends AbstractFunction {
138
139     public CountFunction(Expression[] params) {
140       super("count", params);
141       setAggregate(true);
142
143       if (parameterCount() != 1) {
144         throw new RuntimeException JavaDoc("'count' function must have one argument.");
145       }
146     }
147
148     public TObject evaluate(GroupResolver group, VariableResolver resolver,
149                             QueryContext context) {
150       if (group == null) {
151         throw new RuntimeException JavaDoc(
152                          "'count' can only be used as an aggregate function.");
153       }
154
155       int size = group.size();
156       TObject result;
157       // if, count(*)
158
if (size == 0 || isGlob()) {
159         result = TObject.intVal(size);
160       }
161       else {
162         // Otherwise we need to count the number of non-null entries in the
163
// columns list(s).
164

165         int total_count = size;
166
167         Expression exp = getParameter(0);
168         for (int i = 0; i < size; ++i) {
169           TObject val =
170                     exp.evaluate(null, group.getVariableResolver(i), context);
171           if (val.isNull()) {
172             --total_count;
173           }
174         }
175
176         result = TObject.intVal(total_count);
177       }
178
179       return result;
180     }
181
182   }
183
184   // --
185

186   private static class DistinctCountFunction extends AbstractFunction {
187
188     public DistinctCountFunction(Expression[] params) {
189       super("distinct_count", params);
190       setAggregate(true);
191
192       if (parameterCount() <= 0) {
193         throw new RuntimeException JavaDoc(
194                "'distinct_count' function must have at least one argument.");
195       }
196
197     }
198
199     public TObject evaluate(GroupResolver group, VariableResolver resolver,
200                             QueryContext context) {
201       // There's some issues with implementing this function.
202
// For this function to be efficient, we need to have access to the
203
// underlying Table object(s) so we can use table indexing to sort the
204
// columns. Otherwise, we will need to keep in memory the group
205
// contents so it can be sorted. Or alternatively (and probably worst
206
// of all) don't store in memory, but use an expensive iterative search
207
// for non-distinct rows.
208
//
209
// An iterative search will be terrible for large groups with mostly
210
// distinct rows. But would be okay for large groups with few distinct
211
// rows.
212

213       if (group == null) {
214         throw new RuntimeException JavaDoc(
215                         "'count' can only be used as an aggregate function.");
216       }
217
218       final int rows = group.size();
219       if (rows <= 1) {
220         // If count of entries in group is 0 or 1
221
return TObject.intVal(rows);
222       }
223
224       // Make an array of all cells in the group that we are finding which
225
// are distinct.
226
final int cols = parameterCount();
227       final TObject[] group_r = new TObject[rows * cols];
228       int n = 0;
229       for (int i = 0; i < rows; ++i) {
230         VariableResolver vr = group.getVariableResolver(i);
231         for (int p = 0; p < cols; ++p) {
232           Expression exp = getParameter(p);
233           group_r[n + p] = exp.evaluate(null, vr, context);
234         }
235         n += cols;
236       }
237
238       // A comparator that sorts this set,
239
Comparator JavaDoc c = new Comparator JavaDoc() {
240         public int compare(Object JavaDoc ob1, Object JavaDoc ob2) {
241           int r1 = ((Integer JavaDoc) ob1).intValue();
242           int r2 = ((Integer JavaDoc) ob2).intValue();
243
244           // Compare row r1 with r2
245
int index1 = r1 * cols;
246           int index2 = r2 * cols;
247           for (int n = 0; n < cols; ++n) {
248             int v = group_r[index1 + n].compareTo(group_r[index2 + n]);
249             if (v != 0) {
250               return v;
251             }
252           }
253
254           // If we got here then rows must be equal.
255
return 0;
256         }
257       };
258
259       // The list of indexes,
260
Object JavaDoc[] list = new Object JavaDoc[rows];
261       for (int i = 0; i < rows; ++i) {
262         list[i] = new Integer JavaDoc(i);
263       }
264
265       // Sort the list,
266
Arrays.sort(list, c);
267
268       // The count of distinct elements, (there will always be at least 1)
269
int distinct_count = 1;
270       for (int i = 1; i < rows; ++i) {
271         int v = c.compare(list[i], list[i - 1]);
272         // If v == 0 then entry is not distinct with the previous element in
273
// the sorted list therefore the distinct counter is not incremented.
274
if (v > 0) {
275           // If current entry is greater than previous then we've found a
276
// distinct entry.
277
++distinct_count;
278         }
279         else if (v < 0) {
280           // The current element should never be less if list is sorted in
281
// ascending order.
282
throw new Error JavaDoc("Assertion failed - the distinct list does not " +
283                           "appear to be sorted.");
284         }
285       }
286
287       // If the first entry in the list is NULL then subtract 1 from the
288
// distinct count because we shouldn't be counting NULL entries.
289
if (list.length > 0) {
290         int first_entry = ((Integer JavaDoc) list[0]).intValue();
291         // Assume first is null
292
boolean first_is_null = true;
293         for (int m = 0; m < cols && first_is_null == true; ++m) {
294           TObject val = group_r[(first_entry * cols) + m];
295           if (!val.isNull()) {
296             // First isn't null
297
first_is_null = false;
298           }
299         }
300         // Is first NULL?
301
if (first_is_null) {
302           // decrease distinct count so we don't count the null entry.
303
distinct_count = distinct_count - 1;
304         }
305       }
306
307       return TObject.intVal(distinct_count);
308     }
309
310   }
311
312   // --
313

314   private static class AvgFunction extends AbstractAggregateFunction {
315
316     public AvgFunction(Expression[] params) {
317       super("avg", params);
318     }
319
320     public TObject evalAggregate(GroupResolver group, QueryContext context,
321                                  TObject ob1, TObject ob2) {
322       // This will sum,
323
if (ob1 != null) {
324         if (ob2.isNull()) {
325           return ob1;
326         }
327         else {
328           if (!ob1.isNull()) {
329             return ob1.operatorAdd(ob2);
330           }
331           else {
332             return ob2;
333           }
334         }
335       }
336       return ob2;
337     }
338
339     public TObject postEvalAggregate(GroupResolver group, QueryContext context,
340                                      TObject result) {
341       // Find the average from the sum result
342
if (result.isNull()) {
343         return result;
344       }
345       return result.operatorDivide(TObject.intVal(group.size()));
346     }
347
348   }
349
350   // --
351

352   private static class SumFunction extends AbstractAggregateFunction {
353
354     public SumFunction(Expression[] params) {
355       super("sum", params);
356     }
357
358     public TObject evalAggregate(GroupResolver group, QueryContext context,
359                                  TObject ob1, TObject ob2) {
360       // This will sum,
361
if (ob1 != null) {
362         if (ob2.isNull()) {
363           return ob1;
364         }
365         else {
366           if (!ob1.isNull()) {
367             return ob1.operatorAdd(ob2);
368           }
369           else {
370             return ob2;
371           }
372         }
373       }
374       return ob2;
375     }
376
377   }
378
379   // --
380

381   private static class MinFunction extends AbstractAggregateFunction {
382
383     public MinFunction(Expression[] params) {
384       super("min", params);
385     }
386
387     public TObject evalAggregate(GroupResolver group, QueryContext context,
388                                  TObject ob1, TObject ob2) {
389       // This will find min,
390
if (ob1 != null) {
391         if (ob2.isNull()) {
392           return ob1;
393         }
394         else {
395           if (!ob1.isNull() && ob1.compareToNoNulls(ob2) < 0) {
396             return ob1;
397           }
398           else {
399             return ob2;
400           }
401         }
402       }
403       return ob2;
404     }
405
406     public TType returnTType(VariableResolver resolver, QueryContext context) {
407       // Set to return the same type object as this variable.
408
return getParameter(0).returnTType(resolver, context);
409     }
410
411   }
412
413   // --
414

415   private static class MaxFunction extends AbstractAggregateFunction {
416
417     public MaxFunction(Expression[] params) {
418       super("max", params);
419     }
420
421     public TObject evalAggregate(GroupResolver group, QueryContext context,
422                                  TObject ob1, TObject ob2) {
423       // This will find max,
424
if (ob1 != null) {
425         if (ob2.isNull()) {
426           return ob1;
427         }
428         else {
429           if (!ob1.isNull() && ob1.compareToNoNulls(ob2) > 0) {
430             return ob1;
431           }
432           else {
433             return ob2;
434           }
435         }
436       }
437       return ob2;
438     }
439
440     public TType returnTType(VariableResolver resolver, QueryContext context) {
441       // Set to return the same type object as this variable.
442
return getParameter(0).returnTType(resolver, context);
443     }
444
445   }
446
447   // --
448

449   private static class AggOrFunction extends AbstractAggregateFunction {
450
451     public AggOrFunction(Expression[] params) {
452       super("aggor", params);
453     }
454
455     public TObject evalAggregate(GroupResolver group, QueryContext context,
456                                  TObject ob1, TObject ob2) {
457       // Assuming bitmap numbers, this will find the result of or'ing all the
458
// values in the aggregate set.
459
if (ob1 != null) {
460         if (ob2.isNull()) {
461           return ob1;
462         }
463         else {
464           if (!ob1.isNull()) {
465             return ob1.operatorOr(ob2);
466           }
467           else {
468             return ob2;
469           }
470         }
471       }
472       return ob2;
473     }
474
475   }
476
477
478
479   // ---------- User functions ----------
480

481   // Returns the user name
482
private static class UserFunction extends AbstractFunction {
483
484     public UserFunction(Expression[] params) {
485       super("user", params);
486
487       if (parameterCount() > 0) {
488         throw new RuntimeException JavaDoc("'user' function must have no arguments.");
489       }
490     }
491
492     public TObject evaluate(GroupResolver group, VariableResolver resolver,
493                             QueryContext context) {
494       return TObject.stringVal(context.getUserName());
495     }
496
497     public TType returnTType() {
498       return TType.STRING_TYPE;
499     }
500
501   }
502
503   // Returns the comma (",") deliminated priv groups the user belongs to.
504
private static class PrivGroupsFunction extends AbstractFunction {
505
506     public PrivGroupsFunction(Expression[] params) {
507       super("privgroups", params);
508
509       if (parameterCount() > 0) {
510         throw new RuntimeException JavaDoc(
511                             "'privgroups' function must have no arguments.");
512       }
513     }
514
515     public TObject evaluate(GroupResolver group, VariableResolver resolver,
516                             QueryContext context) {
517       throw new RuntimeException JavaDoc(
518                             "'PrivGroups' function currently not working.");
519     }
520
521     public TType returnTType() {
522       return TType.STRING_TYPE;
523     }
524
525   }
526
527
528
529
530
531
532   // ---------- String functions ----------
533

534   private static class LowerFunction extends AbstractFunction {
535
536     public LowerFunction(Expression[] params) {
537       super("lower", params);
538
539       if (parameterCount() != 1) {
540         throw new RuntimeException JavaDoc("Lower function must have one argument.");
541       }
542     }
543
544     public TObject evaluate(GroupResolver group, VariableResolver resolver,
545                             QueryContext context) {
546       TObject ob = getParameter(0).evaluate(group, resolver, context);
547       if (ob.isNull()) {
548         return ob;
549       }
550       return new TObject(ob.getTType(),
551                          ob.getObject().toString().toLowerCase());
552     }
553
554     public TType returnTType() {
555       return TType.STRING_TYPE;
556     }
557
558   }
559
560   // --
561

562   private static class UpperFunction extends AbstractFunction {
563
564     public UpperFunction(Expression[] params) {
565       super("upper", params);
566
567       if (parameterCount() != 1) {
568         throw new RuntimeException JavaDoc("Upper function must have one argument.");
569       }
570     }
571
572     public TObject evaluate(GroupResolver group, VariableResolver resolver,
573                             QueryContext context) {
574       TObject ob = getParameter(0).evaluate(group, resolver, context);
575       if (ob.isNull()) {
576         return ob;
577       }
578       return new TObject(ob.getTType(),
579                          ob.getObject().toString().toUpperCase());
580     }
581
582     public TType returnTType() {
583       return TType.STRING_TYPE;
584     }
585
586   }
587
588   // --
589

590   private static class ConcatFunction extends AbstractFunction {
591
592     public ConcatFunction(Expression[] params) {
593       super("concat", params);
594
595       if (parameterCount() < 1) {
596         throw new RuntimeException JavaDoc(
597                          "Concat function must have at least one argument.");
598       }
599     }
600
601     public TObject evaluate(GroupResolver group, VariableResolver resolver,
602                             QueryContext context) {
603       StringBuffer JavaDoc cc = new StringBuffer JavaDoc();
604       
605       Locale JavaDoc str_locale = null;
606       int str_strength = 0;
607       int str_decomposition = 0;
608       for (int i = 0; i < parameterCount(); ++i) {
609         Expression cur_parameter = getParameter(i);
610         TObject ob = cur_parameter.evaluate(group, resolver, context);
611         if (!ob.isNull()) {
612           cc.append(ob.getObject().toString());
613           TType type = ob.getTType();
614           if (str_locale == null && type instanceof TStringType) {
615             TStringType str_type = (TStringType) type;
616             str_locale = str_type.getLocale();
617             str_strength = str_type.getStrength();
618             str_decomposition = str_type.getDecomposition();
619           }
620         }
621         else {
622           return ob;
623         }
624       }
625       
626       // We inherit the locale from the first string parameter with a locale,
627
// or use a default STRING_TYPE if no locale found.
628
TType type;
629       if (str_locale != null) {
630         type = new TStringType(SQLTypes.VARCHAR, -1,
631                                str_locale, str_strength, str_decomposition);
632       }
633       else {
634         type = TType.STRING_TYPE;
635       }
636       
637       return new TObject(type, new String JavaDoc(cc));
638     }
639
640     public TType returnTType(VariableResolver resolver, QueryContext context) {
641       // Determine the locale of the first string parameter.
642
Locale JavaDoc str_locale = null;
643       int str_strength = 0;
644       int str_decomposition = 0;
645       for (int i = 0; i < parameterCount() && str_locale == null; ++i) {
646         TType type = getParameter(i).returnTType(resolver, context);
647         if (type instanceof TStringType) {
648           TStringType str_type = (TStringType) type;
649           str_locale = str_type.getLocale();
650           str_strength = str_type.getStrength();
651           str_decomposition = str_type.getDecomposition();
652         }
653       }
654       
655       if (str_locale != null) {
656         return new TStringType(SQLTypes.VARCHAR, -1,
657                                str_locale, str_strength, str_decomposition);
658       }
659       else {
660         return TType.STRING_TYPE;
661       }
662     }
663
664   }
665
666   // --
667

668   private static class LengthFunction extends AbstractFunction {
669
670     public LengthFunction(Expression[] params) {
671       super("length", params);
672
673       if (parameterCount() != 1) {
674         throw new RuntimeException JavaDoc("Length function must have one argument.");
675       }
676     }
677
678     public TObject evaluate(GroupResolver group, VariableResolver resolver,
679                             QueryContext context) {
680       TObject ob = getParameter(0).evaluate(group, resolver, context);
681       if (ob.isNull()) {
682         return ob;
683       }
684       if (ob.getTType() instanceof TBinaryType) {
685         BlobAccessor blob = (BlobAccessor) ob.getObject();
686         return TObject.intVal(blob.length());
687       }
688       if (ob.getTType() instanceof TStringType) {
689         StringAccessor str = (StringAccessor) ob.getObject();
690         return TObject.intVal(str.length());
691       }
692       return TObject.intVal(ob.getObject().toString().length());
693     }
694
695   }
696
697   // --
698

699   private static class SubstringFunction extends AbstractFunction {
700
701     public SubstringFunction(Expression[] params) {
702       super("substring", params);
703
704       if (parameterCount() < 1 || parameterCount() > 3) {
705         throw new RuntimeException JavaDoc(
706                           "Substring function needs one to three arguments.");
707       }
708     }
709
710     public TObject evaluate(GroupResolver group, VariableResolver resolver,
711                             QueryContext context) {
712       TObject ob = getParameter(0).evaluate(group, resolver, context);
713       if (ob.isNull()) {
714         return ob;
715       }
716       String JavaDoc str = ob.getObject().toString();
717       int pcount = parameterCount();
718       int str_length = str.length();
719       int arg1 = 1;
720       int arg2 = str_length;
721       if (pcount >= 2) {
722         arg1 = getParameter(1).evaluate(group, resolver,
723                                         context).toBigNumber().intValue();
724       }
725       if (pcount >= 3) {
726         arg2 = getParameter(2).evaluate(group, resolver,
727                                         context).toBigNumber().intValue();
728 // arg2 = Operator.toNumber(
729
// getParameter(2).evaluate(group, resolver, context)).intValue();
730
}
731
732       // Make sure this call is safe for all lengths of string.
733
if (arg1 < 1) {
734         arg1 = 1;
735       }
736       if (arg1 > str_length) {
737         return TObject.stringVal("");
738       }
739       if (arg2 + arg1 > str_length) {
740         arg2 = (str_length - arg1) + 1;
741       }
742       if (arg2 < 1) {
743         return TObject.stringVal("");
744       }
745
746       return TObject.stringVal(str.substring(arg1 - 1, (arg1 + arg2) - 1));
747     }
748
749     public TType returnTType() {
750       return TType.STRING_TYPE;
751     }
752
753   }
754
755   // --
756

757   private static class SQLTrimFunction extends AbstractFunction {
758
759     public SQLTrimFunction(Expression[] params) {
760       super("sql_trim", params);
761
762 // System.out.println(parameterCount());
763
if (parameterCount() != 3) {
764         throw new RuntimeException JavaDoc(
765                             "SQL Trim function must have three parameters.");
766       }
767     }
768
769     public TObject evaluate(GroupResolver group, VariableResolver resolver,
770                             QueryContext context) {
771       // The type of trim (leading, both, trailing)
772
TObject ttype = getParameter(0).evaluate(group, resolver, context);
773       // Characters to trim
774
TObject cob = getParameter(1).evaluate(group, resolver, context);
775       if (cob.isNull()) {
776         return cob;
777       }
778       else if (ttype.isNull()) {
779         return TObject.stringVal((StringObject) null);
780       }
781       String JavaDoc characters = cob.getObject().toString();
782       String JavaDoc ttype_str = ttype.getObject().toString();
783       // The content to trim.
784
TObject ob = getParameter(2).evaluate(group, resolver, context);
785       if (ob.isNull()) {
786         return ob;
787       }
788       String JavaDoc str = ob.getObject().toString();
789
790       int skip = characters.length();
791       // Do the trim,
792
if (ttype_str.equals("leading") || ttype_str.equals("both")) {
793         // Trim from the start.
794
int scan = 0;
795         while (scan < str.length() &&
796                str.indexOf(characters, scan) == scan) {
797           scan += skip;
798         }
799         str = str.substring(Math.min(scan, str.length()));
800       }
801       if (ttype_str.equals("trailing") || ttype_str.equals("both")) {
802         // Trim from the end.
803
int scan = str.length() - 1;
804         int i = str.lastIndexOf(characters, scan);
805         while (scan >= 0 && i != -1 && i == scan - skip + 1) {
806           scan -= skip;
807           i = str.lastIndexOf(characters, scan);
808         }
809         str = str.substring(0, Math.max(0, scan + 1));
810       }
811
812       return TObject.stringVal(str);
813     }
814
815     public TType returnTType(VariableResolver resolver, QueryContext context) {
816       return TType.STRING_TYPE;
817     }
818
819   }
820
821   // --
822

823   private static class LTrimFunction extends AbstractFunction {
824
825     public LTrimFunction(Expression[] params) {
826       super("ltrim", params);
827
828       if (parameterCount() != 1) {
829         throw new RuntimeException JavaDoc(
830                                 "ltrim function may only have 1 parameter.");
831       }
832     }
833
834     public TObject evaluate(GroupResolver group, VariableResolver resolver,
835                             QueryContext context) {
836       TObject ob = getParameter(0).evaluate(group, resolver, context);
837       if (ob.isNull()) {
838         return ob;
839       }
840       String JavaDoc str = ob.getObject().toString();
841
842       // Do the trim,
843
// Trim from the start.
844
int scan = 0;
845       while (scan < str.length() &&
846              str.indexOf(' ', scan) == scan) {
847         scan += 1;
848       }
849       str = str.substring(Math.min(scan, str.length()));
850
851       return TObject.stringVal(str);
852     }
853
854     public TType returnTType(VariableResolver resolver, QueryContext context) {
855       return TType.STRING_TYPE;
856     }
857
858   }
859
860   // --
861

862   private static class RTrimFunction extends AbstractFunction {
863
864     public RTrimFunction(Expression[] params) {
865       super("rtrim", params);
866
867       if (parameterCount() != 1) {
868         throw new RuntimeException JavaDoc(
869                                  "rtrim function may only have 1 parameter.");
870       }
871     }
872
873     public TObject evaluate(GroupResolver group, VariableResolver resolver,
874                             QueryContext context) {
875       TObject ob = getParameter(0).evaluate(group, resolver, context);
876       if (ob.isNull()) {
877         return ob;
878       }
879       String JavaDoc str = ob.getObject().toString();
880
881       // Do the trim,
882
// Trim from the end.
883
int scan = str.length() - 1;
884       int i = str.lastIndexOf(" ", scan);
885       while (scan >= 0 && i != -1 && i == scan - 2) {
886         scan -= 1;
887         i = str.lastIndexOf(" ", scan);
888       }
889       str = str.substring(0, Math.max(0, scan + 1));
890
891       return TObject.stringVal(str);
892     }
893
894     public TType returnTType(VariableResolver resolver, QueryContext context) {
895       return TType.STRING_TYPE;
896     }
897
898   }
899
900
901
902
903
904
905
906
907   // ---------- Mathematical functions ----------
908

909   private static class AbsFunction extends AbstractFunction {
910
911     public AbsFunction(Expression[] params) {
912       super("abs", params);
913
914       if (parameterCount() != 1) {
915         throw new RuntimeException JavaDoc("Abs function must have one argument.");
916       }
917     }
918
919     public TObject evaluate(GroupResolver group, VariableResolver resolver,
920                             QueryContext context) {
921       TObject ob = getParameter(0).evaluate(group, resolver, context);
922       if (ob.isNull()) {
923         return ob;
924       }
925       BigNumber num = ob.toBigNumber();
926       return TObject.bigNumberVal(num.abs());
927     }
928
929   }
930
931   // --
932

933   private static class SignFunction extends AbstractFunction {
934
935     public SignFunction(Expression[] params) {
936       super("sign", params);
937
938       if (parameterCount() != 1) {
939         throw new RuntimeException JavaDoc("Sign function must have one argument.");
940       }
941     }
942
943     public TObject evaluate(GroupResolver group, VariableResolver resolver,
944                             QueryContext context) {
945       TObject ob = getParameter(0).evaluate(group, resolver, context);
946       if (ob.isNull()) {
947         return ob;
948       }
949       BigNumber num = ob.toBigNumber();
950       return TObject.intVal(num.signum());
951     }
952
953   }
954
955   // --
956

957   private static class ModFunction extends AbstractFunction {
958
959     public ModFunction(Expression[] params) {
960       super("mod", params);
961
962       if (parameterCount() != 2) {
963         throw new RuntimeException JavaDoc("Mod function must have two arguments.");
964       }
965     }
966
967     public TObject evaluate(GroupResolver group, VariableResolver resolver,
968                             QueryContext context) {
969       TObject ob1 = getParameter(0).evaluate(group, resolver, context);
970       TObject ob2 = getParameter(1).evaluate(group, resolver, context);
971       if (ob1.isNull()) {
972         return ob1;
973       }
974       else if (ob2.isNull()) {
975         return ob2;
976       }
977
978       double v = ob1.toBigNumber().doubleValue();
979       double m = ob2.toBigNumber().doubleValue();
980       return TObject.doubleVal(v % m);
981     }
982
983   }
984
985   // --
986

987   private static class RoundFunction extends AbstractFunction {
988
989     public RoundFunction(Expression[] params) {
990       super("round", params);
991
992       if (parameterCount() < 1 || parameterCount() > 2) {
993         throw new RuntimeException JavaDoc(
994                           "Round function must have one or two arguments.");
995       }
996     }
997
998     public TObject evaluate(GroupResolver group, VariableResolver resolver,
999                             QueryContext context) {
1000      TObject ob1 = getParameter(0).evaluate(group, resolver, context);
1001      if (ob1.isNull()) {
1002        return ob1;
1003      }
1004
1005      BigNumber v = ob1.toBigNumber();
1006      int d = 0;
1007      if (parameterCount() == 2) {
1008        TObject ob2 = getParameter(1).evaluate(group, resolver, context);
1009        if (ob2.isNull()) {
1010          d = 0;
1011        }
1012        else {
1013          d = ob2.toBigNumber().intValue();
1014        }
1015      }
1016      return TObject.bigNumberVal(v.setScale(d, BigDecimal.ROUND_HALF_UP));
1017    }
1018
1019  }
1020
1021  // --
1022

1023  private static class PowFunction extends AbstractFunction {
1024
1025    public PowFunction(Expression[] params) {
1026      super("pow", params);
1027
1028      if (parameterCount() != 2) {
1029        throw new RuntimeException JavaDoc("Pow function must have two arguments.");
1030      }
1031    }
1032
1033    public TObject evaluate(GroupResolver group, VariableResolver resolver,
1034                            QueryContext context) {
1035      TObject ob1 = getParameter(0).evaluate(group, resolver, context);
1036      TObject ob2 = getParameter(1).evaluate(group, resolver, context);
1037      if (ob1.isNull()) {
1038        return ob1;
1039      }
1040      else if (ob2.isNull()) {
1041        return ob2;
1042      }
1043
1044      double v = ob1.toBigNumber().doubleValue();
1045      double w = ob2.toBigNumber().doubleValue();
1046      return TObject.doubleVal(Math.pow(v, w));
1047    }
1048
1049  }
1050
1051  // --
1052

1053  private static class SqrtFunction extends AbstractFunction {
1054
1055    public SqrtFunction(Expression[] params) {
1056      super("sqrt", params);
1057
1058      if (parameterCount() != 1) {
1059        throw new RuntimeException JavaDoc("Sqrt function must have one argument.");
1060      }
1061    }
1062
1063    public TObject evaluate(GroupResolver group, VariableResolver resolver,
1064                            QueryContext context) {
1065      TObject ob = getParameter(0).evaluate(group, resolver, context);
1066      if (ob.isNull()) {
1067        return ob;
1068      }
1069
1070      return TObject.bigNumberVal(ob.toBigNumber().sqrt());
1071    }
1072
1073  }
1074
1075  // --
1076

1077  private static class LeastFunction extends AbstractFunction {
1078
1079    public LeastFunction(Expression[] params) {
1080      super("least", params);
1081
1082      if (parameterCount() < 1) {
1083        throw new RuntimeException JavaDoc(
1084                             "Least function must have at least 1 argument.");
1085      }
1086    }
1087
1088    public TObject evaluate(GroupResolver group, VariableResolver resolver,
1089                            QueryContext context) {
1090      TObject least = null;
1091      for (int i = 0; i < parameterCount(); ++i) {
1092        TObject ob = getParameter(i).evaluate(group, resolver, context);
1093        if (ob.isNull()) {
1094          return ob;
1095        }
1096        if (least == null || ob.compareTo(least) < 0) {
1097          least = ob;
1098        }
1099      }
1100      return least;
1101    }
1102
1103    public TType returnTType(VariableResolver resolver, QueryContext context) {
1104      return getParameter(0).returnTType(resolver, context);
1105    }
1106
1107  }
1108
1109  // --
1110

1111  private static class GreatestFunction extends AbstractFunction {
1112
1113    public GreatestFunction(Expression[] params) {
1114      super("greatest", params);
1115
1116      if (parameterCount() < 1) {
1117        throw new RuntimeException JavaDoc(
1118                         "Greatest function must have at least 1 argument.");
1119      }
1120    }
1121
1122    public TObject evaluate(GroupResolver group, VariableResolver resolver,
1123                            QueryContext context) {
1124      TObject great = null;
1125      for (int i = 0; i < parameterCount(); ++i) {
1126        TObject ob = getParameter(i).evaluate(group, resolver, context);
1127        if (ob.isNull()) {
1128          return ob;
1129        }
1130        if (great == null || ob.compareTo(great) > 0) {
1131          great = ob;
1132        }
1133      }
1134      return great;
1135    }
1136
1137    public TType returnTType(VariableResolver resolver, QueryContext context) {
1138      return getParameter(0).returnTType(resolver, context);
1139    }
1140
1141  }
1142
1143  // --
1144

1145  private static class UniqueKeyFunction extends AbstractFunction {
1146
1147    public UniqueKeyFunction(Expression[] params) {
1148      super("uniquekey", params);
1149
1150      // The parameter is the name of the table you want to bring the unique
1151
// key in from.
1152
if (parameterCount() != 1) {
1153        throw new RuntimeException JavaDoc(
1154                          "'uniquekey' function must have only 1 argument.");
1155      }
1156    }
1157
1158    public TObject evaluate(GroupResolver group, VariableResolver resolver,
1159                            QueryContext context) {
1160      String JavaDoc str = getParameter(0).evaluate(group, resolver,
1161                                            context).getObject().toString();
1162      long v = context.nextSequenceValue(str);
1163      return TObject.longVal(v);
1164    }
1165
1166    public TType returnTType(VariableResolver resolver, QueryContext context) {
1167      return TType.NUMERIC_TYPE;
1168    }
1169
1170  }
1171
1172  private static class NextValFunction extends AbstractFunction {
1173
1174    public NextValFunction(Expression[] params) {
1175      super("nextval", params);
1176
1177      // The parameter is the name of the table you want to bring the unique
1178
// key in from.
1179
if (parameterCount() != 1) {
1180        throw new RuntimeException JavaDoc(
1181                            "'nextval' function must have only 1 argument.");
1182      }
1183    }
1184
1185    public TObject evaluate(GroupResolver group, VariableResolver resolver,
1186                            QueryContext context) {
1187      String JavaDoc str = getParameter(0).evaluate(group, resolver,
1188                                            context).getObject().toString();
1189      long v = context.nextSequenceValue(str);
1190      return TObject.longVal(v);
1191    }
1192
1193    public TType returnTType(VariableResolver resolver, QueryContext context) {
1194      return TType.NUMERIC_TYPE;
1195    }
1196
1197  }
1198
1199  private static class CurrValFunction extends AbstractFunction {
1200
1201    public CurrValFunction(Expression[] params) {
1202      super("currval", params);
1203
1204      // The parameter is the name of the table you want to bring the unique
1205
// key in from.
1206
if (parameterCount() != 1) {
1207        throw new RuntimeException JavaDoc(
1208                            "'currval' function must have only 1 argument.");
1209      }
1210    }
1211
1212    public TObject evaluate(GroupResolver group, VariableResolver resolver,
1213                            QueryContext context) {
1214      String JavaDoc str = getParameter(0).evaluate(group, resolver,
1215                                            context).getObject().toString();
1216      long v = context.currentSequenceValue(str);
1217      return TObject.longVal(v);
1218    }
1219
1220    public TType returnTType(VariableResolver resolver, QueryContext context) {
1221      return TType.NUMERIC_TYPE;
1222    }
1223
1224  }
1225
1226  private static class SetValFunction extends AbstractFunction {
1227
1228    public SetValFunction(Expression[] params) {
1229      super("setval", params);
1230
1231      // The parameter is the name of the table you want to bring the unique
1232
// key in from.
1233
if (parameterCount() != 2) {
1234        throw new RuntimeException JavaDoc(
1235                            "'setval' function must have 2 arguments.");
1236      }
1237    }
1238
1239    public TObject evaluate(GroupResolver group, VariableResolver resolver,
1240                            QueryContext context) {
1241      String JavaDoc str = getParameter(0).evaluate(group, resolver,
1242                                            context).getObject().toString();
1243      BigNumber num = getParameter(1).evaluate(group, resolver,
1244                                               context).toBigNumber();
1245      long v = num.longValue();
1246      context.setSequenceValue(str, v);
1247      return TObject.longVal(v);
1248    }
1249
1250    public TType returnTType(VariableResolver resolver, QueryContext context) {
1251      return TType.NUMERIC_TYPE;
1252    }
1253
1254  }
1255
1256  
1257  
1258  
1259  // --
1260

1261  private static class HexToBinaryFunction extends AbstractFunction {
1262
1263    public HexToBinaryFunction(Expression[] params) {
1264      super("hextobinary", params);
1265
1266      // One parameter - our hex string.
1267
if (parameterCount() != 1) {
1268        throw new RuntimeException JavaDoc(
1269                        "'hextobinary' function must have only 1 argument.");
1270      }
1271    }
1272
1273    public TObject evaluate(GroupResolver group, VariableResolver resolver,
1274                            QueryContext context) {
1275      String JavaDoc str = getParameter(0).evaluate(group, resolver,
1276                                            context).getObject().toString();
1277
1278      int str_len = str.length();
1279      if (str_len == 0) {
1280        return new TObject(TType.BINARY_TYPE, new ByteLongObject(new byte[0]));
1281      }
1282      // We translate the string to a byte array,
1283
byte[] buf = new byte[(str_len + 1) / 2];
1284      int index = 0;
1285      if (buf.length * 2 != str_len) {
1286        buf[0] = (byte) Character.digit(str.charAt(0), 16);
1287        ++index;
1288      }
1289      int v = 0;
1290      for (int i = index; i < str_len; i += 2) {
1291        v = (Character.digit(str.charAt(i), 16) << 4) |
1292            (Character.digit(str.charAt(i + 1), 16));
1293        buf[index] = (byte) (v & 0x0FF);
1294        ++index;
1295      }
1296
1297      return new TObject(TType.BINARY_TYPE, new ByteLongObject(buf));
1298    }
1299
1300    public TType returnTType(VariableResolver resolver, QueryContext context) {
1301      return TType.BINARY_TYPE;
1302    }
1303
1304  }
1305
1306  // --
1307

1308  private static class BinaryToHexFunction extends AbstractFunction {
1309
1310    final static char[] digits = {
1311      '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
1312      'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
1313      'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
1314      'u', 'v', 'w', 'x', 'y', 'z'
1315    };
1316
1317    public BinaryToHexFunction(Expression[] params) {
1318      super("binarytohex", params);
1319
1320      // One parameter - our hex string.
1321
if (parameterCount() != 1) {
1322        throw new RuntimeException JavaDoc(
1323                        "'binarytohex' function must have only 1 argument.");
1324      }
1325    }
1326
1327    public TObject evaluate(GroupResolver group, VariableResolver resolver,
1328                            QueryContext context) {
1329      TObject ob = getParameter(0).evaluate(group, resolver, context);
1330      if (ob.isNull()) {
1331        return ob;
1332      }
1333      else if (ob.getTType() instanceof TBinaryType) {
1334        StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
1335        BlobAccessor blob = (BlobAccessor) ob.getObject();
1336        InputStream JavaDoc bin = blob.getInputStream();
1337        try {
1338          int bval = bin.read();
1339          while (bval != -1) {
1340            buf.append(digits[((bval >> 4) & 0x0F)]);
1341            buf.append(digits[(bval & 0x0F)]);
1342            bval = bin.read();
1343          }
1344        }
1345        catch (IOException JavaDoc e) {
1346          e.printStackTrace();
1347          throw new RuntimeException JavaDoc("IO Error: " + e.getMessage());
1348        }
1349
1350// for (int i = 0; i < arr.length; ++i) {
1351
// buf.append(digits[((arr[i] >> 4) & 0x0F)]);
1352
// buf.append(digits[(arr[i] & 0x0F)]);
1353
// }
1354
return TObject.stringVal(buf.toString());
1355      }
1356      else {
1357        throw new RuntimeException JavaDoc(
1358                      "'binarytohex' parameter type is not a binary object.");
1359      }
1360
1361    }
1362
1363    public TType returnTType(VariableResolver resolver, QueryContext context) {
1364      return TType.STRING_TYPE;
1365    }
1366
1367  }
1368
1369  // --
1370

1371  static class SQLCastFunction extends AbstractFunction {
1372
1373    private TType cast_to_type;
1374
1375    public SQLCastFunction(Expression[] params) {
1376      super("sql_cast", params);
1377
1378      // Two parameters - the value to cast and the type to cast to (encoded)
1379
if (parameterCount() != 2) {
1380        throw new RuntimeException JavaDoc(
1381                        "'sql_cast' function must have only 2 arguments.");
1382      }
1383
1384      // Get the encoded type and parse it into a TType object and cache
1385
// locally in this object. We expect that the second parameter of this
1386
// function is always constant.
1387
Expression exp = params[1];
1388      if (exp.size() != 1) {
1389        throw new RuntimeException JavaDoc(
1390                   "'sql_cast' function must have simple second parameter.");
1391      }
1392
1393      Object JavaDoc vob = params[1].last();
1394      if (vob instanceof TObject) {
1395        TObject ob = (TObject) vob;
1396        String JavaDoc encoded_type = ob.getObject().toString();
1397        cast_to_type = TType.decodeString(encoded_type);
1398      }
1399      else {
1400        throw new RuntimeException JavaDoc(
1401                   "'sql_cast' function must have simple second parameter.");
1402      }
1403    }
1404
1405    public TObject evaluate(GroupResolver group, VariableResolver resolver,
1406                            QueryContext context) {
1407
1408      TObject ob = getParameter(0).evaluate(group, resolver, context);
1409      // If types are the same then no cast is necessary and we return this
1410
// object.
1411
if (ob.getTType().getSQLType() == cast_to_type.getSQLType()) {
1412        return ob;
1413      }
1414      // Otherwise cast the object and return the new typed object.
1415
Object JavaDoc casted_ob = TType.castObjectToTType(ob.getObject(), cast_to_type);
1416      return new TObject(cast_to_type, casted_ob);
1417
1418    }
1419
1420    public TType returnTType(VariableResolver resolver, QueryContext context) {
1421      return cast_to_type;
1422    }
1423    
1424  }
1425
1426  // --
1427

1428  static class DateObFunction extends AbstractFunction {
1429
1430    private final static TType DATE_TYPE = new TDateType(SQLTypes.DATE);
1431    
1432    /**
1433     * The date format object that handles the conversion of Date objects to a
1434     * string readable representation of the given date.
1435     * <p>
1436     * NOTE: Due to bad design these objects are not thread-safe.
1437     */

1438    private final static DateFormat date_format_sho;
1439    private final static DateFormat date_format_sql;
1440    private final static DateFormat date_format_med;
1441    private final static DateFormat date_format_lon;
1442    private final static DateFormat date_format_ful;
1443
1444    static {
1445      date_format_med = DateFormat.getDateInstance(DateFormat.MEDIUM);
1446      date_format_sho = DateFormat.getDateInstance(DateFormat.SHORT);
1447      date_format_lon = DateFormat.getDateInstance(DateFormat.LONG);
1448      date_format_ful = DateFormat.getDateInstance(DateFormat.FULL);
1449
1450      // The SQL date format
1451
date_format_sql = new SimpleDateFormat("yyyy-MM-dd");
1452    }
1453
1454    private static TObject dateVal(Date JavaDoc d) {
1455      return new TObject(DATE_TYPE, d);
1456    }
1457    
1458    public DateObFunction(Expression[] params) {
1459      super("dateob", params);
1460
1461      if (parameterCount() > 1) {
1462        throw new RuntimeException JavaDoc(
1463                  "'dateob' function must have only one or zero parameters.");
1464      }
1465    }
1466
1467    public TObject evaluate(GroupResolver group, VariableResolver resolver,
1468                            QueryContext context) {
1469
1470      // No parameters so return the current date.
1471
if (parameterCount() == 0) {
1472        return dateVal(new Date JavaDoc());
1473      }
1474
1475      TObject exp_res = getParameter(0).evaluate(group, resolver, context);
1476      // If expression resolves to 'null' then return current date
1477
if (exp_res.isNull()) {
1478        return dateVal(new Date JavaDoc());
1479      }
1480      // If expression resolves to a BigDecimal, then treat as number of
1481
// seconds since midnight Jan 1st, 1970
1482
else if (exp_res.getTType() instanceof TNumericType) {
1483        BigNumber num = (BigNumber) exp_res.getObject();
1484        return dateVal(new Date JavaDoc(num.longValue()));
1485      }
1486
1487      String JavaDoc date_str = exp_res.getObject().toString();
1488
1489      // We need to synchronize here unfortunately because the Java
1490
// DateFormat objects are not thread-safe.
1491
synchronized (date_format_sho) {
1492        // Try and parse date
1493
try {
1494          return dateVal(date_format_sql.parse(date_str));
1495        }
1496        catch (ParseException e) {}
1497        try {
1498          return dateVal(date_format_sho.parse(date_str));
1499        }
1500        catch (ParseException e) {}
1501        try {
1502          return dateVal(date_format_med.parse(date_str));
1503        }
1504        catch (ParseException e) {}
1505        try {
1506          return dateVal(date_format_lon.parse(date_str));
1507        }
1508        catch (ParseException e) {}
1509        try {
1510          return dateVal(date_format_ful.parse(date_str));
1511        }
1512        catch (ParseException e) {}
1513
1514        throw new RuntimeException JavaDoc(
1515                           "Unable to parse date string '" + date_str + "'");
1516      }
1517
1518    }
1519
1520    public TType returnTType(VariableResolver resolver, QueryContext context) {
1521      return DATE_TYPE;
1522    }
1523
1524  }
1525
1526  // --
1527

1528  static class TimeObFunction extends AbstractFunction {
1529
1530    private final static TType TIME_TYPE = new TDateType(SQLTypes.TIME);
1531
1532    public TimeObFunction(Expression[] params) {
1533      super("timeob", params);
1534
1535      if (parameterCount() > 1) {
1536        throw new RuntimeException JavaDoc(
1537                  "'timeob' function must have only one or zero parameters.");
1538      }
1539    }
1540
1541    private TObject timeNow() {
1542      Calendar JavaDoc c = Calendar.getInstance();
1543      c.setLenient(false);
1544      int hour = c.get(Calendar.HOUR_OF_DAY);
1545      int minute = c.get(Calendar.MINUTE);
1546      int second = c.get(Calendar.SECOND);
1547      int millisecond = c.get(Calendar.MILLISECOND);
1548
1549      c.set(1970, 0, 1);
1550      return new TObject(TIME_TYPE, c.getTime());
1551    }
1552
1553    public TObject evaluate(GroupResolver group, VariableResolver resolver,
1554                            QueryContext context) {
1555
1556      // No parameters so return the current time.
1557
if (parameterCount() == 0) {
1558        return timeNow();
1559      }
1560
1561      TObject exp_res = getParameter(0).evaluate(group, resolver, context);
1562      // If expression resolves to 'null' then return current date
1563
if (exp_res.isNull()) {
1564        return timeNow();
1565      }
1566
1567      String JavaDoc date_str = exp_res.getObject().toString();
1568
1569      return new TObject(TIME_TYPE, CastHelper.toTime(date_str));
1570
1571    }
1572
1573    public TType returnTType(VariableResolver resolver, QueryContext context) {
1574      return TIME_TYPE;
1575    }
1576
1577  }
1578
1579  // --
1580

1581  static class TimeStampObFunction extends AbstractFunction {
1582
1583    private final static TType TIMESTAMP_TYPE =
1584                                           new TDateType(SQLTypes.TIMESTAMP);
1585
1586    public TimeStampObFunction(Expression[] params) {
1587      super("timestampob", params);
1588
1589      if (parameterCount() > 1) {
1590        throw new RuntimeException JavaDoc(
1591             "'timestampob' function must have only one or zero parameters.");
1592      }
1593    }
1594
1595    public TObject evaluate(GroupResolver group, VariableResolver resolver,
1596                            QueryContext context) {
1597
1598      // No parameters so return the current time.
1599
if (parameterCount() == 0) {
1600        return new TObject(TIMESTAMP_TYPE, new Date JavaDoc());
1601      }
1602
1603      TObject exp_res = getParameter(0).evaluate(group, resolver, context);
1604      // If expression resolves to 'null' then return current date
1605
if (exp_res.isNull()) {
1606        return new TObject(TIMESTAMP_TYPE, new Date JavaDoc());
1607      }
1608
1609      String JavaDoc date_str = exp_res.getObject().toString();
1610
1611      return new TObject(TIMESTAMP_TYPE, CastHelper.toTimeStamp(date_str));
1612      
1613    }
1614
1615    public TType returnTType(VariableResolver resolver, QueryContext context) {
1616      return TIMESTAMP_TYPE;
1617    }
1618
1619  }
1620
1621  // --
1622

1623  // A function that formats an input java.sql.Date object to the format
1624
// given using the java.text.SimpleDateFormat class.
1625

1626  static class DateFormatFunction extends AbstractFunction {
1627
1628    final static Cache formatter_cache = new Cache(127, 90, 10);
1629
1630    public DateFormatFunction(Expression[] params) {
1631      super("dateformat", params);
1632
1633      if (parameterCount() != 2) {
1634        throw new RuntimeException JavaDoc(
1635             "'dateformat' function must have exactly two parameters.");
1636      }
1637    }
1638
1639    public TObject evaluate(GroupResolver group, VariableResolver resolver,
1640                            QueryContext context) {
1641
1642      TObject datein = getParameter(0).evaluate(group, resolver, context);
1643      TObject format = getParameter(1).evaluate(group, resolver, context);
1644      // If expression resolves to 'null' then return null
1645
if (datein.isNull()) {
1646        return datein;
1647      }
1648
1649      Date JavaDoc d;
1650      if (!(datein.getTType() instanceof TDateType)) {
1651        throw new RuntimeException JavaDoc(
1652                           "Date to format must be DATE, TIME or TIMESTAMP");
1653      }
1654      else {
1655        d = (Date JavaDoc) datein.getObject();
1656      }
1657
1658      String JavaDoc format_string = format.toString();
1659      synchronized(formatter_cache) {
1660        SimpleDateFormat formatter =
1661                       (SimpleDateFormat) formatter_cache.get(format_string);
1662        if (formatter == null) {
1663          formatter = new SimpleDateFormat(format_string);
1664          formatter_cache.put(format_string, formatter);
1665        }
1666        return TObject.stringVal(formatter.format(d));
1667      }
1668    }
1669
1670    public TType returnTType(VariableResolver resolver, QueryContext context) {
1671      return TType.STRING_TYPE;
1672    }
1673
1674  }
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684  // --
1685

1686  // Casts the expression to a BigDecimal number. Useful in conjunction with
1687
// 'dateob'
1688
private static class ToNumberFunction extends AbstractFunction {
1689
1690    public ToNumberFunction(Expression[] params) {
1691      super("tonumber", params);
1692
1693      if (parameterCount() != 1) {
1694        throw new RuntimeException JavaDoc("TONUMBER function must have one argument.");
1695      }
1696    }
1697
1698    public TObject evaluate(GroupResolver group, VariableResolver resolver,
1699                            QueryContext context) {
1700      // Casts the first parameter to a number
1701
return getParameter(0).evaluate(group, resolver,
1702                                      context).castTo(TType.NUMERIC_TYPE);
1703    }
1704
1705  }
1706
1707  // --
1708

1709  // Conditional - IF(a < 0, NULL, a)
1710
private static class IfFunction extends AbstractFunction {
1711
1712    public IfFunction(Expression[] params) {
1713      super("if", params);
1714      if (parameterCount() != 3) {
1715        throw new RuntimeException JavaDoc(
1716                           "IF function must have exactly three arguments.");
1717      }
1718    }
1719
1720    public TObject evaluate(GroupResolver group, VariableResolver resolver,
1721                            QueryContext context) {
1722      TObject res = getParameter(0).evaluate(group, resolver, context);
1723      if (res.getTType() instanceof TBooleanType) {
1724        // Does the result equal true?
1725
if (res.compareTo(TObject.booleanVal(true)) == 0) {
1726          // Resolved to true so evaluate the first argument
1727
return getParameter(1).evaluate(group, resolver, context);
1728        }
1729        else {
1730          // Otherwise result must evaluate to NULL or false, so evaluate
1731
// the second parameter
1732
return getParameter(2).evaluate(group, resolver, context);
1733        }
1734      }
1735      // Result was not a boolean so return null
1736
return TObject.nullVal();
1737    }
1738
1739    public TType returnTType(VariableResolver resolver, QueryContext context) {
1740      // It's impossible to know the return type of this function until runtime
1741
// because either comparator could be returned. We could assume that
1742
// both branch expressions result in the same type of object but this
1743
// currently is not enforced.
1744

1745      // Returns type of first argument
1746
TType t1 = getParameter(1).returnTType(resolver, context);
1747      // This is a hack for null values. If the first parameter is null
1748
// then return the type of the second parameter which hopefully isn't
1749
// also null.
1750
if (t1 instanceof TNullType) {
1751        return getParameter(2).returnTType(resolver, context);
1752      }
1753      return t1;
1754    }
1755
1756  }
1757
1758  // --
1759

1760  // Coalesce - COALESCE(address2, CONCAT(city, ', ', state, ' ', zip))
1761
private static class CoalesceFunction extends AbstractFunction {
1762
1763    public CoalesceFunction(Expression[] params) {
1764      super("coalesce", params);
1765      if (parameterCount() < 1) {
1766        throw new RuntimeException JavaDoc(
1767                        "COALESCE function must have at least 1 parameter.");
1768      }
1769    }
1770
1771    public TObject evaluate(GroupResolver group, VariableResolver resolver,
1772                            QueryContext context) {
1773      int count = parameterCount();
1774      for (int i = 0; i < count - 1; ++i) {
1775        TObject res = getParameter(i).evaluate(group, resolver, context);
1776        if (!res.isNull()) {
1777          return res;
1778        }
1779      }
1780      return getParameter(count - 1).evaluate(group, resolver, context);
1781    }
1782
1783    public TType returnTType(VariableResolver resolver, QueryContext context) {
1784      // It's impossible to know the return type of this function until runtime
1785
// because either comparator could be returned. We could assume that
1786
// both branch expressions result in the same type of object but this
1787
// currently is not enforced.
1788

1789      // Go through each argument until we find the first parameter we can
1790
// deduce the class of.
1791
int count = parameterCount();
1792      for (int i = 0; i < count; ++i) {
1793        TType t = getParameter(i).returnTType(resolver, context);
1794        if (!(t instanceof TNullType)) {
1795          return t;
1796        }
1797      }
1798      // Can't work it out so return null type
1799
return TType.NULL_TYPE;
1800    }
1801
1802  }
1803
1804
1805
1806  // --
1807

1808  // Instantiates a new java object.
1809
private static class JavaObjectInstantiation extends AbstractFunction {
1810
1811    public JavaObjectInstantiation(Expression[] params) {
1812      super("_new_JavaObject", params);
1813
1814      if (parameterCount() < 1) {
1815        throw new RuntimeException JavaDoc(
1816                         "_new_JavaObject function must have one argument.");
1817      }
1818    }
1819
1820    public TObject evaluate(GroupResolver group, VariableResolver resolver,
1821                            QueryContext context) {
1822      // Resolve the parameters...
1823
final int arg_len = parameterCount() - 1;
1824      Object JavaDoc[] args = new Object JavaDoc[arg_len];
1825      for (int i = 0; i < args.length; ++i) {
1826        args[i] = getParameter(i + 1).evaluate(group, resolver,
1827                                               context).getObject();
1828      }
1829      Object JavaDoc[] casted_args = new Object JavaDoc[arg_len];
1830
1831      try {
1832        String JavaDoc clazz = getParameter(0).evaluate(null, resolver,
1833                                             context).getObject().toString();
1834        Class JavaDoc c = Class.forName(clazz);
1835
1836        Constructor[] constructs = c.getConstructors();
1837        // Search for the first constructor that we can use with the given
1838
// arguments.
1839
search_constructs:
1840        for (int i = 0; i < constructs.length; ++i) {
1841          Class JavaDoc[] construct_args = constructs[i].getParameterTypes();
1842          if (construct_args.length == arg_len) {
1843            for (int n = 0; n < arg_len; ++n) {
1844              // If we are dealing with a primitive,
1845
if (construct_args[n].isPrimitive()) {
1846                String JavaDoc class_name = construct_args[n].getName();
1847                // If the given argument is a number,
1848
if (args[n] instanceof Number JavaDoc) {
1849                  Number JavaDoc num = (Number JavaDoc) args[n];
1850                  if (class_name.equals("byte")) {
1851                    casted_args[n] = new Byte JavaDoc(num.byteValue());
1852                  }
1853                  else if (class_name.equals("char")) {
1854                    casted_args[n] = new Character JavaDoc((char) num.intValue());
1855                  }
1856                  else if (class_name.equals("double")) {
1857                    casted_args[n] = new Double JavaDoc(num.doubleValue());
1858                  }
1859                  else if (class_name.equals("float")) {
1860                    casted_args[n] = new Float JavaDoc(num.floatValue());
1861                  }
1862                  else if (class_name.equals("int")) {
1863                    casted_args[n] = new Integer JavaDoc(num.intValue());
1864                  }
1865                  else if (class_name.equals("long")) {
1866                    casted_args[n] = new Long JavaDoc(num.longValue());
1867                  }
1868                  else if (class_name.equals("short")) {
1869                    casted_args[n] = new Short JavaDoc(num.shortValue());
1870                  }
1871                  else {
1872                    // Can't cast the primitive type to a number so break,
1873
break search_constructs;
1874                  }
1875
1876                }
1877                // If we are a boolean, we can cast to primitive boolean
1878
else if (args[n] instanceof Boolean JavaDoc) {
1879                  // If primitive type constructor arg is a boolean also
1880
if (class_name.equals("boolean")) {
1881                    casted_args[n] = args[n];
1882                  }
1883                  else {
1884                    break search_constructs;
1885                  }
1886                }
1887                // Otherwise we can't cast,
1888
else {
1889                  break search_constructs;
1890                }
1891
1892              }
1893              // Not a primitive type constructor arg,
1894
else {
1895                // PENDING: Allow string -> char conversion
1896
if (construct_args[n].isInstance(args[n])) {
1897                  casted_args[n] = args[n];
1898                }
1899                else {
1900                  break search_constructs;
1901                }
1902              }
1903            } // for (int n = 0; n < arg_len; ++n)
1904
// If we get here, we have a match...
1905
Object JavaDoc ob = constructs[i].newInstance(casted_args);
1906            ByteLongObject serialized_ob = ObjectTranslator.serialize(ob);
1907            return new TObject(new TJavaObjectType(clazz), serialized_ob);
1908          }
1909        }
1910
1911        throw new RuntimeException JavaDoc(
1912                "Unable to find a constructor for '" + clazz +
1913                "' that matches given arguments.");
1914
1915      }
1916      catch (ClassNotFoundException JavaDoc e) {
1917        throw new RuntimeException JavaDoc("Class not found: " + e.getMessage());
1918      }
1919      catch (InstantiationException JavaDoc e) {
1920        throw new RuntimeException JavaDoc("Instantiation Error: " + e.getMessage());
1921      }
1922      catch (IllegalAccessException JavaDoc e) {
1923        throw new RuntimeException JavaDoc("Illegal Access Error: " + e.getMessage());
1924      }
1925      catch (IllegalArgumentException JavaDoc e) {
1926        throw new RuntimeException JavaDoc(
1927                                 "Illegal Argument Error: " + e.getMessage());
1928      }
1929      catch (InvocationTargetException e) {
1930        throw new RuntimeException JavaDoc(
1931                                "Invocation Target Error: " + e.getMessage());
1932      }
1933
1934    }
1935
1936    public TType returnTType(VariableResolver resolver, QueryContext context) {
1937      String JavaDoc clazz = getParameter(0).evaluate(null, resolver,
1938                                              context).getObject().toString();
1939      return new TJavaObjectType(clazz);
1940    }
1941
1942  }
1943
1944  // Instantiates a new java object using Jim McBeath's parameter seach
1945
// algorithm.
1946
private static class JavaObjectInstantiation2 extends AbstractFunction {
1947
1948    public JavaObjectInstantiation2(Expression[] params) {
1949      super("_new_JavaObject", params);
1950
1951      if (parameterCount() < 1) {
1952        throw new RuntimeException JavaDoc(
1953                         "_new_JavaObject function must have one argument.");
1954      }
1955    }
1956
1957    public TObject evaluate(GroupResolver group, VariableResolver resolver,
1958                            QueryContext context) {
1959      // Resolve the parameters...
1960
final int arg_len = parameterCount() - 1;
1961      TObject[] args = new TObject[arg_len];
1962      for (int i = 0; i < args.length; ++i) {
1963        args[i] = getParameter(i + 1).evaluate(group, resolver, context);
1964      }
1965      Caster.deserializeJavaObjects(args);
1966
1967      try {
1968        // Get the class name of the object to be constructed
1969
String JavaDoc clazz = getParameter(0).evaluate(null, resolver,
1970                                             context).getObject().toString();
1971        Class JavaDoc c = Class.forName(clazz);
1972        Constructor[] constructs = c.getConstructors();
1973
1974    Constructor bestConstructor =
1975                                Caster.findBestConstructor(constructs, args);
1976    if (bestConstructor == null) {
1977      // Didn't find a match - build a list of class names of the
1978
// args so the user knows what we were looking for.
1979
String JavaDoc argTypes = Caster.getArgTypesString(args);
1980      throw new RuntimeException JavaDoc(
1981        "Unable to find a constructor for '" + clazz +
1982        "' that matches given arguments: " + argTypes);
1983    }
1984        Object JavaDoc[] casted_args =
1985        Caster.castArgsToConstructor(args, bestConstructor);
1986        // Call the constructor to create the java object
1987
Object JavaDoc ob = bestConstructor.newInstance(casted_args);
1988        ByteLongObject serialized_ob = ObjectTranslator.serialize(ob);
1989        return new TObject(new TJavaObjectType(clazz), serialized_ob);
1990
1991      }
1992      catch (ClassNotFoundException JavaDoc e) {
1993        throw new RuntimeException JavaDoc("Class not found: " + e.getMessage());
1994      }
1995      catch (InstantiationException JavaDoc e) {
1996        throw new RuntimeException JavaDoc("Instantiation Error: " + e.getMessage());
1997      }
1998      catch (IllegalAccessException JavaDoc e) {
1999        throw new RuntimeException JavaDoc("Illegal Access Error: " + e.getMessage());
2000      }
2001      catch (IllegalArgumentException JavaDoc e) {
2002        throw new RuntimeException JavaDoc(
2003                                 "Illegal Argument Error: " + e.getMessage());
2004      }
2005      catch (InvocationTargetException e) {
2006        String JavaDoc msg = e.getMessage();
2007        if (msg == null) {
2008          Throwable JavaDoc th = e.getTargetException();
2009          if (th != null) {
2010            msg = th.getClass().getName() + ": " + th.getMessage();
2011          }
2012        }
2013        throw new RuntimeException JavaDoc("Invocation Target Error: " + msg);
2014      }
2015
2016    }
2017
2018    public TType returnTType(VariableResolver resolver, QueryContext context) {
2019      String JavaDoc clazz = getParameter(0).evaluate(null, resolver,
2020                                              context).getObject().toString();
2021      return new TJavaObjectType(clazz);
2022    }
2023
2024  }
2025  
2026  // --
2027

2028  // Used in the 'getxxxKeys' methods in DatabaseMetaData to convert the
2029
// update delete rule of a foreign key to the JDBC short enum.
2030
private static class ForeignRuleConvert extends AbstractFunction {
2031
2032    public ForeignRuleConvert(Expression[] params) {
2033      super("i_frule_convert", params);
2034
2035      if (parameterCount() != 1) {
2036        throw new RuntimeException JavaDoc(
2037                         "i_frule_convert function must have one argument.");
2038      }
2039    }
2040
2041    public TObject evaluate(GroupResolver group, VariableResolver resolver,
2042                            QueryContext context) {
2043      // The parameter should be a variable reference that is resolved
2044
TObject ob = getParameter(0).evaluate(group, resolver, context);
2045      String JavaDoc str = null;
2046      if (!ob.isNull()) {
2047        str = ob.getObject().toString();
2048      }
2049      int v;
2050      if (str == null || str.equals("") || str.equals("NO ACTION")) {
2051        v = java.sql.DatabaseMetaData.importedKeyNoAction;
2052      }
2053      else if (str.equals("CASCADE")) {
2054        v = java.sql.DatabaseMetaData.importedKeyCascade;
2055      }
2056      else if (str.equals("SET NULL")) {
2057        v = java.sql.DatabaseMetaData.importedKeySetNull;
2058      }
2059      else if (str.equals("SET DEFAULT")) {
2060        v = java.sql.DatabaseMetaData.importedKeySetDefault;
2061      }
2062      else if (str.equals("RESTRICT")) {
2063        v = java.sql.DatabaseMetaData.importedKeyRestrict;
2064      }
2065      else {
2066        throw new Error JavaDoc("Unrecognised foreign key rule: " + str);
2067      }
2068      // Return the correct enumeration
2069
return TObject.intVal(v);
2070    }
2071
2072  }
2073
2074  // --
2075

2076  // Used to form an SQL type string that describes the SQL type and any
2077
// size/scale information together with it.
2078
private static class SQLTypeString extends AbstractFunction {
2079
2080    public SQLTypeString(Expression[] params) {
2081      super("i_sql_type", params);
2082
2083      if (parameterCount() != 3) {
2084        throw new RuntimeException JavaDoc(
2085                           "i_sql_type function must have three arguments.");
2086      }
2087    }
2088
2089    public TObject evaluate(GroupResolver group, VariableResolver resolver,
2090                            QueryContext context) {
2091      // The parameter should be a variable reference that is resolved
2092
TObject type_string = getParameter(0).evaluate(group, resolver, context);
2093      TObject type_size = getParameter(1).evaluate(group, resolver, context);
2094      TObject type_scale = getParameter(2).evaluate(group, resolver, context);
2095
2096      StringBuffer JavaDoc result_str = new StringBuffer JavaDoc();
2097      result_str.append(type_string.toString());
2098      long size = -1;
2099      long scale = -1;
2100      if (!type_size.isNull()) {
2101        size = type_size.toBigNumber().longValue();
2102      }
2103      if (!type_scale.isNull()) {
2104        scale = type_scale.toBigNumber().longValue();
2105      }
2106
2107      if (size != -1) {
2108        result_str.append('(');
2109        result_str.append(size);
2110        if (scale != -1) {
2111          result_str.append(',');
2112          result_str.append(scale);
2113        }
2114        result_str.append(')');
2115      }
2116
2117      return TObject.stringVal(new String JavaDoc(result_str));
2118    }
2119
2120    public TType returnTType(VariableResolver resolver, QueryContext context) {
2121      return TType.STRING_TYPE;
2122    }
2123
2124  }
2125
2126  // --
2127

2128  // Used to convert view data in the system view table to forms that are
2129
// human understandable. Useful function for debugging or inspecting views.
2130

2131  private static class ViewDataConvert extends AbstractFunction {
2132
2133    public ViewDataConvert(Expression[] params) {
2134      super("i_view_data", params);
2135
2136      if (parameterCount() != 2) {
2137        throw new RuntimeException JavaDoc(
2138                             "i_sql_type function must have two arguments.");
2139      }
2140    }
2141
2142    public TObject evaluate(GroupResolver group, VariableResolver resolver,
2143                            QueryContext context) {
2144      // Get the parameters. The first is a string describing the operation.
2145
// The second is the binary data to process and output the information
2146
// for.
2147
TObject command = getParameter(0).evaluate(group, resolver, context);
2148      TObject data = getParameter(1).evaluate(group, resolver, context);
2149      
2150      String JavaDoc command_str = command.getObject().toString();
2151      ByteLongObject blob = (ByteLongObject) data.getObject();
2152
2153      if (command_str.equalsIgnoreCase("referenced tables")) {
2154        ViewDef view_def = ViewDef.deserializeFromBlob(blob);
2155        QueryPlanNode node = view_def.getQueryPlanNode();
2156        ArrayList JavaDoc touched_tables = node.discoverTableNames(new ArrayList JavaDoc());
2157        StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
2158        int sz = touched_tables.size();
2159        for (int i = 0; i < sz; ++i) {
2160          buf.append(touched_tables.get(i));
2161          if (i < sz - 1) {
2162            buf.append(", ");
2163          }
2164        }
2165        return TObject.stringVal(new String JavaDoc(buf));
2166      }
2167      else if (command_str.equalsIgnoreCase("plan dump")) {
2168        ViewDef view_def = ViewDef.deserializeFromBlob(blob);
2169        QueryPlanNode node = view_def.getQueryPlanNode();
2170        StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
2171        node.debugString(0, buf);
2172        return TObject.stringVal(new String JavaDoc(buf));
2173      }
2174      else if (command_str.equalsIgnoreCase("query string")) {
2175        SQLQuery query = SQLQuery.deserializeFromBlob(blob);
2176        return TObject.stringVal(query.toString());
2177      }
2178
2179      return TObject.nullVal();
2180      
2181    }
2182    
2183    public TType returnTType(VariableResolver resolver, QueryContext context) {
2184      return TType.STRING_TYPE;
2185    }
2186
2187  }
2188
2189  // --
2190

2191  // Given a priv_bit number (from SYS_INFO.sUSRGrant), this will return a
2192
// text representation of the privilege.
2193

2194  private static class PrivilegeString extends AbstractFunction {
2195
2196    public PrivilegeString(Expression[] params) {
2197      super("i_privilege_string", params);
2198
2199      if (parameterCount() != 1) {
2200        throw new RuntimeException JavaDoc(
2201                    "i_privilege_string function must have one argument.");
2202      }
2203    }
2204
2205    public TObject evaluate(GroupResolver group, VariableResolver resolver,
2206                            QueryContext context) {
2207      TObject priv_bit_ob = getParameter(0).evaluate(group, resolver, context);
2208      int priv_bit = ((BigNumber) priv_bit_ob.getObject()).intValue();
2209      Privileges privs = new Privileges();
2210      privs = privs.add(priv_bit);
2211      return TObject.stringVal(privs.toString());
2212    }
2213    
2214    public TType returnTType(VariableResolver resolver, QueryContext context) {
2215      return TType.STRING_TYPE;
2216    }
2217
2218  }
2219
2220
2221
2222}
2223
Popular Tags