def test_atomic_on_connection_plus_that_is_in_progress(in_transaction):
    sqlite_conn = sqlite3.connect(':memory:')
    conn_plus = ConnectionPlus(sqlite_conn)

    # explicitly set to True for testing purposes
    conn_plus.atomic_in_progress = True

    # implement parametrizing over connection's `in_transaction` attribute
    if in_transaction:
        conn_plus.cursor().execute('BEGIN')
    assert in_transaction is conn_plus.in_transaction

    isolation_level = conn_plus.isolation_level
    in_transaction = conn_plus.in_transaction

    with atomic(conn_plus) as atomic_conn:
        assert True is conn_plus.atomic_in_progress
        assert isolation_level == conn_plus.isolation_level
        assert in_transaction is conn_plus.in_transaction

        assert True is atomic_conn.atomic_in_progress
        assert isolation_level == atomic_conn.isolation_level
        assert in_transaction is atomic_conn.in_transaction

    assert True is conn_plus.atomic_in_progress
    assert isolation_level == conn_plus.isolation_level
    assert in_transaction is conn_plus.in_transaction

    assert True is atomic_conn.atomic_in_progress
    assert isolation_level == atomic_conn.isolation_level
    assert in_transaction is atomic_conn.in_transaction
Example #2
0
def _populate_results_table(source_conn: ConnectionPlus,
                            target_conn: ConnectionPlus,
                            source_table_name: str,
                            target_table_name: str) -> None:
    """
    Copy over all the entries of the results table
    """
    get_data_query = f"""
                     SELECT *
                     FROM "{source_table_name}"
                     """

    source_cursor = source_conn.cursor()
    target_cursor = target_conn.cursor()

    for row in source_cursor.execute(get_data_query):
        column_names = ','.join(row.keys()[1:])  # the first key is "id"
        values = tuple(val for val in row[1:])
        value_placeholders = sql_placeholder_string(len(values))
        insert_data_query = f"""
                             INSERT INTO "{target_table_name}"
                             ({column_names})
                             values {value_placeholders}
                             """
        target_cursor.execute(insert_data_query, values)
Example #3
0
def connect(name: Union[str, Path], debug: bool = False,
            version: int = -1) -> ConnectionPlus:
    """
    Connect or create  database. If debug the queries will be echoed back.
    This function takes care of registering the numpy/sqlite type
    converters that we need.

    Args:
        name: name or path to the sqlite file
        debug: whether or not to turn on tracing
        version: which version to create. We count from 0. -1 means 'latest'.
            Should always be left at -1 except when testing.

    Returns:
        conn: connection object to the database (note, it is
            `ConnectionPlus`, not `sqlite3.Connection`

    """
    # register numpy->binary(TEXT) adapter
    sqlite3.register_adapter(np.ndarray, _adapt_array)
    # register binary(TEXT) -> numpy converter
    sqlite3.register_converter("array", _convert_array)

    sqlite3_conn = sqlite3.connect(name, detect_types=sqlite3.PARSE_DECLTYPES,
                                   check_same_thread=True)
    conn = ConnectionPlus(sqlite3_conn)

    latest_supported_version = _latest_available_version()
    db_version = get_user_version(conn)

    if db_version > latest_supported_version:
        raise RuntimeError(f"Database {name} is version {db_version} but this "
                           f"version of QCoDeS supports up to "
                           f"version {latest_supported_version}")

    # sqlite3 options
    conn.row_factory = sqlite3.Row

    # Make sure numpy ints and floats types are inserted properly
    for numpy_int in numpy_ints:
        sqlite3.register_adapter(numpy_int, int)

    sqlite3.register_converter("numeric", _convert_numeric)

    for numpy_float in (float,) + numpy_floats:
        sqlite3.register_adapter(numpy_float, _adapt_float)

    for complex_type in complex_types:
        sqlite3.register_adapter(complex_type, _adapt_complex)
    sqlite3.register_converter("complex", _convert_complex)

    if debug:
        conn.set_trace_callback(print)

    init_db(conn)
    perform_db_upgrade(conn, version=version)
    return conn
def test_atomic_on_outmost_connection_that_is_in_transaction():
    conn = ConnectionPlus(sqlite3.connect(':memory:'))

    conn.execute('BEGIN')
    assert True is conn.in_transaction

    match_str = re.escape('SQLite connection has uncommitted transactions. '
                          'Please commit those before starting an atomic '
                          'transaction.')
    with pytest.raises(RuntimeError, match=match_str):
        with atomic(conn):
            pass
def test_connection_plus():
    sqlite_conn = sqlite3.connect(':memory:')
    conn_plus = ConnectionPlus(sqlite_conn)

    assert isinstance(conn_plus, ConnectionPlus)
    assert isinstance(conn_plus, sqlite3.Connection)
    assert False is conn_plus.atomic_in_progress

    match_str = re.escape('Attempted to create `ConnectionPlus` from a '
                          '`ConnectionPlus` object which is not allowed.')
    with pytest.raises(ValueError, match=match_str):
        ConnectionPlus(conn_plus)
Example #6
0
def _update_run_description(conn: ConnectionPlus, run_id: int,
                            description: str) -> None:
    """
    Update the run_description field for the given run_id. The description
    string is NOT validated.
    """
    sql = """
          UPDATE runs
          SET run_description = ?
          WHERE run_id = ?
          """
    with atomic(conn) as conn:
        conn.cursor().execute(sql, (description, run_id))
Example #7
0
def set_run_timestamp(conn: ConnectionPlus, run_id: int) -> None:
    """
    Set the run_timestamp for the run with the given run_id. If the
    run_timestamp has already been set, a RuntimeError is raised.
    """

    query = """
            SELECT run_timestamp
            FROM runs
            WHERE run_id = ?
            """
    cmd = """
          UPDATE runs
          SET run_timestamp = ?
          WHERE run_id = ?
          """

    with atomic(conn) as conn:
        c = conn.cursor()
        timestamp = one(c.execute(query, (run_id, )), 'run_timestamp')
        if timestamp is not None:
            raise RuntimeError('Can not set run_timestamp; it has already '
                               f'been set to: {timestamp}')
        else:
            current_time = time.time()
            c.execute(cmd, (current_time, run_id))
            log.info(f"Set the run_timestamp of run_id {run_id} to "
                     f"{current_time}")
Example #8
0
def get_runid_from_guid(conn: ConnectionPlus, guid: str) -> Union[int, None]:
    """
    Get the run_id of a run based on the guid

    Args:
        conn: connection to the database
        guid: the guid to look up

    Returns:
        The run_id if found, else -1.

    Raises:
        RuntimeError if more than one run with the given    GUID exists
    """
    query = """
            SELECT run_id
            FROM runs
            WHERE guid = ?
            """
    cursor = conn.cursor()
    cursor.execute(query, (guid, ))
    rows = cursor.fetchall()
    if len(rows) == 0:
        run_id = -1
    elif len(rows) > 1:
        errormssg = ('Critical consistency error: multiple runs with'
                     f' the same GUID found! {len(rows)} runs have GUID '
                     f'{guid}')
        log.critical(errormssg)
        raise RuntimeError(errormssg)
    else:
        run_id = int(rows[0]['run_id'])

    return run_id
def test_make_connection_plus_from_connecton_plus():
    conn = ConnectionPlus(sqlite3.connect(':memory:'))
    conn_plus = make_connection_plus_from(conn)

    assert isinstance(conn_plus, ConnectionPlus)
    assert conn.atomic_in_progress is conn_plus.atomic_in_progress
    assert conn_plus is conn
Example #10
0
def test_two_nested_atomics():
    sqlite_conn = sqlite3.connect(':memory:')
    conn_plus = ConnectionPlus(sqlite_conn)

    atomic_in_progress = conn_plus.atomic_in_progress
    isolation_level = conn_plus.isolation_level

    assert False is conn_plus.in_transaction

    with atomic(conn_plus) as atomic_conn_1:
        assert conn_plus_in_transaction(conn_plus)
        assert conn_plus_in_transaction(atomic_conn_1)

        with atomic(atomic_conn_1) as atomic_conn_2:
            assert conn_plus_in_transaction(conn_plus)
            assert conn_plus_in_transaction(atomic_conn_1)
            assert conn_plus_in_transaction(atomic_conn_2)

        assert conn_plus_in_transaction(conn_plus)
        assert conn_plus_in_transaction(atomic_conn_1)
        assert conn_plus_in_transaction(atomic_conn_2)

    assert conn_plus_is_idle(conn_plus, isolation_level)
    assert conn_plus_is_idle(atomic_conn_1, isolation_level)
    assert conn_plus_is_idle(atomic_conn_2, isolation_level)

    assert atomic_in_progress == conn_plus.atomic_in_progress
    assert atomic_in_progress == atomic_conn_1.atomic_in_progress
    assert atomic_in_progress == atomic_conn_2.atomic_in_progress
Example #11
0
def get_metadata_from_run_id(conn: ConnectionPlus, run_id: int) -> Dict:
    """
    Get all metadata associated with the specified run
    """
    # TODO: promote snapshot to be present at creation time
    non_metadata = RUNS_TABLE_COLUMNS + ['snapshot']

    metadata = {}
    possible_tags = []

    # first fetch all columns of the runs table
    query = "PRAGMA table_info(runs)"
    cursor = conn.cursor()
    for row in cursor.execute(query):
        if row['name'] not in non_metadata:
            possible_tags.append(row['name'])

    # and then fetch whatever metadata the run might have
    for tag in possible_tags:
        query = f"""
                SELECT "{tag}"
                FROM runs
                WHERE run_id = ?
                AND "{tag}" IS NOT NULL
                """
        cursor.execute(query, (run_id, ))
        row = cursor.fetchall()
        if row != []:
            metadata[tag] = row[0][tag]

    return metadata
Example #12
0
def is_run_id_in_database(conn: ConnectionPlus, *run_ids) -> Dict[int, bool]:
    """
    Look up run_ids and return a dictionary with the answers to the question
    "is this run_id in the database?"

    Args:
        conn: the connection to the database
        run_ids: the run_ids to look up

    Returns:
        a dict with the run_ids as keys and bools as values. True means that
        the run_id DOES exist in the database
    """
    run_ids = np.unique(run_ids)
    placeholders = sql_placeholder_string(len(run_ids))

    query = f"""
             SELECT run_id
             FROM runs
             WHERE run_id in {placeholders}
            """

    cursor = conn.cursor()
    cursor.execute(query, run_ids)
    rows = cursor.fetchall()
    existing_ids = [row[0] for row in rows]
    return {run_id: (run_id in existing_ids) for run_id in run_ids}
Example #13
0
def test_atomic():
    sqlite_conn = sqlite3.connect(':memory:')

    match_str = re.escape('atomic context manager only accepts ConnectionPlus '
                          'database connection objects.')
    with pytest.raises(ValueError, match=match_str):
        with atomic(sqlite_conn):
            pass

    conn_plus = ConnectionPlus(sqlite_conn)
    assert False is conn_plus.atomic_in_progress

    atomic_in_progress = conn_plus.atomic_in_progress
    isolation_level = conn_plus.isolation_level

    assert False is conn_plus.in_transaction

    with atomic(conn_plus) as atomic_conn:
        assert conn_plus_in_transaction(atomic_conn)
        assert conn_plus_in_transaction(conn_plus)

    assert isolation_level == conn_plus.isolation_level
    assert False is conn_plus.in_transaction
    assert atomic_in_progress is conn_plus.atomic_in_progress

    assert isolation_level == conn_plus.isolation_level
    assert False is atomic_conn.in_transaction
    assert atomic_in_progress is atomic_conn.atomic_in_progress
Example #14
0
def get_parameters(conn: ConnectionPlus, run_id: int) -> List[ParamSpec]:
    """
    Get the list of param specs for run

    Args:
        conn: the connection to the sqlite database
        run_id: The id of the run

    Returns:
        A list of param specs for this run
    """

    sql = f"""
    SELECT parameter FROM layouts WHERE run_id={run_id}
    """
    c = conn.execute(sql)
    param_names_temp = many_many(c, 'parameter')
    param_names = [p[0] for p in param_names_temp]
    param_names = cast(List[str], param_names)

    parspecs = []

    for param_name in param_names:
        parspecs.append(get_paramspec(conn, run_id, param_name))

    return parspecs
Example #15
0
def path_to_dbfile(conn: ConnectionPlus) -> str:
    """
    Return the path of the database file that the conn object is connected to
    """
    cursor = conn.cursor()
    cursor.execute("PRAGMA database_list")
    row = cursor.fetchall()[0]

    return row[2]
Example #16
0
def update_run_description(conn: ConnectionPlus, run_id: int,
                           description: str) -> None:
    """
    Update the run_description field for the given run_id. The description
    string must be a valid JSON string representation of a RunDescriber object
    """
    try:
        RunDescriber.from_json(description)
    except Exception as e:
        raise ValueError("Invalid description string. Must be a JSON string "
                         "representaion of a RunDescriber object.") from e

    sql = """
          UPDATE runs
          SET run_description = ?
          WHERE run_id = ?
          """
    with atomic(conn) as conn:
        conn.cursor().execute(sql, (description, run_id))
Example #17
0
def _2to3_get_result_tables(conn: ConnectionPlus) -> Dict[int, str]:
    rst_query = "SELECT run_id, result_table_name FROM runs"
    cur = conn.cursor()
    cur.execute(rst_query)

    data = cur.fetchall()
    cur.close()
    results = {}
    for row in data:
        results[row['run_id']] = row['result_table_name']
    return results
Example #18
0
def _rewrite_timestamps(target_conn: ConnectionPlus, target_run_id: int,
                        correct_run_timestamp: Optional[float],
                        correct_completed_timestamp: Optional[float]) -> None:
    """
    Update the timestamp to match the original one
    """
    query = """
            UPDATE runs
            SET run_timestamp = ?
            WHERE run_id = ?
            """
    cursor = target_conn.cursor()
    cursor.execute(query, (correct_run_timestamp, target_run_id))

    query = """
            UPDATE runs
            SET completed_timestamp = ?
            WHERE run_id = ?
            """
    cursor = target_conn.cursor()
    cursor.execute(query, (correct_completed_timestamp, target_run_id))
Example #19
0
def _2to3_get_layouts(
        conn: ConnectionPlus) -> Dict[int, Tuple[str, str, str, str]]:
    query = """
            SELECT layout_id, parameter, label, unit, inferred_from
            FROM layouts
            """
    cur = conn.cursor()
    cur.execute(query)

    results: Dict[int, Tuple[str, str, str, str]] = {}
    for row in cur.fetchall():
        results[row['layout_id']] = (row['parameter'], row['label'],
                                     row['unit'], row['inferred_from'])
    return results
Example #20
0
def test_atomic_transaction(tmp_path):
    """Test that atomic_transaction works for ConnectionPlus"""
    dbfile = str(tmp_path / 'temp.db')

    conn = ConnectionPlus(sqlite3.connect(dbfile))

    ctrl_conn = sqlite3.connect(dbfile)

    sql_create_table = 'CREATE TABLE smth (name TEXT)'
    sql_table_exists = 'SELECT sql FROM sqlite_master WHERE TYPE = "table"'

    atomic_transaction(conn, sql_create_table)

    assert sql_create_table in ctrl_conn.execute(
        sql_table_exists).fetchall()[0]
Example #21
0
def test_atomic_with_exception():
    sqlite_conn = sqlite3.connect(':memory:')
    conn_plus = ConnectionPlus(sqlite_conn)

    sqlite_conn.execute('PRAGMA user_version(25)')
    sqlite_conn.commit()

    assert 25 == sqlite_conn.execute('PRAGMA user_version').fetchall()[0][0]

    with pytest.raises(RuntimeError,
                       match="Rolling back due to unhandled exception") as e:
        with atomic(conn_plus) as atomic_conn:
            atomic_conn.execute('PRAGMA user_version(42)')
            raise Exception('intended exception')
    assert error_caused_by(e, 'intended exception')

    assert 25 == sqlite_conn.execute('PRAGMA user_version').fetchall()[0][0]
Example #22
0
def set_journal_mode(conn: ConnectionPlus, journal_mode: JournalMode) -> None:
    """
    Set the ``atomic commit and rollback mode`` of the sqlite database.
    See https://www.sqlite.org/pragma.html#pragma_journal_mode for details.

    Args:
        conn: Connection to the database.
        journal_mode: Which `journal_mode` should be used for atomic commit and rollback.
            Options are DELETE, TRUNCATE, PERSIST, MEMORY, WAL and OFF.
    """
    valid_journal_modes = ["DELETE", "TRUNCATE", "PERSIST", "MEMORY", "WAL", "OFF"]
    if journal_mode not in valid_journal_modes:
        raise RuntimeError(f"Invalid journal_mode {journal_mode} "
                           f"Valid modes are {valid_journal_modes}")
    query = f"PRAGMA journal_mode={journal_mode};"
    cursor = conn.cursor()
    cursor.execute(query)
Example #23
0
def get_matching_exp_ids(conn: ConnectionPlus, **match_conditions) -> List:
    """
    Get exp_ids for experiments matching the match_conditions

    Raises:
        ValueError if a match_condition that is not "name", "sample_name",
        "format_string", "run_counter", "start_time", or "end_time"
    """
    valid_conditions = [
        "name", "sample_name", "start_time", "end_time", "run_counter",
        "format_string"
    ]

    for mcond in match_conditions:
        if mcond not in valid_conditions:
            raise ValueError(f"{mcond} is not a valid match condition.")

    end_time = match_conditions.get('end_time', None)
    time_eq = "=" if end_time is not None else "IS"

    sample_name = match_conditions.get('sample_name', None)
    sample_name_eq = "=" if sample_name is not None else "IS"

    query = "SELECT exp_id FROM experiments "
    for n, mcond in enumerate(match_conditions):
        if n == 0:
            query += f"WHERE {mcond} = ? "
        else:
            query += f"AND {mcond} = ? "

    # now some syntax clean-up
    if "format_string" in match_conditions:
        format_string = match_conditions["format_string"]
        query = query.replace("format_string = ?",
                              f'format_string = "{format_string}"')
        match_conditions.pop("format_string")
    query = query.replace("end_time = ?", f"end_time {time_eq} ?")
    query = query.replace("sample_name = ?", f"sample_name {sample_name_eq} ?")

    cursor = conn.cursor()
    cursor.execute(query, tuple(match_conditions.values()))
    rows = cursor.fetchall()

    return [row[0] for row in rows]
Example #24
0
def _2to3_get_deps(conn: ConnectionPlus) -> DefaultDict[int, List[int]]:
    query = """
            SELECT layouts.run_id, layouts.layout_id
            FROM layouts
            INNER JOIN dependencies
            ON layouts.layout_id==dependencies.dependent
            """
    cur = conn.cursor()
    cur.execute(query)
    data = cur.fetchall()
    cur.close()
    results: DefaultDict[int, List[int]] = defaultdict(list)

    for row in data:
        run_id = row['run_id']
        layout_id = row['layout_id']
        results[run_id].append(layout_id)

    return results
Example #25
0
def test_dataset_in_memory_does_not_create_runs_table(
        meas_with_registered_param, DMM, DAC, tmp_path):
    with meas_with_registered_param.run(
            dataset_class=DataSetType.DataSetInMem) as datasaver:
        for set_v in np.linspace(0, 25, 10):
            DAC.ch1.set(set_v)
            get_v = DMM.v1()
            datasaver.add_result((DAC.ch1, set_v), (DMM.v1, get_v))

    ds = datasaver.dataset
    dbfile = datasaver.dataset._path_to_db

    conn = ConnectionPlus(sqlite3.connect(dbfile))

    tables_query = 'SELECT * FROM sqlite_master WHERE TYPE = "table"'
    tables = list(atomic_transaction(conn, tables_query).fetchall())
    assert len(tables) == 4
    tablenames = tuple(table[1] for table in tables)
    assert all(ds.name not in table_name for table_name in tablenames)
Example #26
0
def _2to3_get_dependencies(
        conn: ConnectionPlus) -> DefaultDict[int, List[int]]:
    query = """
            SELECT dependent, independent
            FROM dependencies
            ORDER BY dependent, axis_num ASC
            """
    cur = conn.cursor()
    cur.execute(query)
    data = cur.fetchall()
    cur.close()
    results: DefaultDict[int, List[int]] = defaultdict(list)

    if len(data) == 0:
        return results

    for row in data:
        dep = row['dependent']
        indep = row['independent']
        results[dep].append(indep)

    return results
Example #27
0
def get_exp_ids_from_run_ids(conn: ConnectionPlus,
                             run_ids: Sequence[int]) -> List[int]:
    """
    Get the corresponding exp_id for a sequence of run_ids

    Args:
        conn: connection to the database
        run_ids: a sequence of the run_ids to get the exp_id of

    Returns:
        A list of exp_ids matching the run_ids
    """
    sql_placeholders = sql_placeholder_string(len(run_ids))
    exp_id_query = f"""
                    SELECT exp_id
                    FROM runs
                    WHERE run_id IN {sql_placeholders}
                    """
    cursor = conn.cursor()
    cursor.execute(exp_id_query, run_ids)
    rows = cursor.fetchall()

    return [exp_id for row in rows for exp_id in row]
Example #28
0
def upgrade_2_to_3(conn: ConnectionPlus) -> None:
    """
    Perform the upgrade from version 2 to version 3

    Insert a new column, run_description, to the runs table and fill it out
    for exisitng runs with information retrieved from the layouts and
    dependencies tables represented as the json output of a RunDescriber
    object
    """

    no_of_runs_query = "SELECT max(run_id) FROM runs"
    no_of_runs = one(atomic_transaction(conn, no_of_runs_query), 'max(run_id)')
    no_of_runs = no_of_runs or 0

    # If one run fails, we want the whole upgrade to roll back, hence the
    # entire upgrade is one atomic transaction

    with atomic(conn) as conn:
        sql = "ALTER TABLE runs ADD COLUMN run_description TEXT"
        transaction(conn, sql)

        result_tables = _2to3_get_result_tables(conn)
        layout_ids_all = _2to3_get_layout_ids(conn)
        indeps_all = _2to3_get_indeps(conn)
        deps_all = _2to3_get_deps(conn)
        layouts = _2to3_get_layouts(conn)
        dependencies = _2to3_get_dependencies(conn)

        pbar = tqdm(range(1, no_of_runs + 1))
        pbar.set_description("Upgrading database")

        for run_id in pbar:

            if run_id in layout_ids_all:

                result_table_name = result_tables[run_id]
                layout_ids = list(layout_ids_all[run_id])
                if run_id in indeps_all:
                    independents = tuple(indeps_all[run_id])
                else:
                    independents = ()
                if run_id in deps_all:
                    dependents = tuple(deps_all[run_id])
                else:
                    dependents = ()

                paramspecs = _2to3_get_paramspecs(conn, layout_ids, layouts,
                                                  dependencies, dependents,
                                                  independents,
                                                  result_table_name)

                interdeps = InterDependencies(*paramspecs.values())
                desc_dict = {'interdependencies': interdeps._to_dict()}
                json_str = json.dumps(desc_dict)

            else:
                desc_dict = {
                    'interdependencies': InterDependencies()._to_dict()
                }
                json_str = json.dumps(desc_dict)

            sql = f"""
                   UPDATE runs
                   SET run_description = ?
                   WHERE run_id == ?
                   """
            cur = conn.cursor()
            cur.execute(sql, (json_str, run_id))
            log.debug(f"Upgrade in transition, run number {run_id}: OK")
Example #29
0
def upgrade_3_to_4(conn: ConnectionPlus) -> None:
    """
    Perform the upgrade from version 3 to version 4. This really
    repeats the version 3 upgrade as it originally had two bugs in
    the inferred annotation. inferred_from was passed incorrectly
    resulting in the parameter being marked inferred_from for each char
    in the inferred_from variable and inferred_from was not handled
    correctly for parameters that were neither dependencies nor dependent on
    other parameters. Both have since been fixed so rerun the upgrade.
    """

    no_of_runs_query = "SELECT max(run_id) FROM runs"
    no_of_runs = one(atomic_transaction(conn, no_of_runs_query), 'max(run_id)')
    no_of_runs = no_of_runs or 0

    # If one run fails, we want the whole upgrade to roll back, hence the
    # entire upgrade is one atomic transaction

    with atomic(conn) as conn:

        result_tables = _2to3_get_result_tables(conn)
        layout_ids_all = _2to3_get_layout_ids(conn)
        indeps_all = _2to3_get_indeps(conn)
        deps_all = _2to3_get_deps(conn)
        layouts = _2to3_get_layouts(conn)
        dependencies = _2to3_get_dependencies(conn)

        pbar = tqdm(range(1, no_of_runs + 1), file=sys.stdout)
        pbar.set_description("Upgrading database; v3 -> v4")

        for run_id in pbar:

            if run_id in layout_ids_all:

                result_table_name = result_tables[run_id]
                layout_ids = list(layout_ids_all[run_id])
                if run_id in indeps_all:
                    independents = tuple(indeps_all[run_id])
                else:
                    independents = ()
                if run_id in deps_all:
                    dependents = tuple(deps_all[run_id])
                else:
                    dependents = ()

                paramspecs = _2to3_get_paramspecs(conn, layout_ids, layouts,
                                                  dependencies, dependents,
                                                  independents,
                                                  result_table_name)

                interdeps = InterDependencies(*paramspecs.values())
                desc_dict = {'interdependencies': interdeps._to_dict()}
                json_str = json.dumps(desc_dict)

            else:
                desc_dict = {
                    'interdependencies': InterDependencies()._to_dict()
                }
                json_str = json.dumps(desc_dict)

            sql = f"""
                   UPDATE runs
                   SET run_description = ?
                   WHERE run_id == ?
                   """
            cur = conn.cursor()
            cur.execute(sql, (json_str, run_id))
            log.debug(f"Upgrade in transition, run number {run_id}: OK")
Example #30
0
def connect(name: str,
            debug: bool = False,
            version: int = -1) -> ConnectionPlus:
    """
    Connect or create  database. If debug the queries will be echoed back.
    This function takes care of registering the numpy/sqlite type
    converters that we need.

    Args:
        name: name or path to the sqlite file
        debug: whether or not to turn on tracing
        version: which version to create. We count from 0. -1 means 'latest'.
            Should always be left at -1 except when testing.

    Returns:
        conn: connection object to the database (note, it is
            `ConnectionPlus`, not `sqlite3.Connection`

    """
    # register numpy->binary(TEXT) adapter
    # the typing here is ignored due to what we think is a flaw in typeshed
    # see https://github.com/python/typeshed/issues/2429
    sqlite3.register_adapter(np.ndarray, _adapt_array)  # type: ignore
    # register binary(TEXT) -> numpy converter
    # for some reasons mypy complains about this
    sqlite3.register_converter("array", _convert_array)

    sqlite3_conn = sqlite3.connect(name, detect_types=sqlite3.PARSE_DECLTYPES)
    conn = ConnectionPlus(sqlite3_conn)

    latest_supported_version = _latest_available_version()
    db_version = get_user_version(conn)

    if db_version > latest_supported_version:
        raise RuntimeError(f"Database {name} is version {db_version} but this "
                           f"version of QCoDeS supports up to "
                           f"version {latest_supported_version}")

    # sqlite3 options
    conn.row_factory = sqlite3.Row

    # Make sure numpy ints and floats types are inserted properly
    for numpy_int in [
            np.int, np.int8, np.int16, np.int32, np.int64, np.uint, np.uint8,
            np.uint16, np.uint32, np.uint64
    ]:
        sqlite3.register_adapter(numpy_int, int)

    sqlite3.register_converter("numeric", _convert_numeric)

    for numpy_float in [np.float, np.float16, np.float32, np.float64]:
        sqlite3.register_adapter(numpy_float, _adapt_float)

    for complex_type in complex_types:
        sqlite3.register_adapter(complex_type, _adapt_complex)  # type: ignore
    sqlite3.register_converter("complex", _convert_complex)

    if debug:
        conn.set_trace_callback(print)

    init_db(conn)
    perform_db_upgrade(conn, version=version)
    return conn