v2 / vlib / db / mssql / stmt_handle.c.v
133 lines · 115 sloc · 4.25 KB · be6be35286ef1056de7992ca890abb61fc1bf073
Raw
1module mssql
2
3// HStmt is handle for sql statement
4struct HStmt {
5mut:
6 // db connection reference. Owner is Connection struct.
7 hdbc C.SQLHDBC = C.SQLHDBC(C.SQL_NULL_HDBC)
8 // statement handle
9 hstmt C.SQLHSTMT = C.SQLHSTMT(C.SQL_NULL_HSTMT)
10 // fields used for computation
11 column_count int = -1
12 // columns
13 buffers [][]char
14 // indicators for each column. Use a V pointer-sized integer to avoid arrays
15 // of C typedefs in generated code while still matching SQLLEN width.
16 indicators []isize
17}
18
19// new_hstmt constructs a new statement handle
20fn new_hstmt(hdbc C.SQLHDBC) !HStmt {
21 mut retcode := C.SQLRETURN(C.SQL_SUCCESS)
22 mut hstmt := C.SQLHSTMT(C.SQL_NULL_HSTMT)
23 // Allocate statement handle
24 retcode = C.SQLAllocHandle(C.SQLSMALLINT(C.SQL_HANDLE_STMT), C.SQLHANDLE(hdbc),
25 unsafe { &C.SQLHANDLE(&hstmt) })
26 check_error(retcode, 'SQLAllocHandle(SQL_HANDLE_STMT)', C.SQLHANDLE(hstmt),
27 C.SQLSMALLINT(C.SQL_HANDLE_STMT))!
28
29 return HStmt{
30 hdbc: hdbc
31 hstmt: hstmt
32 }
33}
34
35// close the statement handle
36fn (mut h HStmt) close() {
37 // Deallocate handle
38 if h.hstmt != C.SQLHSTMT(C.SQL_NULL_HSTMT) {
39 // check error code?
40 C.SQLFreeHandle(C.SQLSMALLINT(C.SQL_HANDLE_STMT), C.SQLHANDLE(h.hstmt))
41 h.hstmt = C.SQLHSTMT(C.SQL_NULL_HSTMT)
42 }
43}
44
45// exec executes a Sql statement. Result is stored in odbc driver, and not yet read.
46fn (h HStmt) exec(sql_ string) ! {
47 retcode := C.SQLExecDirect(h.hstmt, sql_.str, C.SQLINTEGER(C.SQL_NTS))
48 check_error(retcode, 'SQLExecDirect()', C.SQLHANDLE(h.hstmt), C.SQLSMALLINT(C.SQL_HANDLE_STMT))!
49}
50
51// retrieve_affected_rows returns number of rows affected/modified by the last operation. -1 if not applicable.
52fn (h HStmt) retrieve_affected_rows() !int {
53 count_ret := C.SQLLEN(0)
54 retcode := C.SQLRowCount(h.hstmt, &count_ret)
55 check_error(retcode, 'SQLRowCount()', C.SQLHANDLE(h.hstmt), C.SQLSMALLINT(C.SQL_HANDLE_STMT))!
56 return int(count_ret)
57}
58
59fn (h HStmt) retrieve_column_count() !int {
60 mut retcode := C.SQLRETURN(C.SQL_SUCCESS)
61 col_count_buff := C.SQLSMALLINT(0)
62 retcode = C.SQLNumResultCols(h.hstmt, &col_count_buff)
63 check_error(retcode, 'SQLNumResultCols()', C.SQLHANDLE(h.hstmt),
64 C.SQLSMALLINT(C.SQL_HANDLE_STMT))!
65 return int(col_count_buff)
66}
67
68// allocate buffers and bind them to drivers
69fn (mut h HStmt) prepare_read() ! {
70 mut retcode := C.SQLRETURN(C.SQL_SUCCESS)
71
72 column_count := h.retrieve_column_count()!
73 h.column_count = column_count // remember the count because read will need it
74
75 h.buffers = [][]char{len: h.column_count}
76 h.indicators = []isize{len: h.column_count}
77
78 for i := 0; i < h.column_count; i++ {
79 i_col := C.SQLUSMALLINT(i + 1) // col number starts with 1
80 size_ret := C.SQLLEN(0)
81 // find out buffer size needed to read data in this column
82 retcode = C.SQLColAttribute(h.hstmt, i_col, C.SQLUSMALLINT(C.SQL_DESC_LENGTH),
83 C.SQLPOINTER(0), C.SQLSMALLINT(0), C.SQLSMALLINT(0), &size_ret)
84 check_error(retcode, 'SQLColAttribute()', C.SQLHANDLE(h.hstmt),
85 C.SQLSMALLINT(C.SQL_HANDLE_STMT))!
86
87 // buffer allocation is the size + 1 to include termination char, since SQL_DESC_LENGTH does not include it.
88 allocate_size := size_ret + C.SQLLEN(1)
89 allocate_size_int := int(allocate_size)
90 buff := []char{len: allocate_size_int}
91
92 // bind the buffer
93 retcode = C.SQLBindCol(h.hstmt, C.SQLUSMALLINT(i_col), C.SQLSMALLINT(C.SQL_C_CHAR),
94 C.SQLPOINTER(&buff[0]), allocate_size, unsafe { &C.SQLLEN(&h.indicators[i]) })
95 check_error(retcode, 'SQLBindCol()', C.SQLHANDLE(h.hstmt), C.SQLSMALLINT(C.SQL_HANDLE_STMT))!
96
97 // record the buffer in HStmt
98 h.buffers[i] = buff
99 }
100}
101
102// fetch all rows
103fn (h HStmt) read_rows() ![][]string {
104 mut retcode := C.SQLRETURN(C.SQL_SUCCESS)
105
106 mut res := [][]string{}
107
108 if h.column_count <= 0 {
109 // there is nothing in the driver to read from
110 return res
111 }
112
113 // Fetch and print each row of data until SQL_NO_DATA returned.
114 for {
115 mut row := []string{}
116 retcode = C.SQLFetch(h.hstmt)
117 if retcode == C.SQLRETURN(C.SQL_SUCCESS) || retcode == C.SQLRETURN(C.SQL_SUCCESS_WITH_INFO) {
118 // copy buffered result to res
119 for content in h.buffers {
120 row << unsafe { cstring_to_vstring(content.data) }
121 }
122 } else {
123 if retcode != C.SQLRETURN(C.SQL_NO_DATA) {
124 check_error(retcode, 'SQLFetch()', C.SQLHANDLE(h.hstmt),
125 C.SQLSMALLINT(C.SQL_HANDLE_STMT))!
126 } else {
127 break
128 }
129 }
130 res << row
131 }
132 return res
133}
134