Skip to content

Conversation

@cjj2010
Copy link

@cjj2010 cjj2010 commented Dec 22, 2025

Copy link
Member

@xuzifu666 xuzifu666 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,please check them.

*
* @see <a href="https://www.postgresql.org/docs/current/functions-datetime.html">PostgreSQL AGE</a>
*/
@LibraryOperator(libraries = {POSTGRESQL}, exceptLibraries = {REDSHIFT})
Copy link
Member

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?

Copy link
Author

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?

Because Redshift is based on PostgreSQL, but there are many differences, Redshift does not support age

Copy link
Member

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.

Copy link
Member

Choose a reason for hiding this comment

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

Maybe miss here?

Copy link
Member

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.

@cjj2010
Copy link
Author

cjj2010 commented Dec 22, 2025

image May I ask if this is caused by a code issue? @xuzifu666

@xiedeyantu
Copy link
Member

image May I ask if this is caused by a code issue? @xuzifu666

I have rebuilded it, please wait for it.

@caicancai caicancai self-assigned this Dec 24, 2025
@caicancai
Copy link
Member

I will be reviewing this PR in the next couple of days.

Copy link
Member

@caicancai caicancai left a 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() {
Copy link
Member

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?

Copy link
Author

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");


Copy link
Member

Choose a reason for hiding this comment

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

Extra blank lines

Copy link
Author

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)
Copy link
Member

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?

Copy link
Author

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

Copy link
Member

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?

Copy link
Author

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

@cjj2010 cjj2010 changed the title [CALCITE-7337] There is no age function that supports pgSQL [CALCITE-7337] Add age function (enabled in Postgresql library) Dec 25, 2025
@cjj2010
Copy link
Author

cjj2010 commented Dec 25, 2025

Jira titles should be in the form of something like "Add LOG1P function (enabled in Spark library)".

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);
Copy link
Member

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?

| 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
Copy link
Member

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.

Copy link
Author

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);
Copy link
Member

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                            │
└────────────────────────────────────────────────────────────────────────────────────┘

Copy link
Author

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                            │
└────────────────────────────────────────────────────────────────────────────────────┘

I strongly agree with this suggestion, done,thanks

define(FORMAT_TIME, datetimeFormatImpl);
define(FORMAT_TIMESTAMP, datetimeFormatImpl);

defineMethod(AGE_PG, BuiltInMethod.AGE.method, NullPolicy.STRICT);
Copy link
Member

@xiedeyantu xiedeyantu Dec 25, 2025

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?

Copy link
Author

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

@caicancai
Copy link
Member

@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.

@cjj2010
Copy link
Author

cjj2010 commented Dec 26, 2025

@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

@xiedeyantu
Copy link
Member

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.
By the way, I don't quite understand why so many additional submissions were added at the end.

@xuzifu666
Copy link
Member

There are still conflicts with the main branch, which need to be resolved.

@xiedeyantu
Copy link
Member

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.

@caicancai
Copy link
Member

Let me try copilot review 😝

Copy link

Copilot AI left a 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.

cjj2010 and others added 8 commits December 29, 2025 09:32
…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>
@sonarqubecloud
Copy link

@cjj2010
Copy link
Author

cjj2010 commented Dec 29, 2025

There are still conflicts with the main branch, which need to be resolved.

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
link: #4712

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants