Skip to content

caretdev/go-irisnative

Repository files navigation

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?
  • host — IRIS hostname or IP
  • 1972 — superserver port (default)
  • Namespace — IRIS namespace (e.g., USER)

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.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages