Repair using the db_odbc module to query the Oracle database, the program compiled in the release mode, the return of the field value is null.

This commit is contained in:
lihf8515 2016-03-21 15:15:28 +08:00
commit 1a5bde28ed

View file

@ -38,7 +38,7 @@
## ##
## .. code-block:: Nim ## .. code-block:: Nim
## import db_odbc ## import db_odbc
## let db = open("localhost", "user", "password", "dbname") ## var db = open("localhost", "user", "password", "dbname")
## db.close() ## db.close()
## ##
## Creating a table ## Creating a table
@ -64,7 +64,7 @@
## ##
## import db_odbc, math ## import db_odbc, math
## ##
## let theDb = open("localhost", "nim", "nim", "test") ## var theDb = open("localhost", "nim", "nim", "test")
## ##
## theDb.exec(sql"Drop table if exists myTestTbl") ## theDb.exec(sql"Drop table if exists myTestTbl")
## theDb.exec(sql("create table myTestTbl (" & ## theDb.exec(sql("create table myTestTbl (" &
@ -88,9 +88,7 @@
## ##
## theDb.close() ## theDb.close()
import strutils, odbcsql import strutils, odbcsql
import db_common import db_common
export db_common export db_common
@ -169,11 +167,11 @@ proc dbError*(db: var DbConn) {.
properFreeResult(SQL_HANDLE_ENV, db.env) properFreeResult(SQL_HANDLE_ENV, db.env)
raise e raise e
proc SqlCheck(db: var DbConn, resVal: TSqlSmallInt) {.raises: [DbError]} = proc sqlCheck(db: var DbConn, resVal: TSqlSmallInt) {.raises: [DbError]} =
## Wrapper that checks if ``resVal`` is not SQL_SUCCESS and if so, raises [EDb] ## Wrapper that raises [EDb] if ``resVal`` is neither SQL_SUCCESS or SQL_NO_DATA
if resVal != SQL_SUCCESS: dbError(db) if resVal notIn [SQL_SUCCESS, SQL_NO_DATA]: dbError(db)
proc SqlGetDBMS(db: var DbConn): string {. proc sqlGetDBMS(db: var DbConn): string {.
tags: [ReadDbEffect, WriteDbEffect], raises: [] .} = tags: [ReadDbEffect, WriteDbEffect], raises: [] .} =
## Returns the ODBC SQL_DBMS_NAME string ## Returns the ODBC SQL_DBMS_NAME string
const const
@ -182,7 +180,7 @@ proc SqlGetDBMS(db: var DbConn): string {.
sz: TSqlSmallInt = 0 sz: TSqlSmallInt = 0
buf[0] = '\0' buf[0] = '\0'
try: try:
db.SqlCheck(SQLGetInfo(db.hDb, SQL_DBMS_NAME, cast[SqlPointer](buf.addr), db.sqlCheck(SQLGetInfo(db.hDb, SQL_DBMS_NAME, cast[SqlPointer](buf.addr),
4095.TSqlSmallInt, sz.addr)) 4095.TSqlSmallInt, sz.addr))
except: discard except: discard
return $buf.cstring return $buf.cstring
@ -212,7 +210,7 @@ proc dbFormat(formatstr: SqlQuery, args: varargs[string]): string {.
add(result, c) add(result, c)
proc prepareFetch(db: var DbConn, query: SqlQuery, proc prepareFetch(db: var DbConn, query: SqlQuery,
args: varargs[string, `$`]) {. args: varargs[string, `$`]) : TSqlSmallInt {.
tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} = tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} =
# Prepare a statement, execute it and fetch the data to the driver # Prepare a statement, execute it and fetch the data to the driver
# ready for retrieval of the data # ready for retrieval of the data
@ -220,11 +218,13 @@ proc prepareFetch(db: var DbConn, query: SqlQuery,
# requires calling # requires calling
# properFreeResult(SQL_HANDLE_STMT, db.stmt) # properFreeResult(SQL_HANDLE_STMT, db.stmt)
# when finished # when finished
db.SqlCheck(SQLAllocHandle(SQL_HANDLE_STMT, db.hDb, db.stmt)) db.sqlCheck(SQLAllocHandle(SQL_HANDLE_STMT, db.hDb, db.stmt))
var q = dbFormat(query, args) var q = dbFormat(query, args)
db.SqlCheck(SQLPrepare(db.stmt, q.PSQLCHAR, q.len.TSqlSmallInt)) db.sqlCheck(SQLPrepare(db.stmt, q.PSQLCHAR, q.len.TSqlSmallInt))
db.SqlCheck(SQLExecute(db.stmt)) db.sqlCheck(SQLExecute(db.stmt))
db.SqlCheck(SQLFetch(db.stmt)) var retcode = SQLFetch(db.stmt)
db.sqlCheck(retcode)
result=retcode
proc prepareFetchDirect(db: var DbConn, query: SqlQuery, proc prepareFetchDirect(db: var DbConn, query: SqlQuery,
args: varargs[string, `$`]) {. args: varargs[string, `$`]) {.
@ -235,10 +235,10 @@ proc prepareFetchDirect(db: var DbConn, query: SqlQuery,
# requires calling # requires calling
# properFreeResult(SQL_HANDLE_STMT, db.stmt) # properFreeResult(SQL_HANDLE_STMT, db.stmt)
# when finished # when finished
db.SqlCheck(SQLAllocHandle(SQL_HANDLE_STMT, db.hDb, db.stmt)) db.sqlCheck(SQLAllocHandle(SQL_HANDLE_STMT, db.hDb, db.stmt))
var q = dbFormat(query, args) var q = dbFormat(query, args)
db.SqlCheck(SQLExecDirect(db.stmt, q.PSQLCHAR, q.len.TSqlSmallInt)) db.sqlCheck(SQLExecDirect(db.stmt, q.PSQLCHAR, q.len.TSqlSmallInt))
db.SqlCheck(SQLFetch(db.stmt)) db.sqlCheck(SQLFetch(db.stmt))
proc tryExec*(db: var DbConn, query: SqlQuery, args: varargs[string, `$`]): bool {. proc tryExec*(db: var DbConn, query: SqlQuery, args: varargs[string, `$`]): bool {.
tags: [ReadDbEffect, WriteDbEffect], raises: [].} = tags: [ReadDbEffect, WriteDbEffect], raises: [].} =
@ -285,20 +285,30 @@ iterator fastRows*(db: var DbConn, query: SqlQuery,
rowRes: Row rowRes: Row
sz: TSqlSmallInt = 0 sz: TSqlSmallInt = 0
cCnt: TSqlSmallInt = 0.TSqlSmallInt cCnt: TSqlSmallInt = 0.TSqlSmallInt
rCnt = -1 res: TSqlSmallInt = 0.TSqlSmallInt
tempcCnt:TSqlSmallInt # temporary cCnt,Fix the field values to be null when the release schema is compiled.
db.prepareFetch(query, args) # tempcCnt,A field to store the number of temporary variables, for unknown reasons,
db.SqlCheck(SQLNumResultCols(db.stmt, cCnt)) # after performing a sqlgetdata function and circulating variables cCnt value will be changed to 0,
db.SqlCheck(SQLRowCount(db.stmt, rCnt)) # so the values of the temporary variable to store the cCnt.
rowRes = newRow(cCnt) # After every cycle and specified to cCnt. To ensure the traversal of all fields.
for rNr in 1..rCnt: res = db.prepareFetch(query, args)
for colId in 1..cCnt: if res == SQL_NO_DATA:
buf[0] = '\0' discard
db.SqlCheck(SQLGetData(db.stmt, colId.SqlUSmallInt, SQL_C_CHAR, elif res == SQL_SUCCESS:
cast[cstring](buf.addr), 4095.TSqlSmallInt, sz.addr)) res = SQLNumResultCols(db.stmt, cCnt)
rowRes[colId-1] = $buf.cstring rowRes = newRow(cCnt)
db.SqlCheck(SQLFetchScroll(db.stmt, SQL_FETCH_NEXT, 1)) rowRes.setLen(max(cCnt,0))
yield rowRes tempcCnt = cCnt
while res == SQL_SUCCESS:
for colId in 1..cCnt:
buf[0] = '\0'
db.sqlCheck(SQLGetData(db.stmt, colId.SqlUSmallInt, SQL_C_CHAR,
cast[cstring](buf.addr), 4095.TSqlSmallInt, sz.addr))
rowRes[colId-1] = $buf.cstring
cCnt = tempcCnt
yield rowRes
res = SQLFetch(db.stmt)
db.sqlCheck(res)
properFreeResult(SQL_HANDLE_STMT, db.stmt) properFreeResult(SQL_HANDLE_STMT, db.stmt)
iterator instantRows*(db: var DbConn, query: SqlQuery, iterator instantRows*(db: var DbConn, query: SqlQuery,
@ -310,19 +320,30 @@ iterator instantRows*(db: var DbConn, query: SqlQuery,
rowRes: Row rowRes: Row
sz: TSqlSmallInt = 0 sz: TSqlSmallInt = 0
cCnt: TSqlSmallInt = 0.TSqlSmallInt cCnt: TSqlSmallInt = 0.TSqlSmallInt
rCnt = -1 res: TSqlSmallInt = 0.TSqlSmallInt
db.prepareFetch(query, args) tempcCnt:TSqlSmallInt # temporary cCnt,Fix the field values to be null when the release schema is compiled.
db.SqlCheck(SQLNumResultCols(db.stmt, cCnt)) # tempcCnt,A field to store the number of temporary variables, for unknown reasons,
db.SqlCheck(SQLRowCount(db.stmt, rCnt)) # after performing a sqlgetdata function and circulating variables cCnt value will be changed to 0,
rowRes = newRow(cCnt) # so the values of the temporary variable to store the cCnt.
for rNr in 1..rCnt: # After every cycle and specified to cCnt. To ensure the traversal of all fields.
for colId in 1..cCnt: res = db.prepareFetch(query, args)
buf[0] = '\0' if res == SQL_NO_DATA:
db.SqlCheck(SQLGetData(db.stmt, colId.SqlUSmallInt, SQL_C_CHAR, discard
cast[cstring](buf.addr), 4095.TSqlSmallInt, sz.addr)) elif res == SQL_SUCCESS:
rowRes[colId-1] = $buf.cstring res = SQLNumResultCols(db.stmt, cCnt)
db.SqlCheck(SQLFetchScroll(db.stmt, SQL_FETCH_NEXT, 1)) rowRes = newRow(cCnt)
yield (row: rowRes, len: cCnt.int) rowRes.setLen(max(cCnt,0))
tempcCnt = cCnt
while res == SQL_SUCCESS:
for colId in 1..cCnt:
buf[0] = '\0'
db.sqlCheck(SQLGetData(db.stmt, colId.SqlUSmallInt, SQL_C_CHAR,
cast[cstring](buf.addr), 4095.TSqlSmallInt, sz.addr))
rowRes[colId-1] = $buf.cstring
cCnt = tempcCnt
yield (row: rowRes, len: cCnt.int)
res = SQLFetch(db.stmt)
db.sqlCheck(res)
properFreeResult(SQL_HANDLE_STMT, db.stmt) properFreeResult(SQL_HANDLE_STMT, db.stmt)
proc `[]`*(row: InstantRow, col: int): string {.inline.} = proc `[]`*(row: InstantRow, col: int): string {.inline.} =
@ -339,43 +360,68 @@ proc getRow*(db: var DbConn, query: SqlQuery,
## Retrieves a single row. If the query doesn't return any rows, this proc ## Retrieves a single row. If the query doesn't return any rows, this proc
## will return a Row with empty strings for each column. ## will return a Row with empty strings for each column.
var var
rowRes: Row
sz: TSqlSmallInt = 0.TSqlSmallInt sz: TSqlSmallInt = 0.TSqlSmallInt
cCnt: TSqlSmallInt = 0.TSqlSmallInt cCnt: TSqlSmallInt = 0.TSqlSmallInt
rCnt = -1 res: TSqlSmallInt = 0.TSqlSmallInt
result = @[] tempcCnt:TSqlSmallInt # temporary cCnt,Fix the field values to be null when the release schema is compiled.
db.prepareFetch(query, args) ## tempcCnt,A field to store the number of temporary variables, for unknown reasons,
db.SqlCheck(SQLNumResultCols(db.stmt, cCnt)) ## after performing a sqlgetdata function and circulating variables cCnt value will be changed to 0,
## so the values of the temporary variable to store the cCnt.
db.SqlCheck(SQLRowCount(db.stmt, rCnt)) ## After every cycle and specified to cCnt. To ensure the traversal of all fields.
for colId in 1..cCnt: res = db.prepareFetch(query, args)
db.SqlCheck(SQLGetData(db.stmt, colId.SqlUSmallInt, SQL_C_CHAR, if res == SQL_NO_DATA:
cast[cstring](buf.addr), 4095.TSqlSmallInt, sz.addr)) result = @[]
result.add($buf.cstring) elif res == SQL_SUCCESS:
db.SqlCheck(SQLFetchScroll(db.stmt, SQL_FETCH_NEXT, 1)) res = SQLNumResultCols(db.stmt, cCnt)
rowRes = newRow(cCnt)
rowRes.setLen(max(cCnt,0))
tempcCnt = cCnt
for colId in 1..cCnt:
buf[0] = '\0'
db.sqlCheck(SQLGetData(db.stmt, colId.SqlUSmallInt, SQL_C_CHAR,
cast[cstring](buf.addr), 4095.TSqlSmallInt, sz.addr))
rowRes[colId-1] = $buf.cstring
cCnt = tempcCnt
res = SQLFetch(db.stmt)
result = rowRes
db.sqlCheck(res)
properFreeResult(SQL_HANDLE_STMT, db.stmt) properFreeResult(SQL_HANDLE_STMT, db.stmt)
proc getAllRows*(db: var DbConn, query: SqlQuery, proc getAllRows*(db: var DbConn, query: SqlQuery,
args: varargs[string, `$`]): seq[Row] {. args: varargs[string, `$`]): seq[Row] {.
tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} = tags: [ReadDbEffect, WriteDbEffect], raises: [DbError] .} =
## Executes the query and returns the whole result dataset. ## Executes the query and returns the whole result dataset.
var var
rows: seq[Row] = @[]
rowRes: Row rowRes: Row
sz: TSqlSmallInt = 0 sz: TSqlSmallInt = 0
cCnt: TSqlSmallInt = 0.TSqlSmallInt cCnt: TSqlSmallInt = 0.TSqlSmallInt
rCnt = -1 res: TSqlSmallInt = 0.TSqlSmallInt
db.prepareFetch(query, args) tempcCnt:TSqlSmallInt # temporary cCnt,Fix the field values to be null when the release schema is compiled.
db.SqlCheck(SQLNumResultCols(db.stmt, cCnt)) ## tempcCnt,A field to store the number of temporary variables, for unknown reasons,
db.SqlCheck(SQLRowCount(db.stmt, rCnt)) ## after performing a sqlgetdata function and circulating variables cCnt value will be changed to 0,
result = @[] ## so the values of the temporary variable to store the cCnt.
for rNr in 1..rCnt: ## After every cycle and specified to cCnt. To ensure the traversal of all fields.
rowRes = @[] res = db.prepareFetch(query, args)
buf[0] = '\0' if res == SQL_NO_DATA:
for colId in 1..cCnt: result = @[]
db.SqlCheck(SQLGetData(db.stmt, colId.SqlUSmallInt, SQL_C_CHAR, elif res == SQL_SUCCESS:
cast[SqlPointer](buf.addr), 4095.TSqlSmallInt, sz.addr)) res = SQLNumResultCols(db.stmt, cCnt)
rowRes.add($buf.cstring) rowRes = newRow(cCnt)
db.SqlCheck(SQLFetchScroll(db.stmt, SQL_FETCH_NEXT, 1)) rowRes.setLen(max(cCnt,0))
result.add(rowRes) tempcCnt = cCnt
while res == SQL_SUCCESS:
for colId in 1..cCnt:
buf[0] = '\0'
db.sqlCheck(SQLGetData(db.stmt, colId.SqlUSmallInt, SQL_C_CHAR,
cast[cstring](buf.addr), 4095.TSqlSmallInt, sz.addr))
rowRes[colId-1] = $buf.cstring
cCnt = tempcCnt
rows.add(rowRes)
res = SQLFetch(db.stmt)
result = rows
db.sqlCheck(res)
properFreeResult(SQL_HANDLE_STMT, db.stmt) properFreeResult(SQL_HANDLE_STMT, db.stmt)
iterator rows*(db: var DbConn, query: SqlQuery, iterator rows*(db: var DbConn, query: SqlQuery,
@ -407,10 +453,9 @@ proc tryInsertId*(db: var DbConn, query: SqlQuery,
if not tryExec(db, query, args): if not tryExec(db, query, args):
result = -1'i64 result = -1'i64
else: else:
echo "DBMS: ",SqlGetDBMS(db).toLower()
result = -1'i64 result = -1'i64
try: try:
case SqlGetDBMS(db).toLower(): case sqlGetDBMS(db).toLower():
of "postgresql": of "postgresql":
result = getValue(db, sql"SELECT LASTVAL();", []).parseInt result = getValue(db, sql"SELECT LASTVAL();", []).parseInt
of "mysql": of "mysql":
@ -438,15 +483,12 @@ proc execAffectedRows*(db: var DbConn, query: SqlQuery,
## Runs the query (typically "UPDATE") and returns the ## Runs the query (typically "UPDATE") and returns the
## number of affected rows ## number of affected rows
result = -1 result = -1
var res = SQLAllocHandle(SQL_HANDLE_STMT, db.hDb, db.stmt.SqlHandle) db.sqlCheck(SQLAllocHandle(SQL_HANDLE_STMT, db.hDb, db.stmt.SqlHandle))
if res != SQL_SUCCESS: dbError(db)
var q = dbFormat(query, args) var q = dbFormat(query, args)
res = SQLPrepare(db.stmt, q.PSQLCHAR, q.len.TSqlSmallInt) db.sqlCheck(SQLPrepare(db.stmt, q.PSQLCHAR, q.len.TSqlSmallInt))
if res != SQL_SUCCESS: dbError(db)
rawExec(db, query, args) rawExec(db, query, args)
var rCnt = -1 var rCnt = -1
result = SQLRowCount(db.hDb, rCnt) db.sqlCheck(SQLRowCount(db.hDb, rCnt))
if res != SQL_SUCCESS: dbError(db)
properFreeResult(SQL_HANDLE_STMT, db.stmt) properFreeResult(SQL_HANDLE_STMT, db.stmt)
result = rCnt result = rCnt
@ -501,5 +543,5 @@ proc setEncoding*(connection: DbConn, encoding: string): bool {.
## ##
## Sets the encoding of a database connection, returns true for ## Sets the encoding of a database connection, returns true for
## success, false for failure. ## success, false for failure.
#result = set_character_set(connection, encoding) == 0 ##result = set_character_set(connection, encoding) == 0
dbError("setEncoding() is currently not implemented by the db_odbc module") dbError("setEncoding() is currently not implemented by the db_odbc module")