-
Notifications
You must be signed in to change notification settings - Fork 7
/
mksqlite.cpp
2792 lines (2363 loc) · 82.9 KB
/
mksqlite.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/**
* <!-- mksqlite: A MATLAB Interface to SQLite -->
*
* @file mksqlite.cpp
* @brief Main routine (\ref mexFunction())
* @details class implementations (SQLstack and Mksqlite)
* @authors Martin Kortmann <mail@kortmann.de>,
* Andreas Martin <andimartin@users.sourceforge.net>
* @version 2.14
* @date 2008-2024
* @copyright Distributed under BSD-2
* @pre
* @warning
* @bug
*/
/* following define is not really used yet, since this is only one module */
/// @cond
#define MAIN_MODULE
/// @endcond
//#include "config.h" // Defaults
//#include "global.hpp" // Global definitions and statuses
//#include "serialize.hpp" // Serialization of MATLAB variables (undocumented feature)
//#include "number_compressor.hpp" // Some compressing algorithms
//#include "typed_blobs.hpp" // Packing into typed blobs with variable type storage
//#include "utils.hpp" // Utilities
#include "sql_interface.hpp" // SQLite interface
//#include "locale.hpp" // (Error-)Messages
//#include <vector>
/// Returns 0 if \p strA and \p strB are equal (ignoring case)
#define STRMATCH(strA,strB) ( (strA) && (strB) && ( 0 == _strcmpi( (strA), (strB) ) ) )
/// Terminates the function immediately with an error message
#define FINALIZE_STR( message ) mexErrMsgTxt( message )
/// Terminates the function immediately with an error message
#define FINALIZE( identifier ) FINALIZE_STR( ::getLocaleMsg( identifier ) )
/// @cond
// design time assertion ensures int32_t and mwSize as 4 byte data representation
HC_COMP_ASSERT( sizeof(uint32_t)==4 && sizeof(mwSize)==4 );
// Static assertion: Ensure backward compatibility
HC_COMP_ASSERT( sizeof( TypedBLOBHeaderV1 ) == 36 );
/// @endcond
/// MEX Entry function declared the as pure C
extern "C" void mexFunction( int nlhs, mxArray*plhs[], int nrhs, const mxArray*prhs[] );
extern "C" int (*heapcheck_printf)(const char*, ...) = mexPrintf;
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
/**
* \brief SQLite interface stack
*
* Holds a defined number (COUNT_DB) of SQLite slots (with dbids) to
* facilitate usage of multiple parallel databases. Each database can
* be accessed by its dbid (database ID) as first optional parameter to
* mksqlite: \n
* \code
* dbid_1 = mksqlite( 1, 'open', 'first.db' ); // open 1st database with dbid=1
* dbid_2 = mksqlite( 2, 'open', 'second.db' ); // open 2nd database with dbid=2
* assert( dbid_1 == 1 && dbid_2 == 2 );
* query = mksqlite( 2, 'SELECT * FROM table' ); // access to 2nd database
* mksqlite( 0, 'close' ); // closes all databases
* \endcode
*/
static class SQLstack
{
public:
/// Stack size
enum { COUNT_DB = MKSQLITE_CONFIG_MAX_NUM_OF_DBS };
SQLstackitem m_db[COUNT_DB]; ///< SQLite database slots
int m_dbid; ///< recent selected database id, base 0
/// Standard Ctor (first database slot is default)
SQLstack(): m_dbid(0)
{
sqlite3_initialize();
};
/**
* \brief Dtor
*
* Closes all databases and shuts sqlite engine down.
*/
~SQLstack()
{
(void)closeAllDbs();
sqlite3_shutdown();
}
/// Returns the handle of the current database
SQLstackitem& current()
{
return m_db[m_dbid];
}
/// Checks if database \p newId is in valid range
bool isValidId( int newId )
{
return newId >= 0 && newId < COUNT_DB;
}
/// Makes \p newId as current database id
void switchTo( int newId )
{
assert( isValidId( newId ) );
m_dbid = newId;
}
/// Returns a new interface to the current database
SQLiface* createInterface()
{
return new SQLiface( current() );
}
/// Outputs current status for each database slot
void printStatuses( int dbid_req, int dbid )
{
if( dbid_req == 0 )
{
for( int i = 0; i < COUNT_DB; i++ )
{
PRINTF( "DB Handle %d: %s\n", i+1, m_db[i].isOpen() ? "OPEN" : "CLOSED" );
}
}
else
{
dbid = (dbid_req > 0) ? dbid_req : dbid;
PRINTF( "DB Handle %d: %s\n", dbid, m_db[dbid-1].isOpen() ? "OPEN" : "CLOSED" );
}
}
/// Returns the first next free id slot (base 0). Database must be closed
int getNextFreeId()
{
/*
* If there isn't an database id, then try to get one
*/
for( int i = 0; i < COUNT_DB; i++ )
{
if( !m_db[i].isOpen() )
{
return i;
}
}
return -1; // no free slot available
}
/// Closes all open databases and returns the number of closed DBs, if any open
int closeAllDbs()
{
SQLerror err;
int nClosed = 0;
for( int i = 0; i < COUNT_DB; i++ )
{
if( m_db[i].isOpen() )
{
m_db[i].closeDb( err );
nClosed++;
}
}
return nClosed;
}
} SQLstack; ///< Holding the SQLiface slots
/**
* \brief Module deinitialization
*
* Closes all open databases and deinit blosc environment
*/
void mex_module_deinit()
{
if( SQLstack.closeAllDbs() > 0 )
{
/*
* inform the user, databases have been closed
*/
mexWarnMsgTxt( ::getLocaleMsg( MSG_CLOSINGFILES ) );
}
#if MKSQLITE_CONFIG_USE_BLOSC
blosc_destroy();
#endif
}
/**
* \brief Module initialization
*
* Get platform information and initializes blosc.
*/
void mex_module_init()
{
static bool is_initialized = false; // only one initialization per module
if( !is_initialized )
{
mxArray *plhs[3] = {0};
if( 0 == mexCallMATLAB( 3, plhs, 0, NULL, "computer" ) )
{
#if MKSQLITE_CONFIG_USE_BLOSC
g_compression_type = BLOSC_DEFAULT_ID;
blosc_init();
#endif
mexAtExit( mex_module_deinit );
typed_blobs_init();
PRINTF( ::getLocaleMsg( MSG_HELLO ),
SQLITE_VERSION );
PRINTF( "Platform: %s, %s\n\n",
TBH_platform,
TBH_endian[0] == 'L' ? "little endian" : "big endian" );
is_initialized = true;
}
else
{
FINALIZE( MSG_ERRPLATFORMDETECT );
}
if( 0 == mexCallMATLAB( 1, plhs, 0, NULL, "namelengthmax" ) )
{
g_namelengthmax = (int)mxGetScalar( plhs[0] );
}
#if CONFIG_USE_HEAP_CHECK
PRINTF( "Heap checking is on, this may slow down execution time dramatically!\n" );
#endif
}
}
/**
* \brief Transfer fetched SQL value into MATLAB array
*
* @param[in] value encapsulated SQL field value
* @param[out] err_id Error ID (see \ref MSG_IDS)
* @returns a MATLAB array due to value type (string or numeric content)
*
* @see g_result_type
*/
ValueMex createItemFromValueSQL( const ValueSQL& value, int& err_id )
{
mxArray* item = NULL;
switch( value.m_typeID )
{
case SQLITE_NULL:
if( g_NULLasNaN )
{
item = mxCreateDoubleScalar( DBL_NAN );
}
else
{
item = mxCreateDoubleMatrix( 0, 0, mxREAL );
}
break;
case SQLITE_INTEGER:
item = mxCreateNumericMatrix( 1, 1, mxINT64_CLASS, mxREAL );
if(item)
{
*(sqlite3_int64*)mxGetData( item ) = value.m_integer;
}
break;
case SQLITE_FLOAT:
item = mxCreateDoubleScalar( value.m_float );
break;
case SQLITE_TEXT:
item = mxCreateString( value.m_text );
break;
case SQLITE_BLOB:
{
ValueMex blob(value.m_blob);
size_t blob_size = blob.ByData();
if( blob_size > 0 )
{
// check for typed BLOBs
if( !typed_blobs_mode_on() )
{
// BLOB has no type info, it's just an array of bytes
item = mxCreateNumericMatrix( (mwSize)blob_size, 1, mxUINT8_CLASS, mxREAL );
if( item )
{
memcpy( ValueMex(item).Data(), blob.Data(), blob_size );
}
}
else
{
// BLOB has type information and will be "unpacked"
const void* blob = ValueMex( value.m_blob ).Data();
double process_time = 0.0;
double ratio = 0.0;
int err_id;
/* blob_unpack() modifies g_finalize_msg */
err_id = blob_unpack( blob, blob_size, can_serialize(), &item, &process_time, &ratio );
}
}
else
{
// empty BLOB
item = mxCreateDoubleMatrix( 0, 0, mxREAL );
}
break;
} /* end case SQLITE_BLOB */
default:
assert(false);
break;
} /* end switch */
return ValueMex( item ).Adopt();
}
/**
* \brief Transfer MATLAB array into a SQL value
*
* @param[in] item encapsulated MATLAB array
* @param[in] bStreamable true, if serialization is active
* @param[out] iTypeComplexity see ValueMex::type_complexity_e
* @param[out] err_id Error ID (see \ref MSG_IDS)
* @returns a SQL value type
*
* @see g_result_type
*/
ValueSQL createValueSQLFromItem( const ValueMex& item, bool bStreamable, int& iTypeComplexity, int& err_id )
{
iTypeComplexity = item.Item() ? item.Complexity( bStreamable ) : ValueMex::TC_EMPTY;
ValueSQL value;
switch( iTypeComplexity )
{
case ValueMex::TC_COMPLEX:
// structs, cells and complex data
// can only be stored as officially undocumented byte stream feature
// (SQLite typed ByteStream BLOB)
if( !bStreamable || !typed_blobs_mode_on() )
{
err_id = MSG_INVALIDARG;
break;
}
/* fallthrough */
case ValueMex::TC_SIMPLE_ARRAY:
// multidimensional non-complex numeric or char arrays
// will be stored as vector(!).
// Caution: Array dimensions are lost, if you don't use neither typed blobs
// nor serialization
/* fallthrough */
case ValueMex::TC_SIMPLE_VECTOR:
// non-complex numeric vectors (SQLite BLOB)
if( !typed_blobs_mode_on() )
{
// BLOB without type information
value = ValueSQL( item.Item() );
}
else
{
// BLOB with type information. Data and structure types
// will be recovered, when fetched again
void* blob = NULL;
size_t blob_size = 0;
double process_time = 0.0;
double ratio = 0.0;
/* blob_pack() modifies g_finalize_msg */
err_id = blob_pack( item.Item(), bStreamable, &blob, &blob_size, &process_time, &ratio );
if( MSG_NOERROR == err_id )
{
value = ValueSQL( (char*)blob, blob_size );
}
}
break;
case ValueMex::TC_SIMPLE:
// 1-value non-complex scalar, char or simple string (SQLite simple types)
switch( item.ClassID() )
{
case ValueMex::LOGICAL_CLASS:
case ValueMex::INT8_CLASS:
case ValueMex::UINT8_CLASS:
case ValueMex::INT16_CLASS:
case ValueMex::INT32_CLASS:
case ValueMex::UINT16_CLASS:
case ValueMex::UINT32_CLASS:
// scalar integer value
value = ValueSQL( (sqlite3_int64)item.GetInt() );
break;
case ValueMex::INT64_CLASS:
// scalar integer value
value = ValueSQL( item.GetInt64() );
break;
case ValueMex::DOUBLE_CLASS:
case ValueMex::SINGLE_CLASS:
// scalar floating point value
value = ValueSQL( item.GetScalar() );
break;
case ValueMex::CHAR_CLASS:
{
// string argument
char* str_value = item.GetEncString();
if( !str_value )
{
err_id = MSG_ERRMEMORY;
}
else
{
value = ValueSQL( str_value );
}
break;
}
default:
// all other (unsuppored types)
err_id = MSG_INVALIDARG;
break;
} // end switch
break;
case ValueMex::TC_EMPTY:
break;
default:
// all other (unsuppored types)
err_id = MSG_INVALIDARG;
break;
}
return value;
}
/**
* \brief Main routine class
*
*/
class Mksqlite
{
int m_nlhs; ///< count of left hand side arguments
int m_narg; ///< count of right hand side arguments
mxArray** m_plhs; ///< pointer to current left hand side argument
const mxArray** m_parg; ///< pointer to current right hand side argument
char* m_command; ///< SQL command. Allocated and freed by this class
const char* m_query; ///< \p m_command, or a translation from \p m_command
int m_dbid_req; ///< requested database id (user input) -1="arg missing", 0="next free slot" or 1..COUNT_DB
int m_dbid; ///< selected database slot (1..COUNT_DB)
SQLerror m_err; ///< recent error
SQLiface* m_interface; ///< interface (holding current SQLite statement) to current database
/**
* \name Inhibit assignment, default and copy ctors
* @{ */
Mksqlite();
Mksqlite( const Mksqlite& );
Mksqlite& operator=( const Mksqlite& );
/** @} */
public:
/// Standard ctor
Mksqlite( int nlhs, mxArray** plhs, int nrhs, const mxArray** prhs )
: m_nlhs( nlhs ), m_plhs( plhs ),
m_narg( nrhs ), m_parg( prhs ),
m_command(NULL), m_query(NULL), m_dbid_req(-1), m_dbid(1), m_interface( NULL )
{
/*
* no argument -> fail
*/
if( nrhs < 1 )
{
PRINTF( "%s", ::getLocaleMsg( MSG_USAGE ) );
m_err.set( MSG_INVALIDARG );
}
}
/// Release object
void Release()
{
if( m_command )
{
::utils_free_ptr( m_command );
}
if( m_interface )
{
delete m_interface;
}
}
/// Dtor
~Mksqlite()
{
Release();
}
/// Returns true, if any error is pending
bool errPending()
{
return m_err.isPending();
}
/// Clear recent error
void errClear()
{
m_err.clear();
}
/**
* \brief Terminate function
*
* Aborts the running function with an error message. Allocated memory
* (by MATLAB allocation functions) is freed automatically
*/
void returnWithError()
{
const char *errId = NULL;
const char *errMsg = m_err.get( &errId );
assert( errPending() );
mexErrMsgIdAndTxt( errId ? errId : "MKSQLITE:ANY", errMsg );
}
/**
* \brief Ensuring current database is open
*
* Sets \p m_err to MSG_DBNOTOPEN, if not.
*
* \returns true if database is open
*/
bool ensureDbIsOpen()
{
// database must be opened to set busy timeout
if( !assureSQLinterface() || !m_interface->isOpen() )
{
m_err.set( MSG_DBNOTOPEN );
return false;
}
return true;
}
/**
* \brief Omits a warning if database is given but superfluous
*
* \returns true if dbid is undefined
*/
bool warnOnDefDbid()
{
if( m_dbid_req != -1 )
{
mexWarnMsgTxt( ::getLocaleMsg( MSG_DBID_SUPFLOUS ) );
return false;
}
return true;
}
/**
* @brief Creates a SQL interface if not already happen
*/
bool assureSQLinterface()
{
if( !m_interface && m_dbid > 0 )
{
m_interface = SQLstack.createInterface();
}
return m_interface != NULL;
}
/**
* \brief Get next integer from argument list
*
* \param[out] refValue Result will be returned in
* \param[in] asBoolInt If true, \p refValue will be true (1) or false (0) only
*
* Read an integer parameter at current argument read position, and
* write to \p refValue (as 0 or 1 if \p asBoolInt is set to true)
*/
bool argGetNextInteger( int& refValue, bool asBoolInt = false )
{
if( errPending() ) return false;
if( m_narg < 1 )
{
m_err.set( MSG_MISSINGARG );
return false;
}
else if( !mxIsNumeric( m_parg[0] ) )
{
m_err.set( MSG_NUMARGEXPCT );
return false;
}
refValue = ValueMex( m_parg[0] ).GetInt();
if( asBoolInt )
{
refValue = ( refValue != 0 );
}
m_parg++;
m_narg--;
return true;
}
/**
* \brief Get next value as function handle from argument list
*
* \param[out] refValue Result will be returned in
*
* Read next parameter at current argument read position, and
* write to \p refValue
*/
bool argGetNextFcnHandle( const mxArray*& refValue )
{
if( errPending() ) return false;
if( m_narg < 1 )
{
m_err.set( MSG_MISSINGARG );
return false;
}
else if( !mxIsEmpty( m_parg[0]) && mxGetClassID( m_parg[0] ) != mxFUNCTION_CLASS )
{
m_err.set( MSG_FCNHARGEXPCT );
return false;
}
refValue = m_parg[0];
m_parg++;
m_narg--;
return true;
}
/**
* \brief Get next value as literal argument from argument list
*
* \param[out] refValue Result will be returned in
*
* Read next parameter at current argument read position, and
* write to \p refValue
*/
bool argGetNextLiteral( const mxArray*& refValue )
{
if( errPending() ) return false;
if( m_narg < 1 )
{
m_err.set( MSG_MISSINGARG );
return false;
}
else if( mxGetClassID( m_parg[0] ) != mxCHAR_CLASS )
{
m_err.set( MSG_LITERALARGEXPCT );
return false;
}
refValue = m_parg[0];
m_parg++;
m_narg--;
return true;
}
/**
* \brief Get database ID from argument list
*
* Reads the next argument if it is numeric and a valid dbid.
* dbid must be in range 0..\p CONFIG_MAX_NUM_OF_DBS, where 0 has the special
* meaning that the first free slot will later be used.\n
* If the parameter is missing, \p m_dbid_req will be set to -1. \p m_dbid will
* be set to either -1 (argument missing) or a valid dbid 1..\p CONFIG_MAX_NUM_OF_DBS
*/
bool argTryReadValidDbid()
{
if( errPending() ) return false;
/*
* Check if the first argument is a number (base 1), then we have to use
* this number as the requested database id. A number of 0 is allowed and leads
* to find the first free slot.
*/
if( argGetNextInteger( m_dbid_req, /*asBoolInt*/ false ) )
{
if( !SQLstack.isValidId( m_dbid_req-1 ) && m_dbid_req != 0 )
{
m_err.set( MSG_INVALIDDBHANDLE );
return false;
}
} else {
m_err.clear(); // Discard errors
m_dbid_req = -1; // Flag argument is missing
}
// find a free database slot, if user entered 0
if( !m_dbid_req )
{
m_dbid = SQLstack.getNextFreeId();
if( !SQLstack.isValidId( m_dbid++ ) )
{
m_dbid = 0; // No free slot
}
} else {
// select database id or default (1) if no one is given
m_dbid = ( m_dbid_req < 0 ) ? 1 : m_dbid_req;
}
return true;
}
/**
* \brief Get command from argument list
*
* Read the command from current argument position, always a string and thus
* asserted.
*/
bool argReadCommand()
{
if( errPending() ) return false;
/*
* The next (or first if no db number available) is the m_command,
* it has to be a string.
* This fails also, if the first arg is a dbid and there is no
* further argument
*/
if( !m_narg || !mxIsChar( m_parg[0] ) )
{
PRINTF( "%s", ::getLocaleMsg( MSG_USAGE ) );
m_err.set( MSG_INVALIDARG );
return false;
}
else
{
/*
* Get the m_command string
*/
m_command = ValueMex( m_parg[0] ).GetString();
m_parg++;
m_narg--;
}
return true;
}
/**
* \brief Handle flag from command
*
* \param[in] strMatchFlagName Name of flag to test
* \param[out] refFlag Flag value if name matched
* \returns true, if flag could be assigned
*
* Test current command as flag parameter with it's new value.
* \p strMatchFlagName holds the name of the flag to test.
*/
bool cmdTryHandleFlag( const char* strMatchFlagName, int& refFlag )
{
if( errPending() || !STRMATCH( m_command, strMatchFlagName ) )
{
return false;
}
// Global command, dbid useless
warnOnDefDbid();
int iOldValue = refFlag;
if( m_narg > 1 )
{
m_err.set( MSG_UNEXPECTEDARG );
return false;
}
if( m_narg > 0 && !argGetNextInteger( refFlag, /*asBoolInt*/ true ) )
{
// argGetNextInteger() sets m_err
return false;
}
// always return old flag value
m_plhs[0] = mxCreateDoubleScalar( (double)iOldValue );
return true;
}
/**
* \brief Handle version commands
*
* \param[in] strCmdMatchVerMex Command name to get mex version
* \param[in] strCmdMatchVerSql Command name to get SQLite version
* \returns true on success
*
* Test current command as version query to sqlite or mksqlite version numbers.
* \p strCmdMatchVerMex and \p strCmdMatchVerSql hold the mksqlite command names.
* m_plhs[0] will be set to the corresponding version string.
*/
bool cmdTryHandleVersion( const char* strCmdMatchVerMex, const char* strCmdMatchVerSql )
{
if( errPending() ) return false;
if( STRMATCH( m_command, strCmdMatchVerMex ) )
{
// Global command, dbid useless
warnOnDefDbid();
if( m_narg > 0 )
{
m_err.set( MSG_UNEXPECTEDARG );
return false;
}
else
{
if( m_nlhs == 0 )
{
PRINTF( "mksqlite Version %s\n", MKSQLITE_CONFIG_VERSION_STRING );
}
else
{
m_plhs[0] = mxCreateString(MKSQLITE_CONFIG_VERSION_STRING);
}
}
return true;
}
else if( STRMATCH( m_command, strCmdMatchVerSql ) )
{
// Global command, dbid useless
warnOnDefDbid();
if( m_narg > 0 )
{
m_err.set( MSG_UNEXPECTEDARG );
return false;
}
else
{
if( m_nlhs == 0 )
{
PRINTF( "SQLite Version %s\n", SQLITE_VERSION_STRING );
}
else
{
m_plhs[0] = mxCreateString( SQLITE_VERSION_STRING );
}
}
return true;
}
return false;
}
/**
* \brief Handle typed BLOB settings command
*
* \param[in] strCmdMatchName Command name
* \returns true on success
*
* Try to interpret current command as blob mode setting
* \p strCmdMatchName hold the mksqlite command name.
* m_plhs[0] will be set to the old setting.
*/
bool cmdTryHandleTypedBlob( const char* strCmdMatchName )
{
if( errPending() || !STRMATCH( m_command, strCmdMatchName ) )
{
return false;
}
/*
* typedBLOBs setting:
* 0 --> no typed blobs, streaming off
* 1 --> typed blobs, streaming off
* 2 --> typed blobs, streaming on
*
* Streaming is only valid if typed blobs are enabled because
* one could not distinguish between byte arrays and a
* streamed MATLAB array.
*/
// Global command, dbid useless
warnOnDefDbid();
int old_mode = typed_blobs_mode_on();
if( old_mode && g_streaming )
{
old_mode = 2;
}
int new_mode = old_mode;
if( m_narg > 1 )
{
m_err.set( MSG_UNEXPECTEDARG );
return false;
}
if( m_narg > 0 && !argGetNextInteger( new_mode ) )
{
// argGetNextInteger() sets m_err
return false;
}
// action only if something changed
if( new_mode != old_mode )
{
if( new_mode < 0 || new_mode > 2 )
{
m_err.set( MSG_INVALIDARG );
return false;
}
typed_blobs_mode_set( new_mode > 0 );
g_streaming = (new_mode == 2);
}
// always return the old value
m_plhs[0] = mxCreateDoubleScalar( (double)old_mode );
return true;
}
/**
* \brief Handle command to (en-/dis-)able loading extensions
*
* \param[in] strCmdMatchName Command name
*
* Try to interpret current command as setting for sqlite extension enable
* \p strCmdMatchName holds the mksqlite command name.
*/
bool cmdTryHandleEnableExtension( const char* strCmdMatchName )
{
if( errPending() || !STRMATCH( m_command, strCmdMatchName ) )
{