Skip to content

Commit

Permalink
refactor(go/adbc/driver): add driver framework
Browse files Browse the repository at this point in the history
Fixes apache#996.
  • Loading branch information
lidavidm committed Sep 25, 2023
1 parent 940e60a commit ff16e64
Show file tree
Hide file tree
Showing 22 changed files with 1,562 additions and 1,223 deletions.
147 changes: 147 additions & 0 deletions go/adbc/driver/driverbase/database.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
// 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 driverbase

import (
"context"

"github.com/apache/arrow-adbc/go/adbc"
"github.com/apache/arrow/go/v13/arrow/memory"
"golang.org/x/exp/slog"
)

// DatabaseImpl is an interface that drivers implement to provide
// vendor-specific functionality.
type DatabaseImpl interface {
adbc.GetSetOptions
Base() *DatabaseImplBase
Open(context.Context) (adbc.Connection, error)
SetOptions(map[string]string) error
}

// DatabaseImplBase is a struct that provides default implementations of the
// DatabaseImpl interface. It is meant to be used as a composite struct for a
// driver's DatabaseImpl implementation.
type DatabaseImplBase struct {
Alloc memory.Allocator
ErrorHelper ErrorHelper
Logger *slog.Logger
}

// NewDatabaseImplBase instantiates DatabaseImplBase. name is the driver's
// name and is used to construct error messages. alloc is an Arrow allocator
// to use.
func NewDatabaseImplBase(driver *DriverImplBase) DatabaseImplBase {
return DatabaseImplBase{Alloc: driver.Alloc, ErrorHelper: driver.ErrorHelper, Logger: nilLogger()}
}

func (base *DatabaseImplBase) Base() *DatabaseImplBase {
return base
}

func (base *DatabaseImplBase) GetOption(key string) (string, error) {
return "", base.ErrorHelper.Errorf(adbc.StatusNotFound, "Unknown database option '%s'", key)
}

func (base *DatabaseImplBase) GetOptionBytes(key string) ([]byte, error) {
return nil, base.ErrorHelper.Errorf(adbc.StatusNotFound, "Unknown database option '%s'", key)
}

func (base *DatabaseImplBase) GetOptionDouble(key string) (float64, error) {
return 0, base.ErrorHelper.Errorf(adbc.StatusNotFound, "Unknown database option '%s'", key)
}

func (base *DatabaseImplBase) GetOptionInt(key string) (int64, error) {
return 0, base.ErrorHelper.Errorf(adbc.StatusNotFound, "Unknown database option '%s'", key)
}

func (base *DatabaseImplBase) SetOption(key string, val string) error {
return base.ErrorHelper.Errorf(adbc.StatusNotImplemented, "Unknown database option '%s'", key)
}

func (base *DatabaseImplBase) SetOptionBytes(key string, val []byte) error {
return base.ErrorHelper.Errorf(adbc.StatusNotImplemented, "Unknown database option '%s'", key)
}

func (base *DatabaseImplBase) SetOptionDouble(key string, val float64) error {
return base.ErrorHelper.Errorf(adbc.StatusNotImplemented, "Unknown database option '%s'", key)
}

func (base *DatabaseImplBase) SetOptionInt(key string, val int64) error {
return base.ErrorHelper.Errorf(adbc.StatusNotImplemented, "Unknown database option '%s'", key)
}

// database is the implementation of adbc.Database.
type database struct {
impl DatabaseImpl
}

// NewDatabase wraps a DatabaseImpl to create an adbc.Database.
func NewDatabase(impl DatabaseImpl) adbc.Database {
return &database{
impl: impl,
}
}

func (db *database) GetOption(key string) (string, error) {
return db.impl.GetOption(key)
}

func (db *database) GetOptionBytes(key string) ([]byte, error) {
return db.impl.GetOptionBytes(key)
}

func (db *database) GetOptionDouble(key string) (float64, error) {
return db.impl.GetOptionDouble(key)
}

func (db *database) GetOptionInt(key string) (int64, error) {
return db.impl.GetOptionInt(key)
}

func (db *database) SetOption(key string, val string) error {
return db.impl.SetOption(key, val)
}

func (db *database) SetOptionBytes(key string, val []byte) error {
return db.impl.SetOptionBytes(key, val)
}

func (db *database) SetOptionDouble(key string, val float64) error {
return db.impl.SetOptionDouble(key, val)
}

func (db *database) SetOptionInt(key string, val int64) error {
return db.impl.SetOptionInt(key, val)
}

func (db *database) Open(ctx context.Context) (adbc.Connection, error) {
return db.impl.Open(ctx)
}

func (db *database) SetLogger(logger *slog.Logger) {
if logger != nil {
db.impl.Base().Logger = logger
} else {
db.impl.Base().Logger = nilLogger()
}
}

func (db *database) SetOptions(opts map[string]string) error {
return db.impl.SetOptions(opts)
}
66 changes: 66 additions & 0 deletions go/adbc/driver/driverbase/driver.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// 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 driverbase provides a framework for implementing ADBC drivers in
// Go. It intends to reduce boilerplate for common functionality and managing
// state transitions.
package driverbase

import (
"github.com/apache/arrow-adbc/go/adbc"
"github.com/apache/arrow/go/v13/arrow/memory"
)

// DriverImpl is an interface that drivers implement to provide
// vendor-specific functionality.
type DriverImpl interface {
Base() *DriverImplBase
NewDatabase(opts map[string]string) (adbc.Database, error)
}

// DatabaseImplBase is a struct that provides default implementations of the
// DriverImpl interface. It is meant to be used as a composite struct for a
// driver's DriverImpl implementation.
type DriverImplBase struct {
Alloc memory.Allocator
ErrorHelper ErrorHelper
}

func NewDriverImplBase(name string, alloc memory.Allocator) DriverImplBase {
if alloc == nil {
alloc = memory.DefaultAllocator
}
return DriverImplBase{Alloc: alloc, ErrorHelper: ErrorHelper{DriverName: name}}
}

func (base *DriverImplBase) Base() *DriverImplBase {
return base
}

// driver is the actual implementation of adbc.Driver.
type driver struct {
impl DriverImpl
}

// NewDatabase wraps a DriverImpl to create an adbc.Driver.
func NewDriver(impl DriverImpl) adbc.Driver {
return &driver{impl}
}

func (drv *driver) NewDatabase(opts map[string]string) (adbc.Database, error) {
return drv.impl.NewDatabase(opts)
}
37 changes: 37 additions & 0 deletions go/adbc/driver/driverbase/error.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// 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 driverbase

import (
"fmt"

"github.com/apache/arrow-adbc/go/adbc"
)

// ErrorHelper helps format errors for ADBC drivers.
type ErrorHelper struct {
DriverName string
}

func (helper *ErrorHelper) Errorf(code adbc.Status, message string, format ...interface{}) error {
msg := fmt.Sprintf(message, format...)
return adbc.Error{
Code: code,
Msg: fmt.Sprintf("[%s] %s", helper.DriverName, msg),
}
}
32 changes: 32 additions & 0 deletions go/adbc/driver/driverbase/logging.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// 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 driverbase

import (
"os"

"golang.org/x/exp/slog"
)

func nilLogger() *slog.Logger {
h := slog.NewJSONHandler(os.Stderr, &slog.HandlerOptions{
AddSource: false,
Level: slog.LevelError,
})
return slog.New(h)
}
2 changes: 1 addition & 1 deletion go/adbc/driver/flightsql/flightsql_adbc_server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ func (suite *ServerBasedTests) DoSetupSuite(srv flightsql.Server, srvMiddleware
"uri": uri,
}
maps.Copy(args, dbArgs)
suite.db, err = (driver.Driver{}).NewDatabase(args)
suite.db, err = (driver.NewDriver(memory.DefaultAllocator)).NewDatabase(args)
suite.Require().NoError(err)
}

Expand Down
6 changes: 3 additions & 3 deletions go/adbc/driver/flightsql/flightsql_adbc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ func (s *FlightSQLQuirks) SetupDriver(t *testing.T) adbc.Driver {
_ = s.s.Serve()
}()

return driver.Driver{Alloc: s.mem}
return driver.NewDriver(s.mem)
}

func (s *FlightSQLQuirks) TearDownDriver(t *testing.T, _ adbc.Driver) {
Expand Down Expand Up @@ -902,7 +902,7 @@ func (suite *ConnectionTests) SetupSuite() {

var err error
suite.ctx = context.Background()
suite.Driver = driver.Driver{Alloc: suite.alloc}
suite.Driver = driver.NewDriver(suite.alloc)
suite.DB, err = suite.Driver.NewDatabase(map[string]string{
adbc.OptionKeyURI: "grpc+tcp://" + suite.server.Addr().String(),
})
Expand Down Expand Up @@ -995,7 +995,7 @@ func (suite *DomainSocketTests) SetupSuite() {
}()

suite.ctx = context.Background()
suite.Driver = driver.Driver{Alloc: suite.alloc}
suite.Driver = driver.NewDriver(suite.alloc)
suite.DB, err = suite.Driver.NewDatabase(map[string]string{
adbc.OptionKeyURI: "grpc+unix://" + listenSocket,
})
Expand Down
Loading

0 comments on commit ff16e64

Please sign in to comment.