Take the 2-minute tour ×
Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

I'm new to SQLite but have programmed in BASIC (and other languages) for pushing 50 years.

Given the sample code extract, please offer suggestions for improvement:

    Sub addAuthor(ByVal authorlnf As String, ByVal authorfnf As String)
        Dim objConn As SQLiteConnection
        Dim objCommand As SQLiteCommand
        objConn = New SQLiteConnection(CONNECTION_STR)
        objConn.Open()
        objCommand = objConn.CreateCommand()
        objCommand.CommandText = "INSERT INTO [author] ([authorlnf], [authorfnf]) VALUES ('" & authorlnf & "', '" & authorfnf & "');"
        Try
            objCommand.ExecuteNonQuery()
        Catch ex As InvalidOperationException
            MessageBox.Show("An error has occurred: " & ex.Message)
        Catch ex As SQLiteException
            MessageBox.Show("An error has occurred: " & ex.Message)
        End Try

        If Not IsNothing(objConn) Then objConn.Close()

End Sub

This is an index of book authors and this routine will be called several thousand times. I'm replacing VB dictionaries as memory is becoming a limiting factor.

share|improve this question
    
You should consider opening a database connection to be an extremely costly operation. You really don't want to do that often at all. Also look into prepared statements and bulk inserts - I don't know VB or that API, so can't give specific advice, sorry. –  Mat Jun 9 '13 at 12:39
    
I'm beginning to come to that conclusion. The impression I had gotten probably from those using sqlite in multi-user situations is to open/close it to avoid deadlock situations. Since this a single user instance I think it's time to look at how/when I'm opening "things". The real acid test is when I start to do look up on the tables. I expect some degradation over dictionaries - but if I can reduce that impact to a tolerable level then I'm open to any ideas. –  Al Jones Jun 9 '13 at 13:54

2 Answers 2

I have never used SQLite either, but something is jumping at me in your code: the addAuthor() procedure owns the connection, which means if you're calling it 10,000 times in a loop you're going to be opening and closing 10,000 connections, which is ..well to be honest, nuts.

Looking at the method's signature:

Sub addAuthor(ByVal authorlnf As String, ByVal authorfnf As String)

Naming nitpicks

  • I believe PascalCasing is the convention for naming just about everything in VB, but what really matters is consistency. Try to be consistent with the language you're using - the VB API uses PascalCasing for methods.
  • Names authorlnf and authorfnf could just as well be named authorwtf - they tell the reader it's something about an author, but to find out what exactly, one has to dig deeper than the method's signature. Avoid disemvoweling at all costs - it ruins reading even the best-written code base. well now that I think of it I'd say authorlnf would be author's last name and authorfnf would be author's first name.. still scratching my head as for what the last f is standing for...

Serious stuff

What happens when authorlnf contains the value "Robert'); DROP TABLE author;--"?

little bobby tables credits: xkcd/Randall Munroe

Your insert fails and the author table gets dropped (if your user has the permissions and there's no FK constraint involved, but that's not the point). This is called SQL Injection and, depending on where, how and by whom your code is being used, it may or may not be a concern - nevertheless, it's a flaw in your design.

You could write yourself some parameter-cleansing function and think you're protected, but that would leave you the burden of proving that your cleansing function is undeniably safe. And you must never forget to call it!

Or you could use parameters, that take the value of the user input. That's much harder to mess with!


Connection ownership

I think the AddAuthor() method shouldn't create (/own) the connection it's using. Consider this:

Sub AddAuthor(ByVal connection As SQLiteConnection, ByVal authorlnf As String, ByVal authorfnf As String)

All of a sudden, opening, closing and disposing the connection is no longer a concern here: you put that burden on the caller, where you're looping 10,000 times - so you open up the connection, create and execute 10,000 commands inside AddAuthor(), and then close the connection. That puts the lite in SQLite, no?


I'll only add that you're catching known & relevant exceptions and letting unknown ones bubble up, and that's excellent. The only thing is, as @Malachi correctly pointed out, you should be closing the connection in a Finally block, but if your method doesn't own the connection, that's moot. However I believe a Command would implement IDisposable, so it should be .Dispose()'d.

share|improve this answer
    
And now I understand that cartoon strip. :-) –  Jamal Nov 16 '13 at 3:48
    
using parameters is nice, but if they do enter something like '); DROP TABLE Students;-- that just means that the insert or update to the Database won't cause an issue, what if that column is used in another query that isn't as safe as the input from the application/website? it could still cause issues, so you still need to filter out some characters and limit the input into the database. –  Malachi Jan 27 '14 at 18:30
1  
@Malachi "'); DROP TABLE Students;--" would be treated as any other string.. if all executable SQL statements use parameters, it shouldn't be an issue. –  Mat's Mug Jan 27 '14 at 18:35

You aren't closing the connection inside of a finally statement which means that in the case your application hits one of those exceptions it will not close the connection. this isn't good.

I recommend using a using block to surround the code that you need performed by a connection. something like this

 Sub addAuthor(ByVal authorlnf As String, ByVal authorfnf As String)
    Dim cmdText = "INSERT INTO [author] ([authorlnf], [authorfnf]) VALUES ('" & authorlnf & "', '" & authorfnf & "');"

    Using objConn As New SQLiteConnection(CONNECTION_STR)
        Dim objCommand As New SQLiteCommand(cmdText,objConn)
        objConn.Open()
        Try
            objCommand.ExecuteNonQuery()
        Catch ex As InvalidOperationException
            MessageBox.Show("An error has occurred: " & ex.Message)
        Catch ex As SQLiteException
            MessageBox.Show("An error has occurred: " & ex.Message)
        End Try
    End Using
End Sub

The syntax for the SQLite VB code may be incorrect, I was basing it off of the Syntax for SQL Server commands thinking that they would be similar.


If SQLite follows the same usage as SQL Server then @Mat'sMug is right and command also implements IDisposable so you could write it like this

 Sub addAuthor(ByVal authorlnf As String, ByVal authorfnf As String)
    Dim cmdText = "INSERT INTO [author] ([authorlnf], [authorfnf]) VALUES ('" & authorlnf & "', '" & authorfnf & "');"

    Using objConn As New SQLiteConnection(CONNECTION_STR)
        Using objCommand As New SQLiteCommand(cmdText, objConn)
            objConn.Open()
            Try
                objCommand.ExecuteNonQuery()
            Catch ex As InvalidOperationException
                MessageBox.Show("An error has occurred: " & ex.Message)
            Catch ex As SQLiteException
                MessageBox.Show("An error has occurred: " & ex.Message)
            End Try
        End Using
    End Using
End Sub
share|improve this answer
    
I assumed that you already take care of possible SQL Injection in other code before sending the parameters to this sub –  Malachi Nov 13 '13 at 21:54
1  
Screening strings for offensive sql is just partly addressing potential sql injection. Using full-fledged parameters is the safest way I know. That said I don't know sqlite either :) –  Mat's Mug Nov 13 '13 at 23:26

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.