SQLiteObject.cpp
Engine/source/sqlite/SQLiteObject.cpp
Public Functions
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