Skip to content

Conversation

@cjj2010
Copy link

@cjj2010 cjj2010 commented Dec 29, 2025

@xiedeyantu
Copy link
Member

Postgresql -> PostgreSQL

<html>
<body>
<!--StartFragment-->
SqlUnParserTest > testPrecedenceSetOps() STANDARD_OUT
--
  |   |   | invalid git log message 'Add age function (enabled in Postgresql library)'; Message contains 'Postgresql' word; use one of the following instead: [PostgreSQL]
  |   |   | FAILURE   0.2sec, org.apache.calcite.test.LintTest > testLintLog()
  |   |   | java.lang.AssertionError:
  |   |   | Expected: an empty collection
  |   |   | but: <[invalid git log message 'Add age function (enabled in Postgresql library)'; Message contains 'Postgresql' word; use one of the following instead: [PostgreSQL]]>
  |   |   | at org.hamcrest.MatcherAssert.assertThat(MatcherAssert.java:18)
  |   |   | at org.hamcrest.MatcherAssert.assertThat(MatcherAssert.java:6)
  |   |   | at org.apache.calcite.test.LintTest.testLintLog(LintTest.java:308)

<!--EndFragment-->
</body>
</html>

@cjj2010 cjj2010 changed the title [CALCITE-7337] Add age function (enabled in Postgresql library) [CALCITE-7337] Add age function (enabled in PostgreSQL library) Dec 29, 2025
@cjj2010
Copy link
Author

cjj2010 commented Dec 29, 2025

Postgresql -> PostgreSQL

<html>
<body>
<!--StartFragment-->
SqlUnParserTest > testPrecedenceSetOps() STANDARD_OUT
--
  |   |   | invalid git log message 'Add age function (enabled in Postgresql library)'; Message contains 'Postgresql' word; use one of the following instead: [PostgreSQL]
  |   |   | FAILURE   0.2sec, org.apache.calcite.test.LintTest > testLintLog()
  |   |   | java.lang.AssertionError:
  |   |   | Expected: an empty collection
  |   |   | but: <[invalid git log message 'Add age function (enabled in Postgresql library)'; Message contains 'Postgresql' word; use one of the following instead: [PostgreSQL]]>
  |   |   | at org.hamcrest.MatcherAssert.assertThat(MatcherAssert.java:18)
  |   |   | at org.hamcrest.MatcherAssert.assertThat(MatcherAssert.java:6)
  |   |   | at org.apache.calcite.test.LintTest.testLintLog(LintTest.java:308)

<!--EndFragment-->
</body>
</html>

done, thanks

}

/** SQL {@code AGE(timestamp1, timestamp2)} function. */
private static String age(long timestamp1, long timestamp2) {
Copy link
Member

Choose a reason for hiding this comment

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

Please take a look at this code and see if it meets your requirements. I tested it locally, and it passed your test cases. The current code implementation seems somewhat unclear.

  public static String age(long timestamp1, long timestamp2) {
    ZonedDateTime zdt1 = Instant.ofEpochMilli(timestamp1)
        .atZone(ZoneOffset.UTC);
    ZonedDateTime zdt2 = Instant.ofEpochMilli(timestamp2)
        .atZone(ZoneOffset.UTC);

    Period period = Period.between(zdt2.toLocalDate(), zdt1.toLocalDate());
    Duration duration = Duration.between(zdt2, zdt1);

    int years = period.getYears();
    int months = period.getMonths();
    int days = period.getDays();

    long totalSeconds = duration.getSeconds();
    long hours = (totalSeconds / 3600) % 24;
    long minutes = (totalSeconds / 60) % 60;
    long seconds = totalSeconds % 60;
    long millis = duration.toMillis() % 1000;

    return String.format(Locale.ROOT,
        "%d years %d mons %d days %d hours %d mins %.1f secs",
        years, months, days, hours, minutes, seconds + millis / 1000.0
    );
  }

Copy link
Author

Choose a reason for hiding this comment

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

ZonedDateTime zdt1 = Instant.ofEpochMilli(timestamp1)
.atZone(ZoneOffset.UTC);
ZonedDateTime zdt2 = Instant.ofEpochMilli(timestamp2)
.atZone(ZoneOffset.UTC);

Period period = Period.between(zdt2.toLocalDate(), zdt1.toLocalDate());
Duration duration = Duration.between(zdt2, zdt1);

int years = period.getYears();
int months = period.getMonths();
int days = period.getDays();

long totalSeconds = duration.getSeconds();
long hours = (totalSeconds / 3600) % 24;
long minutes = (totalSeconds / 60) % 60;
long seconds = totalSeconds % 60;
long millis = duration.toMillis() % 1000;

Thank you very much for your suggestion. The above code is being executed
SELECT AGE(timestamp '2023-12-25 00:00:00', timestamp '2020-01-01 23:59:59') FROM (VALUES (1)) t, Returned 3 years 11 mons 24 days 0 hours 0 mins 1.0 secs, but pgSQL returned
3 years 11 mons 23 days 0 hours 0 mins 1.0 secs, This test case did not pass, and one more thing is that String.form seems to be prohibited from use. The code I submitted for the first time did not pass the detection because of its use

Copy link
Member

Choose a reason for hiding this comment

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

Yes, you're right. I think we can just add the test cases I mentioned.

Copy link
Author

Choose a reason for hiding this comment

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

Yes, you're right. I think we can just add the test cases I mentioned.

Thank you for your reminder. We have added new test cases

checkSqlResult("postgresql",
"SELECT AGE(timestamp '2023-12-25 00:00:00', timestamp '2020-01-01 23:59:59') FROM (VALUES (1)) t",
"EXPR$0=3 years 11 mons 23 days 0 hours 0 mins 1.0 secs\n");

Copy link
Member

Choose a reason for hiding this comment

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

I think we should add another test case.

    checkSqlResult("postgresql",
        "SELECT AGE(timestamp '2023-12-25 00:00:00.101', timestamp '2020-01-01 23:59:59.202') FROM (VALUES (1)) t",
        "EXPR$0=3 years 11 mons 23 days 0 hours 0 mins 0.9 secs\n");

@xiedeyantu
Copy link
Member

xiedeyantu commented Dec 30, 2025

It was my oversight; I noticed that the results from pgsql are inconsistent with what you provided. The results you provided resemble the format of duckdb. please see pgsql result.
sql:

SELECT AGE(timestamp '2020-01-01 00:00:10', timestamp '2023-12-25 00:00:00') FROM (VALUES (1)) t;

current pr:

-3 years -11 mons -23 days -23 hours -59 mins -50.0 secs

pgsql:

 -3 years -11 mons -23 days -23:59:50

@sonarqubecloud
Copy link

@cjj2010
Copy link
Author

cjj2010 commented Dec 31, 2025

It was my oversight; I noticed that the results from pgsql are inconsistent with what you provided. The results you provided resemble the format of duckdb. please see pgsql result. sql:

SELECT AGE(timestamp '2020-01-01 00:00:10', timestamp '2023-12-25 00:00:00') FROM (VALUES (1)) t;

current pr:

-3 years -11 mons -23 days -23 hours -59 mins -50.0 secs

pgsql:

 -3 years -11 mons -23 days -23:59:50

I used multiple clients to verify this issue again and found that
The values returned by navicat, dbeaver, and pgadmin are -3 years -11 mons -23 days -23:59:50, as shown in the data grid
-3 years -11 mons -23 days -23 hours -59 mins -50.0 secs, In this situation, I agree that the format you mentioned should be used

@cjj2010
Copy link
Author

cjj2010 commented Dec 31, 2025

It was my oversight; I noticed that the results from pgsql are inconsistent with what you provided. The results you provided resemble the format of duckdb. please see pgsql result. sql:

SELECT AGE(timestamp '2020-01-01 00:00:10', timestamp '2023-12-25 00:00:00') FROM (VALUES (1)) t;

current pr:

-3 years -11 mons -23 days -23 hours -59 mins -50.0 secs

pgsql:

 -3 years -11 mons -23 days -23:59:50

I will change the logic of this part later. Thank you for your reminder to make this logic more rigorous

1 similar comment
@cjj2010
Copy link
Author

cjj2010 commented Dec 31, 2025

It was my oversight; I noticed that the results from pgsql are inconsistent with what you provided. The results you provided resemble the format of duckdb. please see pgsql result. sql:

SELECT AGE(timestamp '2020-01-01 00:00:10', timestamp '2023-12-25 00:00:00') FROM (VALUES (1)) t;

current pr:

-3 years -11 mons -23 days -23 hours -59 mins -50.0 secs

pgsql:

 -3 years -11 mons -23 days -23:59:50

I will change the logic of this part later. Thank you for your reminder to make this logic more rigorous

@xuzifu666
Copy link
Member

xuzifu666 commented Dec 31, 2025

Because previously closed two related PR, some comments were lost (we can check if some of the changes can be moved to jira or this pr, so that others can understand what you had done, just a suggestion).
If the output format as @xiedeyantu mentioned is correct, I think this PR should be basically completed.

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.

3 participants