First of all... SQLPrepare should be a function, returning if the stament got prepared successfully or not.
I'ts a simple procedure, with no return code.

Strongly Requst DAW to create an fSQLPrepare, or a GetSQLPrepare (or any other name you want) function, if you don't want to change it, to avoid breaking existing code.
At least we can have a function to use for new implementations.

then you think.. ok, let's check for the global err/lasterr indicators to see if they failed or not..
Useless. both of them keep set to false/0.

So, there is no way to check if the statment got prepared, and when you execute SqlExecute, off course the statement will fail..
But then DF enters in a HUGE LOOP.

It just assumes the execution went fine, and then calls SQLGetStatementAttributes as if it was just executed sucessfully.. :-(

Click image for larger version. 

Name:	SQLExecute_not_handling_errors.PNG 
Views:	52 
Size:	77.7 KB 
ID:	15600

As we can see, Call_Driver call retuend iVoid = 7049 , instead of 0, err and lasterr both are set , but no error check is made, and SQLGetStatementAttributes get's executed.

It then enters in a long, long , long loop trying to fetch attributes from 7049 "un-existing" columns ..
this loops take so long, that it never complets in a webapp. The process is terminated by timeout before getting completed.

This happened in a DF 19.1 workspace, but Checked the sql.pkg from df 20.1 and source code is still exactly the same.
There is no error handling inside SQLExecDiret, SQLPrepare and SQLExecute.. immediately after the Call_Driver command.