jdbcclient.java
来自「这个是内存数据库的客户端」· Java 代码 · 共 1,234 行 · 第 1/3 页
JAVA
1,234 行
/* * The contents of this file are subject to the MonetDB Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://monetdb.cwi.nl/Legal/MonetDBLicense-1.1.html * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the * License for the specific language governing rights and limitations * under the License. * * The Original Code is the MonetDB Database System. * * The Initial Developer of the Original Code is CWI. * Portions created by CWI are Copyright (C) 1997-2007 CWI. * All Rights Reserved. */package nl.cwi.monetdb.client;import nl.cwi.monetdb.client.util.*;import java.sql.*;import java.io.*;import java.util.*;import java.util.zip.*;import java.net.*;/** * This program acts like an extended client program for MonetDB. Its * look and feel is very much like PostgreSQL's interactive terminal * program. Although it looks like this client is designed for MonetDB, * it demonstrates the power of the JDBC interface since it built on top * of JDBC only. * * @author Fabian Groffen <Fabian.Groffen@cwi.nl> * @version 1.2 */public class JdbcClient { private static Connection con; private static Statement stmt; private static BufferedReader in; private static PrintWriter out; private static Exporter e; private static DatabaseMetaData dbmd; public final static void main(String[] args) throws Exception { CmdLineOpts copts = new CmdLineOpts(); // arguments which take exactly one argument copts.addOption("h", "host", CmdLineOpts.CAR_ONE, "localhost", "The hostname of the host that runs the MonetDB database. " + "A port number can be supplied by use of a colon, i.e. " + "-h somehost:12345."); copts.addOption("p", "port", CmdLineOpts.CAR_ONE, "50000", "The port number to connect to."); // todo make it CAR_ONE_MANY copts.addOption("f", "file", CmdLineOpts.CAR_ONE, null, "A file name to use either for reading or writing. The " + "file will be used for writing when dump mode is used " + "(-d --dump). In read mode, the file can also be an URL " + "pointing to a plain text file that is optionally gzip " + "compressed."); copts.addOption("u", "user", CmdLineOpts.CAR_ONE, System.getProperty("user.name"), "The username to use when connecting to the database."); // this one is only here for the .monetdb file parsing, it is // removed before the command line arguments are parsed copts.addOption(null, "password", CmdLineOpts.CAR_ONE, null, null); copts.addOption("d", "database", CmdLineOpts.CAR_ONE, "demo", "Try to connect to the given database (only makes sense " + "if connecting to a DatabasePool, M5 or equivalent process)."); copts.addOption("l", "language", CmdLineOpts.CAR_ONE, "sql", "Use the given language, for example 'xquery'."); // arguments which have no argument(s) copts.addOption(null, "help", CmdLineOpts.CAR_ZERO, null, "This help screen."); copts.addOption(null, "version", CmdLineOpts.CAR_ZERO, null, "Display driver version and exit."); copts.addOption("e", "echo", CmdLineOpts.CAR_ZERO, null, "Also outputs the contents of the input file, if any."); copts.addOption("q", "quiet", CmdLineOpts.CAR_ZERO, null, "Suppress printing the welcome header."); // arguments which have zero to many arguments copts.addOption("D", "dump", CmdLineOpts.CAR_ZERO_MANY, null, "Dumps the given table(s), or the complete database if " + "none given."); // extended options copts.addOption(null, "Xblksize", CmdLineOpts.CAR_ONE, null, "Specifies the blocksize when using block mode, given " + "in bytes."); copts.addOption(null, "Xoutput", CmdLineOpts.CAR_ONE, null, "The output mode when dumping. Default is sql, xml may " + "be used for an experimental XML output."); copts.addOption(null, "Xembedded", CmdLineOpts.CAR_ONE, null, "Uses an \"embedded\" Mserver instance. The argument to " + "this option should be in the form of path/to/mserver:dbname" + "[:dbfarm[:dbinit]]."); copts.addOption(null, "Xhash", CmdLineOpts.CAR_ONE, null, "Use the given hash algorithm during challenge response. " + "Supported algorithm names: SHA1, MD5, plain."); // arguments which can have zero or one argument(s) copts.addOption(null, "Xdebug", CmdLineOpts.CAR_ZERO_ONE, null, "Writes a transmission log to disk for debugging purposes. " + "If a file name is given, it is used, otherwise a file " + "called monet<timestamp>.log is created. A given file " + "never be overwritten; instead a unique variation of the " + "file is used."); copts.addOption(null, "Xbatching", CmdLineOpts.CAR_ZERO_ONE, null, "Indicates that a batch should be used instead of direct " + "communication with the server for each statement. If a " + "number is given, it is used as batch size. i.e. 8000 " + "would execute the contents on the batch after each 8000 " + "statements read. Batching can greatly speedup the " + "process of restoring a database dump."); // we store user and password in separate variables in order to // be able to properly act on them like forgetting the password // from the user's file if the user supplies a username on the // command line arguments String pass = null; String user = null; // look for a file called .monetdb in the current dir or in the // user's homedir and read its preferences File pref = new File(".monetdb"); if (!pref.exists()) pref = new File(System.getProperty("user.home"), ".monetdb"); if (pref.exists()) { try { copts.processFile(pref); } catch (OptionsException e) { System.err.println("Error in " + pref.getAbsolutePath() + ": " + e.getMessage()); System.exit(-1); } user = copts.getOption("user").getArgument(); pass = copts.getOption("password").getArgument(); } // process the command line arguments, remove password option // first, and save the user we had at this point copts.removeOption("password"); try { copts.processArgs(args); } catch (OptionsException e) { System.err.println("Error: " + e.getMessage()); System.exit(-1); } // we can actually compare pointers (objects) here if (user != copts.getOption("user").getArgument()) pass = null; if (copts.getOption("help").isPresent()) { System.out.print("Usage java -jar jdbcclient-X.Y.jar\n" +" [-h host[:port]] [-p port] [-f file] [-u user]\n" +" [-l language] [-b database] [-e] [-d [table]]\n" +" [-X<opt>]\n" +"or using long option equivalents --host --port --file --user --language\n" +"--dump --echo --database.\n" +"Arguments may be written directly after the option like -p50000.\n" +"\n" +"If no host and port are given, localhost and 50000 are assumed. An .monetdb\n" +"file may exist in the user's home directory. This file can contain\n" +"preferences to use each time JdbcClient is started. Options given on the\n" +"command line override the preferences file. The .monetdb file syntax is\n" +"<option>=<value> where option is one of the options host, port, file, mode\n" +"debug, or password. Note that the last one is perilous and therefore not\n" +"available as command line option.\n" +"If no input file is given using the -f flag, an interactive session is\n" +"started on the terminal.\n" +"\n" +"OPTIONS\n" +copts.produceHelpMessage()); System.exit(0); } else if (copts.getOption("version").isPresent()) { // We cannot use the DatabaseMetaData here, because we // cannot get a Connection. So instead, we just get the // values we want out of the Driver directly. System.out.println("Driver: " + nl.cwi.monetdb.jdbc.MonetDriver.getDriverVersion()); System.exit(0); } in = new BufferedReader(new InputStreamReader(System.in)); out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out))); // whether the semi-colon at the end of a String terminates the // query or not (default = yes => SQL) boolean scolonterm = true; boolean xmlMode = "xml".equals(copts.getOption("Xoutput").getArgument()); boolean isEmbedded = copts.getOption("Xembedded").isPresent(); if (isEmbedded) { // user and password don't matter for embedded pass = ""; } // we need the password from the user, fetch it with a pseudo // password protector if (pass == null) { try { char[] tmp = PasswordField.getPassword(System.in, "password: "); if (tmp != null) pass = String.valueOf(tmp); } catch (IOException ioe) { System.err.println("Invalid password!"); System.exit(-1); } System.out.println(""); } user = copts.getOption("user").getArgument(); // build the hostname String host = copts.getOption("host").getArgument(); if (host.indexOf(":") == -1) { host = host + ":" + copts.getOption("port").getArgument(); } // make sure the driver is loaded Class.forName("nl.cwi.monetdb.jdbc.MonetDriver"); // build the extra arguments of the JDBC connect string String attr = "?"; CmdLineOpts.OptionContainer oc = copts.getOption("Xblksize"); if (oc.isPresent()) attr += "blockmode_blocksize=" + oc.getArgument() + "&"; oc = copts.getOption("language"); String lang = oc.getArgument(); if (oc.isPresent()) attr += "language=" + lang + "&"; // set some behaviour based on the language XQuery if (lang.equals("xquery")) { scolonterm = false; // no ; to end a statement if (!copts.getOption("Xoutput").isPresent()) xmlMode = true; // the user will like xml results, most probably } oc = copts.getOption("Xdebug"); if (oc.isPresent()) { attr += "debug=true&"; if (oc.getArgumentCount() == 1) attr += "logfile=" + oc.getArgument() + "&"; } oc = copts.getOption("Xhash"); if (oc.isPresent()) attr += "hash=" + oc.getArgument() + "&"; // request a connection suitable for MonetDB from the driver // manager note that the database specifier is only used when // connecting to a proxy-like service, since MonetDB itself // can't access multiple databases. con = null; String database = copts.getOption("database").getArgument(); try { if (!isEmbedded) { con = DriverManager.getConnection( "jdbc:monetdb://" + host + "/" + database + attr, user, pass ); } else { String[] eargs = copts.getOption("Xembedded").getArgument().split(":"); if (eargs.length < 2 || eargs.length > 4) { System.err.println("Expecting path/to/mserver:dbname" + "[:dbfarm[:dbinit]]"); System.exit(-1); } Properties p = new Properties(); p.setProperty("executable", eargs[0]); p.setProperty("dbname", eargs[1]); if (args.length > 2) p.setProperty("dbfarm", eargs[2]); if (args.length > 3) p.setProperty("dbinit", eargs[3]); nl.cwi.monetdb.jdbc.MonetConnection.setEmbeddedProperties(p); // what do you mean? "descriptive names suck" ?!? out.println("Starting embedded instance..."); out.flush(); con = nl.cwi.monetdb.jdbc.MonetConnection.getEmbeddedInstanceConnection(); } SQLWarning warn = con.getWarnings(); while (warn != null) { System.err.println("Connection warning: " + warn.getMessage()); warn = warn.getNextWarning(); } con.clearWarnings(); } catch (SQLException e) { System.err.println("Database connect failed: " + e.getMessage()); System.exit(-1); } try { dbmd = con.getMetaData(); } catch (SQLException e) { // we ignore this because it's probably because we don't use // SQL language dbmd = null; } stmt = con.createStatement(); // see if we will have to perform a database dump (only in SQL // mode) if ("sql".equals(lang) && copts.getOption("dump").isPresent()) { ResultSet tbl; // use the given file for writing oc = copts.getOption("file"); if (oc.isPresent()) out = new PrintWriter(new BufferedWriter(new FileWriter(oc.getArgument()))); // we only want tables and views to be dumped, unless a specific // table is requested String[] types = {"TABLE", "VIEW"}; if (copts.getOption("dump").getArgumentCount() > 0) types = null; // request the tables available in the database tbl = dbmd.getTables(null, null, null, types); LinkedList tables = new LinkedList(); while (tbl.next()) { tables.add(new Table( tbl.getString("TABLE_CAT"), tbl.getString("TABLE_SCHEM"), tbl.getString("TABLE_NAME"), tbl.getString("TABLE_TYPE"))); } if (xmlMode) { e = new XMLExporter(out); e.setProperty(XMLExporter.TYPE_NIL, XMLExporter.VALUE_XSI); } else { e = new SQLExporter(out); // stick with inserts for now, in the future we might do // COPY INTO's here using VALUE_COPY e.setProperty(SQLExporter.TYPE_OUTPUT, SQLExporter.VALUE_INSERT); } e.useSchemas(true); // start SQL output if (!xmlMode) out.println("START TRANSACTION;\n"); // dump specific table(s) or not? if (copts.getOption("dump").getArgumentCount() > 0) { // yes we do String[] dumpers = copts.getOption("dump").getArguments(); for (int i = 0; i < tables.size(); i++) { Table ttmp = (Table)(tables.get(i)); for (int j = 0; j < dumpers.length; j++) { if (ttmp.getName().equalsIgnoreCase(dumpers[j].toString()) || ttmp.getFqname().equalsIgnoreCase(dumpers[j].toString())) { // dump the table doDump(out, ttmp, dbmd, stmt); } } } } else { tbl = dbmd.getImportedKeys(null, null, null); while (tbl.next()) { // find FK table object Table fk = Table.findTable(tbl.getString("FKTABLE_SCHEM") + "." + tbl.getString("FKTABLE_NAME"), tables); // find PK table object Table pk = Table.findTable(tbl.getString("PKTABLE_SCHEM") + "." + tbl.getString("PKTABLE_NAME"), tables); // should not be possible to happen if (fk == null || pk == null) throw new AssertionError("Illegal table; table not found in list"); // add PK table dependancy to FK table fk.addDependancy(pk); } // search for cycles of type a -> (x ->)+ b probably not // the most optimal way, but it works by just scanning // every table for loops in a recursive manor for (int i = 0; i < tables.size(); i++) { Table.checkForLoop((Table)(tables.get(i)), new ArrayList()); } // find the graph, at this point we know there are no // cycles, thus a solution exists for (int i = 0; i < tables.size(); i++) { List needs = ((Table)(tables.get(i))).requires(tables.subList(0, i + 1)); if (needs.size() > 0) { tables.removeAll(needs); tables.addAll(i, needs); // re-evaluate this position, for there is a new // table now i--; } } // we now have the right order to dump tables for (int i = 0; i < tables.size(); i++) { // dump the table Table t = (Table)(tables.get(i)); doDump(out, t, dbmd, stmt); } } if (!xmlMode) out.println("COMMIT;"); out.flush(); con.close(); System.exit(0); } if (xmlMode) { e = new XMLExporter(out); e.setProperty(XMLExporter.TYPE_NIL, XMLExporter.VALUE_XSI); } else { e = new SQLExporter(out);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?