[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-74640":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":9,"language":10,"languages":9,"totalLinesOfCode":9,"stars":11,"forks":12,"watchers":13,"openIssues":14,"contributorsCount":14,"subscribersCount":14,"size":14,"stars1d":15,"stars7d":16,"stars30d":17,"stars90d":14,"forks30d":14,"starsTrendScore":18,"compositeScore":19,"rankGlobal":9,"rankLanguage":9,"license":20,"archived":21,"fork":21,"defaultBranch":22,"hasWiki":23,"hasPages":21,"topics":24,"createdAt":9,"pushedAt":9,"updatedAt":25,"readmeContent":26,"aiSummary":27,"trendingCount":14,"starSnapshotCount":14,"syncStatus":28,"lastSyncTime":29,"discoverSource":30},74640,"sql-tap","mickamy\u002Fsql-tap","mickamy","Watch SQL traffic in real-time with a TUI",null,"Go",1533,48,6,0,37,38,50,111,97.07,"MIT License",false,"main",true,[],"2026-06-12 04:01:15","# sql-tap\n\n[![Sponsor](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FSponsor-❤-ea4aaa?style=flat-square&logo=github)](https:\u002F\u002Fgithub.com\u002Fsponsors\u002Fmickamy)\n\nReal-time SQL traffic viewer — proxy daemon + TUI \u002F Web client.\n\nsql-tap sits between your application and your database (PostgreSQL, MySQL, or TiDB), capturing every query and\ndisplaying it in an interactive terminal UI. Inspect queries, view transactions, and run EXPLAIN — all without changing\nyour application code.\n\n![tui](.\u002Fdocs\u002Ftui.gif)\n![web](.\u002Fdocs\u002Fweb.png)\n\n## Installation\n\n### Homebrew\n\n```bash\nbrew install mickamy\u002Ftap\u002Fsql-tap\n```\n\n### Go\n\n```bash\ngo install github.com\u002Fmickamy\u002Fsql-tap@latest\ngo install github.com\u002Fmickamy\u002Fsql-tap\u002Fcmd\u002Fsql-tapd@latest\n```\n\n### Build from source\n\n```bash\ngit clone https:\u002F\u002Fgithub.com\u002Fmickamy\u002Fsql-tap.git\ncd sql-tap\nmake install\n```\n\n### Nix\n\n**Using the flake directly**\n\n```bash\nnix profile install github:mickamy\u002Fsql-tap\n```\n\n**In a `devenv.sh` project**\n\nAdd `sql-tap` as a flake input in `devenv.yaml`:\n\n```yaml\ninputs:\n  sql-tap:\n    url: github:mickamy\u002Fsql-tap\n```\n\nThen reference it in `devenv.nix`:\n\n```nix\n{ inputs, pkgs, ... }:\n{\n  packages = [\n    inputs.sql-tap.packages.${pkgs.system}.default\n  ];\n}\n```\n\n### Docker\n\n```dockerfile\nFROM alpine:3\nARG SQL_TAP_VERSION=0.0.1\nARG TARGETARCH\nADD https:\u002F\u002Fgithub.com\u002Fmickamy\u002Fsql-tap\u002Freleases\u002Fdownload\u002Fv${SQL_TAP_VERSION}\u002Fsql-tap_${SQL_TAP_VERSION}_linux_${TARGETARCH}.tar.gz \u002Ftmp\u002Fsql-tap.tar.gz\nRUN tar -xzf \u002Ftmp\u002Fsql-tap.tar.gz -C \u002Fusr\u002Flocal\u002Fbin sql-tapd && rm \u002Ftmp\u002Fsql-tap.tar.gz\nENTRYPOINT [\"sql-tapd\"]\n```\n\nRun as a sidecar alongside your database:\n\n```bash\n# PostgreSQL\ndocker run --rm --network=host sql-tap \\\n  --driver=postgres --listen=:5433 --upstream=localhost:5432 --grpc=:9091\n\n# MySQL\ndocker run --rm --network=host sql-tap \\\n  --driver=mysql --listen=:3307 --upstream=localhost:3306 --grpc=:9091\n```\n\n## Quick start\n\n**1. Start the proxy daemon**\n\n```bash\n# PostgreSQL: proxy listens on :5433, forwards to PostgreSQL on :5432\nDATABASE_URL=\"postgres:\u002F\u002Fuser:pass@localhost:5432\u002Fdb?sslmode=disable\" \\\n  sql-tapd --driver=postgres --listen=:5433 --upstream=localhost:5432\n\n# MySQL: proxy listens on :3307, forwards to MySQL on :3306\nDATABASE_URL=\"user:pass@tcp(localhost:3306)\u002Fdb\" \\\n  sql-tapd --driver=mysql --listen=:3307 --upstream=localhost:3306\n\n# TiDB: proxy listens on :4001, forwards to TiDB on :4000\nDATABASE_URL=\"user:pass@tcp(localhost:4000)\u002Fdb\" \\\n  sql-tapd --driver=tidb --listen=:4001 --upstream=localhost:4000\n```\n\n**2. Point your application at the proxy**\n\nConnect your app to the proxy port instead of the database port. No code changes needed — sql-tapd speaks the native\nwire protocol.\n\n**3. Launch the TUI**\n\n```bash\nsql-tap localhost:9091\n```\n\nAll queries flowing through the proxy appear in real-time.\n\n## Usage\n\n### sql-tapd\n\n```\nsql-tapd — SQL proxy daemon for sql-tap\n\nUsage:\n  sql-tapd [flags]\n\nFlags:\n  -config    path to config file (default: .sql-tap.yaml if exists)\n  -driver    database driver: postgres, mysql, tidb (required)\n  -listen    client listen address (required)\n  -upstream  upstream database address (required)\n  -grpc      gRPC server address for TUI (default: \":9091\")\n  -http      HTTP server address for web UI (e.g. \":8080\")\n  -dsn-env   env var holding DSN for EXPLAIN (default: \"DATABASE_URL\")\n  -nplus1-threshold  N+1 detection threshold (default: 5, 0 to disable)\n  -nplus1-window     N+1 detection time window (default: 1s)\n  -nplus1-cooldown   N+1 alert cooldown per query template (default: 10s)\n  -slow-threshold    slow query threshold (default: 100ms, 0 to disable)\n  -version   show version and exit\n```\n\nSet `DATABASE_URL` (or the env var specified by `-dsn-env`) to enable EXPLAIN support. Without it, the proxy still\ncaptures queries but EXPLAIN is disabled.\n\n### Config file\n\nInstead of passing flags on every invocation, you can create a `.sql-tap.yaml` in your project directory:\n\n```yaml\ndriver: postgres\nlisten: \":5433\"\nupstream: \"localhost:5432\"\ngrpc: \":9091\"\nhttp: \":8080\"\ndsn_env: DATABASE_URL\nslow_threshold: 100ms\nnplus1:\n  threshold: 5\n  window: 1s\n  cooldown: 10s\n```\n\nsql-tapd automatically loads `.sql-tap.yaml` from the current directory. Use `-config` to specify a different path.\nCLI flags override config file values.\n\n### Web UI\n\nAdd `--http=:8080` to serve a browser-based viewer:\n\n```bash\nDATABASE_URL=\"postgres:\u002F\u002Fuser:pass@localhost:5432\u002Fdb?sslmode=disable\" \\\n  sql-tapd --driver=postgres --listen=:5433 --upstream=localhost:5432 --http=:8080\n```\n\nOpen `http:\u002F\u002Flocalhost:8080` in your browser to view queries in real-time. The web UI supports:\n\n- Real-time query stream via SSE\n- Click to inspect query details\n- EXPLAIN \u002F EXPLAIN ANALYZE\n- Text filter\n- Copy query (with or without bound args)\n- N+1 detection (toast + row highlight)\n- Timeline view (Gantt-chart style query visualization)\n\n### sql-tap\n\n```\nsql-tap — Watch SQL traffic in real-time\n\nUsage:\n  sql-tap [flags] \u003Caddr>\n\nFlags:\n  -ci       run in CI mode: collect events until SIGTERM\u002FSIGINT or stream ends, then report and exit\n  -version  Show version and exit\n```\n\n`\u003Caddr>` is the gRPC address of sql-tapd (e.g. `localhost:9091`).\n\n### CI mode\n\nRun `sql-tap -ci` to detect N+1 and slow queries in your test suite. It connects to a running sql-tapd (see [Quick start](#quick-start) for setup), collects events, and exits with code 1 if any problems are found.\n\n```bash\n# Start sql-tap in CI mode (background)\nsql-tap -ci localhost:9091 &\nCI_PID=$!\n\n# Run your tests through the proxy\nDATABASE_URL=\"postgres:\u002F\u002Fuser:pass@localhost:5433\u002Fdb?sslmode=disable\" go test .\u002F...\n\n# Stop sql-tap — prints report and exits 0 (clean) or 1 (problems found)\nkill \"$CI_PID\" 2>\u002Fdev\u002Fnull || true\nwait \"$CI_PID\" 2>\u002Fdev\u002Fnull || true\n```\n\nExample output:\n\n```\nsql-tap CI Report\n=================\nCaptured: 142 queries\n\nProblems found:\n  [N+1]  SELECT * FROM comments WHERE post_id = $1  (detected 12 times)\n  [SLOW] SELECT * FROM users JOIN ...  (avg 523ms, 3 occurrences)\n\nExit: 1 (2 problems found)\n```\n\n## Keybindings\n\n### List view\n\n| Key               | Action                                 |\n|-------------------|----------------------------------------|\n| `j` \u002F `↓`         | Move down                              |\n| `k` \u002F `↑`         | Move up                                |\n| `Ctrl+d` \u002F `PgDn` | Half-page down                         |\n| `Ctrl+u` \u002F `PgUp` | Half-page up                           |\n| `\u002F`               | Incremental text search                |\n| `f`               | Structured filter (see below)          |\n| `s`               | Toggle sort (chronological\u002Fduration)   |\n| `Enter`           | Inspect query \u002F transaction            |\n| `Space`           | Toggle transaction expand \u002F collapse   |\n| `Esc`             | Clear search \u002F filter                  |\n| `x`               | EXPLAIN                                |\n| `X`               | EXPLAIN ANALYZE                        |\n| `e`               | Edit query, then EXPLAIN               |\n| `E`               | Edit query, then EXPLAIN ANALYZE       |\n| `a`               | Analytics view                         |\n| `t`               | Timeline view                          |\n| `c`               | Copy query                             |\n| `C`               | Copy query with bound args             |\n| `w`               | Export queries to file (JSON\u002FMarkdown) |\n| `q`               | Quit                                   |\n\n### Inspector view\n\n| Key       | Action                     |\n|-----------|----------------------------|\n| `j` \u002F `↓` | Scroll down                |\n| `k` \u002F `↑` | Scroll up                  |\n| `x`       | EXPLAIN                    |\n| `X`       | EXPLAIN ANALYZE            |\n| `e` \u002F `E` | Edit and EXPLAIN \u002F ANALYZE |\n| `c`       | Copy query                 |\n| `C`       | Copy query with bound args |\n| `q`       | Back to list               |\n\n### Analytics view\n\n| Key       | Action                       |\n|-----------|------------------------------|\n| `j` \u002F `↓` | Move down                    |\n| `k` \u002F `↑` | Move up                      |\n| `Ctrl+d`  | Half-page down               |\n| `Ctrl+u`  | Half-page up                 |\n| `h` \u002F `←` | Scroll left                  |\n| `l` \u002F `→` | Scroll right                 |\n| `s`       | Cycle sort (total\u002Fcount\u002Favg) |\n| `c`       | Copy query                   |\n| `q`       | Back to list                 |\n\n### Timeline view\n\n| Key               | Action         |\n|-------------------|----------------|\n| `j` \u002F `↓`         | Scroll down    |\n| `k` \u002F `↑`         | Scroll up      |\n| `Ctrl+d` \u002F `PgDn` | Half-page down |\n| `Ctrl+u` \u002F `PgUp` | Half-page up   |\n| `q`               | Back to list   |\n\n### Explain view\n\n| Key       | Action                           |\n|-----------|----------------------------------|\n| `j` \u002F `↓` | Scroll down                      |\n| `k` \u002F `↑` | Scroll up                        |\n| `h` \u002F `←` | Scroll left                      |\n| `l` \u002F `→` | Scroll right                     |\n| `c`       | Copy explain plan                |\n| `e` \u002F `E` | Edit and re-explain \u002F re-analyze |\n| `q`       | Back to list                     |\n\n## Filter syntax\n\nPress `f` in the list view to enter filter mode. Filters support structured conditions that go beyond simple text\nsearch.\n\n| Syntax      | Meaning                 | Example                               |\n|-------------|-------------------------|---------------------------------------|\n| `d>100ms`   | Duration greater than   | `d>1s`, `d>500us`                     |\n| `d\u003C10ms`    | Duration less than      | `d\u003C50ms`                              |\n| `error`     | Events with errors only |                                       |\n| `n+1`       | N+1 flagged queries     | alias: `nplus1`                       |\n| `slow`      | Slow queries only       |                                       |\n| `op:select` | SQL keyword prefix      | `op:insert`, `op:update`, `op:delete` |\n| `op:begin`  | Protocol operation      | `op:commit`, `op:rollback`            |\n| _(other)_   | Text substring match    | `users`, `WHERE id`                   |\n\nMultiple tokens are separated by spaces and combined with AND logic:\n\n```\nop:select d>100ms\n```\n\nThis shows only SELECT queries that took longer than 100ms.\n\nBoth `\u002F` (text search) and `f` (filter) can be active simultaneously — the filter is applied first, then the text search\nnarrows the results further.\n\n## N+1 query detection\n\nsql-tap automatically detects N+1 query patterns — when the same SELECT template is executed many times in a short time\nwindow.\n\nDetection is enabled by default and runs server-side, so both TUI and Web UI benefit:\n\n- **TUI**: alert overlay on first detection + `N+1` marker in the Status column for every flagged query\n- **Web UI**: toast notification on first detection + yellow row highlight + `N+1` in the Status column\n\n### Configuration\n\n| Flag                 | Default | Description                                                     |\n|----------------------|---------|-----------------------------------------------------------------|\n| `--nplus1-threshold` | `5`     | Number of executions to trigger detection (0 to disable)        |\n| `--nplus1-window`    | `1s`    | Sliding time window for counting                                |\n| `--nplus1-cooldown`  | `10s`   | Minimum interval between alert notifications for the same query |\n\nOnly SELECT queries are monitored. INSERT, UPDATE, DELETE, and transaction lifecycle commands (BEGIN, COMMIT, etc.) are\nexcluded. Metadata queries — SELECT statements without a FROM clause, such as `SELECT database()`, `SELECT @@version`,\nor `SELECT 1` — are also excluded, as they are typically driver health checks or system introspection calls rather than\napplication data queries.\n\nOnce the threshold is crossed, all subsequent executions of the same template within the window are flagged. The\ncooldown only affects the notification frequency — the Status column marker always appears.\n\nTo disable detection entirely:\n\n```bash\nsql-tapd --nplus1-threshold=0 ...\n```\n\n## Known limitations\n\n### Arrow key input in search \u002F filter mode\n\nDue to a limitation in the terminal input parser used by [Bubble Tea](https:\u002F\u002Fgithub.com\u002Fcharmbracelet\u002Fbubbletea) v1,\nmulti-byte escape sequences (such as arrow keys: ESC `[` A\u002FB\u002FC\u002FD) can occasionally be split across OS-level `read()`\ncalls. When this happens, the remaining bytes (`[A`, `[B`, `[C`, `[D`, `[F`, `[H`) would appear as garbage text in the\ninput field.\n\nsql-tap includes a workaround that detects and discards these split sequences. As a side effect, the literal\ntwo-character strings `[A`, `[B`, `[C`, `[D`, `[F`, and `[H` cannot be typed in search or filter input. This is unlikely\nto affect real-world usage since these patterns rarely appear in SQL queries.\n\n## How it works\n\n```\n┌─────────────┐      ┌───────────────────────┐      ┌───────────────────────────┐\n│ Application │─────▶│  sql-tapd (proxy)     │─────▶│ PostgreSQL \u002F MySQL \u002F TiDB │\n└─────────────┘      │                       │      └───────────────────────────┘\n                     │  captures queries     │\n                     │  via wire protocol    │\n                     └───────────┬───────────┘\n                                 │ gRPC stream \u002F SSE\n                     ┌───────────▼───────────┐\n                     │  sql-tap (TUI)        │\n                     │  Browser  (Web UI)    │\n                     └───────────────────────┘\n```\n\nsql-tapd parses the database wire protocol (PostgreSQL, MySQL, or TiDB) to intercept queries transparently. It tracks prepared statements, parameter bindings, transactions, execution time, rows affected, and errors. Events are streamed to connected TUI clients via gRPC.\n\n## See also\n\n- **[grpc-tap](https:\u002F\u002Fgithub.com\u002Fmickamy\u002Fgrpc-tap)** — Same concept for gRPC. Transparent HTTP\u002F2 reverse proxy that\n  captures every gRPC \u002F gRPC-Web \u002F Connect call with TUI + Web UI.\n\n## License\n\n[MIT](.\u002FLICENSE)\n","sql-tap 是一个实时SQL流量查看工具，通过代理守护进程和终端用户界面（TUI）或Web客户端展示数据库查询。它支持PostgreSQL、MySQL和TiDB等数据库，能够捕获所有经过代理的SQL查询，并在交互式界面上显示出来，便于用户检查查询、查看事务以及运行EXPLAIN分析，而无需修改应用程序代码。该工具使用Go语言开发，具备轻量级、易部署的特点，适合于需要监控数据库活动、进行性能调优或故障排查的应用场景。",2,"2026-06-11 03:50:15","high_star"]