Skip to content

Parameterized Queries

This document details the parameterized query feature of the cDataBase class, an important security feature for preventing SQL injection attacks.


Table of Contents


Parameterized Query Overview

What is Parameterized Query

Parameterized query is a technique that separates SQL statements and parameter values. Parameter values are passed through placeholders (usually ?).

Why Use Parameterized Queries

  1. Prevent SQL Injection - Parameter values are escaped, cannot execute malicious SQL code
  2. Performance Optimization - Database can cache execution plans
  3. Type Safety - Automatic data type conversion
  4. Clean Code - SQL statements and parameter values are separated, easy to maintain

SQL Injection Example

vb
' Dangerous: Direct SQL concatenation (vulnerable to SQL injection)
Dim sName As String
sName = "'; DROP TABLE users; --"
db.Sql("SELECT * FROM users WHERE name = '" & sName & "'").Query
' Actual SQL executed: SELECT * FROM users WHERE name = ''; DROP TABLE users; --'
' Result: users table deleted!

' Safe: Use parameterized query
db.Sql("SELECT * FROM users WHERE name = ?") _
    .Param("name", sName, VBMAN.adVarWChar) _
    .QueryParam
' Parameter values are safely processed, cannot execute malicious code

Param Method

Syntax

vb
Function Param(ByVal ParamName As String, _
               ByVal ParamValue As Variant, _
               Optional ByVal ParamType As DataTypeEnum = adVarChar) As cDataBase

Parameters

ParameterTypeDescription
ParamNameStringParameter name (for identification, actually uses ? placeholder)
ParamValueVariantParameter value (required)
ParamTypeDataTypeEnumParameter data type (optional, default adVarChar)

Return Value

Returns cDataBase object, supports chained calls.

Example

vb
' Add single parameter
db.Sql("SELECT * FROM users WHERE id = ?") _
    .Param("id", 1, VBMAN.adInteger)

' Add multiple parameters (chained call)
db.Sql("SELECT * FROM users WHERE name = ? AND age > ?") _
    .Param("name", "John", VBMAN.adVarWChar) _
    .Param("age", 18, VBMAN.adInteger)

ExecParam Method

Syntax

The ExecParam method executes parameterized INSERT, UPDATE, DELETE operations.

vb
Function ExecParam(Optional RecordsAffected) As Boolean

Parameters

ParameterTypeDescription
RecordsAffectedVariantReturns number of affected rows (optional)

Return Value

  • True - Execution successful
  • False - Execution failed (view error via LastErr)

Example

vb
' Insert data
If db.Sql("INSERT INTO users (name, age, email) VALUES (?, ?, ?)") _
    .Param("name", "John", VBMAN.adVarWChar) _
    .Param("age", 25, VBMAN.adInteger) _
    .Param("email", "john@example.com", VBMAN.adVarWChar) _
    .ExecParam Then

    Debug.Print "Insert successful, ID: " & db.LastInsertId
End If

' Update data
If db.Sql("UPDATE users SET age = ?, email = ? WHERE id = ?") _
    .Param("age", 26, VBMAN.adInteger) _
    .Param("email", "newemail@example.com", VBMAN.adVarWChar) _
    .Param("id", 1, VBMAN.adInteger) _
    .ExecParam Then

    Debug.Print "Update successful"
End If

' Delete data
If db.Sql("DELETE FROM users WHERE id = ?") _
    .Param("id", 1, VBMAN.adInteger) _
    .ExecParam Then

    Debug.Print "Delete successful"
End If

QueryParam Method

Syntax

The QueryParam method executes parameterized SELECT queries.

vb
Function QueryParam(Optional CurType As CursorTypeEnum = adOpenKeyset, _
                    Optional LockType As LockTypeEnum = adLockOptimistic) As Boolean

Parameters

ParameterTypeDescription
CurTypeCursorTypeEnumCursor type (optional, default adOpenKeyset)
LockTypeLockTypeEnumLock type (optional, default adLockOptimistic)

Return Value

  • True - Query successful, results stored in db.Rs
  • False - Query failed (view error via LastErr)

Important: The QueryParam method returns a Boolean value, not a Recordset. Query results need to be accessed through the db.Rs property.

Example

vb
' Query single record (use db.Rs to access results)
If db.Sql("SELECT * FROM users WHERE id = ?") _
    .Param("id", 1, VBMAN.adInteger) _
    .QueryParam Then

    ' Use db.Rs to access Recordset
    If Not db.Rs.EOF Then
        Debug.Print db.Rs("name")
    End If
    db.Rs.Close
End If

' Query multiple records (use db.Rs to access results)
If db.Sql("SELECT * FROM users WHERE age > ? AND status = ?") _
    .Param("age", 18, VBMAN.adInteger) _
    .Param("status", "active", VBMAN.adVarWChar) _
    .QueryParam Then

    ' Use db.Rs to iterate through results
    Do Until db.Rs.EOF
        Debug.Print db.Rs("name")
        db.Rs.MoveNext
    Loop
    db.Rs.Close
End If

' If using Fetch method, can access db.Rows and db.Row
If db.Sql("SELECT * FROM users WHERE id = ?") _
    .Param("id", 1, VBMAN.adInteger) _
    .QueryParam Then

    ' After using Fetch, can access db.Rows and db.Row
    If db.Fetch Then
        If db.Rows.Count > 0 Then
            Debug.Print db.Row("name")
        End If
    End If
End If

Data Types

Common Data Types

TypeValueDescriptionExample
adVarChar200Variable-length string"John"
adVarWChar202Unicode string"John"
adInteger332-bit integer25
adBigInt2064-bit integer1234567890
adDouble5Double precision float3.14
adDate7Date timeNow
adBoolean11BooleanTrue
adDecimal14Exact numeric99.99

Data Type Selection

vb
' String
db.Param("name", "John", VBMAN.adVarWChar)  ' Unicode string (recommended)
db.Param("name", "John", VBMAN.adVarChar)   ' ANSI string

' Integer
db.Param("age", 25, VBMAN.adInteger)        ' 32-bit integer
db.Param("id", 1234567890, VBMAN.adBigInt)  ' 64-bit integer

' Float
db.Param("price", 99.99, VBMAN.adDouble)    ' Double precision
db.Param("amount", 99.99, VBMAN.adDecimal) ' Exact numeric

' Date time
db.Param("created", Now, VBMAN.adDate)       ' Date time

' Boolean
db.Param("active", True, VBMAN.adBoolean)   ' Boolean

Security Advantages

SQL Injection Protection

vb
' Dangerous: Direct concatenation
Dim sInput As String
sInput = "'; DROP TABLE users; --"
db.Sql("SELECT * FROM users WHERE name = '" & sInput & "'").Query
' Result: users table deleted

' Safe: Parameterized query
db.Sql("SELECT * FROM users WHERE name = ?") _
    .Param("name", sInput, VBMAN.adVarWChar) _
    .QueryParam
' Parameter values are safely processed, cannot execute malicious code

Special Character Handling

vb
' Dangerous: Special characters may cause errors
Dim sName As String
sName = "O'Brien"
db.Sql("SELECT * FROM users WHERE name = '" & sName & "'").Query
' SQL: SELECT * FROM users WHERE name = 'O'Brien'
' Error: Unescaped single quote

' Safe: Parameterized query handles automatically
db.Sql("SELECT * FROM users WHERE name = ?") _
    .Param("name", sName, VBMAN.adVarWChar) _
    .QueryParam
' Special characters are automatically escaped

Usage Examples

Example 1: User Login

vb
Function UserLogin(sUsername As String, sPassword As String) As Boolean
    ' Use parameterized query to prevent SQL injection
    If db.Sql("SELECT * FROM users WHERE username = ? AND password = ?") _
        .Param("username", sUsername, VBMAN.adVarWChar) _
        .Param("password", sPassword, VBMAN.adVarWChar) _
        .QueryParam Then

        If db.Rows.Count > 0 Then
            UserLogin = True
        Else
            UserLogin = False
        End If
    Else
        UserLogin = False
    End If
End Function

Example 2: Search Feature

vb
Function SearchUsers(sKeyword As String) As Collection
    Set SearchUsers = New Collection

    ' Use LIKE with parameterized query
    Dim sSql As String
    sSql = "SELECT * FROM users WHERE name LIKE ? OR email LIKE ?"

    If db.Sql(sSql) _
        .Param("name", "%" & sKeyword & "%", VBMAN.adVarWChar) _
        .Param("email", "%" & sKeyword & "%", VBMAN.adVarWChar) _
        .QueryParam Then

        Set SearchUsers = db.Rows
    End If
End Function

Example 3: Batch Insert

vb
Sub BatchInsertUsers(colUsers As Collection)
    db.TransBegin

    Dim i As Long
    For i = 1 To colUsers.Count
        Dim dictUser As Scripting.Dictionary
        Set dictUser = colUsers(i)

        If Not db.Sql("INSERT INTO users (name, age, email) VALUES (?, ?, ?)") _
            .Param("name", dictUser("name"), VBMAN.adVarWChar) _
            .Param("age", dictUser("age"), VBMAN.adInteger) _
            .Param("email", dictUser("email"), VBMAN.adVarWChar) _
            .ExecParam Then

            db.TransRollback
            Exit Sub
        End If
    Next

    db.TransCommit
End Sub

Example 4: Dynamic Query

vb
Function GetUsers(Optional sName As String = "", _
                  Optional lMinAge As Long = 0, _
                  Optional sStatus As String = "") As Collection
    Set GetUsers = New Collection

    Dim sSql As String
    sSql = "SELECT * FROM users WHERE 1=1"

    ' Dynamically build SQL and parameters
    If sName <> "" Then
        sSql = sSql & " AND name LIKE ?"
    End If
    If lMinAge > 0 Then
        sSql = sSql & " AND age >= ?"
    End If
    If sStatus <> "" Then
        sSql = sSql & " AND status = ?"
    End If

    ' Set SQL
    db.Sql (sSql)

    ' Add parameters
    If sName <> "" Then
        db.Param "name", "%" & sName & "%", VBMAN.adVarWChar
    End If
    If lMinAge > 0 Then
        db.Param "age", lMinAge, VBMAN.adInteger
    End If
    If sStatus <> "" Then
        db.Param "status", sStatus, VBMAN.adVarWChar
    End If

    ' Execute query
    If db.QueryParam Then
        Set GetUsers = db.Rows
    End If
End Function

Best Practices

1. Always Use Parameterized Queries for User Input

vb
' Recommended: Use parameterized queries
db.Sql("SELECT * FROM users WHERE name = ?") _
    .Param("name", txtName.Text, VBMAN.adVarWChar) _
    .QueryParam

' Not recommended: Direct concatenation of user input
db.Sql("SELECT * FROM users WHERE name = '" & txtName.Text & "'").Query

2. Choose Correct Data Types

vb
' Recommended: Explicitly specify data types
db.Param("age", 25, VBMAN.adInteger)
db.Param("name", "John", VBMAN.adVarWChar)
db.Param("price", 99.99, VBMAN.adDecimal)

' Not recommended: Use default types (may cause type mismatch)
db.Param("age", 25)  ' Default adVarChar, may cause errors

3. Use Unicode String Type

vb
' Recommended: Use adVarWChar to support Chinese
db.Param("name", "John", VBMAN.adVarWChar)

' Not recommended: Use adVarChar (may cause Chinese garbled text)
db.Param("name", "John", VBMAN.adVarChar)

4. Parameter Order Must Match Placeholder Order

vb
' Correct: Parameter order matches ? order
db.Sql("SELECT * FROM users WHERE name = ? AND age > ?") _
    .Param("name", "John", VBMAN.adVarWChar) _
    .Param("age", 18, VBMAN.adInteger)

' Wrong: Parameter order is wrong
db.Sql("SELECT * FROM users WHERE name = ? AND age > ?") _
    .Param("age", 18, VBMAN.adInteger) _
    .Param("name", "John", VBMAN.adVarWChar)

5. Clear Parameters

vb
' Note: ExecParam and QueryParam automatically clear parameters
' If only calling Param without executing, need to manually clear (by executing or recreating object)

Common Questions

Q1: Is Parameterized Query Slower Than Direct Concatenation?

Answer: No, parameterized queries are usually faster because:

  • Database can cache execution plans
  • Reduces SQL parsing time
  • Avoids string concatenation overhead

Q2: How to Use Parameters in LIKE Queries?

vb
' Correct: Include wildcards in parameter value
db.Sql("SELECT * FROM users WHERE name LIKE ?") _
    .Param("name", "%" & sKeyword & "%", VBMAN.adVarWChar) _
    .QueryParam

' Wrong: Use wildcards in SQL
db.Sql("SELECT * FROM users WHERE name LIKE '%?%'") _
    .Param("name", sKeyword, VBMAN.adVarWChar) _
    .QueryParam

Q3: How to Handle NULL Values?

vb
' Use Null value
db.Sql("SELECT * FROM users WHERE email = ?") _
    .Param("email", Null, VBMAN.adVarWChar) _
    .QueryParam

' Or use IS NULL
db.Sql("SELECT * FROM users WHERE email IS NULL").Query

Q4: Does Parameterized Query Support IN Clause?

vb
' Note: ADO parameterized query has limited support for IN clause
' Recommendation: Use multiple OR conditions or dynamically build SQL

' Method 1: Use multiple OR
db.Sql("SELECT * FROM users WHERE id = ? OR id = ? OR id = ?") _
    .Param("id1", 1, VBMAN.adInteger) _
    .Param("id2", 2, VBMAN.adInteger) _
    .Param("id3", 3, VBMAN.adInteger) _
    .QueryParam

' Method 2: Dynamic building (requires input validation)
Dim sIds As String
sIds = "1,2,3"  ' Needs format validation
db.Sql("SELECT * FROM users WHERE id IN (" & sIds & ")").Query

Last Updated: 2026-01-21

VB6 and LOGO copyright of Microsoft Corporation