Migration Guide
Included below are some examples and guidance in migrating legacy NWNX ODBC style SQL code to the new NWNX:EE SQL Plugin.
General Information
The previous plugin only had one execution style: the SQL_ExecDirect() function that took a SQL string that had been constructed to execute.
The new SQL Plugin has two methods of operation:
- The same ExecDirect type functionality via NWNX_SQL_ExecuteQuery()
- Prepared Statements similar to that of Java and C# where a SQL string is prepared via NWNX_SQL_PrepareQuery() with placeholders, then script variables are bound to the variables, then executed via NWNX_SQL_ExecutePreparedQuery().
Advantages of using the prepared version are numerous
- No need to escape/un-escape values for things that cause string SQL statements problems (like single quotes or SQL injection issues). Bound data is taken as is and treated as data that won't affect the validity of the SQL statement.
- The ability to serialize/un-serialize ObjectIDs and Objects to and from the database.
- Probably some others, too!
Examples
A legacy (albeit contrived) example might look like this:
int SomeUsefulFunction(object oPC, object oBoard)
{
string sPlayerAccount = SQLEncodeSpecialChars(GetPCPlayerName(oPC));
string sPCName = SQLEncodeSpecialChars(GetName(oPC));
string sql = "SELECT kills FROM kill_history WHERE player = '" + sPlayerAccount +
"' AND name='" + sPCName + "'";
SQLExecDirect(sql);
string sFeedback = "Kill list\n";
int i = 0;
while(SQLFetch() == SQL_SUCCESS) {
sFeedback += SQLDecodeSpecialChars(SQLGetData(1)) + " \n";
i++;
}
if (i == 0)
sFeedback = "No kills.";
AssignCommand(oBoard, ActionSpeakString(sFeedback));
}
The simple, straight forward converted version using existing code as much as possible and with copious comments:
int SomeUsefulFunction(object oPC, object oBoard)
{
string sPlayerAccount = SQLEncodeSpecialChars(GetPCPlayerName(oPC));
string sPCName = SQLEncodeSpecialChars(GetName(oPC));
string sql = "SELECT kills FROM kill_history WHERE player = '" + sPlayerAccount +
"' AND name='" + sPCName + "'";
string sFeedback = "Kill list\n";
int i = 0;
if (ret) {
{
i++;
}
}
else {
}
if (i == 0)
sFeedback = "No kills.";
AssignCommand(oBoard, ActionSpeakString(sFeedback));
}
A more optimal approach that utilizes the parameterized query to avoid having to escape the input data:
int SomeUsefulFunction(object oPC, object oBoard)
{
string sPlayerAccount = GetPCPlayerName(oPC);
string sPCName = GetName(oPC);
string sql = "SELECT kills FROM kill_history WHERE player=? AND name=?";
string sFeedback = "Kill list\n";
int i=0;
if (ret) {
if (ret) {
{
i++;
}
}
else {
}
}
else {
}
if (i == 0)
sFeedback = "No kills.";
AssignCommand(oBoard, ActionSpeakString(sFeedback));
}
Multi-Platform Support
There is a function to retrieve the specific database type to enable multi-platform support through conditional logic.
{
WriteTimestampedLogEntry("Multi-Platform example..");
if (db_type == "MYSQL")
{
sCreate = "CREATE TABLE sql_test (" +
"colInt INT, colFloat FLOAT, colStr VARCHAR(256)," +
"colObjId INT, colObj TEXT(1000000) );";
sInsert = "INSERT INTO sql_test(colInt, colFloat, colStr, colObjId, colObj) VALUES(?, ?, ?, ?, ?)";
}
if (db_type == "POSTGRESQL")
{
sCreate = "CREATE TABLE sql_test (" +
"colInt INT, colFloat FLOAT, colStr VARCHAR(256)," +
"colObjId INT, colObj TEXT );";
sInsert = "INSERT INTO sql_test(colInt, colFloat, colStr, colObjId, colObj) VALUES($1, $2, $3, $4, $5)";
}
if (!b) {
WriteTimestampedLogEntry("Create table failed!");
return;
}
if (!b) {
WriteTimestampedLogEntry("Object insert failed.");
}
...