comparison src/main/java/nl/cwi/monetdb/client/JdbcClient.java @ 172:60063c67f9e7 embedded

Merged with default
author Pedro Ferreira <pedro.ferreira@monetdbsolutions.com>
date Tue, 19 Sep 2017 13:49:34 +0200 (2017-09-19)
parents 08bc9009d190 296c4a16ef9f
children 89c285fc0a49
comparison
equal deleted inserted replaced
171:0f95fee3cf29 172:60063c67f9e7
52 */ 52 */
53 53
54 public final class JdbcClient { 54 public final class JdbcClient {
55 55
56 private static Connection con; 56 private static Connection con;
57 private static DatabaseMetaData dbmd;
57 private static Statement stmt; 58 private static Statement stmt;
58 private static BufferedReader in; 59 private static BufferedReader in;
59 private static PrintWriter out; 60 private static PrintWriter out;
60 private static Exporter exporter; 61 private static Exporter exporter;
61 private static DatabaseMetaData dbmd;
62 62
63 public static void main(String[] args) throws Exception { 63 public static void main(String[] args) throws Exception {
64 CmdLineOpts copts = new CmdLineOpts(); 64 CmdLineOpts copts = new CmdLineOpts();
65 65
66 // arguments which take exactly one argument 66 // arguments which take exactly one argument
156 } catch (OptionsException e) { 156 } catch (OptionsException e) {
157 System.err.println("Error: " + e.getMessage()); 157 System.err.println("Error: " + e.getMessage());
158 System.exit(1); 158 System.exit(1);
159 } 159 }
160 // we can actually compare pointers (objects) here 160 // we can actually compare pointers (objects) here
161 if (!Objects.equals(user, copts.getOption("user").getArgument())) pass = null; 161 if (user != copts.getOption("user").getArgument())
162 pass = null;
162 163
163 if (copts.getOption("help").isPresent()) { 164 if (copts.getOption("help").isPresent()) {
164 System.out.print( 165 System.out.print(
165 "Usage java -jar jdbcclient.jar\n" + 166 "Usage java -jar jdbcclient.jar\n" +
166 " [-h host[:port]] [-p port] [-f file] [-u user]\n" + 167 "\t\t[-h host[:port]] [-p port] [-f file] [-u user]\n" +
167 " [-l language] [-d database] [-e] [-D [table]]\n" + 168 "\t\t[-l language] [-d database] [-e] [-D [table]]\n" +
168 " [-X<opt>]\n" + 169 "\t\t[-X<opt>]\n" +
169 "or using long option equivalents --host --port --file --user --language\n" + 170 "or using long option equivalents --host --port --file --user --language\n" +
170 "--dump --echo --database.\n" + 171 "--dump --echo --database.\n" +
171 "Arguments may be written directly after the option like -p50000.\n" + 172 "Arguments may be written directly after the option like -p50000.\n" +
172 "\n" + 173 "\n" +
173 "If no host and port are given, localhost and 50000 are assumed. An .monetdb\n" + 174 "If no host and port are given, localhost and 50000 are assumed.\n" +
174 "file may exist in the user's home directory. This file can contain\n" + 175 "An .monetdb file may exist in the user's home directory. This file can contain\n" +
175 "preferences to use each time JdbcClient is started. Options given on the\n" + 176 "preferences to use each time JdbcClient is started. Options given on the\n" +
176 "command line override the preferences file. The .monetdb file syntax is\n" + 177 "command line override the preferences file. The .monetdb file syntax is\n" +
177 "<option>=<value> where option is one of the options host, port, file, mode\n" + 178 "<option>=<value> where option is one of the options host, port, file, mode\n" +
178 "debug, or password. Note that the last one is perilous and therefore not\n" + 179 "debug, or password. Note that the last one is perilous and therefore not\n" +
179 "available as command line option.\n" + 180 "available as command line option.\n" +
217 String host = copts.getOption("host").getArgument(); 218 String host = copts.getOption("host").getArgument();
218 if (!host.contains(":")) { 219 if (!host.contains(":")) {
219 host = host + ":" + copts.getOption("port").getArgument(); 220 host = host + ":" + copts.getOption("port").getArgument();
220 } 221 }
221 222
222 //Class.forName("nl.cwi.monetdb.jdbc.MonetDriver"); 223 // make sure the driver is loaded
224 // Class.forName("nl.cwi.monetdb.jdbc.MonetDriver"); // not needed anymore for self registering JDBC drivers
223 225
224 // build the extra arguments of the JDBC connect string 226 // build the extra arguments of the JDBC connect string
225 String attr = "?"; 227 String attr = "?";
226 CmdLineOpts.OptionContainer oc = copts.getOption("language"); 228 CmdLineOpts.OptionContainer oc = copts.getOption("language");
227 String lang = oc.getArgument(); 229 String lang = oc.getArgument();
228 if (oc.isPresent()) 230 if (oc.isPresent())
229 attr += "language=" + lang + "&"; 231 attr += "language=" + lang + "&";
232
233 /* Xquery is no longer functional or supported
230 // set some behaviour based on the language XQuery 234 // set some behaviour based on the language XQuery
231 if (lang.equals("xquery")) { 235 if (lang.equals("xquery")) {
232 scolonterm = false; // no ; to end a statement 236 scolonterm = false; // no ; to end a statement
233 if (!copts.getOption("Xoutput").isPresent()) 237 if (!copts.getOption("Xoutput").isPresent())
234 xmlMode = true; // the user will like xml results, most probably 238 xmlMode = true; // the user will like xml results, most probably
235 } 239 }
240 */
236 oc = copts.getOption("Xdebug"); 241 oc = copts.getOption("Xdebug");
237 if (oc.isPresent()) { 242 if (oc.isPresent()) {
238 attr += "debug=true&"; 243 attr += "debug=true&";
239 if (oc.getArgumentCount() == 1) 244 if (oc.getArgumentCount() == 1)
240 attr += "logfile=" + oc.getArgument() + "&"; 245 attr += "logfile=" + oc.getArgument() + "&";
271 } catch (SQLException e) { 276 } catch (SQLException e) {
272 // we ignore this because it's probably because we don't use 277 // we ignore this because it's probably because we don't use
273 // SQL language 278 // SQL language
274 dbmd = null; 279 dbmd = null;
275 } 280 }
281
276 stmt = con.createStatement(); 282 stmt = con.createStatement();
277 283
278 // see if we will have to perform a database dump (only in SQL mode) 284 // see if we will have to perform a database dump (only in SQL mode)
279 if ("sql".equals(lang) && copts.getOption("dump").isPresent()) { 285 if ("sql".equals(lang) && copts.getOption("dump").isPresent()) {
280 ResultSet tbl; 286 ResultSet tbl;
293 tbl = dbmd.getTables(null, con.getSchema(), null, types); 299 tbl = dbmd.getTables(null, con.getSchema(), null, types);
294 300
295 List<Table> tables = new LinkedList<>(); 301 List<Table> tables = new LinkedList<>();
296 while (tbl.next()) { 302 while (tbl.next()) {
297 tables.add(new Table( 303 tables.add(new Table(
298 tbl.getString("TABLE_SCHEM"), 304 tbl.getString(2), // 2 = "TABLE_SCHEM"
299 tbl.getString("TABLE_NAME"), 305 tbl.getString(3), // 3 = "TABLE_NAME"
300 tbl.getString("TABLE_TYPE"))); 306 tbl.getString(4))); // 4 = "TABLE_TYPE"
301 } 307 }
302 tbl.close(); 308 tbl.close();
303 309
304 if (xmlMode) { 310 if (xmlMode) {
305 exporter = new XMLExporter(out); 311 exporter = new XMLExporter(out);
327 doDump(out, ttmp); 333 doDump(out, ttmp);
328 } 334 }
329 } 335 }
330 } 336 }
331 } else { 337 } else {
332 /* this returns everything, so including SYSTEM TABLE 338 /* this returns everything, so including SYSTEM TABLE constraints */
333 * constraints */
334 tbl = dbmd.getImportedKeys(null, null, null); 339 tbl = dbmd.getImportedKeys(null, null, null);
335 while (tbl.next()) { 340 while (tbl.next()) {
336 // find FK table object 341 // find FK table object 6 = "FKTABLE_SCHEM", 7 = "FKTABLE_NAME"
337 Table fk = Table.findTable(tbl.getString("FKTABLE_SCHEM") + "." + tbl.getString("FKTABLE_NAME"), tables); 342 Table fk = Table.findTable(tbl.getString(6) + "." + tbl.getString(7), tables);
338 343
339 // find PK table object 344 // find PK table object 2 = "PKTABLE_SCHEM", 3 = "PKTABLE_NAME"
340 Table pk = Table.findTable(tbl.getString("PKTABLE_SCHEM") + "." + tbl.getString("PKTABLE_NAME"), tables); 345 Table pk = Table.findTable(tbl.getString(2) + "." + tbl.getString(3), tables);
341 346
342 // this happens when a system table has referential 347 // this happens when a system table has referential constraints
343 // constraints
344 if (fk == null || pk == null) 348 if (fk == null || pk == null)
345 continue; 349 continue;
346 350
347 // add PK table dependancy to FK table 351 // add PK table dependency to FK table
348 fk.addDependancy(pk); 352 fk.addDependency(pk);
349 } 353 }
350 tbl.close(); 354 tbl.close();
351 355
352 // search for cycles of type a -> (x ->)+ b probably not 356 // search for cycles of type a -> (x ->)+ b probably not
353 // the most optimal way, but it works by just scanning 357 // the most optimal way, but it works by just scanning
471 * assigned BufferedReader, or throws an Exception if the given 475 * assigned BufferedReader, or throws an Exception if the given
472 * string couldn't be identified as a valid URL or file. 476 * string couldn't be identified as a valid URL or file.
473 * 477 *
474 * @param uri URL or filename as String 478 * @param uri URL or filename as String
475 * @return a BufferedReader for the uri 479 * @return a BufferedReader for the uri
476 * @throws Exception if uri cannot be identified as a valid URL or 480 * @throws Exception if uri cannot be identified as a valid URL or file
477 * file
478 */ 481 */
479 static BufferedReader getReader(String uri) throws Exception { 482 static BufferedReader getReader(String uri) throws Exception {
480 BufferedReader ret = null; 483 BufferedReader ret = null;
481 URL u = null; 484 URL u = null;
482 485
609 } else if (command.startsWith("\\h")) { 612 } else if (command.startsWith("\\h")) {
610 out.println("Available commands:"); 613 out.println("Available commands:");
611 out.println("\\q quits this program"); 614 out.println("\\q quits this program");
612 out.println("\\h this help screen"); 615 out.println("\\h this help screen");
613 if (dbmd != null) 616 if (dbmd != null)
614 out.println("\\d list available tables and views"); 617 out.println("\\d list available tables and views in current schema");
615 out.println("\\d<obj> describes the given table or view"); 618 out.println("\\d<obj> describes the given table or view");
616 out.println("\\l<uri> executes the contents of the given file or URL"); 619 out.println("\\l<uri> executes the contents of the given file or URL");
617 out.println("\\i<uri> batch executes the inserts from the given file or URL"); 620 out.println("\\i<uri> batch executes the inserts from the given file or URL");
618 } else if (dbmd != null && command.startsWith("\\d")) { 621 } else if (dbmd != null && command.startsWith("\\d")) {
622 ResultSet tbl = null;
619 try { 623 try {
620 String object = command.substring(2).trim().toLowerCase(); 624 String object = command.substring(2).trim();
621 if (scolonterm && object.endsWith(";")) 625 if (scolonterm && object.endsWith(";"))
622 object = object.substring(0, object.length() - 1); 626 object = object.substring(0, object.length() - 1);
623 if (!object.isEmpty()) { 627 if (object.isEmpty()) {
628 // list available tables and views in current schema
629 String current_schema = con.getSchema();
630 tbl = dbmd.getTables(null, current_schema, null, null);
631
632 // give us a list of all non-system tables and views (including temp ones)
633 while (tbl.next()) {
634 String tableType = tbl.getString(4); // 4 = "TABLE_TYPE"
635 if (tableType != null && !tableType.startsWith("SYSTEM "))
636 out.println(tableType + "\t" +
637 tbl.getString(2) + "." + // 2 = "TABLE_SCHEM"
638 tbl.getString(3)); // 3 = "TABLE_NAME"
639 }
640 tbl.close();
641 tbl = null;
642 } else {
643 // describes the given table or view
624 String schema; 644 String schema;
645 String obj_nm = object;
646 boolean found = false;
625 int dot = object.indexOf("."); 647 int dot = object.indexOf(".");
626 if (dot != -1) { 648 if (dot > 0) {
627 // use provided schema 649 // use specified schema
628 schema = object.substring(0, dot); 650 schema = object.substring(0, dot);
629 object = object.substring(dot + 1); 651 obj_nm = object.substring(dot + 1);
630 } else { 652 } else {
631 // use current schema 653 // use current schema
632 schema = con.getSchema(); 654 schema = con.getSchema();
633 } 655 }
634 ResultSet tbl = dbmd.getTables(null, schema, null, null); 656 tbl = dbmd.getTables(null, schema, obj_nm, null);
635 657 while (tbl.next() && !found) {
636 // we have an object, see if we can find it 658 String tableName = tbl.getString(3); // 3 = "TABLE_NAME"
637 boolean found = false; 659 String schemaName = tbl.getString(2); // 2 = "TABLE_SCHEM"
638 while (tbl.next()) { 660 if (obj_nm.equals(tableName) && schema.equals(schemaName)) {
639 String tableName = tbl.getString("TABLE_NAME");
640 String schemaName = tbl.getString("TABLE_SCHEM");
641 if ((dot == -1 && tableName.equalsIgnoreCase(object)) ||
642 (schemaName + "." + tableName).equalsIgnoreCase(object))
643 {
644 // we found it, describe it 661 // we found it, describe it
645 exporter.dumpSchema(dbmd, 662 exporter.dumpSchema(dbmd,
646 tbl.getString("TABLE_TYPE"), 663 tbl.getString(4), // 4 = "TABLE_TYPE"
647 tbl.getString("TABLE_CAT"), 664 tbl.getString(1), // 1 = "TABLE_CAT"
648 schemaName, 665 schemaName,
649 tableName); 666 tableName);
650 667
651 found = true; 668 found = true;
652 break; 669 break;
653 } 670 }
654 } 671 }
655 tbl.close(); 672 tbl.close();
673 tbl = null;
656 674
657 if (!found) 675 if (!found)
658 System.err.println("Unknown table or view: " + object); 676 System.err.println("Unknown table or view: " + object);
659 } else {
660 String current_schema = con.getSchema();
661 ResultSet tbl = dbmd.getTables(null, current_schema, null, null);
662
663 // give us a list of all non-system tables and views (including temp ones)
664 while (tbl.next()) {
665 String tableType = tbl.getString("TABLE_TYPE");
666 if (tableType != null && !tableType.startsWith("SYSTEM "))
667 out.println(tableType + "\t" +
668 tbl.getString("TABLE_SCHEM") + "." +
669 tbl.getString("TABLE_NAME"));
670 }
671 tbl.close();
672 } 677 }
673 } catch (SQLException e) { 678 } catch (SQLException e) {
674 out.flush(); 679 out.flush();
675 do { 680 do {
676 System.err.println("Error [" + e.getSQLState() + "]: " + e.getMessage()); 681 System.err.println("Error [" + e.getSQLState() + "]: " + e.getMessage());
677 // print all error messages in the chain (if any) 682 // print all error messages in the chain (if any)
678 } while ((e = e.getNextException()) != null); 683 } while ((e = e.getNextException()) != null);
684 } finally {
685 if (tbl != null)
686 tbl.close();
679 } 687 }
680 } else if (command.startsWith("\\l") || command.startsWith("\\i")) { 688 } else if (command.startsWith("\\l") || command.startsWith("\\i")) {
681 String object = command.substring(2).trim(); 689 String object = command.substring(2).trim();
682 if (scolonterm && object.endsWith(";")) 690 if (scolonterm && object.endsWith(";"))
683 object = object.substring(0, object.length() - 1); 691 object = object.substring(0, object.length() - 1);
843 * loop above, queries are sent only to the database if a certain batch 851 * loop above, queries are sent only to the database if a certain batch
844 * amount is reached. No client side query checks are made, but everything 852 * amount is reached. No client side query checks are made, but everything
845 * is sent to the server as-is. 853 * is sent to the server as-is.
846 * 854 *
847 * @param batchSize the number of items to store in the batch before 855 * @param batchSize the number of items to store in the batch before
848 * sending them to the database for execution. 856 * sending them to the database for execution.
849 * @throws IOException if an IO exception occurs. 857 * @throws IOException if an IO exception occurs.
850 */ 858 */
851 public static void processBatch(int batchSize) throws IOException { 859 public static void processBatch(int batchSize) throws IOException {
852 StringBuilder query = new StringBuilder(); 860 StringBuilder query = new StringBuilder();
853 String curLine; 861 String curLine;
1118 this.name = name; 1126 this.name = name;
1119 this.type = type; 1127 this.type = type;
1120 this.fqname = schem + "." + name; 1128 this.fqname = schem + "." + name;
1121 } 1129 }
1122 1130
1123 void addDependancy(Table dependsOn) throws Exception { 1131 void addDependency(Table dependsOn) throws Exception {
1124 if (this.fqname.equals(dependsOn.fqname)) 1132 if (this.fqname.equals(dependsOn.fqname))
1125 throw new Exception("Cyclic dependancy graphs are not supported (foreign key relation references self)"); 1133 throw new Exception("Cyclic dependency graphs are not supported (foreign key relation references self)");
1126 1134
1127 if (dependsOn.needs.contains(this)) 1135 if (dependsOn.needs.contains(this))
1128 throw new Exception("Cyclic dependancy graphs are not supported (foreign key relation a->b and b->a)"); 1136 throw new Exception("Cyclic dependency graphs are not supported (foreign key relation a->b and b->a)");
1129 1137
1130 if (!needs.contains(dependsOn)) 1138 if (!needs.contains(dependsOn))
1131 needs.add(dependsOn); 1139 needs.add(dependsOn);
1132 } 1140 }
1133 1141
1146 1154
1147 String getSchem() { 1155 String getSchem() {
1148 return schem; 1156 return schem;
1149 } 1157 }
1150 1158
1151 String getSchemQ() {
1152 return JdbcClient.dq(schem);
1153 }
1154
1155 String getName() { 1159 String getName() {
1156 return name; 1160 return name;
1157 } 1161 }
1158 1162
1159 String getNameQ() {
1160 return JdbcClient.dq(name);
1161 }
1162
1163 String getType() { 1163 String getType() {
1164 return type; 1164 return type;
1165 } 1165 }
1166 1166
1167 String getFqname() { 1167 String getFqname() {
1168 return fqname; 1168 return fqname;
1169 } 1169 }
1170 1170
1171 String getFqnameQ() { 1171 String getFqnameQ() {
1172 return getSchemQ() + "." + getNameQ(); 1172 return JdbcClient.dq(schem) + "." + JdbcClient.dq(name);
1173 } 1173 }
1174 1174
1175 public String toString() { 1175 public String toString() {
1176 return fqname; 1176 return fqname;
1177 } 1177 }
1188 static void checkForLoop(Table table, List<Table> parents) throws Exception { 1188 static void checkForLoop(Table table, List<Table> parents) throws Exception {
1189 parents.add(table); 1189 parents.add(table);
1190 for (int i = 0; i < table.needs.size(); i++) { 1190 for (int i = 0; i < table.needs.size(); i++) {
1191 Table child = table.needs.get(i); 1191 Table child = table.needs.get(i);
1192 if (parents.contains(child)) 1192 if (parents.contains(child))
1193 throw new Exception("Cyclic dependancy graphs are not supported (cycle detected for " + child.fqname + ")"); 1193 throw new Exception("Cyclic dependency graphs are not supported (cycle detected for " + child.fqname + ")");
1194 checkForLoop(child, parents); 1194 checkForLoop(child, parents);
1195 } 1195 }
1196 } 1196 }
1197 } 1197 }