001package bradleyross.library.database;
002import java.sql.PreparedStatement;
003import java.sql.SQLException;
004import java.sql.Driver;
005import java.sql.DriverManager;
006import java.sql.ResultSet;
007import java.sql.Statement;
008import java.sql.ResultSetMetaData;
009import java.io.PrintWriter;
010import java.io.PrintStream;
011import java.sql.Types;
012import java.util.regex.Pattern;
013import java.util.regex.Matcher;
014import bradleyross.library.NotSupportedException;
015import bradleyross.library.database.DatabaseExtras;
016import bradleyross.library.helpers.GenericPrinter;
017/**
018 * Supports usage of databases via JDBC (Java DataBase Connection)
019 * protocol.
020 *<p>This is an abstract class and can't be used directly in the
021 *   creation of objects.  This means that subclasses must be
022 *   created.</p>
023 * <p>When creating subclasses, many fields will have to be set, 
024 *   including
025 *   <code><a href="#handlerClass">handlerClass</a></code>,
026 *   <code><a href="#accountName">accountName</a></code>,
027 *   <code><a href="#connectionString">connectionString</a></code>, and 
028 *   <code><a href="#password">password</a></code>.</p>
029 *<p>By including the objects for the JDBC connection and statement, it
030 *   is possible to encapsulate a large amount of the code for
031 *   the database connectivity.</p>
032 *<p>Please note that the methods in this class do not include one
033 *   for obtaining the password used in connecting to the database.
034 *   If required, this method can be added in the subclass.  
035 *   Eliminating this method adds to the security of the system.
036 *   By encapsulating the password in a field in this class for
037 *   use by the connect method, there is no need for other classes
038 *   to have access to the information.</p>
039 *  <p>SQL Server notes</p>
040 *  <ul>
041 *  <li><p>Wants the string <code>dbo.</code> in front of table names.</p></li>
042 *  <li><p>Specify the default database as part of the user configuration rather
043 *         than including it in the connection string.</p></li>
044 *  </ul>
045 *  <p>Oracle notes</p>
046 *  <ul>
047 *  <li><p>Will not accept semicolons at the end of SQL statements.</p></li>
048 *  </ul>
049 *  <p>MySQL notes</p>
050 * @author Bradley Ross
051 */
052public abstract class DatabaseProperties 
053{
054        protected Driver driver = null;
055        /**
056         * Constructor using a default system and account name.
057         */
058        public DatabaseProperties()
059        { 
060                invalidParameters = false;
061                clearTableNamePrefixes(); 
062                setDatabaseInstance();
063        }
064        /**
065         * Constructor specifying identifier for system and using
066         * a default account name.
067         * @param value Identifier code for system
068         */
069        public DatabaseProperties(String value)
070        { 
071                invalidParameters = false;
072                clearTableNamePrefixes(); 
073                setDatabaseInstance(value);
074        }
075        /**
076         * Constructor specifying system and account name.
077         * @param systemName  Identifier code for system
078         * @param accountName Account name to be used
079         */
080        public DatabaseProperties (String systemName, String accountName)
081        {
082                invalidParameters = false;
083                clearTableNamePrefixes();
084                setDatabaseInstance(systemName, accountName);
085        }
086        /**
087         * Internal program for setting up connection using default
088         * database and account.
089         * @see #setDatabaseInstance(String, String)
090         */
091        public abstract void setDatabaseInstance();
092        /**
093         * Internal program for setting up connection using specified
094         * database with a default account for the database.
095         * @param system Identifying code for database
096         */
097        public abstract void setDatabaseInstance(String system);
098        /**
099         * Internal program for setting up connection with a specified
100         * database and account name.
101         * @param system Identifier code for database
102         * @param user Account name to be used
103         */
104        public abstract void setDatabaseInstance(String system, String user);
105        /**
106         * Amount of diagnostic messages.
107         *<p>A value of zero indicates that no diagnostic messages
108         *   should be displayed.  The higher the value, the more
109         *   diagnostic messages will be generated.</p>
110         */
111        protected int debugLevel = 0;
112        /**
113         * Used to represent associated DatabaseExtras object.
114         */
115        protected DatabaseExtras extras = null;
116        /**
117         * Set value of extras.
118         * @param value Object to be used for DatabaseExtras object.
119         * @see #extras
120         */
121        public void setDatabaseExtras(DatabaseExtras value)
122        {
123                extras = value;
124        }
125        /**
126         * Get value of extras.
127         * @return DatabaseExtras object being used.
128         * @throws NotSupportedException
129         * @see #extras
130         */
131        public DatabaseExtras getDatabaseExtras() throws NotSupportedException
132        {
133                if (extras == null)
134                {
135                        throw new NotSupportedException("Database Extras is not supported");
136                }
137                else
138                {
139                        return extras;
140                }
141        }
142        /**
143         * Set of prefixes for table names
144         */
145        protected String tableNamePrefixes[] = new String[10];
146        /** Get the value of the default table name prefix (item 0)
147         * @return Default table name prefix
148         */
149        public String getTableNamePrefixes()
150        { return tableNamePrefixes[0]; }
151        /** 
152         * Get the value of a table name prefix
153         * @param i Location of table name prefix in list
154         * @return Value of table name prefix
155         */
156        public String getTableNamePrefixes(int i)
157        { return tableNamePrefixes[i]; }
158        /** 
159         * True indicates that autoCommit is set for this database connection when
160         * the connection is opened.
161         */
162        protected boolean autoCommit = true;
163        /**
164         * Optional field containing a human readable description of the
165         * database.  This string will have to be escaped before it is safe
166         * for use in HTML code and SQL queries.
167         */
168        protected String systemDescription = null;
169        /**
170         * Getter for systemDescription
171         * @return Description of system
172         * @see #systemDescription
173         */
174        public String getSystemDescription()
175        {
176                return systemDescription;
177        }
178        /**
179         * This is the name of the system identifier to identify
180         * the database in database systems that can support multiple
181         * databases.
182         */
183        protected String systemName = null;
184        /**
185         * Name of the handler class for the database.
186         * <p><table border>
187         * <tr><td>Product</td><td>Handler Class</td><td>Default<br />Port</td></tr>
188         * <tr><td>MySQL</td><td><code>com.mysql.jdbc.Driver</code></td><td>3306</td></tr>
189         * <tr><td>Oracle</td><td><code>oracle.jdbc.driver.OracleDriver</code></td><td>1521</td></tr>
190         * <tr><td>SQL Server</td><td><code>com.microsoft.sqlserver.jdbc.SQLServerDriver</code></td><td>1433</td></tr>
191         * </table></p>
192         * <p>It may be necessary to use the name of the handlerClass in
193         *    generating the SQL statements, since the syntax for different
194         *    databases such as MySQL, Oracle, DB2, Postgres, etc. differ for
195         *    many of the built in functions.</p>
196         * <p>Although there is no <i>get</i> method for this field, one can
197         *    be added when creating subclasses.</p>
198         */
199        protected String handlerClass = null;
200        /** 
201         * Port number of the database connection.
202         * <p>The default port number is shown in the discussion of the
203         *    <a href="#handlerClass">handler class names</a>.  If a port
204         *    number other than the default is used, it is included as
205         *    part of the <a href="#domainName">domain name</a> and
206         *    <a href="#connectionString">connection string</a>.</p>
207         */
208        protected int portNumber = 0;
209        /**
210         * The connection string to be used to connect to the database using
211         * JDBC (Java DataBase Connectivity)
212         * <p>The connection string is composed as a series of tokens
213         *    separated by colons.  The first token is always
214         *    <code>jdbc</code> while the second is the name
215         *    of the database manager product.  The remaining
216         *    tokens are dependent on the DBMS package used.</p>
217         * <p>The following are examples of connection strings.</p>
218         * <table border>
219         * <tr><td>MySQL</td>
220         *     <td><code>jdbc:mysql://server.foo.com/alpha</code></td>
221         *     <td>alpha is default database</td>
222         *     </tr>
223         * <tr><td>Oracle</td>
224         *     <td><code>jdbc:oracle:thin:@//server.foo.com/weather<br>
225         *                          jdbc:oracle:oci:</code></td>
226         *     <td>Connects to weather service<br>
227         *          The options thin and oci refer to the two types of JDBC connections.  The
228         *          OCI method requires the installation of additional pieces of software
229         *          and uses SQL*Net to connect to the database.</td>
230         *     </tr>
231         * <tr><td>SQL Server</td>
232         *     <td><code>jdbc:sqlserver://server.foo.com</code></td>
233         *     <td>SQL Server allows you to specify a default database as part of the
234         *         user configuration.</td>
235         *     </tr>
236         * </table>
237         */
238        protected String connectionString = null;
239        /**
240         * Domain name for the system containing the database.
241         * <p>The domain name is included as part of the
242         *    <a href="#connectionString">connection string</a>.</p>
243         */
244        protected String domainName = null;
245        /**
246         * The account name to be used on the database server.
247         */
248        protected String accountName = null;
249        /** 
250         * The password to be used for accessing the database server.
251         * <p>It should be noted that there is no <i>get</i> method for
252         *    the field.  This improves security for the package.  If necessary,
253         *    a <i>get</i> method can be added in a subclass.</p>
254         */
255        protected String password = null;
256        /**
257         * When creating MySQL tables, this field can be
258         * used by the getEngineClause to add the ENGINE
259         * statement to CREATE TABLE statements.
260         * @see #getEngineClause()
261         */
262        protected String defaultEngine = null;
263        /**
264         * Default catalog for methods allowing the use
265         * of a default value.
266         * @see #getDefaultCatalog()
267         * @see #setDefaultCatalog(String)
268         */
269        protected String defaultCatalog = null;
270        /**
271         * Get the value of defaultCatalog.
272         * @return Value of defaultCatalog
273         * @see #defaultCatalog
274         */
275        public String getDefaultCatalog()
276        {
277                return defaultCatalog;
278        }
279        /**
280         * Sets value of defaultCatalog.
281         * @param value Value to be used for defaultCatalog
282         * @see #defaultCatalog
283         */
284        public void setDefaultCatalog(String value)
285        {
286                defaultCatalog = value;
287        }
288        /**
289         * Default schema for methods allowing the use
290         * of a default value.
291         * @see #getDefaultSchema()
292         * @see #setDefaultSchema(String)
293         */
294        protected String defaultSchema = null;
295        /**
296         * Gets value of defaultSchema
297         * @return Value of defaultSchema
298         * @see #defaultSchema
299         */
300        public String getDefaultSchema()
301        {
302                return defaultSchema;
303        }
304        /**
305         * Sets value of defaultSchema.
306         * @param value Value to be used for defaultSchema
307         * @see #defaultSchema
308         */
309        public void setDefaultSchema(String value)
310        {
311                defaultSchema = value;
312        }
313        /**
314         * The object representing the connection to the database.
315         * @see #getConnection()
316         */
317        protected java.sql.Connection connection = null;
318        /**
319         * Flag indicating whether database has been connected.
320         */
321        protected boolean isConnected = false;
322        /**
323         * Set the amount of diagnostic messages.
324         * @param level Amount of diagnostic messages.
325         */
326        public void setDebugLevel(int level)
327        { debugLevel = level; }
328        /**
329         * Gets the amount of diagnostic messages to be printed.
330         *
331         * @return Level of diagnostic messages to be printed.
332         */
333        public int getDebugLevel()
334        { return debugLevel; }
335        /**
336         * If the parameters supplied for instantiating the object are
337         * invalid, this field should be set to true.
338         * 
339         * @see #setInvalidParameters(boolean)
340         * @see #hasInvalidParameters()
341         */
342        protected boolean invalidParameters = false;
343        /**
344         * Returns the instance of the JDBC driver used to connect
345         * to the database.
346         * @return JDBC driver
347         */
348        public Driver getDriver() {
349                return driver;
350        }
351        /**
352         * Return the system identifier used for the database connection.
353         * @return System identifier
354         */
355        public String getSystem()
356        { return systemName; }
357        /**
358         * Get name of database management system
359         * <p>The database name is extracted from the 
360         *    <a href="#connectionString">connection string</a>
361         *    as the second token of the string.</p>
362         */
363        public String getDbms()
364        {
365                String working = connectionString.substring(connectionString.indexOf(":")+1);
366                working = working.substring(0, working.indexOf(":"));
367                return working.toLowerCase();
368        }
369        /**
370         * Return the account name used for the database connection.
371         * @return Account name for database.
372         */
373        public String getAccountName()
374        { return accountName; }
375        /**
376         * Create optional ENGINE clause
377         * @see #defaultEngine
378         * @return ENGINE clause for CREATE TABLE SQL statement
379         */
380        public String getEngineClause()
381        {
382                if (defaultEngine == null)
383                {
384                        return " ";
385                }
386                else if (getDbms().equalsIgnoreCase("mysql"))
387                {
388                        return " ENGINE " + defaultEngine + " ";
389                }
390                else return " ";
391        }
392        /**
393         * Open the database connection.
394         */
395        public void connect() throws java.sql.SQLException
396        {
397                if (invalidParameters)
398                {
399                        throw new SQLException("Parameters for system and account name are not valid : " +
400                                        showAttributes());
401                }
402                try
403                {
404                        driver = (Driver) Class.forName(handlerClass).newInstance();
405                }
406                catch (InstantiationException e)
407                {
408                        System.out.println("*** Error in establishing instance of handler");
409                        System.out.println("*** Instantiation Exception");
410                        System.out.println(e.getMessage());
411                        e.printStackTrace(System.out);
412                }
413                catch (IllegalAccessException e )
414                {
415                        System.out.println("*** Error in establishing instance of handler");
416                        System.out.println("*** Illegal Access Exception");
417                        System.out.println(e.getMessage());
418                        e.printStackTrace(System.out);
419                }
420                catch (ClassNotFoundException e)
421                {
422                        System.out.println("*** Error in establishing instance of handler");
423                        System.out.println("*** Class Not Found Exception");
424                        System.out.println(e.getMessage());
425                        e.printStackTrace(System.out);
426                }
427                try
428                {
429                        connection = DriverManager.getConnection(connectionString, accountName,
430                                        password); 
431                        connection.setAutoCommit(autoCommit);
432                }
433                catch (SQLException e)
434                {
435                        System.out.println("SQL Exception");
436                        System.out.println(e.getMessage());
437                        e.printStackTrace(System.out);
438                        throw e;
439                }
440                isConnected = true;
441                if (debugLevel > 1)
442                {
443                        System.out.println("Database connected");
444                }
445        }
446        /**
447         * Close the database connection.
448         */
449        public void close() throws SQLException
450        {
451                try
452                {
453                        connection.close();
454                }
455                catch (SQLException e)
456                {
457                        e.printStackTrace(System.out);
458                        throw e;
459                }
460        }
461        /**
462         * Obtain the connection object for the database connection.
463         * @return connection
464         */
465        public java.sql.Connection getConnection()
466        { return connection;}
467        /**
468         * Execute SQL statement without returning results.
469         * <p>This method is usually used to make updates to the
470         *    database.</p>
471         * @param sqlCode SQL code to be executed.
472         */
473        public int executeUpdate(String sqlCode) throws SQLException
474        {
475                java.sql.Statement stmt = null;
476                int recordCount = -1;
477                try
478                {
479                        if (debugLevel > 0)
480                        { System.out.println("Starting executeUpdate"); }
481                        if (connection == (java.sql.Connection) null)
482                        { connect(); }
483                        stmt = connection.createStatement();
484                        recordCount  = stmt.executeUpdate(sqlCode);
485                        if (debugLevel > 0)
486                        { System.out.println("Statement executed: " +
487                                        Integer.toString(recordCount) + 
488                        " records updated"); }
489                        stmt.close();
490                }
491                catch (SQLException e)
492                {
493                        if (debugLevel > 0 )
494                        {
495                                System.out.println("SQLException in executeUpdate");
496                                System.out.println(e.getMessage());
497                                e.printStackTrace(System.out);
498                        }
499                        throw e;
500                }
501                catch (Exception ex)
502                {
503                        System.out.println("Unknown Exception in executeUpdate");
504                        ex.printStackTrace(System.out);
505                        throw new SQLException(ex.getClass().getName() + " : " + ex.getMessage() + 
506                                        "Unknown Exception in executeUpdate");
507                }
508                return recordCount;
509        }
510        /**
511         * This class reads the first field on the first record from
512         * an SQL Query.
513         * <p>Since only the first field is read, the statement is
514         *    closed after reading the field.  This eliminates the possibility
515         *    of leaving a statement open if it returns multiple rows.</p>
516         * @param sqlCode SQL code to be executed.
517         * @return String object continuing result of SQL query
518         */
519        public String executeSimpleQuery(String sqlCode) 
520        throws SQLException
521        {
522                java.sql.Statement stmt = null;
523                java.sql.ResultSet rs = null;
524                java.lang.String result = null;
525                try
526                {
527                        if (debugLevel > 0)
528                        { System.out.println("Starting executeSimpleQuery"); }
529                        if (connection == (java.sql.Connection) null)
530                        { connect(); }
531                        stmt = connection.createStatement();
532                        rs = stmt.executeQuery(sqlCode);
533                        if (debugLevel > 0)
534                        { System.out.println("Statement executed"); }
535                        /*
536                         * The first method is not supported for forward only
537                         *    result sets.  It looks like forward only result
538                         *    sets are the default for SQL Server.
539                         */
540                        rs.next();
541                        result = rs.getString(1);
542                        stmt.close();
543                }
544                catch (SQLException e)
545                {
546                        if (debugLevel > 0)
547                        {
548                                System.out.println("SQLException in executeSimpleQuery");
549                                System.out.println(e.getMessage());
550                                e.printStackTrace(System.out);
551                        }
552                        throw e;
553                }
554                catch (Exception ex)
555                {
556                        System.out.println("Unknown Exception in executeSimpleQuery");
557                        ex.printStackTrace(System.out);
558                }
559                return result;
560        }
561        /**
562         * This class executes an SQL query and returns a
563         * ResultSet.
564         * @param sqlCode SQL code to be executed.
565         * @return ResultSet
566         */
567        public java.sql.ResultSet executeQuery(String sqlCode) 
568        throws java.sql.SQLException
569        {
570                java.sql.Statement stmt = null;
571                java.sql.ResultSet rs = null;
572                if (!isConnected)
573                {
574                        System.out.println(" Error in executeQuery - database is not connected");
575                        throw new SQLException("Database not connected");
576                }
577                try
578                {
579                        if (debugLevel > 0)
580                        { System.out.println("Starting executeQuery for " + sqlCode); }
581                        if (connection == (java.sql.Connection) null)
582                        { 
583                                if (debugLevel > 1)
584                                {
585                                        System.out.println("Creating connection");
586                                }
587                                connect(); 
588                        }
589                        stmt = connection.createStatement();
590                        if (debugLevel > 1)
591                        {
592                                System.out.println("Statement created");
593                        }
594                        rs = stmt.executeQuery(sqlCode);
595                        if (debugLevel > 0)
596                        { System.out.println("Statement executed"); }
597                }
598                catch (SQLException e)
599                {
600                        System.out.println("SQLException in executeQuery");
601                        System.out.println(e.getMessage());
602                        e.printStackTrace(System.out);
603                        throw e;
604                }
605                catch (Exception ex)
606                {
607                        System.out.println("Unknown Exception in executeQuery");
608                        System.out.println(ex.getClass().getName() + " " + ex.getMessage());
609                        ex.printStackTrace(System.out);
610                }
611                return rs;
612        }
613        /**
614         * Execute a commit statement for the SQL connection.
615         */
616        public void commit()
617        {
618                try
619                { connection.commit(); }
620                catch (SQLException e)
621                {
622                        System.out.println("SQLException during commit");
623                        e.printStackTrace(System.out);
624                }
625                catch (Exception e)
626                {
627                        System.out.println("Unknown exception during commit");
628                        e.printStackTrace(System.out);
629                }
630        }
631        /**
632         * Execute a rollback statement for the SQL connection.
633         */
634        public void rollback()
635        {
636                try
637                { connection.rollback(); }
638                catch (SQLException e)
639                {
640                        System.out.println("SQLException during rollback");
641                        e.printStackTrace(System.out);
642                }
643                catch (Exception e)
644                {
645                        System.out.println("Unknown exception during rollback");
646                        e.printStackTrace(System.out);
647                }
648        }
649        /** 
650         * Set autoCommit flag
651         * @param value Value to be used for setting autoCommit flag
652         */
653        public void setAutoCommit(boolean value)
654        {
655                try
656                { connection.setAutoCommit(value); }
657                catch (SQLException e)
658                {
659                        System.out.println("SQLException while setting autoCommit to " +
660                                        Boolean.toString(value));
661                        e.printStackTrace(System.out);
662                }
663        }
664        /**
665         * Return attributes of the connection properties
666         *
667         * @return String containing connection information
668         */
669        public java.lang.String showAttributes()
670        {
671                return ("systemName:" + systemName +
672                                "\naccountName:" + accountName +
673                                "\ndomainName:" + domainName +
674                                "\nhandlerClass:" + handlerClass);
675        }
676        /**
677         * Setter for accountName.
678         * 
679         * <p>Must be used before database connection is made.</p>
680         * @param value Value for accountName
681         *
682         */
683        
684        public void setAccountName(String value)
685        {
686                if (isConnected) { return; }
687                accountName = value;
688                setDatabaseInstance(systemName, accountName);
689        }
690        /**
691         * Specify the connection string to be used.
692         * 
693         * <p>Do not specify the account name or password as
694         *    part of the connection string.</p>
695         *    
696         * @param value Connection string to be used
697         * @see #connectionString
698         */
699        public void setConnectionString(String value)
700        {
701                if (isConnected) { return; }
702                connectionString = value;
703        }
704        /**
705         * Setter for domainName
706         * <p>Does not include port number</p>
707         * @see #domainName
708         * @param value Value to be used for domain name
709         */
710        public void setDomainName(String value)
711        {
712                if (isConnected) { return; }
713                domainName = value;
714        }
715        /**
716         * Set the class name to be used for the handler class.
717         * @param value Name of handler class
718         * @see #handlerClass
719         */
720        public void setHandlerClass(String value)
721        {
722                if (isConnected) { return; }
723                handlerClass = value;
724        }
725        /** 
726         * Sets the password to be used for the connection
727         * when it is desired to avoid placing the value in
728         * the program.
729         *
730         * @param value Value to be used for the password
731         */
732        public void setPassword(String value)
733        { 
734                if (isConnected) { return; }
735                password = value; 
736        }
737        /**
738         * Setter for port number
739         * @param value port number to be used for connection
740         * @see #portNumber
741         */
742        public void setPortNumber(int value) {
743                portNumber = value;
744        }
745        /**
746         * Setter for systemName.
747         * 
748         * @see #systemName
749         * @param value Value to be used for system name
750         * 
751         */     
752        public void setSystemName(String value)
753        {
754                if (isConnected) { return; }
755                systemName = value;
756        }       
757        /**
758         * Clears the list of table name prefix values.
759         */
760        public void clearTableNamePrefixes()
761        {
762                for (int i=0; i<tableNamePrefixes.length; i++)
763                { tableNamePrefixes[i] = null; }
764        }
765        /**
766         * Prefixes a table name with a value that was set as part of the
767         * database connection.
768         *
769         * @param type Location of the desired prefix in the tableNamePrefixes list
770         * @param value Table name to be prefixed
771         * @return Prefixed table name
772         */
773        public java.lang.String prefixTableName(int type, String value)
774        {
775                if (type < 0 || type > 9)
776                { return " " + value + " "; }
777                if (tableNamePrefixes[type] == null)
778                { return " " + value + " "; }
779                if (tableNamePrefixes[type].length() <= 0)
780                { return value; }
781                return (" " + new String(tableNamePrefixes[type] + value) + " ");
782        }
783        /**
784         * Prefixes a table name with a value that was set as part of the database
785         * connection.
786         *
787         * <p>This version of the method uses the first entry in the tableNamePrefixes 
788         *    array.  (index=0)</p>
789         *
790         * @param value Table name to be prefixed
791         * @return Prefixed table name
792         */
793        public java.lang.String prefixTableName(java.lang.String value)
794        { return prefixTableName(0, value); }
795        /**
796         * Generate a prepared statement for this database.
797         * @param query SQL Query to be used in statement
798         * @return PreparedStatement object
799         * @throws java.sql.SQLException
800         */
801        public java.sql.PreparedStatement prepareStatement(String query)
802        throws java.sql.SQLException
803        { 
804                if (connection == null)
805                {
806                        throw new SQLException("The value of the connection field is null");
807                }
808                return connection.prepareStatement(query);
809        }
810        /**
811         * Returns the parameter as a DDL comment
812         * if the database is of a type that allows comments in
813         * DDL statements.
814         * 
815         * <p>SQL Server does not allow comments in DDL statements.</p>
816         * 
817         * @param text Text to be used in comment
818         * @return Formatted material for comment in CREATE TABLE statement
819         */
820        public String comment(String text)
821        {
822                if (text == null) { return " "; }
823                if (!getDbms().equalsIgnoreCase("sqlserver"))
824                {
825                        Pattern pattern = Pattern.compile("\'");
826                        Matcher matcher = pattern.matcher(text);
827                        String temp = matcher.replaceAll("\'\'");
828                        return (" COMMENT \'" + temp + "\' "); 
829                }
830                else
831                {
832                        return (" ");
833                }
834        }
835        /**
836         * Return the data type used for large BLOBS
837         * (Binary Large Objects).
838         * @return Name of data type.
839         */
840        public String getLongblobTerm()
841        {
842                if (getDbms().equalsIgnoreCase("sqlserver"))
843                {
844                        return " VARBINARY(MAX) ";
845                }
846                else if (getDbms().equalsIgnoreCase("mysql"))
847                {
848                        return " LONGBLOB ";
849                }
850                else 
851                {
852                        return " LONGBLOB ";
853                }
854        }
855        /**
856         * Return the expression used in SQL statements to
857         * obtain the current time.
858         * @return SQL clause.
859         */
860        public String getNowTerm()
861        {
862                if (getDbms().equalsIgnoreCase("sqlserver"))
863                {
864                        return " GETDATE() ";
865                }
866                else if (getDbms().equalsIgnoreCase("mysql"))
867                {
868                        return " NOW() ";
869                }
870                else if (getDbms().equalsIgnoreCase("oracle"))
871                {
872                        return " SYSDATE ";
873                }
874                else 
875                {
876                        return " NOW() ";
877                }
878        }
879        /**
880         * Return the expression used in SQL statements to
881         * represent a data type for holding a date.
882         * 
883         * <p>SQL Server 2000 and 2005 do not have a DATE data type,
884         *    and SMALLDATETIME should be used instead.</p>
885         *    
886         * @return SQL clause.
887         */
888        public String getDateTerm()
889        {
890                if (getDbms().equalsIgnoreCase("sqlserver"))
891                {
892                        return " SMALLDATETIME ";
893                }
894                else if (getDbms().equalsIgnoreCase("mysql"))
895                {
896                        return " DATE ";
897                }
898                else if (getDbms().equalsIgnoreCase("oracle"))
899                {
900                        return " DATE ";
901                }
902                else 
903                {
904                        return " DATE ";
905                }
906        }
907        /**
908         * Return the expression used in SQL statements to
909         * represent a data type for holding a date and time.
910         * 
911         * <p>It appears that Oracle refers to this data type
912         *    as TIMESTAMP.</p>
913         *    
914         * @return SQL clause.
915         */
916        public String getDatetimeTerm()
917        {
918                if (getDbms().equalsIgnoreCase("sqlserver"))
919                {
920                        return " DATETIME ";
921                }
922                else if (getDbms().equalsIgnoreCase("mysql"))
923                {
924                        return " DATETIME ";
925                }
926                else if (getDbms().equalsIgnoreCase("oracle"))
927                {
928                        return " TIMESTAMP ";
929                }
930                else 
931                {
932                        return " DATETIME ";
933                }
934        }
935        /**
936         * Decode the values of constants in java.sql.TYPES to 
937         *    String values.
938         * @param value  Value of constant
939         * @return Corresponding string describing constant
940         * @see Types
941         */
942        public static String decodeSqlType(int value)
943        {
944                if (value == Types.ARRAY)
945                {
946                        return "ARRAY";
947                }
948                else if (value == Types.BIGINT)
949                {
950                        return "BIGINT";
951                }
952                else if (value == Types.BINARY)
953                {
954                        return "BINARY";
955                }
956                else if (value == Types.BIT)
957                {
958                        return "BIT";
959                }
960                else if (value == Types.BLOB)
961                {
962                        return "BLOB";
963                }
964                else if (value == Types.BOOLEAN)
965                {
966                        return "BOOLEAN";
967                }
968                else if (value == Types.CHAR)
969                {
970                        return "CHAR";
971                }
972                else if (value == Types.CLOB)
973                {
974                        return "CLOB";
975                }
976                else if (value == Types.DOUBLE)
977                {
978                        return "DOUBLE";
979                }
980                else if (value == Types.FLOAT)
981                {
982                        return "FLOAT";
983                }
984                else if (value == Types.INTEGER)
985                {
986                        return "INTEGER";
987                }
988                else if (value == Types.JAVA_OBJECT)
989                {
990                        return "JAVA_OBJECT";
991                }
992                else if (value == Types.LONGVARBINARY)
993                {
994                        return "LONGVARBINARY";
995                }
996                else if (value == Types.LONGVARCHAR)
997                {
998                        return "LONGVARCHAR";
999                }
1000                else if (value == Types.REAL)
1001                {
1002                        return "REAL";
1003                }
1004                else if (value == Types.SMALLINT)
1005                {
1006                        return "SMALLINT";
1007                }
1008                else if (value == Types.TINYINT)
1009                {
1010                        return "TINYINT";
1011                }
1012                else if (value == Types.VARCHAR)
1013                {
1014                        return "VARCHAR";
1015                }
1016                else if (value == Types.VARBINARY)
1017                {
1018                        return "VARBINARY";
1019                }
1020                else if (value == Types.DATE)
1021                {
1022                        return "DATE";
1023                }
1024                else if (value == Types.TIME)
1025                {
1026                        return "TIME";
1027                }
1028                else if (value == Types.TIMESTAMP)
1029                {
1030                        return "TIMESTAMP";
1031                }
1032                else
1033                {
1034                        return "Unrecognized value";
1035                }
1036        }
1037        
1038        /**
1039         * Set value of invalidParameters to indicate that constructor is not valid.
1040         * @param value Value to be used for notSupported
1041         */
1042        protected void setInvalidParameters(boolean value)
1043        {
1044                invalidParameters = value;
1045        }
1046        /**
1047         * Determine whether parameters for constructor were valid.
1048         * @return True if combination of system and account is not supported
1049         */
1050        public boolean hasInvalidParameters()
1051        {
1052                return invalidParameters;
1053        }
1054        /**
1055         * Dump contents of database table to PrintWriter object.
1056         * 
1057         * @param table Name of table to be dumped
1058         * @param output Destination PrintWriter
1059         */
1060        public void dumpTable(String table, PrintWriter output)
1061        {
1062                dumpTable(table, table, output, (PrintStream) null);
1063        }
1064        /**
1065         * Dump contents of database to PrintStream object
1066         * @param table  Name of table to be dumped
1067         * @param output Destination PrintStream
1068         */
1069        public void dumpTable(String table, PrintStream output)
1070        {
1071                dumpTable(table, table, (PrintWriter) null, output);
1072        }
1073        /**
1074         * Dumps the contents of a table as a series of SQL statements.
1075         * <p>An OutputStreamWriter can be created from an OutputStream using the
1076         *    constructor <code>OutputStreamWriter(OutputStream out)</code>.</p>
1077         * <p>The objects <code>System.out</code> and <code>System.err</code> are
1078         *    PrintStream objects.  A PrintStream object can be created from an
1079         *    OutputStream object using the constructor
1080         *    <code>PrintStream(OutputStream out)</code>.</p>
1081         * <p>A PrintStream object can be created from a File object using the
1082         *    constructor
1083         *    <code>PrintStream (File out)</code>.</p>
1084         * 
1085         * @param sourceTableName Table to be dumped
1086         * @param targetTableName Table name to be used in SELECT statement
1087         * @param outputWriter PrintWriter object to which information is to be passed
1088         * @param outputStream PrintStream object to which information is to be passed
1089         */
1090        protected void dumpTable(String sourceTableName, String targetTableName, PrintWriter outputWriter,
1091                        PrintStream outputStream)
1092        {
1093                System.out.println("Starting table dumper for " + sourceTableName);
1094                setAutoCommit(false);
1095                String tableName = prefixTableName(sourceTableName);
1096                String sqlQuery = "SELECT * FROM " + tableName ;
1097                if (debugLevel > 1)
1098                {
1099                        System.out.println("Query is " + sqlQuery);
1100                }
1101                Statement stmt = null;
1102                ResultSet rs = null;
1103                try
1104                {               
1105                        boolean test;
1106                        stmt = connection.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
1107                        /*
1108                         * This is apparently a fix for large result sets specific to MySQL.
1109                         * There was also a comment to try 
1110                         * ((com.mysql.jdbc.Statement) stmt).enableStreamingResults();
1111                         */
1112                        if (getDbms().equalsIgnoreCase("mysql"))
1113                        {
1114                                if (debugLevel > 1)
1115                                {
1116                                        System.out.println("Special case for MySQL large result sets");
1117                                }
1118                                stmt.setFetchSize(Integer.MIN_VALUE);
1119                        }
1120                        else if (getDbms().equalsIgnoreCase("sqlserver"))
1121                                stmt.setFetchSize(20);
1122                        if (debugLevel > 1)
1123                        {
1124                                int sample = stmt.getResultSetType();
1125                                if (sample == ResultSet.TYPE_FORWARD_ONLY)
1126                                {
1127                                        System.out.println("Result set is TYPE_FORWARD_ONLY");
1128                                }
1129                                else if (sample == ResultSet.TYPE_SCROLL_INSENSITIVE)
1130                                {
1131                                        System.out.println("Result set is TYPE_SCROLL_INSENSITIVE");
1132                                }
1133                                else if (sample == ResultSet.TYPE_SCROLL_SENSITIVE)
1134                                {
1135                                        System.out.println("Result set is TYPE_SCROLL_SENSITIVE");
1136                                }
1137                                else
1138                                {
1139                                        System.out.println("Result set is of unknown type");
1140                                }
1141                        }
1142                        test = stmt.execute(sqlQuery);
1143                        if (!test)
1144                        {
1145                                System.out.println("Problem in executing " + sqlQuery);
1146                                return;
1147                        }
1148                        rs = stmt.getResultSet();
1149                        if (rs == null)
1150                        {
1151                                System.out.println("Result set was null");
1152                        }
1153                        else
1154                        {
1155                                System.out.println("Result set generated");
1156                        }
1157                }
1158                catch (java.sql.SQLException e)
1159                {
1160                        System.out.println("SQL Exception encountered: " + e.getMessage());
1161                        e.printStackTrace(System.out);
1162                }
1163                catch (Exception e)
1164                {
1165                        System.out.println("Unexpected exception in table dumper");
1166                        System.out.println(e.getClass().getName() + " " + e.getMessage());
1167                        e.printStackTrace(System.out);
1168                }
1169        }
1170        public void dumpTable(String sourceTableName, String targetTableName, 
1171                        GenericPrinter output)
1172        {
1173                System.out.println("dumpTable: Starting table dumper for " + sourceTableName);
1174                setAutoCommit(false);
1175                String tableName = prefixTableName(sourceTableName);
1176                String sqlQuery = "SELECT * FROM " + tableName ;
1177                if (debugLevel > 1)
1178                {
1179                        System.out.println("dumpTable: Query is " + sqlQuery);
1180                }
1181                Statement stmt = null;
1182                ResultSet rs = null;
1183                try
1184                {               
1185                        boolean test;
1186                        stmt = connection.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
1187                        /*
1188                         * This is apparently a fix for large result sets specific to MySQL.
1189                         * There was also a comment to try 
1190                         * ((com.mysql.jdbc.Statement) stmt).enableStreamingResults();
1191                         */
1192                        if (getDbms().equalsIgnoreCase("mysql"))
1193                        {
1194                                if (debugLevel > 1)
1195                                {
1196                                        System.out.println("Special case for MySQL large result sets");
1197                                }
1198                                stmt.setFetchSize(Integer.MIN_VALUE);
1199                        }
1200                        else if (getDbms().equalsIgnoreCase("sqlserver"))
1201                                stmt.setFetchSize(20);
1202                        if (debugLevel > 1)
1203                        {
1204                                int sample = stmt.getResultSetType();
1205                                if (sample == ResultSet.TYPE_FORWARD_ONLY)
1206                                {
1207                                        System.out.println("Result set is TYPE_FORWARD_ONLY");
1208                                }
1209                                else if (sample == ResultSet.TYPE_SCROLL_INSENSITIVE)
1210                                {
1211                                        System.out.println("Result set is TYPE_SCROLL_INSENSITIVE");
1212                                }
1213                                else if (sample == ResultSet.TYPE_SCROLL_SENSITIVE)
1214                                {
1215                                        System.out.println("Result set is TYPE_SCROLL_SENSITIVE");
1216                                }
1217                                else
1218                                {
1219                                        System.out.println("Result set is of unknown type");
1220                                }
1221                        }
1222                        test = stmt.execute(sqlQuery);
1223                        if (!test)
1224                        {
1225                                System.out.println("Problem in executing " + sqlQuery);
1226                                return;
1227                        }
1228                        rs = stmt.getResultSet();
1229                        if (rs == null)
1230                        {
1231                                System.out.println("dumpTable: Result set was null");
1232                        }
1233                        else
1234                        {
1235                                System.out.println("dumpTable: Result set generated");
1236                                dumpResultSet(rs, targetTableName, output);
1237                        }
1238                }
1239                catch (java.sql.SQLException e)
1240                {
1241                        System.out.println("SQL Exception encountered: " + e.getMessage());
1242                        e.printStackTrace(System.out);
1243                }
1244                catch (Exception e)
1245                {
1246                        System.out.println("Unexpected exception in table dumper");
1247                        System.out.println(e.getClass().getName() + " " + e.getMessage());
1248                        e.printStackTrace(System.out);
1249                }
1250        }       
1251        
1252        /**
1253         * Dumps a result set to a PrintWriter object
1254         * @param rs Result Set
1255         * @param targetTableName Table name to be used in INSERT statement
1256         * @param outputWriter Destination PrintWriter object
1257         */
1258        public void dumpResultSet(ResultSet rs, String targetTableName, PrintWriter outputWriter)
1259        {
1260                GenericPrinter out = new GenericPrinter(outputWriter);
1261                dumpResultSet(rs, targetTableName, out);
1262        }
1263        /**
1264         * Dumps a result set to a PrintStream object
1265         * @param rs Result set
1266         * @param targetTableName Table name to be used in INSERT statement
1267         * @param outputStream Destination PrintStream object
1268         */
1269        public void dumpResultSet(ResultSet rs, String targetTableName, PrintStream outputStream)
1270        {
1271                GenericPrinter out = new GenericPrinter(outputStream);
1272                dumpResultSet(rs, targetTableName,  out);
1273        }
1274        /**
1275         * Dumps a result set as a set of INSERT SQL statements.
1276         * 
1277         * @param rs Result set to be dumped
1278         * @param targetTableName Table name to be used in INSERT statements
1279         * @param output GenericPrinter object to which information is passed
1280         *
1281         */
1282        public void dumpResultSet(ResultSet rs, String targetTableName, GenericPrinter output)
1283        {
1284                ResultSetMetaData meta = null;
1285                int columnCount = 0;
1286                String columnNames[];
1287                int columnTypes[];
1288                int rowCounter = 0;
1289                try
1290                {
1291                        rowCounter = 0;
1292                        meta = rs.getMetaData();
1293                        columnCount = meta.getColumnCount();
1294                        if (debugLevel > 0)
1295                        {
1296                                System.out.println("Query executed, " + Integer.toString(columnCount) + " columns"); 
1297                        }
1298                        columnNames = new String[columnCount];
1299                        columnTypes = new int[columnCount];
1300                        for (int i = 0; i < columnCount; i++)
1301                        {
1302                                columnNames[i] = meta.getColumnName(i+1);
1303                                columnTypes[i] = meta.getColumnType(i+1);
1304                        }
1305                        /* Get result set */
1306                        while (rs.next())
1307                        {
1308                                boolean nullValue[] = new boolean[columnCount];
1309                                String columnValue[] = new String[columnCount];
1310                                for (int i = 0; i < columnCount; i++)
1311                                {
1312                                        nullValue[i] = false;
1313                                        columnValue[i] = null;
1314                                }
1315                                rowCounter++;
1316                                if (rowCounter % 500 == 49) 
1317                                { 
1318                                        output.flush();
1319                                }       
1320                                StringBuffer working;
1321                                /* Read values for SQL statement */
1322                                for (int i = 0; i < columnCount; i++)
1323                                {
1324                                        
1325                                        int temp = columnTypes[i];
1326                                        if (temp == Types.TIMESTAMP)
1327                                        {
1328                                                java.sql.Timestamp date = rs.getTimestamp(i+1);
1329                                                if (rs.wasNull())
1330                                                {
1331                                                        nullValue[i] = true;
1332                                                        columnValue[i] = "dummy";
1333                                                }
1334                                                else
1335                                                {
1336                                                        columnValue[i] ="\'" + date.toString() + "\'"; 
1337                                                }
1338                                        }
1339                                        else if (temp == Types.VARCHAR || temp == Types.LONGVARCHAR ||
1340                                                        temp == Types.CHAR)
1341                                        {
1342                                                String value = rs.getString(i+1);
1343                                                if (rs.wasNull())
1344                                                {
1345                                                        nullValue[i] = true;
1346                                                        
1347                                                }
1348                                                else
1349                                                {
1350                                                        
1351                                                        columnValue[i] = "\'" + value + "\'";
1352                                                }
1353                                        }
1354                                        else if (temp == Types.FLOAT || temp == Types.DOUBLE || temp == Types.DECIMAL ||
1355                                                        temp == Types.NUMERIC || temp == Types.REAL)
1356                                        {
1357                                                float value = 0.0f;
1358                                                value = rs.getFloat(i+1);
1359                                                if (rs.wasNull())
1360                                                {
1361                                                        nullValue[i] = true;
1362                                                        
1363                                                }
1364                                                else
1365                                                {
1366                                                        
1367                                                        columnValue[i] = Float.toString(value);
1368                                                }
1369                                        }
1370                                        else if (temp == Types.TINYINT || temp == Types.SMALLINT ||
1371                                                        temp == Types.INTEGER)
1372                                        {
1373                                                int value = rs.getInt(i+1);
1374                                                if (rs.wasNull()) 
1375                                                {
1376                                                        nullValue[i] = true;
1377                                                        
1378                                                }
1379                                                else
1380                                                {
1381                                                        
1382                                                        columnValue[i] = Integer.toString(value);
1383                                                }
1384                                        }
1385                                }
1386                                /*
1387                                 * Write SQL statement
1388                                 */
1389                                working = new StringBuffer();
1390                                boolean firstItemProcessed = false;
1391                                working.append("INSERT INTO " + targetTableName + "(");
1392                                for (int i = 0; i < columnCount; i++)
1393                                {
1394                                        if (nullValue[i])
1395                                        {
1396                                                continue;
1397                                        }
1398                                        if (firstItemProcessed)
1399                                        {
1400                                                working.append(", " + columnNames[i]);
1401                                        }
1402                                        else
1403                                        {
1404                                                working.append(columnNames[i]);
1405                                                firstItemProcessed = true;
1406                                        }
1407                                }
1408                                working.append(") VALUES(");
1409                                firstItemProcessed = false;
1410                                for (int i = 0; i < columnCount; i++)
1411                                {
1412                                        if (nullValue[i])
1413                                        {
1414                                                continue;
1415                                        }
1416                                        if (firstItemProcessed)
1417                                        {
1418                                                working.append(", " + columnValue[i]);
1419                                        }
1420                                        else
1421                                        {
1422                                                working.append(columnValue[i]);
1423                                                firstItemProcessed = true;
1424                                        }
1425                                }
1426                                working.append(");");
1427                                output.println(new String(working));
1428                        }
1429                        if (debugLevel > 0)
1430                        {
1431                                System.out.println(Integer.toString(rowCounter) + " rows were processed");
1432                        }
1433                        rs.close();             
1434                }
1435                catch (java.sql.SQLException e)
1436                {
1437                        System.out.println("SQL Exception encountered: ");
1438                        System.out.println(e.getClass().getName()+ e.getMessage());
1439                        e.printStackTrace(System.out);
1440                }
1441                catch (Exception e)
1442                {
1443                        System.out.println("Unexpected exception in table dumper");
1444                        System.out.println(e.getClass().getName() + " " + e.getMessage());
1445                        e.printStackTrace(System.out);
1446                }
1447        }
1448        /**
1449         * Insert rows in a table where there is only one column in
1450         * the primary key.
1451         * 
1452         * <p>The primary key should be the first column in the list.</p>
1453         * 
1454         * @param tableName Name of the table
1455         * @param columnNames Array containing column names in the database
1456         *        table for the columns to be populated with data.
1457         * @param value Values to be inserted in the columns
1458         * @throws SQLException
1459         * @deprecated
1460         */
1461        public void insertRow (String tableName,
1462                String columnNames[], Object... value) throws SQLException      
1463                        {
1464                                insertRowWithKeys (tableName, columnNames, 1, value);
1465                        }
1466        /**
1467         * Insert rows in a table where there is more than one column in
1468         * the primary key.
1469         * 
1470         * @param tableName Name of the table
1471         * @param columnNames Array containing column names in the database
1472         *        table.
1473         * @param columnCount Number of columns in primary key.  These are the first
1474         *        columns in the list.
1475         * @param value Values to be inserted in the columns
1476         * @throws SQLException
1477         */
1478        public void insertRowWithKeys (String tableName,
1479                        String columnNames[], int columnCount, Object... value) throws SQLException
1480        {
1481                if (debugLevel > 1 || columnNames.length != value.length)
1482                {
1483                        System.out.println("Starting insertRow");
1484
1485                        for (int i = 0; i < columnNames.length; i++)
1486                        {
1487                                System.out.print(columnNames[i] + " ");
1488                        }
1489                        System.out.println();
1490                        for (int i = 0; i < value.length; i ++)
1491                        {
1492                                System.out.print(value[i] + " ");
1493                        }
1494                        System.out.println();
1495                }
1496                StringBuffer queryBuffer = new StringBuffer();
1497                queryBuffer.append("SELECT COUNT(*) AS COUNTER FROM " + 
1498                                prefixTableName(tableName) +
1499                        " WHERE ");
1500                
1501                for (int i = 0; i < columnCount; i++)
1502                {
1503                        if (i > 0) { queryBuffer.append(" AND "); }
1504                                queryBuffer.append(columnNames[i] + "=? ");
1505                        
1506                }
1507                PreparedStatement stmt = prepareStatement(new String(queryBuffer));
1508                if (debugLevel > 0)
1509                {
1510                        System.out.println(new String(queryBuffer));
1511                        System.out.print("     ");
1512                        for (int i = 0; i < columnCount; i++)
1513                        {
1514                                if (i > 0) System.out.print(" ");
1515                                System.out.print(value[i].toString());
1516                        }
1517                        System.out.println();
1518                }
1519                for (int i = 0; i < columnCount; i++)
1520                {
1521                        stmt.setObject(i + 1, value[i]);
1522                }
1523                ResultSet rs = stmt.executeQuery();
1524                queryBuffer = new StringBuffer();
1525                rs.next();
1526                int rowCount = rs.getInt("COUNTER");
1527                if (rowCount == 0)
1528                {
1529                        boolean firstItem = true;
1530                        if (debugLevel > 1)
1531                        {
1532                                System.out.println("Inserting record");
1533                        }
1534                        queryBuffer.append("INSERT INTO " + prefixTableName(tableName) + " ( ");
1535                        for (int i = 0; i < columnNames.length; i++)
1536                        {
1537                                if (value[i] == null) { continue; }
1538                                if (!firstItem) { queryBuffer.append(", "); }
1539                                queryBuffer.append(columnNames[i]);
1540                                firstItem = false;
1541                        }
1542                        firstItem = true;
1543                        queryBuffer.append(" ) VALUES ( ");
1544                        for (int i = 0; i < columnNames.length; i++)
1545                        {
1546                                if (value[i] == null) { continue; }
1547                                if (!firstItem) { queryBuffer.append(", "); }
1548                                if (Number.class.isAssignableFrom(value[i].getClass()))
1549                                {
1550                                        queryBuffer.append(value[i].toString());
1551                                }
1552                                else if (String.class.isAssignableFrom(value[i].getClass()))
1553                                {
1554                                        queryBuffer.append("\'" + value[i].toString() + "\'");
1555                                }
1556                                firstItem = false;
1557                        }
1558                        queryBuffer.append(" )");
1559                }
1560                else
1561                {
1562                        boolean firstItem = true;
1563                        if (debugLevel > 1)
1564                        {
1565                                System.out.println("Updating row");
1566                        }
1567                        if (columnNames.length == 1)
1568                        {
1569                                return;
1570                        }
1571                        if (value[0] == null)
1572                        {
1573                                throw new SQLException("Primary key value may not be null");
1574                        }
1575                        queryBuffer.append("UPDATE " + prefixTableName(tableName) + " SET ");
1576                        for (int i = columnCount; i < columnNames.length; i++)
1577                        {
1578                                if (value[i] == null) { continue; }
1579                                if (!firstItem) 
1580                                { 
1581                                        queryBuffer.append(", "); 
1582                                }
1583                                queryBuffer.append(" " + columnNames[i] + "=");
1584                                if (Number.class.isAssignableFrom(value[i].getClass()))
1585                                {
1586                                        queryBuffer.append(value[i].toString());
1587                                }
1588                                else if (String.class.isAssignableFrom(value[i].getClass()))
1589                                {
1590                                        queryBuffer.append("\'" + value[i].toString() + "\'");
1591                                }
1592                                firstItem = false;
1593                        }
1594                        queryBuffer.append(" WHERE ");
1595                        firstItem = true;
1596                        for (int i = 0; i < columnCount; i++)
1597                        {
1598                                if (!firstItem)
1599                                {
1600                                        queryBuffer.append(" AND ");
1601                                }
1602                                queryBuffer.append(columnNames[i] + "=");
1603                                if (Number.class.isAssignableFrom(value[i].getClass()))
1604                                {
1605                                        queryBuffer.append(value[i].toString());
1606                                }
1607                                else if (String.class.isAssignableFrom(value[i].getClass()))
1608                                {
1609                                        queryBuffer.append("\'" + value[i].toString() + "\'");
1610                                }
1611                                firstItem = false;
1612                        }
1613                }
1614                if (debugLevel > 0)
1615                {
1616                        System.out.println(new String(queryBuffer));
1617                }
1618                executeUpdate(new String(queryBuffer));
1619        }       
1620}