Parameters do not pass to a MySql stored procedure from F# - f#

I'd like to invoke a stored procedure with parameters from F#, but it results in exception "MySql.Data.MySqlClient.MySqlException (0x80004005): Incorrect number of arguments for PROCEDURE test.GetValue; expected 1, got 0".
A similar code in C# works perfectly.
This is .NetCoreApp3.1, FSharp.Core/4.7.0 and MySql.Data/8.0.20. MySql server is 8.0.20, OS is Ubuntu 18.04.4 LTS.
Question: Am I doing sth. wrong or is it a bug?
NOTE: If I remove the parameter from stored procedure and hard-code it in its SELECT query, it works fine in F#. Also, when debugging, I can see the parameters perfectly in place before the call.
F# code that results in exception:
module test_mysql_fsharp.main
open System
open System.Data
open MySql.Data.MySqlClient
[<EntryPoint>]
let main argv =
Console.WriteLine("Hello from test_mysql_fsharp !");
let execStoredProc cs storedProcedureName storedProcedureParameters =
use mySqlConnection = new MySqlConnection(cs)
use command = new MySqlCommand(storedProcedureName, mySqlConnection)
command.CommandType = CommandType.StoredProcedure |> ignore
storedProcedureParameters |> List.iter(fun par -> command.Parameters.AddWithValue(par) |> ignore)
mySqlConnection.Open()
let dataTable = new DataTable()
dataTable.Load(command.ExecuteReader())
dataTable
let cs = "server=127.0.0.1;port=3306;database=test;Uid=***;Pwd=***;"
execStoredProc cs "GetValue" [("par0","KA")] |> ignore
0
The similar code in C# works perfectly:
using System;
using System.Collections.Generic;
using System.Data;
using MySql.Data.MySqlClient;
namespace test_mysql_csharp
{
static class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello from test_mysql_csharp !");
var cs = "server=127.0.0.1;port=3306;database=test;Uid=***;Pwd=***;";
var sp = "GetValue";
var ps = new List<MySqlParameter>();
var p = new MySqlParameter("par0", "KA");
ps.Add(p);
var b = ExecStoredProc(cs, sp, ps.ToArray());
}
private static System.Data.DataTable ExecStoredProc(string cs, string spName,
params MySqlParameter[] spParams)
{
using MySqlConnection connection = new MySqlConnection(cs);
using MySqlCommand command = new MySqlCommand(spName, connection);
command.CommandType = CommandType.StoredProcedure;
command.Parameters.AddRange(spParams);
connection.Open();
DataTable dt = new DataTable();
dt.Load(command.ExecuteReader());
return dt;
}
}
}
A few lines to recreate test database and table:
CREATE SCHEMA `test` DEFAULT CHARACTER SET latin1 COLLATE latin1_general_ci ;
use test;
CREATE TABLE `test`.`test` (
`k` VARCHAR(64) NOT NULL,
`v` VARCHAR(64) NULL,
PRIMARY KEY (`k`));
INSERT INTO test.test (k,v) values ('KA','VA');
DELIMITER $$
CREATE PROCEDURE `GetValue`(par0 varchar(64))
BEGIN
select * from test.test where k = par0;
END$$
DELIMITER ;
Thank you in advance -

Simple: What does THIS do?
command.CommandType = CommandType.StoredProcedure |> ignore
It just compared 2 values.
Should be:
command.CommandType <- CommandType.StoredProcedure |> ignore

Related

Write F# DataTable to CSV file

I am trying to write an F# DataTable to csv (or output in a txt). The table I have is defined as follows:
let setup_report_tbl ( tbl : DataTable ) =
ignore( tbl.Columns.Add("business_date", typeof<System.Int32>) )
ignore( tbl.Columns.Add("ticker", typeof<System.String>) )
ignore( tbl.Columns.Add("price", typeof<System.String>) )
ignore( tbl.Columns.Add("rate", typeof<System.Boolean>) )
ignore( tbl.Columns.Add("range", typeof<System.Double>) )
My goal is to write this empty table with headers into a csv or txt. I'm new to F# and not quite sure where to start here, any help is appreciated thanks!
To write a DataTable as CSV, I would do something like this:
open System
open System.IO
open System.Data
let writeCsv (wtr : StreamWriter) (tbl : DataTable) =
let writeValues (values : seq<_>) =
String.Join(',', values)
|> wtr.WriteLine
tbl.Columns
|> Seq.cast<DataColumn>
|> writeValues
for row in tbl.Rows do
row.ItemArray |> writeValues
Note that I haven't done anything to check for special characters in the values, such commas or quotes.
Example:
let tbl = new DataTable()
setup_report_tbl tbl
tbl.Rows.Add(1, "moo", "baa", true, 2.0) |> ignore
use wtr = new StreamWriter(Console.OpenStandardOutput())
writeCsv wtr tbl
Output is:
business_date,ticker,price,rate,range
1,moo,baa,True,2
Update
To avoid compiler error, perhaps try this:
let writeValues (values : seq<_>) =
let s = String.Join(',', values)
wtr.WriteLine(s)
Note that s is a string, so there should be no ambiguity in which version of WriteLine is called.
If you wanted to use an existing library rather than writing your own CSV encoding (which may get tricky when you need to escape things), you could use Deedle which has an easy way to create data frame from a DataTable and save it to a CSV file:
#r "nuget: Deedle"
open Deedle
open System.Data
// Setup table using your function and add some data
let tbl = new DataTable()
setup_report_tbl tbl
tbl.Rows.Add(1, "very\",evil'ticker", "$42", false, 1.23)
// Turn it into a dataframe and save it
let df = Frame.ReadReader(tbl.CreateDataReader())
df.SaveCsv("C:/temp/test.csv")
As a bonus point, you could see if the data frame type from Deedle lets you do some of the other things you want to do with the data - but this depends on your scenario.

Convert Xml String with node prefixes to XElement

This is my xml string
string fromHeader= "<a:From><a:Address>http://ex1.example.org/</a:Address></a:From>";
I want to load it into an XElement, but doing XElement.Parse(fromHeader) gives me an error due to the 'a' prefixes. I tried the following:
XNamespace xNSa = "http://www.w3.org/2005/08/addressing";
string dummyRoot = "<root xmlns:a=\"{0}\">{1}</root>";
var fromXmlStr = string.Format(dummyRoot, xNSa, fromHeader);
XElement xFrom = XElement.Parse(fromXmlStr).Elements().First();
which works, but seriously, do i need 4 lines of code to do this! What is a quickest / shortest way of getting my XElement?
I found out the above 4 lines are equivalent to
XNamespace xNSa = "http://www.w3.org/2005/08/addressing";
XElement xFrom = new XElement(xNSa + "From", new XElement(xNSa + "Address", "http://ex1.example.org/"));
OR ALTERNATIVELY move the NS into the 'From' element before parsing.
var fromStr = "<a:From xmlns:a=\"http://www.w3.org/2005/08/addressing\"><a:Address>http://ex1.example.org/</a:Address></a:From>";
XElement xFrom = XElement.Parse(fromStr);

How to filter Flat File Source using script component

I have the following scenario:
I have thousands of text files with the below format.The column names are written in separate lines where as the row values are delimited by Pipe(|).
START-OF-FILE
PROGRAMNAME=getdata
DATEFORMAT=yyyymmdd
#Some Text
#Some Text
#Some Text
#Some Text
#Some Text
START-OF-FIELDS
Field1
Field2
Field3
------
FieldN
END-OF-FIELDS
TIMESTARTED=Tue May 12 16:04:42 JST 2015
START-OF-DATA
Field1Value|Field2value|Field3Value|...|Field N Value
Field1Value|Field2value|Field3Value|...|Field N Value
------|...........|----|-------
END-OF-DATA
DATARECORDS=30747
TIMEFINISHED=Tue May 12 16:11:53 JST 2015
END-OF-FILE
Now I have a corresponding SQL Server table, where I can easily load the data as destination.
Since I am new to SSIS, having trouble as to how to write the Script Component so that I can filter the Source Text files and easily load into sql server table.
Thanks in advance!
There are a few ways to do it. If the format of the files are constant, there are some useful properties of the flat file connection manager editor. For example, you can add a new flat file connection into the connection managers. There are some properties such as "Rows to skip" for the above file, you could set this to 18. Then it would start at the columns line with the "|".
Another property of the flat file connection manager that may be useful is that if you open the flat file connection manager, and then click on columns in the side menu, you can set the column delimter to the pipe "|"
But if the format of the file will change, e.g. variable number of header rows, you can use a script task to remove any non-piped rows. e.g. the header and footer.
For example, you can add a method such as file.readalllines and then edit or remove the lines as needed then save the file.
Info about that method is here:
https://msdn.microsoft.com/en-us/library/s2tte0y1%28v=vs.110%29.aspx
e.g. to remove last line in script task
string[] lines = File.ReadAllLines( "input.txt" );
StringBuilder sb = new StringBuilder();
int count = lines.Length - 1; // all except last line
for (int i = 0; i < count; i++)
{
sb.AppendLine(lines[i]);
}
File.WriteAllText("output.txt", sb.ToString());
USE Below VB Script in your SSIS SCript Component Task as source
enter code here
Imports System
Imports System.Data
Imports System.Math
Imports System.IO
Imports Microsoft.SqlServer.Dts.Runtime
Imports Microsoft.SqlServer.Dts.Pipeline.Wrapper
Imports Microsoft.SqlServer.Dts.Runtime.Wrapper
<Microsoft.SqlServer.Dts.Pipeline.SSISScriptComponentEntryPointAttribute()> _
<CLSCompliant(False)> _
Public Class ScriptMain
Inherits UserComponent
'Private strSourceDirectory As String
'Private strSourceFileName As String
Private strSourceSystem As String
Private strSourceSubSystem As String
Private dtBusinessDate As Date
Public Overrides Sub PreExecute()
MyBase.PreExecute()
'
' Add your code here for preprocessing or remove if not needed
''
End Sub
Public Overrides Sub PostExecute()
MyBase.PostExecute()
'
' Add your code here for postprocessing or remove if not needed
' You can set read/write variables here, for example:
Dim strSourceDirectory As String = Me.Variables.GLOBALSourceDirectory.ToString()
Dim strSourceFileName As String = Me.Variables.GLOBALSourceFileName.ToString()
'Dim strSourceSystem As String = Me.Variables.GLOBALSourceSystem.ToString()
'Dim strSourceSubSystem As String = Me.Variables.GLOBALSourceSubSystem.ToString()
'Dim dtBusinessDate As Date = Me.Variables.GLOBALBusinessDate.Date
End Sub
Public Overrides Sub CreateNewOutputRows()
'
' Add rows by calling the AddRow method on the member variable named "<Output Name>Buffer".
' For example, call MyOutputBuffer.AddRow() if your output was named "MyOutput".
'
Dim sr As System.IO.StreamReader
Dim strSourceDirectory As String = Me.Variables.GLOBALSourceDirectory.ToString()
Dim strSourceFileName As String = Me.Variables.GLOBALSourceFileName.ToString()
'Dim strSourceSystem As String = Me.Variables.GLOBALSourceSystem.ToString()
'Dim strSourceSubSystem As String = Me.Variables.GLOBALSourceSubSystem.ToString()
'Dim dtBusinessDate As Date = Me.Variables.GLOBALBusinessDate.Date
'sr = New System.IO.StreamReader("C:\QRM_SourceFiles\BBG_BONDS_OUTPUT_YYYYMMDD.txt")
sr = New System.IO.StreamReader(strSourceDirectory & strSourceFileName)
Dim lineIndex As Integer = 0
While (Not sr.EndOfStream)
Dim line As String = sr.ReadLine()
If (lineIndex <> 0) Then 'remove header row
Dim columnArray As String() = line.Split(Convert.ToChar("|"))
If (columnArray.Length > 1) Then
Output0Buffer.AddRow()
Output0Buffer.Col0 = columnArray(0).ToString()
Output0Buffer.Col3 = columnArray(3).ToString()
Output0Buffer.Col4 = columnArray(4).ToString()
Output0Buffer.Col5 = columnArray(5).ToString()
Output0Buffer.Col6 = columnArray(6).ToString()
Output0Buffer.Col7 = columnArray(7).ToString()
Output0Buffer.Col8 = columnArray(8).ToString()
Output0Buffer.Col9 = columnArray(9).ToString()
Output0Buffer.Col10 = columnArray(10).ToString()
Output0Buffer.Col11 = columnArray(11).ToString()
Output0Buffer.Col12 = columnArray(12).ToString()
Output0Buffer.Col13 = columnArray(13).ToString()
Output0Buffer.Col14 = columnArray(14).ToString()
Output0Buffer.Col15 = columnArray(15).ToString()
Output0Buffer.Col16 = columnArray(16).ToString()
Output0Buffer.Col17 = columnArray(17).ToString()
Output0Buffer.Col18 = columnArray(18).ToString()
Output0Buffer.Col19 = columnArray(19).ToString()
Output0Buffer.Col20 = columnArray(20).ToString()
Output0Buffer.Col21 = columnArray(21).ToString()
Output0Buffer.Col22 = columnArray(22).ToString()
Output0Buffer.Col23 = columnArray(23).ToString()
Output0Buffer.Col24 = columnArray(24).ToString()
End If
End If
lineIndex = lineIndex + 1
End While
sr.Close()
End Sub
End Class
Code End

How to convert a DataSet to FeatureDataSet

I am trying to get the geometry data from a dataset to a featuredataset:
private void QueryCustomer(DataSet ds)
{
SharpMap.Data.FeatureDataSet ds_feature = new SharpMap.Data.FeatureDataSet();
ds_feature = (SharpMap.Data.FeatureDataSet)ds; // ERROR HERE
..
I am getting :
Unable to cast object of type 'System.Data.DataSet' to type 'SharpMap.Data.FeatureDataSet'
Any help would be appreciated. Thanks.
No need to create a DataSet. Just get your table directly from SqLite using the FeatureDataSet:
double x, y;
FeatureDataSet fds = new FeatureDataSet();
Envelope env = new Envelope(double.MinValue, double.MaxValue, double.MinValue, double.MaxValue);
SharpMap.Data.Providers.ManagedSpatiaLite p = new ManagedSpatiaLite(ConnectionString, Table, GeometryColumn.ToUpper(), KeyColumn.ToUpper());
p.Open();
p.ExecuteIntersectionQuery(env, fds);
foreach (FeatureDataRow fdr in ((FeatureDataTable)fds.Tables[0]).Rows)
{
x = fdr.Geometry.Centroid.X;
y = fdr.Geometry.Centroid.Y;
//...process x and y here...
}
p.Close();
p.Dispose();

How to display data on the Datagrid from the dataset using db2 DB

I solved the problem with the data not displaying and have corrected the code below.
I need to populate the dataset with the values returned in a stored procedure in db2. I have written a some code which connected to the DB and seems to execute the SP and am populating the dataset but am not find able to figure out how to display the data from the dataset to the grid. Right now it is blank an no data in the grid.
Imports IBM.Data
Imports IBM.Data.DB2
Imports IBM.Data.DB2.DB2DataReader
'cs is the connection string you create in your application.
Dim conn As DB2Connection = New DB2Connection(cs)
conn.Open()
Dim trans As IDbTransaction = conn.BeginTransaction()
Dim cmd As IDbCommand = conn.CreateCommand()
Dim procName As String = "SP_Name"
cmd.Transaction = trans
cmd.CommandType = CommandType.StoredProcedure
cmd.CommandText = procName
Dim db2da As New DB2DataAdapter
Dim db2ds As New DataSet
db2da.SelectCommand = cmd
cmd.ExecuteNonQuery()
db2da.Fill(db2ds, "Tab1")
Dim introwcount As Integer = db2ds.Tables("Tab1").Rows.Count
Dim intColumncount As Integer = db2ds.Tables("Tab1").Columns.Count
dgvData.DataSource = db2ds.Tables("Tab1")
Appreciate the help.
Thanks
Can you try a DB2Command instead of IDbTransaction? That's how I know to do it, but your requirements may be different than mine.
Dim conn As DB2Connection = New DB2Connection(cs)
Dim cmd As New DB2Command()
cmd.Connection = conn
cmd.CommandText = procName
cmd.CommandType = CommandType.StoredProcedure
Dim rdr AS DB2DataReader = Nothing
Try
conn.Open()
rdr = cmd.ExecuteReader()
dgvData.DataSource = rdr
dgvData.DataBind()
rdr.Close()
Catch ex As DB2Exception
...
Finally
conn.Close()
End Try

Resources