Commit f8e64d37 authored by gmantele's avatar gmantele
Browse files

TAP+TEST: First steps with the TAP configuration file management (Factory,...

TAP+TEST: First steps with the TAP configuration file management (Factory, ServiceConnection, but not yet Metadata) + their JUnit tests + documentation (the html file + configuration file examples)
parent 904130f6
Loading
Loading
Loading
Loading
+308 −0
Original line number Diff line number Diff line
package tap.config;

import static tap.config.TAPConfiguration.*;

import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.sql.ResultSet;
import java.util.Collection;
import java.util.Iterator;
import java.util.Properties;

import tap.ServiceConnection;
import tap.TAPException;
import tap.TAPFactory;
import tap.file.LocalTAPFileManager;
import tap.file.TAPFileManager;
import tap.formatter.OutputFormat;
import tap.log.DefaultTAPLog;
import tap.log.TAPLog;
import tap.metadata.TAPMetadata;
import uws.UWSException;
import uws.service.UserIdentifier;

public final class DefaultServiceConnection implements ServiceConnection<ResultSet> {

	private TAPFileManager fileManager;

	private TAPLog logger;

	private DefaultTAPFactory tapFactory;

	private final String providerName;
	private final String serviceDescription;

	private boolean isAvailable = false;
	private String availability = null;

	private int[] executionDuration = new int[2];
	private int[] retentionPeriod = new int[2];

	public DefaultServiceConnection(final Properties tapConfig) throws NullPointerException, TAPException, UWSException{
		// 1. INITIALIZE THE FILE MANAGER:
		initFileManager(tapConfig);

		// 2. CREATE THE LOGGER:
		logger = new DefaultTAPLog(fileManager);

		// 3. BUILD THE TAP FACTORY:
		tapFactory = new DefaultTAPFactory(this, tapConfig);

		// 4. SET ALL GENERAL SERVICE CONNECTION INFORMATION:
		providerName = getProperty(tapConfig, KEY_PROVIDER_NAME);
		serviceDescription = getProperty(tapConfig, KEY_SERVICE_DESCRIPTION);
		availability = getProperty(tapConfig, KEY_DISABILITY_REASON);
		initRetentionPeriod(tapConfig);
		initExecutionDuration(tapConfig);

		// 5. MAKE THE SERVICE AVAILABLE (or not, depending on the property value):
		String propValue = getProperty(tapConfig, KEY_IS_AVAILABLE);
		isAvailable = (propValue == null) ? DEFAULT_IS_AVAILABLE : Boolean.parseBoolean(propValue);
	}

	private void initFileManager(final Properties tapConfig) throws TAPException{
		// Read the desired file manager:
		String fileManagerType = getProperty(tapConfig, KEY_FILE_MANAGER);
		if (fileManagerType == null)
			throw new TAPException("The property \"" + KEY_FILE_MANAGER + "\" is missing! It is required to create a TAP Service. Two possible values: " + VALUE_LOCAL + " or a class path between {...}.");
		else
			fileManagerType = fileManagerType.trim();

		// LOCAL file manager:
		if (fileManagerType.equalsIgnoreCase(VALUE_LOCAL)){
			// Read the desired root path:
			String rootPath = getProperty(tapConfig, KEY_FILE_ROOT_PATH);
			if (rootPath == null)
				throw new TAPException("The property \"" + KEY_FILE_ROOT_PATH + "\" is missing! It is required to create a TAP Service. Please provide a path toward a directory which will contain all files related to the service.");
			File rootFile = new File(rootPath);

			// Determine whether there should be one directory for each user:
			String propValue = getProperty(tapConfig, KEY_DIRECTORY_PER_USER);
			boolean oneDirectoryPerUser = (propValue == null) ? DEFAULT_DIRECTORY_PER_USER : Boolean.parseBoolean(propValue);

			// Determine whether there should be one directory for each user:
			propValue = getProperty(tapConfig, KEY_GROUP_USER_DIRECTORIES);
			boolean groupUserDirectories = (propValue == null) ? DEFAULT_GROUP_USER_DIRECTORIES : Boolean.parseBoolean(propValue);

			// Build the Local TAP File Manager:
			try{
				fileManager = new LocalTAPFileManager(rootFile, oneDirectoryPerUser, groupUserDirectories);
			}catch(UWSException e){
				throw new TAPException("The property \"" + KEY_FILE_ROOT_PATH + "\" (" + rootPath + ") is incorrect: " + e.getMessage());
			}
		}
		// CUSTOM file manager:
		else{
			Class<TAPFileManager> classObj = fetchClass(fileManagerType, KEY_FILE_MANAGER, TAPFileManager.class);
			if (classObj == null)
				throw new TAPException("Unknown value for the propertie \"" + KEY_FILE_MANAGER + "\": \"" + fileManagerType + "\". Only two possible values: " + VALUE_LOCAL + " or a class path between {...}.");

			try{
				fileManager = classObj.getConstructor(Properties.class).newInstance(tapConfig);
			}catch(InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException e){
				throw new TAPException("Impossible to create a TAPFileManager instance with the constructor (java.util.Properties tapConfig) of \"" + classObj.getName() + "\" for the following reason: " + e.getMessage());
			}
		}
	}

	private void initRetentionPeriod(final Properties tapConfig){
		retentionPeriod = new int[2];

		// Set the default period:
		String propValue = getProperty(tapConfig, KEY_DEFAULT_RETENTION_PERIOD);
		try{
			retentionPeriod[0] = (propValue == null) ? DEFAULT_RETENTION_PERIOD : Integer.parseInt(propValue);
		}catch(NumberFormatException nfe){
			retentionPeriod[0] = DEFAULT_RETENTION_PERIOD;
		}

		// Set the maximum period:
		propValue = getProperty(tapConfig, KEY_MAX_RETENTION_PERIOD);
		try{
			retentionPeriod[1] = (propValue == null) ? DEFAULT_RETENTION_PERIOD : Integer.parseInt(propValue);
		}catch(NumberFormatException nfe){
			retentionPeriod[1] = DEFAULT_RETENTION_PERIOD;
		}

		// The maximum period MUST be greater or equals than the default period.
		// If not, the default period is set (so decreased) to the maximum period.
		if (retentionPeriod[1] > 0 && retentionPeriod[1] < retentionPeriod[0])
			retentionPeriod[0] = retentionPeriod[1];
	}

	private void initExecutionDuration(final Properties tapConfig){
		executionDuration = new int[2];

		// Set the default duration:
		String propValue = getProperty(tapConfig, KEY_DEFAULT_EXECUTION_DURATION);
		try{
			executionDuration[0] = (propValue == null) ? DEFAULT_EXECUTION_DURATION : Integer.parseInt(propValue);
		}catch(NumberFormatException nfe){
			executionDuration[0] = DEFAULT_EXECUTION_DURATION;
		}

		// Set the maximum duration:
		propValue = getProperty(tapConfig, KEY_MAX_EXECUTION_DURATION);
		try{
			executionDuration[1] = (propValue == null) ? DEFAULT_EXECUTION_DURATION : Integer.parseInt(propValue);
		}catch(NumberFormatException nfe){
			executionDuration[1] = DEFAULT_EXECUTION_DURATION;
		}

		// The maximum duration MUST be greater or equals than the default duration.
		// If not, the default duration is set (so decreased) to the maximum duration.
		if (executionDuration[1] > 0 && executionDuration[1] < executionDuration[0])
			executionDuration[0] = executionDuration[1];
	}

	@Override
	public String getProviderName(){
		return providerName;
	}

	@Override
	public String getProviderDescription(){
		return serviceDescription;
	}

	@Override
	public boolean isAvailable(){
		return isAvailable;
	}

	public void setAvailability(final boolean isAvailable){
		this.isAvailable = isAvailable;
	}

	@Override
	public String getAvailability(){
		return availability;
	}

	public void setDisabilityReason(final String disabilityReason){
		availability = disabilityReason;
	}

	@Override
	public int[] getRetentionPeriod(){
		return retentionPeriod;
	}

	public boolean setDefaultRetentionPeriod(final int period){
		if ((retentionPeriod[1] <= 0) || (period > 0 && period <= retentionPeriod[1])){
			retentionPeriod[0] = period;
			return true;
		}else
			return false;
	}

	public boolean setMaxRetentionPeriod(final int period){
		if (period <= 0 || (retentionPeriod[0] > 0 && period >= retentionPeriod[0])){
			retentionPeriod[1] = period;
			return true;
		}else
			return false;
	}

	@Override
	public int[] getExecutionDuration(){
		return executionDuration;
	}

	public boolean setDefaultExecutionDuration(final int period){
		if ((executionDuration[1] <= 0) || (period > 0 && period <= executionDuration[1])){
			executionDuration[0] = period;
			return true;
		}else
			return false;
	}

	public boolean setMaxExecutionDuration(final int period){
		if (period <= 0 || (executionDuration[0] > 0 && period >= executionDuration[0])){
			executionDuration[1] = period;
			return true;
		}else
			return false;
	}

	@Override
	public int[] getOutputLimit(){
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public tap.ServiceConnection.LimitUnit[] getOutputLimitType(){
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public UserIdentifier getUserIdentifier(){
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public boolean uploadEnabled(){
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public int[] getUploadLimit(){
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public tap.ServiceConnection.LimitUnit[] getUploadLimitType(){
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public int getMaxUploadSize(){
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public TAPMetadata getTAPMetadata(){
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public Collection<String> getCoordinateSystems(){
		return null;
	}

	@Override
	public TAPLog getLogger(){
		return logger;
	}

	@Override
	public TAPFactory<ResultSet> getFactory(){
		return tapFactory;
	}

	@Override
	public TAPFileManager getFileManager(){
		return fileManager;
	}

	@Override
	public Iterator<OutputFormat<ResultSet>> getOutputFormats(){
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public OutputFormat<ResultSet> getOutputFormat(String mimeOrAlias){
		// TODO Auto-generated method stub
		return null;
	}

}
+172 −0
Original line number Diff line number Diff line
package tap.config;

import static tap.config.TAPConfiguration.*;

import java.lang.reflect.InvocationTargetException;
import java.sql.ResultSet;
import java.util.Properties;

import adql.translator.ADQLTranslator;
import adql.translator.PgSphereTranslator;
import adql.translator.PostgreSQLTranslator;
import tap.AbstractTAPFactory;
import tap.ServiceConnection;
import tap.TAPException;
import tap.backup.DefaultTAPBackupManager;
import tap.db.DBConnection;
import tap.db.JDBCConnection;
import uws.UWSException;
import uws.service.UWSService;
import uws.service.backup.UWSBackupManager;

public final class DefaultTAPFactory extends AbstractTAPFactory<ResultSet> {

	private Class<? extends ADQLTranslator> translator;

	private final String driverPath;
	private final String dbUrl;
	private final String dbUser;
	private final String dbPassword;

	private boolean backupByUser;
	private long backupFrequency;

	@SuppressWarnings("unchecked")
	public DefaultTAPFactory(ServiceConnection<ResultSet> service, final Properties tapConfig) throws NullPointerException, TAPException{
		super(service);

		/* 0. Extract the DB type and deduce the JDBC Driver path */
		String jdbcDriver = getProperty(tapConfig, KEY_JDBC_DRIVER);
		String dbUrl = null;
		if (jdbcDriver == null){
			dbUrl = getProperty(tapConfig, KEY_JDBC_URL);
			if (dbUrl == null)
				throw new TAPException("JDBC URL missing.");
			else if (!dbUrl.startsWith(JDBCConnection.JDBC_PREFIX + ":"))
				throw new TAPException("JDBC URL format incorrect! It MUST begins with " + JDBCConnection.JDBC_PREFIX + ":");
			else{
				String dbType = dbUrl.substring(JDBCConnection.JDBC_PREFIX.length() + 1);
				if (dbType.indexOf(':') <= 0)
					throw new TAPException("JDBC URL format incorrect! Database type name is missing.");
				dbType = dbType.substring(0, dbType.indexOf(':'));

				jdbcDriver = VALUE_JDBC_DRIVERS.get(dbType);
				if (jdbcDriver == null)
					throw new TAPException("No JDBC driver known for the DBMS \"" + dbType + "\"!");
			}
		}

		/* 1. Set the ADQLTranslator to use in function of the sql_translator property */
		String sqlTranslator = getProperty(tapConfig, KEY_SQL_TRANSLATOR);
		// case a.) no translator specified
		if (sqlTranslator == null || sqlTranslator.isEmpty())
			throw new TAPException("No SQL translator specified !");

		// case b.) PostgreSQL translator
		else if (sqlTranslator.equalsIgnoreCase(VALUE_POSTGRESQL))
			translator = PostgreSQLTranslator.class;

		// case c.) PgSphere translator
		else if (sqlTranslator.equals(VALUE_PGSPHERE))
			translator = PgSphereTranslator.class;

		// case d.) a client defined ADQLTranslator (with the provided class path)
		else if (sqlTranslator.charAt(0) == '{' && sqlTranslator.charAt(sqlTranslator.length() - 1) == '}'){
			sqlTranslator = sqlTranslator.substring(1, sqlTranslator.length() - 2);
			try{
				translator = (Class<? extends ADQLTranslator>)ClassLoader.getSystemClassLoader().loadClass(sqlTranslator);
			}catch(ClassNotFoundException cnfe){
				throw new TAPException("Unable to load the SQL Translator! The class specified by the property sql_translator (" + sqlTranslator + ") can not be found.");
			}catch(ClassCastException cce){
				throw new TAPException("Unable to load the SQL Translator! The class specified by the property sql_translator (" + sqlTranslator + ") is not implementing adql.translator.ADQLTranslator.");
			}
		}
		// case e.) unsupported value
		else
			throw new TAPException("Unsupported value for the property sql_translator: \"" + sqlTranslator + "\" !");

		/* 2. Test the construction of the ADQLTranslator */
		createADQLTranslator();

		/* 3. Store the DB connection parameters */
		this.driverPath = jdbcDriver;
		this.dbUrl = dbUrl;
		this.dbUser = getProperty(tapConfig, KEY_DB_USERNAME);;
		this.dbPassword = getProperty(tapConfig, KEY_DB_PASSWORD);

		/* 4. Test the DB connection */
		DBConnection<ResultSet> dbConn = createDBConnection("0");
		dbConn.close();

		/* 5. Set the UWS Backup Parameter */
		// BACKUP FREQUENCY:
		String propValue = getProperty(tapConfig, KEY_BACKUP_FREQUENCY);
		boolean isTime = false;
		// determine whether the value is a time period ; if yes, set the frequency:
		if (propValue != null){
			try{
				backupFrequency = Long.parseLong(propValue);
				if (backupFrequency > 0)
					isTime = true;
			}catch(NumberFormatException nfe){}
		}
		// if the value was not a valid numeric time period, try to identify the different textual options:
		if (!isTime){
			if (propValue != null && propValue.equalsIgnoreCase(VALUE_USER_ACTION))
				backupFrequency = DefaultTAPBackupManager.AT_USER_ACTION;
			else
				backupFrequency = DEFAULT_BACKUP_FREQUENCY;
		}
		// BACKUP BY USER:
		propValue = getProperty(tapConfig, KEY_BACKUP_BY_USER);
		backupByUser = (propValue == null) ? DEFAULT_BACKUP_BY_USER : Boolean.parseBoolean(propValue);
	}

	/**
	 * Build an {@link ADQLTranslator} instance with the given class ({@link #translator} ;
	 * specified by the property sql_translator). If the instance can not be build,
	 * whatever is the reason, a TAPException MUST be thrown.
	 * 
	 * Note: This function is called at the initialization of {@link DefaultTAPFactory}
	 * in order to check that a translator can be created.
	 * 
	 * @see tap.TAPFactory#createADQLTranslator()
	 */
	@Override
	public ADQLTranslator createADQLTranslator() throws TAPException{
		try{
			return translator.getConstructor().newInstance();
		}catch(InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException e){
			throw new TAPException("Impossible to create an ADQLTranslator instance with the empty constructor of \"" + translator.getName() + "\" (see the property sql_translator) for the following reason: " + e.getMessage());
		}
	}

	/**
	 * Build a {@link JDBCConnection} thanks to the database parameters specified
	 * in the TAP configuration file (the properties: jdbc_driver_path, db_url, db_user, db_password).
	 * 
	 * @see tap.TAPFactory#createDBConnection(java.lang.String)
	 * @see JDBCConnection
	 */
	@Override
	public DBConnection<ResultSet> createDBConnection(String jobID) throws TAPException{
		return new JDBCConnection(jobID, driverPath, dbUrl, dbUser, dbPassword, this.service.getLogger());
	}

	/**
	 * Build an {@link DefaultTAPBackupManager} thanks to the backup manager parameters specified
	 * in the TAP configuration file (the properties: backup_frequency, backup_by_user).
	 * 
	 * Note: If the specified backup_frequency is negative, no backup manager is returned.
	 * 
	 * @return	null if the specified backup frequency is negative, or an instance of {@link DefaultTAPBackupManager} otherwise.
	 * 
	 * @see tap.AbstractTAPFactory#createUWSBackupManager(uws.service.UWSService)
	 * @see DefaultTAPBackupManager
	 */
	@Override
	public UWSBackupManager createUWSBackupManager(UWSService uws) throws TAPException, UWSException{
		return (backupFrequency < 0) ? null : new DefaultTAPBackupManager(uws, backupByUser, backupFrequency);
	}

}
+161 −0
Original line number Diff line number Diff line
package tap.config;

import java.io.File;
import java.io.FileInputStream;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Properties;

import tap.TAPException;
import tap.backup.DefaultTAPBackupManager;

public final class TAPConfiguration {

	/* FILE MANAGER KEYS */
	public final static String KEY_FILE_MANAGER = "file_manager";
	public final static String VALUE_LOCAL = "local";
	public final static String DEFAULT_FILE_MANAGER = VALUE_LOCAL;
	public final static String KEY_FILE_ROOT_PATH = "file_root_path";
	public final static String KEY_DIRECTORY_PER_USER = "directory_per_user";
	public final static boolean DEFAULT_DIRECTORY_PER_USER = false;
	public final static String KEY_GROUP_USER_DIRECTORIES = "group_user_directories";
	public final static boolean DEFAULT_GROUP_USER_DIRECTORIES = false;
	public final static String KEY_DEFAULT_RETENTION_PERIOD = "default_retention_period";
	public final static String KEY_MAX_RETENTION_PERIOD = "max_retention_period";
	public final static int DEFAULT_RETENTION_PERIOD = 0;

	/* UWS BACKUP */
	public final static String KEY_BACKUP_FREQUENCY = "backup_frequency";
	public final static String VALUE_USER_ACTION = "user_action";
	public final static long DEFAULT_BACKUP_FREQUENCY = DefaultTAPBackupManager.MANUAL;	// = "never" => no UWS backup manager
	public final static String KEY_BACKUP_BY_USER = "backup_by_user";
	public final static boolean DEFAULT_BACKUP_BY_USER = false;

	/* EXECUTION DURATION */
	public final static String KEY_DEFAULT_EXECUTION_DURATION = "default_execution_duration";
	public final static String KEY_MAX_EXECUTION_DURATION = "max_execution_duration";
	public final static int DEFAULT_EXECUTION_DURATION = 0;

	/* DATABASE KEYS */
	public final static String KEY_JDBC_DRIVER = "jdbc_driver";
	public final static HashMap<String,String> VALUE_JDBC_DRIVERS = new HashMap<String,String>(4);
	static{
		VALUE_JDBC_DRIVERS.put("oracle", "oracle.jdbc.OracleDriver");
		VALUE_JDBC_DRIVERS.put("postgresql", "org.postgresql.Driver");
		VALUE_JDBC_DRIVERS.put("mysql", "com.mysql.jdbc.Driver");
		VALUE_JDBC_DRIVERS.put("sqlite", "org.sqlite.JDBC");
	}
	public final static String KEY_SQL_TRANSLATOR = "sql_translator";
	public final static String VALUE_POSTGRESQL = "postgres";
	public final static String VALUE_PGSPHERE = "pgsphere";
	public final static String KEY_JDBC_URL = "jdbc_url";
	public final static String KEY_DB_USERNAME = "db_username";
	public final static String KEY_DB_PASSWORD = "db_password";

	/* PROVIDER KEYS */
	public final static String KEY_PROVIDER_NAME = "provider_name";
	public final static String KEY_SERVICE_DESCRIPTION = "service_description";

	/* AVAILABILITY KEYS */
	public final static String KEY_IS_AVAILABLE = "is_available";
	public final static boolean DEFAULT_IS_AVAILABLE = true;
	public final static String KEY_DISABILITY_REASON = "disability_reason";

	/**
	 * Read the asked property from the given Properties object.
	 * 	- The returned property value is trimmed (no space at the beginning and at the end of the string).
	 * 	- If the value is empty (length=0), NULL is returned.
	 * 
	 * @param prop	List of property
	 * @param key	Property whose the value is requested.
	 * 
	 * @return		Return property value.
	 */
	public final static String getProperty(final Properties prop, final String key){
		if (prop == null)
			return null;

		String value = prop.getProperty(key);
		if (value != null){
			value = value.trim();
			return (value.length() == 0) ? null : value;
		}

		return value;
	}

	/**
	 * Test whether a property value is a class path.
	 * Expected syntax: a non-empty string surrounded by brackets ('{' and '}').
	 * 
	 * Note: The class path itself is not checked!
	 * 
	 * @param value	Property value.
	 * 
	 * @return <i>true</i> if the given value is formatted as a class path, <i>false</i> otherwise.
	 */
	public final static boolean isClassPath(final String value){
		return (value != null && value.length() > 2 && value.charAt(0) == '{' && value.charAt(value.length() - 1) == '}');
	}

	/**
	 * Fetch the class object corresponding to the classpath provided between brackets in the given value. 
	 * 
	 * @param value			Value which is supposed to contain the classpath between brackets (see {@link #isClassPath(String)} for more details)
	 * @param propertyName	Name of the property associated with the parameter "value".
	 * @param expectedType	Type of the class expected to be returned ; it is also the type which parameterizes this function: C.
	 * 
	 * @return	The corresponding Class object.
	 * 
	 * @throws TAPException	If the classpath is incorrect or if its type is not compatible with the parameterized type C (represented by the parameter "expectedType").
	 * 
	 * @see {@link #isClassPath(String)}
	 */
	@SuppressWarnings("unchecked")
	public final static < C > Class<C> fetchClass(final String value, final String propertyName, final Class<C> expectedType) throws TAPException{
		if (!isClassPath(value))
			return null;

		String classPath = value.substring(1, value.length() - 1).trim();
		if (classPath.isEmpty())
			return null;

		try{
			Class<C> classObject = (Class<C>)ClassLoader.getSystemClassLoader().loadClass(classPath);
			if (!expectedType.isAssignableFrom(classObject))
				throw new TAPException("The class specified by the property " + propertyName + " (" + value + ") is not implementing " + expectedType.getName() + ".");
			else
				return classObject;
		}catch(ClassNotFoundException cnfe){
			throw new TAPException("The class specified by the property " + propertyName + " (" + value + ") can not be found.");
		}catch(ClassCastException cce){
			throw new TAPException("The class specified by the property " + propertyName + " (" + value + ") is not implementing " + expectedType.getName() + ".");
		}
	}

	public final static void main(final String[] args) throws Throwable{

		FileInputStream configFileStream = null;
		try{
			final File configFile = new File("src/ext/tap_min.properties");
			configFileStream = new FileInputStream(configFile);

			Properties config = new Properties();
			config.load(configFileStream);

			configFileStream.close();
			configFileStream = null;

			Enumeration<Object> keys = config.keys();
			String key;
			while(keys.hasMoreElements()){
				key = keys.nextElement().toString();
				System.out.println("* " + key + " = " + config.getProperty(key));
			}
		}finally{
			if (configFileStream != null)
				configFileStream.close();
		}
	}

}
+49 −0
Original line number Diff line number Diff line
Name|DBType|JDBCType|TAPType|VOTableType
id|character varying(19)|varchar(19)||
ra2|numeric(14,10)|numeric(14,10)||
dec2|numeric(14,10)|numeric(14,10)||
vmag|real|float4||
gmag|real|float4||
gbmag|real|float4||
grmag|real|float4||
gsmag|real|float4||
ra|numeric(14,10)|numeric(14,10)||
deg|numeric(14,10)|numeric(14,10)||
r|double precision|float8||
pmra|double precision|float8||
pmde|double precision|float8||
rv|double precision|float8||
v_i|real|float4||
av|real|float4||
age|real|float4||
alphafe|real|float4||
balb|real|float4||
e|real|float4||
feh|real|float4||
fi|smallint|int2||
galb|real|float4||
fm|smallint|int2||
host|smallint|int2||
i|real|float4||
logg|real|float4||
Omega|real|float4||
mass|double precision|float8||
mbol|real|float4||
nc|smallint|int2||
nt|smallint|int2||
p|double precision|float8||
omega|real|float4||
t0|double precision|float8||
phase|real|float4||
pop|smallint|int2||
beenv|double precision|float8||
radius|double precision|float8||
a|double precision|float8||
teff|integer|int4||
vamp|real|float4||
vper|double precision|float8||
vphase|real|float4||
vtype|character varying(4)|varchar(4)||
vsini|real|float4||
recno|integer|int4||
coord|spoint|spoint||
+469 −0

File added.

Preview size limit exceeded, changes collapsed.

Loading