diff --git a/docs/manual.html b/docs/manual.html index f070d98d..19184b40 100644 --- a/docs/manual.html +++ b/docs/manual.html @@ -2845,4 +2845,4 @@

- \ No newline at end of file + diff --git a/src/docbkx/manual.xml b/src/docbkx/manual.xml index b29d637c..76201f0a 100644 --- a/src/docbkx/manual.xml +++ b/src/docbkx/manual.xml @@ -2614,6 +2614,18 @@ java.sql.SQLException: ORA-00942: table or view does not exist Defaults to false. + + dateformat + + The format for how date values are displayed. Setting + to default causes date values + to be fetched and rendered via ResultSet.getString. + Any other setting results in fetch via ResultSet.getObject + and rendering via + java.text.SimpleDateFormat. For example, + the setting "YYYY_MM_dd" yields values like "1970_01_01". + + fastconnect @@ -2710,7 +2722,7 @@ java.sql.SQLException: ORA-00942: table or view does not exist and rendering via java.text.DecimalFormat. For example, the setting "0.###E0" yields scientific notation - with up to three fractional digits. + with up to three fractional digits, such as "6.022E23". @@ -2775,6 +2787,30 @@ java.sql.SQLException: ORA-00942: table or view does not exist false. + + timeformat + + The format for how time values are displayed. Setting + to default causes time values + to be fetched and rendered via ResultSet.getString. + Any other setting results in fetch via ResultSet.getObject + and rendering via + java.text.SimpleDateFormat. For example, + the setting "HH:mm:ss" yields values like "14:12:11". + + + + timestampformat + + The format for how date values are displayed. Setting + to default causes date values + to be fetched and rendered via ResultSet.getString. + Any other setting results in fetch via ResultSet.getObject + and rendering via + java.text.SimpleDateFormat. For example, + the setting "dd/MM/YYYY'T'HH:mm:ss" yields values like "01/01/1970T12:32:12". + + trimscripts diff --git a/src/main/java/sqlline/Rows.java b/src/main/java/sqlline/Rows.java index 04a15626..a43402b1 100644 --- a/src/main/java/sqlline/Rows.java +++ b/src/main/java/sqlline/Rows.java @@ -16,8 +16,11 @@ import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.sql.Types; +import java.text.DateFormat; import java.text.DecimalFormat; +import java.text.Format; import java.text.NumberFormat; +import java.text.SimpleDateFormat; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -34,17 +37,36 @@ abstract class Rows implements Iterator { final Map> tablePrimaryKeysCache = new HashMap>(); final NumberFormat numberFormat; + final DateFormat dateFormat; + final DateFormat timeFormat; + final DateFormat timestampFormat; Rows(SqlLine sqlLine, ResultSet rs) throws SQLException { this.sqlLine = sqlLine; rsMeta = rs.getMetaData(); int count = rsMeta.getColumnCount(); primaryKeys = new Boolean[count]; - if (sqlLine.getOpts().getNumberFormat().equals("default")) { + if (sqlLine.getOpts().getNumberFormat().equals(SqlLineOpts.DEFAULT)) { numberFormat = null; } else { numberFormat = new DecimalFormat(sqlLine.getOpts().getNumberFormat()); } + if (sqlLine.getOpts().getDateFormat().equals(SqlLineOpts.DEFAULT)) { + dateFormat = null; + } else { + dateFormat = new SimpleDateFormat(sqlLine.getOpts().getDateFormat()); + } + if (sqlLine.getOpts().getTimeFormat().equals(SqlLineOpts.DEFAULT)) { + timeFormat = null; + } else { + timeFormat = new SimpleDateFormat(sqlLine.getOpts().getTimeFormat()); + } + if (sqlLine.getOpts().getTimestampFormat().equals(SqlLineOpts.DEFAULT)) { + timestampFormat = null; + } else { + timestampFormat = + new SimpleDateFormat(sqlLine.getOpts().getTimestampFormat()); + } } public void remove() { @@ -165,14 +187,7 @@ class Row { case Types.DOUBLE: case Types.DECIMAL: case Types.NUMERIC: - o = rs.getObject(i + 1); - if (o == null) { - values[i] = "null"; - } else if (numberFormat != null) { - values[i] = numberFormat.format(o); - } else { - values[i] = o.toString(); - } + setFormat(rs.getObject(i + 1), numberFormat, i); break; case Types.BIT: case Types.CLOB: @@ -183,12 +198,16 @@ class Row { case Types.ROWID: case Types.NCLOB: case Types.SQLXML: - o = rs.getObject(i + 1); - if (o == null) { - values[i] = "null"; - } else { - values[i] = o.toString(); - } + setFormat(rs.getObject(i + 1), null, i); + break; + case Types.TIME: + setFormat(rs.getObject(i + 1), timeFormat, i); + break; + case Types.DATE: + setFormat(rs.getObject(i + 1), dateFormat, i); + break; + case Types.TIMESTAMP: + setFormat(rs.getObject(i + 1), timestampFormat, i); break; default: values[i] = rs.getString(i + 1); @@ -197,6 +216,16 @@ class Row { sizes[i] = values[i] == null ? 1 : values[i].length(); } } + + private void setFormat(Object o, Format format, int i) { + if (o == null) { + values[i] = "null"; + } else if (format != null) { + values[i] = format.format(o); + } else { + values[i] = o.toString(); + } + } } /** @@ -236,7 +265,7 @@ private Set loadAndCachePrimaryKeysForTable(TableKey tableKey) { * schema and table name). The returned set may be cached as a result of * previous requests for the same table key. * - * The result cannot be considered authoritative as since it depends on + *

The result cannot be considered authoritative as since it depends on * whether the JDBC driver property implements * {@link java.sql.ResultSetMetaData#getTableName} and many drivers/databases * do not. diff --git a/src/main/java/sqlline/SqlLineOpts.java b/src/main/java/sqlline/SqlLineOpts.java index 0fe09c04..e067f465 100644 --- a/src/main/java/sqlline/SqlLineOpts.java +++ b/src/main/java/sqlline/SqlLineOpts.java @@ -14,6 +14,7 @@ import java.io.*; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.text.SimpleDateFormat; import java.util.*; import jline.TerminalFactory; @@ -27,6 +28,8 @@ class SqlLineOpts implements Completer { public static final String PROPERTY_PREFIX = "sqlline."; public static final String PROPERTY_NAME_EXIT = PROPERTY_PREFIX + "system.exit"; + public static final String DEFAULT = "default"; + public static final Date TEST_DATE = new Date(); private SqlLine sqlLine; private boolean autoSave = false; private boolean silent = false; @@ -41,7 +44,10 @@ class SqlLineOpts implements Completer { private boolean showElapsedTime = true; private boolean showWarnings = true; private boolean showNestedErrs = false; - private String numberFormat = "default"; + private String numberFormat = DEFAULT; + private String dateFormat = DEFAULT; + private String timeFormat = DEFAULT; + private String timestampFormat = DEFAULT; private int maxWidth = TerminalFactory.get().getWidth(); private int maxHeight = TerminalFactory.get().getHeight(); private int maxColumnWidth = 15; @@ -233,7 +239,11 @@ public boolean set(String key, String value, boolean quiet) { // that sqlline's error() output chokes because it depends // on properties like text coloring that can get set in // arbitrary order. - System.err.println(sqlLine.loc("error-setting", key, e)); + System.err.println( + sqlLine.loc( + "error-setting", + key, + e.getCause() == null ? e : e.getCause())); } return false; } @@ -295,6 +305,30 @@ public String getNumberFormat() { return this.numberFormat; } + public String getDateFormat() { + return this.dateFormat; + } + + public void setDateFormat(String dateFormat) { + this.dateFormat = getValidDateTimePatternOrThrow(dateFormat); + } + + public String getTimeFormat() { + return this.timeFormat; + } + + public void setTimeFormat(String timeFormat) { + this.timeFormat = getValidDateTimePatternOrThrow(timeFormat); + } + + public String getTimestampFormat() { + return this.timestampFormat; + } + + public void setTimestampFormat(String timestampFormat) { + this.timestampFormat = getValidDateTimePatternOrThrow(timestampFormat); + } + public void setMaxWidth(int maxWidth) { this.maxWidth = maxWidth; } @@ -446,6 +480,19 @@ public void setRun(String runFile) { public String getRun() { return this.runFile; } + + private String getValidDateTimePatternOrThrow(String dateTimePattern) { + if (DEFAULT.equals(dateTimePattern)) { + return dateTimePattern; + } + try { + SimpleDateFormat sdf = new SimpleDateFormat(dateTimePattern); + sdf.format(TEST_DATE); + } catch (Exception e) { + throw new IllegalArgumentException(e.getMessage()); + } + return dateTimePattern; + } } // End SqlLineOpts.java diff --git a/src/main/resources/sqlline/SqlLine.properties b/src/main/resources/sqlline/SqlLine.properties index 0d5ac451..e152935b 100644 --- a/src/main/resources/sqlline/SqlLine.properties +++ b/src/main/resources/sqlline/SqlLine.properties @@ -58,6 +58,8 @@ help-set: Set a sqlline variable\ \nautoSave true/false Automatically save preferences\ \ncolor true/false Control whether color is used\ \n for display\ +\ndateFormat pattern Format dates using\ +\n SimpleDateFormat pattern\ \nfastConnect true/false Skip building table/column list\ \n for tab-completion\ \nforce true/false Continue running script even\ @@ -102,8 +104,12 @@ help-set: Set a sqlline variable\ \nshowNestedErrs true/false Display nested errors\ \nshowWarnings true/false Display connection warnings\ \nsilent true/false Be more silent\ +\ntimeFormat pattern Format times using\ +\n SimpleDateFormat pattern\ \ntimeout integer Query timeout in seconds; less\ \n than zero means no timeout\ +\ntimestampFormat pattern Format timestamps using\ +\n SimpleDateFormat pattern\ \ntrimScripts true/false Remove trailing spaces from\ \n lines read from script files\ \nverbose true/false Show verbose error messages and\ @@ -215,6 +221,9 @@ cmd-usage: Usage: java sqlline.SqlLine \n \ \ --showWarnings=[true/false] display connection warnings\n \ \ --showNestedErrs=[true/false] display nested errors\n \ \ --numberFormat=[pattern] format numbers using DecimalFormat pattern\n \ +\ --dateFormat=[pattern] format dates using SimpleDateFormat pattern\n \ +\ --timeFormat=[pattern] format times using SimpleDateFormat pattern\n \ +\ --timestampFormat=[pattern] format timestamps using SimpleDateFormat pattern\n \ \ --force=[true/false] continue running script even after errors\n \ \ --maxWidth=MAXWIDTH the maximum width of the terminal\n \ \ --maxColumnWidth=MAXCOLWIDTH the maximum width to use when displaying columns\n \ diff --git a/src/main/resources/sqlline/manual.txt b/src/main/resources/sqlline/manual.txt index f09068dc..8ceee59d 100644 --- a/src/main/resources/sqlline/manual.txt +++ b/src/main/resources/sqlline/manual.txt @@ -95,6 +95,7 @@ Parameter Reference autocommit autosave color +dateformat fastconnect force headerinterval @@ -111,6 +112,8 @@ shownestederrs showtime showwarnings silent +timeformat +timestampformat trimscripts verbose JDBC Driver Support @@ -209,6 +212,7 @@ Parameter Reference autocommit autosave color +dateformat fastconnect force headerinterval @@ -225,6 +229,8 @@ shownestederrs showtime showwarnings silent +timeformat +timestampformat trimscripts verbose JDBC Driver Support @@ -1889,6 +1895,7 @@ Parameter Reference autocommit autosave color +dateformat fastconnect force headerinterval @@ -1905,6 +1912,8 @@ shownestederrs showtime showwarnings silent +timeformat +timestampformat trimscripts verbose autocommit @@ -1919,9 +1928,13 @@ color If true, then output to the terminal will use color for a more pleasing visual experience. Requires that the terminal support ANSI control codes (most do). Defaults to false. +dateformat + +The format for how date values are displayed. Setting to default causes date values to be fetched and rendered via ResultSet.getString. Any other setting results in fetch via ResultSet.getObject and rendering via java.text.SimpleDateFormat. For example, the setting "YYYY_MM_dd" yields values like "1970_01_01". + fastconnect -When false, any new connection will cause SQLLine to access information about the available tables and columns in order to provide them as candidates for tab-completion. This can be a very slow operation for some databases, do by default it is off. Table and column information can always be explicitely retrieved using the rehash command. +When false, any new connection will cause SQLLine to access information about the available tables and columns in order to provide them as candidates for tab-completion. This can be a very slow operation for some databases, do by default it is off. Table and column information can always be explicitly retrieved using the rehash command. force @@ -1953,7 +1966,7 @@ The maximum width to display before truncating data when using the "table" outpu numberformat -The format for how numeric values are displayed. Setting to default causes numeric values to be fetched and rendered via ResultSet.getString. Any other setting results in fetch via ResultSet.getObject and rendering via java.text.DecimalFormat. For example, the setting "0.###E0" yields scientific notation with up to three fractional digits. +The format for how numeric values are displayed. Setting to default causes numeric values to be fetched and rendered via ResultSet.getString. Any other setting results in fetch via ResultSet.getObject and rendering via java.text.DecimalFormat. For example, the setting "0.###E0" yields scientific notation with up to three fractional digits, values like "6.022E23". outputformat @@ -1983,6 +1996,14 @@ silent If true, then reduce the amount of informational messages displayed. Useful for redirecting a sqlline command to a file for later parsing. Defaults to false. +timeformat + +The format for how time values are displayed. Setting to default causes time values to be fetched and rendered via ResultSet.getString. Any other setting results in fetch via ResultSet.getObject and rendering via java.text.SimpleDateFormat. For example, the setting "HH:mm:ss" yields values like "14:12:11". + +timestampformat + +The format for how timestamp values are displayed. Setting to default causes timestamp values to be fetched and rendered via ResultSet.getString. Any other setting results in fetch via ResultSet.getObject and rendering via java.text.SimpleDateFormat. For example, the setting "dd/MM/YYYY'T'HH:mm:ss" yields values like "01/01/1970T12:32:12". + trimscripts If true, then trim leading and trailing whitespace from lines as they are processed in scripts; otherwise, preserve whitespace. Defaults to true. diff --git a/src/test/java/sqlline/SqlLineArgsTest.java b/src/test/java/sqlline/SqlLineArgsTest.java index b22187da..0f9f066f 100644 --- a/src/test/java/sqlline/SqlLineArgsTest.java +++ b/src/test/java/sqlline/SqlLineArgsTest.java @@ -212,8 +212,8 @@ public void testScriptFilenameWithSpace() throws Throwable { Pair pair = runScript(scriptFile, true); assertThat(pair.status, equalTo(SqlLine.Status.OK)); - assertThat(pair.output, allOf(containsString(" 33 "), - containsString(" 123 "))); + assertThat(pair.output, + allOf(containsString(" 33 "), containsString(" 123 "))); } /** @@ -623,7 +623,7 @@ static class Pair { * HIVE-4566, "NullPointerException if typeinfo and nativesql commands are * executed at beeline before a DB connection is established". * - * @throws UnsupportedEncodingException + * @throws UnsupportedEncodingException on unsupported encoding */ @Test public void testNPE() throws UnsupportedEncodingException { @@ -655,6 +655,84 @@ public void testTablesCsv() throws Throwable { containsString("'PUBLIC','SCOTT','SALGRADE','TABLE','',"))); } + @Test + public void testTimeFormat() throws Throwable { + // Use System.err as it is used in sqlline.SqlLineOpts#set + final PrintStream originalErr = System.err; + try { + final ByteArrayOutputStream errBaos = new ByteArrayOutputStream(); + System.setErr(new PrintStream(errBaos)); + + // successful patterns + final String okTimeFormat = "!set timeFormat HH:mm:ss\n"; + final String defaultTimeFormat = "!set timeFormat default\n"; + final String okDateFormat = "!set dateFormat YYYY-MM-dd\n"; + final String defaultDateFormat = "!set dateFormat default\n"; + final String okTimestampFormat = "!set timestampFormat default\n"; + final String defaultTimestampFormat = "!set timestampFormat default\n"; + + // successful cases + final SqlLine sqlLine = new SqlLine(); + sqlLine.runCommands(Arrays.asList(okTimeFormat), new DispatchCallback()); + assertThat(errBaos.toString("UTF8"), + not( + anyOf(containsString("Error setting configuration"), + containsString("Exception")))); + sqlLine.runCommands( + Arrays.asList(defaultTimeFormat), new DispatchCallback()); + assertThat(errBaos.toString("UTF8"), + not( + anyOf(containsString("Error setting configuration"), + containsString("Exception")))); + sqlLine.runCommands(Arrays.asList(okDateFormat), new DispatchCallback()); + assertThat(errBaos.toString("UTF8"), + not( + anyOf(containsString("Error setting configuration"), + containsString("Exception")))); + sqlLine.runCommands(Arrays.asList(defaultDateFormat), + new DispatchCallback()); + assertThat(errBaos.toString("UTF8"), + not( + anyOf(containsString("Error setting configuration"), + containsString("Exception")))); + sqlLine.runCommands(Arrays.asList(okTimestampFormat), + new DispatchCallback()); + assertThat(errBaos.toString("UTF8"), + not( + anyOf(containsString("Error setting configuration"), + containsString("Exception")))); + sqlLine.runCommands(Arrays.asList(defaultTimestampFormat), + new DispatchCallback()); + assertThat(errBaos.toString("UTF8"), + not( + anyOf(containsString("Error setting configuration"), + containsString("Exception")))); + + // failed patterns + final String wrongTimeFormat = "!set timeFormat qwerty\n"; + final String wrongDateFormat = "!set dateFormat ASD\n"; + final String wrongTimestampFormat = + "!set timestampFormat 'YYYY-MM-ddTHH:MI:ss'\n"; + + // failed cases + sqlLine.runCommands(Arrays.asList(wrongTimeFormat), + new DispatchCallback()); + assertThat(errBaos.toString("UTF8"), + containsString("Illegal pattern character 'q'")); + sqlLine.runCommands(Arrays.asList(wrongDateFormat), + new DispatchCallback()); + assertThat(errBaos.toString("UTF8"), + containsString("Illegal pattern character 'A'")); + sqlLine.runCommands(Arrays.asList(wrongTimestampFormat), + new DispatchCallback()); + assertThat(errBaos.toString("UTF8"), + containsString("Illegal pattern character 'T'")); + } finally { + // Set error stream back + System.setErr(originalErr); + } + } + @Test public void testTables() throws Throwable { // Set width so we don't inherit from the current terminal.