github.com/PDOK/gokoala@v0.50.6/internal/ogc/features/datasources/geopackage/asserts.go (about) 1 package geopackage 2 3 import ( 4 "errors" 5 "fmt" 6 "strings" 7 8 "github.com/PDOK/gokoala/config" 9 10 "github.com/jmoiron/sqlx" 11 ) 12 13 // assertIndexesExist asserts required indexes in the GeoPackage exists 14 func assertIndexesExist( 15 configuredCollections config.GeoSpatialCollections, 16 featureTableByCollectionID map[string]*featureTable, 17 db *sqlx.DB, fidColumn string) error { 18 19 // index needs to contain these columns in the given order 20 defaultSpatialBtreeColumns := strings.Join([]string{fidColumn, "minx", "maxx", "miny", "maxy"}, ",") 21 22 for collID, table := range featureTableByCollectionID { 23 if table == nil { 24 return errors.New("given table can't be nil") 25 } 26 for _, coll := range configuredCollections { 27 if coll.ID == collID && coll.Features != nil { 28 spatialBtreeColumns := defaultSpatialBtreeColumns 29 30 // assert temporal columns are indexed if configured 31 if coll.Metadata != nil && coll.Metadata.TemporalProperties != nil { 32 temporalBtreeColumns := strings.Join([]string{coll.Metadata.TemporalProperties.StartDate, coll.Metadata.TemporalProperties.EndDate}, ",") 33 spatialBtreeColumns = strings.Join([]string{defaultSpatialBtreeColumns, coll.Metadata.TemporalProperties.StartDate, coll.Metadata.TemporalProperties.EndDate}, ",") 34 if err := assertIndexExists(table.TableName, db, temporalBtreeColumns, true); err != nil { 35 return err 36 } 37 } 38 39 // assert spatial b-tree index exists, this index substitutes the r-tree when querying large bounding boxes 40 // if temporal columns are configured, they must be included in this index as well 41 if err := assertIndexExists(table.TableName, db, spatialBtreeColumns, true); err != nil { 42 return err 43 } 44 45 // assert the column for each property filter is indexed. 46 for _, propertyFilter := range coll.Features.Filters.Properties { 47 if err := assertIndexExists(table.TableName, db, propertyFilter.Name, false); err != nil && *propertyFilter.IndexRequired { 48 return fmt.Errorf("%w. To disable this check set 'indexRequired' to 'false'", err) 49 } 50 } 51 break 52 } 53 } 54 } 55 return nil 56 } 57 58 func assertIndexExists(tableName string, db *sqlx.DB, columns string, prefixMatch bool) error { 59 query := fmt.Sprintf(` 60 select group_concat(info.name) as indexed_columns 61 from pragma_index_list('%s') as list, 62 pragma_index_info(list.name) as info 63 group by list.name`, tableName) 64 65 rows, err := db.Queryx(query) 66 if err != nil { 67 return fmt.Errorf("failed to read indexes from table '%s'", tableName) 68 } 69 exists := false 70 for rows.Next() { 71 var indexedColumns string 72 _ = rows.Scan(&indexedColumns) 73 if columns == indexedColumns { 74 exists = true // index on expected columns 75 } else if prefixMatch && strings.HasPrefix(indexedColumns, columns) { 76 exists = true // index with expected prefix columns 77 } 78 } 79 defer rows.Close() 80 if !exists { 81 return fmt.Errorf("missing required index: no index exists on column(s) '%s' in table '%s'", 82 columns, tableName) 83 } 84 return nil 85 }