Python 2.7 (Deprecated)
Python 2 has reached end of life and is no longer supported in Exasol. We recommend that you use Python 3 in all UDFs.
Python 2.7 (the final version of the Python 2 series) reached end of life status in 2020 and is no longer supported in Exasol. We therefore recommend that you use Python 3 in all your UDFs. If you need to continue using Python 2 in your UDFs, refer to Using Python 2 with Exasol 7.1.20 or later for a suggested workaround.
The following information in this section provides details about the now deprecated Python 2.7. For additional information, see the official Python Documentation.
run() and cleanup() Methods
The method run()
is called for each input tuple (SCALAR) or each group (SET). Its
parameter is a type of execution context and provides access to the data and the iterator in
case of a SET script.
To initialize expensive steps such as opening external connections you can write code outside
the run()
method, since this code is executed once at the beginning by each virtual machine.
For deinitialization purpose, the method cleanup()
is available. It is called once
for each virtual machine, at the end of the execution.
Parameters
The internal Python data types and the database SQL types are not identical. Therefore, casts must be done for the input and output data:
Datatype SQL | Datatype Python |
---|---|
DECIMAL(p,0)
|
int
|
DECIMAL(p,s)
|
decimal.Decimal
|
DOUBLE
|
float
|
DATE
|
datetime.date
|
TIMESTAMP
|
datetime.datetime
|
BOOLEAN
|
bool
|
VARCHAR and CHAR |
unicode
|
The Python keyword None
is the equivalent of SQL NULL
.
The input parameters can be addressed by their names. For example, ctx.my_input
. ctx
refers to the name of the context parameter that is passed to the run()
method.
You can also use a dynamic number of parameters via the notation (...)
, for example, CREATE
PYTHON SCALAR SCRIPT my_script (...)
. The parameters can then be accessed
through an index (ctx[0] for the first parameter). The number of parameters and
their data types (both determined during the call of the script) are part of the metadata.
For better performance, use DOUBLE
instead of
DECIMAL
for parameter types.
Metadata
You can access the following metadata through global variables:
Metadata | Description |
---|---|
|
Database name |
|
Database version |
|
Name and version of the script language |
|
Name of the script |
|
Schema in which the script is stored |
|
Schema which is currently opened |
|
Code of the script |
|
Session ID |
|
Statement ID within the session |
|
Current user |
|
Scope user ( |
|
Number of cluster nodes |
|
Local node ID starting with 0 |
|
Unique ID for the local machine (the IDs of the virtual machines have no relation to each other) |
|
Type of the input data (SCALAR or SET) |
|
Number of input columns |
|
Array including the following information: {name, type, sql_type, precision, scale, length} |
|
Type of the output data (RETURNS or EMITS) |
|
Number of output columns |
|
Array including the following information: {name, type, sql_type, precision, scale, length} |
Data Iterator
For scripts having multiple input tuples per call (keyword SET) you can iterate through
that data using the method next()
, which is accessible through the context. Initially, the iterator points to the first input row. For iterating you can use a while True
loop, which
is aborted in case if not ctx.next()
.
If the input data is empty, then the run()
method will not be called,
and similar to aggregate functions the NULL value is returned as
result (for example, SELECT MAX(x) FROM t WHERE
false
).
Additionally, there is a method reset()
that resets the iterator to the first input
element. Therefore, you can do multiple iterations through the data, if this is required for
your algorithm.
The method size()
returns the number of input values.
emit()
You can return multiple output tuples per call (keyword EMITS) using the method emit()
.
The method expects as many parameters as output columns were defined. In the case of
dynamic output parameters, it is handy in Python to use a list
object which can be unpacked using * (like in the example above: ctx.emit(*currentRow)
).
Import of Other Scripts
Other scripts can be imported through the method exa.import_script()
. The return value
of this method must be assigned to a variable, representing the imported module.
Syntax
Example
--/
CREATE OR REPLACE PYTHON SCALAR SCRIPT LIB.MYLIB() RETURNS INT AS
def helloWorld():
return "Hello Python World!"
/
--/
CREATE OR REPLACE PYTHON SCALAR SCRIPT TEST.MYHELLOWORLD() RETURNS VARCHAR(2000) AS
l = exa.import_script('LIB.MYLIB')
def run(ctx):
return l.helloWorld()
/
select TEST.MYHELLOWORLD();
Accessing Connection Definitions
The data that has been specified when defining connections with CREATE CONNECTION
is available in Python UDF scripts through the method exa.get_connection("<connection_
name>")
. The result is a Python object with the following fields:
Fields | Description |
---|---|
|
The type of the connection definition. |
|
The part of the connection definition that followed the |
|
The part of the connection definition that followed the |
|
The part of the connection definition that followed the |
Auxiliary Libraries
The following libraries are provided that are not already part of the language:
Libraries | Description |
---|---|
|
Processing of JSON objects (for details, see http://pypi.python.org/pypi/python-cjson) |
|
XML processing (for details, see http://pypi.python.org/pypi/lxml) |
|
Numeric calculations (for details, see http://www.numpy.org) |
|
Hierarchical database package (for details, see http://www.pytables.org). For this library, the required build package HDF5 is available at http://www.h5py.org. |
|
Time zone functions (for details, see http://pytz.sourceforge.net) |
|
Interface for Redis (for details, see http://pypi.python.org/pypi/redis/) |
|
Machine Learning (for details, see http://scikit-learn.org) |
|
Scientific tools (for details, see http://www.scipy.org). For this library, the required build tool atlas is available at http://pypi.python.org/pypi/atlas. |
requests
|
Standard for making HTTP requests in Python. For more details, see https://pypi.org/project/requests/ |
pycurl
|
Can be used to fetch objects identified by a URL from a Python program. For more details, see https://pypi.org/project/pycurl/ |
boto3
|
Boto3 is the Amazon Web Services (AWS) SDK for Python. For more details, see https://pypi.org/project/boto3/ |
boto
|
Boto is the (deprecated) Amazon Web Services (AWS) SDK for Python. For more details, see https://pypi.org/project/boto/ |
Dynamic Output Parameters Callback Function
If the UDF script is defined with dynamic output parameters and the output parameters
cannot be determined (by specifying EMITS
in the query or by INSERT INTO SELECT
), the database calls the method default_output_columns()
which you can implement. The expected return value is a String with the names and types of the output columns, "a int, b varchar(100)"
. For more details about
when this method is called, see Dynamic input and output parameters
You can access the Metadata exa.meta
in the method
to find out the number and types of input columns.
The method will be executed only once on a single node.
User Defined Import Callback Function
To support a user defined import you can implement the callback method generate_sql_for_import_spec(import_spec)
. For syntax, see Dynamic input and output parameters and IMPORT statement. The parameter import_spec
contains all information about the executed IMPORT FROM SCRIPT
statement. The function has to generate and return a SELECT
statement which will retrieve the data to be imported. import_spec
has the following fields:
Fields | Description |
---|---|
|
Parameters specified in the |
|
This is true, if the |
|
If |
|
If |
|
This returns the name of the connection, if it was
specified. Otherwise it returns None. The UDF script
can then obtain the connection information through |
|
This is only defined, if the user provided connection
information. It returns an object similar to the return value of |
The password is transferred into plaintext and could be visible in the logs. Therefore, it is recommended to create a CONNECTION
before and to specify only the connection
name (can be obtained from connection_name
field). The actual
connection information can be obtained through exa.get_connection(name)
.
User Defined Export Callback Function
To support a user defined export you can implement the callback method generate_sql_for_export_spec(export_spec)
. For syntax, see Dynamic input and output parametersand EXPORT statement. The parameter export_spec
contains all information about the executed EXPORT INTO SCRIPT
statement. The function has to generate and return a SELECT SQL
statement which will generate the data to be exported. The FROM
part of that string can be a dummy table (DUAL) since the export command is aware which table should be exported. But it has to be specified to be able to compile the SQL string successfully.
export_spec
has the following fields:
Fields | Description |
---|---|
|
Parameters specified in the |
|
List of column names of the resulting table that should be exported. |
|
Boolean value from the |
|
Boolean value from the |
|
String value from the |
|
This returns the name of the connection, if it was
specified. Otherwise it returns |
|
This is only defined, if the user provided connection
information. It returns an object similar to the return value of |
The password is transferred into plaintext and could be visible in the logs. Therefore, It is recommended to create a CONNECTION
before and to specify only the connection
name (can be obtained from connection_name
field). The actual
connection information can be obtained through exa.get_connection(name)
.
Example
/*
This example loads from a webserver
and processes the following file goalies.xml:
<?xml version='1.0' encoding='UTF-8'?>
<users>
<user active="1">
<first_name>Manuel</first_name>
<last_name>Neuer</last_name>
</user>
<user active="1">
<first_name>Joe</first_name>
<last_name>Hart</last_name>
</user>
<user active="0">
<first_name>Oliver</first_name>
<last_name>Kahn</last_name>
</user>
</users>
*/
--/
CREATE PYTHON SCALAR SCRIPT process_users(url VARCHAR(500))
EMITS (firstname VARCHAR(20), lastname VARCHAR(20)) AS
import re
import xml.etree.cElementTree as etree
from urllib2 import urlopen
def run(ctx):
content = etree.parse(urlopen(ctx.url))
for user in content.findall('user/[@active="1"]'):
ctx.emit(user.find('first_name').text, user.find('last_name').text)
/
SELECT process_users ('http://www.my_valid_webserver/goalies.xml')
FROM DUAL;
Adapter Script Callback Function
For virtual schemas an adapter script must define the function adapter_call(request_json)
. The parameter is a string in JSON containing the Virtual Schema API request. The return value must also be a string in JSON containing the response. For the Virtual Schema API Documentation, see Information for Developers. The callback function will be executed only on a single node.