CQLAI is a fast, portable interactive terminal for Cassandra (CQL), built in Go. It provides a modern, user-friendly alternative to cqlsh with an advanced terminal UI, client-side command parsing, and enhanced productivity features.
AI features are completely optional - CQLAI works perfectly as a standalone CQL shell without any AI configuration or API keys.
cqlai_2x.mp4
No hidden costs • No premium tiers • No license keys
Community-driven development with full transparency
The original cqlsh command in the Apache Cassandra project is written in Python which requires Python to be installed on the system. cqlai is compiled to a single executable binary, requiring no external dependencies. This project provides binaries for the following platforms:
- Linux x86-64
- macOS x86-64
- Windows x86-64
- Linux aarch64
- macOS arm64
It is built with Bubble Tea, Bubbles, and Lip Gloss for the beautiful terminal UI. A big shout out to the cassandra gocql driver team for implementing the latest Cassandra functionalities gocql
- Project Status
- Features
- Installation
- Usage
- Available Commands
- Configuration
- AI-Powered Query Generation
- Apache Parquet Support
- Known Limitations
- Development
- Technology Stack
- Acknowledgements
- Community & Support
- License
- Legal Notices
CQLAI is production-ready and actively used in development, testing, and production environments with Cassandra clusters. The tool provides a complete, stable alternative to cqlsh with enhanced features and performance.
- All core CQL operations and queries
- Complete meta-command support (
DESCRIBE,SHOW,CONSISTENCY, etc.) - Client-side command parsing (lightweight, no ANTLR dependency)
- Data import/export with
COPY TO/FROM(CSV and Parquet formats) - SSL/TLS connections and authentication
- User-Defined Types (UDTs) and complex data types
- Batch mode for scripting and automation
- Apache Parquet format support for efficient data interchange
- Tab completion for CQL keywords, tables, columns, and keyspaces
- Optional: AI-powered query generation (OpenAI, Anthropic, Google Gemini, Synthetic)
- Enhanced AI context awareness
- Cassandra MCP service
- Additional performance optimizations
We encourage you to try CQLAI today and help shape its development! Your feedback and contributions are invaluable in making this the best CQL shell for the Cassandra community. Please report issues or contribute.
- Interactive CQL Shell: Execute any CQL query that your Cassandra cluster supports.
- Rich Terminal UI:
- A multi-layer, full-screen terminal application with alternate screen buffer (preserves terminal history).
- Virtualized, scrollable table for results with automatic data loading, preventing memory overload from large queries.
- Advanced navigation modes with vim-style keyboard shortcuts.
- Full mouse support: clickable tabs and settings, click-and-drag text selection with no modifier key, and wheel scrolling.
- Sticky footer/status bar showing connection details, query latency, and session status (consistency, tracing).
- Modal overlays for history, help, and command completion.
- Apache Parquet Support:
- High-performance columnar data format for analytics and machine learning workflows.
- Export Cassandra tables to Parquet files with
COPY TOcommand. - Import Parquet files into Cassandra with automatic schema inference.
- Partitioned datasets with Hive-style directory structures.
- TimeUUID / timestamp virtual columns for intelligent time-based partitioning.
- Support for all Cassandra data types including UDTs, collections, and vectors.
- Optional AI-Powered Query Generation:
- Natural language to CQL conversion using AI providers (OpenAI, Anthropic, Google Gemini, Synthetic).
- Schema-aware query generation with automatic context.
- Safe preview and confirmation before execution.
- Support for complex operations including DDL and DML.
- Requires API key configuration - not needed for core functionality.
- Configuration:
- Simple configuration via
cqlai.jsonin the current directory or~/.cassandra/cqlai.json, besidecqlshrc. - Support for SSL/TLS connections with certificate authentication.
- Simple configuration via
- Single Binary: Distributed as a single, static binary with no external dependencies. Fast startup and small footprint.
You can install cqlai in several ways. For detailed instructions including package managers (APT, YUM) and Docker, see the Installation Guide.
Download the appropriate binary for your OS and architecture from the Releases page.
go install github.com/axonops/cqlai/cmd/cqlai@latestgit clone https://github.com/axonops/cqlai.git
cd cqlai
go build -o cqlai cmd/cqlai/main.go# Build the image
docker build -t cqlai .
# Run the container
docker run -it --rm --name cqlai-session cqlai --host your-cassandra-hostConnect to a Cassandra host:
# With password on command line (not recommended - visible in ps)
cqlai --host 127.0.0.1 --port 9042 --username cassandra --password cassandra
# With password prompt (secure - password hidden)
cqlai --host 127.0.0.1 --port 9042 -u cassandra
# Password: [hidden input]
# Using environment variable (secure for scripts/containers)
export CQLAI_PASSWORD=cassandra
cqlai --host 127.0.0.1 -u cassandraOr use a configuration file:
# Create configuration from example
cp cqlai.json.example cqlai.json
# Edit cqlai.json with your settings, then run:
cqlaicqlai [options] [host [port]]Note: Positional arguments are supported for cqlsh compatibility. cqlai 192.168.1.100 9042 is equivalent to cqlai --host 192.168.1.100 --port 9042.
| Option | Short | Description |
|---|---|---|
--host <host> |
Cassandra host (overrides config) | |
--port <port> |
Cassandra port (overrides config) | |
--keyspace <keyspace> |
-k |
Default keyspace (overrides config) |
--username <username> |
-u |
Username for authentication |
--password <password> |
-p |
Password for authentication* |
--ssl |
Enable SSL/TLS connection | |
--consistency <level> |
Default consistency level (e.g., ONE, QUORUM, LOCAL_QUORUM) | |
--no-confirm |
Disable confirmation prompts for destructive commands (DROP, DELETE, TRUNCATE) | |
--connect-timeout <seconds> |
Connection timeout (default: 10) | |
--request-timeout <seconds> |
Request timeout (default: 10) | |
--debug |
Enable debug logging |
*Note: Password can be provided in three ways:
- Command line with
-p(not recommended - visible in process list) - Interactive prompt when
-uis used without-p(recommended) - Environment variable
CQLAI_PASSWORD(good for automation)
| Option | Short | Description |
|---|---|---|
--execute <statement> |
-e |
Execute CQL statement and exit |
--file <file> |
-f |
Execute CQL from file and exit |
--format <format> |
Output format: ascii, json, csv, table | |
--no-header |
Don't output column headers (CSV) | |
--field-separator <sep> |
Field separator for CSV (default: ,) | |
--page-size <n> |
Rows per batch (default: 100) |
| Option | Short | Description |
|---|---|---|
--config-file <path> |
Path to config file (overrides default locations) | |
--help |
-h |
Show help message |
--version |
-v |
Print version and exit |
Execute CQL statements non-interactively (compatible with cqlsh):
# Execute a single statement
cqlai -e "SELECT * FROM system_schema.keyspaces;"
# Execute from a file
cqlai -f script.cql
# Pipe input
echo "SELECT * FROM users;" | cqlai
# Control output format
cqlai -e "SELECT * FROM users;" --format json
cqlai -e "SELECT * FROM users;" --format csv --no-header
# Control pagination size
cqlai -e "SELECT * FROM large_table;" --page-size 50- Execute CQL: Type any CQL statement and press Enter.
- Meta-Commands:
DESCRIBE KEYSPACES; USE my_keyspace; DESCRIBE TABLES; CONSISTENCY QUORUM; TRACING ON; PAGING 50; EXPAND ON; -- Vertical output mode SOURCE 'script.cql'; -- Execute CQL script
- AI-Powered Query Generation:
.ai What keyspaces are there? .ai What columns does the users table have? .ai create a table for storing product inventory .ai delete orders older than 1 year from the orders table
| Shortcut | Action | macOS Alternative |
|---|---|---|
↑/↓ |
Navigate command history | Same |
Ctrl+P/Ctrl+N |
Previous/Next in command history | Same |
Alt+N |
Move to next line in history | Option+N |
Tab |
Autocomplete commands and table/keyspace names | Same |
Ctrl+C |
Clear input / Cancel pagination / Cancel operation (twice to exit) | ⌘+C or Ctrl+C |
Ctrl+D |
Exit application | ⌘+D or Ctrl+D |
Ctrl+R |
Search command history | ⌘+R or Ctrl+R |
Esc |
Toggle navigation mode / Cancel pagination / Close modals | Same |
Enter |
Execute command / Load next page (during pagination) | Same |
| Shortcut | Action | macOS Alternative |
|---|---|---|
Ctrl+A |
Jump to beginning of line | Same |
Ctrl+E |
Jump to end of line | Same |
Ctrl+Left/Ctrl+Right |
Jump by word (or 20 chars) | Same |
PgUp/PgDn (in input) |
Page left/right in long queries | Fn+↑/Fn+↓ |
Ctrl+K |
Cut from cursor to end of line | Same |
Ctrl+U |
Cut from beginning to cursor | Same |
Ctrl+W |
Cut word backward | Same |
Alt+D |
Delete word forward | Option+D |
Ctrl+Y |
Paste previously cut text | Same |
FILE (Alt+F) CONSOLE (F2) SCHEMA (F3) RESULTS (F4) TRACE (F5) CHAT (F6) HELP (F1/Alt+H)
The five views are shown as tabs, so you can see which one you are in and what
the others are. Click a tab, or use the key on it. A view with nothing to show
yet is dimmed, as is CHAT with no AI provider configured.
| Shortcut | Tab | Shows |
|---|---|---|
F2 |
Console | The running transcript of commands and messages |
F3 |
Schema | The cluster's keyspaces and tables, with their definitions |
F4 |
Results | The last query's output, in whatever OUTPUT format is set |
F5 |
Trace | Query trace, when tracing is enabled |
F6 |
Chat | The AI conversation |
Text is selected by dragging with the mouse, and lands on the system clipboard when you let go.
A right click pastes. It asks three things, in order, and takes the first
answer: the terminal, through OSC 52, which is the only route that works through
ssh and tmux and which most terminals refuse - a program that can read the
clipboard can read what you copied out of a password manager; then the machine
CQLAI is running on, through whichever of wl-paste, xclip, xsel, pbpaste
or powershell.exe Get-Clipboard is there, which covers a local desktop
including WSL; and failing both, whatever CQLAI itself last copied.
The terminal's own paste works too, and both go wherever the keys are going: the prompt, a form, or the preferences window.
Schema sits next to the console because what is in the database comes before
anything a query has made of it. The keys read along the line with the tabs, so
Results and Trace have moved from F3 and F4 to F4 and F5.
SCHEMA is the cluster's keyspaces and tables as a tree, with the definition of
whatever is selected beside it. Click a keyspace to see it and open it, click it
again to fold it, and click a table for its CREATE TABLE.
The arrows walk the tree - right opens a keyspace, left closes it - and the
wheel moves whichever pane it is over. Alt+Up and Alt+Down scroll the
definition. Everything else still goes to the prompt, so a query can be typed
while looking at the table it is about. Pressing F3 again, or clicking the tab
you are already on, asks the cluster for the schema afresh.
Nothing is fetched until the tab is opened: the keyspaces on first use, a
keyspace's tables when it is first opened, and a definition when it is first
shown. The definitions are the ones DESCRIBE produces, so the two cannot
disagree.
A schema change is noticed wherever it was made - here, in cqlsh, in another
CQLAI, in an application. Cassandra keeps a schema version that changes whenever
the schema does, whoever changed it; it is the value nodetool describe cluster
prints under "Schema versions". CQLAI reads it every five seconds, which is one
row, and when it differs from the last reading the browser and the tab
completion both fetch again - at once if you are looking at the tree, and when
you next open the tab if you are not. F3 still asks immediately.
SCHEMA shows what a table is. What it does not say is whether the definition
is any good: whether the partition will grow without bound, whether the
clustering serves the query the table is obviously for, whether the compaction
strategy matches how the data is written. Cassandra punishes those months
later, by which time the table has data in it and the answer is to write it all
again somewhere else.
[ Review Schema ] at the top right of the definition pane - or Alt+A -
sends the definition to the configured AI provider. The answer appears under
it, in the pane it is about:
KEYSPACES │ TABLE shop.events_by_user [ Review Schema ]
───────────────── │ ────────────────────────────────────────────────────────
▾ shop │ CREATE TABLE shop.events_by_user (
events_by_user │ user_id uuid,
users │ created_at timestamp,
▸ system │ PRIMARY KEY (user_id, created_at)
│ ─ Review drag to resize ───────────────────────────────
│ WHAT IT IS
│ Partitioned by user_id, ordered by created_at. A feed.
│
│ RISKS
│ - The partition grows without bound: every event a user
│ ever has lands in one.
│
│ WHAT TO CHANGE
│ - Add a time bucket: ((user_id, month), created_at).
The definition is what gets sent - no rows. The line between the two is dragged
the way the trace's is, Esc puts the review away, and moving to another table
puts it away too: an answer about one definition does not belong under
another's.
TRACE shows the trace of the last query, when tracing is on. It is forty rows
of microsecond timings and node names, and what you usually want from it is
which step was slow and what to do about it.
[ Analyse Trace ] at the top right of the view - or Alt+A - sends the
trace to the configured AI provider and asks. The answer appears under the
trace, in the same view:
Alt+A reads this trace with the AI [ Analyse Trace ]
activity source source_elapsed
Parsing SELECT * FROM users 10.0.0.1 120
Read 3 sstables 10.0.0.2 9100
─ Analysis drag to resize ────────────────────────────────────────────────
TIME
3.9ms seq scan across 3 sstables, ReadStage-2
1.8ms submitted 1 concurrent range request
FINDINGS
- A range scan across the whole ring, not a partition lookup.
- 100 live rows, 0 tombstones: the read itself is clean.
WHAT TO DO
- Query by the partition key instead of scanning.
The answer comes back in those three sections, with a line each: where the time went, what the trace shows that the timings do not say on their own, and what to change. Asked for an explanation, the models write an essay restating the trace you already have in front of you, so the shape of the answer is dictated rather than suggested.
Drag the line between the two panes with the mouse to give either of them more
room; let go and it stays where you left it. Esc puts the analysis away.
The trace is sent as it was read from system_traces, not as it is drawn: the
drawn table is cut to the width of the screen, and what gets cut off is the end
of the activity - the part that says what happened.
A query read a page at a time is one request per page, each traced separately
by Cassandra, and the view covers all of them: every page's events, a Page
column saying which is which, and a total that is the sum. A trace of the last
page alone says a scan read 65 rows when the query read 565. A query read to
the end can be hundreds of requests, so the last 20 pages are kept and the
summary says how many there were.
FILE and HELP are not views. They open a menu and a window over whatever you
are looking at, and leave it there. On a narrow terminal the labels shorten and
then the buttons go, rather than the tab names giving way - Alt+F and F1
still work.
╭──────────────╮
│ CONNECT │
│──────────────│
│ SAVE RESULTS │
│ SOURCE │
│ COPY TO │
│ COPY FROM │
│──────────────│
│ AUTOSAVE │
│ PREFERENCES │
│──────────────│
│ QUIT │
╰──────────────╯
CONNECT asks for a cluster. The window is in two parts: the connections that
have been saved are down the left, with + Create New Connection above them,
and the settings of whichever one is chosen are on the right - host, port,
keyspace, username, password, the two timeouts and the SSL settings. ← and
→ move between the two.
A connection has a name, which is what the list shows; left empty it takes the
name of its host. The first of them is the one CQLAI opens with and is marked
(default); the one it is connected to now is marked (connected), and is the
connection the window opens on.
The settings at the top of the file are the default connection written out
again, so that anything reading cqlai.json by hand still finds a host. The
list is what decides: change it and the top of the file follows. Save and Connect writes it to cqlai.json under
connections and makes it the one CQLAI opens with next time. Connect tries
it without saving, and a connection that fails leaves the window open saying
why, so you can change a field and try again.
{
"host": "10.0.0.1",
"connections": [
{ "name": "production", "host": "10.0.0.1", "port": 9042 },
{ "name": "staging", "host": "10.0.1.1", "keyspace": "trial" }
]
}CQLAI starts whether or not a cluster answers. Without one it says so and why,
the tabs that need a cluster are dimmed, and CONNECT is how you get one.
SAVE RESULTS writes what is on screen, and says so when there is nothing to
write. SOURCE runs the CQL in a file, and COPY TO/COPY FROM move a whole
table in or out. Below the first line are the two that settle something rather
than act on it: AUTOSAVE saves every query from now on, and PREFERENCES
edits the settings CQLAI starts with. Below the second, QUIT leaves, asking
first.
Up and down move, Enter picks, Esc closes. AutoSave: on the bottom line says
whether it is on, and clicking it opens the same window.
The windows that ask for a path - SAVE RESULTS and AUTOSAVE, and the File
field on the three forms - open on a file browser under your home directory. Tab
completes, .. walks up, and the wheel scrolls the listing.
A statement without a semicolon is not finished, so the prompt stays open and waits for the rest. Each line goes into the console as you type it, with the prompt it was typed at, and the prompt itself says which line you are on:
> SELECT *
... FROM users
... WHERE id = 1;
Esc gives up on the statement. A result that is only partly loaded says so on
a line of its own at the end of it, with how many rows are showing and how to
get the rest.
Query results carry a second header row saying what each column is: its key marker, where it has one, and its CQL type.
┌──────────┬──────────┬──────────────────────┬─────────┐
│ id │ region │ created │ name │
│ PK1 uuid │ PK2 text │ C timestamp │ varchar │
├──────────┼──────────┼──────────────────────┼─────────┤
│ 1 │ eu-west │ 2026-09-10T11:22:33Z │ alice │
PK is a partition key column and C a clustering column, numbered by
component where there is more than one. It is always shown - there is no key to
press - and costs one row for the whole table rather than widening every column.
DESCRIBE and the listings have no second row: their columns are not columns of
a table and have no CQL type.
Under the results: the last command, how long the query took, and how many rows
came back. Rows: 300+ means there is more to fetch.
When a result outgrows its memory limit, rows are dropped from the start of it and the bar says so:
History: SELECT * FROM events… │ Query: 41ms │ Rows: 300+ │ dropped: first 1200
Without that, scrolling to the top of a result and not finding its first row looks like lost data rather than a decision.
On a narrow terminal the bar drops what it cannot fit, least useful first, and cuts the command rather than dropping it.
| Shortcut | Action | macOS Alternative |
|---|---|---|
PgUp/PgDn |
Scroll viewport by page / Load more data when available | Fn+↑/Fn+↓ |
Space |
Load next page when more data available | Same |
Enter (empty input) |
Load next page when more data available | Same |
Alt+↑/Alt+↓ |
Scroll viewport by single row (respects row boundaries) | Option+↑/Option+↓ |
Alt+←/Alt+→ |
Scroll table horizontally (wide tables) | Option+←/Option+→ |
↑/↓ |
Scroll results in the table and trace views. In the normal view they step through command history; Ctrl+P recalls history from any view |
Same |
Press Esc to toggle navigation mode when viewing tables or traces.
| Shortcut | Action in Navigation Mode |
|---|---|
j / k |
Scroll down/up by single line |
d / u |
Scroll down/up by half page |
g / G |
Jump to top/bottom of results |
< / > |
Scroll left/right by 10 columns |
{ / } |
Scroll left/right by 50 columns |
0 / $ |
Jump to first/last column |
Esc |
Exit navigation mode / Cancel pagination if active |
The mouse is on by default. Click the mode tabs at the top, click KS, CL,
Pg, Trace or Fetch on the bottom line to change them, and click and drag
anywhere in the output to select text. No modifier key for any of it.
Releasing a drag copies the selection to your system clipboard. Double-click
selects a word, triple-click selects a line, and dragging past the top or bottom
edge scrolls, so a selection can run further than one screen. Esc, or a click
somewhere else, clears it.
The copy goes out as OSC 52, the escape sequence a terminal program uses to
write the clipboard. That works through ssh and through tmux, where shelling
out to xclip or pbcopy would put the text on the wrong machine.
There is no right-click paste. Reading the clipboard needs OSC 52 read,
which iTerm2, kitty, foot and WezTerm all refuse by default, for good reason -
any program on the far end of an ssh session could otherwise help itself to
whatever you last copied. Whatever your terminal binds paste to, usually
Shift+right-click or Ctrl+Shift+V, still works.
MOUSE OFF hands the mouse back to the terminal, so its own selection, its
right-click paste and its middle-click paste behave as they do in any other
program - at the cost of the tabs and the settings no longer being clickable.
MOUSE ON takes it back, and MOUSE on its own says which is active. The
status bar shows [MOUSE] or [MOUSE OFF].
The F-keys switch view either way, and every setting on the bottom line has a command, so nothing is only reachable with the mouse.
The wheel scrolls in both modes. With the mouse on it arrives as a wheel event;
with it off, alternate scroll mode has the terminal turn wheel spins into
↑/↓ key presses, which scroll whatever ↑ and ↓ scroll: results in the
table view, the trace in the trace view, the conversation in the AI view.
| Action | Function |
|---|---|
| Mouse Wheel | Scroll vertically with automatic data loading |
| Click+Drag | Select text; releasing copies it to the clipboard |
| Double Click | Select a word |
| Triple Click | Select a line |
| Click a tab | Switch view |
| Click a setting | Change KS, CL, Pg, Trace or Fetch; KS lists the cluster's keyspaces |
| Right Click | Nothing; use whatever your terminal binds paste to |
Esc |
Clear the selection |
Scroll wide tables sideways with Alt+←/Alt+→, or < and > in navigation
mode. Horizontal scrolling with the wheel is not available, because the terminal
only reports vertical wheel spins as key presses.
Note for macOS Users:
- Most
Ctrlshortcuts work as-is on macOS, but you can also use⌘(Command) key as an alternative Altkey is labeled asOptionon Mac keyboards- Function keys (F1-F6) may require holding
Fnkey depending on your Mac settings
CQLAI provides intelligent, context-aware tab completion to speed up your workflow. Press Tab at any point to see available completions.
CQL Keywords & Commands:
- All CQL keywords:
SELECT,INSERT,CREATE,ALTER,DROP, etc. - Meta-commands:
DESCRIBE,CONSISTENCY,COPY,SHOW, etc. - Data types:
TEXT,INT,UUID,TIMESTAMP, etc. - Consistency levels:
ONE,QUORUM,ALL,LOCAL_QUORUM, etc.
Schema Objects:
- Keyspace names
- Table names (within current keyspace)
- Column names (when context allows)
- User-defined type names
- Function and aggregate names
- Index names
Statement Shapes:
CREATE TABLE, INDEX, MATERIALIZED VIEW, TYPE, FUNCTION, AGGREGATE, TRIGGER,
KEYSPACE, ROLE and USER complete a word at a time, through their keywords,
their brackets and their options - CREATE FUNCTION offers everything from the
argument list to CALLED ON NULL INPUT to the language.
So do SELECT, INSERT, UPDATE, DELETE and BATCH: after a column in a WHERE
clause you get the operators (=, IN, CONTAINS KEY, BETWEEN, LIKE,
IS NOT NULL), after the table the clauses that can follow it in the order
they are written, and nothing at all once the statement is finished.
Context-Aware Completions:
-- After SELECT, suggests column names and keywords
SELECT <Tab> -- Shows: *, column names, DISTINCT, JSON, etc.
-- After FROM, suggests table names
SELECT * FROM <Tab> -- Shows: available tables in current keyspace
-- After USE, suggests keyspace names
USE <Tab> -- Shows: available keyspaces
-- After DESCRIBE, suggests object types
DESCRIBE <Tab> -- Shows: KEYSPACE, TABLE, TYPE, etc.
-- After consistency command
CONSISTENCY <Tab> -- Shows: ONE, QUORUM, ALL, etc.
-- After WITH, suggests the table options, and then their values
... WITH <Tab> -- Shows: compaction = , gc_grace_seconds = , etc.
... WITH compaction = {<Tab> -- Shows: the keys the map takes
... WITH compaction = {'class': <Tab> -- Shows: the compaction strategies
... WITH gc_grace_seconds = <Tab> -- Shows: <seconds>
... WITH CLUSTERING ORDER BY (b <Tab> -- Shows: ASC, DESC
-- An index completes through its target, its implementation and its options
CREATE INDEX i ON t (<Tab> -- Shows: keys(, values(, entries(, full(, <column name>
... (email) USING <Tab> -- Shows: 'sai', 'StorageAttachedIndex', etc.
... USING 'sai' WITH OPTIONS = {<Tab> -- Shows: the options that index takesFile Path Completion:
-- For commands that accept file paths
SOURCE '<Tab> -- Shows: files in current directory
SOURCE '/path/<Tab> -- Shows: files in /path/- Case Insensitive: Type
sel<Tab>to getSELECT - Partial Matching: Type part of a word and press Tab
- Multiple Matches: When multiple completions are available:
- First Tab: Shows inline completion if unique
- Second Tab: Shows all available options in a modal
- Smart Filtering: Completions are filtered based on current context
- Escape to Cancel: Press
Escto close the completion modal - What to Type: Where the next word is yours to invent - the name of a keyspace, a table, a column, or a value - the list says so rather than going empty:
<table name>,<column name>,<value>. A note like that is shown in italics and is never put into the prompt.
-- Complete table name
SELECT * FROM us<Tab>
-- Completes to: SELECT * FROM users
-- Complete consistency level
CONSISTENCY LOC<Tab>
-- Shows: LOCAL_ONE, LOCAL_QUORUM, LOCAL_SERIAL
-- Complete column names after SELECT
SELECT id, na<Tab> FROM users
-- Completes to: SELECT id, name FROM users
-- Complete file paths for SOURCE command
SOURCE 'sche<Tab>
-- Completes to: SOURCE 'schema.cql'
-- Complete COPY command options
COPY users TO 'file.csv' WITH <Tab>
-- Shows: HEADER, DELIMITER, NULLVAL, PAGESIZE, etc.
-- Show all tables when multiple exist
SELECT * FROM <Tab>
-- Shows modal with: users, orders, products, etc.- Use Tab liberally: The completion system is smart and context-aware
- Type minimum characters: Often 2-3 characters are enough to get unique completion
- Use for discovery: Press Tab on empty input to see what's available
- File paths: Remember to include quotes for file path completion
- Navigate completions: Use arrow keys to select from multiple options
CQLAI supports all standard CQL commands plus additional meta-commands for enhanced functionality.
Execute any valid CQL statement supported by your Cassandra cluster:
- DDL:
CREATE,ALTER,DROP(KEYSPACE, TABLE, INDEX, etc.) - DML:
SELECT,INSERT,UPDATE,DELETE - DCL:
GRANT,REVOKE - Other:
USE,TRUNCATE,BEGIN BATCH, etc.
Meta-commands provide additional functionality beyond standard CQL:
-
CONSISTENCY
<level>- Set consistency level (ONE, QUORUM, ALL, etc.)CONSISTENCY QUORUM CONSISTENCY LOCAL_ONE
-
PAGING
<size>| OFF - Set result paging sizePAGING 1000 PAGING OFF -
TRACING ON | OFF - Enable/disable query tracing
TRACING ON SELECT * FROM users; TRACING OFF
-
OUTPUT [FORMAT] - Set output format
OUTPUT -- Show current format OUTPUT TABLE -- Table format (default) OUTPUT JSON -- JSON format OUTPUT EXPAND -- Expanded vertical format OUTPUT ASCII -- ASCII table format
The format decides how a result is drawn and nothing else.
OUTPUT JSONused to rewrite the query asSELECT JSON, so what came back was one column of documents rather than the columns asked for; JSON is now written by CQLAI from the values that come back, keeping their types. TypingSELECT JSONyourself still does what it always did.Changing the format redraws the result already on screen, in the new format, without running the query again.
EXPAND ONandEXPAND OFFare the same setting by another name, as is the Output control on the status line. Only the rows fetched so far are redrawn: a switch does not pull the rest of a result in, and says how many rows are showing when there are more.
- DESCRIBE - Show schema information
DESCRIBE KEYSPACES -- List all keyspaces DESCRIBE KEYSPACE <name> -- Show keyspace definition DESCRIBE TABLES -- List tables in current keyspace DESCRIBE TABLE <name> -- Show table structure DESCRIBE TYPES -- List user-defined types DESCRIBE TYPE <name> -- Show UDT definition DESCRIBE FUNCTIONS -- List user functions DESCRIBE FUNCTION <name> -- Show function definition DESCRIBE AGGREGATES -- List user aggregates DESCRIBE AGGREGATE <name> -- Show aggregate definition DESCRIBE MATERIALIZED VIEWS -- List materialized views DESCRIBE MATERIALIZED VIEW <name> -- Show view definition DESCRIBE INDEX <name> -- Show index definition DESCRIBE CLUSTER -- Show cluster information DESC <keyspace>.<table> -- Shorthand for table description
-
COPY TO - Export table data to CSV or Parquet file
-- Basic export to CSV COPY users TO 'users.csv' -- Export to Parquet format (auto-detected by extension) COPY users TO 'users.parquet' -- Export to Parquet with explicit format and compression COPY users TO 'data.parquet' WITH FORMAT='PARQUET' AND COMPRESSION='SNAPPY' -- Export specific columns COPY users (id, name, email) TO 'users_partial.csv' -- Export with options COPY users TO 'users.csv' WITH HEADER = TRUE AND DELIMITER = '|' -- Export to stdout COPY users TO STDOUT WITH HEADER = TRUE -- Available options: -- FORMAT = 'CSV'/'PARQUET' -- Output format (default: CSV, auto-detected) -- HEADER = TRUE/FALSE -- Include column headers (CSV only) -- DELIMITER = ',' -- Field delimiter (CSV only) -- NULLVAL = 'NULL' -- String to use for NULL values -- PAGESIZE = 1000 -- Rows per page for large exports -- COMPRESSION = 'SNAPPY' -- For Parquet: SNAPPY, GZIP, ZSTD, LZ4, NONE -- CHUNKSIZE = 10000 -- Rows per chunk for Parquet
-
COPY FROM - Import CSV or Parquet data into table
-- Basic import from CSV file COPY users FROM 'users.csv' -- Import from Parquet file (auto-detected) COPY users FROM 'users.parquet' -- Import from Parquet with explicit format COPY users FROM 'data.parquet' WITH FORMAT='PARQUET' -- Import with header row (CSV) COPY users FROM 'users.csv' WITH HEADER = TRUE -- Import specific columns COPY users (id, name, email) FROM 'users_partial.csv' -- Import from stdin COPY users FROM STDIN -- Import with custom options COPY users FROM 'users.csv' WITH HEADER = TRUE AND DELIMITER = '|' AND NULLVAL = 'N/A' -- Available options: -- HEADER = TRUE/FALSE -- First row contains column names -- DELIMITER = ',' -- Field delimiter -- NULLVAL = 'NULL' -- String representing NULL values -- MAXROWS = -1 -- Maximum rows to import (-1 = unlimited) -- SKIPROWS = 0 -- Number of initial rows to skip -- MAXPARSEERRORS = -1 -- Max parsing errors allowed (-1 = unlimited) -- MAXINSERTERRORS = 1000 -- Max insert errors allowed -- MAXBATCHSIZE = 20 -- Max rows per batch insert -- MAXREQUESTS = 6 -- Concurrent batch workers (parallelism) -- MINBATCHSIZE = 2 -- Min rows per batch insert -- CHUNKSIZE = 5000 -- Rows between progress updates -- ENCODING = 'UTF8' -- File encoding -- QUOTE = '"' -- Quote character for strings
-
AUTOSAVE - Save every query's output into a directory, as it runs
AUTOSAVE '/exports/' -- Save each query as a text file AUTOSAVE JSON '/exports/' -- One JSON file per query AUTOSAVE CSV '/exports/' -- One CSV file per query AUTOSAVE PARQUET '/exports/' -- One Parquet file per query SELECT * FROM users; -- written to /exports/query_20260910_181240_001.csv SELECT * FROM events; -- written to /exports/query_20260910_181241_002.csv AUTOSAVE OFF -- Stop
A directory, not a file. Each query gets its own timestamped file, because one file cannot hold the output of every query: two queries against different tables have different columns, and a Parquet file has one schema. CSV has the same problem more quietly - a header row, then rows from another table underneath it.
Each file is finished as its query is, so it can be read straight away. With
AUTOFETCHoff, rows that page in later are written as numbered parts of the same query - a Parquet file is sealed by its footer and cannot be added to.CAPTUREstill works as a name for this command, forcqlshcompatibility. -
SAVE - Save displayed query results to file (without re-executing)
-- First run a query SELECT * FROM users WHERE status = 'active'; -- Then save the displayed results in various formats: SAVE -- Interactive dialog (choose format & filename) SAVE TO 'users.csv' -- Save to CSV (format auto-detected) SAVE TO 'users.json' -- Save to JSON (format auto-detected) SAVE TO 'users.parquet' -- Save to Parquet (format auto-detected) SAVE TO 'data.out' AS CSV -- Explicitly specify format -- Key differences from AUTOSAVE: -- - SAVE writes one file, of the result you are looking at -- - AUTOSAVE writes a file per query, for every query from now on -- - No need to re-run the query -- - Preserves exact data shown in terminal -- - Works with paginated results (saves only loaded pages) -- PARQUET needs the column types of the result, so it is offered for query -- results and not for a DESCRIBE, which arrives without them.
-
SHOW - Display session information
SHOW VERSION -- Show Cassandra version SHOW HOST -- Show current connection details SHOW SESSION -- Show all session settings
-
EXPAND ON | OFF - Toggle expanded output mode
EXPAND ON -- Vertical output (one field per line) SELECT * FROM users WHERE id = 1; EXPAND OFF -- Normal table output
- SOURCE - Execute CQL scripts from file
SOURCE 'schema.cql' -- Execute script SOURCE '/path/to/script.cql' -- Absolute path
- HELP - Display command help
HELP -- Show all commands HELP DESCRIBE -- Help for specific command HELP CONSISTENCY -- Help for consistency levels
- .ai
<natural language query>- Generate CQL from natural language.ai show all users with active status .ai create a table for storing user sessions .ai find orders placed in the last 30 days
CQLAI supports multiple configuration methods for maximum flexibility and compatibility with existing Cassandra setups.
Configuration sources are loaded in the following order (later sources override earlier ones):
-
CQLSHRC files (for compatibility with existing cqlsh setups)
~/.cassandra/cqlshrc(standard location)~/.cqlshrc(alternative location)$CQLSH_RC(if environment variable is set)
-
CQLAI JSON configuration files
./cqlai.json(current directory)~/.cassandra/cqlai.json(besidecqlshrc, and where a new one is written)~/.cqlai.json(user home directory, where it used to go)~/.config/cqlai/config.json(XDG config directory)
-
Environment variables
CQLAI_HOST,CQLAI_PORT,CQLAI_KEYSPACE, etc.CASSANDRA_HOST,CASSANDRA_PORT(for compatibility)
FILE > PREFERENCES on the tab line opens a window holding CQLAI's own
settings in cqlai.json - how results are fetched and drawn, the history files,
the AI providers and the auth provider. Fill them in, press Save, and it
writes the JSON file it was loaded from, or ~/.cassandra/cqlai.json when none
was found.
Keys already in the file that CQLAI does not know about are left alone.
Where to connect and how - host, port, keyspace, credentials, timeouts and SSL -
belongs to a connection rather than to CQLAI, and is edited in FILE > CONNECT
against the connection it belongs to. Two windows writing the same setting is
the mistake this project keeps finding in its own code.
What the window edits is what CQLAI starts with. It does not change the session
running now: consistency, paging and output format are set for this session from
the status line, or with CONSISTENCY, PAGING and OUTPUT.
- Command-line flags (highest priority)
--host,--port,--keyspace,--username,--password, etc.
CQLAI can read standard CQLSHRC files used by the traditional cqlsh tool, making migration seamless.
Supported CQLSHRC sections:
[connection]- hostname, port, ssl settings[authentication]- keyspace, credentials file path[auth_provider]- authentication module and username[ssl]- SSL/TLS certificate configuration
Example CQLSHRC file:
; ~/.cassandra/cqlshrc
[connection]
hostname = cassandra.example.com
port = 9042
ssl = true
[authentication]
keyspace = my_keyspace
credentials = ~/.cassandra/credentials
[ssl]
certfile = ~/certs/ca.pem
userkey = ~/certs/client-key.pem
usercert = ~/certs/client-cert.pem
validate = trueSee CQLSHRC_SUPPORT.md for complete CQLSHRC compatibility details.
For advanced features and AI configuration, CQLAI uses its own JSON format:
Example cqlai.json:
{
"host": "127.0.0.1",
"port": 9042,
"keyspace": "",
"username": "cassandra",
"password": "cassandra",
"requireConfirmation": true,
"consistency": "LOCAL_ONE",
"pageSize": 100,
"maxMemoryMB": 10,
"connectTimeout": 10,
"requestTimeout": 10,
"debug": false,
"historyFile": "~/.cqlai/history",
"aiHistoryFile": "~/.cqlai/ai_history",
"ssl": {
"enabled": false,
"certPath": "/path/to/client-cert.pem",
"keyPath": "/path/to/client-key.pem",
"caPath": "/path/to/ca-cert.pem",
"hostVerification": true,
"insecureSkipVerify": false
},
"ai": {
"provider": "openai",
"apiKey": "sk-...",
"model": "gpt-4-turbo-preview"
}
}Note: You can also use the url field to override the API endpoint for OpenAI-compatible APIs:
{
"ai": {
"provider": "openai",
"apiKey": "your-api-key",
"url": "https://api.synthetic.new/openai/v1",
"model": "hf:Qwen/Qwen3-235B-A22B-Instruct-2507"
}
}Note: AI features are completely optional. CQLAI works as a full-featured CQL shell without any AI configuration.
To enable AI-powered query generation, configure your preferred provider in the ai section of your cqlai.json file.
Use OpenAI for high-quality, general-purpose query generation. Requires an OpenAI API key.
- Get API Key: platform.openai.com/api-keys
- Recommended Models:
gpt-4-turbo-preview(default, recommended for best results)gpt-3.5-turbo(faster, more cost-effective)
Configuration:
{
"ai": {
"provider": "openai",
"apiKey": "sk-...",
"model": "gpt-4-turbo-preview"
}
}Use Anthropic for powerful, context-aware models. Ideal for complex queries and reasoning. Requires an Anthropic API key.
These models think before they answer. CQLAI asks for that reasoning and shows
it in the CHAT tab under Thinking:, above the answer it led to - so there is
something to read while it works rather than a pause. A model too old to be
asked for it is asked once and then left alone.
- Get API Key: console.anthropic.com/settings/keys
- Recommended Models:
claude-opus-5(default, most capable)claude-sonnet-5(faster, less expensive)claude-haiku-4-5(fastest)
Configuration:
{
"ai": {
"provider": "anthropic",
"apiKey": "sk-ant-...",
"model": "claude-opus-5"
}
}Use Google Gemini for a fast and capable model from Google. Requires a Google AI Studio API key.
- Get API Key: aistudio.google.com/app/apikey
- Recommended Model:
gemini-pro(default)
Configuration:
{
"ai": {
"provider": "gemini",
"apiKey": "...",
"model": "gemini-pro"
}
}Use Synthetic to access a vast selection of open-source AI models at very reasonable prices. Synthetic provides an OpenAI-compatible API that makes it easy to work with various open-source models.
- Get Started: synthetic.new
- API Documentation: dev.synthetic.new/docs
- Recommended Model:
hf:Qwen/Qwen3-235B-A22B-Instruct-2507(recommended, though we haven't extensively tested all models)
- Available Models: See Always-On Models
Configuration:
{
"ai": {
"provider": "openai",
"apiKey": "your-synthetic-api-key",
"url": "https://api.synthetic.new/openai/v1",
"model": "hf:Qwen/Qwen3-235B-A22B-Instruct-2507"
}
}Key Benefits:
- Access to a wide variety of open-source models
- Cost-effective pricing
- OpenAI-compatible API for easy integration
- No vendor lock-in
Notes:
- Synthetic presents an OpenAI-compatible interface, so you use the
openaiprovider in your configuration - The
urlfield overrides the default OpenAI endpoint to point to Synthetic - API key is required - obtain one from synthetic.new
Use Ollama for running AI models locally or connecting to OpenAI-compatible APIs. Ollama allows you to run powerful language models on your own hardware without sending data to external services.
- Get Started: ollama.ai
- Recommended Models:
llama3.2(Meta's Llama 3.2)codellama(Code-specialized Llama)mistral(Mistral AI's model)qwen2.5-coder(Alibaba's code model)
Configuration:
{
"ai": {
"provider": "ollama",
"model": "llama3.2",
"url": "http://localhost:11434/v1"
}
}Environment Variables:
OLLAMA_URL- Custom Ollama server URL (default:http://localhost:11434/v1)OLLAMA_MODEL- Model to use
Notes:
- No API key required for local Ollama installations
- Supports custom URLs for remote Ollama servers or OpenAI-compatible endpoints
- The
urlfield can be set at the top level (ai.url) or provider-specific (ai.ollama.url)
Use OpenRouter to access multiple AI models through a single API. OpenRouter provides access to various models from different providers.
- Get API Key: openrouter.ai/keys
- Available Models: See openrouter.ai/models
Configuration:
{
"ai": {
"provider": "openrouter",
"apiKey": "sk-or-...",
"model": "anthropic/claude-opus-5",
"url": "https://openrouter.ai/api/v1"
}
}Environment Variables:
OPENROUTER_API_KEY- OpenRouter API keyOPENROUTER_MODEL- Model to useOPENROUTER_URL- Custom OpenRouter URL (default:https://openrouter.ai/api/v1)
The mock provider is the default and requires no API key. It's useful for testing the AI workflow or for users who don't need real AI capabilities. It generates simple, predictable queries based on keywords.
Configuration:
{
"ai": {
"provider": "mock"
}
}For better security, you can provide API keys and custom URLs via environment variables instead of writing them in the configuration file.
API Keys:
- OpenAI:
OPENAI_API_KEY - Anthropic:
ANTHROPIC_API_KEY - Google Gemini:
GEMINI_API_KEY - OpenRouter:
OPENROUTER_API_KEY
Custom URLs:
- Ollama:
OLLAMA_URL(default:http://localhost:11434/v1) - OpenRouter:
OPENROUTER_URL(default:https://openrouter.ai/api/v1)
If an environment variable is set, it will be used even if a value is present in cqlai.json.
Configuration Options:
| Option | Type | Default | Description |
|---|---|---|---|
host |
string | 127.0.0.1 |
Cassandra host address |
port |
number | 9042 |
Cassandra port |
keyspace |
string | "" |
Default keyspace to use |
username |
string | "" |
Authentication username |
password |
string | "" |
Authentication password |
requireConfirmation |
boolean | true |
Require confirmation for destructive commands (DROP, DELETE, TRUNCATE) |
consistency |
string | LOCAL_ONE |
Default consistency level (ANY, ONE, TWO, THREE, QUORUM, ALL, LOCAL_QUORUM, EACH_QUORUM, LOCAL_ONE) |
pageSize |
number | 100 |
Number of rows per page |
maxMemoryMB |
number | 10 |
Maximum memory for query results in MB |
connectTimeout |
number | 10 |
Connection timeout in seconds |
requestTimeout |
number | 10 |
Request timeout in seconds |
historyFile |
string | ~/.cqlai/history |
Path to CQL command history file (supports ~ expansion) |
aiHistoryFile |
string | ~/.cqlai/ai_history |
Path to AI command history file (supports ~ expansion) |
debug |
boolean | false |
Enable debug logging |
CQLAI searches for configuration files in the following locations:
CQLSHRC files:
$CQLSH_RC(if environment variable is set)~/.cassandra/cqlshrc(standard cqlsh location)~/.cqlshrc(alternative location)
CQLAI JSON files:
./cqlai.json(current working directory)~/.cassandra/cqlai.json(besidecqlshrc)~/.cqlai.json(user home directory)~/.config/cqlai/config.json(XDG config directory on Linux/macOS)
All environment variables supported by CQLAI. CQLAI_* variables take precedence over CASSANDRA_* equivalents.
CQLAI_HOSTorCASSANDRA_HOST- Cassandra hostCQLAI_PORTorCASSANDRA_PORT- Cassandra portCQLAI_KEYSPACEorCASSANDRA_KEYSPACE- Default keyspaceCQLAI_USERNAMEorCASSANDRA_USERNAME- Authentication usernameCQLAI_PASSWORDorCASSANDRA_PASSWORD- Authentication passwordCQLAI_CONNECT_TIMEOUT- Connection timeout in seconds (default: 10)CQLAI_REQUEST_TIMEOUT- Request timeout in seconds (default: 10)CQLAI_NO_CONFIRM- Set totrueor1to disable confirmation prompts for destructive commandsCQLAI_DEBUG- Set totrueor1to enable debug logging
CQLAI_CONFIG_FILE- Path to JSON config file (overrides default locations)CQLSH_RC- Path to custom CQLSHRC file
CQLAI_EXECUTE- CQL statement to execute (equivalent to-e)CQLAI_FILE- CQL file to execute (equivalent to-f)CQLAI_FORMAT- Output format: ascii, json, csv, table (default: ascii)CQLAI_NO_HEADER- Set totrueor1to omit column headers (CSV)CQLAI_FIELD_SEPARATOR- Field separator for CSV output (default:,)CQLAI_PAGE_SIZE- Pagination size (default: 100)CQLAI_MAX_MEMORY_MB- Maximum memory for query results in MB (default: 10)
CQLAI_AI_PROVIDERorAI_PROVIDER- AI provider name (mock, openai, anthropic, gemini, ollama, openrouter)CQLAI_AI_API_KEYorAI_API_KEY- General AI API keyCQLAI_AI_MODELorAI_MODEL- General AI model nameOPENAI_API_KEY- OpenAI API keyOPENAI_MODEL- OpenAI model nameANTHROPIC_API_KEY- Anthropic API keyANTHROPIC_MODEL- Anthropic model nameGEMINI_API_KEY- Google Gemini API keyOLLAMA_URL- Ollama server URL (default:http://localhost:11434/v1)OLLAMA_MODEL- Ollama model nameOPENROUTER_API_KEY- OpenRouter API keyOPENROUTER_MODEL- OpenRouter model nameOPENROUTER_URL- OpenRouter API URL (default:https://openrouter.ai/api/v1)
If you're migrating from cqlsh, CQLAI will automatically read your existing ~/.cassandra/cqlshrc file. No changes are needed to start using CQLAI with your existing Cassandra configuration.
CQLAI includes built-in AI capabilities to convert natural language into CQL queries. Simply prefix your request with .ai:
-- Simple queries
.ai show all users
.ai find products with price less than 100
.ai count orders from last month
-- Complex operations
.ai create a table for storing customer feedback with id, customer_id, rating, and comment
.ai update user status to inactive where last_login is older than 90 days
.ai delete all expired sessions
-- Schema exploration
.ai what tables are in this keyspace
.ai describe the structure of the users table- Natural Language Input: Type
.aifollowed by your request in plain English - Schema Context: CQLAI automatically extracts your current schema to provide context
- Query Generation: The AI generates a structured query plan
- Preview & Confirm: Review the generated CQL before execution
- Execute or Edit: Choose to execute, edit, or cancel the query
Configure your preferred AI provider in cqlai.json:
- OpenAI (GPT-4, GPT-3.5)
- Anthropic (Claude 3)
- Google Gemini
- Synthetic (Multiple open-source models)
- Ollama (Local models or OpenAI-compatible APIs)
- OpenRouter (Access to multiple models)
- Mock (default, for testing without API keys)
- Read-only by default: AI prefers SELECT queries unless explicitly asked to modify
- Dangerous operation warnings: DROP, DELETE, TRUNCATE operations show warnings
- Confirmation required: Destructive operations require additional confirmation
- Schema validation: Queries are validated against your current schema
For automation and scripting, you can disable the confirmation prompts for destructive commands (DROP, DELETE, TRUNCATE) using any of these methods:
-
Command-line flag:
cqlai --no-confirm -e "TRUNCATE my_table;" -
Environment variable:
export CQLAI_NO_CONFIRM=true cqlai -e "DROP TABLE old_data;"
-
Configuration file (
cqlai.json):{ "requireConfirmation": false }
Note: Use with caution in production environments. These settings disable safety prompts that help prevent accidental data loss.
CQLAI provides comprehensive support for Apache Parquet format, making it ideal for data analytics workflows and integration with modern data ecosystems.
- Efficient Storage: Columnar format with excellent compression (50-80% smaller than CSV)
- Fast Analytics: Optimized for analytical queries in Spark, Presto, and other engines
- Type Preservation: Maintains Cassandra data types including collections and UDTs
- Machine Learning Ready: Direct compatibility with pandas, PyArrow, and ML frameworks
- Streaming Support: Memory-efficient streaming for large datasets
-- Export to Parquet (auto-detected by extension)
COPY users TO 'users.parquet';
-- Export with compression
COPY events TO 'events.parquet' WITH FORMAT='PARQUET' AND COMPRESSION='ZSTD';
-- Import from Parquet
COPY users FROM 'users.parquet';
-- Save every query as its own Parquet file
AUTOSAVE PARQUET '/exports/';
SELECT * FROM large_table WHERE condition = true;
AUTOSAVE OFF;- All Cassandra primitive types (int, text, timestamp, uuid, etc.)
- Collection types (list, set, map)
- User-Defined Types (UDTs)
- Frozen collections
- Vector types for ML workloads (Cassandra 5.0+)
- Multiple compression algorithms (Snappy, GZIP, ZSTD, LZ4)
For detailed documentation, see Parquet Support Guide.
When outputting data as JSON, there are some limitations due to how the underlying gocql driver handles dynamic typing:
- Issue: NULL values in primitive columns (int, boolean, text, etc.) appear as zero values (
0,false,"") instead ofnull - Cause: The gocql driver returns zero values for NULLs when scanning into dynamic types (
interface{}) - Workaround: Use
SELECT JSONqueries which return proper JSON from Cassandra server-side
- Issue: UDT columns appear as empty objects
{}in JSON output - Cause: The gocql driver cannot properly unmarshal UDTs without compile-time knowledge of their structure
- Workaround: Use
SELECT JSONqueries for proper UDT serialization
-- Regular SELECT (has limitations)
SELECT * FROM users;
-- Returns: {"id": 1, "age": 0, "active": false} -- age and active might be NULL
-- Using SELECT JSON (preserves types correctly)
SELECT JSON * FROM users;
-- Returns: {"id": 1, "age": null, "active": null} -- NULLs properly representedNote: Complex types (lists, sets, maps, vectors) are properly preserved in JSON output.
To work on cqlai, you'll need Go (≥ 1.24).
# Clone the repository
git clone https://github.com/axonops/cqlai.git
cd cqlai
# Install dependencies
go mod download# Build a standard binary
make build
# Build a development binary with race detection
make build-dev# Run all tests
make test
# Run tests with coverage report
make test-coverage
# Run the linter
make lint
# Run all checks (format, lint, test)
make check- Language: Go
- TUI Framework: Bubble Tea
- TUI Components: Bubbles
- Styling: Lip Gloss
- Cassandra Driver: gocql
CQLAI builds upon the foundation laid by several open-source projects, particularly Apache Cassandra. We extend our sincere gratitude to the Apache Cassandra community for their outstanding work and contributions to the field of distributed databases.
Apache Cassandra is a free and open-source, distributed, wide-column store, NoSQL database management system designed to handle large amounts of data across many commodity servers, providing high availability with no single point of failure.
- Official Website: cassandra.apache.org
- Source Code: Available on GitHub or the Apache Git repository at
gitbox.apache.org/repos/asf/cassandra.git - Documentation: Comprehensive guides and references available at the Apache Cassandra website
CQLAI incorporates and extends functionality from various Cassandra tools and utilities, enhancing them to provide a modern, efficient terminal experience for Cassandra developers and DBAs.
We encourage users to explore and contribute to the main Apache Cassandra project, as well as to provide feedback and suggestions for CQLAI through our GitHub discussions and issues pages.
- Share Ideas: Visit our GitHub Discussions to propose new features
- Report Issues: Found a bug? Open an issue
- Contribute: We welcome pull requests! See CONTRIBUTING.md for guidelines
- Star Us: If you find CQLAI useful, please star our repository!
- Website: axonops.com
- Contact: Visit our website for support options
This project is licensed under the Apache 2.0 license. See the LICENSE file for details.
Third-party dependency licenses are available in the THIRD-PARTY-LICENSES directory. To regenerate license attributions, run make licenses.
This project may contain trademarks or logos for projects, products, or services. Any use of third-party trademarks or logos are subject to those third-party's policies.
- AxonOps is a registered trademark of AxonOps Limited.
- Apache, Apache Cassandra, Cassandra, Apache Spark, Spark, Apache TinkerPop, TinkerPop, Apache Kafka and Kafka are either registered trademarks or trademarks of the Apache Software Foundation or its subsidiaries in Canada, the United States and/or other countries.
- DataStax is a registered trademark of DataStax, Inc. and its subsidiaries in the United States and/or other countries.
Made with by the AxonOps Team