neuron-explorer
Steering-first neuron explorer for Qwen3.5-4B: push every MLP neuron, keep every rollout, browse the results
翻譯準備中
Your Query Client, Forged with Rust: Fast, Safe, Efficient.
翻譯準備中
取自儲存庫 README
正在載入完整 README。
檢視完整 README ↗Your fast, native, cross‑platform SQL & NoSQL database client (Desktop, with early groundwork for iPadOS) — built in Rust, and built for the AI era: a safe door between your coding agents and your databases.
Coding agents already write a large share of your SQL. What they lack is a safe way to see the real database. Tabular is that door: Claude Code, Cursor, Codex and any other MCP client can inspect schemas and run queries through Tabular, while the passwords, the SSH tunnels and the final say on writes stay with you.
flowchart LR
subgraph Agents["Your AI agents"]
A1["Claude Code"]
A2["Cursor"]
A3["Codex / Gemini CLI"]
A4["Any MCP client"]
end
subgraph Tabular["Tabular · tabular mcp"]
direction TB
T1["Schema and read-only queries: instant"]
T2["Writes wait for your approval"]
T3["Every call is logged"]
T4["Credentials never leave here"]
end
subgraph DBs["Your databases"]
D1[("PostgreSQL")]
D2[("MySQL / MariaDB")]
D3[("SQLite · SQL Server")]
D4[("MongoDB · Redis")]
end
Agents -- "MCP, no passwords" --> Tabular
Tabular -- "your credentials, SSH, mTLS" --> DBs
What you get
Read only (the
default), Ask, Edit or Agent. Risky statements are queued and run only after you
approve them in the Tabular window.EXPLAIN plans, running queries, ER diagrams, query history, notes and per-project
memory. No more pasting a stale schema dump into a chat.Compared with the usual ways of giving an agent database access
| When an agent needs your database… | Paste schema into chat | Raw DB connection for the agent | Tabular MCP |
|---|---|---|---|
| Agent sees the real, current schema | No, a stale copy | Yes | Yes, plus diagrams and FKs |
| Password stays out of the agent | Yes | No, it sits in .env or the prompt | Yes, OS keychain |
| Writes need a human | Partly, you copy-paste | No, runs blind | Yes, approve in-app |
| Audit trail of agent queries | No | No | Yes, every call |
EXPLAIN and profiling for the agent | No | Partly, raw text | Yes, plan plus bottlenecks |
| Works through SSH bastion / mTLS | Partly | No, the agent must tunnel | Yes, handled per connection |
Connect an agent in one line
claude mcp add tabular -- /path/to/tabular mcp # Claude Code
tabular mcp --print-config # JSON snippet for Cursor, Windsurf, Codex
Full details, the tool list and the safety model are in docs/MCP.md.
Tabular is a lightweight, native database client built with the eframe/egui stack. It focuses on instant startup, responsive UI, safe concurrency, and a distraction‑free workflow for developers, data engineers, and DBAs. Unlike web/electron apps, Tabular ships as a single native binary with a small memory footprint while still offering rich features like autocomplete, multiple drivers, query history, export tools, and self‑update.
EXPLAIN ANALYZE (Postgres, MySQL, MSSQL) with Sugiyama hierarchical layout, cost percentages, and automated bottleneck/scan warningsWHERE clauses with operator dropdowns (=, !=, LIKE, IN, IS NULL, BETWEEN, >, <) pushed directly to the server engine.sql.gz, .tar, .dump), and SQLite pure-Rust backup supportALTER TABLE DDL forward and rollback script generationKill Process / Cancel QueryCmd+P / Cmd+K): Instant fuzzy search across tables, views, procedures, saved queries, history, connections, and commands with quick filter pillsNext-generation SQL editing ergonomics tailored for developer velocity and precision:
FROM users u, orders o or FROM users u JOIN orders o ON ...). Typing u. instantly suggests columns belonging specifically to the users table.JOIN ... ON clauses, relational foreign key conditions are detected from table metadata and surfaced at the very top of autocomplete suggestions (e.g., orders.user_id = users.id).UPPERCASE (SELECT, WHERE), lowercase (select, where), or Preserve according to editor preferences.Ctrl+Enter / Cmd+Enter): Runs only the statement enclosing the cursor without requiring manual text selection.src/query_tools/statement_parser.rs) that safely ignores semicolons (;) inside string literals ('...'), quoted identifiers ("..."), line comments (--), and block comments (/* ... */).Ctrl+/ / Cmd+/): Toggles -- comments on cursor line or multi-line selections with exact indentation preservation.Ctrl+Shift+F / Cmd+Shift+F): Instantly prettifies SQL queries with standardized indentation and active keyword casing.Shift+Alt+Down): Clones the current line or multi-line selection downward.Alt+Up / Alt+Down): Moves the current line or selection up or down smoothly with cursor offset retention.| Action | macOS Shortcut | Linux / Windows Shortcut | Description |
|---|---|---|---|
| Execute Current Statement | Cmd + Enter | Ctrl + Enter | Run statement at cursor (or selection if highlighted) |
| Format Query | Cmd + Shift + F | Ctrl + Shift + F | Prettify SQL with indentation and keyword casing |
| Toggle Comment | Cmd + / | Ctrl + / | Toggle -- comment on current line or selection |
| Duplicate Line Down | Shift + Option + Down | Shift + Alt + Down | Duplicate current line or selection downward |
| Move Line Up | Option + Up | Alt + Up | Move current line or selection upward |
| Move Line Down | Option + Down | Alt + Down | Move current line or selection downward |
| Universal Quick Open | Cmd + P / Cmd + K | Ctrl + P / Ctrl + K | Fuzzy search tables, queries, history, commands |
| Find in Editor | Cmd + F | Ctrl + F | Search with Regex, Match Case, Whole Word |
| Replace in Editor | Cmd + H | Ctrl + H | Find and replace within editor or selection |
| AI Assistant | Cmd + Shift + A | Ctrl + Shift + A | Schema-aware AI SQL generation & completion |
Organize and group editor tabs seamlessly to enhance workflow when dealing with many queries, tables, and API requests.
Instant in-memory filtering for all sidebar navigation panels (Connections, Saved Queries, History, and HTTP Collections).
Analyze and optimize slow queries with interactive visual graphs instead of deciphering raw JSON outputs.
EXPLAIN (ANALYZE, BUFFERS, FORMAT JSON)), MySQL (EXPLAIN FORMAT=JSON), and Microsoft SQL Server ShowPlan.Filter and navigate large data grids with speed and ease.
WHERE conditions pushed directly to the database query.Comprehensive database lifecycle management from within Tabular.
pg_dump, pg_restore, mysqldump, and sqlite3_backup..sql, .sql.gz, .tar, and .dump formats.Monitor database health and manage access rights.
KILL PID).SELECT, INSERT, UPDATE, DELETE, EXECUTE).Extend Tabular with lightweight sandboxed Wasm modules (wasmi engine).
tabular-server) stores only ciphertext it cannot read.Context‑aware AI chat integrated directly into the query editor.
agy), Claude Code (claude), Gemini CLI (gemini), and custom commands) with per-agent model, reasoning effort, extra arguments, and enabled status.Assistant (agy · gemini-3.8-flash-low)).--AI SQL block and HTTP Client AI generation.tabular mcp, read‑only by default): Claude Code receives it per request; agy and gemini register it via the Settings tab.Dedicated visual key explorer for Redis connections.
Lightweight REST/HTTP testing panel available as a tab alongside SQL results.
| Category | Engines / Protocols |
|---|---|
| Relational | PostgreSQL, MySQL/MariaDB, SQLite, Microsoft SQL Server |
| Document | MongoDB (BSON & compression) |
| Key/Value | Redis (standalone & cluster async manager) |
Notes:
mssql-client (TDS over TLS)ssl=off); login and data then travel unencrypted, so use it on a trusted
network or through the SSH tunnel. TDS Version can be pinned (7.1 – 7.4) when auto negotiation
fails. Grid paging and table export switch from OFFSET/FETCH to TOP/ROW_NUMBER() automatically
on servers older than 2012. SQL Server 2000 is not supported (no sys.* catalog views).libsqlite3-sys file mode).dmg (notarized) or .pkg.tar.gz, .deb, or Flatpak.zip / installerTabular.app into /Applications.General requirements:
sudo pacman -Syu --needed base-devel clang llvm pkgconf
export LIBCLANG_PATH=/usr/lib
git clone https://github.com/tabular-id/tabular.git
cd tabular/tabular-client
cargo build --release
sudo apt update
sudo apt install -y build-essential clang libclang-dev pkg-config
git clone https://github.com/tabular-id/tabular.git
cd tabular/tabular-client
cargo build --release
xcode-select --install
git clone https://github.com/tabular-id/tabular.git
cd tabular/tabular-client
cargo build --release
./target/release/tabular
| Variable | Purpose | Example |
|---|---|---|
| TABULAR_DATA_DIR | Override data directory location | /data/tabular |
| RUST_LOG | Enable logging | RUST_LOG=info ./tabular |
For distributing outside the Mac App Store:
export APPLE_ID="[email protected]"
export APPLE_PASSWORD="app-specific-password"
export APPLE_TEAM_ID="TEAMID"
export APPLE_BUNDLE_ID="id.tabular.database"
export APPLE_IDENTITY="Developer ID Application: Your Name (TEAMID)"
export NOTARIZE=1
./build.sh macos --deps
Default location: ~/.tabular
Contents of the data directory:
preferences.* — UI & app settingscache.* — metadata & driver caches (SQLite)queries/ — saved querieshistory/ — executed query historyhttp_state/ — HTTP client collections & environmentssrc/
├── main.rs # App entrypoint
├── window_egui/ # Main UI / egui shell & sub-modules
├── data_table/ # High-performance data grid & inspector
│ ├── export_clipboard.rs # Quick clipboard formatters (Markdown, JSON, CSV, SQL INSERT)
│ ├── filter_sort.rs # Server-side SQL filter builder & sorting
│ ├── inspector.rs # Multi-tab JSON, Hex, Image & Text inspector
│ ├── render_data.rs # Grid rendering, FK hyperlinks, column freeze
│ └── pagination.rs # Paginated streaming
├── query_profiler/ # Visual EXPLAIN / EXPLAIN ANALYZE engine
│ ├── mod.rs # Public profiler API
│ ├── parser.rs # Multi-database JSON/XML EXPLAIN parser
│ ├── graph.rs # Hierarchical Sugiyama layout canvas
│ └── warnings.rs # Automated bottleneck & scan detector
├── query_tools/ # SQL developer ergonomics & statement tools
│ ├── mod.rs # Formatter with keyword casing & snippet registry
│ ├── statement_parser.rs # Intelligent statement boundary & cursor finder
│ └── text_actions.rs # Line comment toggle, duplicate lines, move lines
├── backup_restore.rs # Native dump/restore runner (pg_dump, mysqldump, sqlite)
├── dialog_backup_restore.rs # Visual Backup & Restore wizard modal
├── dba_monitor.rs # Real-time processlist & lock tree monitor
├── user_manager.rs # User, role & permission management GUI
├── ssh_tunnel.rs # Multi-hop SSH bastion tunneling & mTLS
├── plugin_runtime/ # WebAssembly (wasmi) plugin execution engine
│ ├── engine.rs # Sandbox runtime & Host API bindings
│ ├── manager.rs # Plugin registry & lifecycle
│ ├── templates/ # Parquet, DuckDB & ORM starter templates
│ └── ui.rs # Plugin manager & artifact viewer modal
├── quick_open.rs # Universal Quick Open palette (Cmd+P / Cmd+K)
├── editor.rs # Custom editor widget, active line highlight, shortcuts
├── editor_autocomplete_new.rs # IntelliSense 2.0 (alias resolution, FK auto-joins, casing)
├── sync/ # E2EE Cloud Sync & zero-knowledge vault
├── driver_*.rs # Database drivers (Postgres, MySQL, SQLite, MSSQL, Redis, Mongo)
├── http_client.rs # Built-in REST/HTTP tester
├── redis_browser.rs # Redis visual key browser
└── query_ast/ # Database-agnostic AST query optimizer & emitter
git clone https://github.com/tabular-id/tabular.git
cd tabular/tabular-client
cargo run
| Action | Command |
|---|---|
| Build debug | cargo build |
| Run | cargo run |
| Test | cargo test |
| Lint (clippy) | cargo clippy -- -D warnings |
| Format | cargo fmt |
| Release build | cargo build --release |
| API Integration Test | bash test_api.sh |
To test the Tabular synchronization and authentication HTTP API endpoints (server health check, OAuth ticket polling, token refresh, user profile update, and user search):
# Execute against the default production endpoint (https://api.tabular.id)
bash test_api.sh
# Or execute against a local or staging server
API_URL=http://localhost:8080 ./test_api.sh
| Purpose | Crate |
|---|---|
| UI & App Shell | eframe, egui_extras, egui_commonmark |
| Async Runtime | tokio, futures, futures-util |
| Relational DB | sqlx (postgres, mysql, sqlite), mssql-client, mssql-driver-pool |
| Key-Value & Doc | redis, mongodb, bson |
| Plugin Runtime | wasmi (pure-Rust WebAssembly engine) |
| E2EE Crypto | argon2, aes-gcm, sha2, chacha20poly1305 |
| Data Formats | serde, serde_json, chrono, rust_decimal, hex, csv, rust_xlsxwriter, flate2, tar, zip |
| Parser & Trees | tree-sitter, tree-sitter-json, tree-sitter-javascript, sqlformat |
| Dialogs & OS | rfd, dirs, semver, reqwest |
Current package version: 0.17.0.
Contributions are welcome (bug fixes, new drivers, UI, performance). Suggested workflow:
cargo fmt && cargo clippy before committing.cargo test.| Issue | Hint |
|---|---|
| Build fails: clang not found | Install clang / set LIBCLANG_PATH |
| TLS errors on connect | Verify certificates, CA bundle, or mTLS keys |
| SQLite file locked | Close other processes; check file permissions |
| Backup binary not found | Ensure pg_dump or mysqldump is in system PATH |
u.), Foreign Key auto-join completions, statement-level execution (Ctrl+Enter), quick query formatting (Ctrl+Shift+F), line comments (Ctrl+/), line duplication & moving (Alt+Up/Down), active line highlight, and multi-format result clipboard exports (Markdown, JSON, CSV, SQL INSERTs) (v0.17)Cmd+P / Cmd+K): Rapid navigation across tables, queries, history, and actions (v0.13)This project is dual‑licensed:
LICENSE-AGPLLICENSE)src/query_tools/statement_parser.rs) that safely handles semicolons (;) inside string quotes ('...'), quoted identifiers ("..."), line comments (--), and block comments (/* ... */).find_statement_at_cursor enabling statement-level execution with Ctrl+Enter / Cmd+Enter without requiring manual block selection.src/editor_autocomplete_new.rs):
FROM, JOIN, UPDATE, INTO, and comma-separated joins (e.g. FROM users u, orders o), enabling u. to suggest columns for users.JOIN ... ON and surfaces recommended join predicates with highest priority.UPPERCASE, lowercase, or Preserve matching editor settings.src/query_tools/text_actions.rs & src/editor.rs):
Ctrl+/ / Cmd+/): smart addition and removal of -- with exact indentation retention.Shift+Alt+Down): clones single or multi-line selections downwards.Alt+Up / Alt+Down): moves current line or multi-line selection up or down with automatic cursor position tracking.Ctrl+Shift+F / Cmd+Shift+F): fast SQL prettification adhering to keyword casing preferences.src/data_table/export_clipboard.rs):
INSERT INTO statements.sync_profile_inputs_from_account.test_api.sh cURL test suite to automate verification of backend sync and auth endpoints.EXPLAIN ANALYZE (Postgres, MySQL, MSSQL) with Sugiyama layout, cost percentage visualization, and automated bottleneck warnings.pg_dump, mysqldump, and SQLite backup APIs with compression and live log monitoring.ALTER TABLE and ROLLBACK migration scripts.KILL PID).wasmi Wasm engine with Host API, starter plugins for Apache Parquet/DuckDB export and ORM model generation (Diesel, SeaORM, Prisma, TypeORM, SQLAlchemy).Cmd+P / Cmd+K): Fast category-filtered fuzzy search for tables, views, procedures, saved queries, history, connections, and commands.Cmd+Shift+A): Schema-aware SQL generation powered by OpenAI, Claude, Gemini, xAI, Groq, OpenRouter, Copilot, local models (Ollama, llama.cpp, MLX), or custom endpoints.Built with the Rust ecosystem. egui & sqlx projects are especially instrumental.
Made with Rust 🦀 for people who love fast, native tools.
Tabular uses a database‑agnostic AST (Abstract Syntax Tree) to separate query logic from database‑specific implementations. Benefits:
Raw SQL → sqlparser → AST (generic) → Logical Plan
sqlparser crate for universal SQL parsingLogicalQueryPlan (database‑agnostic IR)File: src/query_ast/parser.rs
pub enum LogicalQueryPlan {
Projection { exprs: Vec<Expr>, input: Box<LogicalQueryPlan> },
Filter { predicate: Expr, input: Box<LogicalQueryPlan> },
Sort { items: Vec<SortItem>, input: Box<LogicalQueryPlan> },
Limit { limit: u64, offset: u64, input: Box<LogicalQueryPlan> },
Join { left, right, on, kind },
TableScan { table, alias },
// ... etc
}
File: src/query_ast/logical.rs
Benefits:
Logical Plan → Apply Rules → Optimized Logical Plan
Rules (apply to all databases):
File: src/query_ast/rewrite.rs
Example:
// Before rewrite:
Projection -> Filter -> Filter -> TableScan
// After rewrite:
Projection -> Filter(merged) -> TableScan
Optimized Plan → Dialect → SQL for Target DB
Trait-based:
pub trait SqlDialect {
fn quote_ident(&self, ident: &str) -> String;
fn emit_limit(&self, limit: u64, offset: u64) -> String;
fn supports_window_functions(&self) -> bool;
// ... etc
}
Implementations:
MySqlDialect: Backticks, LIMIT n OFFSET mPostgresDialect: Double quotes, window functionsMssqlDialect: Square brackets, TOP n, OFFSET FETCHSqliteDialect: Backticks, limited window supportMongoDialect: Minimal SQL, mostly native operationsRedisDialect: Very limited SQLFiles:
src/query_ast/emitter/mod.rs (core emitter)src/query_ast/emitter/dialect.rs (dialect trait + implementations)Emitted SQL → Connection Pool → Execute → Results
Trait-based:
#[async_trait]
pub trait DatabaseExecutor {
fn database_type(&self) -> DatabaseType;
async fn execute_query(&self, sql: &str, ...) -> Result<QueryResult, ...>;
fn supports_feature(&self, feature: SqlFeature) -> bool;
}
File: src/query_ast/executor.rs
// Level 1: Structural fingerprint (quick pre-check)
let fp = structural_fingerprint(sql); // hash without parsing
// Level 2: Logical plan hash (after parsing)
let plan = parse(sql);
let plan_hash = hash_plan(&plan);
// Cache key includes: plan_hash + db_type + pagination + options
let cache_key = format!("{}::{:?}::{:?}", plan_hash, db_type, pagination);
Cache Stats Available:
let (hits, misses) = query_ast::cache_stats();
println!("Cache hit rate: {:.1}%", hits as f64 / (hits + misses) as f64 * 100.0);
Same logical plan works for different databases:
Parse once → Rewrite once → Emit N times (one per DB type)
Arc<LogicalQueryPlan> untuk sharing plans| Feature | Status | Notes |
|---|---|---|
| Basic SELECT parsing | ✅ | Includes JOIN, GROUP BY, HAVING, LIMIT |
| Filter/WHERE | ✅ | AND/OR/NOT, comparison ops |
| Projection/SELECT list | ✅ | Columns, aliases, * |
| Sorting/ORDER BY | ✅ | Multiple columns, ASC/DESC |
| Pagination/LIMIT | ✅ | Rewritten per database |
| JOINs | ✅ | INNER, LEFT, RIGHT, FULL |
| GROUP BY | ✅ | Multiple expressions |
| HAVING | ✅ | Post-aggregation filters |
| DISTINCT | ✅ | Deduplication |
| CTEs/WITH | ✅ | Single-use CTE inlining |
| UNION/UNION ALL | ✅ | Set operations |
| Window Functions | ✅ | ROW_NUMBER, RANK, etc |
| Subqueries | ✅ | Scalar, correlated detection |
| Plan Caching | ✅ | Multi-level with fingerprinting |
| Rewrite Rules | ✅ | 9 rules implemented |
| MySQL Dialect | ✅ | Full support |
| PostgreSQL Dialect | ✅ | Full support |
| SQLite Dialect | ✅ | Full support |
| MS SQL Dialect | ✅ | TOP/OFFSET FETCH syntax |
| MongoDB Dialect | 🟡 | Limited SQL, prefer native |
| Redis Dialect | 🟡 | Very limited |
| Feature | Priority | Effort |
|---|---|---|
| Executor trait implementation | HIGH | Medium |
| Database-specific executors | HIGH | Medium |
| Multi-statement support | MEDIUM | High |
| DDL parsing (CREATE/ALTER/DROP) | MEDIUM | High |
| Advanced subquery optimizations | LOW | High |
| Correlated subquery rewrite | LOW | High |
| Cost-based optimization | LOW | Very High |
Simple SELECT: < 1ms (cache hit: < 0.1ms)
With JOIN: < 5ms (cache hit: < 0.1ms)
Complex (3+ JOINs): < 20ms (cache hit: < 0.1ms)
Repeated queries: 95%+ hit rate
Pagination queries: 90%+ hit rate
Ad-hoc queries: 40%+ hit rate (fingerprint matching)
Per cached plan: ~5KB (typical)
Cache size limit: 1000 plans (configurable)
Total overhead: ~5MB for 1000 plans
let debug_str = query_ast::debug_plan(&sql, &db_type)?;
println!("{}", debug_str);
// Output:
// -- debug plan for PostgreSQL
// Projection 3
// Filter "id > 10"
// TableScan(users alias=None)
let rules = query_ast::last_rewrite_rules();
println!("Applied rules: {:?}", rules);
// Output: ["auto_limit", "filter_pushdown", "projection_prune"]
let (nodes, depth, subqueries, correlated, windows) = query_ast::plan_metrics(&sql)?;
println!("Plan complexity: {} nodes, depth {}", nodes, depth);
let (hash, cache_key) = query_ast::plan_structural_hash(&sql, &db_type, pagination, auto_limit)?;
println!("Plan hash: {:x}", hash);
src/query_ast/
├── mod.rs # Public API & main compilation pipeline
├── ast.rs # Raw AST wrapper (thin layer over sqlparser)
├── logical.rs # Logical plan IR (database-agnostic)
├── parser.rs # SQL → Logical plan conversion
├── emitter/
│ ├── mod.rs # Plan → SQL emission
│ └── dialect.rs # Database-specific SQL generation
├── rewrite.rs # Optimization rules
├── executor.rs # Execution trait & registry
├── plan_cache.rs # Multi-level caching
└── errors.rs # Error types
src/driver_*.rs # Database-specific drivers (legacy + AST integration)
src/connection.rs # Connection pool management
src/cache_data.rs # Metadata caching (tables, columns, etc)
See Adding a New Database to Tabular for step-by-step guide to adding new database support.
tests/query_ast_tests.rsLast Updated: 2026-03-30 Version: 0.7.0 Maintainer: Tabular Team
This guide explains how to add support for a new database type using the Agnostic AST architecture.
Tabular uses a layered architecture with clear separation of concerns:
Raw SQL → Parser → Logical Plan → Optimizer/Rewriter → Emitter → Executor
↓ ↓ ↓ ↓
DB-Agnostic DB-Agnostic DB-Specific DB-Specific
Add your database to src/models/enums.rs:
#[derive(Clone, PartialEq, Serialize, Deserialize, Debug)]
pub enum DatabaseType {
MySQL,
PostgreSQL,
SQLite,
Redis,
MsSQL,
MongoDB,
YourNewDB, // <- Add here
}
pub enum DatabasePool {
MySQL(Arc<MySqlPool>),
PostgreSQL(Arc<PgPool>),
SQLite(Arc<SqlitePool>),
Redis(Arc<ConnectionManager>),
MsSQL(Arc<MssqlConfigWrapper>),
MongoDB(Arc<MongoClient>),
YourNewDB(Arc<YourDbConnection>), // <- Add here
}
Create src/query_ast/emitter/your_db_dialect.rs:
use super::dialect::SqlDialect;
use crate::models::enums::DatabaseType;
pub struct YourDbDialect;
impl SqlDialect for YourDbDialect {
fn db_type(&self) -> DatabaseType {
DatabaseType::YourNewDB
}
fn quote_ident(&self, ident: &str) -> String {
// Your database's identifier quoting
format!("\"{}\"", ident.replace('"', "\"\""))
}
fn emit_limit(&self, limit: u64, offset: u64) -> String {
// Your database's LIMIT syntax
if offset > 0 {
format!(" LIMIT {} OFFSET {}", limit, offset)
} else {
format!(" LIMIT {}", limit)
}
}
fn supports_window_functions(&self) -> bool {
true // or false, depending on your DB
}
fn supports_cte(&self) -> bool {
true // or false
}
fn supports_full_join(&self) -> bool {
true // or false
}
// Override other methods as needed for your database
}
Update src/query_ast/emitter/dialect.rs:
pub fn get_dialect(db_type: &DatabaseType) -> Box<dyn SqlDialect> {
match db_type {
DatabaseType::MySQL => Box::new(MySqlDialect),
DatabaseType::PostgreSQL => Box::new(PostgresDialect),
// ... other databases ...
DatabaseType::YourNewDB => Box::new(YourDbDialect), // <- Add here
}
}
Create src/query_ast/executors/your_db_executor.rs:
use async_trait::async_trait;
use crate::query_ast::executor::{DatabaseExecutor, QueryResult};
use crate::query_ast::errors::QueryAstError;
use crate::models::enums::DatabaseType;
pub struct YourDbExecutor {
// Connection pool or client reference
}
impl YourDbExecutor {
pub fn new() -> Self {
Self {}
}
}
#[async_trait]
impl DatabaseExecutor for YourDbExecutor {
fn database_type(&self) -> DatabaseType {
DatabaseType::YourNewDB
}
async fn execute_query(
&self,
sql: &str,
database_name: Option<&str>,
connection_id: i64,
) -> Result<QueryResult, QueryAstError> {
// 1. Get connection from pool using connection_id
// 2. Switch database if database_name is provided
// 3. Execute SQL query
// 4. Convert results to (Vec<String>, Vec<Vec<String>>)
// 5. Return results
todo!("Implement query execution for your database")
}
fn validate_query(&self, sql: &str) -> Result<(), QueryAstError> {
// Optional: Add database-specific validation
Ok(())
}
}
Register in src/query_ast/executor.rs:
impl ExecutorRegistry {
pub fn with_defaults() -> Self {
let mut registry = Self::new();
// ... existing executors ...
#[cfg(feature = "your_db")]
registry.register(Box::new(super::executors::YourDbExecutor::new()));
registry
}
}
Create src/driver_your_db.rs:
use crate::models::enums::DatabaseType;
use crate::window_egui::Tabular;
/// Create connection pool for your database
pub async fn create_connection_pool(
host: &str,
port: u16,
username: &str,
password: &str,
database: Option<&str>,
) -> Result<YourDbConnection, Box<dyn std::error::Error>> {
// Implement connection creation
todo!()
}
/// Fetch tables from your database
pub async fn fetch_tables_from_connection(
tabular: &Tabular,
connection_id: i64,
database_name: Option<String>,
) -> Result<Vec<String>, Box<dyn std::error::Error>> {
// Implement table listing
todo!()
}
/// Fetch columns from a table
pub async fn fetch_columns_from_table(
tabular: &Tabular,
connection_id: i64,
database_name: Option<String>,
table_name: &str,
) -> Result<Vec<(String, String)>, Box<dyn std::error::Error>> {
// Implement column listing (name, type)
todo!()
}
In src/connection.rs, add your database to the connection creation logic:
pub async fn get_or_create_connection_pool(
tabular: &mut Tabular,
connection_id: i64,
) -> Result<models::enums::DatabasePool, Box<dyn std::error::Error>> {
// ... existing code ...
match connection.connection_type {
models::enums::DatabaseType::MySQL => { /* ... */ }
models::enums::DatabaseType::PostgreSQL => { /* ... */ }
// ... other databases ...
models::enums::DatabaseType::YourNewDB => {
let client = driver_your_db::create_connection_pool(
&connection.host,
connection.port,
&connection.username,
&connection.password,
connection.database.as_deref(),
).await?;
let pool = models::enums::DatabasePool::YourNewDB(Arc::new(client));
tabular.connection_pools.insert(connection_id, pool.clone());
Ok(pool)
}
}
}
In src/sidebar_database.rs, add folder icon and logic:
pub fn create_database_folders_from_connections(connections: &[models::structs::Connection]) -> Vec<models::structs::TreeNode> {
// ... existing code ...
models::enums::DatabaseType::YourNewDB => {
let mut node = models::structs::TreeNode {
name: format!("Your DB Connections ({})", count),
node_type: models::enums::NodeType::YourNewDBFolder,
// ... rest of initialization ...
};
node
}
}
Add to src/models/enums.rs:
pub enum NodeType {
// ... existing types ...
YourNewDBFolder, // <- Add here
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_your_db_query() {
let executor = YourDbExecutor::new();
let result = executor.execute_query(
"SELECT * FROM users LIMIT 10",
None,
1,
).await;
assert!(result.is_ok());
}
#[test]
fn test_your_db_dialect() {
let dialect = YourDbDialect;
assert_eq!(dialect.quote_ident("table"), "\"table\"");
}
}
The AST layer automatically caches compiled plans. Ensure your SQL emission is deterministic for cache hits.
Add a feature flag for optional compilation:
In Cargo.toml:
[features]
default = ["mysql", "postgres", "sqlite"]
mysql = ["sqlx/mysql"]
postgres = ["sqlx/postgres"]
sqlite = ["sqlx/sqlite"]
your_db = ["your_db_driver"] # <- Add here
dialect.quote_ident(), never hardcode quotesHere's a minimal example of adding CockroachDB (which is PostgreSQL-compatible):
// 1. Add to DatabaseType enum
pub enum DatabaseType {
// ... existing ...
CockroachDB,
}
// 2. Reuse PostgreSQL dialect (it's compatible!)
pub fn get_dialect(db_type: &DatabaseType) -> Box<dyn SqlDialect> {
match db_type {
// ... existing ...
DatabaseType::CockroachDB => Box::new(PostgresDialect), // Reuse!
}
}
// 3. Create executor (can extend PostgreSQL executor)
pub struct CockroachExecutor {
pg_executor: PostgresExecutor, // Composition over inheritance!
}
#[async_trait]
impl DatabaseExecutor for CockroachExecutor {
fn database_type(&self) -> DatabaseType {
DatabaseType::CockroachDB
}
async fn execute_query(&self, sql: &str, db: Option<&str>, conn_id: i64)
-> Result<QueryResult, QueryAstError>
{
// Delegate to PostgreSQL executor since CockroachDB is wire-compatible
self.pg_executor.execute_query(sql, db, conn_id).await
}
}
After adding your database, measure:
driver_mysql.rs, driver_postgres.rs)query_ast/executor.rsquery_ast/emitter/dialect.rsquery_ast/rewrite.rs for optimization ideas✅ Separation of Concerns: Logic layer independent of database specifics ✅ Reusability: Share optimizations across all databases ✅ Testability: Mock executors for unit testing ✅ Extensibility: Add new databases without touching core logic ✅ Performance: Aggressive caching at every layer ✅ Type Safety: Rust's type system catches errors at compile time
This document explains how to build Tabular for different platforms using the provided build scripts.
xcode-select --installcargo install cross)The easiest way to build Tabular is using the provided build script:
# Build for current platform only
./build.sh
# Build for specific platform
./build.sh macos
./build.sh linux
./build.sh windows
# Build for all platforms
./build.sh all
# Install dependencies and build
./build.sh all --deps
# Clean and build
./build.sh macos --clean
If you prefer using Make directly:
# Show available targets
make help
# Install build dependencies
make install-deps
# Build for specific platforms
make bundle-macos
make bundle-linux
make bundle-windows
# Build everything
make release
# Clean build artifacts
make clean
Creates a universal binary that runs on both Intel and Apple Silicon Macs:
make bundle-macos
Outputs:
dist/macos/Tabular.app - macOS application bundledist/macos/Tabular.dmg - Disk image for distributionCreates binaries for x86_64 and aarch64 Linux systems:
make bundle-linux
Outputs:
dist/linux/tabular-x86_64-unknown-linux-gnu.tar.gzdist/linux/tabular-aarch64-unknown-linux-gnu.tar.gzCreates executables for x86_64 and aarch64 Windows systems:
make bundle-windows
Outputs:
dist/windows/tabular-x86_64-pc-windows-msvc.zipdist/windows/tabular-aarch64-pc-windows-msvc.zip# Development build (debug mode)
make dev
# Run the application
make run
# Run tests
make test
# Check code formatting and linting
make check
# Format code
make fmt
# Show project information
make info
| Platform | Architecture | Target Triple |
|---|---|---|
| macOS | Intel (x86_64) | x86_64-apple-darwin |
| macOS | Apple Silicon (ARM64) | aarch64-apple-darwin |
| Linux | x86_64 | x86_64-unknown-linux-gnu |
| Linux | ARM64 | aarch64-unknown-linux-gnu |
| Windows | x86_64 | x86_64-pc-windows-msvc |
| Windows | ARM64 | aarch64-pc-windows-msvc |
lipoAfter building, the following structure is created:
tabular/
├── target/ # Rust build artifacts
│ ├── x86_64-apple-darwin/
│ ├── aarch64-apple-darwin/
│ ├── universal-apple-darwin/
│ └── ...
├── dist/ # Distribution packages
│ ├── macos/
│ │ ├── Tabular.app
│ │ └── Tabular.dmg
│ ├── linux/
│ │ ├── tabular-x86_64.tar.gz
│ │ └── tabular-aarch64.tar.gz
│ └── windows/
│ ├── tabular-x86_64.zip
│ └── tabular-aarch64.zip
└── ...
The project includes GitHub Actions workflows for automated building:
# Solution: Install missing targets
make install-deps
# Solution: Install cross and Docker
cargo install cross
# Make sure Docker is running
# For development builds, you can skip code signing
# For distribution, you'll need an Apple Developer Certificate
# Make sure you have MSVC build tools installed
# Alternative: Use the GNU toolchain (x86_64-pc-windows-gnu)
If you encounter issues:
make clean && make releaseHere's a complete example of building for all platforms:
# Clone the repository
git clone https://github.com/your-repo/tabular.git
cd tabular
# Install dependencies and build everything
./build.sh all --deps
# Check the results
ls -la dist/
This will create distribution-ready packages for macOS, Linux, and Windows.
Join us at Telegram
tabular mcp): Built-in Model Context Protocol server so Claude Code, Cursor, Codex and other agent harnesses can list connections, describe schemas (relevance-ranked), run read-only queries, get EXPLAIN plans and lint SQL. Credentials never leave Tabular, every connection is read-only for agents until you raise its access level, risky writes wait for your approval, and every agent call is audited. See docs/MCP.md.Cmd+Shift+A): Schema-aware SQL completion with OpenAI, Anthropic Claude, Google Gemini, xAI, Groq, OpenRouter, GitHub Copilot, local models (Ollama, llama.cpp, MLX), or custom endpointsu.), Foreign Key auto-join completions, statement-level execution (Ctrl+Enter), quick query formatting (Ctrl+Shift+F), line comments (Ctrl+/), line duplication & moving (Alt+Up/Down), active line highlight, and multi-format result clipboard exports (Markdown, JSON, CSV, SQL INSERTs).[{"column": "value"}, ...]).INSERT INTO <table> (...) VALUES (...) statements.