driver.go (110 lines of code) (raw):

/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to you 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 avatica provides an Apache Phoenix Query Server/Avatica driver for Go's database/sql package. # Quickstart Import the database/sql package along with the avatica driver. import "database/sql" import _ "github.com/apache/calcite-avatica-go/v5" db, err := sql.Open("avatica", "http://phoenix-query-server:8765") See https://calcite.apache.org/avatica/docs/go_client_reference.html for more details */ package avatica import ( "context" "database/sql" "database/sql/driver" "fmt" "net/http" "github.com/apache/calcite-avatica-go/v5/generic" "github.com/apache/calcite-avatica-go/v5/hsqldb" "github.com/apache/calcite-avatica-go/v5/message" "github.com/apache/calcite-avatica-go/v5/phoenix" "github.com/google/uuid" ) // Driver is exported to allow it to be used directly. type Driver struct{} // Connector implements the driver.Connector interface type Connector struct { Info map[string]string Client *http.Client dsn string } // NewConnector creates a new connector func NewConnector(dsn string) driver.Connector { return &Connector{ Info: make(map[string]string), dsn: dsn, } } func (c *Connector) Connect(ctx context.Context) (driver.Conn, error) { config, err := ParseDSN(c.dsn) if err != nil { return nil, fmt.Errorf("unable to open connection: %w", err) } // propagate user and password to connector info so that it's available in JDBC context for example if config.avaticaUser != "" { c.Info["user"] = config.avaticaUser } if config.avaticaPassword != "" { c.Info["password"] = config.avaticaPassword } connectionId, err := uuid.NewRandom() if err != nil { return nil, fmt.Errorf("error generating connection id: %w", err) } httpClient, err := NewHTTPClient(config.endpoint, c.Client, config) if err != nil { return nil, fmt.Errorf("unable to create HTTP client: %w", err) } conn := &conn{ connectionId: connectionId.String(), httpClient: httpClient, config: config, connectorInfo: c.Info, } err = registerConn(conn) if err != nil { return nil, err } response, err := conn.httpClient.post(ctx, message.DatabasePropertyRequest_builder{ ConnectionId: conn.connectionId, }.Build()) if err != nil { return nil, conn.avaticaErrorToResponseErrorOrError(err) } databasePropertyResponse := response.(*message.DatabasePropertyResponse) adapter := "" for _, property := range databasePropertyResponse.GetProps() { if property.GetKey().GetName() == "GET_DRIVER_NAME" { adapter = property.GetValue().GetStringValue() } } conn.adapter = getAdapter(adapter) return conn, nil } func registerConn(conn *conn) error { info := map[string]string{ "AutoCommit": "true", "Consistency": "8", } for k, v := range conn.connectorInfo { info[k] = v } // Open a connection to the server req := message.OpenConnectionRequest_builder{ ConnectionId: conn.connectionId, Info: info, }.Build() if conn.config.schema != "" { req.GetInfo()["schema"] = conn.config.schema } _, err := conn.httpClient.post(context.Background(), req) if err != nil { return conn.avaticaErrorToResponseErrorOrError(err) } return nil } // Driver returns the underlying driver func (c *Connector) Driver() driver.Driver { return &Driver{} } // Open a Connection to the server. // See https://github.com/apache/calcite-avatica-go#dsn for more information // on how the DSN is formatted. func (a *Driver) Open(dsn string) (driver.Conn, error) { return NewConnector(dsn).Connect(context.TODO()) } func getAdapter(e string) Adapter { switch e { case "HSQL Database Engine Driver": return hsqldb.Adapter{} case "PhoenixEmbeddedDriver": return phoenix.Adapter{} default: return generic.Adapter{} } } func init() { sql.Register("avatica", &Driver{}) }