Torque3D Documentation / _generateds / SQLiteObject.cpp

SQLiteObject.cpp

Engine/source/sqlite/SQLiteObject.cpp

More...

Public Functions

Callback(void * pArg, S32 argc, char ** argv, char ** columnNames)
DefineEngineMethod(SQLiteObject , clearResult , void , (S32 resultSet) , "(S32 resultSet) Clears memory used by the specified result set, and deletes the result set." )
DefineEngineMethod(SQLiteObject , closeDatabase , void , () , "Closes the active database." )
DefineEngineMethod(SQLiteObject , endOfResult , bool , (S32 resultSet) , "(S32 resultSet) Checks <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> see <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> the internal <a href="/coding/file/pointer_8h/#pointer_8h_1aae1f8d263916ad71bd415381591549c0">pointer</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> the specified result set is at the end, indicating there are no more rows left <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> read." )
DefineEngineMethod(SQLiteObject , EOFile , bool , (S32 resultSet) , "(S32 resultSet) Same as endOfResult()." )
DefineEngineMethod(SQLiteObject , EOR , bool , (S32 resultSet) , "(S32 resultSet) Same as endOfResult()." )
DefineEngineMethod(SQLiteObject , escapeString , const char * , (String string) , "(string) Escapes the given string, making it safer <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> pass into <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> query." )
DefineEngineMethod(SQLiteObject , firstRow , void , (S32 resultSet) , "(S32 resultSet) Moves the result set's row <a href="/coding/file/pointer_8h/#pointer_8h_1aae1f8d263916ad71bd415381591549c0">pointer</a> <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the very first row in the result set." )
DefineEngineMethod(SQLiteObject , getColumnIndex , S32 , (S32 resultSet, String columnName) , "(resultSet columnName) Looks up the specified column name in the specified result set, and returns the columns index number. A return <a href="/coding/file/pointer_8h/#pointer_8h_1a32aff7c6c4cd253fdf6563677afab5ce">value</a> of 0 indicates the lookup failed <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> some reason(usually this indicates you specified <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> column name that doesn '<a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1aded116371789db1fd63c90ef00c95a3d">t</a> exist or is spelled wrong)." )
DefineEngineMethod(SQLiteObject , getColumnName , const char * , (S32 resultSet, S32 columnIndex) , "(resultSet columnIndex) Looks up the specified column index in the specified result set, and returns the column 's name. A return <a href="/coding/file/pointer_8h/#pointer_8h_1a32aff7c6c4cd253fdf6563677afab5ce">value</a> of an empty string indicates the lookup failed <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> some reason(usually this indicates you specified <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> column index that is invalid or exceeds the number of columns in the result set). Columns are index starting with 1 not 0" )
DefineEngineMethod(SQLiteObject , getLastRowId , S32 , () , "getLastRowId()" )
DefineEngineMethod(SQLiteObject , getRow , S32 , (S32 resultSet) , "(S32 resultSet) Returns what row the result set's row <a href="/coding/file/pointer_8h/#pointer_8h_1aae1f8d263916ad71bd415381591549c0">pointer</a> is currently on." )
DefineEngineMethod(SQLiteObject , lastRow , void , (S32 resultSet) , "(S32 resultSet) Moves the result set's row <a href="/coding/file/pointer_8h/#pointer_8h_1aae1f8d263916ad71bd415381591549c0">pointer</a> <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the very last row in the result set." )
DefineEngineMethod(SQLiteObject , loadOrSaveDb , bool , (const char *filename, bool isSave) , "Loads or saves <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> cached database from the disk db specifed by filename. Second argument determines loading (false) or saving (true). Returns true or false." )
DefineEngineMethod(SQLiteObject , nextRow , void , (S32 resultSet) , "(S32 resultSet) Moves the result set's row <a href="/coding/file/pointer_8h/#pointer_8h_1aae1f8d263916ad71bd415381591549c0">pointer</a> <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the next row." )
DefineEngineMethod(SQLiteObject , numColumns , S32 , (S32 resultSet) , "(S32 resultSet) Returns the number of columns in the result set." )
DefineEngineMethod(SQLiteObject , numResultSets , S32 , () , "numResultSets()" )
DefineEngineMethod(SQLiteObject , numRows , S32 , (S32 resultSet) , "(S32 resultSet) Returns the number of rows in the result set." )
DefineEngineMethod(SQLiteObject , openDatabase , bool , (const char *filename) , "(const char* filename) Opens the database specifed by filename. Returns true or false." )
DefineEngineMethod(SQLiteObject , previousRow , void , (S32 resultSet) , "(S32 resultSet) Moves the result set's row <a href="/coding/file/pointer_8h/#pointer_8h_1aae1f8d263916ad71bd415381591549c0">pointer</a> <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the previous row" )
DefineEngineMethod(SQLiteObject , setRow , void , (S32 resultSet, S32 row) , "(S32 resultSet <a href="/coding/file/types_8h/#types_8h_1a57a2244776e01ad620c556de58eb7880">S32</a> row) Moves the result set's row <a href="/coding/file/pointer_8h/#pointer_8h_1aae1f8d263916ad71bd415381591549c0">pointer</a> <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the row specified. Row indices start at 1 not 0." )
DefineEngineStringlyVariadicMethod(SQLiteObject , getColumn , const char * , 4 , 4 , "(resultSet column) Returns the <a href="/coding/file/pointer_8h/#pointer_8h_1a32aff7c6c4cd253fdf6563677afab5ce">value</a> of the specified column (Column can be specified by name or index) in the current row of the specified result set. If the call fails, the returned string will indicate the error." )
DefineEngineStringlyVariadicMethod(SQLiteObject , getColumnNumeric , F32 , 4 , 4 , "(resultSet column) Returns the <a href="/coding/file/pointer_8h/#pointer_8h_1a32aff7c6c4cd253fdf6563677afab5ce">value</a> of the specified column (Column can be specified by name or index) in the current row of the specified result set. If the call fails, the returned string will indicate the error." )
DefineEngineStringlyVariadicMethod(SQLiteObject , query , S32 , 1 , 5 , "(const char* sql, <a href="/coding/file/types_8h/#types_8h_1a57a2244776e01ad620c556de58eb7880">S32</a> <a href="/coding/file/zipobject_8cpp/#zipobject_8cpp_1ac6c3dfb4c3a68f849f32cbfb21da4e77">mode</a>) Performs an SQL query on the open database and returns an identifier <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> valid result set. <a href="/coding/file/zipobject_8cpp/#zipobject_8cpp_1ac6c3dfb4c3a68f849f32cbfb21da4e77">mode</a> is currently unused, and is reserved <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> future use." )

Detailed Description

Public Functions

Callback(void * pArg, S32 argc, char ** argv, char ** columnNames)

DefineEngineMethod(SQLiteObject , clearResult , void , (S32 resultSet) , "(S32 resultSet) Clears memory used by the specified result set, and deletes the result set." )

DefineEngineMethod(SQLiteObject , closeDatabase , void , () , "Closes the active database." )

DefineEngineMethod(SQLiteObject , endOfResult , bool , (S32 resultSet) , "(S32 resultSet) Checks <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> see <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> the internal <a href="/coding/file/pointer_8h/#pointer_8h_1aae1f8d263916ad71bd415381591549c0">pointer</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> the specified result set is at the end, indicating there are no more rows left <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> read." )

DefineEngineMethod(SQLiteObject , EOFile , bool , (S32 resultSet) , "(S32 resultSet) Same as endOfResult()." )

DefineEngineMethod(SQLiteObject , EOR , bool , (S32 resultSet) , "(S32 resultSet) Same as endOfResult()." )

DefineEngineMethod(SQLiteObject , escapeString , const char * , (String string) , "(string) Escapes the given string, making it safer <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> pass into <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> query." )

DefineEngineMethod(SQLiteObject , firstRow , void , (S32 resultSet) , "(S32 resultSet) Moves the result set's row <a href="/coding/file/pointer_8h/#pointer_8h_1aae1f8d263916ad71bd415381591549c0">pointer</a> <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the very first row in the result set." )

DefineEngineMethod(SQLiteObject , getColumnIndex , S32 , (S32 resultSet, String columnName) , "(resultSet columnName) Looks up the specified column name in the specified result set, and returns the columns index number. A return <a href="/coding/file/pointer_8h/#pointer_8h_1a32aff7c6c4cd253fdf6563677afab5ce">value</a> of 0 indicates the lookup failed <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> some reason(usually this indicates you specified <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> column name that doesn '<a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1aded116371789db1fd63c90ef00c95a3d">t</a> exist or is spelled wrong)." )

DefineEngineMethod(SQLiteObject , getColumnName , const char * , (S32 resultSet, S32 columnIndex) , "(resultSet columnIndex) Looks up the specified column index in the specified result set, and returns the column 's name. A return <a href="/coding/file/pointer_8h/#pointer_8h_1a32aff7c6c4cd253fdf6563677afab5ce">value</a> of an empty string indicates the lookup failed <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> some reason(usually this indicates you specified <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> column index that is invalid or exceeds the number of columns in the result set). Columns are index starting with 1 not 0" )

DefineEngineMethod(SQLiteObject , getLastRowId , S32 , () , "getLastRowId()" )

DefineEngineMethod(SQLiteObject , getRow , S32 , (S32 resultSet) , "(S32 resultSet) Returns what row the result set's row <a href="/coding/file/pointer_8h/#pointer_8h_1aae1f8d263916ad71bd415381591549c0">pointer</a> is currently on." )

DefineEngineMethod(SQLiteObject , lastRow , void , (S32 resultSet) , "(S32 resultSet) Moves the result set's row <a href="/coding/file/pointer_8h/#pointer_8h_1aae1f8d263916ad71bd415381591549c0">pointer</a> <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the very last row in the result set." )

DefineEngineMethod(SQLiteObject , loadOrSaveDb , bool , (const char *filename, bool isSave) , "Loads or saves <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> cached database from the disk db specifed by filename. Second argument determines loading (false) or saving (true). Returns true or false." )

DefineEngineMethod(SQLiteObject , nextRow , void , (S32 resultSet) , "(S32 resultSet) Moves the result set's row <a href="/coding/file/pointer_8h/#pointer_8h_1aae1f8d263916ad71bd415381591549c0">pointer</a> <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the next row." )

DefineEngineMethod(SQLiteObject , numColumns , S32 , (S32 resultSet) , "(S32 resultSet) Returns the number of columns in the result set." )

DefineEngineMethod(SQLiteObject , numResultSets , S32 , () , "numResultSets()" )

DefineEngineMethod(SQLiteObject , numRows , S32 , (S32 resultSet) , "(S32 resultSet) Returns the number of rows in the result set." )

DefineEngineMethod(SQLiteObject , openDatabase , bool , (const char *filename) , "(const char* filename) Opens the database specifed by filename. Returns true or false." )

DefineEngineMethod(SQLiteObject , previousRow , void , (S32 resultSet) , "(S32 resultSet) Moves the result set's row <a href="/coding/file/pointer_8h/#pointer_8h_1aae1f8d263916ad71bd415381591549c0">pointer</a> <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the previous row" )

DefineEngineMethod(SQLiteObject , setRow , void , (S32 resultSet, S32 row) , "(S32 resultSet <a href="/coding/file/types_8h/#types_8h_1a57a2244776e01ad620c556de58eb7880">S32</a> row) Moves the result set's row <a href="/coding/file/pointer_8h/#pointer_8h_1aae1f8d263916ad71bd415381591549c0">pointer</a> <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the row specified. Row indices start at 1 not 0." )

DefineEngineStringlyVariadicMethod(SQLiteObject , getColumn , const char * , 4 , 4 , "(resultSet column) Returns the <a href="/coding/file/pointer_8h/#pointer_8h_1a32aff7c6c4cd253fdf6563677afab5ce">value</a> of the specified column (Column can be specified by name or index) in the current row of the specified result set. If the call fails, the returned string will indicate the error." )

DefineEngineStringlyVariadicMethod(SQLiteObject , getColumnNumeric , F32 , 4 , 4 , "(resultSet column) Returns the <a href="/coding/file/pointer_8h/#pointer_8h_1a32aff7c6c4cd253fdf6563677afab5ce">value</a> of the specified column (Column can be specified by name or index) in the current row of the specified result set. If the call fails, the returned string will indicate the error." )

DefineEngineStringlyVariadicMethod(SQLiteObject , query , S32 , 1 , 5 , "(const char* sql, <a href="/coding/file/types_8h/#types_8h_1a57a2244776e01ad620c556de58eb7880">S32</a> <a href="/coding/file/zipobject_8cpp/#zipobject_8cpp_1ac6c3dfb4c3a68f849f32cbfb21da4e77">mode</a>) Performs an SQL query on the open database and returns an identifier <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> valid result set. <a href="/coding/file/zipobject_8cpp/#zipobject_8cpp_1ac6c3dfb4c3a68f849f32cbfb21da4e77">mode</a> is currently unused, and is reserved <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> future use." )

IMPLEMENT_CONOBJECT(SQLiteObject )

  1
  2//-----------------------------------------------------------------------------
  3// Copyright (c) 2012 GarageGames, LLC
  4//
  5// Permission is hereby granted, free of charge, to any person obtaining a copy
  6// of this software and associated documentation files (the "Software"), to
  7// deal in the Software without restriction, including without limitation the
  8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  9// sell copies of the Software, and to permit persons to whom the Software is
 10// furnished to do so, subject to the following conditions:
 11//
 12// The above copyright notice and this permission notice shall be included in
 13// all copies or substantial portions of the Software.
 14//
 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 21// IN THE SOFTWARE.
 22//
 23// Additional Copyrights
 24//    Copyright 2004 John Vanderbeck  
 25//    Copyright 2016 Chris Calef
 26//-----------------------------------------------------------------------------
 27
 28//-----------------------------------------------------------------------------
 29// This code implements support for SQLite into Torque and TorqueScript
 30//
 31// Essentially this creates a scriptable object that interfaces with SQLite.
 32//-----------------------------------------------------------------------------
 33
 34#include "SQLiteObject.h"
 35
 36#include "console/simBase.h"
 37#include "console/engineAPI.h"
 38
 39#include "console/consoleInternal.h"
 40#include <cstdlib>
 41
 42IMPLEMENT_CONOBJECT(SQLiteObject);
 43
 44
 45SQLiteObject::SQLiteObject()
 46{
 47   m_pDatabase = NULL;
 48   m_szErrorString = NULL;
 49   m_iLastResultSet = 0;
 50   m_iNextResultSet = 1;
 51}
 52
 53SQLiteObject::~SQLiteObject()
 54{
 55   S32 index;
 56   // if we still have a database open, close it
 57   CloseDatabase();
 58   // Clear out any error string we may have left
 59   ClearErrorString();
 60   // Clean up result sets
 61   //
 62   // Boy oh boy this is such a crazy hack!
 63   // I can't seem to iterate through a vector and clean it up without screwing the vector.
 64   // So (HACK HACK HACK) what i'm doing for now is making a temporary vector that
 65   // contains a list of the result sets that the user hasn't cleaned up.
 66   // Clean up all those result sets, then delete the temp vector.
 67   Vector<S32> vTemp;
 68   Vector<S32>::iterator iTemp;
 69
 70   VectorPtr<sqlite_resultset*>::iterator i;
 71   for (i = m_vResultSets.begin(); i != m_vResultSets.end(); i++)
 72   {
 73      vTemp.push_back((*i)->iResultSet);
 74   }
 75   index = 0;
 76   for (iTemp = vTemp.begin(); iTemp != vTemp.end(); iTemp++)
 77   {
 78      Con::warnf("SQLiteObject Warning: Result set #%i was not cleared by script.  Clearing it now.", vTemp[index]);
 79      ClearResultSet(vTemp[index]);
 80      index++;
 81   }
 82
 83   m_vResultSets.clear();
 84
 85}
 86
 87bool SQLiteObject::processArguments(S32 argc, const char **argv)
 88{
 89   if (argc == 0)
 90      return true;
 91   else
 92      return true;
 93}
 94
 95bool SQLiteObject::onAdd()
 96{
 97   if (!Parent::onAdd())
 98      return false;
 99
100   const char *name = getName();
101   if (name && name[0] && getClassRep())
102   {
103      Namespace *parent = getClassRep()->getNameSpace();
104      Con::linkNamespaces(parent->mName, name);
105      mNameSpace = Con::lookupNamespace(name);
106   }
107
108   return true;
109}
110
111// This is the function that gets called when an instance
112// of your object is being removed from the system and being
113// destroyed.  Use this to do your clean up and what not.
114void SQLiteObject::onRemove()
115{
116   CloseDatabase();
117   Parent::onRemove();
118}
119
120// To be honest i'm not 100% sure on when this is called yet.
121// Basically its used to set the values of any persistant fields
122// the object has.  Similiar to the way datablocks work.  I'm
123// just not sure how and when this gets called.
124void SQLiteObject::initPersistFields()
125{
126   Parent::initPersistFields();
127}
128
129//-----------------------------------------------------------------------
130// These functions below are our custom functions that we will tie into
131// script.
132
133S32 Callback(void *pArg, S32 argc, char **argv, char **columnNames)
134{
135   // basically this callback is called for each row in the SQL query result.
136   // for each row, argc indicates how many columns are returned.
137   // columnNames[i] is the name of the column
138   // argv[i] is the value of the column
139
140   sqlite_resultrow* pRow;
141   sqlite_resultset* pResultSet;
142   char* name;
143   char* value;
144   S32 i;
145
146   if (argc == 0)
147      return 0;
148
149   pResultSet = (sqlite_resultset*)pArg;
150   if (!pResultSet)
151      return -1;
152
153   // create a new result row
154   pRow = new sqlite_resultrow;
155   pResultSet->iNumCols = argc;
156   // loop through all the columns and stuff them into our row
157   for (i = 0; i < argc; i++)
158   {
159      // DBEUG CODE
160  //      Con::printf("%s = %s\n", columnNames[i], argv[i] ? argv[i] : "NULL");
161      dsize_t columnNameLen = dStrlen(columnNames[i]) + 1;
162      name = new char[columnNameLen];
163      dStrcpy(name, columnNames[i], columnNameLen);
164      pRow->vColumnNames.push_back(name);
165      if (argv[i])
166      {
167         dsize_t valueLen = dStrlen(argv[i]) + 1;
168         value = new char[valueLen];
169         dStrcpy(value, argv[i], valueLen);
170         pRow->vColumnValues.push_back(value);
171      }
172      else
173      {
174         value = new char[10];
175         dStrcpy(value, "NULL", 10);
176         pRow->vColumnValues.push_back(value);
177      }
178   }
179   pResultSet->iNumRows++;
180   pResultSet->vRows.push_back(pRow);
181
182   // return 0 or else the sqlexec will be aborted.
183   return 0;
184}
185
186bool SQLiteObject::OpenDatabase(const char* filename)
187{
188   // check to see if we already have an open database, and
189   // if so, close it.
190   CloseDatabase();
191
192   Con::printf("CALLING THE OLD OPEN DATABASE FUNCTION!!!!!!!!!!!!!!!!!!");
193
194   // We persist the error string so that the script may make a  
195    // GetLastError() call at any time.  However when we get  
196    // ready to make a call which could result in a new error,   
197    // we need to clear what we have to avoid a memory leak.  
198   ClearErrorString();
199
200   S32 isOpen = sqlite3_open(filename, &m_pDatabase);
201   if (isOpen == SQLITE_ERROR)
202   {
203      // there was an error and the database could not  
204      // be opened.  
205      m_szErrorString = (char *)sqlite3_errmsg(m_pDatabase);
206      Con::executef(this, "2", "onOpenFailed()", m_szErrorString);
207      return false;
208   }
209   else
210   {
211      // database was opened without error
212      Con::executef(this, "1", "onOpened()");
213
214      //Now, for OpenSimEarth, load spatialite dll, so we can have GIS functions.
215      //S32 canLoadExt = sqlite3_load_extension(m_pDatabase,"mod_spatialite.dll",0,0);
216      //Con::printf("opened spatialite extension: %d",canLoadExt);
217      //Sigh, no luck yet. Cannot find function GeomFromText().
218   }
219   return true;
220}
221
222S32 SQLiteObject::ExecuteSQL(const char* sql)
223{
224   S32 iResult;
225   sqlite_resultset* pResultSet;
226
227   // create a new resultset
228   pResultSet = new sqlite_resultset;
229
230   if (pResultSet)
231   {
232      pResultSet->bValid = false;
233      pResultSet->iCurrentColumn = 0;
234      pResultSet->iCurrentRow = 0;
235      pResultSet->iNumCols = 0;
236      pResultSet->iNumRows = 0;
237      pResultSet->iResultSet = m_iNextResultSet;
238      pResultSet->vRows.clear();
239      m_iLastResultSet = m_iNextResultSet;
240      m_iNextResultSet++;
241   }
242   else
243      return 0;
244
245   iResult = sqlite3_exec(m_pDatabase, sql, Callback, (void*)pResultSet, &m_szErrorString);
246   if (iResult == 0)
247   {
248      //SQLITE_OK
249      SaveResultSet(pResultSet);
250      Con::executef(this, "1", "onQueryFinished()");
251      return pResultSet->iResultSet;
252   }
253   else
254   {
255      // error occured
256      Con::executef(this, "2", "onQueryFailed", m_szErrorString);
257      Con::errorf("SQLite failed to execute query, error %s", m_szErrorString);
258      delete pResultSet;
259      return 0;
260   }
261
262   return 0;
263}
264
265void SQLiteObject::CloseDatabase()
266{
267   if (m_pDatabase)
268      sqlite3_close(m_pDatabase);
269
270   m_pDatabase = NULL;
271}
272
273//(The following function is courtesy of sqlite.org, minus changes to use m_pDatabase instead of pInMemory.)
274/*
275** This function is used to load the contents of a database file on disk
276** into the "main" database of open database connection pInMemory, or
277** to save the current contents of the database opened by pInMemory into
278** a database file on disk. pInMemory is probably an in-memory database,
279** but this function will also work fine if it is not.
280**
281** Parameter zFilename points to a null-terminated string containing the
282** name of the database file on disk to load from or save to. If parameter
283** isSave is non-zero, then the contents of the file zFilename are
284** overwritten with the contents of the database opened by pInMemory. If
285** parameter isSave is zero, then the contents of the database opened by
286** pInMemory are replaced by data loaded from the file zFilename.
287**
288** If the operation is successful, SQLITE_OK is returned. Otherwise, if
289** an error occurs, an SQLite error code is returned.
290*/
291S32 SQLiteObject::loadOrSaveDb(const char *zFilename, bool isSave) 
292{
293   S32 rc;                   /* Function return code */
294   sqlite3 *pFile;           /* Database connection opened on zFilename */
295   sqlite3_backup *pBackup;  /* Backup object used to copy data */
296   sqlite3 *pTo;             /* Database to copy to (pFile or pInMemory) */
297   sqlite3 *pFrom;           /* Database to copy from (pFile or pInMemory) */
298
299                       /* Open the database file identified by zFilename. Exit early if this fails
300                       ** for any reason. */
301
302   Con::printf("calling loadOrSaveDb, isSave = %d", isSave);
303
304   if (isSave == false)
305   {//If we're loading, have to create the memory database.
306      if (!(SQLITE_OK == sqlite3_open(":memory:", &m_pDatabase)))
307      {
308         Con::printf("Unable to open a memory database!");
309         return 0;
310      }
311   }
312   rc = sqlite3_open(zFilename, &pFile);
313   if (rc == SQLITE_OK) {
314
315      /* If this is a 'load' operation (isSave==0), then data is copied
316      ** from the database file just opened to database pInMemory.
317      ** Otherwise, if this is a 'save' operation (isSave==1), then data
318      ** is copied from pInMemory to pFile.  Set the variables pFrom and
319      ** pTo accordingly. */
320      pFrom = (isSave ? m_pDatabase : pFile);
321      pTo = (isSave ? pFile : m_pDatabase);
322
323      /* Set up the backup procedure to copy from the "main" database of
324      ** connection pFile to the main database of connection pInMemory.
325      ** If something goes wrong, pBackup will be set to NULL and an error
326      ** code and message left in connection pTo.
327      **
328      ** If the backup object is successfully created, call backup_step()
329      ** to copy data from pFile to pInMemory. Then call backup_finish()
330      ** to release resources associated with the pBackup object.  If an
331      ** error occurred, then an error code and message will be left in
332      ** connection pTo. If no error occurred, then the error code belonging
333      ** to pTo is set to SQLITE_OK.
334      */
335      pBackup = sqlite3_backup_init(pTo, "main", pFrom, "main");
336      if (pBackup) {
337         (void)sqlite3_backup_step(pBackup, -1);
338         (void)sqlite3_backup_finish(pBackup);
339      }
340      rc = sqlite3_errcode(pTo);
341   }
342
343   /* Close the database connection opened on database file zFilename
344   ** and return the result of this function. */
345   (void)sqlite3_close(pFile);
346
347   //if (isSave ==  true)  // Actually, cancel this, I'm sure it will happen automatically and if we don't do it here, we can also use
348   //{                     // this function for periodic saves, such as after saving mission.
349   // sqlite3_close(m_pDatabase);
350   //}
351
352   Con::printf("finished loadOrSaveDb, rc = %d", rc);
353
354   if (rc == 0)
355      return true;
356   else
357      return false;
358}
359
360void SQLiteObject::NextRow(S32 resultSet)
361{
362   sqlite_resultset* pResultSet;
363
364   pResultSet = GetResultSet(resultSet);
365   if (!pResultSet)
366      return;
367
368   pResultSet->iCurrentRow++;
369}
370
371bool SQLiteObject::EndOfResult(S32 resultSet)
372{
373   sqlite_resultset* pResultSet;
374
375   pResultSet = GetResultSet(resultSet);
376   if (!pResultSet)
377      return true;
378
379   if (pResultSet->iCurrentRow >= pResultSet->iNumRows)
380      return true;
381
382   return false;
383}
384
385void SQLiteObject::ClearErrorString()
386{
387   if (m_szErrorString)
388      sqlite3_free(m_szErrorString);
389
390   m_szErrorString = NULL;
391}
392
393void SQLiteObject::ClearResultSet(S32 index)
394{
395   if (index <= 0)
396      return;
397
398   sqlite_resultset* resultSet;
399   S32 iResultSet;
400
401   // Get the result set specified by index
402   resultSet = GetResultSet(index);
403   iResultSet = GetResultSetIndex(index);
404   if ((!resultSet) || (!resultSet->bValid))
405   {
406      Con::warnf("Warning SQLiteObject::ClearResultSet(%i) failed to retrieve specified result set.  Result set was NOT cleared.", index);
407      return;
408   }
409   // Now we have the specific result set to be cleared.
410   // What we need to do now is iterate through each "Column" in each "Row"
411   // and free the strings, then delete the entries.
412   VectorPtr<sqlite_resultrow*>::iterator iRow;
413   VectorPtr<char*>::iterator iColumnName;
414   VectorPtr<char*>::iterator iColumnValue;
415
416   for (iRow = resultSet->vRows.begin(); iRow != resultSet->vRows.end(); iRow++)
417   {
418      // Iterate through rows
419      // for each row iterate through all the column values and names
420      for (iColumnName = (*iRow)->vColumnNames.begin(); iColumnName != (*iRow)->vColumnNames.end(); iColumnName++)
421      {
422         // Iterate through column names.  Free the memory.
423         delete[](*iColumnName);
424      }
425      for (iColumnValue = (*iRow)->vColumnValues.begin(); iColumnValue != (*iRow)->vColumnValues.end(); iColumnValue++)
426      {
427         // Iterate through column values.  Free the memory.
428         delete[](*iColumnValue);
429      }
430      // free memory used by the row
431      delete (*iRow);
432   }
433   // empty the resultset
434   resultSet->vRows.clear();
435   resultSet->bValid = false;
436   delete resultSet;
437   m_vResultSets.erase_fast(iResultSet);
438}
439
440sqlite_resultset* SQLiteObject::GetResultSet(S32 iResultSet)
441{
442   // Get the result set specified by iResultSet
443   VectorPtr<sqlite_resultset*>::iterator i;
444   for (i = m_vResultSets.begin(); i != m_vResultSets.end(); i++)
445   {
446      if ((*i)->iResultSet == iResultSet)
447         break;
448   }
449
450   return *i;
451}
452
453S32 SQLiteObject::GetResultSetIndex(S32 iResultSet)
454{
455   S32 iIndex;
456   // Get the result set specified by iResultSet
457   VectorPtr<sqlite_resultset*>::iterator i;
458   iIndex = 0;
459   for (i = m_vResultSets.begin(); i != m_vResultSets.end(); i++)
460   {
461      if ((*i)->iResultSet == iResultSet)
462         break;
463      iIndex++;
464   }
465
466   return iIndex;
467}
468
469bool SQLiteObject::SaveResultSet(sqlite_resultset* pResultSet)
470{
471   // Basically just add this to our vector.  It should already be filled up.
472   pResultSet->bValid = true;
473   m_vResultSets.push_back(pResultSet);
474
475   return true;
476}
477
478S32 SQLiteObject::GetColumnIndex(S32 iResult, const char* columnName)
479{
480   S32 iIndex;
481   VectorPtr<char*>::iterator i;
482   sqlite_resultset* pResultSet;
483   sqlite_resultrow* pRow;
484
485   pResultSet = GetResultSet(iResult);
486   if (!pResultSet)
487      return 0;
488
489   pRow = pResultSet->vRows[0];
490   if (!pRow)
491      return 0;
492
493   iIndex = 0;
494   for (i = pRow->vColumnNames.begin(); i != pRow->vColumnNames.end(); i++)
495   {
496      if (dStricmp((*i), columnName) == 0)
497         return iIndex + 1;
498      iIndex++;
499   }
500
501   return 0;
502}
503
504S32 SQLiteObject::numResultSets()
505{
506   return m_vResultSets.size();
507}
508
509void SQLiteObject::escapeSingleQuotes(const char* source, char *dest)
510{
511   //To Do: This function needs to step through the source string and insert another single quote  
512   //immediately after every single quote it finds.
513
514}
515
516
517//-----------------------------------------------------------------------
518// These functions are the code that actually tie our object into the scripting
519// language.  As you can see each one of these is called by script and in turn
520// calls the C++ class function.
521// FIX: change all these to DefineEngineMethod!
522
523DefineEngineMethod(SQLiteObject, openDatabase, bool, (const char* filename),, "(const char* filename) Opens the database specifed by filename.  Returns true or false.")
524{
525   return object->OpenDatabase(filename);
526}
527
528DefineEngineMethod(SQLiteObject, loadOrSaveDb, bool, (const char* filename, bool isSave),,
529   "Loads or saves a cached database from the disk db specifed by filename. Second argument determines loading (false) or saving (true). Returns true or false.")
530{
531   return object->loadOrSaveDb(filename, isSave);
532}
533
534DefineEngineMethod(SQLiteObject, closeDatabase, void, (),, "Closes the active database.")
535{
536   object->CloseDatabase();
537}
538
539
540
541DefineEngineStringlyVariadicMethod(SQLiteObject, query, S32, 1, 5,
542   "(const char* sql, S32 mode) Performs an SQL query on the open database and returns an identifier to a valid result set. mode is currently unused, and is reserved for future use.")
543{
544   S32 iCount;
545   S32 iIndex, iLen, iNewIndex, iArg, iArgLen, i;
546   char* szNew;
547
548   if (argc == 4)
549      return object->ExecuteSQL(argv[1]);
550   else if (argc > 4)
551   {
552      // Support for printf type querys, as per Ben Garney's suggestion
553      // Basically what this does is allow the user to insert question marks into their query that will
554      // be replaced with actual data.  For example:
555      // "SELECT * FROM data WHERE id=? AND age<7 AND name LIKE ?"
556
557      // scan the query and count the question marks
558      iCount = 0;
559      iLen = dStrlen(argv[1]);
560      for (iIndex = 0; iIndex < iLen; iIndex++)
561      {
562         if (argv[1][iIndex] == '?')
563            iCount++;
564      }
565
566      // now that we know how many replacements we have, we need to make sure we
567      // have enough arguments to replace them all.  All arguments above 4 should be our data
568      if (argc - 4 == iCount)
569      {
570         // ok we have the correct number of arguments
571         // so now we need to calc the length of the new query string.  This is easily achieved.
572         // We simply take our base string length, subtract the question marks, then add in
573         // the number of total characters used by our arguments.
574         iLen = dStrlen(argv[1]) - iCount;
575         for (iIndex = 1; iIndex <= iCount; iIndex++)
576         {
577            iLen = iLen + dStrlen(argv[iIndex + 3]);
578         }
579         // iLen should now be the length of our new string
580         szNew = new char[iLen];
581
582         // now we need to replace all the question marks with the actual arguments
583         iLen = dStrlen(argv[1]);
584         iNewIndex = 0;
585         iArg = 1;
586         for (iIndex = 0; iIndex <= iLen; iIndex++)
587         {
588            if (argv[1][iIndex] == '?')
589            {
590               // ok we need to replace this question mark with the actual argument
591               // and iterate our pointers and everything as needed.  This is no doubt
592               // not the best way to do this, but it works for me for now.
593               // My god this is really a mess.
594               iArgLen = dStrlen(argv[iArg + 3]);
595               // copy first character
596               szNew[iNewIndex] = argv[iArg + 3][0];
597               // copy rest of characters, and increment iNewIndex
598               for (i = 1; i < iArgLen; i++)
599               {
600                  iNewIndex++;
601                  szNew[iNewIndex] = argv[iArg + 3][i];
602               }
603               iArg++;
604
605            }
606            else
607               szNew[iNewIndex] = argv[1][iIndex];
608
609            iNewIndex++;
610         }
611      }
612      else
613         return 0; // incorrect number of question marks vs arguments
614      Con::printf("Old SQL: %s\nNew SQL: %s", argv[1].getStringValue(), szNew);
615      return object->ExecuteSQL(szNew);
616   }
617
618   return 0;
619}
620
621DefineEngineMethod(SQLiteObject, clearResult, void, (S32 resultSet),, "(S32 resultSet) Clears memory used by the specified result set, and deletes the result set.")
622{
623   object->ClearResultSet(resultSet);
624}
625
626DefineEngineMethod(SQLiteObject, nextRow, void, (S32 resultSet),, "(S32 resultSet) Moves the result set's row pointer to the next row.")
627{
628   sqlite_resultset* pResultSet;
629   pResultSet = object->GetResultSet(resultSet);
630   if (pResultSet)
631   {
632      pResultSet->iCurrentRow++;
633   }
634}
635
636DefineEngineMethod(SQLiteObject, previousRow, void, (S32 resultSet),, "(S32 resultSet) Moves the result set's row pointer to the previous row")
637{
638   sqlite_resultset* pResultSet;
639   pResultSet = object->GetResultSet(resultSet);
640   if (pResultSet)
641   {
642      pResultSet->iCurrentRow--;
643   }
644}
645
646DefineEngineMethod(SQLiteObject, firstRow, void, (S32 resultSet),, "(S32 resultSet) Moves the result set's row pointer to the very first row in the result set.")
647{
648   sqlite_resultset* pResultSet;
649   pResultSet = object->GetResultSet(resultSet);
650   if (pResultSet)
651   {
652      pResultSet->iCurrentRow = 0;
653   }
654}
655
656DefineEngineMethod(SQLiteObject, lastRow, void, (S32 resultSet),, "(S32 resultSet) Moves the result set's row pointer to the very last row in the result set.")
657{
658   sqlite_resultset* pResultSet;
659   pResultSet = object->GetResultSet(resultSet);
660   if (pResultSet)
661   {
662      pResultSet->iCurrentRow = pResultSet->iNumRows - 1;
663   }
664}
665
666DefineEngineMethod(SQLiteObject, setRow, void, (S32 resultSet, S32 row),, "(S32 resultSet S32 row) Moves the result set's row pointer to the row specified.  Row indices start at 1 not 0.")
667{
668   sqlite_resultset* pResultSet;
669   pResultSet = object->GetResultSet(resultSet);
670   if (pResultSet)
671   {
672      pResultSet->iCurrentRow = row - 1;
673   }
674}
675
676DefineEngineMethod(SQLiteObject, getRow, S32, (S32 resultSet),, "(S32 resultSet) Returns what row the result set's row pointer is currently on.")
677{
678   sqlite_resultset* pResultSet;
679   pResultSet = object->GetResultSet(resultSet);
680   if (pResultSet)
681   {
682      return pResultSet->iCurrentRow + 1;
683   }
684   else
685      return 0;
686}
687
688DefineEngineMethod(SQLiteObject, numRows, S32, (S32 resultSet),, "(S32 resultSet) Returns the number of rows in the result set.")
689{
690   sqlite_resultset* pResultSet;
691   pResultSet = object->GetResultSet(resultSet);
692   if (pResultSet)
693   {
694      return pResultSet->iNumRows;
695   }
696   else
697      return 0;
698}
699
700DefineEngineMethod(SQLiteObject, numColumns, S32, (S32 resultSet),, "(S32 resultSet) Returns the number of columns in the result set.")
701{
702   sqlite_resultset* pResultSet;
703   pResultSet = object->GetResultSet(resultSet);
704   if (pResultSet)
705   {
706      return pResultSet->iNumCols;
707   }
708   else
709      return 0;
710}
711
712DefineEngineMethod(SQLiteObject, endOfResult, bool, (S32 resultSet),, "(S32 resultSet) Checks to see if the internal pointer for the specified result set is at the end, indicating there are no more rows left to read.")
713{
714   return object->EndOfResult(resultSet);
715}
716
717DefineEngineMethod(SQLiteObject, EOR, bool, (S32 resultSet),, "(S32 resultSet) Same as endOfResult().")
718{
719   return object->EndOfResult(resultSet);
720}
721
722DefineEngineMethod(SQLiteObject, EOFile, bool, (S32 resultSet),, "(S32 resultSet) Same as endOfResult().")
723{
724   return object->EndOfResult(resultSet);
725}
726
727DefineEngineMethod(SQLiteObject, getColumnIndex, S32, (S32 resultSet, String columnName),, "(resultSet columnName) Looks up the specified column name in the specified result set, and returns the columns index number.  A return value of 0 indicates the lookup failed for some reason (usually this indicates you specified a column name that doesn't exist or is spelled wrong).")
728{
729   return object->GetColumnIndex(resultSet, columnName);
730}
731
732DefineEngineMethod(SQLiteObject, getColumnName, const char *, (S32 resultSet, S32 columnIndex), , "(resultSet columnIndex) Looks up the specified column index in the specified result set, and returns the column's name.  A return value of an empty string indicates the lookup failed for some reason (usually this indicates you specified a column index that is invalid or exceeds the number of columns in the result set). Columns are index starting with 1 not 0")
733{
734   sqlite_resultset* pResultSet;
735   sqlite_resultrow* pRow;
736   S32 iColumn;
737
738   pResultSet = object->GetResultSet(resultSet);
739   if (pResultSet)
740   {
741      pRow = pResultSet->vRows[pResultSet->iCurrentRow];
742      if (!pRow)
743         return "";
744
745      // We assume they specified column by index.  If they know the column name they wouldn't be calling this function :)
746      iColumn = columnIndex;
747      if (iColumn == 0)
748         return "";  // column indices start at 1, not 0
749
750       // now we should have an index for our column name
751      if (pRow->vColumnNames[iColumn])
752         return pRow->vColumnNames[iColumn];
753      else
754         return "";
755   }
756   else
757      return "";
758}
759
760DefineEngineStringlyVariadicMethod(SQLiteObject, getColumn, const char *, 4, 4, "(resultSet column) Returns the value of the specified column (Column can be specified by name or index) in the current row of the specified result set. If the call fails, the returned string will indicate the error.")
761{
762   sqlite_resultset* pResultSet;
763   sqlite_resultrow* pRow;
764   S32 iColumn;
765
766   pResultSet = object->GetResultSet(dAtoi(argv[2]));
767   if (pResultSet)
768   {
769      if (pResultSet->vRows.size() == 0)
770         return "NULL";
771
772      pRow = pResultSet->vRows[pResultSet->iCurrentRow];
773      if (!pRow)
774         return "invalid_row";
775
776      // Is column specified by a name or an index?
777      iColumn = dAtoi(argv[3]);
778      if (iColumn == 0)
779      {
780         // column was specified by a name
781         iColumn = object->GetColumnIndex(dAtoi(argv[2]), argv[3]);
782         // if this is still 0 then we have some error
783         if (iColumn == 0)
784            return "invalid_column";
785      }
786
787      // We temporarily padded the index in GetColumnIndex() so we could return a 
788      // 0 for error.  So now we need to drop it back down.
789      iColumn--;
790
791      // now we should have an index for our column data
792      if (pRow->vColumnValues[iColumn])
793         return pRow->vColumnValues[iColumn];
794      else
795         return "NULL";
796   }
797   else
798      return "invalid_result_set";
799}
800
801DefineEngineStringlyVariadicMethod(SQLiteObject, getColumnNumeric, F32, 4, 4, "(resultSet column) Returns the value of the specified column (Column can be specified by name or index) in the current row of the specified result set. If the call fails, the returned string will indicate the error.")
802{
803   sqlite_resultset* pResultSet;
804   sqlite_resultrow* pRow;
805   S32 iColumn;
806
807   pResultSet = object->GetResultSet(dAtoi(argv[2]));
808   if (pResultSet)
809   {
810
811      if (pResultSet->vRows.size() == 0)
812         return -1;
813
814      pRow = pResultSet->vRows[pResultSet->iCurrentRow];
815      if (!pRow)
816         return -1;//"invalid_row";
817
818       // Is column specified by a name or an index?
819      iColumn = dAtoi(argv[3]);
820      if (iColumn == 0)
821      {
822         // column was specified by a name
823         iColumn = object->GetColumnIndex(dAtoi(argv[2]), argv[3]);
824         // if this is still 0 then we have some error
825         if (iColumn == 0)
826            return -1;//"invalid_column";
827      }
828
829      // We temporarily padded the index in GetColumnIndex() so we could return a 
830      // 0 for error.  So now we need to drop it back down.
831      iColumn--;
832
833      // now we should have an index for our column data
834      if (pRow->vColumnValues[iColumn])
835         return dAtof(pRow->vColumnValues[iColumn]);
836      else
837         return 0;
838   }
839   else
840      return -1;//"invalid_result_set";
841}
842
843
844DefineEngineMethod(SQLiteObject, escapeString, const char *, (String string),, "(string) Escapes the given string, making it safer to pass into a query.")
845{
846   // essentially what we need to do here is scan the string for any occurrences of: ', ", and \
847    // and prepend them with a slash: \', \", \\
848
849   // to do this we first need to know how many characters we are replacing so we can calculate
850   // the size of the new string
851   S32 iCount;
852   S32 iIndex, iLen, iNewIndex;
853   char* szNew;
854
855   iCount = 0;
856   iLen = dStrlen(string);
857   for (iIndex = 0; iIndex < iLen; iIndex++)
858   {
859      if (string[iIndex] == '\'')
860         iCount++;
861      else if (string[iIndex] == '\"')
862         iCount++;
863      else if (string[iIndex] == '\\')
864         iCount++;
865
866   }
867   //   Con::printf("escapeString counts %i instances of characters to be escaped.  New string will be %i characters longer for a total of %i characters.", iCount, iCount, iLen+iCount);
868   szNew = new char[iLen + iCount];
869   iNewIndex = 0;
870   for (iIndex = 0; iIndex <= iLen; iIndex++)
871   {
872      if (string[iIndex] == '\'')
873      {
874         szNew[iNewIndex] = '\\';
875         iNewIndex++;
876         szNew[iNewIndex] = '\'';
877      }
878      else if (string[iIndex] == '\"')
879      {
880         szNew[iNewIndex] = '\\';
881         iNewIndex++;
882         szNew[iNewIndex] = '\"';
883      }
884      else if (string[iIndex] == '\\')
885      {
886         szNew[iNewIndex] = '\\';
887         iNewIndex++;
888         szNew[iNewIndex] = '\\';
889      }
890      else
891         szNew[iNewIndex] = string[iIndex];
892
893      iNewIndex++;
894   }
895   //   Con::printf("Last characters of each string (new, old): %s, %s", argv[2][iIndex-1], szNew[iNewIndex-1]);
896   //   Con::printf("Old String: %s\nNew String: %s", argv[2], szNew);
897
898   return szNew;
899}
900
901
902DefineEngineMethod(SQLiteObject, numResultSets, S32, (),, "numResultSets()")
903{
904   return object->numResultSets();
905}
906
907DefineEngineMethod(SQLiteObject, getLastRowId, S32, (), , "getLastRowId()")
908{
909   return object->getLastRowId();
910}
911