0Pricing
AI Agents · Ders

SQL Sorguları Oluşturma ve Doğrulama

Güvenli SQL için istem kalıpları: yalnızca SELECT modu ve parametreli sorgular.

SQL Sorguları Oluşturma ve Doğrulama, CoddyKit'te ücretsiz bir AI Agents dersidir. Bu, 4 dersinin 3. dersidir. Aşağıdan dersin tamamını ücretsiz okuyabilir, sonra tarayıcıda yerleşik kod editörü ve 7/24 yapay zeka koçu ile uygulamalı olarak pratik yapabilirsin. Bu, AI Agents öğrenme yolunun bir parçasıdır ve ilerlemeniz web ve CoddyKit uygulaması arasında senkronize olur. AI Agents kursu toplamda 4 dersten oluşur.

SQL Oluşturma Hedefi

Bir SQL sorgusu oluşturmak işin yalnızca yarısıdır. Gerçek bir veritabanında yürütmeden önce sorgunun güvenli ve söz dizimi açısından doğru olduğunu, ayrıca kullanıcının tam olarak niyet ettiği işlemi yaptığını doğrulamanız gerekir.

Bu derste yalnızca SELECT kullanımını zorlama, ayrıştırma, güvenli yürütme ve açıklama planı doğrulaması ele alınmaktadır.

Yalnızca SELECT Modunu Zorlama

Bir NL-to-SQL aracısının yapabileceği en tehlikeli şey, yıkıcı bir ifadeyi yürütmektir. LLM ne döndürürse döndürsün, her zaman yalnızca SELECT modunu zorlayın.

Basit bir dize denetimi yeterli değildir — uygun bir SQL ayrıştırıcısı kullanın.

import sqlparse

def is_select_only(sql):
    parsed = sqlparse.parse(sql)
    if not parsed:
        return False
    for statement in parsed:
        stmt_type = statement.get_type()
        if stmt_type != 'SELECT':
            print(f'Blocked statement type: {stmt_type}')
            return False
    return True

# Test
print(is_select_only('SELECT * FROM users'))  # True
print(is_select_only('DROP TABLE users'))      # False — Blocked

Derinlemesine Savunma için Anahtar Kelime Engelleme Listesi

sqlparse kullanıyor olsanız bile ikincil bir savunma olarak anahtar kelime engelleme listesi ekleyin. Bazı SQL enjeksiyonları ayrıştırıcıları yanıltabilir. Yürütmeden önce tehlikeli anahtar kelimeleri denetlemek ek bir güvenlik katmanı sağlar.

DANGEROUS_KEYWORDS = [
    'INSERT', 'UPDATE', 'DELETE', 'DROP', 'CREATE',
    'ALTER', 'TRUNCATE', 'GRANT', 'REVOKE', 'EXEC',
    'EXECUTE', 'CALL', 'MERGE'
]

def passes_blocklist(sql):
    sql_upper = sql.upper()
    for keyword in DANGEROUS_KEYWORDS:
        # Check as whole word to avoid false positives like 'CREATED_AT'
        import re
        if re.search(r'\b' + keyword + r'\b', sql_upper):
            raise ValueError(f'Blocked keyword detected: {keyword}')
    return True

def validate_sql(sql):
    if not is_select_only(sql):
        raise ValueError('Only SELECT statements are allowed')
    passes_blocklist(sql)
    return True

sqlparse ile SQL Ayrıştırma

sqlparse, SQL dizelerini yürütmeden belirteçlere ayırır ve ayrıştırır. Sorgu yapısını inceleyebilir, tablo adlarını çıkarabilir ve söz dizimi sorunlarını denetleyebilirsiniz.

pip install sqlparse komutuyla yükleyin.

import sqlparse
from sqlparse.sql import IdentifierList, Identifier
from sqlparse.tokens import Keyword, DML

def extract_table_names(sql):
    parsed = sqlparse.parse(sql)[0]
    tables = []
    from_seen = False
    for token in parsed.tokens:
        if token.ttype is DML and token.value.upper() == 'SELECT':
            continue
        if token.ttype is Keyword and token.value.upper() in ('FROM', 'JOIN'):
            from_seen = True
            continue
        if from_seen:
            if isinstance(token, Identifier):
                tables.append(token.get_name())
            elif isinstance(token, IdentifierList):
                for item in token.get_identifiers():
                    tables.append(item.get_name())
            from_seen = False
    return tables

print(extract_table_names('SELECT u.name FROM users u JOIN orders o ON u.id = o.user_id'))
# ['users', 'orders']

Tabloların Şemada Var Olduğunu Doğrulama

Oluşturulan SQL'den tablo adlarını çıkardıktan sonra bunları bildiğiniz şemayla karşılaştırın. LLM bir tablo adını uydurduysa, anlaşılması güç bir veritabanı hatası almak yerine sorguyu yürütmeden önce reddedin.

def validate_tables_exist(sql, known_tables):
    used_tables = extract_table_names(sql)
    invalid = [t for t in used_tables if t and t not in known_tables]
    if invalid:
        raise ValueError(
            f'Query references non-existent tables: {invalid}. '
            f'Available tables: {list(known_tables)[:10]}...'
        )
    return True

# Usage
known = set(build_schema_dict(conn).keys())
try:
    validate_tables_exist(generated_sql, known)
except ValueError as e:
    # Send error back to LLM for correction
    corrected_sql = llm_fix_sql(generated_sql, str(e))
    print('Corrected SQL:', corrected_sql)

Parametreli Yürütme

Kullanıcı tarafından sağlanan değerleri SQL'e eklemek için hiçbir zaman dize biçimlendirmesi kullanmayın. Sorguyu LLM oluştursa da kullanıcı tarafından sağlanan tüm filtre değerleri, SQL enjeksiyonunu önlemek için parametre olarak aktarılmalıdır.

import sqlite3

conn = sqlite3.connect(':memory:')
conn.execute('CREATE TABLE orders (status TEXT, user_id INTEGER)')
conn.execute("INSERT INTO orders VALUES ('pending', 42)")

def safe_execute(conn, sql_template, params=()):
    """Execute with parameterized values."""
    cur = conn.cursor()
    cur.execute(sql_template, params)  # driver handles escaping
    columns = [d[0] for d in cur.description]
    rows = cur.fetchmany(200)
    return {'columns': columns, 'rows': rows}

sql = 'SELECT * FROM orders WHERE status = ? AND user_id = ?'
result = safe_execute(conn, sql, params=('pending', 42))
print(result)

Yürütme Öncesi EXPLAIN Planı

Büyük tablolara yönelik pahalı sorgular için asıl sorgudan önce EXPLAIN çalıştırın. Planlayıcı, milyonlarca satır içeren bir tabloda tam tablo taraması gösteriyorsa kullanıcıyı uyarın veya sorguyu reddedin.

def check_explain_plan(conn, sql):
    explain_sql = f'EXPLAIN {sql}'
    with conn.cursor() as cur:
        cur.execute(explain_sql)
        plan = '\n'.join(row[0] for row in cur.fetchall())

    # Check for sequential scans on large tables
    if 'Seq Scan' in plan:
        print('WARNING: Query involves a sequential scan')
        print(plan)
        return {'safe': False, 'plan': plan, 'warning': 'Sequential scan detected'}

    return {'safe': True, 'plan': plan}

# Use before executing
plan_result = check_explain_plan(conn, generated_sql)
if not plan_result['safe']:
    print(f'Optimization hint: {plan_result["warning"]}')

Satır Sınırını Zorlama

Bir LLM, LIMIT olmadan SELECT * FROM logs oluşturabilir ve bu da potansiyel olarak milyonlarca satır döndürebilir. Sorguya LIMIT ekleyerek veya sınırlı bir sonuç kümesi getirerek her zaman bir üst satır sınırı uygulayın.

import re

MAX_ROWS = 500

def enforce_row_limit(sql, max_rows=MAX_ROWS):
    sql_upper = sql.upper().rstrip().rstrip(';')

    # Check if LIMIT already present
    if re.search(r'\bLIMIT\b', sql_upper):
        # Extract current limit and enforce maximum
        match = re.search(r'LIMIT\s+(\d+)', sql_upper)
        if match:
            current = int(match.group(1))
            if current > max_rows:
                sql = re.sub(r'LIMIT\s+\d+', f'LIMIT {max_rows}', sql, flags=re.IGNORECASE)
    else:
        sql = sql.rstrip(';') + f' LIMIT {max_rows}'

    return sql

print(enforce_row_limit('SELECT * FROM users'))
# SELECT * FROM users LIMIT 500

LLM Çıktısından Temiz SQL Çıkarma

LLM'ler SQL'i genellikle Markdown kod blokları (```sql ... ```) içine sarılmış veya açıklayıcı metinle birlikte döndürür. Ayrıştırmadan ya da yürütmeden önce ham SQL'i çıkarmanız gerekir.

import re

CODE_FENCE = chr(96) * 3  # three backticks, built at runtime to avoid template issues

def extract_sql(llm_response):
    # Remove markdown code blocks ('''sql ... ''' or ''' ... ''')
    pattern = CODE_FENCE + r'(?:sql)?\s*([\s\S]+?)' + CODE_FENCE
    match = re.search(pattern, llm_response, re.IGNORECASE)
    if match:
        return match.group(1).strip()

    # If no code block, look for SELECT statement
    match = re.search(r'(SELECT\s+[\s\S]+?;)', llm_response, re.IGNORECASE)
    if match:
        return match.group(1).strip()

    # Fallback: strip common preamble phrases
    cleaned = re.sub(r'^(Here is|The SQL query is|Query:)[^\n]*\n', '',
                     llm_response, flags=re.IGNORECASE).strip()
    return cleaned

if __name__ == '__main__':
    demo_response = 'Here is the SQL query:\n' + CODE_FENCE + 'sql\nSELECT * FROM users;\n' + CODE_FENCE
    print(extract_sql(demo_response))

Eksiksiz Doğrulama Akışı

Tüm doğrulama adımlarını, ham LLM çıktısını alan ve güvenli, yürütülebilir bir SQL dizesi döndüren ya da kurtarma amacıyla açıklayıcı bir iletiyle hata oluşturan tek bir işlevde birleştirin.

def validate_and_prepare_sql(llm_output, known_tables, max_rows=500):
    # Step 1: extract raw SQL
    sql = extract_sql(llm_output)
    if not sql:
        raise ValueError('No SQL found in LLM response')

    # Step 2: type check
    if not is_select_only(sql):
        raise ValueError('Only SELECT queries allowed')

    # Step 3: keyword blocklist
    passes_blocklist(sql)

    # Step 4: table existence check
    validate_tables_exist(sql, known_tables)

    # Step 5: row limit
    sql = enforce_row_limit(sql, max_rows)

    return sql

# Full flow
try:
    safe_sql = validate_and_prepare_sql(llm_output, known_tables)
    result = safe_execute(conn, safe_sql)
except ValueError as e:
    corrected = llm_fix_sql(llm_output, str(e))
    safe_sql = validate_and_prepare_sql(corrected, known_tables)
    result = safe_execute(conn, safe_sql)

Salt Okunur Veritabanı Kullanıcısı

Kod düzeyindeki doğrulama önemlidir ancak yeterli değildir. Son bir savunma katmanı olarak veritabanına yalnızca SELECT ayrıcalıklarına sahip salt okunur bir kullanıcı hesabı kullanarak bağlanın. Kötü amaçlı bir sorgu tüm denetimleri atlasa bile veritabanı sorguyu reddeder.

# Create read-only user in PostgreSQL:
# CREATE USER nl_to_sql_reader WITH PASSWORD 'secure_password';
# GRANT CONNECT ON DATABASE yourdb TO nl_to_sql_reader;
# GRANT USAGE ON SCHEMA public TO nl_to_sql_reader;
# GRANT SELECT ON ALL TABLES IN SCHEMA public TO nl_to_sql_reader;

import os
import psycopg2

def get_readonly_connection():
    return psycopg2.connect(
        host=os.getenv('DB_HOST'),
        database=os.getenv('DB_NAME'),
        user='nl_to_sql_reader',       # read-only account
        password=os.getenv('DB_READER_PASS')
    )

Bilgi Kontrolü

Bir NL-to-SQL aracısında SQL doğrulaması için derinlemesine savunmaya dayalı doğru yaklaşım nedir?

Özet: SQL Oluşturma ve Doğrulama

Güvenli SQL oluşturma, eksiksiz bir doğrulama akışı gerektirir: LLM çıktısından temiz SQL'i çıkarın, sqlparse kullanarak yalnızca SELECT kullanımını zorlayın, bir anahtar kelime engelleme listesi uygulayın, tablo adlarını gerçek şemaya göre doğrulayın, satır sınırlarını zorlayın ve son güvenlik önlemi olarak salt okunur bir veritabanı kullanıcısı kullanın.

Parametreli sorgular, kullanıcı tarafından sağlanan değerler söz konusu olduğunda enjeksiyona karşı koruma sağlar. EXPLAIN planı denetimleri, beklenmedik derecede pahalı sorguların üretim verileri üzerinde çalıştırılmasını önler.

Sıkça Sorulan Sorular

“SQL Sorguları Oluşturma ve Doğrulama” dersi ücretsiz mi?

Evet — “SQL Sorguları Oluşturma ve Doğrulama” dersin tüm metni burada web'de ücretsiz olarak okunabilir. Etkileşimli olarak pratik yapmak (yerleşik kod editörü ve 7/24 yapay zeka koçu) ve AI Agents kursunun geri kalanını açmak için CoddyKit PRO'ya yükselt. AI Agents kursu toplamda 4 dersten oluşur.

“SQL Sorguları Oluşturma ve Doğrulama” dersinde ne öğreneceğim?

Güvenli SQL için istem kalıpları: yalnızca SELECT modu ve parametreli sorgular. AI Agents ile uygulamalı kodu tarayıcıda doğrudan çalıştırarak pratik yaparsın ve 7/24 yapay zeka koçu dersi çalışırken sorularını yanıtlar.

AI Agents öğrenmeye başlamak için deneyim gerekli mi?

Önceden deneyim gerekmez. CoddyKit'te AI Agents, başlangıçtan ileri seviyeye kadar yapılandırıldığı için buradan başlayabilir veya başından başlayıp kendi hızında ilerleme yapabilirsin. Bu, 4 dersinin 3. dersidir.

“SQL Sorguları Oluşturma ve Doğrulama” dersi ne kadar sürer?

Çoğu CoddyKit dersi yaklaşık 5–10 dakika sürer. Her biri kısa ve etkileşimli olduğu için sabit ilerleme yaparsın ve web ile uygulama arasında tam olarak bıraktığın yerden devam edebilirsin.

Bu AI Agents dersinde kod yazıp çalıştırabilir miyim?

Evet. Her AI Agents dersi yerleşik bir kod editörü içerir, bu sayede tarayıcıda gerçek kod yazıp çalıştırabilir ve anlık yapay zeka geri bildirimi alırsın — yerel kurulum gerekli değildir.

Bu kursun tüm dersleri

  1. NL'den SQL'e Aracılar Nasıl Çalışır
  2. Şema Anlama ve Enjeksiyonu
  3. SQL Sorguları Oluşturma ve Doğrulama
  4. Belirsiz Veritabanı Sorularını Ele Alma
← AI Agents Sayfasına Dön