KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > caucho > quercus > lib > db > PDOStatement


1 /*
2  * Copyright (c) 1998-2006 Caucho Technology -- all rights reserved
3  *
4  * This file is part of Resin(R) Open Source
5  *
6  * Each copy or derived work must preserve the copyright notice and this
7  * notice unmodified.
8  *
9  * Resin Open Source is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * Resin Open Source is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
17  * of NON-INFRINGEMENT. See the GNU General Public License for more
18  * details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with Resin Open Source; if not, write to the
22  *
23  * Free Software Foundation, Inc.
24  * 59 Temple Place, Suite 330
25  * Boston, MA 02111-1307 USA
26  *
27  * @author Sam
28  */

29
30 package com.caucho.quercus.lib.db;
31
32 import com.caucho.quercus.UnimplementedException;
33 import com.caucho.quercus.annotation.Optional;
34 import com.caucho.quercus.annotation.ReadOnly;
35 import com.caucho.quercus.annotation.Reference;
36 import com.caucho.quercus.env.*;
37 import com.caucho.quercus.lib.file.FileReadValue;
38 import com.caucho.util.IntMap;
39 import com.caucho.util.L10N;
40 import com.caucho.vfs.ReadStream;
41 import com.caucho.vfs.TempBuffer;
42 import com.caucho.vfs.TempReadStream;
43
44 import java.io.IOException JavaDoc;
45 import java.io.InputStream JavaDoc;
46 import java.sql.*;
47 import java.util.ArrayList JavaDoc;
48 import java.util.Collections JavaDoc;
49 import java.util.Iterator JavaDoc;
50 import java.util.Map JavaDoc;
51 import java.util.Set JavaDoc;
52 import java.util.logging.Level JavaDoc;
53 import java.util.logging.Logger JavaDoc;
54
55 /**
56  * PDO object oriented API facade.
57  */

58 public class PDOStatement
59   implements Iterable JavaDoc<Value>, java.io.Closeable JavaDoc
60 {
61   private static final Logger JavaDoc log = Logger.getLogger(PDOStatement.class.getName());
62   private static final L10N L = new L10N(PDOStatement.class);
63
64   private static final Value[] NULL_VALUES = new Value[0];
65
66   private static final Value FETCH_FAILURE = new BooleanValue(false) {};
67   private static final Value FETCH_EXHAUSTED = new BooleanValue(false) {};
68   private static final Value FETCH_CONTINUE = new BooleanValue(false) {};
69   private static final Value FETCH_SUCCESS = new BooleanValue(true) {};
70
71   private final Env _env;
72   private final PDOError _error;
73
74   private final String JavaDoc _query;
75
76   private Statement _statement;
77   private PreparedStatement _preparedStatement;
78
79   private ResultSet _resultSet;
80   private ResultSetMetaData _resultSetMetaData;
81   private boolean _resultSetExhausted = true;
82   private String JavaDoc _lastInsertId;
83
84   private int _fetchMode = PDO.FETCH_BOTH;
85   private Value[] _fetchModeArgs = NULL_VALUES;
86   private ArrayList JavaDoc<BindColumn> _bindColumns;
87   private ArrayList JavaDoc<BindParam> _bindParams;
88   private IntMap _parameterNameMap;
89
90   PDOStatement(Env env, Connection conn, String JavaDoc query, boolean isPrepared, ArrayValue options)
91     throws SQLException
92
93   {
94     _env = env;
95     _error = new PDOError(_env);
96
97     _query = query;
98
99     env.addClose(this);
100
101     if (options != null && options.getSize() > 0) {
102       _env.notice(L.l("PDOStatement options unsupported"));
103     }
104
105     query = parseQueryString(query);
106
107     if (isPrepared) {
108       _statement = null;
109       _preparedStatement = conn.prepareStatement(query);
110       _preparedStatement.setEscapeProcessing(false);
111     }
112     else {
113       _preparedStatement = null;
114
115       Statement statement = null;
116
117       try {
118         statement = conn.createStatement();
119         statement.setEscapeProcessing(false);
120
121         if (statement.execute(query)) {
122           _resultSet = statement.getResultSet();
123           _resultSetExhausted = false;
124         }
125
126         _statement = statement;
127
128         statement = null;
129
130       } finally {
131         try {
132           if (statement != null)
133             statement.close();
134         } catch (SQLException e) {
135           log.log(Level.FINE, e.toString(), e);
136         }
137       }
138     }
139   }
140
141   // side-effect, updates _parameterNameMap
142
private String JavaDoc parseQueryString(String JavaDoc query)
143   {
144     final int queryLength = query.length();
145     StringBuilder JavaDoc parsedQuery = new StringBuilder JavaDoc(queryLength);
146
147     int parameterCount = 0;
148     StringBuilder JavaDoc name = null;
149
150     int quote = 0;
151
152     for (int i = 0; i < queryLength + 1; i++) {
153       int ch = -1;
154
155       if (i < queryLength)
156         ch = query.charAt(i);
157
158       if (ch == '\'' || ch == '"') {
159         if (quote == 0)
160           quote = ch;
161         else if (quote == ch)
162           quote = 0;
163       }
164       else if (quote == 0 && ch == '?') {
165         parameterCount++;
166       }
167       else if (quote == 0 && ch == ':') {
168         parameterCount++;
169         name = new StringBuilder JavaDoc();
170         continue;
171       }
172       else if (name != null && (ch == -1 || !Character.isJavaIdentifierStart(ch))) {
173         if (_parameterNameMap == null)
174           _parameterNameMap = new IntMap();
175
176         _parameterNameMap.put(name.toString(), parameterCount);
177
178         parsedQuery.append('?');
179
180         name = null;
181       }
182
183       if (ch != -1) {
184         if (name != null)
185           name.append((char) ch);
186         else
187           parsedQuery.append((char) ch);
188       }
189     }
190
191     return parsedQuery.toString();
192   }
193
194   private boolean advanceResultSet()
195   {
196     if (_resultSet == null || _resultSetExhausted)
197       return false;
198
199     try {
200       boolean isNext = _resultSet.next();
201
202       if (!isNext)
203         _resultSetExhausted = true;
204
205       if (!isNext)
206         return false;
207
208       if (_bindColumns != null) {
209         for (BindColumn bindColumn : _bindColumns)
210           if (!bindColumn.bind())
211             return false;
212       }
213
214       return isNext;
215     }
216     catch (SQLException ex) {
217       _error.error(ex);
218       return false;
219     }
220   }
221
222   public boolean bindColumn(Value column, @Reference Value var, @Optional("-1") int type)
223   {
224     if (_bindColumns == null)
225       _bindColumns = new ArrayList JavaDoc<BindColumn>();
226
227     try {
228       _bindColumns.add(new BindColumn(column, var, type));
229     }
230     catch (SQLException ex) {
231       _error.error(ex);
232       return false;
233     }
234
235     return true;
236   }
237
238   public boolean bindParam(Value parameter,
239                            @Reference Value variable,
240                            @Optional("-1") int dataType,
241                            @Optional("-1") int length,
242                            @Optional Value driverOptions)
243   {
244     if (length != -1)
245       throw new UnimplementedException("length");
246
247     if (!(driverOptions == null || driverOptions.isNull()))
248       throw new UnimplementedException("driverOptions");
249
250     if (dataType == -1)
251       dataType = PDO.PARAM_STR;
252
253     boolean isInputOutput = (dataType & PDO.PARAM_INPUT_OUTPUT) != 0;
254
255     if (isInputOutput) {
256       dataType = dataType & (~PDO.PARAM_INPUT_OUTPUT);
257       if (true) throw new UnimplementedException("PARAM_INPUT_OUTPUT");
258     }
259
260     switch (dataType) {
261       case PDO.PARAM_BOOL:
262       case PDO.PARAM_INT:
263       case PDO.PARAM_LOB:
264       case PDO.PARAM_NULL:
265       case PDO.PARAM_STMT:
266       case PDO.PARAM_STR:
267         break;
268
269       default:
270         _error.warning(L.l("unknown dataType `{0}'", dataType));
271         return false;
272     }
273
274     if (_bindParams == null)
275       _bindParams = new ArrayList JavaDoc<BindParam>();
276
277     BindParam bindParam = new BindParam(parameter, variable, dataType, length, driverOptions);
278
279     _bindParams.add(bindParam);
280
281     return true;
282   }
283
284   public boolean bindValue(Value parameter,
285                            Value value,
286                            @Optional("-1") int dataType)
287   {
288     return bindParam(parameter, value.toValue(), dataType, -1, null);
289   }
290
291   /**
292    * Closes the current cursor.
293    */

294   public boolean closeCursor()
295   {
296     if (_resultSet == null)
297       return false;
298
299     ResultSet resultSet = _resultSet;
300
301     _resultSet = null;
302     _resultSetMetaData = null;
303     _resultSetExhausted = true;
304     _lastInsertId = null;
305
306     try {
307       resultSet.close();
308     }
309     catch (SQLException e) {
310       _error.error(e);
311
312       return false;
313     }
314
315     return true;
316   }
317
318   /**
319    * Returns the number of columns.
320    */

321   public int columnCount()
322   {
323     if (_resultSet == null)
324       return 0;
325
326     try {
327       return getResultSetMetaData().getColumnCount();
328     }
329     catch (SQLException e) {
330       _error.error(e);
331
332       return 0;
333     }
334   }
335
336   public BindParam createBindParam(Value parameter, Value value, int dataType, int length, Value driverOptions)
337   {
338     return new BindParam(parameter, value, dataType, length, driverOptions);
339   }
340
341   public void close()
342   {
343     ResultSet resultSet = _resultSet;
344     Statement statement = _statement;
345     PreparedStatement preparedStatement = _preparedStatement;
346
347     _resultSet = null;
348     _resultSetMetaData = null;
349     _resultSetExhausted = true;
350     _lastInsertId = null;
351     _statement = null;
352     _preparedStatement = null;
353
354     if (resultSet != null) {
355       try {
356         resultSet.close();
357       }
358       catch (SQLException e) {
359         log.log(Level.WARNING, e.toString(), e);
360       }
361     }
362
363     if (statement != null) {
364       try {
365         statement.close();
366       }
367       catch (SQLException e) {
368         log.log(Level.WARNING, e.toString(), e);
369       }
370     }
371
372     if (preparedStatement != null) {
373       try {
374         preparedStatement.close();
375       }
376       catch (SQLException e) {
377         log.log(Level.WARNING, e.toString(), e);
378       }
379     }
380   }
381
382   public String JavaDoc errorCode()
383   {
384     return _error.errorCode();
385   }
386
387   public ArrayValue errorInfo()
388   {
389     return _error.errorInfo();
390   }
391
392   /**
393    * Execute the statement.
394    *
395    * @param inputParameters an array containing input values to correspond to
396    * the bound parameters for the statement.
397    *
398    * @return true for success, false for failure
399    */

400   public boolean execute(@Optional @ReadOnly Value inputParameters)
401   {
402     // XXX: s/b to do this with ArrayValue arg, but cannot differentiate between
403
// no args and bad arg that isn't an ArrayValue
404
ArrayValue parameters;
405
406     if (inputParameters instanceof ArrayValue)
407       parameters = (ArrayValue) inputParameters;
408     else if (inputParameters instanceof DefaultValue)
409       parameters = null;
410     else {
411       _env.warning(L.l("'{0}' is an unexpected argument, expected ArrayValue", inputParameters));
412       return false;
413     }
414
415     closeCursor();
416
417     try {
418       _preparedStatement.clearParameters();
419       _preparedStatement.clearWarnings();
420
421       if (parameters != null) {
422         for (Map.Entry<Value, Value> entry : parameters.entrySet()) {
423           Value key = entry.getKey();
424
425           if (key.isNumberConvertible()) {
426             if (! setParameter(key.toInt() + 1, entry.getValue(), -1))
427               return false;
428           }
429           else {
430             if (! setParameter(resolveParameter(key), entry.getValue(), -1))
431               return false;
432           }
433         }
434       }
435       else if (_bindParams != null) {
436         for (BindParam bindParam : _bindParams) {
437           if (!bindParam.apply())
438             return false;
439         }
440       }
441
442       if (_preparedStatement.execute()) {
443         _resultSet = _preparedStatement.getResultSet();
444         _resultSetExhausted = false;
445       }
446
447       SQLWarning sqlWarning = _preparedStatement.getWarnings();
448
449       if (sqlWarning != null) {
450         _error.error(sqlWarning);
451         return false;
452       }
453
454       return true;
455     } catch (SQLException e) {
456       _error.error(e);
457
458       return false;
459     }
460   }
461
462   /**
463    * Fetch the next row.
464    *
465    * @param fetchMode the mode, 0 to use the value set by {@link #setFetchMode}.
466    * @return a value, BooleanValue.FALSE if there are no more rows or an error occurs.
467    */

468   public Value fetch(@Optional int fetchMode,
469                      @Optional("-1") int cursorOrientation,
470                      @Optional("-1") int cursorOffset)
471   {
472     if (cursorOrientation != -1)
473       throw new UnimplementedException("fetch with cursorOrientation");
474
475     if (cursorOffset != -1)
476       throw new UnimplementedException("fetch with cursorOffset");
477
478     return fetchImpl(fetchMode, -1);
479   }
480
481   /**
482    *
483    * @param fetchMode
484    * @param columnIndex 0-based column index when fetchMode is FETCH_BOTH
485    */

486   public Value fetchAll(@Optional("0") int fetchMode, @Optional("-1") int columnIndex)
487   {
488     int effectiveFetchMode;
489
490     if (fetchMode == 0) {
491       effectiveFetchMode = _fetchMode;
492     }
493     else {
494       effectiveFetchMode = fetchMode;
495     }
496
497     boolean isGroup = (fetchMode & PDO.FETCH_GROUP) != 0;
498     boolean isUnique = (fetchMode & PDO.FETCH_UNIQUE) != 0;
499
500     if (isGroup)
501       throw new UnimplementedException("PDO.FETCH_GROUP");
502
503     if (isUnique)
504       throw new UnimplementedException("PDO.FETCH_UNIQUE");
505
506     effectiveFetchMode = effectiveFetchMode & (~(PDO.FETCH_GROUP | PDO.FETCH_UNIQUE));
507
508     switch (effectiveFetchMode) {
509       case PDO.FETCH_COLUMN:
510         break;
511
512       case PDO.FETCH_LAZY:
513         _error.warning(L.l("PDO::FETCH_LAZY can't be used with PDOStatement::fetchAll()"));
514         return BooleanValue.FALSE;
515
516       default:
517         if (columnIndex != -1) {
518           _error.warning(L.l("unexpected arguments"));
519           return BooleanValue.FALSE;
520         }
521     }
522
523     ArrayValueImpl rows = new ArrayValueImpl();
524
525     while (true) {
526       Value value = fetchImpl(effectiveFetchMode, columnIndex);
527
528       if (value == FETCH_FAILURE) {
529         rows.clear();
530         return rows;
531       }
532
533       if (value == FETCH_EXHAUSTED)
534         break;
535
536       if (value == FETCH_CONTINUE)
537         continue;
538
539       rows.put(value);
540     }
541
542     return rows;
543   }
544
545   private Value fetchAssoc()
546   {
547     try {
548       if (!advanceResultSet())
549         return FETCH_EXHAUSTED;
550
551       if (_fetchModeArgs.length != 0) {
552         _error.notice(L.l("unexpected arguments"));
553         return FETCH_FAILURE;
554       }
555
556       ArrayValueImpl array = new ArrayValueImpl();
557
558       int columnCount = getResultSetMetaData().getColumnCount();
559
560       for (int i = 1; i <= columnCount; i++) {
561         String JavaDoc name = getResultSetMetaData().getColumnName(i);
562         Value value = getColumnValue(i);
563
564         array.put(new StringValueImpl(name), value);
565       }
566
567       return array;
568     }
569     catch (SQLException ex) {
570       _error.error(ex);
571       return FETCH_FAILURE;
572     }
573   }
574
575   private Value fetchBoth()
576   {
577     try {
578       if (!advanceResultSet())
579         return FETCH_EXHAUSTED;
580
581       if (_fetchModeArgs.length != 0) {
582         _error.notice(L.l("unexpected arguments"));
583         return FETCH_FAILURE;
584       }
585
586
587       ArrayValueImpl array = new ArrayValueImpl();
588
589       int columnCount = getResultSetMetaData().getColumnCount();
590
591       for (int i = 1; i <= columnCount; i++) {
592         String JavaDoc name = getResultSetMetaData().getColumnName(i);
593         Value value = getColumnValue(i);
594
595         array.put(new StringValueImpl(name), value);
596         array.put(new LongValue(i - 1), value);
597       }
598
599       return array;
600     }
601     catch (SQLException ex) {
602       _error.error(ex);
603       return FETCH_FAILURE;
604     }
605   }
606
607   private Value fetchBound()
608   {
609     if (!advanceResultSet())
610       return FETCH_EXHAUSTED;
611
612     return FETCH_SUCCESS;
613   }
614
615   private Value fetchClass()
616   {
617     String JavaDoc className;
618     Value[] ctorArgs;
619
620     if (_fetchModeArgs.length == 0 || _fetchModeArgs.length > 2)
621       return fetchBoth();
622
623     className = _fetchModeArgs[0].toString();
624
625     if (_fetchModeArgs.length == 2) {
626       if (_fetchModeArgs[1].isArray()) {
627         // XXX: inefiicient, but args[1].getValueArray(_env) doesn't handle references
628
ArrayValue argsArray = (ArrayValue) _fetchModeArgs[1];
629
630         ctorArgs = new Value[argsArray.getSize()];
631
632         int i = 0;
633
634         for (Value key : argsArray.keySet())
635           ctorArgs[i++] = argsArray.getRef(key);
636       }
637       else
638         return fetchBoth();
639     }
640     else
641       ctorArgs = NULL_VALUES;
642
643     return fetchObject(className, ctorArgs);
644   }
645
646   /**
647    * @param column 0-based column number
648    */

649   public Value fetchColumn(@Optional int column)
650   {
651     if (!advanceResultSet())
652       return FETCH_EXHAUSTED;
653
654     if (column < 0 && _fetchModeArgs.length > 0)
655       column = _fetchModeArgs[0].toInt();
656
657     try {
658       if (column < 0 || column >= getResultSetMetaData().getColumnCount())
659         return FETCH_CONTINUE;
660
661       return getColumnValue(column + 1);
662     }
663     catch (SQLException ex) {
664       _error.error(ex);
665       return FETCH_FAILURE;
666     }
667   }
668
669   private Value fetchFunc()
670   {
671     throw new UnimplementedException();
672   }
673
674   /**
675    * Fetch the next row.
676    *
677    * @param fetchMode the mode, 0 to use the value set by {@link #setFetchMode}.
678    * @return a value, BooleanValue.FALSE if there are no more rows or an error occurs.
679    */

680   private Value fetchImpl(int fetchMode, int columnIndex)
681   {
682     if (fetchMode == 0) {
683       fetchMode = _fetchMode;
684
685       fetchMode = fetchMode & (~(PDO.FETCH_GROUP | PDO.FETCH_UNIQUE));
686     }
687     else {
688       if ((fetchMode & PDO.FETCH_GROUP) != 0) {
689         _error.warning(L.l("FETCH_GROUP is not allowed"));
690         return BooleanValue.FALSE;
691       }
692       else if ((fetchMode & PDO.FETCH_UNIQUE) != 0) {
693         _error.warning(L.l("FETCH_UNIQUE is not allowed"));
694         return BooleanValue.FALSE;
695       }
696     }
697
698     boolean isClasstype = (fetchMode & PDO.FETCH_CLASSTYPE) != 0;
699     boolean isSerialize = (fetchMode & PDO.FETCH_SERIALIZE) != 0;
700
701     fetchMode = fetchMode & (~(PDO.FETCH_CLASSTYPE | PDO.FETCH_SERIALIZE));
702
703     switch (fetchMode) {
704       case PDO.FETCH_ASSOC:
705         return fetchAssoc();
706
707       case PDO.FETCH_BOTH:
708         return fetchBoth();
709
710       case PDO.FETCH_BOUND:
711         return fetchBound();
712
713       case PDO.FETCH_COLUMN:
714         return fetchColumn(columnIndex);
715
716       case PDO.FETCH_CLASS:
717         return fetchClass();
718
719       case PDO.FETCH_FUNC:
720         return fetchFunc();
721
722       case PDO.FETCH_INTO:
723         return fetchInto();
724
725       case PDO.FETCH_LAZY:
726         return fetchLazy();
727
728       case PDO.FETCH_NAMED:
729         return fetchNamed();
730
731       case PDO.FETCH_NUM:
732         return fetchNum();
733
734       case PDO.FETCH_OBJ:
735         return fetchObject();
736
737     default:
738       _error.warning(L.l("invalid fetch mode {0}", fetchMode));
739       closeCursor();
740       return BooleanValue.FALSE;
741     }
742   }
743
744   private Value fetchInto()
745   {
746     assert _fetchModeArgs.length > 0;
747     assert _fetchModeArgs[0].isObject();
748
749     Value var = _fetchModeArgs[0];
750
751     if (!advanceResultSet())
752       return FETCH_EXHAUSTED;
753
754     try {
755       int columnCount = getResultSetMetaData().getColumnCount();
756
757       for (int i = 1; i <= columnCount; i++) {
758         String JavaDoc name = getResultSetMetaData().getColumnName(i);
759         Value value = getColumnValue(i);
760
761         var.putField(_env, name, value);
762       }
763     }
764     catch (SQLException ex) {
765       _error.error(ex);
766       return FETCH_FAILURE;
767     }
768
769     return var;
770   }
771
772   private Value fetchLazy()
773   {
774     // XXX: need to check why lazy is no different than object
775
return fetchObject(null, NULL_VALUES);
776   }
777
778   private Value fetchNamed()
779   {
780     try {
781       if (!advanceResultSet())
782         return FETCH_EXHAUSTED;
783
784       ArrayValue array = new ArrayValueImpl();
785
786       int columnCount = getResultSetMetaData().getColumnCount();
787
788       for (int i = 1; i <= columnCount; i++) {
789         Value name = new StringValueImpl(getResultSetMetaData().getColumnName(i));
790         Value value = getColumnValue(i);
791
792         Value existingValue = array.get(name);
793
794         if (! (existingValue instanceof UnsetValue)) {
795
796           if (! existingValue.isArray()) {
797             ArrayValue arrayValue = new ArrayValueImpl();
798             arrayValue.put(existingValue);
799             array.put(name, arrayValue);
800             existingValue = arrayValue;
801           }
802
803           existingValue.put(value);
804         }
805         else
806           array.put(name, value);
807       }
808
809       return array;
810     }
811     catch (SQLException ex) {
812       _error.error(ex);
813       return FETCH_FAILURE;
814     }
815   }
816
817   private Value fetchNum()
818   {
819     try {
820       if (!advanceResultSet())
821         return FETCH_EXHAUSTED;
822
823       if (_fetchModeArgs.length != 0) {
824         _error.notice(L.l("unexpected arguments"));
825         return FETCH_FAILURE;
826       }
827
828       ArrayValueImpl array = new ArrayValueImpl();
829
830       int columnCount = getResultSetMetaData().getColumnCount();
831
832       for (int i = 1; i <= columnCount; i++) {
833         Value value = getColumnValue(i);
834
835         array.put(value);
836       }
837
838       return array;
839     }
840     catch (SQLException ex) {
841       _error.error(ex);
842       return FETCH_FAILURE;
843     }
844   }
845
846   private Value fetchObject()
847   {
848     return fetchObject(null, NULL_VALUES);
849   }
850
851   public Value fetchObject(@Optional String JavaDoc className, @Optional Value[] args)
852   {
853     QuercusClass cl;
854
855     if (className != null) {
856       cl = _env.findAbstractClass(className);
857
858       if (cl == null)
859         return fetchBoth();
860     }
861     else {
862       cl = null;
863
864       if (args.length != 0) {
865         advanceResultSet();
866         _error.warning(L.l("unexpected arguments"));
867         return BooleanValue.FALSE;
868       }
869     }
870
871     if (!advanceResultSet())
872       return FETCH_EXHAUSTED;
873
874     try {
875       Value object;
876
877       if (cl != null)
878         object = cl.callNew(_env, args);
879       else
880         object = _env.createObject();
881
882       int columnCount = getResultSetMetaData().getColumnCount();
883
884       for (int i = 1; i <= columnCount; i++) {
885         String JavaDoc name = getResultSetMetaData().getColumnName(i);
886         Value value = getColumnValue(i);
887
888         object.putField(_env, name, value);
889       }
890
891       return object;
892     }
893     catch (Throwable JavaDoc ex) {
894       _error.error(ex);
895       return FETCH_FAILURE;
896     }
897
898   }
899
900   public Value getAttribute(int attribute)
901   {
902     _error.unsupportedAttribute(attribute);
903
904     return BooleanValue.FALSE;
905   }
906
907   /**
908    * @param column 0-based column index
909    */

910   public Value getColumnMeta(int column)
911   {
912     throw new UnimplementedException();
913   }
914
915   /**
916    * @param column 1-based column index
917    */

918   private Value getColumnValue(int column)
919     throws SQLException
920   {
921     return getColumnValue(column, -1, -1);
922   }
923
924   /**
925    * @param column 1-based column index
926    * @param jdbcType a jdbc type, or -1 if it is unknown
927    * @param returnType a PDO.PARAM_* type, or -1
928    */

929   private Value getColumnValue(int column, int jdbcType, int returnType)
930     throws SQLException
931   {
932     if (returnType != -1)
933       throw new UnimplementedException("parm type " + returnType);
934
935     if (jdbcType == -1)
936       jdbcType = getResultSetMetaData().getColumnType(column);
937
938     // XXX: needs tests
939

940     switch (jdbcType) {
941       case Types.NULL:
942         return NullValue.NULL;
943
944       case Types.BIT:
945       case Types.TINYINT:
946       case Types.SMALLINT:
947       case Types.INTEGER:
948       case Types.BIGINT:
949       {
950         String JavaDoc value = _resultSet.getString(column);
951
952         if (value == null || _resultSet.wasNull())
953           return NullValue.NULL;
954         else
955           return new StringValueImpl(value);
956       }
957
958       case Types.DOUBLE:
959       {
960         double value = _resultSet.getDouble(column);
961
962         if (_resultSet.wasNull())
963           return NullValue.NULL;
964         else
965           return (new DoubleValue(value)).toStringValue();
966       }
967
968       // XXX: lob
969

970       default:
971       {
972         String JavaDoc value = _resultSet.getString(column);
973
974         if (value == null || _resultSet.wasNull())
975           return NullValue.NULL;
976         else
977           return new StringValueImpl(value);
978       }
979     }
980
981   }
982
983   private ResultSetMetaData getResultSetMetaData()
984     throws SQLException
985   {
986     if (_resultSetMetaData == null)
987       _resultSetMetaData = _resultSet.getMetaData();
988
989     return _resultSetMetaData;
990   }
991
992   /**
993    * Returns an iterator of the values.
994    */

995   public Iterator JavaDoc<Value> iterator()
996   {
997     Value value = fetchAll(0, -1);
998
999     if (value instanceof ArrayValue)
1000      return ((ArrayValue) value).values().iterator();
1001    else {
1002      Set JavaDoc<Value> emptySet = Collections.emptySet();
1003      return emptySet.iterator();
1004    }
1005  }
1006
1007  String JavaDoc lastInsertId(String JavaDoc name)
1008  {
1009    if (!(name == null || name.length() == 0))
1010      throw new UnimplementedException("lastInsertId with name ");
1011
1012    if (_lastInsertId != null)
1013      return _lastInsertId;
1014
1015    String JavaDoc lastInsertId = null;
1016
1017    Statement stmt;
1018
1019    if (_preparedStatement != null)
1020      stmt = _preparedStatement;
1021    else
1022      stmt = _statement;
1023
1024    ResultSet resultSet = null;
1025
1026    try {
1027      resultSet = stmt.getGeneratedKeys();
1028
1029      if (resultSet.next())
1030        lastInsertId = resultSet.getString(1);
1031    }
1032    catch (SQLException ex) {
1033      _error.error(ex);
1034    }
1035    finally {
1036      try {
1037        if (resultSet != null)
1038          resultSet.close();
1039      }
1040      catch (SQLException ex) {
1041        log.log(Level.WARNING, ex.toString(), ex);
1042      }
1043    }
1044
1045    _lastInsertId = lastInsertId == null ? "0" : lastInsertId;
1046
1047    return _lastInsertId;
1048  }
1049
1050  public boolean nextRowset()
1051  {
1052    throw new UnimplementedException();
1053  }
1054
1055  private int resolveParameter(Value parameter)
1056  {
1057    int index = -1;
1058
1059    if (parameter instanceof LongValue) {
1060      // slight optimization for normal case
1061
index = parameter.toInt();
1062    }
1063    else {
1064      String JavaDoc name = parameter.toString();
1065
1066      if (name.length() > 1 && name.charAt(0) == ':') {
1067        name = name.substring(1);
1068        if (_parameterNameMap != null)
1069          index = _parameterNameMap.get(name);
1070      }
1071      else
1072        index = parameter.toInt();
1073    }
1074
1075    return index;
1076  }
1077
1078  public int rowCount()
1079  {
1080    if (_resultSet == null)
1081      return 0;
1082
1083    try {
1084      int row = _resultSet.getRow();
1085
1086      try {
1087        _resultSet.last();
1088
1089        return _resultSet.getRow();
1090      }
1091      finally {
1092        if (row == 0)
1093          _resultSet.beforeFirst();
1094        else
1095          _resultSet.absolute(row);
1096      }
1097    }
1098    catch (SQLException ex) {
1099      _error.error(ex);
1100      return 0;
1101    }
1102  }
1103
1104  public boolean setAttribute(int attribute, Value value)
1105  {
1106    return setAttribute(attribute, value, false);
1107  }
1108
1109  public boolean setAttribute(int attribute, Value value, boolean isFromConstructor)
1110  {
1111    if (isFromConstructor) {
1112      switch (attribute) {
1113        case PDO.CURSOR_FWDONLY:
1114        case PDO.CURSOR_SCROLL:
1115          return setCursor(attribute);
1116      }
1117    }
1118
1119    _error.unsupportedAttribute(attribute);
1120
1121    return false;
1122  }
1123
1124  private boolean setCursor(int attribute)
1125  {
1126    switch (attribute) {
1127      case PDO.CURSOR_FWDONLY:
1128        throw new UnimplementedException();
1129      case PDO.CURSOR_SCROLL:
1130        throw new UnimplementedException();
1131
1132      default:
1133        _error.unsupportedAttribute(attribute);
1134        return false;
1135    }
1136  }
1137
1138
1139  /**
1140   * Sets the fetch mode, the default is {@link PDO.FETCH_BOTH}.
1141   */

1142  public boolean setFetchMode(int fetchMode, Value[] args)
1143  {
1144    _fetchMode = PDO.FETCH_BOTH;
1145    _fetchModeArgs = NULL_VALUES;
1146
1147    int fetchStyle = fetchMode;
1148
1149    boolean isGroup = (fetchMode & PDO.FETCH_GROUP) != 0;
1150    boolean isUnique = (fetchMode & PDO.FETCH_UNIQUE) != 0;
1151
1152    if (isGroup)
1153      throw new UnimplementedException("PDO.FETCH_GROUP");
1154
1155    if (isUnique)
1156      throw new UnimplementedException("PDO.FETCH_UNIQUE");
1157
1158    fetchStyle = fetchStyle & (~(PDO.FETCH_GROUP | PDO.FETCH_UNIQUE));
1159
1160    boolean isClasstype = (fetchMode & PDO.FETCH_CLASSTYPE) != 0;
1161    boolean isSerialize = (fetchMode & PDO.FETCH_SERIALIZE) != 0;
1162
1163    fetchStyle = fetchStyle & (~(PDO.FETCH_CLASSTYPE | PDO.FETCH_SERIALIZE));
1164
1165    switch (fetchStyle) {
1166      case PDO.FETCH_ASSOC:
1167      case PDO.FETCH_BOTH:
1168      case PDO.FETCH_BOUND:
1169      case PDO.FETCH_LAZY:
1170      case PDO.FETCH_NAMED:
1171      case PDO.FETCH_NUM:
1172      case PDO.FETCH_OBJ:
1173        break;
1174
1175      case PDO.FETCH_CLASS:
1176        if (args.length < 1 || args.length > 2)
1177          return false;
1178
1179        if (_env.findAbstractClass(args[0].toString()) == null)
1180          return false;
1181
1182        if (args.length == 2 && !(args[1].isNull() || args[1].isArray())) {
1183          _env.warning(L.l("constructor args must be an array"));
1184
1185          return false;
1186        }
1187
1188        break;
1189
1190      case PDO.FETCH_COLUMN:
1191        if (args.length != 1)
1192          return false;
1193
1194        break;
1195
1196     case PDO.FETCH_FUNC:
1197       _error.warning(L.l("PDO::FETCH_FUNC can only be used with PDOStatement::fetchAll()"));
1198       return false;
1199
1200      case PDO.FETCH_INTO:
1201        if (args.length != 1 || !args[0].isObject())
1202          return false;
1203
1204        break;
1205
1206      default:
1207        _error.warning(L.l("invalid fetch mode"));
1208        break;
1209    }
1210
1211    _fetchModeArgs = args;
1212    _fetchMode = fetchMode;
1213
1214    return true;
1215  }
1216
1217  /**
1218   * @param index 1-based position number
1219   * @param value the value for the parameter
1220   *
1221   * @return true for success, false for failure
1222   */

1223  private boolean setLobParameter(int index, Value value, long length)
1224  {
1225    try {
1226      if (value == null || value.isNull()) {
1227        _preparedStatement.setObject(index, null);
1228      }
1229      else if (value instanceof StringValue) {
1230        if (length < 0) {
1231          _preparedStatement.setBinaryStream(index, value.toInputStream(), value.toString().length());
1232        }
1233        else
1234          _preparedStatement.setBinaryStream(index, value.toInputStream(), (int) length);
1235      }
1236      else {
1237        InputStream JavaDoc inputStream = value.toInputStream();
1238
1239        if (inputStream == null) {
1240          _error.warning(L.l("type {0} ({1}) for parameter index {2} cannot be used for lob", value.getType(), value.getClass(), index));
1241          return false;
1242        }
1243
1244        if (length < 0 && (value instanceof FileReadValue)) {
1245          length = ((FileReadValue) value).getLength();
1246
1247          if (length <= 0)
1248            length = -1;
1249        }
1250
1251        if (length < 0) {
1252          TempBuffer tempBuffer = TempBuffer.allocate();
1253
1254          try {
1255            byte[] bytes = new byte[1024];
1256
1257            int len;
1258
1259            while ((len = inputStream.read(bytes, 0, 1024)) != -1)
1260              tempBuffer.write(bytes, 0, len);
1261          }
1262          catch (IOException JavaDoc ex) {
1263            _error.error(ex);
1264            return false;
1265          }
1266
1267          TempReadStream tempReadStream = new TempReadStream(tempBuffer);
1268          tempReadStream.setFreeWhenDone(true);
1269
1270          _preparedStatement.setBinaryStream(index, new ReadStream(tempReadStream), tempBuffer.getLength());
1271        }
1272        else
1273          _preparedStatement.setBinaryStream(index, inputStream, (int) length);
1274      }
1275    }
1276    catch (SQLException ex) {
1277      _error.error(ex);
1278      return false;
1279    }
1280
1281    return true;
1282  }
1283
1284  /**
1285   * @param index 1-based position number
1286   * @param value the value for the parameter
1287   *
1288   * @return true for success, false for failure
1289   */

1290  private boolean setParameter(int index, Value value, long length)
1291  {
1292    try {
1293      if (value instanceof DoubleValue) {
1294        _preparedStatement.setDouble(index, value.toDouble());
1295      }
1296      else if (value instanceof LongValue) {
1297        _preparedStatement.setLong(index, value.toLong());
1298      }
1299      else if (value instanceof StringValue) {
1300        String JavaDoc string = value.toString();
1301
1302        if (length >= 0)
1303          string = string.substring(0, (int) length);
1304
1305        _preparedStatement.setString(index, string);
1306      }
1307      else if (value instanceof NullValue) {
1308        _preparedStatement.setObject(index, null);
1309      }
1310      else {
1311        _error.warning(L.l("unknown type {0} ({1}) for parameter index {2}", value.getType(), value.getClass(), index));
1312        return false;
1313      }
1314    }
1315    catch (SQLException ex) {
1316      _error.error(ex);
1317      return false;
1318    }
1319
1320    return true;
1321  }
1322
1323  public String JavaDoc toString()
1324  {
1325    return "PDOStatement[" + _query + "]";
1326  }
1327
1328  /**
1329   * Bind a value from a resultSet to a variable.
1330   */

1331  private class BindColumn {
1332    private final String JavaDoc _columnAsName;
1333    private final Value _var;
1334    private final int _type;
1335
1336    private int _column;
1337    private int _jdbcType;
1338
1339    private boolean _isInit;
1340    private boolean _isValid;
1341
1342    /**
1343     * @param column 1-based column index
1344     * @param var reference that receives the value
1345     * @param type a PARM_* type, -1 for default
1346     */

1347    private BindColumn(Value column, Value var, int type)
1348      throws SQLException
1349    {
1350      assert column != null;
1351      assert var != null;
1352
1353      if (column.isNumberConvertible()) {
1354        _column = column.toInt();
1355        _columnAsName = null;
1356      }
1357      else {
1358        _columnAsName = column.toString();
1359      }
1360
1361      _var = var;
1362      _type = type;
1363
1364      if (_resultSet != null)
1365        init();
1366    }
1367
1368    private boolean init()
1369      throws SQLException
1370    {
1371      if (_isInit)
1372        return true;
1373
1374      ResultSetMetaData resultSetMetaData = getResultSetMetaData();
1375
1376      int columnCount = resultSetMetaData.getColumnCount();
1377
1378      if (_columnAsName != null) {
1379
1380        for (int i = 1; i <= columnCount; i++) {
1381          String JavaDoc name = resultSetMetaData.getColumnName(i);
1382          if (name.equals(_columnAsName)) {
1383            _column = i;
1384            break;
1385          }
1386        }
1387      }
1388
1389      _isValid = _column > 0 && _column <= columnCount;
1390
1391      if (_isValid) {
1392        _jdbcType = resultSetMetaData.getColumnType(_column);
1393      }
1394
1395      _isInit = true;
1396
1397      return true;
1398    }
1399
1400    public boolean bind()
1401      throws SQLException
1402    {
1403      if (!init())
1404        return false;
1405
1406      if (!_isValid) {
1407        // this matches php behaviour
1408
_var.set(StringValueImpl.EMPTY);
1409      }
1410      else {
1411        Value value = getColumnValue(_column, _jdbcType, _type);
1412
1413        _var.set(value);
1414      }
1415
1416      return true;
1417    }
1418  }
1419
1420  /**
1421   * Bind a value to a parameter when the statement is executed.
1422   */

1423  private class BindParam {
1424    private final int _index;
1425    private final Value _value;
1426    private final int _dataType;
1427    private final int _length;
1428    private final Value _driverOptions;
1429
1430    public BindParam(Value parameter, Value value, int dataType, int length, Value driverOptions)
1431    {
1432      int index = resolveParameter(parameter);
1433
1434      _index = index;
1435      _value = value;
1436      _dataType = dataType;
1437      _length = length;
1438      _driverOptions = driverOptions;
1439    }
1440
1441    public boolean apply()
1442      throws SQLException
1443    {
1444      switch (_dataType) {
1445        case PDO.PARAM_BOOL:
1446        case PDO.PARAM_INT:
1447        case PDO.PARAM_STR:
1448          return setParameter(_index, _value.toValue(), _length);
1449        case PDO.PARAM_LOB:
1450          return setLobParameter(_index, _value.toValue(), _length);
1451        case PDO.PARAM_NULL:
1452          return setParameter(_index, NullValue.NULL, _length);
1453        case PDO.PARAM_STMT:
1454          throw new UnimplementedException("PDO.PARAM_STMT");
1455        default:
1456          throw new AssertionError JavaDoc();
1457      }
1458    }
1459  }
1460}
1461
Popular Tags