Skip to main content

Reading a Sqlite database using client-side javascript

Ever had one of those problems at work that seems almost impossible (i.e. very hard) to solve at first? well i did and not so long ago, and tuns out that there is a solution to it.

Problem

Read the contents of a Sqlite database client-side and present them in an HTML based GUI. So my first reaction was, even if there is a solution to this, it surely cant be very efficient reading an entire Sqlite database client-side? well those were the requirements and there were some genuine reasons to back up those requirements, so i had to stop whinging and direct my efforts towards an actual solution.

Solution

Turns out that there is a solution for this! Ever heard of emscripten? Funnily enough i had used it while doing my research to port the OpenCV library to javascript. Anyway there is sql.js, which is a javascript port of the Sqlite database, which could do what i wanted it to do. The problem is the documentation of that project is somewhat lacking, so you are left to Stackoverflow posts and issues on their GitHub project page to figure out the solution to what you need.

Requirements

One of the columns in the database i was reading was of type BLOB and i had to parse the contents of that blob.
Ok enough talk now let’s talk about the solution, for the sake of keeping things simple,  let us say we are selecting the sqlite DB file from a web page. Something like this…
<input type="file" id="input" onchange="handleFiles(this.files)" />
The handleFiles function basically takes the file and reads it’s contents using a FileReader. The function looks something like this
function handleFiles(files) {
    var file = files[0];
    var reader = new FileReader();
    reader.readAsBinaryString(file);
    openSqliteDb(reader);
}
Once we have the file i.e.sqlite database, let us open the database and the openSqliteDb function is as follows.
function openSqliteDb(reader) {
    setTimeout(function () {
    if(reader.readyState == reader.DONE) {
      var database = SQL.open(bin2Array(reader.result));
      executeQuery("SELECT col1, col2, hex(col3) FROM table", database);
    } else {
      openSqliteDb(reader);
    }
}, 500);
So reading a file may take some time, so we use a timeout and we recursively keep trying till we have finished reading the file. Once the reader has read the file, we execute our query. Now if you look at the select query, it selects col1 and col2 and uses the hex() function to open col3. Now the hex function is a way to read the contents of a column which is of type blob. I was struggling over this till i found one of the sql.js project issues on their GitHub page. Anyway prior to finding that i too posted a question on Stackoverflow, which managed to give me alternatives to the solution i had.

Like the blog? Subscribe for updates

SELECT col1, col2, hex(col3) FROM table
Ok now let us have a look at the executeQuery function.
function executeQuery(commands, database) {
    commands = commands.replace(/n/g, '; ');
    try {
        var data = database.exec(commands);
        processData(data);
    } catch(e) {
        console.log(e);
    }
}
The data returned by the exec function is JSON data so we can use that data as follows. Here’s is an example of what the process function would look like.
function processData(data) {
    var colHeaders = [];
    var colMap = {};
    var tableContent = [];
    var tableHtml = [];
    tableHtml.push("<table border='1' cellspacing='2' cellpadding-'3'>");
    for (var i = 0; i < data.length; i++) {
        tableContent.push("<tr>");
        var dataElem = data[i];
        if (dataElem instanceof Array) {
            for (var j = 0; j < dataElem.length; j++) {
                var element = dataElem[j];
                if (element.column && !colMap[element.column]) {
                    colHeaders.push("<th>" + element.column + "</th>");
                    colMap[element.column] = colHeaders.length;
                    tableContent.push("<td>&nbsp;" + element.value + "</td>");
                } else {
                    tableContent.push("<td>&nbsp;" + element.value + "</td>");
                }
            }
        } else {
            if (element.column && !colMap[element.column]) {
                colHeaders.push(element.column);
                colMap[element.column] = colHeaders.length;
            }
        }
        tableContent.push("</tr>");
    }
    tableHtml.push(colHeaders.join(" "));
    tableHtml.push(tableContent.join(" "));
    tableHtml.push("</table>");
    document.getElementById("table").innerHTML = tableHtml.join(" ");
}

Conclusion

In this post we looked at how we can use read the contents of a Sqlite database with client-side Javascript, hopefully the above solution saves you the time that it took me to figure it all out.
As usual, if you find any of my posts useful support us by  buying or even trying one of our products and leave us a review on the app store.
‎My Day To-Do - Smart Task List
‎My Day To-Do Lite - Task list
‎Snap! I was there
Developer: Bhuman Soni
Price: $3.99
‎Numbers Game: Calculate Faster
Numbers Game: Calculation Master
‎Simple 'N' Easy Task List
Developer: Bhuman Soni
Price: Free
‎Captain's Personal Log
Developer: Bhuman Soni
Price: $4.99
My Simple Notes
Developer: Bhuman Soni
Price: Free
‎My Simple Notes - Dictate
Developer: Bhuman Soni
Price: Free

Comments

Popular posts from this blog

Upload to AWS S3 from Java API

In this post, you will see code samples for how to upload a file to AWS S3 bucket from a Java Spring Boot app. The code you will see here is from one of my open-source repositories on Github, called document-sharing. Problem Let’s say you are building a document sharing app where you allow your users to upload the file to a public cloud solution. Now, let’s say you are building the API for your app with Spring Boot and you are using AWS S3 as your public cloud solution. How would you do that? This blog post contains the code that can help you achieve that. Read more below,  Upload to AWS S3 bucket from Java Spring Boot app - My Day To-Do (mydaytodo.com)

Addressing app review rejections for auto-renewing subscription in-app purchase (iOS)

The ability to know what the weather is like while planning your day is a feature of  My Day To-Do  Pro and as of the last update it’s also a part of the  Lite version . Unlike the Pro version it’s an auto-renewing subscription based  in-app purchase (IAP)  in the Lite version. What means is that when a user purchases it, the user only pays for the subscription duration after which the user will be automatically charged for the next period. Adding an  auto-renewing  subscription based IAP proved to be somewhat challenging in terms of the app store review i.e. the app update was rejected by the App Review team thrice because of missing information about the IAP. Therefore in this post I will share my experiences and knowledge of adding auto-renewing IAP in hopes to save someone else the time that I had to spend on this problem. In-App purchase This year I started adding IAPs to My Day To-Do Lite which lead to learning about different types of IAP...

Serving HTML content in an iOS app that works in iOS 7 and later (using Swift)

As I have mentioned in an earlier post , I really enjoying coding in Swift. Now what am I doing with it? Well I am trying to build an HTML5 app that must work on devices with iOS 7. So in iOS8 apple has introduced a whole bunch of features that facilitate easy communication between web content and lets just call it back-end Swift code, but those features are not in iOS 7. So why do I want to build something that would work in an older OS? well I do not expect existing iOS users to upgrade to iOS 8 straight away and i also know a couple of people who would be very reluctant to upgrade their iPhones to iOS 8. Now in case you do not, you can have a read of the "Working with WebViews" section of this post , to know how to serve HTML content with WebViews. So when I started building my app, I wanted to know: How do I invoke some Swift code from my HTML content? Well the solution to this may feel a little bit "hacky" but it is a solution to achieve this.  The followi...