05 Mar, 2010, Nash wrote in the 1st comment:
Votes: 0
I've been fiddling with a MySQL engine, but if it reads in more than one query within about 2 seconds of each other, it causes lovely pointer errors. I am pretty sure it's a SQL error, despite the seg fault in sprintf, because when I comment out the second query, it loads data just fine.
(gdb) bt 
#0 0x00007f7915987030 in strlen () from /lib/libc.so.6
#1 0x00007f7915953cb1 in vfprintf () from /lib/libc.so.6
#2 0x00007f7915972dc9 in vsprintf () from /lib/libc.so.6
#3 0x000000000045c4af in printf_to_char (ch=0x13ef340,
fmt=0x536ee0 "(#%d)Guild Name: '%s%s{x'\n\rWho Title: '{g%s{x'\n\r")
at comm.c:2072
#4 0x000000000042fadf in do_guildstat (ch=0x13ef340,
argument=0x7fff1f3858da "skoth") at act_util.c:1130
#5 0x00000000004854c5 in interpret (ch=0x13ef340,
argument=0x7fff1f3858da "skoth") at interp.c:705
#6 0x0000000000441ff5 in substitute_alias (d=0x13ed690,
argument=0x13ee8c8 "guildstat skoth") at alias.c:58
#7 0x000000000045e9dc in game_loop_unix (control=4) at comm.c:334
#8 0x000000000045eec5 in main (argc=5, argv=0x7fff1f387048) at comm.c:136


Anyone familiar with MySQL C API, or what could be causing this problem?
05 Mar, 2010, Zeno wrote in the 2nd comment:
Votes: 0
Can you show us what you comment out where it works?
05 Mar, 2010, Nash wrote in the 3rd comment:
Votes: 0
void cql_load_db(){
MYSQL_RES *res;
MYSQL_ROW row;
int i = 0,finished = false,status;

for(int n = 0;n < MAX_GUILD;n++)
init_blankguild(n);

log_f("Loading dbs.");
if(mysql_query(db,"SELECT * FROM houses; SELECT * FROM house_ranks;")){
//change the above line to if(mysql_query(db,"SELECT * FROM houses;")){
//and it all works fine
log_f("cql_load_db() Error: %s",mysql_error(db));
return;
}

do{
log_f("1");
res = mysql_store_result(db);
if(res){
cql_process_set(res);
mysql_free_result(res);
}
else{
if(!mysql_field_count(db))
log_f("cql_load_db() : Number of rows affected: %lu\n",(unsigned long)mysql_affected_rows(db));
else{
log_f("cql_load_db() : Could not retrieve result set.");
finished = true;
}
}
status = mysql_next_result(db);
if(status != 0){
finished = true;
if(status > 0)
log_f("cql_load_db() : Could not execute statement.");
}
}while(!finished);
}
05 Mar, 2010, Tyche wrote in the 4th comment:
Votes: 0
Have you set the options for performing multiple queries?

Maybe try this:
log_f("Loading dbs.");
mysql_set_server_option(db, MYSQL_OPTION_MULTI_STATEMENTS_ON);
if(mysql_query(db,"SELECT * FROM houses; SELECT * FROM house_ranks;")){
05 Mar, 2010, Nash wrote in the 5th comment:
Votes: 0
That flag was not accepted. I had the following already set. When I changed it from clientmultistatements to mysqloptionmultistatementson, it would bounce back an error saying "; select * from houseranks;" was invalid syntax, which I guess means "multi statement is not on" When I set both flags, it gave the same error at the same location as when I had just my flag on.


void cql_init(){
int opt_flags;
opt_flags |= CLIENT_MULTI_STATEMENTS;
db = mysql_init(NULL);
if(!mysql_real_connect(db,NULL,"root","password","wake",0,NULL,opt_flags)){
log_f("cql_init() Error: %s", mysql_error(db));
return;
}
}
06 Mar, 2010, Kline wrote in the 6th comment:
Votes: 0
Why exactly do you need to run two queries simultaneously? I have a game that makes fairly extensive use of MySQL (in C) but perhaps I'm too noob to realize why you'd ever need to run two at once like that.
06 Mar, 2010, Tyche wrote in the 7th comment:
Votes: 0
Okay. Yeah you can set the options in real_connect too.
What does cql_process_set() look like?
06 Mar, 2010, Nash wrote in the 8th comment:
Votes: 0
You'd want to run multiple queries all at once to load in data, as a replacement for flatfiles. I'm choosing to not have the lag overhead of calling all information on the fly from the DB, and instead just have SQL as a storage medium.

Here's the processor, the entry point is at the bottom because I'm lazy about prototypes when it's all contained in one file:

void cql_process_ranks(MYSQL_RES *res,MYSQL_FIELD *field){
MYSQL_ROW row;
int i,hid,rid;

while((row = mysql_fetch_row(res))){
hid = atoi(row[0]);
rid = atoi(row[1]);
guilds[hid].rank[rid].name = str_dup(row[2]);
guilds[hid].rank[rid].recruit = atoi(row[3]);
guilds[hid].rank[rid].expel = atoi(row[4]);
guilds[hid].rank[rid].promote = atoi(row[5]);
guilds[hid].rank[rid].demote = atoi(row[6]);
}
if(mysql_errno(db) != 0)
log_f("mysql_fetch_row() failed");
}
void cql_process_houses(MYSQL_RES *res,MYSQL_FIELD *field){
MYSQL_ROW row;
unsigned int i = 0;

while((row = mysql_fetch_row(res))){
log_f("'%s' '%s' '%s' '%d' '%d' '%d' '%d' '%d' '%d' '%d'",row[1],row[2],row[3],atoi(row[4]),atoi(row[5]),atoi(row[6]),atoi(row[7]),atoi(row[8]),atoi(row[9]),atoi(row[10]));
guilds[i].name = str_dup(row[1]);
guilds[i].who_name = str_dup(row[2]);
guilds[i].keywords = str_dup(row[3]);
guilds[i].active = atoi(row[4]);
guilds[i].index = atoi(row[5]);
guilds[i].type = atoi(row[6]);
guilds[i].hidden = atoi(row[7]);
guilds[i].recall = atoi(row[8]);
guilds[i].respawn = atoi(row[9]);
guilds[i].area = atoi(row[10]);
i++;
}
if(mysql_errno(db) != 0)
log_f("mysql_fetch_row() failed");
}
void cql_process_set(MYSQL_RES *res){
MYSQL_FIELD *field = mysql_fetch_field(res);
log_f(" >Loading table %s",field->table);
if(!str_cmp(field->table,"houses"))
cql_process_houses(res,field);
else if(!str_cmp(field->table,"house_ranks"))
cql_process_ranks(res,field);
}
06 Mar, 2010, Kline wrote in the 9th comment:
Votes: 0
Nash said:
You'd want to run multiple queries all at once to load in data, as a replacement for flatfiles. I'm choosing to not have the lag overhead of calling all information on the fly from the DB, and instead just have SQL as a storage medium.


I do this too, but currently just have one or two VeryLong™ queries that run separately.
06 Mar, 2010, Tyche wrote in the 10th comment:
Votes: 0
Looks okay to me.

Seems to me you should go back and put a break point on line 2072 of comm.c..
#3 0x000000000045c4af in printf_to_char (ch=0x13ef340,
fmt=0x536ee0 "(#%d)Guild Name: '%s%s{x'\n\rWho Title: '{g%s{x'\n\r")
at comm.c:2072
#4 0x000000000042fadf in do_guildstat (ch=0x13ef340,
argument=0x7fff1f3858da "skoth") at act_util.c:1130

Find out exactly which parameter in your printf_to_char() format string is fubared.

You could also code up a quick command to dump the contents of the two tables you've loaded in order to confirm they are okay.
06 Mar, 2010, Davion wrote in the 11th comment:
Votes: 0
You also may want to put a check in there to make sure the number of returned rows (can be gathered with mysql_num_rows() ) is not larger then the tables you're dumping them into. You could probably even size them on load.
06 Mar, 2010, Nash wrote in the 12th comment:
Votes: 0
#4  0x000000000042fb3f in do_guildstat (ch=0x7f05a9941cc0,
argument=0x7fffb412469a "skoth") at act_util.c:1130
1130 printf_to_char(ch,"(#%d)Guild Name: '%s%s{x'\n\rWho Title: '{g%s{x'\n\r",guilds[i].index,guilds[i].active ? "{G" : "{R",guilds[i].name,guilds[i].who_name);
(gdb) p i
$1 = 2
(gdb) p guilds[i].name
$2 = 0x7f05abf9a844 "The Kingdom of Skothgard"
(gdb) p guilds[i].active
$3 = false
(gdb) p guilds[i].index
$4 = 0
(gdb) p guilds[i].who_name
$5 = 0x7f0501010101 <Address 0x7f0501010101 out of bounds>
(gdb)
07 Mar, 2010, Kline wrote in the 13th comment:
Votes: 0
I'd say you found your issue with the out of bounds name.
07 Mar, 2010, Nash wrote in the 14th comment:
Votes: 0
Not really. The why and how it's going out of bounds is the problem. When I have only one query, it doesn't go out of bounds. But when I have two, it does. I mentioned this in my original post that I already knew what the problem was (pointer error). I just can't figure out how it's being caused.
07 Mar, 2010, Tyche wrote in the 15th comment:
Votes: 0
Did your logf() in cql_process_houses print guild[x].who_name out at the time of the load?
07 Mar, 2010, Nash wrote in the 16th comment:
Votes: 0
Yes. So it's possible that it's a bug in str_dup, but it's such a simple function that's never been touched (at least by me):

char *str_dup(const char *str){
char *str_new;

if (!str[0])
return &str_empty[0];

if (str >= string_space && str < top_string)
return (char*) str;

str_new = (char*) alloc_mem(strlen(str) + 1);
strcpy(str_new,str);
return str_new;
}
07 Mar, 2010, Davion wrote in the 17th comment:
Votes: 0
Problem solved. Someone shouldn't be using raw sql numbers as array indexes :)
07 Mar, 2010, Nash wrote in the 18th comment:
Votes: 0
Yes yes… but sql should be nicer about its increments.

Big props to davion for violating my code with his big canadian brain. rid was going to illegal values, which is another bug in itself to be whacked out.
0.0/18