Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
0141d75
Add WASM compatibility for componentize-py builds
kesmit13 Mar 19, 2026
6b2aec8
Add WIT interface definition for WASM UDF components
kesmit13 Mar 19, 2026
ed94b61
Add type annotations to WASM numpy stub and UDF handler
kesmit13 Mar 20, 2026
d87f550
Add code generation for UDF handler function registration
kesmit13 Mar 23, 2026
17675e2
Replace top-level optional imports with lazy import helpers
kesmit13 Mar 23, 2026
8fdd612
Fix Python 3.10+ union syntax in udf_handler type annotation
kesmit13 Mar 23, 2026
e2eb799
feat: add call_function_accel C function to accel.c
kesmit13 Mar 23, 2026
6acd2e0
Add WASM build script for wasm32-wasip2 target
kesmit13 Mar 25, 2026
7a44ff3
Remove WASM numpy_stub, now unnecessary with lazy imports
kesmit13 Mar 25, 2026
4900c22
Add collocated Python UDF server with pre-fork process mode
kesmit13 Mar 27, 2026
9047562
Fix @@register propagation in collocated server process mode
kesmit13 Mar 31, 2026
e1d175e
Fix broken pipe in collocated UDF server under concurrent load
kesmit13 Apr 1, 2026
539cdd8
Guard np.dtype check in normalize_dtype for environments without numpy
kesmit13 Apr 1, 2026
75a68cb
Guard WASI-incompatible POSIX APIs in accel.c with #ifndef __wasi__
kesmit13 Apr 1, 2026
3c92e7a
Call setup_logging() in FunctionHandler.initialize() for WASM handler
kesmit13 Apr 1, 2026
af787e2
Add WASI stubs for mmap_read/mmap_write/recv_exact and fix accel logging
kesmit13 Apr 1, 2026
c624f8c
Address PR #121 review comments: memory safety, correctness, hardening
kesmit13 Apr 2, 2026
29b573c
Fix recv_exact protocol desync and unchecked PyObject_Length returns
kesmit13 Apr 2, 2026
eafacc0
Fix _iquery DataFrame conversion for non-tuple results_type
kesmit13 Apr 2, 2026
6f8ad80
Fix MYSQL_TYPE_NULL data pointer advancement in call_function_rowdat_1
kesmit13 Apr 7, 2026
cd39537
Fix missing PyErr_Occurred checks in call_function_accel
kesmit13 Apr 8, 2026
52f4981
Add decimal, datetime, date, and time type support across all UDF paths
kesmit13 Apr 9, 2026
435e896
Lazy-load IPython in singlestoredb.utils.events
kesmit13 Apr 9, 2026
7eb99f2
Fix refcount leak, unaligned reads, and YEAR type mismatch in accel.c
kesmit13 Apr 9, 2026
6f9d714
Enable VECTOR type with element type validation in dtypes
kesmit13 Apr 10, 2026
cfd12ed
Add JSON wire format type conversions for UDF data types
kesmit13 Apr 10, 2026
c87d138
Restore socket timeout after partial-read recovery in _recv_exact_py
kesmit13 Apr 10, 2026
695f150
Rename collocated UDF package to plugin
kesmit13 Apr 14, 2026
1e84fe3
Update docs and plugin/ terminology after collocated→plugin rename
kesmit13 Apr 14, 2026
75c474a
Add missing -MYSQL_TYPE_BLOB case in numpy sizing pass
kesmit13 Apr 15, 2026
30a7f19
Add NULL-type bounds checks, fix rowdat_1 null handling, and expand t…
kesmit13 Apr 22, 2026
ef74c88
Consolidate duplicated rowdat_1 type dispatch logic in accel.c
kesmit13 Apr 22, 2026
2e1709e
Remove redundant under2camel in ShowAccessor._iquery
kesmit13 Apr 23, 2026
6337587
Fix JSON mask inversion, add call_function_accel test coverage, and c…
kesmit13 Apr 27, 2026
d45b308
Address PR #121 review comments: refcount leaks, type shadowing, prot…
kesmit13 Apr 28, 2026
a34cab8
Define PY_SSIZE_T_CLEAN before Python.h include
kesmit13 Apr 30, 2026
8d14fd5
Fix UDF discovery to not require Plugin import from wasm module
kesmit13 Apr 30, 2026
3e914a8
Fix row_id type to int64_t to match signed rowdat_1 wire format
kesmit13 Apr 30, 2026
6eed853
Fix decimal/datetime numpy remap in create_numpy_array
kesmit13 Apr 30, 2026
bd935c4
Fix handshake error paths leaking received file descriptors
kesmit13 Apr 30, 2026
45d79c8
Pass plugin module directly to FunctionRegistry.initialize()
kesmit13 May 12, 2026
3a5c409
Add vector/columnar UDF dispatch to plugin call_function()
kesmit13 May 12, 2026
b7ed182
Add pre-release tag builds with GitHub Release assets
kesmit13 May 15, 2026
9dccf1d
Fix pipefail flag order and add handshake protocol tests
kesmit13 May 15, 2026
2c6736b
Guard Unix-only headers and functions against _WIN32
kesmit13 May 15, 2026
a84d981
Add @@delete control signal to plugin server
kesmit13 May 29, 2026
db5738b
Add plugin server integration tests and fix .gitignore test file scope
kesmit13 Jun 1, 2026
d2158a9
Fix four correctness and robustness issues
kesmit13 Jun 1, 2026
25281b4
Document pytest plugin, plugin UDF server CLI, and missing extras
kesmit13 Jun 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions .github/workflows/fusion-docs.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
on:
push:
tags:
- 'v*.*.*'
release:
types: [published]

name: Generate Fusion docs

Expand Down Expand Up @@ -36,6 +35,6 @@ jobs:
- name: Upload release asset
run: |
gh release upload ${{ github.ref_name }} fusion-docs.zip
gh release upload ${{ github.event.release.tag_name }} fusion-docs.zip
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
24 changes: 23 additions & 1 deletion .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@
name: Publish packages

on:
push:
tags:
- 'v*-rc*'
- 'v*-test*'
- 'v*-alpha*'
- 'v*-beta*'
release:
types: [published]
workflow_dispatch:
Expand Down Expand Up @@ -159,13 +165,15 @@ jobs:
permissions:
id-token: write # Required for OIDC trusted publishing
actions: read # Required for actions/download-artifact
contents: read # Required for repository access
contents: write # Required for gh release create/upload

environment:
name: publish
url: https://pypi.org/p/singlestoredb

steps:
- uses: actions/checkout@v3

- name: Download Linux wheels and sdist
uses: actions/download-artifact@v4
with:
Expand All @@ -184,6 +192,20 @@ jobs:
name: artifacts-macOS
path: dist

- name: Create GitHub Release
if: ${{ github.event_name == 'push' }}
run: |
gh release create ${{ github.ref_name }} dist/* --prerelease --title "${{ github.ref_name }}"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Upload release assets
if: ${{ github.event_name == 'release' }}
run: |
gh release upload ${{ github.event.release.tag_name }} dist/* --clobber
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Publish to PyPI
if: ${{ github.event_name == 'release' || github.event.inputs.publish_pypi == 'true' }}
uses: pypa/gh-action-pypi-publish@release/v1
Expand Down
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ dev-docs
**/.ipynb_checkpoints
**/.benchmarks
*.ipynb
test*.py
/test*.py
certs
**/*.prof
**/*.pprof
Expand Down
86 changes: 65 additions & 21 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,14 @@ singlestoredb/
│ ├── mmap.py # Memory-mapped execution
│ ├── json.py # JSON serialization
│ ├── rowdat_1.py # ROWDAT_1 format
│ └── arrow.py # Apache Arrow format
│ ├── arrow.py # Apache Arrow format
│ └── plugin/ # Plugin UDF server (Unix socket)
│ ├── __main__.py # CLI entry point
│ ├── server.py # Socket server with thread/process pool
│ ├── connection.py # Connection handling
│ ├── control.py # Control protocol
│ ├── registry.py # Function registry & discovery
│ └── wasm.py # WASM component interface
├── ai/ # AI/ML integration
│ ├── chat.py # Chat completion factory
Expand Down Expand Up @@ -603,9 +610,9 @@ Located in `singlestoredb/fusion/handlers/`:
## External Functions (UDFs)

The functions module (`singlestoredb/functions/`) enables deploying Python functions
as SingleStore external functions. UDF servers can be deployed as HTTP using
an ASGI application or a collocated socket server that uses mmap files to
transfer data.
as SingleStore external functions. UDF servers can be deployed in three modes: as HTTP using an ASGI application
(remote), a memory-mapped collocated server (lowest latency), or as a plugin
server using Unix sockets with a thread/process pool (CLI-driven).

### Architecture

Expand All @@ -629,6 +636,7 @@ transfer data.
├─────────────────────────────────────────────────────────────────────┤
│ asgi.py │ HTTP server via ASGI (Uvicorn; JSON or ROWDAT_1) │
│ mmap.py │ Memory-mapped shared memory (collocated; ROWDAT_1) │
│ plugin/ │ Plugin UDF server (Unix socket + thread/process pool) │
│ json.py │ JSON serialization over HTTP │
│ rowdat_1.py│ ROWDAT_1 binary format │
│ arrow.py │ Apache Arrow columnar format │
Expand Down Expand Up @@ -659,6 +667,33 @@ python -m singlestoredb.functions.ext.asgi \
my_functions
```

### Plugin Server CLI

The plugin server can be launched via the CLI for Unix socket-based UDF serving:

```bash
# Launch the plugin server
python -m singlestoredb.functions.ext.plugin \
--plugin-name myfuncs \
--search-path /home/user/libs \
--socket /tmp/my-udf.sock

# Or use the console_scripts entry point
python-udf-server --plugin-name myfuncs
```

**CLI Arguments:**

| Argument | Env Variable | Default | Description |
|----------|-------------|---------|-------------|
| `--plugin-name` | `PLUGIN_NAME` | (required) | Python module to import |
| `--search-path` | `PLUGIN_SEARCH_PATH` | `""` | Colon-separated search dirs for the module |
| `--socket` | `PLUGIN_SOCKET_PATH` | auto-generated | Unix socket path |
| `--n-workers` | `PLUGIN_N_WORKERS` | `0` (CPU count) | Worker threads/processes |
| `--max-connections` | `PLUGIN_MAX_CONNECTIONS` | `32` | Socket backlog |
| `--log-level` | `PLUGIN_LOG_LEVEL` | `info` | Logging level (debug/info/warning/error) |
| `--process-mode` | `PLUGIN_PROCESS_MODE` | `process` | Concurrency mode: `thread` or `process` |

### Type Mapping

The `signature.py` module maps Python types to SQL types:
Expand All @@ -682,29 +717,30 @@ The `signature.py` module maps Python types to SQL types:
┌─────────────────────────────────────────────────────────────────────┐
│ SingleStore Database │
└─────────────────────────────────────────────────────────────────────┘
│ │
│ ASGI/HTTP │ Memory-mapped
│ (remote) │ (collocated)
▼ ▼
┌─────────────┐ ┌─────────────┐
│ asgi.py │ │ mmap.py │
│ Uvicorn │ │ Shared │
│ HTTP/2 │ │ Memory │
└─────────────┘ └─────────────┘
│ │
└────────────────────┘
┌─────────────────┐
│ Python UDF │
│ Functions │
└─────────────────┘
│ │
│ ASGI/HTTP │ Memory-mapped │ Plugin
│ (remote) │ (collocated) │ (Unix socket)
▼ ▼
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ asgi.py │ │ mmap.py │ │ plugin/ │
│ Uvicorn │ │ Shared │ │ Unix sock │
│ HTTP/2 │ │ Memory │ │ + pool │
└─────────────┘ └─────────────┘ └─────────────┘
│ │
└────────────────────┴────────────────────
┌─────────────────┐
│ Python UDF │
│ Functions │
└─────────────────┘
```

| Mode | File | Use Case |
|------|------|----------|
| ASGI | `asgi.py` | Remote execution via HTTP, scalable |
| Memory-mapped | `mmap.py` | Collocated execution, lowest latency |
| Plugin | `plugin/` | Unix socket server, thread/process pool, CLI-driven |
| JSON | `json.py` | Simple serialization, debugging |
| ROWDAT_1 | `rowdat_1.py` | Binary format, efficient |
| Arrow | `arrow.py` | Columnar format, analytics |
Expand Down Expand Up @@ -1043,6 +1079,13 @@ with free_tier.start() as server:
| `SINGLESTOREDB_MANAGEMENT_TOKEN` | Management API token | None |
| `SINGLESTORE_LICENSE` | License key for Docker | None |
| `USE_DATA_API` | Use HTTP API for tests | 0 |
| `PLUGIN_NAME` | Plugin server: Python module to import | None |
| `PLUGIN_SEARCH_PATH` | Plugin server: colon-separated module search dirs | `""` |
| `PLUGIN_SOCKET_PATH` | Plugin server: Unix socket path | auto-generated |
| `PLUGIN_N_WORKERS` | Plugin server: worker count (0 = CPU count) | `0` |
| `PLUGIN_MAX_CONNECTIONS` | Plugin server: socket backlog | `32` |
| `PLUGIN_LOG_LEVEL` | Plugin server: logging level | `info` |
| `PLUGIN_PROCESS_MODE` | Plugin server: `thread` or `process` | `process` |

### B. Cursor Type Matrix

Expand Down Expand Up @@ -1096,4 +1139,5 @@ Feature options:
| Management API | `singlestoredb/management/workspace.py` |
| Fusion handlers | `singlestoredb/fusion/handler.py` |
| UDF decorator | `singlestoredb/functions/decorator.py` |
| Plugin UDF server | `singlestoredb/functions/ext/plugin/server.py` |
| Test fixtures | `singlestoredb/pytest.py` |
4 changes: 4 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,10 @@ pytest -v --cov=singlestoredb.connection singlestoredb/tests/test_connection.py
# Test UDF functionality
pytest singlestoredb/tests/test_udf.py

# Manual testing of the plugin UDF server
python -m singlestoredb.functions.ext.plugin \
--plugin-name myfuncs --search-path /path/to/modules

# Test against specific server (skips Docker)
SINGLESTOREDB_URL=admin:pass@localhost:3306 pytest -v singlestoredb/tests

Expand Down
60 changes: 59 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ analytics and vector search.
- **Vector Store**: Pinecone-compatible vector database API for similarity search
applications with built-in connection pooling
- **User-Defined Functions**: Deploy Python functions as SingleStore UDFs with
automatic type mapping
automatic type mapping (HTTP/ASGI or plugin-mode via CLI)
- **SQLAlchemy Support**: Integrate with SQLAlchemy through the optional
`sqlalchemy-singlestoredb` adapter
- **Fusion SQL**: Extend SQL with custom client-side command handlers
Expand Down Expand Up @@ -53,6 +53,9 @@ pip install 'singlestoredb[rsa]'
# Ed25519 key authentication
pip install 'singlestoredb[ed22519]'

# Pytest plugin with Docker test containers
pip install 'singlestoredb[pytest,docker]'

# Multiple extras can be combined
pip install 'singlestoredb[vectorstore,sqlalchemy]'
```
Expand All @@ -66,6 +69,8 @@ pip install 'singlestoredb[vectorstore,sqlalchemy]'
| `kerberos` / `gssapi` | Kerberos/GSSAPI authentication support |
| `rsa` | RSA key exchange for encrypted connections |
| `ed22519` | Ed25519 key authentication |
| `docker` | Docker SDK for automated test container management |
| `pytest` | Pytest plugin with SingleStoreDB Docker test fixtures |

## Documentation

Expand Down Expand Up @@ -250,6 +255,59 @@ conn.execute("""
See [singlestoredb/fusion/README.md](singlestoredb/fusion/README.md)
for details on writing custom Fusion SQL handlers.

## Pytest Plugin

The SDK includes a pytest plugin that automatically manages SingleStoreDB Docker
containers for integration testing. Install with:

```bash
pip install 'singlestoredb[pytest,docker]'
```

The plugin provides these fixtures:

- `singlestoredb_test_container` (session-scoped): Starts and stops a
SingleStoreDB Dev container, or reuses an existing server if
`SINGLESTOREDB_URL` is set
- `singlestoredb_connection` (session-scoped): Database connection to the
test container
- `singlestoredb_tempdb` (function-scoped): Cursor with a fresh temporary
database, dropped after the test

The plugin supports `pytest-xdist` parallel execution with leader/follower
coordination for container lifecycle.

```python
def test_query(singlestoredb_tempdb):
singlestoredb_tempdb.execute('CREATE TABLE t (id INT)')
singlestoredb_tempdb.execute('INSERT INTO t VALUES (1)')
singlestoredb_tempdb.execute('SELECT * FROM t')
assert singlestoredb_tempdb.fetchone() == (1,)
```

## Plugin UDF Server

The SDK ships a high-performance plugin-mode UDF server that runs as a
standalone process, communicating with SingleStoreDB over a Unix socket.

```bash
# Via the installed CLI entry point
python-udf-server --plugin-name myfuncs --search-path /path/to/modules

# Or as a Python module
python -m singlestoredb.functions.ext.plugin --plugin-name myfuncs
```

| Option | Env Variable | Default | Description |
|--------|-------------|---------|-------------|
| `--plugin-name` | `PLUGIN_NAME` | (required) | Python module to import |
| `--search-path` | `PLUGIN_SEARCH_PATH` | `""` | Colon-separated module search dirs |
| `--socket` | `PLUGIN_SOCKET_PATH` | auto-generated | Unix socket path |
| `--n-workers` | `PLUGIN_N_WORKERS` | `0` (CPU count) | Worker threads/processes |
| `--max-connections` | `PLUGIN_MAX_CONNECTIONS` | `32` | Socket backlog |
| `--log-level` | `PLUGIN_LOG_LEVEL` | `info` | Logging level |
| `--process-mode` | `PLUGIN_PROCESS_MODE` | `process` | `thread` or `process` concurrency |

## Advanced Options

### SSL/TLS Configuration
Expand Down
Loading
Loading