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