Skip to content

SQL: Implement DATETIME_FORMAT function for date/time formatting #54832

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Apr 8, 2020

Conversation

matriv
Copy link
Contributor

@matriv matriv commented Apr 6, 2020

Implement DATETIME_FORMAT(<date/datetime/time>, ) function
which allows for formatting a timestamp to the specified format. The patterns
allowed as those of java.time.format.DateTimeFormatter.

Related to #53714

Implement TO_CHAR(<date/datetime/time>, <pattern>) function with
aliases: TOCHAR, DATE_FORMAT, FORMAT which allows for formatting a
timestamp to the specified format. The patterns allowed as those of
`java.time.format.DateTimeFormatter`.

Related to elastic#53714
@elasticmachine
Copy link
Collaborator

Pinging @elastic/es-ql (:Query Languages/SQL)

Copy link
Member

@costin costin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left some comments - the only remark I have is about the function name.
Looks good otherwise.

*Description*: Returns the date/datetime/time as a string using the format specified in the 2nd argument. The formatting
pattern used is the one from
https://docs.oracle.com/en/java/javase/14/docs/api/java.base/java/time/format/DateTimeFormatter.html[`java.time.format.DateTimeFormatter`].
If any of the two arguments is `null` or the pattern is an empty string a `null` is returned.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

drop a : is string null is returned.
If the pattern is empty, null be returned - is that the behavior of the other DBs?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MySQL and PostgreSQL return null for empty pattern, SQL Server returns string in the default format.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's go with the PostgreSQL.

@@ -60,7 +61,8 @@ DAY_OF_WEEK |SCALAR
DAY_OF_YEAR |SCALAR
DOM |SCALAR
DOW |SCALAR
DOY |SCALAR
DOY |SCALAR
FORMAT |SCALAR
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FORMAT seems a bit too generic - is there any DB that uses it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SQL Server

import static org.elasticsearch.xpack.ql.expression.TypeResolutions.isString;
import static org.elasticsearch.xpack.sql.expression.SqlTypeResolutions.isDateOrTime;

public class ToChar extends BinaryDateTimeFunction {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is the function called ToChar and not DateFormat?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No real reason, I just started with the to_char from PostgreSQL.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will rename it according to the name of the function we decide to use finally.

@@ -404,6 +404,48 @@ include-tagged::{sql-specs}/docs/docs.csv-spec[dateDiffDateTimeMinutes]
include-tagged::{sql-specs}/docs/docs.csv-spec[dateDiffDateMinutes]
--------------------------------------------------

[[sql-functions-datetime-dateformat]]
==== `DATE_FORMAT/FORMAT/TO_CHAR/TOCHAR`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do all the function names exist in other DBs? I'd like one which is unique to us due to the Java time format - maybe datetime_format (and datetime_parse) - or vice-versa - format_datetime and parse_datetime.

My point is, I think going forward it would be good to have our own function which uses Java time and then support the rest of the DB functions as translating implementation for the given arguments to our class.
For example DATE_FORMAT which is MySQL specific would exist as such but the MySQL arguments would then be converted into the java.time pattern.
Otherwise looking like MySQL but not supporting its pattern is confusing.

Since the pattern support is tedious, I'm fine with doing that in a separate support for the future. However I'd like to clarify the approach in the docs.

MySQL DATE_FORMAT: https://dev.mysql.com/doc/refman/8.0/en/date-and-time-functions.html#function_date-format
SQL Server FORMAT: https://docs.microsoft.com/en-us/sql/t-sql/functions/format-transact-sql?view=sql-server-ver15#ExampleD
Postgres TO_CHAR: https://www.postgresql.org/docs/9.1/functions-formatting.html
Oracle TOCHAR: https://docs.oracle.com/cd/B19306_01/server.102/b14200/sql_elements004.htm#i34510

Note that outside MySQL, the rest of the functions accept multiple formats and data types so to avoid confusion, I would not create aliases for them yet.
But rather treat them separately since if we are to introduce such functions, we would essentially have to obey their semantics rather then ours.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All of DATE_FORMAT, FORMAT and TO_CHAR and TOCHAR are used in DBs and I agree with the confusion regarding the patterns so I'd go for datetime_format/parse with our own ES-SQL impl (java.time).

@astefan @bpintea Do you agree with the naming?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed to DATETIME_FORMAT

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed now, but it seems sane to me, 👍.

@matriv matriv requested a review from costin April 7, 2020 17:59
Copy link
Contributor

@bpintea bpintea left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nicely done.

Copy link
Member

@costin costin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Copy link
Contributor

@astefan astefan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Left just couple of insignificant comments.

@@ -177,6 +178,7 @@ public SqlFunctionRegistry() {
def(MonthName.class, MonthName::new, "MONTH_NAME", "MONTHNAME"),
def(MonthOfYear.class, MonthOfYear::new, "MONTH_OF_YEAR", "MONTH"),
def(SecondOfMinute.class, SecondOfMinute::new, "SECOND_OF_MINUTE", "SECOND"),
def(DateTimeFormat.class, DateTimeFormat::new, "DATETIME_FORMAT"),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any particular reason for not using the alphabetical order (as it is now in the registry)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, will fix, It was because of the original name ToChar.

Comment on lines +42 to +48
format(
null,
"first argument of [{}] must be one of {} or their aliases; found value [{}]",
sourceText(),
validDateTimeFieldValues(),
Expressions.name(left())
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:-(

if (resolution.unresolved()) {
return resolution;
}
resolution = isString(right(), sourceText(), Expressions.ParamOrdinal.SECOND);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isString is enough here? (vs. isStringAndExact)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, it's enough, tried for example:

SELECT * FROM test WHERE DATETIME_FORMAT(field1, 'uuuu-MM-dd') = '2020-04-08';
SELECT DATETIME_FORMAT(field1, 'uuuu-MM-dd') FROM test;

on an index:

{
    "mappings": {
    "properties": {
      "field1": {
        "type": "date"
      },
      "field2": {
      	"type": "text"
      }
    }
  }
}

and data:

{
    "field1": "05/04/2020 10:20:30",
    "field2": "uuuu-MM-dd"
}

and it works.

@matriv matriv changed the title SQL: Implement TO_CHAR function for date/time formatting SQL: Implement DATETIME_FORMAT function for date/time formatting Apr 8, 2020
@matriv matriv merged commit 72be0b5 into elastic:master Apr 8, 2020
@matriv matriv deleted the impl-tochar branch April 8, 2020 09:49
matriv added a commit that referenced this pull request Apr 8, 2020
) (#54942)

Implement DATETIME_FORMAT(<date/datetime/time>, ) function
which allows for formatting a timestamp to the specified format. The
patterns allowed as those of java.time.format.DateTimeFormatter.

Related to #53714

(cherry picked from commit 72be0b5)
vonsowic added a commit to vonsowic/elasticsearch that referenced this pull request Oct 15, 2020
Implement to_char according to the PostgreSQL spec: https://www.postgresql.org/docs/9.1/functions-formatting.html by translating to the java.time patterns used in DATETIME_FORMAT.
Follows: elastic#54832
palesz pushed a commit to palesz/elasticsearch that referenced this pull request Dec 17, 2020
Implement to_char according to the PostgreSQL spec: https://www.postgresql.org/docs/9.1/functions-formatting.html by translating to the java.time patterns used in DATETIME_FORMAT.
Follows: elastic#54832
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants