mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-10-18 20:18:27 +02:00
* filer: escape LIKE wildcards in SQL list prefixes ListDirectoryPrefixedEntries bound prefix+"%" straight into a LIKE clause, so '_' and '%' in object names (e.g. Langfuse keys like call_03_...) acted as wildcards: wrong results and a ~40x slower range scan because (dirhash, name) can no longer be narrowed. Escape '!'/'%'/'_' in the prefix and declare ESCAPE '!' in the generated list SQL so every abstract_sql backend (mysql, mysql2, postgres, postgres2, sqlite) matches literally with identical semantics. '!' is used instead of '\' because SQLite shares SqlGenMysql but would reject the MySQL-style '\\' literal, and '\' would still need escaping itself. * filer: escape LIKE wildcards in YDB list prefixes Same wildcard-in-prefix bug as the abstract_sql backends: YQL LIKE treated '_' and '%' in the prefix as wildcards. Apply the shared LikePattern helper and declare ESCAPE '!' (YQL allows any escape char except '%', '_' and '\'). * test: cover literal '_'/'%'/'!' prefixes in sqlite filer listing End-to-end regression test through SqliteStore against a real database; runs in CI under the sqlite build tag.
98 lines
3.6 KiB
Go
98 lines
3.6 KiB
Go
package postgres
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
_ "github.com/jackc/pgx/v5/stdlib"
|
|
"github.com/seaweedfs/seaweedfs/weed/filer/abstract_sql"
|
|
)
|
|
|
|
type SqlGenPostgres struct {
|
|
CreateTableSqlTemplate string
|
|
DropTableSqlTemplate string
|
|
UpsertQueryTemplate string
|
|
// Force byte ordering on a locale-aware name column; see ConfigureListOrdering.
|
|
ForceBinaryCollation bool
|
|
}
|
|
|
|
// DefaultUpsertQuery keeps INSERTs idempotent so a duplicate-key failure
|
|
// (23505) cannot poison the surrounding transaction (25P02). Used when the
|
|
// user enables upsert but does not provide their own template.
|
|
const DefaultUpsertQuery = `INSERT INTO "%s" (dirhash,name,directory,meta) VALUES($1,$2,$3,$4) ON CONFLICT (dirhash, name) DO UPDATE SET directory=EXCLUDED.directory, meta=EXCLUDED.meta`
|
|
|
|
// DefaultCreateTableQuery is used when createTable is left unset; an empty
|
|
// template would otherwise render as %!(EXTRA ...) garbage SQL.
|
|
const DefaultCreateTableQuery = `CREATE TABLE IF NOT EXISTS "%s" (dirhash BIGINT, name VARCHAR(65535), directory VARCHAR(65535), meta bytea, PRIMARY KEY (dirhash, name))`
|
|
|
|
var (
|
|
_ = abstract_sql.SqlGenerator(&SqlGenPostgres{})
|
|
)
|
|
|
|
// ResolveCreateTableQuery normalizes the createTable config value. A boolean
|
|
// true (read by viper as the string "true") selects the default template.
|
|
// An empty or false value returns an empty string so the caller can skip
|
|
// table creation. Any other value is treated as a custom SQL template.
|
|
func ResolveCreateTableQuery(createTable string) string {
|
|
switch createTable {
|
|
case "true":
|
|
return DefaultCreateTableQuery
|
|
case "false", "":
|
|
return ""
|
|
default:
|
|
return createTable
|
|
}
|
|
}
|
|
|
|
func (gen *SqlGenPostgres) GetSqlInsert(tableName string) string {
|
|
if gen.UpsertQueryTemplate != "" {
|
|
return fmt.Sprintf(gen.UpsertQueryTemplate, tableName)
|
|
} else {
|
|
return fmt.Sprintf(`INSERT INTO "%s" (dirhash,name,directory,meta) VALUES($1,$2,$3,$4)`, tableName)
|
|
}
|
|
}
|
|
|
|
func (gen *SqlGenPostgres) GetSqlUpdate(tableName string) string {
|
|
return fmt.Sprintf(`UPDATE "%s" SET meta=$1 WHERE dirhash=$2 AND name=$3 AND directory=$4`, tableName)
|
|
}
|
|
|
|
func (gen *SqlGenPostgres) GetSqlFind(tableName string) string {
|
|
return fmt.Sprintf(`SELECT meta FROM "%s" WHERE dirhash=$1 AND name=$2 AND directory=$3`, tableName)
|
|
}
|
|
|
|
func (gen *SqlGenPostgres) GetSqlDelete(tableName string) string {
|
|
return fmt.Sprintf(`DELETE FROM "%s" WHERE dirhash=$1 AND name=$2 AND directory=$3`, tableName)
|
|
}
|
|
|
|
func (gen *SqlGenPostgres) GetSqlDeleteFolderChildren(tableName string) string {
|
|
return fmt.Sprintf(`DELETE FROM "%s" WHERE dirhash=$1 AND directory=$2`, tableName)
|
|
}
|
|
|
|
// nameExpr forces byte ordering on a locale-aware column via COLLATE "C".
|
|
func (gen *SqlGenPostgres) nameExpr() string {
|
|
if gen.ForceBinaryCollation {
|
|
return `name COLLATE "C"`
|
|
}
|
|
return "name"
|
|
}
|
|
|
|
func (gen *SqlGenPostgres) GetSqlListExclusive(tableName string) string {
|
|
name := gen.nameExpr()
|
|
return fmt.Sprintf(`SELECT NAME, meta FROM "%s" WHERE dirhash=$1 AND %s>$2 AND directory=$3 AND %s like $4 ESCAPE '!' ORDER BY %s ASC LIMIT $5`, tableName, name, name, name)
|
|
}
|
|
|
|
func (gen *SqlGenPostgres) GetSqlListInclusive(tableName string) string {
|
|
name := gen.nameExpr()
|
|
return fmt.Sprintf(`SELECT NAME, meta FROM "%s" WHERE dirhash=$1 AND %s>=$2 AND directory=$3 AND %s like $4 ESCAPE '!' ORDER BY %s ASC LIMIT $5`, tableName, name, name, name)
|
|
}
|
|
|
|
func (gen *SqlGenPostgres) GetSqlCreateTable(tableName string) string {
|
|
if gen.CreateTableSqlTemplate == "" {
|
|
return ""
|
|
}
|
|
return fmt.Sprintf(gen.CreateTableSqlTemplate, tableName)
|
|
}
|
|
|
|
func (gen *SqlGenPostgres) GetSqlDropTable(tableName string) string {
|
|
return fmt.Sprintf(gen.DropTableSqlTemplate, tableName)
|
|
}
|