diff --git a/jOOQ/src/main/java/org/jooq/conf/Settings.java b/jOOQ/src/main/java/org/jooq/conf/Settings.java
index c159c5263e..77c3b2289b 100644
--- a/jOOQ/src/main/java/org/jooq/conf/Settings.java
+++ b/jOOQ/src/main/java/org/jooq/conf/Settings.java
@@ -265,6 +265,8 @@ public class Settings
protected String parseDateFormat = "YYYY-MM-DD";
@XmlElement(defaultValue = "YYYY-MM-DD HH24:MI:SS.FF")
protected String parseTimestampFormat = "YYYY-MM-DD HH24:MI:SS.FF";
+ @XmlElement(defaultValue = ":")
+ protected String parseNamedParamPrefix = ":";
@XmlElement(defaultValue = "DEFAULT")
@XmlSchemaType(name = "string")
protected ParseNameCase parseNameCase = ParseNameCase.DEFAULT;
@@ -450,12 +452,12 @@ public class Settings
}
/**
- * The prefix to use for named parameters.
+ * The prefix to use for named parameters in generated SQL.
*
* Named parameter syntax defaults to :name (such as supported by Oracle, JPA, Spring), but
* vendor specific parameters may look differently. This flag can be used to determine the prefix to be
* used by named parameters, such as @ for SQL Server's @name or $
- * for PostgreSQL's $name.
+ * for PostgreSQL's $name, when generating SQL.
*
* "Named indexed" parameters can be obtained in the same way by specifingy {@code ParamType#NAMED} and not
* providing a name to parameters, resulting in :1 or @1 or $1, etc.
@@ -466,12 +468,12 @@ public class Settings
}
/**
- * The prefix to use for named parameters.
+ * The prefix to use for named parameters in generated SQL.
*
* Named parameter syntax defaults to :name (such as supported by Oracle, JPA, Spring), but
* vendor specific parameters may look differently. This flag can be used to determine the prefix to be
* used by named parameters, such as @ for SQL Server's @name or $
- * for PostgreSQL's $name.
+ * for PostgreSQL's $name, when generating SQL.
*
* "Named indexed" parameters can be obtained in the same way by specifingy {@code ParamType#NAMED} and not
* providing a name to parameters, resulting in :1 or @1 or $1, etc.
@@ -2389,6 +2391,38 @@ public class Settings
this.parseTimestampFormat = value;
}
+ /**
+ * The prefix to use for named parameters in parsed SQL.
+ *
+ * Named parameter syntax defaults to :name (such as supported by Oracle, JPA, Spring), but
+ * vendor specific parameters may look differently. This flag can be used to determine the prefix to be
+ * used by named parameters, such as @ for SQL Server's @name or $
+ * for PostgreSQL's $name when parsing SQL.
+ *
+ * "Named indexed" parameters can be obtained in the same way by specifingy {@code ParamType#NAMED} and not
+ * providing a name to parameters, resulting in :1 or @1 or $1, etc.
+ *
+ */
+ public String getParseNamedParamPrefix() {
+ return parseNamedParamPrefix;
+ }
+
+ /**
+ * The prefix to use for named parameters in parsed SQL.
+ *
+ * Named parameter syntax defaults to :name (such as supported by Oracle, JPA, Spring), but
+ * vendor specific parameters may look differently. This flag can be used to determine the prefix to be
+ * used by named parameters, such as @ for SQL Server's @name or $
+ * for PostgreSQL's $name when parsing SQL.
+ *
+ * "Named indexed" parameters can be obtained in the same way by specifingy {@code ParamType#NAMED} and not
+ * providing a name to parameters, resulting in :1 or @1 or $1, etc.
+ *
+ */
+ public void setParseNamedParamPrefix(String value) {
+ this.parseNamedParamPrefix = value;
+ }
+
/**
* [#7337] The default name case for parsed identifiers.
*
@@ -2676,12 +2710,12 @@ public class Settings
}
/**
- * The prefix to use for named parameters.
+ * The prefix to use for named parameters in generated SQL.
*
* Named parameter syntax defaults to :name (such as supported by Oracle, JPA, Spring), but
* vendor specific parameters may look differently. This flag can be used to determine the prefix to be
* used by named parameters, such as @ for SQL Server's @name or $
- * for PostgreSQL's $name.
+ * for PostgreSQL's $name, when generating SQL.
*
* "Named indexed" parameters can be obtained in the same way by specifingy {@code ParamType#NAMED} and not
* providing a name to parameters, resulting in :1 or @1 or $1, etc.
@@ -3355,6 +3389,23 @@ public class Settings
return this;
}
+ /**
+ * The prefix to use for named parameters in parsed SQL.
+ *
+ * Named parameter syntax defaults to :name (such as supported by Oracle, JPA, Spring), but
+ * vendor specific parameters may look differently. This flag can be used to determine the prefix to be
+ * used by named parameters, such as @ for SQL Server's @name or $
+ * for PostgreSQL's $name when parsing SQL.
+ *
+ * "Named indexed" parameters can be obtained in the same way by specifingy {@code ParamType#NAMED} and not
+ * providing a name to parameters, resulting in :1 or @1 or $1, etc.
+ *
+ */
+ public Settings withParseNamedParamPrefix(String value) {
+ setParseNamedParamPrefix(value);
+ return this;
+ }
+
/**
* [#7337] The default name case for parsed identifiers.
*
@@ -3590,6 +3641,7 @@ public class Settings
builder.append("parseLocale", parseLocale);
builder.append("parseDateFormat", parseDateFormat);
builder.append("parseTimestampFormat", parseTimestampFormat);
+ builder.append("parseNamedParamPrefix", parseNamedParamPrefix);
builder.append("parseNameCase", parseNameCase);
builder.append("parseWithMetaLookups", parseWithMetaLookups);
builder.append("parseSetCommands", parseSetCommands);
@@ -4488,6 +4540,15 @@ public class Settings
return false;
}
}
+ if (parseNamedParamPrefix == null) {
+ if (other.parseNamedParamPrefix!= null) {
+ return false;
+ }
+ } else {
+ if (!parseNamedParamPrefix.equals(other.parseNamedParamPrefix)) {
+ return false;
+ }
+ }
if (parseNameCase == null) {
if (other.parseNameCase!= null) {
return false;
@@ -4708,6 +4769,7 @@ public class Settings
result = ((prime*result)+((parseLocale == null)? 0 :parseLocale.hashCode()));
result = ((prime*result)+((parseDateFormat == null)? 0 :parseDateFormat.hashCode()));
result = ((prime*result)+((parseTimestampFormat == null)? 0 :parseTimestampFormat.hashCode()));
+ result = ((prime*result)+((parseNamedParamPrefix == null)? 0 :parseNamedParamPrefix.hashCode()));
result = ((prime*result)+((parseNameCase == null)? 0 :parseNameCase.hashCode()));
result = ((prime*result)+((parseWithMetaLookups == null)? 0 :parseWithMetaLookups.hashCode()));
result = ((prime*result)+((parseSetCommands == null)? 0 :parseSetCommands.hashCode()));
diff --git a/jOOQ/src/main/java/org/jooq/impl/DiagnosticsResultSet.java b/jOOQ/src/main/java/org/jooq/impl/DiagnosticsResultSet.java
index a0ffa4c766..5feb665516 100644
--- a/jOOQ/src/main/java/org/jooq/impl/DiagnosticsResultSet.java
+++ b/jOOQ/src/main/java/org/jooq/impl/DiagnosticsResultSet.java
@@ -596,7 +596,7 @@ final class DiagnosticsResultSet extends DefaultResultSet {
// XXX Utilities
// ------------------------------------------------------------------------
- private final void wasPrimitive(int columnIndex) {
+ private final void wasPrimitive(int columnIndex) throws SQLException {
checkPrimitive();
wasColumnIndex = columnIndex;
@@ -608,7 +608,7 @@ final class DiagnosticsResultSet extends DefaultResultSet {
wasPrimitive(super.findColumn(columnLabel));
}
- private final void checkPrimitive() {
+ private final void checkPrimitive() throws SQLException {
if (wasPrimitive && wasNullable) {
DefaultDiagnosticsContext ctx = ctx();
ctx.resultSetMissingWasNullCall = true;
@@ -628,7 +628,7 @@ final class DiagnosticsResultSet extends DefaultResultSet {
read(super.findColumn(columnLabel));
}
- private final DefaultDiagnosticsContext ctx() {
+ private final DefaultDiagnosticsContext ctx() throws SQLException {
DefaultDiagnosticsContext ctx = new DefaultDiagnosticsContext(sql);
ctx.resultSet = super.getDelegate();
diff --git a/jOOQ/src/main/java/org/jooq/impl/LoaderImpl.java b/jOOQ/src/main/java/org/jooq/impl/LoaderImpl.java
index 0b5fe3d630..b295cdae31 100644
--- a/jOOQ/src/main/java/org/jooq/impl/LoaderImpl.java
+++ b/jOOQ/src/main/java/org/jooq/impl/LoaderImpl.java
@@ -748,7 +748,7 @@ final class LoaderImpl implements
}
@Override
- public void close() {
+ public void close() throws SQLException {
for (CachedPS ps : map.values())
safeClose(ps.getDelegate());
}
diff --git a/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java b/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java
index e23e839b7d..2687589841 100644
--- a/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java
+++ b/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java
@@ -83,6 +83,7 @@ import static org.jooq.impl.Tools.aliased;
import static org.jooq.impl.Tools.normaliseNameCase;
import static org.jooq.impl.XMLPassingMechanism.BY_REF;
import static org.jooq.impl.XMLPassingMechanism.BY_VALUE;
+import static org.jooq.tools.StringUtils.defaultIfNull;
import java.io.ByteArrayOutputStream;
import java.math.BigDecimal;
@@ -7049,9 +7050,15 @@ final class DefaultParseContext extends AbstractScope implements ParseContext {
switch (characterUpper()) {
+
+ // [#8821] Known prefixes so far:
case ':':
+ case '@':
case '?':
- return parseBindVariable();
+ if ((field = parseBindVariableIf()) != null)
+ return field;
+
+ break;
@@ -7065,7 +7072,9 @@ final class DefaultParseContext extends AbstractScope implements ParseContext {
return inline(parseStringLiteral());
case '$':
- if ((value = parseDollarQuotedStringLiteralIf()) != null)
+ if ((field = parseBindVariableIf()) != null)
+ return field;
+ else if ((value = parseDollarQuotedStringLiteralIf()) != null)
return inline((String) value);
break;
@@ -11501,10 +11510,8 @@ final class DefaultParseContext extends AbstractScope implements ParseContext {
return c;
}
- private final Field> parseBindVariable() {
-
- // [#11074] Bindings can be Param or even Field types
- Object binding = nextBinding();
+ private final Field> parseBindVariableIf() {
+ int p = position();
String paramName;
switch (character()) {
@@ -11513,16 +11520,28 @@ final class DefaultParseContext extends AbstractScope implements ParseContext {
paramName = "" + bindIndex;
break;
- case ':':
- parse(':', false);
- Name identifier = parseIdentifier();
- paramName = identifier.last();
- break;
-
default:
- throw exception("Illegal bind variable character");
+ String prefix = defaultIfNull(settings().getParseNamedParamPrefix(), ":");
+
+ if (parseIf(prefix, false)) {
+ Name identifier = parseIdentifier();
+ paramName = identifier.last();
+
+ // [#8821] Avoid conflicts with dollar quoted string literals
+ if ("$".equals(prefix) && paramName.endsWith("$")) {
+ position(p);
+ return null;
+ }
+
+ break;
+ }
+ else
+ return null;
}
+ // [#11074] Bindings can be Param or even Field types
+ Object binding = nextBinding();
+
if (binding instanceof Field)
return (Field>) binding;
@@ -11684,8 +11703,10 @@ final class DefaultParseContext extends AbstractScope implements ParseContext {
private final String parseDollarQuotedStringLiteralIf() {
int previous = position();
- if (!parseIf('$'))
+ if (!peek('$'))
return null;
+ else
+ parse('$');
int openTokenStart = previous;
int openTokenEnd = previous;
@@ -12044,7 +12065,15 @@ final class DefaultParseContext extends AbstractScope implements ParseContext {
private final Field parseUnsignedIntegerOrBindVariable() {
Long i = parseUnsignedIntegerLiteralIf();
- return i != null ? DSL.inline(i) : (Field) parseBindVariable();
+
+ if (i != null)
+ return DSL.inline(i);
+
+ Field> f = parseBindVariableIf();
+ if (f != null)
+ return (Field) f;
+
+ throw expected("Unsigned integer or bind variable");
}
@Override
diff --git a/jOOQ/src/main/java/org/jooq/impl/ParsingConnectionFactory.java b/jOOQ/src/main/java/org/jooq/impl/ParsingConnectionFactory.java
index aaf061570f..0993cf84de 100644
--- a/jOOQ/src/main/java/org/jooq/impl/ParsingConnectionFactory.java
+++ b/jOOQ/src/main/java/org/jooq/impl/ParsingConnectionFactory.java
@@ -76,6 +76,7 @@ final class ParsingConnectionFactory implements ConnectionFactory {
ParsingConnectionFactory(Configuration configuration) {
this.configuration = configuration.derive();
this.configuration.set(SettingsTools.clone(configuration.settings())
+ .withParseNamedParamPrefix("$")
.withRenderNamedParamPrefix("$")
.withParamType(ParamType.NAMED));
}
diff --git a/jOOQ/src/main/java/org/jooq/impl/R2DBC.java b/jOOQ/src/main/java/org/jooq/impl/R2DBC.java
index 10f9b3bc9c..acded29532 100644
--- a/jOOQ/src/main/java/org/jooq/impl/R2DBC.java
+++ b/jOOQ/src/main/java/org/jooq/impl/R2DBC.java
@@ -41,35 +41,24 @@ import static org.jooq.conf.ParamType.NAMED;
import static org.jooq.impl.Tools.recordFactory;
import static org.jooq.tools.StringUtils.defaultIfNull;
-import java.io.InputStream;
-import java.io.Reader;
import java.math.BigDecimal;
-import java.net.URL;
-import java.sql.Array;
-import java.sql.Blob;
-import java.sql.Clob;
import java.sql.Date;
-import java.sql.NClob;
-import java.sql.Ref;
import java.sql.ResultSetMetaData;
-import java.sql.RowId;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.sql.SQLType;
-import java.sql.SQLXML;
import java.sql.Time;
import java.sql.Timestamp;
import java.sql.Types;
-import java.sql.Wrapper;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.ArrayDeque;
-import java.util.Calendar;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Function;
import java.util.function.Supplier;
+import org.jooq.BindingGetResultSetContext;
import org.jooq.Configuration;
import org.jooq.Cursor;
import org.jooq.DataType;
@@ -78,6 +67,7 @@ import org.jooq.Record;
import org.jooq.conf.SettingsTools;
import org.jooq.impl.DefaultRenderContext.Rendered;
import org.jooq.tools.jdbc.DefaultPreparedStatement;
+import org.jooq.tools.jdbc.DefaultResultSet;
import org.reactivestreams.Publisher;
import org.reactivestreams.Subscriber;
@@ -85,6 +75,7 @@ import org.reactivestreams.Subscription;
import io.r2dbc.spi.ColumnMetadata;
import io.r2dbc.spi.Connection;
+import io.r2dbc.spi.Row;
import io.r2dbc.spi.RowMetadata;
import io.r2dbc.spi.Statement;
@@ -159,13 +150,21 @@ final class R2DBC {
RecordDelegate delegate = Tools.newRecord(true, (Supplier) recordFactory(query.getRecordType(), Tools.row0(fields)), query.configuration());
return (R) delegate.operate(record -> {
- // TODO: Go through Field.getBinding()
+
+ // TODO: What data to pass here?
+ DefaultBindingGetResultSetContext> ctx = new DefaultBindingGetResultSetContext<>(
+ query.configuration(),
+ query.configuration().data(),
+ new R2DBCResultSet(query.configuration(), row),
+ 0
+ );
+
// TODO: Make sure all the embeddable records, and other types of nested records are supported
for (int i = 0; i < fields.length; i++) {
- Field> f = fields[i];
- Object value = row.get(i, f.getType());
- record.values[i] = value;
- record.originals[i] = value;
+ ctx.index(i + 1);
+ fields[i].getBinding().get((BindingGetResultSetContext) ctx);
+ record.values[i] = ctx.value();
+ record.originals[i] = ctx.value();
}
return record;
@@ -208,20 +207,15 @@ final class R2DBC {
public final void onNext(Connection c) {
try {
DefaultRenderContext render = new DefaultRenderContext(query.configuration().derive(
- SettingsTools.clone(query.configuration().settings()).withParamType(NAMED).withRenderNamedParamPrefix("$")
+ SettingsTools.clone(query.configuration().settings())
+ .withParseNamedParamPrefix("$")
+ .withRenderNamedParamPrefix("$")
+ .withParamType(NAMED)
));
Rendered r = new Rendered(render.paramType(NAMED).visit(query).render(), render.bindValues(), render.skipUpdateCounts());
Statement stmt = c.createStatement(r.sql);
new DefaultBindContext(query.configuration(), new R2DBCPreparedStatement(query.configuration(), stmt)).visit(r.bindValues);
-// ;
-// int i = 0;
-// for (Param> p : r.bindValues)
-// if (p.getValue() == null)
-// stmt.bindNull(i++, p.getType());
-// else
-// stmt.bind(i++, p.getValue());
-
stmt.execute().subscribe(new ResultSubscriber<>(query, this));
}
@@ -328,28 +322,13 @@ final class R2DBC {
// JDBC to R2DBC bridges for better interop, where it doesn't matter
// -------------------------------------------------------------------------
- static abstract class R2DBCWrapper implements Wrapper {
-
- @Override
- public final T unwrap(Class iface) throws SQLException {
- throw new SQLFeatureNotSupportedException("R2DBC can't unwrap JDBC types");
- }
-
- @Override
- public final boolean isWrapperFor(Class> iface) throws SQLException {
- return false;
- }
- }
-
static final class R2DBCPreparedStatement extends DefaultPreparedStatement {
final Configuration c;
final Statement s;
R2DBCPreparedStatement(Configuration c, Statement s) {
-
- // TODO: Refactor super class to throw a custom exception if trying to dereference this null pointer.
- super(null);
+ super(null, null, () -> new SQLFeatureNotSupportedException("Unsupported operation of the JDBC to R2DBC bridge."));
this.c = c;
this.s = s;
@@ -367,7 +346,7 @@ final class R2DBC {
if (x == null)
s.bindNull(parameterIndex - 1, type);
else
- bindNonNull(parameterIndex - 1, conversion.apply(x));
+ bindNonNull(parameterIndex, conversion.apply(x));
}
private final void bindNonNull(int parameterIndex, Object x) {
@@ -483,154 +462,122 @@ final class R2DBC {
public final void setObject(int parameterIndex, Object x, SQLType targetSqlType) throws SQLException {
setObject(parameterIndex, x, defaultIfNull(targetSqlType.getVendorTypeNumber(), Types.OTHER));
}
+ }
- @Override
- public final void setAsciiStream(int parameterIndex, InputStream x, int length) throws SQLException {
- throw new SQLFeatureNotSupportedException("The JDBC to R2DBC bridge doesn't support this data type");
+ static final class R2DBCResultSet extends DefaultResultSet {
+
+ final Configuration c;
+ final Row r;
+ boolean wasNull;
+
+ R2DBCResultSet(Configuration c, Row r) {
+ super(null, null, () -> new SQLFeatureNotSupportedException("Unsupported operation of the JDBC to R2DBC bridge."));
+
+ this.c = c;
+ this.r = r;
+ }
+
+ private final T wasNull(T nullable) {
+ wasNull = nullable == null;
+ return nullable;
+ }
+
+ private final T nullable(int columnIndex, Class type) {
+ return nullable(columnIndex, type, t -> t);
+ }
+
+ private final U nullable(int columnIndex, Class type, Function super T, ? extends U> conversion) {
+ T t = wasNull(r.get(columnIndex - 1, type));
+ return wasNull ? null : conversion.apply(t);
+ }
+
+ private final T nonNull(int columnIndex, Class type, T nullValue) {
+ T t = wasNull(r.get(columnIndex - 1, type));
+ return wasNull ? nullValue : t;
}
@Override
- public final void setUnicodeStream(int parameterIndex, InputStream x, int length) throws SQLException {
- throw new SQLFeatureNotSupportedException("The JDBC to R2DBC bridge doesn't support this data type");
+ public final boolean wasNull() throws SQLException {
+ return wasNull;
}
@Override
- public final void setBinaryStream(int parameterIndex, InputStream x, int length) throws SQLException {
- throw new SQLFeatureNotSupportedException("The JDBC to R2DBC bridge doesn't support this data type");
+ public final boolean getBoolean(int columnIndex) throws SQLException {
+ return nonNull(columnIndex, Boolean.class, false);
}
@Override
- public final void setCharacterStream(int parameterIndex, Reader reader, int length) throws SQLException {
- throw new SQLFeatureNotSupportedException("The JDBC to R2DBC bridge doesn't support this data type");
+ public final byte getByte(int columnIndex) throws SQLException {
+ return nonNull(columnIndex, Byte.class, (byte) 0);
}
@Override
- public final void setRef(int parameterIndex, Ref x) throws SQLException {
- throw new SQLFeatureNotSupportedException("The JDBC to R2DBC bridge doesn't support this data type");
+ public final short getShort(int columnIndex) throws SQLException {
+ return nonNull(columnIndex, Short.class, (short) 0);
}
@Override
- public final void setBlob(int parameterIndex, Blob x) throws SQLException {
- throw new SQLFeatureNotSupportedException("The JDBC to R2DBC bridge doesn't support this data type");
+ public final int getInt(int columnIndex) throws SQLException {
+ return nonNull(columnIndex, Integer.class, 0);
}
@Override
- public final void setClob(int parameterIndex, Clob x) throws SQLException {
- throw new SQLFeatureNotSupportedException("The JDBC to R2DBC bridge doesn't support this data type");
+ public final long getLong(int columnIndex) throws SQLException {
+ return nonNull(columnIndex, Long.class, 0L);
}
@Override
- public final void setArray(int parameterIndex, Array x) throws SQLException {
- throw new SQLFeatureNotSupportedException("The JDBC to R2DBC bridge doesn't support this data type");
+ public final float getFloat(int columnIndex) throws SQLException {
+ return nonNull(columnIndex, Float.class, 0.0f);
}
@Override
- public final void setDate(int parameterIndex, Date x, Calendar cal) throws SQLException {
- throw new SQLFeatureNotSupportedException("The JDBC to R2DBC bridge doesn't support this data type");
+ public final double getDouble(int columnIndex) throws SQLException {
+ return nonNull(columnIndex, Double.class, 0.0);
}
@Override
- public final void setTime(int parameterIndex, Time x, Calendar cal) throws SQLException {
- throw new SQLFeatureNotSupportedException("The JDBC to R2DBC bridge doesn't support this data type");
+ public final BigDecimal getBigDecimal(int columnIndex) throws SQLException {
+ return nullable(columnIndex, BigDecimal.class);
}
@Override
- public final void setTimestamp(int parameterIndex, Timestamp x, Calendar cal) throws SQLException {
- throw new SQLFeatureNotSupportedException("The JDBC to R2DBC bridge doesn't support this data type");
+ public final String getString(int columnIndex) throws SQLException {
+ return nullable(columnIndex, String.class);
}
@Override
- public final void setURL(int parameterIndex, URL x) throws SQLException {
- throw new SQLFeatureNotSupportedException("The JDBC to R2DBC bridge doesn't support this data type");
+ public final byte[] getBytes(int columnIndex) throws SQLException {
+ return nullable(columnIndex, byte[].class);
}
@Override
- public final void setRowId(int parameterIndex, RowId x) throws SQLException {
- throw new SQLFeatureNotSupportedException("The JDBC to R2DBC bridge doesn't support this data type");
+ public final Date getDate(int columnIndex) throws SQLException {
+ return nullable(columnIndex, LocalDate.class, Date::valueOf);
}
@Override
- public final void setNCharacterStream(int parameterIndex, Reader value, long length) throws SQLException {
- throw new SQLFeatureNotSupportedException("The JDBC to R2DBC bridge doesn't support this data type");
+ public final Time getTime(int columnIndex) throws SQLException {
+ return nullable(columnIndex, LocalTime.class, Time::valueOf);
}
@Override
- public final void setNClob(int parameterIndex, NClob value) throws SQLException {
- throw new SQLFeatureNotSupportedException("The JDBC to R2DBC bridge doesn't support this data type");
+ public final Timestamp getTimestamp(int columnIndex) throws SQLException {
+ return nullable(columnIndex, LocalDateTime.class, Timestamp::valueOf);
}
@Override
- public final void setClob(int parameterIndex, Reader reader, long length) throws SQLException {
- throw new SQLFeatureNotSupportedException("The JDBC to R2DBC bridge doesn't support this data type");
+ public final Object getObject(int columnIndex) throws SQLException {
+ return getObject(columnIndex, Object.class);
}
@Override
- public final void setBlob(int parameterIndex, InputStream inputStream, long length) throws SQLException {
- throw new SQLFeatureNotSupportedException("The JDBC to R2DBC bridge doesn't support this data type");
- }
-
- @Override
- public final void setNClob(int parameterIndex, Reader reader, long length) throws SQLException {
- throw new SQLFeatureNotSupportedException("The JDBC to R2DBC bridge doesn't support this data type");
- }
-
- @Override
- public final void setSQLXML(int parameterIndex, SQLXML xmlObject) throws SQLException {
- throw new SQLFeatureNotSupportedException("The JDBC to R2DBC bridge doesn't support this data type");
- }
-
- @Override
- public final void setAsciiStream(int parameterIndex, InputStream x, long length) throws SQLException {
- throw new SQLFeatureNotSupportedException("The JDBC to R2DBC bridge doesn't support this data type");
- }
-
- @Override
- public final void setBinaryStream(int parameterIndex, InputStream x, long length) throws SQLException {
- throw new SQLFeatureNotSupportedException("The JDBC to R2DBC bridge doesn't support this data type");
- }
-
- @Override
- public final void setCharacterStream(int parameterIndex, Reader reader, long length) throws SQLException {
- throw new SQLFeatureNotSupportedException("The JDBC to R2DBC bridge doesn't support this data type");
- }
-
- @Override
- public final void setAsciiStream(int parameterIndex, InputStream x) throws SQLException {
- throw new SQLFeatureNotSupportedException("The JDBC to R2DBC bridge doesn't support this data type");
- }
-
- @Override
- public final void setBinaryStream(int parameterIndex, InputStream x) throws SQLException {
- throw new SQLFeatureNotSupportedException("The JDBC to R2DBC bridge doesn't support this data type");
- }
-
- @Override
- public final void setCharacterStream(int parameterIndex, Reader reader) throws SQLException {
- throw new SQLFeatureNotSupportedException("The JDBC to R2DBC bridge doesn't support this data type");
- }
-
- @Override
- public final void setNCharacterStream(int parameterIndex, Reader value) throws SQLException {
- throw new SQLFeatureNotSupportedException("The JDBC to R2DBC bridge doesn't support this data type");
- }
-
- @Override
- public final void setClob(int parameterIndex, Reader reader) throws SQLException {
- throw new SQLFeatureNotSupportedException("The JDBC to R2DBC bridge doesn't support this data type");
- }
-
- @Override
- public final void setBlob(int parameterIndex, InputStream inputStream) throws SQLException {
- throw new SQLFeatureNotSupportedException("The JDBC to R2DBC bridge doesn't support this data type");
- }
-
- @Override
- public final void setNClob(int parameterIndex, Reader reader) throws SQLException {
- throw new SQLFeatureNotSupportedException("The JDBC to R2DBC bridge doesn't support this data type");
+ public final T getObject(int columnIndex, Class type) throws SQLException {
+ return nullable(columnIndex, type);
}
}
- static final class R2DBCResultSetMetaData extends R2DBCWrapper implements ResultSetMetaData {
+ static final class R2DBCResultSetMetaData implements ResultSetMetaData {
final Configuration c;
final RowMetadata m;
@@ -644,6 +591,16 @@ final class R2DBC {
return m.getColumnMetadata(column - 1);
}
+ @Override
+ public final T unwrap(Class iface) throws SQLException {
+ throw new SQLFeatureNotSupportedException("R2DBC can't unwrap JDBC types");
+ }
+
+ @Override
+ public final boolean isWrapperFor(Class> iface) throws SQLException {
+ return false;
+ }
+
@Override
public final int getColumnCount() throws SQLException {
return m.getColumnNames().size();
diff --git a/jOOQ/src/main/java/org/jooq/impl/Val.java b/jOOQ/src/main/java/org/jooq/impl/Val.java
index c22001a9e9..4aa1249cf9 100644
--- a/jOOQ/src/main/java/org/jooq/impl/Val.java
+++ b/jOOQ/src/main/java/org/jooq/impl/Val.java
@@ -43,6 +43,7 @@ import static org.jooq.impl.QueryPartListView.wrap;
import static org.jooq.impl.SQLDataType.OTHER;
import static org.jooq.impl.Tools.embeddedFields;
import static org.jooq.impl.Tools.BooleanDataKey.DATA_LIST_ALREADY_INDENTED;
+import static org.jooq.tools.StringUtils.defaultIfNull;
import java.sql.SQLException;
import java.sql.SQLWarning;
@@ -163,7 +164,7 @@ final class Val extends AbstractParam {
final String getBindVariable(Context> ctx) {
if (ctx.paramType() == NAMED || ctx.paramType() == NAMED_OR_INLINED) {
int index = ctx.nextIndex();
- String prefix = StringUtils.defaultIfNull(ctx.settings().getRenderNamedParamPrefix(), ":");
+ String prefix = defaultIfNull(ctx.settings().getRenderNamedParamPrefix(), ":");
if (StringUtils.isBlank(getParamName()))
return prefix + index;
diff --git a/jOOQ/src/main/java/org/jooq/tools/jdbc/DefaultCallableStatement.java b/jOOQ/src/main/java/org/jooq/tools/jdbc/DefaultCallableStatement.java
index e70250dfd4..22314344af 100644
--- a/jOOQ/src/main/java/org/jooq/tools/jdbc/DefaultCallableStatement.java
+++ b/jOOQ/src/main/java/org/jooq/tools/jdbc/DefaultCallableStatement.java
@@ -75,11 +75,11 @@ public class DefaultCallableStatement extends DefaultPreparedStatement implement
}
@Override
- public CallableStatement getDelegate() {
+ public CallableStatement getDelegate() throws SQLException {
return getDelegateCallableStatement();
}
- public CallableStatement getDelegateCallableStatement() {
+ public CallableStatement getDelegateCallableStatement() throws SQLException {
return (CallableStatement) getDelegateStatement();
}
diff --git a/jOOQ/src/main/java/org/jooq/tools/jdbc/DefaultPreparedStatement.java b/jOOQ/src/main/java/org/jooq/tools/jdbc/DefaultPreparedStatement.java
index 29f2c59346..faa92ed17f 100644
--- a/jOOQ/src/main/java/org/jooq/tools/jdbc/DefaultPreparedStatement.java
+++ b/jOOQ/src/main/java/org/jooq/tools/jdbc/DefaultPreparedStatement.java
@@ -60,6 +60,7 @@ import java.sql.Statement;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.Calendar;
+import java.util.function.Supplier;
/**
* A default JDBC PreparedStatement implementation delegating all JDBC 4.0 calls
@@ -81,12 +82,16 @@ public class DefaultPreparedStatement extends DefaultStatement implements Prepar
super(delegate, creator);
}
+ protected DefaultPreparedStatement(Statement delegate, Connection creator, Supplier extends SQLException> errorIfUnsupported) {
+ super(delegate, creator, errorIfUnsupported);
+ }
+
@Override
- public PreparedStatement getDelegate() {
+ public PreparedStatement getDelegate() throws SQLException {
return getDelegatePreparedStatement();
}
- public final PreparedStatement getDelegatePreparedStatement() {
+ public final PreparedStatement getDelegatePreparedStatement() throws SQLException {
return (PreparedStatement) getDelegateStatement();
}
diff --git a/jOOQ/src/main/java/org/jooq/tools/jdbc/DefaultResultSet.java b/jOOQ/src/main/java/org/jooq/tools/jdbc/DefaultResultSet.java
index 38dffc0eea..ac89f7ea1d 100644
--- a/jOOQ/src/main/java/org/jooq/tools/jdbc/DefaultResultSet.java
+++ b/jOOQ/src/main/java/org/jooq/tools/jdbc/DefaultResultSet.java
@@ -59,6 +59,7 @@ import java.sql.Time;
import java.sql.Timestamp;
import java.util.Calendar;
import java.util.Map;
+import java.util.function.Supplier;
/**
* A default JDBC ResultSet implementation delegating all JDBC 4.0 calls to an
@@ -68,20 +69,29 @@ import java.util.Map;
*/
public class DefaultResultSet extends JDBC41ResultSet implements ResultSet {
- private final ResultSet delegate;
- private final Statement creator;
+ private final ResultSet delegate;
+ private final Statement creator;
+ private final Supplier extends SQLException> errorIfUnsupported;
public DefaultResultSet(ResultSet delegate) {
- this(delegate, null);
+ this(delegate, null, null);
}
public DefaultResultSet(ResultSet delegate, Statement creator) {
- this.delegate = delegate;
- this.creator = creator;
+ this(delegate, creator, null);
}
- public ResultSet getDelegate() {
- return delegate;
+ public DefaultResultSet(ResultSet delegate, Statement creator, Supplier extends SQLException> errorIfUnsupported) {
+ this.delegate = delegate;
+ this.creator = creator;
+ this.errorIfUnsupported = errorIfUnsupported;
+ }
+
+ public ResultSet getDelegate() throws SQLException {
+ if (delegate != null || errorIfUnsupported == null)
+ return delegate;
+ else
+ throw errorIfUnsupported.get();
}
@Override
diff --git a/jOOQ/src/main/java/org/jooq/tools/jdbc/DefaultStatement.java b/jOOQ/src/main/java/org/jooq/tools/jdbc/DefaultStatement.java
index 464d97049d..c679f60615 100644
--- a/jOOQ/src/main/java/org/jooq/tools/jdbc/DefaultStatement.java
+++ b/jOOQ/src/main/java/org/jooq/tools/jdbc/DefaultStatement.java
@@ -42,6 +42,7 @@ import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.Statement;
+import java.util.function.Supplier;
/**
* A default JDBC Statement implementation delegating all JDBC 4.0 calls to an
@@ -51,24 +52,33 @@ import java.sql.Statement;
*/
public class DefaultStatement extends JDBC41Statement implements Statement {
- private final Statement delegate;
- private final Connection creator;
+ private final Statement delegate;
+ private final Connection creator;
+ private final Supplier extends SQLException> errorIfUnsupported;
public DefaultStatement(Statement delegate) {
- this(delegate, null);
+ this(delegate, null, null);
}
public DefaultStatement(Statement delegate, Connection creator) {
- this.delegate = delegate;
- this.creator = creator;
+ this(delegate, creator, null);
}
- public Statement getDelegate() {
+ public DefaultStatement(Statement delegate, Connection creator, Supplier extends SQLException> errorIfUnsupported) {
+ this.delegate = delegate;
+ this.creator = creator;
+ this.errorIfUnsupported = errorIfUnsupported;
+ }
+
+ public Statement getDelegate() throws SQLException {
return getDelegateStatement();
}
- public Statement getDelegateStatement() {
- return delegate;
+ public Statement getDelegateStatement() throws SQLException {
+ if (delegate != null || errorIfUnsupported == null)
+ return delegate;
+ else
+ throw errorIfUnsupported.get();
}
// ------------------------------------------------------------------------
diff --git a/jOOQ/src/main/resources/xsd/jooq-runtime-3.15.0.xsd b/jOOQ/src/main/resources/xsd/jooq-runtime-3.15.0.xsd
index a2084883c2..3b56975b81 100644
--- a/jOOQ/src/main/resources/xsd/jooq-runtime-3.15.0.xsd
+++ b/jOOQ/src/main/resources/xsd/jooq-runtime-3.15.0.xsd
@@ -69,12 +69,12 @@ This is set to "QUOTED" by default for backwards-compatibility.
-
Named parameter syntax defaults to :name (such as supported by Oracle, JPA, Spring), but
vendor specific parameters may look differently. This flag can be used to determine the prefix to be
used by named parameters, such as @ for SQL Server's @name or $
-for PostgreSQL's $name.
+for PostgreSQL's $name, when generating SQL.
"Named indexed" parameters can be obtained in the same way by specifingy {@code ParamType#NAMED} and not
providing a name to parameters, resulting in :1 or @1 or $1, etc.]]>
@@ -568,7 +568,19 @@ jOOQ queries, for which no specific fetchSize value was specified.]]>
-
+
+
+
+Named parameter syntax defaults to :name (such as supported by Oracle, JPA, Spring), but
+vendor specific parameters may look differently. This flag can be used to determine the prefix to be
+used by named parameters, such as @ for SQL Server's @name or $
+for PostgreSQL's $name when parsing SQL.
+
+"Named indexed" parameters can be obtained in the same way by specifingy {@code ParamType#NAMED} and not
+providing a name to parameters, resulting in :1 or @1 or $1, etc.]]>
+
+