Newer
Older
package tap.db;
/*
* This file is part of TAPLibrary.
*
* TAPLibrary is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TAPLibrary is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with TAPLibrary. If not, see <http://www.gnu.org/licenses/>.
*
* Copyright 2012-2017 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
* Astronomisches Rechen Institut (ARI)
import java.sql.ResultSet;
import java.sql.Statement;
import adql.query.ADQLQuery;
import tap.data.DataReadException;
import tap.data.TableIterator;
import tap.metadata.TAPColumn;
import tap.metadata.TAPMetadata;
import tap.metadata.TAPTable;
import uws.job.user.JobOwner;
* <p>Connection to the "database" (whatever is the type or whether it is linked to a true DBMS connection).</p>
* <p>It lets executing ADQL queries and updating the TAP datamodel (with the list of schemas, tables and columns published in TAP,
* or with uploaded tables).</p>
* <p><b>IMPORTANT:</b>
* This connection aims only to provide a common and known interface for any kind of database connection.
* A connection MUST be opened/created and closed/freed ONLY by the {@link TAPFactory}, which will usually merely wrap
* the real database connection by a {@link DBConnection} object. That's why this interface does not provide anymore
* a close() function.
* </p>
*
* @author Grégory Mantelet (CDS;ARI)
* @version 2.1 (04/2017)
public interface DBConnection {
/**
* <p>Get any identifier for this connection.</p>
*
* <p><i>note: it is used only for logging purpose.</i></p>
*
* @return ID of this connection.
*/
public String getID();
/**
* <p>Fetch the whole content of TAP_SCHEMA.</p>
* <p>
* This function SHOULD be used only once: at the starting of the TAP service. It is an alternative way
* to get the published schemas, tables and columns. The other way is to build a {@link TAPMetadata} object
* yourself in function of the schemas/tables/columns you want to publish (i.e. which can be done by reading
* metadata from a XML document - following the same schema - XSD- as for the TAP resource <i>tables</i>)
* and then to load them in the DB (see {@link #setTAPSchema(TAPMetadata)} for more details).
* </p>
* <p><b>CAUTION:
* This function MUST NOT be used if the tables to publish or the standard TAP_SCHEMA tables have names in DB different from the
* ones defined by the TAP standard. So, if DB names are different from the ADQL names, you have to write yourself a way to get
* the metadata from the DB.
* </b></p>
* <p><i><b>Important note:</b>
* If the schema or some standard tables or columns are missing, TAP_SCHEMA will be considered as incomplete
* and an exception will be thrown.
* </i></p>
* <p><i>Note:
* This function MUST be able to read the standard tables and columns described by the IVOA. All other tables/columns
* will be merely ignored.
* </i></p>
* @return Content of TAP_SCHEMA inside the DB.
*
* @throws DBException If TAP_SCHEMA can not be found, is incomplete or if some important metadata can not be retrieved.
*
* @since 2.0
public TAPMetadata getTAPSchema() throws DBException;
/**
* <p>Empty and then fill all the TAP_SCHEMA tables with the given list of metadata.</p>
*
* <p>
* This function SHOULD be used only once: at the starting of the TAP service,
* when metadata are loaded from a XML document (following the same schema - XSD-
* as for the TAP resource <i>tables</i>).
* </p>
*
* <p>
* <i>THIS FUNCTION IS MANIPULATING THE SCHEMAS AND TABLES OF YOUR DATABASE.
* SO IT SHOULD HAVE A SPECIFIC BEHAVIOR DESCRIBED BELOW.
* <b>SO PLEASE READ THE FOLLOWINGS AND TRY TO RESPECT IT AS MUCH AS POSSIBLE IN THE IMPLEMENTATIONS</b>
* </i></p>
*
* <h3>TAP_SCHEMA CREATION</h3>
* <p>
* This function MAY drop and then re-create the schema TAP_SCHEMA and all
* its tables listed in the TAP standard (TAP_SCHEMA.schemas, .tables, .columns, .keys and .key_columns).
* <i>All other tables inside TAP_SCHEMA SHOULD NOT be modified!</i>
* </p>
*
* <p>
* The schema and the tables MUST be created using either the <b>standard definition</b> or the
* <b>definition provided in the {@link TAPMetadata} object</b> (if provided). Indeed, if your definition of these TAP tables
* is different from the standard (i.e. the standard + new elements), you MUST provide your modifications in parameter
* through the {@link TAPMetadata} object so that they can be applied and taken into account in TAP_SCHEMA.
* </p>
*
* <p><i>Note:
* DB names provided in the given TAPMetadata (see {@link TAPTable#getDBSchemaName()}, {@link TAPTable#getDBName()} and {@link TAPColumn#getDBName()})
* are used for the creation and filling of the tables.
*
* Whether these requests must be case sensitive or not SHOULD be managed by ADQLTranslator.
* </i></p>
*
* <h3>TAPMetadata PARAMETER</h3>
* <p>
* This object MUST contain all schemas, tables and columns that MUST be published. All its content will be
* used in order to fill the TAP_SCHEMA tables.
* </p>
* <p>
* Of course, TAP_SCHEMA and its tables MAY be provided in this object. But:
* </p>
* <ul>
* <li><b>(a) if TAP_SCHEMA tables are NOT provided</b>:
* this function SHOULD consider their definition as exactly the one provided by
* the TAP standard/protocol. If so, the standard definition MUST be automatically added
* into the {@link TAPMetadata} object AND into TAP_SCHEMA.
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
* </li>
* <li><b>(b) if TAP_SCHEMA tables ARE provided</b>:
* the definition of all given elements will be taken into account while updating the TAP_SCHEMA.
* Each element definition not provided MUST be considered as exactly the same as the standard one
* and MUST be added into the {@link TAPMetadata} object AND into TAP_SCHEMA.
* </li>
* </ul>
*
* <p><i>Note: By default, all implementations of this interface in the TAP library will fill only standard columns and tables of TAP_SCHEMA.
* To fill your own, you MUST implement yourself this interface or to extend an existing implementation.</i></p>
*
* <p><i><b>WARNING</b>:
* (b) lets consider a TAP_SCHEMA different from the standard one. BUT, these differences MUST be only additions,
* NOT modifications or deletion of the standard definition! This function MUST be able to work AT LEAST on a
* standard definition of TAP_SCHEMA.
* </p>
*
* <h3>FILLING BEHAVIOUR</h3>
* <p>
* The TAP_SCHEMA tables SHOULD be completely emptied (in SQL: "DELETE FROM <table_name>;" or merely "DROP TABLE <table_name>") before insertions can be processed.
* </p>
*
* <h3>ERRORS MANAGEMENT</h3>
* <p>
* If any error occurs while executing any "DB" queries (in SQL: DROP, DELETE, INSERT, CREATE, ...), all queries executed
* before in this function MUST be canceled (in SQL: ROLLBACK).
* </p>
*
* @param metadata List of all schemas, tables, columns and foreign keys to insert in the TAP_SCHEMA.
* @throws DBCancelledException If {@link #cancel(boolean)} has been called during the processing.
* @throws DBException If any error occurs while updating the database.
*
* @since 2.0
*/
public void setTAPSchema(final TAPMetadata metadata) throws DBCancelledException, DBException;
/**
* Add the defined and given table inside the TAP_UPLOAD schema.
*
* <p>If the TAP_UPLOAD schema does not already exist, it will be created.</p>
*
* <p><i>note: A table of TAP_UPLOAD MUST be transient and persistent only for the lifetime of the query.
* So, this function should always be used with {@link #dropUploadedTable(TAPTable)}, which is called at
* the end of each query execution.</i></p>
*
* @param tableDef Definition of the table to upload (list of all columns and of their type).
* @param data Rows and columns of the table to upload.
*
* @return <i>true</i> if the given table has been successfully added, <i>false</i> otherwise.
*
* @throws DBCancelledException If {@link #cancel(boolean)} has been called during the processing.
* @throws DBException If any error occurs while adding the table.
* @throws DataReadException If any error occurs while reading the given data (particularly if any limit - in byte or row - set in the TableIterator is reached).
*
* @since 2.0
*/
public boolean addUploadedTable(final TAPTable tableDef, final TableIterator data) throws DBCancelledException, DBException, DataReadException;
/**
* <p>Drop the specified uploaded table from the database.
* More precisely, it means dropping a table from the TAP_UPLOAD schema.</p>
*
* <p><i>Note:
* This function SHOULD drop only one table. So, if more than one table match in the "database" to the given one, an exception MAY be thrown.
* This behavior is implementation-dependent.
* </i></p>
*
* @param tableDef Definition of the uploaded table to drop (the whole object is needed in order to get the DB schema and tables names).
*
* @return <i>true</i> if the specified table has been successfully dropped, <i>false</i> otherwise.
*
* @throws DBException If any error occurs while dropping the specified uploaded table.
*
* @since 2.0
*/
public boolean dropUploadedTable(final TAPTable tableDef) throws DBException;
/**
* <p>Let executing the given ADQL query.</p>
*
* <p>The result of this query must be formatted as a table, and so must be iterable using a {@link TableIterator}.</p>
*
* <p><i>note: the interpretation of the ADQL query is up to the implementation. In most of the cases, it is just needed
* to translate this ADQL query into an SQL query (understandable by the chosen DBMS).</i></p>
*
* <p><b>IMPORTANT:</b>
* A {@link DBConnection} implementation may open resources to perform the query and get the result,
* but it may especially KEEP them OPENED in order to let the returned {@link TableIterator} iterates on
* the result set. So that closing these resources, the function {@link #endQuery()} should be called
* when the result is no longer needed. A good implementation of {@link TableIterator} SHOULD call this
* function when {@link TableIterator#close()} is called. <b>So, do not forget to call {@link TableIterator#close()}
* when you do not need any more the query result.</b>
* </p>
*
* @param adqlQuery ADQL query to execute.
* @param jobOwner The user executing the query
*
* @return The table result.
*
* @throws DBCancelledException If {@link #cancel(boolean)} has been called (i.e. query aborted) during the processing.
* @throws DBException If any errors occurs while executing the query.
*
* @since 2.0
*
* @see #endQuery()
* @see TableIterator#close()
*/
public TableIterator executeQuery(final ADQLQuery adqlQuery, JobOwner jobOwner) throws DBCancelledException, DBException;
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
/**
* <p>Set the number of rows to fetch before searching/getting the following.
* Thus, rows are fetched by block whose the size is set by this function.</p>
*
* <p>
* <i>This feature may not be supported.</i> In such case or if an exception occurs while setting the fetch size,
* this function must not send any exception and the connection stays with its default fetch size. A message may be however
* logged.
* </p>
*
* <p><i>Note:
* The "fetch size" should be taken into account only for SELECT queries executed by {@link #executeQuery(ADQLQuery)}.
* </i></p>
*
* <p>
* This feature is generally implemented by JDBC drivers using the V3 protocol. Thus, here is how the PostgreSQL JDBC documentation
* (https://jdbc.postgresql.org/documentation/head/query.html#query-with-cursor) describes this feature:
* </p>
* <blockquote>
* <p>
* By default the driver collects all the results for the query at once. This can be inconvenient for large data sets
* so the JDBC driver provides a means of basing a ResultSet on a database cursor and only fetching a small number of rows.
* </p>
* <p>
* A small number of rows are cached on the client side of the connection and when exhausted the next block of rows
* is retrieved by repositioning the cursor.
* </p>
* </blockquote>
*
* @param size Blocks size (in number of rows) to fetch.
*
* @since 2.0
*/
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
* before the function returns. This rollback operation is totally independent
* from the cancellation. It means that the rollback is always performed whatever
* is the cancellation result (or whatever the cancellation can be performed or not).
* </p>
*
* <p>
* This function should <b>never</b> throw 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 should 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);
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
/**
* <p>End the last query performed by this {@link DBConnection} and free some associated resources
* opened just for this last query.</p>
*
* <p>
* Originally, this function aims to be called when the result of {@link #executeQuery(ADQLQuery)}
* is no longer needed, in order to clean/free what the {@link DBConnection} needed to keep this
* result set open. In other words, if we take the example of a JDBC connection, this function will
* close the {@link ResultSet}, the {@link Statement} and will end any transaction eventually opened
* by the {@link DBConnection} (for instance if a fetchSize is set).
* </p>
*
* <p>
* However, this function could also be used after any other operation performed by the {@link DBConnection}.
* You should just be aware that, depending of the implementation, if a transaction has been opened, this
* function may end it, which means generally that a rollback is performed.
* </p>
*
* <p>
* Similarly, since it is supposed to end any query lastly performed, this function must also cancel
* any processing. So, the function {@link #cancel(boolean)} should be called.
* </p>
*
* <p>
* Finally, like {@link #cancel(boolean)}, this function should <b>never</b> throw any kind of exception.
* If internally an exception occurs, it should be directly logged at least as a WARNING.
* </p>
*
* @since 2.1
*/
public void endQuery();