Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

SQLite3 - Reading

Learning Goals

By the end of this section you will:

  • know how to execute SQL Query statements in Python using SQLite3

  • understand what SQL injection attacks are and how they occur

  • know how to use parameterised queries to prevent SQL injection attacks

Once you have create a connection and cursor, there are two steps to running a query in using sqlite3 and Python:

  1. Excute the SQL query statement

  2. Fetch the results

Queries without variables

Execute the SQL query statement

The SQL query statement is provided as a string. Below is an example of how I format the code using a multiline string. It makes it easier to read, and, therefore, increases maintainability.

1
2
3
4
5
6
cursor.execute(
    """
    SELECT name, phone_num
    FROM customers
    """
)

Fetch the results

Once the query has been executed, you need to fetch the results that are stored in the cursor. You have three fetching options:

  1. fetchall() - returns all the results

  2. fetchone() - returns the next result

  3. fetchmany(size) - returns the size number of rows

1
2
3
4
5
6
7
8
cursor.execute(
    """
    SELECT name, phone_num
    FROM Customers
    """
)

reults = cursor.fetchall()

Full example

Here is a full example of running a simple SQL Query. Note the layout and use of comments to structure our Datastore class.

Datastore

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import sqlite3

class Datastore:

    def __init__(self):
        self.connection = sqlite3.connect("data/chinook.db")
        self.cursor = self.connection.cursor()

    def close(self):
        self.connection.close()

    # --- GET METHODS

    def get_customers_contact(self):
        # Execute SQL Statement
        self.cursor.execute(
            """
            SELECT FirstName, LastName, Email, Phone
            FROM customers
            """
        )

        # Retrieve Results
        results = self.cursor.fetchall()

        # Process Results

        # Return Results
        return results

Test

Here is the code for test.py. When you run it, notice the stucture of the returned values.

1
2
3
4
5
6
from datastore import Datastore
from pprint import pprint

database = Datastore()

pprint(database.get_customers_contact())

Queries with variables

SQL queries with hardcoded variable provide a limited function. They may be useful for retrieving lists, but any filtering becomes a nightmare. If you want the details of each specific customer, you would have write a method for every single customer. Therefore we need functions that accept variables, but methods incorporate variables into SQL statements are open to malicious attacks called SQL injections.

SQL Injections

Bobby tables

Despite industry addressing SQL injections (SQLi) for decades, they are still one of the most exploited security flaws. This makes them worthwhile spending a bit of time understanding them.

The following Computerphile videos explain SQLi and provide an example of an SQLi in action.

Now we understand the threat of SQLi and how to use parameterised queries to prevent them, it is time to learn how to do this in Python.

Creating Parameterised Queries

We will be using the dictionary sqlite3 method of parameterising our queries.To do this slqite3 uses additional arguements in the execute command. The first argument of the execute command is still a string with one minor change. Where we want to place a variable

Add the code below to the bottom of the Datastore class.

33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
    def get_customer_contact(self, customer_id):
        # Execute SQL Statement
        self.cursor.execute(
            """
            SELECT FirstName, LastName, Email, Phone
            FROM customers
            WHERE CustomerId = :customer_id
            """,
            {
                "customer_id": customer_id
            }
        )

        # Retrieve Results
        results = self.cursor.fetchall()

        # Process Results

        # Return Results
        return results