go.charczuk.com@v0.0.0-20240327042549-bc490516bd1a/sdk/db/dbutil/validate_database_name.go (about) 1 /* 2 3 Copyright (c) 2023 - Present. Will Charczuk. All rights reserved. 4 Use of this source code is governed by a MIT license that can be found in the LICENSE file at the root of the repository. 5 6 */ 7 8 package dbutil 9 10 import ( 11 "errors" 12 "strings" 13 "unicode" 14 "unicode/utf8" 15 ) 16 17 var ( 18 // ErrDatabaseNameReserved is a validation failure. 19 ErrDatabaseNameReserved = errors.New("dbutil; database name is reserved") 20 // ErrDatabaseNameEmpty is a validation failure. 21 ErrDatabaseNameEmpty = errors.New("dbutil; database name is empty") 22 // ErrDatabaseNameInvalidFirstRune is a validation failure. 23 ErrDatabaseNameInvalidFirstRune = errors.New("dbutil; database name must start with a letter or underscore") 24 // ErrDatabaseNameInvalid is a validation failure. 25 ErrDatabaseNameInvalid = errors.New("dbutil; database name must be composed of (in regex form) [a-zA-Z0-9_]") 26 // ErrDatabaseNameTooLong is a validation failure. 27 ErrDatabaseNameTooLong = errors.New("dbutil; database name must be 63 characters or fewer") 28 ) 29 30 var ( 31 // ReservedDatabaseNames are names you cannot use to create a database with. 32 ReservedDatabaseNames = []string{ 33 "postgres", 34 "defaultdb", 35 "template0", 36 "template1", 37 } 38 ) 39 40 const ( 41 // DatabaseNameMaxLength is the maximum length of a database name. 42 DatabaseNameMaxLength = 63 43 ) 44 45 // ValidateDatabaseName validates a database name. 46 func ValidateDatabaseName(name string) error { 47 name = strings.TrimSpace(name) 48 if name == "" { 49 return ErrDatabaseNameEmpty 50 } 51 if len(name) > DatabaseNameMaxLength { 52 return ErrDatabaseNameTooLong 53 } 54 55 firstRune, _ := utf8.DecodeRuneInString(name) 56 if !isValidDatabaseNameFirstRune(firstRune) { 57 return ErrDatabaseNameInvalidFirstRune 58 } 59 60 for _, r := range name { 61 if !isValidDatabaseNameRune(r) { 62 return ErrDatabaseNameInvalid 63 } 64 } 65 66 for _, reserved := range ReservedDatabaseNames { 67 if strings.EqualFold(reserved, name) { 68 return ErrDatabaseNameReserved 69 } 70 } 71 return nil 72 } 73 74 // isValidDatabaseNameFirstRune returns if the rune is valid as a first rune of a database name. 75 func isValidDatabaseNameFirstRune(r rune) bool { 76 return unicode.IsLetter(r) || r == '_' 77 } 78 79 // isValidDatabaseNameRune is a rune predicate that indicites a rune is a valid database name component. 80 func isValidDatabaseNameRune(r rune) bool { 81 return unicode.IsLetter(r) || unicode.IsDigit(r) || r == '_' 82 }