v / vlib / db / mssql / mssql.c.v
129 lines · 111 sloc · 3.96 KB · ff5b3d2ad8968ceede2c6aefccafec1de0abe406
Raw
1module mssql
2
3pub struct Connection {
4mut:
5 henv C.SQLHENV = C.SQLHENV(C.SQL_NULL_HENV) // Environment
6 hdbc C.SQLHDBC = C.SQLHDBC(C.SQL_NULL_HDBC) // Connection handle
7pub mut:
8 conn_str string
9}
10
11// connect to db
12pub fn (mut conn Connection) connect(conn_str string) !bool {
13 conn_str_c := unsafe { &C.SQLCHAR(conn_str.str) }
14 mut retcode := C.SQLRETURN(C.SQL_SUCCESS)
15 // Allocate environment handle
16 retcode = C.SQLAllocHandle(C.SQLSMALLINT(C.SQL_HANDLE_ENV), C.SQLHANDLE(C.SQL_NULL_HANDLE),
17 unsafe { &C.SQLHANDLE(&conn.henv) })
18 check_error(retcode, 'SQLAllocHandle(SQL_HANDLE_ENV)', C.SQLHANDLE(conn.henv),
19 C.SQLSMALLINT(C.SQL_HANDLE_ENV))!
20
21 // Set the ODBC version environment attribute
22 retcode = C.SQLSetEnvAttr(conn.henv, C.SQLINTEGER(C.SQL_ATTR_ODBC_VERSION),
23 C.SQLPOINTER(C.SQL_OV_ODBC3), C.SQLINTEGER(0))
24 check_error(retcode, 'SQLSetEnvAttr(SQL_ATTR_ODBC_VERSION)', C.SQLHANDLE(conn.henv),
25 C.SQLSMALLINT(C.SQL_HANDLE_ENV))!
26
27 // Allocate connection handle
28 retcode = C.SQLAllocHandle(C.SQLSMALLINT(C.SQL_HANDLE_DBC), C.SQLHANDLE(conn.henv),
29 unsafe { &C.SQLHANDLE(&conn.hdbc) })
30 check_error(retcode, 'SQLAllocHandle(SQL_HANDLE_DBC)', C.SQLHANDLE(conn.hdbc),
31 C.SQLSMALLINT(C.SQL_HANDLE_DBC))!
32
33 // Set login timeout to 5 seconds
34 retcode = C.SQLSetConnectAttr(conn.hdbc, C.SQLINTEGER(C.SQL_LOGIN_TIMEOUT), C.SQLPOINTER(5),
35 C.SQLINTEGER(0))
36 check_error(retcode, 'SQLSetConnectAttr(SQL_LOGIN_TIMEOUT)', C.SQLHANDLE(conn.hdbc),
37 C.SQLSMALLINT(C.SQL_HANDLE_DBC))!
38
39 // Connect to data source
40 mut outstr := [1024]char{}
41 mut outstrlen := C.SQLSMALLINT(0)
42 retcode = C.SQLDriverConnect(conn.hdbc, C.SQLHWND(0), conn_str_c, C.SQLSMALLINT(C.SQL_NTS),
43 &C.SQLCHAR(&outstr[0]), C.SQLSMALLINT(sizeof(outstr)), &outstrlen,
44 C.SQLUSMALLINT(C.SQL_DRIVER_NOPROMPT))
45 check_error(retcode, 'SQLDriverConnect()', C.SQLHANDLE(conn.hdbc),
46 C.SQLSMALLINT(C.SQL_HANDLE_DBC))!
47 conn.conn_str = conn_str
48 return true
49}
50
51// close - closes the connection.
52pub fn (mut conn Connection) close() {
53 // Connection
54 if conn.hdbc != C.SQLHDBC(C.SQL_NULL_HDBC) {
55 C.SQLDisconnect(conn.hdbc)
56 C.SQLFreeHandle(C.SQLSMALLINT(C.SQL_HANDLE_DBC), C.SQLHANDLE(conn.hdbc))
57 conn.hdbc = C.SQLHDBC(C.SQL_NULL_HDBC)
58 }
59 // Environment
60 if conn.henv != C.SQLHENV(C.SQL_NULL_HENV) {
61 C.SQLFreeHandle(C.SQLSMALLINT(C.SQL_HANDLE_ENV), C.SQLHANDLE(conn.henv))
62 conn.henv = C.SQLHENV(C.SQL_NULL_HENV)
63 }
64}
65
66// query executes a sql query
67pub fn (mut conn Connection) query(q string) !Result {
68 mut hstmt := new_hstmt(conn.hdbc)!
69 defer {
70 hstmt.close()
71 }
72
73 hstmt.exec(q)!
74
75 affected := hstmt.retrieve_affected_rows()!
76
77 hstmt.prepare_read()!
78 raw_rows := hstmt.read_rows()!
79
80 mut res := Result{
81 rows: []Row{}
82 num_rows_affected: affected
83 }
84
85 for rr in raw_rows {
86 res.rows << Row{
87 vals: rr
88 }
89 }
90
91 return res
92}
93
94// check_error checks odbc return code and extract error string if available
95fn check_error(e C.SQLRETURN, s string, h C.SQLHANDLE, t C.SQLSMALLINT) ! {
96 if e != C.SQLRETURN(C.SQL_SUCCESS) && e != C.SQLRETURN(C.SQL_SUCCESS_WITH_INFO) {
97 err_str := extract_error(s, h, t)
98 return error(err_str)
99 }
100}
101
102// extract_error extracts error string from odbc
103fn extract_error(fnName string, handle C.SQLHANDLE, tp C.SQLSMALLINT) string {
104 mut err_str := fnName
105 mut i := 0
106 mut native_error := C.SQLINTEGER(0)
107 mut sql_state := [7]char{}
108 mut message_text := [256]char{}
109 mut text_length := C.SQLSMALLINT(0)
110 mut ret := C.SQLRETURN(C.SQL_SUCCESS)
111
112 for ret == C.SQLRETURN(C.SQL_SUCCESS) {
113 i++
114 ret = C.SQLGetDiagRec(tp, handle, C.SQLSMALLINT(i), &C.SQLCHAR(&sql_state[0]),
115 &native_error, &C.SQLCHAR(&message_text[0]), C.SQLSMALLINT(sizeof(message_text)),
116 &text_length)
117
118 // add driver error string
119 if ret == C.SQLRETURN(C.SQL_SUCCESS) || ret == C.SQLRETURN(C.SQL_SUCCESS_WITH_INFO) {
120 unsafe {
121 state_str := (&sql_state[0]).vstring()
122 native_error_code := int(native_error)
123 txt_str := (&message_text[0]).vstring()
124 err_str += '\n\todbc=${state_str}:${i}:${native_error_code}:${txt_str}'
125 }
126 }
127 }
128 return err_str
129}
130