Search Shortcut cmd + k | ctrl + k
duckfn_open

Wrap Rust's open and sanitize-filename crates for SQL — open a path, a URL or the output of a query with your desktop's own program, and turn a string into a usable file name

Maintainer(s): shijianjs

Compatibility Matrix

This table covers all DuckDB releases that have not reached end-of-life, including LTS releases, and shows the 2 most recent patch releases of each. Older patch releases are not listed, but the extension may be available for them as well.

Platform 1.4.4 1.4.5 1.5.5 1.5.6 2.0.0
Linux AMD64
Linux ARM64
macOS AMD64
macOS ARM64
Windows AMD64
Windows ARM64
WebAssembly

Installing and Loading

INSTALL duckfn_open FROM community;
LOAD duckfn_open;

Example

-- What would open this on the machine running the query, without opening anything
SELECT op_open_commands('report.html');

-- Hand an existing path to the desktop's default program
SELECT op_open_path('./report.html');

-- Most query output is content, not a path: write it out and get the path back
SELECT op_write_file('{"a":1}', 'json');

About duckfn_open

duckfn_open wraps two mature Rust crates for SQL: open, which hands a path or a URL to whatever the desktop is configured to open it with, and sanitize-filename, whose file-name rules it puts on SQL. Nine scalar function names, in three groups:

  • Paths and URLs — op_open_path(path[, app]) and op_open_path_detached(path[, app]) hand an existing file or link to the desktop, waiting for it or not; op_open_commands(path) lists the commands a launch would try, running none of them.
  • Content — op_write_file(content|blob, ext[, opts]) writes text or bytes to a file and returns the path, starting nothing; op_open_content(…) writes it and opens it in the same call; op_open_html(content) is the zero-configuration version of that; op_print(text[, tail]) writes to stdout, past the result set's column formatting, and hands the text back. The optional third argument is the registered op_write_file_options STRUCT (dir, name, part).
  • File names — op_sanitize_filename(value[, replacement[, truncate, windows]]) rewrites a value into a usable name, and op_sanitize_filename_check(…) answers whether it already is one. This pair starts no process and touches no file, so a name taken from the data can be checked before anything is built on it.

It is aimed at interactive work: a person is at the machine, running DuckDB, and wants the result in front of them. A launcher is desktop software, so DuckDB embedded in an application is outside what this is for — on a machine with no desktop only the half that starts no process is of any use. The extension starts a process and writes the file you ask for (into the directory you name, or the system temp directory); it reads no configuration, opens no network connection of its own, and does not inspect the content it passes along. Requires DuckDB 1.3 or newer.

Documentation: user guide · function reference · source and development notes.

Added Functions

function_name function_type description comment examples
op_open_commands scalar Lists, without running them, the launcher commands op_open_path would try in order The read-only pair of op_open_path: nothing is started, so the result is stable enough to assert in CI on a headless runner [SELECT op_open_commands('report.html')]
op_open_content scalar Writes a piece of text or binary content out to a file and opens it with the program the desktop is configured to use op_write_file plus op_open_path in one call: it returns the path it wrote, so a launcher that cannot start is reported as an error carrying that path instead of a lost false; it does not wait for the program to exit, and the BLOB signatures write the bytes exactly as they are [SELECT op_open_content(logo_blob, 'png'); SELECT op_open_content(read_text('report.html'), 'html'); SELECT op_open_content(svg, 'svg', {'dir':'./out', 'part':'sales'})]
op_open_html scalar Writes an HTML string to a temporary file and opens it in the system default browser, returning the file path The zero-configuration shortcut: op_open_content(content, 'html') with every option left at its default (system temp directory, generated name); needs no configuration because SQL renders HTML [SELECT op_open_html('<h1>hello</h1>'); SELECT op_open_html(read_text('report.html'))]
op_open_path scalar Opens a local path or URL with the program the desktop is configured to use true once a launcher has been started, false when none could be started; NULL input short-circuits to NULL and starts nothing [SELECT op_open_path('report.html'); SELECT op_open_path('https://duckdb.org', 'firefox')]
op_open_path_detached scalar Opens a local path or URL and returns as soon as the launcher is started The non-blocking pair of op_open_path: true means a process was started, not that the target opened [SELECT op_open_path_detached('https://duckdb.org')]
op_print scalar Prints a piece of text straight to stdout, bypassing the result set's column formatting For character art and other line-aligned output the CLI would otherwise wrap: the text is written to stdout as it is and handed back unchanged, so the call can stay in a projection; the optional second argument is what follows the text instead of the default newline [SELECT op_print(report, chr(10) || chr(10)); SELECT op_print(tp_qr('https://query.farm')); SELECT op_print('rows: ' || count(*)) FROM t]
op_sanitize_filename scalar Rewrites a string into something usable as a file name, following sanitize-filename The crate's own defaults: the illegal characters are deleted (pass a replacement to see where they were), the 255-byte cut is applied, and the Windows rules are on because a query should answer the same way on every machine [SELECT op_sanitize_filename('quarterly/report:v2.csv'); SELECT op_sanitize_filename(report_title, '_')]
op_sanitize_filename_check scalar Tells whether a string is already usable as a file name, rewriting nothing The predicate half of sanitize-filename: true means op_sanitize_filename with the same options would answer with the value it was given [SELECT count(*) FROM files WHERE NOT op_sanitize_filename_check(name)]
op_write_file scalar Writes a piece of text or binary content out to a file and returns its path, with the extension required and the directory, file name and name segment optional Starts nothing: hand the returned path to op_open_path to open it. dir defaults to the system temp directory (created when missing), name defaults to a generated open- [SELECT op_write_file(read_blob('logo.png'), 'png'); SELECT op_write_file('{"a":1}', 'json'); SELECT op_write_file(svg, 'svg', {'dir':'./out', 'part':'sales'})]

Overloaded Functions

This extension does not add any function overloads.

Added Types

type_name type_size logical_type type_category internal
op_write_file_options 0 STRUCT COMPOSITE false

Added Settings

name description input_type scope aliases typed_value
temp_directory Set the directory to which temporary files are written. VARCHAR GLOBAL [] post.db.tmp