Newer
Older
// Fill KEYS and KEY_COLUMNS:
metaTable = meta.getTable(STDSchema.TAPSCHEMA.label, STDTable.KEYS.label);
TAPTable metaTable2 = meta.getTable(STDSchema.TAPSCHEMA.label, STDTable.KEY_COLUMNS.label);
fillKeys(metaTable, metaTable2, allKeys);
}
/**
* <p>Fill the standard table TAP_SCHEMA.schemas with the list of all published schemas.</p>
* <p><i>Note:
* Batch updates may be done here if its supported by the DBMS connection.
* In case of any failure while using this feature, it will be flagged as unsupported and one-by-one updates will be processed.
* </i></p>
* @param metaTable Description of TAP_SCHEMA.schemas.
* @param itSchemas Iterator over the list of schemas.
* @return Iterator over the full list of all tables (whatever is their schema).
* @throws DBCancelledException If {@link #cancel(boolean)} has been called during the processing,
* @throws DBException If rows can not be inserted because the SQL update query has failed.
* @throws SQLException If any other SQL exception occurs.
*/
Grégory Mantelet
committed
private Iterator<TAPTable> fillSchemas(final TAPTable metaTable, final Iterator<TAPSchema> itSchemas) throws SQLException, DBCancelledException, DBException {
List<TAPTable> allTables = new ArrayList<TAPTable>();
// Build the SQL update query:
StringBuffer sql = new StringBuffer("INSERT INTO ");
gmantele
committed
sql.append(translator.getTableName(metaTable, supportsSchema)).append(" (");
sql.append(translator.getColumnName(metaTable.getColumn("schema_name")));
sql.append(", ").append(translator.getColumnName(metaTable.getColumn("description")));
sql.append(", ").append(translator.getColumnName(metaTable.getColumn("utype")));
Grégory Mantelet
committed
if (supportsSchema) {
gmantele
committed
sql.append(", ").append(DB_NAME_COLUMN);
sql.append(") VALUES (?, ?, ?, ?)");
Grégory Mantelet
committed
} else
sql.append(") VALUES (?, ?, ?)");
// Prepare the statement:
PreparedStatement stmt = null;
Grégory Mantelet
committed
try {
stmt = connection.prepareStatement(sql.toString());
// Execute the query for each schema:
int nbRows = 0;
Grégory Mantelet
committed
while(itSchemas.hasNext()) {
TAPSchema schema = itSchemas.next();
nbRows++;
// list all tables of this schema:
appendAllInto(allTables, schema.iterator());
// add the schema entry into the DB:
stmt.setString(1, schema.getADQLName());
stmt.setString(2, schema.getDescription());
stmt.setString(3, schema.getUtype());
gmantele
committed
if (supportsSchema)
stmt.setString(4, (schema.getDBName() == null || schema.getDBName().equals(schema.getADQLName())) ? null : schema.getDBName());
// If the query has been aborted, return immediately:
if (isCancelled())
throw new DBCancelledException();
else
executeUpdate(stmt, nbRows);
}
// If the query has been aborted, return immediately:
if (isCancelled())
throw new DBCancelledException();
else
executeBatchUpdates(stmt, nbRows);
Grégory Mantelet
committed
} finally {
close(stmt);
}
return allTables.iterator();
}
/**
* <p>Fill the standard table TAP_SCHEMA.tables with the list of all published tables.</p>
* <p><i>Note:
* Batch updates may be done here if its supported by the DBMS connection.
* In case of any failure while using this feature, it will be flagged as unsupported and one-by-one updates will be processed.
* </i></p>
* @param metaTable Description of TAP_SCHEMA.tables.
* @param itTables Iterator over the list of tables.
* @return Iterator over the full list of all columns (whatever is their table).
* @throws DBCancelledException If {@link #cancel(boolean)} has been called during the processing,
* @throws DBException If rows can not be inserted because the SQL update query has failed.
* @throws SQLException If any other SQL exception occurs.
*/
Grégory Mantelet
committed
private Iterator<TAPColumn> fillTables(final TAPTable metaTable, final Iterator<TAPTable> itTables) throws SQLException, DBCancelledException, DBException {
List<TAPColumn> allColumns = new ArrayList<TAPColumn>();
// Build the SQL update query:
StringBuffer sql = new StringBuffer("INSERT INTO ");
gmantele
committed
sql.append(translator.getTableName(metaTable, supportsSchema)).append(" (");
sql.append(translator.getColumnName(metaTable.getColumn("schema_name")));
sql.append(", ").append(translator.getColumnName(metaTable.getColumn("table_name")));
sql.append(", ").append(translator.getColumnName(metaTable.getColumn("table_type")));
sql.append(", ").append(translator.getColumnName(metaTable.getColumn("description")));
sql.append(", ").append(translator.getColumnName(metaTable.getColumn("utype")));
sql.append(", ").append(translator.getColumnName(metaTable.getColumn("table_index")));
gmantele
committed
sql.append(", ").append(DB_NAME_COLUMN);
sql.append(") VALUES (?, ?, ?, ?, ?, ?, ?)");
// Prepare the statement:
PreparedStatement stmt = null;
Grégory Mantelet
committed
try {
stmt = connection.prepareStatement(sql.toString());
// Execute the query for each table:
int nbRows = 0;
Grégory Mantelet
committed
while(itTables.hasNext()) {
TAPTable table = itTables.next();
nbRows++;
// list all columns of this table:
appendAllInto(allColumns, table.getColumns());
// add the table entry into the DB:
stmt.setString(1, table.getADQLSchemaName());
if (table instanceof TAPTable)
stmt.setString(2, table.getRawName());
gmantele
committed
else
stmt.setString(2, table.getADQLName());
stmt.setString(3, table.getType().toString());
stmt.setString(4, table.getDescription());
stmt.setString(5, table.getUtype());
stmt.setInt(6, table.getIndex());
stmt.setString(7, (table.getDBName() == null || table.getDBName().equals(table.getADQLName())) ? null : table.getDBName());
// If the query has been aborted, return immediately:
if (isCancelled())
throw new DBCancelledException();
else
executeUpdate(stmt, nbRows);
}
// If the query has been aborted, return immediately:
if (isCancelled())
throw new DBCancelledException();
else
executeBatchUpdates(stmt, nbRows);
Grégory Mantelet
committed
} finally {
close(stmt);
}
return allColumns.iterator();
}
/**
* <p>Fill the standard table TAP_SCHEMA.columns with the list of all published columns.</p>
* <p><i>Note:
* Batch updates may be done here if its supported by the DBMS connection.
* In case of any failure while using this feature, it will be flagged as unsupported and one-by-one updates will be processed.
* </i></p>
* @param metaTable Description of TAP_SCHEMA.columns.
* @param itColumns Iterator over the list of columns.
* @return Iterator over the full list of all foreign keys.
* @throws DBCancelledException If {@link #cancel(boolean)} has been called during the processing,
* @throws DBException If rows can not be inserted because the SQL update query has failed.
* @throws SQLException If any other SQL exception occurs.
*/
Grégory Mantelet
committed
private Iterator<TAPForeignKey> fillColumns(final TAPTable metaTable, final Iterator<TAPColumn> itColumns) throws SQLException, DBCancelledException, DBException {
List<TAPForeignKey> allKeys = new ArrayList<TAPForeignKey>();
// Build the SQL update query:
StringBuffer sql = new StringBuffer("INSERT INTO ");
gmantele
committed
sql.append(translator.getTableName(metaTable, supportsSchema)).append(" (");
sql.append(translator.getColumnName(metaTable.getColumn("table_name")));
sql.append(", ").append(translator.getColumnName(metaTable.getColumn("column_name")));
sql.append(", ").append(translator.getColumnName(metaTable.getColumn("description")));
sql.append(", ").append(translator.getColumnName(metaTable.getColumn("unit")));
sql.append(", ").append(translator.getColumnName(metaTable.getColumn("ucd")));
sql.append(", ").append(translator.getColumnName(metaTable.getColumn("utype")));
sql.append(", ").append(translator.getColumnName(metaTable.getColumn("datatype")));
sql.append(", ").append(translator.getColumnName(metaTable.getColumn("arraysize")));
sql.append(", ").append(translator.getColumnName(metaTable.getColumn("size")));
sql.append(", ").append(translator.getColumnName(metaTable.getColumn("principal")));
sql.append(", ").append(translator.getColumnName(metaTable.getColumn("indexed")));
sql.append(", ").append(translator.getColumnName(metaTable.getColumn("std")));
sql.append(", ").append(translator.getColumnName(metaTable.getColumn("column_index")));
gmantele
committed
sql.append(", ").append(DB_NAME_COLUMN);
sql.append(") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
// Prepare the statement:
PreparedStatement stmt = null;
Grégory Mantelet
committed
try {
stmt = connection.prepareStatement(sql.toString());
// Execute the query for each column:
int nbRows = 0;
Grégory Mantelet
committed
while(itColumns.hasNext()) {
TAPColumn col = itColumns.next();
nbRows++;
// list all foreign keys of this column:
appendAllInto(allKeys, col.getTargets());
// add the column entry into the DB:
if (col.getTable() instanceof TAPTable)
stmt.setString(1, ((TAPTable)col.getTable()).getRawName());
gmantele
committed
else
stmt.setString(1, col.getTable().getADQLName());
stmt.setString(2, col.getADQLName());
stmt.setString(3, col.getDescription());
stmt.setString(4, col.getUnit());
stmt.setString(5, col.getUcd());
stmt.setString(6, col.getUtype());
stmt.setString(7, col.getDatatype().type.toString());
stmt.setInt(8, col.getDatatype().length);
stmt.setInt(9, col.getDatatype().length);
stmt.setInt(10, col.isPrincipal() ? 1 : 0);
stmt.setInt(11, col.isIndexed() ? 1 : 0);
stmt.setInt(12, col.isStd() ? 1 : 0);
stmt.setInt(13, col.getIndex());
stmt.setString(14, (col.getDBName() == null || col.getDBName().equals(col.getADQLName())) ? null : col.getDBName());
// If the query has been aborted, return immediately:
if (isCancelled())
throw new DBCancelledException();
else
executeUpdate(stmt, nbRows);
}
// If the query has been aborted, return immediately:
if (isCancelled())
throw new DBCancelledException();
else
executeBatchUpdates(stmt, nbRows);
Grégory Mantelet
committed
} finally {
close(stmt);
}
return allKeys.iterator();
}
/**
* <p>Fill the standard tables TAP_SCHEMA.keys and TAP_SCHEMA.key_columns with the list of all published foreign keys.</p>
* <p><i>Note:
* Batch updates may be done here if its supported by the DBMS connection.
* In case of any failure while using this feature, it will be flagged as unsupported and one-by-one updates will be processed.
* </i></p>
* @param metaKeys Description of TAP_SCHEMA.keys.
* @param metaKeyColumns Description of TAP_SCHEMA.key_columns.
* @param itKeys Iterator over the list of foreign keys.
* @throws DBCancelledException If {@link #cancel(boolean)} has been called during the processing,
* @throws DBException If rows can not be inserted because the SQL update query has failed.
* @throws SQLException If any other SQL exception occurs.
*/
Grégory Mantelet
committed
private void fillKeys(final TAPTable metaKeys, final TAPTable metaKeyColumns, final Iterator<TAPForeignKey> itKeys) throws SQLException, DBCancelledException, DBException {
// Build the SQL update query for KEYS:
StringBuffer sqlKeys = new StringBuffer("INSERT INTO ");
gmantele
committed
sqlKeys.append(translator.getTableName(metaKeys, supportsSchema)).append(" (");
sqlKeys.append(translator.getColumnName(metaKeys.getColumn("key_id")));
sqlKeys.append(", ").append(translator.getColumnName(metaKeys.getColumn("from_table")));
sqlKeys.append(", ").append(translator.getColumnName(metaKeys.getColumn("target_table")));
sqlKeys.append(", ").append(translator.getColumnName(metaKeys.getColumn("description")));
sqlKeys.append(", ").append(translator.getColumnName(metaKeys.getColumn("utype")));
sqlKeys.append(") VALUES (?, ?, ?, ?, ?)");
PreparedStatement stmtKeys = null, stmtKeyCols = null;
Grégory Mantelet
committed
try {
// Prepare the statement for KEYS:
stmtKeys = connection.prepareStatement(sqlKeys.toString());
// Build the SQL update query for KEY_COLUMNS:
StringBuffer sqlKeyCols = new StringBuffer("INSERT INTO ");
gmantele
committed
sqlKeyCols.append(translator.getTableName(metaKeyColumns, supportsSchema)).append(" (");
sqlKeyCols.append(translator.getColumnName(metaKeyColumns.getColumn("key_id")));
sqlKeyCols.append(", ").append(translator.getColumnName(metaKeyColumns.getColumn("from_column")));
sqlKeyCols.append(", ").append(translator.getColumnName(metaKeyColumns.getColumn("target_column")));
sqlKeyCols.append(") VALUES (?, ?, ?)");
// Prepare the statement for KEY_COLUMNS:
stmtKeyCols = connection.prepareStatement(sqlKeyCols.toString());
// Execute the query for each column:
int nbKeys = 0, nbKeyColumns = 0;
Grégory Mantelet
committed
while(itKeys.hasNext()) {
TAPForeignKey key = itKeys.next();
nbKeys++;
// add the key entry into KEYS:
stmtKeys.setString(1, key.getKeyId());
if (key.getFromTable() instanceof TAPTable)
stmtKeys.setString(2, key.getFromTable().getRawName());
gmantele
committed
else
stmtKeys.setString(2, key.getFromTable().getADQLName());
if (key.getTargetTable() instanceof TAPTable)
stmtKeys.setString(3, key.getTargetTable().getRawName());
gmantele
committed
else
stmtKeys.setString(3, key.getTargetTable().getADQLName());
stmtKeys.setString(4, key.getDescription());
stmtKeys.setString(5, key.getUtype());
// If the query has been aborted, return immediately:
if (isCancelled())
throw new DBCancelledException();
else
executeUpdate(stmtKeys, nbKeys);
// add the key columns into KEY_COLUMNS:
Iterator<Map.Entry<String, String>> itAssoc = key.iterator();
Grégory Mantelet
committed
while(itAssoc.hasNext()) {
nbKeyColumns++;
Map.Entry<String, String> assoc = itAssoc.next();
stmtKeyCols.setString(1, key.getKeyId());
stmtKeyCols.setString(2, assoc.getKey());
stmtKeyCols.setString(3, assoc.getValue());
// If the query has been aborted, return immediately:
if (isCancelled())
throw new DBCancelledException();
else
executeUpdate(stmtKeyCols, nbKeyColumns);
}
}
// If the query has been aborted, return immediately:
if (isCancelled())
throw new DBCancelledException();
Grégory Mantelet
committed
else {
executeBatchUpdates(stmtKeys, nbKeys);
executeBatchUpdates(stmtKeyCols, nbKeyColumns);
}
Grégory Mantelet
committed
} finally {
close(stmtKeys);
close(stmtKeyCols);
}
}
/* ***************** */
/* UPLOAD MANAGEMENT */
/* ***************** */
/**
* <p><i><b>Important note:</b>
* Only tables uploaded by users can be created in the database. To ensure that, the schema name of this table MUST be {@link STDSchema#UPLOADSCHEMA} ("TAP_UPLOAD") in ADQL.
* If it has another ADQL name, an exception will be thrown. Of course, the DB name of this schema MAY be different.
* </i></p>
* <p><i><b>Important note:</b>
* This function may modify the given {@link TAPTable} object if schemas are not supported by this connection.
* In this case, this function will prefix the table's DB name by the schema's DB name directly inside the given
* {@link TAPTable} object. Then the DB name of the schema will be set to NULL.
* </i></p>
* <p><i>Note:
* If the upload schema does not already exist in the database, it will be created.
* </i></p>
* @see tap.db.DBConnection#addUploadedTable(tap.metadata.TAPTable, tap.data.TableIterator)
* @see #checkUploadedTableDef(TAPTable)
*/
@Override
Grégory Mantelet
committed
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);
Grégory Mantelet
committed
try {
// Start a transaction:
startTransaction();
// ...create a statement:
getStatement();
DatabaseMetaData dbMeta = connection.getMetaData();
// 1. Create the upload schema, if it does not already exist:
Grégory Mantelet
committed
if (!isSchemaExisting(tableDef.getDBSchemaName(), dbMeta)) {
stmt.executeUpdate("CREATE SCHEMA " + translator.getQualifiedSchemaName(tableDef));
if (logger != null)
logger.logDB(LogLevel.INFO, this, "SCHEMA_CREATED", "Schema \"" + tableDef.getADQLSchemaName() + "\" (in DB: " + translator.getQualifiedSchemaName(tableDef) + ") created.", null);
}
// 1bis. Ensure the table does not already exist and if it is the case, throw an understandable exception:
Grégory Mantelet
committed
else if (isTableExisting(tableDef.getDBSchemaName(), tableDef.getDBName(), dbMeta)) {
gmantele
committed
DBException de = new DBException("Impossible to create the user uploaded table in the database: " + translator.getTableName(tableDef, supportsSchema) + "! This table already exists.");
if (logger != null)
logger.logDB(LogLevel.ERROR, this, "ADD_UPLOAD_TABLE", de.getMessage(), de);
throw de;
}
// If the query has been aborted, return immediately:
if (isCancelled())
throw new DBCancelledException();
// 2. Create the table:
// ...build the SQL query:
StringBuffer sqlBuf = new StringBuffer("CREATE TABLE ");
gmantele
committed
sqlBuf.append(translator.getTableName(tableDef, supportsSchema)).append(" (");
Iterator<TAPColumn> it = tableDef.getColumns();
Grégory Mantelet
committed
while(it.hasNext()) {
TAPColumn col = it.next();
// column name:
sqlBuf.append(translator.getColumnName(col));
// column type:
sqlBuf.append(' ').append(convertTypeToDB(col.getDatatype()));
// last column ?
if (it.hasNext())
sqlBuf.append(',');
}
sqlBuf.append(')');
// ...execute the update query:
stmt.executeUpdate(sqlBuf.toString());
// If the query has been aborted, return immediately:
if (isCancelled())
throw new DBCancelledException();
// 3. Fill the table:
int nbUploadedRows = fillUploadedTable(tableDef, data);
if (isCancelled())
throw new DBCancelledException();
// Commit the transaction:
commit();
// Log the end:
if (logger != null)
logger.logDB(LogLevel.INFO, this, "TABLE_CREATED", "Table \"" + tableDef.getADQLName() + "\" (in DB: " + translator.getTableName(tableDef, supportsSchema) + ") created (" + nbUploadedRows + " rows).", null);
return true;
Grégory Mantelet
committed
} catch(SQLException se) {
rollback();
if (!isCancelled() && logger != null)
gmantele
committed
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);
Grégory Mantelet
committed
} catch(DBException de) {
rollback();
if (logger != null && (de instanceof DBCancelledException || isCancelled()))
logger.logDB(LogLevel.INFO, this, "ADD_UPLOAD_TABLE", "Upload of the table \"" + tableDef.getADQLName() + "\" (in DB: " + translator.getTableName(tableDef, supportsSchema) + ") canceled!", null);
throw de;
Grégory Mantelet
committed
} catch(DataReadException dre) {
rollback();
throw dre;
Grégory Mantelet
committed
} finally {
closeStatement();
endTransaction();
}
}
/**
* <p>Fill the table uploaded by the user with the given data.</p>
* <p><i>Note:
* Batch updates may be done here if its supported by the DBMS connection.
* In case of any failure while using this feature, it will be flagged as unsupported and one-by-one updates will be processed.
* </i></p>
* <p><i>Note:
* This function proceeds to a formatting of TIMESTAMP and GEOMETRY (point, circle, box, polygon) values.
* </i></p>
* @param metaTable Description of the updated table.
* @param data Iterator over the rows to insert.
* @return Number of inserted rows.
* @throws DBCancelledException If {@link #cancel(boolean)} has been called during the processing,
* @throws DBException If rows can not be inserted because the SQL update query has failed.
* @throws SQLException If any other SQL exception occurs.
* @throws DataReadException If there is any error while reading the data from the given {@link TableIterator} (and particularly if a limit - in byte or row - has been reached).
*/
Grégory Mantelet
committed
protected int fillUploadedTable(final TAPTable metaTable, final TableIterator data) throws SQLException, DBCancelledException, DBException, DataReadException {
// 1. Build the SQL update query:
StringBuffer sql = new StringBuffer("INSERT INTO ");
StringBuffer varParam = new StringBuffer();
// ...table name:
gmantele
committed
sql.append(translator.getTableName(metaTable, supportsSchema)).append(" (");
// ...list of columns:
TAPColumn[] cols = data.getMetadata();
Grégory Mantelet
committed
for(int c = 0; c < cols.length; c++) {
if (c > 0) {
sql.append(", ");
varParam.append(", ");
}
sql.append(translator.getColumnName(cols[c]));
varParam.append('?');
}
// ...values pattern:
sql.append(") VALUES (").append(varParam).append(')');
// 2. Prepare the statement:
PreparedStatement stmt = null;
int nbRows = 0;
Grégory Mantelet
committed
try {
stmt = connection.prepareStatement(sql.toString());
// 3. Execute the query for each given row:
Grégory Mantelet
committed
while(data.nextRow()) {
nbRows++;
int c = 1;
Grégory Mantelet
committed
while(data.hasNextCol()) {
Object val = data.nextCol();
Grégory Mantelet
committed
if (val != null && cols[c - 1] != null) {
/* TIMESTAMP FORMATTING */
Grégory Mantelet
committed
if (cols[c - 1].getDatatype().type == DBDatatype.TIMESTAMP) {
try {
val = new Timestamp(ISO8601Format.parse(val.toString()));
Grégory Mantelet
committed
} catch(ParseException pe) {
if (logger != null)
gmantele
committed
logger.logDB(LogLevel.ERROR, this, "UPLOAD", "[l. " + nbRows + ", c. " + c + "] Unexpected date format for the value: \"" + val + "\"! A date formatted in ISO8601 was expected.", pe);
throw new DBException("[l. " + nbRows + ", c. " + c + "] Unexpected date format for the value: \"" + val + "\"! A date formatted in ISO8601 was expected.", pe);
}
}
/* GEOMETRY FORMATTING */
Grégory Mantelet
committed
else if (cols[c - 1].getDatatype().type == DBDatatype.POINT || cols[c - 1].getDatatype().type == DBDatatype.REGION) {
Region region;
gmantele
committed
// parse the region as an STC-S expression:
Grégory Mantelet
committed
try {
region = STCS.parseRegion(val.toString());
Grégory Mantelet
committed
} catch(adql.parser.grammar.ParseException e) {
gmantele
committed
if (logger != null)
logger.logDB(LogLevel.ERROR, this, "UPLOAD", "[l. " + nbRows + ", c. " + c + "] Incorrect STC-S syntax for the geometrical value \"" + val + "\"! " + e.getMessage(), e);
throw new DataReadException("[l. " + nbRows + ", c. " + c + "] Incorrect STC-S syntax for the geometrical value \"" + val + "\"! " + e.getMessage(), e);
}
// translate this STC region into the corresponding column value:
Grégory Mantelet
committed
try {
val = translator.translateGeometryToDB(region);
Grégory Mantelet
committed
} catch(adql.parser.grammar.ParseException e) {
if (logger != null)
gmantele
committed
logger.logDB(LogLevel.ERROR, this, "UPLOAD", "[l. " + nbRows + ", c. " + c + "] Impossible to import the ADQL geometry \"" + val + "\" into the database! " + e.getMessage(), e);
throw new DataReadException("[l. " + nbRows + ", c. " + c + "] Impossible to import the ADQL geometry \"" + val + "\" into the database! " + e.getMessage(), e);
}
}
/* BOOLEAN CASE (more generally, type incompatibility) */
else if (val != null && cols[c - 1].getDatatype().type == DBDatatype.SMALLINT && val instanceof Boolean)
val = ((Boolean)val) ? (short)1 : (short)0;
/* NULL CHARACTER CASE (JUST FOR POSTGRESQL) */
else if ((dbms == null || dbms.equalsIgnoreCase(DBMS_POSTGRES)) && val instanceof Character && (Character)val == 0x00)
val = null;
}
// If the query has been aborted, return immediately:
if (isCancelled())
throw new DBCancelledException();
else
stmt.setObject(c++, val);
}
// If the query has been aborted, return immediately:
if (isCancelled())
throw new DBCancelledException();
else
executeUpdate(stmt, nbRows);
}
// If the query has been aborted, return immediately:
if (isCancelled())
throw new DBCancelledException();
else
executeBatchUpdates(stmt, nbRows);
return nbRows;
Grégory Mantelet
committed
} finally {
close(stmt);
}
}
/**
* <p><i><b>Important note:</b>
* Only tables uploaded by users can be dropped from the database. To ensure that, the schema name of this table MUST be {@link STDSchema#UPLOADSCHEMA} ("TAP_UPLOAD") in ADQL.
* If it has another ADQL name, an exception will be thrown. Of course, the DB name of this schema MAY be different.
* </i></p>
* <p><i><b>Important note:</b>
* This function may modify the given {@link TAPTable} object if schemas are not supported by this connection.
* In this case, this function will prefix the table's DB name by the schema's DB name directly inside the given
* {@link TAPTable} object. Then the DB name of the schema will be set to NULL.
* </i></p>
* <p><i>Note:
* This implementation is able to drop only one uploaded table. So if this function finds more than one table matching to the given one,
* an exception will be thrown and no table will be dropped.
* </i></p>
* @see tap.db.DBConnection#dropUploadedTable(tap.metadata.TAPTable)
* @see #checkUploadedTableDef(TAPTable)
*/
@Override
Grégory Mantelet
committed
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);
Grégory Mantelet
committed
try {
// Check the existence of the table to drop:
if (!isTableExisting(tableDef.getDBSchemaName(), tableDef.getDBName(), connection.getMetaData()))
return true;
// Execute the update:
int cnt = getStatement().executeUpdate("DROP TABLE " + translator.getTableName(tableDef, supportsSchema));
// Log the end:
Grégory Mantelet
committed
if (logger != null) {
gmantele
committed
if (cnt >= 0)
logger.logDB(LogLevel.INFO, this, "TABLE_DROPPED", "Table \"" + tableDef.getADQLName() + "\" (in DB: " + translator.getTableName(tableDef, supportsSchema) + ") dropped.", null);
else
gmantele
committed
logger.logDB(LogLevel.ERROR, this, "TABLE_DROPPED", "Table \"" + tableDef.getADQLName() + "\" (in DB: " + translator.getTableName(tableDef, supportsSchema) + ") NOT dropped.", null);
}
// Ensure the update is successful:
gmantele
committed
return (cnt >= 0);
Grégory Mantelet
committed
} catch(SQLException se) {
if (!isCancelled() && logger != null)
gmantele
committed
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);
Grégory Mantelet
committed
} finally {
closeStatement();
}
}
/**
* <p>Ensures that the given table MUST be inside the upload schema in ADQL.</p>
* <p>Thus, the following cases are taken into account:</p>
* <ul>
* <li>
* The schema name of the given table MUST be {@link STDSchema#UPLOADSCHEMA} ("TAP_UPLOAD") in ADQL.
* If it has another ADQL name, an exception will be thrown. Of course, the DB name of this schema MAY be different.
* </li>
* <li>
* If schemas are not supported by this connection, this function will prefix the table DB name by the schema DB name directly
* inside the given {@link TAPTable} object. Then the DB name of the schema will be set to NULL.
* </li>
* </ul>
* @param tableDef Definition of the table to create/drop.
* @throws DBException If the given table is not in a schema
* or if the ADQL name of this schema is not {@link STDSchema#UPLOADSCHEMA} ("TAP_UPLOAD").
*/
Grégory Mantelet
committed
protected void checkUploadedTableDef(final TAPTable tableDef) throws DBException {
// If the table has no defined schema or if the ADQL name of the schema is not TAP_UPLOAD, throw an exception:
if (tableDef.getSchema() == null || !tableDef.getSchema().getADQLName().equals(STDSchema.UPLOADSCHEMA.label))
throw new DBException("Missing upload schema! An uploaded table must be inside a schema whose the ADQL name is strictly equals to \"" + STDSchema.UPLOADSCHEMA.label + "\" (but the DB name may be different).");
Grégory Mantelet
committed
if (!supportsSchema) {
gmantele
committed
if (tableDef.getADQLSchemaName() != null && tableDef.getADQLSchemaName().trim().length() > 0 && !tableDef.getDBName().startsWith(tableDef.getADQLSchemaName() + "_"))
tableDef.setDBName(tableDef.getADQLSchemaName() + "_" + tableDef.getDBName());
if (tableDef.getSchema() != null)
tableDef.getSchema().setDBName(null);
}
}
/* ************** */
/* TOOL FUNCTIONS */
/* ************** */
/**
* <p>Convert the given TAP type into the corresponding DBMS column type.</p>
* <p>
* This function tries first the type conversion using the translator ({@link JDBCTranslator#convertTypeToDB(DBType)}).
* If it fails, a default conversion is done considering all the known types of the following DBMS:
* PostgreSQL, SQLite, MySQL, Oracle and JavaDB/Derby.
* </p>
* @param type TAP type to convert.
* @return The corresponding DBMS type.
* @see JDBCTranslator#convertTypeToDB(DBType)
* @see #defaultTypeConversion(DBType)
*/
Grégory Mantelet
committed
protected String convertTypeToDB(final DBType type) {
String dbmsType = translator.convertTypeToDB(type);
return (dbmsType == null) ? defaultTypeConversion(type) : dbmsType;
}
/**
* <p>Get the DBMS compatible datatype corresponding to the given column {@link DBType}.</p>
* <p><i>Note 1:
* This function is able to generate a DB datatype compatible with the currently used DBMS.
* In this current implementation, only Postgresql, Oracle, SQLite, MySQL and Java DB/Derby have been considered.
* Most of the TAP types have been tested only with Postgresql and SQLite without any problem.
* If the DBMS you are using has not been considered, note that this function will return the TAP type expression by default.
* </i></p>
* <p><i>Note 2:
* In case the given datatype is NULL or not managed here, the DBMS type corresponding to "VARCHAR" will be returned.
* </i></p>
* <p><i>Note 3:
* The special TAP types POINT and REGION are converted into the DBMS type corresponding to "VARCHAR".
* </i></p>
* @param datatype Column TAP type.
* @return The corresponding DB type, or VARCHAR if the given type is not managed or is NULL.
*/
Grégory Mantelet
committed
protected String defaultTypeConversion(DBType datatype) {
if (datatype == null)
datatype = new DBType(DBDatatype.VARCHAR);
Grégory Mantelet
committed
switch(datatype.type) {
2728
2729
2730
2731
2732
2733
2734
2735
2736
2737
2738
2739
2740
2741
2742
2743
2744
2745
2746
2747
2748
2749
2750
2751
2752
2753
2754
2755
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810
2811
2812
case SMALLINT:
return dbms.equals("sqlite") ? "INTEGER" : "SMALLINT";
case INTEGER:
case REAL:
return datatype.type.toString();
case BIGINT:
if (dbms.equals("oracle"))
return "NUMBER(19,0)";
else if (dbms.equals("sqlite"))
return "INTEGER";
else
return "BIGINT";
case DOUBLE:
if (dbms.equals("postgresql") || dbms.equals("oracle"))
return "DOUBLE PRECISION";
else if (dbms.equals("sqlite"))
return "REAL";
else
return "DOUBLE";
case BINARY:
if (dbms.equals("postgresql"))
return "bytea";
else if (dbms.equals("sqlite"))
return "BLOB";
else if (dbms.equals("oracle"))
return "RAW" + (datatype.length > 0 ? "(" + datatype.length + ")" : "");
else if (dbms.equals("derby"))
return "CHAR" + (datatype.length > 0 ? "(" + datatype.length + ")" : "") + " FOR BIT DATA";
else
return datatype.type.toString();
case VARBINARY:
if (dbms.equals("postgresql"))
return "bytea";
else if (dbms.equals("sqlite"))
return "BLOB";
else if (dbms.equals("oracle"))
return "LONG RAW" + (datatype.length > 0 ? "(" + datatype.length + ")" : "");
else if (dbms.equals("derby"))
return "VARCHAR" + (datatype.length > 0 ? "(" + datatype.length + ")" : "") + " FOR BIT DATA";
else
return datatype.type.toString();
case CHAR:
if (dbms.equals("sqlite"))
return "TEXT";
else
return "CHAR";
case BLOB:
if (dbms.equals("postgresql"))
return "bytea";
else
return "BLOB";
case CLOB:
if (dbms.equals("postgresql") || dbms.equals("mysql") || dbms.equals("sqlite"))
return "TEXT";
else
return "CLOB";
case TIMESTAMP:
if (dbms.equals("sqlite"))
return "TEXT";
else
return "TIMESTAMP";
case POINT:
case REGION:
case VARCHAR:
default:
if (dbms.equals("sqlite"))
return "TEXT";
else
return "VARCHAR";
}
}
/**
* <p>Start a transaction.</p>
* <p>
* Basically, if transactions are supported by this connection, the flag AutoCommit is just turned off.
* It will be turned on again when {@link #endTransaction()} is called.
* </p>
* <p>If transactions are not supported by this connection, nothing is done.</p>
* <p><b><i>Important note:</b>
* If any error interrupts the START TRANSACTION operation, transactions will be afterwards considered as not supported by this connection.
* So, subsequent call to this function (and any other transaction related function) will never do anything.
* </i></p>
* @throws DBException If it is impossible to start a transaction though transactions are supported by this connection.
* If these are not supported, this error can never be thrown.
*/
Grégory Mantelet
committed
protected void startTransaction() throws DBException {
try {
if (supportsTransaction) {
connection.setAutoCommit(false);
if (logger != null)
logger.logDB(LogLevel.INFO, this, "START_TRANSACTION", "Transaction STARTED.", null);
}
Grégory Mantelet
committed
} catch(SQLException se) {
supportsTransaction = false;
if (logger != null)
logger.logDB(LogLevel.ERROR, this, "START_TRANSACTION", "Transaction STARTing impossible!", se);
throw new DBException("Transaction STARTing impossible!", se);
}
}
/**
* <p>Commit the current transaction.</p>
* <p>
* {@link #startTransaction()} must have been called before. If it's not the case the connection
* may throw a {@link SQLException} which will be transformed into a {@link DBException} here.
* </p>
* <p>If transactions are not supported by this connection, nothing is done.</p>
* <p><b><i>Important note:</b>
* If any error interrupts the COMMIT operation, transactions will be afterwards considered as not supported by this connection.
* So, subsequent call to this function (and any other transaction related function) will never do anything.
* </i></p>
* @throws DBException If it is impossible to commit a transaction though transactions are supported by this connection..
* If these are not supported, this error can never be thrown.
*/
Grégory Mantelet
committed
protected void commit() throws DBException {
try {
if (supportsTransaction) {
connection.commit();
if (logger != null)
logger.logDB(LogLevel.INFO, this, "COMMIT", "Transaction COMMITED.", null);
}
Grégory Mantelet
committed
} catch(SQLException se) {
supportsTransaction = false;
if (logger != null)
logger.logDB(LogLevel.ERROR, this, "COMMIT", "Transaction COMMIT impossible!", se);
throw new DBException("Transaction COMMIT impossible!", se);
}
}
/**
* <p>Rollback the current transaction.
* The success or the failure of the rollback operation is always logged (except if no logger is available).</p>
* <p>
* {@link #startTransaction()} must have been called before. If it's not the case the connection
* may throw a {@link SQLException} which will be transformed into a {@link DBException} here.
* </p>
* <p>If transactions are not supported by this connection, nothing is done.</p>
* <p><b><i>Important note:</b>
* If any error interrupts the ROLLBACK operation, transactions will considered afterwards as not supported by this connection.
* So, subsequent call to this function (and any other transaction related function) will never do anything.
* </i></p>
* @throws DBException If it is impossible to rollback a transaction though transactions are supported by this connection..
* If these are not supported, this error can never be thrown.
* @see #rollback(boolean)
*/
Grégory Mantelet
committed
protected final void rollback() {
rollback(true);
}
/**
* <p>Rollback the current transaction.</p>
* <p>
* {@link #startTransaction()} must have been called before. If it's not the case the connection
* may throw a {@link SQLException} which will be transformed into a {@link DBException} here.
* </p>
* <p>If transactions are not supported by this connection, nothing is done.</p>
* <p><b><i>Important note:</b>
* If any error interrupts the ROLLBACK operation, transactions will considered afterwards as not supported by this connection.
* So, subsequent call to this function (and any other transaction related function) will never do anything.
* </i></p>
* @param log <code>true</code> to log the success/failure of the rollback operation,
* <code>false</code> to be quiet whatever happens.
* @throws DBException If it is impossible to rollback a transaction though transactions are supported by this connection..
* If these are not supported, this error can never be thrown.
*/
Grégory Mantelet
committed
protected void rollback(final boolean log) {
try {
if (supportsTransaction && !connection.getAutoCommit()) {
connection.rollback();
if (log && logger != null)
logger.logDB(LogLevel.INFO, this, "ROLLBACK", "Transaction ROLLBACKED.", null);
}
Grégory Mantelet
committed
} catch(SQLException se) {
supportsTransaction = false;
if (log && logger != null)
logger.logDB(LogLevel.ERROR, this, "ROLLBACK", "Transaction ROLLBACK impossible!", se);
}
}
/**
* <p>End the current transaction.
* The success or the failure of the transaction ending operation is always logged (except if no logger is available).</p>
* <p>
* Basically, if transactions are supported by this connection, the flag AutoCommit is just turned on.
* </p>
* <p>If transactions are not supported by this connection, nothing is done.</p>
* <p><b><i>Important note:</b>
* If any error interrupts the END TRANSACTION operation, transactions will be afterwards considered as not supported by this connection.
* So, subsequent call to this function (and any other transaction related function) will never do anything.
* </i></p>
* @throws DBException If it is impossible to end a transaction though transactions are supported by this connection.
* If these are not supported, this error can never be thrown.
* @see #endTransaction(boolean)
*/
Grégory Mantelet
committed
protected final void endTransaction() {
endTransaction(true);
}
/**
* <p>End the current transaction.</p>
* <p>
* Basically, if transactions are supported by this connection, the flag AutoCommit is just turned on.
* </p>
* <p>If transactions are not supported by this connection, nothing is done.</p>
* <p><b><i>Important note:</b>
* If any error interrupts the END TRANSACTION operation, transactions will be afterwards considered as not supported by this connection.
* So, subsequent call to this function (and any other transaction related function) will never do anything.
* </i></p>
* @param log <code>true</code> to log the success/failure of the transaction ending operation,
* <code>false</code> to be quiet whatever happens.
* @throws DBException If it is impossible to end a transaction though transactions are supported by this connection.
* If these are not supported, this error can never be thrown.
*/
Grégory Mantelet
committed
protected void endTransaction(final boolean log) {
try {
if (supportsTransaction) {
connection.setAutoCommit(true);
if (log && logger != null)
logger.logDB(LogLevel.INFO, this, "END_TRANSACTION", "Transaction ENDED.", null);
}
Grégory Mantelet
committed
} catch(SQLException se) {
supportsTransaction = false;
if (log && logger != null)
logger.logDB(LogLevel.ERROR, this, "END_TRANSACTION", "Transaction ENDing impossible!", se);