comparison example/OnClientExample.java @ 558:ebf65f416da9 onclient

Many improvements to the example code
author Joeri van Ruth <joeri.van.ruth@monetdbsolutions.com>
date Thu, 16 Sep 2021 15:12:49 +0200 (2021-09-16)
parents 87feb93330a6
children 6cb395daa7d1
comparison
equal deleted inserted replaced
557:ce2b616ed22e 558:ebf65f416da9
22 22
23 public static void main(String[] args) { 23 public static void main(String[] args) {
24 int status; 24 int status;
25 try { 25 try {
26 // Ideally this would not be hardcoded.. 26 // Ideally this would not be hardcoded..
27 final String dbUrl = "jdbc:monetdb://localhost:55000/banana"; 27 final String dbUrl = "jdbc:monetdb://localhost:50000/demo";
28 final String userName = "monetdb";
29 final String password = "monetdb";
28 final String uploadDir = "/home/jvr/mydata"; 30 final String uploadDir = "/home/jvr/mydata";
29 final boolean filesAreUtf8 = false; 31 final boolean filesAreUtf8 = false;
30 final String userName = "monetdb"; 32 String[] queries = {
31 final String password = "monetdb"; 33 "DROP TABLE IF EXISTS mytable",
34 "CREATE TABLE mytable(i INT, t TEXT)",
35 "COPY INTO mytable FROM 'generated.csv' ON CLIENT",
36 "COPY 20 RECORDS OFFSET 5 INTO mytable FROM 'generated.csv' ON CLIENT",
37 "COPY INTO mytable FROM 'nonexistentfilethatdoesnotexist.csv' ON CLIENT",
38 "SELECT COUNT(*) FROM mytable",
39 };
32 40
33 status = run(dbUrl, userName, password, uploadDir, filesAreUtf8); 41 status = run(dbUrl, userName, password, uploadDir, filesAreUtf8, queries);
34 42
35 } catch (Exception e) { 43 } catch (Exception e) {
36 status = 1; 44 status = 1;
37 e.printStackTrace(); 45 e.printStackTrace();
38 } 46 }
39 System.exit(status); 47 System.exit(status);
40 } 48 }
41 49
42 private static int run(String dbUrl, String userName, String password, String uploadDir, boolean filesAreUtf8) throws ClassNotFoundException, SQLException { 50 private static int run(String dbUrl, String userName, String password, String uploadDir, boolean filesAreUtf8, String[] queries) throws ClassNotFoundException, SQLException {
43 int status = 0; 51 int status = 0;
44 52
45 // Connect 53 // Connect
46 Class.forName("org.monetdb.jdbc.MonetDriver"); 54 Class.forName("org.monetdb.jdbc.MonetDriver");
47 Connection conn = DriverManager.getConnection(dbUrl, userName, password); 55 Connection conn = DriverManager.getConnection(dbUrl, userName, password);
48 56
49 // Register upload handler 57 // Register upload handler
50 MyUploader handler = new MyUploader(uploadDir, filesAreUtf8); 58 MyUploader handler = new MyUploader(uploadDir, filesAreUtf8);
51 conn.unwrap(MonetConnection.class).setUploadHandler(handler); 59 conn.unwrap(MonetConnection.class).setUploadHandler(handler);
52 60
53 // Run some SQL statements involving ON CLIENT
54 String[] queries = {
55 "DROP TABLE IF EXISTS bar",
56 "CREATE TABLE bar(i INT, t TEXT)",
57 "COPY INTO bar FROM 'generated.csv' ON CLIENT",
58 "COPY INTO bar FROM 'file.csv' ON CLIENT",
59 // following statement will run even if file.csv does not exist
60 "SELECT COUNT(*) FROM bar",
61 };
62 Statement stmt = conn.createStatement(); 61 Statement stmt = conn.createStatement();
63 for (String q : queries) { 62 for (String q : queries) {
64 System.out.println(q); 63 System.out.println(q);
65 try { 64 try {
66 stmt.execute(q); 65 boolean hasResultSet = stmt.execute(q);
67 ResultSet rs = stmt.getResultSet(); 66 if (hasResultSet) {
68 if (rs == null) { 67 ResultSet rs = stmt.getResultSet();
69 System.out.printf(" OK, %d rows updated%n", stmt.getUpdateCount());
70 } else {
71 long count = 0; 68 long count = 0;
72 while (rs.next()) { 69 while (rs.next()) {
73 count++; 70 count++;
74 } 71 }
75 System.out.printf(" OK, returned %d rows%n", count); 72 System.out.printf(" OK, returned %d rows%n", count);
73 } else {
74 System.out.printf(" OK, updated %d rows%n", stmt.getUpdateCount());
76 } 75 }
77 } catch (SQLNonTransientException e) { 76 } catch (SQLNonTransientException e) {
78 throw e; 77 throw e;
79 } catch (SQLException e) { 78 } catch (SQLException e) {
80 System.out.println(" => SQL ERROR " + e.getMessage()); 79 System.out.println(" => SQL ERROR " + e.getMessage());
81 status = 1; 80 status = 1;
82 } 81 }
82
83 } 83 }
84 84
85 return status; 85 return status;
86 } 86 }
87 87
88 88
89 private static class MyUploader implements UploadHandler { 89 private static class MyUploader implements UploadHandler {
90 private final Path uploadDir; 90 private final Path uploadDir;
91 private final boolean filesAreUtf8; 91 private final boolean filesAreUtf8;
92 private boolean stopUploading = false;
92 93
93 public MyUploader(String uploadDir, boolean filesAreUtf8) { 94 public MyUploader(String uploadDir, boolean filesAreUtf8) {
94 this.uploadDir = FileSystems.getDefault().getPath(uploadDir).normalize(); 95 this.uploadDir = FileSystems.getDefault().getPath(uploadDir).normalize();
95 this.filesAreUtf8 = filesAreUtf8; 96 this.filesAreUtf8 = filesAreUtf8;
96 } 97 }
97 98
98 @Override 99 @Override
100 public void uploadCancelled() {
101 System.out.println(" CANCELLATION CALLBACK: server cancelled the upload");
102 stopUploading = true;
103 }
104
105 @Override
99 public void handleUpload(MonetConnection.Upload handle, String name, boolean textMode, long linesToSkip) throws IOException { 106 public void handleUpload(MonetConnection.Upload handle, String name, boolean textMode, long linesToSkip) throws IOException {
100
101 // COPY OFFSET line numbers are 1-based but 0 is also allowed.
102 // Compute the number of lines to skip
103 107
104 // We can upload data read from the file system but also make up our own data 108 // We can upload data read from the file system but also make up our own data
105 if (name.equals("generated.csv")) { 109 if (name.equals("generated.csv")) {
106 uploadGenerated(handle, linesToSkip); 110 uploadGeneratedData(handle, linesToSkip);
107 return; 111 return;
108 } 112 }
109 113
110 // Validate the path, demonstrating two ways of dealing with errors 114 // Validate the path, demonstrating two ways of dealing with errors
111 Path path = securityCheck(name); 115 Path path = securityCheck(name);
112 if (path == null || !Files.exists(path)) { 116 if (path == null || !Files.exists(path)) {
113 // This makes the COPY command fail but keeps the connection alive. 117 // This makes the COPY command fail but keeps the connection
114 // Can only be used if we haven't sent any data yet 118 // alive. Can only be used if we haven't sent any data yet
115 handle.sendError("Invalid path"); 119 handle.sendError("Invalid path");
116 return; 120 return;
117 } 121 }
118 if (!Files.isReadable(path)) { 122 if (!Files.isReadable(path)) {
119 // As opposed to handle.sendError(), throwing an IOException ends the whole connection. 123 // As opposed to handle.sendError(), we can throw an IOException
124 // at any time. Unfortunately, the file upload protocol does not
125 // provide a way to indicate to the server that the data sent so
126 // far is incomplete, so for the time being throwing an
127 // IOException from {@handleUpload} terminates the connection.
120 throw new IOException("Unreadable: " + path); 128 throw new IOException("Unreadable: " + path);
121 } 129 }
122 130
123 boolean binary = !textMode; 131 boolean binary = !textMode;
124 if (binary) { 132 if (binary) {
125 uploadBinary(handle, path); 133 uploadAsBinary(handle, path);
126 } else if (linesToSkip == 0 && filesAreUtf8) { 134 } else if (linesToSkip == 0 && filesAreUtf8) {
127 // Avoid unnecessary character set conversions by pretending it's binary 135 // Avoid unnecessary UTF-8 -> Java String -> UTF-8 conversions
128 uploadBinary(handle, path); 136 // by pretending the data is binary.
137 uploadAsBinary(handle, path);
129 } else { 138 } else {
130 // Charset and skip handling really necessary 139 // Charset and skip handling really necessary
131 uploadTextFile(handle, path, linesToSkip); 140 uploadAsText(handle, path, linesToSkip);
132 } 141 }
133 } 142 }
134 143
135 private Path securityCheck(String name) { 144 private Path securityCheck(String name) {
136 Path p = uploadDir.resolve(name).normalize(); 145 Path p = uploadDir.resolve(name).normalize();
139 } else { 148 } else {
140 return null; 149 return null;
141 } 150 }
142 } 151 }
143 152
144 private void uploadGenerated(MonetConnection.Upload handle, long toSkip) throws IOException { 153 private void uploadGeneratedData(MonetConnection.Upload handle, long toSkip) throws IOException {
154 // Set the chunk size to a tiny amount so we can demonstrate
155 // cancellation handling. The default chunk size is one megabyte.
156 // DO NOT DO THIS IN PRODUCTION!
157 handle.setChunkSize(50);
158
159 // Make up some data and upload it.
145 PrintStream stream = handle.getStream(); 160 PrintStream stream = handle.getStream();
146 for (long i = toSkip + 1; i <= 100; i++) { 161 long n = 100;
162 System.out.printf(" HANDLER: uploading %d generated lines, numbered %d to %d%n", n - toSkip, toSkip +1, n);
163 long i;
164 for (i = toSkip + 1; i <= n; i++) {
165 if (stopUploading) {
166 System.out.printf(" HANDLER: at line %d we noticed the server asked us to stop sending%n", i);
167 break;
168 }
147 stream.printf("%d|the number is %d%n", i, i); 169 stream.printf("%d|the number is %d%n", i, i);
148 } 170 }
171 System.out.println(" HANDLER: done uploading");
149 stream.close(); 172 stream.close();
150 } 173 }
151 174
152 private void uploadTextFile(MonetConnection.Upload handle, Path path, long toSkip) throws IOException { 175 private void uploadAsText(MonetConnection.Upload handle, Path path, long toSkip) throws IOException {
153 BufferedReader reader = Files.newBufferedReader(path);// Converts from system encoding to Java text 176 BufferedReader reader = Files.newBufferedReader(path);// Converts from system encoding to Java text
154 for (long i = 0; i < toSkip; i++) { 177 for (long i = 0; i < toSkip; i++) {
155 reader.readLine(); 178 reader.readLine();
156 } 179 }
157 handle.uploadFrom(reader); // Converts from Java text to UTF-8 as required by MonetDB 180 handle.uploadFrom(reader); // Converts from Java text to UTF-8 as required by MonetDB
158 } 181 }
159 182
160 private void uploadBinary(MonetConnection.Upload handle, Path path) throws IOException { 183 private void uploadAsBinary(MonetConnection.Upload handle, Path path) throws IOException {
161 // No charset conversion whatsoever.. 184 // No charset conversion whatsoever..
162 // Use this for binary data or when you are certain the file is UTF-8 encoded. 185 // Use this for binary data or when you are certain the file is UTF-8 encoded.
163 InputStream stream = Files.newInputStream(path); 186 InputStream stream = Files.newInputStream(path);
164 handle.uploadFrom(stream); 187 handle.uploadFrom(stream);
165 } 188 }