github.com/dolthub/go-mysql-server@v0.18.0/sql/databases.go (about) 1 // Copyright 2022 Dolthub, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package sql 16 17 import ( 18 "strings" 19 "time" 20 ) 21 22 const ( 23 // InformationSchemaDatabaseName is the name of the information schema database. 24 InformationSchemaDatabaseName = "information_schema" 25 ) 26 27 // DatabaseProvider is the fundamental interface to integrate with the engine. It provides access to all databases in 28 // a given backend. A DatabaseProvider is provided to the Catalog when the engine is initialized. 29 type DatabaseProvider interface { 30 // Database gets a Database from the provider. 31 Database(ctx *Context, name string) (Database, error) 32 // HasDatabase checks if the Database exists in the provider. 33 HasDatabase(ctx *Context, name string) bool 34 // AllDatabases returns a slice of all Databases in the provider. 35 AllDatabases(ctx *Context) []Database 36 } 37 38 // MutableDatabaseProvider is a DatabaseProvider that can create and drop databases. 39 type MutableDatabaseProvider interface { 40 DatabaseProvider 41 42 // CreateDatabase creates a database and adds it to the provider's collection. 43 CreateDatabase(ctx *Context, name string) error 44 45 // DropDatabase removes a database from the provider's collection. 46 DropDatabase(ctx *Context, name string) error 47 } 48 49 // CollatedDatabaseProvider is a DatabaseProvider that can create a Database with a specific collation. 50 type CollatedDatabaseProvider interface { 51 MutableDatabaseProvider 52 53 // CreateCollatedDatabase creates a collated database and adds it to the provider's collection. 54 CreateCollatedDatabase(ctx *Context, name string, collation CollationID) error 55 } 56 57 // TableFunctionProvider is an interface that allows custom table functions to be provided. It's usually (but not 58 // always) implemented by a DatabaseProvider. 59 type TableFunctionProvider interface { 60 // TableFunction returns the table function with the name provided, case-insensitive 61 TableFunction(ctx *Context, name string) (TableFunction, error) 62 // WithTableFunctions returns a new provider with (only) the list of table functions arguments 63 WithTableFunctions(fns ...TableFunction) (TableFunctionProvider, error) 64 } 65 66 // Database represents the database. Its primary job is to provide access to all tables. 67 type Database interface { 68 Nameable 69 // GetTableInsensitive retrieves a table by its case-insensitive name. To be SQL compliant, databases should not 70 // allow two tables with the same case-insensitive name. Behavior is undefined when two tables have the same 71 // case-insensitive name. 72 GetTableInsensitive(ctx *Context, tblName string) (Table, bool, error) 73 // GetTableNames returns the table names of every table in the database. It does not return the names of temporary 74 // tables 75 GetTableNames(ctx *Context) ([]string, error) 76 } 77 78 // Databaser is a node that contains a reference to a database. 79 type Databaser interface { 80 // Database the current database. 81 Database() Database 82 // WithDatabase returns a new node instance with the database replaced with 83 // the one given as parameter. 84 WithDatabase(Database) (Node, error) 85 } 86 87 // Databaseable is a node with a string reference to a database 88 type Databaseable interface { 89 Database() string 90 } 91 92 // MultiDatabaser is a node that contains a reference to a database provider. This interface is intended for very 93 // specific nodes that must resolve databases during execution time rather than during analysis, such as block 94 // statements where the execution of a nested statement in the block may affect future statements within that same block. 95 type MultiDatabaser interface { 96 // DatabaseProvider returns the current DatabaseProvider. 97 DatabaseProvider() DatabaseProvider 98 // WithDatabaseProvider returns a new node instance with the database provider replaced with the one given as parameter. 99 WithDatabaseProvider(DatabaseProvider) (Node, error) 100 } 101 102 // ReadOnlyDatabase is an extension of Database that may declare itself read-only, which will disallow any DDL or DML 103 // statements from executing. 104 type ReadOnlyDatabase interface { 105 Database 106 // IsReadOnly returns whether this database is read-only. 107 IsReadOnly() bool 108 } 109 110 // TableCreator is a Database that can create new tables. 111 type TableCreator interface { 112 Database 113 // CreateTable creates the table with the given name, schema, collation, and comment. Integrators are 114 // responsible for persisting all provided table metadata. Comment may be optionally persisted, and if 115 // so, sql.Table implementations should also implement sql.CommentedTable so that the comment may be 116 // retrieved. 117 CreateTable(ctx *Context, name string, schema PrimaryKeySchema, collation CollationID, comment string) error 118 } 119 120 // IndexedTableCreator is a Database that can create new tables which have a Primary Key with columns that have 121 // prefix lengths. 122 type IndexedTableCreator interface { 123 Database 124 // CreateIndexedTable creates the table with the given name and schema using the index definition provided for its 125 // primary key index. 126 CreateIndexedTable(ctx *Context, name string, schema PrimaryKeySchema, idxDef IndexDef, collation CollationID) error 127 } 128 129 // TemporaryTableCreator is a database that can create temporary tables that persist only as long as the session. 130 // Note that temporary tables with the same name as persisted tables take precedence in most SQL operations. 131 type TemporaryTableCreator interface { 132 Database 133 // CreateTemporaryTable creates the table with the given name and schema. If a temporary table with that name already exists, must 134 // return sql.ErrTableAlreadyExists 135 CreateTemporaryTable(ctx *Context, name string, schema PrimaryKeySchema, collation CollationID) error 136 } 137 138 // TableDropper is a Datagbase that can drop tables. 139 type TableDropper interface { 140 Database 141 DropTable(ctx *Context, name string) error 142 } 143 144 // TableRenamer is a database that can rename tables. 145 type TableRenamer interface { 146 Database 147 // RenameTable renames a table from oldName to newName as given. 148 RenameTable(ctx *Context, oldName, newName string) error 149 } 150 151 // VersionedDatabase is a Database that can return tables as they existed at different points in time. The engine 152 // supports queries on historical table data via the AS OF construct introduced in SQL 2011. 153 type VersionedDatabase interface { 154 Database 155 // GetTableInsensitiveAsOf retrieves a table by its case-insensitive name with the same semantics as 156 // Database.GetTableInsensitive, but at a particular revision of the database. Implementors must choose which types 157 // of expressions to accept as revision names. 158 GetTableInsensitiveAsOf(ctx *Context, tblName string, asOf interface{}) (Table, bool, error) 159 // GetTableNamesAsOf returns the table names of every table in the database as of the revision given. Implementors 160 // must choose which types of expressions to accept as revision names. 161 GetTableNamesAsOf(ctx *Context, asOf interface{}) ([]string, error) 162 } 163 164 // CollatedDatabase is a Database that can store and update its collation. 165 type CollatedDatabase interface { 166 Database 167 // GetCollation returns this database's collation. 168 GetCollation(ctx *Context) CollationID 169 // SetCollation updates this database's collation. 170 SetCollation(ctx *Context, collation CollationID) error 171 } 172 173 // TriggerDatabase is a Database that supports creating and storing triggers. The engine handles all parsing and 174 // execution logic for triggers. Integrators are not expected to parse or understand the trigger definitions, but must 175 // store and return them when asked. 176 type TriggerDatabase interface { 177 Database 178 // GetTriggers returns all trigger definitions for the database 179 GetTriggers(ctx *Context) ([]TriggerDefinition, error) 180 // CreateTrigger is called when an integrator is asked to create a trigger. The CREATE TRIGGER statement string is 181 // provided to store, along with the name of the trigger. 182 CreateTrigger(ctx *Context, definition TriggerDefinition) error 183 // DropTrigger is called when a trigger should no longer be stored. The name has already been validated. 184 // Returns ErrTriggerDoesNotExist if the trigger was not found. 185 DropTrigger(ctx *Context, name string) error 186 } 187 188 // TriggerDefinition defines a trigger. Integrators are not expected to parse or understand the trigger definitions, 189 // but must store and return them when asked. 190 type TriggerDefinition struct { 191 // The name of this trigger. Trigger names in a database are unique. 192 Name string 193 // The text of the statement to create this trigger. 194 CreateStatement string 195 // The time that the trigger was created. 196 CreatedAt time.Time 197 // SqlMode holds the SQL_MODE that was in use when this trigger was originally defined. It contains information 198 // needed for how to parse the trigger's SQL, such as whether ANSI_QUOTES mode is enabled. 199 SqlMode string 200 } 201 202 // TemporaryTableDatabase is a database that can query the session (which manages the temporary table state) to 203 // retrieve the name of all temporary tables. 204 type TemporaryTableDatabase interface { 205 // GetAllTemporaryTables returns the names of all temporary tables in the session. 206 GetAllTemporaryTables(ctx *Context) ([]Table, error) 207 } 208 209 // TableCopierDatabase is a database that can copy a source table's data (without preserving indexed, fks, etc.) into 210 // another destination table. 211 type TableCopierDatabase interface { 212 // CopyTableData copies the sourceTable data to the destinationTable and returns the number of rows copied. 213 CopyTableData(ctx *Context, sourceTable string, destinationTable string) (uint64, error) 214 } 215 216 // StoredProcedureDatabase is a database that supports the creation and execution of stored procedures. The engine will 217 // handle all parsing and execution logic for stored procedures. Integrators only need to store and retrieve 218 // StoredProcedureDetails, while verifying that all stored procedures have a unique name without regard to 219 // case-sensitivity. 220 type StoredProcedureDatabase interface { 221 Database 222 // GetStoredProcedure returns the desired StoredProcedureDetails from the database. 223 GetStoredProcedure(ctx *Context, name string) (StoredProcedureDetails, bool, error) 224 // GetStoredProcedures returns all StoredProcedureDetails for the database. 225 GetStoredProcedures(ctx *Context) ([]StoredProcedureDetails, error) 226 // SaveStoredProcedure stores the given StoredProcedureDetails to the database. The integrator should verify that 227 // the name of the new stored procedure is unique amongst existing stored procedures. 228 SaveStoredProcedure(ctx *Context, spd StoredProcedureDetails) error 229 // DropStoredProcedure removes the StoredProcedureDetails with the matching name from the database. 230 DropStoredProcedure(ctx *Context, name string) error 231 } 232 233 // EventDatabase is a database that supports the creation and execution of events. The engine will 234 // handle execution logic for events. Integrators only need to store and retrieve EventDefinition. 235 type EventDatabase interface { 236 Database 237 // GetEvent returns the desired EventDefinition and if it exists in the database. 238 // All time values of EventDefinition needs to be converted into appropriate TZ. 239 GetEvent(ctx *Context, name string) (EventDefinition, bool, error) 240 // GetEvents returns all EventDefinition for the database, as well as an opaque token that is used to 241 // track if events need to be reloaded. This token is specific to an EventDatabase and is passed to 242 // NeedsToReloadEvents so that integrators can examine it and signal if events need to be reloaded. If 243 // integrators do not need to implement out-of-band event reloading, then they can simply return nil for 244 // the token. All time values of EventDefinition needs to be converted into appropriate TZ. 245 GetEvents(ctx *Context) (events []EventDefinition, token interface{}, err error) 246 // SaveEvent stores the given EventDefinition to the database. The integrator should verify that 247 // the name of the new event is unique amongst existing events. The time values are converted 248 // into UTC TZ for storage. It returns whether the event status is enabled. 249 SaveEvent(ctx *Context, ed EventDefinition) (bool, error) 250 // DropEvent removes the EventDefinition with the matching name from the database. 251 DropEvent(ctx *Context, name string) error 252 // UpdateEvent updates existing event stored in the database with the given EventDefinition 253 // with the updates. The original name event is required for renaming of an event. 254 // The time values are converted into UTC TZ for storage. It returns whether the event status is enabled. 255 UpdateEvent(ctx *Context, originalName string, ed EventDefinition) (bool, error) 256 // UpdateLastExecuted updated the lastExecuted metadata for the given event. 257 // The lastExecuted time is converted into UTC TZ for storage. 258 UpdateLastExecuted(ctx *Context, eventName string, lastExecuted time.Time) error 259 // NeedsToReloadEvents allows integrators to signal that out-of-band changes have modified an event (i.e. an 260 // event was modified without going through the SaveEvent or UpdateEvent methods in this interface), and that 261 // event definitions need to be reloaded. The event executor will periodically check to see if it needs to reload 262 // the events from a database by calling this method and if this method returns true, then the event executor will 263 // call the ReloadEvents method next to load in the new event definitions. The opaque token is the same token 264 // returned from the last call to GetEvents and integrators are free to use whatever underlying data they 265 // need to track whether an out-of-band event change has occurred. If integrators to do not support events 266 // changing out-of-band, then they can simply return false from this method. 267 NeedsToReloadEvents(ctx *Context, token interface{}) (bool, error) 268 } 269 270 // ViewDatabase is implemented by databases that persist view definitions 271 type ViewDatabase interface { 272 // CreateView persists the definition a view with the name and select statement given. If a view with that name 273 // already exists, should return ErrExistingView 274 CreateView(ctx *Context, name string, selectStatement, createViewStmt string) error 275 276 // DropView deletes the view named from persistent storage. If the view doesn't exist, should return 277 // ErrViewDoesNotExist 278 DropView(ctx *Context, name string) error 279 280 // GetViewDefinition returns the ViewDefinition of the view with the name given, or false if it doesn't exist. 281 GetViewDefinition(ctx *Context, viewName string) (ViewDefinition, bool, error) 282 283 // AllViews returns the definitions of all views in the database 284 AllViews(ctx *Context) ([]ViewDefinition, error) 285 } 286 287 // ViewDefinition is the named textual definition of a view 288 type ViewDefinition struct { 289 Name string 290 TextDefinition string 291 CreateViewStatement string 292 SqlMode string 293 } 294 295 // GetTableInsensitive implements a case-insensitive map lookup for tables keyed off of the table name. 296 // Looks for exact matches first. If no exact matches are found then any table matching the name case insensitively 297 // should be returned. If there is more than one table that matches a case-insensitive comparison the resolution 298 // strategy is not defined. 299 func GetTableInsensitive(tblName string, tables map[string]Table) (Table, bool) { 300 if tbl, ok := tables[tblName]; ok { 301 return tbl, true 302 } 303 304 lwrName := strings.ToLower(tblName) 305 306 for k, tbl := range tables { 307 if lwrName == strings.ToLower(k) { 308 return tbl, true 309 } 310 } 311 312 return nil, false 313 } 314 315 // GetTableNameInsensitive implements a case-insensitive search of a slice of table names. It looks for exact matches 316 // first. If no exact matches are found then any table matching the name case insensitively should be returned. If 317 // there is more than one table that matches a case-insensitive comparison the resolution strategy is not defined. 318 func GetTableNameInsensitive(tblName string, tableNames []string) (string, bool) { 319 for _, name := range tableNames { 320 if tblName == name { 321 return name, true 322 } 323 } 324 325 lwrName := strings.ToLower(tblName) 326 327 for _, name := range tableNames { 328 if lwrName == strings.ToLower(name) { 329 return name, true 330 } 331 } 332 333 return "", false 334 } 335 336 // DBTableIter iterates over all tables returned by db.GetTableNames() calling cb for each one until all tables have 337 // been processed, or an error is returned from the callback, or the cont flag is false when returned from the callback. 338 func DBTableIter(ctx *Context, db Database, cb func(Table) (cont bool, err error)) error { 339 names, err := db.GetTableNames(ctx) 340 341 if err != nil { 342 return err 343 } 344 345 for _, name := range names { 346 tbl, ok, err := db.GetTableInsensitive(ctx, name) 347 348 if err != nil { 349 return err 350 } else if !ok { 351 return ErrTableNotFound.New(name) 352 } 353 354 cont, err := cb(tbl) 355 356 if err != nil { 357 return err 358 } 359 360 if !cont { 361 break 362 } 363 } 364 365 return nil 366 } 367 368 // UnresolvedDatabase is a database which has not been resolved yet. 369 type UnresolvedDatabase string 370 371 var _ Database = UnresolvedDatabase("") 372 373 // Name returns the database name. 374 func (d UnresolvedDatabase) Name() string { 375 return string(d) 376 } 377 378 // Tables returns the tables in the database. 379 func (UnresolvedDatabase) Tables() map[string]Table { 380 return make(map[string]Table) 381 } 382 383 func (UnresolvedDatabase) GetTableInsensitive(ctx *Context, tblName string) (Table, bool, error) { 384 return nil, false, nil 385 } 386 387 func (UnresolvedDatabase) GetTableNames(ctx *Context) ([]string, error) { 388 return []string{}, nil 389 }