DBChat قسمت 3 – پیکربندی، اتصال و تخلیه پایگاه های داده

سلام! من شرجیت ونکاتراما، بنیانگذار Hexmos هستم. در حال حاضر، من در حال ساخت LiveAPI هستم، یک ابزار فوق العاده راحت که با تولید اسناد API عالی از کد شما در عرض چند دقیقه، گردش کار مهندسی را ساده می کند.
در این مجموعه آموزشی، من در سفری هستم تا برای خودم DBChat بسازم – ابزاری ساده برای استفاده از چت هوش مصنوعی برای کاوش و تکامل پایگاههای داده.
برای دریافت متن بیشتر به پست های قبلی مراجعه کنید:
- ساخت DBChat – کاوش و توسعه DB خود با چت ساده (قسمت 1)
- DBChat: گرفتن یک اسباب بازی REPL رفتن در Golang (قسمت 2)
مشکل – چگونه میتوان پایگاههایی را که میخواهیم با آنها سر و کار داشته باشیم، مشخص کنیم؟
فکر اولیه من این بود که – ما باید یک connect
فرمت پشتیبانی شده در REPL ما.
کاربر می تواند REPL را شروع کرده و پیکربندی را وارد کند.
اما در بررسی بیشتر – به نظر می رسید که داشتن نام های دوستانه برای پایگاه های داده رویکرد بهتری باشد.
بنابراین، فعلاً به روش زیر استناد کردم:
-
~/.dbchat.toml
– یک فایل پیکربندی برای dbchat در پوشه خانه. در ابتدا، ساده خواهد بود – فقط یک بخش “اتصالات” در آن، لیست کردن آدرس های مختلف پایگاه داده. - یک جدید
connect
فرمان در داخل پوسته می توان به هر دو اتصالات پایگاه داده ذخیره شده یا اتصالات پایگاه داده تحت اللفظی متصل شد. یعنیconnect
یاconnect
هر دو پشتیبانی خواهند شد
در قسمت های بعدی نحوه اجرای موارد فوق (2) را توضیح خواهم داد
پیکربندی اتصالات پایگاه داده در ~/.dbchat.toml
پیکربندی نمونه در ابتدا چیزی شبیه به این فرض می شود:
# DBChat Sample Configuration File
# Copy this file to ~/.dbchat.toml and modify as needed
[connections]
# Format: name = "connection_string"
local = "postgresql://postgres:postgres@localhost:5432/postgres"
liveapi = "postgresql://user:password@ip:port/database_name"
اجرا کردم cmd/dbchat/utils/config.go
برای خواندن و لیست کردن اتصالات از پیکربندی مانند زیر:
package utils
import (
"fmt"
"os"
"path/filepath"
"strings"
"github.com/BurntSushi/toml"
)
// Config holds the application configuration
type Config struct {
Connections map[string]string `toml:"connections"`
}
// LoadConfig reads the configuration from ~/.dbchat.toml
func LoadConfig() (*Config, error) {
home, err := os.UserHomeDir()
if err != nil {
return nil, fmt.Errorf("error getting home directory: %v", err)
}
configPath := filepath.Join(home, ".dbchat.toml")
var config Config
if _, err := toml.DecodeFile(configPath, &config); err != nil {
// Return empty config if file doesn't exist
if os.IsNotExist(err) {
return &Config{Connections: make(map[string]string)}, nil
}
return nil, fmt.Errorf("error reading config file: %v", err)
}
return &config, nil
}
// ListConnections returns a formatted string of all configured connections
func ListConnections(config *Config) string {
if len(config.Connections) == 0 {
return "No connections configured in ~/.dbchat.toml"
}
var sb strings.Builder
for name := range config.Connections {
sb.WriteString(fmt.Sprintf("- %s\n", name))
}
return sb.String()
}
این connect
اجرای فرمان
دو تغییر در پشتیبانی وجود دارد connect
دستور:
connect
connect
بنابراین در REPL Eval handling، یک مورد جدید برای handling connect اضافه کردم:
case cmd == "connect":
if len(fields) == 1 {
return `Usage: connect
You can either provide a full connection string:
Example: connect postgresql://username:password@localhost:5432/dbname
Format: postgresql://[user]:[password]@[host]:[port]/[dbname]?[params]
Or use a connection name from ~/.dbchat.toml:
Example: connect local
Available connections in config:
` + utils.ListConnections(h.config)
}
connectionStr := strings.Join(fields[1:], " ")
// Check if the argument matches a configured connection name
if configStr, exists := h.config.Connections[connectionStr]; exists {
connectionStr = configStr
}
// Close existing connection if it exists
if h.db != nil {
h.db.Close()
}
// Connect using the connection string
newDb, err := db.Connect(connectionStr)
if err != nil {
return fmt.Sprintf("Failed to connect: %v", err)
}
h.db = newDb
return "Successfully connected to PostgreSQL database! 🎉"
dump schema
دستور دریافت متن پایگاه داده
مهمترین اقدام برای ما این است که کل طرحواره پایگاه داده متصل را دریافت کنیم.
طرحواره مهم است زیرا: ما می توانیم از طرح برای اطلاع دادن به لایه LLM بعدا استفاده کنیم. و این اطلاعات به LLM کمک می کند تا پرس و جوهای مفید و دقیق SQL را ایجاد کند.
بنابراین من ایجاد کردم cmd/dbchat/db/schema.go
:
package db
import (
"database/sql"
"fmt"
"strings"
)
const schemaQuery = `
WITH tables AS (
SELECT
t.table_schema,
t.table_name,
t.table_type,
obj_description((quote_ident(t.table_schema)||'.'||quote_ident(t.table_name))::regclass, 'pg_class') as table_comment
FROM information_schema.tables t
WHERE t.table_schema NOT IN ('pg_catalog', 'information_schema')
ORDER BY t.table_schema, t.table_name
),
columns AS (
SELECT
c.table_schema,
c.table_name,
c.column_name,
c.data_type,
c.is_nullable,
c.column_default,
col_description((quote_ident(c.table_schema)||'.'||quote_ident(c.table_name))::regclass,
c.ordinal_position) as column_comment
FROM information_schema.columns c
WHERE c.table_schema NOT IN ('pg_catalog', 'information_schema')
ORDER BY c.table_schema, c.table_name, c.ordinal_position
)
SELECT
t.table_schema,
t.table_name,
t.table_type,
t.table_comment,
string_agg(
format(
' %s %s%s%s%s',
c.column_name,
c.data_type,
CASE WHEN c.is_nullable="NO" THEN ' NOT NULL' ELSE '' END,
CASE WHEN c.column_default IS NOT NULL THEN ' DEFAULT ' || c.column_default ELSE '' END,
CASE WHEN c.column_comment IS NOT NULL THEN ' -- ' || c.column_comment ELSE '' END
),
E'\n'
) as columns
FROM tables t
LEFT JOIN columns c
ON c.table_schema = t.table_schema
AND c.table_name = t.table_name
GROUP BY t.table_schema, t.table_name, t.table_type, t.table_comment
ORDER BY t.table_schema, t.table_name;
`
// DumpSchema returns a formatted string containing the database schema
func DumpSchema(db *sql.DB) (string, error) {
if db == nil {
return "", fmt.Errorf("database connection required. Use 'connect' command first")
}
rows, err := db.Query(schemaQuery)
if err != nil {
return "", fmt.Errorf("error querying schema: %v", err)
}
defer rows.Close()
var sb strings.Builder
var currentSchema string
for rows.Next() {
var (
schema, tableName, tableType, columns string
tableComment sql.NullString
)
if err := rows.Scan(&schema, &tableName, &tableType, &tableComment, &columns); err != nil {
return "", fmt.Errorf("error scanning row: %v", err)
}
// Print schema header if we're entering a new schema
if schema != currentSchema {
if currentSchema != "" {
sb.WriteString("\n")
}
sb.WriteString(fmt.Sprintf("Schema: %s\n", schema))
sb.WriteString(strings.Repeat("=", len(schema)+8) + "\n\n")
currentSchema = schema
}
// Print table information
sb.WriteString(fmt.Sprintf("Table: %s (%s)\n", tableName, tableType))
if tableComment.Valid {
sb.WriteString(fmt.Sprintf("Comment: %s\n", tableComment.String))
}
sb.WriteString("Columns:\n")
sb.WriteString(columns)
sb.WriteString("\n\n")
}
if err = rows.Err(); err != nil {
return "", fmt.Errorf("error iterating rows: %v", err)
}
return sb.String(), nil
}
نسخه آزمایشی: پیکربندی، اتصال و حذف طرحواره از پایگاه داده PostgreSQL
مراحل بعدی
از آنجایی که ما اتصال پایگاه داده و مکانیسم تخلیه طرحواره داریم – آماده هستیم تا به مرحله بعدی برویم: ادغام LLM.
در مرحله بعدی، کوئریهای کاربر را در زبان طبیعی و طرحواره پایگاهداده برای تولید کوئریهای SQL ترکیب میکنیم.
در مرحله آخر – پرس و جو باید برای تولید نتایج نیز اجرا شود – اما این موضوع فوری نیست.
لایک/اشتراک گذاری/اشتراک گذاری برای تشویق توسعه این سری از پست ها. با تشکر برای خواندن.