mksqlite  2.5
A MATLAB interface to SQLite
mksqlite.cpp
Go to the documentation of this file.
1 
17 /* following define is not really used yet, since this is only one module */
19 #define MAIN_MODULE
20 
22 //#include "config.h" // Defaults
23 //#include "global.hpp" // Global definitions and statuses
24 //#include "serialize.hpp" // Serialization of MATLAB variables (undocumented feature)
25 //#include "number_compressor.hpp" // Some compressing algorithms
26 //#include "typed_blobs.hpp" // Packing into typed blobs with variable type storage
27 //#include "utils.hpp" // Utilities
28 #include "sql_interface.hpp" // SQLite interface
29 //#include "locale.hpp" // (Error-)Messages
30 //#include <vector>
31 
33 #define STRMATCH(strA,strB) ( (strA) && (strB) && ( 0 == _strcmpi( (strA), (strB) ) ) )
34 #define FINALIZE_STR( message ) mexErrMsgTxt( message )
36 #define FINALIZE( identifier ) FINALIZE_STR( ::getLocaleMsg( identifier ) )
38 
40 
41 // design time assertion ensures int32_t and mwSize as 4 byte data representation
42 HC_COMP_ASSERT( sizeof(uint32_t)==4 && sizeof(mwSize)==4 );
43 // Static assertion: Ensure backward compatibility
44 HC_COMP_ASSERT( sizeof( TypedBLOBHeaderV1 ) == 36 );
45 
47 
49 extern "C" void mexFunction( int nlhs, mxArray*plhs[], int nrhs, const mxArray*prhs[] );
50 
51 
54 
70 static class SQLstack
71 {
72 public:
74  enum { COUNT_DB = CONFIG_MAX_NUM_OF_DBS };
75 
76  SQLstackitem m_db[COUNT_DB];
77  int m_dbid;
78 
79 
81  SQLstack(): m_dbid(0)
82  {
83  sqlite3_initialize();
84  };
85 
86 
93  {
94  (void)closeAllDbs();
95  sqlite3_shutdown();
96  }
97 
98 
101  {
102  return m_db[m_dbid];
103  }
104 
105 
107  bool isValidId( int newId )
108  {
109  return newId >= 0 && newId < COUNT_DB;
110  }
111 
112 
114  void switchTo( int newId )
115  {
116  assert( isValidId( newId ) );
117  m_dbid = newId;
118  }
119 
120 
123  {
124  return new SQLiface( current() );
125  }
126 
127 
129  void printStatuses( int dbid_req, int dbid )
130  {
131  if( dbid_req == 0 )
132  {
133  for( int i = 0; i < COUNT_DB; i++ )
134  {
135  PRINTF( "DB Handle %d: %s\n", i+1, m_db[i].isOpen() ? "OPEN" : "CLOSED" );
136  }
137  }
138  else
139  {
140  dbid = (dbid_req > 0) ? dbid_req : dbid;
141  PRINTF( "DB Handle %d: %s\n", dbid, m_db[dbid-1].isOpen() ? "OPEN" : "CLOSED" );
142  }
143  }
144 
145 
148  {
149  /*
150  * If there isn't an database id, then try to get one
151  */
152  for( int i = 0; i < COUNT_DB; i++ )
153  {
154  if( !m_db[i].isOpen() )
155  {
156  return i;
157  }
158  }
159 
160  return -1; // no free slot available
161  }
162 
163 
166  {
167  SQLerror err;
168  int nClosed = 0;
169 
170  for( int i = 0; i < COUNT_DB; i++ )
171  {
172  if( m_db[i].isOpen() )
173  {
174  m_db[i].closeDb( err );
175  nClosed++;
176  }
177  }
178 
179  return nClosed;
180  }
181 
182 } SQLstack;
183 
184 
191 {
192  if( SQLstack.closeAllDbs() > 0 )
193  {
194  /*
195  * inform the user, databases have been closed
196  */
197  mexWarnMsgTxt( ::getLocaleMsg( MSG_CLOSINGFILES ) );
198  }
199 
200  blosc_destroy();
201 }
202 
203 
210 {
211  static bool is_initialized = false; // only one initialization per module
212 
213  if( !is_initialized )
214  {
215  mxArray *plhs[3] = {0};
216 
217  if( 0 == mexCallMATLAB( 3, plhs, 0, NULL, "computer" ) )
218  {
219  g_compression_type = BLOSC_DEFAULT_ID;
220  blosc_init();
221  mexAtExit( mex_module_deinit );
223 
224  PRINTF( ::getLocaleMsg( MSG_HELLO ),
225  SQLITE_VERSION );
226 
227  PRINTF( "Platform: %s, %s\n\n",
228  TBH_platform,
229  TBH_endian[0] == 'L' ? "little endian" : "big endian" );
230 
231  is_initialized = true;
232  }
233  else
234  {
235  FINALIZE( MSG_ERRPLATFORMDETECT );
236  }
237 
238  if( 0 == mexCallMATLAB( 1, plhs, 0, NULL, "namelengthmax" ) )
239  {
240  g_namelengthmax = (int)mxGetScalar( plhs[0] );
241  }
242 
243 #if CONFIG_USE_HEAP_CHECK
244  PRINTF( "Heap checking is on, this may slow down execution time dramatically!\n" );
245 #endif
246 
247  }
248 }
249 
250 
260 ValueMex createItemFromValueSQL( const ValueSQL& value, int& err_id )
261 {
262  mxArray* item = NULL;
263 
264  switch( value.m_typeID )
265  {
266  case SQLITE_NULL:
267  if( g_NULLasNaN )
268  {
269  item = mxCreateDoubleScalar( DBL_NAN );
270  }
271  else
272  {
273  item = mxCreateDoubleMatrix( 0, 0, mxREAL );
274  }
275  break;
276 
277  case SQLITE_INTEGER:
278  item = mxCreateNumericMatrix( 1, 1, mxINT64_CLASS, mxREAL );
279 
280  if(item)
281  {
282  *(sqlite3_int64*)mxGetData( item ) = value.m_integer;
283  }
284  break;
285 
286  case SQLITE_FLOAT:
287  item = mxCreateDoubleScalar( value.m_float );
288  break;
289 
290  case SQLITE_TEXT:
291  item = mxCreateString( value.m_text );
292  break;
293 
294  case SQLITE_BLOB:
295  {
296  ValueMex blob(value.m_blob);
297  size_t blob_size = blob.ByData();
298 
299  if( blob_size > 0 )
300  {
301  // check for typed BLOBs
302  if( !typed_blobs_mode_on() )
303  {
304  // BLOB has no type info, it's just an array of bytes
305  item = mxCreateNumericMatrix( (mwSize)blob_size, 1, mxUINT8_CLASS, mxREAL );
306 
307  if( item )
308  {
309  memcpy( ValueMex(item).Data(), blob.Data(), blob_size );
310  }
311  }
312  else
313  {
314  // BLOB has type information and will be "unpacked"
315  const void* blob = ValueMex( value.m_blob ).Data();
316  double process_time = 0.0;
317  double ratio = 0.0;
318  int err_id;
319 
320  /* blob_unpack() modifies g_finalize_msg */
321  err_id = blob_unpack( blob, blob_size, can_serialize(), &item, &process_time, &ratio );
322  }
323  }
324  else
325  {
326  // empty BLOB
327  item = mxCreateDoubleMatrix( 0, 0, mxREAL );
328  }
329 
330  break;
331  } /* end case SQLITE_BLOB */
332 
333  default:
334  assert(false);
335  break;
336 
337  } /* end switch */
338 
339  return ValueMex( item ).Adopt();
340 }
341 
342 
354 ValueSQL createValueSQLFromItem( const ValueMex& item, bool bStreamable, int& iTypeComplexity, int& err_id )
355 {
356  iTypeComplexity = item.Item() ? item.Complexity( bStreamable ) : ValueMex::TC_EMPTY;
357 
358  ValueSQL value;
359 
360  switch( iTypeComplexity )
361  {
363  // structs, cells and complex data
364  // can only be stored as officially undocumented byte stream feature
365  // (SQLite typed ByteStream BLOB)
366  if( !bStreamable || !typed_blobs_mode_on() )
367  {
368  err_id = MSG_INVALIDARG;
369  break;
370  }
371 
372  /* fallthrough */
374  // multidimensional non-complex numeric or char arrays
375  // will be stored as vector(!).
376  // Caution: Array dimensions are lost, if you don't use neither typed blobs
377  // nor serialization
378 
379  /* fallthrough */
381  // non-complex numeric vectors (SQLite BLOB)
382  if( !typed_blobs_mode_on() )
383  {
384  // BLOB without type information
385  value = ValueSQL( item.Item() );
386  }
387  else
388  {
389  // BLOB with type information. Data and structure types
390  // will be recovered, when fetched again
391  void* blob = NULL;
392  size_t blob_size = 0;
393  double process_time = 0.0;
394  double ratio = 0.0;
395 
396  /* blob_pack() modifies g_finalize_msg */
397  err_id = blob_pack( item.Item(), bStreamable, &blob, &blob_size, &process_time, &ratio );
398 
399  if( MSG_NOERROR == err_id )
400  {
401  value = ValueSQL( (char*)blob, blob_size );
402  }
403  }
404  break;
405 
406  case ValueMex::TC_SIMPLE:
407  // 1-value non-complex scalar, char or simple string (SQLite simple types)
408  switch( item.ClassID() )
409  {
410  case ValueMex::LOGICAL_CLASS:
411  case ValueMex::INT8_CLASS:
412  case ValueMex::UINT8_CLASS:
413  case ValueMex::INT16_CLASS:
414  case ValueMex::INT32_CLASS:
415  case ValueMex::UINT16_CLASS:
416  case ValueMex::UINT32_CLASS:
417  // scalar integer value
418  value = ValueSQL( (sqlite3_int64)item.GetInt() );
419  break;
420 
421  case ValueMex::INT64_CLASS:
422  // scalar integer value
423  value = ValueSQL( item.GetInt64() );
424  break;
425 
426  case ValueMex::DOUBLE_CLASS:
427  case ValueMex::SINGLE_CLASS:
428  // scalar floating point value
429  value = ValueSQL( item.GetScalar() );
430  break;
431 
432  case ValueMex::CHAR_CLASS:
433  {
434  // string argument
435  char* str_value = item.GetEncString();
436 
437  if( !str_value )
438  {
439  err_id = MSG_ERRMEMORY;
440  }
441  else
442  {
443  value = ValueSQL( str_value );
444  }
445  break;
446  }
447 
448  default:
449  // all other (unsuppored types)
450  err_id = MSG_INVALIDARG;
451  break;
452 
453  } // end switch
454  break;
455 
456  case ValueMex::TC_EMPTY:
457  break;
458 
459  default:
460  // all other (unsuppored types)
461  err_id = MSG_INVALIDARG;
462  break;
463  }
464 
465  return value;
466 }
467 
468 
469 
470 
471 
472 
473 
478 class Mksqlite
479 {
480  int m_nlhs;
481  int m_narg;
482  mxArray** m_plhs;
483  const mxArray** m_parg;
484  char* m_command;
485  const char* m_query;
487  int m_dbid;
490 
494  Mksqlite();
495  Mksqlite( const Mksqlite& );
496  Mksqlite& operator=( const Mksqlite& );
499 public:
501  Mksqlite( int nlhs, mxArray** plhs, int nrhs, const mxArray** prhs )
502  : m_nlhs( nlhs ), m_plhs( plhs ),
503  m_narg( nrhs ), m_parg( prhs ),
504  m_command(NULL), m_query(NULL), m_dbid_req(-1), m_dbid(1), m_interface( NULL )
505  {
506  /*
507  * no argument -> fail
508  */
509  if( nrhs < 1 )
510  {
511  PRINTF( "%s", ::getLocaleMsg( MSG_USAGE ) );
512  m_err.set( MSG_INVALIDARG );
513  }
514  }
515 
516 
518  void Release()
519  {
520  if( m_command )
521  {
522  ::utils_free_ptr( m_command );
523  }
524 
525  if( m_interface )
526  {
527  delete m_interface;
528  }
529  }
530 
531 
534  {
535  Release();
536  }
537 
539  bool errPending()
540  {
541  return m_err.isPending();
542  }
543 
544 
546  void errClear()
547  {
548  m_err.clear();
549  }
550 
551 
559  {
560  const char *errId = NULL;
561  const char *errMsg = m_err.get( &errId );
562 
563  assert( errPending() );
564 
565  mexErrMsgIdAndTxt( errId ? errId : "MKSQLITE:ANY", errMsg );
566  }
567 
568 
577  {
578  // database must be opened to set busy timeout
579  if( !assureSQLinterface() || !m_interface->isOpen() )
580  {
581  m_err.set( MSG_DBNOTOPEN );
582  return false;
583  }
584 
585  return true;
586  }
587 
588 
595  {
596  if( m_dbid_req != -1 )
597  {
598  mexWarnMsgTxt( ::getLocaleMsg( MSG_DBID_SUPFLOUS ) );
599  return false;
600  }
601 
602  return true;
603  }
604 
605 
610  {
611  if( !m_interface && m_dbid > 0 )
612  {
613  m_interface = SQLstack.createInterface();
614  }
615 
616  return m_interface != NULL;
617  }
618 
619 
629  bool argGetNextInteger( int& refValue, bool asBoolInt = false )
630  {
631  if( errPending() ) return false;
632 
633  if( m_narg < 1 )
634  {
635  m_err.set( MSG_MISSINGARG );
636  return false;
637  }
638  else if( !mxIsNumeric( m_parg[0] ) )
639  {
640  m_err.set( MSG_NUMARGEXPCT );
641  return false;
642  }
643 
644  refValue = ValueMex( m_parg[0] ).GetInt();
645  if( asBoolInt )
646  {
647  refValue = ( refValue != 0 );
648  }
649 
650  m_parg++;
651  m_narg--;
652 
653  return true;
654  }
655 
656 
665  bool argGetNextFcnHandle( const mxArray*& refValue )
666  {
667  if( errPending() ) return false;
668 
669  if( m_narg < 1 )
670  {
671  m_err.set( MSG_MISSINGARG );
672  return false;
673  }
674  else if( !mxIsEmpty( m_parg[0]) && mxGetClassID( m_parg[0] ) != mxFUNCTION_CLASS )
675  {
676  m_err.set( MSG_FCNHARGEXPCT );
677  return false;
678  }
679 
680  refValue = m_parg[0];
681 
682  m_parg++;
683  m_narg--;
684 
685  return true;
686  }
687 
688 
697  bool argGetNextLiteral( const mxArray*& refValue )
698  {
699  if( errPending() ) return false;
700 
701  if( m_narg < 1 )
702  {
703  m_err.set( MSG_MISSINGARG );
704  return false;
705  }
706  else if( mxGetClassID( m_parg[0] ) != mxCHAR_CLASS )
707  {
708  m_err.set( MSG_LITERALARGEXPCT );
709  return false;
710  }
711 
712  refValue = m_parg[0];
713 
714  m_parg++;
715  m_narg--;
716 
717  return true;
718  }
719 
730  {
731  if( errPending() ) return false;
732 
733  /*
734  * Check if the first argument is a number (base 1), then we have to use
735  * this number as the requested database id. A number of 0 is allowed and leads
736  * to find the first free slot.
737  */
738  if( argGetNextInteger( m_dbid_req, /*asBoolInt*/ false ) )
739  {
740  if( !SQLstack.isValidId( m_dbid_req-1 ) && m_dbid_req != 0 )
741  {
742  m_err.set( MSG_INVALIDDBHANDLE );
743  return false;
744  }
745  } else {
746  m_err.clear(); // Discard errors
747  m_dbid_req = -1; // Flag argument is missing
748  }
749 
750  // find a free database slot, if user entered 0
751  if( !m_dbid_req )
752  {
753  m_dbid = SQLstack.getNextFreeId();
754 
755  if( !SQLstack.isValidId( m_dbid++ ) )
756  {
757  m_dbid = 0; // No free slot
758  }
759  } else {
760  // select database id or default (1) if no one is given
761  m_dbid = ( m_dbid_req < 0 ) ? 1 : m_dbid_req;
762  }
763 
764  return true;
765  }
766 
767 
775  {
776  if( errPending() ) return false;
777 
778  /*
779  * The next (or first if no db number available) is the m_command,
780  * it has to be a string.
781  * This fails also, if the first arg is a dbid and there is no
782  * further argument
783  */
784  if( !m_narg || !mxIsChar( m_parg[0] ) )
785  {
786  PRINTF( "%s", ::getLocaleMsg( MSG_USAGE ) );
787  m_err.set( MSG_INVALIDARG );
788  return false;
789  }
790  else
791  {
792  /*
793  * Get the m_command string
794  */
795  m_command = ValueMex( m_parg[0] ).GetString();
796  m_parg++;
797  m_narg--;
798 
799  }
800 
801  return true;
802  }
803 
804 
815  bool cmdTryHandleFlag( const char* strMatchFlagName, int& refFlag )
816  {
817  if( errPending() || !STRMATCH( m_command, strMatchFlagName ) )
818  {
819  return false;
820  }
821 
822  // Global command, dbid useless
823  warnOnDefDbid();
824 
825  int iOldValue = refFlag;
826 
827  if( m_narg > 1 )
828  {
829  m_err.set( MSG_UNEXPECTEDARG );
830  return false;
831  }
832 
833  if( m_narg > 0 && !argGetNextInteger( refFlag, /*asBoolInt*/ true ) )
834  {
835  // argGetNextInteger() sets m_err
836  return false;
837  }
838 
839  // always return old flag value
840  m_plhs[0] = mxCreateDoubleScalar( (double)iOldValue );
841 
842  return true;
843  }
844 
845 
857  bool cmdTryHandleVersion( const char* strCmdMatchVerMex, const char* strCmdMatchVerSql )
858  {
859  if( errPending() ) return false;
860 
861  if( STRMATCH( m_command, strCmdMatchVerMex ) )
862  {
863  // Global command, dbid useless
864  warnOnDefDbid();
865 
866  if( m_narg > 0 )
867  {
868  m_err.set( MSG_UNEXPECTEDARG );
869  return false;
870  }
871  else
872  {
873  if( m_nlhs == 0 )
874  {
875  PRINTF( "mksqlite Version %s\n", CONFIG_MKSQLITE_VERSION_STRING );
876  }
877  else
878  {
879  m_plhs[0] = mxCreateString( CONFIG_MKSQLITE_VERSION_STRING );
880  }
881  }
882  return true;
883  }
884  else if( STRMATCH( m_command, strCmdMatchVerSql ) )
885  {
886  // Global command, dbid useless
887  warnOnDefDbid();
888 
889  if( m_narg > 0 )
890  {
891  m_err.set( MSG_UNEXPECTEDARG );
892  return false;
893  }
894  else
895  {
896  if( m_nlhs == 0 )
897  {
898  PRINTF( "SQLite Version %s\n", SQLITE_VERSION_STRING );
899  }
900  else
901  {
902  m_plhs[0] = mxCreateString( SQLITE_VERSION_STRING );
903  }
904  }
905  return true;
906  }
907 
908  return false;
909  }
910 
911 
922  bool cmdTryHandleTypedBlob( const char* strCmdMatchName )
923  {
924  if( errPending() || !STRMATCH( m_command, strCmdMatchName ) )
925  {
926  return false;
927  }
928 
929  /*
930  * typedBLOBs setting:
931  * 0 --> no typed blobs, streaming off
932  * 1 --> typed blobs, streaming off
933  * 2 --> typed blobs, streaming on
934  *
935  * Streaming is only valid if typed blobs are enabled because
936  * one could not distinguish between byte arrays and a
937  * streamed MATLAB array.
938  */
939 
940  // Global command, dbid useless
941  warnOnDefDbid();
942 
943  int old_mode = typed_blobs_mode_on();
944 
945  if( old_mode && g_streaming )
946  {
947  old_mode = 2;
948  }
949 
950  int new_mode = old_mode;
951 
952  if( m_narg > 1 )
953  {
954  m_err.set( MSG_UNEXPECTEDARG );
955  return false;
956  }
957 
958  if( m_narg > 0 && !argGetNextInteger( new_mode ) )
959  {
960  // argGetNextInteger() sets m_err
961  return false;
962  }
963 
964  // action only if something changed
965  if( new_mode != old_mode )
966  {
967  if( new_mode < 0 || new_mode > 2 )
968  {
969  m_err.set( MSG_INVALIDARG );
970  return false;
971  }
972 
973  typed_blobs_mode_set( new_mode > 0 );
974 
975  g_streaming = (new_mode == 2);
976  }
977 
978  // always return the old value
979  m_plhs[0] = mxCreateDoubleScalar( (double)old_mode );
980 
981  return true;
982  }
983 
984 
993  bool cmdTryHandleEnableExtension( const char* strCmdMatchName )
994  {
995  if( errPending() || !STRMATCH( m_command, strCmdMatchName ) )
996  {
997  return false;
998  }
999 
1000  // database must be open to change settings
1001  if( !ensureDbIsOpen() )
1002  {
1003  // ensureDbIsOpen() sets m_err
1004  return false;
1005  }
1006 
1007  /*
1008  * There should be one argument to "enable extension"
1009  */
1010  if( m_narg > 1 )
1011  {
1012  m_err.set( MSG_UNEXPECTEDARG );
1013  return false;
1014  }
1015 
1016  int flagOnOff;
1017  if( !argGetNextInteger( flagOnOff, /*asBoolInt*/ true ) )
1018  {
1019  // argGetNextInteger() sets m_err
1020  return false;
1021  }
1022 
1023  if( !m_interface->setEnableLoadExtension( flagOnOff ) )
1024  {
1025  const char* errid = NULL;
1026  m_err.set( m_interface->getErr(&errid), errid );
1027  return false;
1028  }
1029 
1030  PRINTF( "%s\n", ::getLocaleMsg( flagOnOff ? MSG_EXTENSION_EN : MSG_EXTENSION_DIS ) );
1031 
1032  return true;
1033  }
1034 
1035 
1044  bool cmdTryHandleCreateFunction( const char* strCmdMatchName )
1045  {
1046  if( errPending() || !STRMATCH( m_command, strCmdMatchName ) )
1047  {
1048  return false;
1049  }
1050 
1051  // database must be open to change settings
1052  if( !ensureDbIsOpen() )
1053  {
1054  // ensureDbIsOpen() sets m_err
1055  return false;
1056  }
1057 
1058  /*
1059  * There should be a function name and a function handle
1060  */
1061  if( m_narg > 2 )
1062  {
1063  m_err.set( MSG_UNEXPECTEDARG );
1064  return false;
1065  }
1066 
1067  string fcnName;
1068  if(1)
1069  {
1070  const mxArray* arg = NULL;
1071 
1072  if( !argGetNextLiteral( arg ) )
1073  {
1074  // argGetNextFcnHandle() sets m_err
1075  return false;
1076  }
1077 
1078  char* buffer = ::utils_getString( arg );
1079  if( buffer )
1080  {
1081  ::utils_strlwr( buffer );
1082  fcnName = buffer;
1083  ::utils_free_ptr( buffer );
1084  }
1085  }
1086 
1087  const mxArray* fcnHandle;
1088  if( !argGetNextFcnHandle( fcnHandle ) )
1089  {
1090  // argGetNextFcnHandle() sets m_err
1091  return false;
1092  }
1093 
1094  if( !m_interface->attachMexFunction( fcnName.c_str(),
1095  ValueMex( fcnHandle ),
1096  ValueMex( NULL ), ValueMex( NULL ),
1097  SQLstack.current().getException() ) )
1098  {
1099  const char* errid = NULL;
1100  m_err.set( m_interface->getErr(&errid), errid );
1101  return false;
1102  }
1103 
1104  return true;
1105  }
1106 
1107 
1116  bool cmdTryHandleCreateAggregation( const char* strCmdMatchName )
1117  {
1118  if( errPending() || !STRMATCH( m_command, strCmdMatchName ) )
1119  {
1120  return false;
1121  }
1122 
1123  // database must be open to change settings
1124  if( !ensureDbIsOpen() )
1125  {
1126  // ensureDbIsOpen() sets m_err
1127  return false;
1128  }
1129 
1130  /*
1131  * There should be a function name and a function handle
1132  */
1133  if( m_narg > 3 )
1134  {
1135  m_err.set( MSG_UNEXPECTEDARG );
1136  return false;
1137  }
1138 
1139  string fcnName;
1140  if(1)
1141  {
1142  const mxArray* arg = NULL;
1143 
1144  if( !argGetNextLiteral( arg ) )
1145  {
1146  // argGetNextFcnHandle() sets m_err
1147  return false;
1148  }
1149 
1150  char* buffer = ::utils_getString( arg );
1151  if( buffer )
1152  {
1153  ::utils_strlwr( buffer );
1154  fcnName = buffer;
1155  ::utils_free_ptr( buffer );
1156  }
1157  }
1158 
1159  const mxArray* fcnHandleStep;
1160  if( !argGetNextFcnHandle( fcnHandleStep ) )
1161  {
1162  // argGetNextFcnHandle() sets m_err
1163  return false;
1164  }
1165 
1166  const mxArray* fcnHandleFinal;
1167  if( !argGetNextFcnHandle( fcnHandleFinal ) )
1168  {
1169  // argGetNextFcnHandle() sets m_err
1170  return false;
1171  }
1172 
1173  if( !m_interface->attachMexFunction( fcnName.c_str(),
1174  ValueMex( NULL ),
1175  ValueMex( fcnHandleStep ), ValueMex( fcnHandleFinal ),
1176  SQLstack.current().getException() ) )
1177  {
1178  const char* errid = NULL;
1179  m_err.set( m_interface->getErr(&errid), errid );
1180  return false;
1181  }
1182 
1183  return true;
1184  }
1185 
1186 
1197  bool cmdTryHandleCompression( const char* strCmdMatchName )
1198  {
1199  if( errPending() || !STRMATCH( m_command, strCmdMatchName ) )
1200  {
1201  return false;
1202  }
1203 
1204  // Global command, dbid useless
1205  warnOnDefDbid();
1206 
1207  // always return the old settings
1208  if(1)
1209  {
1210  mxArray* cell = mxCreateCellMatrix( 2, 1 );
1211  mxArray* compressor = mxCreateString( g_compression_type ? g_compression_type : "" );
1212  mxArray* level = mxCreateDoubleScalar( (double)g_compression_level );
1213 
1214  mxSetCell( cell, 0, compressor );
1215  mxSetCell( cell, 1, level );
1216 
1217  m_plhs[0] = cell;
1218  }
1219 
1220  if(1)
1221  {
1222  int new_compression_level = 0;
1223  char* new_compressor = NULL;
1224 
1225  if( m_narg < 2 )
1226  {
1227  m_err.set( MSG_MISSINGARG );
1228  return false;
1229  }
1230  else if( m_narg > 2 )
1231  {
1232  m_err.set( MSG_UNEXPECTEDARG );
1233  return false;
1234  }
1235 
1236  if( !mxIsChar( m_parg[0] ) || !mxIsNumeric( m_parg[1] ) )
1237  {
1238  m_err.set( MSG_INVALIDARG );
1239  return false;
1240  }
1241 
1242  // Get new compressor setting
1243  new_compressor = ValueMex( m_parg[0] ).GetString();
1244  new_compression_level = ValueMex( m_parg[1] ).GetInt();
1245 
1246  if( new_compression_level < 0 || new_compression_level > 9 )
1247  {
1248  m_err.set( MSG_INVALIDARG );
1249  return false;
1250  }
1251 
1252  if( STRMATCH( new_compressor, BLOSC_LZ4_ID ) )
1253  {
1254  g_compression_type = BLOSC_LZ4_ID;
1255  }
1256  else if( STRMATCH( new_compressor, BLOSC_LZ4HC_ID ) )
1257  {
1258  g_compression_type = BLOSC_LZ4HC_ID;
1259  }
1260  else if( STRMATCH( new_compressor, BLOSC_DEFAULT_ID ) )
1261  {
1262  g_compression_type = BLOSC_DEFAULT_ID;
1263  }
1264  else if( STRMATCH( new_compressor, QLIN16_ID ) )
1265  {
1266  g_compression_type = QLIN16_ID;
1267  new_compression_level = ( new_compression_level > 0 ); // only 0 or 1
1268  }
1269  else if( STRMATCH( new_compressor, QLOG16_ID ) )
1270  {
1271  g_compression_type = QLOG16_ID;
1272  new_compression_level = ( new_compression_level > 0 ); // only 0 or 1
1273  }
1274  else
1275  {
1276  m_err.set( MSG_INVALIDARG );
1277  }
1278 
1279  ::utils_free_ptr( new_compressor );
1280 
1281  if( !errPending() )
1282  {
1283  g_compression_level = new_compression_level;
1284  }
1285  }
1286  return true;
1287  }
1288 
1289 
1299  bool cmdTryHandleStatus( const char* strCmdMatchName )
1300  {
1301  if( errPending() || !STRMATCH( m_command, strCmdMatchName ) )
1302  {
1303  return false;
1304  }
1305 
1306  // Global command, dbid useless
1307 
1308  /*
1309  * There should be no argument to status
1310  */
1311  if( m_narg > 0 )
1312  {
1313  m_err.set( MSG_UNEXPECTEDARG );
1314  return false;
1315  }
1316 
1317  if( !m_nlhs )
1318  {
1319  SQLstack.printStatuses( m_dbid_req, m_dbid );
1320  }
1321  else
1322  {
1323  if( m_dbid_req == 0 )
1324  {
1325  mxArray* result = mxCreateCellMatrix( SQLstack.COUNT_DB, 1 );
1326  for( int i = 0; result && i < SQLstack.COUNT_DB; i++ )
1327  {
1328  mxArray* item = mxGetCell( result, i );
1329  mxSetCell( result, i, mxCreateString( SQLstack.m_db[i].isOpen() ? "OPEN" : "CLOSED" ) );
1330  mxDestroyArray( item );
1331  }
1332 
1333  if( result )
1334  {
1335  m_plhs[0] = result;
1336  }
1337  }
1338  else
1339  {
1340  m_plhs[0] = mxCreateString( SQLstack.m_db[m_dbid].isOpen() ? "OPEN" : "CLOSED" );
1341  }
1342  }
1343 
1344  return true;
1345  }
1346 
1347 
1357  bool cmdTryHandleLanguage( const char* strCmdMatchName )
1358  {
1359  if( errPending() || !STRMATCH( m_command, strCmdMatchName ) )
1360  {
1361  return false;
1362  }
1363 
1364  // Global command, dbid useless
1365  warnOnDefDbid();
1366 
1367  /*
1368  * There should be one numeric argument
1369  */
1370  if( m_narg < 1 )
1371  {
1372  m_err.set( MSG_MISSINGARG );
1373  return false;
1374  }
1375  else if( m_narg > 1 )
1376  {
1377  m_err.set( MSG_UNEXPECTEDARG );
1378  return false;
1379  }
1380 
1381  if( !mxIsNumeric( m_parg[0] ) )
1382  {
1383  m_err.set( MSG_NUMARGEXPCT );
1384  return false;
1385  } else {
1386  int iLang = ValueMex( m_parg[0] ).GetInt();
1387 
1388  if( !setLocale( iLang ) )
1389  {
1390  m_err.set( MSG_INVALIDARG );
1391  return false;
1392  }
1393  }
1394 
1395  return true;
1396  }
1397 
1398 
1407  bool cmdTryHandleFilename( const char* strCmdMatchName )
1408  {
1409  char *db_filename = NULL;
1410  char *db_name = NULL;
1411 
1412  if( errPending() || !STRMATCH( m_command, strCmdMatchName ) )
1413  {
1414  return false;
1415  }
1416 
1417  SQLstack.switchTo( m_dbid-1 );
1418 
1419  // database must be open to change settings
1420  if( !ensureDbIsOpen() )
1421  {
1422  // ensureDbIsOpen() sets m_err
1423  return false;
1424  }
1425 
1426  /*
1427  * There should be not more than 1 argument to get the db filename
1428  */
1429  if( m_narg > 1 )
1430  {
1431  m_err.set( MSG_UNEXPECTEDARG );
1432  return false;
1433  }
1434 
1435  // Check if database name is given (for attached databases)
1436  if( m_narg == 1 )
1437  {
1438  if( !mxIsChar( m_parg[0] ) )
1439  {
1440  m_err.set( MSG_LITERALARGEXPCT );
1441  return false;
1442  } else {
1443  db_name = ValueMex( m_parg[0] ).GetEncString();
1444  }
1445  }
1446 
1447  db_filename = ::utils_strnewdup( m_interface->getDbFilename( db_name ), /*flagConvertUTF8*/ true );
1448 
1449  if( NULL == db_filename )
1450  {
1451  m_plhs[0] = mxCreateString( "" ); // Memory based database
1452  }
1453  else
1454  {
1455  m_plhs[0] = mxCreateString( db_filename ); // File based database
1456  }
1457 
1458  ::utils_free_ptr( db_name );
1459  ::utils_free_ptr( db_filename );
1460 
1461  return true;
1462  }
1463 
1464 
1475  bool cmdTryHandleStreaming( const char* strCmdMatchName )
1476  {
1477  if( errPending() || !STRMATCH( m_command, strCmdMatchName ) )
1478  {
1479  return false;
1480  }
1481 
1482  // Global command, dbid useless
1483  warnOnDefDbid();
1484 
1485  /*
1486  * Check max number of arguments
1487  */
1488  if( m_narg > 1 )
1489  {
1490  m_err.set( MSG_UNEXPECTEDARG );
1491  return false;
1492  }
1493 
1494  /*
1495  * try to read flag
1496  */
1497  int flagOnOff = g_streaming;
1498  if( m_narg && !argGetNextInteger( flagOnOff, /*asBoolInt*/ true ) )
1499  {
1500  // argGetNextInteger() sets m_err
1501  return false;
1502  }
1503 
1504  // Report, if serialization is not possible (reset flag then)
1505  if( flagOnOff && !have_serialize() )
1506  {
1507  PRINTF( "%s\n", ::getLocaleMsg( MSG_STREAMINGNOTSUPPORTED ) );
1508  flagOnOff = 0;
1509  }
1510 
1511  // Report, if user tries to use streaming with blobs turned off (reset flag then)
1512  if( flagOnOff && !typed_blobs_mode_on() )
1513  {
1514  PRINTF( "%s\n", ::getLocaleMsg( MSG_STREAMINGNEEDTYBLOBS ) );
1515  flagOnOff = 0;
1516  }
1517 
1518  // always return current status
1519  m_plhs[0] = mxCreateDoubleScalar( (double)g_streaming );
1520 
1521  // store new value
1522  g_streaming = flagOnOff;
1523 
1524  return true;
1525  }
1526 
1527 
1538  bool cmdTryHandleResultType( const char* strCmdMatchName )
1539  {
1540  int new_result_type;
1541  int old_result_type = g_result_type;
1542 
1543  if( errPending() || !STRMATCH( m_command, strCmdMatchName ) )
1544  {
1545  return false;
1546  }
1547 
1548  // Global command, dbid useless
1549  warnOnDefDbid();
1550 
1551  /*
1552  * There should be one integer argument
1553  */
1554  if( m_narg > 1 )
1555  {
1556  m_err.set( MSG_UNEXPECTEDARG );
1557  return false;
1558  }
1559 
1560  // No arguments, then serve current state
1561  if( !m_narg )
1562  {
1563  if( !m_nlhs )
1564  {
1565  // Print current result type
1566  PRINTF( "%s(%d)\n", ::getLocaleMsg( MSG_RESULTTYPE ), g_result_type );
1567  }
1568  else
1569  {
1570  // Return current result type
1571  m_plhs[0] = mxCreateDoubleScalar( g_result_type );
1572  }
1573  return true;
1574  }
1575 
1576  // next parameter must be on/off flag
1577  if( m_narg && !argGetNextInteger( new_result_type, /*asBoolInt*/ false ) )
1578  {
1579  // argGetNextInteger() sets m_err
1580  return false;
1581  }
1582 
1583  // action on change only
1584  if( new_result_type != old_result_type )
1585  {
1586  if( new_result_type < 0 || new_result_type > RESULT_TYPE_MAX_ID )
1587  {
1588  m_err.set( MSG_INVALIDARG );
1589  return false;
1590  }
1591 
1592  g_result_type = new_result_type;
1593  }
1594 
1595  // always return the old value
1596  m_plhs[0] = mxCreateDoubleScalar( (double)old_result_type );
1597 
1598  return true;
1599  }
1600 
1601 
1611  bool cmdTryHandleSetBusyTimeout( const char* strCmdMatchName )
1612  {
1613  int iTimeout;
1614 
1615  if( errPending() || !STRMATCH( m_command, strCmdMatchName ) )
1616  {
1617  return false;
1618  }
1619 
1620  // database must be open to set busy timeout
1621  if( !ensureDbIsOpen() )
1622  {
1623  // ensureDbIsOpen() sets m_err
1624  return false;
1625  }
1626 
1627  /*
1628  * There should be one argument, the Timeout in ms
1629  */
1630  if( m_narg > 1 )
1631  {
1632  m_err.set( MSG_UNEXPECTEDARG );
1633  return false;
1634  }
1635 
1636  if( !m_narg && !m_interface->getBusyTimeout( iTimeout ) )
1637  {
1638  const char* errid = NULL;
1639  /*
1640  * Anything wrong? free the database id and inform the user
1641  */
1642  PRINTF( "%s\n", ::getLocaleMsg( MSG_BUSYTIMEOUTFAIL ) );
1643  m_err.set( m_interface->getErr(&errid), errid );
1644  return false;
1645  }
1646 
1647  if( m_narg && !argGetNextInteger( iTimeout, /*asBoolInt*/ false ) )
1648  {
1649  // argGetNextInteger() sets m_err
1650  return false;
1651  }
1652 
1653  // Note that negative timeout values are allowed:
1654  // "Calling this routine with an argument less than or equal
1655  // to zero turns off all busy handlers."
1656 
1657  if( !m_interface->setBusyTimeout( iTimeout ) )
1658  {
1659  const char* errid = NULL;
1660  /*
1661  * Anything wrong? free the database id and inform the user
1662  */
1663  PRINTF( "%s\n", ::getLocaleMsg( MSG_BUSYTIMEOUTFAIL ) );
1664  m_err.set( m_interface->getErr(&errid), errid );
1665  return false;
1666  }
1667 
1668  // always return old timeout value
1669  m_plhs[0] = mxCreateDoubleScalar( (double)iTimeout );
1670 
1671  return true;
1672  }
1673 
1674 
1696  {
1697  if( cmdTryHandleFlag( "check4uniquefields", g_check4uniquefields )
1698  || cmdTryHandleFlag( "convertUTF8", g_convertUTF8 )
1699  || cmdTryHandleFlag( "NULLasNaN", g_NULLasNaN )
1700  || cmdTryHandleFlag( "compression_check", g_compression_check )
1701  || cmdTryHandleFlag( "param_wrapping", g_param_wrapping )
1702  || cmdTryHandleStatus( "status" )
1703  || cmdTryHandleLanguage( "lang" )
1704  || cmdTryHandleFilename( "filename" )
1705  || cmdTryHandleVersion( "version mex", "version sql" )
1706  || cmdTryHandleStreaming( "streaming" )
1707  || cmdTryHandleTypedBlob( "typedBLOBs" )
1708  || cmdTryHandleResultType( "result_type" )
1709  || cmdTryHandleCompression( "compression" )
1710  || cmdTryHandleSetBusyTimeout( "setbusytimeout" )
1711  || cmdTryHandleEnableExtension( "enable extension" )
1712  || cmdTryHandleCreateFunction( "create function" )
1713  || cmdTryHandleCreateAggregation( "create aggregation" ) )
1714  {
1715  return true;
1716  }
1717  else if ( STRMATCH( m_command, "show tables" ) )
1718  {
1719  m_query = "SELECT name as tablename FROM sqlite_master "
1720  "WHERE type IN ('table','view') AND name NOT LIKE 'sqlite_%' "
1721  "UNION ALL "
1722  "SELECT name as tablename FROM sqlite_temp_master "
1723  "WHERE type IN ('table','view') "
1724  "ORDER BY 1;";
1725 
1726  return false; // dispatch unhandled
1727  }
1728 
1729  return false;
1730  }
1731 
1732 
1734  enum command_e { OPEN, CLOSE, QUERY, DONE, FAILED };
1735 
1746  {
1747  if( STRMATCH( m_command, "open" ) ) return OPEN;
1748  if( STRMATCH( m_command, "close" ) ) return CLOSE;
1749  if( cmdTryHandleNonSqlStatement() ) return DONE;
1750  if( errPending() ) return FAILED;
1751 
1752  return QUERY;
1753  }
1754 
1755 
1764  {
1765  int openFlags = 0;
1766 
1767  if( errPending() ) return false;
1768 
1769  /*
1770  * open a database. There has to be one string argument,
1771  * the database filename
1772  */
1773  if( !m_narg || !mxIsChar( m_parg[0] ) )
1774  {
1775  m_err.set( MSG_NOOPENARG );
1776  return false;
1777  }
1778 
1779  char* dbname = ValueMex( m_parg[0] ).GetString();
1780  m_parg++;
1781  m_narg--;
1782 
1783  // close database if open
1784  if( !SQLstack.current().closeDb( m_err ) )
1785  {
1786  const char* errid = NULL;
1787  m_err.set( m_interface->getErr(&errid), errid );
1788  }
1789 
1790  /*
1791  * Open mode (optional)
1792  */
1793  if( m_narg > 0 && !errPending() )
1794  {
1795  char* iomode = ValueMex( m_parg[0] ).GetString();
1796 
1797  m_parg++;
1798  m_narg--;
1799 
1800  if( STRMATCH( iomode, "ro" ) )
1801  {
1802  openFlags |= SQLITE_OPEN_READONLY;
1803  }
1804  else if( STRMATCH( iomode, "rw" ) )
1805  {
1806  openFlags |= SQLITE_OPEN_READWRITE;
1807  }
1808  else if( STRMATCH( iomode, "rwc" ) )
1809  {
1810  openFlags |= SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE;
1811  }
1812  else
1813  {
1814  m_err.set( MSG_ERRUNKOPENMODE );
1815  }
1816 
1817  ::utils_free_ptr( iomode );
1818  }
1819  else
1820  {
1821  openFlags |= SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE; // ="rwc"
1822  }
1823 
1824 
1825  /*
1826  * Threading mode (optional)
1827  */
1828  if( m_narg > 0 && !errPending() )
1829  {
1830  char* threadmode = ValueMex( m_parg[0] ).GetString();
1831 
1832  m_parg++;
1833  m_narg--;
1834 
1835  if( STRMATCH( threadmode, "single" ) )
1836  {
1837  /* default */
1838  }
1839  else if( STRMATCH( threadmode, "multi" ) )
1840  {
1841  openFlags |= SQLITE_OPEN_NOMUTEX;
1842  }
1843  else if( STRMATCH( threadmode, "serial" ) )
1844  {
1845  openFlags |= SQLITE_OPEN_FULLMUTEX;
1846  }
1847  else
1848  {
1849  m_err.set( MSG_ERRUNKTHREADMODE );
1850  }
1851 
1852  ::utils_free_ptr( threadmode );
1853  }
1854 
1855  if( !errPending() )
1856  {
1857  SQLstack.current().openDb( dbname, openFlags, m_err );
1858  }
1859 
1860  /*
1861  * Set default busytimeout
1862  */
1863  if( !errPending() )
1864  {
1865  const char* errid = NULL;
1866 
1867  delete m_interface;
1868  m_interface = SQLstack.createInterface();
1869 
1870  if( !m_interface->setBusyTimeout( CONFIG_BUSYTIMEOUT ) )
1871  {
1872  PRINTF( "%s\n", ::getLocaleMsg( MSG_BUSYTIMEOUTFAIL ) );
1873  m_err.set( m_interface->getErr(&errid), errid );
1874  }
1875  }
1876 
1877 
1878  /*
1879  * always return the used database id
1880  */
1881  m_plhs[0] = mxCreateDoubleScalar( (double)m_dbid );
1882 
1883  ::utils_free_ptr( dbname );
1884 
1885  return !errPending();
1886  }
1887 
1888 
1898  {
1899  if( errPending() ) return false;
1900 
1901  /*
1902  * There should be no argument to close
1903  */
1904  if( m_narg > 0 )
1905  {
1906  m_err.set( MSG_INVALIDARG );
1907  return false;
1908  }
1909 
1910  /*
1911  * if the database id is 0 than close all open databases
1912  */
1913  if( !m_dbid_req )
1914  {
1916  }
1917  else
1918  {
1919  /*
1920  * If the database is open, then close it. Otherwise
1921  * inform the user
1922  */
1923 
1924  SQLstack.current().closeDb( m_err );
1925  }
1926 
1927  return errPending();
1928  }
1929 
1930 
1940  {
1941  int err_id = MSG_NOERROR;
1942  ValueMex item = ::createItemFromValueSQL( value, err_id );
1943 
1944  if( MSG_NOERROR != err_id )
1945  {
1946  m_err.set( err_id );
1947  }
1948 
1949  return item;
1950  }
1951 
1952 
1963  mxArray* createResultColNameMatrix( const ValueSQLCols& cols )
1964  {
1965  mxArray* colNames = mxCreateCellMatrix( (int)cols.size(), (int)cols.size() ? 2 : 0 );
1966 
1967  // iterate columns
1968  for( int i = 0; !errPending() && i < (int)cols.size(); i++ )
1969  {
1970  // get the real column name
1971  mxArray* colNameSql = mxCreateString( cols[i].m_col_name.c_str() );
1972  mxArray* colNameMat = mxCreateString( cols[i].m_name.c_str() );
1973 
1974  if( !colNames || !colNameSql || !colNameMat )
1975  {
1976  ::utils_destroy_array( colNameSql );
1977  ::utils_destroy_array( colNameMat );
1978  ::utils_destroy_array( colNames );
1979 
1980  m_err.set( MSG_ERRMEMORY );
1981  }
1982  else
1983  {
1984  // replace cell contents
1985  int j = (int)cols.size() + i; // 2nd cell matrix column
1986  mxDestroyArray( mxGetCell( colNames, i ) );
1987  mxDestroyArray( mxGetCell( colNames, j ) );
1988  mxSetCell( colNames, i, colNameSql );
1989  mxSetCell( colNames, j, colNameMat );
1990  }
1991  }
1992 
1993  return colNames;
1994  }
1995 
1996 
2008  {
2009  /*
2010  * Allocate an array of MATLAB structs to return as result
2011  */
2012  mxArray* result = mxCreateStructMatrix( (int)cols[0].size(), 1, 0, NULL );
2013 
2014  // iterate columns
2015  for( int i = 0; !errPending() && i < (int)cols.size(); i++ )
2016  {
2017  int j;
2018 
2019  // insert new field into struct
2020  if( !result || -1 == ( j = mxAddField( result, cols[i].m_name.c_str() ) ) )
2021  {
2022  m_err.set( MSG_ERRMEMORY );
2023  }
2024 
2025  // iterate rows
2026  for( int row = 0; !errPending() && row < (int)cols[i].size(); row++ )
2027  {
2028  // get current table element at row and column
2029  mxArray* item = createItemFromValueSQL( cols[i][row] ).Detach();
2030 
2031  if( !item )
2032  {
2033  if( !m_err.isPending() )
2034  {
2035  m_err.set( MSG_ERRMEMORY );
2036  }
2037  }
2038  else
2039  {
2040  // destroy previous item
2041  mxDestroyArray( mxGetFieldByNumber( result, row, j ) );
2042  // and replace with new one
2043  mxSetFieldByNumber( result, row, j, item );
2044 
2045  cols[i].Destroy(row); // release memory
2046  item = NULL; // Do not destroy! (Occupied by MATLAB struct now)
2047  }
2048  } /* end for (rows) */
2049  } /* end for (cols) */
2050 
2051  return result;
2052  }
2053 
2054 
2068  {
2069  /*
2070  * Allocate a MATLAB struct of arrays to return as result
2071  */
2072  mxArray* result = mxCreateStructMatrix( 1, 1, 0, NULL );
2073 
2074  // iterate columns
2075  for( int i = 0; !errPending() && i < (int)cols.size(); i++ )
2076  {
2077  mxArray* column = NULL;
2078  int j;
2079 
2080  // Pure floating point can be archieved in a numeric matrix
2081  // mixed types must be stored in a cell matrix
2082  column = cols[i].m_isAnyType ?
2083  mxCreateCellMatrix( (int)cols[0].size(), 1 ) :
2084  mxCreateDoubleMatrix( (int)cols[0].size(), 1, mxREAL );
2085 
2086  // add a new field in the struct
2087  if( !result || !column || -1 == ( j = mxAddField( result, cols[i].m_name.c_str() ) ) )
2088  {
2089  m_err.set( MSG_ERRMEMORY );
2090  ::utils_destroy_array( column );
2091  }
2092 
2093  if( !cols[i].m_isAnyType )
2094  {
2095  // fast copy of pure floating point data, iterating rows
2096  for( int row = 0; !errPending() && row < (int)cols[i].size(); row++ )
2097  {
2098  assert( cols[i][row].m_typeID == SQLITE_FLOAT );
2099  mxGetPr(column)[row] = cols[i][row].m_float;
2100  } /* end for (rows) */
2101  }
2102  else
2103  {
2104  // build cell array, iterating rows
2105  for( int row = 0; !errPending() && row < (int)cols[i].size(); row++ )
2106  {
2107  mxArray* item = createItemFromValueSQL( cols[i][row] ).Detach();
2108 
2109  if( !item )
2110  {
2111  m_err.set( MSG_ERRMEMORY );
2112  ::utils_destroy_array( column );
2113  }
2114  else
2115  {
2116  // destroy previous item
2117  mxDestroyArray( mxGetCell( column, row ) );
2118  // and replace with new one
2119  mxSetCell( column, row, item );
2120 
2121  cols[i].Destroy(row); // release memory
2122  item = NULL; // Do not destroy! (Occupied by MATLAB cell array now)
2123  }
2124  } /* end for (rows) */
2125  } /* end if */
2126 
2127  if( !errPending() )
2128  {
2129  // assign columns data to struct field
2130  mxSetFieldByNumber( result, 0, j, column );
2131  column = NULL; // Do not destroy! (Occupied by MATLAB struct now)
2132  }
2133  } /* end for (cols) */
2134 
2135  return result;
2136  }
2137 
2138 
2150  {
2151  bool allFloat = true;
2152 
2153  // check if all columns contain numeric values
2154  for( int i = 0; i < (int)cols.size(); i++ )
2155  {
2156  if( cols[i].m_isAnyType )
2157  {
2158  allFloat = false;
2159  break;
2160  }
2161  }
2162 
2163  /*
2164  * Allocate a MATLAB matrix or cell array to return as result
2165  */
2166  mxArray* result = allFloat ?
2167  mxCreateDoubleMatrix( (int)cols[0].size(), (int)cols.size(), mxREAL ) :
2168  mxCreateCellMatrix( (int)cols[0].size(), (int)cols.size() );
2169 
2170  // iterate columns
2171  for( int i = 0; !errPending() && i < (int)cols.size(); i++ )
2172  {
2173  if( !result )
2174  {
2175  m_err.set( MSG_ERRMEMORY );
2176  ::utils_destroy_array( result );
2177  }
2178 
2179  // iterate rows
2180  for( int row = 0; !errPending() && row < (int)cols[i].size(); row++ )
2181  {
2182  if( allFloat )
2183  {
2184  assert( cols[i][row].m_typeID == SQLITE_FLOAT );
2185  double dVal = cols[i][row].m_float;
2186 
2187  mxGetPr(result)[i * (int)cols[0].size() + row] = dVal;
2188  }
2189  else
2190  {
2191  mxArray* item = createItemFromValueSQL( cols[i][row] ).Detach();
2192 
2193  if( !item )
2194  {
2195  m_err.set( MSG_ERRMEMORY );
2196  }
2197  else
2198  {
2199  // destroy previous item
2200  mxDestroyArray( mxGetCell( result, i * (int)cols[i].size() + row ) );
2201  // and replace with new one
2202  mxSetCell( result, i * (int)cols[i].size() + row, item );
2203 
2204  cols[i].Destroy(row); // release memory
2205  item = NULL; // Do not destroy! (Occupied by MATLAB cell array now)
2206  }
2207  }
2208  } /* end for (rows) */
2209  } /* end for (cols) */
2210 
2211  return result;
2212  }
2213 
2214 
2223  {
2224  if( errPending() ) return false;
2225 
2226  /*** Selecting database ***/
2227 
2228  if( !ensureDbIsOpen() )
2229  {
2230  // ensureDbIsOpen() sets m_err
2231  return false;
2232  }
2233 
2234  /*** prepare query, append semicolon ***/
2235 
2236  // m_query can be already set i.e. in case of command 'show tables'
2237  if( !m_query )
2238  {
2239  // Do the charset conversion and append a semicolon
2240  char* new_command = NULL;
2241  int cmd_length = ::utils_latin2utf( (const unsigned char*)m_command );
2242 
2243  if( cmd_length < strlen( m_command ) )
2244  {
2245  cmd_length = (int)strlen( m_command );
2246  }
2247 
2248  new_command = (char*)MEM_ALLOC( cmd_length + 2, 1 );
2249 
2250  if( !new_command )
2251  {
2252  m_err.set( MSG_ERRMEMORY );
2253  return false;
2254  }
2255 
2256  if( g_convertUTF8 )
2257  {
2258  ::utils_latin2utf( (const unsigned char*)m_command, (unsigned char*)new_command );
2259  sprintf( new_command + strlen( new_command ), ";" );
2260  }
2261  else
2262  {
2263  sprintf( new_command, "%s;", m_command );
2264  }
2265  ::utils_free_ptr( m_command );
2266  m_command = new_command;
2267 
2268  m_query = m_command;
2269  }
2270 
2271  /*** prepare statement ***/
2272 
2273  if( !m_interface->setQuery( m_query ) )
2274  {
2275  const char* errid = NULL;
2276  m_err.set( m_interface->getErr(&errid), errid );
2277  return false;
2278  }
2279 
2280  /*** Progress parameters for subsequent queries ***/
2281 
2282  ValueSQLCols cols;
2283  const mxArray** nextBindParam = m_parg;
2284  int countBindParam = m_narg;
2285  int argsNeeded = m_interface->getParameterCount();
2286  bool haveParamCell = false;
2287  bool haveParamStruct = false;
2288  long* last_insert_row = NULL; // kv69: for storing last_insert_row_id after each statement reuse
2289  bool initialize = true; // kv69: flag indicating initialization within first call of fetch procedure
2290  int count = 1; // kv69: number of repeated statements calls
2291 
2292 
2293 
2294  // steaming | off | on |
2295  // parameter wrapping | off | on | off | on |
2296  // ---------------------------+----------------+------------------+---------------+-----------------+
2297  // 'SELECT ?', {1,2,3,4} | fail=> 4>1 | ok=> 4 stmts | ok=> 1 stmt | ?? |
2298  // 'SELECT ?,?', {1,2,3,4} | fail=> 4>2 | ok=> 2 stmts | ?? | ?? |
2299  // 'SELECT ?', struct(2) | fail=> 2>1 | ok=> 2 stmts | ok=> 1 stmt | ?? |
2300  // 'SELECT ?,?', struct(2) | fail=> 2>1 | ok=> 2 stmts | ?? | ok=> 2 stmts |
2301  //
2302  // ( struct(2) is a struct array of size 2 )
2303 
2304 
2305  // Check if a single cell argument is passed
2306  if( countBindParam == 1 && ValueMex(*nextBindParam).IsCell() )
2307  {
2308  haveParamCell = true;
2309 
2310  // If streaming is on, it is casually not clear, how to
2311  // handle the cell argument to any parameter
2312  if( g_streaming )
2313  {
2314  if( g_param_wrapping || (argsNeeded > 1) )
2315  {
2316  m_err.set( MSG_SINGLECELLNOTALLOWED );
2317  goto finalize;
2318  }
2319  haveParamCell = false;
2320  }
2321 
2322  if( haveParamCell )
2323  {
2324  // redirect cell elements as bind arguments
2325  countBindParam = (int)ValueMex(*nextBindParam).NumElements();
2326  nextBindParam = (const mxArray**)ValueMex(*nextBindParam).Data();
2327  }
2328  }
2329 
2330  // Check if a single struct argument is passed
2331  if( countBindParam == 1 && ValueMex(*nextBindParam).IsStruct() )
2332  {
2333  haveParamStruct = true;
2334 
2335  // If streaming is on, it is casually not clear, how to
2336  // handle the struct argument to any parameter
2337  if( g_streaming )
2338  {
2339  if( g_param_wrapping && (argsNeeded > 1) )
2340  {
2341  // allowed
2342  }
2343  else if( g_param_wrapping || (argsNeeded > 1) )
2344  {
2345  m_err.set( MSG_SINGLESTRUCTNOTALLOWED );
2346  goto finalize;
2347  }
2348  else haveParamStruct = false;
2349  }
2350 
2351  if( haveParamStruct )
2352  {
2353  countBindParam = argsNeeded * (int)ValueMex(*nextBindParam).NumElements(); // Number of fields doesn't matter!
2354  }
2355  }
2356 
2357  /*
2358  * If g_param_wrapping is set, more parameters as needed with current
2359  * statement may be passed.
2360  */
2361 
2362  if( g_param_wrapping )
2363  {
2364  // exceeding argument list allowed to omit multiple queries
2365 
2366  count = argsNeeded ? ( countBindParam / argsNeeded ) : 1; // amount of proposed queries
2367  int remain = argsNeeded ? ( countBindParam % argsNeeded ) : 0; // must be 0
2368 
2369  // remainder must be 0, all placeholders must be fulfilled
2370  if( remain || !count )
2371  {
2372  if( haveParamStruct )
2373  {
2374  m_err.set( MSG_MISSINGARG_STRUCT );
2375  }
2376  else if( haveParamCell )
2377  {
2378  m_err.set( MSG_MISSINGARG_CELL );
2379  }
2380  else
2381  {
2382  m_err.set( MSG_MISSINGARG );
2383  }
2384  goto finalize;
2385  }
2386  }
2387  else
2388  {
2389  bool flagIgnoreLessParameters = true;
2390 
2391  // the number of arguments may not exceed the number of placeholders
2392  // in the sql statement
2393  if( countBindParam > argsNeeded )
2394  {
2395  m_err.set( MSG_UNEXPECTEDARG );
2396  goto finalize;
2397  }
2398 
2399  // number of arguments must match now
2400  if( !flagIgnoreLessParameters && countBindParam != argsNeeded )
2401  {
2402  if( haveParamStruct )
2403  {
2404  m_err.set( MSG_MISSINGARG_STRUCT );
2405  }
2406  else if( haveParamCell )
2407  {
2408  m_err.set( MSG_MISSINGARG_CELL );
2409  }
2410  else
2411  {
2412  m_err.set( MSG_MISSINGARG );
2413  }
2414  goto finalize;
2415  }
2416  }
2417 
2418  last_insert_row = new long[count];
2419 
2420  if( !last_insert_row )
2421  {
2422  m_err.set( MSG_ERRMEMORY );
2423  goto finalize;
2424  }
2425 
2426  // loop over parameters
2427  for( int i = 0; i < count; i++ ) // kv69: fixed length loop because we know how often the stmt should be repeated
2428  {
2429  // reset SQL statement and clear bindings
2430  m_interface->reset();
2431  m_interface->clearBindings();
2432 
2433  /*** Bind parameters ***/
2434 
2435  // bind each argument to SQL statement placeholders
2436  for( int iParam = 0; !errPending() && iParam < argsNeeded && countBindParam; iParam++, countBindParam-- )
2437  {
2438  const mxArray* bindParam = NULL;
2439 
2440  if( !haveParamStruct )
2441  {
2442  bindParam = *nextBindParam++;
2443  }
2444  else
2445  {
2446  const char* name = m_interface->getParameterName( iParam + 1 );
2447  bindParam = name ? ValueMex( *nextBindParam ).GetField( i, ++name ) : NULL; // adjusting name behind either '?', ':', '$' or '@'!
2448 
2449  if( !bindParam )
2450  {
2451  m_err.set_printf( MSG_MISSINGARG_STRUCT, NULL, name ? name : "(unnamed)");
2452  goto finalize;
2453  }
2454  }
2455 
2456  if( !m_interface->bindParameter( iParam + 1, ValueMex( bindParam ), can_serialize() ) )
2457  {
2458  const char* errid = NULL;
2459  m_err.set( m_interface->getErr(&errid), errid );
2460  goto finalize;
2461  }
2462  }
2463 
2464  /*** fetch results and store results for output ***/
2465 
2466  // cumulate in "cols"
2467  if( !errPending() && !m_interface->fetch( cols, initialize ) )
2468  {
2469  const char* errid = NULL;
2470  m_err.set( m_interface->getErr(&errid), errid );
2471  goto finalize;
2472  }
2473  initialize = false; // kv69: for next statement use do not initialize query results again but accumulated it
2474 
2475  // kv69: collect last_insert_row_id
2476  last_insert_row[i] = m_interface->getLastRowID();
2477  }
2478 
2479 finalize:
2480  /*
2481  * finalize current sql statement
2482  */
2483  m_interface->finalize();
2484 
2485  /*** Prepare results to return ***/
2486 
2487  if( !errPending() )
2488  {
2489  // check if result is empty (no columns)
2490  if( !cols.size() )
2491  {
2492  /*
2493  * got nothing? return an empty result to MATLAB
2494  */
2495  for( int i = 0; i < m_nlhs; i++ )
2496  {
2497  mxArray* result = mxCreateDoubleMatrix( 0, 0, mxREAL );
2498  if( !result )
2499  {
2500  m_err.set( MSG_CANTCREATEOUTPUT );
2501  break;
2502  }
2503  else
2504  {
2505  m_plhs[i] = result;
2506  }
2507  }
2508  }
2509  else
2510  {
2511  mxArray* result = NULL;
2512 
2513  // dispatch regarding result type
2514  switch( g_result_type )
2515  {
2517  result = createResultAsArrayOfStructs( cols );
2518  break;
2519 
2521  result = createResultAsStructOfArrays( cols );
2522  break;
2523 
2524  case RESULT_TYPE_MATRIX:
2525  result = createResultAsMatrix( cols );
2526  break;
2527 
2528  default:
2529  assert( false );
2530  break;
2531  }
2532 
2533  if( !result )
2534  {
2535  m_err.set( MSG_CANTCREATEOUTPUT );
2536  }
2537  else
2538  {
2539  m_plhs[0] = result;
2540  }
2541  }
2542  }
2543 
2544  if( !errPending() )
2545  {
2546  // If more than 1 return parameter, output the row count
2547  if( m_nlhs > 1 )
2548  {
2549  int row_count = cols.size() > 0 ? (int)cols[0].size() : 0;
2550  m_plhs[1] = mxCreateDoubleScalar( (double)row_count );
2551  assert( NULL != m_plhs[1] );
2552  }
2553 
2554  // If more than 2 return parameters, output the real column names
2555  if( m_nlhs > 2 )
2556  {
2557  // Get a cell matrix of column name relations (SQL <-> MATLAB)
2558  m_plhs[2] = createResultColNameMatrix( cols );
2559  }
2560  // kv69: if more than 3 return parameters, output the last_insert_row_id as vector
2561  if( m_nlhs > 3 )
2562  {
2563  mxArray* result;
2564  result = mxCreateDoubleMatrix(count, 1, mxREAL);
2565  if( !result )
2566  {
2567  m_err.set( MSG_CANTCREATEOUTPUT );
2568  }
2569  if( !errPending() )
2570  {
2571  for (int i = 0; i < count; i++)
2572  {
2573  mxGetPr(result)[i] = (double)last_insert_row[i];
2574  }
2575  }
2576  m_plhs[3] = result;
2577  }
2578 
2579  }
2580 
2581  // kv69: clear array for last insert row
2582  delete[] last_insert_row;
2583 
2584  return !errPending();
2585 
2586  } /* end cmdHandleSQLStatement() */
2587 
2588 
2595  bool switchDBSlot( command_e command )
2596  {
2597  if( command < DONE ) // OPEN, CLOSE, QUERY
2598  {
2599  // Check if user entered an id of 0
2600  if( !m_dbid_req )
2601  {
2602  if( command == OPEN )
2603  {
2604  if( !m_dbid )
2605  {
2606  m_err.set( MSG_NOFREESLOT );
2607  return false;
2608  }
2609  }
2610  else if( command == CLOSE )
2611  {
2612  m_dbid = 1;
2613  }
2614  else
2615  {
2616  m_err.set( MSG_ERRNULLDBID );
2617  return false;
2618  }
2619  }
2620 
2621  SQLstack.switchTo( m_dbid-1 ); // m_dbid is base 1
2622  }
2623 
2624  return true;
2625  }
2626 
2627 
2631  void cmdExecute()
2632  {
2633  // read first numeric argument if given and use it as dbid, then read command string
2634  if( !argTryReadValidDbid() || !argReadCommand() )
2635  {
2636  returnWithError();
2637  }
2638 
2639  command_e command = cmdAnalyseCommand();
2640 
2641  if( switchDBSlot( command ) )
2642  {
2643  // analyse command string (switch, command, or query)
2644  switch( command )
2645  {
2646  case Mksqlite::OPEN:
2647  (void)cmdHandleOpen(); // "open" command
2648  break;
2649 
2650  case Mksqlite::CLOSE:
2651  (void)cmdHandleClose(); // "close" command
2652  break;
2653 
2654  case Mksqlite::QUERY:
2655  (void)cmdHandleSQLStatement(); // common sql query
2656  break;
2657 
2658  case Mksqlite::DONE:
2659  break; // switches (flags) are already handled
2660 
2661  case Mksqlite::FAILED:
2662  break;
2663 
2664  default:
2665  assert( false );
2666  }
2667  }
2668  }
2669 
2670 };
2671 
2672 
2673 
2674 
2675 
2676 
2677 
2678 
2679 
2680 
2683 /* the main routine */
2684 
2693 void mexFunction( int nlhs, mxArray* plhs[], int nrhs, const mxArray*prhs[] )
2694 {
2695  /*
2696  * Get the current language
2697  * -1 means "undefined" (only one init on module load required)
2698  */
2699  if( getLocale() == -1 )
2700  {
2701 #ifdef _WIN32
2702  switch( PRIMARYLANGID( GetUserDefaultLangID() ) )
2703  {
2704  case LANG_GERMAN:
2705  setLocale(1);
2706  break;
2707 
2708  default:
2709  setLocale(0);
2710  }
2711 #else
2712  setLocale(0);
2713 #endif
2714  }
2715 
2716  /*
2717  * Print version information, initializing, ...
2718  */
2719  mex_module_init(); // only done once
2720 
2721  Mksqlite mksqlite( nlhs, plhs, nrhs, prhs );
2722 
2723  mksqlite.cmdExecute();
2724 
2726 
2727  if( mksqlite.errPending() )
2728  {
2729  mksqlite.returnWithError();
2730  }
2731 
2732 #if CONFIG_USE_HEAP_CHECK
2733  mksqlite.Release(); // let destructors work, before heap is checked
2734  HeapCheck.Walk(); // Report, if any leaks exist
2735 #endif
2736 
2737  return;
2738 }
#define STRMATCH(strA, strB)
Returns 0 if strA and strB are equal (ignoring case)
Definition: mksqlite.cpp:33
mxArray * createResultAsMatrix(ValueSQLCols &cols)
Transform SQL fetch to MATLAB (cell) array.
Definition: mksqlite.cpp:2149
bool setEnableLoadExtension(int flagOnOff)
Enable or disable load extensions.
Encapsulating a MATLAB mxArray.
Definition: value.hpp:178
void throwOnException()
(Re-)Throws an exception, if any occurred
SQLiface * m_interface
interface (holding current SQLite statement) to current database
Definition: mksqlite.cpp:489
Class holding an exception array, the function map and the handle for one database.
const mxArray * GetField(int n, const char *name) const
Get field from a struct array.
Definition: value.hpp:738
int getParameterCount()
Returns the count of parameters the current statement expects.
void printStatuses(int dbid_req, int dbid)
Outputs current status for each database slot.
Definition: mksqlite.cpp:129
void errClear()
Clear recent error.
Definition: mksqlite.cpp:546
mxArray * createResultAsStructOfArrays(ValueSQLCols &cols)
Transform SQL fetch to MATLAB struct of arrays.
Definition: mksqlite.cpp:2067
int g_param_wrapping
Wrap parameters.
Definition: global.hpp:275
bool isOpen()
Returns true, if database is opened.
bool setLocale(int iLang)
Sets the current locale.
Definition: locale.hpp:504
const char * getErr(const char **errid=NULL)
Get recent error message.
~Mksqlite()
Dtor.
Definition: mksqlite.cpp:533
void cmdExecute()
Execute the command string passed to mksqlite.
Definition: mksqlite.cpp:2631
Main routine class.
Definition: mksqlite.cpp:478
bool isOpen()
Returns true, if database is open.
void set_printf(int iMessageNr, const char *strId,...)
Set error message by identifier (translations available) with printf arguments.
Definition: locale.hpp:218
char * m_command
SQL command. Allocated and freed by this class.
Definition: mksqlite.cpp:484
mxClassID ClassID() const
Returns item class ID or mxUNKNOWN_CLASS if item is NULL.
Definition: value.hpp:520
command_e
Return values of cmdAnalyseCommand()
Definition: mksqlite.cpp:1734
SQLite interface class.
type_complexity_e Complexity(bool bCanSerialize=false) const
Get complexity information. Which storage level is necessary (scalar, vector, matrix, text, blob)
Definition: value.hpp:531
Matrix/cell array.
Definition: config.h:48
void mex_module_deinit()
Module deinitialization.
Definition: mksqlite.cpp:190
single non-complex value, char or simple string (SQLite simple types)
Definition: value.hpp:187
bool closeDb(SQLerror &err)
Close database.
bool errPending()
Returns true, if any error is pending.
Definition: mksqlite.cpp:539
bool cmdTryHandleStatus(const char *strCmdMatchName)
Handle status command.
Definition: mksqlite.cpp:1299
#define CONFIG_BUSYTIMEOUT
default SQL busy timeout in milliseconds (1000)
Definition: config.h:25
bool argGetNextFcnHandle(const mxArray *&refValue)
Get next value as function handle from argument list.
Definition: mksqlite.cpp:665
~SQLstack()
Dtor.
Definition: mksqlite.cpp:92
char * GetString(bool flagUTF=false, const char *format=NULL) const
Convert a string to char, due flagUTF converted to utf8.
Definition: value.hpp:584
const char * getParameterName(int n)
Returns the name for nth parameter.
int m_narg
count of right hand side arguments
Definition: mksqlite.cpp:481
int GetInt(int errval=0) const
Get integer value from item.
Definition: value.hpp:677
int m_typeID
Type of SQL value as integer ID.
Definition: value.hpp:865
bool warnOnDefDbid()
Omits a warning if database is given but superfluous.
Definition: mksqlite.cpp:594
bool cmdHandleClose()
Handle close command.
Definition: mksqlite.cpp:1897
bool fetch(ValueSQLCols &cols, bool initialize=false)
Proceed a table fetch.
const char * getLocaleMsg(int iMsgNr)
Returns the translation for a defined message.
Definition: locale.hpp:480
mxArray * createResultAsArrayOfStructs(ValueSQLCols &cols)
Transform SQL fetch to MATLAB array of structs.
Definition: mksqlite.cpp:2007
char * utils_strlwr(char *)
Change string to lowercase (inplace)
Definition: utils.hpp:253
bool switchDBSlot(command_e command)
Selects the desired slot from SQLStack for the operation.
Definition: mksqlite.cpp:2595
mxArray * Detach()
Detach hosted MATLAB array.
Definition: value.hpp:381
ValueMex createItemFromValueSQL(const ValueSQL &value)
Transfer fetched SQL value into a MATLAB array.
Definition: mksqlite.cpp:1939
bool setQuery(const char *query)
Dispatch a SQL query.
int closeAllDbs()
Closes all open databases and returns the number of closed DBs, if any open.
Definition: mksqlite.cpp:165
bool cmdTryHandleCompression(const char *strCmdMatchName)
Handle compression setting command.
Definition: mksqlite.cpp:1197
bool argGetNextInteger(int &refValue, bool asBoolInt=false)
Get next integer from argument list.
Definition: mksqlite.cpp:629
int m_dbid
selected database slot (1..COUNT_DB)
Definition: mksqlite.cpp:487
bool attachMexFunction(const char *name, const ValueMex &func, const ValueMex &step, const ValueMex &final, ValueMex &exception)
Attach application-defined function to database object.
const char * getDbFilename(const char *database)
Get the filename of current database.
SQLiface * createInterface()
Returns a new interface to the current database.
Definition: mksqlite.cpp:122
int blob_unpack(const void *pBlob, size_t blob_size, bool bStreamable, mxArray **ppItem, double *pProcess_time, double *pdRatio)
uncompress a typed blob and return as MATLAB array
bool cmdTryHandleFilename(const char *strCmdMatchName)
Get the filename of current database.
Definition: mksqlite.cpp:1407
void clearBindings()
Clears all parameter bindings from current statement.
size_t ByData() const
Returns data size in bytes.
Definition: value.hpp:511
int getNextFreeId()
Returns the first next free id slot (base 0). Database must be closed.
Definition: mksqlite.cpp:147
void finalize()
Clear parameter bindings and finalize current statement.
bool cmdTryHandleNonSqlStatement()
Interpret current argument as command or switch.
Definition: mksqlite.cpp:1695
#define FINALIZE(identifier)
Terminates the function immediately with an error message.
Definition: mksqlite.cpp:37
sqlite3_int64 GetInt64(int errval=0) const
Get 64 bit integer value from item.
Definition: value.hpp:708
Class encapsulating a SQL field value.
Definition: value.hpp:862
SQLite interface.
Struct of arrays.
Definition: config.h:47
void typed_blobs_mode_set(int mode)
Set mode of typed blob usage.
bool cmdTryHandleStreaming(const char *strCmdMatchName)
Handle streaming setting command.
Definition: mksqlite.cpp:1475
char TBH_endian[]
endian (little or big)
int g_result_type
Data organisation of returning query results.
Definition: global.hpp:272
bool isPending()
Returns true, if the current error message is still not handled.
Definition: locale.hpp:285
bool cmdTryHandleEnableExtension(const char *strCmdMatchName)
Handle command to (en-/dis-)able loading extensions.
Definition: mksqlite.cpp:993
structs, cells, complex data (SQLite typed ByteStream BLOB)
Definition: value.hpp:190
void clear()
Reset error message.
Definition: locale.hpp:134
SQLerror m_err
recent error
Definition: mksqlite.cpp:488
void utils_destroy_array(mxArray *&pmxarr)
Freeing memory allocated by mxCreateNumericMatrix() or mxCreateNumericArray().
Definition: utils.hpp:302
ValueSQL createValueSQLFromItem(const ValueMex &item, bool bStreamable, int &iTypeComplexity, int &err_id)
Transfer MATLAB array into a SQL value.
Definition: mksqlite.cpp:354
mxArray ** m_plhs
pointer to current left hand side argument
Definition: mksqlite.cpp:482
void mex_module_init()
Module initialization.
Definition: mksqlite.cpp:209
char TBH_platform[]
platform name
int m_dbid
recent selected database id, base 0
Definition: mksqlite.cpp:77
size_t NumElements() const
Returns number of elements.
Definition: value.hpp:484
int m_nlhs
count of left hand side arguments
Definition: mksqlite.cpp:480
bool isValidId(int newId)
Checks if database newId is in valid range.
Definition: mksqlite.cpp:107
Array of structs.
Definition: config.h:46
Class holding an error.
bool cmdTryHandleVersion(const char *strCmdMatchVerMex, const char *strCmdMatchVerSql)
Handle version commands.
Definition: mksqlite.cpp:857
#define CONFIG_MAX_NUM_OF_DBS
maximum number of databases, simultaneous open
Definition: config.h:56
int g_streaming
Flag: Allow streaming.
Definition: global.hpp:269
mxArray * createResultColNameMatrix(const ValueSQLCols &cols)
Create a MATLAB cell array of column names.
Definition: mksqlite.cpp:1963
int g_namelengthmax
MATALAB specific globals.
Definition: global.hpp:260
bool can_serialize()
Returns true, if streaming is switched on (user setting) and serialization is accessible.
Definition: serialize.hpp:108
Template class extending base class uniquely.
bool cmdTryHandleFlag(const char *strMatchFlagName, int &refFlag)
Handle flag from command.
Definition: mksqlite.cpp:815
void Release()
Release object.
Definition: mksqlite.cpp:518
int g_convertUTF8
Flag: String representation (utf8 or ansi)
Definition: global.hpp:253
int blob_pack(const mxArray *pcItem, bool bStreamable, void **ppBlob, size_t *pBlob_size, double *pdProcess_time, double *pdRatio, const char *compressor=g_compression_type, int level=g_compression_level)
create a compressed typed blob from a Matlab item (deep copy)
vector< ValueSQLCol > ValueSQLCols
type for column container
const char * m_query
m_command, or a translation from m_command
Definition: mksqlite.cpp:485
bool cmdTryHandleCreateFunction(const char *strCmdMatchName)
Handle command to create or delete a SQL user function.
Definition: mksqlite.cpp:1044
bool cmdHandleSQLStatement()
Handle common SQL statement.
Definition: mksqlite.cpp:2222
int typed_blobs_mode_on()
Get mode of typed blob usage.
void switchTo(int newId)
Makes newId as current database id.
Definition: mksqlite.cpp:114
void set(const char *strMsg, const char *strId=NULL)
Set error message to a constant string (without translation)
Definition: locale.hpp:150
ValueMex & getException()
Returns the exception array for this database.
void utils_free_ptr(T *&pmxarr)
Freeing memory allocated by mxAlloc() or mxRealloc()
Definition: utils.hpp:322
void * Data() const
Returns pointer to raw data.
Definition: value.hpp:572
bool cmdTryHandleLanguage(const char *strCmdMatchName)
Handle language command.
Definition: mksqlite.cpp:1357
bool argTryReadValidDbid()
Get database ID from argument list.
Definition: mksqlite.cpp:729
int getLocale()
Get current locale id.
Definition: locale.hpp:519
bool IsStruct() const
Returns true if item is a struct array.
Definition: value.hpp:448
bool openDb(const char *filename, int openFlags, SQLerror &err)
Opens (or create) database.
SQLite interface stack.
Definition: mksqlite.cpp:70
SQLstackitem & current()
Returns the handle of the current database.
Definition: mksqlite.cpp:100
bool cmdTryHandleTypedBlob(const char *strCmdMatchName)
Handle typed BLOB settings command.
Definition: mksqlite.cpp:922
bool cmdTryHandleSetBusyTimeout(const char *strCmdMatchName)
Handle set busy timeout command.
Definition: mksqlite.cpp:1611
ValueMex createItemFromValueSQL(const ValueSQL &value, int &err_id)
Transfer fetched SQL value into MATLAB array.
Definition: mksqlite.cpp:260
bool cmdHandleOpen()
Handle open command.
Definition: mksqlite.cpp:1763
char * GetEncString() const
Returns allocated memory with items test, due to global flag converted to UTF.
Definition: value.hpp:665
int g_NULLasNaN
Flag: return NULL as NaN.
Definition: global.hpp:263
char * utils_strnewdup(const char *s, int flagConvertUTF8)
duplicate a string and recode from UTF8 to char due to flag flagConvertUTF8
Definition: utils.hpp:211
int utils_latin2utf(const unsigned char *s, unsigned char *buffer)
Convert char string to UTF-8 string.
Definition: utils.hpp:171
Helperclass for memory leak and access violation detection.
Definition: heap_check.hpp:52
bool have_serialize()
Returns true, if current MATLAB version supports serialization.
Definition: serialize.hpp:82
double GetScalar() const
Definition: value.hpp:726
ValueMex & Adopt(bool doAdopt=true)
Take ownership (custody) of a MEX array.
Definition: value.hpp:282
#define CONFIG_MKSQLITE_VERSION_STRING
mksqlite version string
Definition: config.h:54
void Walk(const char *text=NULL)
Reporting walk through the linked memory list.
Definition: heap_check.hpp:407
bool argReadCommand()
Get command from argument list.
Definition: mksqlite.cpp:774
bool setBusyTimeout(int iTimeoutValue)
Sets the busy timemout in milliseconds.
bool assureSQLinterface()
Creates a SQL interface if not already happen.
Definition: mksqlite.cpp:609
bool argGetNextLiteral(const mxArray *&refValue)
Get next value as literal argument from argument list.
Definition: mksqlite.cpp:697
bool cmdTryHandleCreateAggregation(const char *strCmdMatchName)
Handle command to create or delete a SQL user aggregate function.
Definition: mksqlite.cpp:1116
void returnWithError()
Terminate function.
Definition: mksqlite.cpp:558
const mxArray * Item() const
Returns hosted MATLAB array.
Definition: value.hpp:351
const mxArray ** m_parg
pointer to current right hand side argument
Definition: mksqlite.cpp:483
bool bindParameter(int index, const ValueMex &item, bool bStreamable)
Binds one parameter from current statement to a MATLAB array.
bool ensureDbIsOpen()
Ensuring current database is open.
Definition: mksqlite.cpp:576
const char * get(const char **errId=NULL)
Get the current error message.
Definition: locale.hpp:266
void reset()
Reset current SQL statement.
Mksqlite(int nlhs, mxArray **plhs, int nrhs, const mxArray **prhs)
Standard ctor.
Definition: mksqlite.cpp:501
command_e cmdAnalyseCommand()
Analyse command string and process if its neither open, close nor a sql command.
Definition: mksqlite.cpp:1745
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
MEX Entry function declared the as pure C.
Definition: mksqlite.cpp:2693
multidimensional non-complex numeric or char arrays (SQLite typed BLOB)
Definition: value.hpp:189
long getLastRowID()
kv69: Returns the number of last row id; usefull for inserts in tables with autoincrement primary key...
non-complex numeric vectors (SQLite BLOB)
Definition: value.hpp:188
Limit for bound checking only.
Definition: config.h:51
bool cmdTryHandleResultType(const char *strCmdMatchName)
Handle result type command.
Definition: mksqlite.cpp:1538
bool getBusyTimeout(int &iTimeoutValue)
Returns the busy timeout in milliseconds.
#define HC_COMP_ASSERT(exp)
Verifies design-time assumptions (exp) at compile-time.
Definition: heap_check.hpp:28
SQLstack()
Standard Ctor (first database slot is default)
Definition: mksqlite.cpp:81
char * utils_getString(const mxArray *str)
Copy string characters into allocated memory.
Definition: utils.hpp:52
#define PRINTF
Global text output function.
Definition: global.hpp:230
int m_dbid_req
requested database id (user input) -1="arg missing", 0="next free slot" or 1..COUNT_DB ...
Definition: mksqlite.cpp:486
void typed_blobs_init()
Initialization.
SQLstackitem m_db[COUNT_DB]
SQLite database slots.
Definition: mksqlite.cpp:76
#define MEM_ALLOC(count, bytes)
standard memory allocator
Definition: global.hpp:156
int g_check4uniquefields
Flag: Check for unique fieldnames.
Definition: global.hpp:266
bool IsCell() const
Returns true if item is a cell array.
Definition: value.hpp:421