diff --git a/src/tap/ADQLExecutor.java b/src/tap/ADQLExecutor.java
index 27ec125ac116d6d36c8cb72b14bd871ba2970214..841e023090afa134b58f7ff194b6fe598c6b56bc 100644
--- a/src/tap/ADQLExecutor.java
+++ b/src/tap/ADQLExecutor.java
@@ -104,7 +104,7 @@ import adql.query.ADQLQuery;
  * </p>
  * 
  * @author Gr&eacute;gory Mantelet (CDS;ARI)
- * @version 2.0 (04/2015)
+ * @version 2.1 (11/2015)
  */
 public class ADQLExecutor {
 
@@ -224,9 +224,20 @@ public class ADQLExecutor {
 		try{
 			return start();
 		}catch(IOException ioe){
-			throw new UWSException(ioe);
+			if (thread.isInterrupted())
+				return report;
+			else
+				throw new UWSException(ioe);
 		}catch(TAPException te){
-			throw new UWSException(te.getHttpErrorCode(), te);
+			if (thread.isInterrupted())
+				return report;
+			else
+				throw new UWSException(te.getHttpErrorCode(), te);
+		}catch(UWSException ue){
+			if (thread.isInterrupted())
+				return report;
+			else
+				throw ue;
 		}
 	}
 
@@ -249,6 +260,17 @@ public class ADQLExecutor {
 			dbConn = service.getFactory().getConnection(jobID);
 	}
 
+	/** 
+	 * Cancel the current SQL query execution or result set fetching if any is currently running.
+	 * If no such process is on going, this function has no effect.
+	 * 
+	 * @since 2.1
+	 */
+	public final void cancelQuery(){
+		if (dbConn != null && progression == ExecutionProgression.EXECUTING_ADQL)
+			dbConn.cancel(true);
+	}
+
 	/**
 	 * <p>Start the synchronous processing of the ADQL query.</p>
 	 * 
@@ -625,12 +647,14 @@ public class ADQLExecutor {
 		}
 		// CASE ASYNCHRONOUS:
 		else{
+			boolean completed = false;
 			long start = -1, end = -1;
+			Result result = null;
+			JobThread jobThread = (JobThread)thread;
 			try{
 				// Create a UWS Result object to store the result
 				// (the result will be stored in a file and this object is the association between the job and the result file):
-				JobThread jobThread = (JobThread)thread;
-				Result result = jobThread.createResult();
+				result = jobThread.createResult();
 
 				// Set the MIME type of the result format in the result description:
 				result.setMimeType(formatter.getMimeType());
@@ -646,10 +670,25 @@ public class ADQLExecutor {
 				// Add the result description and link in the job description:
 				jobThread.publishResult(result);
 
+				completed = true;
+
 				logger.logTAP(LogLevel.INFO, report, "RESULT_WRITTEN", "Result formatted (in " + formatter.getMimeType() + " ; " + (report.nbRows < 0 ? "?" : report.nbRows) + " rows ; " + ((report.resultingColumns == null) ? "?" : report.resultingColumns.length) + " columns) in " + ((start <= 0 || end <= 0) ? "?" : (end - start)) + "ms!", null);
 
 			}catch(IOException ioe){
+				// Propagate the exception:
 				throw new UWSException(UWSException.INTERNAL_SERVER_ERROR, ioe, "Impossible to write in the file into the result of the job " + report.jobID + " must be written!");
+			}finally{
+				if (!completed){
+					// Delete the result file (it is either incomplete or incorrect ;
+					// it is then not reliable and is anyway not associated with the job and so could not be later deleted when the job will be):
+					if (result != null){
+						try{
+							service.getFileManager().deleteResult(result, jobThread.getJob());
+						}catch(IOException ioe){
+							logger.logTAP(LogLevel.ERROR, report, "WRITING_RESULT", "The result writting has failed and the produced partial result must be deleted, but this deletion also failed! (job: " + report.jobID + ")", ioe);
+						}
+					}
+				}
 			}
 		}
 	}
diff --git a/src/tap/TAPJob.java b/src/tap/TAPJob.java
index aa89491428ac40e8d058d38b58fac92a7b6102a1..c2e2ea974465d22cf5d3fbfc1d49911a7c858b9e 100644
--- a/src/tap/TAPJob.java
+++ b/src/tap/TAPJob.java
@@ -45,7 +45,7 @@ import uws.service.log.UWSLog.LogLevel;
  * </p>
  * 
  * @author Gr&eacute;gory Mantelet (CDS;ARI)
- * @version 2.0 (04/2015)
+ * @version 2.1 (11/2015)
  */
 public class TAPJob extends UWSJob {
 	private static final long serialVersionUID = 1L;
@@ -341,6 +341,31 @@ public class TAPJob extends UWSJob {
 		}
 	}
 
+	/** @since 2.1 */
+	@Override
+	protected void stop(){
+		if (!isStopped()){
+			synchronized(thread){
+				stopping = true;
+
+				// Interrupts the thread:
+				thread.interrupt();
+
+				// Cancel the query execution if any currently running:
+				((AsyncThread)thread).executor.cancelQuery();
+
+				// Wait a little for its end:
+				if (waitForStop > 0){
+					try{
+						thread.join(waitForStop);
+					}catch(InterruptedException ie){
+						getLogger().logJob(LogLevel.WARNING, this, "END", "Unexpected InterruptedException while waiting for the end of the execution of the job \"" + jobId + "\" (thread ID: " + thread.getId() + ")!", ie);
+					}
+				}
+			}
+		}
+	}
+
 	/**
 	 * This exception is thrown by a job execution when no database connection are available anymore.
 	 * 
diff --git a/src/tap/config/ConfigurableTAPFactory.java b/src/tap/config/ConfigurableTAPFactory.java
index 02432f78d919c12b7588d28d34f82c946a1394bd..d42e2621f20bb9c7db0ce7ae7b1e3acd1e542f94 100644
--- a/src/tap/config/ConfigurableTAPFactory.java
+++ b/src/tap/config/ConfigurableTAPFactory.java
@@ -74,7 +74,7 @@ import adql.translator.PostgreSQLTranslator;
  * </p>
  * 
  * @author Gr&eacute;gory Mantelet (ARI)
- * @version 2.0 (04/2015)
+ * @version 2.1 (11/2015)
  * @since 2.0
  */
 public final class ConfigurableTAPFactory extends AbstractTAPFactory {
@@ -281,6 +281,9 @@ public final class ConfigurableTAPFactory extends AbstractTAPFactory {
 	@Override
 	public void freeConnection(DBConnection conn){
 		try{
+			// Cancel any possible query that could be running:
+			conn.cancel(false);
+			// Close the connection (if a connection pool is used, the connection is not really closed but is freed and kept in the pool for further usage):
 			((JDBCConnection)conn).getInnerConnection().close();
 		}catch(SQLException se){
 			service.getLogger().error("Can not close properly the connection \"" + conn.getID() + "\"!", se);
diff --git a/src/tap/data/ResultSetTableIterator.java b/src/tap/data/ResultSetTableIterator.java
index af4fdeac67d4a91ae82a04a0bc50778f680b314d..f5e7684fdb4da690f4ba3e12fbeebeefd51dcfe7 100644
--- a/src/tap/data/ResultSetTableIterator.java
+++ b/src/tap/data/ResultSetTableIterator.java
@@ -22,6 +22,7 @@ package tap.data;
 import java.sql.ResultSet;
 import java.sql.ResultSetMetaData;
 import java.sql.SQLException;
+import java.sql.Statement;
 import java.sql.Timestamp;
 import java.util.NoSuchElementException;
 
@@ -42,11 +43,16 @@ import adql.translator.JDBCTranslator;
  * </i></p>
  * 
  * @author Gr&eacute;gory Mantelet (ARI)
- * @version 2.1 (10/2015)
+ * @version 2.1 (11/2015)
  * @since 2.0
  */
 public class ResultSetTableIterator implements TableIterator {
 
+	/** Statement associated with the ResultSet/Dataset to read.
+	 * <i>MAY be NULL</i>
+	 * @since 2.1 */
+	private final Statement stmt;
+
 	/** ResultSet/Dataset to read. */
 	private final ResultSet data;
 
@@ -90,10 +96,41 @@ public class ResultSetTableIterator implements TableIterator {
 	 * @throws DataReadException	If the given ResultSet is closed or if the metadata (columns count and types) can not be fetched.
 	 * 
 	 * @see #convertType(int, String, String)
-	 * @see #ResultSetTableIterator(ResultSet, JDBCTranslator, String, DBColumn[])
+	 * @see #ResultSetTableIterator(Statement, ResultSet, JDBCTranslator, String, DBColumn[])
 	 */
 	public ResultSetTableIterator(final ResultSet dataSet) throws NullPointerException, DataReadException{
-		this(dataSet, null, null, null);
+		this(null, dataSet, null, null, null);
+	}
+
+	/**
+	 * <p>Build a TableIterator able to read rows and columns of the given ResultSet.</p>
+	 * 
+	 * <p>
+	 * 	In order to provide the metadata through {@link #getMetadata()}, this constructor is trying to guess the datatype
+	 * 	from the DBMS column datatype (using {@link #convertType(int, String, String)}).
+	 * </p>
+	 * 
+	 * <h3>Type guessing</h3>
+	 * 
+	 * <p>
+	 * 	In order to guess a TAP type from a DBMS type, this constructor will call {@link #convertType(int, String, String)}
+	 * 	which deals with the most common standard datatypes known in Postgres, SQLite, MySQL, Oracle and JavaDB/Derby.
+	 * 	This conversion is therefore not as precise as the one expected by a translator. That's why it is recommended
+	 * 	to use one of the constructor having a {@link JDBCTranslator} in parameter.
+	 * </p>
+	 * 
+	 * @param dataSet		Dataset over which this iterator must iterate.
+	 * 
+	 * @throws NullPointerException	If NULL is given in parameter.
+	 * @throws DataReadException	If the given ResultSet is closed or if the metadata (columns count and types) can not be fetched.
+	 * 
+	 * @see #convertType(int, String, String)
+	 * @see #ResultSetTableIterator(Statement, ResultSet, JDBCTranslator, String, DBColumn[])
+	 * 
+	 * @since 2.1
+	 */
+	public ResultSetTableIterator(final Statement stmt, final ResultSet dataSet) throws NullPointerException, DataReadException{
+		this(stmt, dataSet, null, null, null);
 	}
 
 	/**
@@ -127,10 +164,49 @@ public class ResultSetTableIterator implements TableIterator {
 	 * @throws DataReadException	If the given ResultSet is closed or if the metadata (columns count and types) can not be fetched.
 	 * 
 	 * @see #convertType(int, String, String)
-	 * @see ResultSetTableIterator#ResultSetTableIterator(ResultSet, JDBCTranslator, String, DBColumn[])
+	 * @see #ResultSetTableIterator(Statement, ResultSet, JDBCTranslator, String, DBColumn[])
 	 */
 	public ResultSetTableIterator(final ResultSet dataSet, final String dbms) throws NullPointerException, DataReadException{
-		this(dataSet, null, dbms, null);
+		this(null, dataSet, null, dbms, null);
+	}
+
+	/**
+	 * <p>Build a TableIterator able to read rows and columns of the given ResultSet.</p>
+	 * 
+	 * <p>
+	 * 	In order to provide the metadata through {@link #getMetadata()}, this constructor is trying to guess the datatype
+	 * 	from the DBMS column datatype (using {@link #convertType(int, String, String)}).
+	 * </p>
+	 * 
+	 * <h3>Type guessing</h3>
+	 * 
+	 * <p>
+	 * 	In order to guess a TAP type from a DBMS type, this constructor will call {@link #convertType(int, String, String)}
+	 * 	which deals with the most common standard datatypes known in Postgres, SQLite, MySQL, Oracle and JavaDB/Derby.
+	 * 	This conversion is therefore not as precise as the one expected by a translator. That's why it is recommended
+	 * 	to use one of the constructor having a {@link JDBCTranslator} in parameter.
+	 * </p>
+	 * 
+	 * <p><i><b>Important</b>:
+	 * 	The second parameter of this constructor is given as second parameter of {@link #convertType(int, String, String)}.
+	 * 	<b>This parameter is really used ONLY when the DBMS is SQLite ("sqlite").</b>
+	 * 	Indeed, SQLite has so many datatype restrictions that it is absolutely needed to know it is the DBMS from which the
+	 * 	ResultSet is coming. Without this information, type guessing will be unpredictable! 
+	 * </i></p>
+	 * 
+	 * @param dataSet		Dataset over which this iterator must iterate.
+	 * @param dbms			Lower-case string which indicates from which DBMS the given ResultSet is coming. <i>note: MAY be NULL.</i>
+	 * 
+	 * @throws NullPointerException	If NULL is given in parameter.
+	 * @throws DataReadException	If the given ResultSet is closed or if the metadata (columns count and types) can not be fetched.
+	 * 
+	 * @see #convertType(int, String, String)
+	 * @see #ResultSetTableIterator(Statement, ResultSet, JDBCTranslator, String, DBColumn[])
+	 * 
+	 * @since 2.1
+	 */
+	public ResultSetTableIterator(final Statement stmt, final ResultSet dataSet, final String dbms) throws NullPointerException, DataReadException{
+		this(stmt, dataSet, null, dbms, null);
 	}
 
 	/**
@@ -159,10 +235,44 @@ public class ResultSetTableIterator implements TableIterator {
 	 * @throws DataReadException	If the given ResultSet is closed or if the metadata (columns count and types) can not be fetched.
 	 * 
 	 * @see #convertType(int, String, String)
-	 * @see ResultSetTableIterator#ResultSetTableIterator(ResultSet, JDBCTranslator, String, DBColumn[])
+	 * @see #ResultSetTableIterator(Statement, ResultSet, JDBCTranslator, String, DBColumn[])
 	 */
 	public ResultSetTableIterator(final ResultSet dataSet, final JDBCTranslator translator) throws NullPointerException, DataReadException{
-		this(dataSet, translator, null, null);
+		this(null, dataSet, translator, null, null);
+	}
+
+	/**
+	 * <p>Build a TableIterator able to read rows and columns of the given ResultSet.</p>
+	 * 
+	 * <p>
+	 * 	In order to provide the metadata through {@link #getMetadata()}, this constructor is trying to guess the datatype
+	 * 	from the DBMS column datatype (using {@link #convertType(int, String, String)}).
+	 * </p>
+	 * 
+	 * <h3>Type guessing</h3>
+	 * 
+	 * <p>
+	 * 	In order to guess a TAP type from a DBMS type, this constructor will call {@link #convertType(int, String, String)}
+	 * 	which will ask to the given translator ({@link JDBCTranslator#convertTypeFromDB(int, String, String, String[])})
+	 * 	if not NULL. However if no translator is provided, this function will proceed to a default conversion
+	 * 	using the most common standard datatypes known in Postgres, SQLite, MySQL, Oracle and JavaDB/Derby.
+	 * 	This conversion is therefore not as precise as the one expected by the translator.
+	 * </p>
+	 * 
+	 * @param dataSet		Dataset over which this iterator must iterate.
+	 * @param translator	The {@link JDBCTranslator} used to transform the ADQL query into SQL query. This translator is also able to convert
+	 *                  	JDBC types and to parse geometrical values. <i>note: MAY be NULL</i> 
+	 * 
+	 * @throws NullPointerException	If NULL is given in parameter.
+	 * @throws DataReadException	If the given ResultSet is closed or if the metadata (columns count and types) can not be fetched.
+	 * 
+	 * @see #convertType(int, String, String)
+	 * @see #ResultSetTableIterator(Statement, ResultSet, JDBCTranslator, String, DBColumn[])
+	 * 
+	 * @since 2.1
+	 */
+	public ResultSetTableIterator(final Statement stmt, final ResultSet dataSet, final JDBCTranslator translator) throws NullPointerException, DataReadException{
+		this(stmt, dataSet, translator, null, null);
 	}
 
 	/**
@@ -199,10 +309,52 @@ public class ResultSetTableIterator implements TableIterator {
 	 * @throws DataReadException	If the given ResultSet is closed or if the metadata (columns count and types) can not be fetched.
 	 * 
 	 * @see #convertType(int, String, String)
-	 * @see ResultSetTableIterator#ResultSetTableIterator(ResultSet, JDBCTranslator, String, DBColumn[])
+	 * @see #ResultSetTableIterator(Statement, ResultSet, JDBCTranslator, String, DBColumn[])
 	 */
 	public ResultSetTableIterator(final ResultSet dataSet, final JDBCTranslator translator, final String dbms) throws NullPointerException, DataReadException{
-		this(dataSet, translator, dbms, null);
+		this(null, dataSet, translator, dbms, null);
+	}
+
+	/**
+	 * <p>Build a TableIterator able to read rows and columns of the given ResultSet.</p>
+	 * 
+	 * <p>
+	 * 	In order to provide the metadata through {@link #getMetadata()}, this constructor is trying to guess the datatype
+	 * 	from the DBMS column datatype (using {@link #convertType(int, String, String)}).
+	 * </p>
+	 * 
+	 * <h3>Type guessing</h3>
+	 * 
+	 * <p>
+	 * 	In order to guess a TAP type from a DBMS type, this constructor will call {@link #convertType(int, String, String)}
+	 * 	which will ask to the given translator ({@link JDBCTranslator#convertTypeFromDB(int, String, String, String[])})
+	 * 	if not NULL. However if no translator is provided, this function will proceed to a default conversion
+	 * 	using the most common standard datatypes known in Postgres, SQLite, MySQL, Oracle and JavaDB/Derby.
+	 * 	This conversion is therefore not as precise as the one expected by the translator.
+	 * </p>
+	 * 
+	 * <p><i><b>Important</b>:
+	 * 	The third parameter of this constructor is given as second parameter of {@link #convertType(int, String, String)}.
+	 * 	<b>This parameter is really used ONLY when the translator conversion failed and when the DBMS is SQLite ("sqlite").</b>
+	 * 	Indeed, SQLite has so many datatype restrictions that it is absolutely needed to know it is the DBMS from which the
+	 * 	ResultSet is coming. Without this information, type guessing will be unpredictable! 
+	 * </i></p>
+	 * 
+	 * @param dataSet		Dataset over which this iterator must iterate.
+	 * @param translator	The {@link JDBCTranslator} used to transform the ADQL query into SQL query. This translator is also able to convert
+	 *                  	JDBC types and to parse geometrical values. <i>note: MAY be NULL</i> 
+	 * @param dbms			Lower-case string which indicates from which DBMS the given ResultSet is coming. <i>note: MAY be NULL.</i>
+	 * 
+	 * @throws NullPointerException	If NULL is given in parameter.
+	 * @throws DataReadException	If the given ResultSet is closed or if the metadata (columns count and types) can not be fetched.
+	 * 
+	 * @see #convertType(int, String, String)
+	 * @see #ResultSetTableIterator(Statement, ResultSet, JDBCTranslator, String, DBColumn[])
+	 * 
+	 * @since 2.1
+	 */
+	public ResultSetTableIterator(final Statement stmt, final ResultSet dataSet, final JDBCTranslator translator, final String dbms) throws NullPointerException, DataReadException{
+		this(stmt, dataSet, translator, dbms, null);
 	}
 
 	/**
@@ -256,12 +408,74 @@ public class ResultSetTableIterator implements TableIterator {
 	 * @throws DataReadException	If the metadata (columns count and types) can not be fetched.
 	 * 
 	 * @see #convertType(int, String, String)
+	 * @see #ResultSetTableIterator(Statement, ResultSet, JDBCTranslator, String, DBColumn[])
 	 */
 	public ResultSetTableIterator(final ResultSet dataSet, final JDBCTranslator translator, final String dbms, final DBColumn[] resultMeta) throws NullPointerException, DataReadException{
+		this(null, dataSet, translator, dbms, resultMeta);
+	}
+
+	/**
+	 * <p>Build a TableIterator able to read rows and columns of the given ResultSet.</p>
+	 * 
+	 * <p>
+	 * 	In order to provide the metadata through {@link #getMetadata()}, this constructor is reading first the given metadata (if any),
+	 * 	and then, try to guess the datatype from the DBMS column datatype (using {@link #convertType(int, String, String)}).
+	 * </p>
+	 * 
+	 * <h3>Provided metadata</h3>
+	 * 
+	 * <p>The third parameter of this constructor aims to provide the metadata expected for each column of the ResultSet.</p>
+	 * 
+	 * <p>
+	 * 	For that, it is expected that all these metadata are {@link TAPColumn} objects. Indeed, simple {@link DBColumn}
+	 * 	instances do not have the type information. If just {@link DBColumn}s are provided, the ADQL name it provides will be kept
+	 * 	but the type will be guessed from the type provide by the ResultSetMetadata.
+	 * </p>
+	 * 
+	 * <p><i>Note:
+	 * 	If this parameter is incomplete (array length less than the column count returned by the ResultSet or some array items are NULL),
+	 * 	column metadata will be associated in the same order as the ResultSet columns. Missing metadata will be built from the
+	 * 	{@link ResultSetMetaData} and so the types will be guessed.
+	 * </i></p>
+	 * 
+	 * <h3>Type guessing</h3>
+	 * 
+	 * <p>
+	 * 	In order to guess a TAP type from a DBMS type, this constructor will call {@link #convertType(int, String, String)}
+	 * 	which will ask to the given translator ({@link JDBCTranslator#convertTypeFromDB(int, String, String, String[])})
+	 * 	if not NULL. However if no translator is provided, this function will proceed to a default conversion
+	 * 	using the most common standard datatypes known in Postgres, SQLite, MySQL, Oracle and JavaDB/Derby.
+	 * 	This conversion is therefore not as precise as the one expected by the translator.
+	 * </p>
+	 * 
+	 * <p><i><b>Important</b>:
+	 * 	The third parameter of this constructor is given as second parameter of {@link #convertType(int, String, String)}.
+	 * 	<b>This parameter is really used ONLY when the translator conversion failed and when the DBMS is SQLite ("sqlite").</b>
+	 * 	Indeed, SQLite has so many datatype restrictions that it is absolutely needed to know it is the DBMS from which the
+	 * 	ResultSet is coming. Without this information, type guessing will be unpredictable! 
+	 * </i></p>
+	 * 
+	 * @param dataSet		Dataset over which this iterator must iterate.
+	 * @param translator	The {@link JDBCTranslator} used to transform the ADQL query into SQL query. This translator is also able to convert
+	 *                  	JDBC types and to parse geometrical values. <i>note: MAY be NULL</i> 
+	 * @param dbms			Lower-case string which indicates from which DBMS the given ResultSet is coming. <i>note: MAY be NULL.</i>
+	 * @param resultMeta	List of expected columns. <i>note: these metadata are expected to be really {@link TAPColumn} objects ; MAY be NULL.</i>
+	 * 
+	 * @throws NullPointerException	If NULL is given in parameter.
+	 * @throws DataReadException	If the metadata (columns count and types) can not be fetched.
+	 * 
+	 * @see #convertType(int, String, String)
+	 * 
+	 * @since 2.1
+	 */
+	public ResultSetTableIterator(final Statement stmt, final ResultSet dataSet, final JDBCTranslator translator, final String dbms, final DBColumn[] resultMeta) throws NullPointerException, DataReadException{
 		// A dataset MUST BE provided:
 		if (dataSet == null)
 			throw new NullPointerException("Missing ResultSet object over which to iterate!");
 
+		// Set the associated statement:
+		this.stmt = stmt;
+
 		// Keep a reference to the ResultSet:
 		data = dataSet;
 
@@ -296,10 +510,17 @@ public class ResultSetTableIterator implements TableIterator {
 
 	@Override
 	public void close() throws DataReadException{
+		boolean rsClosed = false;
 		try{
 			data.close();
+			rsClosed = true;
+			if (stmt != null)
+				stmt.close();
 		}catch(SQLException se){
-			throw new DataReadException("Can not close the iterated ResultSet!", se);
+			if (!rsClosed)
+				throw new DataReadException("Can not close the iterated ResultSet!", se);
+			else
+				throw new DataReadException("ResultSet successfully closed but impossible to closed the associated Statement!", se);
 		}
 	}
 
diff --git a/src/tap/db/DBConnection.java b/src/tap/db/DBConnection.java
index e836211115b3159bae26c789792821f3de27b892..612954ed2a65731e9da8c28daa7e414171eb0f80 100644
--- a/src/tap/db/DBConnection.java
+++ b/src/tap/db/DBConnection.java
@@ -42,7 +42,7 @@ import adql.query.ADQLQuery;
  * </p>
  * 
  * @author Gr&eacute;gory Mantelet (CDS;ARI)
- * @version 2.0 (03/2015)
+ * @version 2.1 (11/2015)
  */
 public interface DBConnection {
 
@@ -268,4 +268,31 @@ public interface DBConnection {
 	 */
 	public void setFetchSize(final int size);
 
+	/**
+	 * <p>Stop the execution of the current query.</p>
+	 * 
+	 * <p>
+	 * 	If asked. a rollback of the current transaction can also be performed
+	 * 	after the cancellation (if successful) of the query.
+	 * </p>
+	 * 
+	 * <p>
+	 * 	This function should <b>never</b> return any kind of exception. This is particularly important
+	 * 	in the following cases:
+	 * </p>
+	 * <ul>
+	 * 	<li>this function is not implemented</li>
+	 * 	<li>the database driver or another API used to interact with a "database" does not support the cancel operation</li>
+	 * 	<li>no query is currently running</li>
+	 * 	<li>a rollback is not possible or failed</li>
+	 * </ul>
+	 * <p>However, if an exception occurs it may be directly logged at least as a WARNING.</p>
+	 * 
+	 * @param rollback	<code>true</code> to cancel the statement AND rollback the current connection transaction,
+	 *                	<code>false</code> to just cancel the statement.
+	 * 
+	 * @since 2.1
+	 */
+	public void cancel(final boolean rollback);
+
 }
diff --git a/src/tap/db/JDBCConnection.java b/src/tap/db/JDBCConnection.java
index f8dc32d10fb854bcc3716424b79ca7e869a228a5..d8ed5977c67f96b46886cf56a38922299cbbfcf6 100644
--- a/src/tap/db/JDBCConnection.java
+++ b/src/tap/db/JDBCConnection.java
@@ -27,6 +27,7 @@ import java.sql.DriverManager;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
+import java.sql.SQLFeatureNotSupportedException;
 import java.sql.Statement;
 import java.sql.Timestamp;
 import java.text.ParseException;
@@ -70,6 +71,29 @@ import adql.translator.TranslationException;
  * 	Then it has been really tested successfully with Postgres and SQLite.
  * </i></p>
  * 
+ * 
+ * <h3>Only one query executed at a time!</h3>
+ * 
+ * <p>
+ * 	With a single instance of {@link JDBCConnection} it is possible to execute only one query (whatever the type: SELECT, UPDATE, DELETE, ...)
+ * 	at a time. This is indeed the simple way chosen with this implementation in order to allow the cancellation of any query by managing only
+ * 	one {@link Statement}. Indeed, only a {@link Statement} has a cancel function able to stop any query execution on the database.
+ * 	So all queries are executed with the same {@link Statement}. Thus, allowing the execution of one query at a time lets
+ * 	abort only one query rather than several in once (though just one should have been stopped).
+ * </p>
+ * 
+ * <p>
+ * 	All the following functions are synchronized in order to prevent parallel execution of them by several threads:
+ * 	{@link #addUploadedTable(TAPTable, TableIterator)}, {@link #dropUploadedTable(TAPTable)}, {@link #executeQuery(ADQLQuery)},
+ * 	{@link #getTAPSchema()} and {@link #setTAPSchema(TAPMetadata)}.
+ * </p>
+ * 
+ * <p>
+ * 	To cancel a query execution the function {@link #cancel(boolean)} must be called. No error is returned by this function in case
+ * 	no query is currently executing.
+ * </p>
+ * 
+ * 
  * <h3>Deal with different DBMS features</h3>
  * 
  * <p>Update queries are taking into account whether the following features are supported by the DBMS:</p>
@@ -99,6 +123,7 @@ import adql.translator.TranslationException;
  * 	All these features have no impact at all on ADQL query executions ({@link #executeQuery(ADQLQuery)}).
  * </i></p>
  * 
+ * 
  * <h3>Datatypes</h3>
  * 
  * <p>
@@ -119,6 +144,7 @@ import adql.translator.TranslationException;
  * 	and managed.
  * </p>
  * 
+ * 
  * <h3>Fetch size</h3>
  * 
  * <p>
@@ -144,7 +170,7 @@ import adql.translator.TranslationException;
  * </i></p>
  * 
  * @author Gr&eacute;gory Mantelet (CDS;ARI)
- * @version 2.1 (07/2015)
+ * @version 2.1 (11/2015)
  * @since 2.0
  */
 public class JDBCConnection implements DBConnection {
@@ -170,6 +196,31 @@ public class JDBCConnection implements DBConnection {
 	/** JDBC connection (created and initialized at the creation of this {@link JDBCConnection} instance). */
 	protected final Connection connection;
 
+	/** <p>The only {@link Statement} instance that should be used in this {@link JDBCConnection}.
+	 * Having the same {@link Statement} for all the interactions with the database lets cancel any when needed (e.g. when the execution is too long).</p>
+	 * <p>This statement is by default NULL ; it must be initialized by the function {@link #getStatement()}.</p>
+	 * @since 2.1 */
+	protected Statement stmt = null;
+
+	/**
+	 * <p>It <code>true</code>, this flag indicates that the function {@link #cancel(boolean)} has been called successfully.</p>
+	 * 
+	 * <p>{@link #cancel(boolean)} sets this flag to <code>true</code>.</p>
+	 * <p>
+	 * 	All functions executing any kind of query on the database MUST set this flag to <code>false</code> before doing anything
+	 * 	by calling the function {@link #resetCancel()}.
+	 * </p>
+	 * <p>
+	 * 	This flag is particularly useful for debugging: when an exception is detected inside a function executing a query,
+	 * 	this flag is used to know whether the exception should be ignored for logging (if <code>true</code>) or not.
+	 * </p>
+	 * <p>
+	 * 	Any access (write AND read) to this flag MUST be synchronized on it using one of the following functions:
+	 * 	{@link #cancel(boolean)}, {@link #resetCancel()} and {@link #isCancelled()}.
+	 * </p>
+	 * @since 2.1 */
+	private Boolean cancelled = false;
+
 	/** The translator this connection must use to translate ADQL into SQL. It is also used to get information about the case sensitivity of all types of identifier (schema, table, column). */
 	protected final JDBCTranslator translator;
 
@@ -198,6 +249,13 @@ public class JDBCConnection implements DBConnection {
 	/** Indicate whether the DBMS has the notion of SCHEMA. Most of the DBMS has it, but not SQLite for instance. <i>note: If not supported, the DB table name will be prefixed by the DB schema name followed by the character "_". Nevertheless, if the DB schema name is NULL, the DB table name will never be prefixed.</i> */
 	protected boolean supportsSchema;
 
+	/** <p>Indicate whether a DBMS statement is able to cancel a query execution.</p>
+	 * <p> Since this information is not provided by {@link DatabaseMetaData} a first attempt is always performed.
+	 * In case a {@link SQLFeatureNotSupportedException} is caught, this flag is set to false preventing any further
+	 * attempt of canceling a query.</p>
+	 * @since 2.1 */
+	protected boolean supportsCancel = true;
+
 	/* CASE SENSITIVITY SUPPORT */
 
 	/** Indicate whether UNquoted identifiers will be considered as case INsensitive and stored in mixed case by the DBMS. <i>note: If FALSE, unquoted identifiers will still be considered as case insensitive for the researches, but will be stored in lower or upper case (in function of {@link #lowerCaseUnquoted} and {@link #upperCaseUnquoted}). If none of these two flags is TRUE, the storage case will be though considered as mixed.</i> */
@@ -397,11 +455,196 @@ public class JDBCConnection implements DBConnection {
 		return connection;
 	}
 
+	/**
+	 * <p>Get the only statement associated with this {@link JDBCConnection}.</p>
+	 * 
+	 * <p>
+	 * 	If no {@link Statement} is yet existing, one is created, stored in this {@link JDBCConnection} (for further uses)
+	 * 	and then returned.
+	 * </p>
+	 * 
+	 * @return	The {@link Statement} instance associated with this {@link JDBCConnection}. <i>Never NULL</i>
+	 * 
+	 * @throws SQLException	In case a {@link Statement} can not be created.
+	 * 
+	 * @since 2.1
+	 */
+	protected Statement getStatement() throws SQLException{
+		if (stmt == null || stmt.isClosed())
+			return (stmt = connection.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY));
+		else
+			return stmt;
+	}
+
+	/**
+	 * Close the only statement associated with this {@link JDBCConnection}.
+	 * 
+	 * @since 2.1
+	 */
+	protected void closeStatement(){
+		close(stmt);
+		stmt = null;
+	}
+
+	/**
+	 * <p>Cancel (and rollback when possible) the currently running query of this {@link JDBCConnection} instance.</p>
+	 * 
+	 * <p><b>Important note:</b>
+	 * 	This function is effective only if the JDBC driver and DBMS both support
+	 * 	this operation.
+	 * </p>
+	 * <p>
+	 * 	If a call of this function fails the flag {@link #supportsCancel} is set to false
+	 * 	so that any subsequent call of this function for this instance of {@link JDBCConnection}
+	 * 	does not try any other cancellation attempt.
+	 * </p>
+	 * 
+	 * <p><i>Note 1: 
+	 * 	A failure of a rollback is not considered as a not supported cancellation feature by the JDBC driver or the DBMS.
+	 * 	So if the cancellation succeeds but a rollback fails, a next call of this function will still try cancelling the given statement.
+	 * </i></p>
+	 * 
+	 * <p><i>Note 2:
+	 * 	In case of cancellation success, the flag {@link #cancelled} is set to <code>true</code>.
+	 * 	Thus, the function executing a query can know that if any SQL exception is thrown, it will be due to the cancellation and
+	 * 	should not be then considered as a real error (=> exception not logged but anyway propagated in order to stop any processing).
+	 * </i></p></p>
+	 * 
+	 * <p><i>Note 3:
+	 * 	This function is synchronized on the {@link #cancelled} flag.
+	 * 	Thus, it may block until another synchronized block on this same flag is finished.
+	 * </i></p>
+	 * 
+	 * @param rollback	The statement to cancel. <i>Note: if closed or NULL, nothing will be done and no exception will be thrown.</i>
+	 * 
+	 * @see DBConnection#cancel(boolean)
+	 * @see #cancel(Statement, boolean)
+	 * 
+	 * @since 2.1
+	 */
+	@Override
+	public final void cancel(final boolean rollback){
+		if (supportsCancel && stmt != null){
+			synchronized(cancelled){
+				cancelled = cancel(stmt, rollback);
+				// Log the success of the cancellation:
+				if (cancelled && logger != null)
+					logger.logDB(LogLevel.INFO, this, "CANCEL", "Query execution successfully stopped!", null);
+			}
+		}
+	}
+
+	/**
+	 * <p>Cancel (and rollback when asked and if possible) the given statement.</p>
+	 * 
+	 * <p><b>Important note:</b>
+	 * 	This function is effective only if the JDBC driver and DBMS both support
+	 * 	this operation.
+	 * </p>
+	 * <p>
+	 * 	If a call of this function fails the flag {@link #supportsCancel} is set to false
+	 * 	so that any subsequent call of this function for this instance of {@link JDBCConnection}
+	 * 	does not try any other cancellation attempt.
+	 * </p>
+	 * 
+	 * <p><i>Note: 
+	 * 	A failure of a rollback is not considered as a not supported cancellation feature by the JDBC driver or the DBMS.
+	 * 	So if the cancellation succeeds but a rollback fails, a next call of this function will still try canceling the given statement.
+	 * </i></p>
+	 * 
+	 * @param stmt		The statement to cancel. <i>Note: if closed or NULL, nothing will be done and no exception will be thrown.</i>
+	 * @param rollback	<code>true</code> to cancel the statement AND rollback the current connection transaction,
+	 *                	<code>false</code> to just cancel the statement.
+	 * 
+	 * @return	<code>true</code> if the cancellation succeeded (or none was running),
+	 *        	<code>false</code> otherwise (and especially if the "cancel" operation is not supported).
+	 * 
+	 * @since 2.1
+	 */
+	protected boolean cancel(final Statement stmt, final boolean rollback){
+		// Not supported "cancel" operation => fail!
+		if (!supportsCancel)
+			return false;
+
+		// No statement => "cancellation" successful!
+		if (stmt == null)
+			return true;
+
+		// If the statement is not already closed, cancel its current query execution:
+		try{
+			if (!stmt.isClosed()){
+				// Cancel the query execution:
+				stmt.cancel();
+				// Rollback all executed operations (only if in a transaction ; that's to say if AutoCommit = false):
+				if (rollback && supportsTransaction){
+					try{
+						if (!connection.getAutoCommit())
+							connection.rollback();
+					}catch(SQLException se){
+						if (logger != null)
+							logger.logDB(LogLevel.ERROR, this, "CANCEL", "Query execution successfully stopped BUT the rollback fails!", se);
+					}
+				}
+			}
+			return true;
+		}catch(SQLFeatureNotSupportedException sfnse){
+			// prevent further cancel attempts:
+			supportsCancel = false;
+			// log a warning:
+			if (logger != null)
+				logger.logDB(LogLevel.WARNING, this, "CANCEL", "This JDBC driver does not support Statement.cancel(). No further cancel attempt will be performed with this JDBCConnection instance.", sfnse);
+			return false;
+
+		}catch(SQLException se){
+			if (logger != null)
+				logger.logDB(LogLevel.ERROR, this, "CANCEL", "Abortion of the current query apparently fails! The query may still run on the database server.", se);
+			return false;
+		}
+	}
+
+	/**
+	 * <p>Tell whether the last query execution has been canceled.</p>
+	 * 
+	 * <p><i>Note:
+	 * 	This function is synchronized on the {@link #cancelled} flag.
+	 * 	Thus, it may block until another synchronized block on this same flag is finished.
+	 * </i></p>
+	 * 
+	 * @return	<code>true</code> if the last query execution has been cancelled,
+	 *        	<code>false</code> otherwise.
+	 * 
+	 * @since 2.1
+	 */
+	protected final boolean isCancelled(){
+		synchronized(cancelled){
+			return cancelled;
+		}
+	}
+
+	/**
+	 * <p>Reset the {@link #cancelled} flag to <code>false</code>.</p>
+	 * 
+	 * <p><i>Note:
+	 * 	This function is synchronized on the {@link #cancelled} flag.
+	 * 	Thus, it may block until another synchronized block on this same flag is finished.
+	 * </i></p>
+	 * 
+	 * @since 2.1
+	 */
+	protected final void resetCancel(){
+		synchronized(cancelled){
+			cancelled = false;
+		}
+	}
+
 	/* ********************* */
 	/* INTERROGATION METHODS */
 	/* ********************* */
 	@Override
-	public TableIterator executeQuery(final ADQLQuery adqlQuery) throws DBException{
+	public synchronized TableIterator executeQuery(final ADQLQuery adqlQuery) throws DBException{
+		// Starting of new query execution => disable the cancel flag: 
+		resetCancel();
+
 		String sql = null;
 		ResultSet result = null;
 		try{
@@ -415,19 +658,25 @@ public class JDBCConnection implements DBConnection {
 				try{
 					connection.setAutoCommit(false);
 				}catch(SQLException se){
-					supportsFetchSize = false;
-					if (logger != null)
-						logger.logDB(LogLevel.WARNING, this, "RESULT", "Fetch size unsupported!", null);
+					if (!isCancelled()){
+						supportsFetchSize = false;
+						if (logger != null)
+							logger.logDB(LogLevel.WARNING, this, "RESULT", "Fetch size unsupported!", null);
+					}
 				}
 			}
-			Statement stmt = connection.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
+
+			getStatement();
+
 			if (supportsFetchSize){
 				try{
 					stmt.setFetchSize(fetchSize);
 				}catch(SQLException se){
-					supportsFetchSize = false;
-					if (logger != null)
-						logger.logDB(LogLevel.WARNING, this, "RESULT", "Fetch size unsupported!", null);
+					if (!isCancelled()){
+						supportsFetchSize = false;
+						if (logger != null)
+							logger.logDB(LogLevel.WARNING, this, "RESULT", "Fetch size unsupported!", null);
+					}
 				}
 			}
 
@@ -443,16 +692,19 @@ public class JDBCConnection implements DBConnection {
 
 		}catch(SQLException se){
 			close(result);
-			if (logger != null)
+			closeStatement();
+			if (!isCancelled() && logger != null)
 				logger.logDB(LogLevel.ERROR, this, "EXECUTE", "Unexpected error while EXECUTING SQL query!", null);
 			throw new DBException("Unexpected error while executing a SQL query: " + se.getMessage(), se);
 		}catch(TranslationException te){
 			close(result);
+			closeStatement();
 			if (logger != null)
 				logger.logDB(LogLevel.ERROR, this, "TRANSLATE", "Unexpected error while TRANSLATING ADQL into SQL!", null);
 			throw new DBException("Unexpected error while translating ADQL into SQL: " + te.getMessage(), te);
 		}catch(DataReadException dre){
 			close(result);
+			closeStatement();
 			if (logger != null)
 				logger.logDB(LogLevel.ERROR, this, "RESULT", "Unexpected error while reading the query result!", null);
 			throw new DBException("Impossible to read the query result, because: " + dre.getMessage(), dre);
@@ -460,7 +712,19 @@ public class JDBCConnection implements DBConnection {
 	}
 
 	/**
-	 * Create a {@link TableIterator} instance which lets reading the given result table.
+	 * <p>Create a {@link TableIterator} instance which lets reading the given result table.</p>
+	 * 
+	 * <p><b>Important note 1:</b>
+	 * 	This function also set to NULL the statement of this {@link JDBCConnection} instance: {@link #stmt}.
+	 * 	However, the statement is not closed ; it is just given to a {@link ResultSetTableIterator} iterator
+	 * 	which will close it in the same time as the given {@link ResultSet}, when its function
+	 * 	{@link ResultSetTableIterator#close()} is called.
+	 * </p>
+	 * 
+	 * <p><b>Important note 2:</b>
+	 * 	In case an exception occurs within this function, the {@link ResultSet} and the {@link Statement}
+	 * 	are <b>immediately closed</b> before propagating the exception.
+	 * </p>
 	 * 
 	 * @param rs				Result of an SQL query.
 	 * @param resultingColumns	Metadata corresponding to each columns of the result.
@@ -471,7 +735,19 @@ public class JDBCConnection implements DBConnection {
 	 *                          	or if any other error occurs.
 	 */
 	protected TableIterator createTableIterator(final ResultSet rs, final DBColumn[] resultingColumns) throws DataReadException{
-		return new ResultSetTableIterator(rs, translator, dbms, resultingColumns);
+		// Dis-associate the current Statement from this JDBCConnection instance:
+		Statement itStmt = stmt;
+		stmt = null;
+		// Return a TableIterator wrapping the given ResultSet:
+		try{
+			return new ResultSetTableIterator(itStmt, rs, translator, dbms, resultingColumns);
+		}catch(Throwable t){
+			// In case of any kind of exception, the ResultSet and the Statement MUST be closed in order to save resources:
+			close(rs);
+			close(itStmt);
+			// Then, the caught exception can be thrown:
+			throw (t instanceof DataReadException) ? (DataReadException)t : new DataReadException(t);
+		}
 	}
 
 	/* *********************** */
@@ -528,7 +804,10 @@ public class JDBCConnection implements DBConnection {
 	 * @see tap.db.DBConnection#getTAPSchema()
 	 */
 	@Override
-	public TAPMetadata getTAPSchema() throws DBException{
+	public synchronized TAPMetadata getTAPSchema() throws DBException{
+		// Starting of new query execution => disable the cancel flag: 
+		resetCancel();
+
 		// Build a virgin TAP metadata:
 		TAPMetadata metadata = new TAPMetadata();
 
@@ -536,10 +815,9 @@ public class JDBCConnection implements DBConnection {
 		TAPSchema tap_schema = TAPMetadata.getStdSchema(supportsSchema);
 
 		// LOAD ALL METADATA FROM THE STANDARD TAP TABLES:
-		Statement stmt = null;
 		try{
 			// create a common statement for all loading functions:
-			stmt = connection.createStatement();
+			getStatement();
 
 			// load all schemas from TAP_SCHEMA.schemas:
 			if (logger != null)
@@ -562,11 +840,11 @@ public class JDBCConnection implements DBConnection {
 			loadKeys(tap_schema.getTable(STDTable.KEYS.label), tap_schema.getTable(STDTable.KEY_COLUMNS.label), lstTables, stmt);
 
 		}catch(SQLException se){
-			if (logger != null)
+			if (!isCancelled() && logger != null)
 				logger.logDB(LogLevel.ERROR, this, "LOAD_TAP_SCHEMA", "Impossible to create a Statement!", se);
 			throw new DBException("Can not create a Statement!", se);
 		}finally{
-			close(stmt);
+			closeStatement();
 		}
 
 		return metadata;
@@ -617,7 +895,7 @@ public class JDBCConnection implements DBConnection {
 				metadata.addSchema(newSchema);
 			}
 		}catch(SQLException se){
-			if (logger != null)
+			if (!isCancelled() && logger != null)
 				logger.logDB(LogLevel.ERROR, this, "LOAD_TAP_SCHEMA", "Impossible to load schemas from TAP_SCHEMA.schemas!", se);
 			throw new DBException("Impossible to load schemas from TAP_SCHEMA.schemas!", se);
 		}finally{
@@ -711,7 +989,7 @@ public class JDBCConnection implements DBConnection {
 
 			return lstTables;
 		}catch(SQLException se){
-			if (logger != null)
+			if (!isCancelled() && logger != null)
 				logger.logDB(LogLevel.ERROR, this, "LOAD_TAP_SCHEMA", "Impossible to load tables from TAP_SCHEMA.tables!", se);
 			throw new DBException("Impossible to load tables from TAP_SCHEMA.tables!", se);
 		}finally{
@@ -799,7 +1077,7 @@ public class JDBCConnection implements DBConnection {
 				table.addColumn(newColumn);
 			}
 		}catch(SQLException se){
-			if (logger != null)
+			if (!isCancelled() && logger != null)
 				logger.logDB(LogLevel.ERROR, this, "LOAD_TAP_SCHEMA", "Impossible to load columns from TAP_SCHEMA.columns!", se);
 			throw new DBException("Impossible to load columns from TAP_SCHEMA.columns!", se);
 		}finally{
@@ -873,7 +1151,7 @@ public class JDBCConnection implements DBConnection {
 					while(rsKeyCols.next())
 						columns.put(rsKeyCols.getString(1), rsKeyCols.getString(2));
 				}catch(SQLException se){
-					if (logger != null)
+					if (!isCancelled() && logger != null)
 						logger.logDB(LogLevel.ERROR, this, "LOAD_TAP_SCHEMA", "Impossible to load key columns from TAP_SCHEMA.key_columns for the foreign key: \"" + key_id + "\"!", se);
 					throw new DBException("Impossible to load key columns from TAP_SCHEMA.key_columns for the foreign key: \"" + key_id + "\"!", se);
 				}finally{
@@ -884,13 +1162,13 @@ public class JDBCConnection implements DBConnection {
 				try{
 					sourceTable.addForeignKey(key_id, targetTable, columns, nullifyIfNeeded(description), nullifyIfNeeded(utype));
 				}catch(Exception ex){
-					if (logger != null)
+					if ((ex instanceof SQLException && !isCancelled()) && logger != null)
 						logger.logDB(LogLevel.ERROR, this, "LOAD_TAP_SCHEMA", "Impossible to create the foreign key \"" + key_id + "\" because: " + ex.getMessage(), ex);
 					throw new DBException("Impossible to create the foreign key \"" + key_id + "\" because: " + ex.getMessage(), ex);
 				}
 			}
 		}catch(SQLException se){
-			if (logger != null)
+			if (!isCancelled() && logger != null)
 				logger.logDB(LogLevel.ERROR, this, "LOAD_TAP_SCHEMA", "Impossible to load columns from TAP_SCHEMA.columns!", se);
 			throw new DBException("Impossible to load columns from TAP_SCHEMA.columns!", se);
 		}finally{
@@ -924,8 +1202,9 @@ public class JDBCConnection implements DBConnection {
 	 * @see tap.db.DBConnection#setTAPSchema(tap.metadata.TAPMetadata)
 	 */
 	@Override
-	public void setTAPSchema(final TAPMetadata metadata) throws DBException{
-		Statement stmt = null;
+	public synchronized void setTAPSchema(final TAPMetadata metadata) throws DBException{
+		// Starting of new query execution => disable the cancel flag: 
+		resetCancel();
 
 		try{
 			// A. GET THE DEFINITION OF ALL STANDARD TAP TABLES:
@@ -934,7 +1213,7 @@ public class JDBCConnection implements DBConnection {
 			startTransaction();
 
 			// B. RE-CREATE THE STANDARD TAP_SCHEMA TABLES:
-			stmt = connection.createStatement();
+			getStatement();
 
 			// 1. Ensure TAP_SCHEMA exists and drop all its standard TAP tables:
 			if (logger != null)
@@ -960,12 +1239,12 @@ public class JDBCConnection implements DBConnection {
 
 			commit();
 		}catch(SQLException se){
-			if (logger != null)
+			if (!isCancelled() && logger != null)
 				logger.logDB(LogLevel.ERROR, this, "CREATE_TAP_SCHEMA", "Impossible to SET TAP_SCHEMA in DB!", se);
 			rollback();
 			throw new DBException("Impossible to SET TAP_SCHEMA in DB!", se);
 		}finally{
-			close(stmt);
+			closeStatement();
 			endTransaction();
 		}
 	}
@@ -1639,21 +1918,23 @@ public class JDBCConnection implements DBConnection {
 	 * @see #checkUploadedTableDef(TAPTable)
 	 */
 	@Override
-	public boolean addUploadedTable(TAPTable tableDef, TableIterator data) throws DBException, DataReadException{
+	public synchronized boolean addUploadedTable(TAPTable tableDef, TableIterator data) throws DBException, DataReadException{
 		// If no table to upload, consider it has been dropped and return TRUE:
 		if (tableDef == null)
 			return true;
 
+		// Starting of new query execution => disable the cancel flag: 
+		resetCancel();
+
 		// Check the table is well defined (and particularly the schema is well set with an ADQL name = TAP_UPLOAD):
 		checkUploadedTableDef(tableDef);
 
-		Statement stmt = null;
 		try{
 
 			// Start a transaction:
 			startTransaction();
 			// ...create a statement:
-			stmt = connection.createStatement();
+			getStatement();
 
 			DatabaseMetaData dbMeta = connection.getMetaData();
 
@@ -1704,7 +1985,7 @@ public class JDBCConnection implements DBConnection {
 
 		}catch(SQLException se){
 			rollback();
-			if (logger != null)
+			if (!isCancelled() && logger != null)
 				logger.logDB(LogLevel.WARNING, this, "ADD_UPLOAD_TABLE", "Impossible to create the uploaded table: " + translator.getTableName(tableDef, supportsSchema) + "!", se);
 			throw new DBException("Impossible to create the uploaded table: " + translator.getTableName(tableDef, supportsSchema) + "!", se);
 		}catch(DBException de){
@@ -1714,7 +1995,7 @@ public class JDBCConnection implements DBConnection {
 			rollback();
 			throw dre;
 		}finally{
-			close(stmt);
+			closeStatement();
 			endTransaction();
 		}
 	}
@@ -1843,15 +2124,17 @@ public class JDBCConnection implements DBConnection {
 	 * @see #checkUploadedTableDef(TAPTable)
 	 */
 	@Override
-	public boolean dropUploadedTable(final TAPTable tableDef) throws DBException{
+	public synchronized boolean dropUploadedTable(final TAPTable tableDef) throws DBException{
 		// If no table to upload, consider it has been dropped and return TRUE:
 		if (tableDef == null)
 			return true;
 
+		// Starting of new query execution => disable the cancel flag: 
+		resetCancel();
+
 		// Check the table is well defined (and particularly the schema is well set with an ADQL name = TAP_UPLOAD):
 		checkUploadedTableDef(tableDef);
 
-		Statement stmt = null;
 		try{
 
 			// Check the existence of the table to drop:
@@ -1859,8 +2142,7 @@ public class JDBCConnection implements DBConnection {
 				return true;
 
 			// Execute the update:
-			stmt = connection.createStatement();
-			int cnt = stmt.executeUpdate("DROP TABLE " + translator.getTableName(tableDef, supportsSchema) + ";");
+			int cnt = getStatement().executeUpdate("DROP TABLE " + translator.getTableName(tableDef, supportsSchema) + ";");
 
 			// Log the end:
 			if (logger != null){
@@ -1874,11 +2156,11 @@ public class JDBCConnection implements DBConnection {
 			return (cnt >= 0);
 
 		}catch(SQLException se){
-			if (logger != null)
+			if (!isCancelled() && logger != null)
 				logger.logDB(LogLevel.WARNING, this, "DROP_UPLOAD_TABLE", "Impossible to drop the uploaded table: " + translator.getTableName(tableDef, supportsSchema) + "!", se);
 			throw new DBException("Impossible to drop the uploaded table: " + translator.getTableName(tableDef, supportsSchema) + "!", se);
 		}finally{
-			close(stmt);
+			closeStatement();
 		}
 	}
 
@@ -2208,17 +2490,32 @@ public class JDBCConnection implements DBConnection {
 	 * <p>If the given {@link Statement} is NULL, nothing (even exception/error) happens.</p>
 	 * 
 	 * <p>
+	 * 	The given statement is explicitly canceled by this function before being closed.
+	 * 	Thus the corresponding DBMS process is ensured to be stopped. Of course, this
+	 * 	cancellation is effective only if this operation is supported by the JDBC driver
+	 * 	and the DBMS.
+	 * </p>
+	 * 
+	 * <p><b>Important note:</b>
+	 * 	In case of cancellation, <b>NO</b> rollback is performed.
+	 * </p>
+	 * 
+	 * <p>
 	 * 	If any {@link SQLException} occurs during this operation, it is caught and just logged
 	 * 	(see {@link TAPLog#logDB(uws.service.log.UWSLog.LogLevel, DBConnection, String, String, Throwable)}).
 	 * 	No error is thrown and nothing else is done.
 	 * </p>
 	 * 
 	 * @param stmt	{@link Statement} to close.
+	 * 
+	 * @see #cancel(Statement, boolean)
 	 */
 	protected final void close(final Statement stmt){
 		try{
-			if (stmt != null)
+			if (stmt != null){
+				cancel(stmt, false);
 				stmt.close();
+			}
 		}catch(SQLException se){
 			if (logger != null)
 				logger.logDB(LogLevel.WARNING, this, "CLOSE", "Can not close a Statement!", null);
@@ -2624,7 +2921,8 @@ public class JDBCConnection implements DBConnection {
 			try{
 				stmt.addBatch();
 			}catch(SQLException se){
-				supportsBatchUpdates = false;
+				if (!isCancelled())
+					supportsBatchUpdates = false;
 				/*
 				 * If the error happens for the first row, it is still possible to insert all rows
 				 * with the non-batch function - executeUpdate().
@@ -2633,10 +2931,10 @@ public class JDBCConnection implements DBConnection {
 				 * and must stop the whole TAP_SCHEMA initialization.
 				 */
 				if (indRow == 1){
-					if (logger != null)
+					if (!isCancelled() && logger != null)
 						logger.logDB(LogLevel.WARNING, this, "EXEC_UPDATE", "BATCH query impossible => TRYING AGAIN IN A NORMAL EXECUTION (executeUpdate())!", se);
 				}else{
-					if (logger != null)
+					if (!isCancelled() && logger != null)
 						logger.logDB(LogLevel.ERROR, this, "EXEC_UPDATE", "BATCH query impossible!", se);
 					throw new DBException("BATCH query impossible!", se);
 				}
@@ -2689,9 +2987,11 @@ public class JDBCConnection implements DBConnection {
 			try{
 				rows = stmt.executeBatch();
 			}catch(SQLException se){
-				supportsBatchUpdates = false;
-				if (logger != null)
-					logger.logDB(LogLevel.ERROR, this, "EXEC_UPDATE", "BATCH execution impossible!", se);
+				if (!isCancelled()){
+					supportsBatchUpdates = false;
+					if (logger != null)
+						logger.logDB(LogLevel.ERROR, this, "EXEC_UPDATE", "BATCH execution impossible!", se);
+				}
 				throw new DBException("BATCH execution impossible!", se);
 			}
 
@@ -2699,7 +2999,7 @@ public class JDBCConnection implements DBConnection {
 			try{
 				stmt.clearBatch();
 			}catch(SQLException se){
-				if (logger != null)
+				if (!isCancelled() && logger != null)
 					logger.logDB(LogLevel.WARNING, this, "EXEC_UPDATE", "CLEAR BATCH impossible!", se);
 			}
 
diff --git a/src/tap/formatter/FITSFormat.java b/src/tap/formatter/FITSFormat.java
index 20c7d928c84661d522ec3b17643db904840ce382..13c5b85e06e3a0dda7d549ecead3ce063703b43c 100644
--- a/src/tap/formatter/FITSFormat.java
+++ b/src/tap/formatter/FITSFormat.java
@@ -36,7 +36,7 @@ import uk.ac.starlink.table.StoragePolicy;
  * Format any given query (table) result into FITS.
  * 
  * @author Gr&eacute;gory Mantelet (ARI)
- * @version 2.0 (04/2015)
+ * @version 2.1 (11/2015)
  * @since 2.0
  */
 public class FITSFormat implements OutputFormat {
@@ -84,15 +84,21 @@ public class FITSFormat implements OutputFormat {
 		ColumnInfo[] colInfos = VOTableFormat.toColumnInfos(result, execReport, thread);
 
 		// Turns the result set into a table:
-		LimitedStarTable table = new LimitedStarTable(result, colInfos, execReport.parameters.getMaxRec());
+		LimitedStarTable table = new LimitedStarTable(result, colInfos, execReport.parameters.getMaxRec(), thread);
 
 		// Copy the table on disk (or in memory if the table is short):
 		StarTable copyTable = StoragePolicy.PREFER_DISK.copyTable(table);
 
+		if (thread.isInterrupted())
+			throw new InterruptedException();
+
 		/* Format the table in FITS (2 passes are needed for that, hence the copy on disk),
 		 * and write it in the given output stream: */
 		new FitsTableWriter().writeStarTable(copyTable, output);
 
+		if (thread.isInterrupted())
+			throw new InterruptedException();
+
 		execReport.nbRows = table.getNbReadRows();
 
 		output.flush();
diff --git a/src/tap/formatter/VOTableFormat.java b/src/tap/formatter/VOTableFormat.java
index 07e7d398f8b02df899a44cad8b9490147d705bb7..923349e24e1281fa2fe0daceb7bc6c891f274605 100644
--- a/src/tap/formatter/VOTableFormat.java
+++ b/src/tap/formatter/VOTableFormat.java
@@ -83,7 +83,7 @@ import adql.db.DBType.DBDatatype;
  * </p>
  * 
  * @author Gr&eacute;gory Mantelet (CDS;ARI)
- * @version 2.1 (07/2015)
+ * @version 2.1 (11/2015)
  */
 public class VOTableFormat implements OutputFormat {
 
@@ -330,7 +330,7 @@ public class VOTableFormat implements OutputFormat {
 		ColumnInfo[] colInfos = toColumnInfos(queryResult, execReport, thread);
 
 		/* Turns the result set into a table. */
-		LimitedStarTable table = new LimitedStarTable(queryResult, colInfos, execReport.parameters.getMaxRec());
+		LimitedStarTable table = new LimitedStarTable(queryResult, colInfos, execReport.parameters.getMaxRec(), thread);
 
 		/* Prepares the object that will do the serialization work. */
 		VOSerializer voser = VOSerializer.makeSerializer(votFormat, votVersion, table);
@@ -347,6 +347,9 @@ public class VOTableFormat implements OutputFormat {
 		execReport.nbRows = table.getNbReadRows();
 		out.flush();
 
+		if (thread.isInterrupted())
+			throw new InterruptedException();
+
 		/* Check for overflow and write INFO if required. */
 		if (table.lastSequenceOverflowed()){
 			out.write("<INFO name=\"QUERY_STATUS\" value=\"OVERFLOW\"/>");
@@ -588,7 +591,7 @@ public class VOTableFormat implements OutputFormat {
 	 * </p>
 	 * 
 	 * @author Gr&eacute;gory Mantelet (CDS;ARI)
-	 * @version 2.0 (10/2014)
+	 * @version 2.1 (11/2015)
 	 * @since 2.0
 	 */
 	public static class LimitedStarTable extends AbstractStarTable {
@@ -602,6 +605,10 @@ public class VOTableFormat implements OutputFormat {
 		/** Iterator over the data to read using this special {@link StarTable} */
 		private final TableIterator tableIt;
 
+		/** Thread covering this execution. If it is interrupted, the writing must stop as soon as possible.
+		 * @since 2.1 */
+		private final Thread threadToWatch;
+
 		/** Limit on the number of rows to read. Over this limit, an "overflow" event occurs and {@link #overflow} is set to TRUE. */
 		private final long maxrec;
 
@@ -620,9 +627,11 @@ public class VOTableFormat implements OutputFormat {
 		 * @param tableIt	Data on which to iterate using this special {@link StarTable}.
 		 * @param colInfos	Information about all columns.
 		 * @param maxrec	Limit on the number of rows to read. <i>(if negative, there will be no limit)</i>
+		 * @param thread	Parent thread. When an interruption is detected the writing must stop as soon as possible.
 		 */
-		LimitedStarTable(final TableIterator tableIt, final ColumnInfo[] colInfos, final long maxrec){
+		LimitedStarTable(final TableIterator tableIt, final ColumnInfo[] colInfos, final long maxrec, final Thread thread){
 			this.tableIt = tableIt;
+			this.threadToWatch = thread;
 			nbCol = colInfos.length;
 			columnInfos = colInfos;
 			this.maxrec = maxrec;
@@ -675,7 +684,7 @@ public class VOTableFormat implements OutputFormat {
 				public boolean next() throws IOException{
 					irow++;
 					try{
-						if (maxrec < 0 || irow < maxrec){
+						if (!threadToWatch.isInterrupted() && (maxrec < 0 || irow < maxrec)){
 							boolean hasNext = tableIt.nextRow();
 							if (hasNext){
 								for(int i = 0; i < nbCol && tableIt.hasNextCol(); i++)
diff --git a/src/uws/job/UWSJob.java b/src/uws/job/UWSJob.java
index b2a28c9f367b13560925b963c2bdec220571e065..37c09951e6da9aca01bfd26712ac4de3017d0841 100644
--- a/src/uws/job/UWSJob.java
+++ b/src/uws/job/UWSJob.java
@@ -1320,7 +1320,7 @@ public class UWSJob extends SerializableUWSObject {
 
 				// Set the end time:
 				setEndTime(new Date());
-			}else if (thread == null || (thread != null && !thread.isAlive()))
+			}else if ((thread == null || (thread != null && !thread.isAlive())) && phase.getPhase() != ExecutionPhase.ABORTED)
 				throw new UWSException(UWSException.BAD_REQUEST, UWSExceptionFactory.incorrectPhaseTransition(getJobId(), phase.getPhase(), ExecutionPhase.ABORTED));
 		}else
 			getLogger().logJob(LogLevel.WARNING, this, "ABORT", "Abortion of the job \"" + getJobId() + "\" asked but not yet effective (after having waited " + waitForStop + "ms)!", null);
@@ -1391,12 +1391,20 @@ public class UWSJob extends SerializableUWSObject {
 	}
 
 	/**
-	 * Tells whether the thread is different from <i>null</i>, is not alive, is interrupted or is finished (see {@link JobThread#isFinished()}).
+	 * <p>Tells whether the thread is different from <i>null</i>, is not alive or is finished (see {@link JobThread#isFinished()}).</p>
+	 * 
+	 * <p><i><b>Important note:</b>
+	 * 	Having the interrupted flag set to <code>true</code> is not enough to consider the job as stopped.
+	 * 	So, if the job has been interrupted but is still running, it should mean that the {@link JobThread#jobWork()} does not
+	 * 	check the interrupted flag of the thread often enough or not at the right moments. In such case, the job can not be
+	 * 	considered as stopped/aborted - so the phase stays {@link ExecutionPhase#EXECUTING EXECUTING} - until the thread is "unblocked"
+	 * 	and the interruption is detected.
+	 * </i></p>
 	 * 
 	 * @return	<i>true</i> if the thread is not still running, <i>false</i> otherwise.
 	 */
 	protected final boolean isStopped(){
-		return thread == null || !thread.isAlive() || thread.isInterrupted() || thread.isFinished();
+		return thread == null || !thread.isAlive() || thread.isFinished();
 	}
 
 	/**