Executable and ExecutablePool Table Engines
The Executable and ExecutablePool table engines allow you to define a table whose rows are generated from a script that you define (by writing rows to stdout). The executable script is stored in the users_scripts directory and can read data from any source.
Executabletables: the script is run on every queryExecutablePooltables: maintains a pool of persistent processes, and takes processes from the pool for reads
You can optionally include one or more input queries that stream their results to stdin for the script to read.
Creating an Executable Table
The Executable table engine requires two parameters: the name of the script and the format of the incoming data. You can optionally pass in one or more input queries:
Here are the relevant settings for an Executable table:
send_chunk_header- Description: Send the number of rows in each chunk before sending a chunk to process. This setting can help to write your script in a more efficient way to preallocate some resources
- Default value: false
command_termination_timeout- Description: Command termination timeout in seconds
- Default value: 10
command_read_timeout- Description: Timeout for reading data from command stdout in milliseconds
- Default value: 10000
command_write_timeout- Description: Timeout for writing data to command stdin in milliseconds
- Default value: 10000
Let's look at an example. The following Python script is named my_script.py and is saved in the user_scripts folder. It reads in a number i and prints i random strings, with each string preceded by a number that is separated by a tab:
The following my_executable_table is built from the output of my_script.py, which will generate 10 random strings every time you run a SELECT from my_executable_table:
Creating the table returns immediately and does not invoke the script. Querying my_executable_table causes the script to be invoked:
Passing Query Results to a Script
Users of the Hacker News website leave comments. Python contains a natural language processing toolkit (nltk) with a SentimentIntensityAnalyzer for determining if comments are positive, negative, or neutral - including assigning a value between -1 (a very negative comment) and 1 (a very positive comment). Let's create an Executable table that computes the sentiment of Hacker News comments using nltk.
This example uses the hackernews table described here. The hackernews table includes an id column of type UInt64 and a String column named comment. Let's start by defining the Executable table:
Some comments about the sentiment table:
- The file
sentiment.pyis saved in theuser_scriptsfolder (the default folder of theuser_scripts_pathsetting) - The
TabSeparatedformat means our Python script needs to generate rows of raw data that contain tab-separated values - The query selects two columns from
hackernews. The Python script will need to parse out those column values from the incoming rows
Here is the definition of sentiment.py:
Some comments about our Python script:
- For this to work, you will need to run
nltk.downloader.download('vader_lexicon'). This could have been placed in the script, but then it would have been downloaded every time a query was executed on thesentimenttable - which is not efficient - Each value of
rowis going to be a row in the result set ofSELECT id, comment FROM hackernews WHERE id > 0 AND comment != '' LIMIT 20 - The incoming row is tab-separated, so we parse out the
idandcommentusing the Pythonsplitfunction - The result of
polarity_scoresis a JSON object with a handful of values. We decided to just grab thecompoundvalue of this JSON object - Recall that the
sentimenttable in ClickHouse uses theTabSeparatedformat and contains two columns, so ourprintfunction separates those columns with a tab
Every time you write a query that selects rows from the sentiment table, the SELECT id, comment FROM hackernews WHERE id > 0 AND comment != '' LIMIT 20 query is executed and the result is passed to sentiment.py. Let's test it out:
The response looks like:
Creating an ExecutablePool Table
The syntax for ExecutablePool is similar to Executable, but there are a couple of relevant settings unique to an ExecutablePool table:
pool_size- Description: Processes pool size. If size is 0, then there are no size restrictions
- Default value: 16
max_command_execution_time- Description: Max command execution time in seconds
- Default value: 10
We can easily convert the sentiment table above to use ExecutablePool instead of Executable:
ClickHouse will maintain 4 processes on-demand when your client queries the sentiment_pooled table.