Introduction
In this lesson, you will learn how to install the local SQLite management console, inspect SQLite databases using an online tool, and finally how to create and access a SQLite database from R and Java.
About SQLite
SQLite is a server-less, file-based relational database (although it might not be a database management system). It is purposefully lightweight in terms of installation, administration, and required computational resources. It runs on embedded computers, mobile systems (iOS and Android, to name two), and on all modern operating systems including Linux, MacOS, and Windows.
SQLite has the following key features:
- self-contained
- serverless
- zero-configuration
- transactional
Serverless means that it does not run in a separate process, rather all database processing (the “engine”) is compiled into the application. This is unlike server-based database management systems such as MySQL, Oracle, SQL Server, PostgreSQL, etc., which require a separate server process to run and be connected to by the application via a TCP/IP socket.
SQLite is among the most commonly used and most popular databases as it is often embedded within iOS and Android applications. It is ideal for learning and small database applications that do not require concurrent multi-user access to the database.
Client/Server Databases
In server-based databases, applications that need to access the database connect first via a TCP/IP port and then use that port to send database requests in the form of SQL statements and receive results in the form of tables. This type of architecture is generally called a two-tier client/server architecture.
The diagram below illustrates a typical two-teir client/server database architecture:
Client/Server Database Architecture
The client running a program containing embedded SQL or running an ad hoc query tool sends a SQL query (SELECT) to the database management system (typically running on a remote server). The DBMS interprets the query, formulates a query plan, and then accesses the stored data. The result of the query is returned to the client program as a result set in the form of a table. The client program then processes the data further, stores it locally, visualizes the data, or generates a report containing the data. Updates to the database are done similarly.
The key is that the database “engine” and all database processing occurs in a separate process that is most likely on a shared server computer. This makes concurrent access to the data from multiple applications simple. The database becomes a shared resource.
Architecture of SQLite
The architecture of SQLite is decidedly different. The database is all contained within a single file located on the client application’s storage system. The database is single-user and not shared. Processing of SQL statements all occur within the application’s SQLite library that is “compiled into” the application. There is no separate server. In fact, one does not even have to install SQLite as a separate program to access a SQLite database from a programming language such as R, Python, Java, C++, Racket, or others. In this regard, SQLite is very similar to Microsoft Access, another common server-less database.
The diagram below illustrates the file-based nature of SQLite.
Serverless Architecture of SQLite
The key is that all SQL processing actually happens within the process and memory space of the application that connected to SQLite. There is no separate server process. Of course, this makes shared access quite difficult – it is possible but requires file-locking which makes concurrent access very slow.
Managing SQLite Databases
SQLite databases can be managed in several ways:
sqlite3: The sqlite3 utility allows access to database files and administration using the command line. Download the utilities from http://sqlite.org.
DB Browser: A graphical user interface to managing a SQLite database, including table creation, ad hoc queries, schema management, and data inserts, updates, and deletes. Download the program from https://sqlitebrowser.org.
Programs: Using database management functions from programs, e.g., the functions from the RSQLite package in R, including dbListTables()
.
Connect to SQLite from R
This is not a complete tutorial on how to create and work with SQLite databases from R, rather this section seeks to show how to connect to a SQLite database from R.
To connect to or access a SQLite database from R requires two steps:
- load the RSQLite package
- call the function
dbConnect()
library(RSQLite)
dbcon <- dbConnect(RSQLite::SQLite(), "myDB.db")
If the database file does not already exist, a new database is created.
Once the connection to the database has been made, you can access the database from R using functions from the RSQLite package, such as dbExecute()
, dbGetQuery()
, and dbSendStatement()
. Other tutorials explain how to work with SQLite databases from R in more detail.
create table lessons (
lname text not null,
llength integer not null
);
Let’s add a few rows of data to the table so we can see how SQLite assigns a row id.
insert into lessons values
('Intro C++', 90),
('Intro Java', 90),
('SQL Joins', 60)
Now, finally, let’s run a query that returns data.
Table 1: 3 records
Intro C++ |
90 |
Intro Java |
90 |
SQL Joins |
60 |
So, now you have seen how to create, connect to, and work with a SQLite database in R.
Conclusion
SQLite is an important and popular relational database, albeit one that is file rather than server-based. However, it seamlessly integrates with R, Python, and many other programming language and supports standard SQL.
LS0tCnRpdGxlOiAiVGhlIFNRTGl0ZSBEYXRhYmFzZSIKcGFyYW1zOgogIGNhdGVnb3J5OiA3MAogIG51bWJlcjogODAxCiAgdGltZTogMzAKICBsZXZlbDogYmVnaW5uZXIKICB0YWdzOiAic3FsaXRlLHJlbGF0aW9uYWwgZGF0YWJhc2UsZGF0YWJhc2UsUixKYXZhIgogIGRlc2NyaXB0aW9uOiAiVGhpcyBsZXNzb24gaW50cm9kdWNlcyB0aGUgU1FMaXRlIHJlbGF0aW9uYWwgZGF0YWJhc2UgYW5kIGhvdwogICAgICAgICAgICAgICAgdG8gd29yayB3aXRoIHRoZSBkYXRhYmFzZSBmcm9tIHRoZSBjb25zb2xlLCBhbiBvbmxpbmUgaW50ZXJmYWNlLAogICAgICAgICAgICAgICAgUiwgYW5kIEphdmEuIgpkYXRlOiAiPHNtYWxsPmByIFN5cy5EYXRlKClgPC9zbWFsbD4iCmF1dGhvcjogIjxzbWFsbD5NYXJ0aW4gU2NoZWRsYmF1ZXI8L3NtYWxsPiIKZW1haWw6ICJtLnNjaGVkbGJhdWVyQG5ldS5lZHUiCmFmZmlsaXRhdGlvbjogIk5vcnRoZWFzdGVybiBVbml2ZXJzaXR5IgpvdXRwdXQ6IAogIGJvb2tkb3duOjpodG1sX2RvY3VtZW50MjoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICBjb2xsYXBzZWQ6IGZhbHNlCiAgICBudW1iZXJfc2VjdGlvbnM6IGZhbHNlCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCiAgICB0aGVtZTogc3BhY2VsYWIKICAgIGhpZ2hsaWdodDogdGFuZ28KLS0tCgotLS0KdGl0bGU6ICI8c21hbGw+YHIgcGFyYW1zJGNhdGVnb3J5YC5gciBwYXJhbXMkbnVtYmVyYDwvc21hbGw+PGJyLz48c3BhbiBzdHlsZT0nY29sb3I6ICMyRTQwNTM7IGZvbnQtc2l6ZTogMC45ZW0nPmByIHJtYXJrZG93bjo6bWV0YWRhdGEkdGl0bGVgPC9zcGFuPiIKLS0tCgpgYGB7ciBjb2RlPXhmdW46OnJlYWRfdXRmOChwYXN0ZTAoaGVyZTo6aGVyZSgpLCcvUi9faW5zZXJ0MkRCLlInKSksIGluY2x1ZGUgPSBGQUxTRX0KYGBgCgojIyBJbnRyb2R1Y3Rpb24KCkluIHRoaXMgbGVzc29uLCB5b3Ugd2lsbCBsZWFybiBob3cgdG8gaW5zdGFsbCB0aGUgbG9jYWwgU1FMaXRlIG1hbmFnZW1lbnQgY29uc29sZSwgaW5zcGVjdCBTUUxpdGUgZGF0YWJhc2VzIHVzaW5nIGFuIG9ubGluZSB0b29sLCBhbmQgZmluYWxseSBob3cgdG8gY3JlYXRlIGFuZCBhY2Nlc3MgYSBTUUxpdGUgZGF0YWJhc2UgZnJvbSBSIGFuZCBKYXZhLgoKIyMgQWJvdXQgU1FMaXRlCgpTUUxpdGUgaXMgYSBzZXJ2ZXItbGVzcywgZmlsZS1iYXNlZCByZWxhdGlvbmFsIGRhdGFiYXNlIChhbHRob3VnaCBpdCBtaWdodCBub3QgYmUgYSBkYXRhYmFzZSBtYW5hZ2VtZW50IHN5c3RlbSkuIEl0IGlzIHB1cnBvc2VmdWxseSBsaWdodHdlaWdodCBpbiB0ZXJtcyBvZiBpbnN0YWxsYXRpb24sIGFkbWluaXN0cmF0aW9uLCBhbmQgcmVxdWlyZWQgY29tcHV0YXRpb25hbCByZXNvdXJjZXMuIEl0IHJ1bnMgb24gZW1iZWRkZWQgY29tcHV0ZXJzLCBtb2JpbGUgc3lzdGVtcyAoaU9TIGFuZCBBbmRyb2lkLCB0byBuYW1lIHR3byksIGFuZCBvbiBhbGwgbW9kZXJuIG9wZXJhdGluZyBzeXN0ZW1zIGluY2x1ZGluZyBMaW51eCwgTWFjT1MsIGFuZCBXaW5kb3dzLgoKU1FMaXRlIGhhcyB0aGUgZm9sbG93aW5nIGtleSBmZWF0dXJlczoKCi0gICBzZWxmLWNvbnRhaW5lZAotICAgc2VydmVybGVzcwotICAgemVyby1jb25maWd1cmF0aW9uCi0gICB0cmFuc2FjdGlvbmFsCgpTZXJ2ZXJsZXNzIG1lYW5zIHRoYXQgaXQgZG9lcyBub3QgcnVuIGluIGEgc2VwYXJhdGUgcHJvY2VzcywgcmF0aGVyIGFsbCBkYXRhYmFzZSBwcm9jZXNzaW5nICh0aGUgImVuZ2luZSIpIGlzIGNvbXBpbGVkIGludG8gdGhlIGFwcGxpY2F0aW9uLiBUaGlzIGlzIHVubGlrZSBzZXJ2ZXItYmFzZWQgZGF0YWJhc2UgbWFuYWdlbWVudCBzeXN0ZW1zIHN1Y2ggYXMgTXlTUUwsIE9yYWNsZSwgU1FMIFNlcnZlciwgUG9zdGdyZVNRTCwgKmV0Yy4qLCB3aGljaCByZXF1aXJlIGEgc2VwYXJhdGUgc2VydmVyIHByb2Nlc3MgdG8gcnVuIGFuZCBiZSBjb25uZWN0ZWQgdG8gYnkgdGhlIGFwcGxpY2F0aW9uIHZpYSBhIFRDUC9JUCBzb2NrZXQuCgpTUUxpdGUgaXMgYW1vbmcgdGhlIG1vc3QgY29tbW9ubHkgdXNlZCBhbmQgbW9zdCBwb3B1bGFyIGRhdGFiYXNlcyBhcyBpdCBpcyBvZnRlbiBlbWJlZGRlZCB3aXRoaW4gaU9TIGFuZCBBbmRyb2lkIGFwcGxpY2F0aW9ucy4gSXQgaXMgaWRlYWwgZm9yIGxlYXJuaW5nIGFuZCBzbWFsbCBkYXRhYmFzZSBhcHBsaWNhdGlvbnMgdGhhdCBkbyBub3QgcmVxdWlyZSBjb25jdXJyZW50IG11bHRpLXVzZXIgYWNjZXNzIHRvIHRoZSBkYXRhYmFzZS4KCiMjIENsaWVudC9TZXJ2ZXIgRGF0YWJhc2VzCgpJbiBzZXJ2ZXItYmFzZWQgZGF0YWJhc2VzLCBhcHBsaWNhdGlvbnMgdGhhdCBuZWVkIHRvIGFjY2VzcyB0aGUgZGF0YWJhc2UgY29ubmVjdCBmaXJzdCB2aWEgYSBUQ1AvSVAgcG9ydCBhbmQgdGhlbiB1c2UgdGhhdCBwb3J0IHRvIHNlbmQgZGF0YWJhc2UgcmVxdWVzdHMgaW4gdGhlIGZvcm0gb2YgU1FMIHN0YXRlbWVudHMgYW5kIHJlY2VpdmUgcmVzdWx0cyBpbiB0aGUgZm9ybSBvZiB0YWJsZXMuIFRoaXMgdHlwZSBvZiBhcmNoaXRlY3R1cmUgaXMgZ2VuZXJhbGx5IGNhbGxlZCBhIHR3by10aWVyIGNsaWVudC9zZXJ2ZXIgYXJjaGl0ZWN0dXJlLgoKVGhlIGRpYWdyYW0gYmVsb3cgaWxsdXN0cmF0ZXMgYSB0eXBpY2FsIHR3by10ZWlyIGNsaWVudC9zZXJ2ZXIgZGF0YWJhc2UgYXJjaGl0ZWN0dXJlOgoKIVtDbGllbnQvU2VydmVyIERhdGFiYXNlIEFyY2hpdGVjdHVyZV0oaW1hZ2VzL1R3b1RpZXJDbGllbnRTZXJ2ZXJEQkFyY2hpdGVjdHVyZS5qcGcpCgpUaGUgY2xpZW50IHJ1bm5pbmcgYSBwcm9ncmFtIGNvbnRhaW5pbmcgZW1iZWRkZWQgU1FMIG9yIHJ1bm5pbmcgYW4gYWQgaG9jIHF1ZXJ5IHRvb2wgc2VuZHMgYSBTUUwgcXVlcnkgKFNFTEVDVCkgdG8gdGhlIGRhdGFiYXNlIG1hbmFnZW1lbnQgc3lzdGVtICh0eXBpY2FsbHkgcnVubmluZyBvbiBhIHJlbW90ZSBzZXJ2ZXIpLiBUaGUgREJNUyBpbnRlcnByZXRzIHRoZSBxdWVyeSwgZm9ybXVsYXRlcyBhIHF1ZXJ5IHBsYW4sIGFuZCB0aGVuIGFjY2Vzc2VzIHRoZSBzdG9yZWQgZGF0YS4gVGhlIHJlc3VsdCBvZiB0aGUgcXVlcnkgaXMgcmV0dXJuZWQgdG8gdGhlIGNsaWVudCBwcm9ncmFtIGFzIGEgcmVzdWx0IHNldCBpbiB0aGUgZm9ybSBvZiBhIHRhYmxlLiBUaGUgY2xpZW50IHByb2dyYW0gdGhlbiBwcm9jZXNzZXMgdGhlIGRhdGEgZnVydGhlciwgc3RvcmVzIGl0IGxvY2FsbHksIHZpc3VhbGl6ZXMgdGhlIGRhdGEsIG9yIGdlbmVyYXRlcyBhIHJlcG9ydCBjb250YWluaW5nIHRoZSBkYXRhLiBVcGRhdGVzIHRvIHRoZSBkYXRhYmFzZSBhcmUgZG9uZSBzaW1pbGFybHkuCgpUaGUga2V5IGlzIHRoYXQgdGhlIGRhdGFiYXNlICJlbmdpbmUiIGFuZCBhbGwgZGF0YWJhc2UgcHJvY2Vzc2luZyBvY2N1cnMgaW4gYSBzZXBhcmF0ZSBwcm9jZXNzIHRoYXQgaXMgbW9zdCBsaWtlbHkgb24gYSBzaGFyZWQgc2VydmVyIGNvbXB1dGVyLiBUaGlzIG1ha2VzIGNvbmN1cnJlbnQgYWNjZXNzIHRvIHRoZSBkYXRhIGZyb20gbXVsdGlwbGUgYXBwbGljYXRpb25zIHNpbXBsZS4gVGhlIGRhdGFiYXNlIGJlY29tZXMgYSBzaGFyZWQgcmVzb3VyY2UuCgojIyBBcmNoaXRlY3R1cmUgb2YgU1FMaXRlCgpUaGUgYXJjaGl0ZWN0dXJlIG9mIFNRTGl0ZSBpcyBkZWNpZGVkbHkgZGlmZmVyZW50LiBUaGUgZGF0YWJhc2UgaXMgYWxsIGNvbnRhaW5lZCB3aXRoaW4gYSBzaW5nbGUgZmlsZSBsb2NhdGVkIG9uIHRoZSBjbGllbnQgYXBwbGljYXRpb24ncyBzdG9yYWdlIHN5c3RlbS4gVGhlIGRhdGFiYXNlIGlzIHNpbmdsZS11c2VyIGFuZCBub3Qgc2hhcmVkLiBQcm9jZXNzaW5nIG9mIFNRTCBzdGF0ZW1lbnRzIGFsbCBvY2N1ciB3aXRoaW4gdGhlIGFwcGxpY2F0aW9uJ3MgU1FMaXRlIGxpYnJhcnkgdGhhdCBpcyAiY29tcGlsZWQgaW50byIgdGhlIGFwcGxpY2F0aW9uLiBUaGVyZSBpcyBubyBzZXBhcmF0ZSBzZXJ2ZXIuIEluIGZhY3QsIG9uZSBkb2VzIG5vdCBldmVuIGhhdmUgdG8gaW5zdGFsbCBTUUxpdGUgYXMgYSBzZXBhcmF0ZSBwcm9ncmFtIHRvIGFjY2VzcyBhIFNRTGl0ZSBkYXRhYmFzZSBmcm9tIGEgcHJvZ3JhbW1pbmcgbGFuZ3VhZ2Ugc3VjaCBhcyBSLCBQeXRob24sIEphdmEsIEMrKywgUmFja2V0LCBvciBvdGhlcnMuIEluIHRoaXMgcmVnYXJkLCBTUUxpdGUgaXMgdmVyeSBzaW1pbGFyIHRvIE1pY3Jvc29mdCBBY2Nlc3MsIGFub3RoZXIgY29tbW9uIHNlcnZlci1sZXNzIGRhdGFiYXNlLgoKVGhlIGRpYWdyYW0gYmVsb3cgaWxsdXN0cmF0ZXMgdGhlIGZpbGUtYmFzZWQgbmF0dXJlIG9mIFNRTGl0ZS4KCiFbU2VydmVybGVzcyBBcmNoaXRlY3R1cmUgb2YgU1FMaXRlXShpbWFnZXMvU2VydmVybGVzc1NRTGl0ZUFyY2hpdGVjdHVyZS5qcGcpCgpUaGUga2V5IGlzIHRoYXQgYWxsIFNRTCBwcm9jZXNzaW5nIGFjdHVhbGx5IGhhcHBlbnMgd2l0aGluIHRoZSBwcm9jZXNzIGFuZCBtZW1vcnkgc3BhY2Ugb2YgdGhlIGFwcGxpY2F0aW9uIHRoYXQgY29ubmVjdGVkIHRvIFNRTGl0ZS4gVGhlcmUgaXMgbm8gc2VwYXJhdGUgc2VydmVyIHByb2Nlc3MuIE9mIGNvdXJzZSwgdGhpcyBtYWtlcyBzaGFyZWQgYWNjZXNzIHF1aXRlIGRpZmZpY3VsdCAtLSBpdCBpcyBwb3NzaWJsZSBidXQgcmVxdWlyZXMgZmlsZS1sb2NraW5nIHdoaWNoIG1ha2VzIGNvbmN1cnJlbnQgYWNjZXNzIHZlcnkgc2xvdy4KCiMjIE1hbmFnaW5nIFNRTGl0ZSBEYXRhYmFzZXMKClNRTGl0ZSBkYXRhYmFzZXMgY2FuIGJlIG1hbmFnZWQgaW4gc2V2ZXJhbCB3YXlzOgoKMS4gICoqc3FsaXRlMyoqOiBUaGUgKnNxbGl0ZTMqIHV0aWxpdHkgYWxsb3dzIGFjY2VzcyB0byBkYXRhYmFzZSBmaWxlcyBhbmQgYWRtaW5pc3RyYXRpb24gdXNpbmcgdGhlIGNvbW1hbmQgbGluZS4gRG93bmxvYWQgdGhlIHV0aWxpdGllcyBmcm9tIDxodHRwOi8vc3FsaXRlLm9yZz4uCgoyLiAgKipEQiBCcm93c2VyKio6IEEgZ3JhcGhpY2FsIHVzZXIgaW50ZXJmYWNlIHRvIG1hbmFnaW5nIGEgU1FMaXRlIGRhdGFiYXNlLCBpbmNsdWRpbmcgdGFibGUgY3JlYXRpb24sIGFkIGhvYyBxdWVyaWVzLCBzY2hlbWEgbWFuYWdlbWVudCwgYW5kIGRhdGEgaW5zZXJ0cywgdXBkYXRlcywgYW5kIGRlbGV0ZXMuIERvd25sb2FkIHRoZSBwcm9ncmFtIGZyb20gPGh0dHBzOi8vc3FsaXRlYnJvd3Nlci5vcmc+LgoKMy4gICoqUHJvZ3JhbXMqKjogVXNpbmcgZGF0YWJhc2UgbWFuYWdlbWVudCBmdW5jdGlvbnMgZnJvbSBwcm9ncmFtcywgKmUuZy4qLCB0aGUgZnVuY3Rpb25zIGZyb20gdGhlICoqUlNRTGl0ZSoqIHBhY2thZ2UgaW4gUiwgaW5jbHVkaW5nIGBkYkxpc3RUYWJsZXMoKWAuCgojIyBDb25uZWN0IHRvIFNRTGl0ZSBmcm9tIFIKClRoaXMgaXMgbm90IGEgY29tcGxldGUgdHV0b3JpYWwgb24gaG93IHRvIGNyZWF0ZSBhbmQgd29yayB3aXRoIFNRTGl0ZSBkYXRhYmFzZXMgZnJvbSBSLCByYXRoZXIgdGhpcyBzZWN0aW9uIHNlZWtzIHRvIHNob3cgaG93IHRvIGNvbm5lY3QgdG8gYSBTUUxpdGUgZGF0YWJhc2UgZnJvbSBSLgoKVG8gY29ubmVjdCB0byBvciBhY2Nlc3MgYSBTUUxpdGUgZGF0YWJhc2UgZnJvbSBSIHJlcXVpcmVzIHR3byBzdGVwczoKCjEuICBsb2FkIHRoZSAqKlJTUUxpdGUqKiBwYWNrYWdlW14xXQoyLiAgY2FsbCB0aGUgZnVuY3Rpb24gPGNvZGU+ZGJDb25uZWN0KCk8L2NvZGU+CgpbXjFdOiBOYXR1cmFsbHksIHlvdSBtdXN0IGZpcnN0IGluc3RhbGwgdGhlICoqUlNRTGl0ZSoqIHBhY2thZ2UuIENoZWNrIExlc3NvbiBbNi4xMDEgRmlyc3QgU3RlcHMgaW4gUl0oaHR0cDovL2FydGlmaWNpdW0udXMvbGVzc29ucy8wNi5yL2wtNi0xMDEtZmlyc3Qtc3RlcHMtaW4tci9sLTYtMTAxLWZpcnN0LXN0ZXBzLWluLXIuaHRtbCNJbnN0YWxsaW5nX2FuZF9Mb2FkaW5nX1BhY2thZ2VzKSBmb3IgbW9yZSBpbmZvcm1hdGlvbiBvbiBob3cgdG8gd29yayB3aXRoIHBhY2thZ2VzLgoKYGBge3IgY3JlYXRlREIsIGVjaG89VCwgZXZhbD1UfQpsaWJyYXJ5KFJTUUxpdGUpCmRiY29uIDwtIGRiQ29ubmVjdChSU1FMaXRlOjpTUUxpdGUoKSwgIm15REIuZGIiKQpgYGAKCklmIHRoZSBkYXRhYmFzZSBmaWxlIGRvZXMgbm90IGFscmVhZHkgZXhpc3QsIGEgbmV3IGRhdGFiYXNlIGlzIGNyZWF0ZWQuCgpPbmNlIHRoZSBjb25uZWN0aW9uIHRvIHRoZSBkYXRhYmFzZSBoYXMgYmVlbiBtYWRlLCB5b3UgY2FuIGFjY2VzcyB0aGUgZGF0YWJhc2UgZnJvbSBSIHVzaW5nIGZ1bmN0aW9ucyBmcm9tIHRoZSAqKlJTUUxpdGUqKiBwYWNrYWdlLCBzdWNoIGFzIDxjb2RlPmRiRXhlY3V0ZSgpPC9jb2RlPiwgPGNvZGU+ZGJHZXRRdWVyeSgpPC9jb2RlPiwgYW5kIDxjb2RlPmRiU2VuZFN0YXRlbWVudCgpPC9jb2RlPi4gT3RoZXIgdHV0b3JpYWxzIGV4cGxhaW4gaG93IHRvIHdvcmsgd2l0aCBTUUxpdGUgZGF0YWJhc2VzIGZyb20gUiBpbiBtb3JlIGRldGFpbC4KCmBgYHtzcWwgY29ubmVjdGlvbj1kYmNvbiwgZWNobz1GfQpkcm9wIHRhYmxlIGlmIGV4aXN0cyBsZXNzb25zOwpgYGAKCmBgYHtzcWwgY29ubmVjdGlvbj1kYmNvbn0KY3JlYXRlIHRhYmxlIGxlc3NvbnMgKAogIGxuYW1lIHRleHQgbm90IG51bGwsCiAgbGxlbmd0aCBpbnRlZ2VyIG5vdCBudWxsCik7CmBgYAoKTGV0J3MgYWRkIGEgZmV3IHJvd3Mgb2YgZGF0YSB0byB0aGUgdGFibGUgc28gd2UgY2FuIHNlZSBob3cgU1FMaXRlIGFzc2lnbnMgYSByb3cgaWQuCgpgYGB7c3FsIGNvbm5lY3Rpb249ZGJjb259Cmluc2VydCBpbnRvIGxlc3NvbnMgdmFsdWVzCiAgKCdJbnRybyBDKysnLCA5MCksCiAgKCdJbnRybyBKYXZhJywgOTApLAogICgnU1FMIEpvaW5zJywgNjApCmBgYAoKTm93LCBmaW5hbGx5LCBsZXQncyBydW4gYSBxdWVyeSB0aGF0IHJldHVybnMgZGF0YS4KCmBgYHtzcWwgY29ubmVjdGlvbj1kYmNvbn0Kc2VsZWN0ICogZnJvbSBsZXNzb25zOwpgYGAKClNvLCBub3cgeW91IGhhdmUgc2VlbiBob3cgdG8gY3JlYXRlLCBjb25uZWN0IHRvLCBhbmQgd29yayB3aXRoIGEgU1FMaXRlIGRhdGFiYXNlIGluIFIuCgojIyBDb25jbHVzaW9uCgpTUUxpdGUgaXMgYW4gaW1wb3J0YW50IGFuZCBwb3B1bGFyIHJlbGF0aW9uYWwgZGF0YWJhc2UsIGFsYmVpdCBvbmUgdGhhdCBpcyBmaWxlIHJhdGhlciB0aGFuIHNlcnZlci1iYXNlZC4gSG93ZXZlciwgaXQgc2VhbWxlc3NseSBpbnRlZ3JhdGVzIHdpdGggUiwgUHl0aG9uLCBhbmQgbWFueSBvdGhlciBwcm9ncmFtbWluZyBsYW5ndWFnZSBhbmQgc3VwcG9ydHMgc3RhbmRhcmQgU1FMLgoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIyBGaWxlcyAmIFJlc291cmNlcwoKYGBge3IgemlwRmlsZXMsIGVjaG89RkFMU0V9CnppcE5hbWUgPSBzcHJpbnRmKCJMZXNzb25GaWxlcy0lcy0lcy56aXAiLCAKICAgICAgICAgICAgICAgICBwYXJhbXMkY2F0ZWdvcnksCiAgICAgICAgICAgICAgICAgcGFyYW1zJG51bWJlcikKCnRleHRBTGluayA9IHBhc3RlMCgiQWxsIEZpbGVzIGZvciBMZXNzb24gIiwgCiAgICAgICAgICAgICAgIHBhcmFtcyRjYXRlZ29yeSwiLiIscGFyYW1zJG51bWJlcikKCiMgZG93bmxvYWRGaWxlc0xpbmsoKSBpcyBpbmNsdWRlZCBmcm9tIF9pbnNlcnQyREIuUgprbml0cjo6cmF3X2h0bWwoZG93bmxvYWRGaWxlc0xpbmsoIi4iLCB6aXBOYW1lLCB0ZXh0QUxpbmspKQpgYGAKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyMgUmVmZXJlbmNlcwoKLSAgIFtTUUxpdGUgVHV0b3JpYWxdKGh0dHBzOi8vd3d3LnNxbGl0ZXR1dG9yaWFsLm5ldC8pCgojIyBFcnJhdGEKCltMZXQgdXMga25vd10oaHR0cHM6Ly9mb3JtLmpvdGZvcm0uY29tLzIxMjE4NzA3Mjc4NDE1Nyl7dGFyZ2V0PSJfYmxhbmsifS4K