Domanda

I'm teaching myself Pro*C and have a program that goes (supposedly) through records in a database with a cursor, and it compiles and runs. The problem is that I'm getting whatever junk was in memory when the variables print (those read in using the cursor).

I tried splitting up the sql exec statements several different ways but this didn't help. Also tried opening and closing the sql at different places, also unhelpful. I'm really at the end of a long process of debugging and I'm quite sure at this point that I'm making an extremely newbie mistake. If any Oracle programmers here wouldn't mind taking a second, I'd really like a little feedback on how to get back on track here.

It's supposed to print:

Enter a Guest_ID(type 0 to terminate)>>

1

Charge Summary for: Firstname Lastname Guest-ID: 1

Sales_Item: 1 – Room (Taxable)

Hotel-Id Hotel-Name Trans-Date Quantity Unit-Price Extended-Price

Hotel-Id Hotel-Name Trans-Date Quantity Unit-Price Extended-Price

Hotel-Id Hotel-Name Trans-Date Quantity Unit-Price Extended-Price

Sales Item Total Quantity Extended-Price

It actually prints:

Enter a Guest_ID(type 0 to terminate)>>

3

Charge Summary for: l▒   Guest_ID: 3

I feel like I'm completely messing up the cursor, but I can't pinpoint exactly where the problem is because I'm still getting used to how the variables are declared and used in Pro*C. Also, C programs are usually debugged but this is run on a remote server and debugging is very limited, don't even have dbx commands.

The code:

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
exec sql include sqlca;

// OK - Here we GO
void main()
{
    // First, create all the variables that we will need to communicate between 
    // the "C" program and the database
    exec sql begin declare section;
        //VARCHAR sLastName[51], sFirstName[51], sHotelName[51], sCheckInDate[12], sRoom[11];
        VARCHAR sLastName[51], sFirstName[51], sHotelName[51], sTransDate[11];
        //int nDays, nGuest_ID, nCount;
        int nGuest_ID, nQuantity, nUnitPrice, nCount, nHotelID, nItemID;
        //VARCHAR sInCity[11];
        VARCHAR sItemName[31], sTaxable[11];
        VARCHAR sUserID[21], sPassword[21];
    exec sql end declare section;

        // Now define the cursor we will use to get all of the charges that the guest incurred at all hotels
    exec sql declare dbGuest cursor for
        Select G.Guest_ID, G.Last_Name, G.First_Name, C.Item_ID, C.Item_Name, C.Quantity, C.Unit_Price, C.Trans_Date, H.Hotel_Name, H.Hotel_ID, SI.Taxable
        From Hotel H, Charge C, Stay S, Guest G, Sales_Item SI Where
        C.Stay_ID=S.Stay_ID And H.Hotel_ID=S.Hotel_ID And G.Guest_ID=S.Guest_ID
            And SI.Item_ID=C.Item_ID
        Group By S.Guest_ID;

    // Set up the user-id and password to access my database
    // Because we are using the local database on this server
    // we don't need to use any database location or SID
    strcpy(sUserID.arr,"myuserid"); 
    strcpy(sPassword.arr,"mypassword");  
    sUserID.len=strlen(sUserID.arr);
    sPassword.len=strlen(sPassword.arr);
    exec sql connect :sUserID identified by :sPassword;

    // sqlca.sqlcode is a variable that is set based on the last command sent in to the database
    // a value anything other than zero for what we just did (connect to the database) indicates
    // a error.
    if(sqlca.sqlcode !=0)
       {
        //printf("Sorry, cannot connect to server, pgm aborted %s\n",sqlca.sqlcode); //correction 2/5/14
        printf("Sorry, cannot connect to server, pgm aborted %d\n",sqlca.sqlcode); //change to %d
        exit(1);
       }
    //we made it here, so we were able to open the database correctly
    exec sql SELECT COUNT(*) INTO :nCount FROM Guest;
    printf ("There are %d Guests.\n",nCount);
    for(;;){
        // Read in through stdio the Guest we want to query, then set it up do we can use it
        printf("Enter a Guest_ID(type 0 to terminate)>>\n");
        scanf("%d",&nGuest_ID);
        //Guest_ID.len= strlen(Guest_ID.arr);
        if(nGuest_ID==0)
        {
            printf("BYE\n");
            exit(0);
        }
        printf("%s %s %s %s %d\n","Charge Summary for:", sFirstName.arr, sLastName.arr, " Guest_ID:", nGuest_ID);
        //printf("I do not work yet (type exit to terminate)>>\n");
                // Open our cursor and begin reading records
        exec sql open dbGuest;
        for(;;)
        {
            //exec sql fetch dbGuest into :nGuest_ID, :sLastName, :sFirstName, :sHotelName, :sCheckInDate, :nDays, :sRoom;
            exec sql fetch dbGuest into :sLastName, :sFirstName, :nItemID, :sItemName, :nQuantity, :nUnitPrice, :sTransDate, :sHotelName, :nHotelID;
            if(sqlca.sqlcode !=0)  // If anything went wrong or we read past eof, stop the loop
            {
                break;
            }
            printf("%s %s %s %s %d\n","Charge Summary for:", sFirstName.arr, sLastName.arr, " Guest_ID:", nGuest_ID);
            // Do the crazy stuff to end the C-Strings
            sLastName.arr[sLastName.len] = 0;
            sFirstName.arr[sFirstName.len] = 0;
            sItemName.arr[sItemName.len] = 0;
            sTransDate.arr[sTransDate.len] = 0;
            sHotelName.arr[sHotelName.len] = 0;

            // Print out the information for this guest
            printf("%s %d %s %s \n", "Sales_Item: ", nItemID, " - ", sItemName.arr);

            printf("%d %s %s %d %d \n", nHotelID, " ", sHotelName.arr, " ",sTransDate.arr, " ", nQuantity, " ", nUnitPrice);
        }
        // close the cursor and end the program
        exec sql close dbGuest ;
    }
    exit(0);
}

Normally C programs would be run in debuggers but this is ProC and I'm kind of lost with the whole Oracle ProC debugging thing (since it's running on a remote database).

Went through these but not helpful:

Strange behaviours with oracle nested cursors

Oracle ProC INSERT INTO VALUES ( (select ...) )

Oracle Pro*C updating table with cursor failed

I've been told that the VARCHAR variables should be declared differently, but other ways seem to throw errors.

È stato utile?

Soluzione

Even before fetching the values into sFirstName, you print them. To start with, as you didn't initialise it, it prints garbage value. Also if you feel the cursor fetch is interrupted, before *break*ing the LOOP, use sqlca's sqlerrm to print the error message. Like sqlca.sqlerrm.sqlerrmc

Then, your OPEN CURSOR call would fail, since the query has syntax errors. So, you need to either modify the cursor as below, or modify the query correctly.

We have to check the status of the OPEN cursor, before proceeding, else FETCH would again fail, and the results may be unpredictable. So, please check the sqlca.sqlcode after every EXEC SQL call.

Also, we need to handle NULL, we can use NVL() in queries, if Indicator variables are not used

   exec sql declare dbGuest cursor for
        Select G.Guest_ID,
               G.Last_Name,
               G.First_Name,
               C.Item_ID,
               C.Item_Name,
               C.Quantity,
               C.Unit_Price,
               C.Trans_Date,
               H.Hotel_Name,
               H.Hotel_ID,
               SI.Taxable
        From Hotel H, Charge C, Stay S, Guest G, Sales_Item SI
        Where C.Stay_ID=S.Stay_ID 
          And H.Hotel_ID=S.Hotel_ID 
          And G.Guest_ID=S.Guest_ID
          And SI.Item_ID=C.Item_ID;

Non aggregated columns can be used only with aggregate functions. So either Remove the Grouping or add MAX() to other columns.

Add the below to your declarations

int temp_sales_id = -999;
int first_iter = 1;
int total_nQuantity = 0;
float total_nUnitPrice = 0.0;

Then,

exec sql open dbGuest;

    /* Lets check the status of the OPEN statement before proceeding , else exceptions would be suppressed */
    if(sqlca.sqlcode !=0)  // If anything went wrong or we read past eof, stop the loop
     {
        printf("Error while opening Cursor <%d><%s>\n",sqlca.sqlcode,sqlca.sqlerrm.sqlerrmc);
        break;
     }

for(;;)
    {
        //exec sql fetch dbGuest into :nGuest_ID, :sLastName, :sFirstName, :sHotelName, :sCheckInDate, :nDays, :sRoom;
        exec sql fetch dbGuest into :sLastName, :sFirstName, :nItemID, :sItemName, :nQuantity, :nUnitPrice, :sTransDate, :sHotelName, :nHotelID;

        /* Check for No DATA FOUND */
        if(sqlca.sqlcode == 100 || sqlca.sqlcode == 1403)  // If anything went wrong or we read past eof, stop the loop
        {
            printf("CURSOR is empty after all fetch");
            break;
        }
        /* Check for other errors */
        else if(sqlca.sqlcode != 0)
        {
             printf("Error while fetching from Cursor <%d><%s>\n",sqlca.sqlcode,sqlca.sqlerrm.sqlerrmc);
             break;
        }

        if(first_iter) {
           printf("%s %s %s %s %d\n","Charge Summary for:", sFirstName.arr, sLastName.arr, " Guest_ID:", nGuest_ID);
           first_iter = 0;
        }
        // Do the crazy stuff to end the C-Strings
        sLastName.arr[sLastName.len] = 0;
        sFirstName.arr[sFirstName.len] = 0;
        sItemName.arr[sItemName.len] = 0;
        sTransDate.arr[sTransDate.len] = 0;
        sHotelName.arr[sHotelName.len] = 0;

        if(temp_sales_id == -999 || temp_sales_id != nItemID)
        {
          /* First Item or Sales Item has Changed (next sales id)*/
          temp_sales_id = nItemID;
          // Print out the information for this guest
          printf("%s %d %s %s \n", "Sales_Item: ", nItemID, " - ", sItemName.arr);

          printf("%d %s %s %d %d \n", nHotelID, " ", sHotelName.arr, " ",sTransDate.arr, " ", nQuantity, " ", nUnitPrice);

          total_nQuantity += nQuantity;
          total_nUnitPrice += nUnitPrice;
        }
        if (temp_sales_id != nItemID) {
            /* Printing total for Current Sale id */
            /* If you want to Sum all the sale id together take this finally */
            printf("Total Quantity <%d> Total Extended Price <%g>\n",total_nQuantity,total_nUnitPrice);
            total_nUnitPrice = 0;
            total_nQuantity = 0;
        }

        if(temp_sales_id == -999 || temp_sales_id == nItemID) {
          printf("%d %s %s %d %d \n", nHotelID, " ", sHotelName.arr, " ",sTransDate.arr, " ", nQuantity, " ", nUnitPrice);
        }
    }
    // close the cursor and end the program
    exec sql close dbGuest ;

Altri suggerimenti

If you have garbage values, it could mean that the fetched data is a NULL value in the database. You will need to use an indicator array to see what is NULL. The alternative is to set the DB column to NOT NULL to ensure you will have data to fetch into the variables.

I guess the first thing to check is execute the SQL query in SQLPLUS and see what results you're getting. If there are no NULL values in the result, then your fetched variables should not contain garbage.

If there are NULL values in the result, look to introduce indicator array in your ProC code to handle that situation.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top