Repository: microsoft/Qcodes

Issue:
Potential SQL bug
I guess this method is unused but it looks wrong to me



Discussion:
# [Codecov](https://codecov.io/gh/QCoDeS/Qcodes/pull/1001?src=pr&el=h1) Report
> Merging [#1001](https://codecov.io/gh/QCoDeS/Qcodes/pull/1001?src=pr&el=desc) into [master](https://codecov.io/gh/QCoDeS/Qcodes/commit/f7a44692343d296470964aa0efe58cf0cc23599b?src=pr&el=desc) will **not change** coverage.
> The diff coverage is `0%`.


```diff
@@           Coverage Diff           @@
##           master    #1001   +/-   ##
=======================================
  Coverage   79.12%   79.12%           
=======================================
  Files          45       45           
  Lines        6425     6425           
=======================================
  Hits         5084     5084           
  Misses       1341     1341
```




It's being used in `get_dependents`, but it's probably better to remove `one_column` entirely and just use `many_many`.

Resolving patch:
diff --git a/qcodes/dataset/data_set.py b/qcodes/dataset/data_set.py
--- a/qcodes/dataset/data_set.py
+++ b/qcodes/dataset/data_set.py
@@ -19,7 +19,7 @@
 import qcodes.config
 from qcodes.dataset.param_spec import ParamSpec
 from qcodes.instrument.parameter import _BaseParameter
-from qcodes.dataset.sqlite_base import (atomic, atomicTransaction,
+from qcodes.dataset.sqlite_base import (atomic, atomic_transaction,
                                         transaction, add_parameter,
                                         connect, create_run, get_parameters,
                                         get_experiments,
@@ -102,7 +102,7 @@ def __init__(self, dataSet, sub_id: str,
         BEGIN
             SELECT {self.callbackid}({param_sql});
         END;"""
-        atomicTransaction(self.conn, sql)
+        atomic_transaction(self.conn, sql)
         self.data: Queue = Queue()
         self._data_set_len = len(dataSet)
         super().__init__()
@@ -205,7 +205,7 @@ def number_of_results(self):
         tabnam = self.table_name
         # TODO: is it better/faster to use the max index?
         sql = f'SELECT COUNT(*) FROM "{tabnam}"'
-        cursor = atomicTransaction(self.conn, sql)
+        cursor = atomic_transaction(self.conn, sql)
         return one(cursor, 'COUNT(*)')
 
     @property
@@ -575,7 +575,7 @@ def unsubscribe_all(self):
         Remove all subscribers
         """
         sql = "select * from sqlite_master where type = 'trigger';"
-        triggers = atomicTransaction(self.conn, sql).fetchall()
+        triggers = atomic_transaction(self.conn, sql).fetchall()
         with atomic(self.conn):
             for trigger in triggers:
                 self._remove_trigger(trigger['name'])
diff --git a/qcodes/dataset/sqlite_base.py b/qcodes/dataset/sqlite_base.py
--- a/qcodes/dataset/sqlite_base.py
+++ b/qcodes/dataset/sqlite_base.py
@@ -126,23 +126,6 @@ def one(curr: sqlite3.Cursor, column: Union[int, str]) -> Any:
         return res[0][column]
 
 
-# TODO: This is just a special case of many_many, isn't it?
-def one_column(curr: sqlite3.Cursor, column: str) -> List[Any]:
-    """
-    Get the value of one column, all its rows
-
-    Args:
-        curr: cursor to operate on
-        column: name of the column
-
-    Returns:
-        the values
-    """
-    res = curr.fetchall()
-    res = [r[0] for r in res]
-    return res
-
-
 def many(curr: sqlite3.Cursor, *columns: str) -> List[Any]:
     """Get the values of many columns from one row
     Args:
@@ -236,7 +219,7 @@ def transaction(conn: sqlite3.Connection,
     return c
 
 
-def atomicTransaction(conn: sqlite3.Connection,
+def atomic_transaction(conn: sqlite3.Connection,
                       sql: str, *args: Any) -> sqlite3.Cursor:
     """Perform an **atomic** transaction.
     The transaction is committed if there are no exceptions else the
@@ -301,12 +284,22 @@ def insert_column(conn: sqlite3.Connection, table: str, name: str,
         name: column name
         type: sqlite type of the column
     """
-    if paramtype:
-        transaction(conn,
-                    f'ALTER TABLE "{table}" ADD COLUMN "{name}" {paramtype}')
-    else:
-        transaction(conn,
-                    f'ALTER TABLE "{table}" ADD COLUMN "{name}"')
+    # first check that the column is not already there
+    # and do nothing if it is
+    query = f'PRAGMA TABLE_INFO("{table}");'
+    cur = atomic_transaction(conn, query)
+    columns = many_many(cur, "name")
+    if name in [col[0] for col in columns]:
+        return
+
+    with atomic(conn):
+        if paramtype:
+            transaction(conn,
+                        f'ALTER TABLE "{table}" ADD COLUMN "{name}" '
+                        f'{paramtype}')
+        else:
+            transaction(conn,
+                        f'ALTER TABLE "{table}" ADD COLUMN "{name}"')
 
 
 def select_one_where(conn: sqlite3.Connection, table: str, column: str,
@@ -318,7 +311,7 @@ def select_one_where(conn: sqlite3.Connection, table: str, column: str,
     WHERE
         {where_column} = ?
     """
-    cur = atomicTransaction(conn, query, where_value)
+    cur = atomic_transaction(conn, query, where_value)
     res = one(cur, column)
     return res
 
@@ -333,7 +326,7 @@ def select_many_where(conn: sqlite3.Connection, table: str, *columns: str,
     WHERE
         {where_column} = ?
     """
-    cur = atomicTransaction(conn, query, where_value)
+    cur = atomic_transaction(conn, query, where_value)
     res = many(cur, *columns)
     return res
 
@@ -361,7 +354,7 @@ def update_where(conn: sqlite3.Connection, table: str,
     WHERE
         {where_column} = ?
     """
-    transaction(conn, query, *values, where_value)
+    atomic_transaction(conn, query, *values, where_value)
 
 
 def insert_values(conn: sqlite3.Connection,
@@ -381,7 +374,8 @@ def insert_values(conn: sqlite3.Connection,
     VALUES
         ({_values})
     """
-    c = transaction(conn, query, *values)
+
+    c = atomic_transaction(conn, query, *values)
     return c.lastrowid
 
 
@@ -448,7 +442,9 @@ def insert_many_values(conn: sqlite3.Connection,
         # we need to make values a flat list from a list of list
         flattened_values = [item for sublist in values[start:stop]
                             for item in sublist]
-        c = transaction(conn, query, *flattened_values)
+
+        c = atomic_transaction(conn, query, *flattened_values)
+
         if ii == 0:
             return_value = c.lastrowid
         start += chunk
@@ -479,7 +475,7 @@ def modify_values(conn: sqlite3.Connection,
     WHERE
         rowid = {index+1}
     """
-    c = transaction(conn, query, *values)
+    c = atomic_transaction(conn, query, *values)
     return c.rowcount
 
 
@@ -523,7 +519,7 @@ def length(conn: sqlite3.Connection,
         the lenght of the table
     """
     query = f"select MAX(id) from '{formatted_name}'"
-    c = atomicTransaction(conn, query)
+    c = atomic_transaction(conn, query)
     _len = c.fetchall()[0][0]
     if _len is None:
         return 0
@@ -580,7 +576,7 @@ def get_data(conn: sqlite3.Connection,
         SELECT {_columns}
         FROM "{table_name}"
         """
-    c = transaction(conn, query)
+    c = atomic_transaction(conn, query)
     res = many_many(c, *columns)
 
     return res
@@ -604,7 +600,7 @@ def get_values(conn: sqlite3.Connection,
     SELECT {param_name} FROM "{table_name}"
     WHERE {param_name} IS NOT NULL
     """
-    c = transaction(conn, sql)
+    c = atomic_transaction(conn, sql)
     res = many_many(c, param_name)
 
     return res
@@ -633,7 +629,7 @@ def get_setpoints(conn: sqlite3.Connection,
     sql = """
     SELECT run_id FROM runs WHERE result_table_name = ?
     """
-    c = transaction(conn, sql, table_name)
+    c = atomic_transaction(conn, sql, table_name)
     run_id = one(c, 'run_id')
 
     # get the parameter layout id
@@ -642,7 +638,7 @@ def get_setpoints(conn: sqlite3.Connection,
     WHERE parameter = ?
     and run_id = ?
     """
-    c = transaction(conn, sql, param_name, run_id)
+    c = atomic_transaction(conn, sql, param_name, run_id)
     layout_id = one(c, 'layout_id')
 
     # get the setpoint layout ids
@@ -650,7 +646,7 @@ def get_setpoints(conn: sqlite3.Connection,
     SELECT independent FROM dependencies
     WHERE dependent = ?
     """
-    c = transaction(conn, sql, layout_id)
+    c = atomic_transaction(conn, sql, layout_id)
     indeps = many_many(c, 'independent')
     indeps = [idp[0] for idp in indeps]
 
@@ -659,7 +655,7 @@ def get_setpoints(conn: sqlite3.Connection,
     SELECT parameter FROM layouts WHERE layout_id
     IN {str(indeps).replace('[', '(').replace(']', ')')}
     """
-    c = transaction(conn, sql)
+    c = atomic_transaction(conn, sql)
     setpoint_names_temp = many_many(c, 'parameter')
     setpoint_names = [spn[0] for spn in setpoint_names_temp]
     setpoint_names = cast(List[str], setpoint_names)
@@ -672,7 +668,7 @@ def get_setpoints(conn: sqlite3.Connection,
         FROM "{table_name}"
         WHERE {param_name} IS NOT NULL
         """
-        c = transaction(conn, sql)
+        c = atomic_transaction(conn, sql)
         sps = many_many(c, sp_name)
         output.append(sps)
 
@@ -694,12 +690,44 @@ def get_layout(conn: sqlite3.Connection,
     sql = """
     SELECT parameter, label, unit FROM layouts WHERE layout_id=?
     """
-    c = transaction(conn, sql, layout_id)
+    c = atomic_transaction(conn, sql, layout_id)
     t_res = many(c, 'parameter', 'label', 'unit')
     res = dict(zip(['name', 'label', 'unit'], t_res))
     return res
 
 
+def get_layout_id(conn: sqlite3.Connection,
+                  parameter: Union[ParamSpec, str],
+                  run_id: int) -> int:
+    """
+    Get the layout id of a parameter in a given run
+
+    Args:
+        conn: The database connection
+        parameter: A ParamSpec or the name of the parameter
+        run_id: The run_id of the run in question
+    """
+    # get the parameter layout id
+    sql = """
+    SELECT layout_id FROM layouts
+    WHERE parameter = ?
+    and run_id = ?
+    """
+
+    if isinstance(parameter, ParamSpec):
+        name = parameter.name
+    elif isinstance(parameter, str):
+        name = parameter
+    else:
+        raise ValueError('Wrong parameter type, must be ParamSpec or str, '
+                         f'received {type(parameter)}.')
+
+    c = atomic_transaction(conn, sql, name, run_id)
+    res = one(c, 'layout_id')
+
+    return res
+
+
 def get_dependents(conn: sqlite3.Connection,
                    run_id: int) -> List[int]:
     """
@@ -710,8 +738,8 @@ def get_dependents(conn: sqlite3.Connection,
     SELECT layout_id FROM layouts
     WHERE run_id=? and layout_id in (SELECT dependent FROM dependencies)
     """
-    c = transaction(conn, sql, run_id)
-    res = one_column(c, 'layout_id')
+    c = atomic_transaction(conn, sql, run_id)
+    res = [d[0] for d in many_many(c, 'layout_id')]
     return res
 
 
@@ -728,7 +756,7 @@ def get_dependencies(conn: sqlite3.Connection,
     sql = """
     SELECT independent, axis_num FROM dependencies WHERE dependent=?
     """
-    c = transaction(conn, sql, layout_id)
+    c = atomic_transaction(conn, sql, layout_id)
     res = many_many(c, 'independent', 'axis_num')
     return res
 
@@ -757,7 +785,7 @@ def new_experiment(conn: sqlite3.Connection,
     VALUES
         (?,?,?,?,?)
     """
-    curr = atomicTransaction(conn, query, name, sample_name,
+    curr = atomic_transaction(conn, query, name, sample_name,
                              time.time(), format_string, 0)
     return curr.lastrowid
 
@@ -778,7 +806,7 @@ def mark_run(conn: sqlite3.Connection, run_id: int, complete: bool):
         is_completed=?
     WHERE run_id=?;
     """
-    atomicTransaction(conn, query, time.time(), complete, run_id)
+    atomic_transaction(conn, query, time.time(), complete, run_id)
 
 
 def completed(conn: sqlite3.Connection, run_id)->bool:
@@ -802,7 +830,7 @@ def finish_experiment(conn: sqlite3.Connection, exp_id: int):
     query = """
     UPDATE experiments SET end_time=? WHERE exp_id=?;
     """
-    atomicTransaction(conn, query, time.time(), exp_id)
+    atomic_transaction(conn, query, time.time(), exp_id)
 
 
 def get_run_counter(conn: sqlite3.Connection, exp_id: int) -> int:
@@ -832,7 +860,8 @@ def get_experiments(conn: sqlite3.Connection) -> List[sqlite3.Row]:
     sql = """
     SELECT * FROM experiments
     """
-    c = transaction(conn, sql)
+    c = atomic_transaction(conn, sql)
+
     return c.fetchall()
 
 
@@ -841,7 +870,7 @@ def get_last_experiment(conn: sqlite3.Connection) -> int:
     Return last started experiment id
     """
     query = "SELECT MAX(exp_id) FROM experiments"
-    c = atomicTransaction(conn, query)
+    c = atomic_transaction(conn, query)
     return c.fetchall()[0][0]
 
 
@@ -855,17 +884,19 @@ def get_runs(conn: sqlite3.Connection,
     Returns:
         list of rows
     """
-    if exp_id:
-        sql = """
-        SELECT * FROM runs
-        where exp_id = ?
-        """
-        c = transaction(conn, sql, exp_id)
-    else:
-        sql = """
-        SELECT * FROM runs
-        """
-        c = transaction(conn, sql)
+    with atomic(conn):
+        if exp_id:
+            sql = """
+            SELECT * FROM runs
+            where exp_id = ?
+            """
+            c = transaction(conn, sql, exp_id)
+        else:
+            sql = """
+            SELECT * FROM runs
+            """
+            c = transaction(conn, sql)
+
     return c.fetchall()
 
 
@@ -875,7 +906,7 @@ def get_last_run(conn: sqlite3.Connection, exp_id: int) -> str:
     FROM runs
     WHERE exp_id = ?;
     """
-    c = transaction(conn, query, exp_id)
+    c = atomic_transaction(conn, query, exp_id)
     return one(c, 'run_id')
 
 
@@ -890,7 +921,7 @@ def data_sets(conn: sqlite3.Connection) -> List[sqlite3.Row]:
     sql = """
     SELECT * FROM runs
     """
-    c = transaction(conn, sql)
+    c = atomic_transaction(conn, sql)
     return c.fetchall()
 
 
@@ -907,39 +938,42 @@ def _insert_run(conn: sqlite3.Connection, exp_id: int, name: str,
     run_counter += 1
     formatted_name = format_string.format(name, exp_id, run_counter)
     table = "runs"
-    if parameters:
-        query = f"""
-        INSERT INTO {table}
-            (name,exp_id,result_table_name,result_counter,run_timestamp,parameters,is_completed)
-        VALUES
-            (?,?,?,?,?,?,?)
-        """
-        curr = transaction(conn, query,
-                           name,
-                           exp_id,
-                           formatted_name,
-                           run_counter,
-                           time.time(),
-                           ",".join([p.name for p in parameters]),
-                           False
-                           )
-        _add_parameters_to_layout_and_deps(conn, formatted_name, *parameters)
+    with atomic(conn):
+
+        if parameters:
+            query = f"""
+            INSERT INTO {table}
+                (name,exp_id,result_table_name,result_counter,run_timestamp,parameters,is_completed)
+            VALUES
+                (?,?,?,?,?,?,?)
+            """
+            curr = transaction(conn, query,
+                               name,
+                               exp_id,
+                               formatted_name,
+                               run_counter,
+                               time.time(),
+                               ",".join([p.name for p in parameters]),
+                               False)
+
+            _add_parameters_to_layout_and_deps(conn, formatted_name,
+                                               *parameters)
+
+        else:
+            query = f"""
+            INSERT INTO {table}
+                (name,exp_id,result_table_name,result_counter,run_timestamp,is_completed)
+            VALUES
+                (?,?,?,?,?,?)
+            """
+            curr = transaction(conn, query,
+                               name,
+                               exp_id,
+                               formatted_name,
+                               run_counter,
+                               time.time(),
+                               False)
 
-    else:
-        query = f"""
-        INSERT INTO {table}
-            (name,exp_id,result_table_name,result_counter,run_timestamp,is_completed)
-        VALUES
-            (?,?,?,?,?,?)
-        """
-        curr = transaction(conn, query,
-                           name,
-                           exp_id,
-                           formatted_name,
-                           run_counter,
-                           time.time(),
-                           False
-                           )
     return run_counter, formatted_name, curr.lastrowid
 
 
@@ -950,7 +984,7 @@ def _update_experiment_run_counter(conn: sqlite3.Connection, exp_id: int,
     SET run_counter = ?
     WHERE exp_id = ?
     """
-    transaction(conn, query, run_counter, exp_id)
+    atomic_transaction(conn, query, run_counter, exp_id)
 
 
 def get_parameters(conn: sqlite3.Connection,
@@ -1072,14 +1106,16 @@ def add_parameter(conn: sqlite3.Connection,
         SELECT parameters FROM runs
         WHERE result_table_name=?
         """
-        c = transaction(conn, sql, formatted_name)
+        with atomic(conn):
+            c = transaction(conn, sql, formatted_name)
         old_parameters = one(c, 'parameters')
         if old_parameters:
             new_parameters = ",".join([old_parameters] + p_names)
         else:
             new_parameters = ",".join(p_names)
         sql = "UPDATE runs SET parameters=? WHERE result_table_name=?"
-        transaction(conn, sql, new_parameters, formatted_name)
+        with atomic(conn):
+            transaction(conn, sql, new_parameters, formatted_name)
 
         # Update the layouts table
         c = _add_parameters_to_layout_and_deps(conn, formatted_name,
@@ -1107,26 +1143,33 @@ def _add_parameters_to_layout_and_deps(conn: sqlite3.Connection,
     INSERT INTO layouts (run_id, parameter, label, unit, inferred_from)
     VALUES {placeholder}
     """
-    c = transaction(conn, sql, *layout_args)
-    layout_id = c.lastrowid
 
-    # TODO: how to manage the axis_num?
-    for p in parameter:
-        if p.depends_on != '':
-            deps = p.depends_on.split(', ')
-            for ax_num, dp in enumerate(deps):
-                sql = """
-                SELECT layout_id FROM layouts
-                WHERE run_id=? and parameter=?;
-                """
-                c = transaction(conn, sql, run_id, dp)
-                dep_ind = one(c, 'layout_id')
-
-                sql = """
-                INSERT INTO dependencies (dependent, independent, axis_num)
-                VALUES (?,?,?)
-                """
-                c = transaction(conn, sql, layout_id, dep_ind, ax_num)
+    with atomic(conn):
+        c = transaction(conn, sql, *layout_args)
+
+        for p in parameter:
+
+            if p.depends_on != '':
+
+                layout_id = get_layout_id(conn, p, run_id)
+
+                deps = p.depends_on.split(', ')
+                for ax_num, dp in enumerate(deps):
+
+                    sql = """
+                    SELECT layout_id FROM layouts
+                    WHERE run_id=? and parameter=?;
+                    """
+
+                    c = transaction(conn, sql, run_id, dp)
+                    dep_ind = one(c, 'layout_id')
+
+                    sql = """
+                    INSERT INTO dependencies (dependent, independent, axis_num)
+                    VALUES (?,?,?)
+                    """
+
+                    c = transaction(conn, sql, layout_id, dep_ind, ax_num)
     return c
 
 
@@ -1140,7 +1183,6 @@ def _validate_table_name(table_name: str) -> bool:
     return valid
 
 
-# (WilliamHPNielsen) This creates a result table, right?
 def _create_run_table(conn: sqlite3.Connection,
                       formatted_name: str,
                       parameters: Optional[List[ParamSpec]] = None,
@@ -1148,41 +1190,42 @@ def _create_run_table(conn: sqlite3.Connection,
                       ) -> None:
     """Create run table with formatted_name as name
 
-    NOTE this need to be committed before closing the connection.
-
     Args:
         conn: database connection
         formatted_name: the name of the table to create
     """
     _validate_table_name(formatted_name)
-    if parameters and values:
-        _parameters = ",".join([p.sql_repr() for p in parameters])
-        query = f"""
-        CREATE TABLE "{formatted_name}" (
-            id INTEGER PRIMARY KEY,
-            {_parameters}
-        );
-        """
-        transaction(conn, query)
-        # now insert values
-        insert_values(conn, formatted_name,
-                      [p.name for p in parameters], values)
-    elif parameters:
-        _parameters = ",".join([p.sql_repr() for p in parameters])
-        query = f"""
-        CREATE TABLE "{formatted_name}" (
-            id INTEGER PRIMARY KEY,
-            {_parameters}
-        );
-        """
-        transaction(conn, query)
-    else:
-        query = f"""
-        CREATE TABLE "{formatted_name}" (
-            id INTEGER PRIMARY KEY
-        );
-        """
-        transaction(conn, query)
+
+    with atomic(conn):
+
+        if parameters and values:
+            _parameters = ",".join([p.sql_repr() for p in parameters])
+            query = f"""
+            CREATE TABLE "{formatted_name}" (
+                id INTEGER PRIMARY KEY,
+                {_parameters}
+            );
+            """
+            transaction(conn, query)
+            # now insert values
+            insert_values(conn, formatted_name,
+                          [p.name for p in parameters], values)
+        elif parameters:
+            _parameters = ",".join([p.sql_repr() for p in parameters])
+            query = f"""
+            CREATE TABLE "{formatted_name}" (
+                id INTEGER PRIMARY KEY,
+                {_parameters}
+            );
+            """
+            transaction(conn, query)
+        else:
+            query = f"""
+            CREATE TABLE "{formatted_name}" (
+                id INTEGER PRIMARY KEY
+            );
+            """
+            transaction(conn, query)
 
 
 def create_run(conn: sqlite3.Connection, exp_id: int, name: str,
@@ -1208,15 +1251,16 @@ def create_run(conn: sqlite3.Connection, exp_id: int, name: str,
         - run_id: the row id of the newly created run
         - formatted_name: the name of the newly created table
     """
-    with atomic(conn):
-        run_counter, formatted_name, run_id = _insert_run(conn,
-                                                          exp_id,
-                                                          name,
-                                                          parameters)
-        if metadata:
-            add_meta_data(conn, run_id, metadata)
-        _update_experiment_run_counter(conn, exp_id, run_counter)
-        _create_run_table(conn, formatted_name, parameters, values)
+
+    run_counter, formatted_name, run_id = _insert_run(conn,
+                                                      exp_id,
+                                                      name,
+                                                      parameters)
+    if metadata:
+        add_meta_data(conn, run_id, metadata)
+    _update_experiment_run_counter(conn, exp_id, run_counter)
+    _create_run_table(conn, formatted_name, parameters, values)
+
     return run_counter, run_id, formatted_name
 
 
@@ -1283,11 +1327,11 @@ def add_meta_data(conn: sqlite3.Connection,
 
 def get_user_version(conn: sqlite3.Connection) -> int:
 
-    curr = atomicTransaction(conn, 'PRAGMA user_version')
+    curr = atomic_transaction(conn, 'PRAGMA user_version')
     res = one(curr, 0)
     return res
 
 
 def set_user_version(conn: sqlite3.Connection, version: int) -> None:
 
-    atomicTransaction(conn, 'PRAGMA user_version({})'.format(version))
+    atomic_transaction(conn, 'PRAGMA user_version({})'.format(version))


Test patch:
diff --git a/qcodes/tests/dataset/test_dataset_basic.py b/qcodes/tests/dataset/test_dataset_basic.py
--- a/qcodes/tests/dataset/test_dataset_basic.py
+++ b/qcodes/tests/dataset/test_dataset_basic.py
@@ -7,7 +7,7 @@
 from qcodes import load_by_id, load_by_counter
 from qcodes.dataset.sqlite_base import connect, init_db, _unicode_categories
 import qcodes.dataset.data_set
-from qcodes.dataset.sqlite_base import get_user_version, set_user_version, atomicTransaction
+from qcodes.dataset.sqlite_base import get_user_version, set_user_version, atomic_transaction
 from qcodes.dataset.data_set import CompletedError
 from qcodes.dataset.database import initialise_database
 
@@ -328,7 +328,7 @@ def test_database_upgrade(empty_temp_db):
                            " {}".format(userversion))
     sql = 'ALTER TABLE "runs" ADD COLUMN "quality"'
 
-    atomicTransaction(connection, sql)
+    atomic_transaction(connection, sql)
     set_user_version(connection, 1)
 
 
diff --git a/qcodes/tests/dataset/test_sqlite_base.py b/qcodes/tests/dataset/test_sqlite_base.py
--- a/qcodes/tests/dataset/test_sqlite_base.py
+++ b/qcodes/tests/dataset/test_sqlite_base.py
@@ -12,6 +12,8 @@
 
 import qcodes as qc
 import qcodes.dataset.sqlite_base as mut  # mut: module under test
+from qcodes.dataset.database import initialise_database
+from qcodes.dataset.param_spec import ParamSpec
 
 _unicode_categories = ('Lu', 'Ll', 'Lt', 'Lm', 'Lo', 'Nd', 'Pc', 'Pd', 'Zs')
 
@@ -21,16 +23,8 @@ def empty_temp_db():
     # create a temp database for testing
     with tempfile.TemporaryDirectory() as tmpdirname:
         qc.config["core"]["db_location"] = os.path.join(tmpdirname, 'temp.db')
-        qc.config["core"]["db_debug"] = False
-        # this is somewhat annoying but these module scope variables
-        # are initialized at import time so they need to be overwritten
-        qc.dataset.experiment_container.DB = qc.config["core"]["db_location"]
-        qc.dataset.data_set.DB = qc.config["core"]["db_location"]
-        qc.dataset.experiment_container.debug_db = qc.config["core"]["db_debug"]
-        _c = mut.connect(qc.config["core"]["db_location"],
-                         qc.config["core"]["db_debug"])
-        mut.init_db(_c)
-        _c.close()
+        qc.config["core"]["db_debug"] = True
+        initialise_database()
         yield
 
 
@@ -48,13 +42,13 @@ def test_one_raises(experiment):
         mut.one(conn.cursor(), column='Something_you_dont_have')
 
 
-def test_atomicTransaction_raises(experiment):
+def test_atomic_transaction_raises(experiment):
     conn = experiment.conn
 
     bad_sql = '""'
 
     with pytest.raises(OperationalError):
-        mut.atomicTransaction(conn, bad_sql)
+        mut.atomic_transaction(conn, bad_sql)
 
 
 def test_atomic_raises(experiment):
@@ -90,3 +84,42 @@ def test__validate_table_raises(table_name):
             mut._validate_table_name(table_name)
     else:
         assert mut._validate_table_name(table_name)
+
+
+def test_get_dependents(experiment):
+
+    x = ParamSpec('x', 'numeric')
+    t = ParamSpec('t', 'numeric')
+    y = ParamSpec('y', 'numeric', depends_on=['x', 't'])
+
+    # Make a dataset
+    (_, run_id, _) = mut.create_run(experiment.conn,
+                                    experiment.exp_id,
+                                    name='testrun',
+                                    parameters=[x, t, y])
+
+    deps = mut.get_dependents(experiment.conn, run_id)
+
+    layout_id = mut.get_layout_id(experiment.conn,
+                                  'y', run_id)
+
+    assert deps == [layout_id]
+
+    # more parameters, more complicated dependencies
+
+    x_raw = ParamSpec('x_raw', 'numeric')
+    x_cooked = ParamSpec('x_cooked', 'numeric', inferred_from=['x_raw'])
+    z = ParamSpec('z', 'numeric', depends_on=['x_cooked'])
+
+    (_, run_id, _) = mut.create_run(experiment.conn,
+                                    experiment.exp_id,
+                                    name='testrun',
+                                    parameters=[x, t, x_raw,
+                                                x_cooked, y, z])
+
+    deps = mut.get_dependents(experiment.conn, run_id)
+
+    expected_deps = [mut.get_layout_id(experiment.conn, 'y', run_id),
+                     mut.get_layout_id(experiment.conn, 'z', run_id)]
+
+    assert deps == expected_deps