package pq import ( "bytes" "strings" ) // QuoteIdentifier quotes an "identifier" (e.g. a table or a column name) to be // used as part of an SQL statement. For example: // // tblname := "my_table" // data := "my_data" // quoted := pq.QuoteIdentifier(tblname) // err := db.Exec(fmt.Sprintf("INSERT INTO %s VALUES ($1)", quoted), data) // // Any double quotes in name will be escaped. The quoted identifier will be case // sensitive when used in a query. If the input string contains a zero byte, the // result will be truncated immediately before it. func QuoteIdentifier(name string) string { end := strings.IndexRune(name, 0) if end > -1 { name = name[:end] } return `"` + strings.Replace(name, `"`, `""`, -1) + `"` } // BufferQuoteIdentifier satisfies the same purpose as QuoteIdentifier, but backed by a // byte buffer. func BufferQuoteIdentifier(name string, buffer *bytes.Buffer) { // TODO(v2): this should have accepted an io.Writer, not *bytes.Buffer. end := strings.IndexRune(name, 0) if end > -1 { name = name[:end] } buffer.WriteRune('"') buffer.WriteString(strings.Replace(name, `"`, `""`, -1)) buffer.WriteRune('"') } // QuoteLiteral quotes a 'literal' (e.g. a parameter, often used to pass literal // to DDL and other statements that do not accept parameters) to be used as part // of an SQL statement. For example: // // exp_date := pq.QuoteLiteral("2023-01-05 15:00:00Z") // err := db.Exec(fmt.Sprintf("CREATE ROLE my_user VALID UNTIL %s", exp_date)) // // Any single quotes in name will be escaped. Any backslashes (i.e. "\") will be // replaced by two backslashes (i.e. "\\") and the C-style escape identifier // that PostgreSQL provides ('E') will be prepended to the string. func QuoteLiteral(literal string) string { // This follows the PostgreSQL internal algorithm for handling quoted literals // from libpq, which can be found in the "PQEscapeStringInternal" function, // which is found in the libpq/fe-exec.c source file: // https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/interfaces/libpq/fe-exec.c // // substitute any single-quotes (') with two single-quotes ('') literal = strings.Replace(literal, `'`, `''`, -1) // determine if the string has any backslashes (\) in it. // if it does, replace any backslashes (\) with two backslashes (\\) // then, we need to wrap the entire string with a PostgreSQL // C-style escape. Per how "PQEscapeStringInternal" handles this case, we // also add a space before the "E" if strings.Contains(literal, `\`) { literal = strings.Replace(literal, `\`, `\\`, -1) literal = ` E'` + literal + `'` } else { // otherwise, we can just wrap the literal with a pair of single quotes literal = `'` + literal + `'` } return literal }