View previous topic :: View next topic |
Author |
Message |
OVLD_NZ
Joined: 14 Nov 2006 Posts: 18
|
Posted: Fri Dec 08, 2006 5:04 Post subject: SQLFetch - Possible bug |
|
|
SQLFetch doesn't appear to be stepping through the dataset rows.
Code: |
SQLExecDirect("SELECT col1,col2,col3 FROM mytable WHERE id=1");
while(SQLFetch()==SQL_SUCCESS)
{
int icol1=StringToInt(SQLGetData(1));
int icol2=StringToInt(SQLGetData(2));
string scol3=SQLGetData(3);
}
|
The query returns 2 rows affected, but the second time SQLFetch is called it returns SQL_ERROR.
From my understanding this should be working? Shouldn't it? _________________ The World of Judur - The Shadow has Awoken |
|
Back to top |
|
|
caloup
Joined: 29 Sep 2006 Posts: 59 Location: albi (france)
|
Posted: Fri Dec 08, 2006 22:43 Post subject: |
|
|
aren't you supposed to use :
Quote: |
// Position cursor on next row of the resultset
// Call this before using SQLGetData().
// - Leave the parameter empty to advance to the next row.
// - Pass "NEXT" as parameter to fetch the first row of the
// next resultset (for statements that return multiple resultsets)
// returns: SQL_SUCCESS if there is a row
// SQL_ERROR if there are no more rows
int SQLFetch(string mode = " "); |
SQLFetch("NEXT") in your case ?
(i ask but i don't know, i haven't understand yet how does it work) |
|
Back to top |
|
|
Papillon x-man
Joined: 28 Dec 2004 Posts: 1060 Location: Germany
|
Posted: Fri Dec 08, 2006 22:44 Post subject: |
|
|
It should, and it should return all rows that are matched by the query.
First of all, which DB plugin are you using ? Can you please give some more detailed examples (including data in the table, log file output, and so on) ? _________________ Papillon |
|
Back to top |
|
|
caloup
Joined: 29 Sep 2006 Posts: 59 Location: albi (france)
|
Posted: Sat Dec 09, 2006 0:05 Post subject: |
|
|
and have you rename the database dll and ini that you don't need ?
for example, if you are using xp_mysql.dll : rename xp_sqlite.dll to off_xp_sqlite.dll and do the same for xp_sqlite.ini...
(in case of...) |
|
Back to top |
|
|
OVLD_NZ
Joined: 14 Nov 2006 Posts: 18
|
Posted: Sat Dec 09, 2006 5:39 Post subject: |
|
|
The plugin I'm using is MySQL and yes sqlite is not being loaded. There's nothing in the log files, so not much point posting them. But here's the other information.
SQL to create table
Code: |
CREATE TABLE `judur`.`gd_quest_reward` (
`rewardid` int(10) unsigned NOT NULL auto_increment,
`taskid` int(10) unsigned NOT NULL default '0',
`rewardtype` int(10) unsigned NOT NULL default '0',
`reward` text NOT NULL,
`questid` int(10) unsigned NOT NULL default '0',
PRIMARY KEY (`rewardid`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
NWN Script
Code: |
SQLExecDirect("SELECT rewardtype,reward FROM gd_quest_reward WHERE taskid=1");
while(SQLFetch()==SQL_SUCCESS)
{
int rewardtype=StringToInt(SQLGetData(1));
string reward =SQLGetData(2);
}
|
The data in gd_quest_reward is:
Code: |
rewardid,taskid,rewardtype,reward,questid
1, 1, 1, '200', 1
2, 2, 2, 'ju_i_rulirs_pick', 1
3, 1, 1, '200', 1
|
_________________ The World of Judur - The Shadow has Awoken |
|
Back to top |
|
|
Grumalg
Joined: 04 Nov 2005 Posts: 70
|
Posted: Sat Dec 09, 2006 6:58 Post subject: |
|
|
While I haven't tested your specific code I can make some observations just from looking at it.
Your table definition uses a lot of numeric fields. Since NWNX is limited to passing string data by the nature of the hooking mechanism this may be your problem. It's true that MySQL will do a certain amount of implicit type casting under the right conditions, just don't think it will do it correctly as you are useing it. I normally use VARCHAR(n) or CHAR(n) fields to store n digit numbers and do IntToString on writes and StringToInt on reads for numeric data.
Your 'reward' field is TEXT, do you really want to reserve 64kb per field? This too would benefit for useing VARCHAR or CHAR datatype.
Your primary key index is pretty useless, and you have no index at all on the 'taskid' field. You'd get faster performance with an index on 'taskid' since you use it in WHERE clause matching. Note that you cannot make 'taskid' a primary key as it contains non-unique values, but you can make it an ordinary index. There is no requirement that a table must have a primary key at all.
--- Grumalg --- |
|
Back to top |
|
|
OVLD_NZ
Joined: 14 Nov 2006 Posts: 18
|
Posted: Sat Dec 09, 2006 8:26 Post subject: |
|
|
Well, I know I can optimize the table better. But I don't see that as the problem here.
The code will read the first row fine, but fails to goto the next row when I call SQL_Fetch(). Which returns SQL_ERROR.
I can try converting everything to VARCHAR and see what happens, but I don't think it'll solve the problem. _________________ The World of Judur - The Shadow has Awoken |
|
Back to top |
|
|
Grumalg
Joined: 04 Nov 2005 Posts: 70
|
Posted: Sat Dec 09, 2006 10:15 Post subject: |
|
|
My TEXT field and indexing comments didn't apply to your problem. They were just meant to be helpful in general.
I'm not saying you haven't encountered a bug. But attempting to SELECT a numeric field through nwnx could be causeing your problems. Nwnx has never supported returning numbers as binary. By definition everything must be passed back and forth in string form.
Getting a bit more detailed, when you SELECT a numeric field in a table MySQL returns the binary form of the number. When you call SQLGetData(), nwnx grabs the returned bytes and stuffs them into a local string on the module. SQLGetData() then returns the contents of that local string. Your code then takes those bytes and passes them to StringToInt. Since these bytes are binary instead of characters, it's entirely possible that either StringToInt or GetLocalString is chokeing on them. It may be that only certain byte values cause a problem. In that case, it would appear to work on some values and not on others. It's also possible that nwnx itself has some trouble with the multiple binary returns though it's intended to be transparent. However, it's not meant to be used that way, so you can't really say thats a bug.
If you really need the data in the table to be an actual number instead of a number stored as a string, you'd need to CAST such fields into CHAR within the SELECT statement to get them through nwnx. Since that approach entails additional overhead in conversions general practice is to store numbers as strings except for special cases.
It's a bit different when you are writing to the database. You still do need to pass the number down in string form, but MySQL recognizes that you are trying to put a string into a number field and does the conversion automatically if the string can be converted. This is an example of 'implicit' type casting
If you try passing everything through as string and still it doesn't work, then you may have found a bug.
-- Grumalg --- |
|
Back to top |
|
|
caloup
Joined: 29 Sep 2006 Posts: 59 Location: albi (france)
|
Posted: Sat Dec 09, 2006 10:48 Post subject: |
|
|
I think you could try to order your table by taskid before reading the data (but it's just an idea to try if that do anything...) |
|
Back to top |
|
|
OVLD_NZ
Joined: 14 Nov 2006 Posts: 18
|
Posted: Sat Dec 09, 2006 12:10 Post subject: |
|
|
Problem solved
Within the while loop I was performing another database query and of course it resets the dataset. After removing the inner query it works fine. Even with INTEGER fields in the database.
I just needed you guys to make me look harder. _________________ The World of Judur - The Shadow has Awoken |
|
Back to top |
|
|
Papillon x-man
Joined: 28 Dec 2004 Posts: 1060 Location: Germany
|
Posted: Sat Dec 09, 2006 12:18 Post subject: |
|
|
I tried your code and table definition and it works for me.
Code: |
WriteTimestampedLogEntry("Quest rewards with taskid=1:");
SQLExecDirect("SELECT rewardtype,reward FROM gd_quest_reward WHERE taskid=1");
while(SQLFetch()==SQL_SUCCESS)
{
WriteTimestampedLogEntry("Type=" + SQLGetData(1) + ", reward=" + SQLGetData(2) + ".");
}
|
Result:
Code: |
[Sat Dec 09 11:09:19] Quest rewards with taskid=1:
[Sat Dec 09 11:09:19] Type= 1, reward=200.
[Sat Dec 09 11:09:19] Type= 1, reward=200.
|
Please try this debug version of xp_mysql and post the relevant portion of the log file. Note that you have to delete the normal DLL and rename the ini file to xp_mysqld.ini for this to work. You might want to check nwnx.txt and make sure that it is really the debug version that is beeing loaded.
The log should look like this:
Code: | Trace: (TRACE_VERBOSE) * Plugin DoRequest(0x3ef0e20, EXEC, SELECT rewardtype,reward FROM gd_quest_reward WHERE taskid=1)
Trace: (TRACE_VERBOSE) * function=EXEC
Trace: (TRACE_VERBOSE) * Plugin DoRequest(0x3ef0e20, FETCH, )
Trace: (TRACE_VERBOSE) * function=FETCH
Trace: (TRACE_VERBOSE) * Fetch returns a row.
Trace: (TRACE_VERBOSE) * Plugin DoRequest(0x3ef0e20, GETDATA!0, ...................................)
Trace: (TRACE_VERBOSE) * function=GETDATA
Trace: (TRACE_VERBOSE) * parameter1=0
Trace: (TRACE_VERBOSE) * GetData: Get column 0, buffer size 128 bytes
Trace: (TRACE_VERBOSE) * Plugin DoRequest(0x3ef0e20, GETDATA!1, ...................................)
Trace: (TRACE_VERBOSE) * function=GETDATA
Trace: (TRACE_VERBOSE) * parameter1=1
Trace: (TRACE_VERBOSE) * GetData: Get column 1, buffer size 128 bytes
Trace: (TRACE_VERBOSE) * Plugin DoRequest(0x3ef0e20, FETCH, )
Trace: (TRACE_VERBOSE) * function=FETCH
Trace: (TRACE_VERBOSE) * Fetch returns a row.
Trace: (TRACE_VERBOSE) * Plugin DoRequest(0x3ef0e20, GETDATA!0, ...................................)
Trace: (TRACE_VERBOSE) * function=GETDATA
Trace: (TRACE_VERBOSE) * parameter1=0
Trace: (TRACE_VERBOSE) * GetData: Get column 0, buffer size 128 bytes
Trace: (TRACE_VERBOSE) * Plugin DoRequest(0x3ef0e20, GETDATA!1, ...................................)
Trace: (TRACE_VERBOSE) * function=GETDATA
Trace: (TRACE_VERBOSE) * parameter1=1
Trace: (TRACE_VERBOSE) * GetData: Get column 1, buffer size 128 bytes
Trace: (TRACE_VERBOSE) * Plugin DoRequest(0x3ef0e20, FETCH, )
Trace: (TRACE_VERBOSE) * function=FETCH
Trace: (TRACE_VERBOSE) * Fetch returns no row.
|
_________________ Papillon |
|
Back to top |
|
|
Grinning Fool
Joined: 12 Feb 2005 Posts: 264
|
Posted: Sat Dec 09, 2006 20:05 Post subject: |
|
|
Quote: | Getting a bit more detailed, when you SELECT a numeric field in a table MySQL returns the binary form of the number. When you call SQLGetData(), nwnx grabs the returned bytes and stuffs them into a local string on the module. |
I'm not sure this is right -- all of the native mysql APIs handle returned data in string format. A row is returned as MYSQL_ROW, which is char ** -- an array holding individual column values. The conversion here is done before the application sees the data.
Too, when doing joins between multiple tables, it is generally more efficient join on integer values than on varchars, especially if the varchars are more than a few bytes long. _________________ Khalidine, a NWN2 persistent world
Looking for volunteers. |
|
Back to top |
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|