Skip to content

Commit 3adede0

Browse files
committed
Improve the implementation of prepared statement.
Motivation: The implementation of prepared statement tries to accomodate both a lazy mode and a non lazy one, leading to the utilization of a future to unify the obtention of the prepared statement. This is clearly suboptimal for the common case, since lazy prepared statement is only necessary when meeting indeterminate prepared statement (ambiguous) that requires a tuple for an actual preparation. In addition the execution is done using a context comparison which fails when on a duplicated context. Changes: Use the context in thread comparison instead of context equality. Make a prepared statement base which has two implementations lazy/direct which are both optimized for their specific cases.
1 parent 0aaff5f commit 3adede0

File tree

4 files changed

+77
-57
lines changed

4 files changed

+77
-57
lines changed

vertx-sql-client/src/main/java/io/vertx/sqlclient/impl/CursorImpl.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
public class CursorImpl implements Cursor {
3737

3838
private final Connection conn;
39-
private final PreparedStatementImpl ps;
39+
private final PreparedStatementBase ps;
4040
private final ContextInternal context;
4141
private final boolean autoCommit;
4242
private final TupleInternal params;
@@ -45,7 +45,7 @@ public class CursorImpl implements Cursor {
4545
private boolean closed;
4646
private QueryResultBuilder<RowSet<Row>, RowSetImpl<Row>, RowSet<Row>> result;
4747

48-
CursorImpl(PreparedStatementImpl ps, Connection conn, ContextInternal context, boolean autoCommit, TupleInternal params) {
48+
CursorImpl(PreparedStatementBase ps, Connection conn, ContextInternal context, boolean autoCommit, TupleInternal params) {
4949
this.ps = ps;
5050
this.conn = conn;
5151
this.context = context;

vertx-sql-client/src/main/java/io/vertx/sqlclient/impl/PreparedStatementImpl.java renamed to vertx-sql-client/src/main/java/io/vertx/sqlclient/impl/PreparedStatementBase.java

Lines changed: 70 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -44,45 +44,92 @@
4444
/**
4545
* @author <a href="mailto:[email protected]">Julien Viet</a>
4646
*/
47-
public class PreparedStatementImpl implements PreparedStatement {
47+
public abstract class PreparedStatementBase implements PreparedStatement {
4848

4949
public static PreparedStatement create(Connection conn, ContextInternal context, io.vertx.sqlclient.internal.PreparedStatement ps, boolean autoCommit) {
50-
return new PreparedStatementImpl(conn, context, ps, autoCommit);
50+
return new PreparedStatementBase(conn, context, ps, autoCommit) {
51+
@Override
52+
protected void withPreparedStatement2(PrepareOptions options, Tuple args, Handler<AsyncResult<io.vertx.sqlclient.internal.PreparedStatement>> handler) {
53+
handler.handle(Future.succeededFuture(ps));
54+
}
55+
@Override
56+
protected void doClose(Promise<Void> promise) {
57+
CloseStatementCommand cmd = new CloseStatementCommand(ps);
58+
conn.schedule(cmd, promise);
59+
}
60+
@Override
61+
protected void closeCursor(String cursorId, Promise<Void> promise) {
62+
CloseCursorCommand cmd = new CloseCursorCommand(cursorId, ps);
63+
conn.schedule(cmd, promise);
64+
}
65+
};
5166
}
5267

5368
public static PreparedStatement create(Connection conn, ContextInternal context, PrepareOptions options, String sql, boolean autoCommit) {
54-
return new PreparedStatementImpl(conn, context, sql, options, autoCommit);
69+
return new PreparedStatementBase(conn, context, sql, options, autoCommit) {
70+
Future<io.vertx.sqlclient.internal.PreparedStatement> future;
71+
@Override
72+
protected void withPreparedStatement2(PrepareOptions options, Tuple args, Handler<AsyncResult<io.vertx.sqlclient.internal.PreparedStatement>> handler) {
73+
if (future == null) {
74+
Promise<io.vertx.sqlclient.internal.PreparedStatement> promise = context.promise();
75+
PrepareStatementCommand prepare = new PrepareStatementCommand(sql, options, true, args.types());
76+
conn.schedule(prepare, promise);
77+
future = promise.future();
78+
}
79+
future.onComplete(handler);
80+
}
81+
@Override
82+
protected void doClose(Promise<Void> promise) {
83+
if (future != null) {
84+
future.onComplete(ar -> {
85+
if (ar.succeeded()) {
86+
CloseStatementCommand cmd = new CloseStatementCommand(ar.result());
87+
conn.schedule(cmd, promise);
88+
} else {
89+
promise.fail(ar.cause());
90+
}
91+
});
92+
}
93+
}
94+
@Override
95+
protected void closeCursor(String cursorId, Promise<Void> promise) {
96+
if (future != null) {
97+
future.onComplete(ar -> {
98+
if (ar.succeeded()) {
99+
CloseCursorCommand cmd = new CloseCursorCommand(cursorId, ar.result());
100+
conn.schedule(cmd, promise);
101+
} else {
102+
promise.fail(ar.cause());
103+
}
104+
});
105+
} else {
106+
promise.fail("Invalid");
107+
}
108+
}
109+
};
55110
}
56111

57112
private final Connection conn;
58113
private final ContextInternal context;
59-
private final String sql;
60114
private final PrepareOptions options;
61-
private Promise<io.vertx.sqlclient.internal.PreparedStatement> promise;
62-
private Future<io.vertx.sqlclient.internal.PreparedStatement> future;
63115
private final boolean autoCommit;
64116
private final AtomicBoolean closed = new AtomicBoolean();
65117

66-
private PreparedStatementImpl(Connection conn, ContextInternal context, io.vertx.sqlclient.internal.PreparedStatement ps, boolean autoCommit) {
118+
private PreparedStatementBase(Connection conn, ContextInternal context, io.vertx.sqlclient.internal.PreparedStatement ps, boolean autoCommit) {
67119
this.conn = conn;
68120
this.context = context;
69-
this.sql = null;
70121
this.options = null;
71-
this.promise = null;
72-
this.future = Future.succeededFuture(ps);
73122
this.autoCommit = autoCommit;
74123
}
75124

76-
private PreparedStatementImpl(Connection conn,
125+
private PreparedStatementBase(Connection conn,
77126
ContextInternal context,
78127
String sql,
79128
PrepareOptions options,
80129
boolean autoCommit) {
81130
this.conn = conn;
82131
this.context = context;
83-
this.sql = sql;
84132
this.options = options;
85-
this.promise = Promise.promise();
86133
this.autoCommit = autoCommit;
87134
}
88135

@@ -97,19 +144,15 @@ public PreparedQuery<RowSet<Row>> query() {
97144
}
98145

99146
void withPreparedStatement(PrepareOptions options, Tuple args, Handler<AsyncResult<io.vertx.sqlclient.internal.PreparedStatement>> handler) {
100-
if (context == Vertx.currentContext()) {
101-
if (future == null) {
102-
// Lazy statement;
103-
PrepareStatementCommand prepare = new PrepareStatementCommand(sql, options, true, args.types());
104-
conn.schedule(prepare, promise);
105-
future = promise.future();
106-
}
107-
future.onComplete(handler);
147+
if (context.inThread()) {
148+
withPreparedStatement2(options, args, handler);
108149
} else {
109150
context.runOnContext(v -> withPreparedStatement(options, args, handler));
110151
}
111152
}
112153

154+
protected abstract void withPreparedStatement2(PrepareOptions options, Tuple args, Handler<AsyncResult<io.vertx.sqlclient.internal.PreparedStatement>> handler);
155+
113156
<R, F extends SqlResult<R>> void execute(Tuple args,
114157
int fetch,
115158
String cursorId,
@@ -155,27 +198,13 @@ private Cursor cursor(TupleInternal args) {
155198
return new CursorImpl(this, conn, context, autoCommit, args);
156199
}
157200

201+
protected abstract void doClose(Promise<Void> promise);
202+
158203
@Override
159204
public Future<Void> close() {
160205
if (closed.compareAndSet(false, true)) {
161206
Promise<Void> promise = context.promise();
162-
if (this.promise == null) {
163-
CloseStatementCommand cmd = new CloseStatementCommand(future.result());
164-
conn.schedule(cmd, promise);
165-
} else {
166-
if (future == null) {
167-
future = this.promise.future();
168-
this.promise.fail("Closed");
169-
}
170-
future.onComplete(ar -> {
171-
if (ar.succeeded()) {
172-
CloseStatementCommand cmd = new CloseStatementCommand(ar.result());
173-
conn.schedule(cmd, promise);
174-
} else {
175-
promise.complete();
176-
}
177-
});
178-
}
207+
doClose(promise);
179208
return promise.future();
180209
} else {
181210
return context.failedFuture("Already closed");
@@ -187,16 +216,7 @@ public RowStream<Row> createStream(int fetch, Tuple args) {
187216
return new RowStreamImpl(this, context, fetch, args);
188217
}
189218

190-
void closeCursor(String cursorId, Promise<Void> promise) {
191-
future.onComplete(ar -> {
192-
if (ar.succeeded()) {
193-
CloseCursorCommand cmd = new CloseCursorCommand(cursorId, ar.result());
194-
conn.schedule(cmd, promise);
195-
} else {
196-
promise.fail(ar.cause());
197-
}
198-
});
199-
}
219+
protected abstract void closeCursor(String cursorId, Promise<Void> promise);
200220

201221
private class PreparedStatementQuery<T, R extends SqlResult<T>> extends QueryBase<T, R> implements PreparedQuery<R> {
202222

@@ -232,7 +252,7 @@ public Future<R> execute(Tuple args) {
232252
}
233253

234254
private void execute(Tuple args, PromiseInternal<R> promise) {
235-
PreparedStatementImpl.this.execute(args, 0, null, false, builder, promise);
255+
PreparedStatementBase.this.execute(args, 0, null, false, builder, promise);
236256
}
237257

238258
@Override
@@ -246,7 +266,7 @@ private void executeBatch(List<Tuple> argsList, PromiseInternal<R> promise) {
246266
if (argsList.isEmpty()) {
247267
promise.fail("Empty batch");
248268
} else {
249-
PreparedStatementImpl.this.executeBatch(argsList, builder, promise);
269+
PreparedStatementBase.this.executeBatch(argsList, builder, promise);
250270
}
251271
}
252272
}

vertx-sql-client/src/main/java/io/vertx/sqlclient/impl/RowStreamImpl.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232

3333
public class RowStreamImpl implements RowStreamInternal, Handler<AsyncResult<RowSet<Row>>> {
3434

35-
private final PreparedStatementImpl ps;
35+
private final PreparedStatementBase ps;
3636
private final ContextInternal context;
3737
private final int fetch;
3838
private final Tuple params;
@@ -46,7 +46,7 @@ public class RowStreamImpl implements RowStreamInternal, Handler<AsyncResult<Row
4646
private boolean readInProgress;
4747
private Iterator<Row> result;
4848

49-
RowStreamImpl(PreparedStatementImpl ps, ContextInternal context, int fetch, Tuple params) {
49+
RowStreamImpl(PreparedStatementBase ps, ContextInternal context, int fetch, Tuple params) {
5050
this.ps = ps;
5151
this.context = context;
5252
this.fetch = fetch;

vertx-sql-client/src/main/java/io/vertx/sqlclient/internal/SqlConnectionBase.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
import io.vertx.sqlclient.PreparedStatement;
2727
import io.vertx.sqlclient.SqlConnection;
2828
import io.vertx.sqlclient.Transaction;
29-
import io.vertx.sqlclient.impl.PreparedStatementImpl;
29+
import io.vertx.sqlclient.impl.PreparedStatementBase;
3030
import io.vertx.sqlclient.impl.TransactionImpl;
3131
import io.vertx.sqlclient.internal.command.CommandBase;
3232
import io.vertx.sqlclient.internal.command.PrepareStatementCommand;
@@ -78,10 +78,10 @@ public Future<PreparedStatement> prepare(String sql, PrepareOptions options) {
7878
schedule(new PrepareStatementCommand(sql, options, true), promise);
7979
return promise.future()
8080
.compose(
81-
cr -> Future.succeededFuture(PreparedStatementImpl.create(conn, context, cr, autoCommit())),
81+
cr -> Future.succeededFuture(PreparedStatementBase.create(conn, context, cr, autoCommit())),
8282
err -> {
8383
if (conn.isIndeterminatePreparedStatementError(err)) {
84-
return Future.succeededFuture(PreparedStatementImpl.create(conn, context, options, sql, autoCommit()));
84+
return Future.succeededFuture(PreparedStatementBase.create(conn, context, options, sql, autoCommit()));
8585
} else {
8686
return Future.failedFuture(err);
8787
}

0 commit comments

Comments
 (0)