Fairy
Resources

How to Avoid: SQL Built by String Interpolation

August 5, 2026 · 8-minute read · Fairy

The short answer

To prevent SQL built by string interpolation in AI-generated code, replace all concatenated or template-literal queries with parameterized queries (prepared statements). Use your database driver's placeholder syntax—like $1 in PostgreSQL or ? in MySQL—and pass user input as separate parameters. This separates code from data and eliminates injection vectors entirely.

The Direct Answer: Use Parameterized Queries, Never Interpolate

SQL injection remains the most exploited vulnerability in web applications, and AI-generated code frequently introduces it through string interpolation. The fix is straightforward: replace every concatenated or template-literal SQL query with parameterized queries that pass user input as separate parameters.

Instead of this:

// VULNERABLE: String interpolation
const query = `SELECT * FROM users WHERE id = ${userId}`;
db.query(query);

Write this:

// SAFE: Parameterized query
const query = 'SELECT * FROM users WHERE id = $1';
db.query(query, [userId]);

This single change eliminates SQL injection at the protocol level. The database driver handles escaping, and user input can never be interpreted as SQL code.

Why AI Models Produce This Vulnerability

AI code generation models learn from massive datasets of public code—tutorials, Stack Overflow answers, GitHub repositories, and documentation. String interpolation appears constantly in these sources because it's visually clear and produces shorter code snippets.

Consider how tutorials typically demonstrate SQL concepts:

# Common tutorial pattern (INSECURE)
username = input("Enter username: ")
cursor.execute(f"SELECT * FROM users WHERE username = '{username}'")

This code is easy to read and understand. It demonstrates the SQL concept without "cluttering" the example with security considerations. AI models, optimizing for patterns that appear frequently and seem readable, reproduce this style.

The model has no understanding that username might contain '; DROP TABLE users; --. It doesn't reason about the boundary between trusted code and untrusted data. It simply generates what statistically follows from the prompt context.

The Training Data Problem

Security-conscious code is underrepresented in AI training data for several reasons:

  1. Tutorial bias: Educational content prioritizes clarity over security
  2. Legacy code: Older repositories predate modern security practices
  3. Prototype code: Rapid development samples skip production hardening
  4. Copy-paste culture: Insecure patterns propagate across repositories

When you ask an AI to "write a function that queries users by email," it produces what it's seen most often—and that's frequently interpolated SQL.

How to Detect String Interpolation in AI-Generated Code

Before any AI-generated database code reaches production, audit it for these patterns:

Pattern 1: Template Literals with SQL Keywords

// RED FLAG: Template literal + SQL
const result = await db.query(`SELECT * FROM orders WHERE customer_id = ${customerId}`);

Search your codebase for backticks (`) or f-strings (f"...") containing SELECT, INSERT, UPDATE, DELETE, or WHERE.

Pattern 2: String Concatenation

# RED FLAG: Concatenation
query = "SELECT * FROM products WHERE category = '" + category + "'"

Any + operator joining strings that include SQL keywords deserves scrutiny.

Pattern 3: Format Strings

# RED FLAG: Format method
query = "SELECT * FROM users WHERE email = '{}'".format(email)

The .format() method and % formatting in Python are equally dangerous when used with SQL.

Pattern 4: ORM Raw Query Escapes

ORMs like Prisma, Sequelize, and SQLAlchemy provide safe query builders by default. But they also offer raw query methods that AI frequently misuses:

// RED FLAG: Prisma raw with interpolation
const users = await prisma.$queryRaw`SELECT * FROM users WHERE name = ${name}`;

Note: Prisma's tagged template literal $queryRaw actually handles this safely, but the similar-looking $queryRawUnsafe does not. AI often confuses these or uses the unsafe variant. Always verify which method the AI selected.

// ACTUALLY DANGEROUS: Prisma unsafe raw
const users = await prisma.$queryRawUnsafe(`SELECT * FROM users WHERE name = '${name}'`);

Automated Detection Signals

Set up static analysis to flag these signals in AI-generated code:

The Correct Pattern: Parameterized Queries by Database

Every major database driver supports parameterized queries. The syntax varies slightly:

PostgreSQL (node-postgres)

// Correct: Positional parameters
const { rows } = await pool.query(
  'SELECT * FROM users WHERE email = $1 AND status = $2',
  [email, status]
);

MySQL (mysql2)

// Correct: Question mark placeholders
const [rows] = await connection.execute(
  'SELECT * FROM users WHERE email = ? AND status = ?',
  [email, status]
);

SQLite (better-sqlite3)

// Correct: Named or positional parameters
const stmt = db.prepare('SELECT * FROM users WHERE email = ? AND status = ?');
const rows = stmt.all(email, status);

Python (psycopg2)

# Correct: %s placeholders with tuple
cursor.execute(
    "SELECT * FROM users WHERE email = %s AND status = %s",
    (email, status)
)

Python (SQLAlchemy)

# Correct: Bound parameters
from sqlalchemy import text

result = connection.execute(
    text("SELECT * FROM users WHERE email = :email AND status = :status"),
    {"email": email, "status": status}
)

Why Parameterized Queries Work

Parameterized queries prevent injection at the protocol level, not through escaping. When you use placeholders, the database receives two separate pieces of information:

  1. The query structure: SELECT * FROM users WHERE email = $1
  2. The parameter values: ['user@example.com']

The database compiles the query structure first, then binds the parameter values. User input is never parsed as SQL—it's always treated as data. Even if someone submits '; DROP TABLE users; -- as their email, the database sees it as a literal string value to match against the email column.

This is fundamentally different from escaping, which tries to neutralize dangerous characters after they're already part of the query string. Escaping can fail in edge cases involving character encoding, quote styles, or database-specific syntax. Parameterization cannot fail because the attack vector doesn't exist.

Common AI Objections and How to Handle Them

When you prompt AI to rewrite interpolated SQL as parameterized queries, it sometimes pushes back or introduces new problems:

"I'll add input validation instead"

AI might propose validating or sanitizing input rather than fixing the query:

// AI "solution" that doesn't solve the problem
const sanitizedId = userId.replace(/[^0-9]/g, '');
const query = `SELECT * FROM users WHERE id = ${sanitizedId}`;

This is defense in depth at best, false security at worst. Validation can have bugs. Character sets change. New attack vectors emerge. Parameterized queries eliminate the vulnerability class entirely—validation is an addition, not a replacement.

"Dynamic column names can't be parameterized"

This is actually true—you can't parameterize identifiers like column or table names. AI sometimes uses this as justification for interpolating everything:

// AI sees this as justification for all interpolation
const sortColumn = req.query.sort;
const query = `SELECT * FROM users ORDER BY ${sortColumn}`;

The correct approach: whitelist allowed column names explicitly.

// Correct: Whitelist identifiers, parameterize values
const allowedColumns = ['name', 'email', 'created_at'];
const sortColumn = allowedColumns.includes(req.query.sort) 
  ? req.query.sort 
  : 'created_at';
const query = `SELECT * FROM users WHERE status = $1 ORDER BY ${sortColumn}`;
await db.query(query, [status]);

"Building dynamic WHERE clauses is complex"

AI-generated code often needs conditional filters. The AI might argue that parameterized queries make this harder:

// AI's "simpler" approach (VULNERABLE)
let query = 'SELECT * FROM products WHERE 1=1';
if (category) query += ` AND category = '${category}'`;
if (minPrice) query += ` AND price >= ${minPrice}`;

The correct approach builds parameters dynamically:

// Correct: Dynamic parameterization
const conditions = ['1=1'];
const params = [];
let paramIndex = 1;

if (category) {
  conditions.push(`category = $${paramIndex++}`);
  params.push(category);
}
if (minPrice) {
  conditions.push(`price >= $${paramIndex++}`);
  params.push(minPrice);
}

const query = `SELECT * FROM products WHERE ${conditions.join(' AND ')}`;
await db.query(query, params);

Yes, it's more verbose. That's acceptable—security isn't a feature you trade for brevity.

Beyond Injection: Related Database Security Issues

SQL injection through string interpolation often appears alongside other database security problems in AI-generated code. When you find one, check for others:

Missing Row-Level Security

AI-generated Supabase code frequently creates tables without enabling Row-Level Security (RLS), leaving every row readable and writable by any user. If you're using Supabase, verify that RLS is enabled on every user-data table and that explicit policies control access.

Authentication Bypass

Human-verified findings from AI code reviews reveal that AI-generated authentication logic sometimes allows bypass through query parameters or client-supplied claims. Never trust client-side auth indicators—always verify sessions and roles server-side.

Building Verification Into Your Workflow

Catching SQL injection in AI-generated code requires systematic review, not just developer vigilance. Before AI-generated database code reaches production:

  1. Static analysis: Configure linters to flag SQL keywords inside template literals
  2. Code review checklists: Require explicit sign-off on parameterization for every database query
  3. Automated testing: Include injection attempt payloads in integration tests
  4. Expert verification: Have security-aware reviewers audit database interaction code

The goal isn't to eliminate AI from your development process—it's to ensure that AI-generated code meets production standards before deployment. AI does the work; verification makes it reliable.

For teams deploying significant AI-generated code, Fairy for Code provides expert verification specifically designed to catch these vulnerabilities before they ship. The platform's reviewers understand both the patterns AI produces and the security implications that AI misses.

Conclusion

SQL injection through string interpolation is a critical vulnerability that AI generates frequently. The fix is non-negotiable: parameterized queries, always, for every user-influenced value.

Detect interpolation through static analysis and code review. Understand why AI produces it (training data bias toward readable but insecure patterns). Apply the correct parameterized syntax for your specific database driver. And build verification into your workflow so these vulnerabilities never reach production.

The vulnerability is old. The vector is new. The solution remains the same.

Frequently asked questions

Why does AI-generated code use string interpolation for SQL?

AI models optimize for readability and brevity in training data. String interpolation produces shorter, more "readable" code that appears frequently in tutorials and Stack Overflow answers. The model doesn't reason about security consequences—it pattern-matches to common examples.

What's the difference between parameterized queries and prepared statements?

They're functionally equivalent for preventing injection. Parameterized queries pass user input as separate parameters. Prepared statements pre-compile the query structure. Both separate SQL code from user data, making injection impossible at the protocol level.

Can ORMs still produce SQL injection vulnerabilities?

Yes. While ORMs default to safe queries, most provide escape hatches for raw SQL. AI often uses these raw methods when the ORM syntax is complex, reintroducing interpolation vulnerabilities. Always audit raw() and similar methods.

How do I detect string interpolation in existing codebases?

Search for patterns like template literals containing SQL keywords (SELECT, INSERT, UPDATE, DELETE) combined with variable references. Look for string concatenation operators near query execution. Static analysis tools can automate this detection.


Have AI-generated work you’d want verified? Connect with a Fairy → or run a free check with Scout.

More resources