Skip to content

Commit

Permalink
vlib: move the mysql/sqlite/pg/mssql modules under vlib/db (vlang#16820)
Browse files Browse the repository at this point in the history
  • Loading branch information
yuyi98 authored Jan 13, 2023
1 parent 2d8f160 commit 64558df
Show file tree
Hide file tree
Showing 76 changed files with 3,668 additions and 320 deletions.
1 change: 1 addition & 0 deletions cmd/tools/vast/vast.v
Original file line number Diff line number Diff line change
Expand Up @@ -1752,6 +1752,7 @@ fn (t Tree) sql_expr(node ast.SqlExpr) &Node {
fn (t Tree) sql_stmt(node ast.SqlStmt) &Node {
mut obj := new_object()
obj.add_terse('ast_type', t.string_node('SqlStmt'))
obj.add_terse('db_expr_type', t.type_node(node.db_expr_type))
obj.add_terse('db_expr', t.expr(node.db_expr))
obj.add_terse('or_expr', t.or_expr(node.or_expr))
obj.add('pos', t.pos(node.pos))
Expand Down
16 changes: 8 additions & 8 deletions cmd/tools/vtest-self.v
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,8 @@ const (
'cmd/tools/vdoc/tests/vdoc_file_test.v', /* fails on Windows; order of output is not as expected */
'vlib/context/onecontext/onecontext_test.v',
'vlib/context/deadline_test.v' /* sometimes blocks */,
'vlib/mysql/mysql_orm_test.v' /* mysql not installed */,
'vlib/pg/pg_orm_test.v' /* pg not installed */,
'vlib/db/mysql/mysql_orm_test.v' /* mysql not installed */,
'vlib/db/pg/pg_orm_test.v' /* pg not installed */,
]
skip_fsanitize_too_slow = [
// These tests are too slow to be run in the CI on each PR/commit
Expand Down Expand Up @@ -120,9 +120,9 @@ const (
'vlib/net/tcp_test.v',
'vlib/orm/orm_test.v',
'vlib/orm/orm_sql_or_blocks_test.v',
'vlib/sqlite/sqlite_test.v',
'vlib/sqlite/sqlite_orm_test.v',
'vlib/sqlite/sqlite_vfs_lowlevel_test.v',
'vlib/db/sqlite/sqlite_test.v',
'vlib/db/sqlite/sqlite_orm_test.v',
'vlib/db/sqlite/sqlite_vfs_lowlevel_test.v',
'vlib/v/tests/orm_sub_struct_test.v',
'vlib/v/tests/orm_sub_array_struct_test.v',
'vlib/v/tests/orm_joined_tables_select_test.v',
Expand Down Expand Up @@ -170,9 +170,9 @@ const (
'vlib/net/http/http_test.v',
'vlib/net/http/status_test.v',
'vlib/net/websocket/ws_test.v',
'vlib/sqlite/sqlite_test.v',
'vlib/sqlite/sqlite_orm_test.v',
'vlib/sqlite/sqlite_vfs_lowlevel_test.v',
'vlib/db/sqlite/sqlite_test.v',
'vlib/db/sqlite/sqlite_orm_test.v',
'vlib/db/sqlite/sqlite_vfs_lowlevel_test.v',
'vlib/orm/orm_test.v',
'vlib/orm/orm_sql_or_blocks_test.v',
'vlib/v/tests/orm_sub_struct_test.v',
Expand Down
2 changes: 1 addition & 1 deletion doc/docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -4715,7 +4715,7 @@ V's ORM provides a number of benefits:
then manually construct objects from the parsed results.)

```v
import sqlite
import db.sqlite
// sets a custom table name. Default is struct name (case-sensitive)
[table: 'customers']
Expand Down
2 changes: 1 addition & 1 deletion examples/database/mysql.v
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import mysql
import db.mysql

fn main() {
mut conn := mysql.Connection{
Expand Down
6 changes: 3 additions & 3 deletions examples/database/orm.v
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import sqlite
import mysql
import pg
import db.sqlite
import db.mysql
import db.pg

[table: 'modules']
struct Module {
Expand Down
2 changes: 1 addition & 1 deletion examples/database/psql/customer.v
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module main

import pg
import db.pg

const dash = '----------------------------------------------------------------'

Expand Down
2 changes: 1 addition & 1 deletion examples/database/sqlite.v
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import sqlite
import db.sqlite

fn main() {
db := sqlite.connect(':memory:')!
Expand Down
2 changes: 1 addition & 1 deletion examples/js_dom_draw_bechmark_chart/v_vweb_orm/src/main.v
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ module main

import vweb
import time
import sqlite
import db.sqlite

struct App {
vweb.Context
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module databases

import sqlite
import db.sqlite

pub fn create_db_connection() !sqlite.DB {
mut db := sqlite.connect('database.db')!
Expand Down
2 changes: 1 addition & 1 deletion tutorials/building_a_simple_web_blog_with_vweb/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ Add a SQLite handle to `App`:

```v oksyntax
// blog.v
import sqlite
import db.sqlite
import vweb
struct App {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ module main

import vweb
import time
import sqlite
import db.sqlite
import json

struct App {
Expand Down
69 changes: 69 additions & 0 deletions vlib/db/mssql/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# SQL Server ODBC

* This is a V wrapper of SQL Server ODBC C/C++ library

## Dependencies
* ODBC C/C++ library
* Linux Install:
* Details: https://docs.microsoft.com/en-us/sql/connect/odbc/linux-mac/installing-the-microsoft-odbc-driver-for-sql-server
* `msodbcsql17` and `unixodbc-dev` packages are needed
* Windows Install:
* `odbc` lib is included in windows sdk for most of distributions,
so there is no need to install it separately
* Details: https://docs.microsoft.com/en-us/sql/connect/odbc/microsoft-odbc-driver-for-sql-server

## Windows Notes
### Using `msvc`
* Make sure `cl.exe` of `msvc` is accessible from command line.
You can run `v` commands in `Visual Studio 2019 Developer Command Prompt` to be safe.
* C Headers and dlls can be automatically resolved by `msvc`.
### Using `tcc`
* Copy those headers to `@VEXEROOT\thirdparty\mssql\include`.
The version number `10.0.18362.0` might differ on your system.
Command Prompt commands:
```cmd
copy "C:\Program Files (x86)\Windows Kits\10\Include\10.0.18362.0\um\sql.h" thirdparty\mssql\include
copy "C:\Program Files (x86)\Windows Kits\10\Include\10.0.18362.0\um\sqlext.h" thirdparty\mssql\include
copy "C:\Program Files (x86)\Windows Kits\10\Include\10.0.18362.0\um\sqltypes.h" thirdparty\mssql\include
copy "C:\Program Files (x86)\Windows Kits\10\Include\10.0.18362.0\um\sqlucode.h" thirdparty\mssql\include
copy "C:\Program Files (x86)\Windows Kits\10\Include\10.0.18362.0\shared\sal.h" thirdparty\mssql\include
copy "C:\Program Files (x86)\Windows Kits\10\Include\10.0.18362.0\shared\concurrencysal.h" thirdparty\mssql\include
```
* dlls can be automatically resolved by `tcc`

## TODO
* Support Mac
* Support ORM

## Usage
```v ignore
import mssql
fn test_example() ? {
// connect to server
config := mssql.Config{
driver: 'ODBC Driver 17 for SQL Server'
server: 'tcp:localhost'
uid: '<your username>'
pwd: '<your password>'
}
mut conn := mssql.Connection{}
conn.connect(config.get_conn_str())?
defer {
conn.close()
}
// get current db name
mut query := 'SELECT DB_NAME()'
mut res := conn.query(query)?
assert res == mssql.Result{
rows: [mssql.Row{
vals: ['master']
}]
num_rows_affected: -1
}
}
```
6 changes: 6 additions & 0 deletions vlib/db/mssql/_cdef_nix.c.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module mssql

#flag -lodbc

#include <sql.h>
#include <sqlext.h>
12 changes: 12 additions & 0 deletions vlib/db/mssql/_cdef_windows.c.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
module mssql

// mssql module does not support tcc on windows

// odbc32 lib comes with windows sdk and does not need to be installed separately.
// v builder for msvc can resolve the sdk includes search path, so no need to repeat here.
#flag windows -lodbc32

// Special handling of sql headers on windows.
// Source is in v third party folder.
#flag windows -I@VEXEROOT/thirdparty/mssql/include
#include <mssql.h>
27 changes: 27 additions & 0 deletions vlib/db/mssql/_cdefs.c.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
module mssql

fn C.SQLAllocHandle(handle_type C.SQLSMALLINT, input_handle C.SQLHANDLE, output_handle &C.SQLHANDLE) C.SQLRETURN

fn C.SQLSetEnvAttr(environment_handle C.SQLHENV, attribute C.SQLINTEGER, value C.SQLPOINTER, string_length C.SQLINTEGER) C.SQLRETURN

fn C.SQLGetDiagRec(handle_type C.SQLSMALLINT, handle C.SQLHANDLE, rec_number C.SQLSMALLINT, sql_state &C.SQLCHAR, native_error &C.SQLINTEGER, message_text &C.SQLCHAR, buffer_length C.SQLSMALLINT, text_length &C.SQLSMALLINT) C.SQLRETURN

fn C.SQLSetConnectAttr(connection_handle C.SQLHDBC, attribute C.SQLINTEGER, value C.SQLPOINTER, string_length C.SQLINTEGER) C.SQLRETURN

fn C.SQLDriverConnect(hdbc C.SQLHDBC, hwnd C.SQLHWND, sz_conn_str_in &C.SQLCHAR, cb_conn_str_in C.SQLSMALLINT, sz_conn_str_out &C.SQLCHAR, cb_conn_str_out_max C.SQLSMALLINT, pcb_conn_str_out &C.SQLSMALLINT, f_driver_completion C.SQLUSMALLINT) C.SQLRETURN

fn C.SQLDisconnect(connection_handle C.SQLHDBC) C.SQLRETURN

fn C.SQLExecDirect(statement_handle C.SQLHSTMT, statement_text &C.SQLCHAR, text_length C.SQLINTEGER) C.SQLRETURN

fn C.SQLBindCol(statement_handle C.SQLHSTMT, column_number C.SQLUSMALLINT, target_type C.SQLSMALLINT, target_value C.SQLPOINTER, buffer_length C.SQLLEN, str_len_or_ind &C.SQLLEN) C.SQLRETURN

fn C.SQLFetch(statement_handle C.SQLHSTMT) C.SQLRETURN

fn C.SQLFreeHandle(handle_type C.SQLSMALLINT, handle C.SQLHANDLE) C.SQLRETURN

fn C.SQLNumResultCols(statement_handle C.SQLHSTMT, column_count &C.SQLSMALLINT) C.SQLRETURN

fn C.SQLColAttribute(statement_handle C.SQLHSTMT, column_number C.SQLUSMALLINT, field_identifier C.SQLUSMALLINT, character_attribute C.SQLPOINTER, buffer_length C.SQLSMALLINT, string_length C.SQLSMALLINT, numeric_attribute &C.SQLLEN) C.SQLRETURN

fn C.SQLRowCount(statement_handle C.SQLHSTMT, row_count &C.SQLLEN) C.SQLRETURN
22 changes: 22 additions & 0 deletions vlib/db/mssql/config.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
module mssql

// Config TODO
pub struct Config {
pub:
driver string
server string
uid string
pwd string
// if dbname empty, conn str will not contain Database info,
// and it is up to the server to choose which db to connect to.
dbname string
}

// get_conn_str TODO
pub fn (cfg Config) get_conn_str() string {
mut str := 'Driver=${cfg.driver};Server=${cfg.server};UID=${cfg.uid};PWD=${cfg.pwd}'
if cfg.dbname != '' {
str += ';Database=${cfg.dbname}'
}
return str
}
125 changes: 125 additions & 0 deletions vlib/db/mssql/mssql.v
Original file line number Diff line number Diff line change
@@ -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
}
13 changes: 13 additions & 0 deletions vlib/db/mssql/result.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
module mssql

pub struct Row {
pub mut:
vals []string
}

pub struct Result {
pub mut:
rows []Row
// the number of rows affected by sql statement
num_rows_affected int
}
Loading

0 comments on commit 64558df

Please sign in to comment.