~ubuntu-branches/ubuntu/maverick/hsqldb/maverick

« back to all changes in this revision

Viewing changes to src/org/hsqldb/util/SqlTool.java

  • Committer: Bazaar Package Importer
  • Author(s): Matthias Klose
  • Date: 2006-09-26 11:47:49 UTC
  • mfrom: (1.1.2 upstream)
  • Revision ID: james.westby@ubuntu.com-20060926114749-3jd0utm7w21x1iqt
Tags: 1.8.0.5-2ubuntu1
* Synchronise with Debian unstable; remaining changes:
  - build using java-gcj-compat.
* libhsqldb-java: Add gij as alternative dependency.
* Build a libhsqldb-java-gcj package.

Show diffs side-by-side

added added

removed removed

Lines of Context:
29
29
 */
30
30
 
31
31
 
32
 
package org.hsqldb.util;
33
 
 
34
 
import java.io.File;
35
 
import java.io.IOException;
36
 
import java.sql.Connection;
37
 
import java.sql.DatabaseMetaData;
38
 
import java.sql.SQLException;
39
 
import java.util.HashMap;
40
 
import java.util.StringTokenizer;
41
 
 
42
 
/* $Id: SqlTool.java,v 1.49 2005/06/08 19:52:38 fredt Exp $ */
43
 
 
44
 
/**
45
 
 * Sql Tool.  A command-line and/or interactive SQL tool.
46
 
 * (Note:  For every Javadoc block comment, I'm using a single blank line
47
 
 *  immediately after the description, just like's Sun's examples in
48
 
 *  their Coding Conventions document).
49
 
 *
50
 
 * See JavaDocs for the main method for syntax of how to run.
51
 
 *
52
 
 * @see @main()
53
 
 * @version $Revision: 1.49 $
54
 
 * @author Blaine Simpson unsaved@users
55
 
 */
56
 
public class SqlTool {
57
 
 
58
 
    private static final String DEFAULT_RCFILE =
59
 
        System.getProperty("user.home") + "/sqltool.rc";
60
 
    private static Connection conn;
61
 
 
62
 
    // N.b. the following is static!
63
 
    private static boolean noexit;    // Whether System.exit() may be called.
64
 
    private static String  revnum = null;
65
 
 
66
 
    static {
67
 
        revnum = "$Revision: 1.49 $".substring("$Revision: ".length(),
68
 
                                               "$Revision: 1.49 $".length()
69
 
                                               - 2);
70
 
    }
71
 
 
72
 
    private static final String SYNTAX_MESSAGE =
73
 
        "Usage: java [-Dsqlfile.X=Y...] org.hsqldb.util.SqlTool \\\n"
74
 
        + "    [--optname [optval...]] urlid [file1.sql...]\n"
75
 
        + "where arguments are:\n"
76
 
        + "    --help                   Displays this message\n"
77
 
        + "    --list                   List urlids in the rcfile\n"
78
 
        + "    --noinput                Do not read stdin (default if sql file given\n"
79
 
        + "                             or --sql switch used).\n"
80
 
        + "    --stdinput               Read stdin IN ADDITION to sql files/--sql input\n"
81
 
        + "    --debug                  Print Debug info to stderr\n"
82
 
        + "    --noAutoFile             Do not execute auto.sql from home dir\n"
83
 
        + "    --autoCommit             Auto-commit JDBC DML commands\n"
84
 
        + "    --sql \"SQL; Statements\"  Execute given SQL instead of stdin (before\n"
85
 
        + "                             SQL files if any are specified) where \"SQL\"\n"
86
 
        + "                             consists of SQL command(s).  See the Guide.\n"
87
 
        + "    --rcfile /file/path.rc   Connect Info File [$HOME/sqltool.rc]\n"
88
 
        + "    --abortOnErr             Abort on Error (overrides defaults)\n"
89
 
        + "    --continueOnErr          Continue on Error (overrides defaults)\n"
90
 
        + "    --setvar NAME1=val1[,NAME2=val2...]   PL variables\n"
91
 
        + "    --driver a.b.c.Driver    JDBC driver class ["
92
 
        + RCData.DEFAULT_JDBC_DRIVER + "]\n"
93
 
        + "    urlid                    ID of url/userame/password in rcfile\n"
94
 
        + "    file1.sql...             SQL files to be executed [stdin]\n"
95
 
        + "                             "
96
 
        + "(Use '-' for non-interactively stdin).\n"
97
 
        + "See the SqlTool Manual for the supported sqltool.* System Properties.\n"
98
 
        + "SqlTool v. " + revnum + ".";
99
 
 
100
 
    /** Utility nested class for internal use. */
101
 
    private static class BadCmdline extends Exception {}
102
 
    ;
103
 
 
104
 
    /** Utility object for internal use. */
105
 
    private static BadCmdline bcl = new BadCmdline();
106
 
 
107
 
    /** Nested class for external callers of SqlTool.main() */
108
 
    public static class SqlToolException extends Exception {
109
 
 
110
 
        public SqlToolException() {
111
 
            super();
112
 
        }
113
 
 
114
 
        public SqlToolException(String s) {
115
 
            super(s);
116
 
        }
117
 
    }
118
 
 
119
 
    /**
120
 
     * Exit the main() method by either throwing an exception or exiting JVM.
121
 
     *
122
 
     * Call return() right after you call this method, because this method
123
 
     * will not exit if (noexit is true && retval == 0).
124
 
     */
125
 
    private static void exitMain(int retval) throws SqlToolException {
126
 
        exitMain(retval, null);
127
 
    }
128
 
 
129
 
    /**
130
 
     * Exit the main() method by either throwing an exception or exiting JVM.
131
 
     *
132
 
     * Call return() right after you call this method, because this method
133
 
     * will not exit if (noexit is true && retval == 0).
134
 
     */
135
 
    private static void exitMain(int retval,
136
 
                                 String msg) throws SqlToolException {
137
 
 
138
 
        if (noexit) {
139
 
            if (retval == 0) {
140
 
                return;
141
 
            } else if (msg == null) {
142
 
                throw new SqlToolException();
143
 
            } else {
144
 
                throw new SqlToolException(msg);
145
 
            }
146
 
        } else {
147
 
            if (msg != null) {
148
 
                ((retval == 0) ? System.out
149
 
                               : System.err).println(msg);
150
 
            }
151
 
 
152
 
            System.exit(retval);
153
 
        }
154
 
    }
155
 
 
156
 
    /**
157
 
     * Connect to a JDBC Database and execute the commands given on
158
 
     * stdin or in SQL file(s).
159
 
     * Like most main methods, this is not intended to be thread-safe.
160
 
     *
161
 
     * @param arg  Run "java... org.hsqldb.util.SqlTool --help" for syntax.
162
 
     * @throws SqlToolException May be thrown only if the system property
163
 
     *                          'sqltool.noexit' is set (to anything).
164
 
     */
165
 
    public static void main(String[] arg) throws SqlToolException {
166
 
 
167
 
        /*
168
 
         * The big picture is, we parse input args; load a RCData;
169
 
         * get a JDBC Connection with the RCData; instantiate and
170
 
         * execute as many SqlFiles as we need to.
171
 
         */
172
 
        String  rcFile           = null;
173
 
        File    tmpFile          = null;
174
 
        String  sqlText          = null;
175
 
        String  driver           = null;
176
 
        String  targetDb         = null;
177
 
        String  varSettings      = null;
178
 
        boolean debug            = false;
179
 
        File[]  scriptFiles      = null;
180
 
        int     i                = -1;
181
 
        boolean listMode         = false;
182
 
        boolean interactive      = false;
183
 
        boolean noinput          = false;
184
 
        boolean noautoFile       = false;
185
 
        boolean autoCommit       = false;
186
 
        Boolean coeOverride      = null;
187
 
        Boolean stdinputOverride = null;
188
 
 
189
 
        noexit = System.getProperty("sqltool.noexit") != null;
190
 
 
191
 
        try {
192
 
            while ((i + 1 < arg.length) && arg[i + 1].startsWith("--")) {
193
 
                i++;
194
 
 
195
 
                if (arg[i].length() == 2) {
196
 
                    break;             // "--"
197
 
                }
198
 
 
199
 
                if (arg[i].substring(2).equals("help")) {
200
 
                    exitMain(0, SYNTAX_MESSAGE);
201
 
 
202
 
                    return;
203
 
                }
204
 
 
205
 
                if (arg[i].substring(2).equals("abortOnErr")) {
206
 
                    if (coeOverride != null) {
207
 
                        exitMain(
208
 
                            0, "Switches '--abortOnErr' and "
209
 
                            + "'--continueOnErr' are mutually exclusive");
210
 
 
211
 
                        return;
212
 
                    }
213
 
 
214
 
                    coeOverride = Boolean.FALSE;
215
 
 
216
 
                    continue;
217
 
                }
218
 
 
219
 
                if (arg[i].substring(2).equals("continueOnErr")) {
220
 
                    if (coeOverride != null) {
221
 
                        exitMain(
222
 
                            0, "Switches '--abortOnErr' and "
223
 
                            + "'--continueOnErr' are mutually exclusive");
224
 
 
225
 
                        return;
226
 
                    }
227
 
 
228
 
                    coeOverride = Boolean.TRUE;
229
 
 
230
 
                    continue;
231
 
                }
232
 
 
233
 
                if (arg[i].substring(2).equals("list")) {
234
 
                    listMode = true;
235
 
 
236
 
                    continue;
237
 
                }
238
 
 
239
 
                if (arg[i].substring(2).equals("rcfile")) {
240
 
                    if (++i == arg.length) {
241
 
                        throw bcl;
242
 
                    }
243
 
 
244
 
                    rcFile = arg[i];
245
 
 
246
 
                    continue;
247
 
                }
248
 
 
249
 
                if (arg[i].substring(2).equals("setvar")) {
250
 
                    if (++i == arg.length) {
251
 
                        throw bcl;
252
 
                    }
253
 
 
254
 
                    varSettings = arg[i];
255
 
 
256
 
                    continue;
257
 
                }
258
 
 
259
 
                if (arg[i].substring(2).equals("sql")) {
260
 
                    noinput = true;    // but turn back on if file "-" specd.
261
 
 
262
 
                    if (++i == arg.length) {
263
 
                        throw bcl;
264
 
                    }
265
 
 
266
 
                    sqlText = arg[i];
267
 
 
268
 
                    if (sqlText.charAt(sqlText.length() - 1) != ';') {
269
 
                        sqlText += ";";
270
 
                    }
271
 
 
272
 
                    continue;
273
 
                }
274
 
 
275
 
                if (arg[i].substring(2).equals("debug")) {
276
 
                    debug = true;
277
 
 
278
 
                    continue;
279
 
                }
280
 
 
281
 
                if (arg[i].substring(2).equals("noAutoFile")) {
282
 
                    noautoFile = true;
283
 
 
284
 
                    continue;
285
 
                }
286
 
 
287
 
                if (arg[i].substring(2).equals("autoCommit")) {
288
 
                    autoCommit = true;
289
 
 
290
 
                    continue;
291
 
                }
292
 
 
293
 
                if (arg[i].substring(2).equals("stdinput")) {
294
 
                    noinput          = false;
295
 
                    stdinputOverride = Boolean.TRUE;
296
 
 
297
 
                    continue;
298
 
                }
299
 
 
300
 
                if (arg[i].substring(2).equals("noinput")) {
301
 
                    noinput          = true;
302
 
                    stdinputOverride = Boolean.FALSE;
303
 
 
304
 
                    continue;
305
 
                }
306
 
 
307
 
                if (arg[i].substring(2).equals("driver")) {
308
 
                    if (++i == arg.length) {
309
 
                        throw bcl;
310
 
                    }
311
 
 
312
 
                    driver = arg[i];
313
 
 
314
 
                    continue;
315
 
                }
316
 
 
317
 
                throw bcl;
318
 
            }
319
 
 
320
 
            if (!listMode) {
321
 
                if (++i == arg.length) {
322
 
                    throw bcl;
323
 
                }
324
 
 
325
 
                targetDb = arg[i];
326
 
            }
327
 
 
328
 
            int scriptIndex = 0;
329
 
 
330
 
            if (sqlText != null) {
331
 
                try {
332
 
                    tmpFile = File.createTempFile("sqltool-", ".sql");
333
 
 
334
 
                    //(new java.io.FileWriter(tmpFile)).write(sqlText);
335
 
                    java.io.FileWriter fw = new java.io.FileWriter(tmpFile);
336
 
 
337
 
                    fw.write("/* " + (new java.util.Date()) + ".  "
338
 
                             + SqlTool.class.getName()
339
 
                             + " command-line SQL. */\n\n");
340
 
                    fw.write(sqlText + '\n');
341
 
                    fw.flush();
342
 
                    fw.close();
343
 
                } catch (IOException ioe) {
344
 
                    exitMain(4, "Failed to write given sql to temp file: "
345
 
                             + ioe);
346
 
 
347
 
                    return;
348
 
                }
349
 
            }
350
 
 
351
 
            if (stdinputOverride != null) {
352
 
                noinput = !stdinputOverride.booleanValue();
353
 
            }
354
 
 
355
 
            interactive = (!noinput) && (arg.length <= i + 1);
356
 
 
357
 
            if (arg.length == i + 2 && arg[i + 1].equals("-")) {
358
 
                if (stdinputOverride == null) {
359
 
                    noinput = false;
360
 
                }
361
 
            } else if (arg.length > i + 1) {
362
 
 
363
 
                // I.e., if there are any SQL files specified.
364
 
                scriptFiles =
365
 
                    new File[arg.length - i - 1 + ((stdinputOverride == null ||!stdinputOverride.booleanValue()) ? 0
366
 
                                                                                                                 : 1)];
367
 
 
368
 
                if (debug) {
369
 
                    System.err.println("scriptFiles has "
370
 
                                       + scriptFiles.length + " elements");
371
 
                }
372
 
 
373
 
                while (i + 1 < arg.length) {
374
 
                    scriptFiles[scriptIndex++] = new File(arg[++i]);
375
 
                }
376
 
 
377
 
                if (stdinputOverride != null
378
 
                        && stdinputOverride.booleanValue()) {
379
 
                    scriptFiles[scriptIndex++] = null;
380
 
                    noinput                    = true;
381
 
                }
382
 
            }
383
 
        } catch (BadCmdline bcl) {
384
 
            exitMain(2, SYNTAX_MESSAGE);
385
 
 
386
 
            return;
387
 
        }
388
 
 
389
 
        RCData conData = null;
390
 
 
391
 
        try {
392
 
            conData = new RCData(new File((rcFile == null) ? DEFAULT_RCFILE
393
 
                                                           : rcFile), targetDb);
394
 
        } catch (Exception e) {
395
 
            exitMain(1, "Failed to retrieve connection info for database '"
396
 
                     + targetDb + "': " + e.getMessage());
397
 
 
398
 
            return;
399
 
        }
400
 
 
401
 
        if (listMode) {
402
 
            exitMain(0);
403
 
 
404
 
            return;
405
 
        }
406
 
 
407
 
        if (debug) {
408
 
            conData.report();
409
 
        }
410
 
 
411
 
        try {
412
 
            conn = conData.getConnection(
413
 
                driver, System.getProperty("sqlfile.charset"),
414
 
                System.getProperty("javax.net.ssl.trustStore"));
415
 
 
416
 
            conn.setAutoCommit(autoCommit);
417
 
 
418
 
            DatabaseMetaData md = null;
419
 
 
420
 
            if (interactive && (md = conn.getMetaData()) != null) {
421
 
                System.out.println("JDBC Connection established to a "
422
 
                                   + md.getDatabaseProductName() + " v. "
423
 
                                   + md.getDatabaseProductVersion()
424
 
                                   + " database as '" + md.getUserName()
425
 
                                   + "'.");
426
 
            }
427
 
        } catch (Exception e) {
428
 
            e.printStackTrace();
429
 
 
430
 
            // Let's not continue as if nothing is wrong.
431
 
            exitMain(10,
432
 
                     "Failed to get a connection to " + conData.url + " as "
433
 
                     + conData.username + ".  " + e.getMessage());
434
 
 
435
 
            return;
436
 
        }
437
 
 
438
 
        File[] emptyFileArray      = {};
439
 
        File[] singleNullFileArray = { null };
440
 
        File   autoFile            = null;
441
 
 
442
 
        if (interactive &&!noautoFile) {
443
 
            autoFile = new File(System.getProperty("user.home")
444
 
                                + "/auto.sql");
445
 
 
446
 
            if ((!autoFile.isFile()) ||!autoFile.canRead()) {
447
 
                autoFile = null;
448
 
            }
449
 
        }
450
 
 
451
 
        if (scriptFiles == null) {
452
 
 
453
 
            // I.e., if no SQL files given on command-line.
454
 
            // Input file list is either nothing or {null} to read stdin.
455
 
            scriptFiles = (noinput ? emptyFileArray
456
 
                                   : singleNullFileArray);
457
 
        }
458
 
 
459
 
        int numFiles = scriptFiles.length;
460
 
 
461
 
        if (tmpFile != null) {
462
 
            numFiles += 1;
463
 
        }
464
 
 
465
 
        if (autoFile != null) {
466
 
            numFiles += 1;
467
 
        }
468
 
 
469
 
        SqlFile[] sqlFiles = new SqlFile[numFiles];
470
 
        HashMap   userVars = new HashMap();
471
 
 
472
 
        if (varSettings != null) {
473
 
            int             equals;
474
 
            String          curSetting, var, val;
475
 
            StringTokenizer allvars = new StringTokenizer(varSettings, ",");
476
 
 
477
 
            while (allvars.hasMoreTokens()) {
478
 
                curSetting = allvars.nextToken().trim();
479
 
                equals     = curSetting.indexOf('=');
480
 
 
481
 
                if (equals < 1) {
482
 
                    exitMain(24, "Var settings not of format NAME=var[,...]");
483
 
 
484
 
                    return;
485
 
                }
486
 
 
487
 
                var = curSetting.substring(0, equals).trim();
488
 
                val = curSetting.substring(equals + 1).trim();
489
 
 
490
 
                if (var.length() < 1 || val.length() < 1) {
491
 
                    exitMain(24, "Var settings not of format NAME=var[,...]");
492
 
 
493
 
                    return;
494
 
                }
495
 
 
496
 
                userVars.put(var, val);
497
 
            }
498
 
        }
499
 
 
500
 
        // We print version before execing this one.
501
 
        int interactiveFileIndex = -1;
502
 
 
503
 
        try {
504
 
            int fileIndex = 0;
505
 
 
506
 
            if (autoFile != null) {
507
 
                sqlFiles[fileIndex++] = new SqlFile(autoFile, false,
508
 
                                                    userVars);
509
 
            }
510
 
 
511
 
            if (tmpFile != null) {
512
 
                sqlFiles[fileIndex++] = new SqlFile(tmpFile, false, userVars);
513
 
            }
514
 
 
515
 
            for (int j = 0; j < scriptFiles.length; j++) {
516
 
                if (interactiveFileIndex < 0 && interactive) {
517
 
                    interactiveFileIndex = fileIndex;
518
 
                }
519
 
 
520
 
                sqlFiles[fileIndex++] = new SqlFile(scriptFiles[j],
521
 
                                                    interactive, userVars);
522
 
            }
523
 
        } catch (IOException ioe) {
524
 
            try {
525
 
                conn.close();
526
 
            } catch (Exception e) {}
527
 
 
528
 
            exitMain(2, ioe.getMessage());
529
 
 
530
 
            return;
531
 
        }
532
 
 
533
 
        int retval = 0;    // Value we will return via System.exit().
534
 
 
535
 
        try {
536
 
            for (int j = 0; j < sqlFiles.length; j++) {
537
 
                if (j == interactiveFileIndex) {
538
 
                    System.out.print("SqlTool v. " + revnum
539
 
                                     + ".                        ");
540
 
                }
541
 
 
542
 
                sqlFiles[j].execute(conn, coeOverride);
543
 
            }
544
 
        } catch (IOException ioe) {
545
 
            System.err.println("Failed to execute SQL:  " + ioe.getMessage());
546
 
 
547
 
            retval = 3;
548
 
 
549
 
            // These two Exception types are handled properly inside of SqlFile.
550
 
            // We just need to return an appropriate error status.
551
 
        } catch (SqlToolError ste) {
552
 
            retval = 2;
553
 
 
554
 
            // Should not be handling SQLExceptions here!  SqlFile should handle
555
 
            // them.
556
 
        } catch (SQLException se) {
557
 
            retval = 1;
558
 
        } finally {
559
 
            try {
560
 
                conn.close();
561
 
            } catch (Exception e) {}
562
 
        }
563
 
 
564
 
        // Taking file removal out of final block because this is good debug
565
 
        // info to keep around if the program aborts.
566
 
        if (tmpFile != null &&!tmpFile.delete()) {
567
 
            System.err.println(
568
 
                "Error occurred while trying to remove temp file '" + tmpFile
569
 
                + "'");
570
 
        }
571
 
 
572
 
        exitMain(retval);
573
 
 
574
 
        return;
575
 
    }
576
 
}
 
32
package org.hsqldb.util;
 
33
 
 
34
import java.io.BufferedReader;
 
35
import java.io.File;
 
36
import java.io.IOException;
 
37
import java.io.InputStreamReader;
 
38
import java.sql.Connection;
 
39
import java.sql.DatabaseMetaData;
 
40
import java.sql.SQLException;
 
41
import java.util.HashMap;
 
42
import java.util.Map;
 
43
import java.util.StringTokenizer;
 
44
 
 
45
/* $Id: SqlTool.java,v 1.55 2006/07/27 20:04:31 fredt Exp $ */
 
46
 
 
47
/**
 
48
 * Sql Tool.  A command-line and/or interactive SQL tool.
 
49
 * (Note:  For every Javadoc block comment, I'm using a single blank line
 
50
 *  immediately after the description, just like's Sun's examples in
 
51
 *  their Coding Conventions document).
 
52
 *
 
53
 * See JavaDocs for the main method for syntax of how to run.
 
54
 *
 
55
 * @see #main()
 
56
 * @version $Revision: 1.55 $
 
57
 * @author Blaine Simpson unsaved@users
 
58
 */
 
59
public class SqlTool {
 
60
 
 
61
    private static final String DEFAULT_RCFILE =
 
62
        System.getProperty("user.home") + "/sqltool.rc";
 
63
    private static Connection conn;
 
64
 
 
65
    // N.b. the following is static!
 
66
    private static boolean noexit;    // Whether System.exit() may be called.
 
67
    private static String  revnum = null;
 
68
 
 
69
    /**
 
70
     * The configuration identifier to use when connection parameters are
 
71
     * specified on the command line
 
72
     */
 
73
    private static String CMDLINE_ID = "cmdline";
 
74
 
 
75
    static {
 
76
        revnum = "$Revision: 1.55 $".substring("$Revision: ".length(),
 
77
                                               "$Revision: 1.55 $".length()
 
78
                                               - 2);
 
79
    }
 
80
 
 
81
    private static final String SYNTAX_MESSAGE =
 
82
        "Usage: java [-Dsqlfile.X=Y...] org.hsqldb.util.SqlTool \\\n"
 
83
        + "    [--optname [optval...]] urlid [file1.sql...]\n"
 
84
        + "where arguments are:\n"
 
85
        + "    --help                   Displays this message\n"
 
86
        + "    --list                   List urlids in the rc file\n"
 
87
        + "    --noInput                Do not read stdin (default if sql file given\n"
 
88
        + "                             or --sql switch used).\n"
 
89
        + "    --stdInput               Read stdin IN ADDITION to sql files/--sql input\n"
 
90
        + "    --inlineRc URL=val1,USER=val2[,DRIVER=val3][,CHARSET=val4][,TRUST=val5]\n"
 
91
        + "                             Inline RC file variables\n"
 
92
        + "    --debug                  Print Debug info to stderr\n"
 
93
        + "    --noAutoFile             Do not execute auto.sql from home dir\n"
 
94
        + "    --autoCommit             Auto-commit JDBC DML commands\n"
 
95
        + "    --sql \"SQL; Statements\"  Execute given SQL instead of stdin (before\n"
 
96
        + "                             SQL files if any are specified) where \"SQL\"\n"
 
97
        + "                             consists of SQL command(s).  See the Guide.\n"
 
98
        + "    --rcFile /file/path.rc   Connect Info File [$HOME/sqltool.rc]\n"
 
99
        + "    --abortOnErr             Abort on Error (overrides defaults)\n"
 
100
        + "    --continueOnErr          Continue on Error (overrides defaults)\n"
 
101
        + "    --setVar NAME1=val1[,NAME2=val2...]   PL variables\n"
 
102
        + "    --driver a.b.c.Driver    JDBC driver class ["
 
103
        + RCData.DEFAULT_JDBC_DRIVER + "]\n"
 
104
        + "    urlid                    ID of url/userame/password in rcfile\n"
 
105
        + "    file1.sql...             SQL files to be executed [stdin]\n"
 
106
        + "                             "
 
107
        + "(Use '-' for non-interactively stdin).\n"
 
108
        + "See the SqlTool Manual for the supported sqltool.* System Properties.\n"
 
109
        + "SqlTool v. " + revnum + ".";
 
110
 
 
111
    /** Utility nested class for internal use. */
 
112
    private static class BadCmdline extends Exception {}
 
113
    ;
 
114
 
 
115
    /** Utility object for internal use. */
 
116
    private static BadCmdline bcl = new BadCmdline();
 
117
 
 
118
    /** Nested class for external callers of SqlTool.main() */
 
119
    public static class SqlToolException extends Exception {
 
120
 
 
121
        public SqlToolException() {
 
122
            super();
 
123
        }
 
124
 
 
125
        public SqlToolException(String s) {
 
126
            super(s);
 
127
        }
 
128
    }
 
129
 
 
130
    /**
 
131
     * Exit the main() method by either throwing an exception or exiting JVM.
 
132
     *
 
133
     * Call return() right after you call this method, because this method
 
134
     * will not exit if (noexit is true && retval == 0).
 
135
     */
 
136
    private static void exitMain(int retval) throws SqlToolException {
 
137
        exitMain(retval, null);
 
138
    }
 
139
 
 
140
    /**
 
141
     * Exit the main() method by either throwing an exception or exiting JVM.
 
142
     *
 
143
     * Call return() right after you call this method, because this method
 
144
     * will not exit if (noexit is true && retval == 0).
 
145
     */
 
146
    private static void exitMain(int retval,
 
147
                                 String msg) throws SqlToolException {
 
148
 
 
149
        if (noexit) {
 
150
            if (retval == 0) {
 
151
                return;
 
152
            } else if (msg == null) {
 
153
                throw new SqlToolException();
 
154
            } else {
 
155
                throw new SqlToolException(msg);
 
156
            }
 
157
        } else {
 
158
            if (msg != null) {
 
159
                ((retval == 0) ? System.out
 
160
                               : System.err).println(msg);
 
161
            }
 
162
 
 
163
            System.exit(retval);
 
164
        }
 
165
    }
 
166
 
 
167
    /**
 
168
     * Prompt the user for a password.
 
169
     *
 
170
     * @param username The user the password is for
 
171
     * @return The password the user entered
 
172
     */
 
173
    private static String promptForPassword(String username)
 
174
    throws SqlToolException {
 
175
 
 
176
        BufferedReader console;
 
177
        String         password;
 
178
 
 
179
        password = null;
 
180
 
 
181
        try {
 
182
            console = new BufferedReader(new InputStreamReader(System.in));
 
183
 
 
184
            // Prompt for password
 
185
            System.out.print(username + "'s password: ");
 
186
 
 
187
            // Read the password from the command line
 
188
            password = console.readLine();
 
189
 
 
190
            if (password == null) {
 
191
                password = "";
 
192
            } else {
 
193
                password = password.trim();
 
194
            }
 
195
        } catch (IOException e) {
 
196
            exitMain(30,
 
197
                     "Error while reading password from console: "
 
198
                     + e.getMessage());
 
199
        }
 
200
 
 
201
        return password;
 
202
    }
 
203
 
 
204
    /**
 
205
     * Parses a comma delimited string of name value pairs into a
 
206
     * <code>Map</code> object.
 
207
     *
 
208
     * @param varString The string to parse
 
209
     * @param varMap The map to save the pared values into
 
210
     * @param lowerCaseKeys Set to <code>true</code> if the map keys should be
 
211
     *        converted to lower case
 
212
     */
 
213
    private static void varParser(String varString, Map varMap,
 
214
                                  boolean lowerCaseKeys)
 
215
                                  throws SqlToolException {
 
216
 
 
217
        int             equals;
 
218
        String          curSetting;
 
219
        String          var;
 
220
        String          val;
 
221
        StringTokenizer allvars;
 
222
 
 
223
        if ((varMap == null) || (varString == null)) {
 
224
            return;
 
225
        }
 
226
 
 
227
        allvars = new StringTokenizer(varString, ",");
 
228
 
 
229
        while (allvars.hasMoreTokens()) {
 
230
            curSetting = allvars.nextToken().trim();
 
231
            equals     = curSetting.indexOf('=');
 
232
 
 
233
            if (equals < 1) {
 
234
                throw new SqlToolException(
 
235
                    "Var settings not of format NAME=var[,...]");
 
236
            }
 
237
 
 
238
            var = curSetting.substring(0, equals).trim();
 
239
            val = curSetting.substring(equals + 1).trim();
 
240
 
 
241
            if (var.length() < 1 || val.length() < 1) {
 
242
                throw new SqlToolException(
 
243
                    "Var settings not of format NAME=var[,...]");
 
244
            }
 
245
 
 
246
            if (lowerCaseKeys) {
 
247
                var = var.toLowerCase();
 
248
            }
 
249
 
 
250
            varMap.put(var, val);
 
251
        }
 
252
    }
 
253
 
 
254
    /**
 
255
     * Connect to a JDBC Database and execute the commands given on
 
256
     * stdin or in SQL file(s).
 
257
     * Like most main methods, this is not intended to be thread-safe.
 
258
     *
 
259
     * @param arg  Run "java... org.hsqldb.util.SqlTool --help" for syntax.
 
260
     * @throws SqlToolException May be thrown only if the system property
 
261
     *                          'sqltool.noexit' is set (to anything).
 
262
     */
 
263
    public static void main(String[] arg) throws SqlToolException {
 
264
 
 
265
        /*
 
266
         * The big picture is, we parse input args; load a RCData;
 
267
         * get a JDBC Connection with the RCData; instantiate and
 
268
         * execute as many SqlFiles as we need to.
 
269
         */
 
270
        String  rcFile           = null;
 
271
        File    tmpFile          = null;
 
272
        String  sqlText          = null;
 
273
        String  driver           = null;
 
274
        String  targetDb         = null;
 
275
        String  varSettings      = null;
 
276
        boolean debug            = false;
 
277
        File[]  scriptFiles      = null;
 
278
        int     i                = -1;
 
279
        boolean listMode         = false;
 
280
        boolean interactive      = false;
 
281
        boolean noinput          = false;
 
282
        boolean noautoFile       = false;
 
283
        boolean autoCommit       = false;
 
284
        Boolean coeOverride      = null;
 
285
        Boolean stdinputOverride = null;
 
286
        String  rcParams         = null;
 
287
        String  rcUrl            = null;
 
288
        String  rcUsername       = null;
 
289
        String  rcPassword       = null;
 
290
        String  rcDriver         = null;
 
291
        String  rcCharset        = null;
 
292
        String  rcTruststore     = null;
 
293
        Map     rcFields         = null;
 
294
        String  parameter;
 
295
 
 
296
        noexit = System.getProperty("sqltool.noexit") != null;
 
297
 
 
298
        try {
 
299
            while ((i + 1 < arg.length) && arg[i + 1].startsWith("--")) {
 
300
                i++;
 
301
 
 
302
                if (arg[i].length() == 2) {
 
303
                    break;             // "--"
 
304
                }
 
305
 
 
306
                parameter = arg[i].substring(2).toLowerCase();
 
307
 
 
308
                if (parameter.equals("help")) {
 
309
                    exitMain(0, SYNTAX_MESSAGE);
 
310
 
 
311
                    return;
 
312
                } else if (parameter.equals("abortonerr")) {
 
313
                    if (coeOverride != null) {
 
314
                        exitMain(
 
315
                            0, "Switches '--abortOnErr' and "
 
316
                            + "'--continueOnErr' are mutually exclusive");
 
317
 
 
318
                        return;
 
319
                    }
 
320
 
 
321
                    coeOverride = Boolean.FALSE;
 
322
                } else if (parameter.equals("continueonerr")) {
 
323
                    if (coeOverride != null) {
 
324
                        exitMain(
 
325
                            0, "Switches '--abortOnErr' and "
 
326
                            + "'--continueOnErr' are mutually exclusive");
 
327
 
 
328
                        return;
 
329
                    }
 
330
 
 
331
                    coeOverride = Boolean.TRUE;
 
332
                } else if (parameter.equals("list")) {
 
333
                    listMode = true;
 
334
                } else if (parameter.equals("rcfile")) {
 
335
                    if (++i == arg.length) {
 
336
                        throw bcl;
 
337
                    }
 
338
 
 
339
                    rcFile = arg[i];
 
340
                } else if (parameter.equals("setvar")) {
 
341
                    if (++i == arg.length) {
 
342
                        throw bcl;
 
343
                    }
 
344
 
 
345
                    varSettings = arg[i];
 
346
                } else if (parameter.equals("sql")) {
 
347
                    noinput = true;    // but turn back on if file "-" specd.
 
348
 
 
349
                    if (++i == arg.length) {
 
350
                        throw bcl;
 
351
                    }
 
352
 
 
353
                    sqlText = arg[i];
 
354
 
 
355
                    if (sqlText.charAt(sqlText.length() - 1) != ';') {
 
356
                        sqlText += ";";
 
357
                    }
 
358
                } else if (parameter.equals("debug")) {
 
359
                    debug = true;
 
360
                } else if (parameter.equals("noautofile")) {
 
361
                    noautoFile = true;
 
362
                } else if (parameter.equals("autocommit")) {
 
363
                    autoCommit = true;
 
364
                } else if (parameter.equals("stdinput")) {
 
365
                    noinput          = false;
 
366
                    stdinputOverride = Boolean.TRUE;
 
367
                } else if (parameter.equals("noinput")) {
 
368
                    noinput          = true;
 
369
                    stdinputOverride = Boolean.FALSE;
 
370
                } else if (parameter.equals("driver")) {
 
371
                    if (++i == arg.length) {
 
372
                        throw bcl;
 
373
                    }
 
374
 
 
375
                    driver = arg[i];
 
376
                } else if (parameter.equals("inlinerc")) {
 
377
                    if (++i == arg.length) {
 
378
                        throw bcl;
 
379
                    }
 
380
 
 
381
                    rcParams = arg[i];
 
382
                } else {
 
383
                    throw bcl;
 
384
                }
 
385
            }
 
386
 
 
387
            if (!listMode) {
 
388
 
 
389
                // If an inline RC file was specified, don't worry about the targetDb
 
390
                if (rcParams == null) {
 
391
                    if (++i == arg.length) {
 
392
                        throw bcl;
 
393
                    }
 
394
 
 
395
                    targetDb = arg[i];
 
396
                }
 
397
            }
 
398
 
 
399
            int scriptIndex = 0;
 
400
 
 
401
            if (sqlText != null) {
 
402
                try {
 
403
                    tmpFile = File.createTempFile("sqltool-", ".sql");
 
404
 
 
405
                    //(new java.io.FileWriter(tmpFile)).write(sqlText);
 
406
                    java.io.FileWriter fw = new java.io.FileWriter(tmpFile);
 
407
 
 
408
                    fw.write("/* " + (new java.util.Date()) + ".  "
 
409
                             + SqlTool.class.getName()
 
410
                             + " command-line SQL. */\n\n");
 
411
                    fw.write(sqlText + '\n');
 
412
                    fw.flush();
 
413
                    fw.close();
 
414
                } catch (IOException ioe) {
 
415
                    exitMain(4, "Failed to write given sql to temp file: "
 
416
                             + ioe);
 
417
 
 
418
                    return;
 
419
                }
 
420
            }
 
421
 
 
422
            if (stdinputOverride != null) {
 
423
                noinput = !stdinputOverride.booleanValue();
 
424
            }
 
425
 
 
426
            interactive = (!noinput) && (arg.length <= i + 1);
 
427
 
 
428
            if (arg.length == i + 2 && arg[i + 1].equals("-")) {
 
429
                if (stdinputOverride == null) {
 
430
                    noinput = false;
 
431
                }
 
432
            } else if (arg.length > i + 1) {
 
433
 
 
434
                // I.e., if there are any SQL files specified.
 
435
                scriptFiles =
 
436
                    new File[arg.length - i - 1 + ((stdinputOverride == null ||!stdinputOverride.booleanValue()) ? 0
 
437
                                                                                                                 : 1)];
 
438
 
 
439
                if (debug) {
 
440
                    System.err.println("scriptFiles has "
 
441
                                       + scriptFiles.length + " elements");
 
442
                }
 
443
 
 
444
                while (i + 1 < arg.length) {
 
445
                    scriptFiles[scriptIndex++] = new File(arg[++i]);
 
446
                }
 
447
 
 
448
                if (stdinputOverride != null
 
449
                        && stdinputOverride.booleanValue()) {
 
450
                    scriptFiles[scriptIndex++] = null;
 
451
                    noinput                    = true;
 
452
                }
 
453
            }
 
454
        } catch (BadCmdline bcl) {
 
455
            exitMain(2, SYNTAX_MESSAGE);
 
456
 
 
457
            return;
 
458
        }
 
459
 
 
460
        RCData conData = null;
 
461
 
 
462
        // Use the inline RC file if it was specified        
 
463
        if (rcParams != null) {
 
464
            rcFields = new HashMap();
 
465
 
 
466
            try {
 
467
                varParser(rcParams, rcFields, true);
 
468
            } catch (SqlToolException e) {
 
469
                exitMain(24, e.getMessage());
 
470
            }
 
471
 
 
472
            try {
 
473
                rcUrl        = (String) rcFields.get("url");
 
474
                rcUsername   = (String) rcFields.get("user");
 
475
                rcDriver     = (String) rcFields.get("driver");
 
476
                rcCharset    = (String) rcFields.get("charset");
 
477
                rcTruststore = (String) rcFields.get("truststore");
 
478
                rcPassword   = promptForPassword(rcUsername);
 
479
                conData = new RCData(CMDLINE_ID, rcUrl, rcUsername,
 
480
                                     rcPassword, rcDriver, rcCharset,
 
481
                                     rcTruststore);
 
482
            } catch (SqlToolException e) {
 
483
                throw e;
 
484
            } catch (Exception e) {
 
485
                exitMain(1, "Invalid inline RC file specified: "
 
486
                         + e.getMessage());
 
487
 
 
488
                return;
 
489
            }
 
490
        } else {
 
491
            try {
 
492
                conData = new RCData(new File((rcFile == null)
 
493
                                              ? DEFAULT_RCFILE
 
494
                                              : rcFile), targetDb);
 
495
            } catch (Exception e) {
 
496
                exitMain(
 
497
                    1, "Failed to retrieve connection info for database '"
 
498
                    + targetDb + "': " + e.getMessage());
 
499
 
 
500
                return;
 
501
            }
 
502
        }
 
503
 
 
504
        if (listMode) {
 
505
            exitMain(0);
 
506
 
 
507
            return;
 
508
        }
 
509
 
 
510
        if (debug) {
 
511
            conData.report();
 
512
        }
 
513
 
 
514
        try {
 
515
            conn = conData.getConnection(
 
516
                driver, System.getProperty("sqlfile.charset"),
 
517
                System.getProperty("javax.net.ssl.trustStore"));
 
518
 
 
519
            conn.setAutoCommit(autoCommit);
 
520
 
 
521
            DatabaseMetaData md = null;
 
522
 
 
523
            if (interactive && (md = conn.getMetaData()) != null) {
 
524
                System.out.println("JDBC Connection established to a "
 
525
                                   + md.getDatabaseProductName() + " v. "
 
526
                                   + md.getDatabaseProductVersion()
 
527
                                   + " database as '" + md.getUserName()
 
528
                                   + "'.");
 
529
            }
 
530
        } catch (Exception e) {
 
531
            e.printStackTrace();
 
532
 
 
533
            // Let's not continue as if nothing is wrong.
 
534
            exitMain(10,
 
535
                     "Failed to get a connection to " + conData.url + " as "
 
536
                     + conData.username + ".  " + e.getMessage());
 
537
 
 
538
            return;
 
539
        }
 
540
 
 
541
        File[] emptyFileArray      = {};
 
542
        File[] singleNullFileArray = { null };
 
543
        File   autoFile            = null;
 
544
 
 
545
        if (interactive &&!noautoFile) {
 
546
            autoFile = new File(System.getProperty("user.home")
 
547
                                + "/auto.sql");
 
548
 
 
549
            if ((!autoFile.isFile()) ||!autoFile.canRead()) {
 
550
                autoFile = null;
 
551
            }
 
552
        }
 
553
 
 
554
        if (scriptFiles == null) {
 
555
 
 
556
            // I.e., if no SQL files given on command-line.
 
557
            // Input file list is either nothing or {null} to read stdin.
 
558
            scriptFiles = (noinput ? emptyFileArray
 
559
                                   : singleNullFileArray);
 
560
        }
 
561
 
 
562
        int numFiles = scriptFiles.length;
 
563
 
 
564
        if (tmpFile != null) {
 
565
            numFiles += 1;
 
566
        }
 
567
 
 
568
        if (autoFile != null) {
 
569
            numFiles += 1;
 
570
        }
 
571
 
 
572
        SqlFile[] sqlFiles = new SqlFile[numFiles];
 
573
        HashMap   userVars = new HashMap();
 
574
 
 
575
        if (varSettings != null) {
 
576
            varParser(varSettings, userVars, false);
 
577
        }
 
578
 
 
579
        // We print version before execing this one.
 
580
        int interactiveFileIndex = -1;
 
581
 
 
582
        try {
 
583
            int fileIndex = 0;
 
584
 
 
585
            if (autoFile != null) {
 
586
                sqlFiles[fileIndex++] = new SqlFile(autoFile, false,
 
587
                                                    userVars);
 
588
            }
 
589
 
 
590
            if (tmpFile != null) {
 
591
                sqlFiles[fileIndex++] = new SqlFile(tmpFile, false, userVars);
 
592
            }
 
593
 
 
594
            for (int j = 0; j < scriptFiles.length; j++) {
 
595
                if (interactiveFileIndex < 0 && interactive) {
 
596
                    interactiveFileIndex = fileIndex;
 
597
                }
 
598
 
 
599
                sqlFiles[fileIndex++] = new SqlFile(scriptFiles[j],
 
600
                                                    interactive, userVars);
 
601
            }
 
602
        } catch (IOException ioe) {
 
603
            try {
 
604
                conn.close();
 
605
            } catch (Exception e) {}
 
606
 
 
607
            exitMain(2, ioe.getMessage());
 
608
 
 
609
            return;
 
610
        }
 
611
 
 
612
        int retval = 0;    // Value we will return via System.exit().
 
613
 
 
614
        try {
 
615
            for (int j = 0; j < sqlFiles.length; j++) {
 
616
                if (j == interactiveFileIndex) {
 
617
                    System.out.print("SqlTool v. " + revnum
 
618
                                     + ".                        ");
 
619
                }
 
620
 
 
621
                sqlFiles[j].execute(conn, coeOverride);
 
622
            }
 
623
        } catch (IOException ioe) {
 
624
            System.err.println("Failed to execute SQL:  " + ioe.getMessage());
 
625
 
 
626
            retval = 3;
 
627
 
 
628
            // These two Exception types are handled properly inside of SqlFile.
 
629
            // We just need to return an appropriate error status.
 
630
        } catch (SqlToolError ste) {
 
631
            retval = 2;
 
632
 
 
633
            // Should not be handling SQLExceptions here!  SqlFile should handle
 
634
            // them.
 
635
        } catch (SQLException se) {
 
636
            retval = 1;
 
637
        } finally {
 
638
            try {
 
639
                conn.close();
 
640
            } catch (Exception e) {}
 
641
        }
 
642
 
 
643
        // Taking file removal out of final block because this is good debug
 
644
        // info to keep around if the program aborts.
 
645
        if (tmpFile != null &&!tmpFile.delete()) {
 
646
            System.err.println(
 
647
                "Error occurred while trying to remove temp file '" + tmpFile
 
648
                + "'");
 
649
        }
 
650
 
 
651
        exitMain(retval);
 
652
 
 
653
        return;
 
654
    }
 
655
}