[jOOQ/jOOQ#13053] java.lang.NoClassDefFoundError: org/postgresql/util/PGInterval when rendering SQL without the pgjdbc dependency
This commit is contained in:
parent
96b09d7686
commit
6f099c6cc9
12
jOOQ/pom.xml
12
jOOQ/pom.xml
@ -109,18 +109,6 @@
|
||||
<artifactId>jakarta.xml.bind-api</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.postgresql</groupId>
|
||||
<artifactId>postgresql</artifactId>
|
||||
<scope>provided</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -44,8 +44,6 @@ module org.jooq {
|
||||
|
||||
|
||||
|
||||
requires static org.postgresql.jdbc;
|
||||
|
||||
exports org.jooq;
|
||||
exports org.jooq.conf;
|
||||
exports org.jooq.exception;
|
||||
|
||||
@ -163,12 +163,12 @@ import static org.jooq.tools.jdbc.JDBCUtils.safeFree;
|
||||
import static org.jooq.tools.jdbc.JDBCUtils.wasNull;
|
||||
import static org.jooq.tools.reflect.Reflect.onClass;
|
||||
import static org.jooq.tools.reflect.Reflect.wrapper;
|
||||
import static org.jooq.util.postgres.PostgresUtils.toDayToSecond;
|
||||
import static org.jooq.util.postgres.PostgresUtils.toPGArray;
|
||||
import static org.jooq.util.postgres.PostgresUtils.toPGArrayString;
|
||||
import static org.jooq.util.postgres.PostgresUtils.toPGInterval;
|
||||
import static org.jooq.util.postgres.PostgresUtils.toYearToMonth;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Serializable;
|
||||
import java.io.StringReader;
|
||||
import java.lang.reflect.Modifier;
|
||||
@ -2429,12 +2429,6 @@ public class DefaultBinding<T, U> implements Binding<T, U> {
|
||||
|
||||
// [#566] Interval data types are best bound as Strings
|
||||
if (REQUIRE_PG_INTERVAL.contains(ctx.dialect()))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
ctx.render().visit(inline(toPGInterval(value).toString()));
|
||||
else
|
||||
super.sqlInline0(ctx, value);
|
||||
@ -2445,13 +2439,7 @@ public class DefaultBinding<T, U> implements Binding<T, U> {
|
||||
|
||||
// [#566] Interval data types are best bound as Strings
|
||||
if (REQUIRE_PG_INTERVAL.contains(ctx.dialect()))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
ctx.statement().setObject(ctx.index(), toPGInterval(value));
|
||||
ctx.statement().setString(ctx.index(), toPGInterval(value).toString());
|
||||
else
|
||||
ctx.statement().setString(ctx.index(), renderDTS(ctx, value));
|
||||
}
|
||||
@ -2463,20 +2451,16 @@ public class DefaultBinding<T, U> implements Binding<T, U> {
|
||||
|
||||
@Override
|
||||
final DayToSecond get0(BindingGetResultSetContext<U> ctx) throws SQLException {
|
||||
if (REQUIRE_PG_INTERVAL.contains(ctx.dialect())) {
|
||||
Object object = ctx.resultSet().getObject(ctx.index());
|
||||
return object == null ? null : PostgresUtils.toDayToSecond(object);
|
||||
}
|
||||
if (REQUIRE_PG_INTERVAL.contains(ctx.dialect()))
|
||||
return toDayToSecond(ctx.resultSet().getString(ctx.index()));
|
||||
else
|
||||
return parseDTS(ctx, ctx.resultSet().getString(ctx.index()));
|
||||
}
|
||||
|
||||
@Override
|
||||
final DayToSecond get0(BindingGetStatementContext<U> ctx) throws SQLException {
|
||||
if (REQUIRE_PG_INTERVAL.contains(ctx.dialect())) {
|
||||
Object object = ctx.statement().getObject(ctx.index());
|
||||
return object == null ? null : PostgresUtils.toDayToSecond(object);
|
||||
}
|
||||
if (REQUIRE_PG_INTERVAL.contains(ctx.dialect()))
|
||||
return toDayToSecond(ctx.statement().getString(ctx.index()));
|
||||
else
|
||||
return parseDTS(ctx, ctx.statement().getString(ctx.index()));
|
||||
}
|
||||
@ -5548,12 +5532,6 @@ public class DefaultBinding<T, U> implements Binding<T, U> {
|
||||
|
||||
// [#566] Interval data types are best bound as Strings
|
||||
if (REQUIRE_PG_INTERVAL.contains(ctx.dialect()))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
ctx.render().visit(inline(toPGInterval(value).toString()));
|
||||
else
|
||||
super.sqlInline0(ctx, value);
|
||||
@ -5564,13 +5542,7 @@ public class DefaultBinding<T, U> implements Binding<T, U> {
|
||||
|
||||
// [#566] Interval data types are best bound as Strings
|
||||
if (REQUIRE_PG_INTERVAL.contains(ctx.dialect()))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
ctx.statement().setObject(ctx.index(), toPGInterval(value));
|
||||
ctx.statement().setString(ctx.index(), toPGInterval(value).toString());
|
||||
else
|
||||
ctx.statement().setString(ctx.index(), value.toString());
|
||||
}
|
||||
@ -5629,12 +5601,6 @@ public class DefaultBinding<T, U> implements Binding<T, U> {
|
||||
|
||||
// [#566] Interval data types are best bound as Strings
|
||||
if (REQUIRE_PG_INTERVAL.contains(ctx.dialect()))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
ctx.render().visit(inline(toPGInterval(value).toString()));
|
||||
else
|
||||
super.sqlInline0(ctx, value);
|
||||
@ -5645,13 +5611,7 @@ public class DefaultBinding<T, U> implements Binding<T, U> {
|
||||
|
||||
// [#566] Interval data types are best bound as Strings
|
||||
if (REQUIRE_PG_INTERVAL.contains(ctx.dialect()))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
ctx.statement().setObject(ctx.index(), toPGInterval(value));
|
||||
ctx.statement().setString(ctx.index(), toPGInterval(value).toString());
|
||||
else
|
||||
ctx.statement().setString(ctx.index(), renderYTM(ctx, value));
|
||||
}
|
||||
@ -5663,20 +5623,16 @@ public class DefaultBinding<T, U> implements Binding<T, U> {
|
||||
|
||||
@Override
|
||||
final YearToMonth get0(BindingGetResultSetContext<U> ctx) throws SQLException {
|
||||
if (REQUIRE_PG_INTERVAL.contains(ctx.dialect())) {
|
||||
Object object = ctx.resultSet().getObject(ctx.index());
|
||||
return object == null ? null : PostgresUtils.toYearToMonth(object);
|
||||
}
|
||||
if (REQUIRE_PG_INTERVAL.contains(ctx.dialect()))
|
||||
return toYearToMonth(ctx.resultSet().getString(ctx.index()));
|
||||
else
|
||||
return parseYTM(ctx, ctx.resultSet().getString(ctx.index()));
|
||||
}
|
||||
|
||||
@Override
|
||||
final YearToMonth get0(BindingGetStatementContext<U> ctx) throws SQLException {
|
||||
if (REQUIRE_PG_INTERVAL.contains(ctx.dialect())) {
|
||||
Object object = ctx.statement().getObject(ctx.index());
|
||||
return object == null ? null : PostgresUtils.toYearToMonth(object);
|
||||
}
|
||||
if (REQUIRE_PG_INTERVAL.contains(ctx.dialect()))
|
||||
return toYearToMonth(ctx.statement().getString(ctx.index()));
|
||||
else
|
||||
return parseYTM(ctx, ctx.statement().getString(ctx.index()));
|
||||
}
|
||||
|
||||
@ -348,6 +348,8 @@ final class SelectQueryImpl<R extends Record> extends AbstractResultQuery<R> imp
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
final WithImpl with;
|
||||
private final SelectFieldList<SelectFieldOrAsterisk> select;
|
||||
private Table<?> intoTable;
|
||||
@ -3044,9 +3046,11 @@ final class SelectQueryImpl<R extends Record> extends AbstractResultQuery<R> imp
|
||||
if (actualLimit.isApplicable()) {
|
||||
ctx.visit(actualLimit);
|
||||
}
|
||||
|
||||
// [#13509] Force a LIMIT clause to prevent optimisation of "unnecessary" ORDER BY
|
||||
else if (!actualOrderBy.isEmpty() && TRUE.equals(ctx.data(DATA_FORCE_LIMIT_WITH_ORDER_BY))) {
|
||||
Limit l = new Limit();
|
||||
l.setLimit(Integer.MAX_VALUE);
|
||||
l.setLimit(Long.MAX_VALUE);
|
||||
ctx.visit(l);
|
||||
}
|
||||
}
|
||||
@ -3120,7 +3124,6 @@ final class SelectQueryImpl<R extends Record> extends AbstractResultQuery<R> imp
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
private static final Set<SQLDialect> NO_SUPPORT_UNION_PARENTHESES = SQLDialect.supportedBy(SQLITE);
|
||||
|
||||
541
jOOQ/src/main/java/org/jooq/util/postgres/PGInterval.java
Normal file
541
jOOQ/src/main/java/org/jooq/util/postgres/PGInterval.java
Normal file
@ -0,0 +1,541 @@
|
||||
/*
|
||||
* Copyright (c) 2004, PostgreSQL Global Development Group
|
||||
* See the LICENSE file in the project root for more information.
|
||||
*/
|
||||
package org.jooq.util.postgres;
|
||||
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.NumberFormat;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus.Internal;
|
||||
|
||||
/**
|
||||
* This implements a class that handles the PostgreSQL interval type.
|
||||
*/
|
||||
@Internal
|
||||
public class PGInterval extends PGobject {
|
||||
|
||||
private static final int MICROS_IN_SECOND = 1000000;
|
||||
|
||||
private int years;
|
||||
private int months;
|
||||
private int days;
|
||||
private int hours;
|
||||
private int minutes;
|
||||
private int wholeSeconds;
|
||||
private int microSeconds;
|
||||
private boolean isNull;
|
||||
|
||||
/**
|
||||
* required by the driver.
|
||||
*/
|
||||
public PGInterval() {
|
||||
type = "interval";
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a interval with a given interval string representation.
|
||||
*
|
||||
* @param value String representated interval (e.g. '3 years 2 mons')
|
||||
* @see PGobject#setValue(String)
|
||||
*/
|
||||
public PGInterval(String value) {
|
||||
this();
|
||||
setValue(value);
|
||||
}
|
||||
|
||||
private int lookAhead(String value, int position, String find) {
|
||||
char [] tokens = find.toCharArray();
|
||||
int found = -1;
|
||||
|
||||
for ( int i = 0; i < tokens.length; i++ ) {
|
||||
found = value.indexOf(tokens[i], position);
|
||||
if ( found > 0 ) {
|
||||
return found;
|
||||
}
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
private void parseISO8601Format(String value) {
|
||||
int number = 0;
|
||||
String dateValue;
|
||||
String timeValue = null;
|
||||
|
||||
int hasTime = value.indexOf('T');
|
||||
if ( hasTime > 0 ) {
|
||||
/* skip over the P */
|
||||
dateValue = value.substring(1,hasTime);
|
||||
timeValue = value.substring(hasTime + 1);
|
||||
} else {
|
||||
/* skip over the P */
|
||||
dateValue = value.substring(1);
|
||||
}
|
||||
|
||||
for ( int i = 0; i < dateValue.length(); i++ ) {
|
||||
int lookAhead = lookAhead(dateValue, i, "YMD");
|
||||
if (lookAhead > 0) {
|
||||
number = Integer.parseInt(dateValue.substring(i, lookAhead));
|
||||
if (dateValue.charAt(lookAhead) == 'Y') {
|
||||
setYears(number);
|
||||
} else if (dateValue.charAt(lookAhead) == 'M') {
|
||||
setMonths(number);
|
||||
} else if (dateValue.charAt(lookAhead) == 'D') {
|
||||
setDays(number);
|
||||
}
|
||||
i = lookAhead;
|
||||
}
|
||||
}
|
||||
if ( timeValue != null ) {
|
||||
for (int i = 0; i < timeValue.length(); i++) {
|
||||
int lookAhead = lookAhead(timeValue, i, "HMS");
|
||||
if (lookAhead > 0) {
|
||||
number = Integer.parseInt(timeValue.substring(i, lookAhead));
|
||||
if (timeValue.charAt(lookAhead) == 'H') {
|
||||
setHours(number);
|
||||
} else if (timeValue.charAt(lookAhead) == 'M') {
|
||||
setMinutes(number);
|
||||
} else if (timeValue.charAt(lookAhead) == 'S') {
|
||||
setSeconds(number);
|
||||
}
|
||||
i = lookAhead;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes all values of this interval to the specified values.
|
||||
*
|
||||
* @param years years
|
||||
* @param months months
|
||||
* @param days days
|
||||
* @param hours hours
|
||||
* @param minutes minutes
|
||||
* @param seconds seconds
|
||||
* @see #setValue(int, int, int, int, int, double)
|
||||
*/
|
||||
public PGInterval(int years, int months, int days, int hours, int minutes, double seconds) {
|
||||
this();
|
||||
setValue(years, months, days, hours, minutes, seconds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a interval string represented value to this instance. This method only recognize the
|
||||
* format, that Postgres returns - not all input formats are supported (e.g. '1 yr 2 m 3 s').
|
||||
*
|
||||
* @param value String representated interval (e.g. '3 years 2 mons')
|
||||
*/
|
||||
@Override
|
||||
public void setValue(String value) {
|
||||
isNull = value == null;
|
||||
if (value == null) {
|
||||
setValue(0, 0, 0, 0, 0, 0);
|
||||
isNull = true;
|
||||
return;
|
||||
}
|
||||
final boolean PostgresFormat = !value.startsWith("@");
|
||||
if (value.startsWith("P")) {
|
||||
parseISO8601Format(value);
|
||||
return;
|
||||
}
|
||||
// Just a simple '0'
|
||||
if (!PostgresFormat && value.length() == 3 && value.charAt(2) == '0') {
|
||||
setValue(0, 0, 0, 0, 0, 0.0);
|
||||
return;
|
||||
}
|
||||
|
||||
int years = 0;
|
||||
int months = 0;
|
||||
int days = 0;
|
||||
int hours = 0;
|
||||
int minutes = 0;
|
||||
double seconds = 0;
|
||||
|
||||
String valueToken = null;
|
||||
|
||||
value = value.replace('+', ' ').replace('@', ' ');
|
||||
final StringTokenizer st = new StringTokenizer(value);
|
||||
for (int i = 1; st.hasMoreTokens(); i++) {
|
||||
String token = st.nextToken();
|
||||
|
||||
if ((i & 1) == 1) {
|
||||
int endHours = token.indexOf(':');
|
||||
if (endHours == -1) {
|
||||
valueToken = token;
|
||||
continue;
|
||||
}
|
||||
|
||||
// This handles hours, minutes, seconds and microseconds for
|
||||
// ISO intervals
|
||||
int offset = (token.charAt(0) == '-') ? 1 : 0;
|
||||
|
||||
hours = nullSafeIntGet(token.substring(offset + 0, endHours));
|
||||
minutes = nullSafeIntGet(token.substring(endHours + 1, endHours + 3));
|
||||
|
||||
// Pre 7.4 servers do not put second information into the results
|
||||
// unless it is non-zero.
|
||||
int endMinutes = token.indexOf(':', endHours + 1);
|
||||
if (endMinutes != -1) {
|
||||
seconds = nullSafeDoubleGet(token.substring(endMinutes + 1));
|
||||
}
|
||||
|
||||
if (offset == 1) {
|
||||
hours = -hours;
|
||||
minutes = -minutes;
|
||||
seconds = -seconds;
|
||||
}
|
||||
|
||||
valueToken = null;
|
||||
} else {
|
||||
// This handles years, months, days for both, ISO and
|
||||
// Non-ISO intervals. Hours, minutes, seconds and microseconds
|
||||
// are handled for Non-ISO intervals here.
|
||||
|
||||
if (token.startsWith("year")) {
|
||||
years = nullSafeIntGet(valueToken);
|
||||
} else if (token.startsWith("mon")) {
|
||||
months = nullSafeIntGet(valueToken);
|
||||
} else if (token.startsWith("day")) {
|
||||
days = nullSafeIntGet(valueToken);
|
||||
} else if (token.startsWith("hour")) {
|
||||
hours = nullSafeIntGet(valueToken);
|
||||
} else if (token.startsWith("min")) {
|
||||
minutes = nullSafeIntGet(valueToken);
|
||||
} else if (token.startsWith("sec")) {
|
||||
seconds = nullSafeDoubleGet(valueToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!PostgresFormat && value.endsWith("ago")) {
|
||||
// Inverse the leading sign
|
||||
setValue(-years, -months, -days, -hours, -minutes, -seconds);
|
||||
} else {
|
||||
setValue(years, months, days, hours, minutes, seconds);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set all values of this interval to the specified values.
|
||||
*
|
||||
* @param years years
|
||||
* @param months months
|
||||
* @param days days
|
||||
* @param hours hours
|
||||
* @param minutes minutes
|
||||
* @param seconds seconds
|
||||
*/
|
||||
public void setValue(int years, int months, int days, int hours, int minutes, double seconds) {
|
||||
setYears(years);
|
||||
setMonths(months);
|
||||
setDays(days);
|
||||
setHours(hours);
|
||||
setMinutes(minutes);
|
||||
setSeconds(seconds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the stored interval information as a string.
|
||||
*
|
||||
* @return String represented interval
|
||||
*/
|
||||
@Override
|
||||
public String getValue() {
|
||||
if (isNull) {
|
||||
return null;
|
||||
}
|
||||
DecimalFormat df = (DecimalFormat) NumberFormat.getInstance(Locale.US);
|
||||
df.applyPattern("0.0#####");
|
||||
|
||||
return String.format(
|
||||
Locale.ROOT,
|
||||
"%d years %d mons %d days %d hours %d mins %s secs",
|
||||
years,
|
||||
months,
|
||||
days,
|
||||
hours,
|
||||
minutes,
|
||||
df.format(getSeconds())
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the years represented by this interval.
|
||||
*
|
||||
* @return years represented by this interval
|
||||
*/
|
||||
public int getYears() {
|
||||
return years;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the years of this interval to the specified value.
|
||||
*
|
||||
* @param years years to set
|
||||
*/
|
||||
public void setYears(int years) {
|
||||
isNull = false;
|
||||
this.years = years;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the months represented by this interval.
|
||||
*
|
||||
* @return months represented by this interval
|
||||
*/
|
||||
public int getMonths() {
|
||||
return months;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the months of this interval to the specified value.
|
||||
*
|
||||
* @param months months to set
|
||||
*/
|
||||
public void setMonths(int months) {
|
||||
isNull = false;
|
||||
this.months = months;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the days represented by this interval.
|
||||
*
|
||||
* @return days represented by this interval
|
||||
*/
|
||||
public int getDays() {
|
||||
return days;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the days of this interval to the specified value.
|
||||
*
|
||||
* @param days days to set
|
||||
*/
|
||||
public void setDays(int days) {
|
||||
isNull = false;
|
||||
this.days = days;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the hours represented by this interval.
|
||||
*
|
||||
* @return hours represented by this interval
|
||||
*/
|
||||
public int getHours() {
|
||||
return hours;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the hours of this interval to the specified value.
|
||||
*
|
||||
* @param hours hours to set
|
||||
*/
|
||||
public void setHours(int hours) {
|
||||
isNull = false;
|
||||
this.hours = hours;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the minutes represented by this interval.
|
||||
*
|
||||
* @return minutes represented by this interval
|
||||
*/
|
||||
public int getMinutes() {
|
||||
return minutes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the minutes of this interval to the specified value.
|
||||
*
|
||||
* @param minutes minutes to set
|
||||
*/
|
||||
public void setMinutes(int minutes) {
|
||||
isNull = false;
|
||||
this.minutes = minutes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the seconds represented by this interval.
|
||||
*
|
||||
* @return seconds represented by this interval
|
||||
*/
|
||||
public double getSeconds() {
|
||||
return wholeSeconds + (double) microSeconds / MICROS_IN_SECOND;
|
||||
}
|
||||
|
||||
public int getWholeSeconds() {
|
||||
return wholeSeconds;
|
||||
}
|
||||
|
||||
public int getMicroSeconds() {
|
||||
return microSeconds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the seconds of this interval to the specified value.
|
||||
*
|
||||
* @param seconds seconds to set
|
||||
*/
|
||||
public void setSeconds(double seconds) {
|
||||
isNull = false;
|
||||
wholeSeconds = (int) seconds;
|
||||
microSeconds = (int) Math.round((seconds - wholeSeconds) * MICROS_IN_SECOND);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rolls this interval on a given calendar.
|
||||
*
|
||||
* @param cal Calendar instance to add to
|
||||
*/
|
||||
public void add(Calendar cal) {
|
||||
if (isNull) {
|
||||
return;
|
||||
}
|
||||
|
||||
final int milliseconds = (microSeconds + ((microSeconds < 0) ? -500 : 500)) / 1000 + wholeSeconds * 1000;
|
||||
|
||||
cal.add(Calendar.MILLISECOND, milliseconds);
|
||||
cal.add(Calendar.MINUTE, getMinutes());
|
||||
cal.add(Calendar.HOUR, getHours());
|
||||
cal.add(Calendar.DAY_OF_MONTH, getDays());
|
||||
cal.add(Calendar.MONTH, getMonths());
|
||||
cal.add(Calendar.YEAR, getYears());
|
||||
}
|
||||
|
||||
/**
|
||||
* Rolls this interval on a given date.
|
||||
*
|
||||
* @param date Date instance to add to
|
||||
*/
|
||||
public void add(Date date) {
|
||||
if (isNull) {
|
||||
return;
|
||||
}
|
||||
final Calendar cal = Calendar.getInstance();
|
||||
cal.setTime(date);
|
||||
add(cal);
|
||||
date.setTime(cal.getTime().getTime());
|
||||
}
|
||||
|
||||
/**
|
||||
* Add this interval's value to the passed interval. This is backwards to what I would expect, but
|
||||
* this makes it match the other existing add methods.
|
||||
*
|
||||
* @param interval intval to add
|
||||
*/
|
||||
public void add(PGInterval interval) {
|
||||
if (isNull || interval.isNull) {
|
||||
return;
|
||||
}
|
||||
interval.setYears(interval.getYears() + getYears());
|
||||
interval.setMonths(interval.getMonths() + getMonths());
|
||||
interval.setDays(interval.getDays() + getDays());
|
||||
interval.setHours(interval.getHours() + getHours());
|
||||
interval.setMinutes(interval.getMinutes() + getMinutes());
|
||||
interval.setSeconds(interval.getSeconds() + getSeconds());
|
||||
}
|
||||
|
||||
/**
|
||||
* Scale this interval by an integer factor. The server can scale by arbitrary factors, but that
|
||||
* would require adjusting the call signatures for all the existing methods like getDays() or
|
||||
* providing our own justification of fractional intervals. Neither of these seem like a good idea
|
||||
* without a strong use case.
|
||||
*
|
||||
* @param factor scale factor
|
||||
*/
|
||||
public void scale(int factor) {
|
||||
if (isNull) {
|
||||
return;
|
||||
}
|
||||
setYears(factor * getYears());
|
||||
setMonths(factor * getMonths());
|
||||
setDays(factor * getDays());
|
||||
setHours(factor * getHours());
|
||||
setMinutes(factor * getMinutes());
|
||||
setSeconds(factor * getSeconds());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns integer value of value or 0 if value is null.
|
||||
*
|
||||
* @param value integer as string value
|
||||
* @return integer parsed from string value
|
||||
* @throws NumberFormatException if the string contains invalid chars
|
||||
*/
|
||||
private static int nullSafeIntGet(String value) throws NumberFormatException {
|
||||
return (value == null) ? 0 : Integer.parseInt(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns double value of value or 0 if value is null.
|
||||
*
|
||||
* @param value double as string value
|
||||
* @return double parsed from string value
|
||||
* @throws NumberFormatException if the string contains invalid chars
|
||||
*/
|
||||
private static double nullSafeDoubleGet(String value) throws NumberFormatException {
|
||||
return (value == null) ? 0 : Double.parseDouble(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether an object is equal to this one or not.
|
||||
*
|
||||
* @param obj Object to compare with
|
||||
* @return true if the two intervals are identical
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!(obj instanceof PGInterval)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final PGInterval pgi = (PGInterval) obj;
|
||||
if (isNull) {
|
||||
return pgi.isNull;
|
||||
} else if (pgi.isNull) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return pgi.years == years
|
||||
&& pgi.months == months
|
||||
&& pgi.days == days
|
||||
&& pgi.hours == hours
|
||||
&& pgi.minutes == minutes
|
||||
&& pgi.wholeSeconds == wholeSeconds
|
||||
&& pgi.microSeconds == microSeconds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a hashCode for this object.
|
||||
*
|
||||
* @return hashCode
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
if (isNull) {
|
||||
return 0;
|
||||
}
|
||||
return (((((((8 * 31 + microSeconds) * 31 + wholeSeconds) * 31 + minutes) * 31 + hours) * 31
|
||||
+ days) * 31 + months) * 31 + years) * 31;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object clone() throws CloneNotSupportedException {
|
||||
// squid:S2157 "Cloneables" should implement "clone
|
||||
return super.clone();
|
||||
}
|
||||
}
|
||||
129
jOOQ/src/main/java/org/jooq/util/postgres/PGobject.java
Normal file
129
jOOQ/src/main/java/org/jooq/util/postgres/PGobject.java
Normal file
@ -0,0 +1,129 @@
|
||||
/*
|
||||
* Copyright (c) 2003, PostgreSQL Global Development Group
|
||||
* See the LICENSE file in the project root for more information.
|
||||
*/
|
||||
package org.jooq.util.postgres;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus.Internal;
|
||||
|
||||
/**
|
||||
* PGobject is a class used to describe unknown types An unknown type is any type that is unknown by
|
||||
* JDBC Standards.
|
||||
*/
|
||||
@Internal
|
||||
public class PGobject implements Serializable, Cloneable {
|
||||
protected String type;
|
||||
protected String value;
|
||||
|
||||
/**
|
||||
* This is called by org.postgresql.Connection.getObject() to create the object.
|
||||
*/
|
||||
public PGobject() {
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>This method sets the type of this object.</p>
|
||||
*
|
||||
* <p>It should not be extended by subclasses, hence it is final</p>
|
||||
*
|
||||
* @param type a string describing the type of the object
|
||||
*/
|
||||
public final void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method sets the value of this object. It must be overridden.
|
||||
*
|
||||
* @param value a string representation of the value of the object
|
||||
* @throws SQLException thrown if value is invalid for this type
|
||||
*/
|
||||
public void setValue(String value) throws SQLException {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* As this cannot change during the life of the object, it's final.
|
||||
*
|
||||
* @return the type name of this object
|
||||
*/
|
||||
public final String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* This must be overidden, to return the value of the object, in the form required by
|
||||
* org.postgresql.
|
||||
*
|
||||
* @return the value of this object
|
||||
*/
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the current object wraps `null` value.
|
||||
* This might be helpful
|
||||
*
|
||||
* @return true if the current object wraps `null` value.
|
||||
*/
|
||||
public boolean isNull() {
|
||||
return getValue() != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* This must be overidden to allow comparisons of objects.
|
||||
*
|
||||
* @param obj Object to compare with
|
||||
* @return true if the two boxes are identical
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj instanceof PGobject) {
|
||||
final Object otherValue = ((PGobject) obj).getValue();
|
||||
|
||||
if (otherValue == null) {
|
||||
return getValue() == null;
|
||||
}
|
||||
return otherValue.equals(getValue());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This must be overidden to allow the object to be cloned.
|
||||
*/
|
||||
@Override
|
||||
public Object clone() throws CloneNotSupportedException {
|
||||
return super.clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* This is defined here, so user code need not overide it.
|
||||
*
|
||||
* @return the value of this object, in the syntax expected by org.postgresql
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute hash. As equals() use only value. Return the same hash for the same value.
|
||||
*
|
||||
* @return Value hashcode, 0 if value is null {@link java.util.Objects#hashCode(Object)}
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
String value = getValue();
|
||||
return value != null ? value.hashCode() : 0;
|
||||
}
|
||||
|
||||
protected static boolean equals(Object a, Object b) {
|
||||
return a == b || a != null && a.equals(b);
|
||||
}
|
||||
}
|
||||
@ -51,7 +51,6 @@ import java.util.List;
|
||||
|
||||
import org.jooq.Converter;
|
||||
import org.jooq.EnumType;
|
||||
// ...
|
||||
import org.jooq.Record;
|
||||
import org.jooq.exception.DataTypeException;
|
||||
import org.jooq.tools.StringUtils;
|
||||
@ -59,9 +58,8 @@ import org.jooq.types.DayToSecond;
|
||||
import org.jooq.types.YearToMonth;
|
||||
import org.jooq.types.YearToSecond;
|
||||
|
||||
import org.postgresql.util.PGInterval;
|
||||
import org.jetbrains.annotations.ApiStatus.Internal;
|
||||
|
||||
// ...
|
||||
|
||||
/**
|
||||
* A collection of utilities to cover the Postgres JDBC driver's missing
|
||||
@ -73,6 +71,7 @@ import org.postgresql.util.PGInterval;
|
||||
* @author Lukas Eder
|
||||
* @author Peter Ertl
|
||||
*/
|
||||
@Internal
|
||||
public class PostgresUtils {
|
||||
|
||||
private static final String POSTGRESQL_HEX_STRING_PREFIX = "\\x";
|
||||
@ -85,12 +84,6 @@ public class PostgresUtils {
|
||||
private static final int PG_OBJECT_AFTER_VALUE = 4;
|
||||
private static final int PG_OBJECT_END = 5;
|
||||
|
||||
private static volatile Boolean pgIntervalAvailable;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Parse a Postgres-encoded <code>bytea</code> string
|
||||
*/
|
||||
@ -209,7 +202,7 @@ public class PostgresUtils {
|
||||
/**
|
||||
* Convert a jOOQ <code>DAY TO SECOND</code> interval to a Postgres representation
|
||||
*/
|
||||
public static Object toPGInterval(DayToSecond interval) {
|
||||
public static PGInterval toPGInterval(DayToSecond interval) {
|
||||
return new PGInterval(0, 0,
|
||||
interval.getSign() * interval.getDays(),
|
||||
interval.getSign() * interval.getHours(),
|
||||
@ -222,7 +215,7 @@ public class PostgresUtils {
|
||||
/**
|
||||
* Convert a jOOQ <code>YEAR TO SECOND</code> interval to a Postgres representation
|
||||
*/
|
||||
public static Object toPGInterval(YearToSecond interval) {
|
||||
public static PGInterval toPGInterval(YearToSecond interval) {
|
||||
return new PGInterval(
|
||||
interval.getSign() * interval.getYears(),
|
||||
interval.getSign() * interval.getMonths(),
|
||||
@ -237,7 +230,7 @@ public class PostgresUtils {
|
||||
/**
|
||||
* Convert a jOOQ <code>YEAR TO MONTH</code> interval to a Postgres representation
|
||||
*/
|
||||
public static Object toPGInterval(YearToMonth interval) {
|
||||
public static PGInterval toPGInterval(YearToMonth interval) {
|
||||
return new PGInterval(
|
||||
interval.getSign() * interval.getYears(),
|
||||
interval.getSign() * interval.getMonths(),
|
||||
@ -245,136 +238,69 @@ public class PostgresUtils {
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Convert a Postgres interval to a jOOQ <code>DAY TO SECOND</code> interval
|
||||
*/
|
||||
public static DayToSecond toDayToSecond(Object pgInterval) {
|
||||
if (pgInterval == null)
|
||||
return null;
|
||||
else if (pgInterval instanceof PGInterval)
|
||||
return toDayToSecond((PGInterval) pgInterval);
|
||||
else
|
||||
return toDayToSecond(new PGInterval(pgInterval.toString()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a Postgres interval to a jOOQ <code>DAY TO SECOND</code> interval
|
||||
*/
|
||||
public static DayToSecond toDayToSecond(PGInterval pgInterval) {
|
||||
boolean negative = pgInterval.toString().contains("-");
|
||||
|
||||
if (pgIntervalAvailable() && pgInterval instanceof PGInterval) {
|
||||
PGInterval i = (PGInterval) pgInterval;
|
||||
if (negative)
|
||||
i.scale(-1);
|
||||
if (negative)
|
||||
pgInterval.scale(-1);
|
||||
|
||||
Double seconds = i.getSeconds();
|
||||
DayToSecond result = new DayToSecond(
|
||||
i.getDays(),
|
||||
i.getHours(),
|
||||
i.getMinutes(),
|
||||
seconds.intValue(),
|
||||
(int) (1000000000 * (seconds - seconds.intValue()))
|
||||
);
|
||||
Double seconds = pgInterval.getSeconds();
|
||||
DayToSecond result = new DayToSecond(
|
||||
pgInterval.getDays(),
|
||||
pgInterval.getHours(),
|
||||
pgInterval.getMinutes(),
|
||||
seconds.intValue(),
|
||||
(int) (1000000000 * (seconds - seconds.intValue()))
|
||||
);
|
||||
|
||||
if (negative)
|
||||
result = result.neg();
|
||||
if (negative)
|
||||
result = result.neg();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
else
|
||||
throw new IllegalArgumentException("Unsupported interval type. Make sure you have the pgjdbc or redshift driver on your classpath: " + pgInterval);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a Postgres interval to a jOOQ <code>YEAR TO MONTH</code> interval
|
||||
*/
|
||||
public static YearToMonth toYearToMonth(Object pgInterval) {
|
||||
if (pgInterval == null)
|
||||
return null;
|
||||
else if (pgInterval instanceof PGInterval)
|
||||
return toYearToMonth((PGInterval) pgInterval);
|
||||
else
|
||||
return toYearToMonth(new PGInterval(pgInterval.toString()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a Postgres interval to a jOOQ <code>YEAR TO MONTH</code> interval
|
||||
*/
|
||||
public static YearToMonth toYearToMonth(PGInterval pgInterval) {
|
||||
boolean negative = pgInterval.toString().contains("-");
|
||||
|
||||
if (pgIntervalAvailable() && pgInterval instanceof PGInterval) {
|
||||
PGInterval i = (PGInterval) pgInterval;
|
||||
if (negative)
|
||||
i.scale(-1);
|
||||
if (negative)
|
||||
pgInterval.scale(-1);
|
||||
|
||||
YearToMonth result = new YearToMonth(i.getYears(), i.getMonths());
|
||||
YearToMonth result = new YearToMonth(pgInterval.getYears(), pgInterval.getMonths());
|
||||
|
||||
if (negative)
|
||||
result = result.neg();
|
||||
if (negative)
|
||||
result = result.neg();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
else
|
||||
throw new IllegalArgumentException("Unsupported interval type. Make sure you have the pgjdbc or redshift driver on your classpath: " + pgInterval);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -638,37 +564,4 @@ public class PostgresUtils {
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static final boolean pgIntervalAvailable() {
|
||||
if (pgIntervalAvailable == null) {
|
||||
try {
|
||||
new PGInterval();
|
||||
pgIntervalAvailable = true;
|
||||
}
|
||||
catch (NoClassDefFoundError e) {
|
||||
pgIntervalAvailable = false;
|
||||
}
|
||||
}
|
||||
|
||||
return pgIntervalAvailable;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
6
pom.xml
6
pom.xml
@ -30,7 +30,6 @@
|
||||
<postgres.version>42.3.3</postgres.version>
|
||||
<sqlserver.version>10.2.0.jre11</sqlserver.version>
|
||||
<oracle.version>21.5.0.0</oracle.version>
|
||||
<redshift.version>2.1.0.4</redshift.version>
|
||||
|
||||
<!-- R2DBC SPI version and some matching driver versions -->
|
||||
<io.r2dbc.version>0.9.0.RELEASE</io.r2dbc.version>
|
||||
@ -385,11 +384,6 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- Some examples require testcontainers as dependencies -->
|
||||
<dependency>
|
||||
<groupId>org.testcontainers</groupId>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user