Skip to content

Commit 2d7d0e4

Browse files
committed
Handle Microsoft Access MEMO parameters properly
Fixes alexbrainman#106
1 parent d5d4e9f commit 2d7d0e4

File tree

5 files changed

+99
-17
lines changed

5 files changed

+99
-17
lines changed

access_test.go

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
// Copyright 2018 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package odbc
6+
7+
import (
8+
"database/sql"
9+
"fmt"
10+
"io/ioutil"
11+
"os"
12+
"path/filepath"
13+
"testing"
14+
15+
ole "github.com/go-ole/go-ole"
16+
"github.com/go-ole/go-ole/oleutil"
17+
)
18+
19+
func TestAccessMemo(t *testing.T) {
20+
tmpdir, err := ioutil.TempDir("", "TestAccessMemo")
21+
if err != nil {
22+
t.Fatal(err)
23+
}
24+
defer os.RemoveAll(tmpdir)
25+
26+
dbfilename := filepath.Join(tmpdir, "db.mdb")
27+
createAccessDB(t, dbfilename)
28+
29+
db, err := sql.Open("odbc", fmt.Sprintf("DRIVER={Microsoft Access Driver (*.mdb)};DBQ=%s;", dbfilename))
30+
if err != nil {
31+
t.Fatal(err)
32+
}
33+
defer db.Close()
34+
35+
err = db.Ping()
36+
if err != nil {
37+
t.Fatal(err)
38+
}
39+
40+
_, err = db.Exec("create table mytable (m memo)")
41+
if err != nil {
42+
t.Fatal(err)
43+
}
44+
for s := ""; len(s) < 1000; s += "0123456789" {
45+
_, err = db.Exec("insert into mytable (m) values (?)", s)
46+
if err != nil {
47+
t.Fatal(err)
48+
}
49+
}
50+
}
51+
52+
func createAccessDB(t *testing.T, dbfilename string) {
53+
err := ole.CoInitialize(0)
54+
if err != nil {
55+
t.Fatal(err)
56+
}
57+
defer ole.CoUninitialize()
58+
59+
unk, err := oleutil.CreateObject("adox.catalog")
60+
if err != nil {
61+
t.Fatal(err)
62+
}
63+
cat, err := unk.QueryInterface(ole.IID_IDispatch)
64+
if err != nil {
65+
t.Fatal(err)
66+
}
67+
_, err = oleutil.CallMethod(cat, "create", fmt.Sprintf("provider=microsoft.jet.oledb.4.0;data source=%s;", dbfilename))
68+
if err != nil {
69+
t.Fatal(err)
70+
}
71+
}

conn.go

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,21 @@ package odbc
66

77
import (
88
"database/sql/driver"
9+
"strings"
910
"unsafe"
1011

1112
"github.com/alexbrainman/odbc/api"
1213
)
1314

1415
type Conn struct {
15-
h api.SQLHDBC
16-
tx *Tx
17-
bad bool
16+
h api.SQLHDBC
17+
tx *Tx
18+
bad bool
19+
isMSAccessDriver bool
1820
}
1921

22+
var accessDriverSubstr = strings.ToUpper(strings.Replace("DRIVER={Microsoft Access Driver", " ", "", -1))
23+
2024
func (d *Driver) Open(dsn string) (driver.Conn, error) {
2125
var out api.SQLHANDLE
2226
ret := api.SQLAllocHandle(api.SQL_HANDLE_DBC, api.SQLHANDLE(d.h), &out)
@@ -34,7 +38,8 @@ func (d *Driver) Open(dsn string) (driver.Conn, error) {
3438
defer releaseHandle(h)
3539
return nil, NewError("SQLDriverConnect", h)
3640
}
37-
return &Conn{h: h}, nil
41+
isAccess := strings.Contains(strings.ToUpper(strings.Replace(dsn, " ", "", -1)), accessDriverSubstr)
42+
return &Conn{h: h, isMSAccessDriver: isAccess}, nil
3843
}
3944

4045
func (c *Conn) Close() (err error) {

odbcstmt.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ func (s *ODBCStmt) releaseHandle() error {
9595

9696
var testingIssue5 bool // used during tests
9797

98-
func (s *ODBCStmt) Exec(args []driver.Value) error {
98+
func (s *ODBCStmt) Exec(args []driver.Value, conn *Conn) error {
9999
if len(args) != len(s.Parameters) {
100100
return fmt.Errorf("wrong number of arguments %d, %d expected", len(args), len(s.Parameters))
101101
}
@@ -105,7 +105,7 @@ func (s *ODBCStmt) Exec(args []driver.Value) error {
105105
// 2) set their (vars) values here;
106106
// but rebinding parameters for every new parameter value
107107
// should be efficient enough for our purpose.
108-
if err := s.Parameters[i].BindValue(s.h, i, a); err != nil {
108+
if err := s.Parameters[i].BindValue(s.h, i, a, conn); err != nil {
109109
return err
110110
}
111111
}

param.go

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ func (p *Parameter) StoreStrLen_or_IndPtr(v api.SQLLEN) *api.SQLLEN {
3232

3333
}
3434

35-
func (p *Parameter) BindValue(h api.SQLHSTMT, idx int, v driver.Value) error {
35+
func (p *Parameter) BindValue(h api.SQLHSTMT, idx int, v driver.Value, conn *Conn) error {
3636
// TODO(brainman): Reuse memory for previously bound values. If memory
3737
// is reused, we, probably, do not need to call SQLBindParameter either.
3838
var ctype, sqltype, decimal api.SQLSMALLINT
@@ -64,15 +64,21 @@ func (p *Parameter) BindValue(h api.SQLHSTMT, idx int, v driver.Value) error {
6464
l *= 2 // every char takes 2 bytes
6565
buflen = api.SQLLEN(l)
6666
plen = p.StoreStrLen_or_IndPtr(buflen)
67-
switch {
68-
case size >= 4000:
67+
if !conn.isMSAccessDriver {
68+
switch {
69+
case size >= 4000:
70+
sqltype = api.SQL_WLONGVARCHAR
71+
case p.isDescribed:
72+
sqltype = p.SQLType
73+
case size <= 1:
74+
sqltype = api.SQL_WVARCHAR
75+
default:
76+
sqltype = api.SQL_WCHAR
77+
}
78+
} else {
79+
// MS Acess requires SQL_WLONGVARCHAR for MEMO.
80+
// https://docs.microsoft.com/en-us/sql/odbc/microsoft/microsoft-access-data-types
6981
sqltype = api.SQL_WLONGVARCHAR
70-
case p.isDescribed:
71-
sqltype = p.SQLType
72-
case size <= 1:
73-
sqltype = api.SQL_WVARCHAR
74-
default:
75-
sqltype = api.SQL_WCHAR
7682
}
7783
case int64:
7884
if -0x80000000 < d && d < 0x7fffffff {

stmt.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ func (s *Stmt) Exec(args []driver.Value) (driver.Result, error) {
6161
}
6262
s.os = os
6363
}
64-
err := s.os.Exec(args)
64+
err := s.os.Exec(args, s.c)
6565
if err != nil {
6666
return nil, err
6767
}
@@ -88,7 +88,7 @@ func (s *Stmt) Query(args []driver.Value) (driver.Rows, error) {
8888
}
8989
s.os = os
9090
}
91-
err := s.os.Exec(args)
91+
err := s.os.Exec(args, s.c)
9292
if err != nil {
9393
return nil, err
9494
}

0 commit comments

Comments
 (0)