Chapter 05 · CQL Foundations: DDL, DML, Filtering Rules, and cqlsh Workflows

CREATE / ALTER / DROP TABLE and Keyspace Objects with Schema Agreement Awareness

Build safe Cassandra DDL habits by observing keyspace/table metadata, schema propagation, and the difference between schema agreement and application readiness.

Intermediate105–150 minutesMechanism-first CQL labApache Cassandra 5.0.9 · Java 17 · ASF Java Driver 4.19.3Last reviewed: September 2026

Learning outcomes

AtlasMart is about to add its first application-owned CQL schema. The immediate risk is not remembering the syntax of CREATE TABLE; it is assuming that DDL completion means every node, every driver, and every deployed application version is ready for the new schema. This lesson builds a schema-change mental model from keyspace/table definitions through cluster-wide schema agreement.

01

Explain how a Cassandra keyspace and table differ from a relational database/schema/table hierarchy.

02

Create, alter and drop disposable schema objects while preserving query-first primary-key intent.

03

Inspect schema through DESCRIBE, system_schema, and cluster schema-version evidence.

04

Distinguish schema agreement from data convergence, application rollout, and backward compatibility.

05

Design additive/rollback-friendly DDL procedures instead of treating DDL as an instantaneous global switch.

Pinned Chapter 05 baseline

Examples target Apache Cassandra 5.0.9, Java 17, cqlsh/nodetool from the same 5.0.9 distribution, three local Docker nodes in dc1/rack1..rack3, NetworkTopologyStrategy RF=3, 16 vnodes per node, and QUORUM for the chapter's normal replicated reads/writes. The ASF Java driver baseline used where application behavior matters is 4.19.3. Authentication/TLS are intentionally disabled only inside the isolated disposable Docker network; later security chapters replace that learning shortcut.

Execution note

This generation environment does not provide a running Docker/Cassandra cluster. Commands and expected output shapes were checked against current official Cassandra/CQL/driver documentation, but no runtime result is presented as captured evidence. Record the exact output, versions, schema UUIDs, timestamps, TTLs, traces, and latencies produced on your own machine.

1. CQL looks familiar, but its schema encodes a distributed access path

Cassandra Query Language (CQL) deliberately resembles SQL, but the similarity stops well before the execution model. A Cassandra keyspace is the namespace where replication policy is defined. A table defines the partition key and optional clustering columns that determine placement and on-partition order. The primary key is therefore not merely a uniqueness constraint: its partition-key component is part of the routing contract.

In a relational system, adding an index or letting the optimizer choose a join order may rescue a query whose access path was not anticipated. Cassandra expects the common query shapes to be designed into tables. CQL has no implicit joins and no subquery mechanism that reconstructs an arbitrary relational graph at request time. Chapter 05 therefore treats DDL as application architecture, not just catalog administration.

Concept Cassandra meaning Do not confuse it with
Keyspace Namespace plus replication/durable-write options A filesystem folder or a security tenant boundary
Table Rows organized by a declared primary key and storage options A relation that can be queried arbitrarily
Partition key Hash input that determines token/replica placement Only a uniqueness constraint
Clustering column On-partition row ordering and slice-query dimension A global secondary sort key
Schema agreement Nodes report the same schema version Application compatibility or repaired data

2. Establish the disposable cluster and create an application keyspace

Reuse the isolated topology from Chapters 01–04 when it is still running. Otherwise create it below. The chapter uses its own atlasmart_cql keyspace so DDL experiments cannot alter the earlier replication/token labs.

bash · create or verify the course cluster
# Disposable Chapter 05 cluster: three nodes, three racks, one DC, 16 vnodes/node# If these objects already exist from Chapters 01–04, reuse them instead of recreating them.docker network create atlasmart-cassandradocker volume create atlasmart-cass-1-datadocker volume create atlasmart-cass-2-datadocker volume create atlasmart-cass-3-datadocker run -d --name atlasmart-cass-1 --hostname atlasmart-cass-1 --network atlasmart-cassandra   -e CASSANDRA_CLUSTER_NAME=atlasmart-course   -e CASSANDRA_DC=dc1 -e CASSANDRA_RACK=rack1   -e CASSANDRA_ENDPOINT_SNITCH=GossipingPropertyFileSnitch   -e CASSANDRA_NUM_TOKENS=16   -v atlasmart-cass-1-data:/var/lib/cassandra cassandra:5.0.9# Wait for node 1 to accept CQL, then start the two peers.docker run -d --name atlasmart-cass-2 --hostname atlasmart-cass-2 --network atlasmart-cassandra   -e CASSANDRA_CLUSTER_NAME=atlasmart-course -e CASSANDRA_SEEDS=atlasmart-cass-1   -e CASSANDRA_DC=dc1 -e CASSANDRA_RACK=rack2   -e CASSANDRA_ENDPOINT_SNITCH=GossipingPropertyFileSnitch   -e CASSANDRA_NUM_TOKENS=16   -v atlasmart-cass-2-data:/var/lib/cassandra cassandra:5.0.9docker run -d --name atlasmart-cass-3 --hostname atlasmart-cass-3 --network atlasmart-cassandra   -e CASSANDRA_CLUSTER_NAME=atlasmart-course -e CASSANDRA_SEEDS=atlasmart-cass-1   -e CASSANDRA_DC=dc1 -e CASSANDRA_RACK=rack3   -e CASSANDRA_ENDPOINT_SNITCH=GossipingPropertyFileSnitch   -e CASSANDRA_NUM_TOKENS=16   -v atlasmart-cass-3-data:/var/lib/cassandra cassandra:5.0.9docker exec atlasmart-cass-1 nodetool statusdocker exec atlasmart-cass-1 nodetool versiondocker exec atlasmart-cass-1 java -version
sql · create the Chapter 05 schema
CREATE KEYSPACE IF NOT EXISTS atlasmart_cqlWITH replication = {'class': 'NetworkTopologyStrategy', 'dc1': 3}AND durable_writes = true;CONSISTENCY QUORUM;CREATE TABLE IF NOT EXISTS atlasmart_cql.products_by_id (    product_id text PRIMARY KEY,    name text,    category text,    price_cents bigint,    status text,    description text,    updated_at timestamp);CREATE TABLE IF NOT EXISTS atlasmart_cql.products_by_category (    category text,    bucket tinyint,    product_id text,    name text,    status text,    price_cents bigint,    PRIMARY KEY ((category, bucket), product_id));CREATE TABLE IF NOT EXISTS atlasmart_cql.inventory_by_product (    product_id text,    location_id text,    quantity int,    status text,    promo_note text,    PRIMARY KEY ((product_id), location_id));

Now inspect what Cassandra actually stored instead of trusting the text you typed. DESCRIBE KEYSPACE is a cqlsh convenience. The system_schema tables are queryable schema metadata maintained by Cassandra.

bash · inspect the schema from node 1
docker exec atlasmart-cass-1 cqlsh -e "DESCRIBE KEYSPACE atlasmart_cql"docker exec atlasmart-cass-1 cqlsh -e "SELECT keyspace_name, durable_writes, replication FROM system_schema.keyspaces WHERE keyspace_name='atlasmart_cql';"docker exec atlasmart-cass-1 cqlsh -e "SELECT table_name FROM system_schema.tables WHERE keyspace_name='atlasmart_cql';"docker exec atlasmart-cass-1 cqlsh -e "SELECT table_name, column_name, kind, position, type FROM system_schema.columns WHERE keyspace_name='atlasmart_cql' AND table_name='products_by_category';"

The expected evidence is the replication map containing NetworkTopologyStrategy and dc1:3, plus column metadata showing category and bucket as partition-key components and product_id as a clustering column. Exact formatting and option ordering are not an API contract.

3. ALTER is a schema mutation, not a free application migration

Use additive DDL first. A new nullable/non-key column can often be introduced before all application instances start writing it. That does not make every alteration safe: changing primary-key shape requires a new table, and Cassandra intentionally restricts type changes that could invalidate existing encoded data.

sql · add and verify a column
ALTER TABLE atlasmart_cql.products_by_idADD IF NOT EXISTS manufacturer text;SELECT column_name, kind, typeFROM system_schema.columnsWHERE keyspace_name='atlasmart_cql'  AND table_name='products_by_id'  AND column_name='manufacturer';

A safe rollout separates at least four events: schema mutation accepted, nodes reach schema agreement, deployed readers tolerate the new shape, and deployed writers start using the field. Reversing that order can break old binaries even when Cassandra itself is perfectly healthy.

Schema agreement is narrower than rollout success

If all nodes report one schema version, Cassandra has converged on the same schema metadata. That does not prove a Java process has refreshed metadata, a serializer understands the new field, an old query still returns the columns it expects, or historical rows contain any new value.

4. Observe agreement instead of assuming it

nodetool describecluster reports cluster metadata including schema versions. Run it from more than one member when teaching agreement. Also query system_schema directly on individual nodes: a client normally hides which node answered unless you intentionally control the contact point.

bash · compare schema evidence across nodes
docker exec atlasmart-cass-1 nodetool describeclusterdocker exec atlasmart-cass-2 nodetool describeclusterdocker exec atlasmart-cass-3 nodetool describeclusterfor n in 1 2 3; do  docker exec atlasmart-cass-$n cqlsh -e "SELECT column_name,type FROM system_schema.columns WHERE keyspace_name='atlasmart_cql' AND table_name='products_by_id';"done

On PowerShell, run the three docker exec commands separately instead of relying on the Bash loop. Agreement should converge quickly in a healthy local cluster, but “quickly” is not a correctness guarantee. Deployment automation should poll with a bounded timeout and fail visibly rather than sleep for an arbitrary number of seconds and hope.

5. Deliberately wrong DDL: destructive convenience and false confidence

A common migration mistake is to use DROP TABLE as a development reset while connected to an endpoint whose identity was never verified. Another is to see one successful DESCRIBE and declare the rollout complete. Both errors confuse local client success with system-wide intent.

sql · safe disposable DROP experiment
CREATE TABLE atlasmart_cql.ddl_scratch (  id text PRIMARY KEY,  note text);INSERT INTO atlasmart_cql.ddl_scratch (id,note) VALUES ('only-row','disposable');SELECT * FROM atlasmart_cql.ddl_scratch;DROP TABLE atlasmart_cql.ddl_scratch;-- This should now fail because the table no longer exists:SELECT * FROM atlasmart_cql.ddl_scratch;

The failure after DROP is evidence that the catalog object disappeared; it is not a backup test, nor does it tell you whether filesystem snapshots exist. The safe repair is procedural: verify cluster/keyspace identity first, use migration review, prefer additive changes, make destructive DDL explicitly gated, and maintain tested restore paths.

6. Production judgment and lab acceptance

DDL is low-frequency but high-impact. Treat keyspace/table definitions as versioned application contracts. Record exact Cassandra and driver versions, RF, DC/rack layout, table options, schema versions before/after, and the compatibility window for old/new application binaries. A DDL rollback can be harder than the forward change once writers have emitted new data, so the rollback plan must describe data as well as schema.

Verification checklist

  • atlasmart_cql uses NetworkTopologyStrategy with RF=3 in dc1.
  • products_by_category shows the intended composite partition key in system_schema.columns.
  • All three nodes converge to one schema version after the additive ALTER.
  • You can explain why agreement is not application compatibility.
  • No destructive command targeted any keyspace outside atlasmart_cql.

Check your understanding

  1. Why is a Cassandra primary key more than a uniqueness constraint?
  2. What does schema agreement prove?
  3. Why prefer additive DDL during a rolling application rollout?
  4. Why is DROP TABLE dangerous even in a shell that connected successfully?
  5. Where can you inspect Cassandra schema without relying only on DESCRIBE?
Review the answers

1. Its partition-key component determines token routing and replica placement, while clustering components determine row organization within the partition.

2. It proves participating nodes report the same schema version; it does not prove application rollout, data backfill, driver compatibility, or repair.

3. Old and new application versions can overlap more safely when new schema does not immediately invalidate the old contract.

4. Successful connectivity does not establish that the endpoint is the intended disposable cluster; identity and blast radius must be verified first.

5. The system_schema keyspace exposes keyspace, table, column, index, type and related metadata.

Summary and next bridge

You now have a schema object model plus an evidence model: CQL DDL changes distributed metadata, nodes must converge, and application compatibility remains a separate responsibility. Lesson 2 moves from schema to mutations and proves why INSERT is normally an upsert and why Cassandra resolves individual cells by timestamps.

bash · reset the disposable Chapter 05 lab
# Chapter-only reset or deliberate preservation# Keep atlasmart_cql if you are continuing directly to the next lesson.# Full reset (deletes only the dedicated course containers/volumes/network)docker rm -f atlasmart-cass-1 atlasmart-cass-2 atlasmart-cass-3docker volume rm atlasmart-cass-1-data atlasmart-cass-2-data atlasmart-cass-3-datadocker network rm atlasmart-cassandra

Authoritative references

Keep knowledge open

Help the academy stay free and grow.

If these tutorials save you time, a small donation supports new lessons, technical review, diagrams, examples, and long-term maintenance.

ETHEthereum / ERC-20 only
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0

Send only Ethereum or ERC-20 compatible assets to this address.