使用Golang脚本仅获取Postgres数据库模式的方法

使用Golang脚本仅获取Postgres数据库模式的方法 如何仅使用Golang脚本获取Postgres数据库的架构(仅DDL脚本)

5 回复

有什么原因导致你不能使用 pg_dump 吗?

更多关于使用Golang脚本仅获取Postgres数据库模式的方法的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


你好,Satya!

能否请你尝试用不同的词语来描述你的问题?就目前而言,很难理解你在问什么。

是否有办法像使用 pg_dump 命令那样,通过 Go 语言脚本仅获取 PostgreSQL 数据库的表、视图和函数的 DDL 架构,并将其复制到纯文本文件中。

我不太清楚 pf_dump 具体是如何工作的,但如果它仅依赖常规的 SQL 来获取要导出的信息,那么你可以通过通常的连接数据库并进行通信的方式重新实现它。

你只需要找出正确的查询语句,但这与 Go 无关。

func main() {
    fmt.Println("hello world")
}

在Golang中获取PostgreSQL数据库的完整DDL脚本,可以通过查询系统目录表来实现。以下是几种常用方法:

方法1:使用pg_dump命令(推荐)

package main

import (
    "fmt"
    "os/exec"
    "strings"
)

func getSchemaDDL(host, port, user, password, dbname string) (string, error) {
    // 设置连接字符串
    connStr := fmt.Sprintf("host=%s port=%s user=%s password=%s dbname=%s",
        host, port, user, password, dbname)
    
    // 执行pg_dump命令,仅导出模式(不包含数据)
    cmd := exec.Command("pg_dump", 
        "--schema-only",      // 仅导出模式
        "--no-owner",         // 不包含所有者信息
        "--no-privileges",    // 不包含权限信息
        "-d", connStr)
    
    output, err := cmd.Output()
    if err != nil {
        return "", fmt.Errorf("pg_dump执行失败: %v", err)
    }
    
    return string(output), nil
}

func main() {
    ddl, err := getSchemaDDL("localhost", "5432", "postgres", "password", "mydb")
    if err != nil {
        panic(err)
    }
    fmt.Println(ddl)
}

方法2:直接查询系统表(纯Go实现)

package main

import (
    "database/sql"
    "fmt"
    "strings"
    _ "github.com/lib/pq"
)

type TableDDL struct {
    TableName string
    DDL       string
}

func getDatabaseSchema(db *sql.DB) ([]TableDDL, error) {
    var tables []TableDDL
    
    // 获取所有用户表的DDL
    query := `
        SELECT 
            schemaname || '.' || tablename as table_name,
            'CREATE TABLE ' || schemaname || '.' || tablename || ' (' || 
            string_agg(column_def, ', ') || 
            COALESCE(', ' || condef, '') || ');' as ddl
        FROM (
            SELECT 
                n.nspname as schemaname,
                c.relname as tablename,
                a.attname || ' ' || 
                pg_catalog.format_type(a.atttypid, a.atttypmod) || 
                CASE WHEN a.attnotnull THEN ' NOT NULL' ELSE '' END as column_def,
                con.contype,
                'CONSTRAINT ' || con.conname || ' ' || 
                pg_catalog.pg_get_constraintdef(con.oid) as condef
            FROM pg_catalog.pg_class c
            JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
            JOIN pg_catalog.pg_attribute a ON a.attrelid = c.oid
            LEFT JOIN pg_catalog.pg_constraint con ON con.conrelid = c.oid AND a.attnum = ANY(con.conkey)
            WHERE c.relkind = 'r' 
                AND n.nspname NOT IN ('pg_catalog', 'information_schema')
                AND a.attnum > 0
                AND NOT a.attisdropped
            ORDER BY c.relname, a.attnum
        ) t
        GROUP BY schemaname, tablename, condef
    `
    
    rows, err := db.Query(query)
    if err != nil {
        return nil, err
    }
    defer rows.Close()
    
    for rows.Next() {
        var table TableDDL
        if err := rows.Scan(&table.TableName, &table.DDL); err != nil {
            return nil, err
        }
        tables = append(tables, table)
    }
    
    return tables, nil
}

func getFunctionsDDL(db *sql.DB) ([]string, error) {
    var functions []string
    
    query := `
        SELECT 
            'CREATE OR REPLACE FUNCTION ' || 
            n.nspname || '.' || p.proname || '(' || 
            pg_catalog.pg_get_function_arguments(p.oid) || ')\n' ||
            'RETURNS ' || pg_catalog.pg_get_function_result_string(p.oid) || '\n' ||
            'LANGUAGE ' || l.lanname || '\n' ||
            'AS $$\n' || 
            pg_catalog.pg_get_functiondef(p.oid) || 
            '\n$$;' as function_ddl
        FROM pg_proc p
        LEFT JOIN pg_namespace n ON n.oid = p.pronamespace
        LEFT JOIN pg_language l ON l.oid = p.prolang
        WHERE n.nspname NOT IN ('pg_catalog', 'information_schema')
    `
    
    rows, err := db.Query(query)
    if err != nil {
        return nil, err
    }
    defer rows.Close()
    
    for rows.Next() {
        var ddl string
        if err := rows.Scan(&ddl); err != nil {
            return nil, err
        }
        functions = append(functions, ddl)
    }
    
    return functions, nil
}

func main() {
    connStr := "host=localhost port=5432 user=postgres password=password dbname=mydb sslmode=disable"
    db, err := sql.Open("postgres", connStr)
    if err != nil {
        panic(err)
    }
    defer db.Close()
    
    // 获取表结构
    tables, err := getDatabaseSchema(db)
    if err != nil {
        panic(err)
    }
    
    // 获取函数定义
    functions, err := getFunctionsDDL(db)
    if err != nil {
        panic(err)
    }
    
    // 输出完整的DDL
    var builder strings.Builder
    builder.WriteString("-- 表结构定义\n")
    for _, table := range tables {
        builder.WriteString(table.DDL)
        builder.WriteString("\n\n")
    }
    
    builder.WriteString("-- 函数定义\n")
    for _, fn := range functions {
        builder.WriteString(fn)
        builder.WriteString("\n\n")
    }
    
    fmt.Println(builder.String())
}

方法3:使用第三方库(简化操作)

package main

import (
    "database/sql"
    "fmt"
    "github.com/davecgh/go-spew/spew"
    _ "github.com/lib/pq"
    "github.com/xo/dburl"
)

func getFullSchemaDDL(dbURL string) (string, error) {
    // 解析数据库URL
    u, err := dburl.Parse(dbURL)
    if err != nil {
        return "", err
    }
    
    db, err := sql.Open(u.Driver, u.DSN)
    if err != nil {
        return "", err
    }
    defer db.Close()
    
    // 获取所有对象
    var ddl strings.Builder
    
    // 获取扩展
    rows, err := db.Query(`
        SELECT 'CREATE EXTENSION IF NOT EXISTS ' || extname || ';'
        FROM pg_extension
        WHERE extname NOT LIKE 'pg_%'
    `)
    if err == nil {
        for rows.Next() {
            var ext string
            rows.Scan(&ext)
            ddl.WriteString(ext + "\n")
        }
        rows.Close()
    }
    
    // 获取序列
    rows, err = db.Query(`
        SELECT 'CREATE SEQUENCE ' || schemaname || '.' || sequencename || ';'
        FROM pg_sequences
        WHERE schemaname NOT IN ('pg_catalog', 'information_schema')
    `)
    if err == nil {
        for rows.Next() {
            var seq string
            rows.Scan(&seq)
            ddl.WriteString(seq + "\n")
        }
        rows.Close()
    }
    
    return ddl.String(), nil
}

func main() {
    dbURL := "postgres://postgres:password@localhost:5432/mydb?sslmode=disable"
    
    ddl, err := getFullSchemaDDL(dbURL)
    if err != nil {
        panic(err)
    }
    
    spew.Dump(ddl)
}

方法4:使用information_schema(标准SQL)

package main

import (
    "database/sql"
    "fmt"
    _ "github.com/lib/pq"
)

func getSchemaFromInformationSchema(db *sql.DB) error {
    // 获取所有表
    query := `
        SELECT 
            table_schema,
            table_name,
            column_name,
            data_type,
            is_nullable,
            column_default
        FROM information_schema.columns
        WHERE table_schema NOT IN ('pg_catalog', 'information_schema')
        ORDER BY table_schema, table_name, ordinal_position
    `
    
    rows, err := db.Query(query)
    if err != nil {
        return err
    }
    defer rows.Close()
    
    for rows.Next() {
        var schema, table, column, dataType, nullable, defaultValue sql.NullString
        err := rows.Scan(&schema, &table, &column, &dataType, &nullable, &defaultValue)
        if err != nil {
            return err
        }
        
        fmt.Printf("表: %s.%s, 列: %s, 类型: %s, 可空: %s, 默认值: %s\n",
            schema.String, table.String, column.String, dataType.String, 
            nullable.String, defaultValue.String)
    }
    
    return nil
}

func main() {
    db, err := sql.Open("postgres", 
        "host=localhost port=5432 user=postgres password=password dbname=mydb sslmode=disable")
    if err != nil {
        panic(err)
    }
    defer db.Close()
    
    err = getSchemaFromInformationSchema(db)
    if err != nil {
        panic(err)
    }
}

依赖安装

go get github.com/lib/pq
go get github.com/xo/dburl
go get github.com/davecgh/go-spew/spew

方法1是最简单直接的方式,利用系统工具生成完整的DDL。方法2提供了更细粒度的控制,可以自定义输出格式。方法3使用第三方库简化了URL处理。方法4使用标准SQL但可能无法获取所有PostgreSQL特有的对象定义。

回到顶部