Skip to content
This repository was archived by the owner on Aug 31, 2021. It is now read-only.

Commit f9106bd

Browse files
committed
[[ Sqlite382 ]] Added second option parameter to connection - 'binary' and 'extensions'
[[ Sqlite382 ]] If 'binary' specified then no binary encode/decode is done on BLOB columns - the data is stored in sqlite verbatim. [[ Sqlite382 ]] If 'extensions' specified then loading extensions is enabled.
1 parent 049575b commit f9106bd

File tree

4 files changed

+162
-21
lines changed

4 files changed

+162
-21
lines changed

revdb/src/dbsqlite.h

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ along with LiveCode. If not see <http://www.gnu.org/licenses/>. */
4444
class DBCursor_SQLITE : public CDBCursor
4545
{
4646
public:
47-
DBCursor_SQLITE(SqliteDatabase &db);
47+
DBCursor_SQLITE(SqliteDatabase &db, bool enable_binary);
4848
virtual ~DBCursor_SQLITE();
4949

5050
Bool open(DBConnection *newconnection);
@@ -62,6 +62,9 @@ class DBCursor_SQLITE : public CDBCursor
6262
Bool getFieldsInformation();
6363
SqliteDatabase &mDB;
6464
Dataset *mDataset;
65+
66+
// MW-2014-01-29: [[ Sqlite382 ]] If true the binary data will not be decoded.
67+
bool m_enable_binary : 1;
6568
};
6669

6770
class DBConnection_SQLITE : public CDBConnection
@@ -71,7 +74,8 @@ class DBConnection_SQLITE : public CDBConnection
7174
~DBConnection_SQLITE();
7275

7376
Bool IsError();
74-
77+
bool IsBinaryEnabled(void);
78+
7579
Bool connect(char **args, int numargs);
7680

7781
void disconnect();
@@ -101,5 +105,8 @@ class DBConnection_SQLITE : public CDBConnection
101105
SqliteDatabase mDB;
102106
char *mErrorStr;
103107
bool mIsError;
108+
109+
bool m_enable_extensions : 1;
110+
bool m_enable_binary : 1;
104111
};
105112
#endif

revdb/src/sqlite_connection.cpp

Lines changed: 105 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ DBConnection_SQLITE::DBConnection_SQLITE() :
3131
mIsError(false)
3232
{
3333
connectionType = CT_SQLITE;
34+
35+
// MW-2014-01-29: [[ Sqlite382 ]] Make sure options are set to defaults (false).
36+
m_enable_binary = false;
37+
m_enable_extensions = false;
3438
}
3539

3640
DBConnection_SQLITE::~DBConnection_SQLITE()
@@ -39,6 +43,11 @@ DBConnection_SQLITE::~DBConnection_SQLITE()
3943
disconnect();
4044
}
4145

46+
bool DBConnection_SQLITE::IsBinaryEnabled(void)
47+
{
48+
return m_enable_binary;
49+
}
50+
4251
// Only 1 arguments are expected.
4352
// args[0]: --unused--
4453
// args[1]: filename of database
@@ -50,7 +59,8 @@ Bool DBConnection_SQLITE::connect(char **args, int numargs)
5059
Bool ret = False;
5160

5261

53-
if(!isConnected && numargs > 1) {
62+
// MW-2014-01-29: [[ Sqlite382 ]] We only need a minimum of one argument.
63+
if(!isConnected && numargs >= 1) {
5464
char *fname;
5565
string bhash;
5666
string mash;
@@ -61,6 +71,36 @@ Bool DBConnection_SQLITE::connect(char **args, int numargs)
6171
// path encoded as UTF-8.
6272
fname = os_path_to_native_utf8(args[0]);
6373

74+
// MW-2014-01-29: [[ Sqlite382 ]] If there's a second argument, then interpret
75+
// it as an options string.
76+
if (numargs >= 2)
77+
{
78+
const char *t_start;
79+
t_start = args[1];
80+
for(;;)
81+
{
82+
// Find the end of the item (delimited by ',').
83+
const char *t_end;
84+
t_end = strchr(t_start, ',');
85+
if (t_end == NULL)
86+
t_end = t_start + strlen(t_start);
87+
88+
// Check to see if we recognise the option (ignoring ones we don't know
89+
// anything about).
90+
if ((t_end - t_start) == 6 && strncasecmp(t_start, "binary", 6) == 0)
91+
m_enable_binary = true;
92+
if ((t_end - t_start) == 10 && strncasecmp(t_start, "extensions", 10) == 0)
93+
m_enable_extensions = true;
94+
95+
// If the end points to NUL we are done.
96+
if (*t_end == '\0')
97+
break;
98+
99+
// Start is the char after the separating ','.
100+
t_start = t_end + 1;
101+
}
102+
}
103+
64104
try
65105
{
66106
mDB.setDatabase(fname);
@@ -72,13 +112,18 @@ Bool DBConnection_SQLITE::connect(char **args, int numargs)
72112
{
73113
ret = True;
74114
isConnected = True;
115+
116+
// MW-2014-01-29: [[ Sqlite382 ]] Now we have a handle, configure extension
117+
// loading.
118+
sqlite3_enable_load_extension(mDB . getHandle(), m_enable_extensions ? 1 : 0);
75119
}
76120
}
77121
catch(DbErrors &e)
78122
{
79123
mIsError = true;
80124
setErrorStr(e.getMsg());
81125
}
126+
82127
free(fname);
83128
}
84129
return ret;
@@ -193,7 +238,7 @@ DBCursor *DBConnection_SQLITE::sqlQuery(char *query, DBString *args, int numargs
193238
}
194239

195240
try {
196-
ret = new DBCursor_SQLITE(mDB);
241+
ret = new DBCursor_SQLITE(mDB, m_enable_binary);
197242
Dataset *ds = ret->getDataset();
198243

199244
ds->query(newquery);
@@ -325,6 +370,13 @@ char *replaceString(char *p_string, char *p_find, char *p_replace)
325370
return t_output_buffer_copy;
326371
}
327372

373+
static char num2nibble(int p_nibble)
374+
{
375+
if (p_nibble < 10)
376+
return '0' + p_nibble;
377+
return 'A' + (p_nibble - 10);
378+
}
379+
328380
bool queryCallback(void *p_context, int p_placeholder, DBBuffer& p_output)
329381
{
330382
QueryMetadata *t_query_metadata;
@@ -339,11 +391,45 @@ bool queryCallback(void *p_context, int p_placeholder, DBBuffer& p_output)
339391
size_t t_escaped_string_length;
340392
t_escaped_string_length = 0;
341393

394+
// MW-2014-01-29: [[ Sqlite382 ]] If true the value needs quoting, otherwise it is
395+
// already quoted appropriately.
396+
bool t_needs_quotes;
397+
t_needs_quotes = true;
342398
if (t_parameter_value . isbinary)
343399
{
344-
// According to documentation in sqlitedecode.cpp, this is the required size of output buffer
345-
t_escaped_string = malloc(2 + (257 * t_parameter_value . length) / 254);
346-
t_escaped_string_length = sqlite_encode_binary((const unsigned char *)t_parameter_value . sptr, t_parameter_value . length, (unsigned char *)t_escaped_string);
400+
DBConnection_SQLITE *t_conn;
401+
t_conn = (DBConnection_SQLITE *)t_query_metadata -> connection;
402+
403+
if (t_conn -> IsBinaryEnabled())
404+
{
405+
// MW-2014-01-29: [[ Sqlite382 ]] Encode the binary as BLOB literal - X'<hex>'. Thus
406+
// the length of the escaped string is 3 + 2 * size.;
407+
t_escaped_string_length = 3 + 2 * t_parameter_value . length;
408+
t_escaped_string = malloc(t_escaped_string_length);
409+
t_needs_quotes = false;
410+
411+
// Quote the binary!
412+
char *t_buffer;
413+
t_buffer = (char *)t_escaped_string;
414+
415+
*t_buffer++ = 'X';
416+
*t_buffer++ = '\'';
417+
for(size_t i = 0; i < t_parameter_value . length; i++)
418+
{
419+
char t_high_nibble, t_low_nibble;
420+
t_high_nibble = num2nibble(((unsigned)t_parameter_value . sptr[i] & 0xff) >> 4);
421+
t_low_nibble = num2nibble(((unsigned)t_parameter_value . sptr[i]) & 0xf);
422+
*t_buffer++ = t_high_nibble;
423+
*t_buffer++ = t_low_nibble;
424+
}
425+
*t_buffer++ = '\'';
426+
}
427+
else
428+
{
429+
// According to documentation in sqlitedecode.cpp, this is the required size of output buffer
430+
t_escaped_string = malloc(2 + (257 * t_parameter_value . length) / 254);
431+
t_escaped_string_length = sqlite_encode_binary((const unsigned char *)t_parameter_value . sptr, t_parameter_value . length, (unsigned char *)t_escaped_string);
432+
}
347433
}
348434
else
349435
{
@@ -363,18 +449,26 @@ bool queryCallback(void *p_context, int p_placeholder, DBBuffer& p_output)
363449
}
364450
}
365451

366-
p_output . ensure(t_escaped_string_length + 2);
367-
memcpy(p_output . getFrontier(), "'", 1);
368-
p_output . advance(1);
452+
if (t_needs_quotes)
453+
{
454+
p_output . ensure(t_escaped_string_length + 2);
455+
memcpy(p_output . getFrontier(), "'", 1);
456+
p_output . advance(1);
457+
}
458+
else
459+
p_output . ensure(t_escaped_string_length);
369460

370461
if (t_escaped_string != NULL)
371462
{
372463
memcpy(p_output . getFrontier(), t_escaped_string, t_escaped_string_length);
373464
p_output . advance(t_escaped_string_length);
374465
}
375466

376-
memcpy(p_output . getFrontier(), "'", 1);
377-
p_output . advance(1);
467+
if (t_needs_quotes)
468+
{
469+
memcpy(p_output . getFrontier(), "'", 1);
470+
p_output . advance(1);
471+
}
378472

379473
free(t_escaped_string);
380474
return true;
@@ -398,6 +492,7 @@ char *DBConnection_SQLITE::BindVariables(char *p_query, int p_query_length, DBSt
398492
QueryMetadata t_query_metadata;
399493
t_query_metadata . argument_count = p_argument_count;
400494
t_query_metadata . arguments = p_arguments;
495+
t_query_metadata . connection = this;
401496

402497
DBBuffer t_query_buffer(t_parsed_query_length + 1);
403498

revdb/src/sqlite_cursor.cpp

Lines changed: 47 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,16 @@ last - move to last row of resultset
4040

4141
#include "dbsqlite.h"
4242

43+
#include <sqlitedecode.h>
44+
4345
#define ENTER
4446

45-
DBCursor_SQLITE::DBCursor_SQLITE(SqliteDatabase &db)
47+
DBCursor_SQLITE::DBCursor_SQLITE(SqliteDatabase &db, bool enable_binary)
4648
: mDB(db)
4749
{
4850
ENTER;
4951
mDataset = mDB.CreateDataset();
52+
m_enable_binary = enable_binary;
5053
}
5154

5255
DBCursor_SQLITE::~DBCursor_SQLITE()
@@ -283,6 +286,12 @@ Bool DBCursor_SQLITE::getFieldsInformation()
283286
MDEBUG0("Type: double\n");
284287
tfield->fieldType = FT_DOUBLE;
285288
break;
289+
case ft_Object :
290+
if (m_enable_binary)
291+
tfield -> fieldType = FT_BLOB;
292+
else
293+
tfield -> fieldType = FT_STRING;
294+
break;
286295
case ft_String :
287296
default:
288297
MDEBUG0("Type: string\n");
@@ -337,18 +346,48 @@ Bool DBCursor_SQLITE::getRowData()
337346
fields[i]->isNull = fv.get_isNull();
338347

339348
if(!fields[i]->isNull) {
349+
340350
std::string tmp = fv.get_asString();
341-
int bufsize = tmp.size();
342-
fields[i]->data = new char[tmp.size() + 1];
343-
351+
352+
// MW-2014-01-29: [[ Sqlite382 ]] If the field type is BLOB and we aren't in binary mode,
353+
// we must decode the data; otherwise we just use the contents directly.
354+
if (fv.get_fType() == ft_Object && !m_enable_binary)
355+
{
356+
// This code has moved from libsqlite/sqlitedataset.cpp:callback().
357+
int bufsize;
358+
bufsize = tmp.size();
359+
360+
char *mybuff;
361+
mybuff = (char *)malloc(bufsize);
362+
memset(mybuff,0,bufsize);
363+
bufsize = sqlite_decode_binary((const unsigned char *)tmp.c_str(), bufsize, ( unsigned char *)mybuff, bufsize);
364+
if (bufsize == -1)
365+
{
366+
fields[i] -> data = NULL;
367+
fields[i] -> dataSize = 0;
368+
fields[i] -> isNull = True;
369+
free(mybuff);
370+
}
371+
else
372+
{
373+
fields[i] -> data = mybuff;
374+
fields[i] -> dataSize = bufsize;
375+
}
376+
}
377+
else
378+
{
379+
int bufsize = tmp.size();
380+
fields[i]->data = new char[tmp.size() + 1];
381+
382+
memset(fields[i]->data,0,bufsize+1);
383+
memcpy(fields[i]->data, tmp.data(), tmp.size());
384+
fields[i]->dataSize = tmp.size();
385+
}
386+
344387
// OK-2009-05-26: [[Bug 8065]] - Because we copied the data in the line above,
345388
// We have to set FreeBuffer otherwise it will not be freed when the cursor is closed,
346389
// leading to memory leaks.
347390
fields[i] -> freeBuffer = True;
348-
349-
memset(fields[i]->data,0,bufsize+1);
350-
memcpy(fields[i]->data, tmp.data(), tmp.size());
351-
fields[i]->dataSize = tmp.size();
352391
}
353392

354393
MDEBUG("getRowData() -- fv=[%s]\n", fields[i]->data);

thirdparty

0 commit comments

Comments
 (0)