Need beginner help with C (LabWindows/CVI)!

L

lgerhardx

First of all, please forgive my newness to the language. I have had
much experience programming...but not in C! :)

I am modifying a program designed in LabWindows/CVI, which as far as I
can tell is a version of ANSI C. The program needs to communicate
with my AS/400 via sql/odbc to retrieve some information about a
barcode scanned part.

This works. Once. After that, it blows up on the SQLPrepare
command. I have no idea why, and I really don't understand where I've
failed. Any guidance would be greatly appreciated as I try to learn
this language in a matter of days to complete this project... >_<

Below you will find the procedures that I have added for the AS400
connection, per research I did online. In the opening process of the
program it calls DBopen. Then, I am simulating a barcode scan by
calling a procedure TestAS400. It asks for a barcode label and then
uses that in a dynamic sql statement. As I said, this works the first
time. The first time I get the following output:
--------------------------
[data]
DONE,BYE
--------------------------
The second time I get the last 15 characters of the sql statement
followed by 'Error or SQLPrepare!!', then "error!', then "DONE,BYE".

I just don't get it! Is my connection not being closed properly? How
do I fix this? Any ideas are very much appreciated.

Oh, and even though I only wanted one field returned from my sql
statement, I had to make the statement ask for two or my variable
would be corrupted?! I really am being tossed in to this. Sorry if
these are newbie errors.

Procedures:
--------------------------
int DBopen(void)
{
int res=0;
RETCODE retcode;

printf("DBopen Start\n");

/*allocate the environment handle*/
if(SQLAllocEnv(&henv)==SQL_SUCCESS)
{
/*allocate the connection handle*/
if(SQLAllocConnect(henv, &hdbc)==SQL_SUCCESS)
{
/* Set login timeout to 5 seconds. */
//SQLSetConnectOption(hdbc, SQL_LOGIN_TIMEOUT, 5);
//SQLSetConnectOption(hdbc, SQL_CURSOR_TYPE,
SQL_CURSOR_STATIC);
/* Connect to data source */
retcode = SQLConnect(hdbc, "iSeries", SQL_NTS, "USER",
SQL_NTS, "PWD", SQL_NTS);

if (retcode == SQL_SUCCESS || retcode ==
SQL_SUCCESS_WITH_INFO)
{
res=1;
}
}
else
{
SQLFreeConnect(hdbc);
}
}
else
{
SQLFreeEnv(henv);

}
dbopen=res;
return res;
}

void DBclose(void)
{
if(dbopen)
{
SQLDisconnect(hdbc);
SQLFreeConnect(hdbc);
SQLFreeEnv(henv);
}
dbopen=0;
}
int DBexecute(char *sql,HSTMT *hstmt)
{
int res=0;
RETCODE retcode;

if(SQLAllocStmt(hdbc, hstmt)== SQL_SUCCESS)
{
retcode=SQLPrepare(*hstmt,sql,strlen(sql));
if(retcode==SQL_SUCCESS)
{
retcode=SQLExecute(*hstmt);
if(retcode==SQL_SUCCESS)
{
res=1;
}
else
{
printf("Error on SQLExecute!!\n");
}
}
else
{
printf(sql);
printf("Error on SQLPrepare!!\n");
}
}
else
{
printf("Error on SQLAllocStmt!!\n");
}
return res;
}

void DBcloseCursor(HSTMT hstmt)
{
SQLFreeStmt(hstmt, SQL_DROP);
}

void TestAS400()
{
char sql[160];
char *sql2;
HSTMT fstmt;
long lens;
RETCODE retcode;
char name[2 + 1];
char szBarcode[13 + 1];

scanf("%s", szBarcode );

sql2 = strcat("SELECT field1,field2 FROM library.file where field3 =
(select field4 from library.file where field5 ='",szBarcode);
sql2 = strcat(sql2,"')\n");

if(DBexecute(sql2,&fstmt))
{
SQLBindCol(fstmt,1,SQL_C_CHAR,name,sizeof(name),&lens);

retcode = SQLFetch(fstmt);
while(retcode == SQL_SUCCESS || retcode ==
SQL_SUCCESS_WITH_INFO)
{
printf("%s\n",name);
retcode = SQLFetch(fstmt);
}
DBcloseCursor(fstmt);
}
else
{
printf("error!\n");
}
printf("DONE,BYE\n");
//DBclose();

}
 
L

lgerhardx

Oops, forgot to post my headers! There are a number of them in the
source file, but the ones I added for this, and in this order are:

#include <windows.h>

/* for db stuff */
#include <sql.h>
#include <sqlext.h>
#include <sqltypes.h>
#include <sqlucode.h>
#include <stdio.h>
#include <string.h>
/* */

and I also have this defined right after the includes, and outside of
the procedures listed prior:

HENV henv;
HDBC hdbc;
int dbopen=0;
 
A

Antoninus Twink

void TestAS400()
{
char sql[160];
char *sql2;
HSTMT fstmt;
long lens;
RETCODE retcode;
char name[2 + 1];
char szBarcode[13 + 1];

scanf("%s", szBarcode );

sql2 = strcat("SELECT field1,field2 FROM library.file where field3 =
(select field4 from library.file where field5 ='",szBarcode);
sql2 = strcat(sql2,"')\n");

This is very bad news. Firstly, sql2 could be pointing anywhere - you
need to allocate memory before you can write to it. Secondly, strcat()
doesn't behave the way you think it does.

Try:

sql2 = malloc(snprintf(NULL, 0,
"SELECT field1,field2 FROM library.file where field3 = "
"(select field4 from library.file where field5 ='%s')\n", szBarcode)+1);
if(sql2)
sprintf(sql2,
"SELECT field1,field2 FROM library.file where field3 = "
"(select field4 from library.file where field5 ='%s')\n", szBarcode);
else
/* handle error */



Then remember to free(sql2); when you're done with it.
 
K

Keith Thompson

void TestAS400()
{
char sql[160];
char *sql2;
HSTMT fstmt;
long lens;
RETCODE retcode;
char name[2 + 1];
char szBarcode[13 + 1];

scanf("%s", szBarcode );

This reads one whitespace-delimited "word" from stdin and stores it in
szBarcode. You normally have no control over what appears on stdin.
What if the word you read is bigger than what szBarcode can hold? Use
a length specifier rather than a bare "%s" format to avoid this.
Better, read input a line at a time and then process it.
sql2 = strcat("SELECT field1,field2 FROM library.file where field3 =
(select field4 from library.file where field5 ='",szBarcode);
sql2 = strcat(sql2,"')\n");
[...]

Your strcat() calls are certainly a problem. Whether they explain the
symptoms you're seeing is another matter.

strcat() takes two char* arguments, both of which must point to
strings. A copy of the contents of the string pointed to by the
second argument is appended to the end of the string pointed to by the
first argument.

This means that (a) the first string needs to be writable, and (b)
there must be enough room to store the result.

A string literal may or may not be writable, depending on your
implementation, but in any case attempting to write to it invokes
undefined behavior. (Note that this *might* mean that it behaves as
you expect it to.) Either way, a string literal does not allocate any
additional space beyond what's needed for the string itself (including
the terminating '\0'), so you're also attempting to write over memory
that you haven't allocated.

Here's how I might do it:

char *sql2;
const char command[] = "SELECT field1,...";
...
sql2 = malloc(sizeof command + strlen(szBarcode) + 1);
/* Check whether sql2==NULL */
strcpy(sql2, command);
strcat(sql2, szBarcode);

You'll need to check whether malloc succeeded before attempting to use
the result, which means you'll need to decide what to do if it fails;
aborting the program with an error message is probably better than
ignoring the error.

I haven't gone over your code thoroughly, so there may be other
errors; this one just jumped out at me.
 
D

David Given

Antoninus Twink wrote:
[...]
sql2 = malloc(snprintf(NULL, 0,
"SELECT field1,field2 FROM library.file where field3 = "
"(select field4 from library.file where field5 ='%s')\n", szBarcode)+1);
if(sql2)
sprintf(sql2,
"SELECT field1,field2 FROM library.file where field3 = "
"(select field4 from library.file where field5 ='%s')\n", szBarcode);
else
/* handle error */

Just as an addendum to what Antoninus posted, if you don't care about
portability (and it looks like you're a Windows programmer), look to see
if your platform has an aprintf() function (sometimes called
asprintf()). Most libcs have an extension that does this. What it is is
an sprintf() that will return you a newly allocated block of the right
size, which means you avoid having to allocate it yourself (and
therefore avoid getting it wrong). While it's not terribly efficient, it
can make string handling code much, much easier to get right.

If you *don't* have one, or you do care about portability, you may want
to consider writing your own.
Then remember to free(sql2); when you're done with it.

You still need to do this with aprintf(), though.
 
L

lgerhardx

Great! Thanks everyone for the awesome help! I'll put these newly-
learned points to use today.

I do have one more quick dummy question though about what I've seen
here. Sometimes I notice ints and chars defined with an asterisk. I
have not had much success searching online for information on what
that means exactly. Is there either 1) any quick and dirty reference
that you could point me to that would explain such simple things to
me, or 2) just explain in 7,204^2 words or less what exactly the
significance of the * is? <G>

Thanks! I really appreciate your kind assistance!!
 
L

lgerhardx

Thanks for the quick notes, and thanks everyone for your comments and
help! I know a lot more now because of such assistance, and now I
think I can hit the ground running!

I made the some of the changes that were offered and have been having
perfect success retrieving the data I wanted! THANK YOU!
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,482
Members
44,901
Latest member
Noble71S45

Latest Threads

Top