├── VERSION ├── .dockerignore ├── python ├── sqlite_html │ ├── noop.c │ ├── sqlite_html │ │ ├── version.py │ │ └── __init__.py │ ├── setup.py │ └── README.md ├── version.py.tmpl ├── datasette_sqlite_html │ ├── datasette_sqlite_html │ │ ├── version.py │ │ └── __init__.py │ ├── README.md │ ├── tests │ │ └── test_sqlite_html.py │ └── setup.py ├── README.md └── .gitignore ├── bindings ├── ruby │ ├── .gitignore │ ├── Rakefile │ ├── lib │ │ ├── version.rb │ │ ├── version.rb.tmpl │ │ └── sqlite_html.rb │ ├── Gemfile │ └── sqlite_html.gemspec └── sqlite-utils │ ├── sqlite_utils_sqlite_html │ ├── version.py │ ├── version.py.tmpl │ └── __init__.py │ ├── README.md │ ├── pyproject.toml │ ├── pyproject.toml.tmpl │ └── .gitignore ├── npm ├── sqlite-html-linux-x64 │ ├── lib │ │ └── .gitkeep │ ├── package.json │ └── README.md ├── sqlite-html-darwin-arm64 │ ├── lib │ │ └── .gitkeep │ ├── package.json │ └── README.md ├── sqlite-html-darwin-x64 │ ├── lib │ │ └── .gitkeep │ ├── package.json │ └── README.md ├── sqlite-html-windows-x64 │ ├── lib │ │ └── .gitkeep │ ├── package.json │ └── README.md ├── README.md ├── platform-package.package.json.tmpl ├── platform-package.README.md.tmpl ├── sqlite-html │ ├── package.json │ ├── package.json.tmpl │ ├── test.js │ ├── src │ │ └── index.js │ └── README.md └── .gitignore ├── .gitattributes ├── .gitignore ├── sqlite ├── sqlite-html.h ├── core_init.c └── sqlite3ext.h ├── Dockerfile ├── deno ├── deno.json ├── deno.json.tmpl ├── test.ts ├── README.md ├── README.md.tmpl ├── mod.ts └── deno.lock ├── scripts ├── deno_generate_package.sh ├── publish_release.sh ├── npm_generate_platform_packages.sh └── rename-wheels.py ├── shared.c ├── tests ├── test-python.py ├── test-sqlite3.py └── test-loadable.py ├── go.mod ├── examples └── datasette │ ├── fly.toml │ └── Dockerfile ├── LICENSE ├── .github └── workflows │ ├── upload-deno-assets.js │ ├── test.yaml │ └── release.yaml ├── shared.go ├── meta.go ├── attrs.go ├── utils.go ├── go.sum ├── Makefile ├── query.go ├── elements.go ├── README.md └── docs.md /VERSION: -------------------------------------------------------------------------------- 1 | 0.1.3 -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | dist/ -------------------------------------------------------------------------------- /python/sqlite_html/noop.c: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /bindings/ruby/.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | -------------------------------------------------------------------------------- /npm/sqlite-html-linux-x64/lib/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | sqlite/* linguist-vendored -------------------------------------------------------------------------------- /npm/sqlite-html-darwin-arm64/lib/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /npm/sqlite-html-darwin-x64/lib/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /npm/sqlite-html-windows-x64/lib/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | tests/data 2 | dist/ 3 | .vscode 4 | *.dylib 5 | *.so 6 | *.dll -------------------------------------------------------------------------------- /bindings/ruby/Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" 2 | task :default => :spec 3 | -------------------------------------------------------------------------------- /python/version.py.tmpl: -------------------------------------------------------------------------------- 1 | __version__ = "${VERSION}" 2 | __version_info__ = tuple(__version__.split(".")) 3 | -------------------------------------------------------------------------------- /sqlite/sqlite-html.h: -------------------------------------------------------------------------------- 1 | int sqlite3_html_init(sqlite3 *db, char **pzErrMsg, const sqlite3_api_routines *pApi); 2 | -------------------------------------------------------------------------------- /python/sqlite_html/sqlite_html/version.py: -------------------------------------------------------------------------------- 1 | __version__ = "0.1.3" 2 | __version_info__ = tuple(__version__.split(".")) 3 | -------------------------------------------------------------------------------- /bindings/ruby/lib/version.rb: -------------------------------------------------------------------------------- 1 | # automatically generated, do not edit by hand. 2 | module SqliteHtml 3 | VERSION = "0.1.3" 4 | end 5 | -------------------------------------------------------------------------------- /bindings/sqlite-utils/sqlite_utils_sqlite_html/version.py: -------------------------------------------------------------------------------- 1 | __version__ = "0.1.3" 2 | __version_info__ = tuple(__version__.split(".")) 3 | -------------------------------------------------------------------------------- /bindings/sqlite-utils/README.md: -------------------------------------------------------------------------------- 1 | # `sqlite-utils-sqlite-html` 2 | 3 | A `sqlite-utils` plugin that registers the `sqlite-html` extension. 4 | -------------------------------------------------------------------------------- /python/datasette_sqlite_html/datasette_sqlite_html/version.py: -------------------------------------------------------------------------------- 1 | __version__ = "0.1.3" 2 | __version_info__ = tuple(__version__.split(".")) 3 | -------------------------------------------------------------------------------- /bindings/ruby/lib/version.rb.tmpl: -------------------------------------------------------------------------------- 1 | # automatically generated, do not edit by hand. 2 | module SqliteHtml 3 | VERSION = "${VERSION}" 4 | end 5 | -------------------------------------------------------------------------------- /bindings/sqlite-utils/sqlite_utils_sqlite_html/version.py.tmpl: -------------------------------------------------------------------------------- 1 | __version__ = "${VERSION}" 2 | __version_info__ = tuple(__version__.split(".")) 3 | -------------------------------------------------------------------------------- /bindings/ruby/Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | git_source(:github) {|repo_name| "https://github.com/#{repo_name}" } 4 | 5 | gemspec 6 | -------------------------------------------------------------------------------- /sqlite/core_init.c: -------------------------------------------------------------------------------- 1 | #include "sqlite-html.h" 2 | int core_init(const char *dummy) { 3 | return sqlite3_auto_extension((void *) sqlite3_html_init); 4 | } 5 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.17-buster 2 | RUN apt-get update && \ 3 | apt-get install -y \ 4 | build-essential gcc libc6-dev-i386 gcc-8-arm-linux-gnueabi gcc-arm-linux-gnueabi -------------------------------------------------------------------------------- /deno/deno.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sqlite-html", 3 | "version": "0.1.3", 4 | "github": "https://github.com/asg017/sqlite-html", 5 | "tasks": { 6 | "test": "deno test --unstable -A test.ts" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /deno/deno.json.tmpl: -------------------------------------------------------------------------------- 1 | { 2 | "name": "${PACKAGE_NAME}", 3 | "version": "${VERSION}", 4 | "github": "https://github.com/asg017/${PACKAGE_NAME}", 5 | "tasks": { 6 | "test": "deno test --unstable -A test.ts" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /bindings/ruby/lib/sqlite_html.rb: -------------------------------------------------------------------------------- 1 | require "version" 2 | 3 | module SqliteHtml 4 | class Error < StandardError; end 5 | def self.html_loadable_path 6 | File.expand_path('../html0', __FILE__) 7 | end 8 | def self.load(db) 9 | db.load_extension(self.html_loadable_path) 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /python/datasette_sqlite_html/datasette_sqlite_html/__init__.py: -------------------------------------------------------------------------------- 1 | from datasette import hookimpl 2 | import sqlite_html 3 | 4 | from datasette_sqlite_html.version import __version_info__, __version__ 5 | 6 | @hookimpl 7 | def prepare_connection(conn): 8 | conn.enable_load_extension(True) 9 | sqlite_html.load(conn) 10 | conn.enable_load_extension(False) -------------------------------------------------------------------------------- /bindings/sqlite-utils/sqlite_utils_sqlite_html/__init__.py: -------------------------------------------------------------------------------- 1 | from sqlite_utils import hookimpl 2 | import sqlite_html 3 | 4 | from sqlite_utils_sqlite_html.version import __version_info__, __version__ 5 | 6 | 7 | @hookimpl 8 | def prepare_connection(conn): 9 | conn.enable_load_extension(True) 10 | sqlite_html.load(conn) 11 | conn.enable_load_extension(False) 12 | -------------------------------------------------------------------------------- /scripts/deno_generate_package.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euo pipefail 4 | 5 | export PACKAGE_NAME="sqlite-html" 6 | export EXTENSION_NAME="html0" 7 | export VERSION=$(cat VERSION) 8 | 9 | envsubst < deno/deno.json.tmpl > deno/deno.json 10 | echo "✅ generated deno/deno.json" 11 | 12 | envsubst < deno/README.md.tmpl > deno/README.md 13 | echo "✅ generated deno/README.md" 14 | -------------------------------------------------------------------------------- /python/sqlite_html/sqlite_html/__init__.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sqlite3 3 | 4 | from sqlite_html.version import __version_info__, __version__ 5 | 6 | def loadable_path(): 7 | loadable_path = os.path.join(os.path.dirname(__file__), "html0") 8 | return os.path.normpath(loadable_path) 9 | 10 | def load(conn: sqlite3.Connection) -> None: 11 | conn.load_extension(loadable_path()) 12 | -------------------------------------------------------------------------------- /shared.c: -------------------------------------------------------------------------------- 1 | #include "sqlite3ext.h" 2 | 3 | SQLITE_EXTENSION_INIT3 4 | 5 | extern int go_sqlite3_extension_init(const char*, sqlite3*, char**); 6 | 7 | #ifdef _WIN32 8 | __declspec(dllexport) 9 | #endif 10 | int sqlite3_html_init(sqlite3* db, char** pzErrMsg, const sqlite3_api_routines *pApi) { 11 | SQLITE_EXTENSION_INIT2(pApi) 12 | return go_sqlite3_extension_init("html", db, pzErrMsg); 13 | } -------------------------------------------------------------------------------- /npm/README.md: -------------------------------------------------------------------------------- 1 | # sqlite-html on npm 2 | 3 | `sqlite-html` is also available for download through [`npm`](https://www.npmjs.com/) for Node.js developers. See the [`sqlite-html` NPM package README](./sqlite-html/README.md) for details. 4 | 5 | The other NPM packages in this folder (`sqlite-html-darwin-x64`, `sqlite-html-windows-x64` etc.) are autogenerated platform-specific packages. See [Supported Platforms](./sqlite-html/README.md#supported-platforms) for details. 6 | -------------------------------------------------------------------------------- /npm/sqlite-html-darwin-x64/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "//": "Autogenerated by the npm_generate_platform_packages.sh script, do not edit by hand", 3 | "name": "sqlite-html-darwin-x64", 4 | "version": "0.1.3", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/asg017/sqlite-html.git", 8 | "directory": "npm/sqlite-html-darwin-x64" 9 | }, 10 | "author": "Alex Garcia ", 11 | "os": [ 12 | "darwin" 13 | ], 14 | "cpu": [ 15 | "x64" 16 | ] 17 | } -------------------------------------------------------------------------------- /npm/sqlite-html-linux-x64/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "//": "Autogenerated by the npm_generate_platform_packages.sh script, do not edit by hand", 3 | "name": "sqlite-html-linux-x64", 4 | "version": "0.1.3", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/asg017/sqlite-html.git", 8 | "directory": "npm/sqlite-html-linux-x64" 9 | }, 10 | "author": "Alex Garcia ", 11 | "os": [ 12 | "linux" 13 | ], 14 | "cpu": [ 15 | "x64" 16 | ] 17 | } -------------------------------------------------------------------------------- /tests/test-python.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import sqlite3 3 | import sqlite_html 4 | 5 | class TestSqliteHtmlPython(unittest.TestCase): 6 | def test_path(self): 7 | db = sqlite3.connect(':memory:') 8 | db.enable_load_extension(True) 9 | 10 | self.assertEqual(type(sqlite_html.loadable_path()), str) 11 | 12 | sqlite_html.load(db) 13 | version, = db.execute('select html_version()').fetchone() 14 | self.assertEqual(version[0], "v") 15 | 16 | if __name__ == '__main__': 17 | unittest.main() -------------------------------------------------------------------------------- /npm/sqlite-html-windows-x64/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "//": "Autogenerated by the npm_generate_platform_packages.sh script, do not edit by hand", 3 | "name": "sqlite-html-windows-x64", 4 | "version": "0.1.3", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/asg017/sqlite-html.git", 8 | "directory": "npm/sqlite-html-windows-x64" 9 | }, 10 | "author": "Alex Garcia ", 11 | "os": [ 12 | "windows" 13 | ], 14 | "cpu": [ 15 | "x64" 16 | ] 17 | } -------------------------------------------------------------------------------- /npm/sqlite-html-darwin-arm64/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "//": "Autogenerated by the npm_generate_platform_packages.sh script, do not edit by hand", 3 | "name": "sqlite-html-darwin-arm64", 4 | "version": "0.1.3", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/asg017/sqlite-html.git", 8 | "directory": "npm/sqlite-html-darwin-arm64" 9 | }, 10 | "author": "Alex Garcia ", 11 | "os": [ 12 | "darwin" 13 | ], 14 | "cpu": [ 15 | "arm64" 16 | ] 17 | } -------------------------------------------------------------------------------- /npm/platform-package.package.json.tmpl: -------------------------------------------------------------------------------- 1 | { 2 | "//": "Autogenerated by the npm_generate_platform_packages.sh script, do not edit by hand", 3 | "name": "${PACKAGE_NAME}", 4 | "version": "${VERSION}", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/asg017/${PACKAGE_NAME_BASE}.git", 8 | "directory": "npm/${PACKAGE_NAME}" 9 | }, 10 | "author": "Alex Garcia ", 11 | "os": [ 12 | "${PLATFORM_OS}" 13 | ], 14 | "cpu": [ 15 | "${PLATFORM_ARCH}" 16 | ] 17 | } -------------------------------------------------------------------------------- /python/README.md: -------------------------------------------------------------------------------- 1 | # `sqlite-html` Python Packages 2 | 3 | The `sqlite-html` project offers two python packages for easy distribution. They are: 4 | 5 | 1. The [`sqlite-html` Python package](https://pypi.org/project/sqlite-html/), source in [`sqlite_html/`](./sqlite_html/README.md) 6 | 2. The [`datasette-sqlite-html` Python package](https://pypi.org/project/sqlite-html/), a [Datasette](https://datasette.io/) plugin,which is a light wrapper around the `sqlite-html` package, source in [`datasette_sqlite_html/`](./datasette_sqlite_html/README.md) 7 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/asg017/sqlite-html 2 | 3 | go 1.17 4 | 5 | require ( 6 | github.com/PuerkitoBio/goquery v1.7.1 7 | github.com/augmentable-dev/vtab v0.0.0-20210818144031-5c7659b723dd 8 | go.riyazali.net/sqlite v0.0.0-20230320080028-80a51d3944c0 9 | golang.org/x/net v0.0.0-20210614182718-04defd469f4e 10 | ) 11 | 12 | require ( 13 | github.com/andybalholm/cascadia v1.2.0 // indirect 14 | github.com/mattn/go-pointer v0.0.1 // indirect 15 | ) 16 | 17 | replace github.com/augmentable-dev/vtab => github.com/asg017/vtab v0.0.0-20230324234256-713839099704 18 | -------------------------------------------------------------------------------- /bindings/sqlite-utils/pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "sqlite-utils-sqlite-html" 3 | version = "0.1.3" 4 | description = "TODO" 5 | readme = "README.md" 6 | authors = [{name = "Alex Garcia"}] 7 | license = {text = "Apache-2.0"} 8 | classifiers = [] 9 | 10 | dependencies = [ 11 | "sqlite-utils", 12 | "sqlite-html" 13 | ] 14 | 15 | [project.urls] 16 | Homepage = "https://github.com/asg017/sqlite-html" 17 | Changelog = "https://github.com/asg017/sqlite-html/releases" 18 | Issues = "https://github.com/asg017/sqlite-html/issues" 19 | 20 | [project.entry-points.sqlite_utils] 21 | sqlite_html = "sqlite_utils_sqlite_html" 22 | -------------------------------------------------------------------------------- /bindings/sqlite-utils/pyproject.toml.tmpl: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "sqlite-utils-sqlite-html" 3 | version = "${VERSION}" 4 | description = "TODO" 5 | readme = "README.md" 6 | authors = [{name = "Alex Garcia"}] 7 | license = {text = "Apache-2.0"} 8 | classifiers = [] 9 | 10 | dependencies = [ 11 | "sqlite-utils", 12 | "sqlite-html" 13 | ] 14 | 15 | [project.urls] 16 | Homepage = "https://github.com/asg017/sqlite-html" 17 | Changelog = "https://github.com/asg017/sqlite-html/releases" 18 | Issues = "https://github.com/asg017/sqlite-html/issues" 19 | 20 | [project.entry-points.sqlite_utils] 21 | sqlite_html = "sqlite_utils_sqlite_html" 22 | -------------------------------------------------------------------------------- /deno/test.ts: -------------------------------------------------------------------------------- 1 | import * as sqlite_html from "./mod.ts"; 2 | import meta from "./deno.json" assert { type: "json" }; 3 | 4 | import { assertEquals } from "https://deno.land/std@0.177.0/testing/asserts.ts"; 5 | import { Database } from "https://deno.land/x/sqlite3@0.8.0/mod.ts"; 6 | 7 | Deno.test("x/sqlite3", (t) => { 8 | const db = new Database(":memory:"); 9 | 10 | db.enableLoadExtension = true; 11 | sqlite_html.load(db); 12 | 13 | const [version] = db.prepare("select html_version()").value<[string]>()!; 14 | 15 | assertEquals(version[0], "v"); 16 | assertEquals(version.substring(1), meta.version); 17 | 18 | db.close(); 19 | }); 20 | -------------------------------------------------------------------------------- /npm/sqlite-html-linux-x64/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # sqlite-html-linux-x64 4 | 5 | A `sqlite-html` platform-specific package for `linux-x64`. 6 | 7 | When `sqlite-html` is installed and the host computer has a `linux` operating system with `x64` architecture, then this package is downloaded with the pre-compiled SQLite extension bundled under `lib/html0.so`. At runtime, the `sqlite-html` package will resolve to this platform-specific package for use with [`better-sqlite3`](https://github.com/WiseLibs/better-sqlite3)' or [`node-sqlite3`](https://github.com/TryGhost/node-sqlite3). 8 | 9 | See the `sqlite-html` package for more details. -------------------------------------------------------------------------------- /npm/sqlite-html-darwin-x64/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # sqlite-html-darwin-x64 4 | 5 | A `sqlite-html` platform-specific package for `darwin-x64`. 6 | 7 | When `sqlite-html` is installed and the host computer has a `darwin` operating system with `x64` architecture, then this package is downloaded with the pre-compiled SQLite extension bundled under `lib/html0.dylib`. At runtime, the `sqlite-html` package will resolve to this platform-specific package for use with [`better-sqlite3`](https://github.com/WiseLibs/better-sqlite3)' or [`node-sqlite3`](https://github.com/TryGhost/node-sqlite3). 8 | 9 | See the `sqlite-html` package for more details. -------------------------------------------------------------------------------- /npm/sqlite-html-windows-x64/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # sqlite-html-windows-x64 4 | 5 | A `sqlite-html` platform-specific package for `windows-x64`. 6 | 7 | When `sqlite-html` is installed and the host computer has a `windows` operating system with `x64` architecture, then this package is downloaded with the pre-compiled SQLite extension bundled under `lib/html0.dll`. At runtime, the `sqlite-html` package will resolve to this platform-specific package for use with [`better-sqlite3`](https://github.com/WiseLibs/better-sqlite3)' or [`node-sqlite3`](https://github.com/TryGhost/node-sqlite3). 8 | 9 | See the `sqlite-html` package for more details. -------------------------------------------------------------------------------- /npm/sqlite-html-darwin-arm64/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # sqlite-html-darwin-arm64 4 | 5 | A `sqlite-html` platform-specific package for `darwin-arm64`. 6 | 7 | When `sqlite-html` is installed and the host computer has a `darwin` operating system with `arm64` architecture, then this package is downloaded with the pre-compiled SQLite extension bundled under `lib/html0.dylib`. At runtime, the `sqlite-html` package will resolve to this platform-specific package for use with [`better-sqlite3`](https://github.com/WiseLibs/better-sqlite3)' or [`node-sqlite3`](https://github.com/TryGhost/node-sqlite3). 8 | 9 | See the `sqlite-html` package for more details. -------------------------------------------------------------------------------- /scripts/publish_release.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euo pipefail xtrace 4 | 5 | if [[ -n $(git status --porcelain | grep -v VERSION) ]]; then 6 | echo "❌ There are other un-staged changes to the repository besides VERSION" 7 | exit 1 8 | fi 9 | 10 | VERSION="$(cat VERSION)" 11 | 12 | echo "Publishing version v$VERSION..." 13 | 14 | make version 15 | git add --all 16 | git commit -m "v$VERSION" 17 | git tag v$VERSION 18 | git push origin main v$VERSION 19 | 20 | if grep -qE "alpha|beta" VERSION; then 21 | gh release create v$VERSION --title=v$VERSION --prerelease --notes="" 22 | else 23 | gh release create v$VERSION --title=v$VERSION 24 | fi 25 | 26 | 27 | echo "✅ Published! version v$VERSION" 28 | -------------------------------------------------------------------------------- /python/datasette_sqlite_html/README.md: -------------------------------------------------------------------------------- 1 | # The `datasette-sqlite-html` Datasette Plugin 2 | 3 | `datasette-sqlite-html` is a [Datasette plugin](https://docs.datasette.io/en/stable/plugins.html) that loads the [`sqlite-html`](https://github.com/asg017/sqlite-html) extension in Datasette instances, allowing you to generate and work with [TODO](https://github.com/html/spec) in SQL. 4 | 5 | ``` 6 | datasette install datasette-sqlite-html 7 | ``` 8 | 9 | See [`docs.md`](../../docs.md) for a full API reference for the html SQL functions. 10 | 11 | Alternatively, when publishing Datasette instances, you can use the `--install` option to install the plugin. 12 | 13 | ``` 14 | datasette publish cloudrun data.db --service=my-service --install=datasette-sqlite-html 15 | 16 | ``` 17 | -------------------------------------------------------------------------------- /python/datasette_sqlite_html/tests/test_sqlite_html.py: -------------------------------------------------------------------------------- 1 | from datasette.app import Datasette 2 | import pytest 3 | 4 | 5 | @pytest.mark.asyncio 6 | async def test_plugin_is_installed(): 7 | datasette = Datasette(memory=True) 8 | response = await datasette.client.get("/-/plugins.json") 9 | assert response.status_code == 200 10 | installed_plugins = {p["name"] for p in response.json()} 11 | assert "datasette-sqlite-html" in installed_plugins 12 | 13 | @pytest.mark.asyncio 14 | async def test_sqlite_html_functions(): 15 | datasette = Datasette(memory=True) 16 | response = await datasette.client.get("/_memory.json?sql=select+html_version()") 17 | assert response.status_code == 200 18 | html_version, = response.json()["rows"][0] 19 | assert html_version[0] == "v" -------------------------------------------------------------------------------- /npm/platform-package.README.md.tmpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | # ${PACKAGE_NAME} 4 | 5 | A `${PACKAGE_NAME_BASE}` platform-specific package for `${PLATFORM_OS}-${PLATFORM_ARCH}`. 6 | 7 | When `${PACKAGE_NAME_BASE}` is installed and the host computer has a `${PLATFORM_OS}` operating system with `${PLATFORM_ARCH}` architecture, then this package is downloaded with the pre-compiled SQLite extension bundled under `lib/${EXTENSION_NAME}.${EXTENSION_SUFFIX}`. At runtime, the `${PACKAGE_NAME_BASE}` package will resolve to this platform-specific package for use with [`better-sqlite3`](https://github.com/WiseLibs/better-sqlite3)' or [`node-sqlite3`](https://github.com/TryGhost/node-sqlite3). 8 | 9 | See the `${PACKAGE_NAME_BASE}` package for more details. -------------------------------------------------------------------------------- /examples/datasette/fly.toml: -------------------------------------------------------------------------------- 1 | # fly.toml file generated for sqlite-html-demo on 2022-08-02T12:15:31-07:00 2 | 3 | app = "sqlite-html-demo" 4 | 5 | kill_signal = "SIGINT" 6 | kill_timeout = 5 7 | processes = [] 8 | 9 | [env] 10 | 11 | [experimental] 12 | allowed_public_ports = [] 13 | auto_rollback = true 14 | 15 | [[services]] 16 | http_checks = [] 17 | internal_port = 8080 18 | processes = ["app"] 19 | protocol = "tcp" 20 | script_checks = [] 21 | 22 | [services.concurrency] 23 | hard_limit = 25 24 | soft_limit = 20 25 | type = "connections" 26 | 27 | [[services.ports]] 28 | force_https = true 29 | handlers = ["http"] 30 | port = 80 31 | 32 | [[services.ports]] 33 | handlers = ["tls", "http"] 34 | port = 443 35 | 36 | [[services.tcp_checks]] 37 | grace_period = "1s" 38 | interval = "15s" 39 | restart_limit = 0 40 | timeout = "2s" 41 | -------------------------------------------------------------------------------- /npm/sqlite-html/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "//": "Autogenerated by the npm_generate_platform_packages.sh script, do not edit by hand", 3 | "name": "sqlite-html", 4 | "version": "0.1.3", 5 | "description": "", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/asg017/sqlite-html.git", 9 | "directory": "npm/sqlite-html" 10 | }, 11 | "author": "Alex Garcia ", 12 | "license": "(MIT OR Apache-2.0)", 13 | "main": "src/index.js", 14 | "type": "module", 15 | "scripts": { 16 | "test": "node test.js" 17 | }, 18 | "files": [ 19 | "*.dylib", 20 | "*.so", 21 | "*.dll" 22 | ], 23 | "optionalDependencies": { 24 | "sqlite-html-darwin-x64": "0.1.3", 25 | "sqlite-html-linux-x64": "0.1.3", 26 | "sqlite-html-windows-x64": "0.1.3" 27 | }, 28 | "devDependencies": { 29 | "better-sqlite3": "^8.1.0", 30 | "sqlite3": "^5.1.4" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /npm/sqlite-html/package.json.tmpl: -------------------------------------------------------------------------------- 1 | { 2 | "//": "Autogenerated by the npm_generate_platform_packages.sh script, do not edit by hand", 3 | "name": "${PACKAGE_NAME_BASE}", 4 | "version": "${VERSION}", 5 | "description": "", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/asg017/${PACKAGE_NAME_BASE}.git", 9 | "directory": "npm/${PACKAGE_NAME_BASE}" 10 | }, 11 | "author": "Alex Garcia ", 12 | "license": "(MIT OR Apache-2.0)", 13 | "main": "src/index.js", 14 | "type": "module", 15 | "scripts": { 16 | "test": "node test.js" 17 | }, 18 | "files": [ 19 | "*.dylib", 20 | "*.so", 21 | "*.dll" 22 | ], 23 | "optionalDependencies": { 24 | "${PACKAGE_NAME_BASE}-darwin-x64": "${VERSION}", 25 | "${PACKAGE_NAME_BASE}-linux-x64": "${VERSION}", 26 | "${PACKAGE_NAME_BASE}-windows-x64": "${VERSION}" 27 | }, 28 | "devDependencies": { 29 | "better-sqlite3": "^8.1.0", 30 | "sqlite3": "^5.1.4" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /python/datasette_sqlite_html/setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | 3 | version = {} 4 | with open("datasette_sqlite_html/version.py") as fp: 5 | exec(fp.read(), version) 6 | 7 | VERSION = version['__version__'] 8 | 9 | setup( 10 | name="datasette-sqlite-html", 11 | description="", 12 | long_description="", 13 | long_description_content_type="text/markdown", 14 | author="Alex Garcia", 15 | url="https://github.com/asg017/sqlite-html", 16 | project_urls={ 17 | "Issues": "https://github.com/asg017/sqlite-html/issues", 18 | "CI": "https://github.com/asg017/sqlite-html/actions", 19 | "Changelog": "https://github.com/asg017/sqlite-html/releases", 20 | }, 21 | license="MIT License, Apache License, Version 2.0", 22 | version=VERSION, 23 | packages=["datasette_sqlite_html"], 24 | entry_points={"datasette": ["sqlite_html = datasette_sqlite_html"]}, 25 | install_requires=["datasette", "sqlite-html"], 26 | extras_require={"test": ["pytest"]}, 27 | python_requires=">=3.7", 28 | ) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2022 Alexander Garcia 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /scripts/npm_generate_platform_packages.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euo pipefail 4 | 5 | export PACKAGE_NAME_BASE="sqlite-html" 6 | export EXTENSION_NAME="html0" 7 | export VERSION=$(cat VERSION) 8 | 9 | generate () { 10 | export PLATFORM_OS=$1 11 | export PLATFORM_ARCH=$2 12 | export PACKAGE_NAME=$PACKAGE_NAME_BASE-$PLATFORM_OS-$PLATFORM_ARCH 13 | 14 | if [ "$PLATFORM_OS" == "windows" ]; then 15 | export EXTENSION_SUFFIX="dll" 16 | elif [ "$PLATFORM_OS" == "darwin" ]; then 17 | export EXTENSION_SUFFIX="dylib" 18 | else 19 | export EXTENSION_SUFFIX="so" 20 | fi 21 | 22 | 23 | mkdir -p npm/$PACKAGE_NAME/lib 24 | 25 | envsubst < npm/platform-package.package.json.tmpl > npm/$PACKAGE_NAME/package.json 26 | envsubst < npm/platform-package.README.md.tmpl > npm/$PACKAGE_NAME/README.md 27 | 28 | touch npm/$PACKAGE_NAME/lib/.gitkeep 29 | 30 | echo "✅ generated npm/$PACKAGE_NAME" 31 | } 32 | 33 | envsubst < npm/$PACKAGE_NAME_BASE/package.json.tmpl > npm/$PACKAGE_NAME_BASE/package.json 34 | echo "✅ generated npm/$PACKAGE_NAME_BASE" 35 | 36 | generate darwin x64 37 | generate darwin arm64 38 | generate linux x64 39 | generate windows x64 -------------------------------------------------------------------------------- /.github/workflows/upload-deno-assets.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs").promises; 2 | 3 | const compiled_extensions = [ 4 | { 5 | path: "sqlite-html-macos/html0.dylib", 6 | name: "deno-darwin-x86_64.html0.dylib", 7 | }, 8 | { 9 | path: "sqlite-html-macos-aarch64/html0.dylib", 10 | name: "deno-darwin-aarch64.html0.dylib", 11 | }, 12 | { 13 | path: "sqlite-html-linux_x86/html0.so", 14 | name: "deno-linux-x86_64.html0.so", 15 | }, 16 | { 17 | path: "sqlite-html-windows/html0.dll", 18 | name: "deno-windows-x86_64.html0.dll", 19 | }, 20 | ]; 21 | 22 | module.exports = async ({ github, context }) => { 23 | const { owner, repo } = context.repo; 24 | const release = await github.rest.repos.getReleaseByTag({ 25 | owner, 26 | repo, 27 | tag: process.env.GITHUB_REF.replace("refs/tags/", ""), 28 | }); 29 | const release_id = release.data.id; 30 | 31 | await Promise.all( 32 | compiled_extensions.map(async ({ name, path }) => { 33 | return github.rest.repos.uploadReleaseAsset({ 34 | owner, 35 | repo, 36 | release_id, 37 | name, 38 | data: await fs.readFile(path), 39 | }); 40 | }) 41 | ); 42 | }; 43 | -------------------------------------------------------------------------------- /npm/sqlite-html/test.js: -------------------------------------------------------------------------------- 1 | import test from "node:test"; 2 | import * as assert from "node:assert"; 3 | 4 | import { getLoadablePath } from "./src/index.js"; 5 | import { basename, extname, isAbsolute } from "node:path"; 6 | 7 | import Database from "better-sqlite3"; 8 | import sqlite3 from "sqlite3"; 9 | 10 | test("getLoadblePath()", (t) => { 11 | const loadablePath = getLoadablePath(); 12 | assert.strictEqual(isAbsolute(loadablePath), true); 13 | assert.strictEqual(basename(loadablePath, extname(loadablePath)), "html0"); 14 | }); 15 | 16 | test("better-sqlite3", (t) => { 17 | const db = new Database(":memory:"); 18 | db.loadExtension(getLoadablePath()); 19 | const version = db.prepare("select html_version()").pluck().get(); 20 | assert.strictEqual(version[0], "v"); 21 | db.close(); 22 | }); 23 | 24 | test("sqlite3", async (t) => { 25 | const db = new sqlite3.Database(":memory:"); 26 | db.loadExtension(getLoadablePath()); 27 | let version = await new Promise((resolve, reject) => { 28 | db.get("select html_version()", (err, row) => { 29 | if (err) return reject(err); 30 | resolve(row["html_version()"]); 31 | }); 32 | }); 33 | assert.strictEqual(version[0], "v"); 34 | db.close(); 35 | }); 36 | -------------------------------------------------------------------------------- /shared.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "go.riyazali.net/sqlite" 5 | ) 6 | 7 | // following linker flags are needed to suppress missing symbol warning in intermediate stages 8 | 9 | // #cgo linux LDFLAGS: -Wl,--unresolved-symbols=ignore-in-object-files 10 | // #cgo darwin LDFLAGS: -Wl,-undefined,dynamic_lookup 11 | // #cgo windows LDFLAGS: -Wl,--allow-multiple-definition 12 | import "C" 13 | 14 | // Set in Makefile 15 | var ( 16 | Commit string 17 | Date string 18 | Version string 19 | ) 20 | 21 | 22 | func Register(api *sqlite.ExtensionApi) (sqlite.ErrorCode, error) { 23 | 24 | if err := RegisterMeta(api); err != nil { 25 | return sqlite.SQLITE_ERROR, err 26 | } 27 | if err := RegisterAttrs(api); err != nil { 28 | return sqlite.SQLITE_ERROR, err 29 | } 30 | if err := RegisterElements(api); err != nil { 31 | return sqlite.SQLITE_ERROR, err 32 | } 33 | if err := RegisterQuery(api); err != nil { 34 | return sqlite.SQLITE_ERROR, err 35 | } 36 | if err := RegisterUtils(api); err != nil { 37 | return sqlite.SQLITE_ERROR, err 38 | } 39 | return sqlite.SQLITE_OK, nil 40 | } 41 | 42 | func init() { 43 | sqlite.RegisterNamed("html", Register) 44 | sqlite.Register(Register) 45 | } 46 | 47 | func main() {} 48 | -------------------------------------------------------------------------------- /bindings/ruby/sqlite_html.gemspec: -------------------------------------------------------------------------------- 1 | 2 | lib = File.expand_path("../lib", __FILE__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | require "version" 5 | 6 | Gem::Specification.new do |spec| 7 | spec.name = "sqlite-html" 8 | spec.version = SqliteHtml::VERSION 9 | spec.authors = ["Alex Garcia"] 10 | spec.email = ["alexsebastian.garcia@gmail.com"] 11 | 12 | spec.summary = "a" 13 | spec.description = "b" 14 | spec.homepage = "https://github.com/asg017/sqlite-html" 15 | spec.license = "MIT" 16 | 17 | # The --platform flag would work in most cases, but on a GH action 18 | # linux runner, it would set platform to "ruby" and not "x86-linux". 19 | # Setting this to Gem::Platform::CURRENT 20 | spec.platform = ENV['PLATFORM'] 21 | 22 | if spec.respond_to?(:metadata) 23 | 24 | spec.metadata["homepage_uri"] = spec.homepage 25 | spec.metadata["source_code_uri"] = spec.homepage 26 | spec.metadata["changelog_uri"] = spec.homepage 27 | else 28 | raise "RubyGems 2.0 or newer is required to protect against " \ 29 | "public gem pushes." 30 | end 31 | 32 | spec.files = Dir["lib/*.rb"] + Dir.glob('lib/*.{so,dylib,dll}') 33 | 34 | spec.require_paths = ["lib"] 35 | 36 | spec.add_development_dependency "bundler", "~> 1.17" 37 | spec.add_development_dependency "rake", "~> 10.0" 38 | end 39 | -------------------------------------------------------------------------------- /meta.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "runtime" 6 | 7 | "go.riyazali.net/sqlite" 8 | ) 9 | 10 | /** html_version() 11 | * Returns the semver version of the current sqlite-html module. 12 | **/ 13 | type HtmlVersionFunc struct{} 14 | 15 | func (*HtmlVersionFunc) Deterministic() bool { return true } 16 | func (*HtmlVersionFunc) Args() int { return 0 } 17 | func (f *HtmlVersionFunc) Apply(c *sqlite.Context, values ...sqlite.Value) { 18 | c.ResultText(Version) 19 | } 20 | 21 | /** html_debug() 22 | * Returns more information for the current html module, 23 | * including build date + commit hash. 24 | **/ 25 | type HtmlDebugFunc struct{} 26 | 27 | func (*HtmlDebugFunc) Deterministic() bool { return true } 28 | func (*HtmlDebugFunc) Args() int { return 0 } 29 | func (f *HtmlDebugFunc) Apply(c *sqlite.Context, values ...sqlite.Value) { 30 | c.ResultText(fmt.Sprintf("Version: %s\nCommit: %s\nRuntime: %s %s/%s\nDate: %s\n", 31 | Version, 32 | Commit, 33 | runtime.Version(), 34 | runtime.GOOS, 35 | runtime.GOARCH, 36 | Date, 37 | )) 38 | } 39 | 40 | func RegisterMeta(api *sqlite.ExtensionApi) error { 41 | var err error 42 | if err = api.CreateFunction("html_version", &HtmlVersionFunc{}); err != nil { 43 | return err 44 | } 45 | if err = api.CreateFunction("html_debug", &HtmlDebugFunc{}); err != nil { 46 | return err 47 | } 48 | return nil 49 | } 50 | -------------------------------------------------------------------------------- /examples/datasette/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM datasetteproject/datasette 2 | 3 | RUN apt-get update && apt-get install -y \ 4 | curl sqlite3 \ 5 | && rm -rf /var/lib/apt/lists/* 6 | 7 | # Download pre-build sqlite-html binary for linux (.so suffix) 8 | ADD https://github.com/asg017/sqlite-html/releases/download/v0.1.0/html0.so /html0.so 9 | 10 | # download some example webpages, save to a a SQLite database on disk 11 | RUN mkdir -p /data \ 12 | && curl -o /data/ca.gov.html https://www.ca.gov \ 13 | && curl -o /data/whitehouse.gov.html https://www.whitehouse.gov/ \ 14 | && curl -o /data/text.npr.org.html https://text.npr.org/ \ 15 | && curl -o /data/blog.pythonlibrary.org.html https://blog.pythonlibrary.org/ \ 16 | && curl -o /data/lacounty.html https://www.lavote.gov/home/voting-elections/current-elections/election-results/past-election-results 17 | 18 | RUN sqlite3 /data/data.db 'create table pages as \ 19 | select \ 20 | "ca.gov" as name, \ 21 | cast(column2 as text) as page \ 22 | from(values \ 23 | ("ca.gov", readfile("/data/ca.gov.html")), \ 24 | ("whitehouse.gov", readfile("/data/whitehouse.gov.html")), \ 25 | ("text.npr.org", readfile("/data/text.npr.org.html")), \ 26 | ("lacounty-results", readfile("/data/lacounty.html")) \ 27 | )' 28 | 29 | # Run datasette instance on that saved DB, and load the sqlite-html extension 30 | CMD datasette /data/data.db \ 31 | -p 8080 -h 0.0.0.0 --cors --load-extension=/html0 32 | -------------------------------------------------------------------------------- /scripts/rename-wheels.py: -------------------------------------------------------------------------------- 1 | # This file is a small utility that rename all .whl files in a given directory 2 | # and "generalizes" them. The wheels made by python/sqlite_path contain the 3 | # pre-compiled sqlite extension, but those aren't bound by a specfic Python 4 | # runtime or version, that other wheels might be. So, this file will rename 5 | # those wheels to be "generalized", like replacing "c37-cp37" to "py3-none". 6 | import sys 7 | import os 8 | from pathlib import Path 9 | 10 | wheel_dir = sys.argv[1] 11 | 12 | is_macos_arm_build = '--is-macos-arm' in sys.argv 13 | 14 | for filename in os.listdir(wheel_dir): 15 | filename = Path(wheel_dir, filename) 16 | if not filename.suffix == '.whl': 17 | continue 18 | new_filename = (filename.name 19 | .replace('cp37-cp37', 'py3-none') 20 | .replace('cp38-cp38', 'py3-none') 21 | .replace('cp39-cp39', 'py3-none') 22 | .replace('cp310-cp310', 'py3-none') 23 | .replace('cp311-cp311', 'py3-none') 24 | .replace('linux_x86_64', 'manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux1_x86_64') 25 | 26 | 27 | ) 28 | if is_macos_arm_build: 29 | new_filename = (new_filename 30 | .replace('macosx_11_0_universal2', 'macosx_11_0_arm64') 31 | .replace('macosx_12_0_universal2', 'macosx_11_0_arm64') 32 | ) 33 | else: 34 | new_filename = (new_filename 35 | .replace('macosx_12_0_universal2', 'macosx_10_6_x86_64') 36 | .replace('macosx_12_0_x86_64', 'macosx_10_6_x86_64') 37 | ) 38 | 39 | os.rename(filename, Path(wheel_dir, new_filename)) -------------------------------------------------------------------------------- /tests/test-sqlite3.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import subprocess 3 | 4 | class Results: 5 | def __init__(self, stdout, stderr): 6 | self.stdout = stdout 7 | self.stderr = stderr 8 | 9 | def run_sqlite3(input): 10 | if type(input) is list: 11 | args = ["dist/sqlite3", ":memory:"] + input 12 | else: 13 | args = ["dist/sqlite3", ":memory:"] + [input] 14 | 15 | proc = subprocess.run(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE) 16 | out = proc.stdout.decode('utf8') if type(proc.stdout) is bytes else None 17 | err = proc.stderr.decode('utf8') if type(proc.stderr) is bytes else None 18 | return Results(out, err) 19 | 20 | class TestSqliteHtmlCli(unittest.TestCase): 21 | def test_cli_scalar(self): 22 | self.assertEqual(run_sqlite3('select 1;').stdout, '1\n') 23 | self.assertEqual( 24 | run_sqlite3(['select name from pragma_function_list where name like "html%" order by 1']).stdout, 25 | "html\nhtml_attr_get\nhtml_attr_has\nhtml_attribute_get\nhtml_attribute_has\nhtml_count\nhtml_debug\nhtml_element\nhtml_escape\nhtml_extract\nhtml_table\nhtml_text\nhtml_trim\nhtml_unescape\nhtml_valid\nhtml_version\n" 26 | ) 27 | self.assertEqual( 28 | run_sqlite3(['select name from pragma_module_list where name like "html_%" order by 1']).stdout, 29 | "html_each\n" 30 | ) 31 | self.assertEqual( 32 | run_sqlite3(['select rowid, html, text from html_each("
x y z", "a")']).stdout, 33 | "0|x|x\n1|y|y\n2|z|z\n" 34 | ) 35 | 36 | if __name__ == '__main__': 37 | unittest.main() -------------------------------------------------------------------------------- /deno/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # `x/sqlite_html` Deno Module 4 | 5 | [![Tags](https://img.shields.io/github/release/asg017/sqlite-html)](https://github.com/asg017/sqlite-html/releases) 6 | [![Doc](https://doc.deno.land/badge.svg)](https://doc.deno.land/https/deno.land/x/sqlite-html@0.1.3/mod.ts) 7 | 8 | The [`sqlite-html`](https://github.com/asg017/sqlite-html) SQLite extension is available to Deno developers with the [`x/sqlite_html`](https://deno.land/x/sqlite-html) Deno module. It works with [`x/sqlite3`](https://deno.land/x/sqlite3), the fastest and native Deno SQLite3 module. 9 | 10 | ```js 11 | import { Database } from "https://deno.land/x/sqlite3@0.8.0/mod.ts"; 12 | import * as sqlite_html from "https://deno.land/x/sqlite_html@v0.1.3/mod.ts"; 13 | 14 | const db = new Database(":memory:"); 15 | 16 | db.enableLoadExtension = true; 17 | db.loadExtension(sqlite_html.getLoadablePath()); 18 | 19 | const [version] = db 20 | .prepare("select html_version()") 21 | .value<[string]>()!; 22 | 23 | console.log(version); 24 | 25 | ``` 26 | 27 | Like `x/sqlite3`, `x/sqlite_html` requires network and filesystem permissions to download and cache the pre-compiled SQLite extension for your machine. Though `x/sqlite3` already requires `--allow-ffi` and `--unstable`, so you might as well use `--allow-all`/`-A`. 28 | 29 | ```bash 30 | deno run -A --unstable 31 | ``` 32 | 33 | `x/sqlite_html` does not work with [`x/sqlite`](https://deno.land/x/sqlite@v3.7.0), which is a WASM-based Deno SQLite module that does not support loading extensions. 34 | -------------------------------------------------------------------------------- /deno/README.md.tmpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | # `x/sqlite_html` Deno Module 4 | 5 | [![Tags](https://img.shields.io/github/release/asg017/sqlite-html)](https://github.com/asg017/sqlite-html/releases) 6 | [![Doc](https://doc.deno.land/badge.svg)](https://doc.deno.land/https/deno.land/x/sqlite-html@${VERSION}/mod.ts) 7 | 8 | The [`sqlite-html`](https://github.com/asg017/sqlite-html) SQLite extension is available to Deno developers with the [`x/sqlite_html`](https://deno.land/x/sqlite-html) Deno module. It works with [`x/sqlite3`](https://deno.land/x/sqlite3), the fastest and native Deno SQLite3 module. 9 | 10 | ```js 11 | import { Database } from "https://deno.land/x/sqlite3@0.8.0/mod.ts"; 12 | import * as sqlite_html from "https://deno.land/x/sqlite_html@v${VERSION}/mod.ts"; 13 | 14 | const db = new Database(":memory:"); 15 | 16 | db.enableLoadExtension = true; 17 | db.loadExtension(sqlite_html.getLoadablePath()); 18 | 19 | const [version] = db 20 | .prepare("select html_version()") 21 | .value<[string]>()!; 22 | 23 | console.log(version); 24 | 25 | ``` 26 | 27 | Like `x/sqlite3`, `x/sqlite_html` requires network and filesystem permissions to download and cache the pre-compiled SQLite extension for your machine. Though `x/sqlite3` already requires `--allow-ffi` and `--unstable`, so you might as well use `--allow-all`/`-A`. 28 | 29 | ```bash 30 | deno run -A --unstable 31 | ``` 32 | 33 | `x/sqlite_html` does not work with [`x/sqlite`](https://deno.land/x/sqlite@v3.7.0), which is a WASM-based Deno SQLite module that does not support loading extensions. 34 | -------------------------------------------------------------------------------- /python/sqlite_html/setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, Extension 2 | import os 3 | import platform 4 | 5 | version = {} 6 | with open("sqlite_html/version.py") as fp: 7 | exec(fp.read(), version) 8 | 9 | VERSION = version['__version__'] 10 | 11 | 12 | system = platform.system() 13 | machine = platform.machine() 14 | 15 | print(system, machine) 16 | 17 | if system == 'Darwin': 18 | if machine not in ['x86_64', 'arm64']: 19 | raise Exception("unsupported platform") 20 | elif system == 'Linux': 21 | if machine not in ['x86_64']: 22 | raise Exception("unsupported platform") 23 | elif system == 'Windows': 24 | # TODO only 64 bit I think 25 | pass 26 | else: 27 | raise Exception("unsupported platform") 28 | 29 | setup( 30 | name="sqlite-html", 31 | description="", 32 | long_description="", 33 | long_description_content_type="text/markdown", 34 | author="Alex Garcia", 35 | url="https://github.com/asg017/sqlite-html", 36 | project_urls={ 37 | "Issues": "https://github.com/asg017/sqlite-html/issues", 38 | "CI": "https://github.com/asg017/sqlite-html/actions", 39 | "Changelog": "https://github.com/asg017/sqlite-html/releases", 40 | }, 41 | license="MIT License, Apache License, Version 2.0", 42 | version=VERSION, 43 | packages=["sqlite_html"], 44 | package_data={"sqlite_html": ['*.so', '*.dylib', '*.dll']}, 45 | install_requires=[], 46 | # Adding an Extension makes `pip wheel` believe that this isn't a 47 | # pure-python package. The noop.c was added since the windows build 48 | # didn't seem to respect optional=True 49 | ext_modules=[Extension("noop", ["noop.c"], optional=True)], 50 | extras_require={"test": ["pytest"]}, 51 | python_requires=">=3.7", 52 | ) -------------------------------------------------------------------------------- /npm/sqlite-html/src/index.js: -------------------------------------------------------------------------------- 1 | import { join } from "node:path"; 2 | import { fileURLToPath } from "node:url"; 3 | import { arch, platform } from "node:process"; 4 | import { statSync } from "node:fs"; 5 | 6 | const supportedPlatforms = [ 7 | ["darwin", "x64"], 8 | ["darwin", "arm64"], 9 | ["win32", "x64"], 10 | ["linux", "x64"], 11 | ]; 12 | 13 | function validPlatform(platform, arch) { 14 | return ( 15 | supportedPlatforms.find(([p, a]) => platform == p && arch === a) !== null 16 | ); 17 | } 18 | function extensionSuffix(platform) { 19 | if (platform === "win32") return "dll"; 20 | if (platform === "darwin") return "dylib"; 21 | return "so"; 22 | } 23 | function platformPackageName(platform, arch) { 24 | const os = platform === "win32" ? "windows" : platform; 25 | return `sqlite-html-${os}-${arch}`; 26 | } 27 | 28 | export function getLoadablePath() { 29 | if (!validPlatform(platform, arch)) { 30 | throw new Error( 31 | `Unsupported platform for sqlite-html, on a ${platform}-${arch} machine, but not in supported platforms (${supportedPlatforms 32 | .map(([p, a]) => `${p}-${a}`) 33 | .join(",")}). Consult the sqlite-html NPM package README for details. ` 34 | ); 35 | } 36 | const packageName = platformPackageName(platform, arch); 37 | const loadablePath = join( 38 | fileURLToPath(new URL(".", import.meta.url)), 39 | "..", 40 | "..", 41 | packageName, 42 | "lib", 43 | `html0.${extensionSuffix(platform)}` 44 | ); 45 | if (!statSync(loadablePath, { throwIfNoEntry: false })) { 46 | throw new Error( 47 | `Loadble extension for sqlite-html not found. Was the ${packageName} package installed? Avoid using the --no-optional flag, as the optional dependencies for sqlite-html are required.` 48 | ); 49 | } 50 | 51 | return loadablePath; 52 | } 53 | -------------------------------------------------------------------------------- /deno/mod.ts: -------------------------------------------------------------------------------- 1 | import { download } from "https://deno.land/x/plug@1.0.1/mod.ts"; 2 | import meta from "./deno.json" assert { type: "json" }; 3 | 4 | const BASE = `${meta.github}/releases/download/v${meta.version}`; 5 | 6 | // Similar to https://github.com/denodrivers/sqlite3/blob/f7529897720631c2341b713f0d78d4d668593ea9/src/ffi.ts#L561 7 | let path: string; 8 | try { 9 | const customPath = Deno.env.get("DENO_SQLITE_HTML_PATH"); 10 | if (customPath) path = customPath; 11 | else { 12 | path = await download({ 13 | url: { 14 | darwin: { 15 | x86_64: `${BASE}/deno-darwin-x86_64.html0.dylib`, 16 | }, 17 | windows: { 18 | x86_64: `${BASE}/deno-windows-x86_64.html0.dll`, 19 | }, 20 | linux: { 21 | x86_64: `${BASE}/deno-linux-x86_64.html0.so`, 22 | }, 23 | }, 24 | suffixes: { 25 | darwin: "", 26 | linux: "", 27 | windows: "", 28 | }, 29 | }); 30 | } 31 | } catch (e) { 32 | if (e instanceof Deno.errors.PermissionDenied) { 33 | throw e; 34 | } 35 | 36 | const error = new Error("Failed to load sqlite-html extension"); 37 | error.cause = e; 38 | 39 | throw error; 40 | } 41 | 42 | /** 43 | * Returns the full path to the compiled sqlite-html extension. 44 | * Caution: this will not be named "html0.dylib|so|dll", since plug will 45 | * replace the name with a hash. 46 | */ 47 | export function getLoadablePath(): string { 48 | return path; 49 | } 50 | 51 | /** 52 | * Entrypoint name for the sqlite-html extension. 53 | */ 54 | export const entrypoint = "sqlite3_html_init"; 55 | 56 | interface Db { 57 | // after https://deno.land/x/sqlite3@0.8.0/mod.ts?s=Database#method_loadExtension_0 58 | loadExtension(file: string, entrypoint?: string | undefined): void; 59 | } 60 | /** 61 | * Loads the sqlite-html extension on the given sqlite3 database. 62 | */ 63 | export function load(db: Db): void { 64 | db.loadExtension(path, entrypoint); 65 | } 66 | -------------------------------------------------------------------------------- /attrs.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/PuerkitoBio/goquery" 7 | "go.riyazali.net/sqlite" 8 | ) 9 | 10 | /** html_attribute_get(document, selector, name) 11 | * html_attr_get(document, selector, name) 12 | * Get the value of the "name" attribute from the element found in document, using selector 13 | **/ 14 | type HtmlAttributeGetFunc struct{} 15 | 16 | func (*HtmlAttributeGetFunc) Deterministic() bool { return true } 17 | func (*HtmlAttributeGetFunc) Args() int { return 3 } 18 | func (*HtmlAttributeGetFunc) Apply(c *sqlite.Context, values ...sqlite.Value) { 19 | html := values[0].Text() 20 | selector := values[1].Text() 21 | attribute := values[2].Text() 22 | 23 | doc, err := goquery.NewDocumentFromReader(strings.NewReader(html)) 24 | 25 | if err != nil { 26 | c.ResultError(err) 27 | return 28 | } 29 | 30 | attr, exists := doc.FindMatcher(goquery.Single(selector)).Attr(attribute) 31 | 32 | if !exists { 33 | c.ResultNull() 34 | } else { 35 | c.ResultText(attr) 36 | } 37 | 38 | } 39 | 40 | 41 | /** html_attribute_has(document, selector, name) 42 | * html_attr_has(document, selector, name) 43 | * Returns 1 or 0, if the "name" attribute from the element 44 | * found in document, using selector, exists 45 | **/ 46 | // 47 | type HtmlAttributeHasFunc struct{} 48 | 49 | func (*HtmlAttributeHasFunc) Deterministic() bool { return true } 50 | func (*HtmlAttributeHasFunc) Args() int { return 3 } 51 | func (*HtmlAttributeHasFunc) Apply(c *sqlite.Context, values ...sqlite.Value) { 52 | html := values[0].Text() 53 | selector := values[1].Text() 54 | attribute := values[2].Text() 55 | 56 | doc, err := goquery.NewDocumentFromReader(strings.NewReader(html)) 57 | 58 | if err != nil { 59 | c.ResultError(err) 60 | return 61 | } 62 | 63 | _, exists := doc.FindMatcher(goquery.Single(selector)).Attr(attribute) 64 | 65 | if !exists { 66 | c.ResultInt(0) 67 | } else { 68 | c.ResultInt(1) 69 | } 70 | 71 | } 72 | 73 | func RegisterAttrs(api *sqlite.ExtensionApi) error { 74 | var err error 75 | if err = api.CreateFunction("html_attribute_get", &HtmlAttributeGetFunc{}); err != nil { 76 | return err 77 | } 78 | if err = api.CreateFunction("html_attr_get", &HtmlAttributeGetFunc{}); err != nil { 79 | return err 80 | } 81 | if err = api.CreateFunction("html_attribute_has", &HtmlAttributeHasFunc{}); err != nil { 82 | return err 83 | } 84 | if err = api.CreateFunction("html_attr_has", &HtmlAttributeHasFunc{}); err != nil { 85 | return err 86 | } 87 | return nil 88 | } 89 | -------------------------------------------------------------------------------- /npm/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | .pnpm-debug.log* 9 | 10 | # Diagnostic reports (https://nodejs.org/api/report.html) 11 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 12 | 13 | # Runtime data 14 | pids 15 | *.pid 16 | *.seed 17 | *.pid.lock 18 | 19 | # Directory for instrumented libs generated by jscoverage/JSCover 20 | lib-cov 21 | 22 | # Coverage directory used by tools like istanbul 23 | coverage 24 | *.lcov 25 | 26 | # nyc test coverage 27 | .nyc_output 28 | 29 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 30 | .grunt 31 | 32 | # Bower dependency directory (https://bower.io/) 33 | bower_components 34 | 35 | # node-waf configuration 36 | .lock-wscript 37 | 38 | # Compiled binary addons (https://nodejs.org/api/addons.html) 39 | build/Release 40 | 41 | # Dependency directories 42 | node_modules/ 43 | jspm_packages/ 44 | 45 | # Snowpack dependency directory (https://snowpack.dev/) 46 | web_modules/ 47 | 48 | # TypeScript cache 49 | *.tsbuildinfo 50 | 51 | # Optional npm cache directory 52 | .npm 53 | 54 | # Optional eslint cache 55 | .eslintcache 56 | 57 | # Optional stylelint cache 58 | .stylelintcache 59 | 60 | # Microbundle cache 61 | .rpt2_cache/ 62 | .rts2_cache_cjs/ 63 | .rts2_cache_es/ 64 | .rts2_cache_umd/ 65 | 66 | # Optional REPL history 67 | .node_repl_history 68 | 69 | # Output of 'npm pack' 70 | *.tgz 71 | 72 | # Yarn Integrity file 73 | .yarn-integrity 74 | 75 | # dotenv environment variable files 76 | .env 77 | .env.development.local 78 | .env.test.local 79 | .env.production.local 80 | .env.local 81 | 82 | # parcel-bundler cache (https://parceljs.org/) 83 | .cache 84 | .parcel-cache 85 | 86 | # Next.js build output 87 | .next 88 | out 89 | 90 | # Nuxt.js build / generate output 91 | .nuxt 92 | dist 93 | 94 | # Gatsby files 95 | .cache/ 96 | # Comment in the public line in if your project uses Gatsby and not Next.js 97 | # https://nextjs.org/blog/next-9-1#public-directory-support 98 | # public 99 | 100 | # vuepress build output 101 | .vuepress/dist 102 | 103 | # vuepress v2.x temp and cache directory 104 | .temp 105 | .cache 106 | 107 | # Docusaurus cache and generated files 108 | .docusaurus 109 | 110 | # Serverless directories 111 | .serverless/ 112 | 113 | # FuseBox cache 114 | .fusebox/ 115 | 116 | # DynamoDB Local files 117 | .dynamodb/ 118 | 119 | # TernJS port file 120 | .tern-port 121 | 122 | # Stores VSCode versions used for testing VSCode extensions 123 | .vscode-test 124 | 125 | # yarn v2 126 | .yarn/cache 127 | .yarn/unplugged 128 | .yarn/build-state.yml 129 | .yarn/install-state.gz 130 | .pnp.* 131 | 132 | # not needed for libraries, right? running into some issues in ci with this, so gonna yeet 133 | package-lock.json -------------------------------------------------------------------------------- /python/sqlite_html/README.md: -------------------------------------------------------------------------------- 1 | # The `sqlite-html` Python package 2 | 3 | `sqlite-html` is also distributed on PyPi as a Python package, for use in Python applications. It works well with the builtin [`sqlite3`](https://docs.python.org/3/library/sqlite3.html) Python module. 4 | 5 | ``` 6 | pip install sqlite-html 7 | ``` 8 | 9 | ## Usage 10 | 11 | The `sqlite-html` python package exports two functions: `loadable_html()`, which returns the full path to the loadable extension, and `load(conn)`, which loads the `sqlite-html` extension into the given [sqlite3 Connection object](https://docs.python.org/3/library/sqlite3.html#connection-objects). 12 | 13 | ```python 14 | import sqlite_html 15 | print(sqlite_html.loadable_html()) 16 | # '/.../venv/lib/python3.9/site-packages/sqlite_html/html0' 17 | 18 | import sqlite3 19 | conn = sqlite3.connect(':memory:') 20 | sqlite_html.load(conn) 21 | conn.execute('select html_version()').fetchone() 22 | # ('v0.1.0') 23 | ``` 24 | 25 | See [the full API Reference](#api-reference) for the Python API, and [`docs.md`](../../docs.md) for documentation on the `sqlite-html` SQL API. 26 | 27 | See [`datasette-sqlite-html`](../datasette_sqlite_html/) for a Datasette plugin that is a light wrapper around the `sqlite-html` Python package. 28 | 29 | ## Compatibility 30 | 31 | Currently the `sqlite-html` Python package is only distributed on PyPi as pre-build wheels, it's not possible to install from the source distribution. This is because the underlying `sqlite-html` extension requires a lot of build dependencies like `make`, `cc`, and `cargo`. 32 | 33 | If you get a `unsupported platform` error when pip installing `sqlite-html`, you'll have to build the `sqlite-html` manually and load in the dynamic library manually. 34 | 35 | ## API Reference 36 | 37 |

loadable_html()

38 | 39 | Returns the full path to the locally-install `sqlite-html` extension, without the filename. 40 | 41 | This can be directly passed to [`sqlite3.Connection.load_extension()`](https://docs.python.org/3/library/sqlite3.html#sqlite3.Connection.load_extension), but the [`sqlite_html.load()`](#load) function is preferred. 42 | 43 | ```python 44 | import sqlite_html 45 | print(sqlite_html.loadable_html()) 46 | # '/.../venv/lib/python3.9/site-packages/sqlite_html/html0' 47 | ``` 48 | 49 | > Note: this extension path doesn't include the file extension (`.dylib`, `.so`, `.dll`). This is because [SQLite will infer the correct extension](https://www.sqlite.org/loadext.html#loading_an_extension). 50 | 51 |

load(connection)

52 | 53 | Loads the `sqlite-html` extension on the given [`sqlite3.Connection`](https://docs.python.org/3/library/sqlite3.html#sqlite3.Connection) object, calling [`Connection.load_extension()`](https://docs.python.org/3/library/sqlite3.html#sqlite3.Connection.load_extension). 54 | 55 | ```python 56 | import sqlite_html 57 | import sqlite3 58 | conn = sqlite3.connect(':memory:') 59 | 60 | conn.enable_load_extension(True) 61 | sqlite_html.load(conn) 62 | conn.enable_load_extension(False) 63 | 64 | conn.execute('select html_version()').fetchone() 65 | # ('v0.1.0') 66 | ``` 67 | -------------------------------------------------------------------------------- /utils.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "html" 6 | "strings" 7 | 8 | "github.com/PuerkitoBio/goquery" 9 | "go.riyazali.net/sqlite" 10 | ) 11 | 12 | /** html_valid(document) 13 | * Returns 1 if the given document is valid HTML, 0 otherwise. 14 | * @param document {text | html} - HTML document to read from. 15 | **/ 16 | type HtmlValidFunc struct{} 17 | 18 | func (*HtmlValidFunc) Deterministic() bool { return true } 19 | func (*HtmlValidFunc) Args() int { return 1 } 20 | func (*HtmlValidFunc) Apply(c *sqlite.Context, values ...sqlite.Value) { 21 | html := values[0].Text() 22 | _, err := goquery.NewDocumentFromReader(strings.NewReader(html)) 23 | 24 | if err != nil { 25 | c.ResultInt(0) 26 | } else { 27 | c.ResultInt(1) 28 | } 29 | } 30 | 31 | /** html_escape(content) 32 | * Returns an HTML escaped version of the given content. 33 | * @param content {text} - Text content to escape. 34 | **/ 35 | type HtmlEscapeFunc struct{} 36 | 37 | func (*HtmlEscapeFunc) Deterministic() bool { return true } 38 | func (*HtmlEscapeFunc) Args() int { return 1 } 39 | func (*HtmlEscapeFunc) Apply(c *sqlite.Context, values ...sqlite.Value) { 40 | c.ResultText(html.EscapeString(values[0].Text())) 41 | } 42 | 43 | /** html_unescape(content) 44 | * Returns an HTML unescaped version of the given content. 45 | * @param content {text} - Text content to unescape. 46 | **/ 47 | type HtmlUnescapeFunc struct{} 48 | 49 | func (*HtmlUnescapeFunc) Deterministic() bool { return true } 50 | func (*HtmlUnescapeFunc) Args() int { return 1 } 51 | func (*HtmlUnescapeFunc) Apply(c *sqlite.Context, values ...sqlite.Value) { 52 | c.ResultText(html.UnescapeString(values[0].Text())) 53 | } 54 | 55 | /** html_trim(content) 56 | * Trim whitespace around the given text content. Useful for output of html_text 57 | * @param content {text} - Text content to trim. 58 | **/ 59 | type HtmlTrimFunc struct{} 60 | 61 | func (*HtmlTrimFunc) Deterministic() bool { return true } 62 | func (*HtmlTrimFunc) Args() int { return 1 } 63 | func (*HtmlTrimFunc) Apply(c *sqlite.Context, values ...sqlite.Value) { 64 | s := values[0].Text() 65 | c.ResultText(strings.TrimSpace(s)) 66 | } 67 | 68 | 69 | /** html_table(content) 70 | * Wrap the given content around a HTML table. Useful for parsing table rows. 71 | * @param content {text} - Text content to wrap. 72 | **/ 73 | type HtmlTableFunc struct{} 74 | 75 | func (*HtmlTableFunc) Deterministic() bool { return true } 76 | func (*HtmlTableFunc) Args() int { return 1 } 77 | func (*HtmlTableFunc) Apply(c *sqlite.Context, values ...sqlite.Value) { 78 | s := values[0].Text() 79 | c.ResultText(fmt.Sprintf("%s", s)) 80 | } 81 | 82 | func RegisterUtils(api *sqlite.ExtensionApi) error { 83 | var err error 84 | if err = api.CreateFunction("html_valid", &HtmlValidFunc{}); err != nil { 85 | return err 86 | } 87 | if err = api.CreateFunction("html_table", &HtmlTableFunc{}); err != nil { 88 | return err 89 | } 90 | if err = api.CreateFunction("html_escape", &HtmlEscapeFunc{}); err != nil { 91 | return err 92 | } 93 | if err = api.CreateFunction("html_unescape", &HtmlUnescapeFunc{}); err != nil { 94 | return err 95 | } 96 | if err = api.CreateFunction("html_trim", &HtmlTrimFunc{}); err != nil { 97 | return err 98 | } 99 | return nil 100 | } 101 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/PuerkitoBio/goquery v1.7.1 h1:oE+T06D+1T7LNrn91B4aERsRIeCLJ/oPSa6xB9FPnz4= 2 | github.com/PuerkitoBio/goquery v1.7.1/go.mod h1:XY0pP4kfraEmmV1O7Uf6XyjoslwsneBbgeDjLYuN8xY= 3 | github.com/andybalholm/cascadia v1.2.0 h1:vuRCkM5Ozh/BfmsaTm26kbjm0mIOM3yS5Ek/F5h18aE= 4 | github.com/andybalholm/cascadia v1.2.0/go.mod h1:YCyR8vOZT9aZ1CHEd8ap0gMVm2aFgxBp0T0eFw1RUQY= 5 | github.com/asg017/vtab v0.0.0-20230324234256-713839099704 h1:sFF0fnfSu7XKQx0XCFISjLGNGnFRwiKAd/KIB3prEBY= 6 | github.com/asg017/vtab v0.0.0-20230324234256-713839099704/go.mod h1:h3Dvue0LB+WA78WGXdYf48KTZK+IAiYCmkvA/loa6ys= 7 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 8 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 9 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 10 | github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= 11 | github.com/jmoiron/sqlx v1.3.4 h1:wv+0IJZfL5z0uZoUjlpKgHkgaFSYD+r9CfrXjEXsO7w= 12 | github.com/jmoiron/sqlx v1.3.4/go.mod h1:2BljVx/86SuTyjE+aPYlHCTNvZrnJXghYGpNiXLBMCQ= 13 | github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= 14 | github.com/mattn/go-pointer v0.0.1 h1:n+XhsuGeVO6MEAp7xyEukFINEa+Quek5psIR/ylA6o0= 15 | github.com/mattn/go-pointer v0.0.1/go.mod h1:2zXcozF6qYGgmsG+SeTZz3oAbFLdD3OWqnUbNvJZAlc= 16 | github.com/mattn/go-sqlite3 v1.14.6 h1:dNPt6NO46WmLVt2DLNpwczCmdV5boIZ6g/tlDrlRUbg= 17 | github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= 18 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 19 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 20 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 21 | github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= 22 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 23 | go.riyazali.net/sqlite v0.0.0-20220820100132-b0f5d97504db/go.mod h1:UVocl0mLwS0QKUKa5mI6lppmBjvQnUEkFjFfoWqFWQU= 24 | go.riyazali.net/sqlite v0.0.0-20230320080028-80a51d3944c0 h1:59rDFi9pMMud3hjl4DEWIiZdx8kR4LpAIVaWEnpOn6s= 25 | go.riyazali.net/sqlite v0.0.0-20230320080028-80a51d3944c0/go.mod h1:UVocl0mLwS0QKUKa5mI6lppmBjvQnUEkFjFfoWqFWQU= 26 | golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 27 | golang.org/x/net v0.0.0-20210614182718-04defd469f4e h1:XpT3nA5TvE525Ne3hInMh6+GETgn27Zfm9dxsThnX2Q= 28 | golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 29 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 30 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 31 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 32 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 33 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 34 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 35 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 36 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= 37 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 38 | -------------------------------------------------------------------------------- /python/.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | *.py,cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | cover/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | .pybuilder/ 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # IPython 82 | profile_default/ 83 | ipython_config.py 84 | 85 | # pyenv 86 | # For a library or package, you might want to ignore these files since the code is 87 | # intended to run in multiple environments; otherwise, check them in: 88 | # .python-version 89 | 90 | # pipenv 91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 94 | # install all needed dependencies. 95 | #Pipfile.lock 96 | 97 | # poetry 98 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 99 | # This is especially recommended for binary packages to ensure reproducibility, and is more 100 | # commonly ignored for libraries. 101 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 102 | #poetry.lock 103 | 104 | # pdm 105 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 106 | #pdm.lock 107 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 108 | # in version control. 109 | # https://pdm.fming.dev/#use-with-ide 110 | .pdm.toml 111 | 112 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 113 | __pypackages__/ 114 | 115 | # Celery stuff 116 | celerybeat-schedule 117 | celerybeat.pid 118 | 119 | # SageMath parsed files 120 | *.sage.py 121 | 122 | # Environments 123 | .env 124 | .venv 125 | env/ 126 | venv/ 127 | ENV/ 128 | env.bak/ 129 | venv.bak/ 130 | 131 | # Spyder project settings 132 | .spyderproject 133 | .spyproject 134 | 135 | # Rope project settings 136 | .ropeproject 137 | 138 | # mkdocs documentation 139 | /site 140 | 141 | # mypy 142 | .mypy_cache/ 143 | .dmypy.json 144 | dmypy.json 145 | 146 | # Pyre type checker 147 | .pyre/ 148 | 149 | # pytype static type analyzer 150 | .pytype/ 151 | 152 | # Cython debug symbols 153 | cython_debug/ 154 | 155 | # PyCharm 156 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 157 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 158 | # and can be added to the global gitignore or merged into this file. For a more nuclear 159 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 160 | #.idea/ -------------------------------------------------------------------------------- /bindings/sqlite-utils/.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | *.py,cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | cover/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | .pybuilder/ 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # IPython 82 | profile_default/ 83 | ipython_config.py 84 | 85 | # pyenv 86 | # For a library or package, you might want to ignore these files since the code is 87 | # intended to run in multiple environments; otherwise, check them in: 88 | # .python-version 89 | 90 | # pipenv 91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 94 | # install all needed dependencies. 95 | #Pipfile.lock 96 | 97 | # poetry 98 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 99 | # This is especially recommended for binary packages to ensure reproducibility, and is more 100 | # commonly ignored for libraries. 101 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 102 | #poetry.lock 103 | 104 | # pdm 105 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 106 | #pdm.lock 107 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 108 | # in version control. 109 | # https://pdm.fming.dev/#use-with-ide 110 | .pdm.toml 111 | 112 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 113 | __pypackages__/ 114 | 115 | # Celery stuff 116 | celerybeat-schedule 117 | celerybeat.pid 118 | 119 | # SageMath parsed files 120 | *.sage.py 121 | 122 | # Environments 123 | .env 124 | .venv 125 | env/ 126 | venv/ 127 | ENV/ 128 | env.bak/ 129 | venv.bak/ 130 | 131 | # Spyder project settings 132 | .spyderproject 133 | .spyproject 134 | 135 | # Rope project settings 136 | .ropeproject 137 | 138 | # mkdocs documentation 139 | /site 140 | 141 | # mypy 142 | .mypy_cache/ 143 | .dmypy.json 144 | dmypy.json 145 | 146 | # Pyre type checker 147 | .pyre/ 148 | 149 | # pytype static type analyzer 150 | .pytype/ 151 | 152 | # Cython debug symbols 153 | cython_debug/ 154 | 155 | # PyCharm 156 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 157 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 158 | # and can be added to the global gitignore or merged into this file. For a more nuclear 159 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 160 | #.idea/ 161 | -------------------------------------------------------------------------------- /npm/sqlite-html/README.md: -------------------------------------------------------------------------------- 1 | # `sqlite-html` NPM Package 2 | 3 | `sqlite-html` is distributed on `npm` for Node.js developers. To install on [supported platforms](#supported-platforms), simply run: 4 | 5 | ``` 6 | npm install sqlite-html 7 | ``` 8 | 9 | The `sqlite-html` package is meant to be used with Node SQLite clients like [`better-sqlite3`](https://github.com/WiseLibs/better-sqlite3) and [`node-sqlite3`](https://github.com/TryGhost/node-sqlite3). For `better-sqlite3`, call [`.loadExtension()`](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#loadextensionpath-entrypoint---this) on your database object, passing in [`getLoadablePath()`](#getLoadablePath). 10 | 11 | ```js 12 | import Database from "better-sqlite3"; 13 | import * as sqlite_html from "sqlite-html"; 14 | 15 | const db = new Database(":memory:"); 16 | 17 | db.loadExtension(sqlite_html.getLoadablePath()); 18 | 19 | const version = db.prepare("select html_version()").pluck().get(); 20 | console.log(version); // "v0.2.0" 21 | ``` 22 | 23 | For `node-sqlite3`, call the similarly named [`.loadExtension()`](https://github.com/TryGhost/node-sqlite3/wiki/API#loadextensionpath--callback) method on your database object, and pass in [`getLoadablePath()`](#getLoadablePath). 24 | 25 | ```js 26 | import sqlite3 from "sqlite3"; 27 | import * as sqlite_html from "sqlite-html"; 28 | 29 | const db = new sqlite3.Database(":memory:"); 30 | 31 | db.loadExtension(sqlite_html.getLoadablePath()); 32 | 33 | db.get("select html_version()", (err, row) => { 34 | console.log(row); // {json_schema_version(): "v0.2.0"} 35 | }); 36 | ``` 37 | 38 | See [the full API Reference](#api-reference) for the Node API, and [`docs.md`](../../docs.md) for documentation on the `sqlite-html` SQL API. 39 | 40 | ## Supported Platforms 41 | 42 | Since the underlying `html0` SQLite extension is pre-compiled, the `sqlite-html` NPM package only works on a few "platforms" (operating systems + CPU architectures). These platforms include: 43 | 44 | - `darwin-x64` (MacOS x86_64) 45 | - `win32-x64` (Windows x86_64) 46 | - `linux-x64` (Linux x86_64) 47 | 48 | To see which platform your machine is, check the [`process.arch`](https://nodejs.org/api/process.html#processarch) and [`process.platform`](https://nodejs.org/api/process.html#processplatform) values like so: 49 | 50 | ```bash 51 | $ node -e 'console.log([process.platform, process.arch])' 52 | [ 'darwin', 'x64' ] 53 | ``` 54 | 55 | When the `sqlite-html` NPM package is installed, the correct pre-compiled extension for your operating system and CPU architecture will be downloaded from the [optional dependencies](https://docs.npmjs.com/cli/v9/configuring-npm/package-json#optionaldependencies), with platform-specific packages like `sqlite-html-darwin-x64`. This will be done automatically, there's no need to directly install those packages. 56 | 57 | More platforms may be supported in the future. Consider [supporting my work](https://github.com/sponsors/asg017/) if you'd like to see more operating systems and CPU architectures supported in `sqlite-html`. 58 | 59 | ## API Reference 60 | 61 | #getLoadablePath [<>](https://github.com/asg017/sqlite-html/blob/main/npm/sqlite-html/src/index.js "Source") 62 | 63 | Returns the full path to where the `sqlite-html` _should_ be installed, based on the `sqlite-html`'s `package.json` optional dependencies and the host's operating system and architecture. 64 | 65 | This path can be directly passed into [`better-sqlite3`](https://github.com/WiseLibs/better-sqlite3)'s [`.loadExtension()`](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#loadextensionpath-entrypoint---this). 66 | 67 | ```js 68 | import Database from "better-sqlite3"; 69 | import * as sqlite_html from "sqlite-html"; 70 | 71 | const db = new Database(":memory:"); 72 | db.loadExtension(sqlite_html.getLoadablePath()); 73 | ``` 74 | 75 | It can also be used in [`node-sqlite3`](https://github.com/TryGhost/node-sqlite3)'s [`.loadExtension()`](https://github.com/TryGhost/node-sqlite3/wiki/API#loadextensionpath--callback). 76 | 77 | ```js 78 | import sqlite3 from "sqlite3"; 79 | import * as sqlite_html from "sqlite-html"; 80 | 81 | const db = new sqlite3.Database(":memory:"); 82 | db.loadExtension(sqlite_html.getLoadablePath()); 83 | ``` 84 | 85 | This function throws an `Error` in two different cases. The first case is when `sqlite-html` is installed and run on an [unsupported platform](#supported-platforms). The second case is when the platform-specific optional dependency is not installed. If you reach this, ensure you aren't using `--no-optional` flag, and [file an issue](https://github.com/asg017/sqlite-html/issues/new) if you are stuck. 86 | 87 | The `db.loadExtension()` function may also throw an Error if the compiled extension is incompatible with your SQLite connection for any reason, including missing system packages, outdated glib versions, or other misconfigurations. If you reach this, please [file an issue](https://github.com/asg017/sqlite-html/issues/new). 88 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | COMMIT=$(shell git rev-parse HEAD) 2 | VERSION=$(shell cat VERSION) 3 | DATE=$(shell date +'%FT%TZ%z') 4 | VENDOR_SQLITE=$(shell pwd)/sqlite 5 | GO_BUILD_LDFLAGS=-ldflags '-X main.Version=v$(VERSION) -X main.Commit=$(COMMIT) -X main.Date=$(DATE)' 6 | #GO_BUILD_CGO_CFLAGS=CGO_CFLAGS=-DSQLITE3_INIT_FN=sqlite3_html_init 7 | GO_BUILD_CGO_CFLAGS=CGO_ENABLED=1 CGO_CFLAGS="-DUSE_LIBSQLITE3" CPATH="$(VENDOR_SQLITE)" 8 | 9 | 10 | ifeq ($(shell uname -s),Darwin) 11 | CONFIG_DARWIN=y 12 | else ifeq ($(OS),Windows_NT) 13 | CONFIG_WINDOWS=y 14 | else 15 | CONFIG_LINUX=y 16 | endif 17 | 18 | # framework stuff is needed bc https://github.com/golang/go/issues/42459#issuecomment-896089738 19 | ifdef CONFIG_DARWIN 20 | LOADABLE_EXTENSION=dylib 21 | SQLITE3_CFLAGS=-framework CoreFoundation -framework Security 22 | endif 23 | 24 | ifdef CONFIG_LINUX 25 | LOADABLE_EXTENSION=so 26 | endif 27 | 28 | 29 | ifdef CONFIG_WINDOWS 30 | LOADABLE_EXTENSION=dll 31 | endif 32 | 33 | ifdef python 34 | PYTHON=$(python) 35 | else 36 | PYTHON=python3 37 | endif 38 | 39 | ifdef IS_MACOS_ARM 40 | RENAME_WHEELS_ARGS=--is-macos-arm 41 | else 42 | RENAME_WHEELS_ARGS= 43 | endif 44 | 45 | prefix=dist 46 | 47 | TARGET_LOADABLE=$(prefix)/html0.$(LOADABLE_EXTENSION) 48 | TARGET_OBJ=$(prefix)/html0.o 49 | TARGET_WHEELS=$(prefix)/wheels 50 | TARGET_SQLITE3=$(prefix)/sqlite3 51 | TARGET_PACKAGE=$(prefix)/package.zip 52 | 53 | INTERMEDIATE_PYPACKAGE_EXTENSION=python/sqlite_html/sqlite_html/html0.$(LOADABLE_EXTENSION) 54 | 55 | loadable: $(TARGET_LOADABLE) 56 | sqlite3: $(TARGET_SQLITE3) 57 | package: $(TARGET_PACKAGE) 58 | all: loadable sqlite3 package 59 | 60 | $(prefix): 61 | mkdir -p $(prefix) 62 | 63 | $(TARGET_WHEELS): $(prefix) 64 | mkdir -p $(TARGET_WHEELS) 65 | 66 | $(TARGET_LOADABLE): $(shell find . -type f -name '*.go') 67 | $(GO_BUILD_CGO_CFLAGS) go build -buildmode=c-shared -tags="shared" \ 68 | $(GO_BUILD_LDFLAGS) \ 69 | -o $@ . 70 | 71 | python: $(TARGET_WHEELS) $(TARGET_LOADABLE) $(TARGET_WHEELS) scripts/rename-wheels.py $(shell find python/sqlite_html -type f -name '*.py') 72 | cp $(TARGET_LOADABLE) $(INTERMEDIATE_PYPACKAGE_EXTENSION) 73 | rm $(TARGET_WHEELS)/sqlite_html* || true 74 | pip3 wheel python/sqlite_html/ -w $(TARGET_WHEELS) 75 | python3 scripts/rename-wheels.py $(TARGET_WHEELS) $(RENAME_WHEELS_ARGS) 76 | echo "✅ generated python wheel" 77 | 78 | python-versions: python/version.py.tmpl 79 | VERSION=$(VERSION) envsubst < python/version.py.tmpl > python/sqlite_html/sqlite_html/version.py 80 | echo "✅ generated python/sqlite_html/sqlite_html/version.py" 81 | 82 | VERSION=$(VERSION) envsubst < python/version.py.tmpl > python/datasette_sqlite_html/datasette_sqlite_html/version.py 83 | echo "✅ generated python/datasette_sqlite_html/datasette_sqlite_html/version.py" 84 | 85 | datasette: $(TARGET_WHEELS) $(shell find python/datasette_sqlite_html -type f -name '*.py') 86 | rm $(TARGET_WHEELS)/datasette* || true 87 | pip3 wheel python/datasette_sqlite_html/ --no-deps -w $(TARGET_WHEELS) 88 | 89 | bindings/sqlite-utils/pyproject.toml: bindings/sqlite-utils/pyproject.toml.tmpl VERSION 90 | VERSION=$(VERSION) envsubst < $< > $@ 91 | echo "✅ generated $@" 92 | 93 | bindings/sqlite-utils/sqlite_utils_sqlite_html/version.py: bindings/sqlite-utils/sqlite_utils_sqlite_html/version.py.tmpl VERSION 94 | VERSION=$(VERSION) envsubst < $< > $@ 95 | echo "✅ generated $@" 96 | 97 | sqlite-utils: $(TARGET_WHEELS) bindings/sqlite-utils/pyproject.toml bindings/sqlite-utils/sqlite_utils_sqlite_html/version.py 98 | python3 -m build bindings/sqlite-utils -w -o $(TARGET_WHEELS) 99 | 100 | npm: VERSION npm/platform-package.README.md.tmpl npm/platform-package.package.json.tmpl npm/sqlite-html/package.json.tmpl scripts/npm_generate_platform_packages.sh 101 | scripts/npm_generate_platform_packages.sh 102 | 103 | deno: VERSION deno/deno.json.tmpl 104 | scripts/deno_generate_package.sh 105 | 106 | bindings/ruby/lib/version.rb: bindings/ruby/lib/version.rb.tmpl VERSION 107 | VERSION=$(VERSION) envsubst < $< > $@ 108 | 109 | ruby: bindings/ruby/lib/version.rb 110 | 111 | version: 112 | make python-versions 113 | make python 114 | make bindings/sqlite-utils/pyproject.toml bindings/sqlite-utils/sqlite_utils_sqlite_html/version.py 115 | make npm 116 | make deno 117 | make ruby 118 | 119 | $(TARGET_OBJ): $(shell find . -type f -name '*.go') 120 | $(GO_BUILD_CGO_CFLAGS) CGO_ENABLED=1 go build -buildmode=c-archive \ 121 | $(GO_BUILD_LDFLAGS) \ 122 | -o $@ . 123 | 124 | # I don't think we can include DSQLITE_OMIT_LOAD_EXTENSION - maybe riyaz-ali/sqlite uses it? 125 | # add back later -DHAVE_READLINE -lreadline -lncurses 126 | $(TARGET_SQLITE3): $(TARGET_OBJ) dist/sqlite3-extra.c sqlite/shell.c 127 | gcc \ 128 | $(SQLITE3_CFLAGS) \ 129 | -lm -pthread \ 130 | dist/sqlite3-extra.c sqlite/shell.c $(TARGET_OBJ) \ 131 | -L. -I./sqlite \ 132 | -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION=1 \ 133 | -DSQLITE_EXTRA_INIT=core_init -DSQLITE3_INIT_FN=sqlite3_html_init \ 134 | -o $@ 135 | 136 | $(TARGET_PACKAGE): $(TARGET_LOADABLE) $(TARGET_OBJ) sqlite/sqlite-html.h $(TARGET_SQLITE3) 137 | zip --junk-paths $@ $(TARGET_LOADABLE) $(TARGET_OBJ) sqlite/sqlite-html.h $(TARGET_SQLITE3) 138 | 139 | dist/sqlite3-extra.c: sqlite/sqlite3.c sqlite/core_init.c 140 | cat sqlite/sqlite3.c sqlite/core_init.c > $@ 141 | 142 | clean: 143 | rm dist/* 144 | 145 | test: 146 | make test-loadable 147 | make test-sqlite3 148 | make test-python 149 | make test-npm 150 | make test-deno 151 | 152 | test-loadable: $(TARGET_LOADABLE) 153 | $(PYTHON) tests/test-loadable.py 154 | 155 | test-python: 156 | $(PYTHON) tests/test-python.py 157 | 158 | test-npm: 159 | node npm/sqlite-html/test.js 160 | 161 | test-deno: 162 | deno task --config deno/deno.json test 163 | 164 | test-loadable-watch: $(TARGET_LOADABLE) 165 | watchexec -w . -w $(TARGET_LOADABLE) -w tests/test-loadable.py --clear -- make test-loadable 166 | 167 | test-sqlite3: sqlite3 168 | python3 tests/test-sqlite3.py 169 | 170 | format: 171 | gofmt -s -w . 172 | 173 | publish-release: 174 | ./scripts/publish_release.sh 175 | 176 | .PHONY: all clean format publish-release \ 177 | python python-versions datasette sqlite-utils npm deno ruby version \ 178 | test test-loadable test-sqlite3 \ 179 | loadable sqlite3 package 180 | -------------------------------------------------------------------------------- /query.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "io" 5 | "strings" 6 | 7 | "github.com/PuerkitoBio/goquery" 8 | "github.com/augmentable-dev/vtab" 9 | "go.riyazali.net/sqlite" 10 | ) 11 | 12 | /** html_text(document [, selector]) 13 | * Returns the combined text contents of the selected element. similar to .innerText 14 | * Raises an error if document is not proper HTML. 15 | * @param document {text | html} - HTML document to read from. 16 | * @param selector {text} - CSS-style selector of which element in document to read. 17 | */ 18 | type HtmlTextFunc struct{ 19 | nArgs int 20 | } 21 | 22 | func (*HtmlTextFunc) Deterministic() bool { return true } 23 | func (h *HtmlTextFunc) Args() int { return h.nArgs } 24 | func (*HtmlTextFunc) Apply(c *sqlite.Context, values ...sqlite.Value) { 25 | html := values[0].Text() 26 | doc, err := goquery.NewDocumentFromReader(strings.NewReader(html)) 27 | 28 | if err != nil { 29 | c.ResultError(err) 30 | return 31 | } 32 | if len(values) > 1 { 33 | selector := values[1].Text() 34 | c.ResultText(doc.FindMatcher(goquery.Single(selector)).Text()) 35 | }else { 36 | c.ResultText(doc.Text()) 37 | } 38 | } 39 | 40 | /** html_extract(document, selector) 41 | * Returns the entire HTML representation of the selected element from document, using selector. 42 | * Raises an error if document is not proper HTML. 43 | * @param document {text | html} - HTML document to read from. 44 | * @param selector {text} - CSS-style selector of which element in document to read. 45 | */ 46 | type HtmlExtractFunc struct{} 47 | 48 | func (*HtmlExtractFunc) Deterministic() bool { return true } 49 | func (*HtmlExtractFunc) Args() int { return 2 } 50 | func (*HtmlExtractFunc) Apply(c *sqlite.Context, values ...sqlite.Value) { 51 | html := values[0].Text() 52 | selector := values[1].Text() 53 | 54 | doc, err := goquery.NewDocumentFromReader(strings.NewReader(html)) 55 | 56 | if err != nil { 57 | c.ResultError(err) 58 | return 59 | } 60 | 61 | sub, err := goquery.OuterHtml(doc.FindMatcher(goquery.Single(selector))) 62 | if err != nil { 63 | c.ResultError(err) 64 | return 65 | } 66 | 67 | c.ResultText(sub) 68 | c.ResultSubType(HTML_SUBTYPE) 69 | } 70 | 71 | /** html_count(document, selector) 72 | * Count the number of matching selected elements in the given document. 73 | * Raises an error if document is not proper HTML. 74 | * @param document {text | html} - HTML document to read from. 75 | * @param selector {text} - CSS-style selector of which element in document to read. 76 | */ 77 | type HtmlCountFunc struct{} 78 | 79 | func (*HtmlCountFunc) Deterministic() bool { return true } 80 | func (*HtmlCountFunc) Args() int { return 2 } 81 | func (*HtmlCountFunc) Apply(c *sqlite.Context, values ...sqlite.Value) { 82 | html := values[0].Text() 83 | selector := values[1].Text() 84 | 85 | doc, err := goquery.NewDocumentFromReader(strings.NewReader(html)) 86 | 87 | if err != nil { 88 | c.ResultError(err) 89 | return 90 | } 91 | 92 | count := doc.Find(selector).Length() 93 | 94 | c.ResultInt(count) 95 | } 96 | 97 | /** html_each(document, selector) 98 | * A table value function returned a row for every matching element inside document using selector. 99 | * Raises an error if document is not proper HTML. 100 | * @param document {text | html} - HTML document to read from. 101 | * @param selector {text} - CSS-style selector of which element in document to read. 102 | */ 103 | var HtmlEachColumns = []vtab.Column{ 104 | {Name: "document", Type: sqlite.SQLITE_TEXT.String(), NotNull: true, Hidden: true, Filters: []*vtab.ColumnFilter{{Op: sqlite.INDEX_CONSTRAINT_EQ, OmitCheck: true}}}, 105 | {Name: "selector", Type: sqlite.SQLITE_TEXT.String(), NotNull: true, Hidden: true, Filters: []*vtab.ColumnFilter{{Op: sqlite.INDEX_CONSTRAINT_EQ, OmitCheck: true}}}, 106 | 107 | {Name: "html", Type: sqlite.SQLITE_TEXT.String()}, 108 | {Name: "text", Type: sqlite.SQLITE_TEXT.String()}, 109 | } 110 | 111 | type HtmlEachCursor struct { 112 | current int 113 | 114 | document *goquery.Document 115 | children *goquery.Selection 116 | } 117 | 118 | func (cur *HtmlEachCursor) Column(ctx vtab.Context, c int) error { 119 | 120 | col := HtmlEachColumns[c].Name 121 | switch col { 122 | case "document": 123 | ctx.ResultText("") 124 | case "selector": 125 | ctx.ResultText("") 126 | 127 | case "html": 128 | html, err := goquery.OuterHtml(cur.children.Eq(cur.current)) 129 | if err != nil { 130 | ctx.ResultError(err) 131 | } else { 132 | ctx.ResultText(html) 133 | ctx.ResultSubType(HTML_SUBTYPE) 134 | } 135 | case "text": 136 | ctx.ResultText(cur.children.Eq(cur.current).Text()) 137 | } 138 | return nil 139 | } 140 | 141 | func (cur *HtmlEachCursor) Next() (vtab.Row, error) { 142 | cur.current += 1 143 | if cur.current >= cur.children.Size() { 144 | return nil, io.EOF 145 | } 146 | return cur, nil 147 | } 148 | 149 | func HtmlEachIterator(constraints []*vtab.Constraint, order []*sqlite.OrderBy) (vtab.Iterator, error) { 150 | document := "" 151 | selector := "" 152 | 153 | for _, constraint := range constraints { 154 | if constraint.Op == sqlite.INDEX_CONSTRAINT_EQ { 155 | switch constraint.ColIndex { 156 | case 0: 157 | document = constraint.Value.Text() 158 | case 1: 159 | selector = constraint.Value.Text() 160 | } 161 | } 162 | } 163 | 164 | doc, err := goquery.NewDocumentFromReader(strings.NewReader(document)) 165 | if err != nil { 166 | return nil, sqlite.SQLITE_ABORT 167 | } 168 | 169 | children := doc.Find(selector) 170 | current := -1 171 | 172 | return &HtmlEachCursor{ 173 | current: current, 174 | document: doc, 175 | children: children, 176 | }, nil 177 | } 178 | 179 | func RegisterQuery(api *sqlite.ExtensionApi) error { 180 | var err error 181 | if err = api.CreateFunction("html_extract", &HtmlExtractFunc{}); err != nil { 182 | return err 183 | } 184 | if err = api.CreateFunction("html_text", &HtmlTextFunc{nArgs: 1}); err != nil { 185 | return err 186 | } 187 | if err = api.CreateFunction("html_text", &HtmlTextFunc{nArgs: 2}); err != nil { 188 | return err 189 | } 190 | if err = api.CreateFunction("html_count", &HtmlCountFunc{}); err != nil { 191 | return err 192 | } 193 | if err = api.CreateModule("html_each", vtab.NewTableFunc("html_each", HtmlEachColumns, HtmlEachIterator)); err != nil { 194 | return err 195 | } 196 | return nil 197 | } 198 | -------------------------------------------------------------------------------- /deno/deno.lock: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2", 3 | "remote": { 4 | "https://deno.land/std@0.152.0/_util/assert.ts": "e94f2eb37cebd7f199952e242c77654e43333c1ac4c5c700e929ea3aa5489f74", 5 | "https://deno.land/std@0.152.0/_util/os.ts": "3b4c6e27febd119d36a416d7a97bd3b0251b77c88942c8f16ee5953ea13e2e49", 6 | "https://deno.land/std@0.152.0/path/_constants.ts": "df1db3ffa6dd6d1252cc9617e5d72165cd2483df90e93833e13580687b6083c3", 7 | "https://deno.land/std@0.152.0/path/_interface.ts": "ee3b431a336b80cf445441109d089b70d87d5e248f4f90ff906820889ecf8d09", 8 | "https://deno.land/std@0.152.0/path/_util.ts": "c1e9686d0164e29f7d880b2158971d805b6e0efc3110d0b3e24e4b8af2190d2b", 9 | "https://deno.land/std@0.152.0/path/common.ts": "bee563630abd2d97f99d83c96c2fa0cca7cee103e8cb4e7699ec4d5db7bd2633", 10 | "https://deno.land/std@0.152.0/path/glob.ts": "cb5255638de1048973c3e69e420c77dc04f75755524cb3b2e160fe9277d939ee", 11 | "https://deno.land/std@0.152.0/path/mod.ts": "56fec03ad0ebd61b6ab39ddb9b0ddb4c4a5c9f2f4f632e09dd37ec9ebfd722ac", 12 | "https://deno.land/std@0.152.0/path/posix.ts": "c1f7afe274290ea0b51da07ee205653b2964bd74909a82deb07b69a6cc383aaa", 13 | "https://deno.land/std@0.152.0/path/separator.ts": "fe1816cb765a8068afb3e8f13ad272351c85cbc739af56dacfc7d93d710fe0f9", 14 | "https://deno.land/std@0.152.0/path/win32.ts": "bd7549042e37879c68ff2f8576a25950abbfca1d696d41d82c7bca0b7e6f452c", 15 | "https://deno.land/std@0.176.0/_util/asserts.ts": "178dfc49a464aee693a7e285567b3d0b555dc805ff490505a8aae34f9cfb1462", 16 | "https://deno.land/std@0.176.0/_util/os.ts": "d932f56d41e4f6a6093d56044e29ce637f8dcc43c5a90af43504a889cf1775e3", 17 | "https://deno.land/std@0.176.0/encoding/hex.ts": "50f8c95b52eae24395d3dfcb5ec1ced37c5fe7610ef6fffdcc8b0fdc38e3b32f", 18 | "https://deno.land/std@0.176.0/fmt/colors.ts": "938c5d44d889fb82eff6c358bea8baa7e85950a16c9f6dae3ec3a7a729164471", 19 | "https://deno.land/std@0.176.0/fs/_util.ts": "65381f341af1ff7f40198cee15c20f59951ac26e51ddc651c5293e24f9ce6f32", 20 | "https://deno.land/std@0.176.0/fs/copy.ts": "14214efd94fc3aa6db1e4af2b4b9578e50f7362b7f3725d5a14ad259a5df26c8", 21 | "https://deno.land/std@0.176.0/fs/empty_dir.ts": "c3d2da4c7352fab1cf144a1ecfef58090769e8af633678e0f3fabaef98594688", 22 | "https://deno.land/std@0.176.0/fs/ensure_dir.ts": "724209875497a6b4628dfb256116e5651c4f7816741368d6c44aab2531a1e603", 23 | "https://deno.land/std@0.176.0/fs/ensure_file.ts": "c38602670bfaf259d86ca824a94e6cb9e5eb73757fefa4ebf43a90dd017d53d9", 24 | "https://deno.land/std@0.176.0/fs/ensure_link.ts": "c0f5b2f0ec094ed52b9128eccb1ee23362a617457aa0f699b145d4883f5b2fb4", 25 | "https://deno.land/std@0.176.0/fs/ensure_symlink.ts": "2955cc8332aeca9bdfefd05d8d3976b94e282b0f353392a71684808ed2ffdd41", 26 | "https://deno.land/std@0.176.0/fs/eol.ts": "f1f2eb348a750c34500741987b21d65607f352cf7205f48f4319d417fff42842", 27 | "https://deno.land/std@0.176.0/fs/exists.ts": "b8c8a457b71e9d7f29b9d2f87aad8dba2739cbe637e8926d6ba6e92567875f8e", 28 | "https://deno.land/std@0.176.0/fs/expand_glob.ts": "45d17e89796a24bd6002e4354eda67b4301bb8ba67d2cac8453cdabccf1d9ab0", 29 | "https://deno.land/std@0.176.0/fs/mod.ts": "bc3d0acd488cc7b42627044caf47d72019846d459279544e1934418955ba4898", 30 | "https://deno.land/std@0.176.0/fs/move.ts": "4cb47f880e3f0582c55e71c9f8b1e5e8cfaacb5e84f7390781dd563b7298ec19", 31 | "https://deno.land/std@0.176.0/fs/walk.ts": "ea95ffa6500c1eda6b365be488c056edc7c883a1db41ef46ec3bf057b1c0fe32", 32 | "https://deno.land/std@0.176.0/path/_constants.ts": "e49961f6f4f48039c0dfed3c3f93e963ca3d92791c9d478ac5b43183413136e0", 33 | "https://deno.land/std@0.176.0/path/_interface.ts": "6471159dfbbc357e03882c2266d21ef9afdb1e4aa771b0545e90db58a0ba314b", 34 | "https://deno.land/std@0.176.0/path/_util.ts": "d7abb1e0dea065f427b89156e28cdeb32b045870acdf865833ba808a73b576d0", 35 | "https://deno.land/std@0.176.0/path/common.ts": "ee7505ab01fd22de3963b64e46cff31f40de34f9f8de1fff6a1bd2fe79380000", 36 | "https://deno.land/std@0.176.0/path/glob.ts": "d479e0a695621c94d3fd7fe7abd4f9499caf32a8de13f25073451c6ef420a4e1", 37 | "https://deno.land/std@0.176.0/path/mod.ts": "4b83694ac500d7d31b0cdafc927080a53dc0c3027eb2895790fb155082b0d232", 38 | "https://deno.land/std@0.176.0/path/posix.ts": "8b7c67ac338714b30c816079303d0285dd24af6b284f7ad63da5b27372a2c94d", 39 | "https://deno.land/std@0.176.0/path/separator.ts": "0fb679739d0d1d7bf45b68dacfb4ec7563597a902edbaf3c59b50d5bcadd93b1", 40 | "https://deno.land/std@0.176.0/path/win32.ts": "d186344e5583bcbf8b18af416d13d82b35a317116e6460a5a3953508c3de5bba", 41 | "https://deno.land/std@0.177.0/fmt/colors.ts": "938c5d44d889fb82eff6c358bea8baa7e85950a16c9f6dae3ec3a7a729164471", 42 | "https://deno.land/std@0.177.0/testing/_diff.ts": "1a3c044aedf77647d6cac86b798c6417603361b66b54c53331b312caeb447aea", 43 | "https://deno.land/std@0.177.0/testing/_format.ts": "a69126e8a469009adf4cf2a50af889aca364c349797e63174884a52ff75cf4c7", 44 | "https://deno.land/std@0.177.0/testing/asserts.ts": "984ab0bfb3faeed92ffaa3a6b06536c66811185328c5dd146257c702c41b01ab", 45 | "https://deno.land/x/plug@1.0.1/deps.ts": "35ea2acd5e3e11846817a429b7ef4bec47b80f2d988f5d63797147134cbd35c2", 46 | "https://deno.land/x/plug@1.0.1/download.ts": "8d6a023ade0806a0653b48cd5f6f8b15fcfaa1dbf2aa1f4bc90fc5732d27b144", 47 | "https://deno.land/x/plug@1.0.1/mod.ts": "5dec80ee7a3a325be45c03439558531bce7707ac118f4376cebbd6740ff24bfb", 48 | "https://deno.land/x/plug@1.0.1/types.ts": "d8eb738fc6ed883e6abf77093442c2f0b71af9090f15c7613621d4039e410ee1", 49 | "https://deno.land/x/plug@1.0.1/util.ts": "5ba8127b9adc36e070b9e22971fb8106869eea1741f452a87b4861e574f13481", 50 | "https://deno.land/x/sqlite3@0.8.0/deno.json": "61fbd0665a1b48f5e0f1773371d49b776f559cd6c3747a4e674adc3eb423686c", 51 | "https://deno.land/x/sqlite3@0.8.0/deps.ts": "722c865b9cef27b4cde0bb1ac9ebb08e94c43ad090a7313cea576658ff1e3bb0", 52 | "https://deno.land/x/sqlite3@0.8.0/mod.ts": "d41b8b30e1b20b537ef4d78cae98d90f6bd65c727b64aa1a18bffbb28f7d6ec3", 53 | "https://deno.land/x/sqlite3@0.8.0/src/blob.ts": "a956fc0cf4a8c7a21dc3fcb71a07ef773bcd08b5fd72e8ace89b1bfbd031bf06", 54 | "https://deno.land/x/sqlite3@0.8.0/src/constants.ts": "85fd27aa6e199093f25f5f437052e16fd0e0870b96ca9b24a98e04ddc8b7d006", 55 | "https://deno.land/x/sqlite3@0.8.0/src/database.ts": "c68c7fdfa7548000ea7e194360cdce86b81b667aab6b0778ea7ed9b74a37b7cb", 56 | "https://deno.land/x/sqlite3@0.8.0/src/ffi.ts": "d5d5e3b4524cf0b980d23e57b08f75dc0debc7af2e93ff8b00cdc5af52637b31", 57 | "https://deno.land/x/sqlite3@0.8.0/src/statement.ts": "0b2b3c8b5564ad3c35f2c7d57607e5755c38b23b835e9a46aa1002e68e6ec3a2", 58 | "https://deno.land/x/sqlite3@0.8.0/src/util.ts": "9627ebecc7a5eb250d2df9386a456a9a9ed7842a20fe32be1ee6b7c663c77bd3" 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /elements.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "errors" 7 | "fmt" 8 | "strings" 9 | 10 | "github.com/PuerkitoBio/goquery" 11 | "go.riyazali.net/sqlite" 12 | "golang.org/x/net/html" 13 | ) 14 | 15 | // A random "magic" number to use for sqlite subtypes. 16 | // only the lower 8 bits are used. chose at random tbh 17 | // https://www.sqlite.org/c3ref/result_subtype.html 18 | var HTML_SUBTYPE = 0xdd 19 | 20 | func subtypeIsHtml(value sqlite.Value) bool { 21 | return value.SubType() == HTML_SUBTYPE 22 | } 23 | 24 | // https://github.com/sqlite/sqlite/blob/8b554e2a1ea4de0cb30a49357684836710f44905/ext/misc/json1.c#L159 25 | const JSON_SUBTYPE = 74 26 | 27 | /** html(document) 28 | * Verifies and "cleans" (quotes attributes) the given document as HTML. 29 | * Also sets the return subtype to the HTML magic number, for 30 | * use in other funcs like html_element to designate something as "HTML" 31 | **/ 32 | type HtmlFunc struct{} 33 | 34 | func (*HtmlFunc) Deterministic() bool { return true } 35 | func (*HtmlFunc) Args() int { return 1 } 36 | func (*HtmlFunc) Apply(c *sqlite.Context, values ...sqlite.Value) { 37 | html := values[0].Text() 38 | doc, err := goquery.NewDocumentFromReader(strings.NewReader(html)) 39 | 40 | if err != nil { 41 | c.ResultError(err) 42 | return 43 | } 44 | // goquery seems to wrap with "" stuff, so only get original 45 | outHtml, err := doc.Find("body").Html() 46 | 47 | if err != nil { 48 | c.ResultError(err) 49 | return 50 | } 51 | 52 | c.ResultText(outHtml) 53 | c.ResultSubType(HTML_SUBTYPE) 54 | } 55 | 56 | /** html_element(tag, attributes, child1, ...) 57 | * Create an HTML element with the given tag, attributes, and children. 58 | * Modeled after React.createElement https://reactjs.org/docs/react-without-jsx.html 59 | * @param tag {text} - required top-level tag name for the returned root element. 60 | * @param attributes {json} - should be a json object with string keys/values, for the attributes of the element. 61 | * @param children {text | html} - are either strings or html elements. 62 | * If 'children" is a string, then it will be rendered as a TextNode in the top-level element 63 | * If 'children" is an html element, from "html()" or "html_element()" then it will be rendered as a RawNode in the top-level element 64 | */ 65 | type HtmlElementFunc struct{} 66 | 67 | func (*HtmlElementFunc) Deterministic() bool { return true } 68 | func (*HtmlElementFunc) Args() int { return -1 } 69 | func (*HtmlElementFunc) Apply(c *sqlite.Context, values ...sqlite.Value) { 70 | var children []sqlite.Value 71 | if len(values) < 1 { 72 | c.ResultError(errors.New("html_element requires tag")) 73 | } 74 | tag := values[0].Text() 75 | if len(values) >= 3 { 76 | children = values[2:] 77 | } 78 | var attr []html.Attribute 79 | 80 | if len(values) > 1 && values[1].Type() != sqlite.SQLITE_NULL { 81 | rawAttrs := values[1].Text() 82 | 83 | var attrs map[string]interface{} 84 | if err := json.Unmarshal([]byte(rawAttrs), &attrs); err != nil { 85 | c.ResultError(errors.New("attributes is not a JSON object")) 86 | } 87 | 88 | for k, v := range attrs { 89 | var Val string 90 | switch t := v.(type) { 91 | case string: 92 | Val = t 93 | case int64: 94 | Val = fmt.Sprintf("%d", t) 95 | case float64: 96 | Val = fmt.Sprintf("%f", t) 97 | } 98 | attr = append(attr, html.Attribute{ 99 | Key: k, 100 | Val: Val, 101 | }) 102 | } 103 | 104 | } 105 | 106 | root := &html.Node{ 107 | Type: html.ElementNode, 108 | Data: tag, 109 | Attr: attr, 110 | } 111 | 112 | for _, v := range children { 113 | var child *html.Node 114 | childData := v.Text() 115 | 116 | if subtypeIsHtml(v) { 117 | child = &html.Node{ 118 | Type: html.RawNode, 119 | Data: childData, 120 | } 121 | } else { 122 | child = &html.Node{ 123 | Type: html.TextNode, 124 | Data: childData, 125 | } 126 | } 127 | root.AppendChild(child) 128 | } 129 | 130 | var buf bytes.Buffer 131 | html.Render(&buf, root) 132 | c.ResultText(buf.String()) 133 | c.ResultSubType(HTML_SUBTYPE) 134 | } 135 | 136 | type HtmlGroupElementFunc struct{ 137 | parent string 138 | } 139 | 140 | func (h *HtmlGroupElementFunc) Args() int { return -1 } 141 | func (h *HtmlGroupElementFunc) Deterministic() bool { return true } 142 | 143 | type HtmlGroupElementContext struct { 144 | root *html.Node 145 | } 146 | 147 | func (s *HtmlGroupElementFunc) Step(ctx *sqlite.AggregateContext, values ...sqlite.Value) { 148 | if len(values) < 1 { 149 | ctx.ResultError(errors.New("html_element requires tag")) 150 | } 151 | 152 | if ctx.Data() == nil { 153 | root := &html.Node{ 154 | Type: html.ElementNode, 155 | Data: s.parent, 156 | Attr: nil, 157 | } 158 | ctx.SetData(&HtmlGroupElementContext{root:root,}) 159 | } 160 | 161 | var gCtx = ctx.Data().(*HtmlGroupElementContext) 162 | var children []sqlite.Value 163 | 164 | if len(values) >= 3 { 165 | children = values[2:] 166 | } 167 | var attr []html.Attribute 168 | 169 | if len(values) > 1 && values[1].Type() != sqlite.SQLITE_NULL { 170 | rawAttrs := values[1].Text() 171 | 172 | var attrs map[string]string 173 | if err := json.Unmarshal([]byte(rawAttrs), &attrs); err != nil { 174 | ctx.ResultError(errors.New("attributes is not a JSON object")) 175 | } 176 | 177 | for k, v := range attrs { 178 | attr = append(attr, html.Attribute{ 179 | Key: k, 180 | Val: v, 181 | }) 182 | } 183 | 184 | } 185 | 186 | 187 | node := &html.Node{ 188 | Type: html.ElementNode, 189 | Data: values[0].Text(), 190 | Attr: attr, 191 | } 192 | for _, v := range children { 193 | var child *html.Node 194 | childData := v.Text() 195 | 196 | if subtypeIsHtml(v) { 197 | child = &html.Node{ 198 | Type: html.RawNode, 199 | Data: childData, 200 | } 201 | } else { 202 | child = &html.Node{ 203 | Type: html.TextNode, 204 | Data: childData, 205 | } 206 | } 207 | node.AppendChild(child) 208 | } 209 | gCtx.root.AppendChild(node) 210 | } 211 | 212 | func (s *HtmlGroupElementFunc) Final(ctx *sqlite.AggregateContext) { 213 | if ctx.Data() != nil { 214 | var buf bytes.Buffer 215 | var gCtx = ctx.Data().(*HtmlGroupElementContext) 216 | html.Render(&buf, gCtx.root) 217 | ctx.ResultText(buf.String()) 218 | ctx.ResultSubType(HTML_SUBTYPE) 219 | } 220 | } 221 | 222 | func RegisterElements(api *sqlite.ExtensionApi) error { 223 | var err error 224 | if err = api.CreateFunction("html", &HtmlFunc{}); err != nil { 225 | return err 226 | } 227 | if err = api.CreateFunction("html_element", &HtmlElementFunc{}); err != nil { 228 | return err 229 | } 230 | if err = api.CreateFunction("html_group_element_div", &HtmlGroupElementFunc{parent: "div"}); err != nil { 231 | return err 232 | } 233 | if err = api.CreateFunction("html_group_element_span", &HtmlGroupElementFunc{parent: "span"}); err != nil { 234 | return err 235 | } 236 | return nil 237 | } 238 | -------------------------------------------------------------------------------- /tests/test-loadable.py: -------------------------------------------------------------------------------- 1 | import sqlite3 2 | import unittest 3 | 4 | EXT_PATH = "dist/html0" 5 | 6 | FUNCTIONS = [ 7 | "html", 8 | "html_attr_get", 9 | "html_attr_has", 10 | "html_attribute_get", 11 | "html_attribute_has", 12 | "html_count", 13 | "html_debug", 14 | "html_element", 15 | "html_escape", 16 | "html_extract", 17 | "html_group_element_div", 18 | "html_group_element_span", 19 | "html_table", 20 | "html_text", 21 | "html_text", 22 | "html_trim", 23 | "html_unescape", 24 | "html_valid", 25 | "html_version", 26 | ] 27 | MODULES = [ 28 | "html_each" 29 | ] 30 | 31 | ALIASES = ["html_attr_get", "html_attr_has"] 32 | 33 | def connect(ext): 34 | db = sqlite3.connect(":memory:") 35 | 36 | db.execute("create table base_functions as select name from pragma_function_list") 37 | db.execute("create table base_modules as select name from pragma_module_list") 38 | 39 | db.enable_load_extension(True) 40 | db.load_extension(ext) 41 | 42 | db.execute("create temp table loaded_functions as select name from pragma_function_list where name not in (select name from base_functions) order by name") 43 | db.execute("create temp table loaded_modules as select name from pragma_module_list where name not in (select name from base_modules) order by name") 44 | 45 | db.row_factory = sqlite3.Row 46 | return db 47 | 48 | 49 | db = connect(EXT_PATH) 50 | 51 | class TestHtml(unittest.TestCase): 52 | def test_funcs(self): 53 | funcs = list(map(lambda a: a[0], db.execute("select name from loaded_functions").fetchall())) 54 | self.assertEqual(funcs, FUNCTIONS) 55 | 56 | def test_modules(self): 57 | funcs = list(map(lambda a: a[0], db.execute("select name from loaded_modules").fetchall())) 58 | self.assertEqual(funcs, MODULES) 59 | 60 | def test_html_version(self): 61 | with open("./VERSION") as f: 62 | version = "v" + f.read() 63 | v, = db.execute("select html_version()").fetchone() 64 | self.assertEqual(v, version) 65 | 66 | def test_html_debug(self): 67 | d, = db.execute("select html_debug()").fetchone() 68 | lines = d.splitlines() 69 | self.assertEqual(len(lines), 4) 70 | self.assertTrue(lines[0].startswith("Version")) 71 | self.assertTrue(lines[1].startswith("Commit")) 72 | self.assertTrue(lines[2].startswith("Runtime")) 73 | self.assertTrue(lines[3].startswith("Date")) 74 | 75 | def test_html_table(self): 76 | d, = db.execute("select html_table('a')").fetchone() 77 | self.assertEqual(d, "
a") 78 | 79 | def test_html_escape(self): 80 | d, = db.execute("select html_escape('')").fetchone() 81 | self.assertEqual(d, "<a>") 82 | 83 | def test_html_unescape(self): 84 | d, = db.execute("select html_unescape('<a')").fetchone() 85 | self.assertEqual(d, "b'), html('b

") 98 | # TODO what should this do 99 | self.assertEqual(c, None) 100 | 101 | def test_html_element(self): 102 | a, b, c = db.execute("""select 103 | html_element('p', null, "ayoo"), 104 | html_element('p', json_object("class", "cool"), "ayoo"), 105 | html_element( 106 | 'p', 107 | null, 108 | html_element("span", null, "My name is "), 109 | html_element("b", null, "Alex Garcia"), 110 | "." 111 | ) 112 | """).fetchone() 113 | self.assertEqual(a, "

ayoo

") 114 | self.assertEqual(b, "

ayoo

") 115 | # TODO what should this do 116 | self.assertEqual(c, "

My name is Alex Garcia.

") 117 | 118 | def test_html_attribute_has(self): 119 | a, b, c = db.execute("""select 120 | html_attribute_has('

', 'p', 'x'), 121 | html_attribute_has('

abc', 'p', 'x'), 122 | html_attr_has('

yo', 'span', 'z' ) 123 | """).fetchone() 124 | self.assertEqual(a, 1) 125 | self.assertEqual(b, 0) 126 | self.assertEqual(c, 1) 127 | 128 | def test_html_attribute_get(self): 129 | a, b, c = db.execute("""select 130 | html_attribute_get('

', 'p', 'x'), 131 | html_attribute_get('

abc', 'p', 'x'), 132 | html_attr_get('

yo', 'span', 'x' ) 133 | """).fetchone() 134 | self.assertEqual(a, None) 135 | self.assertEqual(b, None) 136 | self.assertEqual(c, "z") 137 | 138 | def test_html_extract(self): 139 | a, b, c = db.execute("""select 140 | html_extract('

asdfasdf

abc

asdfasdf
', 'p'), 141 | html_extract('

abc', 'p'), 142 | html_extract('

asdf

', 'x') 143 | """).fetchone() 144 | self.assertEqual(a, "

abc

") 145 | self.assertEqual(b, "

abc

") 146 | self.assertEqual(c, None) 147 | 148 | def test_html_text(self): 149 | a, b, c = db.execute("""select 150 | html_text('
asdfasdf

abc

asdfasdf
', 'p'), 151 | html_text('

abc

'), 152 | html_text('

asdf

', 'x') 153 | """).fetchone() 154 | self.assertEqual(a, "abc") 155 | self.assertEqual(b, "abc") 156 | self.assertEqual(c, None) 157 | 158 | def test_html_valid(self): 159 | html_valid = lambda x: db.execute("select html_valid(?)", [x]).fetchone()[0] 160 | self.assertEqual(html_valid("
a"), 1) 161 | # TODO wtf isn't valid HTML 162 | 163 | def test_html_group_element_div(self): 164 | self.skipTest("") 165 | def test_html_group_element_span(self): 166 | self.skipTest("") 167 | 168 | def test_html_count(self): 169 | a, b, c = db.execute("""select 170 | html_count('
', 'p'), 171 | html_count('

abc', 'p'), 172 | html_count('

a

', 'p.x') 173 | """).fetchone() 174 | self.assertEqual(a, 0) 175 | self.assertEqual(b, 1) 176 | self.assertEqual(c, 2) 177 | 178 | def test_html_each(self): 179 | rows = db.execute("""select rowid, * 180 | from html_each('

181 |

a

182 |

b

183 |

c1c2

184 | ', 'p') 185 | """).fetchall() 186 | 187 | self.assertEqual(list(map(lambda x: dict(x), rows)), [ 188 | {"rowid":0,"html":"

a

","text":"a"}, 189 | {"rowid":1,"html":"

b

","text":"b"}, 190 | {"rowid":2,"html":"

c1c2

","text":"c1c2"} 191 | ]) 192 | 193 | class TestCoverage(unittest.TestCase): 194 | def test_coverage(self): 195 | test_methods = [method for method in dir(TestHtml) if method.startswith('test_html')] 196 | funcs_with_tests = set([x.replace("test_", "") for x in test_methods]) 197 | for func in FUNCTIONS: 198 | if func in ALIASES: continue 199 | self.assertTrue(func in funcs_with_tests, f"{func} does not have cooresponding test in {funcs_with_tests}") 200 | 201 | if __name__ == '__main__': 202 | unittest.main() -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # sqlite-html 2 | 3 | A SQLite extension for querying, manipulating, and creating HTML elements. 4 | 5 | - Extract HTML or text from HTML with CSS selectors, like `.querySelector()`, `.innerHTML`, and `.innerText` 6 | - Generate a table of matching elements from a CSS selector, like `.querySelectorAll()` 7 | - Safely create HTML elements in a query, like `.createElement()` and `.appendChild()` 8 | 9 | `sqlite-html`'s API is modeled after the official [JSON1](https://www.sqlite.org/json1.html#jmini) SQLite extension. 10 | 11 | This extension is written in Go, thanks to [riyaz-ali/sqlite](https://github.com/riyaz-ali/sqlite). While this library aims to be fast and efficient, it is overall slower than what a pure C SQLite extension could be, but in practice you may not notice much of a difference. 12 | 13 | ## Usage 14 | 15 | ```sql 16 | .load ./html0 17 | select html_extract('

Anakin Skywalker

', 'b'); 18 | -- "Skywalker" 19 | ``` 20 | 21 | `sqlite-html` is similar to other HTML scraping tools like [BeautifulSoup](https://beautiful-soup-4.readthedocs.io/en/latest/) (Python) or [cheerio](https://cheerio.js.org/) (Node.js) or [nokogiri](https://nokogiri.org/) (Ruby). You can use CSS selectors to extract individual elements or groups of elements to query data from HTML sources. 22 | 23 | For example, here we find all `href` links in an `index.html` file. 24 | 25 | ```sql 26 | select 27 | text as name, 28 | html_attribute_get(anchors, 'a', 'href') as href 29 | from html_each(readfile('index.html'), 'a') as anchors 30 | ``` 31 | 32 | We can also safely generate HTML with `html_element`, modeled after React's [`React.createElement`](https://reactjs.org/docs/react-api.html#createelement). 33 | 34 | ```sql 35 | select html_element('p', null, 36 | 'Luke, I am your', 37 | html_element('b', null, 'father'), 38 | '!', 39 | 40 | html_element('img', json_object( 41 | 'src', 'https://images.dog.ceo/breeds/groenendael/n02105056_4600.jpg', 42 | 'width', 200 43 | )) 44 | ); 45 | 46 | -- "

Luke, I am yourfather!

" 47 | ``` 48 | 49 | ## Documentation 50 | 51 | See [`docs.md`](./docs.md) for a full API reference. 52 | 53 | ## Installing 54 | 55 | | Language | Install | | 56 | | -------------- | ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | 57 | | Python | `pip install sqlite-html` | [![PyPI](https://img.shields.io/pypi/v/sqlite-html.svg?color=blue&logo=python&logoColor=white)](https://pypi.org/project/sqlite-html/) | 58 | | Datasette | `datasette install datasette-sqlite-html` | [![Datasette](https://img.shields.io/pypi/v/datasette-sqlite-html.svg?color=B6B6D9&label=Datasette+plugin&logoColor=white&logo=python)](https://datasette.io/plugins/datasette-sqlite-html) | 59 | | Node.js | `npm install sqlite-html` | [![npm](https://img.shields.io/npm/v/sqlite-html.svg?color=green&logo=nodedotjs&logoColor=white)](https://www.npmjs.com/package/sqlite-html) | 60 | | Deno | [`deno.land/x/sqlite_html`](https://deno.land/x/sqlite_html) | [![deno.land/x release](https://img.shields.io/github/v/release/asg017/sqlite-html?color=fef8d2&include_prereleases&label=deno.land%2Fx&logo=deno)](https://deno.land/x/sqlite_html) | 61 | | Ruby | `gem install sqlite-html` | ![Gem](https://img.shields.io/gem/v/sqlite-html?color=red&logo=rubygems&logoColor=white) | 62 | | Github Release | | ![GitHub tag (latest SemVer pre-release)](https://img.shields.io/github/v/tag/asg017/sqlite-html?color=lightgrey&include_prereleases&label=Github+release&logo=github) | 63 | 64 | 69 | 70 | The [Releases page](https://github.com/asg017/sqlite-html/releases) contains pre-built binaries for Linux amd64, MacOS amd64 (no arm), and Windows. 71 | 72 | ### As a loadable extension 73 | 74 | If you want to use `sqlite-html` as a [Runtime-loadable extension](https://www.sqlite.org/loadext.html), Download the `html0.dylib` (for MacOS), `html0.so` (Linux), or `html0.dll` (Windows) file from a release and load it into your SQLite environment. 75 | 76 | > **Note:** 77 | > The `0` in the filename (`html0.dylib`/ `html0.so`/`html0.dll`) denotes the major version of `sqlite-html`. Currently `sqlite-html` is pre v1, so expect breaking changes in future versions. 78 | 79 | For example, if you are using the [SQLite CLI](https://www.sqlite.org/cli.html), you can load the library like so: 80 | 81 | ```sql 82 | .load ./html0 83 | select html_version(); 84 | -- v0.0.1 85 | ``` 86 | 87 | Or in Python, using the builtin [sqlite3 module](https://docs.python.org/3/library/sqlite3.html): 88 | 89 | ```python 90 | import sqlite3 91 | 92 | con = sqlite3.connect(":memory:") 93 | 94 | con.enable_load_extension(True) 95 | con.load_extension("./html0") 96 | 97 | print(con.execute("select html_version()").fetchone()) 98 | # ('v0.0.1',) 99 | ``` 100 | 101 | Or in Node.js using [better-sqlite3](https://github.com/WiseLibs/better-sqlite3): 102 | 103 | ```javascript 104 | const Database = require("better-sqlite3"); 105 | const db = new Database(":memory:"); 106 | 107 | db.loadExtension("./html0"); 108 | 109 | console.log(db.prepare("select html_version()").get()); 110 | // { 'html_version()': 'v0.0.1' } 111 | ``` 112 | 113 | Or with [Datasette](https://datasette.io/): 114 | 115 | ``` 116 | datasette data.db --load-extension ./html0 117 | ``` 118 | 119 | ## See also 120 | 121 | - [sqlite-http](https://github.com/asg017/sqlite-http), for making HTTP requests in SQLite (pairs great with this tool) 122 | - [htmlq](https://github.com/mgdm/htmlq), for a similar but CLI-based HTML query tool using CSS selectors 123 | - [riyaz-ali/sqlite](https://github.com/riyaz-ali/sqlite), the brilliant Go library that this library depends on 124 | - [nalgeon/sqlean](https://github.com/nalgeon/sqlean), several pre-compiled handy SQLite functions, in C 125 | -------------------------------------------------------------------------------- /.github/workflows/test.yaml: -------------------------------------------------------------------------------- 1 | on: [push] 2 | name: "test" 3 | jobs: 4 | build-linux_x86-extension: 5 | runs-on: ubuntu-20.04 6 | steps: 7 | - uses: actions/checkout@v2 8 | # using actions/setup-go@v1 is the only way to get macos build to work. 9 | # otherwise, with v2, would get this cgo error: 'cgo-generated-wrappers:13:13: error: redefinition of 'free' as different kind of symbol' 10 | - name: Set up Go 1.20 11 | uses: actions/setup-go@v1 12 | with: 13 | go-version: 1.20 14 | - run: make loadable 15 | - uses: actions/upload-artifact@v3 16 | with: 17 | name: sqlite-html-linux_x86 18 | path: dist/html0.so 19 | build-linux_x86-python: 20 | runs-on: ubuntu-20.04 21 | needs: [build-linux_x86-extension] 22 | steps: 23 | - uses: actions/checkout@v3 24 | - name: Download workflow artifacts 25 | uses: actions/download-artifact@v3 26 | with: 27 | name: sqlite-html-linux_x86 28 | path: dist/ 29 | - uses: actions/setup-python@v3 30 | - run: pip install wheel 31 | - run: make python 32 | - run: make datasette 33 | - uses: actions/upload-artifact@v3 34 | with: 35 | name: sqlite-html-linux_x86-wheels 36 | path: dist/wheels/*.whl 37 | test-linux_x86: 38 | runs-on: ubuntu-20.04 39 | needs: [build-linux_x86-extension, build-linux_x86-python] 40 | env: 41 | DENO_DIR: deno_cache 42 | steps: 43 | - uses: actions/checkout@v3 44 | - uses: actions/download-artifact@v3 45 | with: 46 | name: sqlite-html-linux_x86 47 | path: dist/ 48 | - uses: actions/download-artifact@v3 49 | with: 50 | name: sqlite-html-linux_x86-wheels 51 | path: dist/ 52 | - run: cp dist/html0.so npm/sqlite-html-linux-x64/lib 53 | - run: pip3 install --find-links dist/ sqlite_html 54 | - run: make test-loadable 55 | - run: make test-python 56 | # for test-npm 57 | - uses: actions/setup-node@v3 58 | with: 59 | cache: "npm" 60 | cache-dependency-path: npm/sqlite-html/package.json 61 | - run: npm install 62 | working-directory: npm/sqlite-html 63 | - run: make test-npm 64 | # for test-deno 65 | - uses: denoland/setup-deno@v1 66 | with: 67 | deno-version: v1.30 68 | - name: Cache Deno dependencies 69 | uses: actions/cache@v3 70 | with: 71 | path: ${{ env.DENO_DIR }} 72 | key: ${{ runner.os }}-${{ hashFiles('deno/deno.lock') }} 73 | - run: make test-deno 74 | env: 75 | DENO_SQLITE_HTML_PATH: ${{ github.workspace }}/dist/html0 76 | build-macos-extension: 77 | runs-on: macos-latest 78 | steps: 79 | - uses: actions/checkout@v2 80 | # using actions/setup-go@v1 is the only way to get macos build to work. 81 | # otherwise, with v2, would get this cgo error: 'cgo-generated-wrappers:13:13: error: redefinition of 'free' as different kind of symbol' 82 | - name: Set up Go 1.20 83 | uses: actions/setup-go@v1 84 | with: 85 | go-version: 1.20 86 | - run: make loadable 87 | - uses: actions/upload-artifact@v3 88 | with: 89 | name: sqlite-html-macos 90 | path: dist/html0.dylib 91 | build-macos-python: 92 | runs-on: macos-latest 93 | needs: [build-macos-extension] 94 | steps: 95 | - uses: actions/checkout@v3 96 | - name: Download workflow artifacts 97 | uses: actions/download-artifact@v3 98 | with: 99 | name: sqlite-html-macos 100 | path: dist/ 101 | - uses: actions/setup-python@v3 102 | - run: pip install wheel 103 | - run: make python 104 | - run: make datasette 105 | - uses: actions/upload-artifact@v3 106 | with: 107 | name: sqlite-html-macos-wheels 108 | path: dist/wheels/*.whl 109 | test-macos: 110 | runs-on: macos-latest 111 | needs: [build-macos-extension, build-macos-python] 112 | env: 113 | DENO_DIR: deno_cache 114 | steps: 115 | - uses: actions/checkout@v3 116 | - uses: actions/download-artifact@v3 117 | with: 118 | name: sqlite-html-macos 119 | path: dist/ 120 | - run: cp dist/html0.dylib npm/sqlite-html-darwin-x64/lib 121 | - uses: actions/download-artifact@v3 122 | with: 123 | name: sqlite-html-macos-wheels 124 | path: dist/ 125 | - run: brew install python 126 | - run: /usr/local/opt/python@3/libexec/bin/pip install --find-links dist/ sqlite_html 127 | - run: make test-loadable python=/usr/local/opt/python@3/libexec/bin/python 128 | - run: make test-python python=/usr/local/opt/python@3/libexec/bin/python 129 | # for test-npm 130 | - uses: actions/setup-node@v3 131 | with: 132 | cache: "npm" 133 | cache-dependency-path: npm/sqlite-html/package.json 134 | - run: npm install 135 | working-directory: npm/sqlite-html 136 | - run: make test-npm 137 | # for test-deno 138 | - uses: denoland/setup-deno@v1 139 | with: 140 | deno-version: v1.30 141 | - name: Cache Deno dependencies 142 | uses: actions/cache@v3 143 | with: 144 | path: ${{ env.DENO_DIR }} 145 | key: ${{ runner.os }}-${{ hashFiles('deno/deno.lock') }} 146 | - run: make test-deno 147 | env: 148 | DENO_SQLITE_HTML_PATH: ${{ github.workspace }}/dist/html0.dylib 149 | build-windows-extension: 150 | name: Building windows extension 151 | runs-on: windows-latest 152 | steps: 153 | - uses: actions/checkout@v2 154 | # using actions/setup-go@v1 is the only way to get macos build to work. 155 | # otherwise, with v2, would get this cgo error: 'cgo-generated-wrappers:13:13: error: redefinition of 'free' as different kind of symbol' 156 | - name: Set up Go 1.20 157 | uses: actions/setup-go@v1 158 | with: 159 | go-version: 1.20 160 | - run: make loadable 161 | - uses: actions/upload-artifact@v3 162 | with: 163 | name: sqlite-html-windows 164 | path: dist/html0.dll 165 | build-windows-python: 166 | runs-on: windows-latest 167 | needs: [build-windows-extension] 168 | steps: 169 | - uses: actions/checkout@v3 170 | - name: Download workflow artifacts 171 | uses: actions/download-artifact@v3 172 | with: 173 | name: sqlite-html-windows 174 | path: dist/ 175 | - uses: actions/setup-python@v3 176 | - run: pip install wheel 177 | - run: make python 178 | - run: make datasette 179 | - uses: actions/upload-artifact@v3 180 | with: 181 | name: sqlite-html-windows-wheels 182 | path: dist/wheels/*.whl 183 | test-windows: 184 | runs-on: windows-2022 185 | needs: [build-windows-extension, build-windows-python] 186 | env: 187 | DENO_DIR: deno_cache 188 | steps: 189 | - uses: actions/checkout@v3 190 | - uses: actions/download-artifact@v3 191 | with: 192 | name: sqlite-html-windows 193 | path: dist/ 194 | - uses: actions/download-artifact@v3 195 | with: 196 | name: sqlite-html-windows 197 | path: npm/sqlite-html-windows-x64/lib 198 | - uses: actions/download-artifact@v3 199 | with: 200 | name: sqlite-html-windows-wheels 201 | path: dist/ 202 | - run: pip install --find-links dist/ sqlite_html 203 | - run: make test-loadable 204 | -------------------------------------------------------------------------------- /docs.md: -------------------------------------------------------------------------------- 1 | # API Reference 2 | 3 | As a reminder, `sqlite-html` is still young, so breaking changes should be expected while `sqlite-html` is in a pre-v1 stage. 4 | 5 | ## Overview 6 | 7 | - `sqlite-html` information 8 | - [html_version](#html_version)() 9 | - [html_debug](#html_debug)() 10 | - Query HTML elements using CSS selectors 11 | - [html_each](#html_each)(_document, selector_) 12 | - [html_extract](#html_extract)(_document, selector_) 13 | - [html_text](#html_text)(_document, selector_) 14 | - [html_count](#html_count)(_document, selector_) 15 | - Safely generating HTML elements 16 | - [html](#html)(_document_) 17 | - [html_element](#html_element)(_tag, attributes, child1, ..._) 18 | - HTML attributes 19 | - [html_attribute_get](#html_attribute_get)(_document, selector, attribute_) 20 | - [html_attribute_has](#html_attribute_has)(_document, selector, attribute_) 21 | - Misc. HTML utilities 22 | - [html_escape](#html_escape)(_text_) 23 | - [html_unescape](#html_unescape)(_text_) 24 | - [html_trim](#html_trim)(_text_) 25 | - [html_table](#html_table)(_document_) 26 | 27 | ### Query HTML Elements 28 | 29 | #### `html_each()` 30 | 31 | A [table function](https://www.sqlite.org/vtab.html#tabfunc2) with the following schema: 32 | 33 | ```sql 34 | CREATE TABLE html_each( 35 | html TEXT, -- HTML of the extracted element 36 | text TEXT, -- textContent of the HTML element 37 | 38 | document TEXT hidden, -- input HTML document 39 | selector TEXT hidden -- input CSS selector 40 | ); 41 | ``` 42 | 43 | The `html` column contains the matching element's HTML representation. 44 | 45 | The `text` column contains the matching element's textContent representation, similar to the JavaScript DOM API's `.textContent` or the `html_text` function in this library. 46 | 47 | ```sql 48 | sqlite> select * from html_each('
"` before `contents`. 194 | 195 | While seemingly unnecessary, it's useful when dealing with HTML table `` rows. The string `example foo ` isn't valid HTML, so trying to extract `"foo"` won't work by itself. So wrapping `html_table()` before extracting from a `tr` element will allow for `sqlite-html` to parse correctly. 196 | 197 | ```sql 198 | select html_table('xyz'); 199 | -- "
xyz" 200 | 201 | -- Try removing the "html_table()" calls and see blank results 202 | select 203 | html_text(html_table(rows.html), 'td:nth-child(1)') as name, 204 | html_text(html_table(rows.html), 'td:nth-child(2)') as age 205 | from html_each('
206 | 207 | 208 | 209 |
Alex 1
Brian 2
Craig 3
', 'tr') as rows; 210 | /* 211 | ┌───────┬─────┐ 212 | │ name │ age │ 213 | ├───────┼─────┤ 214 | │ Alex │ 1 │ 215 | │ Brian │ 2 │ 216 | │ Craig │ 3 │ 217 | └───────┴─────┘ 218 | */ 219 | ``` 220 | 221 | ### `sqlite-html` Information 222 | 223 | #### `html_version()` 224 | 225 | Returns the version string of the `sqlite-html` library, modeled after [`sqlite_version()`](https://www.sqlite.org/lang_corefunc.html#sqlite_version). 226 | 227 | Examples: 228 | 229 | ```sql 230 | sqlite> select html_version(); 231 | v0.0.0 232 | ``` 233 | 234 | #### `html_debug()` 235 | 236 | Returns debug information of the `sqlite-html` library, including the version string. Subject to change. 237 | 238 | Examples: 239 | 240 | ```sql 241 | sqlite> select html_debug(); 242 | Version: v0.0.0 243 | Commit: 0cd144a880b47f4a57a5c7f8ceb96eb9dc821508 244 | Runtime: go1.17 darwin/amd64 245 | Date: 2021-11-17T17:06:12Z-0800 246 | ``` 247 | -------------------------------------------------------------------------------- /.github/workflows/release.yaml: -------------------------------------------------------------------------------- 1 | name: "release" 2 | on: 3 | release: 4 | types: [published] 5 | workflow_dispatch: 6 | permissions: 7 | contents: read 8 | jobs: 9 | build-linux_x86-extension: 10 | runs-on: ubuntu-20.04 11 | steps: 12 | - uses: actions/checkout@v2 13 | # using actions/setup-go@v1 is the only way to get macos build to work. 14 | # otherwise, with v2, would get this cgo error: 'cgo-generated-wrappers:13:13: error: redefinition of 'free' as different kind of symbol' 15 | - name: Set up Go 1.20 16 | uses: actions/setup-go@v1 17 | with: 18 | go-version: 1.20 19 | - run: make loadable 20 | - uses: actions/upload-artifact@v3 21 | with: 22 | name: sqlite-html-linux_x86 23 | path: dist/html0.so 24 | build-linux_x86-python: 25 | runs-on: ubuntu-20.04 26 | needs: [build-linux_x86-extension] 27 | steps: 28 | - uses: actions/checkout@v3 29 | - name: Download workflow artifacts 30 | uses: actions/download-artifact@v3 31 | with: 32 | name: sqlite-html-linux_x86 33 | path: dist/ 34 | - uses: actions/setup-python@v3 35 | - run: pip install wheel 36 | - run: make python 37 | - uses: actions/upload-artifact@v3 38 | with: 39 | name: sqlite-html-linux_x86-wheels 40 | path: dist/wheels/*.whl 41 | 42 | build-macos-extension: 43 | runs-on: macos-latest 44 | steps: 45 | - uses: actions/checkout@v2 46 | # using actions/setup-go@v1 is the only way to get macos build to work. 47 | # otherwise, with v2, would get this cgo error: 'cgo-generated-wrappers:13:13: error: redefinition of 'free' as different kind of symbol' 48 | - name: Set up Go 1.20 49 | uses: actions/setup-go@v1 50 | with: 51 | go-version: 1.20 52 | - run: make loadable 53 | - uses: actions/upload-artifact@v3 54 | with: 55 | name: sqlite-html-macos 56 | path: dist/html0.dylib 57 | build-macos-python: 58 | runs-on: macos-latest 59 | needs: [build-macos-extension] 60 | steps: 61 | - uses: actions/checkout@v3 62 | - name: Download workflow artifacts 63 | uses: actions/download-artifact@v3 64 | with: 65 | name: sqlite-html-macos 66 | path: dist/ 67 | - uses: actions/setup-python@v3 68 | - run: pip install wheel 69 | - run: make python 70 | - uses: actions/upload-artifact@v3 71 | with: 72 | name: sqlite-html-macos-wheels 73 | path: dist/wheels/*.whl 74 | build-macos-aarch64-extension: 75 | runs-on: macos-latest 76 | steps: 77 | - uses: actions/checkout@v2 78 | # using actions/setup-go@v1 is the only way to get macos build to work. 79 | # otherwise, with v2, would get this cgo error: 'cgo-generated-wrappers:13:13: error: redefinition of 'free' as different kind of symbol' 80 | - name: Set up Go 1.20 81 | uses: actions/setup-go@v1 82 | with: 83 | go-version: 1.20 84 | - run: GOOS=darwin GOARCH=arm64 CC="gcc -target arm64-apple-macos11" make loadable 85 | - uses: actions/upload-artifact@v3 86 | with: 87 | name: sqlite-html-macos-aarch64 88 | path: dist/html0.dylib 89 | build-macos-aarch64-python: 90 | runs-on: macos-latest 91 | needs: [build-macos-aarch64-extension] 92 | steps: 93 | - uses: actions/checkout@v3 94 | - name: Download workflow artifacts 95 | uses: actions/download-artifact@v3 96 | with: 97 | name: sqlite-html-macos-aarch64 98 | path: dist/ 99 | - uses: actions/setup-python@v3 100 | - run: pip install wheel 101 | - run: make python IS_MACOS_ARM=1 102 | - uses: actions/upload-artifact@v3 103 | with: 104 | name: sqlite-html-macos-aarch64-wheels 105 | path: dist/wheels/*.whl 106 | 107 | build-windows-extension: 108 | name: Building windows extension 109 | runs-on: windows-latest 110 | steps: 111 | - uses: actions/checkout@v2 112 | # using actions/setup-go@v1 is the only way to get macos build to work. 113 | # otherwise, with v2, would get this cgo error: 'cgo-generated-wrappers:13:13: error: redefinition of 'free' as different kind of symbol' 114 | - name: Set up Go 1.20 115 | uses: actions/setup-go@v1 116 | with: 117 | go-version: 1.20 118 | - run: make loadable 119 | - uses: actions/upload-artifact@v3 120 | with: 121 | name: sqlite-html-windows 122 | path: dist/html0.dll 123 | build-windows-python: 124 | runs-on: windows-latest 125 | needs: [build-windows-extension] 126 | steps: 127 | - uses: actions/checkout@v3 128 | - name: Download workflow artifacts 129 | uses: actions/download-artifact@v3 130 | with: 131 | name: sqlite-html-windows 132 | path: dist/ 133 | - uses: actions/setup-python@v3 134 | - run: pip install wheel 135 | - run: make python 136 | - uses: actions/upload-artifact@v3 137 | with: 138 | name: sqlite-html-windows-wheels 139 | path: dist/wheels/*.whl 140 | build-datasette-sqlite-utils: 141 | runs-on: ubuntu-20.04 142 | steps: 143 | - uses: actions/checkout@v3 144 | - uses: actions/setup-python@v3 145 | - run: pip install wheel build 146 | - run: make datasette sqlite-utils 147 | - uses: actions/upload-artifact@v3 148 | with: 149 | name: sqlite-html-datasette-sqlite-utils-wheels 150 | path: dist/wheels/*.whl 151 | upload-extensions: 152 | name: Upload release assets 153 | needs: 154 | [ 155 | build-macos-extension, 156 | build-macos-aarch64-extension, 157 | build-linux_x86-extension, 158 | build-windows-extension, 159 | ] 160 | permissions: 161 | contents: write 162 | runs-on: ubuntu-latest 163 | steps: 164 | - uses: actions/checkout@v2 165 | - uses: actions/download-artifact@v2 166 | - uses: asg017/upload-spm@main 167 | id: upload-spm 168 | with: 169 | name: sqlite-html 170 | github-token: ${{ secrets.GITHUB_TOKEN }} 171 | platforms: | 172 | linux-x86_64: sqlite-html-linux_x86/* 173 | macos-x86_64: sqlite-html-macos/* 174 | macos-aarch64: sqlite-html-macos-aarch64/* 175 | windows-x86_64: sqlite-html-windows/* 176 | upload-deno: 177 | name: Upload Deno release assets 178 | needs: 179 | [ 180 | build-macos-extension, 181 | build-macos-aarch64-extension, 182 | build-linux_x86-extension, 183 | build-windows-extension, 184 | ] 185 | permissions: 186 | contents: write 187 | runs-on: ubuntu-latest 188 | steps: 189 | - uses: actions/checkout@v3 190 | - name: Download workflow artifacts 191 | uses: actions/download-artifact@v2 192 | - uses: actions/github-script@v6 193 | with: 194 | github-token: ${{ secrets.GITHUB_TOKEN }} 195 | script: | 196 | const script = require('.github/workflows/upload-deno-assets.js') 197 | await script({github, context}) 198 | upload-npm: 199 | needs: 200 | [ 201 | build-macos-extension, 202 | build-macos-aarch64-extension, 203 | build-linux_x86-extension, 204 | build-windows-extension, 205 | ] 206 | runs-on: ubuntu-latest 207 | steps: 208 | - uses: actions/checkout@v3 209 | - name: Download workflow artifacts 210 | uses: actions/download-artifact@v2 211 | - run: | 212 | cp sqlite-html-linux_x86/html0.so npm/sqlite-html-linux-x64/lib/html0.so 213 | cp sqlite-html-macos/html0.dylib npm/sqlite-html-darwin-x64/lib/html0.dylib 214 | cp sqlite-html-macos-aarch64/html0.dylib npm/sqlite-html-darwin-arm64/lib/html0.dylib 215 | cp sqlite-html-windows/html0.dll npm/sqlite-html-windows-x64/lib/html0.dll 216 | - name: Install node 217 | uses: actions/setup-node@v3 218 | with: 219 | node-version: "16" 220 | registry-url: "https://registry.npmjs.org" 221 | - name: Publish NPM sqlite-html-linux-x64 222 | working-directory: npm/sqlite-html-linux-x64 223 | run: npm publish --access public 224 | env: 225 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 226 | - name: Publish NPM sqlite-html-darwin-x64 227 | working-directory: npm/sqlite-html-darwin-x64 228 | run: npm publish --access public 229 | env: 230 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 231 | - name: Publish NPM sqlite-html-darwin-arm64 232 | working-directory: npm/sqlite-html-darwin-arm64 233 | run: npm publish --access public 234 | env: 235 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 236 | - name: Publish NPM sqlite-html-windows-x64 237 | working-directory: npm/sqlite-html-windows-x64 238 | run: npm publish --access public 239 | env: 240 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 241 | - name: Publish NPM sqlite-html 242 | working-directory: npm/sqlite-html 243 | run: npm publish --access public 244 | env: 245 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 246 | upload-pypi: 247 | needs: 248 | [ 249 | build-linux_x86-python, 250 | build-macos-python, 251 | build-macos-aarch64-python, 252 | build-windows-python, 253 | build-datasette-sqlite-utils, 254 | ] 255 | runs-on: ubuntu-latest 256 | steps: 257 | - uses: actions/download-artifact@v3 258 | with: 259 | name: sqlite-html-windows-wheels 260 | path: dist 261 | - uses: actions/download-artifact@v3 262 | with: 263 | name: sqlite-html-linux_x86-wheels 264 | path: dist 265 | - uses: actions/download-artifact@v3 266 | with: 267 | name: sqlite-html-macos-wheels 268 | path: dist 269 | - uses: actions/download-artifact@v3 270 | with: 271 | name: sqlite-html-macos-aarch64-wheels 272 | path: dist 273 | - uses: actions/download-artifact@v3 274 | with: 275 | name: sqlite-html-datasette-sqlite-utils-wheels 276 | path: dist 277 | - uses: pypa/gh-action-pypi-publish@release/v1 278 | with: 279 | password: ${{ secrets.PYPI_API_TOKEN }} 280 | skip_existing: true 281 | upload-gem: 282 | needs: 283 | [ 284 | build-macos-extension, 285 | build-macos-aarch64-extension, 286 | build-linux_x86-extension, 287 | build-windows-extension, 288 | ] 289 | permissions: 290 | contents: write 291 | runs-on: ubuntu-latest 292 | steps: 293 | - uses: actions/checkout@v2 294 | - uses: actions/download-artifact@v2 295 | - uses: ruby/setup-ruby@v1 296 | with: 297 | ruby-version: 3.2 298 | - run: | 299 | rm bindings/ruby/lib/*.{dylib,so,dll} || true 300 | cp sqlite-html-macos/*.dylib bindings/ruby/lib 301 | gem -C bindings/ruby build -o x86_64-darwin.gem sqlite_html.gemspec 302 | env: 303 | PLATFORM: x86_64-darwin 304 | - run: | 305 | rm bindings/ruby/lib/*.{dylib,so,dll} || true 306 | cp sqlite-html-macos-aarch64/*.dylib bindings/ruby/lib 307 | gem -C bindings/ruby build -o arm64-darwin.gem sqlite_html.gemspec 308 | env: 309 | PLATFORM: arm64-darwin 310 | - run: | 311 | rm bindings/ruby/lib/*.{dylib,so,dll} || true 312 | cp sqlite-html-linux_x86/*.so bindings/ruby/lib 313 | gem -C bindings/ruby build -o x86_64-linux.gem sqlite_html.gemspec 314 | env: 315 | PLATFORM: x86_64-linux 316 | - run: | 317 | rm bindings/ruby/lib/*.{dylib,so,dll} || true 318 | cp sqlite-html-windows/*.dll bindings/ruby/lib 319 | gem -C bindings/ruby build -o ${{ env.PLATFORM }}.gem sqlite_html.gemspec 320 | env: 321 | PLATFORM: x64-mingw32 322 | - run: | 323 | gem push bindings/ruby/x86_64-linux.gem 324 | gem push bindings/ruby/x86_64-darwin.gem 325 | gem push bindings/ruby/arm64-darwin.gem 326 | gem push bindings/ruby/x64-mingw32.gem 327 | env: 328 | GEM_HOST_API_KEY: ${{ secrets.GEM_HOST_API_KEY }} 329 | -------------------------------------------------------------------------------- /sqlite/sqlite3ext.h: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2006 June 7 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** 7 | ** May you do good and not evil. 8 | ** May you find forgiveness for yourself and forgive others. 9 | ** May you share freely, never taking more than you give. 10 | ** 11 | ************************************************************************* 12 | ** This header file defines the SQLite interface for use by 13 | ** shared libraries that want to be imported as extensions into 14 | ** an SQLite instance. Shared libraries that intend to be loaded 15 | ** as extensions by SQLite should #include this file instead of 16 | ** sqlite3.h. 17 | */ 18 | #ifndef SQLITE3EXT_H 19 | #define SQLITE3EXT_H 20 | #include "sqlite3.h" 21 | 22 | /* 23 | ** The following structure holds pointers to all of the SQLite API 24 | ** routines. 25 | ** 26 | ** WARNING: In order to maintain backwards compatibility, add new 27 | ** interfaces to the end of this structure only. If you insert new 28 | ** interfaces in the middle of this structure, then older different 29 | ** versions of SQLite will not be able to load each other's shared 30 | ** libraries! 31 | */ 32 | struct sqlite3_api_routines { 33 | void * (*aggregate_context)(sqlite3_context*,int nBytes); 34 | int (*aggregate_count)(sqlite3_context*); 35 | int (*bind_blob)(sqlite3_stmt*,int,const void*,int n,void(*)(void*)); 36 | int (*bind_double)(sqlite3_stmt*,int,double); 37 | int (*bind_int)(sqlite3_stmt*,int,int); 38 | int (*bind_int64)(sqlite3_stmt*,int,sqlite_int64); 39 | int (*bind_null)(sqlite3_stmt*,int); 40 | int (*bind_parameter_count)(sqlite3_stmt*); 41 | int (*bind_parameter_index)(sqlite3_stmt*,const char*zName); 42 | const char * (*bind_parameter_name)(sqlite3_stmt*,int); 43 | int (*bind_text)(sqlite3_stmt*,int,const char*,int n,void(*)(void*)); 44 | int (*bind_text16)(sqlite3_stmt*,int,const void*,int,void(*)(void*)); 45 | int (*bind_value)(sqlite3_stmt*,int,const sqlite3_value*); 46 | int (*busy_handler)(sqlite3*,int(*)(void*,int),void*); 47 | int (*busy_timeout)(sqlite3*,int ms); 48 | int (*changes)(sqlite3*); 49 | int (*close)(sqlite3*); 50 | int (*collation_needed)(sqlite3*,void*,void(*)(void*,sqlite3*, 51 | int eTextRep,const char*)); 52 | int (*collation_needed16)(sqlite3*,void*,void(*)(void*,sqlite3*, 53 | int eTextRep,const void*)); 54 | const void * (*column_blob)(sqlite3_stmt*,int iCol); 55 | int (*column_bytes)(sqlite3_stmt*,int iCol); 56 | int (*column_bytes16)(sqlite3_stmt*,int iCol); 57 | int (*column_count)(sqlite3_stmt*pStmt); 58 | const char * (*column_database_name)(sqlite3_stmt*,int); 59 | const void * (*column_database_name16)(sqlite3_stmt*,int); 60 | const char * (*column_decltype)(sqlite3_stmt*,int i); 61 | const void * (*column_decltype16)(sqlite3_stmt*,int); 62 | double (*column_double)(sqlite3_stmt*,int iCol); 63 | int (*column_int)(sqlite3_stmt*,int iCol); 64 | sqlite_int64 (*column_int64)(sqlite3_stmt*,int iCol); 65 | const char * (*column_name)(sqlite3_stmt*,int); 66 | const void * (*column_name16)(sqlite3_stmt*,int); 67 | const char * (*column_origin_name)(sqlite3_stmt*,int); 68 | const void * (*column_origin_name16)(sqlite3_stmt*,int); 69 | const char * (*column_table_name)(sqlite3_stmt*,int); 70 | const void * (*column_table_name16)(sqlite3_stmt*,int); 71 | const unsigned char * (*column_text)(sqlite3_stmt*,int iCol); 72 | const void * (*column_text16)(sqlite3_stmt*,int iCol); 73 | int (*column_type)(sqlite3_stmt*,int iCol); 74 | sqlite3_value* (*column_value)(sqlite3_stmt*,int iCol); 75 | void * (*commit_hook)(sqlite3*,int(*)(void*),void*); 76 | int (*complete)(const char*sql); 77 | int (*complete16)(const void*sql); 78 | int (*create_collation)(sqlite3*,const char*,int,void*, 79 | int(*)(void*,int,const void*,int,const void*)); 80 | int (*create_collation16)(sqlite3*,const void*,int,void*, 81 | int(*)(void*,int,const void*,int,const void*)); 82 | int (*create_function)(sqlite3*,const char*,int,int,void*, 83 | void (*xFunc)(sqlite3_context*,int,sqlite3_value**), 84 | void (*xStep)(sqlite3_context*,int,sqlite3_value**), 85 | void (*xFinal)(sqlite3_context*)); 86 | int (*create_function16)(sqlite3*,const void*,int,int,void*, 87 | void (*xFunc)(sqlite3_context*,int,sqlite3_value**), 88 | void (*xStep)(sqlite3_context*,int,sqlite3_value**), 89 | void (*xFinal)(sqlite3_context*)); 90 | int (*create_module)(sqlite3*,const char*,const sqlite3_module*,void*); 91 | int (*data_count)(sqlite3_stmt*pStmt); 92 | sqlite3 * (*db_handle)(sqlite3_stmt*); 93 | int (*declare_vtab)(sqlite3*,const char*); 94 | int (*enable_shared_cache)(int); 95 | int (*errcode)(sqlite3*db); 96 | const char * (*errmsg)(sqlite3*); 97 | const void * (*errmsg16)(sqlite3*); 98 | int (*exec)(sqlite3*,const char*,sqlite3_callback,void*,char**); 99 | int (*expired)(sqlite3_stmt*); 100 | int (*finalize)(sqlite3_stmt*pStmt); 101 | void (*free)(void*); 102 | void (*free_table)(char**result); 103 | int (*get_autocommit)(sqlite3*); 104 | void * (*get_auxdata)(sqlite3_context*,int); 105 | int (*get_table)(sqlite3*,const char*,char***,int*,int*,char**); 106 | int (*global_recover)(void); 107 | void (*interruptx)(sqlite3*); 108 | sqlite_int64 (*last_insert_rowid)(sqlite3*); 109 | const char * (*libversion)(void); 110 | int (*libversion_number)(void); 111 | void *(*malloc)(int); 112 | char * (*mprintf)(const char*,...); 113 | int (*open)(const char*,sqlite3**); 114 | int (*open16)(const void*,sqlite3**); 115 | int (*prepare)(sqlite3*,const char*,int,sqlite3_stmt**,const char**); 116 | int (*prepare16)(sqlite3*,const void*,int,sqlite3_stmt**,const void**); 117 | void * (*profile)(sqlite3*,void(*)(void*,const char*,sqlite_uint64),void*); 118 | void (*progress_handler)(sqlite3*,int,int(*)(void*),void*); 119 | void *(*realloc)(void*,int); 120 | int (*reset)(sqlite3_stmt*pStmt); 121 | void (*result_blob)(sqlite3_context*,const void*,int,void(*)(void*)); 122 | void (*result_double)(sqlite3_context*,double); 123 | void (*result_error)(sqlite3_context*,const char*,int); 124 | void (*result_error16)(sqlite3_context*,const void*,int); 125 | void (*result_int)(sqlite3_context*,int); 126 | void (*result_int64)(sqlite3_context*,sqlite_int64); 127 | void (*result_null)(sqlite3_context*); 128 | void (*result_text)(sqlite3_context*,const char*,int,void(*)(void*)); 129 | void (*result_text16)(sqlite3_context*,const void*,int,void(*)(void*)); 130 | void (*result_text16be)(sqlite3_context*,const void*,int,void(*)(void*)); 131 | void (*result_text16le)(sqlite3_context*,const void*,int,void(*)(void*)); 132 | void (*result_value)(sqlite3_context*,sqlite3_value*); 133 | void * (*rollback_hook)(sqlite3*,void(*)(void*),void*); 134 | int (*set_authorizer)(sqlite3*,int(*)(void*,int,const char*,const char*, 135 | const char*,const char*),void*); 136 | void (*set_auxdata)(sqlite3_context*,int,void*,void (*)(void*)); 137 | char * (*xsnprintf)(int,char*,const char*,...); 138 | int (*step)(sqlite3_stmt*); 139 | int (*table_column_metadata)(sqlite3*,const char*,const char*,const char*, 140 | char const**,char const**,int*,int*,int*); 141 | void (*thread_cleanup)(void); 142 | int (*total_changes)(sqlite3*); 143 | void * (*trace)(sqlite3*,void(*xTrace)(void*,const char*),void*); 144 | int (*transfer_bindings)(sqlite3_stmt*,sqlite3_stmt*); 145 | void * (*update_hook)(sqlite3*,void(*)(void*,int ,char const*,char const*, 146 | sqlite_int64),void*); 147 | void * (*user_data)(sqlite3_context*); 148 | const void * (*value_blob)(sqlite3_value*); 149 | int (*value_bytes)(sqlite3_value*); 150 | int (*value_bytes16)(sqlite3_value*); 151 | double (*value_double)(sqlite3_value*); 152 | int (*value_int)(sqlite3_value*); 153 | sqlite_int64 (*value_int64)(sqlite3_value*); 154 | int (*value_numeric_type)(sqlite3_value*); 155 | const unsigned char * (*value_text)(sqlite3_value*); 156 | const void * (*value_text16)(sqlite3_value*); 157 | const void * (*value_text16be)(sqlite3_value*); 158 | const void * (*value_text16le)(sqlite3_value*); 159 | int (*value_type)(sqlite3_value*); 160 | char *(*vmprintf)(const char*,va_list); 161 | /* Added ??? */ 162 | int (*overload_function)(sqlite3*, const char *zFuncName, int nArg); 163 | /* Added by 3.3.13 */ 164 | int (*prepare_v2)(sqlite3*,const char*,int,sqlite3_stmt**,const char**); 165 | int (*prepare16_v2)(sqlite3*,const void*,int,sqlite3_stmt**,const void**); 166 | int (*clear_bindings)(sqlite3_stmt*); 167 | /* Added by 3.4.1 */ 168 | int (*create_module_v2)(sqlite3*,const char*,const sqlite3_module*,void*, 169 | void (*xDestroy)(void *)); 170 | /* Added by 3.5.0 */ 171 | int (*bind_zeroblob)(sqlite3_stmt*,int,int); 172 | int (*blob_bytes)(sqlite3_blob*); 173 | int (*blob_close)(sqlite3_blob*); 174 | int (*blob_open)(sqlite3*,const char*,const char*,const char*,sqlite3_int64, 175 | int,sqlite3_blob**); 176 | int (*blob_read)(sqlite3_blob*,void*,int,int); 177 | int (*blob_write)(sqlite3_blob*,const void*,int,int); 178 | int (*create_collation_v2)(sqlite3*,const char*,int,void*, 179 | int(*)(void*,int,const void*,int,const void*), 180 | void(*)(void*)); 181 | int (*file_control)(sqlite3*,const char*,int,void*); 182 | sqlite3_int64 (*memory_highwater)(int); 183 | sqlite3_int64 (*memory_used)(void); 184 | sqlite3_mutex *(*mutex_alloc)(int); 185 | void (*mutex_enter)(sqlite3_mutex*); 186 | void (*mutex_free)(sqlite3_mutex*); 187 | void (*mutex_leave)(sqlite3_mutex*); 188 | int (*mutex_try)(sqlite3_mutex*); 189 | int (*open_v2)(const char*,sqlite3**,int,const char*); 190 | int (*release_memory)(int); 191 | void (*result_error_nomem)(sqlite3_context*); 192 | void (*result_error_toobig)(sqlite3_context*); 193 | int (*sleep)(int); 194 | void (*soft_heap_limit)(int); 195 | sqlite3_vfs *(*vfs_find)(const char*); 196 | int (*vfs_register)(sqlite3_vfs*,int); 197 | int (*vfs_unregister)(sqlite3_vfs*); 198 | int (*xthreadsafe)(void); 199 | void (*result_zeroblob)(sqlite3_context*,int); 200 | void (*result_error_code)(sqlite3_context*,int); 201 | int (*test_control)(int, ...); 202 | void (*randomness)(int,void*); 203 | sqlite3 *(*context_db_handle)(sqlite3_context*); 204 | int (*extended_result_codes)(sqlite3*,int); 205 | int (*limit)(sqlite3*,int,int); 206 | sqlite3_stmt *(*next_stmt)(sqlite3*,sqlite3_stmt*); 207 | const char *(*sql)(sqlite3_stmt*); 208 | int (*status)(int,int*,int*,int); 209 | int (*backup_finish)(sqlite3_backup*); 210 | sqlite3_backup *(*backup_init)(sqlite3*,const char*,sqlite3*,const char*); 211 | int (*backup_pagecount)(sqlite3_backup*); 212 | int (*backup_remaining)(sqlite3_backup*); 213 | int (*backup_step)(sqlite3_backup*,int); 214 | const char *(*compileoption_get)(int); 215 | int (*compileoption_used)(const char*); 216 | int (*create_function_v2)(sqlite3*,const char*,int,int,void*, 217 | void (*xFunc)(sqlite3_context*,int,sqlite3_value**), 218 | void (*xStep)(sqlite3_context*,int,sqlite3_value**), 219 | void (*xFinal)(sqlite3_context*), 220 | void(*xDestroy)(void*)); 221 | int (*db_config)(sqlite3*,int,...); 222 | sqlite3_mutex *(*db_mutex)(sqlite3*); 223 | int (*db_status)(sqlite3*,int,int*,int*,int); 224 | int (*extended_errcode)(sqlite3*); 225 | void (*log)(int,const char*,...); 226 | sqlite3_int64 (*soft_heap_limit64)(sqlite3_int64); 227 | const char *(*sourceid)(void); 228 | int (*stmt_status)(sqlite3_stmt*,int,int); 229 | int (*strnicmp)(const char*,const char*,int); 230 | int (*unlock_notify)(sqlite3*,void(*)(void**,int),void*); 231 | int (*wal_autocheckpoint)(sqlite3*,int); 232 | int (*wal_checkpoint)(sqlite3*,const char*); 233 | void *(*wal_hook)(sqlite3*,int(*)(void*,sqlite3*,const char*,int),void*); 234 | int (*blob_reopen)(sqlite3_blob*,sqlite3_int64); 235 | int (*vtab_config)(sqlite3*,int op,...); 236 | int (*vtab_on_conflict)(sqlite3*); 237 | /* Version 3.7.16 and later */ 238 | int (*close_v2)(sqlite3*); 239 | const char *(*db_filename)(sqlite3*,const char*); 240 | int (*db_readonly)(sqlite3*,const char*); 241 | int (*db_release_memory)(sqlite3*); 242 | const char *(*errstr)(int); 243 | int (*stmt_busy)(sqlite3_stmt*); 244 | int (*stmt_readonly)(sqlite3_stmt*); 245 | int (*stricmp)(const char*,const char*); 246 | int (*uri_boolean)(const char*,const char*,int); 247 | sqlite3_int64 (*uri_int64)(const char*,const char*,sqlite3_int64); 248 | const char *(*uri_parameter)(const char*,const char*); 249 | char *(*xvsnprintf)(int,char*,const char*,va_list); 250 | int (*wal_checkpoint_v2)(sqlite3*,const char*,int,int*,int*); 251 | /* Version 3.8.7 and later */ 252 | int (*auto_extension)(void(*)(void)); 253 | int (*bind_blob64)(sqlite3_stmt*,int,const void*,sqlite3_uint64, 254 | void(*)(void*)); 255 | int (*bind_text64)(sqlite3_stmt*,int,const char*,sqlite3_uint64, 256 | void(*)(void*),unsigned char); 257 | int (*cancel_auto_extension)(void(*)(void)); 258 | int (*load_extension)(sqlite3*,const char*,const char*,char**); 259 | void *(*malloc64)(sqlite3_uint64); 260 | sqlite3_uint64 (*msize)(void*); 261 | void *(*realloc64)(void*,sqlite3_uint64); 262 | void (*reset_auto_extension)(void); 263 | void (*result_blob64)(sqlite3_context*,const void*,sqlite3_uint64, 264 | void(*)(void*)); 265 | void (*result_text64)(sqlite3_context*,const char*,sqlite3_uint64, 266 | void(*)(void*), unsigned char); 267 | int (*strglob)(const char*,const char*); 268 | /* Version 3.8.11 and later */ 269 | sqlite3_value *(*value_dup)(const sqlite3_value*); 270 | void (*value_free)(sqlite3_value*); 271 | int (*result_zeroblob64)(sqlite3_context*,sqlite3_uint64); 272 | int (*bind_zeroblob64)(sqlite3_stmt*, int, sqlite3_uint64); 273 | /* Version 3.9.0 and later */ 274 | unsigned int (*value_subtype)(sqlite3_value*); 275 | void (*result_subtype)(sqlite3_context*,unsigned int); 276 | /* Version 3.10.0 and later */ 277 | int (*status64)(int,sqlite3_int64*,sqlite3_int64*,int); 278 | int (*strlike)(const char*,const char*,unsigned int); 279 | int (*db_cacheflush)(sqlite3*); 280 | /* Version 3.12.0 and later */ 281 | int (*system_errno)(sqlite3*); 282 | /* Version 3.14.0 and later */ 283 | int (*trace_v2)(sqlite3*,unsigned,int(*)(unsigned,void*,void*,void*),void*); 284 | char *(*expanded_sql)(sqlite3_stmt*); 285 | /* Version 3.18.0 and later */ 286 | void (*set_last_insert_rowid)(sqlite3*,sqlite3_int64); 287 | /* Version 3.20.0 and later */ 288 | int (*prepare_v3)(sqlite3*,const char*,int,unsigned int, 289 | sqlite3_stmt**,const char**); 290 | int (*prepare16_v3)(sqlite3*,const void*,int,unsigned int, 291 | sqlite3_stmt**,const void**); 292 | int (*bind_pointer)(sqlite3_stmt*,int,void*,const char*,void(*)(void*)); 293 | void (*result_pointer)(sqlite3_context*,void*,const char*,void(*)(void*)); 294 | void *(*value_pointer)(sqlite3_value*,const char*); 295 | int (*vtab_nochange)(sqlite3_context*); 296 | int (*value_nochange)(sqlite3_value*); 297 | const char *(*vtab_collation)(sqlite3_index_info*,int); 298 | /* Version 3.24.0 and later */ 299 | int (*keyword_count)(void); 300 | int (*keyword_name)(int,const char**,int*); 301 | int (*keyword_check)(const char*,int); 302 | sqlite3_str *(*str_new)(sqlite3*); 303 | char *(*str_finish)(sqlite3_str*); 304 | void (*str_appendf)(sqlite3_str*, const char *zFormat, ...); 305 | void (*str_vappendf)(sqlite3_str*, const char *zFormat, va_list); 306 | void (*str_append)(sqlite3_str*, const char *zIn, int N); 307 | void (*str_appendall)(sqlite3_str*, const char *zIn); 308 | void (*str_appendchar)(sqlite3_str*, int N, char C); 309 | void (*str_reset)(sqlite3_str*); 310 | int (*str_errcode)(sqlite3_str*); 311 | int (*str_length)(sqlite3_str*); 312 | char *(*str_value)(sqlite3_str*); 313 | /* Version 3.25.0 and later */ 314 | int (*create_window_function)(sqlite3*,const char*,int,int,void*, 315 | void (*xStep)(sqlite3_context*,int,sqlite3_value**), 316 | void (*xFinal)(sqlite3_context*), 317 | void (*xValue)(sqlite3_context*), 318 | void (*xInv)(sqlite3_context*,int,sqlite3_value**), 319 | void(*xDestroy)(void*)); 320 | /* Version 3.26.0 and later */ 321 | const char *(*normalized_sql)(sqlite3_stmt*); 322 | /* Version 3.28.0 and later */ 323 | int (*stmt_isexplain)(sqlite3_stmt*); 324 | int (*value_frombind)(sqlite3_value*); 325 | /* Version 3.30.0 and later */ 326 | int (*drop_modules)(sqlite3*,const char**); 327 | /* Version 3.31.0 and later */ 328 | sqlite3_int64 (*hard_heap_limit64)(sqlite3_int64); 329 | const char *(*uri_key)(const char*,int); 330 | const char *(*filename_database)(const char*); 331 | const char *(*filename_journal)(const char*); 332 | const char *(*filename_wal)(const char*); 333 | /* Version 3.32.0 and later */ 334 | char *(*create_filename)(const char*,const char*,const char*, 335 | int,const char**); 336 | void (*free_filename)(char*); 337 | sqlite3_file *(*database_file_object)(const char*); 338 | /* Version 3.34.0 and later */ 339 | int (*txn_state)(sqlite3*,const char*); 340 | /* Version 3.36.1 and later */ 341 | sqlite3_int64 (*changes64)(sqlite3*); 342 | sqlite3_int64 (*total_changes64)(sqlite3*); 343 | /* Version 3.37.0 and later */ 344 | int (*autovacuum_pages)(sqlite3*, 345 | unsigned int(*)(void*,const char*,unsigned int,unsigned int,unsigned int), 346 | void*, void(*)(void*)); 347 | /* Version 3.38.0 and later */ 348 | int (*error_offset)(sqlite3*); 349 | int (*vtab_rhs_value)(sqlite3_index_info*,int,sqlite3_value**); 350 | int (*vtab_distinct)(sqlite3_index_info*); 351 | int (*vtab_in)(sqlite3_index_info*,int,int); 352 | int (*vtab_in_first)(sqlite3_value*,sqlite3_value**); 353 | int (*vtab_in_next)(sqlite3_value*,sqlite3_value**); 354 | }; 355 | 356 | /* 357 | ** This is the function signature used for all extension entry points. It 358 | ** is also defined in the file "loadext.c". 359 | */ 360 | typedef int (*sqlite3_loadext_entry)( 361 | sqlite3 *db, /* Handle to the database. */ 362 | char **pzErrMsg, /* Used to set error string on failure. */ 363 | const sqlite3_api_routines *pThunk /* Extension API function pointers. */ 364 | ); 365 | 366 | /* 367 | ** The following macros redefine the API routines so that they are 368 | ** redirected through the global sqlite3_api structure. 369 | ** 370 | ** This header file is also used by the loadext.c source file 371 | ** (part of the main SQLite library - not an extension) so that 372 | ** it can get access to the sqlite3_api_routines structure 373 | ** definition. But the main library does not want to redefine 374 | ** the API. So the redefinition macros are only valid if the 375 | ** SQLITE_CORE macros is undefined. 376 | */ 377 | #if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) 378 | #define sqlite3_aggregate_context sqlite3_api->aggregate_context 379 | #ifndef SQLITE_OMIT_DEPRECATED 380 | #define sqlite3_aggregate_count sqlite3_api->aggregate_count 381 | #endif 382 | #define sqlite3_bind_blob sqlite3_api->bind_blob 383 | #define sqlite3_bind_double sqlite3_api->bind_double 384 | #define sqlite3_bind_int sqlite3_api->bind_int 385 | #define sqlite3_bind_int64 sqlite3_api->bind_int64 386 | #define sqlite3_bind_null sqlite3_api->bind_null 387 | #define sqlite3_bind_parameter_count sqlite3_api->bind_parameter_count 388 | #define sqlite3_bind_parameter_index sqlite3_api->bind_parameter_index 389 | #define sqlite3_bind_parameter_name sqlite3_api->bind_parameter_name 390 | #define sqlite3_bind_text sqlite3_api->bind_text 391 | #define sqlite3_bind_text16 sqlite3_api->bind_text16 392 | #define sqlite3_bind_value sqlite3_api->bind_value 393 | #define sqlite3_busy_handler sqlite3_api->busy_handler 394 | #define sqlite3_busy_timeout sqlite3_api->busy_timeout 395 | #define sqlite3_changes sqlite3_api->changes 396 | #define sqlite3_close sqlite3_api->close 397 | #define sqlite3_collation_needed sqlite3_api->collation_needed 398 | #define sqlite3_collation_needed16 sqlite3_api->collation_needed16 399 | #define sqlite3_column_blob sqlite3_api->column_blob 400 | #define sqlite3_column_bytes sqlite3_api->column_bytes 401 | #define sqlite3_column_bytes16 sqlite3_api->column_bytes16 402 | #define sqlite3_column_count sqlite3_api->column_count 403 | #define sqlite3_column_database_name sqlite3_api->column_database_name 404 | #define sqlite3_column_database_name16 sqlite3_api->column_database_name16 405 | #define sqlite3_column_decltype sqlite3_api->column_decltype 406 | #define sqlite3_column_decltype16 sqlite3_api->column_decltype16 407 | #define sqlite3_column_double sqlite3_api->column_double 408 | #define sqlite3_column_int sqlite3_api->column_int 409 | #define sqlite3_column_int64 sqlite3_api->column_int64 410 | #define sqlite3_column_name sqlite3_api->column_name 411 | #define sqlite3_column_name16 sqlite3_api->column_name16 412 | #define sqlite3_column_origin_name sqlite3_api->column_origin_name 413 | #define sqlite3_column_origin_name16 sqlite3_api->column_origin_name16 414 | #define sqlite3_column_table_name sqlite3_api->column_table_name 415 | #define sqlite3_column_table_name16 sqlite3_api->column_table_name16 416 | #define sqlite3_column_text sqlite3_api->column_text 417 | #define sqlite3_column_text16 sqlite3_api->column_text16 418 | #define sqlite3_column_type sqlite3_api->column_type 419 | #define sqlite3_column_value sqlite3_api->column_value 420 | #define sqlite3_commit_hook sqlite3_api->commit_hook 421 | #define sqlite3_complete sqlite3_api->complete 422 | #define sqlite3_complete16 sqlite3_api->complete16 423 | #define sqlite3_create_collation sqlite3_api->create_collation 424 | #define sqlite3_create_collation16 sqlite3_api->create_collation16 425 | #define sqlite3_create_function sqlite3_api->create_function 426 | #define sqlite3_create_function16 sqlite3_api->create_function16 427 | #define sqlite3_create_module sqlite3_api->create_module 428 | #define sqlite3_create_module_v2 sqlite3_api->create_module_v2 429 | #define sqlite3_data_count sqlite3_api->data_count 430 | #define sqlite3_db_handle sqlite3_api->db_handle 431 | #define sqlite3_declare_vtab sqlite3_api->declare_vtab 432 | #define sqlite3_enable_shared_cache sqlite3_api->enable_shared_cache 433 | #define sqlite3_errcode sqlite3_api->errcode 434 | #define sqlite3_errmsg sqlite3_api->errmsg 435 | #define sqlite3_errmsg16 sqlite3_api->errmsg16 436 | #define sqlite3_exec sqlite3_api->exec 437 | #ifndef SQLITE_OMIT_DEPRECATED 438 | #define sqlite3_expired sqlite3_api->expired 439 | #endif 440 | #define sqlite3_finalize sqlite3_api->finalize 441 | #define sqlite3_free sqlite3_api->free 442 | #define sqlite3_free_table sqlite3_api->free_table 443 | #define sqlite3_get_autocommit sqlite3_api->get_autocommit 444 | #define sqlite3_get_auxdata sqlite3_api->get_auxdata 445 | #define sqlite3_get_table sqlite3_api->get_table 446 | #ifndef SQLITE_OMIT_DEPRECATED 447 | #define sqlite3_global_recover sqlite3_api->global_recover 448 | #endif 449 | #define sqlite3_interrupt sqlite3_api->interruptx 450 | #define sqlite3_last_insert_rowid sqlite3_api->last_insert_rowid 451 | #define sqlite3_libversion sqlite3_api->libversion 452 | #define sqlite3_libversion_number sqlite3_api->libversion_number 453 | #define sqlite3_malloc sqlite3_api->malloc 454 | #define sqlite3_mprintf sqlite3_api->mprintf 455 | #define sqlite3_open sqlite3_api->open 456 | #define sqlite3_open16 sqlite3_api->open16 457 | #define sqlite3_prepare sqlite3_api->prepare 458 | #define sqlite3_prepare16 sqlite3_api->prepare16 459 | #define sqlite3_prepare_v2 sqlite3_api->prepare_v2 460 | #define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2 461 | #define sqlite3_profile sqlite3_api->profile 462 | #define sqlite3_progress_handler sqlite3_api->progress_handler 463 | #define sqlite3_realloc sqlite3_api->realloc 464 | #define sqlite3_reset sqlite3_api->reset 465 | #define sqlite3_result_blob sqlite3_api->result_blob 466 | #define sqlite3_result_double sqlite3_api->result_double 467 | #define sqlite3_result_error sqlite3_api->result_error 468 | #define sqlite3_result_error16 sqlite3_api->result_error16 469 | #define sqlite3_result_int sqlite3_api->result_int 470 | #define sqlite3_result_int64 sqlite3_api->result_int64 471 | #define sqlite3_result_null sqlite3_api->result_null 472 | #define sqlite3_result_text sqlite3_api->result_text 473 | #define sqlite3_result_text16 sqlite3_api->result_text16 474 | #define sqlite3_result_text16be sqlite3_api->result_text16be 475 | #define sqlite3_result_text16le sqlite3_api->result_text16le 476 | #define sqlite3_result_value sqlite3_api->result_value 477 | #define sqlite3_rollback_hook sqlite3_api->rollback_hook 478 | #define sqlite3_set_authorizer sqlite3_api->set_authorizer 479 | #define sqlite3_set_auxdata sqlite3_api->set_auxdata 480 | #define sqlite3_snprintf sqlite3_api->xsnprintf 481 | #define sqlite3_step sqlite3_api->step 482 | #define sqlite3_table_column_metadata sqlite3_api->table_column_metadata 483 | #define sqlite3_thread_cleanup sqlite3_api->thread_cleanup 484 | #define sqlite3_total_changes sqlite3_api->total_changes 485 | #define sqlite3_trace sqlite3_api->trace 486 | #ifndef SQLITE_OMIT_DEPRECATED 487 | #define sqlite3_transfer_bindings sqlite3_api->transfer_bindings 488 | #endif 489 | #define sqlite3_update_hook sqlite3_api->update_hook 490 | #define sqlite3_user_data sqlite3_api->user_data 491 | #define sqlite3_value_blob sqlite3_api->value_blob 492 | #define sqlite3_value_bytes sqlite3_api->value_bytes 493 | #define sqlite3_value_bytes16 sqlite3_api->value_bytes16 494 | #define sqlite3_value_double sqlite3_api->value_double 495 | #define sqlite3_value_int sqlite3_api->value_int 496 | #define sqlite3_value_int64 sqlite3_api->value_int64 497 | #define sqlite3_value_numeric_type sqlite3_api->value_numeric_type 498 | #define sqlite3_value_text sqlite3_api->value_text 499 | #define sqlite3_value_text16 sqlite3_api->value_text16 500 | #define sqlite3_value_text16be sqlite3_api->value_text16be 501 | #define sqlite3_value_text16le sqlite3_api->value_text16le 502 | #define sqlite3_value_type sqlite3_api->value_type 503 | #define sqlite3_vmprintf sqlite3_api->vmprintf 504 | #define sqlite3_vsnprintf sqlite3_api->xvsnprintf 505 | #define sqlite3_overload_function sqlite3_api->overload_function 506 | #define sqlite3_prepare_v2 sqlite3_api->prepare_v2 507 | #define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2 508 | #define sqlite3_clear_bindings sqlite3_api->clear_bindings 509 | #define sqlite3_bind_zeroblob sqlite3_api->bind_zeroblob 510 | #define sqlite3_blob_bytes sqlite3_api->blob_bytes 511 | #define sqlite3_blob_close sqlite3_api->blob_close 512 | #define sqlite3_blob_open sqlite3_api->blob_open 513 | #define sqlite3_blob_read sqlite3_api->blob_read 514 | #define sqlite3_blob_write sqlite3_api->blob_write 515 | #define sqlite3_create_collation_v2 sqlite3_api->create_collation_v2 516 | #define sqlite3_file_control sqlite3_api->file_control 517 | #define sqlite3_memory_highwater sqlite3_api->memory_highwater 518 | #define sqlite3_memory_used sqlite3_api->memory_used 519 | #define sqlite3_mutex_alloc sqlite3_api->mutex_alloc 520 | #define sqlite3_mutex_enter sqlite3_api->mutex_enter 521 | #define sqlite3_mutex_free sqlite3_api->mutex_free 522 | #define sqlite3_mutex_leave sqlite3_api->mutex_leave 523 | #define sqlite3_mutex_try sqlite3_api->mutex_try 524 | #define sqlite3_open_v2 sqlite3_api->open_v2 525 | #define sqlite3_release_memory sqlite3_api->release_memory 526 | #define sqlite3_result_error_nomem sqlite3_api->result_error_nomem 527 | #define sqlite3_result_error_toobig sqlite3_api->result_error_toobig 528 | #define sqlite3_sleep sqlite3_api->sleep 529 | #define sqlite3_soft_heap_limit sqlite3_api->soft_heap_limit 530 | #define sqlite3_vfs_find sqlite3_api->vfs_find 531 | #define sqlite3_vfs_register sqlite3_api->vfs_register 532 | #define sqlite3_vfs_unregister sqlite3_api->vfs_unregister 533 | #define sqlite3_threadsafe sqlite3_api->xthreadsafe 534 | #define sqlite3_result_zeroblob sqlite3_api->result_zeroblob 535 | #define sqlite3_result_error_code sqlite3_api->result_error_code 536 | #define sqlite3_test_control sqlite3_api->test_control 537 | #define sqlite3_randomness sqlite3_api->randomness 538 | #define sqlite3_context_db_handle sqlite3_api->context_db_handle 539 | #define sqlite3_extended_result_codes sqlite3_api->extended_result_codes 540 | #define sqlite3_limit sqlite3_api->limit 541 | #define sqlite3_next_stmt sqlite3_api->next_stmt 542 | #define sqlite3_sql sqlite3_api->sql 543 | #define sqlite3_status sqlite3_api->status 544 | #define sqlite3_backup_finish sqlite3_api->backup_finish 545 | #define sqlite3_backup_init sqlite3_api->backup_init 546 | #define sqlite3_backup_pagecount sqlite3_api->backup_pagecount 547 | #define sqlite3_backup_remaining sqlite3_api->backup_remaining 548 | #define sqlite3_backup_step sqlite3_api->backup_step 549 | #define sqlite3_compileoption_get sqlite3_api->compileoption_get 550 | #define sqlite3_compileoption_used sqlite3_api->compileoption_used 551 | #define sqlite3_create_function_v2 sqlite3_api->create_function_v2 552 | #define sqlite3_db_config sqlite3_api->db_config 553 | #define sqlite3_db_mutex sqlite3_api->db_mutex 554 | #define sqlite3_db_status sqlite3_api->db_status 555 | #define sqlite3_extended_errcode sqlite3_api->extended_errcode 556 | #define sqlite3_log sqlite3_api->log 557 | #define sqlite3_soft_heap_limit64 sqlite3_api->soft_heap_limit64 558 | #define sqlite3_sourceid sqlite3_api->sourceid 559 | #define sqlite3_stmt_status sqlite3_api->stmt_status 560 | #define sqlite3_strnicmp sqlite3_api->strnicmp 561 | #define sqlite3_unlock_notify sqlite3_api->unlock_notify 562 | #define sqlite3_wal_autocheckpoint sqlite3_api->wal_autocheckpoint 563 | #define sqlite3_wal_checkpoint sqlite3_api->wal_checkpoint 564 | #define sqlite3_wal_hook sqlite3_api->wal_hook 565 | #define sqlite3_blob_reopen sqlite3_api->blob_reopen 566 | #define sqlite3_vtab_config sqlite3_api->vtab_config 567 | #define sqlite3_vtab_on_conflict sqlite3_api->vtab_on_conflict 568 | /* Version 3.7.16 and later */ 569 | #define sqlite3_close_v2 sqlite3_api->close_v2 570 | #define sqlite3_db_filename sqlite3_api->db_filename 571 | #define sqlite3_db_readonly sqlite3_api->db_readonly 572 | #define sqlite3_db_release_memory sqlite3_api->db_release_memory 573 | #define sqlite3_errstr sqlite3_api->errstr 574 | #define sqlite3_stmt_busy sqlite3_api->stmt_busy 575 | #define sqlite3_stmt_readonly sqlite3_api->stmt_readonly 576 | #define sqlite3_stricmp sqlite3_api->stricmp 577 | #define sqlite3_uri_boolean sqlite3_api->uri_boolean 578 | #define sqlite3_uri_int64 sqlite3_api->uri_int64 579 | #define sqlite3_uri_parameter sqlite3_api->uri_parameter 580 | #define sqlite3_uri_vsnprintf sqlite3_api->xvsnprintf 581 | #define sqlite3_wal_checkpoint_v2 sqlite3_api->wal_checkpoint_v2 582 | /* Version 3.8.7 and later */ 583 | #define sqlite3_auto_extension sqlite3_api->auto_extension 584 | #define sqlite3_bind_blob64 sqlite3_api->bind_blob64 585 | #define sqlite3_bind_text64 sqlite3_api->bind_text64 586 | #define sqlite3_cancel_auto_extension sqlite3_api->cancel_auto_extension 587 | #define sqlite3_load_extension sqlite3_api->load_extension 588 | #define sqlite3_malloc64 sqlite3_api->malloc64 589 | #define sqlite3_msize sqlite3_api->msize 590 | #define sqlite3_realloc64 sqlite3_api->realloc64 591 | #define sqlite3_reset_auto_extension sqlite3_api->reset_auto_extension 592 | #define sqlite3_result_blob64 sqlite3_api->result_blob64 593 | #define sqlite3_result_text64 sqlite3_api->result_text64 594 | #define sqlite3_strglob sqlite3_api->strglob 595 | /* Version 3.8.11 and later */ 596 | #define sqlite3_value_dup sqlite3_api->value_dup 597 | #define sqlite3_value_free sqlite3_api->value_free 598 | #define sqlite3_result_zeroblob64 sqlite3_api->result_zeroblob64 599 | #define sqlite3_bind_zeroblob64 sqlite3_api->bind_zeroblob64 600 | /* Version 3.9.0 and later */ 601 | #define sqlite3_value_subtype sqlite3_api->value_subtype 602 | #define sqlite3_result_subtype sqlite3_api->result_subtype 603 | /* Version 3.10.0 and later */ 604 | #define sqlite3_status64 sqlite3_api->status64 605 | #define sqlite3_strlike sqlite3_api->strlike 606 | #define sqlite3_db_cacheflush sqlite3_api->db_cacheflush 607 | /* Version 3.12.0 and later */ 608 | #define sqlite3_system_errno sqlite3_api->system_errno 609 | /* Version 3.14.0 and later */ 610 | #define sqlite3_trace_v2 sqlite3_api->trace_v2 611 | #define sqlite3_expanded_sql sqlite3_api->expanded_sql 612 | /* Version 3.18.0 and later */ 613 | #define sqlite3_set_last_insert_rowid sqlite3_api->set_last_insert_rowid 614 | /* Version 3.20.0 and later */ 615 | #define sqlite3_prepare_v3 sqlite3_api->prepare_v3 616 | #define sqlite3_prepare16_v3 sqlite3_api->prepare16_v3 617 | #define sqlite3_bind_pointer sqlite3_api->bind_pointer 618 | #define sqlite3_result_pointer sqlite3_api->result_pointer 619 | #define sqlite3_value_pointer sqlite3_api->value_pointer 620 | /* Version 3.22.0 and later */ 621 | #define sqlite3_vtab_nochange sqlite3_api->vtab_nochange 622 | #define sqlite3_value_nochange sqlite3_api->value_nochange 623 | #define sqlite3_vtab_collation sqlite3_api->vtab_collation 624 | /* Version 3.24.0 and later */ 625 | #define sqlite3_keyword_count sqlite3_api->keyword_count 626 | #define sqlite3_keyword_name sqlite3_api->keyword_name 627 | #define sqlite3_keyword_check sqlite3_api->keyword_check 628 | #define sqlite3_str_new sqlite3_api->str_new 629 | #define sqlite3_str_finish sqlite3_api->str_finish 630 | #define sqlite3_str_appendf sqlite3_api->str_appendf 631 | #define sqlite3_str_vappendf sqlite3_api->str_vappendf 632 | #define sqlite3_str_append sqlite3_api->str_append 633 | #define sqlite3_str_appendall sqlite3_api->str_appendall 634 | #define sqlite3_str_appendchar sqlite3_api->str_appendchar 635 | #define sqlite3_str_reset sqlite3_api->str_reset 636 | #define sqlite3_str_errcode sqlite3_api->str_errcode 637 | #define sqlite3_str_length sqlite3_api->str_length 638 | #define sqlite3_str_value sqlite3_api->str_value 639 | /* Version 3.25.0 and later */ 640 | #define sqlite3_create_window_function sqlite3_api->create_window_function 641 | /* Version 3.26.0 and later */ 642 | #define sqlite3_normalized_sql sqlite3_api->normalized_sql 643 | /* Version 3.28.0 and later */ 644 | #define sqlite3_stmt_isexplain sqlite3_api->stmt_isexplain 645 | #define sqlite3_value_frombind sqlite3_api->value_frombind 646 | /* Version 3.30.0 and later */ 647 | #define sqlite3_drop_modules sqlite3_api->drop_modules 648 | /* Version 3.31.0 and later */ 649 | #define sqlite3_hard_heap_limit64 sqlite3_api->hard_heap_limit64 650 | #define sqlite3_uri_key sqlite3_api->uri_key 651 | #define sqlite3_filename_database sqlite3_api->filename_database 652 | #define sqlite3_filename_journal sqlite3_api->filename_journal 653 | #define sqlite3_filename_wal sqlite3_api->filename_wal 654 | /* Version 3.32.0 and later */ 655 | #define sqlite3_create_filename sqlite3_api->create_filename 656 | #define sqlite3_free_filename sqlite3_api->free_filename 657 | #define sqlite3_database_file_object sqlite3_api->database_file_object 658 | /* Version 3.34.0 and later */ 659 | #define sqlite3_txn_state sqlite3_api->txn_state 660 | /* Version 3.36.1 and later */ 661 | #define sqlite3_changes64 sqlite3_api->changes64 662 | #define sqlite3_total_changes64 sqlite3_api->total_changes64 663 | /* Version 3.37.0 and later */ 664 | #define sqlite3_autovacuum_pages sqlite3_api->autovacuum_pages 665 | /* Version 3.38.0 and later */ 666 | #define sqlite3_error_offset sqlite3_api->error_offset 667 | #define sqlite3_vtab_rhs_value sqlite3_api->vtab_rhs_value 668 | #define sqlite3_vtab_distinct sqlite3_api->vtab_distinct 669 | #define sqlite3_vtab_in sqlite3_api->vtab_in 670 | #define sqlite3_vtab_in_first sqlite3_api->vtab_in_first 671 | #define sqlite3_vtab_in_next sqlite3_api->vtab_in_next 672 | #endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */ 673 | 674 | #if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) 675 | /* This case when the file really is being compiled as a loadable 676 | ** extension */ 677 | # define SQLITE_EXTENSION_INIT1 const sqlite3_api_routines *sqlite3_api=0; 678 | # define SQLITE_EXTENSION_INIT2(v) sqlite3_api=v; 679 | # define SQLITE_EXTENSION_INIT3 \ 680 | extern const sqlite3_api_routines *sqlite3_api; 681 | #else 682 | /* This case when the file is being statically linked into the 683 | ** application */ 684 | # define SQLITE_EXTENSION_INIT1 /*no-op*/ 685 | # define SQLITE_EXTENSION_INIT2(v) (void)v; /* unused parameter */ 686 | # define SQLITE_EXTENSION_INIT3 /*no-op*/ 687 | #endif 688 | 689 | #endif /* SQLITE3EXT_H */ 690 | --------------------------------------------------------------------------------