Skip to content

Commit

Permalink
Add bindParameterIndex for working with named bound parameters
Browse files Browse the repository at this point in the history
Add Database.SQLite3.Direct support for querying the index of a named
parameter in a statement.

Named parameters (e.g. 'SELECT :foo') allow for some convenient forms
of developing SQL queries that are harder to write and maintain with
positional arguments.
  • Loading branch information
nurpax committed Apr 22, 2014
1 parent 06b9236 commit f9175ad
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 0 deletions.
5 changes: 5 additions & 0 deletions Database/SQLite3/Bindings.hs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ module Database.SQLite3.Bindings (
-- * Parameter and column information
c_sqlite3_bind_parameter_count,
c_sqlite3_bind_parameter_name,
c_sqlite3_bind_parameter_index,
c_sqlite3_column_count,
c_sqlite3_column_name,

Expand Down Expand Up @@ -198,6 +199,10 @@ foreign import ccall unsafe "sqlite3_bind_parameter_count"
foreign import ccall unsafe "sqlite3_bind_parameter_name"
c_sqlite3_bind_parameter_name :: Ptr CStatement -> CParamIndex -> IO CString

-- | <http://www.sqlite.org/c3ref/bind_parameter_index.html>
foreign import ccall unsafe "sqlite3_bind_parameter_index"
c_sqlite3_bind_parameter_index :: Ptr CStatement -> CString -> IO CParamIndex

-- | <http://www.sqlite.org/c3ref/column_count.html>
foreign import ccall unsafe "sqlite3_column_count"
c_sqlite3_column_count :: Ptr CStatement -> IO CColumnCount
Expand Down
8 changes: 8 additions & 0 deletions Database/SQLite3/Direct.hs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ module Database.SQLite3.Direct (
-- * Parameter and column information
bindParameterCount,
bindParameterName,
bindParameterIndex,
columnCount,
columnName,

Expand Down Expand Up @@ -406,6 +407,13 @@ bindParameterName (Statement stmt) idx =
c_sqlite3_bind_parameter_name stmt (toFFI idx) >>=
packUtf8 Nothing Just

-- | <http://www.sqlite.org/c3ref/bind_parameter_index.html>
bindParameterIndex :: Statement -> Utf8 -> IO (Maybe ParamIndex)
bindParameterIndex (Statement stmt) (Utf8 name) =
BS.useAsCString name $ \name' -> do
idx <- fromFFI <$> c_sqlite3_bind_parameter_index stmt name'
return $ if idx == 0 then Nothing else Just idx

-- | <http://www.sqlite.org/c3ref/column_count.html>
columnCount :: Statement -> IO ColumnCount
columnCount (Statement stmt) =
Expand Down
27 changes: 27 additions & 0 deletions test/Main.hs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ regressionTests =
, TestLabel "Params" . testBindParamCounts
, TestLabel "Params" . testBindParamName
, TestLabel "Params" . testBindErrorValidation
, TestLabel "Params" . testNamedBindParams
, TestLabel "Columns" . testColumns
, TestLabel "TypedColumns" . testTypedColumns
, TestLabel "ColumnName" . testColumnName
Expand Down Expand Up @@ -302,6 +303,32 @@ testBindErrorValidation TestEnv{..} = TestCase $ do
-- Invalid use, one param in q string, 2 given
testException2 stmt = bind stmt [SQLInteger 1, SQLInteger 2]

testNamedBindParams :: TestEnv -> Test
testNamedBindParams TestEnv{..} = TestCase $ do
withConn $ \conn -> do
withStmt conn "SELECT :foo / :bar" $ \stmt -> do
-- Test that we get something back for known names
Just fooIdx <- Direct.bindParameterIndex stmt ":foo"
Just barIdx <- Direct.bindParameterIndex stmt ":bar"
-- Test that we get Nothing back for unknown names
Nothing <- Direct.bindParameterIndex stmt "intentionally_undefined"
Right () <- Direct.bindInt64 stmt fooIdx 4
Right () <- Direct.bindInt64 stmt barIdx 2
Row <- step stmt
1 <- columnCount stmt
[SQLInteger 2] <- columns stmt
Done <- step stmt
return ()
withStmt conn "SELECT @n1+@n2" $ \stmt -> do
-- Test that we get something back for known names
Just _n1 <- Direct.bindParameterIndex stmt "@n1"
Just _n2 <- Direct.bindParameterIndex stmt "@n2"
-- Here's where things get confusing.. You can't mix different
-- types of :/$/@ parameter conventions.
Nothing <- Direct.bindParameterIndex stmt ":n1"
Nothing <- Direct.bindParameterIndex stmt ":n2"
return ()

testColumns :: TestEnv -> Test
testColumns TestEnv{..} = TestCase $ do
withConn $ \conn -> do
Expand Down

0 comments on commit f9175ad

Please sign in to comment.