This really doesn't seem too hard! Here's a quick draft leveraging the translation capabilities from the jOOQ 3.15.0-SNAPSHOT Professional Edition to translate Teradata QUALIFY to PostgreSQL (I marked the interesting stuff in yellow):
package org.jooq.r2dbc;
import static org.jooq.impl.DSL.val;
import java.util.Map;
import java.util.TreeMap;
import org.jooq.Configuration;
import org.jooq.Param;
import org.jooq.Query;
import org.jooq.SQLDialect;
import org.jooq.conf.ParamType;
import org.jooq.impl.DefaultConfiguration;
import org.reactivestreams.Publisher;
import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;
import io.r2dbc.postgresql.PostgresqlConnectionConfiguration;
import io.r2dbc.postgresql.PostgresqlConnectionFactory;
import io.r2dbc.spi.Batch;
import io.r2dbc.spi.Connection;
import io.r2dbc.spi.ConnectionFactory;
import io.r2dbc.spi.ConnectionFactoryMetadata;
import io.r2dbc.spi.ConnectionMetadata;
import io.r2dbc.spi.IsolationLevel;
import io.r2dbc.spi.Result;
import io.r2dbc.spi.Statement;
import io.r2dbc.spi.TransactionDefinition;
import io.r2dbc.spi.ValidationDepth;
import reactor.core.publisher.Flux;
public class X {
public static void main(String[] args) {
PostgresqlConnectionConfiguration config = PostgresqlConnectionConfiguration.builder()
.username("postgres")
.password("test")
.host("localhost")
.database("postgres")
.build();
ConnectionFactory f = new PostgresqlConnectionFactory(config);
Flux.from(new X(f).factory().create())
.flatMap(connection -> connection
.createStatement(
"select row_number() over w, table_schema, table_name "
+ "from information_schema.tables "
+ "window w as (order by table_schema, table_name) "
+ "qualify row_number() over w between :1 and :2 "
+ "order by 1, 2 ")
.bind(1, 6)
.bind(2, 10)
.execute()
)
.flatMap(it -> it.map((a, b) -> String.format("%1$5s: %2$s.%3$s", a.get(0), a.get(1), a.get(2))))
.collectList()
.block()
.stream()
.forEach(System.out::println);
}
final Configuration configuration;
final ConnectionFactory delegate;
public X(ConnectionFactory delegate) {
this.configuration = new DefaultConfiguration()
.set(SQLDialect.POSTGRES);
this.configuration
.settings()
.withRenderNamedParamPrefix("$")
.withParamType(ParamType.NAMED);
this.delegate = delegate;
}
ConnectionFactory factory() {
return new ConnectionFactory() {
@Override
public ConnectionFactoryMetadata getMetadata() {
return () -> "jOOQ";
}
@Override
public Publisher<? extends Connection> create() {
Publisher<? extends Connection> connection = delegate.create();
return subscriber -> {
connection.subscribe(new Subscriber<Connection>() {
@Override
public void onSubscribe(Subscription s) {
subscriber.onSubscribe(s);
}
@Override
public void onNext(Connection connection) {
subscriber.onNext(new Connection() {
@Override
public Publisher<Void> beginTransaction() {
return connection.beginTransaction();
}
@Override
public Publisher<Void> beginTransaction(TransactionDefinition definition) {
return connection.beginTransaction(definition);
}
@Override
public Publisher<Void> close() {
return connection.close();
}
@Override
public Publisher<Void> commitTransaction() {
return connection.commitTransaction();
}
@Override
public Publisher<Void> createSavepoint(String name) {
return connection.createSavepoint(name);
}
@Override
public boolean isAutoCommit() {
return connection.isAutoCommit();
}
@Override
public ConnectionMetadata getMetadata() {
return connection.getMetadata();
}
@Override
public IsolationLevel getTransactionIsolationLevel() {
return connection.getTransactionIsolationLevel();
}
@Override
public Publisher<Void> releaseSavepoint(String name) {
return connection.releaseSavepoint(name);
}
@Override
public Publisher<Void> rollbackTransaction() {
return connection.rollbackTransaction();
}
@Override
public Publisher<Void> rollbackTransactionToSavepoint(String name) {
return connection.rollbackTransactionToSavepoint(name);
}
@Override
public Publisher<Void> setAutoCommit(boolean autoCommit) {
return connection.setAutoCommit(autoCommit);
}
@Override
public Publisher<Void> setTransactionIsolationLevel(IsolationLevel isolationLevel) {
return connection.setTransactionIsolationLevel(isolationLevel);
}
@Override
public Publisher<Boolean> validate(ValidationDepth depth) {
return connection.validate(depth);
}
@Override
public Batch createBatch() {
// TODO
throw new UnsupportedOperationException();
}
@Override
public Statement createStatement(String input) {
return new Statement() {
Map<Integer, Param<?>> params = new TreeMap<>();
@Override
public Statement add() {
// TODO
throw new UnsupportedOperationException();
}
@Override
public Statement bind(int index, Object value) {
params.put(index, val(value));
return this;
}
@Override
public Statement bind(String name, Object value) {
// TODO
throw new UnsupportedOperationException();
}
@Override
public Statement bindNull(int index, Class<?> type) {
params.put(index, val(null, type));
return this;
}
@Override
public Statement bindNull(String name, Class<?> type) {
// TODO
throw new UnsupportedOperationException();
}
@Override
public Publisher<? extends Result> execute() {
Query query = configuration.dsl().parser().parseQuery(input, params.values().toArray());
Statement statement = connection.createStatement(query.getSQL());
int i = 0;
for (Param<?> o : query.getParams().values())
if (!o.isInline())
if (o.getValue() == null)
statement.bindNull(i++, o.getType());
else
statement.bind(i++, o.getValue());
return statement.execute();
}
};
}
});
}
@Override
public void onError(Throwable t) {
subscriber.onError(t);
}
@Override
public void onComplete() {
subscriber.onComplete();
}
});
};
}
};
}
}