New upstream version 1.0.34
Christopher Hoskin
6 years ago
35 | 35 | <!-- =================================================================== --> |
36 | 36 | <property name="TALK" value="false" /> |
37 | 37 | <property name="name" value="csvjdbc"/> |
38 | <property name="rel" value="1.0-31"/> | |
38 | <property name="rel" value="1.0-33"/> | |
39 | 39 | <property name="rel.name" value="${name}-${rel}"/> |
40 | 40 | <property name="build.dir" value="../build"/> |
41 | 41 | <property name="src.dir" value="../src"/> |
2 | 2 | <modelVersion>4.0.0</modelVersion> |
3 | 3 | <groupId>net.sourceforge.csvjdbc</groupId> |
4 | 4 | <artifactId>csvjdbc</artifactId> |
5 | <version>1.0.31</version> | |
5 | <version>1.0.34</version> | |
6 | 6 | <packaging>jar</packaging> |
7 | 7 | <name>CsvJdbc</name> |
8 | 8 | <description>a Java JDBC driver for reading comma-separated-value files</description> |
17 | 17 | |
18 | 18 | <scm> |
19 | 19 | <connection>scm:git:ssh://git.code.sf.net/p/csvjdbc/code</connection> |
20 | <tag>HEAD</tag> | |
20 | <tag>csvjdbc-1.0.34</tag> | |
21 | 21 | <url>http://sourceforge.net/p/csvjdbc/_list/git</url> |
22 | 22 | </scm> |
23 | 23 | |
131 | 131 | <plugin> |
132 | 132 | <groupId>org.apache.maven.plugins</groupId> |
133 | 133 | <artifactId>maven-release-plugin</artifactId> |
134 | <version>2.5.3</version> | |
134 | 135 | <configuration> |
135 | 136 | <autoVersionSubmodules>true</autoVersionSubmodules> |
136 | 137 | <useReleaseProfile>false</useReleaseProfile> |
137 | 138 | <!-- references the profile defined just below --> |
138 | 139 | <releaseProfiles>release</releaseProfiles> |
139 | 140 | <goals>deploy</goals> |
140 | <pushChanges>false</pushChanges> | |
141 | <pushChanges>true</pushChanges> | |
141 | 142 | </configuration> |
142 | 143 | </plugin> |
143 | 144 | <plugin> |
24 | 24 | |
25 | 25 | class ColumnName extends Expression |
26 | 26 | { |
27 | String columnName; | |
27 | private String columnName; | |
28 | ||
28 | 29 | public ColumnName(String columnName) |
29 | 30 | { |
30 | 31 | this.columnName = columnName.toUpperCase(); |
31 | 32 | } |
33 | ||
34 | public String getColumnName() | |
35 | { | |
36 | return columnName; | |
37 | } | |
38 | ||
32 | 39 | public Object eval(Map<String, Object> env) |
33 | 40 | { |
34 | 41 | return env.get(columnName); |
35 | 42 | } |
43 | ||
36 | 44 | public String toString() |
37 | 45 | { |
38 | 46 | return "["+columnName+"]"; |
39 | 47 | } |
48 | ||
40 | 49 | public List<String> usedColumns(Set<String> availableColumns) |
41 | 50 | { |
42 | 51 | List<String> result = new LinkedList<String>(); |
41 | 41 | import java.sql.Statement; |
42 | 42 | import java.sql.Struct; |
43 | 43 | import java.util.ArrayList; |
44 | import java.util.Collections; | |
44 | 45 | import java.util.HashMap; |
46 | import java.util.HashSet; | |
45 | 47 | import java.util.List; |
46 | 48 | import java.util.Locale; |
47 | 49 | import java.util.Map; |
48 | 50 | import java.util.Properties; |
49 | 51 | import java.util.Vector; |
50 | 52 | import java.util.concurrent.Executor; |
53 | import java.util.regex.Matcher; | |
54 | import java.util.regex.Pattern; | |
51 | 55 | |
52 | 56 | import org.relique.io.CryptoFilter; |
53 | 57 | import org.relique.io.TableReader; |
1388 | 1392 | return name.endsWith(extension); |
1389 | 1393 | } |
1390 | 1394 | }); |
1395 | ||
1396 | Pattern fileNameRE = null; | |
1397 | if (indexedFiles) | |
1398 | { | |
1399 | /* | |
1400 | * Accept any filenames that match the fileTailPattern that was | |
1401 | * configured for this connection. | |
1402 | */ | |
1403 | fileNameRE = Pattern.compile("(.+)" + fileNamePattern); | |
1404 | } | |
1405 | ||
1406 | HashSet<String> indexedTableNames = new HashSet<String>(); | |
1407 | ||
1391 | 1408 | for (int i = 0; i < matchingFiles.length; i++) |
1392 | 1409 | { |
1393 | 1410 | if (matchingFiles[i].isFile() && matchingFiles[i].canRead()) |
1395 | 1412 | String filename = matchingFiles[i].getName(); |
1396 | 1413 | String tableName = filename.substring(0, |
1397 | 1414 | filename.length() - extension.length()); |
1398 | tableNames.add(tableName); | |
1415 | ||
1416 | if (indexedFiles) | |
1417 | { | |
1418 | Matcher m = fileNameRE.matcher(tableName); | |
1419 | if (m.matches()) | |
1420 | { | |
1421 | /* | |
1422 | * We want only the first part of the filename, before | |
1423 | * the fileTailPattern. | |
1424 | * | |
1425 | * We use a java.util.Set so that each indexed table name | |
1426 | * is added only one time, despite comprising of several files. | |
1427 | */ | |
1428 | indexedTableNames.add(m.group(1)); | |
1429 | } | |
1430 | } | |
1431 | else | |
1432 | { | |
1433 | tableNames.add(tableName); | |
1434 | } | |
1399 | 1435 | } |
1400 | 1436 | } |
1437 | tableNames.addAll(indexedTableNames); | |
1401 | 1438 | } |
1402 | 1439 | else |
1403 | 1440 | { |
1408 | 1445 | if (list != null) |
1409 | 1446 | tableNames = list; |
1410 | 1447 | } |
1448 | ||
1449 | /* | |
1450 | * Ensure tables are always reported in the same order. | |
1451 | */ | |
1452 | Collections.sort(tableNames); | |
1411 | 1453 | return tableNames; |
1412 | 1454 | } |
1413 | 1455 | } |
15 | 15 | */ |
16 | 16 | package org.relique.jdbc.csv; |
17 | 17 | |
18 | import java.net.URLDecoder; | |
19 | import java.sql.*; | |
20 | import java.util.Properties; | |
21 | 18 | import java.io.File; |
22 | 19 | import java.io.IOException; |
23 | 20 | import java.io.PrintStream; |
24 | 21 | import java.io.PrintWriter; |
25 | 22 | import java.io.UnsupportedEncodingException; |
23 | import java.net.URLDecoder; | |
24 | import java.sql.Connection; | |
25 | import java.sql.Date; | |
26 | import java.sql.Driver; | |
27 | import java.sql.DriverManager; | |
28 | import java.sql.DriverPropertyInfo; | |
29 | import java.sql.ResultSet; | |
30 | import java.sql.ResultSetMetaData; | |
31 | import java.sql.SQLException; | |
32 | import java.sql.SQLFeatureNotSupportedException; | |
33 | import java.sql.Time; | |
34 | import java.sql.Timestamp; | |
35 | import java.sql.Types; | |
36 | import java.util.Locale; | |
37 | import java.util.Properties; | |
26 | 38 | import java.util.logging.Logger; |
27 | 39 | |
28 | 40 | import org.relique.io.TableReader; |
283 | 295 | String separator = DEFAULT_SEPARATOR; |
284 | 296 | Character quoteChar = Character.valueOf(DEFAULT_QUOTECHAR); |
285 | 297 | String quoteStyle = DEFAULT_QUOTE_STYLE; |
298 | String dateFormat = DEFAULT_DATE_FORMAT; | |
299 | String timeFormat = DEFAULT_TIME_FORMAT; | |
300 | String timestampFormat = DEFAULT_TIMESTAMP_FORMAT; | |
301 | String timeZoneName = DEFAULT_TIME_ZONE_NAME; | |
302 | Locale locale = null; | |
286 | 303 | |
287 | 304 | if (resultSet instanceof CsvResultSet) |
288 | 305 | { |
294 | 311 | separator = csvConnection.getSeparator(); |
295 | 312 | quoteChar = csvConnection.getQuotechar(); |
296 | 313 | quoteStyle = csvConnection.getQuoteStyle(); |
297 | } | |
314 | dateFormat = csvConnection.getDateFormat(); | |
315 | timeFormat = csvConnection.getTimeFormat(); | |
316 | timestampFormat = csvConnection.getTimestampFormat(); | |
317 | timeZoneName = csvConnection.getTimeZoneName(); | |
318 | locale = csvConnection.getLocale(); | |
319 | } | |
320 | ||
321 | StringConverter converter = new StringConverter(dateFormat, timeFormat, timestampFormat, timeZoneName, locale); | |
298 | 322 | |
299 | 323 | ResultSetMetaData meta = null; |
300 | 324 | int columnCount = 0; |
324 | 348 | { |
325 | 349 | if (i > 1) |
326 | 350 | out.print(separator); |
327 | String value = resultSet.getString(i); | |
351 | String value = null; | |
352 | ||
353 | /* | |
354 | * Use same dateFormat, timeFormat and timestampFormat for output as the input CSV file. | |
355 | */ | |
356 | int columnType = meta.getColumnType(i); | |
357 | if (columnType == Types.DATE) | |
358 | { | |
359 | Date d = resultSet.getDate(i); | |
360 | if (d != null) | |
361 | value = converter.formatDate(d); | |
362 | } | |
363 | else if (columnType == Types.TIME) | |
364 | { | |
365 | Time t = resultSet.getTime(i); | |
366 | if (t != null) | |
367 | value = converter.formatTime(t); | |
368 | } | |
369 | else if (columnType == Types.TIMESTAMP) | |
370 | { | |
371 | Timestamp timestamp = resultSet.getTimestamp(i); | |
372 | if (timestamp != null) | |
373 | value = converter.formatTimestamp(timestamp); | |
374 | } | |
375 | else | |
376 | { | |
377 | value = resultSet.getString(i); | |
378 | } | |
328 | 379 | if (value != null) |
329 | 380 | { |
330 | 381 | if (quoteChar != null) |
90 | 90 | |
91 | 91 | CsvDriver.writeLog("CsvPreparedStatement:executeQuery() - sql= " + templateQuery); |
92 | 92 | |
93 | setTimeoutMillis(); | |
94 | cancelled = false; | |
95 | ||
93 | 96 | ResultSet resultSet = executeQuery(); |
94 | 97 | lastResultSet = resultSet; |
95 | 98 | |
117 | 120 | lastResultSet = null; |
118 | 121 | } |
119 | 122 | |
123 | setTimeoutMillis(); | |
124 | cancelled = false; | |
125 | ||
120 | 126 | parser.setPlaceholdersValues(parameters); |
121 | 127 | return executeParsedQuery(parser); |
122 | 128 | } |
33 | 33 | import java.sql.RowId; |
34 | 34 | import java.sql.SQLException; |
35 | 35 | import java.sql.SQLFeatureNotSupportedException; |
36 | import java.sql.SQLTimeoutException; | |
36 | 37 | import java.sql.SQLWarning; |
37 | 38 | import java.sql.SQLXML; |
38 | 39 | import java.sql.Statement; |
868 | 869 | throw new SQLException(CsvResources.getString("closedResultSet")); |
869 | 870 | } |
870 | 871 | |
872 | private void checkTimeout() throws SQLTimeoutException | |
873 | { | |
874 | if (statement.getTimeoutMillis() != Long.MAX_VALUE) | |
875 | { | |
876 | if (System.currentTimeMillis() >= statement.getTimeoutMillis()) | |
877 | throw new SQLTimeoutException(); | |
878 | } | |
879 | } | |
880 | ||
871 | 881 | @Override |
872 | 882 | public boolean next() throws SQLException |
873 | 883 | { |
874 | 884 | boolean thereWasAnAnswer; |
875 | 885 | |
876 | 886 | checkOpen(); |
887 | checkTimeout(); | |
888 | ||
889 | /* | |
890 | * Has query been cancelled by another thread? | |
891 | */ | |
892 | if (statement.isCancelled()) | |
893 | throw new SQLException(CsvResources.getString("statementCancelled")); | |
877 | 894 | |
878 | 895 | if ((this.groupByColumns != null || |
879 | 896 | this.aggregateFunctions.size() > 0 || |
56 | 56 | private int maxRows = 0; |
57 | 57 | private int fetchSize = 1; |
58 | 58 | private int queryTimeout = Integer.MAX_VALUE; |
59 | private long timeoutMillis = Long.MAX_VALUE; | |
59 | 60 | private int fetchDirection = ResultSet.FETCH_FORWARD; |
60 | 61 | private boolean closed; |
62 | protected boolean cancelled; | |
61 | 63 | |
62 | 64 | protected int resultSetType = ResultSet.TYPE_SCROLL_INSENSITIVE; |
63 | 65 | |
286 | 288 | lastResultSet = null; |
287 | 289 | multipleParsers = null; |
288 | 290 | } |
291 | ||
292 | setTimeoutMillis(); | |
293 | cancelled = false; | |
289 | 294 | |
290 | 295 | SqlParser parser = new SqlParser(); |
291 | 296 | try |
511 | 516 | } |
512 | 517 | } |
513 | 518 | |
519 | public boolean isCancelled() throws SQLException | |
520 | { | |
521 | return cancelled; | |
522 | } | |
523 | ||
514 | 524 | @Override |
515 | 525 | public void cancel() throws SQLException |
516 | 526 | { |
517 | throw new SQLException(CsvResources.getString("methodNotSupported") + ": Statement.cancel()"); | |
527 | /* | |
528 | * JDBC documentation states that this method may be called | |
529 | * from a different thread to other JDBC calls. | |
530 | * Just set a flag here, so that the next JDBC call will | |
531 | * trigger the statement to close, avoiding any race condition. | |
532 | */ | |
533 | cancelled = true; | |
518 | 534 | } |
519 | 535 | |
520 | 536 | @Override |
521 | 537 | public void clearWarnings() throws SQLException |
522 | 538 | { |
523 | 539 | checkOpen(); |
540 | } | |
541 | ||
542 | /** | |
543 | * Set timestamp at which current query will throw an SQLTimeoutException. | |
544 | */ | |
545 | protected void setTimeoutMillis() | |
546 | { | |
547 | if (queryTimeout == 0 || queryTimeout == Integer.MAX_VALUE) | |
548 | timeoutMillis = Long.MAX_VALUE; | |
549 | else | |
550 | timeoutMillis = System.currentTimeMillis() + queryTimeout * 1000; | |
551 | } | |
552 | ||
553 | /** | |
554 | * Get timestamp at which current query will throw an SQLTimeoutException. | |
555 | * @return timestamp in milliseconds, or MAX_VALUE if no timeout. | |
556 | */ | |
557 | public long getTimeoutMillis() | |
558 | { | |
559 | return timeoutMillis; | |
524 | 560 | } |
525 | 561 | |
526 | 562 | @Override |
541 | 577 | lastResultSet = null; |
542 | 578 | multipleParsers = null; |
543 | 579 | } |
580 | ||
581 | setTimeoutMillis(); | |
582 | cancelled = false; | |
544 | 583 | |
545 | 584 | /* |
546 | 585 | * Execute one or more SQL statements. |
63 | 63 | sb.append(separator); |
64 | 64 | separator = ", "; |
65 | 65 | QueryEnvEntry queryEnvEntry = (QueryEnvEntry)(expr.content); |
66 | sb.append(queryEnvEntry.expression.toString()); | |
66 | sb.append(queryEnvEntry.getExpression().toString()); | |
67 | 67 | } |
68 | 68 | if (tableEntries != null) |
69 | 69 | { |
169 | 169 | for (ParsedExpression parsedExpr : queryEntries) |
170 | 170 | { |
171 | 171 | QueryEnvEntry queryEnvEntry = (QueryEnvEntry)(parsedExpr.content); |
172 | List<String> columns = queryEnvEntry.expression.usedColumns(availableColumns); | |
172 | List<String> columns = queryEnvEntry.getExpression().usedColumns(availableColumns); | |
173 | 173 | result.addAll(columns); |
174 | 174 | } |
175 | 175 | } |
207 | 207 | for (ParsedExpression parsedExpr : queryEntries) |
208 | 208 | { |
209 | 209 | QueryEnvEntry queryEnvEntry = (QueryEnvEntry)(parsedExpr.content); |
210 | List<AggregateFunction> functions = queryEnvEntry.expression.aggregateFunctions(); | |
210 | List<AggregateFunction> functions = queryEnvEntry.getExpression().aggregateFunctions(); | |
211 | 211 | result.addAll(functions); |
212 | 212 | } |
213 | 213 | } |
22 | 22 | |
23 | 23 | class QueryEnvEntry extends Expression |
24 | 24 | { |
25 | String key; | |
26 | Expression expression; | |
25 | private String key; | |
26 | private Expression expression; | |
27 | 27 | |
28 | 28 | public QueryEnvEntry(String fieldName, Expression exp) |
29 | 29 | { |
30 | 30 | this.key = fieldName.toUpperCase(); |
31 | 31 | this.expression = exp; |
32 | 32 | } |
33 | ||
34 | public Expression getExpression() | |
35 | { | |
36 | return expression; | |
37 | } | |
38 | ||
39 | public String getKey() | |
40 | { | |
41 | return key; | |
42 | } | |
43 | ||
33 | 44 | public Object eval(Map<String, Object> env) throws SQLException |
34 | 45 | { |
35 | 46 | return expression.eval(env); |
36 | 47 | } |
48 | ||
37 | 49 | public String toString() |
38 | 50 | { |
39 | 51 | return key+": "+expression.toString(); |
40 | 52 | } |
53 | ||
41 | 54 | public void resetAggregateFunctions() |
42 | 55 | { |
43 | 56 | expression.resetAggregateFunctions(); |
159 | 159 | /* |
160 | 160 | * A logical expression such as A > 5 is not allowed as a query expression. |
161 | 161 | */ |
162 | if ((cc.expression instanceof LogicalExpression) || | |
163 | (cc.expression.isValid() == false)) | |
162 | if ((cc.getExpression() instanceof LogicalExpression) || | |
163 | (cc.getExpression().isValid() == false)) | |
164 | 164 | { |
165 | 165 | throw new SQLException("invalidQueryExpression"); |
166 | 166 | } |
167 | 167 | |
168 | String key = cc.key; | |
168 | String key = cc.getKey(); | |
169 | 169 | for (int i = 0; i < tableNames.size(); i++) |
170 | 170 | { |
171 | 171 | if (tableAliases.get(i) != null && key.startsWith(tableAliases.get(i) + ".")) |
179 | 179 | break; |
180 | 180 | } |
181 | 181 | } |
182 | environment.add(new Object[]{ key, cc.expression }); | |
182 | environment.add(new Object[]{ key, cc.getExpression() }); | |
183 | 183 | } |
184 | 184 | } |
185 | 185 |
27 | 27 | import java.text.ParseException; |
28 | 28 | import java.text.SimpleDateFormat; |
29 | 29 | import java.util.ArrayList; |
30 | import java.util.Calendar; | |
30 | 31 | import java.util.GregorianCalendar; |
31 | 32 | import java.util.HashMap; |
32 | 33 | import java.util.List; |
51 | 52 | private Pattern timestampPattern; |
52 | 53 | private SimpleDateFormat timestampFormat; |
53 | 54 | private SimpleDateFormat simpleDateFormat; |
55 | private int currentYear; | |
54 | 56 | |
55 | 57 | public StringConverter(String dateformat, String timeformat, String timestampformat, |
56 | 58 | String timeZoneName) |
75 | 77 | * SimpleDateFormat parsing required? |
76 | 78 | */ |
77 | 79 | // TODO prefer to use SimpleDateFormat for everything but existing regex not 100% compatible |
78 | String upper = dateformat.toUpperCase(); | |
80 | String upper = dateformat.toUpperCase(Locale.US); | |
79 | 81 | boolean useSimpleDateFormat = false; |
80 | 82 | if (upper.contains("MMM")) |
81 | 83 | { |
132 | 134 | |
133 | 135 | TimeZone timeZone = TimeZone.getTimeZone(timeZoneName); |
134 | 136 | calendar = new GregorianCalendar(); |
137 | calendar.setTimeInMillis(System.currentTimeMillis()); | |
138 | currentYear = calendar.get(Calendar.YEAR); | |
135 | 139 | calendar.clear(); |
136 | 140 | calendar.setTimeZone(timeZone); |
137 | 141 | if (timestampformat != null && timestampformat.length() > 0) |
333 | 337 | private String makeISODate(String date, String format) |
334 | 338 | { |
335 | 339 | // first memorize the original order of the groups. |
336 | format = format.toLowerCase(); | |
340 | format = format.toLowerCase(Locale.US); | |
337 | 341 | int dpos = format.indexOf('d'); |
338 | 342 | int mpos = format.indexOf('m'); |
339 | 343 | int ypos = format.indexOf('y'); |
344 | int ylen = 4; | |
340 | 345 | |
341 | 346 | int day = 1, month = 1, year = 1; |
342 | 347 | if (dpos > mpos) |
369 | 374 | part = Pattern.compile("y+"); |
370 | 375 | m = part.matcher(format); |
371 | 376 | if (m.find()) |
377 | { | |
378 | ylen = m.end() - m.start(); | |
372 | 379 | format = format.replace(m.group(), "([0-9]{" + (m.end() - m.start()) + ",4})"); |
380 | } | |
373 | 381 | |
374 | 382 | format = format + ".*"; |
375 | 383 | |
379 | 387 | { |
380 | 388 | // and return the groups in ISO8601 format. |
381 | 389 | String yearGroup = m.group(year); |
390 | if (yearGroup.length() == 2 && ylen == 2) | |
391 | { | |
392 | // Parsed year only contains two digits. Determine full year including | |
393 | // century using same logic as java.text.SimpleDateFormat: | |
394 | // select century that is less than 20 years in the future and | |
395 | // less than 80 years in the past. | |
396 | int century = currentYear / 100; | |
397 | int proposedYear = (century * 100) + Integer.parseInt(yearGroup); | |
398 | if (proposedYear - currentYear > 20) | |
399 | proposedYear -= 100; | |
400 | if (currentYear - proposedYear > 80) | |
401 | proposedYear += 100; | |
402 | yearGroup = String.format("%04d", proposedYear); | |
403 | } | |
382 | 404 | String monthGroup = m.group(month); |
383 | 405 | if (monthGroup.length() < 2) |
384 | 406 | monthGroup = "0" + monthGroup; |
424 | 446 | } |
425 | 447 | } |
426 | 448 | |
449 | /** | |
450 | * Create date string in format accepted by method {@link #parseDate(String) parseDate}. | |
451 | * @param d date to format. | |
452 | * @return formatted date. | |
453 | */ | |
454 | public String formatDate(Date d) | |
455 | { | |
456 | String formatted = null; | |
457 | ||
458 | if (d != null) | |
459 | { | |
460 | if (simpleDateFormat != null) | |
461 | { | |
462 | formatted = simpleDateFormat.format(d); | |
463 | } | |
464 | else | |
465 | { | |
466 | formatted = dateFormat.toLowerCase(Locale.US); | |
467 | Calendar gregorianCalendar = new GregorianCalendar(); | |
468 | gregorianCalendar.setTime(d); | |
469 | ||
470 | // use regular expressions to replace in day, month, year in format string | |
471 | Pattern part; | |
472 | Matcher m; | |
473 | ||
474 | part = Pattern.compile("d+"); | |
475 | m = part.matcher(formatted); | |
476 | if (m.find()) | |
477 | { | |
478 | int patternLength = m.end() - m.start(); | |
479 | String dayOfMonth = Integer.toString(gregorianCalendar.get(Calendar.DAY_OF_MONTH)); | |
480 | while (dayOfMonth.length() < patternLength) | |
481 | dayOfMonth = "0" + dayOfMonth; | |
482 | formatted = formatted.replace(m.group(), dayOfMonth); | |
483 | } | |
484 | part = Pattern.compile("m+"); | |
485 | m = part.matcher(formatted); | |
486 | if (m.find()) | |
487 | { | |
488 | int patternLength = m.end() - m.start(); | |
489 | String month = Integer.toString(gregorianCalendar.get(Calendar.MONTH) + 1); | |
490 | while (month.length() < patternLength) | |
491 | month = "0" + month; | |
492 | formatted = formatted.replace(m.group(), month); | |
493 | } | |
494 | part = Pattern.compile("y+"); | |
495 | m = part.matcher(formatted); | |
496 | if (m.find()) | |
497 | { | |
498 | int patternLength = m.end() - m.start(); | |
499 | int fullYear = gregorianCalendar.get(Calendar.YEAR); | |
500 | if (patternLength == 2) | |
501 | fullYear = fullYear % 100; | |
502 | String year = Integer.toString(fullYear); | |
503 | while (year.length() < patternLength) | |
504 | year = "0" + year; | |
505 | formatted = formatted.replace(m.group(), year); | |
506 | } | |
507 | } | |
508 | } | |
509 | return formatted; | |
510 | } | |
511 | ||
427 | 512 | public Time parseTime(String str) |
428 | 513 | { |
429 | 514 | try |
450 | 535 | { |
451 | 536 | return null; |
452 | 537 | } |
538 | } | |
539 | ||
540 | /** | |
541 | * Create time string in format accepted by method {@link #parseTime(String) parseTime}. | |
542 | * @param t time to format. | |
543 | * @return formatted time. | |
544 | */ | |
545 | public String formatTime(Time t) | |
546 | { | |
547 | String formatted = null; | |
548 | ||
549 | if (t != null) | |
550 | { | |
551 | formatted = simpleTimeFormat.format(t); | |
552 | } | |
553 | return formatted; | |
453 | 554 | } |
454 | 555 | |
455 | 556 | public Timestamp parseTimestamp(String str) |
488 | 589 | { |
489 | 590 | } |
490 | 591 | return result; |
592 | } | |
593 | ||
594 | /** | |
595 | * Create timestamp string in format accepted by method {@link #parseTimestamp(String) parseTimestamp}. | |
596 | * @param timestamp timestamp to format. | |
597 | * @return formatted timestamp. | |
598 | */ | |
599 | public String formatTimestamp(Timestamp timestamp) | |
600 | { | |
601 | String formatted = null; | |
602 | ||
603 | if (timestamp != null) | |
604 | { | |
605 | if (timestampFormat != null) | |
606 | { | |
607 | formatted = timestampFormat.format(timestamp); | |
608 | } | |
609 | else | |
610 | { | |
611 | calendar.setTime(timestamp); | |
612 | int year = calendar.get(Calendar.YEAR); | |
613 | int month = calendar.get(Calendar.MONTH) + 1; | |
614 | int dayOfMonth = calendar.get(Calendar.DAY_OF_MONTH); | |
615 | int hourOfDay = calendar.get(Calendar.HOUR_OF_DAY); | |
616 | int minutes = calendar.get(Calendar.MINUTE); | |
617 | int seconds = calendar.get(Calendar.SECOND); | |
618 | formatted = String.format("%04d-%02d-%02d %02d:%02d:%02d", year, month, dayOfMonth, hourOfDay, minutes, seconds); | |
619 | } | |
620 | } | |
621 | return formatted; | |
491 | 622 | } |
492 | 623 | |
493 | 624 | public InputStream parseAsciiStream(String str) |
558 | 689 | |
559 | 690 | public Class<?> forSQLName(String sqlTypeName) |
560 | 691 | { |
561 | sqlTypeName = sqlTypeName.toLowerCase(); | |
692 | sqlTypeName = sqlTypeName.toLowerCase(Locale.US); | |
562 | 693 | return forSQLNameMap.get(sqlTypeName); |
563 | 694 | } |
564 | 695 | |
572 | 703 | public static Object getLiteralForTypeName(String sqlTypeName) |
573 | 704 | { |
574 | 705 | Object retval = null; |
575 | sqlTypeName = sqlTypeName.toLowerCase(); | |
706 | sqlTypeName = sqlTypeName.toLowerCase(Locale.US); | |
576 | 707 | if (sqlTypeName.equals("string")) |
577 | 708 | retval = ""; |
578 | 709 | else if (sqlTypeName.equals("boolean")) |
699 | 699 | (expression = binaryOperation()((<AS>)?alias = columnAlias())? |
700 | 700 | { |
701 | 701 | if (alias != null) |
702 | result = new QueryEnvEntry(((ColumnName)alias).columnName, expression); | |
702 | result = new QueryEnvEntry(((ColumnName)alias).getColumnName(), expression); | |
703 | 703 | else if (expression instanceof ColumnName) |
704 | result = new QueryEnvEntry(((ColumnName)expression).columnName, expression); | |
704 | result = new QueryEnvEntry(((ColumnName)expression).getColumnName(), expression); | |
705 | 705 | else |
706 | 706 | result = new QueryEnvEntry(expression.toString(), expression); |
707 | 707 | return new ParsedExpression(result); |
1131 | 1131 | Number value = null; |
1132 | 1132 | digits = sign+t.image; |
1133 | 1133 | isLong = false; |
1134 | if (digits.toUpperCase().endsWith("L")) | |
1134 | if (digits.endsWith("L") || digits.endsWith("l")) | |
1135 | 1135 | { |
1136 | 1136 | digits = digits.substring(0, digits.length() - 1); |
1137 | 1137 | isLong = true; |
69 | 69 | notNamedSavepoint=Not a named Savepoint |
70 | 70 | orderByNotInGroupBy=ORDER BY column not included in GROUP BY |
71 | 71 | parameterIndex=Parameter index out of range |
72 | statementCancelled=Statement cancelled | |
72 | 73 | statementClosed=Statement is already closed |
73 | 74 | streamClosed=Stream is already closed |
74 | 75 | subqueryOneColumn=Subquery must return one column |
578 | 578 | } |
579 | 579 | |
580 | 580 | @Test |
581 | public void testDatabaseMetadataColumnsWithIndexedFiles() throws SQLException | |
582 | { | |
583 | Properties props = new Properties(); | |
584 | props.put("fileExtension", ".txt"); | |
585 | props.put("fileTailPattern", "-([0-9]{3})-([0-9]{8})"); | |
586 | props.put("fileTailParts", "location,file_date"); | |
587 | props.put("indexedFiles", "True"); | |
588 | Connection conn = DriverManager.getConnection("jdbc:relique:csv:" | |
589 | + filePath, props); | |
590 | DatabaseMetaData metadata = conn.getMetaData(); | |
591 | ResultSet results = metadata.getColumns(null, null, "test", null); | |
592 | assertTrue(results.next()); | |
593 | assertEquals("Wrong table name", "test", results.getString("TABLE_NAME")); | |
594 | assertEquals("Wrong column name", "Datum", results.getString("COLUMN_NAME")); | |
595 | assertTrue(results.next()); | |
596 | assertEquals("Wrong table name", "test", results.getString("TABLE_NAME")); | |
597 | assertEquals("Wrong column name", "Tijd", results.getString("COLUMN_NAME")); | |
598 | results.close(); | |
599 | conn.close(); | |
600 | } | |
601 | ||
602 | @Test | |
581 | 603 | public void testDatabaseMetadataProcedures() throws SQLException |
582 | 604 | { |
583 | 605 | Connection conn = DriverManager.getConnection("jdbc:relique:csv:" |
3456 | 3478 | } |
3457 | 3479 | |
3458 | 3480 | @Test |
3481 | public void testStatementCancelled() throws SQLException | |
3482 | { | |
3483 | Connection conn = DriverManager.getConnection("jdbc:relique:csv:" | |
3484 | + filePath); | |
3485 | Statement stmt = conn.createStatement(); | |
3486 | ||
3487 | try | |
3488 | { | |
3489 | ResultSet results = stmt.executeQuery("SELECT * FROM sample"); | |
3490 | stmt.cancel(); | |
3491 | results.next(); | |
3492 | fail("expected exception java.sql.SQLException: Statement cancelled"); | |
3493 | } | |
3494 | catch (SQLException e) | |
3495 | { | |
3496 | assertEquals("wrong exception and/or exception text!", | |
3497 | "java.sql.SQLException: " + CsvResources.getString("statementCancelled"), | |
3498 | "" + e); | |
3499 | } | |
3500 | ||
3501 | conn.close(); | |
3502 | } | |
3503 | ||
3504 | @Test | |
3459 | 3505 | public void testTrimValues() throws SQLException |
3460 | 3506 | { |
3461 | 3507 | Properties props = new Properties(); |
4720 | 4766 | } |
4721 | 4767 | |
4722 | 4768 | @Test |
4769 | public void testWriteToCsvWithDates() throws SQLException, UnsupportedEncodingException, IOException | |
4770 | { | |
4771 | Properties props = new Properties(); | |
4772 | props.put("columnTypes", "Int,Date,Time,Timestamp"); | |
4773 | props.put("dateFormat", "dd-MMM-yy"); | |
4774 | props.put("timeFormat", "hh:mm:ss.SSS aa"); | |
4775 | props.put("timestampFormat", "yyyy-MM-dd'T'HH:mm:ss.SSS"); | |
4776 | if (!Locale.getDefault().equals(Locale.US)) | |
4777 | { | |
4778 | /* | |
4779 | * Ensure that test passes when running on non-English language computers. | |
4780 | */ | |
4781 | props.put("locale", Locale.US.toString()); | |
4782 | } | |
4783 | ||
4784 | Connection conn = DriverManager.getConnection("jdbc:relique:csv:" | |
4785 | + filePath, props); | |
4786 | Statement stmt = conn.createStatement(); | |
4787 | ResultSet results = stmt.executeQuery("SELECT * FROM sunil_date_time"); | |
4788 | ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); | |
4789 | PrintStream printStream = new PrintStream(byteStream); | |
4790 | ||
4791 | /* | |
4792 | * Check that writing ResultSet to CSV file generates exactly the same CSV file | |
4793 | * that the query was originally read from. | |
4794 | */ | |
4795 | boolean writeHeaderLine = true; | |
4796 | CsvDriver.writeToCsv(results, printStream, writeHeaderLine); | |
4797 | ||
4798 | BufferedReader reader1 = null; | |
4799 | BufferedReader reader2 = null; | |
4800 | try | |
4801 | { | |
4802 | reader1 = new BufferedReader(new FileReader(filePath + File.separator + "sunil_date_time.csv")); | |
4803 | reader2 = new BufferedReader(new StringReader(byteStream.toString("US-ASCII"))); | |
4804 | String line1 = reader1.readLine(); | |
4805 | String line2 = reader2.readLine(); | |
4806 | ||
4807 | while (line1 != null || line2 != null) | |
4808 | { | |
4809 | assertTrue("line1 is null", line1 != null); | |
4810 | assertTrue("line2 is null", line2 != null); | |
4811 | assertTrue("lines do not match", line1.equalsIgnoreCase(line2)); | |
4812 | line1 = reader1.readLine(); | |
4813 | line2 = reader2.readLine(); | |
4814 | } | |
4815 | assertNull("line1 not empty", line1); | |
4816 | assertNull("line2 not empty", line2); | |
4817 | } | |
4818 | finally | |
4819 | { | |
4820 | if (reader1 != null) | |
4821 | reader1.close(); | |
4822 | if (reader2 != null) | |
4823 | reader2.close(); | |
4824 | } | |
4825 | } | |
4826 | ||
4827 | @Test | |
4723 | 4828 | public void testBooleanConversion() throws SQLException, |
4724 | 4829 | ParseException |
4725 | 4830 | { |