aboutsummaryrefslogtreecommitdiff
path: root/v_windows/v/vlib/mssql/mssql.v
diff options
context:
space:
mode:
Diffstat (limited to 'v_windows/v/vlib/mssql/mssql.v')
-rw-r--r--v_windows/v/vlib/mssql/mssql.v125
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
+}