For some reason this is not catching the exception and crashing with a "SIGSEGV: Illegal storage access. (Attempt to read from nil?)"
proc select_all(): Future[seq[Table[string, string]]] {.async.} =
var dbC: DbColumns = @[]
var data = newSeq[Table[string, string]]()
for x in db.instantRows(dbC, sql(SELECT_ALL % table_name)):
var row = initTable[string, string]()
for idx, column in dbC:
try:
row[column.name] = "$#" % x[idx]
except:
row[column.name] = "none"
data.add(row)
result = data
Am I doing something wrong...?
Edit: This only happens if the field in Mysql is defaulted to NULL.
I think you want this:
for idx, column in dbC:
row[column.name] = if x[idx] == nil: "none" else: x[idx]
I had used this before.. and this correctly shows the NULL values for the example which crashes in the higher level case. I don't yet see how that is happening :)
import strutils, os
# NOTE: Some versions of the nim library have the wrong size for TFIELDS
import mysql
template mRaise(m: string) =
var e = new IOError
e.msg = m
raise e
template mRaise(con: PMySQL) =
var e = new IOError
e.msg = $ mysql.error(con)
raise e
template traceIt(m: string, doit: stmt): stmt =
echo "Trace: ", m
doit
proc showFields*(res: PRES) =
let fnum = int(mysql.numFields(res)) # cast string to int
#var fnames: seq[string]
#newSeq(fnames, fnum)
for i in 0.. <fnum:
let fp = mysql.fetch_field_direct(res, cast[cint](i))
case fp.ftype
of FIELD_TYPE_VAR_STRING: echo i, " ", fp.name, " (vstring)"
of FIELD_TYPE_STRING: echo i, " ", fp.name, " (string)"
of FIELD_TYPE_LONG: echo i, " ", fp.name, " (int32)"
of FIELD_TYPE_INT24: echo i, " ", fp.name, " (int24)"
of FIELD_TYPE_TINY: echo i, " ", fp.name, " (int8)"
of FIELD_TYPE_DECIMAL: echo i, " ", fp.name, " (decimal)"
of FIELD_TYPE_TIMESTAMP: echo i, " ", fp.name, " (timestamp)"
of FIELD_TYPE_DATE: echo i, " ", fp.name, " (date)"
of FIELD_TYPE_BLOB: echo i, " ", fp.name, " (binary)"
else: echo i, " ", fp.name, " (", fp.ftype, ")!!!!!!!!!!!!!!!!!!"
#fnames.add ($fp.name)
let con: PMySQL = mysql.init(nil)
if con == nil: mRaise "init failed"
if mysql.realConnect(con, "localhost", "root", "", "test", 0, nil, 0) == nil:
defer: traceIt "close con", mysql.close(con)
mRaise con
let q = "SELECT * FROM data"
if mysql.realQuery(con, q, q.len) != 0: mRaise con
#var res = mysql.storeResult(con)
var res = mysql.useResult(con)
if res != nil:
showFields res
#var f = cast[ptr array[2, TFIELD]](mysql.fetch_fields(res))
#quit 0
#echo res.row_count
#echo mysql.affected_rows con
#echo mysql.num_rows res
var row: cstringArray
while true:
row = mysql.fetchRow(res)
if row == nil: break
let fnum = int(mysql.numFields(res)) # cast string to int
for i in 0.. <fnum:
if row[i] == nil:
echo "NULL"
else:
echo row[i]
if row != nil:
while mysql.fetchRow(res) != nil: discard
mysql.freeResult(res)
There seems to be a compiler problem probably.
When I rewrite db_mysql like this:
iterator instantRows*(db: DbConn; columns: var DbColumns; query: SqlQuery;
args: varargs[string, `$`]): InstantRow =
## Same as fastRows but returns a handle that can be used to get column text
## on demand using []. Returned handle is valid only within the iterator body.
rawExec(db, query, args)
var sqlres = mysql.useResult(db)
if sqlres != nil:
let L = int(mysql.numFields(sqlres))
setColumnInfo(columns, sqlres, L)
var row: cstringArray
while true:
row = mysql.fetchRow(sqlres)
if row == nil: break
for f in 0.. <L:
if row[f] == nil: # HERE the nil check is working
row[f] = "NULL".cstring
yield InstantRow(row: row, len: L) # after being added into the InstantRow type the "nil" seems to bring a problem I don't understand
properFreeResult(sqlres, row)