master
go 217 lines 5.26 KB
Raw
1 // Package odbcbridge provides an optimized ODBC connection interface using CGO.
2
3 //go:build cgo
4
5 package odbcbridge
6
7 import (
8 "context"
9 "database/sql"
10 "database/sql/driver"
11 "fmt"
12 "io"
13 "sync"
14 )
15
16 func init() {
17 sql.Register("odbcbridge", &ODBCDriver{})
18 }
19
20 // ODBCDriver implements database/sql/driver.Driver
21 type ODBCDriver struct{}
22
23 // Open returns a new connection to the database
24 func (d *ODBCDriver) Open(dsn string) (driver.Conn, error) {
25 conn, err := ConnectOptimized(dsn)
26 if err != nil {
27 return nil, err
28 }
29 return &driverConn{conn: conn}, nil
30 }
31
32 // driverConn implements driver.Conn
33 type driverConn struct {
34 conn *OptimizedConnection
35 mu sync.Mutex
36 }
37
38 // Prepare returns a prepared statement
39 func (dc *driverConn) Prepare(query string) (driver.Stmt, error) {
40 return dc.PrepareContext(context.Background(), query)
41 }
42
43 // PrepareContext returns a prepared statement
44 func (dc *driverConn) PrepareContext(ctx context.Context, query string) (driver.Stmt, error) {
45 dc.mu.Lock()
46 defer dc.mu.Unlock()
47
48 stmt, err := dc.conn.PrepareContext(ctx, query)
49 if err != nil {
50 return nil, err
51 }
52 return &driverStmt{stmt: stmt, conn: dc}, nil
53 }
54
55 // Close closes the connection
56 func (dc *driverConn) Close() error {
57 dc.mu.Lock()
58 defer dc.mu.Unlock()
59 return dc.conn.Close()
60 }
61
62 // Begin starts and returns a new transaction
63 func (dc *driverConn) Begin() (driver.Tx, error) {
64 return nil, fmt.Errorf("transactions not implemented")
65 }
66
67 // BeginTx starts and returns a new transaction
68 func (dc *driverConn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.Tx, error) {
69 return nil, fmt.Errorf("transactions not implemented")
70 }
71
72 // QueryContext executes a query that returns rows
73 func (dc *driverConn) QueryContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Rows, error) {
74 dc.mu.Lock()
75 defer dc.mu.Unlock()
76
77 if len(args) > 0 {
78 return nil, fmt.Errorf("query arguments not supported for direct queries, use prepared statements")
79 }
80
81 rows, err := dc.conn.QueryContext(ctx, query)
82 if err != nil {
83 return nil, err
84 }
85 return &driverRows{rows: rows}, nil
86 }
87
88 // ExecContext executes a query that doesn't return rows
89 func (dc *driverConn) ExecContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Result, error) {
90 dc.mu.Lock()
91 defer dc.mu.Unlock()
92
93 if len(args) > 0 {
94 return nil, fmt.Errorf("exec arguments not supported for direct queries, use prepared statements")
95 }
96
97 rows, err := dc.conn.QueryContext(ctx, query)
98 if err != nil {
99 return nil, err
100 }
101 defer rows.Close()
102
103 // Get row count for result
104 rowCount := rows.RowCount()
105 return &driverResult{rowsAffected: rowCount}, nil
106 }
107
108 // Ping verifies a connection to the database is still alive
109 func (dc *driverConn) Ping(ctx context.Context) error {
110 dc.mu.Lock()
111 defer dc.mu.Unlock()
112
113 if !dc.conn.IsConnected() {
114 return fmt.Errorf("connection is closed")
115 }
116
117 // Execute a simple query to verify connection
118 rows, err := dc.conn.QueryContext(ctx, "SELECT 1 FROM SYSIBM.SYSDUMMY1")
119 if err != nil {
120 return err
121 }
122 rows.Close()
123 return nil
124 }
125
126 // driverStmt implements driver.Stmt
127 type driverStmt struct {
128 stmt *PreparedStatement
129 conn *driverConn
130 }
131
132 // Close closes the statement
133 func (ds *driverStmt) Close() error {
134 return ds.stmt.Close()
135 }
136
137 // NumInput returns the number of placeholder parameters
138 func (ds *driverStmt) NumInput() int {
139 // ODBC doesn't provide this information easily
140 return -1
141 }
142
143 // Exec executes a prepared statement
144 func (ds *driverStmt) Exec(args []driver.Value) (driver.Result, error) {
145 return nil, fmt.Errorf("Exec not implemented, use ExecContext")
146 }
147
148 // ExecContext executes a prepared statement
149 func (ds *driverStmt) ExecContext(ctx context.Context, args []driver.NamedValue) (driver.Result, error) {
150 // For now, execute without parameters
151 rows, err := ds.stmt.Execute()
152 if err != nil {
153 return nil, err
154 }
155 defer rows.Close()
156
157 rowCount := rows.RowCount()
158 return &driverResult{rowsAffected: rowCount}, nil
159 }
160
161 // Query executes a prepared statement and returns rows
162 func (ds *driverStmt) Query(args []driver.Value) (driver.Rows, error) {
163 return nil, fmt.Errorf("Query not implemented, use QueryContext")
164 }
165
166 // QueryContext executes a prepared statement and returns rows
167 func (ds *driverStmt) QueryContext(ctx context.Context, args []driver.NamedValue) (driver.Rows, error) {
168 // For now, execute without parameters
169 rows, err := ds.stmt.Execute()
170 if err != nil {
171 return nil, err
172 }
173 return &driverRows{rows: rows}, nil
174 }
175
176 // driverRows implements driver.Rows
177 type driverRows struct {
178 rows *OptimizedRows
179 closed bool
180 }
181
182 // Columns returns the column names
183 func (dr *driverRows) Columns() []string {
184 return dr.rows.Columns()
185 }
186
187 // Close closes the rows
188 func (dr *driverRows) Close() error {
189 if dr.closed {
190 return nil
191 }
192 dr.closed = true
193 return dr.rows.Close()
194 }
195
196 // Next advances to the next row
197 func (dr *driverRows) Next(dest []driver.Value) error {
198 if dr.closed {
199 return io.EOF
200 }
201 return dr.rows.Next(dest)
202 }
203
204 // driverResult implements driver.Result
205 type driverResult struct {
206 rowsAffected int64
207 }
208
209 // LastInsertId returns the last inserted ID
210 func (dr *driverResult) LastInsertId() (int64, error) {
211 return 0, fmt.Errorf("LastInsertId not supported")
212 }
213
214 // RowsAffected returns the number of rows affected
215 func (dr *driverResult) RowsAffected() (int64, error) {
216 return dr.rowsAffected, nil
217 }