Executing ADQL queries from the command line and external scripts

The entry point is a TAP (Table Access Protocol) server, that allows to execute queries that returns data as tables. To get final products like images or spectra please read their corresponding sections.

You may use HTTP protocol to execute TAP requests at https://eas.esac.esa.int/tap-server/tap.

TAP provides two operation modes: Synchronous and Asynchronous:

  • Synchronous: the response to the request will be generated as soon as the request received by the server.

  • Asynchronous: the server will start a job that will execute the request. The first response to the request is the required information (a link) to obtain the job status. Once the job is finished, the results can be retrieved.

Our TAP server provides two access modes: public and authenticated:

  • Public: this is the standard TAP access. A user can execute ADQL queries and upload tables to be used in a query 'on-the-fly' (these tables will be removed once the query is executed). The results are available to any other user and they will remain in the server for a limited space of time.

  • Authenticated: some functionalities are restricted to authenticated users only. The results are saved in a private user space and they will remain in their account. The user account space is limited, currently of 10 GB by default . The user can select specific jobs and delete them manually from the ADQL FORM interface.

    • ADQL queries and results are saved in a user private area.

    • Cross-match operations: a catalogue cross-match operation can be executed. Cross-match operations results are saved in a user private area.

    • Persistence of uploaded tables: a user can upload a table in a private space (top left options of the ADQL FORM). These tables can be used in queries as well as in cross-matches operations.

Below you will find some examples on how to execute queries using the curl tool. This method can be used in scripts and programs to automatically get data from the Euclid databases. The URLs for the curl command can also be pasted in a browser tab for a quick test (remember to log in to access private tables).

Non authenticated access

Getting all public tables

curl "https://eas.esac.esa.int/tap-server/tap/tables"

Authenticated access

To authenticate (log in and out) using the command line, you can use the following two commands. Any query in next subsections should be executed between them.

curl -k -c cookies.txt -X POST -d username=USER -d password=PASSWORD -L "https://eas.esac.esa.int/tap-server/login"
curl -k -b cookies.txt -X POST -d -L "https://eas.esac.esa.int/tap-server/logout"

Synchronous query

curl -k -b cookies.txt -X GET "https://eas.esac.esa.int/tap-server/tap/sync?REQUEST=doQuery&LANG=ADQL&FORMAT=csv&QUERY=SELECT+TOP+50+object_id,right_ascension,declination+FROM+sascat_sc8.mer_final_cat_sc8" > out.csv

The retrieved results is CSV table, or a VO table if FORMAT is not specified (see '3.2. Synchronous Queries' section parameters to specify a different output format). Ending the command with a > out.csv, the results can be saved in a file and inspected using any analysis tool like TOPCAT, for instance.

Asynchronous query

curl -i -k -b cookies.txt -X POST --data "PHASE=run&LANG=ADQL&REQUEST=doQuery&QUERY=SELECT+TOP+50+object_id,right_ascension,declination+FROM+sascat_sc8.mer_final_cat_sc8" "https://eas.esac.esa.int/tap-server/tap/async"

The response will contain the URL of the job running at server side (see Location header):

HTTP/1.1 303 See Other
Date: Thu, 21 Sep 2017 16:16:08 GMT
Server: Apache-Coyote/1.1
Location: https://eas.esac.esa.int/tap-server/tap/async/1506010568630L
Content-Type: text/plain;charset=ISO-8859-1
Connection: close
Transfer-Encoding: chunked

To obtain the status of the running job:

curl -k -b cookies.txt -X GET "https://eas.esac.esa.int/tap-server/tap/async/1506010568630L"

The status response is something like:

<?xml version="1.0" encoding="UTF-8"?>
<uws:job xmlns:uws="http://www.ivoa.net/xml/UWS/v1.0" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.ivoa.net/xml/UWS/v1.0 http://www.ivoa.net/xml/UWS/v1.0" >
<uws:jobId><![CDATA[1506010568630L]]></uws:jobId>
<uws:runId xsi:nil="true"/>
<uws:ownerId><![CDATA[anonymous]]></uws:ownerId>
<uws:phase>COMPLETED</uws:phase>
<uws:quote>-1</uws:quote>
<uws:startTime>2021-09-21T18:16:09.052+0200</uws:startTime>
<uws:endTime>2021-09-21T18:16:09.323+0200</uws:endTime>
<uws:executionDuration>0</uws:executionDuration>
<uws:destruction>2021-09-24T18:16:08.630+0200</uws:destruction>
<uws:creationTime>2021-09-21T18:16:08.630+0200</uws:creationTime>
<uws:parameters>
<uws:parameter id="query"><![CDATA[SELECT TOP 50 objectid,rightascension,declination FROM sascat_sc8.mer_final_cat_sc8]]></uws:parameter>
<uws:parameter id="request"><![CDATA[doQuery]]></uws:parameter>
<uws:parameter id="format"><![CDATA[csv]]></uws:parameter>
<uws:parameter id="phase"><![CDATA[run]]></uws:parameter>
<uws:parameter id="lang"><![CDATA[ADQL]]></uws:parameter>
</uws:parameters>
<uws:results>
<uws:result id="result" xlink:type="simple" xlink:href="https://eas.esac.esa.int/tap-server/tap/async/1506010568630L/results/result" mime="application/x-csv+xml" size="2148" rows="50" />
</uws:results>
<uws:errorSummary xsi:nil="true"/>
</uws:job>

To obtain the results of the job (once the job is finished):

curl -k -b cookies.txt -X GET "https://eas.esac.esa.int/tap-server/tap/async/1506010568630L/results/result"

The retrieved results is a VO table by default (see '3.3. Asynchronous Queries' section parameters to specify a different output format). As in previous examples the results can be saved in a file and inspected using any analysis tool like TOPCAT, for instance.

Python 3 script to execute an asynchronous query and wait until the results are available

See also the Jupyter Notebook section.

#ASYNCHRONOUS REQUEST
import urllib.parse
import urllib.request
import time
from xml.dom.minidom import parseString
 
url = "https://eas.esac.esa.int/tap-server/tap/async"
contenttype = "application/x-www-form-urlencoded"
textplain = "text/plain"
#-------------------------------------
#Create job
 
params = urllib.parse.urlencode({\
"REQUEST": "doQuery", \
"LANG": "ADQL", \
"FORMAT": "csv", \
"PHASE": "RUN", \
"JOBNAME": "Any name (optional)", \
"JOBDESCRIPTION": "Any description (optional)", \
"QUERY": "SELECT DISTANCE(POINT('ICRS', right_ascension, declination), POINT('ICRS',9.95,-19.20)) AS dist, * FROM sascat_sc8.mer_final_cat_sc8 WHERE 1=CONTAINS(POINT('ICRS', right_ascension, declination),CIRCLE('ICRS',9.95,-19.20,0.05)) ORDER BY dist ASC"
})
 
request = urllib.request.Request(url, method="POST")
request.add_header("Content-type", "application/x-www-form-urlencoded")
request.add_header("Accept", "text/plain")
connection = urllib.request.urlopen(request, data=params.encode("UTF-8"))
#Status
print ("Status: " +str(connection.getcode()), "Reason: " + str(connection.reason))
 
#Server job location (URL)
info = connection.info()
redirection = connection.geturl()
#Jobid
jobid = redirection[redirection.rfind('/')+1:]
print ("Job id: " + jobid)
 
connection.close()
 
#-------------------------------------
#Check job status, wait until finished
 
while True:
request = urllib.request.Request(redirection, method="GET")
connection = urllib.request.urlopen(request)
data = connection.read().decode("UTF-8")
#XML response: parse it to obtain the current status
dom = parseString(data)
phaseElement = dom.getElementsByTagName('uws:phase')[0]
phaseValueElement = phaseElement.firstChild
phase = phaseValueElement.toxml()
print ("Status: " + phase)
#Check finished
if phase == 'COMPLETED': break
#wait and repeat
time.sleep(0.2)
 
connection.close()
 
#-------------------------------------
#Get results
request = urllib.request.Request(redirection+"/results/result", method="GET")
connection = urllib.request.urlopen(request)
data = connection.read().decode("UTF-8")
outputFileName = "example3_csv_output.csv"
outputFile = open(outputFileName, "w")
outputFile.write(data)
outputFile.close()
connection.close()
print ("Data saved in: " + outputFileName)

The saved file is a VO table (by default, see '3.3. Asynchronous Queries' section parameters to specify a different output format). The file can be inspected using any analysis tool like TOPCAT, for instance.

Python 3 script to save the results of asynchronous job into VOSpace account

To insert files into VOSpace we need to generate the following XML file transfer_push_to_a.xml, where:

  • target: is the VOSpace URI to put the query result

  • direction: to upload files, it should be fixed to 'pushToVoSpace'

  • view: parameter fixed to 'text'

  • protocol: parameter fixed to 'httpput'

To use the following Python code, you should fill the following parameters:

  • user: name of the ESA Ldap user account

  • pssd: password of the ESA Ldap account

  • metadataTransfer: name of the above file

  • toupload: name of the file to upload to VOSpace

import urllib.request, requests, ssl, base64, time, json
import xml.dom.minidom
 
user='user-ldap-name'
pssd='user-password'
metadataTransfer='name-xml-metadata-file'
toupload='name-file-to-be-saved'
transfer_url='https://vospace.esac.esa.int/vospace/servlet/transfers/async?PHASE=RUN'
end_point = "https://vospace.esac.esa.int/vospace/service/data/"
 
f = open(metadataTransfer, 'r', encoding="utf-8").read()
files = {'file': (metadataTransfer, f)}
 
try:
upload_request = requests.post(transfer_url, files=files, auth=(user, pssd))
except requests.exceptions.RequestException as e: # This is the correct syntax
print (upload_request.text)
sys.exit(1)
else: # 200
redirection = upload_request.url
jobid = redirection[redirection.rfind('/')+1:]
#print ("Job id: " + jobid)
upload_request.close()
while True:
request = requests.get(redirection, auth=(user, pssd))
#XML response: parse it to obtain the current status
data=request.text
dom = xml.dom.minidom.parseString(data)
phaseElement = dom.getElementsByTagName('uws:phase')[0]
phaseValueElement = phaseElement.firstChild
phase = phaseValueElement.toxml()
print ("Status: " + phase)
#Check finished
if phase == 'COMPLETED': break
if phase == 'ERROR' : sys.exit(1)
#wait and repeat
time.sleep(0.3)
# Open XML document using minidom parser
DOMTree = xml.dom.minidom.parseString(data)
collection = DOMTree.documentElement
jobId_element = collection.getElementsByTagName('uws:jobId')[0]
jobId = jobId_element.childNodes[0].data
 
f = open(toupload, 'rb').read()
files = {'file': (toupload, f)}
 
try:
upload_post = requests.post(end_point + user + "/" + jobId, files=files, auth=(user, pssd))
except requests.exceptions.RequestException as e: # This is the correct syntax
print (upload_post.text)
sys.exit(1)
else: # 200
#print(upload_post.text)
redirection = upload_post.url
jobid = redirection[redirection.rfind('/')+1:]
print ("Job id: " + jobid)
upload_post.close()
if upload_post.ok:
print ("Done, Ok.........................................................");

Interface

See the following specifications:

TAP resources

https://eas.esac.esa.int/tap-server/tap/

Tables

https://eas.esac.esa.int/tap-server/tap/tables

Synchronous access

https://eas.esac.esa.int/tap-server/tap/sync

Asynchronous access

https://eas.esac.esa.int/tap-server/tap/async

Service availability

https://eas.esac.esa.int/tap-server/tap/availability

Synchronous Queries

Parameter

Value

Comments

REQUEST

doQuery

Requests to execute the provided query

LANG

ADQL

Query language

FORMAT

- votable
- csv
- json

Results output format

QUERY

ADQL query

query

Asynchronous Queries

Parameter

Value

Comments

Same parameters as those defined in 3.2 Synchronous Queries and

PHASE

run

Query job initial phase

The response header will contain the location of the job.

TAP+ login

Parameter

Value

Comments

username

user_name

User name

password

user_password

User password

The response header will contain the session identifier.

TAP+ logout

Parameter

Value

Comments

session identifier

session identifier

Session identifier provided by a login request
Must be added to the HTTP header