1// Copyright 2015 Google LLC 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 15package bigquery 16 17import ( 18 "context" 19 "errors" 20 "fmt" 21 "time" 22 23 "cloud.google.com/go/internal/optional" 24 "cloud.google.com/go/internal/trace" 25 bq "google.golang.org/api/bigquery/v2" 26 "google.golang.org/api/iterator" 27) 28 29// Dataset is a reference to a BigQuery dataset. 30type Dataset struct { 31 ProjectID string 32 DatasetID string 33 c *Client 34} 35 36// DatasetMetadata contains information about a BigQuery dataset. 37type DatasetMetadata struct { 38 // These fields can be set when creating a dataset. 39 Name string // The user-friendly name for this dataset. 40 Description string // The user-friendly description of this dataset. 41 Location string // The geo location of the dataset. 42 DefaultTableExpiration time.Duration // The default expiration time for new tables. 43 Labels map[string]string // User-provided labels. 44 Access []*AccessEntry // Access permissions. 45 DefaultEncryptionConfig *EncryptionConfig 46 47 // These fields are read-only. 48 CreationTime time.Time 49 LastModifiedTime time.Time // When the dataset or any of its tables were modified. 50 FullID string // The full dataset ID in the form projectID:datasetID. 51 52 // ETag is the ETag obtained when reading metadata. Pass it to Dataset.Update to 53 // ensure that the metadata hasn't changed since it was read. 54 ETag string 55} 56 57// DatasetMetadataToUpdate is used when updating a dataset's metadata. 58// Only non-nil fields will be updated. 59type DatasetMetadataToUpdate struct { 60 Description optional.String // The user-friendly description of this table. 61 Name optional.String // The user-friendly name for this dataset. 62 63 // DefaultTableExpiration is the default expiration time for new tables. 64 // If set to time.Duration(0), new tables never expire. 65 DefaultTableExpiration optional.Duration 66 67 // DefaultEncryptionConfig defines CMEK settings for new resources created 68 // in the dataset. 69 DefaultEncryptionConfig *EncryptionConfig 70 71 // The entire access list. It is not possible to replace individual entries. 72 Access []*AccessEntry 73 74 labelUpdater 75} 76 77// Dataset creates a handle to a BigQuery dataset in the client's project. 78func (c *Client) Dataset(id string) *Dataset { 79 return c.DatasetInProject(c.projectID, id) 80} 81 82// DatasetInProject creates a handle to a BigQuery dataset in the specified project. 83func (c *Client) DatasetInProject(projectID, datasetID string) *Dataset { 84 return &Dataset{ 85 ProjectID: projectID, 86 DatasetID: datasetID, 87 c: c, 88 } 89} 90 91// Create creates a dataset in the BigQuery service. An error will be returned if the 92// dataset already exists. Pass in a DatasetMetadata value to configure the dataset. 93func (d *Dataset) Create(ctx context.Context, md *DatasetMetadata) (err error) { 94 ctx = trace.StartSpan(ctx, "cloud.google.com/go/bigquery.Dataset.Create") 95 defer func() { trace.EndSpan(ctx, err) }() 96 97 ds, err := md.toBQ() 98 if err != nil { 99 return err 100 } 101 ds.DatasetReference = &bq.DatasetReference{DatasetId: d.DatasetID} 102 // Use Client.Location as a default. 103 if ds.Location == "" { 104 ds.Location = d.c.Location 105 } 106 call := d.c.bqs.Datasets.Insert(d.ProjectID, ds).Context(ctx) 107 setClientHeader(call.Header()) 108 _, err = call.Do() 109 return err 110} 111 112func (dm *DatasetMetadata) toBQ() (*bq.Dataset, error) { 113 ds := &bq.Dataset{} 114 if dm == nil { 115 return ds, nil 116 } 117 ds.FriendlyName = dm.Name 118 ds.Description = dm.Description 119 ds.Location = dm.Location 120 ds.DefaultTableExpirationMs = int64(dm.DefaultTableExpiration / time.Millisecond) 121 ds.Labels = dm.Labels 122 var err error 123 ds.Access, err = accessListToBQ(dm.Access) 124 if err != nil { 125 return nil, err 126 } 127 if !dm.CreationTime.IsZero() { 128 return nil, errors.New("bigquery: Dataset.CreationTime is not writable") 129 } 130 if !dm.LastModifiedTime.IsZero() { 131 return nil, errors.New("bigquery: Dataset.LastModifiedTime is not writable") 132 } 133 if dm.FullID != "" { 134 return nil, errors.New("bigquery: Dataset.FullID is not writable") 135 } 136 if dm.ETag != "" { 137 return nil, errors.New("bigquery: Dataset.ETag is not writable") 138 } 139 if dm.DefaultEncryptionConfig != nil { 140 ds.DefaultEncryptionConfiguration = dm.DefaultEncryptionConfig.toBQ() 141 } 142 return ds, nil 143} 144 145func accessListToBQ(a []*AccessEntry) ([]*bq.DatasetAccess, error) { 146 var q []*bq.DatasetAccess 147 for _, e := range a { 148 a, err := e.toBQ() 149 if err != nil { 150 return nil, err 151 } 152 q = append(q, a) 153 } 154 return q, nil 155} 156 157// Delete deletes the dataset. Delete will fail if the dataset is not empty. 158func (d *Dataset) Delete(ctx context.Context) (err error) { 159 return d.deleteInternal(ctx, false) 160} 161 162// DeleteWithContents deletes the dataset, as well as contained resources. 163func (d *Dataset) DeleteWithContents(ctx context.Context) (err error) { 164 return d.deleteInternal(ctx, true) 165} 166 167func (d *Dataset) deleteInternal(ctx context.Context, deleteContents bool) (err error) { 168 ctx = trace.StartSpan(ctx, "cloud.google.com/go/bigquery.Dataset.Delete") 169 defer func() { trace.EndSpan(ctx, err) }() 170 171 call := d.c.bqs.Datasets.Delete(d.ProjectID, d.DatasetID).Context(ctx).DeleteContents(deleteContents) 172 setClientHeader(call.Header()) 173 return call.Do() 174} 175 176// Metadata fetches the metadata for the dataset. 177func (d *Dataset) Metadata(ctx context.Context) (md *DatasetMetadata, err error) { 178 ctx = trace.StartSpan(ctx, "cloud.google.com/go/bigquery.Dataset.Metadata") 179 defer func() { trace.EndSpan(ctx, err) }() 180 181 call := d.c.bqs.Datasets.Get(d.ProjectID, d.DatasetID).Context(ctx) 182 setClientHeader(call.Header()) 183 var ds *bq.Dataset 184 if err := runWithRetry(ctx, func() (err error) { 185 ds, err = call.Do() 186 return err 187 }); err != nil { 188 return nil, err 189 } 190 return bqToDatasetMetadata(ds) 191} 192 193func bqToDatasetMetadata(d *bq.Dataset) (*DatasetMetadata, error) { 194 dm := &DatasetMetadata{ 195 CreationTime: unixMillisToTime(d.CreationTime), 196 LastModifiedTime: unixMillisToTime(d.LastModifiedTime), 197 DefaultTableExpiration: time.Duration(d.DefaultTableExpirationMs) * time.Millisecond, 198 DefaultEncryptionConfig: bqToEncryptionConfig(d.DefaultEncryptionConfiguration), 199 Description: d.Description, 200 Name: d.FriendlyName, 201 FullID: d.Id, 202 Location: d.Location, 203 Labels: d.Labels, 204 ETag: d.Etag, 205 } 206 for _, a := range d.Access { 207 e, err := bqToAccessEntry(a, nil) 208 if err != nil { 209 return nil, err 210 } 211 dm.Access = append(dm.Access, e) 212 } 213 return dm, nil 214} 215 216// Update modifies specific Dataset metadata fields. 217// To perform a read-modify-write that protects against intervening reads, 218// set the etag argument to the DatasetMetadata.ETag field from the read. 219// Pass the empty string for etag for a "blind write" that will always succeed. 220func (d *Dataset) Update(ctx context.Context, dm DatasetMetadataToUpdate, etag string) (md *DatasetMetadata, err error) { 221 ctx = trace.StartSpan(ctx, "cloud.google.com/go/bigquery.Dataset.Update") 222 defer func() { trace.EndSpan(ctx, err) }() 223 224 ds, err := dm.toBQ() 225 if err != nil { 226 return nil, err 227 } 228 call := d.c.bqs.Datasets.Patch(d.ProjectID, d.DatasetID, ds).Context(ctx) 229 setClientHeader(call.Header()) 230 if etag != "" { 231 call.Header().Set("If-Match", etag) 232 } 233 var ds2 *bq.Dataset 234 if err := runWithRetry(ctx, func() (err error) { 235 ds2, err = call.Do() 236 return err 237 }); err != nil { 238 return nil, err 239 } 240 return bqToDatasetMetadata(ds2) 241} 242 243func (dm *DatasetMetadataToUpdate) toBQ() (*bq.Dataset, error) { 244 ds := &bq.Dataset{} 245 forceSend := func(field string) { 246 ds.ForceSendFields = append(ds.ForceSendFields, field) 247 } 248 249 if dm.Description != nil { 250 ds.Description = optional.ToString(dm.Description) 251 forceSend("Description") 252 } 253 if dm.Name != nil { 254 ds.FriendlyName = optional.ToString(dm.Name) 255 forceSend("FriendlyName") 256 } 257 if dm.DefaultTableExpiration != nil { 258 dur := optional.ToDuration(dm.DefaultTableExpiration) 259 if dur == 0 { 260 // Send a null to delete the field. 261 ds.NullFields = append(ds.NullFields, "DefaultTableExpirationMs") 262 } else { 263 ds.DefaultTableExpirationMs = int64(dur / time.Millisecond) 264 } 265 } 266 if dm.DefaultEncryptionConfig != nil { 267 ds.DefaultEncryptionConfiguration = dm.DefaultEncryptionConfig.toBQ() 268 ds.DefaultEncryptionConfiguration.ForceSendFields = []string{"KmsKeyName"} 269 } 270 if dm.Access != nil { 271 var err error 272 ds.Access, err = accessListToBQ(dm.Access) 273 if err != nil { 274 return nil, err 275 } 276 if len(ds.Access) == 0 { 277 ds.NullFields = append(ds.NullFields, "Access") 278 } 279 } 280 labels, forces, nulls := dm.update() 281 ds.Labels = labels 282 ds.ForceSendFields = append(ds.ForceSendFields, forces...) 283 ds.NullFields = append(ds.NullFields, nulls...) 284 return ds, nil 285} 286 287// Table creates a handle to a BigQuery table in the dataset. 288// To determine if a table exists, call Table.Metadata. 289// If the table does not already exist, use Table.Create to create it. 290func (d *Dataset) Table(tableID string) *Table { 291 return &Table{ProjectID: d.ProjectID, DatasetID: d.DatasetID, TableID: tableID, c: d.c} 292} 293 294// Tables returns an iterator over the tables in the Dataset. 295func (d *Dataset) Tables(ctx context.Context) *TableIterator { 296 it := &TableIterator{ 297 ctx: ctx, 298 dataset: d, 299 } 300 it.pageInfo, it.nextFunc = iterator.NewPageInfo( 301 it.fetch, 302 func() int { return len(it.tables) }, 303 func() interface{} { b := it.tables; it.tables = nil; return b }) 304 return it 305} 306 307// A TableIterator is an iterator over Tables. 308type TableIterator struct { 309 ctx context.Context 310 dataset *Dataset 311 tables []*Table 312 pageInfo *iterator.PageInfo 313 nextFunc func() error 314} 315 316// Next returns the next result. Its second return value is Done if there are 317// no more results. Once Next returns Done, all subsequent calls will return 318// Done. 319func (it *TableIterator) Next() (*Table, error) { 320 if err := it.nextFunc(); err != nil { 321 return nil, err 322 } 323 t := it.tables[0] 324 it.tables = it.tables[1:] 325 return t, nil 326} 327 328// PageInfo supports pagination. See the google.golang.org/api/iterator package for details. 329func (it *TableIterator) PageInfo() *iterator.PageInfo { return it.pageInfo } 330 331// listTables exists to aid testing. 332var listTables = func(it *TableIterator, pageSize int, pageToken string) (*bq.TableList, error) { 333 call := it.dataset.c.bqs.Tables.List(it.dataset.ProjectID, it.dataset.DatasetID). 334 PageToken(pageToken). 335 Context(it.ctx) 336 setClientHeader(call.Header()) 337 if pageSize > 0 { 338 call.MaxResults(int64(pageSize)) 339 } 340 var res *bq.TableList 341 err := runWithRetry(it.ctx, func() (err error) { 342 res, err = call.Do() 343 return err 344 }) 345 return res, err 346} 347 348func (it *TableIterator) fetch(pageSize int, pageToken string) (string, error) { 349 res, err := listTables(it, pageSize, pageToken) 350 if err != nil { 351 return "", err 352 } 353 for _, t := range res.Tables { 354 it.tables = append(it.tables, bqToTable(t.TableReference, it.dataset.c)) 355 } 356 return res.NextPageToken, nil 357} 358 359func bqToTable(tr *bq.TableReference, c *Client) *Table { 360 if tr == nil { 361 return nil 362 } 363 return &Table{ 364 ProjectID: tr.ProjectId, 365 DatasetID: tr.DatasetId, 366 TableID: tr.TableId, 367 c: c, 368 } 369} 370 371// Model creates a handle to a BigQuery model in the dataset. 372// To determine if a model exists, call Model.Metadata. 373// If the model does not already exist, you can create it via execution 374// of a CREATE MODEL query. 375func (d *Dataset) Model(modelID string) *Model { 376 return &Model{ProjectID: d.ProjectID, DatasetID: d.DatasetID, ModelID: modelID, c: d.c} 377} 378 379// Models returns an iterator over the models in the Dataset. 380func (d *Dataset) Models(ctx context.Context) *ModelIterator { 381 it := &ModelIterator{ 382 ctx: ctx, 383 dataset: d, 384 } 385 it.pageInfo, it.nextFunc = iterator.NewPageInfo( 386 it.fetch, 387 func() int { return len(it.models) }, 388 func() interface{} { b := it.models; it.models = nil; return b }) 389 return it 390} 391 392// A ModelIterator is an iterator over Models. 393type ModelIterator struct { 394 ctx context.Context 395 dataset *Dataset 396 models []*Model 397 pageInfo *iterator.PageInfo 398 nextFunc func() error 399} 400 401// Next returns the next result. Its second return value is Done if there are 402// no more results. Once Next returns Done, all subsequent calls will return 403// Done. 404func (it *ModelIterator) Next() (*Model, error) { 405 if err := it.nextFunc(); err != nil { 406 return nil, err 407 } 408 t := it.models[0] 409 it.models = it.models[1:] 410 return t, nil 411} 412 413// PageInfo supports pagination. See the google.golang.org/api/iterator package for details. 414func (it *ModelIterator) PageInfo() *iterator.PageInfo { return it.pageInfo } 415 416// listTables exists to aid testing. 417var listModels = func(it *ModelIterator, pageSize int, pageToken string) (*bq.ListModelsResponse, error) { 418 call := it.dataset.c.bqs.Models.List(it.dataset.ProjectID, it.dataset.DatasetID). 419 PageToken(pageToken). 420 Context(it.ctx) 421 setClientHeader(call.Header()) 422 if pageSize > 0 { 423 call.MaxResults(int64(pageSize)) 424 } 425 var res *bq.ListModelsResponse 426 err := runWithRetry(it.ctx, func() (err error) { 427 res, err = call.Do() 428 return err 429 }) 430 return res, err 431} 432 433func (it *ModelIterator) fetch(pageSize int, pageToken string) (string, error) { 434 res, err := listModels(it, pageSize, pageToken) 435 if err != nil { 436 return "", err 437 } 438 for _, t := range res.Models { 439 it.models = append(it.models, bqToModel(t.ModelReference, it.dataset.c)) 440 } 441 return res.NextPageToken, nil 442} 443 444func bqToModel(mr *bq.ModelReference, c *Client) *Model { 445 if mr == nil { 446 return nil 447 } 448 return &Model{ 449 ProjectID: mr.ProjectId, 450 DatasetID: mr.DatasetId, 451 ModelID: mr.ModelId, 452 c: c, 453 } 454} 455 456// Routine creates a handle to a BigQuery routine in the dataset. 457// To determine if a routine exists, call Routine.Metadata. 458func (d *Dataset) Routine(routineID string) *Routine { 459 return &Routine{ 460 ProjectID: d.ProjectID, 461 DatasetID: d.DatasetID, 462 RoutineID: routineID, 463 c: d.c} 464} 465 466// Routines returns an iterator over the routines in the Dataset. 467func (d *Dataset) Routines(ctx context.Context) *RoutineIterator { 468 it := &RoutineIterator{ 469 ctx: ctx, 470 dataset: d, 471 } 472 it.pageInfo, it.nextFunc = iterator.NewPageInfo( 473 it.fetch, 474 func() int { return len(it.routines) }, 475 func() interface{} { b := it.routines; it.routines = nil; return b }) 476 return it 477} 478 479// A RoutineIterator is an iterator over Routines. 480type RoutineIterator struct { 481 ctx context.Context 482 dataset *Dataset 483 routines []*Routine 484 pageInfo *iterator.PageInfo 485 nextFunc func() error 486} 487 488// Next returns the next result. Its second return value is Done if there are 489// no more results. Once Next returns Done, all subsequent calls will return 490// Done. 491func (it *RoutineIterator) Next() (*Routine, error) { 492 if err := it.nextFunc(); err != nil { 493 return nil, err 494 } 495 t := it.routines[0] 496 it.routines = it.routines[1:] 497 return t, nil 498} 499 500// PageInfo supports pagination. See the google.golang.org/api/iterator package for details. 501func (it *RoutineIterator) PageInfo() *iterator.PageInfo { return it.pageInfo } 502 503// listRoutines exists to aid testing. 504var listRoutines = func(it *RoutineIterator, pageSize int, pageToken string) (*bq.ListRoutinesResponse, error) { 505 call := it.dataset.c.bqs.Routines.List(it.dataset.ProjectID, it.dataset.DatasetID). 506 PageToken(pageToken). 507 Context(it.ctx) 508 setClientHeader(call.Header()) 509 if pageSize > 0 { 510 call.MaxResults(int64(pageSize)) 511 } 512 var res *bq.ListRoutinesResponse 513 err := runWithRetry(it.ctx, func() (err error) { 514 res, err = call.Do() 515 return err 516 }) 517 return res, err 518} 519 520func (it *RoutineIterator) fetch(pageSize int, pageToken string) (string, error) { 521 res, err := listRoutines(it, pageSize, pageToken) 522 if err != nil { 523 return "", err 524 } 525 for _, t := range res.Routines { 526 it.routines = append(it.routines, bqToRoutine(t.RoutineReference, it.dataset.c)) 527 } 528 return res.NextPageToken, nil 529} 530 531func bqToRoutine(mr *bq.RoutineReference, c *Client) *Routine { 532 if mr == nil { 533 return nil 534 } 535 return &Routine{ 536 ProjectID: mr.ProjectId, 537 DatasetID: mr.DatasetId, 538 RoutineID: mr.RoutineId, 539 c: c, 540 } 541} 542 543// Datasets returns an iterator over the datasets in a project. 544// The Client's project is used by default, but that can be 545// changed by setting ProjectID on the returned iterator before calling Next. 546func (c *Client) Datasets(ctx context.Context) *DatasetIterator { 547 return c.DatasetsInProject(ctx, c.projectID) 548} 549 550// DatasetsInProject returns an iterator over the datasets in the provided project. 551// 552// Deprecated: call Client.Datasets, then set ProjectID on the returned iterator. 553func (c *Client) DatasetsInProject(ctx context.Context, projectID string) *DatasetIterator { 554 it := &DatasetIterator{ 555 ctx: ctx, 556 c: c, 557 ProjectID: projectID, 558 } 559 it.pageInfo, it.nextFunc = iterator.NewPageInfo( 560 it.fetch, 561 func() int { return len(it.items) }, 562 func() interface{} { b := it.items; it.items = nil; return b }) 563 return it 564} 565 566// DatasetIterator iterates over the datasets in a project. 567type DatasetIterator struct { 568 // ListHidden causes hidden datasets to be listed when set to true. 569 // Set before the first call to Next. 570 ListHidden bool 571 572 // Filter restricts the datasets returned by label. The filter syntax is described in 573 // https://cloud.google.com/bigquery/docs/labeling-datasets#filtering_datasets_using_labels 574 // Set before the first call to Next. 575 Filter string 576 577 // The project ID of the listed datasets. 578 // Set before the first call to Next. 579 ProjectID string 580 581 ctx context.Context 582 c *Client 583 pageInfo *iterator.PageInfo 584 nextFunc func() error 585 items []*Dataset 586} 587 588// PageInfo supports pagination. See the google.golang.org/api/iterator package for details. 589func (it *DatasetIterator) PageInfo() *iterator.PageInfo { return it.pageInfo } 590 591// Next returns the next Dataset. Its second return value is iterator.Done if 592// there are no more results. Once Next returns Done, all subsequent calls will 593// return Done. 594func (it *DatasetIterator) Next() (*Dataset, error) { 595 if err := it.nextFunc(); err != nil { 596 return nil, err 597 } 598 item := it.items[0] 599 it.items = it.items[1:] 600 return item, nil 601} 602 603// for testing 604var listDatasets = func(it *DatasetIterator, pageSize int, pageToken string) (*bq.DatasetList, error) { 605 call := it.c.bqs.Datasets.List(it.ProjectID). 606 Context(it.ctx). 607 PageToken(pageToken). 608 All(it.ListHidden) 609 setClientHeader(call.Header()) 610 if pageSize > 0 { 611 call.MaxResults(int64(pageSize)) 612 } 613 if it.Filter != "" { 614 call.Filter(it.Filter) 615 } 616 var res *bq.DatasetList 617 err := runWithRetry(it.ctx, func() (err error) { 618 res, err = call.Do() 619 return err 620 }) 621 return res, err 622} 623 624func (it *DatasetIterator) fetch(pageSize int, pageToken string) (string, error) { 625 res, err := listDatasets(it, pageSize, pageToken) 626 if err != nil { 627 return "", err 628 } 629 for _, d := range res.Datasets { 630 it.items = append(it.items, &Dataset{ 631 ProjectID: d.DatasetReference.ProjectId, 632 DatasetID: d.DatasetReference.DatasetId, 633 c: it.c, 634 }) 635 } 636 return res.NextPageToken, nil 637} 638 639// An AccessEntry describes the permissions that an entity has on a dataset. 640type AccessEntry struct { 641 Role AccessRole // The role of the entity 642 EntityType EntityType // The type of entity 643 Entity string // The entity (individual or group) granted access 644 View *Table // The view granted access (EntityType must be ViewEntity) 645} 646 647// AccessRole is the level of access to grant to a dataset. 648type AccessRole string 649 650const ( 651 // OwnerRole is the OWNER AccessRole. 652 OwnerRole AccessRole = "OWNER" 653 // ReaderRole is the READER AccessRole. 654 ReaderRole AccessRole = "READER" 655 // WriterRole is the WRITER AccessRole. 656 WriterRole AccessRole = "WRITER" 657) 658 659// EntityType is the type of entity in an AccessEntry. 660type EntityType int 661 662const ( 663 // DomainEntity is a domain (e.g. "example.com"). 664 DomainEntity EntityType = iota + 1 665 666 // GroupEmailEntity is an email address of a Google Group. 667 GroupEmailEntity 668 669 // UserEmailEntity is an email address of an individual user. 670 UserEmailEntity 671 672 // SpecialGroupEntity is a special group: one of projectOwners, projectReaders, projectWriters or 673 // allAuthenticatedUsers. 674 SpecialGroupEntity 675 676 // ViewEntity is a BigQuery view. 677 ViewEntity 678 679 // IAMMemberEntity represents entities present in IAM but not represented using 680 // the other entity types. 681 IAMMemberEntity 682) 683 684func (e *AccessEntry) toBQ() (*bq.DatasetAccess, error) { 685 q := &bq.DatasetAccess{Role: string(e.Role)} 686 switch e.EntityType { 687 case DomainEntity: 688 q.Domain = e.Entity 689 case GroupEmailEntity: 690 q.GroupByEmail = e.Entity 691 case UserEmailEntity: 692 q.UserByEmail = e.Entity 693 case SpecialGroupEntity: 694 q.SpecialGroup = e.Entity 695 case ViewEntity: 696 q.View = e.View.toBQ() 697 case IAMMemberEntity: 698 q.IamMember = e.Entity 699 default: 700 return nil, fmt.Errorf("bigquery: unknown entity type %d", e.EntityType) 701 } 702 return q, nil 703} 704 705func bqToAccessEntry(q *bq.DatasetAccess, c *Client) (*AccessEntry, error) { 706 e := &AccessEntry{Role: AccessRole(q.Role)} 707 switch { 708 case q.Domain != "": 709 e.Entity = q.Domain 710 e.EntityType = DomainEntity 711 case q.GroupByEmail != "": 712 e.Entity = q.GroupByEmail 713 e.EntityType = GroupEmailEntity 714 case q.UserByEmail != "": 715 e.Entity = q.UserByEmail 716 e.EntityType = UserEmailEntity 717 case q.SpecialGroup != "": 718 e.Entity = q.SpecialGroup 719 e.EntityType = SpecialGroupEntity 720 case q.View != nil: 721 e.View = c.DatasetInProject(q.View.ProjectId, q.View.DatasetId).Table(q.View.TableId) 722 e.EntityType = ViewEntity 723 case q.IamMember != "": 724 e.Entity = q.IamMember 725 e.EntityType = IAMMemberEntity 726 default: 727 return nil, errors.New("bigquery: invalid access value") 728 } 729 return e, nil 730} 731