CLR Assembly invocation in SQL Server - sql-server

I have developed a small DLL containing a single function as shown below:
Imports Microsoft.Office.Interop.Excel
Imports Microsoft.Office.Interop
Public Class UWQ_IF_to_Excel
Public Function Excel_Calculate(ByVal p_File_Reference, ByVal p_Sheet_Reference, ByVal p_In_Cells_Values, ByVal p_Result_Cell, ByRef p_Result, ByRef p_Result_Code)
<body>
Return 0
End Function
End Class
Out DBA loaded it with the name xsp_excel.
From the documentation I learned that I need to create a function referencing the assembly, but I keep getting syntax errors even though I'm following the instructions in here.
Could anyone post the right way of doing it?
(NOTE: I'm aware that SQL SERVER does not allow OUT parameters in function (in contrast to other DBMSs), but at this point I want to reach the point in which the function is invoked; once this is working, will change the DLL to return a table like in the example of the link above).
Edit
Based on the example provided gotqn (see comment below), I'm trying this:
CREATE PROCEDURE [UWQ].[p_FUND_Calculate_With_Excel] ( #_l_Excel_File NVARCHAR(512) ,
#_l_Excel_Sheet NVARCHAR( 64) ,
#_l_Excel_String_to_Send NVARCHAR(999) ,
#_l_Excel_Result_Cell NVARCHAR( 16) ,
#_l_Result FLOAT OUTPUT ,
#_l_Result_Code INT OUTPUT )
AS EXTERNAL NAME [dbo].[xsp_excel].[UWQ_IF_to_Excel].[Excel_Calculate] ;
As far as I can see, the syntax appears to be correct (obviously I'm missing something though). Still, I'm getting the error:
Msg 102, Level 15, State 1, Procedure p_FUND_Calculate_With_Excel, Line 8
Incorrect syntax near '.'.
When double-clicking at the error, the cursor is moved to the line AS EXTERNAL NAME [dbo].[xsp_excel].[UWQ_IF_to_Excel].[Excel_Calculate] ;.
Can anyone tell me what the error is?

Related

Search button for a database in vb.net for listview

The code below is not searching, keeps showing error. It is supposed to search for records using a number.
Private Sub btn_search_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btn_search.Click
Me.Sales_fileBindingSource.Filter = "Receipt Number LIKE '" & "%'" & txt_search.Text & "%'"
End Sub
The error being shown is: Syntax error: Missing operand after 'Number' operator.
Click here to see error screenshot
It would make it easier to see what's going on if the code used an interpolated string, so it might end up like:
Me.Sales_fileBindingSource.Filter = $"[Receipt Number] LIKE '%{txt_search.Text}%'"
It is necessary to put the column name in square brackets as it has a space in it - there are examples of the syntax at DataView RowFilter Syntax [C#] (the syntax for the filter is independent of the language).
If you are using an older version of Visual Basic, before interpolated strings were introduced (the year 2015), you'll need something like:
Me.Sales_fileBindingSource.Filter = String.Format("[Receipt Number] LIKE '%{0}%'", txt_search.Text)
which, while a bit longer, still makes it easier to see what is happening than using lots of " & x & "'-type code.
Finally got it! All i had to do was use an under score to join the two words Receipt and Number to one like this "Receipt_Number" Also made other changes like using String.Format. Check code below.
Me.Sales_fileBindingSource.Filter = String.Format("(Convert(Receipt_Number, 'System.String') LIKE '%{0}%')", txt_search.Text)

SQL Server 2012 assembly execution failure - System.NullReferenceException: Object reference not set to an instance of an object

I am trying to execute a UDF which uses a CLR assembly. Each time I try to run it I get the below error:
Msg 6522, Level 16, State 1, Line 45
A .NET Framework error occurred during execution of user-defined routine or aggregate "Geocode":
System.NullReferenceException: Object reference not set to an instance of an object.
at ProSpatial.UserDefinedFunctions.GeocodeUDF(SqlString countryRegion, SqlString adminDistrict, SqlString locality, SqlString postalCode, SqlString addressLine)
It is a geocoding assembly, which is based on the below blog:
https://alastaira.wordpress.com/2012/05/04/geocoding-in-sql-server-with-the-bing-maps-locations-api/
Is there anything I can do to fix it? It was working fine, but just stopped working recently. I changed the Bing Maps key to a new one, but that hasn't resolved the issue. Anyone have any ideas?
Cheers
Edit: I entered a formatted URL into Chrome, to see if I can get the error. I entered the below URL format:
http://dev.virtualearth.net/REST/v1/Locations?countryRegion={0}&adminDistrict={1}&locality={2}&postalCode={3}&addressLine={4}&key={5}&output=xml
All I did was to replace items 0 to 5 with their respective entries. I left the curly brackets in there, and then also tried it without the curly brackets. Without the curly brackets, the URL returned a result with no issues.
In the browser, I am getting geocoded results, but not in SQL any more
Just worked it out. One of the address fields that was being geocoded was null in the database, and the API did not like that. So I removed that from the geocoding list
Looking at the code in that blog, if you are passing in a NULL, then it's not the API that's throwing the error. The error is happening because NULL values are not being handled when the method is first called. The code just casts the SqlString input parameters to string without first checking to see if there is a value in the SqlString variable. SqlString is very similar to a nullable string.
Fortunately, it is rather easy to adjust the code to properly handle NULL values in the input parameters. Starting with a redacted snippet of the original code below, I will show how to do this for one parameter and you can repeat that modification for the others (well, the ones that might actually be NULL in the DB; no need to do this for parameters that are guaranteed to be there due to being in a NOT NULL column).
public static SqlGeography GeocodeUDF(
SqlString countryRegion,
SqlString adminDistrict,
SqlString locality,
SqlString postalCode,
SqlString addressLine
)
{
...
string localAdminDistrict = string.Empty;
if (!adminDistrict.IsNull)
{
localAdminDistrict = adminDistrict.Value;
}
// Attempt to geocode the requested address
try
{
geocodeResponse = Geocode(
(string)countryRegion,
localAdminDistrict,
(string)locality,
(string)postalCode,
(string)addressLine
);
}
All I did was:
Added a local variable for localAdminDistrict, defaulted to an empty string.
Added an if block to set localAdminDistrict if adminDistrict is not null.
Updated the call to Geocode() to use localAdminDistrict instead of (string)adminDistrict.
This should work as long as the Bing Maps API is ok with an empty value for adminDistrict (i.e. ...&adminDistrict=&locality=... ), as opposed to adminDistrict not being present in the GET request (which is easy enough, it just requires an additional modification).
ALSO: As long as you are going to be in there updating the code, you really should move the calls to the Close() and Dispose() methods into a finally block for that try / catch.
For more info on working with SQLCLR in general, please visit: SQLCLR Info

Simplejdbccall Stored Procedure withNamedBinding true

I want to execute stored procedure with dynamic parameters with SimpleJdbcCall. In total I have 6 optional parameters in SQL server SP, out of them I must be able to pass any or none. My SP executes fine as expected in MS Studio. But not by SimpleJdbcCall. I tried in many ways and one of them I tried is withNamedBinding. But it gives Input Syntax error near "=" as below.
this.simpleJdbcCall = new SimpleJdbcCall(jdbcTemplateObject)
.withNamedBinding()
.withSchemaName("dbo")
.withProcedureName("EmployeeDetails")
.useInParameterNames(
paramNameArray)
.returningResultSet("detailReportData", BeanPropertyRowMapper.newInstance(Employee.class));
Map<String,Object> out = this.simpleJdbcCall.execute(sqlSource);
Log:
2019-01-31 18:14:49 DEBUG SimpleJdbcCall:405 - The following
parameters are used for call {call dbo.EmployeeDetails(empCode => ?,
empName => ?, empLoc => ?)} with {empCode=0, empName='hgkghdkgf',
empLoc='kjhjk'} 2019-01-31 18:14:49 DEBUG DispatcherServlet:993 -
Could not complete request
org.springframework.jdbc.UncategorizedSQLException:
CallableStatementCallback; uncategorized SQLException for SQL [{call
dbo.EmployeeDetails(empCode => ?, empName => ?, empLoc => ?)}]; SQL
state [S0001]; error code [102]; Incorrect syntax near '='.; nested
exception is com.microsoft.sqlserver.jdbc.SQLServerException:
Incorrect syntax near '='.
I had faced similar type of problem and found few solutions in this stack overflow question. But in my case I was using MS-SQL server and it was giving me this same syntax error. While searching for solutions I came across this example of Spring (See Section 11.5.6. Declaring parameters to use for a SimpleJdbcCall).
Just in case if the link becomes unavailable, here is what it says
We can opt to declare one, some or all of the parameters explicitly. The parameter metadata is still being used. By calling the method withoutProcedureColumnMetaDataAccess we can specify that we would like to bypass any processing of the metadata lookups for potential parameters and only use the declared ones. Another situation that can arise is that one or more in parameters have default values and we would like to leave them out of the call. To do that we will just call the useInParameterNames to specify the list of in parameter names to include.
And here is the sample code
public class JdbcActorDao implements ActorDao {
private SimpleJdbcCall procReadActor;
public void setDataSource(DataSource dataSource) {
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
jdbcTemplate.setResultsMapCaseInsensitive(true);
this.procReadActor =
new SimpleJdbcCall(jdbcTemplate)
.withProcedureName("read_actor")
.withoutProcedureColumnMetaDataAccess()
.useInParameterNames("in_id")
.declareParameters(
new SqlParameter("in_id", Types.NUMERIC),
new SqlOutParameter("out_first_name", Types.VARCHAR),
new SqlOutParameter("out_last_name", Types.VARCHAR),
new SqlOutParameter("out_birth_date", Types.DATE)
);
}
// ... additional methods
}
I am not sure why but the withNamedBinding() method seems to not work properly with ms-sql server and creates the syntax error.
So in the above solution the work around is to
not to use the .withNamedBinding() method.
use the .withoutProcedureColumnMetaDataAccess() method.
while passing your parameter array to the .useInParameterNames() method, make sure you pass it in the sequence it is declared in the proc (as we are not using name binding). My out parameter was declared few parameters before the last one and so it was giving me error while converting var to int as my out param was int.
So now your solution should look something like this :
this.simpleJdbcCall = new SimpleJdbcCall(jdbcTemplateObject)
.withSchemaName("dbo")
.withProcedureName("EmployeeDetails")
.withoutProcedureColumnMetaDataAccess()
.useInParameterNames(paramNameArray)
.returningResultSet("detailReportData", BeanPropertyRowMapper.newInstance(Employee.class));
Map<String,Object> out = this.simpleJdbcCall.execute(sqlSource);
Give it a try and see if this works for you.

How to call a function with void return using Firedac FDConnection Component in Delphi XE5?

I recently started using the [ExecSQLScalar]1 and [ExecSQL]2 methods of the FDConnection component in Delphi XE5. It's very handy not to need to build a Dataset object, like FDQuery just for simple queries or executions.
However I had a curious problem when executing a function with void return that has internal validations where it can generate exceptions. I'm using a Postgres database.
CREATE FUNCTION can_be_exception()
RETURNS void AS
$$
BEGIN
RAISE EXCEPTION E'fail';
END;
$$
LANGUAGE plpgsql STABLE;
In delphi, I call the ExecSQLScalar function ...
FDConnection1.ExecSQLScalar('select 1');
FDConnection1.ExecSQLScalar('select can_be_exception()');
On first run, I get the following error:
Project TFDConnectionDEMO.exe raised exception class
EPgNativeException with message '[FireDAC][Phys][PG][libpq] ERROR:
fail'.
On the second run, I get a Violation Access error:
Project TFDConnectionDEMO.exe raised exception class $C0000005 with
message 'access violation at 0x00000000: read of address 0x00000000'.
Apparently the error occurs in the line below in unit FireDAC.Comp.Client
function TFDCustomConnection.ExecSQLScalar(const ASQL: String;
const AParams: array of Variant; const ATypes: array of TFieldType): Variant;
var
oCmd: IFDPhysCommand;
begin
oCmd := BaseCreateSQL;
try
if BasePrepareSQL(oCmd, ASQL, AParams, ATypes) or (FExecSQLTab = nil) then begin
FDFree(FExecSQLTab);
...
ignoring the previous error and trying again, another error is displayed...
Project TZConnectionDEMO.exe raised exception class EFDException with
message '[FireDAC][DatS]-24. Row is not nested'.
Searching, I found no response to this error. I figured my mistake would be to call the bank raise_exception function using the ExecSQLScalar function of the FDConnection component. So I tried using FDConnection.ExecSQL and as I imagined, you can not use this if there is a SELECT clause in the parameter.
Is there a better way to call function with void return using FDConnection.ExecSQL? would a BUG be in the component? or would not it be correct to make that kind of call?
Using ExecSQLScalar is fine in this case. This is certainly a bug (which was already fixed, at least in Delphi 10.2.3). As you've correctly pointed out, the problem is in releasing a table storage object instance held by the FExecSQLTab field by using FDFree procedure.
I don't have Delphi XE5 source code but maybe you can see something like this inside (comments about what happened are added by me):
if BasePrepareSQL(oCmd, ASQL, AParams, ATypes) or (FExecSQLTab = nil) then
begin
FDFree(FExecSQLTab); { ← directly calls destructor if object is not nil }
FExecSQLTab := oCmd.Define; { ← no assignment if command execution raises exception }
end;
Problem was that when a SQL command execution raised exception during storage table definition stage (oCmd.Define), reference to previously destroyed storage table object instance (by FDFree) remained stored in the FExecSQLTab field (as a dangling pointer).
Then when a different command was executed that way, FDFree procedure was called just for that dangling pointer. Hence the access violation.
Way to correct this is replacing line e.g. by:
FDFree(FExecSQLTab);
by:
FDFreeAndNil(FExecSQLTab);
which was done in some later Delphi release.

Display a (Synopse) SQLite3 table-column in a Delphi7 TListView

I would like to take the following unit (DrivesData) and display the drive column in a TListView. I've never worked with the (Synopse) SQLite3 code before so I'm hoping someone could give me a little push in the right direction.
Just add the DrivesData unit to the uses clause then run and it will create the "drives.sqlite" database file with a list of drives 'A' to 'Z'.
unit DrivesData;
interface
uses
SynCommons, SQLite3Commons;
type
TDrives = class(TSQLRecord)
private
{ Private declarations }
FDrive: RawUTF8;
protected
{ Protected declarations }
FDrivesModel: TSQLModel;
FDrivesDatabase: TSQLRest;
public
{ Public declarations }
constructor Create(); override;
destructor Destroy(); override;
published
{ Published declarations }
property Drive: RawUTF8 read FDrive write FDrive;
end;
var
DriveRecord: TDrives;
implementation
uses
SQLite3;
function CreateDrivesModel(): TSQLModel;
begin
Result := TSQLModel.Create([TDrives]);
end;
{ TDrives }
constructor TDrives.Create();
var
X: Char;
begin
inherited Create();
FDrivesModel := CreateDrivesModel();
FDrivesDatabase := TSQLRestServerDB.Create(FDrivesModel, 'drives.sqlite');
TSQLRestServerDB(FDrivesDatabase).DB.Execute(
'CREATE TABLE IF NOT EXISTS drives ' +
'(id INTEGER PRIMARY KEY, drive TEXT NOT NULL UNIQUE COLLATE NOCASE);');
for X := 'A' to 'Z' do
begin
TSQLRestServerDB(FDrivesDatabase).DB.Execute(
'INSERT OR IGNORE INTO drives (drive) VALUES ("' + X + ':")');
end;
end;
destructor TDrives.Destroy();
begin
if Assigned(FDrivesDatabase) then
FDrivesDatabase.Free();
if Assigned(FDrivesModel) then
FDrivesModel.Free();
inherited Destroy();
end;
initialization
DriveRecord := TDrives.Create();
finalization
if Assigned(DriveRecord) then
DriveRecord.Free();
end.
Nice try!
But I'm afraid you are missing some points of the framework:
for instance you're mixing record level and MVC application level: a TSQLRecord maps a DB table and you should not declare MVC TSQLModel and TSQLRest inside this class;
and you're missing the ORM approach, you don't need to write all those SQL code (the CREATE TABLE and the INSERT): the framework will write it for you, with no error, and the exact expected column type (with collations)!
Instead of using a TSQLRestServerDB directly by itself, you should better use a TSQLRestClientDB (which will instantiate its privately owned TSQLRestServerDB), even if you are still working locally. So you'll get a lot more features with no performance penalty.
You are using a Char type in your code. Our framework is UTF-8 oriented, so you should use AnsiChar instead, or use StringToUtf8() function to ensure correctness (at least with Unicode version of Delphi).
I'll recommend that you take a look at the sample code source code and the provided documentation (especially the SAD document, in the general presentation in the first pages, including the SynFile main demo).
To retrieve some data, then display it in the VCL (e.g. in a TListBox), take a look at the TSQLTableJSON class. There are some code sample in the SAD document (take a look at the keyword index, at the beginning of the document, if you're a bit lost).
Perhaps StackOverflow is not the best place to ask such specific questions. You have our forum available at http://synopse.info to post any questions regarding this framework. You can post your code here.
Thanks for your interest!

Resources