| 226 | ReloadSnapshot() error |
| 227 | } |
| 228 | |
| 229 | type gormAPIStore struct { |
| 230 | store *configstore.ConfigStore |
| 231 | } |
| 232 | |
| 233 | func newGormAPIStore(store *configstore.ConfigStore) apiStore { |
| 234 | return &gormAPIStore{store: store} |
| 235 | } |
| 236 | |
| 237 | func (s *gormAPIStore) db() *gorm.DB { |
| 238 | return s.store.DB() |
| 239 | } |
| 240 | |
| 241 | func (s *gormAPIStore) ListOrgs() ([]configstore.Org, error) { |
| 242 | var orgs []configstore.Org |
| 243 | if err := s.db().Preload("Users").Preload("Warehouse").Preload("Teams").Find(&orgs).Error; err != nil { |
| 244 | return nil, err |
| 245 | } |
| 246 | return orgs, nil |
| 247 | } |
| 248 | |
| 249 | func (s *gormAPIStore) CreateOrg(org *configstore.Org, teamID int64, schemaName string) error { |
| 250 | org.Warehouse = nil |
| 251 | // One transaction: org row + its first team row (same contract as the |
| 252 | // provisioning path — an org cannot exist without a team). |
| 253 | return s.db().Transaction(func(tx *gorm.DB) error { |
| 254 | // Serialize name reuse with credential lifecycle operations. Grants do |
| 255 | // not have a foreign key, so clean up rows orphaned by older Duckgres |
| 256 | // versions before this name becomes an org again. |
| 257 | if err := configstore.LockOrgConnectionAdmissionTx(tx, org.Name); err != nil { |
| 258 | return err |