F#: Cannot call stored procedure on MariaDB database using SQLProvider - sql

I adapted my code from the instructions here.
open FSharp.Data.Sql
let [<Literal>] connection_str = "Server=localhost;Port=3306;SSL Mode=None;Uid=<UID>;Pwd=<PWD>;Database=<DB>"
type provider = SqlDataProvider<Common.DatabaseProviderTypes.MYSQL, connection_str>
let context = provider.GetDataContext()
context.Procedures.SpGetFrontContracts.Invoke(1)
Intellisense works until the period after SpGetFrontContracts. After that, nothing. Trying to compile, I get:
Error FS0039 The field, constructor or member 'Invoke' is not defined.
I am otherwise able to connect to the database and insert and query data, as long as I stick to tables and views.
SpGetFrontContracts is a valid stored procedure in my database (its actual name is sp_get_front_contracts, but the type provider seems to remove underscores). I can run it successfully using HeidiSQL. In case it's useful, here's the create code:
CREATE DEFINER=`<UID>`#`localhost` PROCEDURE `sp_get_front_contracts`(
IN `Group` INT
)
LANGUAGE SQL
NOT DETERMINISTIC
CONTAINS SQL
SQL SECURITY DEFINER
COMMENT ''
BEGIN
SELECT p.DateTime, p.Contract, p.Volume, c.Name
FROM tbl_contract_price_data p
INNER JOIN tbl_contracts c on p.Contract = c.ID
WHERE c.`Group` = `Group`
ORDER BY p.DateTime
LIMIT 1000;
END
I tried creating a simpler sproc that was named 'test', took no parameters, and simply ran a select statement. It showed up in the type provider under Procedures, but again I could not call Invoke on it.
My best guess is that Invoke is being inherited from some namespace or assembly reference I don't have, so I'm currently looking through the SqlProvider source to try to figure out what it might be. I'm currently referencing:
FSharp.Core
FSharp.Data
FSharp.Data.SqlProvider
mscorlib
System
System.Core
System.Data
System.Numerics
System.ValueTyple
System.Xml.Linq
Thank you for any suggestions.
Edit: It looks like the action is in SqlDesignTime.fs in a function called generateSprocMethod that starts on line 346. I'll try to figure out what's going on in there.
Edit: After three days banging my head against this, I gave up and just used MySQL Connector/NET.

I'm not sure about your underlying data or structures, but I know these are two other (correct) ways to solve it:
WHERE c.`Group` = `Group` -- what you have.
-- possible corrections:
WHERE c.`Group` = p.`Group` -- did you mean this?
WHERE c.`Group` = "Group" -- or this?

Related

How to prevent SQL Injection in PL/SQL

We have some few packages where we need to resolve some SQL Injection issues. I need some help to rewrite sql statement or sanitize the inputs. Below is the line number where veracode throw the error.
open c_ccl (p_part_nr,p_ctry_cd);
// Source code
CREATE OR REPLACE EDITIONABLE PACKAGE BODY "schema"."Test_PKG" AS
v_data t_cla_class_data;
FUNCTION nat_eccn_cd( p_part_nr IN t_part_nr, p_ctry_cd IN t_ctry_cd )
RETURN t_us_eccn_cd IS
CURSOR c_ccl(p_part_nr CHAR, p_ctry_cd CHAR) IS
SELECT NAT_CCL_CD FROM CLSDBA.CLA_EXP_PART_CTRY e
WHERE e.PART_NR = p_part_nr AND e.CTRY_CD = p_ctry_cd
ORDER BY e.VAL_FROM_DT DESC;
v_ctry_cd char(4) := p_ctry_cd;
v_trf_cd char(4);
BEGIN
v_data.nat_eccn_cd := NULL;
open c_ccl (p_part_nr,p_ctry_cd);
fetch c_ccl INTO v_data.nat_eccn_cd;
close c_ccl;
return (trim(v_data.nat_eccn_cd));
exception when others then return NULL;
end;
I don't see any SQL injection issues with your code - there is no dynamic code where the user inputs could be evaluated and escape out of the expected code flow. Unless your code snippet is generated somewhere else, or one of the column names is really a function that calls dynamic SQL, your code looks safe.
You used the phrase "sanitize the inputs", which is terrible advice for database programming. As much as I love the comic strip XKCD, Randall got this one wrong.
Bind variables are the best solution to avoiding SQL injection. I'll take this opportunity to (poorly) change his comic:

Oracle spatial request working on one instance and not on another

I have this statement that is generated by Geoserver
SELECT
shape AS shape
FROM
(
SELECT
c.chantier_id id,
sdo_geom.sdo_buffer(c.shape, m.diminfo, 1) shape,
c.datedebut datedebut,
c.datefin datefin,
o.nom operation,
c.brouillon brouillon,
e.code etat,
u.utilisateur_id utilisateur,
u.groupe_id groupe
FROM
user_sdo_geom_metadata m, lyv_chantier c
JOIN lyv_utilisateur u ON c.createur_id = u.utilisateur_id
JOIN lyv_etat e ON c.etat_id = e.etat_id
JOIN lyv_operation o ON c.operation = o.id
WHERE
m.table_name = 'LYV_CHANTIER'
AND m.column_name = 'SHAPE'
) vtable
WHERE
( brouillon = 0
AND ( etat != 'archive'
OR etat IS NULL )
AND sdo_filter(shape, mdsys.sdo_geometry(2003, 4326, NULL, mdsys.sdo_elem_info_array(1, 1003, 1), mdsys.sdo_ordinate_array(
2.23365783691406, 48.665657043457, 2.23365783691406, 48.9341354370117, 2.76649475097656, 48.9341354370117, 2.76649475097656, 48.665657043457, 2.23365783691406, 48.665657043457)), 'mask=anyinteract querytype=WINDOW') = 'TRUE' );
On my local instance (dockerized if that can explain anything) it works fine, but on another instance I get an error :
ORA-13226: interface not supported without a spatial index
I guess that the SDO_FILTER is applied to the result of SDO_BUFFER which is therefore not indexed.
But why is it working on my local instance ?!
Is there some kind of weird configuration shenanigan that could explain the different behavior maybe ?
EDIT : The idea behind this is to get around a bug in Geoserver with Oracle databases where it renders only the first point of MultiPoint geometries, but works fine with MutltiPolygon.
I am using a SQL view as layer in Geoserver (hence the subselect I guess).
First, you need to do some debugging here.
Connect to each instance, on the same user as your Geoserver's datasource, and run the sql. From the same connections (in each instance) you must also verify that the user's metadata view (user_sdo_geom_metadata) have an entry for the table and the table has a spatial index - whose owner is the same user as the one you connect.
Also, your query ( select ... from 'vtable') has a column 'shape' which is a buffer of the column lyv_chantier.shape. The sdo_filter, in this sql, expects a spatial index on the vtable.shape - which cannot exist. You should try to use a different alias (e.g. buf_shape) and sdo_filter(buf_shape,...) - to see if the sql fails in both instances, as it should.
I'm in a bit of a hurry right now, so my instructions are summarized. If you want, do this debugging and post the results. We then can go into details.
EDIT: Judging from your efforts, I'd say that the simplest approach is: 1) add a second geometry column to lyv_chantier (e.g. buf_shp). 2) update lyv_chantier set buf_shp = sdo_geom.sdo_buffer(shape,...). 3) insert into user_sdo_geom_metadata the values (lyv_chantier, buf_shp, ...). 4) create a spatial index on column buf_shp. You may need to consider a trigger to update buf_shp whenever shape changes...
This is a very practical approach but you don't provide any info about your case (what is the oracle version, how many rows does the table have, how is it used, why do you want to use sdo_buffer, etc), so that's my recommendation for now.
Also, since you are, most likely, using an sql view as layer in Geoserver (you don't say anything about that, either), you could also consider using pure GS functionality to achieve your goal.
At the end, without describing your goal, it's difficult to provide anything more tailor-made.

SELECT FROM #itab causes syntax error. Why?

I try to use SELECT FROM #itab like explained here in SAP docs.
I have never used this feature, but think this is great. You can query a internal data structure which just exists in the RAM of the interpreter like it would be a real table in the database. I am impressed.
Here is the ABAP code:
data: lt_get_auth_values TYPE STANDARD TABLE OF US335.
CALL FUNCTION 'GET_AUTH_VALUES'
EXPORTING
OBJECT1 = 'Z:FOO'
USER = sy-uname
TABLES
VALUES = lt_get_auth_values.
SELECT highval from #lt_get_auth_values as mytab WHERE field = 'WERKS'
INTO TABLE #DATA(static_perm_filter_fields).
I can't active the function because "from #lt_get_auth_values" is a syntax error according to my system.
What's wrong with this line?
SAP Version: 740 (sorry, it first I thought it was 752)
SELECT ... FROM #itab appeared in 7.52 so it should work.
On my 7.52 system it works but you must indicate a table alias. There's an example in the ABAP documentation (cf first link above).

Delphi Parameterised Query won't work

I'm only a beginner in learning to use Parameterised Queries as I used to do a lot of concatentating before. I've been trying to get this query below to work. It is a simple 'Book' table, with a field called 'BookTitle'. I have a simple textbox where I invite the user to enter any title...and it should run the query below to find if that book exists. Below is my code that, when run, manages to compile. However, when an entry into the textbox is added and the button to run the query is pressed, a Debugger Exception Notification appears with the following statement.
Debugger Exception Notification: Project Project1.exe raised exception class EOleException with message 'Arguments are of the wrong type, are out of acceptable range, or are in conflict with one another'.
I then have the option to press 'Break' or 'Continue'. If I press 'Break', the line:
qbook.Parameters.ParamByName('BookTitle').DataType := ftString;
is filled with a purple/red colour (not sure what this means?).
That said, if I press 'Continue', the program will work as expected, and will continue to do so. Here is the code i've been testing.
procedure TForm4.btnRunQueryClick(Sender: TObject);
var BookEntry:string;
begin
BookEntry:=edtBookTitle.Text;
qbook.SQL.Text:='SELECT BookTitle FROM Book WHERE BookTitle = :BookTitle';
qbook.Parameters.ParamByName('BookTitle').DataType := ftString;
qbook.Parameters.ParamByName('BookTitle').Value := BookEntry;
qbook.Open;
end;
Further points to note: The components in my Delphi form are as follows
a TADOQuery named 'qbook',
a TDataSource,
a TDBGrid,
aTEdit into which the user enters their desired search criteria and
a TButton that once pressed, initiates the query.
With regards to the database, it is:
a MySQL database (Community Edition)
a table named 'Book', where BookID is the PK and is of INT data type.
a field entitled 'BookTitle' which i've set as VARCHAR(35). It is not part of the key. However, it is in the BookTitle field, that i want to apply my query.
NOTE: This answer was posted based on the original code in the question, which has been edited to match what is in my answer. See the question's revision history for the first version of the question on which my answer was based.
The solution you saw in the other post was correct; it was just for a standard TQuery and not TADOQuery. TADOQuery requires a couple of minor syntax changes:
Use Parameters.ParamByName() instead of Parameters
Set a DataType for each parameter before using it
Use .Value instead of .AsString
Here's a corrected version of your code (which also includes setting a value for BookTitle before using it.
procedure TForm4.btnRunQueryClick(Sender: TObject);
var
BookEntry:string;
begin
BookEntry := 'Some book title'; // or QueryEdit.Text or whatever
qbook.SQL.Text:='SELECT BookTitle FROM Book WHERE BookTitle = :BookTitle';
qbook.Parameters.ParamByName('BookTitle').DataType := ftString;
qbook.Parameters.ParamByName('BookTitle').Value := BookEntry;
qbook.Open;
end;
I have never known a string type query parameter need the datatype or whatever set, I would simply remove any reference to the datatype.
After all, if it hurts when you bang your head on a wall, just stop banging it.

.NET ODBC Oracle Params getting param name returned by db provider- possible?

I'm converting some RDO code to ODBC Provider code in .NET.
The problem is parameter names were not specified in the orignal code, but param values were retrieved by parameter name after the command was executed.
Is there anyway to have parameter names populated by the provider once the command is executed so calling code can access params by name.
Let me show you an example of the declaration of param and accessing of it.
With rdqryClntBasic
.Parameters.Add(.CreateParameter) : .Parameters(0).Direction = ParameterDirection.Input
.Parameters(0).DbType = DbType.String
.Parameters(0).Value = sClntProdCd
End With
.EffectiveDate = ToDate(rdqryClntBasic.Parameters("dtEffDt").Value)
You can now see how this "used to work in RDO/VB". For some reason it would accept this and know what the param names were after execution. I imagine it had to do another round trip to the db to get this info.
Is there anyway to mimic this behaviour in .NET for ODBC Provider (using Oracle)? Or am I stuck manually specifying the param names in the code (I understand this is the better option, but wondering what the alternative is to match the original code as closely as possible).
No, parameters in ODBC are positional not by name.