Skip to content

Commit 1a2c423

Browse files
committedDec 20, 2018
Fix Assert issue & Add StackOverflow db example
1 parent fddff95 commit 1a2c423

File tree

7 files changed

+328
-0
lines changed

7 files changed

+328
-0
lines changed
 

‎README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,5 +125,12 @@ SQLite console:
125125

126126
![](console_screenshot.png?raw=true)
127127

128+
Output of Querying StackOverflow DB through WebServer example:
129+
130+
![](output_web_so.png?raw=true)
131+
![](output_web_so_id.png?raw=true)
132+
![](output_web_so_name.png?raw=true)
133+
![](output_web_so_loc.png?raw=true)
134+
128135
## Issues
129136
Please contact the author (Arundale Ramanathan) at arun@siara.cc if you find any problem (or create issue here).
Lines changed: 316 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,316 @@
1+
/*
2+
This example shows how to retrieve data from Sqlite3 databases from SD Card
3+
through the Web Server and display in the form of HTML page.
4+
It also demonstrates query filtering by parameter passing and chunked encoding.
5+
Before running please copy 'so_users.db' to SD Card. Please see
6+
7+
https://github.com/siara-cc/stackoverflow_db
8+
9+
to find out how to obtain so_users.db
10+
11+
Please see https://github.com/siara-cc/esp32_arduino_sqlite3_lib/
12+
for more inforemation.
13+
14+
Copyright (c) 2018, Siara Logics (cc)
15+
*/
16+
17+
/*
18+
* Copyright (c) 2015, Majenko Technologies
19+
* All rights reserved.
20+
*
21+
* Redistribution and use in source and binary forms, with or without modification,
22+
* are permitted provided that the following conditions are met:
23+
*
24+
* * Redistributions of source code must retain the above copyright notice, this
25+
* list of conditions and the following disclaimer.
26+
*
27+
* * Redistributions in binary form must reproduce the above copyright notice, this
28+
* list of conditions and the following disclaimer in the documentation and/or
29+
* other materials provided with the distribution.
30+
*
31+
* * Neither the name of Majenko Technologies nor the names of its
32+
* contributors may be used to endorse or promote products derived from
33+
* this software without specific prior written permission.
34+
*
35+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
36+
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
37+
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
38+
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
39+
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
40+
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
41+
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
42+
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
43+
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
44+
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
45+
*/
46+
47+
#include <WiFi.h>
48+
#include <WiFiClient.h>
49+
#include <WebServer.h>
50+
#include <ESPmDNS.h>
51+
#include <sqlite3.h>
52+
#include <FS.h>
53+
#include "SD_MMC.h"
54+
55+
const char *ssid = "Nokia1";
56+
const char *password = "nokiafour";
57+
58+
WebServer server(80);
59+
60+
const int led = 13;
61+
62+
void handleRoot() {
63+
digitalWrite ( led, 1 );
64+
String temp;
65+
int sec = millis() / 1000;
66+
int min = sec / 60;
67+
int hr = min / 60;
68+
69+
temp = F("<html><head>\
70+
<title>ESP32 Demo to Query database on Micro SD</title>\
71+
<style>\
72+
body { font-family: Arial, Helvetica, Sans-Serif; font-size: large; Color: #000088; }\
73+
</style>\
74+
</head>\
75+
<body>\
76+
<h1>Hello from ESP32!</h1>\
77+
<h2>Query StackOverflow Users database on Micro SD Card</h2>\
78+
<p>StackOverflow publishes snapshot of its data periodically at archive.org \
79+
<a href='https://archive.org/download/stackexchange'>here</a> \
80+
and is <a href='https://ia800107.us.archive.org/27/items/stackexchange/license.txt'> \
81+
licensed under cc-by-sa 3.0</a>.</p>\
82+
<p><a href='https://github.com/siara-cc/stackoverflow_db'>This repository</a> \
83+
hosts StackOverflow User data imported into a convenient SQLite database\
84+
(size 1.94 GB and contains close to 10 million records). The date of snapshot is 3-Dec-2018.</p>\
85+
<p>This example shows how to retrieve data from this database copied to Micro SD Card \
86+
attached to ESP32 through its Web Server and display in the form of HTML page.</p>\
87+
<h3>Query by User Id</h3>\
88+
<form name='params' method='GET' action='query_db'>\
89+
Enter id: <input type=text style='font-size: large' value='5072621' name='so_id'/> \
90+
<input type=hidden value='' name='so_disp_name'/> \
91+
<p>(To find your id, see search box after clicking your profile icon on https://stackoverflow.com)</p>\
92+
<input type=submit style='font-size: large' value='Query database by Id'/>\
93+
</form>\
94+
<hr>\
95+
<h3>Query by Display name</h3>\
96+
<form name='params' method='GET' action='query_db'>\
97+
<input type=hidden value='' name='so_id'/> \
98+
Enter Display name: <input type=text style='font-size: large' value='roadrunner' name='so_disp_name'/> \
99+
<br><br><input type=submit style='font-size: large' value='Query database by Display Name'/>\
100+
</form>\
101+
<hr>\
102+
<h3>Aggregate Query by Location</h3>\
103+
<form name='params' method='GET' action='query_db'>\
104+
<input type=hidden value='' name='so_id'/> \
105+
<input type=hidden value='' name='so_disp_name'/> \
106+
Enter Location (blank for all): \
107+
<input type=text style='font-size: large' value='' name='so_loc'/> \
108+
<br>Minimum count:\
109+
<input type=text style='font-size: large' value='10000' name='so_loc_count'/> \
110+
<br><br><input type=submit style='font-size: large' value='Aggregate Query by Location'/>\
111+
</form>\
112+
<hr>\
113+
</body>\
114+
</html>");
115+
server.send(200, "text/html", temp.c_str());
116+
digitalWrite(led, 0);
117+
}
118+
119+
void handleNotFound() {
120+
digitalWrite(led, 1);
121+
String message = F("File Not Found\n\n");
122+
message += F("URI: ");
123+
message += server.uri();
124+
message += F("\nMethod: ");
125+
message += (server.method() == HTTP_GET) ? "GET" : "POST";
126+
message += F("\nArguments: ");
127+
message += server.args();
128+
message += F("\n");
129+
for ( uint8_t i = 0; i < server.args(); i++ ) {
130+
message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
131+
}
132+
server.send(404, "text/plain", message);
133+
digitalWrite(led, 0);
134+
}
135+
136+
sqlite3 *db1;
137+
int rc;
138+
sqlite3_stmt *res;
139+
int rec_count = 0;
140+
const char *tail;
141+
142+
int openDb(const char *filename, sqlite3 **db) {
143+
int rc = sqlite3_open(filename, db);
144+
if (rc) {
145+
Serial.print(F("Can't open database: "));
146+
Serial.println(sqlite3_errmsg(*db));
147+
return rc;
148+
} else {
149+
Serial.println(F("Opened database successfully"));
150+
}
151+
return rc;
152+
}
153+
154+
void setup(void) {
155+
pinMode(led, OUTPUT);
156+
digitalWrite(led, 0);
157+
Serial.begin(115200);
158+
WiFi.mode(WIFI_STA);
159+
WiFi.begin(ssid, password);
160+
Serial.println(F("Hello"));
161+
162+
// Wait for connection
163+
while (WiFi.status() != WL_CONNECTED) {
164+
delay(500);
165+
Serial.print(F("."));
166+
}
167+
168+
Serial.println("");
169+
Serial.print(F("Connected to "));
170+
Serial.println(ssid);
171+
Serial.print(F("IP address: "));
172+
Serial.println(WiFi.localIP());
173+
174+
if (MDNS.begin("esp32")) {
175+
Serial.println(F("MDNS responder started"));
176+
}
177+
178+
SD_MMC.begin();
179+
sqlite3_initialize();
180+
181+
// Open database
182+
if (openDb("/sdcard/so_users.db", &db1))
183+
return;
184+
185+
server.on("/", handleRoot);
186+
server.on("/query_db", []() {
187+
long start = micros();
188+
String sql = F("Select Count(*) From SO_Users Where ");
189+
if (server.arg("so_disp_name").length() > 0) {
190+
sql += F("DisplayName = '");
191+
sql += server.arg("so_disp_name");
192+
sql += "'";
193+
} else if (server.arg("so_id").length() > 0) {
194+
sql += F("Id = '");
195+
sql += server.arg("so_id");
196+
sql += F("'");
197+
} else {
198+
sql = "";
199+
}
200+
int step_res;
201+
if (sql.length() > 0) {
202+
rc = sqlite3_prepare_v2(db1, sql.c_str(), 1000, &res, &tail);
203+
if (rc != SQLITE_OK) {
204+
String resp = F("Failed to fetch data: ");
205+
resp += sqlite3_errmsg(db1);
206+
resp += F(".<br><br><input type=button onclick='location.href=\"/\"' value='back'/>");
207+
server.send ( 200, "text/html", resp.c_str());
208+
Serial.println(resp.c_str());
209+
return;
210+
}
211+
do {
212+
step_res = sqlite3_step(res);
213+
if (step_res == SQLITE_ROW) {
214+
rec_count = sqlite3_column_int(res, 0);
215+
if (rec_count > 5000) {
216+
String resp = F("Too many records: ");
217+
resp += rec_count;
218+
resp += F(". Please select different range");
219+
resp += F(".<br><br><input type=button onclick='location.href=\"/\"' value='back'/>");
220+
server.send ( 200, "text/html", resp.c_str());
221+
Serial.println(resp.c_str());
222+
sqlite3_finalize(res);
223+
return;
224+
}
225+
}
226+
} while (step_res != SQLITE_DONE && step_res != SQLITE_ERROR);
227+
sqlite3_finalize(res);
228+
} else
229+
rec_count = -1;
230+
231+
sql = F("Select * From SO_Users Where ");
232+
if (server.arg("so_disp_name").length() > 0) {
233+
sql += F("DisplayName = '");
234+
sql += server.arg("so_disp_name");
235+
sql += F("'");
236+
} else if (server.arg("so_id").length() > 0) {
237+
sql += F("Id = '");
238+
sql += server.arg("so_id");
239+
sql += F("'");
240+
} else {
241+
sql = F("Select Location, Count(*) Count From SO_Users ");
242+
if (server.arg("so_loc").length() > 0) {
243+
sql += F("Where Location = '");
244+
sql += server.arg("so_loc");
245+
sql += F("' ");
246+
} else
247+
sql += F("Where Location > '' ");
248+
sql += F("Group by Location ");
249+
if (server.arg("so_loc_count").length() > 0) {
250+
sql += F("Having Count(*) >= ");
251+
sql += server.arg("so_loc_count");
252+
}
253+
}
254+
rc = sqlite3_prepare_v2(db1, sql.c_str(), 1000, &res, &tail);
255+
if (rc != SQLITE_OK) {
256+
String resp = F("Failed to fetch data: ");
257+
resp += sqlite3_errmsg(db1);
258+
resp += F("<br><br><a href='/'>back</a>");
259+
server.send ( 200, "text/html", resp.c_str());
260+
Serial.println(resp.c_str());
261+
return;
262+
}
263+
264+
server.setContentLength(CONTENT_LENGTH_UNKNOWN);
265+
String resp = F("<!DOCTYPE html><html><head>\
266+
<title>StackOverflow Database query on ESP32 through web server</title>\
267+
<style>\
268+
body { font-family: Arial, Helvetica, Sans-Serif; font-size: large; Color: #000088; }\
269+
</style><head><body><h1>Query StackOverflow Users db on Micro SD card attached to ESP32 through its web server</h1><h3>");
270+
resp += sql;
271+
resp += F("</h3>");
272+
if (rec_count >= 0) {
273+
resp += F("<p>No. of records: ");
274+
resp += rec_count;
275+
resp += F("</p>");
276+
}
277+
resp += F("<table cellspacing='1' cellpadding='1' border='1'>");
278+
server.send(200, "text/html", resp.c_str());
279+
int cols = sqlite3_column_count(res);
280+
resp = F("<tr>");
281+
for (int i = 0; i < cols; i++) {
282+
resp += F("<td>");
283+
resp += (const char *) sqlite3_column_name(res, i);
284+
resp += F("</td>");
285+
}
286+
resp += F("</tr>");
287+
server.sendContent(resp);
288+
do {
289+
step_res = sqlite3_step(res);
290+
if (step_res == SQLITE_ROW) {
291+
resp = F("<tr>");
292+
for (int i = 0; i < cols; i++) {
293+
resp += F("<td>");
294+
resp += (const char *) sqlite3_column_text(res, i);
295+
resp += F("</td>");
296+
}
297+
resp += F("</tr>");
298+
server.sendContent(resp);
299+
rec_count++;
300+
}
301+
} while (step_res != SQLITE_DONE && step_res != SQLITE_ERROR);
302+
resp = F("</table>");
303+
resp += F("<br>Time taken (seconds): ");
304+
resp += (micros()-start)/1000000;
305+
resp += F("<br><br><input type=button onclick='location.href=\"/\"' value='back'/>");
306+
server.sendContent(resp);
307+
sqlite3_finalize(res);
308+
});
309+
server.onNotFound(handleNotFound);
310+
server.begin();
311+
Serial.println(F("HTTP server started"));
312+
}
313+
314+
void loop ( void ) {
315+
server.handleClient();
316+
}

‎output_web_so.png

152 KB
Loading

‎output_web_so_id.png

110 KB
Loading

‎output_web_so_loc.png

163 KB
Loading

‎output_web_so_name.png

222 KB
Loading

‎src/sqlite3.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13476,6 +13476,11 @@ SQLITE_PRIVATE void sqlite3HashClear(Hash*);
1347613476
#include <assert.h>
1347713477
#include <stddef.h>
1347813478

13479+
#ifdef NDEBUG
13480+
#undef assert
13481+
#define assert(e)
13482+
#endif
13483+
1347913484
/*
1348013485
** Use a macro to replace memcpy() if compiled with SQLITE_INLINE_MEMCPY.
1348113486
** This allows better measurements of where memcpy() is used when running

0 commit comments

Comments
 (0)
Please sign in to comment.