-
Notifications
You must be signed in to change notification settings - Fork 2.5k
[CALCITE-7337] Add age function (enabled in Postgresql library) #4703
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
Conversation
xuzifu666
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Left some comments,please check them.
testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
Outdated
Show resolved
Hide resolved
| * | ||
| * @see <a href="https://www.postgresql.org/docs/current/functions-datetime.html">PostgreSQL AGE</a> | ||
| */ | ||
| @LibraryOperator(libraries = {POSTGRESQL}, exceptLibraries = {REDSHIFT}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What are the reasons for excluding REDSHIFT here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What are the reasons for excluding
REDSHIFThere?
Because Redshift is based on PostgreSQL, but there are many differences, Redshift does not support age
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps adding DUCKDB is necessary? I'm not very familiar with this area.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe miss here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Currently, there is no DuckDB library, only a DuckDB dialect, so DuckDB maybe cannot be considered here.
May I ask if this is caused by a code issue? @xuzifu666
|
I have rebuilded it, please wait for it. |
|
I will be reviewing this PR in the next couple of days. |
caicancai
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Jira titles should be in the form of something like "Add LOG1P function (enabled in Spark library)".
| /** Test case for | ||
| * <a href="https://issues.apache.org/jira/browse/CALCITE-7337">[CALCITE-7337] | ||
| * There is no age function that supports pgSQL</a>. */ | ||
| @Test void testAgePg() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Need to add invalid date format test?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Need to add invalid date format test?
done, thanks
| // Test single timestamp argument (relative to current time) | ||
| f.checkType("age(timestamp '2023-12-25')", "VARCHAR NOT NULL"); | ||
|
|
||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Extra blank lines
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Extra blank lines
done
| public static String age(long timestamp) { | ||
| // Get current date at midnight in UTC | ||
| long currentTimestamp = Instant.now() | ||
| .atZone(ZoneOffset.UTC) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does it have to be UTC?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does it have to be UTC?
This is just a default strategy, other functions also use UTC. Converting time uniformly to UTC can avoid inaccurate calculations caused by different time zones
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you please post the relevant PG documentation?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you please post the relevant PG documentation?
This pr and jira have doc link,We will find that PG in the document converts the time to the current session time zone and then calculates it
https://www.postgresql.org/docs/current/functions-datetime.html
done |
| FUNCTIONAL_DEPENDENCY_GET_FDS(FunctionalDependency.class, "getFDs"); | ||
| FUNCTIONAL_DEPENDENCY_GET_FDS(FunctionalDependency.class, "getFDs"), | ||
| AGE(SqlFunctions.class, "age", long.class, long.class), | ||
| AGE_ONE_PARAM(SqlFunctions.class, "age", long.class); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm wondering if we could refer to the implementation of SqlSubstringFunction, since SUBSTRING can also accept two or three parameters. Is it possible to avoid declaring two functions based on the number of input parameters here?
site/_docs/reference.md
Outdated
| | o p r h | TO_DATE(string, format) | Converts *string* to a date using the format *format* | ||
| | o p r | TO_TIMESTAMP(string, format) | Converts *string* to a timestamp using the format *format* | ||
| | p | AGE(timestamp) | Returns the difference between the current timestamp and the specified timestamp | ||
| | p | AGE(timestamp1, timestamp2) | Returns the difference between timestamp1 and timestamp2 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I saw the syntax | b | TRUNC(numeric1 [, integer2 ]) near this line, and I think this form should be used.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
| define(FORMAT_TIME, datetimeFormatImpl); | ||
| define(FORMAT_TIMESTAMP, datetimeFormatImpl); | ||
|
|
||
| defineReflective(AGE_PG, BuiltInMethod.AGE.method, BuiltInMethod.AGE_ONE_PARAM.method); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we can support variable input parameters in a single execution function, then we wouldn't need to use defineReflective here, would we? Also, I noticed that DuckDB also supports this function; could we support it as well, and stop using the name AGE_PG?
duckdb> SELECT AGE(timestamp '2023-12-25 08:08:08.080', timestamp '2020-01-01') FROM (VALUES (1)) t;
┌────────────────────────────────────────────────────────────────────────────────────┐
│ age(CAST('2023-12-25 08:08:08.080' AS TIMESTAMP), CAST('2020-01-01' AS TIMESTAMP)) │
╞════════════════════════════════════════════════════════════════════════════════════╡
│ 0 years 47 mons 24 days 8 hours 8 mins 8.080000000 secs │
└────────────────────────────────────────────────────────────────────────────────────┘
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we can support variable input parameters in a single execution function, then we wouldn't need to use
defineReflectivehere, would we? Also, I noticed that DuckDB also supports this function; could we support it as well, and stop using the nameAGE_PG?duckdb> SELECT AGE(timestamp '2023-12-25 08:08:08.080', timestamp '2020-01-01') FROM (VALUES (1)) t; ┌────────────────────────────────────────────────────────────────────────────────────┐ │ age(CAST('2023-12-25 08:08:08.080' AS TIMESTAMP), CAST('2020-01-01' AS TIMESTAMP)) │ ╞════════════════════════════════════════════════════════════════════════════════════╡ │ 0 years 47 mons 24 days 8 hours 8 mins 8.080000000 secs │ └────────────────────────────────────────────────────────────────────────────────────┘
I strongly agree with this suggestion, done,thanks
| define(FORMAT_TIME, datetimeFormatImpl); | ||
| define(FORMAT_TIMESTAMP, datetimeFormatImpl); | ||
|
|
||
| defineMethod(AGE_PG, BuiltInMethod.AGE.method, NullPolicy.STRICT); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AGE_PG change to AGE?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AGE_PG change to AGE?
ok,done
|
@cjj2010 Thank you for your prompt response, but your commit messages are the same every time, which makes it difficult to understand the focus of your changes. You should make each commit message reflect the content of your changes, and after approval, squash the commit messages and ensure they match the Jira ticket title. |
done,thanks |
|
The current code looks fine for me, but I'm not very familiar with the implementation of functions. @xuzifu666 and @caicancai are more familiar with this part, so please see if they have any other comments. |
|
There are still conflicts with the main branch, which need to be resolved. |
|
As @xuzifu666 said, I don't think you need to make so many extra commits to indicate what was changed in each part. I understand @caicancai 's suggestion to include relevant commit descriptions later on. If the information you added doesn't affect the overall code, I think it's better to delete it, or you can compress these commits into one and wait for the final review. |
|
Let me try copilot review 😝 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR adds support for PostgreSQL's AGE function to Apache Calcite. The AGE function calculates the difference between two timestamps and returns it as a formatted string showing years, months, days, hours, minutes, and seconds. The function supports both one-argument (calculates age relative to current time) and two-argument (calculates difference between two timestamps) forms.
Key Changes
- Added AGE function implementation in SqlFunctions.java that calculates timestamp differences using Java's Period and Duration APIs
- Registered AGE as a PostgreSQL library function in SqlLibraryOperators.java with VARCHAR return type
- Added comprehensive test coverage including edge cases for leap years, month boundaries, and negative intervals
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java | Core implementation of the age calculation logic using Period and Duration APIs, supporting 1 or 2 timestamp arguments |
| core/src/main/java/org/apache/calcite/sql/fun/SqlLibraryOperators.java | PostgreSQL library operator definition with type inference and operand validation |
| core/src/main/java/org/apache/calcite/util/BuiltInMethod.java | Added AGE enum entry referencing the SqlFunctions.age method |
| core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java | Registered AGE function implementation with NullPolicy.STRICT |
| testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java | Comprehensive unit tests covering various timestamp scenarios, boundary conditions, null handling, and error cases |
| babel/src/test/java/org/apache/calcite/test/BabelTest.java | Integration tests validating end-to-end execution of AGE function with PostgreSQL library |
| site/_docs/reference.md | Documentation entry describing the AGE function syntax and behavior |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
core/src/main/java/org/apache/calcite/sql/fun/SqlLibraryOperators.java
Outdated
Show resolved
Hide resolved
core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
Outdated
Show resolved
Hide resolved
core/src/main/java/org/apache/calcite/sql/fun/SqlLibraryOperators.java
Outdated
Show resolved
Hide resolved
core/src/main/java/org/apache/calcite/sql/fun/SqlLibraryOperators.java
Outdated
Show resolved
Hide resolved
…ors.java Optimize Annotations Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
|
After accepting the suggestion to modify the previously submitted information and then rebase it to the main branch, conflicts will arise. Now that the conflicts have been resolved locally and resubmitted, the detector still shows conflicts. I have to switch to another branch again |





jira:https://issues.apache.org/jira/projects/CALCITE/issues/CALCITE-7337
we can refer postgresql document about Position:
https://www.postgresql.org/docs/current/functions-datetime.html
link: #4689