Source code: com/tripi/asp/ADODB/RecordSet.java
1 /**
2 * ArrowHead ASP Server
3 * This is a source file for the ArrowHead ASP Server - an 100% Java
4 * VBScript interpreter and ASP server.
5 *
6 * For more information, see http://www.tripi.com/arrowhead
7 *
8 * Copyright (C) 2002 Terence Haddock
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 *
24 */
25 package com.tripi.asp.ADODB;
26
27 import com.tripi.asp.*;
28
29 import java.sql.SQLException;
30 import java.sql.ResultSet;
31 import java.sql.ResultSetMetaData;
32 import java.sql.Statement;
33
34 import java.util.Enumeration;
35 import java.util.Hashtable;
36
37 import java.io.*;
38
39 import org.apache.log4j.Category;
40
41 /**
42 * This class implements the ADODB.RecordSet object.
43 * RecordSet Implementation status:
44 * <ul>
45 * <li>AbsolutePage - Implemented <b>TODO</b> - locking/cursor type
46 * <li>AbsolutePosition - Implemented <b>TODO</b> - locking/cursor type
47 * <li>ActiveCommand - Fully implemented
48 * <li>ActiveConnection - Implemented
49 * <li>BOF - Implemented
50 * <li>EOF - Implemented
51 * <li>Bookmark - <b>TODO</b> Not implemented
52 * <li>CacheSize - <b>TODO</b> Not implemented
53 * <li>CursorLocation - <b>TODO</b> Not implemented
54 * <li>CursorType - <b>TODO</b> Not implemented
55 * <li>Filter - <b>TODO</b> Not implemented
56 * <li>Index - <b>TODO</b> Not implemented
57 * <li>LockType - <b>TODO</b> Not implemented
58 * <li>MarshalOptions - <b>TODO</b> Not implemented
59 * <li>MaxRecords - <b>TODO</b> Not implemented
60 * <li>PageCount - Implemented <b>TODO</b> - locking / cursor type
61 * <li>PageSize - <b>TODO</b> Not implemented
62 * <li>RecordCount - Implemented <b>TODO</b> - locking / cursor type
63 * <li>Sort - <B>TODO</b> Not implemented
64 * <li>Source - Implemented
65 * <li>State - Implemented
66 * <li>Status - Implemented
67 * <li>StayInSync - <b>TODO</b> Not implemented
68 * <li>AddNew - Implemented
69 * <li>Cancel - Not implemented, not usable in web-based ADODB
70 * <li>CancelBatch - <b>TODO</b> Not implemented
71 * <li>CancelUpdate - Implemented
72 * <li>Clone - <b>TODO</b> Not implemented
73 * <li>Close - Implemented
74 * <li>CompareBookmarks - <B>TODO</b> Not implemented
75 * <li>Delete - <B>TODO</b> Not implemented
76 * <li>Find - <B>TODO</b> Not implemented
77 * <li>GetRows - <B>TODO</b> Not implemented
78 * <li>GetString - <b>TODO</b> Not implemented
79 * <li>Move - Implemented
80 * <li>MoveFirst - Implemented
81 * <li>MoveLast - Implemented
82 * <li>MoveNext - Implemented
83 * <li>MovePrevious - Implemented
84 * <li>NextRecordSet - <B>TODO</b> Not implemented
85 * <li>Open - Implemented
86 * <li>Requery - Implemented
87 * <li>Resync - <B>TODO</b> Not implemented
88 * <li>Save - <B>TODO</b> Not implemented
89 * <li>Seek - <B>TODO</b> Not implemented
90 * <li>Supports - <B>TODO</b> Not implemented
91 * <li>Update - Implemented, <B>TODO</b> Array-updating not implemented
92 * <li>UpdateBatch - <B>TODO</b> Not implemented
93 * </ul>
94 * ADODB.Field implementation status:
95 * <ul>
96 * <li>ActualSize - <b>TODO</b> Not implemented
97 * <li>Attributes - <b>TODO</b> Not implemented
98 * <li>DefinedSize - Implemented
99 * <li>Name - Implemented
100 * <li>NumericScale - Implemented
101 * <li>OriginalValue - <B>TODO</b> Not implemented
102 * <li>Precision - Implemented
103 * <li>Status - <b>TODO</b> Not implemented
104 * <li>Type - <b>TODO</b> Not implemented
105 * <li>UnderlyingValue - <b>TODO</b> Not implemented
106 * <li>Value - Implemented
107 * <li>AppendChunk - <b>TODO</b> Not implemented
108 * <li>GetChunk - <b>TODO</b> Not implemented
109 * <li>Append - <b>TODO</b> Not implemented
110 * </ul>
111 * @author Terence Haddock
112 */
113 public class RecordSet implements SimpleMap
114 {
115 /** Debugging category */
116 static Category DBG = Category.getInstance(RecordSet.class.getName());
117
118 /** Active record set, null if no active RS */
119 java.sql.ResultSet rs;
120
121 /** Active statement, null if no active statement */
122 java.sql.Statement stmt;
123
124 /** Meta data for the query */
125 ResultSetMetaData rsmd = null;
126
127 /** Hashtable mapping column names to index. */
128 Hashtable columnNames;
129
130 /** This variable is marked true when there are no results in the query */
131 boolean hasNoElements = false;
132
133 /** This variable is marked true when the query is currently on the insert row */
134 boolean isOnInsertRow = false;
135
136 /** This flag is true if we opened the connection through RS.Open */
137 boolean ownConnection = false;
138
139 /** ASP - Accessible fields array */
140 public FieldsMap Fields = new FieldsMap();
141
142 /** ASP - Accessible EOF marker */
143 public boolean EOF;
144
145 /** ASP - Accessible BOF marker */
146 public boolean BOF = true;
147
148 /** Where is the cursor, server or client? */
149 public int CursorLocation = 0;
150
151 /** Type of cursor */
152 public int CursorType = 0;
153
154 /** Size for paging */
155 public int PageSize = 10;
156
157 /** Size for caching */
158 public int CacheSize = 10;
159
160 /** Internal variable used to re-query the SQL */
161 protected String _ActiveSQL = null;
162
163 /** Internal variable to store the active connection */
164 protected Connection _ActiveConnection = null;
165
166 /** Internal variable to store the active command */
167 protected Command _ActiveCommand = null;
168
169 /** Constructor with no parameters. */
170 public RecordSet()
171 {
172 this.rs = null;
173 }
174
175 /**
176 * ASP-Accessible function
177 * @return the active connection
178 */
179 public String ActiveConnection()
180 {
181 return _ActiveConnection.ConnectionString;
182 }
183
184 /**
185 * ASP-Accessible function
186 * @return the active command
187 */
188 public Object ActiveCommand()
189 {
190 if (_ActiveCommand == null)
191 return "";
192 return _ActiveCommand;
193 }
194
195 /**
196 * Internal function to set the active connection.
197 * @param con New connection
198 */
199 protected void setConnection(Connection con)
200 {
201 this._ActiveConnection = con;
202 }
203
204 /**
205 * Internal function to set the active command.
206 * @param com New command
207 */
208 protected void setCommand(Command com)
209 {
210 this._ActiveCommand = com;
211 }
212
213 /**
214 * Internal function to set the active statement.
215 * @param stmt Active statement
216 */
217 protected void setStatement(java.sql.Statement stmt)
218 {
219 this.stmt = stmt;
220 }
221
222 /**
223 * Internal function to set the active result set
224 * @param rs active result set
225 */
226 protected void setResultSet(java.sql.ResultSet rs) throws AspException
227 {
228 this.rs = rs;
229 BOF = true;
230 try {
231 if (rs == null)
232 {
233 EOF = true;
234 } else {
235 rsmd = rs.getMetaData();
236 getColumnNames();
237 EOF = !rs.next();
238 if (!EOF) BOF = false;
239 if (DBG.isDebugEnabled())
240 DBG.debug("EOF: " + EOF);
241 }
242 hasNoElements = EOF;
243 } catch (SQLException ex) {
244 throw _ActiveConnection.processException(ex);
245 }
246 }
247
248 /**
249 * Internal function which obtains the column names from the result set.
250 */
251 private void getColumnNames() throws AspException
252 {
253 try {
254 int i;
255 columnNames = new Hashtable();
256 if (DBG.isDebugEnabled()) DBG.debug("Number of columns: " +
257 rsmd.getColumnCount());
258 for (i = 1; i <= rsmd.getColumnCount(); i++)
259 {
260 String name = rsmd.getColumnName(i);
261 if (DBG.isDebugEnabled())
262 DBG.debug("Column name: " + name);
263 columnNames.put(name.toLowerCase(), new Integer(i));
264 }
265 } catch (SQLException ex) {
266 throw _ActiveConnection.processException(ex);
267 }
268 }
269
270 /**
271 * ASP-Accessible function to move to the next row.
272 * @throws AspException on SQL error
273 */
274 public void MoveNext() throws AspException
275 {
276 try {
277 EOF = !rs.next();
278 if (!EOF) BOF = false;
279 } catch (SQLException ex) {
280 throw _ActiveConnection.processException(ex);
281 }
282 }
283
284 /**
285 * ASP-Accessible function to move to the first row.
286 * @throws AspException on SQL error
287 */
288 public void MoveFirst() throws AspException
289 {
290 try {
291 rs.first();
292 } catch (SQLException ex) {
293 throw _ActiveConnection.processException(ex);
294 }
295 }
296
297 /**
298 * ASP-Accessible function to move back one row.
299 * @throws AspException on SQL error
300 */
301 public void MovePrevious() throws AspException
302 {
303 try {
304 rs.previous();
305 } catch (SQLException ex) {
306 throw _ActiveConnection.processException(ex);
307 }
308 }
309
310 /**
311 * ASP-Accessible function to move to the last row.
312 * @throws AspException on SQL error
313 */
314 public void MoveLast() throws AspException
315 {
316 try {
317 rs.last();
318 } catch (SQLException ex) {
319 throw _ActiveConnection.processException(ex);
320 }
321 }
322
323 /**
324 * ASP-Accessible function to move by the specified number of rows.
325 * @param numRows number of rows to move by, can be negative to move
326 * backwards.
327 * @throws AspException on SQL error
328 */
329 public void Move(int numRows) throws AspException
330 {
331 try {
332 boolean result = rs.relative(numRows);
333 if (!result)
334 {
335 if (numRows>0) EOF = true;
336 else BOF = true;
337 }
338 if (!EOF) BOF = false;
339 } catch (SQLException ex) {
340 throw _ActiveConnection.processException(ex);
341 }
342 }
343
344 /**
345 * Open a connection, based on a string DSN.
346 * @param sql SQL statement to use
347 * @param DSN DSN to use to connect
348 * @throws AspException on SQL error
349 * @throws AspException on ASP error
350 */
351 public boolean Open(String sql, String DSN)
352 throws AspException
353 {
354 return Open(sql, DSN, 0, 0);
355 }
356
357 /**
358 * Open a connection, based on a string DSN, with a specified cursor type.
359 * @param sql SQL statement to use
360 * @param DSN DSN to use to connect
361 * @param type Cursor type
362 * @throws AspException on ASP error
363 */
364 public boolean Open(String sql, String DSN, int type)
365 throws AspException
366 {
367 return Open(sql, DSN, type, 0);
368 }
369
370 /**
371 * Open a connection, based on a string DSN, with a specified cursor type.
372 * and a specified lock type.
373 * @param sql SQL statement to use
374 * @param DSN DSN to use to connect
375 * @param type Cursor type
376 * @param lock Lock type
377 * @throws AspException on ASP error
378 */
379 public boolean Open(String sql, String DSN, int type, int lock)
380 throws AspException
381 {
382 ownConnection = true;
383 Connection con = new Connection();
384 con.Open(DSN);
385 return Open(sql, con, type, lock);
386 }
387
388 /**
389 * Open a result set, based on the specified connection object.
390 * @param sql SQL statement to use
391 * @param con Connection object
392 * @throws AspException on SQL error
393 */
394 public boolean Open(String sql, Connection con)
395 throws AspException
396 {
397 return Open(sql, con, 0, 0);
398 }
399
400 /**
401 * Open a result set, based on the specified connection object,
402 * with a specified lock type.
403 * @param sql SQL statement to use
404 * @param con Connection object
405 * @param type cursor type
406 * @throws AspException on SQL error
407 */
408 public boolean Open(String sql, Connection con, int type)
409 throws AspException
410 {
411 return Open(sql, con, type, 0);
412 }
413
414 /**
415 * Open a result set, based on the specified connection object,
416 * with a specified cursor type, and specified lock semantics.
417 * @param sql SQL statement to use
418 * @param con Connection object
419 * @param type cursor type
420 * @param lock lock type
421 * @throws AspException on SQL error
422 */
423 public boolean Open(String sql, Connection con, int type, int lock)
424 throws AspException
425 {
426 return Open(sql, con, type, lock, 0);
427 }
428
429 /**
430 * Open a result set, based on the specified connection object,
431 * with a specified cursor type, and specified lock semantics.
432 * @param sql SQL statement to use
433 * @param con Connection object
434 * @param type cursor type
435 * @param lock lock type
436 * @param flag some sort of flag
437 * @throws AspException on SQL error
438 */
439 public boolean Open(String sql, Connection con, int type, int lock, int flag)
440 throws AspException
441 {
442 try {
443 java.sql.Statement stmt = con.cx.createStatement(
444 java.sql.ResultSet.TYPE_SCROLL_INSENSITIVE,
445 java.sql.ResultSet.CONCUR_READ_ONLY);
446 _ActiveSQL = sql;
447 boolean result = stmt.execute(sql + ";");
448 setConnection(con);
449 setStatement(stmt);
450 if (result)
451 {
452 setResultSet(stmt.getResultSet());
453 } else {
454 setResultSet(null);
455 }
456 return true;
457 } catch (SQLException ex) {
458 throw con.processException(ex);
459 }
460 }
461
462 /**
463 * Open a result set, based on the specified command object.
464 * @param sql SQL statement to use
465 * @param command Command object
466 * @throws AspException on SQL error
467 */
468 public int Open(Command command) throws AspException
469 {
470 return Open(command, null, 0, 0);
471 }
472
473 /**
474 * Open a result set, based on the specified command object.
475 * <B>TODO</b> Figure out these parameters.
476 * @param sql SQL statement to use
477 * @param command Command object
478 * @param a Object A
479 * @param b Cursor type
480 * @param c Lock type
481 * @throws AspException on SQL error
482 */
483 public int Open(Command command, Object a, int b, int c)
484 throws AspException
485 {
486 return Open(command, a, b, c, 0);
487 }
488
489 /**
490 * Open a result set, based on the specified command object.
491 * <B>TODO</b> Figure out these parameters.
492 * @param sql SQL statement to use
493 * @param command Command object
494 * @param a Object A
495 * @param b Cursor type
496 * @param c Lock type
497 * @param d More flags
498 * @throws AspException on SQL error
499 */
500 public int Open(Command command, Object a, int b, int c, int d)
501 throws AspException
502 {
503 try {
504 if (DBG.isDebugEnabled()) {
505 DBG.debug("Command.CommandText: " +
506 command.CommandText);
507 DBG.debug("A = " + a);
508 DBG.debug("B = " + b);
509 DBG.debug("C = " + c);
510 DBG.debug("D = " + d);
511 }
512 Connection con = command.ActiveConnection();
513 setConnection(con);
514 String sql = command.CommandText;
515 _ActiveSQL = sql;
516 java.sql.Statement stmt = con.cx.createStatement(
517 java.sql.ResultSet.TYPE_SCROLL_INSENSITIVE,
518 java.sql.ResultSet.CONCUR_READ_ONLY);
519 boolean result = stmt.execute(sql + ";");
520 setCommand(command);
521 setStatement(stmt);
522 if (result)
523 {
524 setResultSet(stmt.getResultSet());
525 } else {
526 setResultSet(null);
527 }
528 return stmt.getUpdateCount();
529 } catch (SQLException ex) {
530 throw _ActiveConnection.processException(ex);
531 }
532 }
533
534 /**
535 * ASP-Accessible function to requery the database.
536 * @throws AspException on SQL error
537 */
538 public void Requery() throws AspException
539 {
540 Open(_ActiveSQL, _ActiveConnection, 0, 0);
541 }
542
543 /**
544 * ASP-Accessible function to add a new row.
545 * @throws AspException on SQL error
546 */
547 public void AddNew() throws AspException
548 {
549 try {
550 rs.moveToInsertRow();
551 isOnInsertRow = true;
552 } catch (SQLException ex) {
553 throw _ActiveConnection.processException(ex);
554 }
555 }
556
557 /**
558 * ASP-Accessible function to add a new row with predefined values.
559 * @param fields array of field names
560 * @param values arrow of column values
561 * @throws AspException on SQL error
562 */
563 public void AddNew(Object fields[], Object values[]) throws AspException
564 {
565 if (fields.length != values.length)
566 {
567 throw new AspException("Parameters to AddNew() should be the " +
568 "same length");
569 }
570 AddNew();
571 for (int i = 0; i < fields.length; i++)
572 {
573 if (DBG.isDebugEnabled())
574 {
575 DBG.debug("Field[" + i + "] = " + fields[i]);
576 DBG.debug("Value[" + i + "] = " + values[i]);
577 }
578 Fields.put(fields[i], values[i]);
579 }
580 Update();
581 }
582
583 /**
584 * ASP-Accessible function to return the cursor position.
585 * @return cursor position
586 * @throws AspException on SQL error
587 */
588 public int AbsolutePosition() throws AspException
589 {
590 try {
591 int pos = rs.getRow();
592 if (pos == 0) return -1;
593 return pos;
594 } catch (SQLException ex) {
595 throw _ActiveConnection.processException(ex);
596 }
597 }
598
599 /**
600 * ASP-Accessible function to set the cursor position.
601 * @param pos new cursor position
602 * @throws AspException on SQL error
603 */
604 public void AbsolutePosition(int pos) throws AspException
605 {
606 try {
607 rs.absolute(pos);
608 if (hasNoElements)
609 {
610 EOF = true;
611 } else {
612 EOF = rs.isAfterLast();
613 }
614 } catch (SQLException ex) {
615 throw _ActiveConnection.processException(ex);
616 }
617 }
618
619 /**
620 * ASP-Accessible function to return cursor page.
621 * @return cursor page
622 * @throws AspException on SQL error
623 */
624 public int AbsolutePage() throws AspException
625 {
626 int row = AbsolutePosition();
627 if (row <= 0) return -1;
628 return ((row - 1) / PageSize) + 1;
629 }
630
631 /**
632 * ASP-Accessible function to obtain the number of pages in the query
633 * @return number of pages in the query
634 * @throws AspException on SQL error
635 */
636 public int PageCount() throws AspException
637 {
638 int count = RecordCount();
639 return ((count - 1) / PageSize) + 1;
640 }
641
642 /**
643 * ASP-Accessible function to obtain the number of records in the query.
644 * @return number of records
645 * @throws AspException on SQL error
646 */
647 public int RecordCount() throws AspException
648 {
649 try {
650 int pos, last;
651
652 if (hasNoElements) {
653 return 0;
654 }
655
656 pos = rs.getRow();
657 rs.last();
658 last = rs.getRow();
659 if (pos != 0) {
660 /* If pos = 0, there is no rows in the result set,
661 so there is no reason to change the position. */
662 DBG.debug("Setting position to: " + pos);
663 rs.absolute(pos);
664 }
665 return last;
666 } catch (SQLException ex) {
667 throw _ActiveConnection.processException(ex);
668 }
669 }
670
671 /**
672 * ASP-Accessible function to close the record set.
673 * @throws AspException on SQL error
674 */
675 public void Close() throws AspException
676 {
677 try {
678 rs.close();
679 stmt.close();
680 if (ownConnection)
681 {
682 _ActiveConnection.Close();
683 }
684 } catch (SQLException ex) {
685 throw _ActiveConnection.processException(ex);
686 }
687 }
688
689 /**
690 * ASP-Accessible function to obtain a set of rows in an array.
691 * @return array of data
692 */
693 public ArrayNode GetRows() throws AspException
694 {
695 return GetRows(-1);
696 }
697
698 /**
699 * ASP-Accessible function to obtain a set of rows in an array.
700 * @param rows Number of rows to obtain
701 * @return array of data
702 */
703 public ArrayNode GetRows(int rows) throws AspException
704 {
705 if (rows == -1)
706 rows = (RecordCount() - AbsolutePosition()) + 1;
707 int fieldsCount = columnNames.size();
708 ArrayNode retArray = new ArrayNode(fieldsCount);
709 ArrayNode fields[] = new ArrayNode[fieldsCount];
710 for (int i = 0; i < fieldsCount; i++)
711 {
712 ArrayNode subArray = new ArrayNode(rows);
713 retArray._setValue(i, subArray);
714 fields[i] = subArray;
715 }
716 int row = 0;
717 while ((!EOF)&&(row < rows))
718 {
719 for (Enumeration e = columnNames.keys();e.hasMoreElements();)
720 {
721 Object key = e.nextElement();
722 Integer fieldIndex = (Integer)columnNames.get(key);
723 Object value = Fields.get(key);
724 value = Types.dereference(value);
725 fields[fieldIndex.intValue() - 1]._setValue(row, value);
726 }
727 MoveNext();
728 row++;
729 }
730 return retArray;
731 }
732
733 /**
734 * ASP-Accessible function to cancel any pending updates.
735 * @throws AspException on SQL error
736 */
737 public void CancelUpdate() throws AspException
738 {
739 try {
740 rs.cancelRowUpdates();
741 } catch (SQLException ex) {
742 throw _ActiveConnection.processException(ex);
743 }
744 }
745
746 /**
747 * ASP-Accessible function to update all pending changes.
748 * @throws AspException on SQL error
749 */
750 public void Update() throws AspException
751 {
752 try {
753 if (isOnInsertRow)
754 {
755 rs.insertRow();
756 rs.moveToCurrentRow();
757 isOnInsertRow = false;
758 } else {
759 rs.updateRow();
760 }
761 } catch (SQLException ex) {
762 throw _ActiveConnection.processException(ex);
763 }
764 }
765
766 /**
767 * SimpleMap function to obtain the value of the record at the specified
768 * index.
769 * @param key key to obtain
770 * @return value of the specified column
771 * @throws AspException on SQL error
772 */
773 public Object get(Object key) throws AspException
774 {
775 return Fields.get(key);
776 }
777
778 /**
779 * SimpleMap function to set the value of the record at the specified
780 * index.
781 * @param key key to use
782 * @param value Value to set
783 */
784 public void put(Object key, Object value) throws AspException
785 {
786 Fields.put(key, value);
787 }
788
789 /**
790 * SimpleMap function to obtain the keys in the record, effectivly the
791 * column names.
792 */
793 public Enumeration getKeys() throws AspException
794 {
795 return Fields.getKeys();
796 }
797
798 /**
799 * Internal function called on page end.
800 */
801 public void OnPageEnd(Object ignoreme)
802 {
803 try {
804 Close();
805 } catch (AspException ex)
806 {
807 DBG.error("Error on ADODB.ResultSet.OnPageEnd", ex);
808 }
809 }
810
811 /**
812 * This class implements the ADODB.Fields records.
813 */
814 public class Field implements SimpleReference
815 {
816 /**
817 * This class's column index.
818 */
819 int columnIndex;
820
821 /**
822 * Internal constructor, column index based
823 */
824 protected Field(int columnIndex)
825 {
826 this.columnIndex = columnIndex;
827 }
828
829 /**
830 * Getter, obtains the value.
831 * @return value of this object
832 * @throws AspException on error
833 */
834 public Object getValue() throws AspException
835 {
836 return Value();
837 }
838
839 /**
840 * Setting, set the value.
841 * @param newValue new value of this object
842 * @throws AspException on error
843 */
844 public void setValue(Object newValue) throws AspException
845 {
846 Value(newValue);
847 }
848
849 /**
850 * ASP-Accessible function to obtain the 'actual size' of a column.
851 * This function always return 4, as ActualSize makes no sense
852 * in Java/JDBC.
853 * @return size of column
854 */
855 public int ActualSize() throws SQLException, AspException
856 {
857 String typeName = rsmd.getColumnTypeName(columnIndex);
858 if (typeName.equalsIgnoreCase("varchar") ||
859 typeName.equalsIgnoreCase("text")) {
860 String value = (String)Value();
861 return value.length();
862 }
863
864 return 4;
865 }
866
867 /**
868 * ASP-Accessible function to obtain the column's display size.
869 * @return display size of a column.
870 * @throws SQLException on error
871 */
872 public int DefinedSize() throws SQLException
873 {
874 return rsmd.getColumnDisplaySize(columnIndex);
875 }
876
877 /**
878 * ASP-Accessible function to obtain the column's name.
879 * @return name of a column.
880 * @throws SQLException on error
881 */
882 public String Name() throws SQLException
883 {
884 return rsmd.getColumnName(columnIndex);
885 }
886
887 /**
888 * ASP-Accessible function to obtain the column's numeric scale.
889 * @return numeric scale of a column.
890 * @throws SQLException on error
891 */
892 public int NumericScale() throws SQLException
893 {
894 return rsmd.getScale(columnIndex);
895 }
896
897 /**
898 * ASP-Accessible function to obtain the column's precision.
899 * @return precision of a column.
900 * @throws SQLException on error
901 */
902 public int Precision() throws SQLException
903 {
904 return rsmd.getPrecision(columnIndex);
905 }
906
907 /**
908 * ASP-Accessible function to obtain the value of the column.
909 * @return value of column
910 * @throws SQLException on SQL error
911 */
912 public Object Value() throws AspException
913 {
914 try {
915 if (DBG.isDebugEnabled()) {
916 DBG.debug("Index of column: " + columnIndex);
917 DBG.debug("Type index: " + rsmd.getColumnType(columnIndex));
918 DBG.debug("Type name: " + rsmd.getColumnTypeName(columnIndex));
919 }
920
921 String typeName = rsmd.getColumnTypeName(columnIndex);
922 if (typeName.equalsIgnoreCase("uniqueidentifier")) {
923 InputStream is = rs.getBinaryStream(columnIndex);
924 if (is == null) {
925 return Constants.nullNode;
926 }
927 byte[] uniqueID = new byte[16];
928 int nread;
929 try {
930 nread = is.read(uniqueID);
931 } catch (java.io.IOException ex) {
932 throw new RuntimeException(ex.toString());
933 }
934 if (nread == -1) {
935 return Constants.nullNode;
936 } else if (nread != 16) {
937 throw new RuntimeException("Invalid unique identifier (length = " + nread + ")");
938 }
939 return new UniqueIdentifierObj(uniqueID);
940 } else if (typeName.equalsIgnoreCase("timestamp")) {
941 java.util.Date timestamp = rs.getTimestamp(columnIndex);
942 if (timestamp == null) return Constants.nullNode;
943 return new AspDate(timestamp);
944 } else if (typeName.equalsIgnoreCase("date")) {
945 java.util.Date date = rs.getDate(columnIndex);
946 if (date == null) return Constants.nullNode;
947 return new AspDate(date, false, true);
948 } else {
949 Object ret = rs.getObject(columnIndex);
950 if (ret == null) return Constants.nullNode;
951 return ret;
952 }
953 } catch (SQLException ex)
954 {
955 throw new AspNestedException(ex);
956 }
957 }
958
959 /**
960 * ASP-Accessible function to set this column's value.
961 * @param value Column's new value
962 * @throws SQLException on SQL error
963 */
964 public void Value(Object value) throws AspException
965 {
966 try {
967 if (DBG.isDebugEnabled()) {
968 DBG.debug("Index of column: " + columnIndex);
969 DBG.debug("Type index: " + rsmd.getColumnType(columnIndex));
970 DBG.debug("Type name: " + rsmd.getColumnTypeName(columnIndex));
971 }
972
973 String typeName = rsmd.getColumnTypeName(columnIndex);
974 if (typeName.equalsIgnoreCase("uniqueidentifier")) {
975 String uidValue = value.toString();
976 rs.updateString(columnIndex, uidValue);
977 } else {
978 if (value == null)
979 {
980 rs.updateNull(columnIndex);
981 } else {
982 rs.updateObject(columnIndex, value);
983 }
984 }
985 } catch (SQLException ex) {
986 throw new AspNestedException(ex);
987 }
988 }
989 }
990
991 /**
992 * This class represents a mapping from a field name/index to a
993 * ADODB.Fields record
994 */
995 public class FieldsMap implements SimpleMap
996 {
997 public Object get(Object key) throws AspException
998 {
999 while (key instanceof SimpleReference)
1000 {
1001 key = ((SimpleReference)key).getValue();
1002 }
1003 int keyIndex;
1004 if (key instanceof Integer)
1005 {
1006 keyIndex = ((Integer)key).intValue() + 1;
1007 } else {
1008 Integer iKey = (Integer)columnNames.get(key.toString().toLowerCase());
1009 if (iKey == null)
1010 throw new RuntimeException("Unknown column name: " + key.toString());
1011 keyIndex = iKey.intValue();
1012 }
1013 return new Field(keyIndex);
1014 }
1015
1016 public void put(Object key, Object value) throws AspException
1017 {
1018 while (key instanceof SimpleReference)
1019 {
1020 key = ((SimpleReference)key).getValue();
1021 }
1022 int keyIndex;
1023 if (key instanceof Integer)
1024 {
1025 keyIndex = ((Integer)key).intValue() + 1;
1026 } else {
1027 Integer iKey = (Integer)columnNames.get(key.toString().toLowerCase());
1028 if (iKey == null)
1029 throw new RuntimeException("Unknown column name: " + key.toString());
1030 keyIndex = iKey.intValue();
1031 }
1032 Field field = new Field(keyIndex);
1033 field.setValue(value);
1034 }
1035
1036 public Enumeration getKeys() throws AspException
1037 {
1038 throw new RuntimeException("Not implemented");
1039 }
1040
1041 /**
1042 * ASP-Accessible function to obtain the number of fields.
1043 */
1044 public int Count()
1045 {
1046 return columnNames.size();
1047 }
1048 }
1049
1050 public static class UniqueIdentifierObj
1051 implements SimpleReference
1052 {
1053 byte uniqueID[];
1054
1055 public UniqueIdentifierObj(byte bytes[]) {
1056 this.uniqueID = bytes;
1057 }
1058
1059 public Object getValue() {
1060 return toString();
1061 }
1062
1063 public void setValue(Object obj) throws AspException
1064 {
1065 throw new AspException("Modification read-only value");
1066 }
1067
1068 public String toString()
1069 {
1070 StringBuffer buf = new StringBuffer();
1071 buf.append('{');
1072 for (int i = 0; i < uniqueID.length; i++)
1073 {
1074 int j;
1075 if (i < 4)
1076 {
1077 j = 3 - i;
1078 } else if (i < 6) {
1079 j = (5 - i) + 4;
1080 } else if (i < 8) {
1081 j = (7 - i) + 6;
1082 } else {
1083 j = i;
1084 }
1085 int ch = uniqueID[j];
1086 if (ch < 0) {
1087 ch+=256;
1088 }
1089 if (ch<16) {
1090 buf.append("0");
1091 }
1092 buf.append(Integer.toHexString(ch).toUpperCase());
1093 if (i == 3 || i == 5 || i == 7 || i == 9) {
1094 buf.append('-');
1095 }
1096 }
1097 buf.append('}');
1098 return buf.toString();
1099 }
1100 }
1101}
1102