mirror of
https://github.com/hanzoai/base.git
synced 2026-10-21 03:41:50 +00:00
- Delete 7 one-off migration utilities (cmd/fix*, cmd/token*) - Rename env vars: PB_ID_TOKEN_LEEWAY → HZ_ID_TOKEN_LEEWAY, PB_FILES_DELETE_MAX_WORKERS → HZ_FILES_DELETE_MAX_WORKERS - Rename temp file prefix: pb_buffer_file_ → hz_buffer_ - Rename test data dir: pb_base_app_test_data_dir → test_data_dir - Strip all "PocketBase" from comments (→ "Base" or removed) - Strip pb_data references in backup error messages - Update @group PocketBase → @group Base in JSVM type docs - Update filesystem blob/s3 package doc comments - _hzf_ field IDs in test DB (already done prior commit) Zero remaining PocketBase/pb_ references in production Go code. Only legitimate deps remain: pocketbase/dbx (query builder), pocketbase/tygoja (JS VM bindings).
156 lines
4.1 KiB
Go
156 lines
4.1 KiB
Go
package core
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
// PostgresDialect implements SQLDialect for PostgreSQL databases.
|
|
type PostgresDialect struct{}
|
|
|
|
func (d *PostgresDialect) DriverName() string {
|
|
return "pgx"
|
|
}
|
|
|
|
func (d *PostgresDialect) Placeholder(n int) string {
|
|
return fmt.Sprintf("$%d", n)
|
|
}
|
|
|
|
func (d *PostgresDialect) AutoincrementType() string {
|
|
return "BIGSERIAL PRIMARY KEY"
|
|
}
|
|
|
|
func (d *PostgresDialect) JSONExtract(column, path string) string {
|
|
// Strip leading "$." or "$" prefix used by SQLite JSON paths.
|
|
p := path
|
|
if strings.HasPrefix(p, "$.") {
|
|
p = p[2:]
|
|
} else if strings.HasPrefix(p, "$") {
|
|
p = p[1:]
|
|
}
|
|
|
|
// Handle nested paths like "a.b.c" -> col #>> '{a,b,c}'
|
|
parts := strings.Split(p, ".")
|
|
if len(parts) == 1 {
|
|
return fmt.Sprintf("%s->>'%s'", column, parts[0])
|
|
}
|
|
return fmt.Sprintf("%s #>> '{%s}'", column, strings.Join(parts, ","))
|
|
}
|
|
|
|
func (d *PostgresDialect) JSONEach(column string) string {
|
|
return fmt.Sprintf("jsonb_array_elements(%s)", column)
|
|
}
|
|
|
|
func (d *PostgresDialect) JSONValid(column string) string {
|
|
return fmt.Sprintf("(%s)::jsonb IS NOT NULL", column)
|
|
}
|
|
|
|
func (d *PostgresDialect) JSONArray(values ...string) string {
|
|
return fmt.Sprintf("jsonb_build_array(%s)", strings.Join(values, ", "))
|
|
}
|
|
|
|
// sqliteToPostgresFormat maps SQLite strftime format specifiers to PostgreSQL to_char patterns.
|
|
var sqliteToPostgresFormat = map[string]string{
|
|
"%Y": "YYYY",
|
|
"%m": "MM",
|
|
"%d": "DD",
|
|
"%H": "HH24",
|
|
"%M": "MI",
|
|
"%S": "SS",
|
|
"%s": "epoch",
|
|
"%w": "D",
|
|
"%f": "SS.US",
|
|
}
|
|
|
|
func (d *PostgresDialect) Strftime(format, column string) string {
|
|
// Handle epoch specially: extract epoch returns a number, not a formatted string.
|
|
if format == "%s" {
|
|
return fmt.Sprintf("EXTRACT(EPOCH FROM %s)", column)
|
|
}
|
|
|
|
pgFormat := format
|
|
for sqliteFmt, pgFmt := range sqliteToPostgresFormat {
|
|
pgFormat = strings.ReplaceAll(pgFormat, sqliteFmt, pgFmt)
|
|
}
|
|
|
|
// Handle the datetime format: %Y-%m-%d %H:%M:%fZ
|
|
// After replacement this becomes: YYYY-MM-DD HH24:MI:SS.USZ
|
|
return fmt.Sprintf("to_char(%s, '%s')", column, pgFormat)
|
|
}
|
|
|
|
func (d *PostgresDialect) Now() string {
|
|
return "NOW()"
|
|
}
|
|
|
|
func (d *PostgresDialect) RandomID(byteLen int) string {
|
|
return fmt.Sprintf("encode(gen_random_bytes(%d), 'hex')", byteLen)
|
|
}
|
|
|
|
func (d *PostgresDialect) Concat(parts ...string) string {
|
|
return strings.Join(parts, " || ")
|
|
}
|
|
|
|
func (d *PostgresDialect) GroupConcat(column, separator string) string {
|
|
return fmt.Sprintf("string_agg(%s, '%s')", column, separator)
|
|
}
|
|
|
|
func (d *PostgresDialect) TableColumns(table string) string {
|
|
return fmt.Sprintf(
|
|
"SELECT column_name, data_type, is_nullable, column_default FROM information_schema.columns WHERE table_name = '%s' ORDER BY ordinal_position",
|
|
table,
|
|
)
|
|
}
|
|
|
|
func (d *PostgresDialect) HasTable(table string) string {
|
|
return fmt.Sprintf(
|
|
"SELECT 1 FROM information_schema.tables WHERE table_schema = 'public' AND table_name = '%s' LIMIT 1",
|
|
table,
|
|
)
|
|
}
|
|
|
|
func (d *PostgresDialect) ListTables() string {
|
|
return "SELECT table_name FROM information_schema.tables WHERE table_schema = 'public' AND table_type = 'BASE TABLE' ORDER BY table_name"
|
|
}
|
|
|
|
func (d *PostgresDialect) ListViews() string {
|
|
return "SELECT table_name FROM information_schema.tables WHERE table_schema = 'public' AND table_type = 'VIEW' ORDER BY table_name"
|
|
}
|
|
|
|
// pgTypeMap maps SQLite type names to PostgreSQL equivalents.
|
|
var pgTypeMap = map[string]string{
|
|
"INTEGER": "BIGINT",
|
|
"REAL": "DOUBLE PRECISION",
|
|
"BLOB": "BYTEA",
|
|
"JSON": "JSONB",
|
|
// These map to themselves but are listed for clarity.
|
|
"TEXT": "TEXT",
|
|
"BOOLEAN": "BOOLEAN",
|
|
}
|
|
|
|
func (d *PostgresDialect) NormalizeColumnType(sqliteType string) string {
|
|
upper := strings.ToUpper(strings.TrimSpace(sqliteType))
|
|
if pgType, ok := pgTypeMap[upper]; ok {
|
|
return pgType
|
|
}
|
|
return sqliteType
|
|
}
|
|
|
|
func (d *PostgresDialect) Vacuum() string {
|
|
return "VACUUM"
|
|
}
|
|
|
|
func (d *PostgresDialect) BooleanValue(b bool) string {
|
|
if b {
|
|
return "TRUE"
|
|
}
|
|
return "FALSE"
|
|
}
|
|
|
|
func (d *PostgresDialect) SupportsReturning() bool {
|
|
return true
|
|
}
|
|
|
|
func (d *PostgresDialect) CastToText(column string) string {
|
|
return fmt.Sprintf("CAST(%s AS TEXT)", column)
|
|
}
|