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.TAPFactory; import tap.data.DataReadException; import tap.data.TableIterator; import tap.metadata.TAPColumn; import tap.metadata.TAPMetadata; import tap.metadata.TAPTable; /** * <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. * </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. * * @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) throws DBCancelledException, DBException; /** * <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); /** * <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(); }