SQL functions

sqx is SQLite plus extra functions for URLs, HTTP, HTML, DNS, regular expressions and hashing. Every built-in SQLite function, including the JSON functions, works too.

URL functions

These parse URLs locally and never touch the network.

FunctionReturnsExample
url_host(url)
url_domain(url)
Lowercase hostname'https://Sub.Example.com:8080/a' → sub.example.com
url_scheme(url)Scheme'https://example.com' → https
url_path(url)Path (/ if empty)'https://example.com/blog?x=1' → /blog
url_query(url, key)One query parameter('https://x.com/?id=42', 'id') → 42
url_ext(url)Lowercase file extension'https://cdn.com/app.min.js?v=2' → .js
url_norm(url)Lowercase host, no fragment, sorted query'https://example.com/?b=2&a=1#top' → https://example.com/?a=1&b=2
url_join(base, ref)Resolved URL('https://a.com/dir/page.html', '../img.png') → https://a.com/img.png
sqx "SELECT url_host('https://Sub.Example.com:8080/a') AS host,
            url_norm('https://example.com/?b=2&a=1#top') AS normalized"

HTTP functions

Each URL is fetched once and stored in the cache, however many of these functions you call.

FunctionReturns
http_status(url)Status code, or 0 on a network error
http_body(url)Response body
http_header(url, name)One header (case-insensitive)
http_headers_json(url)All headers as a JSON object, names in lowercase
http_time_ms(url)Fetch duration in milliseconds
http_final_url(url)URL after redirects
http_size(url)Body size in bytes
http_error(url)Error message, or NULL

Check security headers for a list of sites:

cat urls.txt | sqx -i lines "
  SELECT url, http_header(url, 'strict-transport-security') AS hsts,
         http_header(url, 'content-security-policy') IS NOT NULL AS has_csp
  FROM stdin"

HTML functions

Each function accepts either a URL, fetched through the cache, or a raw HTML string.

FunctionReturns
html_title(x)Text of <title>
html_meta(x, key)content of <meta name|property|http-equiv="key">
html_text(x)Plain text without tags, scripts, styles, noscript, comments or the doctype
html_links(x)JSON array of <a href> links. Made absolute when x is a URL; #anchors and javascript: links are skipped.
html_tag_texts(x, tag)JSON array of the inner text of every <tag>
html_attr(x, tag, attr)JSON array of attribute values

Combine the JSON-array functions with json_each(...) to get one row per item, or json_array_length(...) to count them:

sqx -f pages=data/pages.jsonl "
  SELECT url_path(url) AS page,
         json_array_length(html_attr(html, 'img', 'src')) AS images,
         json_array_length(html_attr(html, 'img', 'alt')) AS with_alt
  FROM pages"

DNS and network functions

FunctionReturns
dns_ip(host_or_url)First resolved address
dns_ips_json(host_or_url)JSON array of every address
dns_txt_json(host_or_url)JSON array of TXT records (SPF, DMARC, verification tokens)
ip_is_private(ip)1 for loopback, private or link-local addresses, otherwise 0
cat data/urls.txt | sqx -i lines "
  SELECT url_host(url) AS host, dns_ip(url) AS ip, ip_is_private(dns_ip(url)) AS is_private
  FROM stdin GROUP BY host"

Regular expressions

Patterns use Go RE2 syntax. Add (?i) at the start for a case-insensitive match.

FunctionReturns
text REGEXP pattern1 on a match
re_match(text, pattern)Same as REGEXP, written as a function
re_extract(text, pattern, group)One capture group (0 is the whole match)
re_extract_all(text, pattern)JSON array of every match
cat data/access_log.csv | sqx "
  SELECT ts, client_ip, request_url FROM stdin
  WHERE request_url REGEXP '(?i)(\.\./|%27|/etc/passwd)'"

Hashing and entropy

FunctionReturns
sha256(text)Hex SHA-256 digest
shannon_entropy(text)Entropy in bits per character. Random tokens and secrets score high.
sqx -f pages=data/pages.jsonl "
  SELECT substr(sha256(html_text(html)), 1, 12) AS content_hash, COUNT(*) AS pages,
         GROUP_CONCAT(url_path(url), ' | ') AS urls
  FROM pages GROUP BY content_hash HAVING pages > 1"