intersystems

package module
v0.3.0 Latest Latest
Warning

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

Go to latest
Published: Jul 7, 2026 License: Apache-2.0 Imports: 11 Imported by: 1

README

go-irisnative

A Golang driver for InterSystems IRIS that implements database/sql.

Project status: alpha. API may change. Feedback and PRs welcome.

This project does not contain or distribute any proprietary source code from InterSystems. All observed behaviors or configuration techniques are derived from publicly available information, empirical testing, and independent analysis intended solely to improve interoperability within the IRIS ecosystem.


Installation

# replace the module path with the final repo path when published
go get github.com/caretdev/go-irisnative

Register the driver by importing it for side‑effects:

import (
  "database/sql"
  _ "github.com/caretdev/go-irisnative" // registers driver as "iris"
)

DSN formats

The driver accepts a URL-style DSN (recommended) or key=value pairs.

URL style

iris://user:password@host:1972/NAMESPACE?max_rows=200&query_timeout=30
  • host — IRIS hostname or IP
  • 1972 — superserver port (default)
  • Namespace — IRIS namespace (e.g., USER)

DSN parameters

  • max_rows — Maximum number of rows to fetch in a single request (default: 0 = no limit)
  • query_timeout — Query timeout in seconds (default: 0 = no timeout)

Quick start (database/sql)

package main

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

	_ "github.com/caretdev/go-irisnative"
)

func main() {
	dsn := "iris://_SYSTEM:SYS@localhost:1972/USER"
	db, err := sql.Open("iris", dsn)
	if err != nil { log.Fatal(err) }
	defer db.Close()

	// Connection pool tuning
	db.SetMaxOpenConns(10)
	db.SetMaxIdleConns(5)
	db.SetConnMaxLifetime(30 * time.Minute)

	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()

	_, err = db.ExecContext(ctx, `DROP TABLE IF EXISTS demo_person`)
	if err != nil { log.Fatal("drop table:", err) }

	// 1) Create a table (id INT PRIMARY KEY, name VARCHAR(80))
	_, err = db.ExecContext(ctx, `CREATE TABLE IF NOT EXISTS demo_person (
		id INT PRIMARY KEY,
		name VARCHAR(80) NOT NULL,
		created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
	)`)
	if err != nil { log.Fatal("create table:", err) }

	// 2) Insert with placeholders
	res, err := db.ExecContext(ctx, `INSERT INTO demo_person(id, name) VALUES(?, ?)`, 1, "Alice")
	if err != nil { log.Fatal("insert:", err) }
	if n, _ := res.RowsAffected(); n > 0 { fmt.Println("inserted:", n) }

	// 3) Query rows
	rows, err := db.QueryContext(ctx, `SELECT id, name, created_at FROM demo_person ORDER BY id`)
	if err != nil { log.Fatal("query:", err) }
	defer rows.Close()

	for rows.Next() {
		var (
			id int
			name string
			createdAt time.Time
		)
		if err := rows.Scan(&id, &name, &createdAt); err != nil { log.Fatal(err) }
		fmt.Printf("row: id=%d name=%s created_at=%s\n", id, name, createdAt.Format(time.RFC3339))
	}
	if err := rows.Err(); err != nil { log.Fatal(err) }

	// 4) Prepared statement
	stmt, err := db.PrepareContext(ctx, `UPDATE demo_person SET name=? WHERE id=?`)
	if err != nil { log.Fatal("prepare:", err) }
	defer stmt.Close()
	if _, err := stmt.ExecContext(ctx, "Alice Updated", 1); err != nil { log.Fatal("update:", err) }

	// 5) Transaction example
	tx, err := db.BeginTx(ctx, &sql.TxOptions{Isolation: sql.LevelReadCommitted})
	if err != nil { log.Fatal("begin tx:", err) }
	if _, err := tx.ExecContext(ctx, `INSERT INTO demo_person(id, name) VALUES(?, ?)`, 2, "Bob"); err != nil {
		tx.Rollback()
		log.Fatal("tx insert:", err)
	}
	if err := tx.Commit(); err != nil { log.Fatal("commit:", err) }
}
Query single value helper
var count int
if err := db.QueryRowContext(ctx, `SELECT COUNT(*) FROM demo_person`).Scan(&count); err != nil {
	log.Fatal(err)
}
fmt.Println("count=", count)

Using with sqlx

sqlx adds nice helpers over database/sql like struct scanning and named queries.

go get github.com/jmoiron/sqlx
package main

import (
	"context"
	"fmt"
	"log"
	"time"

	_ "github.com/caretdev/go-irisnative" // driver
	"github.com/jmoiron/sqlx"
)

type Person struct {
	ID        int       `db:"id"`
	Name      string    `db:"name"`
	CreatedAt time.Time `db:"created_at"`
}

func create(ctx context.Context, db *sqlx.DB) {
	drop(ctx, db)
	_, err := db.ExecContext(ctx, `CREATE TABLE IF NOT EXISTS demo_person (
		id INT PRIMARY KEY,
		name VARCHAR(80) NOT NULL,
		created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
	)`)
	if err != nil {
		panic(err)
	}
}

func drop(ctx context.Context, db *sqlx.DB) {
	_, err := db.ExecContext(ctx, `DROP TABLE IF EXISTS demo_person`)
	if err != nil {
		panic(err)
	}
}

func main() {
	ctx := context.Background()
	dsn := "iris://_SYSTEM:SYS@localhost:1972/USER"
	db := sqlx.MustConnect("iris", dsn)
	defer db.Close()

	create(ctx, db)
	defer drop(ctx, db)

	// Struct-based insert with NamedExec
	p := Person{ID: 3, Name: "Carol"}
	_, err := db.NamedExecContext(ctx,
		`INSERT INTO demo_person(id, name) VALUES(:id, :name)`, p,
	)
	if err != nil {
		log.Fatal("named insert:", err)
	}

	// Select into slice of structs
	var people []Person
	if err := db.SelectContext(ctx, &people, `SELECT id, name, created_at FROM demo_person ORDER BY id`); err != nil {
		log.Fatal(err)
	}
	fmt.Printf("people: %#v\n", people)

	// Get a single struct
	var one Person
	if err := db.GetContext(ctx, &one, `SELECT id, name, created_at FROM demo_person WHERE id=?`, people[0].ID); err != nil {
		log.Fatal(err)
	}
	fmt.Printf("one: %+v\n", one)

	// Named query with IN (sqlx.In)
	ids := []int{1, 2, 3}
	q, args, err := sqlx.In(`SELECT id, name FROM demo_person WHERE id IN (?)`, ids)
	if err != nil {
		log.Fatal(err)
	}
	q = db.Rebind(q) // ensure driver-specific bindvars
	rows, err := db.QueryxContext(ctx, q, args...)
	if err != nil {
		log.Fatal(err)
	}
	defer rows.Close()
	for rows.Next() {
		var id int
		var name string
		if err := rows.Scan(&id, &name); err != nil {
			log.Fatal(err)
		}
		fmt.Println(id, name)
	}
}

Placeholders & rebind

  • The driver uses ? positional placeholders.
  • With sqlx, always call db.Rebind(q) after sqlx.In(...) to adapt placeholders.

Context, timeouts & cancellations

All examples use Context. Set sensible timeouts to avoid runaway queries:

ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

Error handling tips

  • Check rows.Err() after iteration.
  • Prefer ExecContext/QueryContext to ensure timeouts are respected.
  • Wrap errors with operation context (e.g., fmt.Errorf("create table: %w", err)).

Testing locally

  1. Start IRIS and ensure SQL is enabled for your namespace (e.g., USER).
  2. Create a SQL user with privileges to connect and create tables.
  3. Verify connectivity using the DSN shown above.

Compatibility

  • Go: 1.21+
  • InterSystems IRIS: 2025.1+

License

MIT


Contributing

  • Run go vet and go test ./... before submitting PRs.
  • Add tests for new behaviors.
  • Document any DSN parameters you introduce.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrCouldNotDetectUsername = errors.New("intersystems: Could not detect default username. Please provide one explicitly")
)

Functions

func Open

func Open(dsn string) (_ driver.Conn, err error)

func ParseURL

func ParseURL(url string) (string, error)

ParseURL no longer needs to be used by clients of this library since supplying a URL as a connection string to sql.Open() is now supported:

sql.Open("intersystems", "iris://_system:SYS@1.2.3.4:1972/USER")

It remains exported here for backwards-compatibility.

ParseURL converts a url to a connection string for driver.Open. Example:

"iris://_system:SYS@1.2.3.4:1972/USER"

converts to:

"user=_system password=SYS host=1.2.3.4 port=1972 namespace=USER"

A minimal example:

"iris://"

This will be blank, causing driver.Open to use all of the defaults

Types

type Connector

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

Connector represents a fixed configuration for the pq driver with a given name. Connector satisfies the database/sql/driver Connector interface and can be used to create any number of DB Conn's via the database/sql OpenDB function.

See https://golang.org/pkg/database/sql/driver/#Connector. See https://golang.org/pkg/database/sql/#OpenDB.

func NewConnector

func NewConnector(dsn string) (*Connector, error)

NewConnector returns a connector for the pq driver in a fixed configuration with the given dsn. The returned connector can be used to create any number of equivalent Conn's. The returned connector is intended to be used with database/sql.OpenDB.

See https://golang.org/pkg/database/sql/driver/#Connector. See https://golang.org/pkg/database/sql/#OpenDB.

func (*Connector) Connect

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

Connect returns a connection to the database using the fixed configuration of this Connector. Context is not used.

func (*Connector) Driver

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

Driver returns the underlying driver of this Connector.

type Driver

type Driver struct{}

Driver implements database/sql/driver.Driver.

func (Driver) Open

func (d Driver) Open(name string) (driver.Conn, error)

Directories

Path Synopsis
example
sql command
sqlx command
src

Jump to

Keyboard shortcuts

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