I'm teaching myself Pro*C and this is a program with a cursor through records in a database, and it compiles and runs. It gets as a far as prompting "Enter a Guest_ID(type exit to terminate)>>". After that it errors as "Segmentation fault (core dumped)" if an integer is entered. If a string is entered, it seems to go into the conditional immediately inside the outer for loop at
if(nGuest_ID==0)
{
printf("BYE\n");
exit(0);
}
and prints "BYE" then exits. Since I'm still getting familiar with how variables are declared and fed in from SQL, I wasn't sure what declarations might be crucial for troubleshooting here so I'm leaving the code largely intact here.
#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;
/////// begin needs work ///////
// 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;
//////// end needs work ///////
// 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,"myusername");
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 exit 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 :nGuest_ID, :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;
}
// 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);
}
// close the cursor and end the program
exec sql close dbGuest ;
}
exit(0);
}
I'm sure I'm making some simple mistake but I didn't find anything helpful on search. I'm running this on a server, and I'm not getting any debugging back (this is the only major bug I haven't been able to solve up to this point), so what you have is what I have. 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). With this kind of error I'd usually suspect not allocating memory properly, but I don't see anything like that here.
Went through these but not helpful:
Segmentation fault (core dumped) runtime error in C
Not so Useful Error -- Segmentation Fault (Core Dump) in Homework Assignment
Segmentation Fault(core dumped)
Segmentation fault (core dumped) read from stdin
Since nGuest_ID is an int, when calling scanf(), you'll need to provide address of nGuest_ID:
scanf("%d",&nGuest_ID);
That's the likely cause of the core dump you're experiencing, but, also, OracleUser made some excellent suggestions.
Hope that helps.
Related
I believe I may have uncovered a production bug, which causes intermittent problems. Basically, I am trying to understand what the AS400 does when dealing with embedded SQL and cursors. I am thinking that the cursor does not get closed in some cases, causing the next case to fail since the cursor is still open.
Here is a snapshot of the code:
begsr checkfile;
exec sql
declare T1 cursor for
select * from FILE1
where field1 = :var1;
exec sql
open T1;
exec sql
fetch next from T1 into :vrDS;
dow SQLCOD = *zeros;
if a=b;
eval found = 'Y';
leavesr;
endif;
enddo;
exec sql
close T1;
endsr;
My concern is in the leavesr line. If the condition is met, it leaves the subroutine, which skips the close of the cursor T1. In the job log, there are informational messages like "Cursor T1 already open or allocated.". I assume this means that it didn't do anything, or maybe even fetched from the previous cursor? I'm also wondering if the declare statement gets executed every time, or if it just skips that part of the code after the first execution of it. I think I need to put a close T1 statement before the open statement, but I wanted to get a second opinion since it's a production issue that is nearly impossible to recreate due to security key differences between test & production.
Thanks!
a DECLARE CURSOR is actually a compile time statement.
It's never executed at run-time.
:var1 is passed to the DB when the OPEN is done at run time. Here's an answer with a detailed example Using cursor for multiple search conditions
Yes, as the code is shown, the cursor would be left open. And possibly read from rather than opening a fresh cursor during the next run. Depends on what the CLOSQLCUR option is during compile (or as set using EXEC SQL SET OPTION)
You should be checking SQLSTATE/SQLCODE after the OPEN and the FETCH
Another common practice is to do a CLOSE before the OPEN, again be sure to check SQLSTATE/SQLCODE and ignore the CURSOR NOT OPEN on the close.
What Charles said, and also I believe you may in some cases even create an infinite loop with this code! Maybe you aren't giving the whole code, but if the fetch is successful (SQLCOD = 0) and a <> b, then you are stuck in a loop.
I like to put the fetch in a sub procedure that returns *On if a record is successfully read. Then you can do something like this:
dcl-proc MyProc;
dcl-pi *n;
... parameters ...
end-pi;
C1_OpenCursor(parameters);
dow C1_FetchCursor(record);
... do something with the record ...
enddo;
C1_CloseCursor();
end-proc;
// ------------------------
// SQL Open the cursor
dcl-proc C1_OpenCursor;
dcl-pi *n;
... parameters ...
end-pi;
exec sql
declare C1 cursor for ...
exec sql
open C1;
if SQLCOD < 0;
.. error processing ...
endif;
end-proc;
// ------------------------
// SQL Read the cursor
dcl-proc C1_FetchCursor;
dcl-pi *n Ind;
... parameters ...
end-pi;
exec sql
fetch C1 into ...
if SQLCOD = 100;
return *Off;
elseif SQLCOD < 0;
... error handling ...
return *Off;
endif;
return *On;
end-proc;
// ------------------------
// SQL Close the cursor
dcl-proc C1_CloseCursor;
exec sql close C1;
end-proc;
This lets you keep all of your database code in one place, and just call it from your program. Database procedures just access the database and report errors in some way. Your program logic can remain uncluttered by sometimes wordy database and error handling code.
One other thing, I don't check for errors on cursor close because the only error (other than syntax errors) that can be returned here is that the cursor is not open. I don't care about that because that's what I want anyway.
Folks,
Got an odd one for you.
I've got a stored procedure I use to parse some tabular data to create proper SQL geometry objects.
The SP makes use of the function MakeValid().
In my case I have found some objects that fail the test. These do not break the stored procedure however. The Stored procedure is happy to run and simply show the error in the results.
Msg 6522, Level 16, State 1, Line 257 A .NET Framework error occurred
during execution of user-defined routine or aggregate "geometry":
System.FormatException: 24306:
However when I execute this SP in a scheduled job, the first time it encounters one of these exceptions the job fails and the SP stops running.
I'm actually not sure what to do here. I've tried a TRY..CATCH block but this doesn't stop the error being raised and the job exits anyway.
So while it's a MakeValid function that's raising the error I would imagine an SP that encounters a function error would cause the same issue.
Any thoughts how I might handle this? BTW I have no control over the input quality other than to flag the errors and then go back and repair them. I'd still like the rest of the valid records to be processed though.
Here's the operable section of code.
SET #GEOM = geometry::STGeomFromText(#GEOMWKT, 4326)
IF #GEOM.STIsValid() = 0
BEGIN
BEGIN TRY
SET #GEOM = #GEOM.MakeValid()
END TRY
BEGIN CATCH
Print 'Error here'
END CATCH
END
Here is an example of an invalid geometry that throws an error.
SET #GEOM = geometry::STGeomFromText('LINESTRING(-121.895652 37.37225, -121.895652 37.37225)', 4326)
Thanks for any and all assistance.
I'm having a hard time validating that this will work for you (i.e. my local instance is too good at calling MakeValid()), but this might just work for you.
using Microsoft.SqlServer.Server;
using Microsoft.SqlServer.Types;
using System.Data.SqlTypes;
public partial class UserDefinedFunctions
{
[SqlFunction]
public static SqlGeometry TryMakeValid_geometry(SqlGeometry g)
{
SqlGeometry r;
try
{
r = g.MakeValid();
}
catch (System.Exception)
{
r = SqlGeometry.Null;
}
return r;
}
[SqlFunction]
public static SqlGeometry TryParseWKT_geometry(SqlString wkt)
{
SqlGeometry r;
try
{
r = SqlGeometry.Parse(wkt).MakeValid();
}
catch
{
r = SqlGeometry.Null;
}
return r;
}
}
Deploy this CLR to your database and call the function with your geometry instance. It should return NULL if the call to MakeValid() fails.
OK this is the answer that I'm going with but significant credit goes to Ben Thul for the CLR which achieves the same result using a different method.
The realisation is more correctly understanding the question. I made two mistakes. The first was not realising the error I was trying to catch was outside the TRY..CATCH block. The second was believing I needed to try and validate the WKB expression of the geometry. In fact, the following method allows the validation of the WKT before attempting to create a WKB. The following method identifies the invalid WKT, sets a TRUE/FALSE condition. With the WKT validated a valid WKB can be safely created.
Once I understood the question asking the right question turned up the solution here.
https://gis.stackexchange.com/questions/66642/detecting-invalid-wkt-in-text-column-in-sql-server
I have adapted the code to suit my implementation.
/*REVISED CODE*/
DECLARE #valid bit
DECLARE #GEOMWKT varchar(max)
DECLARE #GEOM geometry
Set #GEOMWKT = 'LINESTRING(-121.895652 37.37225, -121.895652 37.37225)'-- Test faulty value
Set #valid = 1
--Test WKT
BEGIN TRY --The is the test here.
SET #valid = geometry::STGeomFromText(#GEOMWKT, 4326).STIsValid()--We don't try to
END TRY --MakeValid(). No point.
BEGIN CATCH
SET #valid = 0
END CATCH
--Now that we know whether the WKT will pass or fail we can operate in a known
--state and avoid raising an error
IF #valid = 1
BEGIN
SET #GEOM = geometry::STGeomFromText(#GEOMWKT, 4326)--Safely create a valid WKB
END
ELSE
BEGIN
SET #GEOM = geometry::STGeomFromText('POINT EMPTY', 4326)-- Set geometry to empty on fail
Print 'Geometry Fail'
END
I am running a database and trying to do all data handling through PS/SQL, and the thing is that once I establish the connection, I have this code, but it fails with the error of
SQLException:Invalid SQL type: sqlKind = UNINITIALIZED
The reason why I am executing it in java is because my assignment tells me that java will handle some things (that I haven't included here), but as for everything else (data manipulation and error checking), it must be handled by oracle. Honestly this harsh restriction sounds silly to me.
The intent of the code below is to read user input via an anonymous block and then print it to the DBMS output, though I'm not sure if that is even possible.
Can anyone help me to fix this?
try {
CallableStatement cs = dbConnection.prepareCall("" +
"DECLARE" +
"myMessage varchar2 (20);" +
"BEGIN" +
"myMessage := '&myMessage';" +
"DBMS_OUTPUT.PUT_LINE(myMessage);" +
"END;");
cs.execute();
} catch (SQLException x) {
System.out.println("SQLException:" + x.getMessage());
}
I'm working with files in free pascal and I'm trying to open a file, but if it doesn't exists then I create it.
This is my code:
program messages;
const PATH_ = 'data/messages/';
type messageFields =
record
date : String
; viewed : Boolean
; text : String
; sender : String [ 8 ]
end
; messagesFile = file of messageFields
;
procedure openMessagesFile ( var _file: messagesFile; _fileName: String; var error: Boolean );
begin
error := false;
assign ( _file, PATH_+_fileName );
{$I-}
reset ( _file );
{$I+}
if ( ioResult <> 0 ) then
error := true;
end;
var _file: messagesFile
; fileName: String
; error: boolean;
begin
readln(filename);
openMessageFile(_file, filename, error);
if ( error ) then
rewrite(_file);
end.
The first time that I execute the program, since the file doesn't exists, throw me an exception.
The second time, works fine!
This is the exception:
An unhandled exception occurred at $00401759 :
EInOutError : Access denied
Have you reproduced this error with the exact code you've posted and I really can't see it causing the error you're getting. I cannot reproduce it and since you haven't included uses SysUtils you should get Runtime error 5 instead of EInOutError.
One thing that's terribly wrong with your code is that you're not closing the file after opening/creating it (although OS usually cleans it up after program finishes). Given this and the fact that you're getting EInOutError instead of Runtime error 5 I believe that your (real, bigger) program keeps the file open after creating it and trying to open it later, but fails since the file is already opened. The second time you run the program the file is already created so it's only opened once (for reading).
The code is a bit atypical, but Windows is known to keep fleeting locks on files for a few seconds even after they are closed and Dos originating code like this might suffer from that.
Maybe using FPC's FileExist() directly works better (IIRC on windows it is findfirst based, and not createfile based)
I'm writting a Perl script for which I need the uptime in seconds to do some calculation, in all the machine in the shop (i.e. linux, SunOS, and AIX). I have a way to get the uptime for linux (/proc/uptime), and SunOS (kstat -p unix:0:system_misc:boot_time), thanks to an another posting on this site, but I can find a good way of getting it for AIX. I don't really like the idea of parsing uptime with reg-ex, since uptime changes when the machine is been up just sec, mins, days, or over a year.
This snippet in C works under AIX 6.1.
I can't give you the source article as I only have source code left.
#include <utmpx.h>
int main ( )
{
int nBootTime = 0;
int nCurrentTime = time ( NULL );
struct utmpx * ent;
while ( ( ent = getutxent ( ) ) ) {
if ( !strcmp ( "system boot", ent->ut_line ) ) {
nBootTime = ent->ut_tv.tv_sec;
}
}
printf ( "System was booted %d seconds ago\n", nCurrentTime - nBootTime );
}
Parse the output of last(1)?
Find a file/directory that is only created/refreshed at boot time and stat it?
Frankly, using different regexs to handle the different possible outputs from uptime doesn't sound so bad.
Answering old thread for new interested stumblers.
We're going to make a lightweight C program called getProcStartTime that you'll have plenty of other uses for. It tells you when a process was started, given the PID. I believe you will still get a time stamp down to the second even if the process was started months or years ago. Save this source code as a file called getProcStartTime.c:
#include <time.h>
#include <procinfo.h>
main(argc, argv)
char *argv[];
{
struct procentry64 psinfo;
pid_t pid;
if (argc > 1) {
pid = atoi(argv[1]);
}
else {
printf("Usage : getProcStartTime pid\n");
return 1;
}
if (getprocs64(&psinfo, sizeof(struct procentry64), NULL, sizeof(struct fdsinfo64) , &pid, 1) > 0) {
time_t result;
result = psinfo.pi_start;
struct tm* brokentime = localtime(&result);
printf("%s", asctime(brokentime));
return 0;
} else {
perror("getproc64");
return 1;
}
}
Then compile it:
gcc getProcStartTime.c -o getProcStartTime
Here's the magic logic: AIX just like Linux has a process called init with PID 1. It can't be killed or restarted. So the start time of PID 1 is the boot time of your server.
./getProcStartTime 1
On my server, returns Wed Apr 23 10:33:30 2014; yours will be different.
Note, I originally made getProcStartTime specifically for this purpose, but now I use it in all kinds of other scripts. Want to know how long an Oracle database has been up? Find the PID of Oracle's PMON and pass that PID as your arg after getProcStartTime.
If you really want the output as an integer number of seconds, it would be an easy programming exercise to modify the code above. The name getProcUptime is just a suggestion. Then you could just call:
./getProcUptime 1
UPDATE: Source code and precompiled binary for AIX 6.1/7.1 have been put on my Github repo here: https://github.com/Josholith/getProcStartTime