Parametrizing dynamic sql query (VB.Net) - sql-server

I have an unsecured query which allows for an injection, and I'm not sure how to go about parameterizing it to prevent said injection
Dim sInsertSQL As String
sInsertSQL = "Insert into tbl_userprop (Prop_Def) values "
Dim tempString As String() = PropertyDefinitions.Split("|")
For i As Integer = 1 To tempString.Length
If tempString(i - 1).Length > 0 Then
sInsertSQL = sInsertSQL + " ('" + tempString(i - 1) + "'),"
bInsert = True
End If
Next
There are up to 10 values stored in tempString and they are concatenated onto sInsertSQL as such: ('val1'), ('val2'), etc
Figured it out, update for the curious:
Dim lstParams As New Collections.Generic.List(Of SqlParameter)
Dim tempString As String() = PropertyDefinitions.Split("|")
For i As Integer = 1 To tempString.Length
If tempString(i - 1).Length > 0 Then
Dim sParamName As String = String.Format("#param{0}", i)
Dim sparam As New SqlParameter(sParamName, tempString(i - 1))
lstParams.Add(sparam)
sInsertSQL = sInsertSQL + " (" + sParamName + "),"
bInsert = True
End If
Next

Once you have split the string you know how many parameters there will be, so you can create the # items for the SQL. After that, you can add the parameters by going through the lists of parameter names and values:
Dim PropertyDefinitions = "abc|def|ghi|jkl|mno"
Dim values = PropertyDefinitions.Split({"|"c})
Dim paramNames = Enumerable.Range(0, values.Count()).Select(Function(n) $"#p{n}")
Dim paramList = String.Join(", ", paramNames.Select(Function(s) $"({s})"))
Dim sql = "INSERT INTO [tbl_userprop] (Prop_Def) VALUES " & paramList
' The following line with the sample data would output '
' INSERT INTO [tbl_userprop] (Prop_Def) VALUES (#p0), (#p1), (#p2), (#p3), (#p4)
'Console.WriteLine(sql)
Dim connStr = "YourConnectionStringHere"
Using conn As New SqlConnection(connStr)
Using cmd As New SqlCommand(sql, conn)
For i = 0 To values.Count() - 1
'TODO: Set the .SqlDbType and .Size to conform to the database definition of [tbl_userprop]. '
cmd.Parameters.Add(New SqlParameter With {.ParameterName = paramNames(i),
.Value = values(i),
.SqlDbType = SqlDbType.NVarChar,
.Size = 99})
Next
'conn.Open()
'cmd.ExecuteNonQuery()
End Using
End Using

Related

Excel (MAC) to SQL Server connection using macro

I have a macro enabled Excel sheet in MAC where I have some data and want to export it to SQL Server. I am trying to truncate the target table first and then insert but my code is not working. The code needs to be working across both MAC and Windows. Please help.
Sub Export_Click()
Dim sqlstring As String
Dim connstring As String
Dim sLogin As String
Dim rng As Range
Dim defaultDate As String
Dim sql As String, bulkSql As String
Dim intImportRow As Integer
Dim Product, Resource As String
Dim name, address as String
'sLogin = "Uid=$;Pwd=$;"
'sqlstringfirma = "SELECT * from tableA"
connstring = "ODBC;DRIVER=SQL Server;SERVER=ServerIP;DATABASE=databse;UID=uid;PWD=pwd"
sql_trunc = "truncate table tableA"
sql_commit = "commit"
sql_query = "select * from table tableA"
With ActiveSheet.QueryTables.Add(Connection:=connstring, Destination:=Range("A1"), sql:=sql_trunc)
.BackgroundQuery = False
'.Refresh
End With
intImportRow = 10
Do Until .Cells(intImportRow, 1) = ""
Product = .Cells(intImportRow, 1)
Resource = .Cells(intImportRow, 2)
sql_insert = "insert into tableA (name, address) values ('" & name & "', '" & address & "')"
With ActiveSheet.QueryTables.Add(Connection:=connstring, Destination:=Range("A1"),
sql:=sql_insert)
.BackgroundQuery = False
.Refr
intImportRow = intImportRow + 1
Loop
End Sub

Passing variables between subs inside class

This may have been asked and answered several times, but I have been unable to find the answer.
In CbProdukt1_SelectedIndexChanged(), I need the associated value from an array from another sub:
Private Sub CbProdukt1_SelectedIndexChanged(sender As Object, e As EventArgs) Handles CbProdukt1.SelectedIndexChanged
With Me.LblEnhed1
.Text = Enhed(CbProdukt1.SelectedIndex)
.Location = New Point(230, 150)
End With
Me.Controls.Add(LblEnhed1)
End Sub
This would be easy enough if Enhed() had been public. But it is not as it is dimensioned and retrieved from Sqlite in another sub:
Dim count As Integer
sql = "SELECT COUNT(varenr) AS varenrcount FROM '" & Varedatabase & "'"
cmd = New SQLiteCommand(sql, conn)
count = cmd.ExecuteScalar
sql = "SELECT * FROM '" & Varedatabase & "'"
cmd = New SQLiteCommand(sql, conn)
reader = cmd.ExecuteReader
Dim a = 0
Dim Varenr(count + 5) As String
Dim Varenavn(count + 5) As String
Dim Enhed(count + 5) As String
While (reader.Read())
Varenr(a) = reader("varenr")
Varenavn(a) = reader("Varenavn")
Enhed(a) = reader("Enhed")
CbProdukt1.Items.Add(varenavn(a))
a += 1
End While
Public declarations are only allowed prior to subs at the top of the class.
So how do I get the Enhed(a) from sub to sub?

Convert from String to integer without truncating zeroes at the left side

I have three strings. 1. str1="450" 2. str2="SKDR" 3. str3="008001". I want to join these three strings. Each time str3 value should be incremented by 1. If we take it as integer then intial zeroes will be truncated.
Private Sub btnSave_Click(sender As Object, e As EventArgs) Handles btnSave.Click
Dim d1 As String = ""
Dim s As String = ""
Dim dtqry1 As Integer = 0
Dim br As String = dgvCanaraBankCAU.CurrentRow.Cells(2).Value.ToString
Dim pro As String = dgvCanaraBankCAU.CurrentRow.Cells(3).Value.ToString
Dim dtqry As DataTable = mainModule.DatabaseTableQuery("SELECT DPNO,StartNumber from Master_CINumber where Branch='" + br + "' and ProjectOffice='" + pro + "'")
If dtqry.Rows.Count > 0 Then
For Each dr As DataRow In dtqry.Rows
d1 = dr(0).ToString
s = dr(1).ToString
Next
Dim chk As DataTable = mainModule.DatabaseTableQuery("Select * from CBEntry where BankBranch='" + br + "' and ProjectOffice='" + pro + "'")
If chk.Rows.Count = 0 Then
dtqry1 = Val(s)
Else
dtqry1 = mainModule.DatabaseScalarQuery("SELECT max(startNumber) from CBEntry where BankBranch='" + br + "' and ProjectOffice='" + pro + "'")
dtqry1 = dtqry1 + 1
End If
End If
End Sub
Use a custom format string passed to In32.ToString to include leading zeros.
Eg.
Dim x = 42
Console.WriteLine(x.ToString("000000"))
will output
000042

Split string from multiple line

I have the following code to split strings in arrays and then instert the arrays into database
Private Sub ReadAllModules()
Dim list As String() = rtbComData.Text.Split(Environment.NewLine.ToCharArray())
For Each Row As String In list
If Not (Row = "AT+BC=FL " + dtp_Rinterval.Value.ToString("dd/MM/yyyy") + "" Or Row = "OK" Or Row = "") Then
Try
Dim separator As String() = {"+", "|", "*"}
Dim s = Row.Split(separator, StringSplitOptions.RemoveEmptyEntries)
Dim con As New SqlConnection
Dim cmd As New SqlCommand
con.ConnectionString = "Data Source=.\SQLEXPRESS;AttachDbFilename=|DataDirectory|\Database\smdData.mdf;Integrated Security=True;User Instance=True"
con.Open()
cmd.Connection = con
cmd.CommandText = "UPDATE smdTable SET date='" + s(1) + "',datetime='" + s(2) + "',Interval1='" + s(3) + "',vi1='" + s(4) + "',Interval2='" + s(5) + "',vi2='" + s(6) + "',Interval3='" + s(7) + "' WHERE AdresaUnica= '" + s(0) + "'"
cmd.ExecuteNonQuery()
Me.SmdTableTableAdapter.Fill(Me.SmdDataDataSet1.smdTable)
Catch ex As Exception
MsgBox(ex.Message)
End Try
End If
Next
End Sub
Until now I had the strings coming on a single line and that was ok. However now my strings comes on different lines like:
AT+BC=FL 09/09/2015
OK
+00019500000068B9|1:9/9/2015|06:55:18|19:30-22:30*100|22:30-00:30*80|00:30- 05:00*40|05:00-
+00019500000068B9|2:07:30*90|OFF
how do I insert the second line string in an array and insert it in the database?

Trouble writing to SQL

I have been writing in VB.Net for a couple months now and have used SQL commands in my code many times successfully but am having trouble writing to one particular table in my database. I believe the problem lies in that i have a numeric column that i am trying to write to (I came to this conclusion because it is the only one i don't use very often) and my code keeps coming up with an exception: Additional information: SqlCommand.Prepare method requires all variable length parameters to have an explicitly set non-zero Size. with the code:
Dim cn As System.Data.SqlClient.SqlConnection
Dim command As System.Data.SqlClient.SqlCommand
Dim VL1 As System.Data.SqlClient.SqlParameter
Dim VL2 As System.Data.SqlClient.SqlParameter
Dim VL3 As System.Data.SqlClient.SqlParameter
Dim VL4 As System.Data.SqlClient.SqlParameter
Dim commandText As String
cn = New System.Data.SqlClient.SqlConnection(ConnectStr)
cn.Open()
commandText = "INSERT INTO [dbo].[HourMeterLog]" _
+ "([MachineName],[TotalHours],[HLRD],[DateTime])" _
+ "VALUES (#VL1,#VL2,#VL3,#VL4)"
command = New System.Data.SqlClient.SqlCommand(commandText, cn)
VL1 = command.Parameters.Add("#VL1", System.Data.SqlDbType.NVarChar, 25)
VL2 = command.Parameters.Add("#VL2", System.Data.SqlDbType.Float)
VL3 = command.Parameters.Add("#VL3", System.Data.SqlDbType.Int, 10)
VL4 = command.Parameters.Add("#VL4", System.Data.SqlDbType.DateTime2, 0)
command.Prepare()
VL1.Value = "MachineName"
VL2.Value = 0
VL3.Value = 735562
VL4.Value = Now()
command.ExecuteNonQuery()
cn.Close()
I know my code connects because I have at least 15 other queries that work using this format. I would prefer to not use float to write to the numeric field but i had it work the first time I launched this code (not once since then).
According to MSDN the Max character Length of the DateTime2 is 27. So I have set the Length of DateTime2 to 27 and its working fine.
VL4 = command.Parameters.Add("#VL4", System.Data.SqlDbType.DateTime2, 27)
And my working code is,
Dim cn As System.Data.SqlClient.SqlConnection
Dim command As System.Data.SqlClient.SqlCommand
Dim VL1 As System.Data.SqlClient.SqlParameter
Dim VL2 As System.Data.SqlClient.SqlParameter
Dim VL3 As System.Data.SqlClient.SqlParameter
Dim VL4 As System.Data.SqlClient.SqlParameter
Dim commandText As String
cn = New System.Data.SqlClient.SqlConnection(connectionString)
cn.Open()
commandText = "INSERT INTO [dbo].[HourMeterLog]" _
+ "([MachineName],[TotalHours],[HLRD],[DateTime])" _
+ "VALUES (#VL1,#VL2,#VL3,#VL4)"
command = New System.Data.SqlClient.SqlCommand(commandText, cn)
VL1 = command.Parameters.Add("#VL1", System.Data.SqlDbType.NVarChar, 25)
VL2 = command.Parameters.Add("#VL2", System.Data.SqlDbType.Float)
VL3 = command.Parameters.Add("#VL3", System.Data.SqlDbType.Int, 10)
VL4 = command.Parameters.Add("#VL4", System.Data.SqlDbType.DateTime2, 27)
command.Prepare()
VL1.Value = "MachineName"
VL2.Value = 0
VL3.Value = 735562
VL4.Value = Now()
command.ExecuteNonQuery()
cn.Close()
Okay so I guess I wrote the AddWithValue method wrong when i tried that. I got the command to write with:
Dim cn As System.Data.SqlClient.SqlConnection
Dim command As System.Data.SqlClient.SqlCommand
Dim VL1 As System.Data.SqlClient.SqlParameter
Dim VL2 As System.Data.SqlClient.SqlParameter
Dim VL3 As System.Data.SqlClient.SqlParameter
Dim VL4 As System.Data.SqlClient.SqlParameter
Dim commandText As String
cn = New System.Data.SqlClient.SqlConnection(ConnectStr)
cn.Open()
commandText = "INSERT INTO [dbo].[HourMeterLog]" _
+ "([MachineName],[TotalHours],[HLRD],[DateTime])" _
+ "VALUES (#VL1,#VL2,#VL3,#VL4)"
command = New System.Data.SqlClient.SqlCommand(commandText, cn)
VL1 = command.Parameters.AddWithValue("#VL1", "MachineName")
VL2 = command.Parameters.AddWithValue("#VL2", 0)
VL3 = command.Parameters.AddWithValue("#VL3", 735562)
VL4 = command.Parameters.AddWithValue("#VL4", Now())
'VL1 = command.Parameters.Add("#VL1", System.Data.SqlDbType.NVarChar, 25)
'VL2 = command.Parameters.Add("#VL2", System.Data.SqlDbType.Float)
'VL3 = command.Parameters.Add("#VL3", System.Data.SqlDbType.Int)
'VL4 = command.Parameters.Add("#VL4", System.Data.SqlDbType.DateTime2)
'command.Prepare()
'VL1.Value = "MachineName"
'VL2.Value = 0
'VL3.Value = 735562
'VL4.Value = Now()
command.ExecuteNonQuery()
cn.Close()
I would really prefer to use the format i had used before but i am happy that i at least am writing now. Thanks for the help guys. If anyone has any ideas on how to improve my original format to make it write correctly let me know. and for those who wanted a little info on the table I am writing too the field the Float was writing to is a Numeric(18,1) field. Other than that column the others can be extrapolated from the code.
For the error messge, without know anything about your table...
"SqlCommand.Prepare method requires all variable length parameters to have an explicitly set non-zero Size"
From MSDN (https://msdn.microsoft.com/en-us/library/40959t6x(v=vs.110).aspx?cs-save-lang=1&cs-lang=vb#code-snippet-1)
'Declaration
Public Function Add ( _
parameterName As String, _
sqlDbType As SqlDbType, _
size As Integer _
) As SqlParameter
Maybe your problems comes from:
VL4 = command.Parameters.Add("#VL4", System.Data.SqlDbType.DateTime2, 0)
Change it to
VL4 = command.Parameters.Add("#VL4", System.Data.SqlDbType.DateTime2)
Here is a short example what the 'Size' parameter of SqlParameter has an influence.
Sample table is
CREATE TABLE [dbo].[DateTimeTest](
[CreatedAt] [datetime2](7) NOT NULL,
[SizeValue] [int] NOT NULL
) ON [PRIMARY]
Sample code in C# is
string connectionString = "Data Source=.;Integrated Security=SSPI;Initial Catalog=Sandbox";
using (SqlConnection connection = new(connectionString))
{
connection.Open();
string commandText = #"
INSERT INTO [dbo].[DateTimeTest]
([CreatedAt]
,[SizeValue])
VALUES
(#CreatedAt
,#SizeValue);
";
DateTime timestamp = DateTime.Now;
for (int i = 1; i < 28; i++)
{
SqlCommand command = new SqlCommand(commandText, connection);
SqlParameter createdAtParam = new SqlParameter("#CreatedAt", SqlDbType.DateTime2, i); // i = 0 will raise an exception on call to command.Prepare()
command.Parameters.Add(createdAtParam);
createdAtParam.Value = timestamp;
SqlParameter sizeValueParam = new SqlParameter("#SizeValue", SqlDbType.Int);
command.Parameters.Add(sizeValueParam);
sizeValueParam.Value = i;
command.Prepare();
command.ExecuteNonQuery();
}
}
As you can see, the value of 'Size' has no influence on the precision of the inserted DateTime value.
SqlParameter createdAtParam = new SqlParameter("#CreatedAt", SqlDbType.DateTime2, 0);
or
SqlParameter createdAtParam = new SqlParameter("#CreatedAt", SqlDbType.DateTime2);
will raise an exception on call to
command.Prepare();

Resources