private void checkSQL(ExecuteContext ctx, boolean patched) { assertTrue(ctx.batchSQL()[0].toLowerCase().contains("insert")); if (patched) { assertTrue(ctx.batchSQL()[0].toLowerCase().contains("values (")); } }
@Override public void renderStart(ExecuteContext ctx) { renderStart = ++callbackCount; checkBase(ctx); assertNull(ctx.batchSQL()[0]); assertNull(ctx.sql()); assertNull(ctx.statement()); }
private void checkSQL(ExecuteContext ctx, boolean patched) { assertTrue(ctx.batchSQL()[0].toLowerCase().contains("select")); assertTrue(ctx.sql().toLowerCase().contains("select")); assertEquals(ctx.sql(), ctx.batchSQL()[0]); if (patched) { assertTrue(ctx.sql().toLowerCase().contains("as my_field")); } }
private void checkSQL(ExecuteContext ctx, boolean patched) { for (int i = 0; i < rendered; i++) { assertTrue(ctx.batchQueries()[i].toString().toLowerCase().contains("insert")); if (patched) { assertTrue(ctx.batchSQL()[i].toLowerCase().contains("values (")); } } }
@Override public void exception(ExecuteContext ctx) { SQLDialect dialect = ctx.configuration().dialect(); SQLExceptionTranslator translator = (dialect != null) ? new SQLErrorCodeSQLExceptionTranslator(dialect.name()) : new SQLStateSQLExceptionTranslator(); ctx.exception(translator.translate("jOOQ", ctx.sql(), ctx.sqlException())); }
@Override protected final void prepare(ExecuteContext ctx) throws SQLException { // [#1296] These dialects do not implement FOR UPDATE. But the same // effect can be achieved using ResultSet.CONCUR_UPDATABLE if (isForUpdate() && asList(CUBRID, SQLSERVER).contains(ctx.getDialect())) { ctx.statement( ctx.getConnection().prepareStatement(ctx.sql(), TYPE_SCROLL_SENSITIVE, CONCUR_UPDATABLE)); } // Regular behaviour else { ctx.statement(ctx.getConnection().prepareStatement(ctx.sql())); } // [#1263] Allow for negative fetch sizes to support some non-standard // MySQL feature, where Integer.MIN_VALUE is used if (size != 0) { if (log.isDebugEnabled()) log.debug("Setting fetch size", size); ctx.statement().setFetchSize(size); } // [#1854] Set the max number of rows for this result query if (maxRows != 0) { ctx.statement().setMaxRows(maxRows); } }
@Override public void bindEnd(ExecuteContext ctx) { bindEnd = ++callbackCount; checkBase(ctx); checkSQL(ctx, true); checkStatement(ctx, true); assertNull(ctx.resultSet()); assertNull(ctx.record()); assertNull(ctx.result()); }
@Override public void renderEnd(ExecuteContext ctx) { renderEnd.add(++callbackCount); rendered++; checkBase(ctx); checkStatement(ctx, false); checkSQL(ctx, false); ctx.batchSQL()[rendered - 1] = ctx.batchSQL()[rendered - 1].replaceFirst("(?i:values\\s+)", "values "); checkSQL(ctx, true); }
@Override public void fetchStart(ExecuteContext ctx) { fetchStart = ++callbackCount; checkBase(ctx); checkSQL(ctx, true); checkStatement(ctx, true); checkResultSet(ctx, true); assertNull(ctx.record()); assertNull(ctx.result()); }
@Override public void renderEnd(ExecuteContext ctx) { renderEnd = ++callbackCount; checkBase(ctx); checkSQL(ctx, false); assertNull(ctx.statement()); ctx.sql(ctx.sql().replaceFirst("(?i:values\\s+)", "values ")); checkSQL(ctx, true); }
/** * Default implementation for query execution using a prepared statement. Subclasses may override * this method. */ protected int execute(ExecuteContext ctx, ExecuteListener listener) throws SQLException { int result = 0; listener.executeStart(ctx); if (!ctx.statement().execute()) { result = ctx.statement().getUpdateCount(); ctx.rows(result); } listener.executeEnd(ctx); return result; }
@Override public void end(ExecuteContext ctx) { end = ++callbackCount; checkBase(ctx); checkSQL(ctx, true); checkStatement(ctx, true); checkResultSet(ctx, true); assertNotNull(ctx.record()); assertEquals(2, ctx.record().fieldsRow().size()); assertNotNull(ctx.result()); assertEquals(2, ctx.result().size()); }
@Override public void executeEnd(ExecuteContext ctx) { executeEnd = ++callbackCount; checkBase(ctx); checkSQL(ctx, true); checkStatement(ctx, true); checkResultSet(ctx, false); // TODO patch result set checkResultSet(ctx, true); assertNull(ctx.record()); assertNull(ctx.result()); }
@Override public void recordEnd(ExecuteContext ctx) { recordEnd.add(++callbackCount); checkBase(ctx); checkSQL(ctx, true); checkStatement(ctx, true); checkResultSet(ctx, true); assertNotNull(ctx.record()); assertEquals(2, ctx.record().fieldsRow().size()); assertEquals(ids.remove(), ctx.record().getValue(0)); assertEquals("Hello", ctx.record().getValue(1)); }
@Override public void prepareStart(ExecuteContext ctx) { prepareStart = ++callbackCount; checkBase(ctx); checkSQL(ctx, true); assertNull(ctx.statement()); }
@Override public void renderStart(ExecuteContext ctx) { renderStart.add(++callbackCount); checkBase(ctx); checkStatement(ctx, false); checkSQL(ctx, false); assertNull(ctx.sql()); }
/** Workarounds for the unimplemented Postgres JDBC driver features */ @SuppressWarnings("unchecked") private static <T> T pgGetArray(ExecuteContext ctx, Class<? extends T> type, int index) throws SQLException { ResultSet rs = ctx.resultSet(); // Get the JDBC Array and check for null. If null, that's OK Array array = rs.getArray(index); if (array == null) { return null; } // Try fetching a Java Object[]. That's gonna work for non-UDT types try { return (T) convertArray(rs.getArray(index), (Class<? extends Object[]>) type); } // This might be a UDT (not implemented exception...) catch (Exception e) { List<Object> result = new ArrayList<Object>(); // Try fetching the array as a JDBC ResultSet try { ctx.resultSet(array.getResultSet()); while (ctx.resultSet().next()) { result.add(getFromResultSet(ctx, type.getComponentType(), 2)); } } // That might fail too, then we don't know any further... catch (Exception fatal) { log.error("Cannot parse Postgres array: " + rs.getString(index)); log.error(fatal); return null; } finally { ctx.resultSet(rs); } return (T) convertArray(result.toArray(), (Class<? extends Object[]>) type); } }
@Override public final Result<R> fetch(int number) { // [#1157] This invokes listener.fetchStart(ctx), which has to be called // Before listener.resultStart(ctx) iterator(); ResultImpl<R> result = new ResultImpl<R>(ctx.configuration(), fields); R record = null; ctx.result(result); listener.resultStart(ctx); for (int i = 0; i < number && ((record = iterator().next()) != null); i++) { result.addRecord(record); } ctx.result(result); listener.resultEnd(ctx); return result; }
private final String getSQL0(ExecuteContext ctx) { if (executePreparedStatements(configuration().settings())) { try { RenderContext render = new DefaultRenderContext(configuration); render.data(DATA_COUNT_BIND_VALUES, true); return render.render(this); } catch (DefaultRenderContext.ForceInlineSignal e) { ctx.data(DATA_FORCE_STATIC_STATEMENT, true); return getSQL(INLINED); } } else { return getSQL(INLINED); } }
private final Rendered getSQL0(ExecuteContext ctx) { Rendered result; // [#3542] [#4977] Some dialects do not support bind values in DDL statements if (ctx.type() == DDL) { ctx.data(DATA_FORCE_STATIC_STATEMENT, true); result = new Rendered(getSQL(INLINED)); } else if (executePreparedStatements(configuration().settings())) { try { DefaultRenderContext render = new DefaultRenderContext(configuration); render.data(DATA_COUNT_BIND_VALUES, true); render.visit(this); result = new Rendered(render.render(), render.bindValues()); } catch (DefaultRenderContext.ForceInlineSignal e) { ctx.data(DATA_FORCE_STATIC_STATEMENT, true); result = new Rendered(getSQL(INLINED)); } } else { result = new Rendered(getSQL(INLINED)); } return result; }
/** * Default implementation for query execution using a prepared statement. Subclasses may override * this method. */ protected int execute(ExecuteContext ctx, ExecuteListener listener) throws SQLException { int result = 0; PreparedStatement stmt = ctx.statement(); try { listener.executeStart(ctx); // [#1829] Statement.execute() is preferred over Statement.executeUpdate(), as // we might be executing plain SQL and returning results. if (!stmt.execute()) { result = stmt.getUpdateCount(); ctx.rows(result); } listener.executeEnd(ctx); return result; } // [#3011] [#3054] Consume additional exceptions if there are any catch (SQLException e) { consumeExceptions(ctx.configuration(), stmt, e); throw e; } }
@Override public void start(ExecuteContext ctx) { start = ++callbackCount; checkBase(ctx); assertNull(ctx.batchSQL()[0]); assertNull(ctx.sql()); assertNull(ctx.statement()); assertNull(ctx.resultSet()); assertNull(ctx.record()); assertNull(ctx.result()); }
@Override public void renderEnd(ExecuteContext ctx) { renderEnd = ++callbackCount; checkBase(ctx); checkSQL(ctx, false); assertNull(ctx.statement()); assertNull(ctx.resultSet()); assertNull(ctx.record()); assertNull(ctx.result()); ctx.sql(ctx.sql().replaceFirst("(?i:from)", "as my_field from")); checkSQL(ctx, true); }
@Override protected final int execute(ExecuteContext ctx, ExecuteListener listener) throws SQLException { if (returning.isEmpty()) { return super.execute(ctx, listener); } else { int result = 1; ResultSet rs; switch (ctx.configuration().dialect()) { // SQLite can select _rowid_ after the insert case SQLITE: { listener.executeStart(ctx); result = ctx.statement().executeUpdate(); listener.executeEnd(ctx); DSLContext create = DSL.using(ctx.connection(), SQLDialect.SQLITE, ctx.configuration().settings()); returned = create .select(returning) .from(getInto()) .where(rowid().equal(rowid().getDataType().convert(create.lastID()))) .fetchInto(getInto()); return result; } // Sybase can select @@identity after the insert // TODO [#832] Fix this. This might be a driver issue. JDBC // Generated keys don't work with jconn3, but they seem to work // with jTDS (which is used for Sybase ASE integration) case CUBRID: case SYBASE: { listener.executeStart(ctx); result = ctx.statement().executeUpdate(); listener.executeEnd(ctx); selectReturning(ctx.configuration(), create(ctx.configuration()).lastID()); return result; } // Some dialects can only retrieve "identity" (AUTO_INCREMENT) values // Additional values have to be fetched explicitly // [#1260] TODO CUBRID supports this, but there's a JDBC bug case ASE: case DERBY: case H2: case INGRES: case MYSQL: case SQLSERVER: { listener.executeStart(ctx); result = ctx.statement().executeUpdate(); listener.executeEnd(ctx); rs = ctx.statement().getGeneratedKeys(); try { List<Object> list = new ArrayList<Object>(); // Some JDBC drivers seem to illegally return null // from getGeneratedKeys() sometimes if (rs != null) { while (rs.next()) { list.add(rs.getObject(1)); } } selectReturning(ctx.configuration(), list.toArray()); return result; } finally { JDBCUtils.safeClose(rs); } } // Firebird and Postgres can execute the INSERT .. RETURNING // clause like a select clause. JDBC support is not implemented // in the Postgres JDBC driver case FIREBIRD: case POSTGRES: { listener.executeStart(ctx); rs = ctx.statement().executeQuery(); listener.executeEnd(ctx); break; } // These dialects have full JDBC support case DB2: case HSQLDB: case ORACLE: default: { listener.executeStart(ctx); result = ctx.statement().executeUpdate(); listener.executeEnd(ctx); rs = ctx.statement().getGeneratedKeys(); break; } } ExecuteContext ctx2 = new DefaultExecuteContext(ctx.configuration()); ExecuteListener listener2 = new ExecuteListeners(ctx2); ctx2.resultSet(rs); returned = new CursorImpl<R>(ctx2, listener2, fieldArray(returning), null, true, false) .fetch() .into(getInto()); return result; } }
@Override protected final void prepare(ExecuteContext ctx) throws SQLException { Connection connection = ctx.connection(); // Just in case, always set Sybase ASE statement mode to return // Generated keys if client code wants to SELECT @@identity afterwards if (ctx.configuration().dialect() == SQLDialect.ASE) { ctx.statement(connection.prepareStatement(ctx.sql(), Statement.RETURN_GENERATED_KEYS)); return; } // Normal statement preparing if no values should be returned else if (returning.isEmpty()) { super.prepare(ctx); return; } // Values should be returned from the INSERT else { switch (ctx.configuration().dialect()) { // Postgres uses the RETURNING clause in SQL case FIREBIRD: case POSTGRES: // SQLite will select last_insert_rowid() after the INSER case SQLITE: // Sybase will select @@identity after the INSERT case CUBRID: case SYBASE: super.prepare(ctx); return; // Some dialects can only return AUTO_INCREMENT values // Other values have to be fetched in a second step // [#1260] TODO CUBRID supports this, but there's a JDBC bug case ASE: case DERBY: case H2: case INGRES: case MYSQL: case SQLSERVER: ctx.statement(connection.prepareStatement(ctx.sql(), Statement.RETURN_GENERATED_KEYS)); return; // The default is to return all requested fields directly case DB2: case HSQLDB: case ORACLE: default: { List<String> names = new ArrayList<String>(); for (Field<?> field : returning) { names.add(field.getName()); } ctx.statement( connection.prepareStatement(ctx.sql(), names.toArray(new String[names.size()]))); return; } } } }
@SuppressWarnings("unchecked") public static <T> T getFromStatement(ExecuteContext ctx, Class<? extends T> type, int index) throws SQLException { CallableStatement stmt = (CallableStatement) ctx.statement(); if (type == Blob.class) { return (T) stmt.getBlob(index); } else if (type == Boolean.class) { return (T) checkWasNull(stmt, Boolean.valueOf(stmt.getBoolean(index))); } else if (type == BigInteger.class) { BigDecimal result = stmt.getBigDecimal(index); return (T) (result == null ? null : result.toBigInteger()); } else if (type == BigDecimal.class) { return (T) stmt.getBigDecimal(index); } else if (type == Byte.class) { return (T) checkWasNull(stmt, Byte.valueOf(stmt.getByte(index))); } else if (type == byte[].class) { return (T) stmt.getBytes(index); } else if (type == Clob.class) { return (T) stmt.getClob(index); } else if (type == Date.class) { return (T) stmt.getDate(index); } else if (type == Double.class) { return (T) checkWasNull(stmt, Double.valueOf(stmt.getDouble(index))); } else if (type == Float.class) { return (T) checkWasNull(stmt, Float.valueOf(stmt.getFloat(index))); } else if (type == Integer.class) { return (T) checkWasNull(stmt, Integer.valueOf(stmt.getInt(index))); } else if (type == Long.class) { return (T) checkWasNull(stmt, Long.valueOf(stmt.getLong(index))); } else if (type == Short.class) { return (T) checkWasNull(stmt, Short.valueOf(stmt.getShort(index))); } else if (type == String.class) { return (T) stmt.getString(index); } else if (type == Time.class) { return (T) stmt.getTime(index); } else if (type == Timestamp.class) { return (T) stmt.getTimestamp(index); } else if (type == YearToMonth.class) { if (ctx.getDialect() == POSTGRES) { Object object = stmt.getObject(index); return (T) (object == null ? null : PostgresUtils.toYearToMonth(object)); } else { String string = stmt.getString(index); return (T) (string == null ? null : YearToMonth.valueOf(string)); } } else if (type == DayToSecond.class) { if (ctx.getDialect() == POSTGRES) { Object object = stmt.getObject(index); return (T) (object == null ? null : PostgresUtils.toDayToSecond(object)); } else { String string = stmt.getString(index); return (T) (string == null ? null : DayToSecond.valueOf(string)); } } else if (type == UByte.class) { String string = stmt.getString(index); return (T) (string == null ? null : UByte.valueOf(string)); } else if (type == UShort.class) { String string = stmt.getString(index); return (T) (string == null ? null : UShort.valueOf(string)); } else if (type == UInteger.class) { String string = stmt.getString(index); return (T) (string == null ? null : UInteger.valueOf(string)); } else if (type == ULong.class) { String string = stmt.getString(index); return (T) (string == null ? null : ULong.valueOf(string)); } // The type byte[] is handled earlier. byte[][] can be handled here else if (type.isArray()) { return (T) convertArray(stmt.getObject(index), (Class<? extends Object[]>) type); } else if (ArrayRecord.class.isAssignableFrom(type)) { return (T) getArrayRecord(ctx, stmt.getArray(index), (Class<? extends ArrayRecord<?>>) type); } else if (EnumType.class.isAssignableFrom(type)) { return getEnumType(type, stmt.getString(index)); } else if (MasterDataType.class.isAssignableFrom(type)) { return (T) getMasterDataType(type, stmt.getString(index)); } else if (UDTRecord.class.isAssignableFrom(type)) { switch (ctx.getDialect()) { case POSTGRES: return (T) pgNewUDTRecord(type, stmt.getObject(index)); } return (T) stmt.getObject(index, DataTypes.udtRecords()); } else if (Result.class.isAssignableFrom(type)) { ResultSet nested = (ResultSet) stmt.getObject(index); return (T) getNewFactory(ctx).fetch(nested); } else { return (T) stmt.getObject(index); } }
@SuppressWarnings("unchecked") private static <T> T getFromResultSet(ExecuteContext ctx, Class<? extends T> type, int index) throws SQLException { ResultSet rs = ctx.resultSet(); if (type == Blob.class) { return (T) rs.getBlob(index); } else if (type == Boolean.class) { return (T) checkWasNull(rs, Boolean.valueOf(rs.getBoolean(index))); } else if (type == BigInteger.class) { // The SQLite JDBC driver doesn't support BigDecimals if (ctx.getDialect() == SQLDialect.SQLITE) { return Convert.convert(rs.getString(index), (Class<? extends T>) BigInteger.class); } else { BigDecimal result = rs.getBigDecimal(index); return (T) (result == null ? null : result.toBigInteger()); } } else if (type == BigDecimal.class) { // The SQLite JDBC driver doesn't support BigDecimals if (ctx.getDialect() == SQLDialect.SQLITE) { return Convert.convert(rs.getString(index), (Class<? extends T>) BigDecimal.class); } else { return (T) rs.getBigDecimal(index); } } else if (type == Byte.class) { return (T) checkWasNull(rs, Byte.valueOf(rs.getByte(index))); } else if (type == byte[].class) { return (T) rs.getBytes(index); } else if (type == Clob.class) { return (T) rs.getClob(index); } else if (type == Date.class) { return (T) getDate(ctx.getDialect(), rs, index); } else if (type == Double.class) { return (T) checkWasNull(rs, Double.valueOf(rs.getDouble(index))); } else if (type == Float.class) { return (T) checkWasNull(rs, Float.valueOf(rs.getFloat(index))); } else if (type == Integer.class) { return (T) checkWasNull(rs, Integer.valueOf(rs.getInt(index))); } else if (type == Long.class) { return (T) checkWasNull(rs, Long.valueOf(rs.getLong(index))); } else if (type == Short.class) { return (T) checkWasNull(rs, Short.valueOf(rs.getShort(index))); } else if (type == String.class) { return (T) rs.getString(index); } else if (type == Time.class) { return (T) getTime(ctx.getDialect(), rs, index); } else if (type == Timestamp.class) { return (T) getTimestamp(ctx.getDialect(), rs, index); } else if (type == YearToMonth.class) { if (ctx.getDialect() == POSTGRES) { Object object = rs.getObject(index); return (T) (object == null ? null : PostgresUtils.toYearToMonth(object)); } else { String string = rs.getString(index); return (T) (string == null ? null : YearToMonth.valueOf(string)); } } else if (type == DayToSecond.class) { if (ctx.getDialect() == POSTGRES) { Object object = rs.getObject(index); return (T) (object == null ? null : PostgresUtils.toDayToSecond(object)); } else { String string = rs.getString(index); return (T) (string == null ? null : DayToSecond.valueOf(string)); } } else if (type == UByte.class) { String string = rs.getString(index); return (T) (string == null ? null : UByte.valueOf(string)); } else if (type == UShort.class) { String string = rs.getString(index); return (T) (string == null ? null : UShort.valueOf(string)); } else if (type == UInteger.class) { String string = rs.getString(index); return (T) (string == null ? null : UInteger.valueOf(string)); } else if (type == ULong.class) { String string = rs.getString(index); return (T) (string == null ? null : ULong.valueOf(string)); } // The type byte[] is handled earlier. byte[][] can be handled here else if (type.isArray()) { switch (ctx.getDialect()) { case POSTGRES: { return pgGetArray(ctx, type, index); } default: // Note: due to a HSQLDB bug, it is not recommended to call rs.getObject() here: // See https://sourceforge.net/tracker/?func=detail&aid=3181365&group_id=23316&atid=378131 return (T) convertArray(rs.getArray(index), (Class<? extends Object[]>) type); } } else if (ArrayRecord.class.isAssignableFrom(type)) { return (T) getArrayRecord(ctx, rs.getArray(index), (Class<? extends ArrayRecord<?>>) type); } else if (EnumType.class.isAssignableFrom(type)) { return getEnumType(type, rs.getString(index)); } else if (MasterDataType.class.isAssignableFrom(type)) { return (T) getMasterDataType(type, rs.getObject(index)); } else if (UDTRecord.class.isAssignableFrom(type)) { switch (ctx.getDialect()) { case POSTGRES: return (T) pgNewUDTRecord(type, rs.getObject(index)); } return (T) rs.getObject(index, DataTypes.udtRecords()); } else if (Result.class.isAssignableFrom(type)) { ResultSet nested = (ResultSet) rs.getObject(index); return (T) getNewFactory(ctx).fetch(nested); } else { return (T) rs.getObject(index); } }
private final int[] executePrepared() { ExecuteContext ctx = new DefaultExecuteContext(configuration, new Query[] {query}); ExecuteListener listener = new ExecuteListeners(ctx); Connection connection = ctx.connection(); // [#1371] fetch bind variables to restore them again, later DataType<?>[] paramTypes = dataTypes(query.getParams().values().toArray(new Field[0])); try { listener.renderStart(ctx); // [#1520] TODO: Should the number of bind values be checked, here? ctx.sql(create.render(query)); listener.renderEnd(ctx); listener.prepareStart(ctx); ctx.statement(connection.prepareStatement(ctx.sql())); listener.prepareEnd(ctx); for (Object[] bindValues : allBindValues) { listener.bindStart(ctx); // [#1371] [#2139] Don't bind variables directly onto statement, bind them through the // collected params // list to preserve type information // [#3547] The original query may have no Params specified - e.g. when it was // constructed with // plain SQL. In that case, infer the bind value type directly from the bind // value List<Field<?>> params = (paramTypes.length > 0) ? fields(bindValues, paramTypes) : fields(bindValues); visitAll(new DefaultBindContext(configuration, ctx.statement()), params); listener.bindEnd(ctx); ctx.statement().addBatch(); } try { listener.executeStart(ctx); int[] result = ctx.statement().executeBatch(); int[] batchRows = ctx.batchRows(); for (int i = 0; i < batchRows.length && i < result.length; i++) batchRows[i] = result[i]; listener.executeEnd(ctx); return result; } finally { consumeWarnings(ctx, listener); } } catch (RuntimeException e) { ctx.exception(e); listener.exception(ctx); throw ctx.exception(); } catch (SQLException e) { ctx.sqlException(e); listener.exception(ctx); throw ctx.exception(); } finally { Utils.safeClose(listener, ctx); } }
@Override protected final int execute(ExecuteContext ctx, ExecuteListener listener) throws SQLException { Connection connection = ctx.getConnection(); boolean autoCommit = false; // [#706] Postgres requires two separate queries running in the same // transaction to be executed when fetching refcursor types if (ctx.getDialect() == SQLDialect.POSTGRES && isSelectingRefCursor()) { autoCommit = connection.getAutoCommit(); if (autoCommit) { if (log.isDebugEnabled()) log.debug("Unsetting auto-commit", false); connection.setAutoCommit(false); } } try { listener.executeStart(ctx); // JTDS doesn't seem to implement PreparedStatement.execute() // correctly, at least not for sp_help if (ctx.getDialect() == ASE) { ctx.resultSet(ctx.statement().executeQuery()); } // [#1232] Avoid executeQuery() in order to handle queries that may // not return a ResultSet, e.g. SQLite's pragma foreign_key_list(table) else if (ctx.statement().execute()) { ctx.resultSet(ctx.statement().getResultSet()); } listener.executeEnd(ctx); // Fetch a single result set if (!many) { if (ctx.resultSet() != null) { FieldList fields = new FieldList(getFields(ctx.resultSet().getMetaData())); cursor = new CursorImpl<R>(ctx, listener, fields, getRecordType()); if (!lazy) { result = cursor.fetch(); cursor = null; } } else { result = new ResultImpl<R>(ctx, new FieldList()); } } // Fetch several result sets else { results = new ArrayList<Result<Record>>(); boolean anyResults = false; while (ctx.resultSet() != null) { anyResults = true; FieldProvider fields = new MetaDataFieldProvider(ctx, ctx.resultSet().getMetaData()); Cursor<Record> c = new CursorImpl<Record>(ctx, listener, fields, true); results.add(c.fetch()); if (ctx.statement().getMoreResults()) { ctx.resultSet(ctx.statement().getResultSet()); } else { ctx.resultSet(null); } } // Call this only when there was at least one ResultSet. // Otherwise, this call is not supported by ojdbc... if (anyResults) { ctx.statement().getMoreResults(Statement.CLOSE_ALL_RESULTS); } ctx.statement().close(); } } finally { if (autoCommit) { if (log.isDebugEnabled()) log.debug("Resetting auto-commit", autoCommit); connection.setAutoCommit(autoCommit); } } return result != null ? result.size() : 0; }
@Override public void renderEnd(ExecuteContext ctx) { throw new QueryCollectorSignal(ctx.sql(), ctx.query()); }