使用 pgx 在 postgres 中创建用户 (SQLSTATE 42601)

发布于 2025-01-17 05:16:37 字数 797 浏览 3 评论 0 原文

我正在尝试在 postgres 中创建一个用户。目前正在尝试使用 https://github.com/jackc/pgx 作为连接到的驱动程序数据库。我有以下内容

package main

import (
    "context"
    "fmt"
    "os"

    "github.com/jackc/pgx/v4"
)

func main() {
    ctx := context.Background()
    conn, err := pgx.Connect(ctx, "host=localhost port=5432 user=postgres password=postgres dbname=postgres")
    if err != nil {
        panic(err)
    }
    defer conn.Close(ctx)

    // create user
    _, err = conn.Exec(ctx, "CREATE USER $1 WITH PASSWORD $2", "moulick", "testpass")
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }
}

但我收到此错误:“$1”处或附近的语法错误(SQLSTATE 42601)

我不明白这里有什么问题?

I am trying to create a user in postgres. currently trying to use https://github.com/jackc/pgx as the driver to connect to the db. I have the below

package main

import (
    "context"
    "fmt"
    "os"

    "github.com/jackc/pgx/v4"
)

func main() {
    ctx := context.Background()
    conn, err := pgx.Connect(ctx, "host=localhost port=5432 user=postgres password=postgres dbname=postgres")
    if err != nil {
        panic(err)
    }
    defer conn.Close(ctx)

    // create user
    _, err = conn.Exec(ctx, "CREATE USER $1 WITH PASSWORD $2", "moulick", "testpass")
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }
}

But I get this ERROR: syntax error at or near "$1" (SQLSTATE 42601)

I don't get what's the problem here ?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

空城之時有危險 2025-01-24 05:16:38

Postgres 准备好的语句(带有 $1$2 等的语句)仅支持 SELECT、INSERT、UPDATE、DELETE、MERGE 或 VALUES 语句(请参阅 PREPARE 文档)。

由于问题是关于 CREATE USER 的,我们需要自己构建 SQL 语句,正确地转义不同的部分。用户名是一个标识符(使用 Identifier.Sanitize( )),密码是一个字符串(使用 PgConn.EscapeString())。以下程序正确转义这两个部分以创建 sql 字符串,然后将其按原样传递给 Exec()

package main

import (
    "context"
    "fmt"

    "github.com/jackc/pgx/v5"
)

func main() {
    ctx := context.Background()
    conn, err := pgx.Connect(ctx, "host=localhost port=5432 user=postgres password=postgres dbname=postgres")
    if err != nil {
        panic(err)
    }
    defer conn.Close(ctx)

    username := "user with spaces"
    password := "test pass ' with \" special chars"

    escapedUsername := pgx.Identifier{username}.Sanitize()
    escapedPassword, err := conn.PgConn().EscapeString(password)
    if err != nil {
        panic(err)
    }
    sql := fmt.Sprintf("CREATE USER %s WITH PASSWORD '%s'", escapedUsername, escapedPassword)
    _, err = conn.Exec(ctx, sql)
    if err != nil {
        panic(err)
    }
}

Postgres prepared statements (those with $1, $2, etc.) are supported only for SELECT, INSERT, UPDATE, DELETE, MERGE, or VALUES statements (see documentation of PREPARE).

Since the question is about CREATE USER, we need to build the SQL statement ourselves, properly escaping the different parts. The username is an identifier (use Identifier.Sanitize()), and the password is a string (use PgConn.EscapeString()). The following program escapes both parts correctly to create the sql string, which is then passed as-is to Exec().

package main

import (
    "context"
    "fmt"

    "github.com/jackc/pgx/v5"
)

func main() {
    ctx := context.Background()
    conn, err := pgx.Connect(ctx, "host=localhost port=5432 user=postgres password=postgres dbname=postgres")
    if err != nil {
        panic(err)
    }
    defer conn.Close(ctx)

    username := "user with spaces"
    password := "test pass ' with \" special chars"

    escapedUsername := pgx.Identifier{username}.Sanitize()
    escapedPassword, err := conn.PgConn().EscapeString(password)
    if err != nil {
        panic(err)
    }
    sql := fmt.Sprintf("CREATE USER %s WITH PASSWORD '%s'", escapedUsername, escapedPassword)
    _, err = conn.Exec(ctx, sql)
    if err != nil {
        panic(err)
    }
}

调妓 2025-01-24 05:16:37

“我不明白这里有什么问题?” -- 问题是 位置参数只能用于,不能用于标识符

位置参数引用用于指示从外部提供给 SQL 语句的

您无法在 CREATE USER 中使用位置参数,就像无法在 SELECT t. 中使用位置参数一样。 FROM <表名> AS t 。

user_name := "moulick"
_, err = conn.Exec(ctx, "CREATE USER "+user_name+" WITH PASSWORD $1", "testpass")
if err != nil {
    fmt.Println(err)
    os.Exit(1)
}

如果user_name不是硬编码的,而是来自未知的用户输入,您需要自己验证它以避免SQL注入的可能性。这不是一项艰巨的任务,因为词汇结构标识符仅限于一小组规则,如果您愿意,您可以进一步将其减少到更小的子集(例如,不允许变音符号和非拉丁字母):

SQL 标识符和关键字必须以字母(az,但也
带变音符号的字母和非拉丁字母)或下划线
(_)。标识符或关键字中的后续字符可以是
字母、下划线、数字 (0-9) 或美元符号 ($)。注意
根据以下字母,标识符中不允许使用美元符号
SQL 标准,因此它们的使用可能会降低应用程序的性能
便携的。 SQL 标准不会定义包含以下内容的关键字
数字或以下划线开头或结尾,因此此标识符
形式可以避免与未来扩展可能发生的冲突
标准。

"I don't get what's the problem here ?" -- The problem is that positional parameters can be used only for values, and not for identifiers.

A positional parameter reference is used to indicate a value that is supplied externally to an SQL statement.

You cannot use positional parameters in CREATE USER <user_name> the same way you cannot use them in SELECT t.<column_name> FROM <table_name> AS t.

user_name := "moulick"
_, err = conn.Exec(ctx, "CREATE USER "+user_name+" WITH PASSWORD $1", "testpass")
if err != nil {
    fmt.Println(err)
    os.Exit(1)
}

If the user_name is not hardcoded, but instead comes from an unknown user input, you need to validate it yourself to avoid the possibility of SQL injections. This is not a difficult task since the lexical structure of identifiers is limited to a small set of rules, which you can further reduce to an even smaller subset if you like (e.g. disallowing diacritical marks and non-Latin letters):

SQL identifiers and key words must begin with a letter (a-z, but also
letters with diacritical marks and non-Latin letters) or an underscore
(_). Subsequent characters in an identifier or key word can be
letters, underscores, digits (0-9), or dollar signs ($). Note that
dollar signs are not allowed in identifiers according to the letter of
the SQL standard, so their use might render applications less
portable. The SQL standard will not define a key word that contains
digits or starts or ends with an underscore, so identifiers of this
form are safe against possible conflict with future extensions of the
standard.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文