| // Copyright 2015 Google Inc. All Rights Reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| package bigquery |
| |
| // TODO(mcgreevy): support dry-run mode when creating jobs. |
| |
| import ( |
| "fmt" |
| |
| "google.golang.org/api/option" |
| "google.golang.org/api/transport" |
| |
| "golang.org/x/net/context" |
| bq "google.golang.org/api/bigquery/v2" |
| ) |
| |
| const prodAddr = "https://www.googleapis.com/bigquery/v2/" |
| |
| // A Source is a source of data for the Copy function. |
| type Source interface { |
| implementsSource() |
| } |
| |
| // A Destination is a destination of data for the Copy function. |
| type Destination interface { |
| implementsDestination() |
| } |
| |
| // An Option is an optional argument to Copy. |
| type Option interface { |
| implementsOption() |
| } |
| |
| // A ReadSource is a source of data for the Read function. |
| type ReadSource interface { |
| implementsReadSource() |
| } |
| |
| // A ReadOption is an optional argument to Read. |
| type ReadOption interface { |
| customizeRead(conf *pagingConf) |
| } |
| |
| const Scope = "https://www.googleapis.com/auth/bigquery" |
| const userAgent = "gcloud-golang-bigquery/20160429" |
| |
| // Client may be used to perform BigQuery operations. |
| type Client struct { |
| service service |
| projectID string |
| } |
| |
| // NewClient constructs a new Client which can perform BigQuery operations. |
| // Operations performed via the client are billed to the specified GCP project. |
| func NewClient(ctx context.Context, projectID string, opts ...option.ClientOption) (*Client, error) { |
| o := []option.ClientOption{ |
| option.WithEndpoint(prodAddr), |
| option.WithScopes(Scope), |
| option.WithUserAgent(userAgent), |
| } |
| o = append(o, opts...) |
| httpClient, endpoint, err := transport.NewHTTPClient(ctx, o...) |
| if err != nil { |
| return nil, fmt.Errorf("dialing: %v", err) |
| } |
| |
| s, err := newBigqueryService(httpClient, endpoint) |
| if err != nil { |
| return nil, fmt.Errorf("constructing bigquery client: %v", err) |
| } |
| |
| c := &Client{ |
| service: s, |
| projectID: projectID, |
| } |
| return c, nil |
| } |
| |
| // Close closes any resources held by the client. |
| // Close should be called when the client is no longer needed. |
| // It need not be called at program exit. |
| func (c *Client) Close() error { |
| return nil |
| } |
| |
| // initJobProto creates and returns a bigquery Job proto. |
| // The proto is customized using any jobOptions in options. |
| // The list of Options is returned with the jobOptions removed. |
| func initJobProto(projectID string, options []Option) (*bq.Job, []Option) { |
| job := &bq.Job{} |
| |
| var other []Option |
| for _, opt := range options { |
| if o, ok := opt.(jobOption); ok { |
| o.customizeJob(job, projectID) |
| } else { |
| other = append(other, opt) |
| } |
| } |
| return job, other |
| } |
| |
| // Copy starts a BigQuery operation to copy data from a Source to a Destination. |
| func (c *Client) Copy(ctx context.Context, dst Destination, src Source, options ...Option) (*Job, error) { |
| switch dst := dst.(type) { |
| case *Table: |
| switch src := src.(type) { |
| case *GCSReference: |
| return c.load(ctx, dst, src, options) |
| case *Table: |
| return c.cp(ctx, dst, Tables{src}, options) |
| case Tables: |
| return c.cp(ctx, dst, src, options) |
| case *Query: |
| return c.query(ctx, dst, src, options) |
| } |
| case *GCSReference: |
| if src, ok := src.(*Table); ok { |
| return c.extract(ctx, dst, src, options) |
| } |
| } |
| return nil, fmt.Errorf("no Copy operation matches dst/src pair: dst: %T ; src: %T", dst, src) |
| } |
| |
| // Query creates a query with string q. You may optionally set |
| // DefaultProjectID and DefaultDatasetID on the returned query before using it. |
| func (c *Client) Query(q string) *Query { |
| return &Query{Q: q, client: c} |
| } |
| |
| // Read submits a query for execution and returns the results via an Iterator. |
| // |
| // Read uses a temporary table to hold the results of the query job. |
| // |
| // For more control over how a query is performed, don't use this method but |
| // instead pass the Query as a Source to Client.Copy, and call Read on the |
| // resulting Job. |
| func (q *Query) Read(ctx context.Context, options ...ReadOption) (*Iterator, error) { |
| dest := &Table{} |
| job, err := q.client.Copy(ctx, dest, q, WriteTruncate) |
| if err != nil { |
| return nil, err |
| } |
| return job.Read(ctx, options...) |
| } |
| |
| // executeQuery submits a query for execution and returns the results via an Iterator. |
| func (c *Client) executeQuery(ctx context.Context, q *Query, options ...ReadOption) (*Iterator, error) { |
| dest := &Table{} |
| job, err := c.Copy(ctx, dest, q, WriteTruncate) |
| if err != nil { |
| return nil, err |
| } |
| |
| return c.Read(ctx, job, options...) |
| } |
| |
| // Dataset creates a handle to a BigQuery dataset in the client's project. |
| func (c *Client) Dataset(id string) *Dataset { |
| return c.DatasetInProject(c.projectID, id) |
| } |
| |
| // DatasetInProject creates a handle to a BigQuery dataset in the specified project. |
| func (c *Client) DatasetInProject(projectID, datasetID string) *Dataset { |
| return &Dataset{ |
| projectID: projectID, |
| id: datasetID, |
| service: c.service, |
| } |
| } |