fireboltgosdk

package module
v1.11.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 11, 2025 License: Apache-2.0 Imports: 16 Imported by: 5

README

Firebolt GO SDK

Nightly code check Code quality checks Integration tests Coverage

Firebolt GO driver is an implementation of database/sql/driver.

Installation
go get github.com/firebolt-db/firebolt-go-sdk
DSN (Data source name)
Cloud instance

All information for the connection should be specified using the DSN string. The firebolt dsn string has the following format:

firebolt://[/database]?account_name=account_name&client_id=client_id&client_secret=client_secret[&engine=engine]
  • client_id - client id of the service account.
  • client_secret - client secret of the service account.
  • account_name - the name of Firebolt account to log in to.
  • database - (optional) the name of the database to connect to.
  • engine - (optional) the name of the engine to run SQL on.
Core instance

For the core instance, the DSN string has the following format:

firebolt://[/database]?url=core_instance_url
  • url - the URL of the core instance to connect to. It should contain the full URL, including schema. E.g. http://localhost:3473.
  • database - (optional) the name of the database to connect to.
Querying example

Here is an example of establishing a connection and executing a simple select query. For it to run successfully, you have to specify your credentials, and have a default engine up and running.

package main

import (
	"database/sql"
	"fmt"
	"log"

	// we need to import firebolt-go-sdk in order to register the driver
	_ "github.com/firebolt-db/firebolt-go-sdk"
)

func main() {

	// set your Firebolt credentials to construct a dsn string
	clientId := ""
	clientSecret := ""
	accountName := ""
	databaseName := ""
	engineName := ""
	dsn := fmt.Sprintf("firebolt:///%s?account_name=%s&client_id=%s&client_secret=%s&engine=%s", databaseName, accountName, clientId, clientSecret, engineName)

	// open a Firebolt connection
	db, err := sql.Open("firebolt", dsn)
	if err != nil {
		log.Fatalf("error during opening a driver: %v", err)
	}

	// create a table
	_, err = db.Query("CREATE TABLE test_table(id INT, value TEXT)")
	if err != nil {
		log.Fatalf("error during select query: %v", err)
	}

	// execute a parametrized insert (only ? placeholders are supported)
	_, err = db.Query("INSERT INTO test_table VALUES (?, ?)", 1, "my value")
	if err != nil {
		log.Fatalf("error during select query: %v", err)
	}

	// execute a simple select query
	rows, err := db.Query("SELECT id FROM test_table")
	if err != nil {
		log.Fatalf("error during select query: %v", err)
	}

	// iterate over the result
	defer func() {
		if err := rows.Close(); err != nil {
			log.Printf("error during rows.Close(): %v\n", err)
		}
	}()

	for rows.Next() {
		var id int
		if err := rows.Scan(&id); err != nil {
			log.Fatalf("error during scan: %v", err)
		}
		log.Print(id)
	}

	if err := rows.Err(); err != nil {
		log.Fatalf("error during rows iteration: %v\n", err)
	}
}
Streaming example

In order to stream the query result (and not store it in memory fully), you need to pass a special context with streaming enabled.

Warning: If you enable streaming the result, the query execution might finish successfully, but the actual error might be returned during the iteration over the rows.

Here is an example of how to do it:

package main

import (
	"context"
	"database/sql"
	"fmt"
	"log"

	// we need to import firebolt-go-sdk in order to register the driver
	_ "github.com/firebolt-db/firebolt-go-sdk"
	fireboltContext "github.com/firebolt-db/firebolt-go-sdk/context"
)

func main() {
	// set your Firebolt credentials to construct a dsn string
	clientId := ""
	clientSecret := ""
	accountName := ""
	databaseName := ""
	engineName := ""
	dsn := fmt.Sprintf("firebolt:///%s?account_name=%s&client_id=%s&client_secret=%s&engine=%s", databaseName, accountName, clientId, clientSecret, engineName)

	// open a Firebolt connection
	db, err := sql.Open("firebolt", dsn)
	if err != nil {
		log.Fatalf("error during opening a driver: %v", err)
	}

	// create a streaming context
	streamingCtx := fireboltContext.WithStreaming(context.Background())

	// execute a large select query
	rows, err := db.QueryContext(streamingCtx, "SELECT 'abc' FROM generate_series(1, 100000000)")
	if err != nil {
		log.Fatalf("error during select query: %v", err)
	}

	// iterating over the result is exactly the same as in the previous example
	defer func() {
		if err := rows.Close(); err != nil {
			log.Printf("error during rows.Close(): %v\n", err)
		}
	}()

	for rows.Next() {
		var data string
		if err := rows.Scan(&data); err != nil {
			log.Fatalf("error during scan: %v", err)
		}
		log.Print(data)
	}

	if err := rows.Err(); err != nil {
		log.Fatalf("error during rows iteration: %v\n", err)
	}
}
Errors in streaming

If you enable streaming the result, the query execution might finish successfully, but the actual error might be returned during the iteration over the rows.

Prepared statements

The SDK supports two types of prepared statements:

  1. Native - client-side prepared statements. Uses ? as a placeholder for parameters.
  2. FBNumeric - server-side prepared statements. Uses $i as a placeholder for parameters.

You can manually create a prepared statement using the Prepare method of the sql.DB object. You can also use the Exec method of the sql.DB object to execute a prepared statement directly without creating it first.

Example of client-side prepared statements
package main
import (
    "database/sql"
    "fmt"
    "log"

    // we need to import firebolt-go-sdk in order to register the driver
    _ "github.com/firebolt-db/firebolt-go-sdk"
)
func main() {
    // set your Firebolt credentials to construct a dsn string
    clientId := ""
    clientSecret := ""
    accountName := ""
    databaseName := ""
    engineName := ""
    dsn := fmt.Sprintf("firebolt:///%s?account_name=%s&client_id=%s&client_secret=%s&engine=%s", databaseName, accountName, clientId, clientSecret, engineName)

	// open a Firebolt connection
	db, err := sql.Open("firebolt", dsn)
	if err != nil {
		log.Fatalf("error during opening a driver: %v", err)
	}
	defer db.Close()
	log.Printf("successfully opened a driver with dsn: %s", dsn)

	// Initially created client-side prepared statement
	nativeStmt, err := db.Prepare("INSERT INTO test_table VALUES (?, ?)")
	if err != nil {
		log.Fatalf("error preparing native statement: %v", err)
	}
	defer nativeStmt.Close()
	log.Printf("successfully prepared a native statement")

	_, err = nativeStmt.Exec(1, "value")
	if err != nil {
		log.Fatalf("error executing native prepared statement: %v", err)
	}
	log.Printf("successfully executed native prepared statement with args: 1, \"value\"")

	// Executing the same statement directly using Exec
	_, err = db.Exec("INSERT INTO test_table VALUES (?, ?)", 2, "another value")
	if err != nil {
		log.Fatalf("error executing native prepared statement directly: %v", err)
	}
	log.Printf("successfully executed native prepared statement directly with args: 2, \"another_value\"")
}
Example of server-side prepared statements
package main

import (
	"context"
	"database/sql"
	"fmt"
	"log"

	// we need to import firebolt-go-sdk in order to register the driver
	_ "github.com/firebolt-db/firebolt-go-sdk"
	fireboltContext "github.com/firebolt-db/firebolt-go-sdk/context"
)

func main() {
	// set your Firebolt credentials to construct a dsn string
	clientId := ""
	clientSecret := ""
	accountName := ""
	databaseName := ""
	engineName := ""
	dsn := fmt.Sprintf("firebolt:///%s?account_name=%s&client_id=%s&client_secret=%s&engine=%s", databaseName, accountName, clientId, clientSecret, engineName)

	// open a Firebolt connection
	db, err := sql.Open("firebolt", dsn)
	if err != nil {
		log.Fatalf("error during opening a driver: %v", err)
	}
	defer db.Close()
	log.Printf("successfully opened a driver with dsn: %s", dsn)

	// We need to specify the prepared statement style in the context. Native is used by default.
	serverSideCtx := fireboltContext.WithPreparedStatementsStyle(context.Background(), fireboltContext.PreparedStatementsStyleFbNumeric)

	// Initially created server-side prepared statement
	fbnumericStmt, err := db.PrepareContext(serverSideCtx, "INSERT INTO test_table VALUES ($1, $2)")
	if err != nil {
		log.Fatalf("error preparing FBNumeric statement: %v", err)
	}
	defer fbnumericStmt.Close()
	log.Printf("successfully prepared a native statement")

	_, err = fbnumericStmt.Exec(1, "value")
	if err != nil {
		log.Fatalf("error executing FBNumeric prepared statement: %v", err)
	}
	log.Printf("successfully executed native prepared statement with args: 1, \"value\"")

	// Executing the same statement directly using Exec
	_, err = db.ExecContext(serverSideCtx, "INSERT INTO test_table VALUES ($1, $2)", 2, "another value")
	if err != nil {
		log.Fatalf("error executing FBNumeric prepared statement directly: %v", err)
	}
	log.Printf("successfully executed native prepared statement directly with args: 2, \"another_value\"")
}
Server-side asynchronous execution

The SDK supports server-side asynchronous execution of queries. This allows you to execute long-running queries on the background and retrieve the results later.

Note: the asynchronous execution does not support returning query results. It is useful for long-running queries that do not require immediate results, such as data loading or transformation tasks.

Here is an example of how to use server-side asynchronous execution:

package main

import (
    "context"
    "database/sql"
    "fmt"
    "log"
	
    firebolt "github.com/firebolt-db/firebolt-go-sdk"
)

func main() {
    // set your Firebolt credentials to construct a dsn string
    clientId := ""
    clientSecret := ""
    accountName := ""
    databaseName := ""
    engineName := ""
    dsn := fmt.Sprintf("firebolt:///%s?account_name=%s&client_id=%s&client_secret=%s&engine=%s", databaseName, accountName, clientId, clientSecret, engineName)

    // open a Firebolt connection
    db, err := sql.Open("firebolt", dsn)
    if err != nil {
        log.Fatalf("error during opening a driver: %v", err)
    }
    defer db.Close()
    log.Printf("successfully opened a driver with dsn: %s", dsn)
	
	
    token, err := firebolt.ExecAsync(db, "INSERT INTO test_table VALUES (?, ?)", 1, "async value")
    if err != nil {
        log.Fatalf("error executing asynchronous query: %v", err)
    }

	log.Print("started asynchronous query execution")
	
	for {
		running, err := firebolt.IsAsyncQueryRunning(db, token)
		if err != nil {
			log.Fatalf("Failed to check async query status: %v", err)
		}
		if !running {
			break
		}
    }
	
	success, err := firebolt.IsAsyncQuerySuccessful(db, token)
	if err != nil {
		log.Fatalf("Failed to check async query success: %v", err)
	}
	if success {
		log.Println("asynchronous query executed successfully")
	} else {
        log.Println("asynchronous query failed")
    }
}
Transaction support

The SDK supports transactions using the sql.Tx interface. Firebolt uses snapshot isolation for DML (INSERT, UPDATE, DELETE) statements and strict serializability for DDL (CREATE, ALTER, DROP) statements. Here is an example of how to use transactions:

package main

import (
    "context"
    "database/sql"
    "fmt"
    "log"
	
    firebolt "github.com/firebolt-db/firebolt-go-sdk"
)

func main() {
	// set your Firebolt credentials to construct a dsn string
	clientId := ""
	clientSecret := ""
	accountName := ""
	databaseName := ""
	engineName := ""
	dsn := fmt.Sprintf("firebolt:///%s?account_name=%s&client_id=%s&client_secret=%s&engine=%s", databaseName, accountName, clientId, clientSecret, engineName)

	// open a Firebolt connection
	db, err := sql.Open("firebolt", dsn)
	if err != nil {
		log.Fatalf("error during opening a driver: %v", err)
	}
	defer db.Close()
	log.Printf("successfully opened a driver with dsn: %s", dsn)
	
	// Start a transaction
	tx, err := db.Begin()
	if err != nil {
        log.Fatalf("error starting transaction: %v", err)
    }
	
	// Execute a sql query within the transaction
	_, err = tx.ExecContext(context.Background(), "INSERT INTO test_table VALUES (?, ?)", 1, "value")
	
	// Rollback the transaction if there was an error
	if err != nil {
        log.Printf("error executing query within transaction: %v", err)
        if rollbackErr := tx.Rollback(); rollbackErr != nil {
            log.Fatalf("error rolling back transaction: %v", rollbackErr)
        }
        return
    }
	
	// Commit the transaction if everything was successful
	if err = tx.Commit(); err != nil {
        log.Fatalf("error committing transaction: %v", err)
    } else {
        log.Println("transaction committed successfully")
    }
}
Error handling

The SDK provides specific error types that can be checked using Go's errors.Is() function. Here's how to handle different types of errors:

package main

import (
	"database/sql"
	"errors"
	"fmt"
	"log"

	_ "github.com/firebolt-db/firebolt-go-sdk"
	fireboltErrors "github.com/firebolt-db/firebolt-go-sdk/errors"
)

func main() {
	// set your Firebolt credentials to construct a dsn string
	clientId := ""
	clientSecret := ""
	accountName := ""
	databaseName := ""
	engineName := ""

	// Example 1: Invalid DSN format (using account-name instead of account_name)
	invalidDSN := fmt.Sprintf("firebolt:///%s?account-name=%s&client_id=%s&client_secret=%s&engine=%s",
		databaseName, accountName, clientId, clientSecret, engineName)
	db, err := sql.Open("firebolt", invalidDSN)
	if err != nil {
		if errors.Is(err, fireboltErrors.DSNParseError) {
			log.Println("Invalid DSN format, please update your DSN and try again")
		} else {
			log.Fatalf("Unexpected error type: %v", err)
		}
	}

	// Example 2: Invalid credentials
	invalidCredentialsDSN := fmt.Sprintf("firebolt:///%s?account_name=%s&client_id=%s&client_secret=%s&engine=%s",
		databaseName, accountName, "invalid", "invalid", engineName)
	db, err = sql.Open("firebolt", invalidCredentialsDSN)
	if err != nil {
		if errors.Is(err, fireboltErrors.AuthenticationError) {
			log.Println("Authentication error. Please check your credentials and try again")
		} else {
			log.Fatalf("Unexpected error type: %v", err)
		}
	}
	
	// Example 3: Invalid account name
    invalidAccountDSN := fmt.Sprintf("firebolt:///%s?account_name=%s&client_id=%s&client_secret=%s&engine=%s",
        databaseName, "invalid", clientId, clientSecret, engineName)
    db, err = sql.Open("firebolt", invalidAccountDSN)
	if err != nil {
        if errors.Is(err, fireboltErrors.InvalidAccountError) {
            log.Println("Invalid account name. Please check your account name and try again")
        } else {
            log.Fatalf("Unexpected error type: %v", err)
        }
    }
	
	// Example 4: Invalid SQL query
	dsn := fmt.Sprintf("firebolt:///%s?account_name=%s&client_id=%s&client_secret=%s&engine=%s",
		databaseName, accountName, clientId, clientSecret, engineName)
	db, err = sql.Open("firebolt", dsn)
	if err != nil {
		log.Fatalf("Failed to open connection: %v", err)
	}
	defer db.Close()

	// Try to execute an invalid SQL query
	_, err = db.Query("SELECT * FROM non_existent_table")
	if err != nil {
		if errors.Is(err, fireboltErrors.QueryExecutionError) {
			log.Printf("Error during query execution. Please fix your SQL query and try again")
		} else {
			log.Fatalf("Unexpected error type: %v", err)
		}
	}
}

The SDK provides the following error types:

  • DSNParseError: Provided DSN string format is invalid
  • AuthenticationError: Authentication failure
  • QueryExecutionError: SQL query execution error
  • AuthorizationError:A user doesn't have permission to perform an action
  • InvalidAccountError: Provided account name is invalid or no permissions to access the account

Each error type can be checked using errors.Is(err, errorType). This allows for specific error handling based on the type of error encountered.

Limitations

Although, all interfaces are available, not all of them are implemented or could be implemented:

  • driver.Result is a dummy implementation and doesn't return the real result values.
  • Named query parameters are not supported

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func CancelAsyncQuery added in v1.11.0

func CancelAsyncQuery(db *sql.DB, token rows.AsyncResult) error

func ExecAsync added in v1.11.0

func ExecAsync(db *sql.DB, query string, args ...any) (rows.AsyncResult, error)

func ExecAsyncContext added in v1.11.0

func ExecAsyncContext(ctx context.Context, db *sql.DB, query string, args ...any) (rows.AsyncResult, error)

func IsAsyncQueryRunning added in v1.11.0

func IsAsyncQueryRunning(db *sql.DB, token rows.AsyncResult) (bool, error)

func IsAsyncQuerySuccessful added in v1.11.0

func IsAsyncQuerySuccessful(db *sql.DB, token rows.AsyncResult) (bool, error)

func NoError added in v1.8.0

func NoError(option driverOption) driverOptionWithError

func ParseDSNString

func ParseDSNString(dsn string) (*types.FireboltSettings, error)

ParseDSNString parses a dsn in a format: firebolt://username:password@db_name[/engine_name][?account_name=organization] returns a settings object where all parsed values are populated returns an error if required fields couldn't be parsed or if after parsing some characters were left unparsed

func WithAccountID added in v1.6.1

func WithAccountID(accountID string) driverOption

WithAccountID defines account ID for the driver

func WithAccountName added in v1.8.0

func WithAccountName(accountName string) driverOptionWithError

WithAccountName defines account name for the driver

func WithClientParams added in v1.1.0

func WithClientParams(accountID string, token string, userAgent string) driverOption

WithClientParams defines client parameters for the driver

func WithDatabaseAndEngineName added in v1.8.0

func WithDatabaseAndEngineName(databaseName, engineName string) driverOptionWithError

WithDatabaseAndEngineName defines database name and engine name for the driver

func WithDatabaseName added in v1.1.0

func WithDatabaseName(databaseName string) driverOption

WithDatabaseName defines database name for the driver

func WithEngineUrl added in v1.1.0

func WithEngineUrl(engineUrl string) driverOption

WithEngineUrl defines engine url for the driver

func WithToken added in v1.6.1

func WithToken(token string) driverOption

WithToken defines token for the driver

func WithUserAgent added in v1.6.1

func WithUserAgent(userAgent string) driverOption

WithUserAgent defines user agent for the driver

Types

type FireboltConnector added in v1.1.0

type FireboltConnector struct {
	// contains filtered or unexported fields
}

FireboltConnector is an intermediate type between a Connection and a Driver which stores session data

func FireboltConnectorWithOptions added in v1.1.0

func FireboltConnectorWithOptions(opts ...driverOption) *FireboltConnector

FireboltConnectorWithOptions builds a custom connector

func FireboltConnectorWithOptionsWithErrors added in v1.8.0

func FireboltConnectorWithOptionsWithErrors(opts ...driverOptionWithError) (*FireboltConnector, error)

FireboltConnectorWithOptionsWithErrors builds a custom connector with error handling

func (*FireboltConnector) Connect added in v1.1.0

func (c *FireboltConnector) Connect(ctx context.Context) (driver.Conn, error)

Connect returns a connection to the database

func (*FireboltConnector) Driver added in v1.1.0

func (c *FireboltConnector) Driver() driver.Driver

Driver returns the underlying driver of the Connector

type FireboltDriver

type FireboltDriver struct {
	// contains filtered or unexported fields
}

func (*FireboltDriver) Open

func (d *FireboltDriver) Open(dsn string) (driver.Conn, error)

Open parses the dsn string, and if correct tries to establish a connection

func (*FireboltDriver) OpenConnector added in v1.1.0

func (d *FireboltDriver) OpenConnector(dsn string) (driver.Connector, error)

type QueryStatus added in v1.11.0

type QueryStatus string
const (
	QueryStatusRunning    QueryStatus = "RUNNING"
	QueryStatusSuccessful QueryStatus = "ENDED_SUCCESSFULLY"
	QueryStatusCanceled   QueryStatus = "CANCELED_EXECUTION"
)

type QueryStatusResponse added in v1.11.0

type QueryStatusResponse struct {
	// contains filtered or unexported fields
}

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL