0% found this document useful (0 votes)
59 views26 pages

API With Rust

Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
59 views26 pages

API With Rust

Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

Självständigt arbete i informationsteknologi

Bachelor Thesis 15 hp

May 2024

Designing and Implementing a


Rust API for Mimer SQL by
Foreign Function Interfacing
Edvard Axelman, Edvin Bruce, Simon Eriksson, William
Forslund, Fredrik Hammarberg, Viktor Wallsten

Master’s Programme in Computer and Information


Engineering
Designing and Implementing a
Rust API for Mimer SQL by
Foreign Function Interfacing

Edvard Axelman, Edvin Bruce, Simon Eriksson, William Forslund, Fredrik


Hammarberg, Viktor Wallsten

Abstract

According to research, software security vulnerabilities most often stem from memory related is-
sues. These memory issues are mitigated in many programming languages, but often at the cost of
performance. Rust, however, is a programming language which addresses these memory issues
without significant losses in performance. Leveraging this strength, an Application Programming
Interface (API) for interacting with Mimer SQL databases was developed in Rust. As there is no
standard for writing database APIs in Rust, inspiration was drawn from other Rust APIs for rela-
tional database management systems to ensure similar functionality. The development process
involved three main steps: stakeholder requirement specification, system design and implemen-
tation, and testing and evaluation. Stakeholder requirements were identified collaboratively to
ensure alignment between user needs and API functionality. The system design consists of two
libraries: the “FFI Library”, facilitating interaction with the Mimer SQL C API, and the “API library”,
providing a user-friendly interface for Rust developers. Key features include connection manage-
ment, statement preparation, cursor handling, and transaction support. Testing methodologies
include unit and integration testing, performance benchmarking against the C API, and consider-
ation for memory safety. The resulting API demonstrates performance parity with the C API and
adherence to Rust conventions.

Faculty of Science and Technology

Uppsala University, Place of Publication Uppsala

External Supervisors: Ludvig Von Feilitzen, Fredrik Ålund Supervisor: Sofia Alfsson

Examiner: Mats Daniels


Designing and Implementing a
Rust API for Mimer SQL by
Foreign Function Interfacing

Edvard Axelman, Edvin Bruce, Simon Eriksson, William Forslund, Fredrik


Hammarberg, Viktor Wallsten

Sammanfattning

Enligt forskning beror säkerhetsbrister i programvara oftast på minnesrelaterade problem. Dessa


minnesproblem motverkas i många programmeringsspråk, men ofta på bekostnad av prestan-
da. Rust är dock ett programmeringsspråk som åtgärdar dessa minnesproblem utan betydande
prestandaförluster. Genom att utnyttja denna styrka utvecklades ett applikationsprogrammerings-
gränssnitt (API) för att interagera med Mimer SQL-databaser i Rust. Eftersom det inte finns nå-
gon standard för att skriva databas-API:er i Rust hämtades inspiration från andra Rust-API:er
för relationsdatabaser för att säkerställa liknande funktionalitet. Utvecklingsprocessen innefattade
tre huvudsteg: specifikation av intressentkrav, systemdesign och implementering samt testning
och utvärdering. Intressentkraven identifierades genom samarbete för att säkerställa överens-
stämmelse mellan användarbehov och API-funktionalitet. Systemdesignen består av två bibliotek:
“FFI-biblioteket”, som erbjuder interaktionen med Mimer SQL C API:t, och “API-biblioteket”, som
erbjuder ett användarvänligt gränssnitt för Rust-utvecklare. Nyckelfunktioner inkluderar anslut-
ningshantering, validering av SQL-satser, iterering över resultatset och transaktionsstöd. Testme-
toderna omfattar enhets- och integrationstester, prestandajämförelser med C API:t och testning av
minnessäkerhet. Det resulterande API:t visar prestandaparitet med C API:t och följer konventioner
i Rust.

Teknisk-naturvetenskapliga fakulteten

Uppsala universitet, Utgivningsort Uppsala

Externa handledare: Ludvig Von Feilitzen, Fredrik Ålund Handledare: Sofia Alfsson

Examinator: Mats Daniels


Course: Independent Project in Information Engineering

Table of Contents
1 Introduction 1
1.1 Research Questions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1

2 Background and Theory 2


2.1 Memory Safety . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
2.2 Rust . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
2.2.1 Ownership and Borrowing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
2.2.2 Enums and Traits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
2.2.3 Iterators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
2.3 Application Programming Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
2.4 SQL and RDBMS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
2.5 Mimer SQL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
2.5.1 Mimer SQL C API . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
2.6 Foreign Function Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6

3 Related Work 7
3.1 RDBMSs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
3.2 APIs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7

4 Methods 8
4.1 System Design and Implementation . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
4.2 Testing and Evaluation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
4.2.1 Unit and Integration Testing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
4.2.2 Performance Testing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9

5 System Design 10
5.1 The API Library . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10

6 System Implementation 12
6.1 Borrowing and Mutability . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
6.2 Dereferencing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
6.3 Error Handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
6.4 Enums and Traits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
6.5 Iterators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14

7 Results 15

8 Discussion 17

9 Future Work 18

10 Conclusion 19

References 20
Course: Independent Project in Information Engineering

1 Introduction
According to research conducted by the tech companies Google and Microsoft, approximately 70%
of all software security vulnerabilities are memory related [1]. Memory safety vulnerabilities create
opportunities for attackers to insert malicious code into exposed machines. Such breaches can
lead to unauthorised data access or unexpected system behaviour. In response to threats emerg-
ing from memory safety vulnerabilities, government agencies in the US, UK, Canada, Australia, and
New Zealand are calling for future programs to be written in memory safe programming languages
[2].

Although most modern languages are memory-safe, they often lack the performance of lower level
languages such as C or C++ [1]. In 2006, developers at Mozilla began creating a programming
language called Rust, which was later released in 2015 [3]. Rust is deliberately designed to address
memory-related vulnerabilities and ensures memory safety while having comparable performance to
lower level languages [4].

Due to Rust being a new language, development tools and libraries are not as comprehensive as
those available for older languages like Java [5]. For instance, Java benefits from having a standard-
ised database access [6], which ensures uniform design for any database Application Programming
Interface (API). This offloads users of such APIs from having to design their code differently depending
on which database they are using. Currently, no such standardisation exists in Rust.

To help expand Rust’s development toolkit, an API for the Mimer SQL database has been developed.
Mimer SQL is a Relational Database Management System (RDBMS) that currently has APIs for a
wide variety of programming languages, including C and now also Rust. The Mimer SQL Rust API
enables users of Mimer SQL to interact with their database through programs written in Rust. The
Rust API is released as open source code to contribute towards a future standard for database APIs
in the Rust programming language.

1.1 Research Questions


This report aims to evaluate the strengths and limitations of implementing a Rust API for Mimer SQL.
To support this research, the following sub-questions will be answered:

• How can memory safety be ensured in Rust when using code from the memory unsafe language
C?

• How much performance overhead will the Rust API have compared to the Mimer SQL C API?

• How will the Rust API structurally differ from the Mimer SQL C API?

1
Course: Independent Project in Information Engineering

2 Background and Theory


The system design and implementation requires understanding of several concepts. These con-
cepts include memory safety, APIs and Foreign Function Interfaces (FFI), the principles of relational
databases, and the distinctive features of Rust and Mimer SQL respectively. Consequently, explana-
tions of each topic follows.

2.1 Memory Safety


Memory safety refers to a set of features and practices designed to ensure that programs run with-
out memory related errors [7]. These errors, can lead to crashes, vulnerabilities, or unpredictable
behaviour. Consequently, by exploiting these errors, the security of a system can be compromised.
Errors related to memory safety include buffer overflows, data races, memory leaks, and use-after-
free.

Buffer Overflows: Refers to when a program attempts to write data beyond the bounds of allo-
cated memory [8]. This can overwrite adjacent data, potentially leading to crashes, erratic program
behaviour, or exploitation by attackers to execute arbitrary code.

Data Races: This can occur when multiple accesses are made to the same memory location simulta-
neously, where at least one of them is a write operation [9]. Data races can lead to program crashes,
data corruption, inconsistency, or undefined behaviour. This means that if concurrent accesses inter-
fere with each other, it can result in unpredictable and incorrect program states.

Memory Leaks: Occurs when a program fails to release memory that is no longer needed, leading to a
gradual depletion of available memory resources [10]. While memory leaks may not cause immediate
failures, they can eventually lead to performance degradation and program instability.

Use-after-Free: This refers to when a program attempts to access a memory area after deallocating
it, commonly referred to as using a dangling pointer. Undefined behaviour may occur if the memory
has been reallocated by a different object or modified by other parts of the program [7].

To address memory safety concerns, programming languages and development frameworks employ
various strategies and mechanisms, including static analysis, memory management techniques, and
language features.

Static Analysis: Involves analysing code without executing it, thereby identifying potential memory-
related errors such as buffer overflows, and memory leaks. By detecting these issues early in the
development process, static analysis helps improve code quality and reliability [7].

Memory Management Techniques: There exists two primary memory management techniques:
manual and automatic. Manual memory management, used in e.g. C and C++, requires skilled and
experienced programmers to ensure memory safety [11]. Automatic memory management, as em-
ployed in e.g. Java and C#, is inherently memory safe. The cost of this automatic memory manage-
ment is the performance overhead it entails.

Language Features: Some programming languages incorporate memory safety features into their
design, meaning the language’s compiler can check for unsafe memory operations [12].

2.2 Rust
Rust is a multi-paradigm, general-purpose programming language that emphasises memory safety
and performance [13]. Being a multi-paradigm language, Rust does not strictly adhere to any single
programming paradigm such as imperative, object-oriented, or functional. Instead, Rust incorporates

2
Course: Independent Project in Information Engineering

features and concepts from all three paradigms, allowing developers to leverage a diverse set of
programming styles and techniques. It ensures memory safety through its unique ownership and
borrowing systems. These systems prevent common bugs such as use-after-free, data races, and
also memory leakage, thereby promoting safer memory usage without needing automatic memory
management during runtime.

Additionally, Rust has a strong and static type system, which enables error detection early in the de-
velopment process [14]. A strong static type system means that data types are required to be carefully
defined in the code, and a programmer will get errors from the compiler until this is fulfilled. Rust also
implements Zero-cost abstractions to ensure that higher-level constructs do not add overhead at run-
time. This feature of the compiler analyses the code and rewrites it into more efficient forms, allowing
programmers to write code in various ways without affecting performance.x

In Rust, libraries are called Crates, and [Link] is the official repository where developers can
publish their Crates. This central repository simplifies the process of finding and incorporating various
Crates into Rust projects.

2.2.1 Ownership and Borrowing

The ownership system is a set of rules dictating how values in a Rust program should adhere to
one another. The rules are: each value must have an owner, a value can only have one owner at
any given time, and when the owner goes out of scope, the value will be dropped. When a variable
is declared in Rust, it is bound to a specific scope. A variable is only accessible within the scope
that it was initially declared in. Once the scope ends, the variable goes out of scope, meaning its
resources are dropped automatically. This is explained in Listing 1 where inside the function main(),
two variables named x and y are declared. The first variable x is declared inside main() meaning
that after the function has finished executing, the value will be deallocated. The second variable y
is declared inside a new scope that lies inside main(). This means that when that scope ends, the
variable y is deallocated.

1 fn main() { // Outer scope starts here


2 let x = 5;
3

4 { // Inner scope starts here


5

6 let y = 10;
7

8 } // Inner scope ends here, y is destroyed


9

10 } // Outer scope ends here, x is destroyed


Listing 1: Basic example of scopes in Rust.

The borrowing system allows a part of a Rust program to borrow a value that is owned by another
part of the program. This means that instead of taking ownership of the value, it takes a reference to
the value. There are two types of borrows, mutable and immutable borrows.

Mutable borrow: Allows both reading and modifying a value. It is not possible to mutably borrow
a value more than once at any given time. As a real world example, when borrowing an item from
someone, that item can only be borrowed by one person at a time. The item may also be modified
before it is returned.

Immutable borrow: Allows reading a value without the ability to modify it. There can be multiple

3
Course: Independent Project in Information Engineering

immutable borrows of a value at the same time, as they do not interfere with each other. However,
there cannot be an immutable and a mutable borrow of a value simultaneously since the immutable
borrow is made on the premise that the value will not be modified. To explain this concept further, a
real world example follows: Alice is the owner of a car, and Bob takes a photograph of the car. Just
because Bob has a photograph of the car, does not mean that Bob is the owner of the car. However,
Bob can use this photograph to refer to the car, e.g. if he wants to mention it in a conversation. In
this example, Bob cannot alter the car, as only the owner of the car can do such. Furthermore, Alice
cannot change anything regarding the car for as long as Bob still has his reference to the car, as the
photo would no longer be accurate to the current state of the car.

2.2.2 Enums and Traits

In Rust, an enum is a data structure that allows a variable to be one of several specified types [15].
Enums have many use cases in Rust. Firstly, they can be used to define specific and finite grouped
states. For example, a custom defined enum ArticleState could be either Published, Draft, or
NotStarted. This means that a variable of the type ArticleState must be one of the specified
states. This enum can be seen in Listing 2.

1 enum ArticleState {
2 Published,
3 Draft,
4 NotStarted,
5 }
Listing 2: Enum example with state of an article.

Secondly, enums can be used to group and define specific types with data. For example, an enum
named Shapes can contain the types Square(i32,i32) and Circle(i32) which can be seen in Listing
3.

1 enum Shapes {
2 Square(i32,i32)
3 Circle(i32)
4 }
Listing 3: Enum example with shapes.

Furthermore, traits can be implemented on an enum. Traits enable the definition of shared behaviour
among different types. When applied to an enum, traits allow specifying common methods or functions
that can be utilised across different variants of that enum. To continue with the example of the enum
Shapes, a fitting trait would be Area, which should then be implemented as a function that returns the
area of the corresponding shape.

This way of sorting related variables into one type enhances code readability and maintainability,
making it easier to handle multiple related types systematically and safely.

2.2.3 Iterators

Iterators in Rust are used both for iterating through data, e.g. via for-loops, and for using functionality
from functional programming, e.g. filters or maps [13]. Thus iterators are versatile and can be used
for many purposes in Rust. A specific type of iterator that exists in Rust is a Fallible Streaming Iterator
[16]. Fallible means that this iterator can be used on data structures where the retrieval of the next
item can fail, i.e. returning an error instead of the item. Streaming implies that the iterator can handle
data sequentially without needing to retain previous items.

4
Course: Independent Project in Information Engineering

2.3 Application Programming Interfaces


An Application Programming Interface, commonly referred to as an API, serves as an intermediary
that allows different software applications to communicate and interact with each other. APIs define a
set of functions that enable developers to access specific features or data from a software application,
without needing to understand its internal workings [17]. This is often called encapsulation which is a
common software design principle that encourages limiting direct access to data or functions. It also
creates more maintainable code by reducing dependencies, and isolating changes.

2.4 SQL and RDBMS


RDBMSs are software systems that enable storage, management, and retrieval of structured data
[18]. They are based on the relational model of data, which organises information into tables with
rows and columns. Structured Query Language (SQL) is the standard language used to interact
with these databases, allowing users to perform various operations such as querying data, updating
records, and defining database structures. SQL supports a wide range of operations, including:

Data Querying: Retrieving specific data from one or more tables using SELECT statements.

Data Manipulation: Modifying existing data in tables using INSERT, UPDATE, and DELETE statements.

Schema Definition: Defining the structure of the database, including tables, indexes, views, and
constraints, using CREATE, ALTER, and DROP statements.

Transaction Control: Managing transactions to ensure the consistency and integrity of data using
COMMIT, ROLLBACK, and SAVEPOINT statements.

2.5 Mimer SQL


Mimer Information Technology AB, from here on referred to as Mimer, is the owner of the RDBMS
called Mimer SQL [19]. Mimer is a corporation which initially started as a research project at Uppsala
University in the 1970s. The project initially focused on developing an RDBMS that could support
the needs of emerging software applications. Today, Mimer continues to focus on enhancing the
capabilities of Mimer SQL.

Mimer SQL provides support for executing SQL commands and is known for its reliability, close com-
pliance with industry standards, and performance [20]. Mimer SQL is designed to scale efficiently to
handle increasing data volumes and user loads, with support for features that ensure high reliability
[21].

One notable feature in Mimer SQL is called optimistic concurrency control, which avoids locking and
deadlock issues [22]. It tracks changes during a transaction without applying them immediately. Upon
commit, it compares read values with the current state. If no conflicts arise, changes are applied
atomically and made visible. Conflicts trigger transaction abort, prompting the application to decide
on subsequent actions, such as retrying the transaction or presenting updated data to the user for
resolution.

2.5.1 Mimer SQL C API

Mimer offers the Mimer SQL C API, a library enabling users to interact with a Mimer database in
the programming language C [23]. It prioritises simplicity, platform independence, and maintaining
a small footprint, making it suitable for various development environments. The API separates its
functionalities into four main categories: session management, statement management, input data
management, and output data management.

5
Course: Independent Project in Information Engineering

Session Management: This group of routines handles establishing, disconnecting, and managing
transactions within database sessions. It includes functions for initiating and ending both sessions
and transactions.

Statement Management: This involves routines for creating, executing, and finalising database
statements. It covers the handling of statements used for defining database structures, as well as
statements with parameters for data modification. Additionally, it manages queries that produce re-
sult sets by utilising a cursor to fetch the data sequentially.

Input Data Management: Commonly referred to as set routines. This category encompasses rou-
tines for supplying data to the database through statements. It provides functions to set various data
types like integers, strings, doubles, and booleans as input parameters for the statements.

Output Data Management: Commonly referred to as get routines. These routines handle retrieving
data returned by the database in response to statements. Contrary to input routines, these routines
provide functionality to receive data in various forms.

2.6 Foreign Function Interface


An FFI is a mechanism allowing code written in one programming language to call functions or use
data structures defined in another programming language [24]. In essence, an FFI defines a set of
rules and conventions that enable interaction between different languages. This involves specifying
how functions are invoked, how data is passed between languages, and how memory management
is handled.

For instance, if a Python program wants to use a function from Java, it must carefully specify data
structures to ensure they match the expected inputs and outputs of the Java function. This is neces-
sary since data types can vary between programming languages. The specification of these transla-
tions is what defines an FFI.

6
Course: Independent Project in Information Engineering

3 Related Work
In this section, various popular RDBMSs and their associated database APIs are reviewed, highlight-
ing their features, applications, and integration capabilities.

3.1 RDBMSs
Popular RDBMSs that are in use worldwide include Oracle Database [25], SQLite [26], and Post-
greSQL [27]. Oracle Database is known for its robust feature set and reliability, making it ideal for
large-scale enterprise environments where performance and scalability are critical. Furthermore, Or-
acle Database integrates seamlessly with other products from Oracle, e.g. Oracle Cloud Infrastructure
and Applications [28]. SQLite offers a lightweight solution suitable for mobile applications and small
desktop software, where a smaller footprint is preferred. An SQLite instance is always hosted lo-
cally, meaning it is stored and accessed on the same machine where the application is running [29].
PostgreSQL is open-source and has strong support for both SQL and NoSQL data types, and it con-
forms tightly to the SQL standard [27]. Furthermore, PostgreSQL operates on a client-server model,
where the client connects to a server hosting the database [30]. Mimer SQL, being a general-purpose
RDBMS is well adapted for both enterprise and resource limited environments, and similarly to Post-
greSQL, Mimer SQL strictly adheres to the SQL standard. Moreover, Mimer SQL is customisable and
can be run either as a local database or operate on a client-server model [31].

3.2 APIs
Both SQLite [32] and PostgreSQL [33] have APIs for Rust. The SQLite API in Rust is built with an
FFI to a C API [32]. On the contrary, the PostgreSQL API uses only natively written Rust code [34].
The Mimer SQL Rust API also utilises an FFI, resembling SQLite in this regard. However, in terms
of functionality, the API more closely resembles PostgreSQL’s API rather than SQLite’s, since Mimer
SQL shares more similarities with PostgreSQL.

Several APIs exist which enable access to Mimer SQL in different programming languages. The first
API released by Mimer was written in C. Since then, Mimer has released APIs for other languages
where PHP, Python, and Java are a few of them [35]. Some of these APIs utilise an FFI wrapping
the Mimer SQL C API, which the Mimer SQL Rust API also does. The MimerPy API is an example
of this. It offers developers a way to interact with Mimer SQL databases from Python programs [36].
MimerPy differs from the Rust API as it is implemented in Python, but highlights the possibility of using
an FFI with the Mimer SQL C API to create support for new languages.

7
Course: Independent Project in Information Engineering

4 Methods
In order to develop the Mimer SQL Rust API, three main steps were taken: specification of the stake-
holder requirements, followed by system design and implementation, and finally testing and evalua-
tion.

Initially, stakeholder requirements were specified in cooperation with Mimer. This cooperation was
realised as a meeting between two employees at Mimer (the stakeholders) and the Rust API develop-
ers. In this meeting the over-all requirements for the Rust API were laid out. These requirements were
then translated into concrete goals that the Rust API was designed according to, and was evaluated
against.

4.1 System Design and Implementation


The system design and implementation was conducted as a collaborative and iterative process. The
purpose of this was to meet the needs of Mimer while remaining flexible to account for changing
requirements. This involved a structured meeting framework and breaking down the API development
into increments.

Three different types of meetings were held during the development process: weekly planning ses-
sions, weekly meetings with Mimer, and short daily meetings. Weekly planning meetings within the
group were carried out to synchronise team efforts and prioritise tasks associated with each incre-
ment of the development phase. These were often based on feedback from meetings with Mimer.
The Mimer meetings addressed various topics including design decisions, unexpected problems, and
emerging requirements. Prior to each meeting, an agenda outlining the topics for discussion was
prepared. This approach ensured that the meetings with Mimer were utilised efficiently, resulting in
answers to the most relevant questions. Additionally, short daily meetings were used to keep all group
members updated on the progress and discuss possible issues.

4.2 Testing and Evaluation


Throughout the development process, various forms of testing were implemented on the functionality
of the API as it became available. By testing the functionality continuously, potential issues such as
software bugs and errors, could be found and prevented early in the development process. Thorough
testing of a product contributes to its reliability, security, and performance. This ultimately saves time
and costs while also increasing customer satisfaction and product quality [37]. In order to verify that
the API fulfils the specified requirements, both unit tests, integration tests and various benchmarks
were conducted. Lastly, it is important to note that testing can only show the presence of bugs, not
guarantee the software to be bug-free.

4.2.1 Unit and Integration Testing

Each module was tested internally using unit tests to ensure that their individual functionalities met
the expected requirements. These unit tests were designed to validate the behaviour of specific
functions or components within each module, making it easier to catch bugs and ensure correctness
at a granular level.

Additionally, integration tests were used to verify how these modules interact and function together as
a cohesive system. These tests focused on the integration points between different modules, ensuring
that they seamlessly communicated and cooperated as intended. Furthermore, some integration
tests were designed to test the entire API in a normal use case. These tests were conducted to verify
that the API operates as intended, including all the necessary functionality, and that each function
performed as specified.

8
Course: Independent Project in Information Engineering

Both unit and integration tests were run through Valgrind, which is a tool for detecting memory leaks
and monitoring memory accesses [38].

4.2.2 Performance Testing

To evaluate the performance of the Rust API and address the research question regarding its rela-
tive performance compared to the Mimer SQL C API, a comparison between the two was conducted.
Testing the Rust code involved using Cargo Bench [39], while the C code was evaluated using the
Operating System (OS) function gettimeofday() [40]. The Cargo Bench approach leverages Crite-
rion, a benchmarking library for Rust, to conduct performance tests. Criterion automates the process
of running benchmarks, providing statistical analysis and visualisation of results. It employs wall-
time measurement, which captures the total elapsed time, including time spent in the CPU and time
spent waiting [41]. The gettimeofday() function provides access to the OS’s current time, enabling
precise measurement of execution duration. Similar to Criterion, gettimeofday() employs wall-time
measurement, ensuring consistency in the benchmarking process [40].

The performance tests were designed to execute identical queries on a database in a way that is as
consistent as possible across both languages. This process essentially involved writing a test in Rust
and then creating an equivalent test in C. The functions tested were all called 10,000 times. Each test
was then run 100 times, and the average time obtained was used to find the relative performance of
the Rust and C APIs. Testing was conducted on five central functionalities, and a description of each
test is provided below.

Execute Inserts: This test benchmarks the performance of inserting rows into a database using
individual executes without preparing the statement beforehand.

Fully Prepared Inserts: This test benchmarks the performance of inserting rows into a database
using a prepared statement that is executed within a loop.

Prepared Inserts with Parameters: This test benchmarks the performance of inserting rows into a
database using prepared statements with varying parameters. Each iteration of the loop executes the
statement with different parameter values.

Inserts with Transaction: This test benchmarks the performance of inserting rows into a database
within a transaction. This involves starting a transaction, performing inserts similar to the “Execute
Inserts” test, and ending the transaction by committing the changes to the database.

Select and Read Rows: This test benchmarks the performance of reading rows from the database.
It fetches all rows from a table and retrieves values from each row.

9
Course: Independent Project in Information Engineering

Figure 1: Abstracted diagram of the system architecture.

5 System Design
The API consists of two libraries, from here on referred to as the FFI Library and API Library. The FFI
Library serves as a wrapper for the Mimer SQL C API, providing an interface for accessing these C-
functions. Meanwhile, the API Library calls functions provided by the FFI Library, and this relationship
is illustrated in Figure 1. The API Library contains a range of features necessary for an efficient and
user-friendly database API.

5.1 The API Library


The API Library is the interface between users and Mimer SQL databases. The functionality of this
library is divided into four main modules, namely Connection, Statement, Cursor, and Transaction.
This separation into multiple modules allows for better organisation and modularity, making the API
easier to maintain and extend.

The Connection module is the starting point for any access to a Mimer SQL database. This mod-
ule contains functionality for initialising connections to already existing Mimer SQL databases [42].
Furthermore, the module provides functionality for creating statements, initialising transactions, and
executing unprepared queries. Unprepared queries are primarily intended for data definition, e.g. by
using CREATE, ALTER, and DROP in SQL.

The Statement module contains functionality for preparing SQL queries. Preparing a query means
compiling it, thus allowing for multiple reuses without the need for recompilation. Furthermore, a pre-
pared query is also assured to be free of various syntax errors and malicious segments, such as SQL
injections [43]. It is therefore both an optimisation with regards to performance, while also providing
safety-checks before execution to protect data from being accessed or manipulated in unintended
ways. Additionally, the Statement module provides support for preparing partial queries containing
unset parameters. These queries must have their parameters set before they can be executed and
committed to the database. To exemplify this, a prepared query might consist of an insert operation
with a “student” object containing unset parameters “name” and “age”. Running this query multiple
times allows for reducing repetition, as each “student” object will remain mostly identical except for
the unset parameters, which can be set differently in each run. The Statement module also permits
opening a cursor on a prepared query where all parameters have been set.

The Cursor module serves the purpose of fetching data from queries that yield results, such as SELECT
queries. In simple terms, a result set can be seen as a list, with the cursor pointing at a specific entry
within that list. A cursor operates in either forward-only or scrollable mode. A forward-only cursor
can only move forward through the result set, and only by one row at a time. In contrast, a scrollable
cursor can move forward and backward arbitrarily or move to any index of the result set. The reason
for having two different modes is that a scrollable cursor is more advanced and generally better if the

10
Course: Independent Project in Information Engineering

user allows for it. However, a forward-only cursor is simpler and needs less resources to run, thus
fitting better for resource limited systems.

The Transaction module allows users to perform atomic operations on the database. Being atomic in
this context means that the operations within a transaction are indivisible; they either all succeed or
fail collectively. This ensures that database changes occur reliably and consistently without partial or
incomplete alterations. Transactions can consist of one or multiple executed queries. After executing
a transaction, users have two options: “rollback” or “commit”. Rolling back a transaction means
discarding any changes made during the transaction, effectively reverting the database to the state
it was in before initialising the transaction. On the contrary, committing a transaction applies all its
changes to the database through a single server call. Therefore, this guarantees that the transaction’s
contents are committed simultaneously, which is also beneficial for performance, as server calls are
especially time-consuming from a CPU perspective.

11
Course: Independent Project in Information Engineering

(a) (b) (c)

Figure 2: Abstracted diagrams of the module relations. (a) Describes the relation between Connec-
tion and Transaction. (b) Describes the relation between Transaction, Connection, and Statement.
(c) Describes the relation between Statement and Cursor.

6 System Implementation
The API and FFI libraries are each implemented in Rust as two separate Crates. Together, they con-
tribute to a combined Crate that is the Mimer SQL Rust API. The FFI between the Crates is made
using Bindgen, which is a tool especially focused on creating FFIs between Rust and C [44]. This in-
volves specifying the header files containing the C API declarations to Bindgen, which then generates
the corresponding interface in Rust code. This generated code comprises the FFI Crate.

The implementation of the API Crate is split into several modules each focusing on a specific func-
tionality. Each module is implemented as a structure, which is a collection of data with associated
methods [13]. Rust’s structures, comparable to the programming concept objects, provide strong type
safety, meaning that certain errors can be found during compilation. They also support Rust’s own-
ership model, which helps prevent common memory bugs. An alternative to using structures would
be to implement the system more imperatively. However, this would lead to a user experience that is
uncommon in Rust. Structures are preferred in this context because they effectively symbolise their
respective functionalities.

Because of inherent dependencies, certain structures can only be created from other structures, re-
ferred to as their superior structure. For instance, a Transaction can only be created from a Connec-
tion, while a Statement can be created from either a Connection or a Transaction. Lastly, a Cursor
exclusively stems from a Statement. These relations are illustrated in Figure 2.

This means that a new instance of a structure must be created through a method from an instance of
a superior structure. This is in contrast to having the methods for creating new instances within each
respective structure. The latter approach could lead to confusion since a superior structure instance
is then only implicitly required.

Most of the Rust API’s functionality is enclosed within the methods of the module’s structures. The
majority of these methods work as a wrapper of the C API functions, with Rust-specific error handling
and translation between data types. When methods are not wrappers, it is typically due to Rust’s
memory safety regulations or to enhance the user-friendliness of the API.

To be able to use the FFI in the API Crate, unsafe blocks need to be used. Unsafe blocks are used
when writing code that Rust’s memory safety normally does not allow [13]. This is needed as the
Rust API calls functions written in C, which may not inherently be memory safe. Since Rust does
not guarantee memory safety in unsafe blocks, care must be taken to ensure proper error handling

12
Course: Independent Project in Information Engineering

and resource management. Unsafe blocks are used sparingly and encapsulated within safe Rust
abstractions to minimise the risk of memory faults which could lead to undefined behaviour.

6.1 Borrowing and Mutability


Borrowing is used in the Rust API to manage access to database connections, statements, and trans-
actions. When interacting with the Mimer SQL C API, Rust code often needs to borrow references
to resources managed by the C API. For example, when executing an SQL query or beginning a
transaction, the Rust code borrows references to the underlying C objects representing the database
connection and transaction.

One important consideration when borrowing references to resources managed by the Mimer SQL C
API is ensuring mutability when necessary. Since the C API may modify the state of these resources,
it is crucial to have mutable references to these. This is to prevent data races and ensure that changes
made by the C API are reflected correctly in the Rust code. However, Rust’s usual memory safety
guarantees are challenged by the use of C functions through the FFI. The Rust compiler does not
enforce mutability checks within the called C functions. Since it is unclear which functions of the
C API actually modify underlying structures, all C functions are assumed to do so. To account for
this, every function call to the C API through the FFI is explicitly marked as mutable in the Rust
API. This approach ensures that despite the lack of mutability checks in C, the Rust code remains
robust and maintains data consistency when interacting with the C API. However, this comes with
some limitations since a mutable reference can only be used by one variable at a given time. The
alternative to this would be declaring the resources as immutable, which the Rust compiler accepts,
but may imply that the API lies to the compiler about whether a variable is mutated or not by the C
API.

6.2 Dereferencing
The transaction structure implements a trait called dereferencing. This enables a similar mechanism
to inheritance in object oriented programming languages, and creates a more user-friendly interface.
In this case, methods defined in a connection structure can be called from a transaction structure.
Without dereferencing, it would require the user to handle both transaction and connection structures
separately in order to access their methods, thereby increasing the number of variables the user
needs to interact with.

6.3 Error Handling


In the Mimer SQL Rust API, there can be errors related to the Mimer SQL C API, such as invalid
syntax in SQL statements or invalid parameter values in function calls. Functions in the C API return
a non-negative number when the operation is successful and a negative number if an error occurs.
Additionally, errors may occur within the Rust API. However, the design of the Rust API ensures that
errors from C functions are always propagated. As a result, the Rust API has minimal Rust-specific
errors, maximising compatibility with the already existing error documentation from Mimer.

MimerError acts as a helper module to the other modules described in Section 5.1. If a Rust API user
wants to obtain further details about a specific error without manually searching for the error message
in the documentation, they can call the method get_error(). This returns a MimerError object which
consists of the error code and its corresponding error message. A MimerError could be returned with
each failing method call, giving users more information about it. However, invoking get_error() in
every method would be expensive as it requires a call to the database server. Providing get_error()
as an option instead results in an API with a smaller footprint, making it better suited for resource-
constrained environments. Additionally, according to Mimer, users are not always interested in error
messages.

13
Course: Independent Project in Information Engineering

6.4 Enums and Traits


Enums are used in the Mimer SQL Rust API to define various constants that dictate different options
from functions in the C API. An example of this is the mode of operation of a cursor. The cursor mode
dictates how a cursor is allowed to traverse the result set of an executed SQL statement. An enum
CursorMode is defined that contains two possible types: “Forward” and “Scrollable”, where both types
directly reference an option in the C API.

Furthermore, translation of data types between Rust and Mimer SQL is implemented as an enum
called MimerDataTypes. This enum contains encapsulations of Rust data types. An example of a
data type in MimerDataTypes is BigInt(i64), which contains the Rust data type i64, i.e. 64-bit signed
integer. “BigInt” is then used in the Rust API to convert its encapsulated i64 into the Mimer SQL data
type “BIGINT”.

This conversion between Rust and Mimer data types is done with two traits: FromSQL and ToSQL, which
are implemented on the enum MimerDataTypes. The FromSQL trait defines data type conversions from
Mimer to Rust. Contrarily, the ToSQL trait defines conversions from Rust data types to Mimer data
types. Both traits are implemented separately for each variant of the enum.

Handling the data types without using enums or traits would lead to an implementation such as using
regular functions instead. This approach would lead to more lines of code and less expandability. It
is however more straightforward for developers not familiar with Rust features. Since expandability
and better maintainability is desirable, the former option was chosen.

6.5 Iterators
An iterator is implemented for the Cursor due to the capability and versatility of iterators in Rust. When
retrieving data using the cursor, errors can occur, known as fallibility. Furthermore, as the cursor only
holds the current row in memory, it acts as a data stream. These two facts combined mean that the
Cursor structure solely implements a Fallible Streaming Iterator [16].

14
Course: Independent Project in Information Engineering

Table 1: System requirements

1 The Rust API should be able to execute queries on the database.

2 The code should be written and documented in a way that allows for
future expansion and maintenance.

3 The parts of code which Rust cannot guarantee to be memory safe


should be written/designed in a memory safe manner.

4 The Rust API can be at most 10% slower than the C API in single core
performance tests.

5 The architecture of the Rust API should closely resemble already exist-
ing database APIs in Rust.

7 Results
From the meeting with Mimer, it became apparent that they aimed for a Rust API that enables inter-
action with the Mimer database while utilising Rust’s core principles and benefits. Importance was
placed on reliability, performance, memory safety, and alignment with industry standards. Aligning
with industry standards ensures compatibility with existing Rust ecosystems. A well-documented final
product supporting future scalability and maintainability was also desired. These goals were concre-
tised into the five goals in Table 1. With these goals in mind, the API was designed and implemented.
Once the implementation of the API was finished, the API was evaluated against the same require-
ments in Table 1. The evaluation for each goal was:

1. According to testing, the API is able to execute various queries on the database.

2. The source code was documented according to Rust standard [13] and written in a manner
which should facilitate future expansion and maintenance.

3. Parts of the source code that the Rust compiler could not guarantee to be memory-safe, are,
according to testing done in Valgrind, free from memory mismanagement.

4. The largest performance overhead found by testing was 3.1%, which was below the maximum
performance overhead specified. See Table 2. Furthermore, the average performance over-
head was even lower, at 1.4%.

5. Mimer considered the API’s design to be sufficiently similar to existing Rust APIs for other
RDBMSs.

15
Course: Independent Project in Information Engineering

Table 2: Comparison between performance of Mimer SQL C API and Mimer SQL Rust API

Test Time for C [ms] Time for Rust [ms] Difference [%]
Execute Inserts 1362.9 1366.4 +0.3

Fully Prepared Inserts 1229.1 1231.6 +0.2

Prepared Inserts with 1268.8 1287.8 +1.5


Parameters
Inserts with Transaction 379.4 391.2 +3.1

Select and Read Rows 169.1 172.6 +2.1

16
Course: Independent Project in Information Engineering

8 Discussion
The Rust API provides an interface for communicating with a Mimer SQL database as specified in the
system requirements, see Table 1. This implies functionality for executing queries on a database in a
fashion which is common in database APIs. Furthermore, the architecture of the system was designed
to cohere with existing Rust APIs for database communication to a high degree. For instance, the
naming of structures and methods in this API are mostly consistent with previously existing APIs,
with their functionalities largely mirroring one another. Certain differences exist as a result of inherent
differences between the RDBMS.

When comparing the C API and the Rust API for Mimer SQL, more differences become apparent.
Most notable is the implementation of iterators since the C API does not implement an iterator but
the Rust API does. Furthermore, the Rust API uses structures to represent the state for when a
Connection initialises a Transaction and when a Statement opens a Cursor. On the other hand,
the C API only uses the internal state meaning that there is no separate structure for a cursor or
transaction.

As seen in table 2, testing showed that the Mimer SQL Rust API has an average overhead of 1.4%
compared to the Mimer SQL C API, which is better than initially expected. There are two factors that
lead to this large difference in expectation and reality. Using an FFI in Rust for calling functions in C
programs did not introduce any performance overhead [45]. Additionally, Rust zero-cost abstractions
were more efficient at optimising code than expected.

As mentioned in Section 4.2.2, gettimeofday() and Cargo Bench measure the total time from when
the program starts executing until it finishes. This means that the benchmark performance is depen-
dent on other programs that are running on the OS. For more accurate results, it would be better
to isolate the performance of the program from external factors as much as possible. This could be
achieved by only measuring the CPU-time needed for executing each test, excluding factors like I/O
wait time and OS-scheduling. This was mitigated by conducting the performance tests multiple times
and calculating the mean value of the results. However, this approach still does not entirely eliminate
the influence of external factors.

While Rust ensures safe memory management through ownership and borrowing, this can become
challenging when Rust interacts with code from memory unsafe languages like C through an FFI. De-
spite its benefits, this interaction introduces the risk of memory safety violations that are inherent to
memory unsafe languages. Common issues like dangling pointers or buffer overflow may propagate
into Rust, compromising its memory safety guarantees. To try to ensure the API’s memory safety, it un-
derwent testing with Valgrind, a tool used for memory debugging. Tests with Valgrind were conducted
on both unit tests and integration tests, confirming that all tests handle memory safely. However, it is
important to note that while Valgrind ensures that the existing tests handle memory safely, it does not
guarantee that the API is completely free from potential memory safety issues.

17
Course: Independent Project in Information Engineering

9 Future Work
There are still features in the Mimer SQL C API that have not been implemented in the Mimer SQL
Rust API. These unimplemented features include both unused functions and unrepresented data
types. Ideally, the Rust API should provide equivalent or more functionality than the C API, however,
that is currently not the case. Regarding data types, priority was placed on representing the most
commonly used data types. This entails that some more specific data types, e.g. timestamps, remain
unsupported or poorly supported.

Additionally, a Rust-specific feature currently lacking in the API is a wider support for iterators. Cur-
rently, the API only implements a Fallible Streaming Iterator, which does not provide the same func-
tionality as other iterators. For example, a Fallible Iterator is necessary when access to the entire
dataset is needed, contrasting with a Fallible Streaming Iterator which only offers access to the cur-
rent item. Having a Fallible Iterator would allow a user to traverse any point in the dataset as well
as allowing for more straightforward error handling. Implementing a Fallible Iterator would mean that
all the rows of the Cursor needs to be held in memory all at once. Therefore, this would introduce
a limitation in the usage of the Cursor in resource constrained systems. However, it could be made
optional, such that the Fallible Iterator would only be available if the user specifies what type of iterator
is desired.

A different approach would be to implement a Rust API natively, meaning without the usage of an
FFI. By writing an API completely in Rust, memory safety could be fully guaranteed, without having
to rely on checks by memory monitoring tools on foreign functions. However, this approach may
require a considerably larger workload, as developing a comprehensive API in Rust could be a much
larger project compared to wrapping existing C libraries via an FFI. Furthermore, access to proprietary
source code would be needed to interact with the Mimer RDBMS and to develop such an API. This
could be problematic if the source code is sensitive and should not be exposed publicly, which often
is the case with proprietary source code. On that note, publishing proprietary crates in Rust is not
trivial, as its largest crate repository [Link] does not allow publishing without open source.

18
Course: Independent Project in Information Engineering

10 Conclusion
The Mimer SQL Rust API provides the ability to write memory safe programs that interact with Mimer
SQL databases. The Rust API uses an FFI for calling functions in the Mimer SQL C API. The Rust
API is divided in two Crates: the FFI Crate and the API Crate. The FFI Crate generates an interface
for accessing the Mimer SQL C API functions. Meanwhile, the API Crate uses the functions provided
by the FFI Crate to produce a user-friendly interface. The API Crate is divided into multiple mod-
ules with corresponding methods that wrap around Mimer SQL C API functions. The API Crate was
developed in iterations and employed comprehensive testing which resulted in an API that satisfied
the requirements that were set for the product. Furthermore, with the results from the testing, no
memory errors were discovered but the fact still remains that complete memory safety cannot be fully
guaranteed. Due to this, it can be concluded that memory safety cannot be ensured in Rust when
using code from C. However, specific uses of Rust code which invokes C code can be ensured to
be memory safe through the use of the tool Valgrind. From the performance tests, the Rust API was
on average 1.4% slower than the C API. However, due to the discrepancies discussed in Section 8,
this average performance overhead cannot be guaranteed for every program that utilises the Rust
API. Additionally, the structure of the Mimer SQL Rust API differs slightly from the Mimer SQL C API,
mainly by adding support for iterators and state encoding.

In conclusion, implementing an API in Rust for Mimer SQL offers several advantages. One key benefit
is the higher level of abstraction provided by the Mimer SQL Rust API compared to the existing Mimer
SQL C API. This abstraction simplifies interactions by concealing many of the underlying complexities.
However, the most crucial advantage is the memory safety ensured by the Rust compiler, which
in turn the Mimer SQL Rust API provides. This memory safety provides a secure environment to
communicate with Mimer SQL databases, making the Mimer SQL Rust API a robust and reliable
choice.

19
Course: Independent Project in Information Engineering

References
[1] B. Lord, “The Urgent Need for Memory Safety in Software Products”, Cybersecurity & Infras-
tructure Security Agency, 2023. [Online]. Available: [Link] events/
news/urgent-need-memory-safety-software-products.
[2] I. Arghire, “Five Eyes Agencies Publish Guidance on Eliminating Memory Safety Bugs”, secu-
rityweek, 2023. [Online]. Available: [Link]
publish-guidance-on-eliminating-memory-safety-bugs/.
[3] C. Thompson, “How Rust went from a side project to the world’s most-loved programming lan-
guage”, 2023, visited on 2024-04-16. [Online]. Available: [Link]
com/2023/02/14/1067869/rust-worlds-fastest-growing-programming-language/.
[4] The Rust Team, Rust Programming language, visited on 2024-04-16. [Online]. Available: https:
//[Link]/.
[5] Rayobyte, “Rust Vs. Java”, Rayobyte, 2023, visited on 2024-04-16. [Online]. Available: https:
//[Link]/blog/rust-vs-java/.
[6] Java JDBC API, visited on 2024-04-16, Oracle. [Online]. Available: [Link]
com/javase/8/docs/technotes/guides/jdbc/.
[7] R. D. Caballar, “The Move to Memory-Safe Programming”, IEEE Spectrum, 2023, visited on
2024-04-23. [Online]. Available: [Link]
languages.
[8] OWASP Foundation, “Buffer Overflow”, OWASP, 2022, visited on 2024-04-23. [Online]. Avail-
able: [Link]
[9] A. B. Cyrille Artho Klaus Havelund, “High-level Data Races”, Computer Systems Institute (ETH
Zurich), and Kestrel Technology, Tech. Rep., visited on 2024-05-06. [Online]. Available: https:
//[Link]/~artho/papers/[Link].
[10] GeeksForGeeks, What is Memory Leak? How can we avoid?, visited on 2024-04-23, 2024.
[Online]. Available: [Link]
avoid/.
[11] R. Sheldon, “garbage collection (GC)”, TechTarget, 2022, visited on 2024-04-23. [Online]. Avail-
able: [Link]
[12] C. Rohlf, “Memory Safety: An Explainer”, CSET, 2023, visited on 2024-04-23. [Online]. Avail-
able: [Link]
[13] C. N. S. Klabnik, The Rust Programming Language, 2nd Edition. No Starch Press, 2022.
[14] K. Oleksandr, “Demystifying Rust Performance: Strategies For Faster Code”, 2023, visited
on 2024-03-28. [Online]. Available: https : / / marketsplash . com / tutorials / rust / rust -
performance/.
[15] Enums - Rust By Example, Visited on 2024-05-03. [Online]. Available: [Link]
[Link]/rust-by-example/custom_types/[Link].
[16] S. Fackler, Crate fallible streaming iterator, visited on 2024-05-15. [Online]. Available: https:
//[Link]/fallible-streaming-iterator/0.1.9/fallible_streaming_iterator/index.
html.
[17] M. Goodwin, What is an API?, visited on 2024-05-06. [Online]. Available: [Link]
com/topics/api.
[18] Amazon Web Services, What is SQL (Structured Query Language)?, visited on 2024-04-23.
[Online]. Available: [Link]
[19] About us - Mimer, Visited on 2024-03-26. [Online]. Available: [Link]
us/.
[20] Mimer Information Technology, “Mimer SQL 11.0.8: A Powerhouse Of Performance, Scalability,
And Versatility”, Mimer, 2024, visited on 2024-04-16. [Online]. Available: [Link]
com/mimer-sql-11-0-8-a-powerhouse-of-performance-scalability-and-versatility/.

20
Course: Independent Project in Information Engineering

[21] Mimer SQL 11.0 Technical description, Visited on 2024-03-26, Mimer Information Technol-
ogy. [Online]. Available: [Link]
[Link].
[22] Mimer Information Technology, Transaction Concurrency – Optimistic Concurrency Control, vis-
ited on 2024-04-23. [Online]. Available: [Link]
concurrency-optimistic-concurrency-control/.
[23] Mimer SQL C API, Visited on 2024-03-25. [Online]. Available: https : / / developer . mimer .
com/article/mimer-sql-c-api/.
[24] O. Gorpynich, “What is FFI (Foreign Function Interface) — An Intuitive Explanation”, Medium,
2023. [Online]. Available: https : / / levelup . gitconnected . com / what - is - ffi - foreign -
function-interface-an-intuitive-explanation-7327444e347a.
[25] Oracle, Database, visited on 2024-05-16. [Online]. Available: https : / / www . oracle . com /
database/.
[26] SQLite, About SQLite, visited on 2024-05-16. [Online]. Available: [Link]
[Link].
[27] The PostgreSQL Global Development Group, About PostgreSQL, visited on 2024-05-16. [On-
line]. Available: [Link]
[28] Oracle, Oracle Products, Solutions, and Services, visited on 2024-05-16. [Online]. Available:
[Link]
[29] SQLite, SQLite Is Serverless, visited on 2024-05-06. [Online]. Available: [Link]
org/[Link].
[30] The PostgreSQL 16 manual chapter 1.2, Visited on 2024-03-26, The PostgreSQL Global Devel-
opment Group. [Online]. Available: [Link]
[Link].
[31] Products - Mimer, Visited on 2024-05-06. [Online]. Available: https : / / www . mimer . com /
products-new/.
[32] Native bindings to the libsqlite3 library, Visited on 2024-03-26, Rust Foundation. [Online]. Avail-
able: [Link]
[33] Postgres, Visited on 2024-04-08, Rust Foundation. [Online]. Available: [Link]
crates/postgres.
[34] S. Fackler, Rust-postgres, visited on 2024-05-06. [Online]. Available: [Link]
sfackler/rust-postgres.
[35] Database APIs, Visited on 2024-04-08. [Online]. Available: [Link]
features/database-apis/.
[36] MimerPy – Python database interface for Mimer SQL, Visited on 2024-04-08. [Online]. Avail-
able: [Link]
[37] T. Hamilton, “What is Software Testing?”, Guru99, 2024, visited on 2024-05-06. [Online]. Avail-
able: [Link]
[38] The Valgrind Developers, About Valgrind, visited on 2024-05-03. [Online]. Available: https :
//[Link]/info/[Link].
[39] Cargo-bench(1), visited on 2024-05-02, The Rust Foundation. [Online]. Available: https : / /
[Link]/cargo/commands/[Link].
[40] gettimeofday(2) — Linux manual page, visited on 2024-05-02, [Link]. [Online]. Available:
[Link]
[41] B. Heisler, [Link] Documentation, visited on 2024-05-02. [Online]. Available: https : / /
[Link]/[Link]/book/criterion_rs.html.
[42] Creating a Mimer SQL Database, Visited on 2024-03-25. [Online]. Available: [Link]
[Link]/MimerSqlManual/latest/#t=Manuals%2FEstab_DB%2FEstab_DB.htm.
[43] SQL (Structured Query Language) Injection, Visited on 2024-04-23. [Online]. Available: https:
//[Link]/learn/application-security/sql-injection-sqli/.
[44] The Rust Programming Language, The bindgen User Guide, Visited on 2024-05-21. [Online].
Available: [Link]

21
Course: Independent Project in Information Engineering

[45] A. Crichton, “Rust Once, Run Everywhere”, Rust-blog, 2015. [Online]. Available: https : / /
[Link]/2015/04/24/[Link].

22

You might also like