diff options
Diffstat (limited to 'v_windows/v/vlib/mssql/mssql.v')
-rw-r--r-- | v_windows/v/vlib/mssql/mssql.v | 125 |
1 files changed, 125 insertions, 0 deletions
diff --git a/v_windows/v/vlib/mssql/mssql.v b/v_windows/v/vlib/mssql/mssql.v new file mode 100644 index 0000000..a885e4b --- /dev/null +++ b/v_windows/v/vlib/mssql/mssql.v @@ -0,0 +1,125 @@ +module mssql + +pub struct Connection { +mut: + henv C.SQLHENV = C.SQLHENV(C.SQL_NULL_HENV) // Environment + hdbc C.SQLHDBC = C.SQLHDBC(C.SQL_NULL_HDBC) // Connection handle +pub mut: + conn_str string +} + +// connect to db +pub fn (mut conn Connection) connect(conn_str string) ?bool { + conn_str_c := unsafe { &C.SQLCHAR(conn_str.str) } + mut retcode := C.SQLRETURN(C.SQL_SUCCESS) + // Allocate environment handle + retcode = C.SQLAllocHandle(C.SQLSMALLINT(C.SQL_HANDLE_ENV), C.SQLHANDLE(C.SQL_NULL_HANDLE), + unsafe { &C.SQLHANDLE(&conn.henv) }) + check_error(retcode, 'SQLAllocHandle(SQL_HANDLE_ENV)', C.SQLHANDLE(conn.henv), C.SQLSMALLINT(C.SQL_HANDLE_ENV)) ? + + // Set the ODBC version environment attribute + retcode = C.SQLSetEnvAttr(conn.henv, C.SQLINTEGER(C.SQL_ATTR_ODBC_VERSION), &C.SQLPOINTER(C.SQL_OV_ODBC3), + C.SQLINTEGER(0)) + check_error(retcode, 'SQLSetEnvAttr(SQL_ATTR_ODBC_VERSION)', C.SQLHANDLE(conn.henv), + C.SQLSMALLINT(C.SQL_HANDLE_ENV)) ? + + // Allocate connection handle + retcode = C.SQLAllocHandle(C.SQLSMALLINT(C.SQL_HANDLE_DBC), C.SQLHANDLE(conn.henv), + unsafe { &C.SQLHANDLE(&conn.hdbc) }) + check_error(retcode, 'SQLAllocHandle(SQL_HANDLE_DBC)', C.SQLHANDLE(conn.hdbc), C.SQLSMALLINT(C.SQL_HANDLE_DBC)) ? + + // Set login timeout to 5 seconds + retcode = C.SQLSetConnectAttr(conn.hdbc, C.SQLINTEGER(C.SQL_LOGIN_TIMEOUT), C.SQLPOINTER(5), + C.SQLINTEGER(0)) + check_error(retcode, 'SQLSetConnectAttr(SQL_LOGIN_TIMEOUT)', C.SQLHANDLE(conn.hdbc), + C.SQLSMALLINT(C.SQL_HANDLE_DBC)) ? + + // Connect to data source + mut outstr := [1024]char{} + mut outstrlen := C.SQLSMALLINT(0) + retcode = C.SQLDriverConnect(conn.hdbc, C.SQLHWND(0), conn_str_c, C.SQLSMALLINT(C.SQL_NTS), + &C.SQLCHAR(&outstr[0]), C.SQLSMALLINT(sizeof(outstr)), &outstrlen, C.SQLUSMALLINT(C.SQL_DRIVER_NOPROMPT)) + check_error(retcode, 'SQLDriverConnect()', C.SQLHANDLE(conn.hdbc), C.SQLSMALLINT(C.SQL_HANDLE_DBC)) ? + conn.conn_str = conn_str + return true +} + +// close - closes the connection. +pub fn (mut conn Connection) close() { + // Connection + if conn.hdbc != C.SQLHDBC(C.SQL_NULL_HDBC) { + C.SQLDisconnect(conn.hdbc) + C.SQLFreeHandle(C.SQLSMALLINT(C.SQL_HANDLE_DBC), C.SQLHANDLE(conn.hdbc)) + conn.hdbc = C.SQLHDBC(C.SQL_NULL_HDBC) + } + // Environment + if conn.henv != C.SQLHENV(C.SQL_NULL_HENV) { + C.SQLFreeHandle(C.SQLSMALLINT(C.SQL_HANDLE_ENV), C.SQLHANDLE(conn.henv)) + conn.henv = C.SQLHENV(C.SQL_NULL_HENV) + } +} + +// query executes a sql query +pub fn (mut conn Connection) query(q string) ?Result { + mut hstmt := new_hstmt(conn.hdbc) ? + defer { + hstmt.close() + } + + hstmt.exec(q) ? + + affected := hstmt.retrieve_affected_rows() ? + + hstmt.prepare_read() ? + raw_rows := hstmt.read_rows() ? + + mut res := Result{ + rows: []Row{} + num_rows_affected: affected + } + + for rr in raw_rows { + res.rows << Row{ + vals: rr + } + } + + return res +} + +// check_error checks odbc return code and extract error string if available +fn check_error(e C.SQLRETURN, s string, h C.SQLHANDLE, t C.SQLSMALLINT) ? { + if e != C.SQLRETURN(C.SQL_SUCCESS) && e != C.SQLRETURN(C.SQL_SUCCESS_WITH_INFO) { + err_str := extract_error(s, h, t) + return error(err_str) + } +} + +// extract_error extracts error string from odbc +fn extract_error(fnName string, handle C.SQLHANDLE, tp C.SQLSMALLINT) string { + mut err_str := fnName + mut i := 0 + mut native_error := C.SQLINTEGER(0) + mut sql_state := [7]char{} + mut message_text := [256]char{} + mut text_length := C.SQLSMALLINT(0) + mut ret := C.SQLRETURN(C.SQL_SUCCESS) + + for ret == C.SQLRETURN(C.SQL_SUCCESS) { + i++ + ret = C.SQLGetDiagRec(tp, handle, C.SQLSMALLINT(i), &C.SQLCHAR(&sql_state[0]), + &native_error, &C.SQLCHAR(&message_text[0]), C.SQLSMALLINT(sizeof(message_text)), + &text_length) + + // add driver error string + if ret == C.SQLRETURN(C.SQL_SUCCESS) || ret == C.SQLRETURN(C.SQL_SUCCESS_WITH_INFO) { + unsafe { + state_str := (&sql_state[0]).vstring() + native_error_code := int(native_error) + txt_str := (&message_text[0]).vstring() + err_str += '\n\todbc=$state_str:$i:$native_error_code:$txt_str' + } + } + } + return err_str +} |