SQLite / Basic: the embedded engine and correct data / Lesson 1
The embedded engine and connection lifecycle
Build a SQLite-specific model of SQL execution, connection ownership, private memory databases, files, shell commands, and runtime identity.
On this page
Lesson goals
- Distinguish embedded SQLite from a database server.
- Explain memory versus file connections.
- Identify runtime and tool-command boundaries.
Follow SQL inside the application process
SQLite is an embedded library that executes inside the calling process rather than a separately configured database server. A connection opens a database, prepares statements, steps through results, and releases resources. There is no SQLite server login to create for these examples. File access, application identity, and the host integration determine important security and lifecycle boundaries.
Embedded does not mean that correctness, concurrency, or recovery happen automatically. Several connections can access one database file, while transactions coordinate their work. A program crash and a SQL constraint rejection are different failures. Begin by identifying who owns the connection, where the database resides, and when statements and transactions finish before treating the engine as a convenient dictionary.
Use a complete private-memory example
Each ordinary :memory: connection starts its own transient database. Closing the connection discards that database. The SQL example is complete when submitted to a fresh connection by the course validator: it creates its own table, inserts synthetic rows, and returns an explicitly ordered result. It depends on no application schema, existing file, credential, or service.
The two items remain available only for that connection's lifetime. Reopening another :memory: connection does not restore them. Named shared-memory URI configurations have different rules and are outside this beginner example. Do not generalize memory-database behavior into a persistence guarantee; durable application data requires an intentionally selected file and an operational recovery design.
CREATE TABLE item(id INTEGER PRIMARY KEY, name TEXT NOT NULL);
INSERT INTO item(id,name) VALUES (1,'Notebook'),(2,'Pencil');
SELECT id,name FROM item ORDER BY id;
id|name
1|Notebook
2|Pencil
The fresh database contains exactly two synthetic items. ORDER BY defines presentation rather than relying on insertion order.
Separate SQL from command-line shell features
The sqlite3 shell is a client tool built around the library. Dot commands such as .tables, .schema, and .mode are interpreted by that shell, not SQL executed by the engine. Sending .tables through a normal database API is therefore not a metadata query. SQL statements use semicolons as separators, and a host driver can impose its own single-statement or script interface.
Use SQL catalog queries when code needs metadata, and keep shell recipes separate from application commands. A shell's version can differ from the library embedded in Python, a mobile app, or a framework. Check sqlite_version() on the actual connection instead of inferring capability from an unrelated executable that happens to be installed.
Inspect a known schema without inventing a runtime
The second script queries sqlite_schema after creating one local table. It reports that table's name and type, avoiding a fabricated universal version string. A runtime query can separately report the actual engine version and compile options. Feature availability depends on both release and build configuration; JSON or optional virtual-table extensions must be checked where the application runs.
Examples are validated on SQLite3.45.1 through Python3.12.3. This identifies the tested environment, not a production patch recommendation or a newest-release claim. Later lessons label STRICT tables, RETURNING, JSONB, and other version-sensitive features. The current official WAL documentation also describes a historical concurrency defect, which the advanced operational discussion treats explicitly.
CREATE TABLE training_note(id INTEGER PRIMARY KEY, body TEXT);
SELECT name,type FROM sqlite_schema
WHERE name='training_note' ORDER BY name;
name|type
training_note|table
The catalog contains the table just created. System-generated objects are excluded by the exact name predicate.
Close resources and state the persistence contract
A host-language connection has a lifecycle independent of a query result object. Finish or close cursors, end transactions deliberately, and close owned connections. In Python, a connection context manager manages transaction completion but does not itself close the connection; contextlib.closing or an explicit finally can own that cleanup. Do not leave cleanup to eventual garbage collection.
For file-backed learning, use a newly created private temporary directory and remove it only after all owned connections close. Never open a configured project database to discover whether an example is harmless. A useful exercise record states the connection kind, schema setup, expected rows, transaction outcome, and cleanup; this separates SQL reasoning from accidental environmental state.
Common errors and fixes
no such table: item
- Why it happens
- A new connection opened a different private memory database.
- How to fix it
- Create the self-contained schema on that connection or select the intended database deliberately.
- Prevent it next time
- Record connection ownership and database identity.
near ".": syntax error
- Why it happens
- A shell dot command was sent as SQL.
- How to fix it
- Use the shell for its commands or query sqlite_schema through the API.
- Prevent it next time
- Keep tool syntax separate from application SQL.
A database remains locked after a screen closes.
- Why it happens
- An owned connection, cursor, or transaction outlived its intended work.
- How to fix it
- Inspect ownership and finish resources under the host API contract.
- Prevent it next time
- Test cleanup after both success and failure.
Check your understanding
1. Two ordinary :memory: connections are opened. Why can the second not see a table created by the first?
Show worked solution
They are separate transient databases, despite using the same special name. Reuse the intended connection for this exercise or choose an explicitly designed shared configuration. Do not treat a new connection as reopening durable storage.
2. A shell reports one SQLite version while Python reports another. Which governs a Python feature?
Show worked solution
The version and build options of the library used by Python govern that connection. Query sqlite_version() there and check the required feature. The shell is a separate executable and may link another library.
Key takeaways
- SQLite executes inside its host application.
- Connection identity and lifetime determine private-memory visibility.
- Tool commands, engine SQL, and runtime capabilities are separate contracts.