Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions sdk/wlxplugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#define lc_newparams 2
#define lc_selectall 3
#define lc_setpercent 4
#define lc_focus 5

#define lcp_wraptext 1
#define lcp_fittowindow 2
Expand Down
1 change: 1 addition & 0 deletions wlx/dbview/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
build/
174 changes: 174 additions & 0 deletions wlx/dbview/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
cmake_minimum_required(VERSION 3.20)
project(dbview_qt6 CXX C)

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_AUTOMOC ON)

# Allow older cmake_minimum_required in FetchContent dependencies
set(CMAKE_POLICY_VERSION_MINIMUM 3.5 CACHE STRING "" FORCE)

# Optional engine flags
option(ENABLE_ROCKSDB_LEVELDB "Enable RocksDB and LevelDB support using RocksDB (large dependency)" OFF)
if(ENABLE_ROCKSDB_LEVELDB)
add_compile_options(-include stdint.h)
endif()

find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets Sql)

# Ensure the platform static library is built with -fPIC
set(CMAKE_POSITION_INDEPENDENT_CODE ON)

# Pull in the platform library (builds libwlxbase_wlqt.a)
add_subdirectory(
${CMAKE_CURRENT_SOURCE_DIR}/../wlxbase_wlqt
${CMAKE_CURRENT_BINARY_DIR}/wlxbase_wlqt
)

# ---------------------------------------------------------------------------
# External dependencies via FetchContent
# ---------------------------------------------------------------------------
include(FetchContent)

# --- DuckDB ---
set(BUILD_SHELL OFF CACHE BOOL "" FORCE)
set(BUILD_UNITTESTS OFF CACHE BOOL "" FORCE)
set(BUILD_BENCHMARKS OFF CACHE BOOL "" FORCE)
set(BUILD_EXTENSIONS "parquet" CACHE STRING "" FORCE)
set(EXTENSION_STATIC_BUILD ON CACHE BOOL "" FORCE)
set(ENABLE_EXTENSION_AUTOLOADING OFF CACHE BOOL "" FORCE)
set(ENABLE_EXTENSION_AUTOINSTALL OFF CACHE BOOL "" FORCE)
FetchContent_Declare(duckdb
GIT_REPOSITORY https://github.com/duckdb/duckdb.git
GIT_TAG v1.3.0
GIT_SHALLOW TRUE
)
FetchContent_MakeAvailable(duckdb)

# --- RocksDB (optional) ---
if(ENABLE_ROCKSDB_LEVELDB)
set(WITH_TESTS OFF CACHE BOOL "" FORCE)
set(WITH_BENCHMARK_TOOLS OFF CACHE BOOL "" FORCE)
set(WITH_TOOLS OFF CACHE BOOL "" FORCE)
set(WITH_GFLAGS OFF CACHE BOOL "" FORCE)
set(ROCKSDB_BUILD_SHARED OFF CACHE BOOL "" FORCE)
set(FAIL_ON_WARNINGS OFF CACHE BOOL "" FORCE)
FetchContent_Declare(rocksdb
GIT_REPOSITORY https://github.com/facebook/rocksdb.git
GIT_TAG v9.11.2
GIT_SHALLOW TRUE
PATCH_COMMAND sed -i "1i #include <cstdint>" db/blob/blob_file_meta.h
)
FetchContent_MakeAvailable(rocksdb)
endif()

# --- MS Access static libmdb ---
add_library(mdb STATIC
src/libmdb/catalog.c
src/libmdb/file.c
src/libmdb/table.c
src/libmdb/data.c
src/libmdb/dump.c
src/libmdb/backend.c
src/libmdb/money.c
src/libmdb/sargs.c
src/libmdb/index.c
src/libmdb/like.c
src/libmdb/write.c
src/libmdb/stats.c
src/libmdb/map.c
src/libmdb/props.c
src/libmdb/worktable.c
src/libmdb/options.c
src/libmdb/iconv.c
src/libmdb/version.c
src/libmdb/rc4.c
src/libmdb/fakeglib.c
)
target_include_directories(mdb PRIVATE
include/mdbtools
)
target_compile_definitions(mdb PRIVATE FAKE_GLIB=1 "TLS=__thread" HAVE_ICONV=1 "ICONV_CONST=")

# ---------------------------------------------------------------------------
# Plugin library
# ---------------------------------------------------------------------------
# NOTE: Headers must be listed explicitly so AUTOMOC processes them.
# RocksDbEngine.h is excluded when ENABLE_ROCKSDB is OFF to prevent MOC
# from generating code that references uncompiled RocksDbEngine symbols.
set(DBVIEW_HEADERS
include/DbEngine.h
include/DbViewWidget.h
include/DuckDbEngine.h
include/DuckDbModel.h
include/KeyValueModel.h
include/SqliteEngine.h
include/FirebirdEngine.h
include/MdbEngine.h
include/BdbEngine.h
include/LmdbEngine.h
)

set(DBVIEW_SOURCES
${DBVIEW_HEADERS}
src/wlx_entry.cpp
src/DbEngine.cpp
src/DbViewWidget.cpp
src/SqliteEngine.cpp
src/DuckDbEngine.cpp
src/DuckDbModel.cpp
src/KeyValueModel.cpp
src/FirebirdEngine.cpp
src/MdbEngine.cpp
src/BdbEngine.cpp
src/LmdbEngine.cpp
)

if(ENABLE_ROCKSDB_LEVELDB)
list(APPEND DBVIEW_SOURCES
include/LevelDbEngine.h src/LevelDbEngine.cpp
include/RocksDbEngine.h src/RocksDbEngine.cpp
)
endif()

add_library(dbview_qt6 SHARED ${DBVIEW_SOURCES})

if(ENABLE_ROCKSDB_LEVELDB)
target_compile_definitions(dbview_qt6 PRIVATE ENABLE_ROCKSDB_LEVELDB)
endif()

set_target_properties(dbview_qt6 PROPERTIES
PREFIX ""
SUFFIX ".wlx"
)

target_include_directories(dbview_qt6 PRIVATE
include
include/mdbtools
${CMAKE_CURRENT_SOURCE_DIR}/../../sdk
${duckdb_SOURCE_DIR}/src/include
)

if(ENABLE_ROCKSDB_LEVELDB)
target_include_directories(dbview_qt6 PRIVATE
${rocksdb_SOURCE_DIR}/include
)
endif()

set(DBVIEW_LINK_LIBS
wlxbase_wlqt
Qt6::Widgets
Qt6::Gui
Qt6::Core
Qt6::Sql
duckdb
mdb
lmdb
db
)

if(ENABLE_ROCKSDB_LEVELDB)
list(APPEND DBVIEW_LINK_LIBS rocksdb)
endif()

target_link_libraries(dbview_qt6 PRIVATE ${DBVIEW_LINK_LIBS})
120 changes: 120 additions & 0 deletions wlx/dbview/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
# dbview — Multi-Engine Database WLX Plugin

![dbview screenshot](dbview.png)

A Qt6 WLX (Lister) plugin for [Double Commander](https://doublecmd.github.io/) that views and edits database files: **SQLite**, **DuckDB**, **LevelDB**, **RocksDB**, **LMDB**, **Berkeley DB**, **Firebird Embedded**, **MS Access**, and **Apache Parquet**.

Built on the [`wlxbase_wlqt`](../wlxbase_wlqt/) platform library.

> [!WARNING]
> **DATA MUTATION & LOCKING WARNING**
>
> By default, this plugin attempts to open databases in **read-write** mode to allow direct grid editing and data mutation.
> - **File Locking:** Opening a database with read-write privileges may lock the file, preventing other applications from writing to it.
> - **Concurrent Access Fallback:** If the database file is locked by another process, the plugin will silently fall back to **read-only** mode.
> - **Data Integrity:** Any edited cells must be explicitly committed using the **Commit** button (or `Ctrl+S` / `Ctrl+Shift+Z`) for relational databases, or are saved instantly for key-value stores. Handle write mode with care to prevent unintended database modifications.

---

## Features

### All Engines
- **Schema Navigation Tree:** Hierarchical tree panel on the left displaying Tables, Views, Columns (with data types, Primary/Foreign keys), and Indexes.
- **Find** (`Ctrl+F`) — search across all visible cells in the selected table.
- **Copy Selection** (`Ctrl+C`) — copy selected cell values as tab-separated values.
- **Word Wrap & Grid Lines** — toolbar actions to toggle wrapping and gridlines.
- **`GridMode::LiveDatabase`** — direct table mapping with minimal memory overhead.

### SQL Engines (SQLite, DuckDB, Firebird Embedded, Apache Parquet)
- **SQL Console:** A vertical split panel containing a query editor (with execution via `Ctrl+Return` or `Execute` button), results grid, and CSV/TSV results exporter.
- **Apache Parquet Proxying:** Opening a `.parquet`/`.pq` file initializes an in-memory DuckDB database and reads it via a virtual `read_parquet` view, making it SQL-queryable.
- **In-place Grid Editing:** Cells are editable, with modifications buffered.
- **Commit / Revert** (`Ctrl+S` / `Ctrl+Z` or `Ctrl+Shift+Z`) — commit or rollback pending changes.

### Key-Value & Non-Relational Engines (LevelDB, RocksDB, LMDB, Berkeley DB, MS Access)
- **Hidden SQL Console:** The SQL Console panel is automatically hidden as these engines do not support custom SQL.
- **Two-Column Grid:** Displays Key and Value columns.
- **Immediate Writing:** Key-value writes are saved instantly (no buffering).
- **Binary/BLOB Value Detection:** Non-UTF-8 values and large binaries display placeholder information `[Binary Data - X bytes]`.
- **Right-Click Context Menu Options:**
- **Hex View Toggle:** Displays binary data as space-separated hex strings.
- **BLOB Export ("Save Cell to File"):** Save raw binary values to any local file.
- **BLOB Import ("Load File into Cell"):** Import binary files into a cell (available in write mode only).
- **Directory Detection (LevelDB/RocksDB):** Selecting a `.sst`/`.ldb`/`.log` file inside a LevelDB/RocksDB directory automatically targets the parent database directory.

---

## Keyboard Shortcuts

| Shortcut | Action |
|----------|--------|
| `Ctrl+S` | Commit changes (Relational engines only) |
| `Ctrl+Z` | Revert pending changes (Relational engines only) |
| `Ctrl+Shift+Z` | Alternative Commit/Redo shortcut |
| `Ctrl+Return` | Execute custom SQL query inside the SQL Console |
| `Ctrl+F` | Toggle Find panel |
| `Ctrl+C` | Copy selection |

---

## Engine Selection Logic

The factory (`DbEngine::createForFile()`) resolves the file type by extension and locks:

| Extension / File Pattern | Engine | SQL Console | Read-Only | Notes |
|-------------------------|--------|-------------|-----------|-------|
| `.sqlite`, `.sqlite3`, `.db`, `.db3` | SQLite | Yes | No* | QSQLITE driver connection |
| `.duckdb` | DuckDB | Yes | No* | C++ native client API |
| `.parquet`, `.pq` | DuckDB | Yes | No* | Virtual view via `read_parquet()` |
| `.fdb` | Firebird Embedded | Yes | No* | QIBASE SQL driver connection |
| `.mdb`, `.accdb` (if user table) | MS Access | No | **Yes** | Statically linked `libmdb` |
| `.lmdb`, `data.mdb` | LMDB | No | No* | C API client |
| `.bdb` | Berkeley DB | No | No* | C API with B-Tree cursors |
| `.ldb`, `.sst`, `.log` (if parent has `CURRENT`) | LevelDB | No | **Yes** | C++ API client via RocksDB (if compiled) |
| `.sst`, `.log` (fallback if LevelDB fails) | RocksDB | No | **Yes** | C++ API (if compiled) |

*\* Note: Opens as Read-Only automatically if the file lacks write permissions or if the database is currently locked by another process.*

---

## Building

### Prerequisites
- Qt6 (Core, Gui, Widgets, Sql)
- CMake ≥ 3.20
- Git (for FetchContent dependencies)
- Libraries: `liblmdb`, `libdb` (Berkeley DB), `libfbclient` (Firebird client)

### Compile
```bash
cd wlx/dbview
mkdir build && cd build
cmake ..
make -j$(nproc)
```

To enable support for **LevelDB** and **RocksDB**, you must compile with the `ENABLE_ROCKSDB_LEVELDB` flag set to `ON`. This flag is disabled by default because linking the RocksDB libraries increases the final `dbview_qt6.wlx` plugin size by over 12 MB. Both formats will be opened in strict read-only mode.

```bash
cmake .. -DENABLE_ROCKSDB_LEVELDB=ON
make -j$(nproc)
```

**Output:** `dbview_qt6.wlx` (shared library)

---

## Installation

Add `dbview_qt6.wlx` to your Double Commander plugins list.

**Default Detection String:**
```
EXT="DB" | EXT="SQLITE" | EXT="SQLITE3" | EXT="DB3" | EXT="DUCKDB" | EXT="LMDB" | EXT="BDB" | EXT="FDB" | EXT="MDB" | EXT="ACCDB" | EXT="PARQUET" | EXT="PQ"
```

**If compiled with `ENABLE_ROCKSDB_LEVELDB=ON`:**
Include the extensions for LevelDB and RocksDB:
```
EXT="DB" | EXT="SQLITE" | EXT="SQLITE3" | EXT="DB3" | EXT="DUCKDB" | EXT="LDB" | EXT="SST" | EXT="LOG" | EXT="LMDB" | EXT="BDB" | EXT="FDB" | EXT="MDB" | EXT="ACCDB" | EXT="PARQUET" | EXT="PQ"
```
Binary file added wlx/dbview/dbview.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
29 changes: 29 additions & 0 deletions wlx/dbview/include/BdbEngine.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#pragma once

#include "DbEngine.h"
#include <db.h>

class BdbEngine : public DbEngine {
Q_OBJECT
public:
explicit BdbEngine(QObject *parent = nullptr);
~BdbEngine() override;

bool open(const QString &filepath) override;
void close() override;

QStringList tableNames() const override;
QAbstractItemModel *modelForTable(const QString &tableName) override;
QString currentTableName() const override;

bool supportsMultipleTables() const override { return false; }
bool supportsSubmitRevert() const override { return false; }
QString engineName() const override { return QStringLiteral("Berkeley DB"); }

private:
int countKeys() const;

DB *m_db = nullptr;
int m_keyCount = 0;
QAbstractItemModel *m_model = nullptr;
};
Loading