001: /*
002: * tinySQL.java
003: *
004: * A trivial implementation of SQL in an abstract class.
005: * Plug it in to your favorite non-SQL data source, and
006: * QUERY AWAY!
007: *
008: * Copyright 1996, Brian C. Jepson
009: * (bjepson@ids.net)
010: *
011: * $Author: davis $
012: * $Date: 2004/12/18 21:23:50 $
013: * $Revision: 1.1 $
014: *
015: * This library is free software; you can redistribute it and/or
016: * modify it under the terms of the GNU Lesser General Public
017: * License as published by the Free Software Foundation; either
018: * version 2.1 of the License, or (at your option) any later version.
019: *
020: * This library is distributed in the hope that it will be useful,
021: * but WITHOUT ANY WARRANTY; without even the implied warranty of
022: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
023: * Lesser General Public License for more details.
024: *
025: * You should have received a copy of the GNU Lesser General Public
026: * License along with this library; if not, write to the Free Software
027: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
028: *
029: * Revision History:
030: *
031: * Major rewrite in 2003/4 by Davis Swan SQL*Magic Ltd.
032: */
033:
034: package com.sqlmagic.tinysql;
035:
036: import java.util.*;
037: import java.lang.*;
038: import java.io.*;
039: import java.sql.SQLException;
040: import java.sql.Types;
041:
042: /**
043: * @author Thomas Morger <mgs@sherito.org> Changed tinySQL to reflect changes
044: * in tinySQLResultSet - an instance of the connection is passed to a new
045: * resultset at construction time.
046: *
047: * When fetching a resultset, the number of rows to fetch can be set in the
048: * statement. The builder will return when the given number of rows has been
049: * reached and will be restarted by tsResultSet when more rows are needed.
050: */
051: public abstract class tinySQL {
052:
053: boolean debug = false, groupBreak = false, keepRecord = true;
054: boolean exDebug = false, performDebug = false;
055: Hashtable groupFunctions;
056: String newLine = System.getProperty("line.separator");
057: static tinySQLTable insertTable = (tinySQLTable) null;
058: tinySQLWhere wc;
059: // This is the InputStream from which the parser reads.
060: //
061: private InputStream SQLStream;
062:
063: // This is the last SQL Statement processed by sqlexec(String s).
064: // Note that sqlexec() does *not* support this.
065: //
066: private tinySQLStatement sqlstatement = null;
067:
068: /*
069: *
070: * Constructs a new tinySQL object.
071: *
072: */
073: public tinySQL() {
074:
075: }
076:
077: /*
078: * Reads SQL statements from System.in() and returns a
079: * tsResultSet for the last statement executed. This is
080: * really only good for testing.
081: *
082: * @exception tinySQLException
083: */
084: public tsResultSet sqlexec() throws tinySQLException {
085:
086: SQLStream = (InputStream) System.in;
087: System.err.println("Reading SQL Statements from STDIN...");
088: System.err.println("CRASHING AFTER THIS POINT IS SURE...");
089: System.err
090: .println("Have no Statement, no connection and no clue how to continue ...");
091: return sql(null);
092:
093: }
094:
095: /*
096: * Processes the SQL Statement s and returns
097: * a tsResultSet.
098: *
099: * @param s SQL Statement to execute
100: * @exception tinySQLException
101: */
102: public tsResultSet sqlexec(tinySQLStatement s)
103: throws tinySQLException {
104: return sql(s);
105: }
106:
107: public tsResultSet sqlexec(tinySQLPreparedStatement s)
108: throws tinySQLException {
109: return sql(s);
110: }
111:
112: /*
113: *
114: * Read SQL Statements from the SQLStream, and
115: * return a result set for the last SQL Statement
116: * executed.
117: *
118: * @returns the ResultSet or null, if no result set was created
119: * @exception tinySQLException
120: *
121: */
122: protected tsResultSet sql(Object s) throws tinySQLException {
123: /*
124: * Build the ResultSet
125: */
126: tsResultSet rs = null;
127: tinySQLTable jtbl;
128: tinySQLPreparedStatement pstmt = (tinySQLPreparedStatement) null;
129: boolean useTinyParser = true, distinct = false;
130: Vector actions, columns, columnDefs, values, columnContexts, columnAliases;
131: String actionType, orderType, tableName, statementType, byteString;
132: Hashtable h, selectTables;
133: byte[] bStream;
134: ByteArrayInputStream st;
135: int i;
136: String actionString;
137: groupBreak = false;
138: keepRecord = true;
139: statementType = s.getClass().getName();
140: try {
141: /*
142: * Instantiate a new parser object which reads from the SQLStream. This
143: * should probably be changed to a String at some point. Note that
144: * parsing is only done once for a PreparedStatement.
145: */
146: actions = (Vector) null;
147: if (statementType.endsWith("tinySQLPreparedStatement")) {
148: pstmt = (tinySQLPreparedStatement) s;
149: pstmt.updateActions(actions);
150: actions = pstmt.getActions();
151: byteString = pstmt.getSQLString();
152: bStream = (byte[]) null;
153: if (pstmt.getSQLString() != (String) null)
154: bStream = pstmt.getSQLString().getBytes();
155: } else if (statementType.endsWith("tinySQLStatement")) {
156: bStream = ((tinySQLStatement) s).getSQLString()
157: .getBytes();
158: } else {
159: throw new tinySQLException("Unknown statement type"
160: + statementType);
161: }
162: if (actions == (Vector) null) {
163: st = new ByteArrayInputStream(bStream);
164: SQLStream = (InputStream) st;
165: tinySQLParser tinyp = new tinySQLParser(SQLStream, this );
166: tinySQLGlobals.writeLongNames();
167: actions = tinyp.getActions();
168: if (statementType.endsWith("tinySQLPreparedStatement"))
169: pstmt.updateActions(actions);
170: }
171: /*
172: * The actions Vector consists of a list of Hashtables. Each of these
173: * action Hashtables contains elements required for a particular SQL
174: * statement. The following elements are used for various actions;
175: *
176: * Type Name Description
177: *
178: * String tableName The name of the affected table for
179: * CREATE,INSERT,UPDATE,DELETE actions.
180: *
181: * Hashtable selectTables Hashtable of tables in a SELECT action.
182: *
183: * Vector columns A list of column names used by the
184: * the action.
185: *
186: * Vector columnContexts A list of Strings indicating the context
187: * for the elements in the columns Vector.
188: * Values can be SELECT,ORDER,GROUP.
189: *
190: * Vector columnDefs A list of column objects used by the
191: * CREATE TABLE and ALTER TABLE ADD actions.
192: *
193: * Vector values A list of String values used in INSERT
194: * and UPDATE actions.
195: *
196: * String oldColumnName Old column name for the
197: * ALTER TABLE RENAME action.
198: *
199: * String newColumnName New column name for the
200: * ALTER TABLE RENAME action.
201: *
202: * String orderType Type or ORDER BY - ASC or DESC.
203: *
204: * String distinct Distinct rows only - TRUE or NULL
205: *
206: * tinySQLWhere whereClause An object containing the where clause
207: * which can be updated and queried.
208: */
209: for (i = 0; i < actions.size(); i++) {
210: h = (Hashtable) actions.elementAt(i);
211: actionType = (String) h.get("TYPE");
212: if (tinySQLGlobals.DEBUG)
213: System.out.println("Action: " + actionType);
214: /*
215: * Many actions have a table specification. If this one, build
216: * a table object to update the where clause if there is one.
217: */
218: tableName = (String) h.get("TABLE");
219: wc = (tinySQLWhere) h.get("WHERE");
220: if (tableName != (String) null
221: & !actionType.equals("DROP_TABLE")
222: & !actionType.equals("CREATE_TABLE")
223: & !actionType.equals("INSERT")
224: & !actionType.startsWith("ALTER")) {
225: jtbl = getTable(tableName);
226: /*
227: * For prepared statements, store any table objects that
228: * are created so that they can be explicitly closed when
229: * the statement processing is complete.
230: */
231: if (statementType
232: .endsWith("tinySQLPreparedStatement"))
233: pstmt.addTable(jtbl);
234: }
235: actionString = UtilString.actionToString(h);
236: if (debug)
237: System.out.println("ACTION: " + actionString);
238: if (actionType.equals("UPDATE")) {
239: /*
240: * SQL UPDATE
241: */
242: columns = (Vector) h.get("COLUMNS");
243: values = (Vector) h.get("VALUES");
244: UpdateStatement(tableName, columns, values, wc);
245: } else if (actionType.equals("DELETE")) {
246: /*
247: * SQL DELETE
248: */
249: DeleteStatement(tableName, wc);
250: } else if (actionType.equals("SELECT")) {
251: /*
252: * SQL SELECT
253: */
254: selectTables = (Hashtable) h.get("TABLES");
255: columns = (Vector) h.get("COLUMNS");
256: orderType = (String) h.get("ORDER_TYPE");
257: if ((String) h.get("DISTINCT") != (String) null)
258: distinct = true;
259: rs = SelectStatement(selectTables, columns, wc,
260: orderType, distinct, s);
261: } else if (actionType.equals("INSERT")) {
262: /*
263: * SQL INSERT
264: */
265: columns = (Vector) h.get("COLUMNS");
266: values = (Vector) h.get("VALUES");
267: InsertStatement(statementType, tableName, columns,
268: values);
269: } else if (actionType.equals("CREATE_TABLE")) {
270: /*
271: * SQL CREATE TABLE
272: *
273: * CREATE TABLE User(user_oid NUMBER(8) NOT NULL,
274: * userType VARCHAR(80) DEFAULT '-' NOT NULL,
275: * PRIMARY KEY (user_oid))
276: *
277: * -> DEFAULT / NOT NULL / PRIMARY KEY is not supported
278: *
279: */
280: columnDefs = (Vector) h.get("COLUMN_DEF");
281: CreateTable(tableName, columnDefs);
282: } else if (actionType.equals("ALTER_ADD")) {
283: /*
284: * SQL ALTER TABLE ADD
285: */
286: columnDefs = (Vector) h.get("COLUMN_DEF");
287: AlterTableAddCol(tableName, columnDefs);
288: } else if (actionType.equals("ALTER_DROP")) {
289: /*
290: * SQL ALTER TABLE DROP
291: */
292: columns = (Vector) h.get("COLUMNS");
293: AlterTableDropCol(tableName, columns);
294: } else if (actionType.equals("ALTER_RENAME")) {
295: /*
296: * SQL ALTER TABLE RENAME
297: */
298: String oldColumnName = (String) h.get("OLD_COLUMN");
299: String newColumnName = (String) h.get("NEW_COLUMN");
300: AlterTableRenameCol(tableName, oldColumnName,
301: newColumnName);
302: } else if (actionType.equals("DROP_TABLE")) {
303: /*
304: * SQL DROP TABLE
305: */
306: DropTable(tableName);
307: } else {
308: System.out.println("Unrecognized action "
309: + actionType);
310: }
311: }
312: } catch (Exception e) {
313: if (tinySQLGlobals.EX_DEBUG)
314: e.printStackTrace(System.out);
315: throw new tinySQLException(e.getMessage());
316: }
317: return rs;
318: }
319:
320: /*
321: * Execute an SQL Select Statement
322: */
323: protected tsResultSet SelectStatement(Hashtable t, Vector c,
324: tinySQLWhere w, String ot, boolean distinct, Object stmt)
325: throws tinySQLException {
326: Hashtable tables;
327: Vector tableList;
328: tsResultSet jrs;
329: tsColumn columnObject;
330: int i;
331: /*
332: * Instantiate a new, empty tsResultSet
333: */
334: jrs = new tsResultSet(w, this );
335: groupFunctions = new Hashtable();
336: try {
337: jrs.setFetchSize(((tinySQLStatement) stmt).getFetchSize());
338: jrs.setType(((tinySQLStatement) stmt).getResultSetType());
339: } catch (SQLException sqle) {
340: Utils
341: .log("Caught SQLException while setting Fetchsize and ResultSetType");
342: Utils.log(" This event is (should be) impossible!");
343: }
344: tables = t;
345: tableList = (Vector) tables.get("TABLE_SELECT_ORDER");
346: /*
347: * Add the column objects to the ResultSet.
348: */
349: for (i = 0; i < c.size(); i++) {
350: columnObject = (tsColumn) c.elementAt(i);
351: /*
352: * The column object is now added to the ResultSet
353: */
354: jrs.addColumn(columnObject);
355: if (debug)
356: System.out.println("Adding "
357: + columnObject.contextToString() + " column "
358: + newLine + columnObject.toString() + newLine);
359: }
360: jrs.setState(1, tables, ot, distinct);
361: contSelectStatement(jrs);
362: return jrs;
363: }
364:
365: /*
366: * Support function for restartable queries. Continue to
367: * read the query result. The current state is taken from
368: * tsResultSet. Proceed until maxFetchSize has reached.
369: */
370: protected void contSelectStatement(tsResultSet jrs)
371: throws tinySQLException {
372: /*
373: * The table scan here is an iterative tree expansion, similar to
374: * the algorithm shown in the outline example in Chapter 5.
375: */
376: String columnName, columnString, whereStatus, tableAndAlias;
377: boolean addOK;
378: int i, rowCount;
379: int level = jrs.getLevel();
380: tinySQLTable jtbl;
381: tsColumn updateColumn;
382: Hashtable tables = jrs.getTableState();
383: tinySQLWhere wc = jrs.getWhereClause();
384: /*
385: * Create a hashtable to enumerate the tables to be scanned and initialize
386: * with the first table name.
387: */
388: Hashtable tbl_list = new Hashtable();
389: Vector groupFunction;
390: Vector t = (Vector) tables.get("TABLE_SELECT_ORDER");
391: String current = (String) t.elementAt(0);
392: String firstColumn = "*";
393: tbl_list.put(current, new Integer(1));
394: /*
395: * Create a row object; this is added to the ResultSet.
396: */
397: tsRow record = new tsRow();
398: Vector resultSet = new Vector();
399: /*
400: * Keep retrieving rows until we run out of rows to process.
401: */
402: while (level > 0) {
403: boolean levelFound = false;
404: /*
405: * Find an item within the tbl_list which has the same level as the
406: * one we're on.
407: */
408: Enumeration keys = tbl_list.keys();
409: while (keys.hasMoreElements()) {
410: /*
411: * Get the next element in the "to be processed"
412: * Hashtable, and find out its level, storing this
413: * value in currLevel.
414: */
415: String hashkey = (String) keys.nextElement();
416: int currLevel = ((Integer) tbl_list.get(hashkey))
417: .intValue();
418: /*
419: * As soon as an element is found whose level is equal to the
420: * one currently being processed, grab it's primary key (the hashkey),
421: * flag levelfound, and break!
422: */
423: if (currLevel == level) {
424: current = hashkey;
425: levelFound = true;
426: break;
427: }
428: }
429: boolean eof = false; // did we hit eof?
430: boolean haveRecord = false; // did we get a record or not?
431: /*
432: * If a table was found at the current level, then we should
433: * try to get another row from it.
434: */
435:
436: if (levelFound) {
437: /*
438: * Get the current table
439: */
440: jtbl = (tinySQLTable) tables.get(current);
441: tableAndAlias = jtbl.table + "->" + jtbl.tableAlias;
442: if (tinySQLGlobals.WHERE_DEBUG)
443: System.out.println("Processing level " + level
444: + " table " + tableAndAlias + "\n");
445: /*
446: * The following code is the start of setting up simple indexes
447: * for tinySQL. The concept involves creating a new tinySQLCondition
448: * class, instances of which will be created by tinySQLWhere. At least
449: * initially only conditions that are equal to a constant will be
450: * considered. One of these conditions will be stored inside the
451: * dbfFileTable object. Calls to NextRecord would then have to add
452: * logic to check to see if an index exists. The presence of a
453: * complete index would be indicated by a counter that was equal
454: * to the number of rows in the table. In that case a single get
455: * would deliver the record numbers required for the where condition.
456: * The NextRecord function would return these record numbers in
457: * sequence. If the index did not exist then new values in the
458: * index Hashtable would be created. None of this code has been
459: * developed or implemented, let alone tested.
460: *
461: *
462: * if ( wc != (tinySQLWhere)null )
463: * jtbl.setIndexCondition(wc.getIndexCondition(tableAndAlias));
464: */
465: if (performDebug)
466: System.out.println("Selecting records from "
467: + jtbl.table);
468: /*
469: * Skip to the next undeleted record; at some point,
470: * this will run out of records, and found will be false.
471: */
472: boolean found = false;
473: while (jtbl.NextRecord()) {
474: /*
475: * Clear the column values for this table before starting
476: * to process the next row.
477: */
478: for (i = 0; i < jrs.numcols(); i++) {
479: updateColumn = jrs.columnAtIndex(i, true);
480: updateColumn.clear(tableAndAlias);
481: }
482: if (wc != (tinySQLWhere) null)
483: wc.clearValues(tableAndAlias);
484: if (!jtbl.isDeleted()) {
485: /*
486: * Evaluate the where clause for each column in the table. If
487: * it is false, skip to the next row. Otherwise, add the
488: * column value to the output record.
489: */
490: Enumeration cols = jtbl.column_info.keys();
491: found = true;
492: whereStatus = "TRUE";
493: while (cols.hasMoreElements()) {
494: columnName = tableAndAlias + "."
495: + (String) cols.nextElement();
496: columnString = jtbl.GetCol(columnName);
497: if (wc != (tinySQLWhere) null) {
498: whereStatus = wc.evaluate(columnName,
499: columnString);
500: if (whereStatus.equals("FALSE")) {
501: /*
502: * This column value caused the where clause to
503: * be FALSE. Go to the next row in the table.
504: */
505: found = false;
506: break;
507: }
508: }
509: /*
510: * Update the ResultSet tsColumn values
511: */
512: jrs.updateColumns(columnName, columnString);
513: }
514: /*
515: * If no where condition has evaluated to false then this
516: * record is a candidate for output. Break to the next table
517: * in this case for further WHERE processing.
518: */
519: if (found)
520: break;
521: }
522: }
523: if (found) {
524: if (tinySQLGlobals.DEBUG)
525: System.out.println("Build candidate record.");
526: for (i = 0; i < jrs.numcols(); i++) {
527: updateColumn = jrs.columnAtIndex(i, true);
528: /*
529: * Evaluate all functions before adding the
530: * column to the output record.
531: */
532: updateColumn.updateFunctions();
533: columnString = updateColumn.getString();
534: if (updateColumn.isNotNull()) {
535: record.put(updateColumn.name, columnString);
536: } else {
537: record.remove(updateColumn.name);
538: }
539: }
540: if (performDebug)
541: System.out.println("Record is "
542: + record.toString());
543: /*
544: * Since we were just able to get a row, then
545: * we are not at the end of file
546: */
547: eof = false;
548: /*
549: * If the table we are processing is not the last in
550: * the list, then we should increment level and loop to the top.
551: */
552: if (level < t.size()) {
553: /*
554: * Increment level
555: */
556: level++;
557: /*
558: * Add the next table in the list of tables to
559: * the tbl_list, the Hashtable of "to be processed" tables.
560: */
561: String next_tbl = (String) t
562: .elementAt(level - 1);
563: tbl_list.put(next_tbl, new Integer(level));
564: } else {
565: /*
566: * If the table that was just processed is the last in
567: * the list, then we have drilled down to the bottom;
568: * all columns have values, and we can add it to the
569: * result set. The next time through, the program
570: * will try to read another row at this level; if it's
571: * found, only columns for the table being read will
572: * be overwritten in the tsRow.
573: *
574: * Columns for the other table will be left alone, and
575: * another row will be added to the result set. Here
576: * is the essence of the Cartesian Product which is
577: * being built here.
578: */
579: haveRecord = true;
580: }
581: } else {
582: /*
583: * We didn't find any more records at this level.
584: * Reset the record pointer to the top of the table,
585: * and decrement level. We have hit end of file here.
586: */
587: if (wc != (tinySQLWhere) null)
588: wc.clearValues(tableAndAlias);
589: level--;
590: eof = true;
591: jtbl.GoTop();
592: }
593: } else {
594: /*
595: * No tables were found at this level; back up a level
596: * and see if there's any up there.
597: */
598: level--;
599: }
600: /*
601: * If we got a record, then add it to the result set.
602: */
603: if (haveRecord) {
604: /*
605: * If group functions are involved, add records only after a break,
606: * which is defined as a change in all of the group columns.
607: * Otherwise, update the current record.
608: */
609: if (jrs.isGrouped()) {
610: if (groupBreak) {
611: addOK = jrs.addRow((tsRow) record.clone());
612: if (addOK == false) {
613: jrs.setLevel(level);
614: return;
615: }
616: groupBreak = false;
617: } else {
618: jrs.updateRow((tsRow) record.clone());
619: }
620: } else {
621: /*
622: * No group functions are involved. Just add the record.
623: */
624: addOK = jrs.addRow((tsRow) record.clone());
625: if (addOK == false) {
626: jrs.setLevel(level);
627: return;
628: }
629: }
630: firstColumn = "*";
631: }
632: }
633: /*
634: * Close all the tables
635: */
636: for (i = 0; i < t.size(); i++) {
637: jtbl = (tinySQLTable) tables.get((String) t.elementAt(i));
638: jtbl.close();
639: }
640: /*
641: * return a result set
642: */
643: jrs.setLevel(0);
644: }
645:
646: /*
647: * Delete rows which match a where clause.
648: */
649: private void DeleteStatement(String tableName, tinySQLWhere wc)
650: throws tinySQLException {
651: tinySQLTable jtbl;
652: Hashtable tables;
653: String columnName, columnString, whereStatus;
654: Enumeration cols;
655: /*
656: * Create a table object and put it in the Hashtable.
657: */
658: jtbl = getTable(tableName);
659: tables = new Hashtable();
660: tables.put(tableName, jtbl);
661: /*
662: * Process each row in the table ignoring deleted rows.
663: */
664: jtbl.GoTop();
665: while (jtbl.NextRecord()) {
666: if (!jtbl.isDeleted()) {
667: cols = jtbl.column_info.keys();
668: whereStatus = "TRUE";
669: while (cols.hasMoreElements()) {
670: columnName = jtbl.table + "->" + jtbl.tableAlias
671: + "." + (String) cols.nextElement();
672: columnString = jtbl.GetCol(columnName);
673: /*
674: * Check the status of the where clause for each column value.
675: */
676: if (wc != (tinySQLWhere) null)
677: whereStatus = wc.evaluate(columnName,
678: columnString);
679: if (whereStatus.equals("FALSE"))
680: break;
681: }
682: if (whereStatus.equals("TRUE"))
683: jtbl.DeleteRow();
684: if (wc != (tinySQLWhere) null)
685: wc.clearValues(jtbl.table + "->" + jtbl.tableAlias);
686: }
687: }
688: jtbl.close();
689: }
690:
691: /*
692: * Update rows which match a WHERE clause
693: */
694: private void UpdateStatement(String tableName, Vector c, Vector v,
695: tinySQLWhere wc) throws tinySQLException {
696: /*
697: * Create a table object and put it in the Hashtable.
698: */
699: tinySQLTable jtbl = getTable(tableName);
700: Hashtable tables = new Hashtable();
701: tables.put(tableName, jtbl);
702: String columnName, columnString, whereStatus;
703: /*
704: * Process each row in the table ignoring deleted rows.
705: */
706: jtbl.GoTop();
707: while (jtbl.NextRecord()) {
708: if (!jtbl.isDeleted()) {
709: Enumeration cols = jtbl.column_info.keys();
710: whereStatus = "TRUE";
711: while (cols.hasMoreElements()) {
712: /*
713: * Use the table name for the table alias for updates.
714: */
715: columnName = jtbl.table + "->" + jtbl.table + "."
716: + (String) cols.nextElement();
717: columnString = jtbl.GetCol(columnName);
718: /*
719: * Check the status of the where clause for each column value.
720: */
721: if (wc != (tinySQLWhere) null)
722: whereStatus = wc.evaluate(columnName,
723: columnString);
724: if (whereStatus.equals("FALSE"))
725: break;
726: }
727: if (whereStatus.equals("TRUE"))
728: jtbl.UpdateCurrentRow(c, v);
729: if (wc != (tinySQLWhere) null)
730: wc.clearValues(jtbl.table + "->" + jtbl.tableAlias);
731: }
732: }
733: jtbl.close();
734: }
735:
736: /*
737: * Create the tinySQLTable object, then insert a row, and update
738: * it with the c and v Vectors
739: */
740: private void InsertStatement(String statementType,
741: String tableName, Vector c, Vector v)
742: throws tinySQLException {
743: String columnName, valueString;
744: int i, columnType, columnSize;
745: tinySQLTable jtbl = (tinySQLTable) null;
746: double value;
747: insertTable = getTable(tableName);
748: /*
749: * Check that the values supplied are the correct type and length.
750: */
751: for (i = 0; i < c.size(); i++) {
752: columnName = (String) c.elementAt(i);
753: valueString = (String) v.elementAt(i);
754: if (valueString == (String) null)
755: continue;
756: valueString = UtilString.removeQuotes(valueString);
757: valueString = UtilString.replaceAll(valueString, "''", "'");
758: columnType = insertTable.ColType(columnName);
759: if (Utils.isNumberColumn(columnType)) {
760: try {
761: value = Double.parseDouble(valueString);
762: } catch (Exception e) {
763: throw new tinySQLException("Insert failed: column "
764: + columnName + " is numeric - found "
765: + valueString);
766: }
767: } else if (Utils.isDateColumn(columnType)) {
768: try {
769: /*
770: * Modify the input to be the standard YYYYMMDD format
771: */
772: if (valueString.trim().length() > 0)
773: v.setElementAt(UtilString
774: .dateValue(valueString), i);
775: } catch (Exception e) {
776: throw new tinySQLException("Insert failed: "
777: + e.getMessage());
778: }
779: }
780: columnSize = insertTable.ColSize(columnName);
781: if (valueString.length() > columnSize) {
782: throw new tinySQLException(
783: "Insert failed: string too long for "
784: + " column "
785: + columnName
786: + " "
787: + Integer
788: .toString(valueString.length())
789: + " > " + Integer.toString(columnSize)
790: + "<" + valueString + ">");
791: }
792: }
793: insertTable.InsertRow(c, v);
794: /*
795: * Close the table file that has just been updated unless this is a
796: * PreparedStatement. In that case an explicit close must be done
797: * on the statement object to close any open files.
798: */
799: if (!statementType.endsWith("tinySQLPreparedStatement"))
800: insertTable.close();
801: }
802:
803: /*
804: * Creates a table given a tableName, and a Vector of column
805: * definitions.
806: *
807: * The column definitions are an array of tsColumn
808: */
809: abstract void CreateTable(String tableName, Vector v)
810: throws IOException, tinySQLException;
811:
812: /*
813: * Creates new Columns given a tableName, and a Vector of
814: * column definition (tsColumn) arrays.<br>
815: *
816: * ALTER TABLE table [ * ] ADD [ COLUMN ] column type
817: */
818: abstract void AlterTableAddCol(String tableName, Vector v)
819: throws IOException, tinySQLException;
820:
821: /*
822: * Deletes Columns given a tableName, and a Vector of
823: * column definition (tsColumn) arrays.<br>
824: *
825: * ALTER TABLE table DROP [ COLUMN ] column { RESTRICT | CASCADE }
826: */
827: abstract void AlterTableDropCol(String tableName, Vector v)
828: throws IOException, tinySQLException;
829:
830: /*
831: * Rename columns
832: *
833: * ALTER TABLE table RENAME war TO peace
834: */
835: abstract void AlterTableRenameCol(String tableName,
836: String oldColumnName, String newColumnName)
837: throws tinySQLException;
838:
839: /*
840: * Drops a table by name
841: */
842: abstract void DropTable(String tableName) throws tinySQLException;
843:
844: /*
845: * Create a tinySQLTable object by table name
846: */
847: abstract tinySQLTable getTable(String tableName)
848: throws tinySQLException;
849:
850: }
|