Mercurial > hg > monetdb-java
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 } |