Merge branch 'master' of https://github.com/Araq/Nim into patch-global-log-level

This commit is contained in:
Bruce Doan 2016-01-20 22:40:32 +07:00
commit 863dd62bc4
186 changed files with 5652 additions and 4330 deletions

View file

@ -97,7 +97,7 @@ type
nskUnknown, nskConditional, nskDynLib, nskParam,
nskGenericParam, nskTemp, nskModule, nskType, nskVar, nskLet,
nskConst, nskResult,
nskProc, nskMethod, nskIterator, nskClosureIterator,
nskProc, nskMethod, nskIterator,
nskConverter, nskMacro, nskTemplate, nskField,
nskEnumField, nskForVar, nskLabel,
nskStub
@ -416,8 +416,7 @@ proc newLit*(i: BiggestInt): NimNode {.compileTime.} =
proc newLit*(b: bool): NimNode {.compileTime.} =
## produces a new boolean literal node.
result = newNimNode(nnkIntLit)
result.intVal = ord(b)
result = if b: bindSym"true" else: bindSym"false"
proc newLit*(f: BiggestFloat): NimNode {.compileTime.} =
## produces a new float literal node.

View file

@ -61,7 +61,10 @@ type
## wrapped value and **must not** live longer than
## its wrapped value.
value: pointer
rawType: PNimType
when defined(js):
rawType: PNimType
else:
rawTypePtr: pointer
ppointer = ptr pointer
pbyteArray = ptr array[0.. 0xffff, int8]
@ -71,6 +74,14 @@ type
when defined(gogc):
elemSize: int
PGenSeq = ptr TGenericSeq
when not defined(js):
template rawType(x: Any): PNimType =
cast[PNimType](x.rawTypePtr)
template `rawType=`(x: var Any, p: PNimType) =
x.rawTypePtr = cast[pointer](p)
{.deprecated: [TAny: Any, TAnyKind: AnyKind].}
when defined(gogc):
@ -108,7 +119,7 @@ proc selectBranch(aa: pointer, n: ptr TNimNode): ptr TNimNode =
else:
result = n.sons[n.len]
proc newAny(value: pointer, rawType: PNimType): Any =
proc newAny(value: pointer, rawType: PNimType): Any {.inline.} =
result.value = value
result.rawType = rawType
@ -126,8 +137,7 @@ proc toAny*[T](x: var T): Any {.inline.} =
## constructs a ``Any`` object from `x`. This captures `x`'s address, so
## `x` can be modified with its ``Any`` wrapper! The client needs to ensure
## that the wrapper **does not** live longer than `x`!
result.value = addr(x)
result.rawType = cast[PNimType](getTypeInfo(x))
newAny(addr(x), cast[PNimType](getTypeInfo(x)))
proc kind*(x: Any): AnyKind {.inline.} =
## get the type kind
@ -345,7 +355,7 @@ proc `[]`*(x: Any, fieldName: string): Any =
result.value = x.value +!! n.offset
result.rawType = n.typ
elif x.rawType.kind == tyObject and x.rawType.base != nil:
return `[]`(Any(value: x.value, rawType: x.rawType.base), fieldName)
return `[]`(newAny(x.value, x.rawType.base), fieldName)
else:
raise newException(ValueError, "invalid field name: " & fieldName)

View file

@ -1,7 +1,7 @@
#
#
# Nim's Runtime Library
# (c) Copyright 2012 Andreas Rumpf
# (c) Copyright 2015 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@ -10,7 +10,49 @@
## A higher level `mySQL`:idx: database wrapper. The same interface is
## implemented for other databases too.
##
## Example:
## See also: `db_odbc <db_odbc.html>`_, `db_sqlite <db_sqlite.html>`_,
## `db_postgres <db_postgres.html>`_.
##
## Parameter substitution
## ----------------------
##
## All ``db_*`` modules support the same form of parameter substitution.
## That is, using the ``?`` (question mark) to signify the place where a
## value should be placed. For example:
##
## .. code-block:: Nim
## sql"INSERT INTO myTable (colA, colB, colC) VALUES (?, ?, ?)"
##
##
## Examples
## --------
##
## Opening a connection to a database
## ==================================
##
## .. code-block:: Nim
## import db_mysql
## let db = open("localhost", "user", "password", "dbname")
## db.close()
##
## Creating a table
## ================
##
## .. code-block:: Nim
## db.exec(sql"DROP TABLE IF EXISTS myTable")
## db.exec(sql("""CREATE TABLE myTable (
## id integer,
## name varchar(50) not null)"""))
##
## Inserting data
## ==============
##
## .. code-block:: Nim
## db.exec(sql"INSERT INTO myTable (id, name) VALUES (0, ?)",
## "Dominik")
##
## Larger example
## ==============
##
## .. code-block:: Nim
##
@ -43,45 +85,26 @@
import strutils, mysql
import db_common
export db_common
type
DbConn* = PMySQL ## encapsulates a database connection
DbConn* = PMySQL ## encapsulates a database connection
Row* = seq[string] ## a row of a dataset. NULL database values will be
## transformed always to the empty string.
InstantRow* = tuple[row: cstringArray, len: int] ## a handle that can be
## used to get a row's
## column text on demand
EDb* = object of IOError ## exception that is raised if a database error occurs
## converted to nil.
InstantRow* = object ## a handle that can be used to get a row's
## column text on demand
row: cstringArray
len: int
{.deprecated: [TRow: Row, TDbConn: DbConn].}
SqlQuery* = distinct string ## an SQL query string
FDb* = object of IOEffect ## effect that denotes a database operation
FReadDb* = object of FDb ## effect that denotes a read operation
FWriteDb* = object of FDb ## effect that denotes a write operation
{.deprecated: [TRow: Row, TSqlQuery: SqlQuery, TDbConn: DbConn].}
proc sql*(query: string): SqlQuery {.noSideEffect, inline.} =
## constructs a SqlQuery from the string `query`. This is supposed to be
## used as a raw-string-literal modifier:
## ``sql"update user set counter = counter + 1"``
##
## If assertions are turned off, it does nothing. If assertions are turned
## on, later versions will check the string for valid syntax.
result = SqlQuery(query)
proc dbError(db: DbConn) {.noreturn.} =
## raises an EDb exception.
var e: ref EDb
proc dbError*(db: DbConn) {.noreturn.} =
## raises a DbError exception.
var e: ref DbError
new(e)
e.msg = $mysql.error(db)
raise e
proc dbError*(msg: string) {.noreturn.} =
## raises an EDb exception with message `msg`.
var e: ref EDb
new(e)
e.msg = msg
raise e
when false:
proc dbQueryOpt*(db: DbConn, query: string, args: varargs[string, `$`]) =
var stmt = mysql_stmt_init(db)
@ -114,7 +137,7 @@ proc dbFormat(formatstr: SqlQuery, args: varargs[string]): string =
add(result, c)
proc tryExec*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]): bool {.
tags: [FReadDB, FWriteDb].} =
tags: [ReadDbEffect, WriteDbEffect].} =
## tries to execute the query and returns true if successful, false otherwise.
var q = dbFormat(query, args)
return mysql.realQuery(db, q, q.len) == 0'i32
@ -124,7 +147,7 @@ proc rawExec(db: DbConn, query: SqlQuery, args: varargs[string, `$`]) =
if mysql.realQuery(db, q, q.len) != 0'i32: dbError(db)
proc exec*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]) {.
tags: [FReadDB, FWriteDb].} =
tags: [ReadDbEffect, WriteDbEffect].} =
## executes the query and raises EDB if not successful.
var q = dbFormat(query, args)
if mysql.realQuery(db, q, q.len) != 0'i32: dbError(db)
@ -139,7 +162,7 @@ proc properFreeResult(sqlres: mysql.PRES, row: cstringArray) =
mysql.freeResult(sqlres)
iterator fastRows*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): Row {.tags: [FReadDB].} =
args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} =
## executes the query and iterates over the result dataset.
##
## This is very fast, but potentially dangerous. Use this iterator only
@ -167,9 +190,9 @@ iterator fastRows*(db: DbConn, query: SqlQuery,
iterator instantRows*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): InstantRow
{.tags: [FReadDb].} =
## 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 interator body.
{.tags: [ReadDbEffect].} =
## 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:
@ -178,20 +201,102 @@ iterator instantRows*(db: DbConn, query: SqlQuery,
while true:
row = mysql.fetchRow(sqlres)
if row == nil: break
yield (row: row, len: L)
yield InstantRow(row: row, len: L)
properFreeResult(sqlres, row)
proc setTypeName(t: var DbType; f: PFIELD) =
shallowCopy(t.name, $f.name)
t.maxReprLen = Natural(f.max_length)
if (NOT_NULL_FLAG and f.flags) != 0: t.notNull = true
case f.ftype
of TYPE_DECIMAL:
t.kind = dbDecimal
of TYPE_TINY:
t.kind = dbInt
t.size = 1
of TYPE_SHORT:
t.kind = dbInt
t.size = 2
of TYPE_LONG:
t.kind = dbInt
t.size = 4
of TYPE_FLOAT:
t.kind = dbFloat
t.size = 4
of TYPE_DOUBLE:
t.kind = dbFloat
t.size = 8
of TYPE_NULL:
t.kind = dbNull
of TYPE_TIMESTAMP:
t.kind = dbTimestamp
of TYPE_LONGLONG:
t.kind = dbInt
t.size = 8
of TYPE_INT24:
t.kind = dbInt
t.size = 3
of TYPE_DATE:
t.kind = dbDate
of TYPE_TIME:
t.kind = dbTime
of TYPE_DATETIME:
t.kind = dbDatetime
of TYPE_YEAR:
t.kind = dbDate
of TYPE_NEWDATE:
t.kind = dbDate
of TYPE_VARCHAR, TYPE_VAR_STRING, TYPE_STRING:
t.kind = dbVarchar
of TYPE_BIT:
t.kind = dbBit
of TYPE_NEWDECIMAL:
t.kind = dbDecimal
of TYPE_ENUM: t.kind = dbEnum
of TYPE_SET: t.kind = dbSet
of TYPE_TINY_BLOB, TYPE_MEDIUM_BLOB, TYPE_LONG_BLOB,
TYPE_BLOB: t.kind = dbBlob
of TYPE_GEOMETRY:
t.kind = dbGeometry
proc setColumnInfo(columns: var DbColumns; res: PRES; L: int) =
setLen(columns, L)
for i in 0..<L:
let fp = mysql.fetch_field_direct(res, cint(i))
setTypeName(columns[i].typ, fp)
columns[i].name = $fp.name
columns[i].tableName = $fp.table
columns[i].primaryKey = (fp.flags and PRI_KEY_FLAG) != 0
#columns[i].foreignKey = there is no such thing in mysql
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
yield InstantRow(row: row, len: L)
properFreeResult(sqlres, row)
proc `[]`*(row: InstantRow, col: int): string {.inline.} =
## returns text for given column of the row
## Returns text for given column of the row.
$row.row[col]
proc len*(row: InstantRow): int {.inline.} =
## returns number of columns in the row
## Returns number of columns in the row.
row.len
proc getRow*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): Row {.tags: [FReadDB].} =
## retrieves a single row. If the query doesn't return any rows, this proc
args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} =
## Retrieves a single row. If the query doesn't return any rows, this proc
## will return a Row with empty strings for each column.
rawExec(db, query, args)
var sqlres = mysql.useResult(db)
@ -209,7 +314,7 @@ proc getRow*(db: DbConn, query: SqlQuery,
properFreeResult(sqlres, row)
proc getAllRows*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): seq[Row] {.tags: [FReadDB].} =
args: varargs[string, `$`]): seq[Row] {.tags: [ReadDbEffect].} =
## executes the query and returns the whole result dataset.
result = @[]
rawExec(db, query, args)
@ -232,19 +337,19 @@ proc getAllRows*(db: DbConn, query: SqlQuery,
mysql.freeResult(sqlres)
iterator rows*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): Row {.tags: [FReadDB].} =
args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} =
## same as `fastRows`, but slower and safe.
for r in items(getAllRows(db, query, args)): yield r
proc getValue*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): string {.tags: [FReadDB].} =
args: varargs[string, `$`]): string {.tags: [ReadDbEffect].} =
## executes the query and returns the first column of the first row of the
## result dataset. Returns "" if the dataset contains no rows or the database
## value is NULL.
result = getRow(db, query, args)[0]
proc tryInsertId*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} =
args: varargs[string, `$`]): int64 {.tags: [WriteDbEffect].} =
## executes the query (typically "INSERT") and returns the
## generated ID for the row or -1 in case of an error.
var q = dbFormat(query, args)
@ -254,7 +359,7 @@ proc tryInsertId*(db: DbConn, query: SqlQuery,
result = mysql.insertId(db)
proc insertId*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} =
args: varargs[string, `$`]): int64 {.tags: [WriteDbEffect].} =
## executes the query (typically "INSERT") and returns the
## generated ID for the row.
result = tryInsertID(db, query, args)
@ -262,18 +367,18 @@ proc insertId*(db: DbConn, query: SqlQuery,
proc execAffectedRows*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): int64 {.
tags: [FReadDB, FWriteDb].} =
tags: [ReadDbEffect, WriteDbEffect].} =
## runs the query (typically "UPDATE") and returns the
## number of affected rows
rawExec(db, query, args)
result = mysql.affectedRows(db)
proc close*(db: DbConn) {.tags: [FDb].} =
proc close*(db: DbConn) {.tags: [DbEffect].} =
## closes the database connection.
if db != nil: mysql.close(db)
proc open*(connection, user, password, database: string): DbConn {.
tags: [FDb].} =
tags: [DbEffect].} =
## opens a database connection. Raises `EDb` if the connection could not
## be established.
result = mysql.init(nil)
@ -291,7 +396,7 @@ proc open*(connection, user, password, database: string): DbConn {.
dbError(errmsg)
proc setEncoding*(connection: DbConn, encoding: string): bool {.
tags: [FDb].} =
tags: [DbEffect].} =
## sets the encoding of a database connection, returns true for
## success, false for failure.
result = mysql.set_character_set(connection, encoding) == 0

505
lib/impure/db_odbc.nim Normal file
View file

@ -0,0 +1,505 @@
#
#
# Nim's Runtime Library
# (c) Copyright 2015 Nim Contributors
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## A higher level `ODBC` database wrapper.
##
## This is the same interface that is implemented for other databases.
##
## This has NOT yet been (extensively) tested against ODBC drivers for
## Teradata, Oracle, Sybase, MSSqlvSvr, et. al. databases.
##
## Currently all queries are ANSI calls, not Unicode.
##
## See also: `db_postgres <db_postgres.html>`_, `db_sqlite <db_sqlite.html>`_,
## `db_mysql <db_mysql.html>`_.
##
## Parameter substitution
## ----------------------
##
## All ``db_*`` modules support the same form of parameter substitution.
## That is, using the ``?`` (question mark) to signify the place where a
## value should be placed. For example:
##
## .. code-block:: Nim
## sql"INSERT INTO myTable (colA, colB, colC) VALUES (?, ?, ?)"
##
##
## Examples
## --------
##
## Opening a connection to a database
## ==================================
##
## .. code-block:: Nim
## import db_odbc
## let db = open("localhost", "user", "password", "dbname")
## db.close()
##
## Creating a table
## ================
##
## .. code-block:: Nim
## db.exec(sql"DROP TABLE IF EXISTS myTable")
## db.exec(sql("""CREATE TABLE myTable (
## id integer,
## name varchar(50) not null)"""))
##
## Inserting data
## ==============
##
## .. code-block:: Nim
## db.exec(sql"INSERT INTO myTable (id, name) VALUES (0, ?)",
## "Andreas")
##
## Large example
## =============
##
## .. code-block:: Nim
##
## import db_odbc, math
##
## let theDb = open("localhost", "nim", "nim", "test")
##
## theDb.exec(sql"Drop table if exists myTestTbl")
## theDb.exec(sql("create table myTestTbl (" &
## " Id INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY, " &
## " Name VARCHAR(50) NOT NULL, " &
## " i INT(11), " &
## " f DECIMAL(18,10))"))
##
## theDb.exec(sql"START TRANSACTION")
## for i in 1..1000:
## theDb.exec(sql"INSERT INTO myTestTbl (name,i,f) VALUES (?,?,?)",
## "Item#" & $i, i, sqrt(i.float))
## theDb.exec(sql"COMMIT")
##
## for x in theDb.fastRows(sql"select * from myTestTbl"):
## echo x
##
## let id = theDb.tryInsertId(sql"INSERT INTO myTestTbl (name,i,f) VALUES (?,?,?)",
## "Item#1001", 1001, sqrt(1001.0))
## echo "Inserted item: ", theDb.getValue(sql"SELECT name FROM myTestTbl WHERE id=?", id)
##
## theDb.close()
import strutils, odbcsql
import db_common
export db_common
type
OdbcConnTyp = tuple[hDb: SqlHDBC, env: SqlHEnv, stmt: SqlHStmt]
DbConn* = OdbcConnTyp ## encapsulates a database connection
Row* = seq[string] ## a row of a dataset. NULL database values will be
## converted to nil.
InstantRow* = tuple[row: seq[string], len: int] ## a handle that can be
## used to get a row's
## column text on demand
{.deprecated: [TRow: Row, TSqlQuery: SqlQuery, TDbConn: DbConn].}
var
buf: array[0..4096, char]
proc properFreeResult(hType: int, sqlres: var SqlHandle) {.
tags: [WriteDbEffect], raises: [].} =
try:
discard SQLFreeHandle(hType.TSqlSmallInt, sqlres)
sqlres = nil
except: discard
proc getErrInfo(db: var DbConn): tuple[res: int, ss, ne, msg: string] {.
tags: [ReadDbEffect], raises: [].} =
## Returns ODBC error information
var
sqlState: array[0..512, char]
nativeErr: array[0..512, char]
errMsg: array[0..512, char]
retSz: TSqlSmallInt = 0
res: TSqlSmallInt = 0
try:
sqlState[0] = '\0'
nativeErr[0] = '\0'
errMsg[0] = '\0'
res = SQLErr(db.env, db.hDb, db.stmt,
cast[PSQLCHAR](sqlState.addr),
cast[PSQLCHAR](nativeErr.addr),
cast[PSQLCHAR](errMsg.addr),
511.TSqlSmallInt, retSz.addr.PSQLSMALLINT)
except:
discard
return (res.int, $sqlState, $nativeErr, $errMsg)
proc dbError*(db: var DbConn) {.
tags: [ReadDbEffect, WriteDbEffect], raises: [DbError] .} =
## Raises an `[DbError]` exception with ODBC error information
var
e: ref DbError
ss, ne, msg: string = ""
isAnError = false
res: int = 0
prevSs = ""
while true:
prevSs = ss
(res, ss, ne, msg) = db.getErrInfo()
if prevSs == ss:
break
# sqlState of 00000 is not an error
elif ss == "00000":
break
elif ss == "01000":
echo "\nWarning: ", ss, " ", msg
continue
else:
isAnError = true
echo "\nError: ", ss, " ", msg
if isAnError:
new(e)
e.msg = "ODBC Error"
if db.stmt != nil:
properFreeResult(SQL_HANDLE_STMT, db.stmt)
properFreeResult(SQL_HANDLE_DBC, db.hDb)
properFreeResult(SQL_HANDLE_ENV, db.env)
raise e
proc SqlCheck(db: var DbConn, resVal: TSqlSmallInt) {.raises: [DbError]} =
## Wrapper that checks if ``resVal`` is not SQL_SUCCESS and if so, raises [EDb]
if resVal != SQL_SUCCESS: dbError(db)
proc SqlGetDBMS(db: var DbConn): string {.
tags: [ReadDbEffect, WriteDbEffect], raises: [] .} =
## Returns the ODBC SQL_DBMS_NAME string
const
SQL_DBMS_NAME = 17.SqlUSmallInt
var
sz: TSqlSmallInt = 0
buf[0] = '\0'
try:
db.SqlCheck(SQLGetInfo(db.hDb, SQL_DBMS_NAME, cast[SqlPointer](buf.addr),
4095.TSqlSmallInt, sz.addr))
except: discard
return $buf.cstring
proc dbQuote*(s: string): string {.noSideEffect.} =
## DB quotes the string.
result = "'"
for c in items(s):
if c == '\'': add(result, "''")
else: add(result, c)
add(result, '\'')
proc dbFormat(formatstr: SqlQuery, args: varargs[string]): string {.
noSideEffect.} =
## Replace any ``?`` placeholders with `args`,
## and quotes the arguments
result = ""
var a = 0
for c in items(string(formatstr)):
if c == '?':
if args[a] == nil:
add(result, "NULL")
else:
add(result, dbQuote(args[a]))
inc(a)
else:
add(result, c)
proc prepareFetch(db: var DbConn, query: SqlQuery,
args: varargs[string, `$`]) {.
tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} =
# Prepare a statement, execute it and fetch the data to the driver
# ready for retrieval of the data
# Used internally by iterators and retrieval procs
# requires calling
# properFreeResult(SQL_HANDLE_STMT, db.stmt)
# when finished
db.SqlCheck(SQLAllocHandle(SQL_HANDLE_STMT, db.hDb, db.stmt))
var q = dbFormat(query, args)
db.SqlCheck(SQLPrepare(db.stmt, q.PSQLCHAR, q.len.TSqlSmallInt))
db.SqlCheck(SQLExecute(db.stmt))
db.SqlCheck(SQLFetch(db.stmt))
proc prepareFetchDirect(db: var DbConn, query: SqlQuery,
args: varargs[string, `$`]) {.
tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} =
# Prepare a statement, execute it and fetch the data to the driver
# ready for retrieval of the data
# Used internally by iterators and retrieval procs
# requires calling
# properFreeResult(SQL_HANDLE_STMT, db.stmt)
# when finished
db.SqlCheck(SQLAllocHandle(SQL_HANDLE_STMT, db.hDb, db.stmt))
var q = dbFormat(query, args)
db.SqlCheck(SQLExecDirect(db.stmt, q.PSQLCHAR, q.len.TSqlSmallInt))
db.SqlCheck(SQLFetch(db.stmt))
proc tryExec*(db: var DbConn, query: SqlQuery, args: varargs[string, `$`]): bool {.
tags: [ReadDbEffect, WriteDbEffect], raises: [].} =
## Tries to execute the query and returns true if successful, false otherwise.
var
res:TSqlSmallInt = -1
try:
db.prepareFetchDirect(query, args)
var
rCnt = -1
res = SQLRowCount(db.stmt, rCnt)
if res != SQL_SUCCESS: dbError(db)
properFreeResult(SQL_HANDLE_STMT, db.stmt)
except: discard
return res == SQL_SUCCESS
proc rawExec(db: var DbConn, query: SqlQuery, args: varargs[string, `$`]) {.
tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} =
db.prepareFetchDirect(query, args)
proc exec*(db: var DbConn, query: SqlQuery, args: varargs[string, `$`]) {.
tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} =
## Executes the query and raises EDB if not successful.
db.prepareFetchDirect(query, args)
properFreeResult(SQL_HANDLE_STMT, db.stmt)
proc newRow(L: int): Row {.noSideEFfect.} =
newSeq(result, L)
for i in 0..L-1: result[i] = ""
iterator fastRows*(db: var DbConn, query: SqlQuery,
args: varargs[string, `$`]): Row {.
tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} =
## Executes the query and iterates over the result dataset.
##
## This is very fast, but potentially dangerous. Use this iterator only
## if you require **ALL** the rows.
##
## Breaking the fastRows() iterator during a loop may cause a driver error
## for subsequenct queries
##
## Rows are retrieved from the server at each iteration.
var
rowRes: Row
sz: TSqlSmallInt = 0
cCnt: TSqlSmallInt = 0.TSqlSmallInt
rCnt = -1
db.prepareFetch(query, args)
db.SqlCheck(SQLNumResultCols(db.stmt, cCnt))
db.SqlCheck(SQLRowCount(db.stmt, rCnt))
rowRes = newRow(cCnt)
for rNr in 1..rCnt:
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
db.SqlCheck(SQLFetchScroll(db.stmt, SQL_FETCH_NEXT, 1))
yield rowRes
properFreeResult(SQL_HANDLE_STMT, db.stmt)
iterator instantRows*(db: var DbConn, query: SqlQuery,
args: varargs[string, `$`]): InstantRow
{.tags: [ReadDbEffect, WriteDbEffect].} =
## 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 interator body.
var
rowRes: Row
sz: TSqlSmallInt = 0
cCnt: TSqlSmallInt = 0.TSqlSmallInt
rCnt = -1
db.prepareFetch(query, args)
db.SqlCheck(SQLNumResultCols(db.stmt, cCnt))
db.SqlCheck(SQLRowCount(db.stmt, rCnt))
rowRes = newRow(cCnt)
for rNr in 1..rCnt:
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
db.SqlCheck(SQLFetchScroll(db.stmt, SQL_FETCH_NEXT, 1))
yield (row: rowRes, len: cCnt.int)
properFreeResult(SQL_HANDLE_STMT, db.stmt)
proc `[]`*(row: InstantRow, col: int): string {.inline.} =
## Returns text for given column of the row
row.row[col]
proc len*(row: InstantRow): int {.inline.} =
## Returns number of columns in the row
row.len
proc getRow*(db: var DbConn, query: SqlQuery,
args: varargs[string, `$`]): Row {.
tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} =
## Retrieves a single row. If the query doesn't return any rows, this proc
## will return a Row with empty strings for each column.
var
sz: TSqlSmallInt = 0.TSqlSmallInt
cCnt: TSqlSmallInt = 0.TSqlSmallInt
rCnt = -1
result = @[]
db.prepareFetch(query, args)
db.SqlCheck(SQLNumResultCols(db.stmt, cCnt))
db.SqlCheck(SQLRowCount(db.stmt, rCnt))
for colId in 1..cCnt:
db.SqlCheck(SQLGetData(db.stmt, colId.SqlUSmallInt, SQL_C_CHAR,
cast[cstring](buf.addr), 4095.TSqlSmallInt, sz.addr))
result.add($buf.cstring)
db.SqlCheck(SQLFetchScroll(db.stmt, SQL_FETCH_NEXT, 1))
properFreeResult(SQL_HANDLE_STMT, db.stmt)
proc getAllRows*(db: var DbConn, query: SqlQuery,
args: varargs[string, `$`]): seq[Row] {.
tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} =
## Executes the query and returns the whole result dataset.
var
rowRes: Row
sz: TSqlSmallInt = 0
cCnt: TSqlSmallInt = 0.TSqlSmallInt
rCnt = -1
db.prepareFetch(query, args)
db.SqlCheck(SQLNumResultCols(db.stmt, cCnt))
db.SqlCheck(SQLRowCount(db.stmt, rCnt))
result = @[]
for rNr in 1..rCnt:
rowRes = @[]
buf[0] = '\0'
for colId in 1..cCnt:
db.SqlCheck(SQLGetData(db.stmt, colId.SqlUSmallInt, SQL_C_CHAR,
cast[SqlPointer](buf.addr), 4095.TSqlSmallInt, sz.addr))
rowRes.add($buf.cstring)
db.SqlCheck(SQLFetchScroll(db.stmt, SQL_FETCH_NEXT, 1))
result.add(rowRes)
properFreeResult(SQL_HANDLE_STMT, db.stmt)
iterator rows*(db: var DbConn, query: SqlQuery,
args: varargs[string, `$`]): Row {.
tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} =
## Same as `fastRows`, but slower and safe.
##
## This retrieves ALL rows into memory before
## iterating through the rows.
## Large dataset queries will impact on memory usage.
for r in items(getAllRows(db, query, args)): yield r
proc getValue*(db: var DbConn, query: SqlQuery,
args: varargs[string, `$`]): string {.
tags: [ReadDbEffect, WriteDbEffect], raises: [].} =
## Executes the query and returns the first column of the first row of the
## result dataset. Returns "" if the dataset contains no rows or the database
## value is NULL.
result = ""
try:
result = getRow(db, query, args)[0]
except: discard
proc tryInsertId*(db: var DbConn, query: SqlQuery,
args: varargs[string, `$`]): int64 {.
tags: [ReadDbEffect, WriteDbEffect], raises: [].} =
## Executes the query (typically "INSERT") and returns the
## generated ID for the row or -1 in case of an error.
if not tryExec(db, query, args):
result = -1'i64
else:
echo "DBMS: ",SqlGetDBMS(db).toLower()
result = -1'i64
try:
case SqlGetDBMS(db).toLower():
of "postgresql":
result = getValue(db, sql"SELECT LASTVAL();", []).parseInt
of "mysql":
result = getValue(db, sql"SELECT LAST_INSERT_ID();", []).parseInt
of "sqlite":
result = getValue(db, sql"SELECT LAST_INSERT_ROWID();", []).parseInt
of "microsoft sql server":
result = getValue(db, sql"SELECT SCOPE_IDENTITY();", []).parseInt
of "oracle":
result = getValue(db, sql"SELECT id.currval FROM DUAL;", []).parseInt
else: result = -1'i64
except: discard
proc insertId*(db: var DbConn, query: SqlQuery,
args: varargs[string, `$`]): int64 {.
tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} =
## Executes the query (typically "INSERT") and returns the
## generated ID for the row.
result = tryInsertID(db, query, args)
if result < 0: dbError(db)
proc execAffectedRows*(db: var DbConn, query: SqlQuery,
args: varargs[string, `$`]): int64 {.
tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} =
## Runs the query (typically "UPDATE") and returns the
## number of affected rows
result = -1
var res = SQLAllocHandle(SQL_HANDLE_STMT, db.hDb, db.stmt.SqlHandle)
if res != SQL_SUCCESS: dbError(db)
var q = dbFormat(query, args)
res = SQLPrepare(db.stmt, q.PSQLCHAR, q.len.TSqlSmallInt)
if res != SQL_SUCCESS: dbError(db)
rawExec(db, query, args)
var rCnt = -1
result = SQLRowCount(db.hDb, rCnt)
if res != SQL_SUCCESS: dbError(db)
properFreeResult(SQL_HANDLE_STMT, db.stmt)
result = rCnt
proc close*(db: var DbConn) {.
tags: [WriteDbEffect], raises: [].} =
## Closes the database connection.
if db.hDb != nil:
try:
var res = SQLDisconnect(db.hDb)
if db.stmt != nil:
res = SQLFreeHandle(SQL_HANDLE_STMT, db.stmt)
res = SQLFreeHandle(SQL_HANDLE_DBC, db.hDb)
res = SQLFreeHandle(SQL_HANDLE_ENV, db.env)
db = (hDb: nil, env: nil, stmt: nil)
except:
discard
proc open*(connection, user, password, database: string): DbConn {.
tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} =
## Opens a database connection.
##
## Raises `EDb` if the connection could not be established.
##
## Currently the database parameter is ignored,
## but included to match ``open()`` in the other db_xxxxx library modules.
var
val: TSqlInteger = SQL_OV_ODBC3
resLen = 0
result = (hDb: nil, env: nil, stmt: nil)
# allocate environment handle
var res = SQLAllocHandle(SQL_HANDLE_ENV, result.env, result.env)
if res != SQL_SUCCESS: dbError("Error: unable to initialise ODBC environment.")
res = SQLSetEnvAttr(result.env,
SQL_ATTR_ODBC_VERSION.TSqlInteger,
val, resLen.TSqlInteger)
if res != SQL_SUCCESS: dbError("Error: unable to set ODBC driver version.")
# allocate hDb handle
res = SQLAllocHandle(SQL_HANDLE_DBC, result.env, result.hDb)
if res != SQL_SUCCESS: dbError("Error: unable to allocate connection handle.")
# Connect: connection = dsn str,
res = SQLConnect(result.hDb,
connection.PSQLCHAR , connection.len.TSqlSmallInt,
user.PSQLCHAR, user.len.TSqlSmallInt,
password.PSQLCHAR, password.len.TSqlSmallInt)
if res != SQL_SUCCESS:
result.dbError()
proc setEncoding*(connection: DbConn, encoding: string): bool {.
tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} =
## Currently not implemented for ODBC.
##
## Sets the encoding of a database connection, returns true for
## success, false for failure.
#result = set_character_set(connection, encoding) == 0
dbError("setEncoding() is currently not implemented by the db_odbc module")

View file

@ -10,6 +10,9 @@
## A higher level `PostgreSQL`:idx: database wrapper. This interface
## is implemented for other databases also.
##
## See also: `db_odbc <db_odbc.html>`_, `db_sqlite <db_sqlite.html>`_,
## `db_mysql <db_mysql.html>`_.
##
## Parameter substitution
## ----------------------
##
@ -27,7 +30,7 @@
##
## 2. ``SqlPrepared`` using ``$1, $2, $3, ...``
##
## .. code-block:: Nim
## .. code-block:: Nim
## prepare(db, "myExampleInsert",
## sql"""INSERT INTO myTable
## (colA, colB, colC)
@ -62,47 +65,28 @@
## "Dominik")
import strutils, postgres
import db_common
export db_common
type
DbConn* = PPGconn ## encapsulates a database connection
Row* = seq[string] ## a row of a dataset. NULL database values will be
## transformed always to the empty string.
## converted to nil.
InstantRow* = tuple[res: PPGresult, line: int32] ## a handle that can be
## used to get a row's
## column text on demand
EDb* = object of IOError ## exception that is raised if a database error occurs
SqlQuery* = distinct string ## an SQL query string
SqlPrepared* = distinct string ## a identifier for the prepared queries
FDb* = object of IOEffect ## effect that denotes a database operation
FReadDb* = object of FDb ## effect that denotes a read operation
FWriteDb* = object of FDb ## effect that denotes a write operation
{.deprecated: [TRow: Row, TSqlQuery: SqlQuery, TDbConn: DbConn,
{.deprecated: [TRow: Row, TDbConn: DbConn,
TSqlPrepared: SqlPrepared].}
proc sql*(query: string): SqlQuery {.noSideEffect, inline.} =
## constructs a SqlQuery from the string `query`. This is supposed to be
## used as a raw-string-literal modifier:
## ``sql"update user set counter = counter + 1"``
##
## If assertions are turned off, it does nothing. If assertions are turned
## on, later versions will check the string for valid syntax.
result = SqlQuery(query)
proc dbError*(db: DbConn) {.noreturn.} =
## raises an EDb exception.
var e: ref EDb
## raises a DbError exception.
var e: ref DbError
new(e)
e.msg = $pqErrorMessage(db)
raise e
proc dbError*(msg: string) {.noreturn.} =
## raises an EDb exception with message `msg`.
var e: ref EDb
new(e)
e.msg = msg
raise e
proc dbQuote*(s: string): string =
## DB quotes the string.
result = "'"
@ -127,7 +111,7 @@ proc dbFormat(formatstr: SqlQuery, args: varargs[string]): string =
add(result, c)
proc tryExec*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): bool {.tags: [FReadDB, FWriteDb].} =
args: varargs[string, `$`]): bool {.tags: [ReadDbEffect, WriteDbEffect].} =
## tries to execute the query and returns true if successful, false otherwise.
var res = pqexecParams(db, dbFormat(query, args), 0, nil, nil,
nil, nil, 0)
@ -135,7 +119,8 @@ proc tryExec*(db: DbConn, query: SqlQuery,
pqclear(res)
proc tryExec*(db: DbConn, stmtName: SqlPrepared,
args: varargs[string, `$`]): bool {.tags: [FReadDB, FWriteDb].} =
args: varargs[string, `$`]): bool {.tags: [
ReadDbEffect, WriteDbEffect].} =
## tries to execute the query and returns true if successful, false otherwise.
var arr = allocCStringArray(args)
var res = pqexecPrepared(db, stmtName.string, int32(args.len), arr,
@ -145,7 +130,7 @@ proc tryExec*(db: DbConn, stmtName: SqlPrepared,
pqclear(res)
proc exec*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]) {.
tags: [FReadDB, FWriteDb].} =
tags: [ReadDbEffect, WriteDbEffect].} =
## executes the query and raises EDB if not successful.
var res = pqexecParams(db, dbFormat(query, args), 0, nil, nil,
nil, nil, 0)
@ -153,7 +138,7 @@ proc exec*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]) {.
pqclear(res)
proc exec*(db: DbConn, stmtName: SqlPrepared,
args: varargs[string]) {.tags: [FReadDB, FWriteDb].} =
args: varargs[string]) {.tags: [ReadDbEffect, WriteDbEffect].} =
var arr = allocCStringArray(args)
var res = pqexecPrepared(db, stmtName.string, int32(args.len), arr,
nil, nil, 0)
@ -167,11 +152,7 @@ proc newRow(L: int): Row =
proc setupQuery(db: DbConn, query: SqlQuery,
args: varargs[string]): PPGresult =
# s is a dummy unique id str for each setupQuery query
let s = "setupQuery_Query_" & string(query)
var res = pqprepare(db, s, dbFormat(query, args), 0, nil)
result = pqexecPrepared(db, s, 0, nil,
nil, nil, 0)
result = pqexec(db, dbFormat(query, args))
if pqResultStatus(result) != PGRES_TUPLES_OK: dbError(db)
proc setupQuery(db: DbConn, stmtName: SqlPrepared,
@ -184,8 +165,10 @@ proc setupQuery(db: DbConn, stmtName: SqlPrepared,
proc prepare*(db: DbConn; stmtName: string, query: SqlQuery;
nParams: int): SqlPrepared =
## Creates a new ``SqlPrepared`` statement. Parameter substitution is done
## via ``$1``, ``$2``, ``$3``, etc.
if nParams > 0 and not string(query).contains("$1"):
dbError("""parameter substitution expects "$1" """)
dbError("parameter substitution expects \"$1\"")
var res = pqprepare(db, stmtName, query.string, int32(nParams), nil)
if pqResultStatus(res) != PGRES_COMMAND_OK: dbError(db)
return SqlPrepared(stmtName)
@ -200,7 +183,7 @@ proc setRow(res: PPGresult, r: var Row, line, cols: int32) =
add(r[col], x)
iterator fastRows*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): Row {.tags: [FReadDB].} =
args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} =
## executes the query and iterates over the result dataset. This is very
## fast, but potenially dangerous: If the for-loop-body executes another
## query, the results can be undefined. For Postgres it is safe though.
@ -213,7 +196,7 @@ iterator fastRows*(db: DbConn, query: SqlQuery,
pqclear(res)
iterator fastRows*(db: DbConn, stmtName: SqlPrepared,
args: varargs[string, `$`]): Row {.tags: [FReadDB].} =
args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} =
## executes the prepared query and iterates over the result dataset.
var res = setupQuery(db, stmtName, args)
var L = pqNfields(res)
@ -225,9 +208,9 @@ iterator fastRows*(db: DbConn, stmtName: SqlPrepared,
iterator instantRows*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): InstantRow
{.tags: [FReadDb].} =
{.tags: [ReadDbEffect].} =
## same as fastRows but returns a handle that can be used to get column text
## on demand using []. Returned handle is valid only within interator body.
## on demand using []. Returned handle is valid only within iterator body.
var res = setupQuery(db, query, args)
for i in 0..pqNtuples(res)-1:
yield (res: res, line: i)
@ -235,9 +218,9 @@ iterator instantRows*(db: DbConn, query: SqlQuery,
iterator instantRows*(db: DbConn, stmtName: SqlPrepared,
args: varargs[string, `$`]): InstantRow
{.tags: [FReadDb].} =
{.tags: [ReadDbEffect].} =
## same as fastRows but returns a handle that can be used to get column text
## on demand using []. Returned handle is valid only within interator body.
## on demand using []. Returned handle is valid only within iterator body.
var res = setupQuery(db, stmtName, args)
for i in 0..pqNtuples(res)-1:
yield (res: res, line: i)
@ -252,7 +235,7 @@ proc len*(row: InstantRow): int32 {.inline.} =
pqNfields(row.res)
proc getRow*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): Row {.tags: [FReadDB].} =
args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} =
## retrieves a single row. If the query doesn't return any rows, this proc
## will return a Row with empty strings for each column.
var res = setupQuery(db, query, args)
@ -262,7 +245,7 @@ proc getRow*(db: DbConn, query: SqlQuery,
pqclear(res)
proc getRow*(db: DbConn, stmtName: SqlPrepared,
args: varargs[string, `$`]): Row {.tags: [FReadDB].} =
args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} =
var res = setupQuery(db, stmtName, args)
var L = pqNfields(res)
result = newRow(L)
@ -270,39 +253,52 @@ proc getRow*(db: DbConn, stmtName: SqlPrepared,
pqClear(res)
proc getAllRows*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): seq[Row] {.tags: [FReadDB].} =
args: varargs[string, `$`]): seq[Row] {.
tags: [ReadDbEffect].} =
## executes the query and returns the whole result dataset.
result = @[]
for r in fastRows(db, query, args):
result.add(r)
proc getAllRows*(db: DbConn, stmtName: SqlPrepared,
args: varargs[string, `$`]): seq[Row] {.tags: [FReadDB].} =
args: varargs[string, `$`]): seq[Row] {.tags:
[ReadDbEffect].} =
## executes the prepared query and returns the whole result dataset.
result = @[]
for r in fastRows(db, stmtName, args):
result.add(r)
iterator rows*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): Row {.tags: [FReadDB].} =
args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} =
## same as `fastRows`, but slower and safe.
for r in items(getAllRows(db, query, args)): yield r
iterator rows*(db: DbConn, stmtName: SqlPrepared,
args: varargs[string, `$`]): Row {.tags: [FReadDB].} =
args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} =
## same as `fastRows`, but slower and safe.
for r in items(getAllRows(db, stmtName, args)): yield r
proc getValue*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): string {.tags: [FReadDB].} =
args: varargs[string, `$`]): string {.
tags: [ReadDbEffect].} =
## executes the query and returns the first column of the first row of the
## result dataset. Returns "" if the dataset contains no rows or the database
## value is NULL.
var x = pqgetvalue(setupQuery(db, query, args), 0, 0)
result = if isNil(x): "" else: $x
proc getValue*(db: DbConn, stmtName: SqlPrepared,
args: varargs[string, `$`]): string {.
tags: [ReadDbEffect].} =
## executes the query and returns the first column of the first row of the
## result dataset. Returns "" if the dataset contains no rows or the database
## value is NULL.
var x = pqgetvalue(setupQuery(db, stmtName, args), 0, 0)
result = if isNil(x): "" else: $x
proc tryInsertID*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): int64 {.tags: [FWriteDb].}=
args: varargs[string, `$`]): int64 {.
tags: [WriteDbEffect].}=
## executes the query (typically "INSERT") and returns the
## generated ID for the row or -1 in case of an error. For Postgre this adds
## ``RETURNING id`` to the query, so it only works if your primary key is
@ -315,7 +311,8 @@ proc tryInsertID*(db: DbConn, query: SqlQuery,
result = -1
proc insertID*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} =
args: varargs[string, `$`]): int64 {.
tags: [WriteDbEffect].} =
## executes the query (typically "INSERT") and returns the
## generated ID for the row. For Postgre this adds
## ``RETURNING id`` to the query, so it only works if your primary key is
@ -325,7 +322,7 @@ proc insertID*(db: DbConn, query: SqlQuery,
proc execAffectedRows*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): int64 {.tags: [
FReadDB, FWriteDb].} =
ReadDbEffect, WriteDbEffect].} =
## executes the query (typically "UPDATE") and returns the
## number of affected rows.
var q = dbFormat(query, args)
@ -336,7 +333,7 @@ proc execAffectedRows*(db: DbConn, query: SqlQuery,
proc execAffectedRows*(db: DbConn, stmtName: SqlPrepared,
args: varargs[string, `$`]): int64 {.tags: [
FReadDB, FWriteDb].} =
ReadDbEffect, WriteDbEffect].} =
## executes the query (typically "UPDATE") and returns the
## number of affected rows.
var arr = allocCStringArray(args)
@ -347,12 +344,12 @@ proc execAffectedRows*(db: DbConn, stmtName: SqlPrepared,
result = parseBiggestInt($pqcmdTuples(res))
pqclear(res)
proc close*(db: DbConn) {.tags: [FDb].} =
proc close*(db: DbConn) {.tags: [DbEffect].} =
## closes the database connection.
if db != nil: pqfinish(db)
proc open*(connection, user, password, database: string): DbConn {.
tags: [FDb].} =
tags: [DbEffect].} =
## opens a database connection. Raises `EDb` if the connection could not
## be established.
##
@ -374,10 +371,10 @@ proc open*(connection, user, password, database: string): DbConn {.
if pqStatus(result) != CONNECTION_OK: dbError(result) # result = nil
proc setEncoding*(connection: DbConn, encoding: string): bool {.
tags: [FDb].} =
tags: [DbEffect].} =
## sets the encoding of a database connection, returns true for
## success, false for failure.
return pqsetClientEncoding(connection, encoding) == 0
# Tests are in ../../tests/untestable/tpostgres.
# Tests are in ../../tests/untestable/tpostgres.

View file

@ -1,7 +1,7 @@
#
#
# Nim's Runtime Library
# (c) Copyright 2012 Andreas Rumpf
# (c) Copyright 2015 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@ -10,7 +10,48 @@
## A higher level `SQLite`:idx: database wrapper. This interface
## is implemented for other databases too.
##
## Example:
## See also: `db_odbc <db_odbc.html>`_, `db_postgres <db_postgres.html>`_,
## `db_mysql <db_mysql.html>`_.
##
## Parameter substitution
## ----------------------
##
## All ``db_*`` modules support the same form of parameter substitution.
## That is, using the ``?`` (question mark) to signify the place where a
## value should be placed. For example:
##
## .. code-block:: Nim
## sql"INSERT INTO myTable (colA, colB, colC) VALUES (?, ?, ?)"
##
## Examples
## --------
##
## Opening a connection to a database
## ==================================
##
## .. code-block:: Nim
## import db_sqlite
## let db = open("localhost", "user", "password", "dbname")
## db.close()
##
## Creating a table
## ================
##
## .. code-block:: Nim
## db.exec(sql"DROP TABLE IF EXISTS myTable")
## db.exec(sql("""CREATE TABLE myTable (
## id integer,
## name varchar(50) not null)"""))
##
## Inserting data
## ==============
##
## .. code-block:: Nim
## db.exec(sql"INSERT INTO myTable (id, name) VALUES (0, ?)",
## "Jack")
##
## Larger example
## ==============
##
## .. code-block:: nim
##
@ -40,47 +81,30 @@
##
## theDb.close()
{.deadCodeElim:on.}
import strutils, sqlite3
import db_common
export db_common
type
DbConn* = PSqlite3 ## encapsulates a database connection
Row* = seq[string] ## a row of a dataset. NULL database values will be
## transformed always to the empty string.
## converted to nil.
InstantRow* = Pstmt ## a handle that can be used to get a row's column
## text on demand
EDb* = object of IOError ## exception that is raised if a database error occurs
{.deprecated: [TRow: Row, TDbConn: DbConn].}
SqlQuery* = distinct string ## an SQL query string
FDb* = object of IOEffect ## effect that denotes a database operation
FReadDb* = object of FDb ## effect that denotes a read operation
FWriteDb* = object of FDb ## effect that denotes a write operation
{.deprecated: [TRow: Row, TSqlQuery: SqlQuery, TDbConn: DbConn].}
proc sql*(query: string): SqlQuery {.noSideEffect, inline.} =
## constructs a SqlQuery from the string `query`. This is supposed to be
## used as a raw-string-literal modifier:
## ``sql"update user set counter = counter + 1"``
##
## If assertions are turned off, it does nothing. If assertions are turned
## on, later versions will check the string for valid syntax.
result = SqlQuery(query)
proc dbError(db: DbConn) {.noreturn.} =
## raises an EDb exception.
var e: ref EDb
proc dbError*(db: DbConn) {.noreturn.} =
## raises a DbError exception.
var e: ref DbError
new(e)
e.msg = $sqlite3.errmsg(db)
raise e
proc dbError*(msg: string) {.noreturn.} =
## raises an EDb exception with message `msg`.
var e: ref EDb
new(e)
e.msg = msg
raise e
proc dbQuote(s: string): string =
proc dbQuote*(s: string): string =
## DB quotes the string.
if s.isNil: return "NULL"
result = "'"
for c in items(s):
@ -99,7 +123,8 @@ proc dbFormat(formatstr: SqlQuery, args: varargs[string]): string =
add(result, c)
proc tryExec*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): bool {.tags: [FReadDb, FWriteDb].} =
args: varargs[string, `$`]): bool {.
tags: [ReadDbEffect, WriteDbEffect].} =
## tries to execute the query and returns true if successful, false otherwise.
var q = dbFormat(query, args)
var stmt: sqlite3.Pstmt
@ -108,8 +133,8 @@ proc tryExec*(db: DbConn, query: SqlQuery,
result = finalize(stmt) == SQLITE_OK
proc exec*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]) {.
tags: [FReadDb, FWriteDb].} =
## executes the query and raises EDB if not successful.
tags: [ReadDbEffect, WriteDbEffect].} =
## executes the query and raises DbError if not successful.
if not tryExec(db, query, args): dbError(db)
proc newRow(L: int): Row =
@ -129,14 +154,14 @@ proc setRow(stmt: Pstmt, r: var Row, cols: cint) =
if not isNil(x): add(r[col], x)
iterator fastRows*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): Row {.tags: [FReadDb].} =
args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} =
## Executes the query and iterates over the result dataset.
##
## This is very fast, but potentially dangerous. Use this iterator only
## if you require **ALL** the rows.
##
## Breaking the fastRows() iterator during a loop will cause the next
## database query to raise an [EDb] exception ``unable to close due to ...``.
## database query to raise a DbError exception ``unable to close due to ...``.
var stmt = setupQuery(db, query, args)
var L = (column_count(stmt))
var result = newRow(L)
@ -147,14 +172,47 @@ iterator fastRows*(db: DbConn, query: SqlQuery,
iterator instantRows*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): InstantRow
{.tags: [FReadDb].} =
{.tags: [ReadDbEffect].} =
## 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 interator body.
## on demand using []. Returned handle is valid only within the iterator body.
var stmt = setupQuery(db, query, args)
while step(stmt) == SQLITE_ROW:
yield stmt
if finalize(stmt) != SQLITE_OK: dbError(db)
proc toTypeKind(t: var DbType; x: int32) =
case x
of SQLITE_INTEGER:
t.kind = dbInt
t.size = 8
of SQLITE_FLOAT:
t.kind = dbFloat
t.size = 8
of SQLITE_BLOB: t.kind = dbBlob
of SQLITE_NULL: t.kind = dbNull
of SQLITE_TEXT: t.kind = dbVarchar
else: t.kind = dbUnknown
proc setColumns(columns: var DbColumns; x: PStmt) =
let L = column_count(x)
setLen(columns, L)
for i in 0'i32 ..< L:
columns[i].name = $column_name(x, i)
columns[i].typ.name = $column_decltype(x, i)
toTypeKind(columns[i].typ, column_type(x, i))
columns[i].tableName = $column_table_name(x, i)
iterator instantRows*(db: DbConn; columns: var DbColumns; query: SqlQuery,
args: varargs[string, `$`]): InstantRow
{.tags: [ReadDbEffect].} =
## 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.
var stmt = setupQuery(db, query, args)
setColumns(columns, stmt)
while step(stmt) == SQLITE_ROW:
yield stmt
if finalize(stmt) != SQLITE_OK: dbError(db)
proc `[]`*(row: InstantRow, col: int32): string {.inline.} =
## returns text for given column of the row
$column_text(row, col)
@ -164,7 +222,7 @@ proc len*(row: InstantRow): int32 {.inline.} =
column_count(row)
proc getRow*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): Row {.tags: [FReadDb].} =
args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} =
## retrieves a single row. If the query doesn't return any rows, this proc
## will return a Row with empty strings for each column.
var stmt = setupQuery(db, query, args)
@ -175,19 +233,19 @@ proc getRow*(db: DbConn, query: SqlQuery,
if finalize(stmt) != SQLITE_OK: dbError(db)
proc getAllRows*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): seq[Row] {.tags: [FReadDb].} =
args: varargs[string, `$`]): seq[Row] {.tags: [ReadDbEffect].} =
## executes the query and returns the whole result dataset.
result = @[]
for r in fastRows(db, query, args):
result.add(r)
iterator rows*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): Row {.tags: [FReadDb].} =
args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} =
## same as `FastRows`, but slower and safe.
for r in fastRows(db, query, args): yield r
proc getValue*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): string {.tags: [FReadDb].} =
args: varargs[string, `$`]): string {.tags: [ReadDbEffect].} =
## executes the query and returns the first column of the first row of the
## result dataset. Returns "" if the dataset contains no rows or the database
## value is NULL.
@ -205,7 +263,7 @@ proc getValue*(db: DbConn, query: SqlQuery,
proc tryInsertID*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): int64
{.tags: [FWriteDb], raises: [].} =
{.tags: [WriteDbEffect], raises: [].} =
## executes the query (typically "INSERT") and returns the
## generated ID for the row or -1 in case of an error.
var q = dbFormat(query, args)
@ -218,7 +276,7 @@ proc tryInsertID*(db: DbConn, query: SqlQuery,
result = -1
proc insertID*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} =
args: varargs[string, `$`]): int64 {.tags: [WriteDbEffect].} =
## executes the query (typically "INSERT") and returns the
## generated ID for the row. For Postgre this adds
## ``RETURNING id`` to the query, so it only works if your primary key is
@ -228,18 +286,18 @@ proc insertID*(db: DbConn, query: SqlQuery,
proc execAffectedRows*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): int64 {.
tags: [FReadDb, FWriteDb].} =
tags: [ReadDbEffect, WriteDbEffect].} =
## executes the query (typically "UPDATE") and returns the
## number of affected rows.
exec(db, query, args)
result = changes(db)
proc close*(db: DbConn) {.tags: [FDb].} =
proc close*(db: DbConn) {.tags: [DbEffect].} =
## closes the database connection.
if sqlite3.close(db) != SQLITE_OK: dbError(db)
proc open*(connection, user, password, database: string): DbConn {.
tags: [FDb].} =
tags: [DbEffect].} =
## opens a database connection. Raises `EDb` if the connection could not
## be established. Only the ``connection`` parameter is used for ``sqlite``.
var db: DbConn
@ -249,7 +307,7 @@ proc open*(connection, user, password, database: string): DbConn {.
dbError(db)
proc setEncoding*(connection: DbConn, encoding: string): bool {.
tags: [FDb].} =
tags: [DbEffect].} =
## sets the encoding of a database connection, returns true for
## success, false for failure.
##

View file

@ -14,36 +14,35 @@ when not defined(js) and not defined(Nimdoc):
{.error: "This module only works on the JavaScript platform".}
type
TEventHandlers* {.importc.} = object of RootObj
onabort*: proc (event: ref TEvent) {.nimcall.}
onblur*: proc (event: ref TEvent) {.nimcall.}
onchange*: proc (event: ref TEvent) {.nimcall.}
onclick*: proc (event: ref TEvent) {.nimcall.}
ondblclick*: proc (event: ref TEvent) {.nimcall.}
onerror*: proc (event: ref TEvent) {.nimcall.}
onfocus*: proc (event: ref TEvent) {.nimcall.}
onkeydown*: proc (event: ref TEvent) {.nimcall.}
onkeypress*: proc (event: ref TEvent) {.nimcall.}
onkeyup*: proc (event: ref TEvent) {.nimcall.}
onload*: proc (event: ref TEvent) {.nimcall.}
onmousedown*: proc (event: ref TEvent) {.nimcall.}
onmousemove*: proc (event: ref TEvent) {.nimcall.}
onmouseout*: proc (event: ref TEvent) {.nimcall.}
onmouseover*: proc (event: ref TEvent) {.nimcall.}
onmouseup*: proc (event: ref TEvent) {.nimcall.}
onreset*: proc (event: ref TEvent) {.nimcall.}
onselect*: proc (event: ref TEvent) {.nimcall.}
onsubmit*: proc (event: ref TEvent) {.nimcall.}
onunload*: proc (event: ref TEvent) {.nimcall.}
addEventListener*: proc(ev: cstring, cb: proc(ev: ref TEvent), useCapture: bool = false) {.nimcall.}
EventTarget* = ref EventTargetObj
EventTargetObj {.importc.} = object of RootObj
onabort*: proc (event: Event) {.nimcall.}
onblur*: proc (event: Event) {.nimcall.}
onchange*: proc (event: Event) {.nimcall.}
onclick*: proc (event: Event) {.nimcall.}
ondblclick*: proc (event: Event) {.nimcall.}
onerror*: proc (event: Event) {.nimcall.}
onfocus*: proc (event: Event) {.nimcall.}
onkeydown*: proc (event: Event) {.nimcall.}
onkeypress*: proc (event: Event) {.nimcall.}
onkeyup*: proc (event: Event) {.nimcall.}
onload*: proc (event: Event) {.nimcall.}
onmousedown*: proc (event: Event) {.nimcall.}
onmousemove*: proc (event: Event) {.nimcall.}
onmouseout*: proc (event: Event) {.nimcall.}
onmouseover*: proc (event: Event) {.nimcall.}
onmouseup*: proc (event: Event) {.nimcall.}
onreset*: proc (event: Event) {.nimcall.}
onselect*: proc (event: Event) {.nimcall.}
onsubmit*: proc (event: Event) {.nimcall.}
onunload*: proc (event: Event) {.nimcall.}
Window* = ref WindowObj
WindowObj {.importc.} = object of TEventHandlers
WindowObj {.importc.} = object of EventTargetObj
document*: Document
event*: ref TEvent
history*: ref THistory
location*: ref TLocation
event*: Event
history*: History
location*: Location
closed*: bool
defaultStatus*: cstring
innerHeight*, innerWidth*: int
@ -57,50 +56,15 @@ type
statusbar*: ref TStatusBar
status*: cstring
toolbar*: ref TToolBar
alert*: proc (msg: cstring) {.nimcall.}
back*: proc () {.nimcall.}
blur*: proc () {.nimcall.}
captureEvents*: proc (eventMask: int) {.nimcall.}
clearInterval*: proc (interval: ref TInterval) {.nimcall.}
clearTimeout*: proc (timeout: ref TTimeOut) {.nimcall.}
close*: proc () {.nimcall.}
confirm*: proc (msg: cstring): bool {.nimcall.}
disableExternalCapture*: proc () {.nimcall.}
enableExternalCapture*: proc () {.nimcall.}
find*: proc (text: cstring, caseSensitive = false,
backwards = false) {.nimcall.}
focus*: proc () {.nimcall.}
forward*: proc () {.nimcall.}
handleEvent*: proc (e: ref TEvent) {.nimcall.}
home*: proc () {.nimcall.}
moveBy*: proc (x, y: int) {.nimcall.}
moveTo*: proc (x, y: int) {.nimcall.}
open*: proc (uri, windowname: cstring,
properties: cstring = nil): Window {.nimcall.}
print*: proc () {.nimcall.}
prompt*: proc (text, default: cstring): cstring {.nimcall.}
releaseEvents*: proc (eventMask: int) {.nimcall.}
resizeBy*: proc (x, y: int) {.nimcall.}
resizeTo*: proc (x, y: int) {.nimcall.}
routeEvent*: proc (event: ref TEvent) {.nimcall.}
scrollBy*: proc (x, y: int) {.nimcall.}
scrollTo*: proc (x, y: int) {.nimcall.}
setInterval*: proc (code: cstring, pause: int): ref TInterval {.nimcall.}
setTimeout*: proc (code: cstring, pause: int): ref TTimeOut {.nimcall.}
stop*: proc () {.nimcall.}
frames*: seq[TFrame]
Frame* = ref FrameObj
FrameObj {.importc.} = object of WindowObj
ClassList* {.importc.} = object of RootObj
add*: proc (class: cstring) {.nimcall.}
remove*: proc (class: cstring) {.nimcall.}
contains*: proc (class: cstring):bool {.nimcall.}
toggle*: proc (class: cstring) {.nimcall.}
ClassList* = ref ClassListObj
ClassListObj {.importc.} = object of RootObj
TNodeType* = enum
NodeType* = enum
ElementNode = 1,
AttributeNode,
TextNode,
@ -115,7 +79,7 @@ type
NotationNode
Node* = ref NodeObj
NodeObj {.importc.} = object of TEventHandlers
NodeObj {.importc.} = object of EventTargetObj
attributes*: seq[Node]
childNodes*: seq[Node]
children*: seq[Node]
@ -124,29 +88,12 @@ type
lastChild*: Node
nextSibling*: Node
nodeName*: cstring
nodeType*: TNodeType
nodeType*: NodeType
nodeValue*: cstring
parentNode*: Node
previousSibling*: Node
appendChild*: proc (child: Node) {.nimcall.}
appendData*: proc (data: cstring) {.nimcall.}
cloneNode*: proc (copyContent: bool): Node {.nimcall.}
deleteData*: proc (start, len: int) {.nimcall.}
getAttribute*: proc (attr: cstring): cstring {.nimcall.}
getAttributeNode*: proc (attr: cstring): Node {.nimcall.}
hasChildNodes*: proc (): bool {.nimcall.}
innerHTML*: cstring
insertBefore*: proc (newNode, before: Node) {.nimcall.}
insertData*: proc (position: int, data: cstring) {.nimcall.}
removeAttribute*: proc (attr: cstring) {.nimcall.}
removeAttributeNode*: proc (attr: Node) {.nimcall.}
removeChild*: proc (child: Node) {.nimcall.}
replaceChild*: proc (newNode, oldNode: Node) {.nimcall.}
replaceData*: proc (start, len: int, text: cstring) {.nimcall.}
scrollIntoView*: proc () {.nimcall.}
setAttribute*: proc (name, value: cstring) {.nimcall.}
setAttributeNode*: proc (attr: Node) {.nimcall.}
style*: ref TStyle
style*: Style
Document* = ref DocumentObj
DocumentObj {.importc.} = object of NodeObj
@ -164,31 +111,16 @@ type
title*: cstring
URL*: cstring
vlinkColor*: cstring
captureEvents*: proc (eventMask: int) {.nimcall.}
createAttribute*: proc (identifier: cstring): Node {.nimcall.}
createElement*: proc (identifier: cstring): Element {.nimcall.}
createTextNode*: proc (identifier: cstring): Node {.nimcall.}
getElementById*: proc (id: cstring): Element {.nimcall.}
getElementsByName*: proc (name: cstring): seq[Element] {.nimcall.}
getElementsByTagName*: proc (name: cstring): seq[Element] {.nimcall.}
getElementsByClassName*: proc (name: cstring): seq[Element] {.nimcall.}
getSelection*: proc (): cstring {.nimcall.}
handleEvent*: proc (event: ref TEvent) {.nimcall.}
open*: proc () {.nimcall.}
releaseEvents*: proc (eventMask: int) {.nimcall.}
routeEvent*: proc (event: ref TEvent) {.nimcall.}
write*: proc (text: cstring) {.nimcall.}
writeln*: proc (text: cstring) {.nimcall.}
anchors*: seq[AnchorElement]
forms*: seq[FormElement]
images*: seq[ImageElement]
applets*: seq[ref TApplet]
applets*: seq[Element]
embeds*: seq[EmbedElement]
links*: seq[LinkElement]
Element* = ref ElementObj
ElementObj {.importc.} = object of NodeObj
classList*: ref Classlist
classList*: Classlist
checked*: bool
defaultChecked*: bool
defaultValue*: cstring
@ -196,14 +128,7 @@ type
form*: FormElement
name*: cstring
readOnly*: bool
blur*: proc () {.nimcall.}
click*: proc () {.nimcall.}
focus*: proc () {.nimcall.}
handleEvent*: proc (event: ref TEvent) {.nimcall.}
select*: proc () {.nimcall.}
options*: seq[OptionElement]
getElementsByTagName*: proc (name: cstring): seq[Element] {.nimcall.}
getElementsByClassName*: proc (name: cstring): seq[Element] {.nimcall.}
LinkElement* = ref LinkObj
LinkObj {.importc.} = object of ElementObj
@ -220,16 +145,12 @@ type
width*: int
`type`*: cstring
vspace*: int
play*: proc () {.nimcall.}
stop*: proc () {.nimcall.}
AnchorElement* = ref AnchorObj
AnchorObj {.importc.} = object of ElementObj
text*: cstring
x*, y*: int
TApplet* {.importc.} = object of RootObj
OptionElement* = ref OptionObj
OptionObj {.importc.} = object of ElementObj
defaultSelected*: bool
@ -244,8 +165,6 @@ type
encoding*: cstring
`method`*: cstring
target*: cstring
reset*: proc () {.nimcall.}
submit*: proc () {.nimcall.}
elements*: seq[Element]
ImageElement* = ref ImageObj
@ -259,8 +178,8 @@ type
vspace*: int
width*: int
TStyle* {.importc.} = object of RootObj
Style = ref StyleObj
StyleObj {.importc.} = object of RootObj
background*: cstring
backgroundAttachment*: cstring
backgroundColor*: cstring
@ -350,11 +269,9 @@ type
width*: cstring
wordSpacing*: cstring
zIndex*: int
getAttribute*: proc (attr: cstring, caseSensitive=false): cstring {.nimcall.}
removeAttribute*: proc (attr: cstring, caseSensitive=false) {.nimcall.}
setAttribute*: proc (attr, value: cstring, caseSensitive=false) {.nimcall.}
TEvent* {.importc.} = object of RootObj
Event* = ref EventObj
EventObj {.importc.} = object of RootObj
target*: Node
altKey*, ctrlKey*, shiftKey*: bool
button*: int
@ -393,7 +310,8 @@ type
SUBMIT*: int
UNLOAD*: int
TLocation* {.importc.} = object of RootObj
Location* = ref LocationObj
LocationObj {.importc.} = object of RootObj
hash*: cstring
host*: cstring
hostname*: cstring
@ -402,16 +320,13 @@ type
port*: cstring
protocol*: cstring
search*: cstring
reload*: proc () {.nimcall.}
replace*: proc (s: cstring) {.nimcall.}
THistory* {.importc.} = object of RootObj
History* = ref HistoryObj
HistoryObj {.importc.} = object of RootObj
length*: int
back*: proc () {.nimcall.}
forward*: proc () {.nimcall.}
go*: proc (pagesToJump: int) {.nimcall.}
TNavigator* {.importc.} = object of RootObj
Navigator* = ref NavigatorObj
NavigatorObj {.importc.} = object of RootObj
appCodeName*: cstring
appName*: cstring
appVersion*: cstring
@ -419,7 +334,6 @@ type
language*: cstring
platform*: cstring
userAgent*: cstring
javaEnabled*: proc (): bool {.nimcall.}
mimeTypes*: seq[ref TMimeType]
TPlugin* {.importc.} = object of RootObj
@ -441,7 +355,8 @@ type
TToolBar* = TLocationBar
TStatusBar* = TLocationBar
TScreen* {.importc.} = object of RootObj
Screen = ref ScreenObj
ScreenObj {.importc.} = object of RootObj
availHeight*: int
availWidth*: int
colorDepth*: int
@ -452,11 +367,127 @@ type
TTimeOut* {.importc.} = object of RootObj
TInterval* {.importc.} = object of RootObj
{.push importcpp.}
# EventTarget "methods"
proc addEventListener*(et: EventTarget, ev: cstring, cb: proc(ev: Event), useCapture: bool = false)
# Window "methods"
proc alert*(w: Window, msg: cstring)
proc back*(w: Window)
proc blur*(w: Window)
proc captureEvents*(w: Window, eventMask: int) {.deprecated.}
proc clearInterval*(w: Window, interval: ref TInterval)
proc clearTimeout*(w: Window, timeout: ref TTimeOut)
proc close*(w: Window)
proc confirm*(w: Window, msg: cstring): bool
proc disableExternalCapture*(w: Window)
proc enableExternalCapture*(w: Window)
proc find*(w: Window, text: cstring, caseSensitive = false,
backwards = false)
proc focus*(w: Window)
proc forward*(w: Window)
proc handleEvent*(w: Window, e: Event)
proc home*(w: Window)
proc moveBy*(w: Window, x, y: int)
proc moveTo*(w: Window, x, y: int)
proc open*(w: Window, uri, windowname: cstring,
properties: cstring = nil): Window
proc print*(w: Window)
proc prompt*(w: Window, text, default: cstring): cstring
proc releaseEvents*(w: Window, eventMask: int) {.deprecated.}
proc resizeBy*(w: Window, x, y: int)
proc resizeTo*(w: Window, x, y: int)
proc routeEvent*(w: Window, event: Event)
proc scrollBy*(w: Window, x, y: int)
proc scrollTo*(w: Window, x, y: int)
proc setInterval*(w: Window, code: cstring, pause: int): ref TInterval
proc setTimeout*(w: Window, code: cstring, pause: int): ref TTimeOut
proc stop*(w: Window)
# Node "methods"
proc appendChild*(n, child: Node)
proc appendData*(n: Node, data: cstring)
proc cloneNode*(n: Node, copyContent: bool): Node
proc deleteData*(n: Node, start, len: int)
proc getAttribute*(n: Node, attr: cstring): cstring
proc getAttributeNode*(n: Node, attr: cstring): Node
proc hasChildNodes*(n: Node): bool
proc insertBefore*(n, newNode, before: Node)
proc insertData*(n: Node, position: int, data: cstring)
proc removeAttribute*(n: Node, attr: cstring)
proc removeAttributeNode*(n, attr: Node)
proc removeChild*(n, child: Node)
proc replaceChild*(n, newNode, oldNode: Node)
proc replaceData*(n: Node, start, len: int, text: cstring)
proc scrollIntoView*(n: Node)
proc setAttribute*(n: Node, name, value: cstring)
proc setAttributeNode*(n: Node, attr: Node)
# Document "methods"
proc captureEvents*(d: Document, eventMask: int) {.deprecated.}
proc createAttribute*(d: Document, identifier: cstring): Node
proc createElement*(d: Document, identifier: cstring): Element
proc createTextNode*(d: Document, identifier: cstring): Node
proc getElementById*(d: Document, id: cstring): Element
proc getElementsByName*(d: Document, name: cstring): seq[Element]
proc getElementsByTagName*(d: Document, name: cstring): seq[Element]
proc getElementsByClassName*(d: Document, name: cstring): seq[Element]
proc getSelection*(d: Document): cstring
proc handleEvent*(d: Document, event: Event)
proc open*(d: Document)
proc releaseEvents*(d: Document, eventMask: int) {.deprecated.}
proc routeEvent*(d: Document, event: Event)
proc write*(d: Document, text: cstring)
proc writeln*(d: Document, text: cstring)
# Element "methods"
proc blur*(e: Element)
proc click*(e: Element)
proc focus*(e: Element)
proc handleEvent*(e: Element, event: Event)
proc select*(e: Element)
proc getElementsByTagName*(e: Element, name: cstring): seq[Element]
proc getElementsByClassName*(e: Element, name: cstring): seq[Element]
# FormElement "methods"
proc reset*(f: FormElement)
proc submit*(f: FormElement)
# EmbedElement "methods"
proc play*(e: EmbedElement)
proc stop*(e: EmbedElement)
# Location "methods"
proc reload*(loc: Location)
proc replace*(loc: Location, s: cstring)
# History "methods"
proc back*(h: History)
proc forward*(h: History)
proc go*(h: History, pagesToJump: int)
# Navigator "methods"
proc javaEnabled*(h: Navigator): bool
# ClassList "methods"
proc add*(c: ClassList, class: cstring)
proc remove*(c: ClassList, class: cstring)
proc contains*(c: ClassList, class: cstring):bool
proc toggle*(c: ClassList, class: cstring)
# Style "methods"
proc getAttribute*(s: Style, attr: cstring, caseSensitive=false): cstring
proc removeAttribute*(s: Style, attr: cstring, caseSensitive=false)
proc setAttribute*(s: Style, attr, value: cstring, caseSensitive=false)
{.pop.}
var
window* {.importc, nodecl.}: Window
document* {.importc, nodecl.}: Document
navigator* {.importc, nodecl.}: ref TNavigator
screen* {.importc, nodecl.}: ref TScreen
navigator* {.importc, nodecl.}: Navigator
screen* {.importc, nodecl.}: Screen
proc decodeURI*(uri: cstring): cstring {.importc, nodecl.}
proc encodeURI*(uri: cstring): cstring {.importc, nodecl.}
@ -474,6 +505,7 @@ proc parseInt*(s: cstring, radix: int):int {.importc, nodecl.}
type
TEventHandlers* {.deprecated.} = EventTargetObj
TWindow* {.deprecated.} = WindowObj
TFrame* {.deprecated.} = FrameObj
TNode* {.deprecated.} = NodeObj
@ -485,3 +517,11 @@ type
TOption* {.deprecated.} = OptionObj
TForm* {.deprecated.} = FormObj
TImage* {.deprecated.} = ImageObj
TNodeType* {.deprecated.} = NodeType
TEvent* {.deprecated.} = EventObj
TLocation* {.deprecated.} = LocationObj
THistory* {.deprecated.} = HistoryObj
TNavigator* {.deprecated.} = NavigatorObj
TStyle* {.deprecated.} = StyleObj
TScreen* {.deprecated.} = ScreenObj
TApplet* {.importc, deprecated.} = object of RootObj

View file

@ -418,10 +418,6 @@ typedef int assert_numbits[sizeof(NI) == sizeof(void*) && NIM_INTBITS == sizeof(
# define NIM_EXTERNC
#endif
/* we have to tinker with TNimType as it's both part of system.nim and
typeinfo.nim but system.nim doesn't export it cleanly... */
typedef struct TNimType TNimType;
/* ---------------- platform specific includes ----------------------- */
/* VxWorks related includes */

View file

@ -173,7 +173,41 @@ proc nimNextToken(g: var GeneralTokenizer) =
while g.buf[pos] in {' ', '\x09'..'\x0D'}: inc(pos)
of '#':
g.kind = gtComment
while not (g.buf[pos] in {'\0', '\x0A', '\x0D'}): inc(pos)
inc(pos)
var isDoc = false
if g.buf[pos] == '#':
inc(pos)
isDoc = true
if g.buf[pos] == '[':
g.kind = gtLongComment
var nesting = 0
while true:
case g.buf[pos]
of '\0': break
of '#':
if isDoc:
if g.buf[pos+1] == '#' and g.buf[pos+2] == '[':
inc nesting
elif g.buf[pos+1] == '[':
inc nesting
inc pos
of ']':
if isDoc:
if g.buf[pos+1] == '#' and g.buf[pos+2] == '#':
if nesting == 0:
inc(pos, 3)
break
dec nesting
elif g.buf[pos+1] == '#':
if nesting == 0:
inc(pos, 2)
break
dec nesting
inc pos
else:
inc pos
else:
while g.buf[pos] notin {'\0', '\x0A', '\x0D'}: inc(pos)
of 'a'..'z', 'A'..'Z', '_', '\x80'..'\xFF':
var id = ""
while g.buf[pos] in SymChars + {'_'}:

View file

@ -534,7 +534,7 @@ proc generateDocumentationJumps(docs: IndexedDocs): string =
for title in titles:
chunks.add("<a href=\"" & title.link & "\">" & title.keyword & "</a>")
result.add(chunks.join(", ") & ".<br>")
result.add(chunks.join(", ") & ".<br/>")
proc generateModuleJumps(modules: seq[string]): string =
## Returns a plain list of hyperlinks to the list of modules.
@ -544,7 +544,7 @@ proc generateModuleJumps(modules: seq[string]): string =
for name in modules:
chunks.add("<a href=\"" & name & ".html\">" & name & "</a>")
result.add(chunks.join(", ") & ".<br>")
result.add(chunks.join(", ") & ".<br/>")
proc readIndexDir(dir: string):
tuple[modules: seq[string], symbols: seq[IndexEntry], docs: IndexedDocs] =

View file

@ -2165,6 +2165,10 @@ proc pwrite*(a1: cint, a2: pointer, a3: int, a4: Off): int {.
importc, header: "<unistd.h>".}
proc read*(a1: cint, a2: pointer, a3: int): int {.importc, header: "<unistd.h>".}
proc readlink*(a1, a2: cstring, a3: int): int {.importc, header: "<unistd.h>".}
proc ioctl*(f: FileHandle, device: uint): int {.importc: "ioctl",
header: "<sys/ioctl.h>", varargs, tags: [WriteIOEffect].}
## A system call for device-specific input/output operations and other
## operations which cannot be expressed by regular system calls
proc rmdir*(a1: cstring): cint {.importc, header: "<unistd.h>".}
proc setegid*(a1: Gid): cint {.importc, header: "<unistd.h>".}

View file

@ -288,7 +288,7 @@ proc defaultOnProgressChanged*(total, progress: BiggestInt,
result.complete()
proc retrFile*(ftp: AsyncFtpClient, file, dest: string,
onProgressChanged = defaultOnProgressChanged) {.async.} =
onProgressChanged: ProgressChangedProc = defaultOnProgressChanged) {.async.} =
## Downloads ``file`` and saves it to ``dest``.
## The ``EvRetr`` event is passed to the specified ``handleEvent`` function
## when the download is finished. The event's ``filename`` field will be equal
@ -339,7 +339,7 @@ proc doUpload(ftp: AsyncFtpClient, file: File,
await countdownFut or sendFut
proc store*(ftp: AsyncFtpClient, file, dest: string,
onProgressChanged = defaultOnProgressChanged) {.async.} =
onProgressChanged: ProgressChangedProc = defaultOnProgressChanged) {.async.} =
## Uploads ``file`` to ``dest`` on the remote FTP server. Usage of this
## function asynchronously is recommended to view the progress of
## the download.

View file

@ -232,7 +232,7 @@ iterator mpairs*[T](c: var CritBitTree[T]): tuple[key: string, val: var T] =
## yields all (key, value)-pairs of `c`. The yielded values can be modified.
for x in leaves(c.root): yield (x.key, x.val)
proc allprefixedAux[T](c: CritBitTree[T], key: string): Node[T] =
proc allprefixedAux[T](c: CritBitTree[T], key: string; longestMatch: bool): Node[T] =
var p = c.root
var top = p
if p != nil:
@ -242,43 +242,51 @@ proc allprefixedAux[T](c: CritBitTree[T], key: string): Node[T] =
let dir = (1 + (ch.ord or p.otherBits.ord)) shr 8
p = p.child[dir]
if q.byte < key.len: top = p
for i in 0 .. <key.len:
if p.key[i] != key[i]: return
if not longestMatch:
for i in 0 .. <key.len:
if p.key[i] != key[i]: return
result = top
iterator itemsWithPrefix*[T](c: CritBitTree[T], prefix: string): string =
## yields all keys starting with `prefix`.
let top = allprefixedAux(c, prefix)
iterator itemsWithPrefix*[T](c: CritBitTree[T], prefix: string;
longestMatch=false): string =
## yields all keys starting with `prefix`. If `longestMatch` is true,
## the longest match is returned, it doesn't have to be a complete match then.
let top = allprefixedAux(c, prefix, longestMatch)
for x in leaves(top): yield x.key
iterator keysWithPrefix*[T](c: CritBitTree[T], prefix: string): string =
iterator keysWithPrefix*[T](c: CritBitTree[T], prefix: string;
longestMatch=false): string =
## yields all keys starting with `prefix`.
let top = allprefixedAux(c, prefix)
let top = allprefixedAux(c, prefix, longestMatch)
for x in leaves(top): yield x.key
iterator valuesWithPrefix*[T](c: CritBitTree[T], prefix: string): T =
iterator valuesWithPrefix*[T](c: CritBitTree[T], prefix: string;
longestMatch=false): T =
## yields all values of `c` starting with `prefix` of the
## corresponding keys.
let top = allprefixedAux(c, prefix)
let top = allprefixedAux(c, prefix, longestMatch)
for x in leaves(top): yield x.val
iterator mvaluesWithPrefix*[T](c: var CritBitTree[T], prefix: string): var T =
iterator mvaluesWithPrefix*[T](c: var CritBitTree[T], prefix: string;
longestMatch=false): var T =
## yields all values of `c` starting with `prefix` of the
## corresponding keys. The values can be modified.
let top = allprefixedAux(c, prefix)
let top = allprefixedAux(c, prefix, longestMatch)
for x in leaves(top): yield x.val
iterator pairsWithPrefix*[T](c: CritBitTree[T],
prefix: string): tuple[key: string, val: T] =
prefix: string;
longestMatch=false): tuple[key: string, val: T] =
## yields all (key, value)-pairs of `c` starting with `prefix`.
let top = allprefixedAux(c, prefix)
let top = allprefixedAux(c, prefix, longestMatch)
for x in leaves(top): yield (x.key, x.val)
iterator mpairsWithPrefix*[T](c: var CritBitTree[T],
prefix: string): tuple[key: string, val: var T] =
prefix: string;
longestMatch=false): tuple[key: string, val: var T] =
## yields all (key, value)-pairs of `c` starting with `prefix`.
## The yielded values can be modified.
let top = allprefixedAux(c, prefix)
let top = allprefixedAux(c, prefix, longestMatch)
for x in leaves(top): yield (x.key, x.val)
proc `$`*[T](c: CritBitTree[T]): string =

View file

@ -10,12 +10,9 @@
## :Author: Alexander Mitchell-Robinson (Amrykid)
##
## This module implements operations for the built-in `seq`:idx: type which
## were inspired by functional programming languages. If you are looking for
## the typical `map` function which applies a function to every element in a
## sequence, it already exists in the `system <system.html>`_ module in both
## mutable and immutable styles.
## were inspired by functional programming languages.
##
## Also, for functional style programming you may want to pass `anonymous procs
## For functional style programming you may want to pass `anonymous procs
## <manual.html#anonymous-procs>`_ to procs like ``filter`` to reduce typing.
## Anonymous procs can use `the special do notation <manual.html#do-notation>`_
## which is more convenient in certain situations.
@ -471,7 +468,7 @@ template toSeq*(iter: expr): expr {.immediate.} =
## if x mod 2 == 1:
## result = true)
## assert odd_numbers == @[1, 3, 5, 7, 9]
when compiles(iter.len):
var i = 0
var result = newSeq[type(iter)](iter.len)

View file

@ -887,7 +887,7 @@ proc mget*[A](t: CountTableRef[A], key: A): var int {.deprecated.} =
result = t[][key]
proc getOrDefault*[A](t: CountTableRef[A], key: A): int =
getOrDefaultImpl(t, key)
result = t[].getOrDefault(key)
proc hasKey*[A](t: CountTableRef[A], key: A): bool =
## returns true iff `key` is in the table `t`.
@ -1028,3 +1028,15 @@ when isMainModule:
assert(merged["foo"] == 5)
assert(merged["bar"] == 3)
assert(merged["baz"] == 14)
block:
const testKey = "TESTKEY"
let t: CountTableRef[string] = newCountTable[string]()
# Before, does not compile with error message:
#test_counttable.nim(7, 43) template/generic instantiation from here
#lib/pure/collections/tables.nim(117, 21) template/generic instantiation from here
#lib/pure/collections/tableimpl.nim(32, 27) Error: undeclared field: 'hcode
doAssert 0 == t.getOrDefault(testKey)
t.inc(testKey,3)
doAssert 3 == t.getOrDefault(testKey)

103
lib/pure/db_common.nim Normal file
View file

@ -0,0 +1,103 @@
#
#
# Nim's Runtime Library
# (c) Copyright 2015 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## Common datatypes and definitions for all ``db_*.nim`` (
## `db_mysql <db_mysql.html>`_, `db_postgres <db_postgres.html>`_,
## and `db_sqlite <db_sqlite.html>`_) modules.
type
DbError* = object of IOError ## exception that is raised if a database error occurs
SqlQuery* = distinct string ## an SQL query string
DbEffect* = object of IOEffect ## effect that denotes a database operation
ReadDbEffect* = object of DbEffect ## effect that denotes a read operation
WriteDbEffect* = object of DbEffect ## effect that denotes a write operation
DbTypeKind* = enum ## a superset of datatypes that might be supported.
dbUnknown, ## unknown datatype
dbSerial, ## datatype used for primary auto-increment keys
dbNull, ## datatype used for the NULL value
dbBit, ## bit datatype
dbBool, ## boolean datatype
dbBlob, ## blob datatype
dbFixedChar, ## string of fixed length
dbVarchar, ## string datatype
dbJson, ## JSON datatype
dbXml, ## XML datatype
dbInt, ## some integer type
dbUInt, ## some unsigned integer type
dbDecimal, ## decimal numbers (fixed-point number)
dbFloat, ## some floating point type
dbDate, ## a year-month-day description
dbTime, ## HH:MM:SS information
dbDatetime, ## year-month-day and HH:MM:SS information,
## plus optional time or timezone information
dbTimestamp, ## Timestamp values are stored as the number of seconds
## since the epoch ('1970-01-01 00:00:00' UTC).
dbTimeInterval, ## an interval [a,b] of times
dbEnum, ## some enum
dbSet, ## set of enum values
dbArray, ## an array of values
dbComposite, ## composite type (record, struct, etc)
dbUrl, ## a URL
dbUuid, ## a UUID
dbInet, ## an IP address
dbMacAddress, ## a MAC address
dbGeometry, ## some geometric type
dbPoint, ## Point on a plane (x,y)
dbLine, ## Infinite line ((x1,y1),(x2,y2))
dbLseg, ## Finite line segment ((x1,y1),(x2,y2))
dbBox, ## Rectangular box ((x1,y1),(x2,y2))
dbPath, ## Closed or open path (similar to polygon) ((x1,y1),...)
dbPolygon, ## Polygon (similar to closed path) ((x1,y1),...)
dbCircle, ## Circle <(x,y),r> (center point and radius)
dbUser1, ## user definable datatype 1 (for unknown extensions)
dbUser2, ## user definable datatype 2 (for unknown extensions)
dbUser3, ## user definable datatype 3 (for unknown extensions)
dbUser4, ## user definable datatype 4 (for unknown extensions)
dbUser5 ## user definable datatype 5 (for unknown extensions)
DbType* = object ## describes a database type
kind*: DbTypeKind ## the kind of the described type
notNull*: bool ## does the type contain NULL?
name*: string ## the name of the type
size*: Natural ## the size of the datatype; 0 if of variable size
maxReprLen*: Natural ## maximal length required for the representation
precision*, scale*: Natural ## precision and scale of the number
min*, max*: BiggestInt ## the minimum and maximum of allowed values
validValues*: seq[string] ## valid values of an enum or a set
DbColumn* = object ## information about a database column
name*: string ## name of the column
tableName*: string ## name of the table the column belongs to (optional)
typ*: DbType ## type of the column
primaryKey*: bool ## is this a primary key?
foreignKey*: bool ## is this a foreign key?
DbColumns* = seq[DbColumn]
{.deprecated: [EDb: DbError, TSqlQuery: SqlQuery, FDb: DbEffect,
FReadDb: ReadDbEffect, FWriteDb: WriteDbEffect].}
template sql*(query: string): SqlQuery =
## constructs a SqlQuery from the string `query`. This is supposed to be
## used as a raw-string-literal modifier:
## ``sql"update user set counter = counter + 1"``
##
## If assertions are turned off, it does nothing. If assertions are turned
## on, later versions will check the string for valid syntax.
SqlQuery(query)
proc dbError*(msg: string) {.noreturn, noinline.} =
## raises an DbError exception with message `msg`.
var e: ref DbError
new(e)
e.msg = msg
raise e

View file

@ -57,7 +57,7 @@ proc addHandler*(handler: var EventHandler, fn: proc(e: EventArgs) {.closure.})
proc removeHandler*(handler: var EventHandler, fn: proc(e: EventArgs) {.closure.}) =
## Removes the callback from the specified event handler.
for i in countup(0, len(handler.handlers) -1):
for i in countup(0, len(handler.handlers)-1):
if fn == handler.handlers[i]:
handler.handlers.del(i)
break

View file

@ -10,6 +10,9 @@
## This module allows you to monitor files or directories for changes using
## asyncio.
##
## **Warning**: This module will likely disappear soon and be moved into a
## new Nimble package.
##
## Windows support is not yet implemented.
##
## **Note:** This module uses ``inotify`` on Linux (Other Unixes are not yet
@ -34,8 +37,8 @@ type
MonitorEventType* = enum ## Monitor event type
MonitorAccess, ## File was accessed.
MonitorAttrib, ## Metadata changed.
MonitorCloseWrite, ## Writtable file was closed.
MonitorCloseNoWrite, ## Unwrittable file closed.
MonitorCloseWrite, ## Writable file was closed.
MonitorCloseNoWrite, ## Non-writable file closed.
MonitorCreate, ## Subfile was created.
MonitorDelete, ## Subfile was deleted.
MonitorDeleteSelf, ## Watched file/directory was itself deleted.
@ -78,21 +81,21 @@ proc add*(monitor: FSMonitor, target: string,
## watched paths of ``monitor``.
## You can specify the events to report using the ``filters`` parameter.
var INFilter = -1
var INFilter = 0
for f in filters:
case f
of MonitorAccess: INFilter = INFilter and IN_ACCESS
of MonitorAttrib: INFilter = INFilter and IN_ATTRIB
of MonitorCloseWrite: INFilter = INFilter and IN_CLOSE_WRITE
of MonitorCloseNoWrite: INFilter = INFilter and IN_CLOSE_NO_WRITE
of MonitorCreate: INFilter = INFilter and IN_CREATE
of MonitorDelete: INFilter = INFilter and IN_DELETE
of MonitorDeleteSelf: INFilter = INFilter and IN_DELETE_SELF
of MonitorModify: INFilter = INFilter and IN_MODIFY
of MonitorMoveSelf: INFilter = INFilter and IN_MOVE_SELF
of MonitorMoved: INFilter = INFilter and IN_MOVED_FROM and IN_MOVED_TO
of MonitorOpen: INFilter = INFilter and IN_OPEN
of MonitorAll: INFilter = INFilter and IN_ALL_EVENTS
of MonitorAccess: INFilter = INFilter or IN_ACCESS
of MonitorAttrib: INFilter = INFilter or IN_ATTRIB
of MonitorCloseWrite: INFilter = INFilter or IN_CLOSE_WRITE
of MonitorCloseNoWrite: INFilter = INFilter or IN_CLOSE_NO_WRITE
of MonitorCreate: INFilter = INFilter or IN_CREATE
of MonitorDelete: INFilter = INFilter or IN_DELETE
of MonitorDeleteSelf: INFilter = INFilter or IN_DELETE_SELF
of MonitorModify: INFilter = INFilter or IN_MODIFY
of MonitorMoveSelf: INFilter = INFilter or IN_MOVE_SELF
of MonitorMoved: INFilter = INFilter or IN_MOVED_FROM or IN_MOVED_TO
of MonitorOpen: INFilter = INFilter or IN_OPEN
of MonitorAll: INFilter = INFilter or IN_ALL_EVENTS
result = inotifyAddWatch(monitor.fd, target, INFilter.uint32)
if result < 0:
@ -200,9 +203,18 @@ proc register*(d: Dispatcher, monitor: FSMonitor,
when not defined(testing) and isMainModule:
proc main =
var disp = newDispatcher()
var monitor = newMonitor()
echo monitor.add("/home/dom/inotifytests/")
var
disp = newDispatcher()
monitor = newMonitor()
n = 0
n = monitor.add("/tmp")
assert n == 1
n = monitor.add("/tmp", {MonitorAll})
assert n == 1
n = monitor.add("/tmp", {MonitorCloseWrite, MonitorCloseNoWrite})
assert n == 1
n = monitor.add("/tmp", {MonitorMoved, MonitorOpen, MonitorAccess})
assert n == 1
disp.register(monitor,
proc (m: FSMonitor, ev: MonitorEvent) =
echo("Got event: ", ev.kind)

View file

@ -11,6 +11,8 @@
## key-value mapping. The keys are required to be strings, but the values
## may be any Nim or user defined type. This module supports matching
## of keys in case-sensitive, case-insensitive and style-insensitive modes.
##
## **Warning:** This module is deprecated, new code shouldn't use it!
{.deprecated.}

View file

@ -110,7 +110,7 @@ type
EInvalidProtocol: ProtocolError, EHttpRequestErr: HttpRequestError
].}
const defUserAgent* = "Nim httpclient/0.1"
const defUserAgent* = "Nim httpclient/" & NimVersion
proc httpError(msg: string) =
var e: ref ProtocolError
@ -389,6 +389,7 @@ proc request*(url: string, httpMethod: string, extraHeaders = "",
## | An optional timeout can be specified in milliseconds, if reading from the
## server takes longer than specified an ETimeout exception will be raised.
var r = if proxy == nil: parseUri(url) else: proxy.url
var hostUrl = if proxy == nil: r else: parseUri(url)
var headers = substr(httpMethod, len("http"))
# TODO: Use generateHeaders further down once it supports proxies.
if proxy == nil:
@ -402,10 +403,10 @@ proc request*(url: string, httpMethod: string, extraHeaders = "",
headers.add(" HTTP/1.1\c\L")
if r.port == "":
add(headers, "Host: " & r.hostname & "\c\L")
if hostUrl.port == "":
add(headers, "Host: " & hostUrl.hostname & "\c\L")
else:
add(headers, "Host: " & r.hostname & ":" & r.port & "\c\L")
add(headers, "Host: " & hostUrl.hostname & ":" & hostUrl.port & "\c\L")
if userAgent != "":
add(headers, "User-Agent: " & userAgent & "\c\L")
@ -414,7 +415,6 @@ proc request*(url: string, httpMethod: string, extraHeaders = "",
add(headers, "Proxy-Authorization: basic " & auth & "\c\L")
add(headers, extraHeaders)
add(headers, "\c\L")
var s = newSocket()
if s == nil: raiseOSError(osLastError())
var port = net.Port(80)

View file

@ -9,6 +9,9 @@
## This module implements a simple HTTP-Server.
##
## **Warning**: This module will soon be deprecated in favour of
## the ``asyncdispatch`` module, you should use it instead.
##
## Example:
##
## .. code-block:: nim

View file

@ -28,7 +28,10 @@ type
BaseLexer* = object of RootObj ## the base lexer. Inherit your lexer from
## this object.
bufpos*: int ## the current position within the buffer
buf*: cstring ## the buffer itself
when defined(js): ## the buffer itself
buf*: string
else:
buf*: cstring
bufLen*: int ## length of buffer in characters
input: Stream ## the input stream
lineNumber*: int ## the current line number
@ -43,7 +46,8 @@ const
proc close*(L: var BaseLexer) =
## closes the base lexer. This closes `L`'s associated stream too.
dealloc(L.buf)
when not defined(js):
dealloc(L.buf)
close(L.input)
proc fillBuffer(L: var BaseLexer) =
@ -58,8 +62,11 @@ proc fillBuffer(L: var BaseLexer) =
toCopy = L.bufLen - L.sentinel - 1
assert(toCopy >= 0)
if toCopy > 0:
moveMem(L.buf, addr(L.buf[L.sentinel + 1]), toCopy * chrSize)
# "moveMem" handles overlapping regions
when defined(js):
for i in 0 ..< toCopy: L.buf[i] = L.buf[L.sentinel + 1 + i]
else:
# "moveMem" handles overlapping regions
moveMem(L.buf, addr L.buf[L.sentinel + 1], toCopy * chrSize)
charsRead = readData(L.input, addr(L.buf[toCopy]),
(L.sentinel + 1) * chrSize) div chrSize
s = toCopy + charsRead
@ -81,7 +88,10 @@ proc fillBuffer(L: var BaseLexer) =
# double the buffer's size and try again:
oldBufLen = L.bufLen
L.bufLen = L.bufLen * 2
L.buf = cast[cstring](realloc(L.buf, L.bufLen * chrSize))
when defined(js):
L.buf.setLen(L.bufLen)
else:
L.buf = cast[cstring](realloc(L.buf, L.bufLen * chrSize))
assert(L.bufLen - oldBufLen == oldBufLen)
charsRead = readData(L.input, addr(L.buf[oldBufLen]),
oldBufLen * chrSize) div chrSize
@ -139,7 +149,10 @@ proc open*(L: var BaseLexer, input: Stream, bufLen: int = 8192;
L.bufpos = 0
L.bufLen = bufLen
L.refillChars = refillChars
L.buf = cast[cstring](alloc(bufLen * chrSize))
when defined(js):
L.buf = newString(bufLen)
else:
L.buf = cast[cstring](alloc(bufLen * chrSize))
L.sentinel = bufLen - 1
L.lineStart = 0
L.lineNumber = 1 # lines start at 1

View file

@ -118,26 +118,6 @@ proc sum*[T](x: openArray[T]): T {.noSideEffect.} =
## If `x` is empty, 0 is returned.
for i in items(x): result = result + i
template toFloat(f: float): float = f
proc mean*[T](x: openArray[T]): float {.noSideEffect.} =
## Computes the mean of the elements in `x`, which are first converted to floats.
## If `x` is empty, NaN is returned.
## ``toFloat(x: T): float`` must be defined.
for i in items(x): result = result + toFloat(i)
result = result / toFloat(len(x))
proc variance*[T](x: openArray[T]): float {.noSideEffect.} =
## Computes the variance of the elements in `x`.
## If `x` is empty, NaN is returned.
## ``toFloat(x: T): float`` must be defined.
result = 0.0
var m = mean(x)
for i in items(x):
var diff = toFloat(i) - m
result = result + diff*diff
result = result / toFloat(len(x))
proc random*(max: int): int {.benign.}
## Returns a random number in the range 0..max-1. The sequence of
## random number is always the same, unless `randomize` is called
@ -376,48 +356,6 @@ proc random*[T](a: openArray[T]): T =
## returns a random element from the openarray `a`.
result = a[random(a.low..a.len)]
type
RunningStat* = object ## an accumulator for statistical data
n*: int ## number of pushed data
sum*, min*, max*, mean*: float ## self-explaining
oldM, oldS, newS: float
{.deprecated: [TFloatClass: FloatClass, TRunningStat: RunningStat].}
proc push*(s: var RunningStat, x: float) =
## pushes a value `x` for processing
inc(s.n)
# See Knuth TAOCP vol 2, 3rd edition, page 232
if s.n == 1:
s.min = x
s.max = x
s.oldM = x
s.mean = x
s.oldS = 0.0
else:
if s.min > x: s.min = x
if s.max < x: s.max = x
s.mean = s.oldM + (x - s.oldM)/toFloat(s.n)
s.newS = s.oldS + (x - s.oldM)*(x - s.mean)
# set up for next iteration:
s.oldM = s.mean
s.oldS = s.newS
s.sum = s.sum + x
proc push*(s: var RunningStat, x: int) =
## pushes a value `x` for processing. `x` is simply converted to ``float``
## and the other push operation is called.
push(s, toFloat(x))
proc variance*(s: RunningStat): float =
## computes the current variance of `s`
if s.n > 1: result = s.newS / (toFloat(s.n - 1))
proc standardDeviation*(s: RunningStat): float =
## computes the current standard deviation of `s`
result = sqrt(variance(s))
{.pop.}
{.pop.}

View file

@ -203,9 +203,12 @@ proc getAddrInfo*(address: string, port: Port, domain: Domain = AF_INET,
hints.ai_family = toInt(domain)
hints.ai_socktype = toInt(sockType)
hints.ai_protocol = toInt(protocol)
# OpenBSD doesn't support AI_V4MAPPED and doesn't define the macro AI_V4MAPPED.
# FreeBSD doesn't support AI_V4MAPPED but defines the macro.
# https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=198092
when not defined(freebsd):
hints.ai_flags = AI_V4MAPPED
when not defined(freebsd) and not defined(openbsd) and not defined(netbsd):
if domain == AF_INET6:
hints.ai_flags = AI_V4MAPPED
var gaiResult = getaddrinfo(address, $port, addr(hints), result)
if gaiResult != 0'i32:
when useWinVersion:

View file

@ -1,7 +1,7 @@
#
#
# Nim's Runtime Library
# (c) Copyright 2012 Andreas Rumpf
# (c) Copyright 2015 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@ -117,24 +117,38 @@ when defined(memProfiler):
var
gTicker {.threadvar.}: int
proc hook(st: StackTrace, size: int) {.nimcall.} =
proc requestedHook(): bool {.nimcall.} =
if gTicker == 0:
gTicker = -1
when defined(ignoreAllocationSize):
hookAux(st, 1)
else:
hookAux(st, size)
gTicker = SamplingInterval
result = true
dec gTicker
proc hook(st: StackTrace, size: int) {.nimcall.} =
when defined(ignoreAllocationSize):
hookAux(st, 1)
else:
hookAux(st, size)
else:
var
t0 {.threadvar.}: Ticks
gTicker: int # we use an additional counter to
# avoid calling 'getTicks' too frequently
proc requestedHook(): bool {.nimcall.} =
if interval == 0: result = true
elif gTicker == 0:
gTicker = 500
if getTicks() - t0 > interval:
result = true
else:
dec gTicker
proc hook(st: StackTrace) {.nimcall.} =
#echo "profiling! ", interval
if interval == 0:
hookAux(st, 1)
elif int64(t0) == 0 or getTicks() - t0 > interval:
else:
hookAux(st, 1)
t0 = getTicks()
@ -145,9 +159,10 @@ proc cmpEntries(a, b: ptr ProfileEntry): int =
result = b.getTotal - a.getTotal
proc `//`(a, b: int): string =
result = format("$1/$2 = $3%", a, b, formatFloat(a / b * 100.0, ffDefault, 2))
result = format("$1/$2 = $3%", a, b, formatFloat(a / b * 100.0, ffDecimal, 2))
proc writeProfile() {.noconv.} =
system.profilingRequestedHook = nil
when declared(system.StackTrace):
system.profilerHook = nil
const filename = "profile_results.txt"
@ -193,14 +208,15 @@ var
proc disableProfiling*() =
when declared(system.StackTrace):
atomicDec disabled
system.profilerHook = nil
system.profilingRequestedHook = nil
proc enableProfiling*() =
when declared(system.StackTrace):
if atomicInc(disabled) >= 0:
system.profilerHook = hook
system.profilingRequestedHook = requestedHook
when declared(system.StackTrace):
system.profilingRequestedHook = requestedHook
system.profilerHook = hook
addQuitProc(writeProfile)

View file

@ -7,6 +7,9 @@
# distribution, for details about the copyright.
#
## **Warning:** This module will be moved out of the stdlib and into a
## Nimble package, don't use it.
type OneVarFunction* = proc (x: float): float
{.deprecated: [TOneVarFunction: OneVarFunction].}

View file

@ -28,7 +28,7 @@
##
## .. code-block:: nim
##
## import optionals
## import options
##
## proc find(haystack: string, needle: char): Option[int] =
## for i, c in haystack:
@ -156,7 +156,7 @@ proc `$`*[T]( self: Option[T] ): string =
when isMainModule:
import unittest, sequtils
suite "optionals":
suite "options":
# work around a bug in unittest
let intNone = none(int)
let stringNone = none(string)

View file

@ -810,6 +810,10 @@ type
{.deprecated: [TPathComponent: PathComponent].}
proc staticWalkDir(dir: string; relative: bool): seq[
tuple[kind: PathComponent, path: string]] =
discard
iterator walkDir*(dir: string; relative=false): tuple[kind: PathComponent, path: string] {.
tags: [ReadDirEffect].} =
## walks over the directory `dir` and yields for each directory or file in
@ -833,49 +837,53 @@ iterator walkDir*(dir: string; relative=false): tuple[kind: PathComponent, path:
## dirA/dirC
## dirA/fileA1.txt
## dirA/fileA2.txt
when defined(windows):
var f: WIN32_FIND_DATA
var h = findFirstFile(dir / "*", f)
if h != -1:
while true:
var k = pcFile
if not skipFindData(f):
if (f.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) != 0'i32:
k = pcDir
if (f.dwFileAttributes and FILE_ATTRIBUTE_REPARSE_POINT) != 0'i32:
k = succ(k)
let xx = if relative: extractFilename(getFilename(f))
else: dir / extractFilename(getFilename(f))
yield (k, xx)
if findNextFile(h, f) == 0'i32: break
findClose(h)
when nimvm:
for k, v in items(staticWalkDir(dir, relative)):
yield (k, v)
else:
var d = opendir(dir)
if d != nil:
while true:
var x = readdir(d)
if x == nil: break
var y = $x.d_name
if y != "." and y != "..":
var s: Stat
if not relative:
y = dir / y
when defined(windows):
var f: WIN32_FIND_DATA
var h = findFirstFile(dir / "*", f)
if h != -1:
while true:
var k = pcFile
if not skipFindData(f):
if (f.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) != 0'i32:
k = pcDir
if (f.dwFileAttributes and FILE_ATTRIBUTE_REPARSE_POINT) != 0'i32:
k = succ(k)
let xx = if relative: extractFilename(getFilename(f))
else: dir / extractFilename(getFilename(f))
yield (k, xx)
if findNextFile(h, f) == 0'i32: break
findClose(h)
else:
var d = opendir(dir)
if d != nil:
while true:
var x = readdir(d)
if x == nil: break
var y = $x.d_name
if y != "." and y != "..":
var s: Stat
if not relative:
y = dir / y
var k = pcFile
when defined(linux) or defined(macosx) or defined(bsd):
if x.d_type != DT_UNKNOWN:
if x.d_type == DT_DIR: k = pcDir
if x.d_type == DT_LNK:
if dirExists(y): k = pcLinkToDir
else: k = succ(k)
yield (k, y)
continue
when defined(linux) or defined(macosx) or defined(bsd):
if x.d_type != DT_UNKNOWN:
if x.d_type == DT_DIR: k = pcDir
if x.d_type == DT_LNK:
if dirExists(y): k = pcLinkToDir
else: k = succ(k)
yield (k, y)
continue
if lstat(y, s) < 0'i32: break
if S_ISDIR(s.st_mode): k = pcDir
if S_ISLNK(s.st_mode): k = succ(k)
yield (k, y)
discard closedir(d)
if lstat(y, s) < 0'i32: break
if S_ISDIR(s.st_mode): k = pcDir
if S_ISLNK(s.st_mode): k = succ(k)
yield (k, y)
discard closedir(d)
iterator walkDirRec*(dir: string, filter={pcFile, pcDir}): string {.
tags: [ReadDirEffect].} =
@ -1353,7 +1361,7 @@ proc getAppFilename*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect].} =
# /proc/<pid>/file
when defined(windows):
when useWinUnicode:
var buf = cast[WideCString](alloc(256*2))
var buf = newWideCString("", 256)
var len = getModuleFileNameW(0, buf, 256)
result = buf$len
else:

View file

@ -886,7 +886,7 @@ elif not defined(useNimRtl):
discard write(data.pErrorPipe[writeIdx], addr error, sizeof(error))
exitnow(1)
when defined(macosx) or defined(freebsd):
when defined(macosx) or defined(freebsd) or defined(netbsd) or defined(android):
var environ {.importc.}: cstringArray
proc startProcessAfterFork(data: ptr StartProcessData) =
@ -916,7 +916,7 @@ elif not defined(useNimRtl):
discard fcntl(data.pErrorPipe[writeIdx], F_SETFD, FD_CLOEXEC)
if data.optionPoUsePath:
when defined(macosx) or defined(freebsd):
when defined(macosx) or defined(freebsd) or defined(netbsd) or defined(android):
# MacOSX doesn't have execvpe, so we need workaround.
# On MacOSX we can arrive here only from fork, so this is safe:
environ = data.sysEnv
@ -937,9 +937,10 @@ elif not defined(useNimRtl):
if p.inStream != nil: close(p.inStream)
if p.outStream != nil: close(p.outStream)
if p.errStream != nil: close(p.errStream)
discard close(p.inHandle)
discard close(p.outHandle)
discard close(p.errHandle)
if poParentStreams notin p.options:
discard close(p.inHandle)
discard close(p.outHandle)
discard close(p.errHandle)
proc suspend(p: Process) =
if kill(p.id, SIGSTOP) != 0'i32: raiseOsError(osLastError())

27
lib/pure/oswalkdir.nim Normal file
View file

@ -0,0 +1,27 @@
## Compile-time only version for walkDir if you need it at compile-time
## for JavaScript.
type
PathComponent* = enum ## Enumeration specifying a path component.
pcFile, ## path refers to a file
pcLinkToFile, ## path refers to a symbolic link to a file
pcDir, ## path refers to a directory
pcLinkToDir ## path refers to a symbolic link to a directory
proc staticWalkDir(dir: string; relative: bool): seq[
tuple[kind: PathComponent, path: string]] =
discard
iterator walkDir*(dir: string; relative=false): tuple[kind: PathComponent, path: string] =
for k, v in items(staticWalkDir(dir, relative)):
yield (k, v)
iterator walkDirRec*(dir: string, filter={pcFile, pcDir}): string =
var stack = @[dir]
while stack.len > 0:
for k,p in walkDir(stack.pop()):
if k in filter:
case k
of pcFile, pcLinkToFile: yield p
of pcDir, pcLinkToDir: stack.add(p)

View file

@ -70,7 +70,7 @@ when not defined(createNimRtl):
## Initializes option parser from current command line arguments.
return initOptParser(commandLineParams())
proc next*(p: var OptParser) {.rtl, extern: "npo$1".}
proc next*(p: var OptParser) {.rtl, extern: "npo2$1".}
proc nextOption(p: var OptParser, token: string, allowEmpty: bool) =
for splitchar in [':', '=']:
@ -113,7 +113,7 @@ proc next(p: var OptParser) =
p.key = token
p.val = ""
proc cmdLineRest*(p: OptParser): TaintedString {.rtl, extern: "npo$1", deprecated.} =
proc cmdLineRest*(p: OptParser): TaintedString {.rtl, extern: "npo2$1", deprecated.} =
## Returns part of command line string that has not been parsed yet.
## Do not use - does not correctly handle whitespace.
return p.cmd[p.pos..p.cmd.len-1].join(" ")

View file

@ -25,7 +25,7 @@ const
proc toLower(c: char): char {.inline.} =
result = if c in {'A'..'Z'}: chr(ord(c)-ord('A')+ord('a')) else: c
proc parseHex*(s: string, number: var int, start = 0): int {.
proc parseHex*(s: string, number: var int, start = 0; maxLen = 0): int {.
rtl, extern: "npuParseHex", noSideEffect.} =
## Parses a hexadecimal number and stores its value in ``number``.
##
@ -45,11 +45,14 @@ proc parseHex*(s: string, number: var int, start = 0): int {.
## discard parseHex("0x38", value)
## assert value == -200
##
## If 'maxLen==0' the length of the hexadecimal number has no
## upper bound. Not more than ```maxLen`` characters are parsed.
var i = start
var foundDigit = false
if s[i] == '0' and (s[i+1] == 'x' or s[i+1] == 'X'): inc(i, 2)
elif s[i] == '#': inc(i)
while true:
let last = if maxLen == 0: s.len else: i+maxLen
while i < last:
case s[i]
of '_': discard
of '0'..'9':

View file

@ -7,6 +7,9 @@
# distribution, for details about the copyright.
#
## **Warning:** This module will be moved out of the stdlib and into a
## Nimble package, don't use it.
import math
import strutils
import numeric

File diff suppressed because it is too large Load diff

View file

@ -9,6 +9,9 @@
## Module for converting an integer to a Roman numeral.
## See http://en.wikipedia.org/wiki/Roman_numerals for reference.
##
## **Warning:** This module will be moved out of the stdlib and into a
## Nimble package, don't use it.
const
RomanNumeralDigits* = {'I', 'i', 'V', 'v', 'X', 'x', 'L', 'l', 'C', 'c',

348
lib/pure/stats.nim Normal file
View file

@ -0,0 +1,348 @@
#
#
# Nim's Runtime Library
# (c) Copyright 2015 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## Statistical analysis framework for performing
## basic statistical analysis of data.
## The data is analysed in a single pass, when a data value
## is pushed to the ``RunningStat`` or ``RunningRegress`` objects
##
## ``RunningStat`` calculates for a single data set
## - n (data count)
## - min (smallest value)
## - max (largest value)
## - sum
## - mean
## - variance
## - varianceS (sample var)
## - standardDeviation
## - standardDeviationS (sample stddev)
## - skewness (the third statistical moment)
## - kurtosis (the fourth statistical moment)
##
## ``RunningRegress`` calculates for two sets of data
## - n
## - slope
## - intercept
## - correlation
##
## Procs have been provided to calculate statistics on arrays and sequences.
##
## However, if more than a single statistical calculation is required, it is more
## efficient to push the data once to the RunningStat object, and
## call the numerous statistical procs for the RunningStat object.
##
## .. code-block:: Nim
##
## var rs: RunningStat
## rs.push(MySeqOfData)
## rs.mean()
## rs.variance()
## rs.skewness()
## rs.kurtosis()
from math import FloatClass, sqrt, pow, round
{.push debugger:off .} # the user does not want to trace a part
# of the standard library!
{.push checks:off, line_dir:off, stack_trace:off.}
type
RunningStat* = object ## an accumulator for statistical data
n*: int ## number of pushed data
min*, max*, sum*: float ## self-explaining
mom1, mom2, mom3, mom4: float ## statistical moments, mom1 is mean
RunningRegress* = object ## an accumulator for regression calculations
n*: int ## number of pushed data
x_stats*: RunningStat ## stats for first set of data
y_stats*: RunningStat ## stats for second set of data
s_xy: float ## accumulated data for combined xy
{.deprecated: [TFloatClass: FloatClass, TRunningStat: RunningStat].}
# ----------- RunningStat --------------------------
proc clear*(s: var RunningStat) =
## reset `s`
s.n = 0
s.min = toBiggestFloat(int.high)
s.max = 0.0
s.sum = 0.0
s.mom1 = 0.0
s.mom2 = 0.0
s.mom3 = 0.0
s.mom4 = 0.0
proc push*(s: var RunningStat, x: float) =
## pushes a value `x` for processing
if s.n == 0: s.min = x
inc(s.n)
# See Knuth TAOCP vol 2, 3rd edition, page 232
if s.min > x: s.min = x
if s.max < x: s.max = x
s.sum += x
let n = toFloat(s.n)
let delta = x - s.mom1
let delta_n = delta / toFloat(s.n)
let delta_n2 = delta_n * delta_n
let term1 = delta * delta_n * toFloat(s.n - 1)
s.mom4 += term1 * delta_n2 * (n*n - 3*n + 3) +
6*delta_n2*s.mom2 - 4*delta_n*s.mom3
s.mom3 += term1 * delta_n * (n - 2) - 3*delta_n*s.mom2
s.mom2 += term1
s.mom1 += delta_n
proc push*(s: var RunningStat, x: int) =
## pushes a value `x` for processing.
##
## `x` is simply converted to ``float``
## and the other push operation is called.
s.push(toFloat(x))
proc push*(s: var RunningStat, x: openarray[float|int]) =
## pushes all values of `x` for processing.
##
## Int values of `x` are simply converted to ``float`` and
## the other push operation is called.
for val in x:
s.push(val)
proc mean*(s: RunningStat): float =
## computes the current mean of `s`
result = s.mom1
proc variance*(s: RunningStat): float =
## computes the current population variance of `s`
result = s.mom2 / toFloat(s.n)
proc varianceS*(s: RunningStat): float =
## computes the current sample variance of `s`
if s.n > 1: result = s.mom2 / toFloat(s.n - 1)
proc standardDeviation*(s: RunningStat): float =
## computes the current population standard deviation of `s`
result = sqrt(variance(s))
proc standardDeviationS*(s: RunningStat): float =
## computes the current sample standard deviation of `s`
result = sqrt(varianceS(s))
proc skewness*(s: RunningStat): float =
## computes the current population skewness of `s`
result = sqrt(toFloat(s.n)) * s.mom3 / pow(s.mom2, 1.5)
proc skewnessS*(s: RunningStat): float =
## computes the current sample skewness of `s`
let s2 = skewness(s)
result = sqrt(toFloat(s.n*(s.n-1)))*s2 / toFloat(s.n-2)
proc kurtosis*(s: RunningStat): float =
## computes the current population kurtosis of `s`
result = toFloat(s.n) * s.mom4 / (s.mom2 * s.mom2) - 3.0
proc kurtosisS*(s: RunningStat): float =
## computes the current sample kurtosis of `s`
result = toFloat(s.n-1) / toFloat((s.n-2)*(s.n-3)) *
(toFloat(s.n+1)*kurtosis(s) + 6)
proc `+`*(a, b: RunningStat): RunningStat =
## combine two RunningStats.
##
## Useful if performing parallel analysis of data series
## and need to re-combine parallel result sets
result.clear()
result.n = a.n + b.n
let delta = b.mom1 - a.mom1
let delta2 = delta*delta
let delta3 = delta*delta2
let delta4 = delta2*delta2
let n = toFloat(result.n)
result.mom1 = (a.n.float*a.mom1 + b.n.float*b.mom1) / n
result.mom2 = a.mom2 + b.mom2 + delta2 * a.n.float * b.n.float / n
result.mom3 = a.mom3 + b.mom3 +
delta3 * a.n.float * b.n.float * (a.n.float - b.n.float)/(n*n);
result.mom3 += 3.0*delta * (a.n.float*b.mom2 - b.n.float*a.mom2) / n
result.mom4 = a.mom4 + b.mom4 +
delta4*a.n.float*b.n.float * toFloat(a.n*a.n - a.n*b.n + b.n*b.n) /
(n*n*n)
result.mom4 += 6.0*delta2 * (a.n.float*a.n.float*b.mom2 + b.n.float*b.n.float*a.mom2) /
(n*n) +
4.0*delta*(a.n.float*b.mom3 - b.n.float*a.mom3) / n
result.max = max(a.max, b.max)
result.min = max(a.min, b.min)
proc `+=`*(a: var RunningStat, b: RunningStat) {.inline.} =
## add a second RunningStats `b` to `a`
a = a + b
# ---------------------- standalone array/seq stats ---------------------
proc mean*[T](x: openArray[T]): float =
## computes the mean of `x`
var rs: RunningStat
rs.push(x)
result = rs.mean()
proc variance*[T](x: openArray[T]): float =
## computes the population variance of `x`
var rs: RunningStat
rs.push(x)
result = rs.variance()
proc varianceS*[T](x: openArray[T]): float =
## computes the sample variance of `x`
var rs: RunningStat
rs.push(x)
result = rs.varianceS()
proc standardDeviation*[T](x: openArray[T]): float =
## computes the population standardDeviation of `x`
var rs: RunningStat
rs.push(x)
result = rs.standardDeviation()
proc standardDeviationS*[T](x: openArray[T]): float =
## computes the sanple standardDeviation of `x`
var rs: RunningStat
rs.push(x)
result = rs.standardDeviationS()
proc skewness*[T](x: openArray[T]): float =
## computes the population skewness of `x`
var rs: RunningStat
rs.push(x)
result = rs.skewness()
proc skewnessS*[T](x: openArray[T]): float =
## computes the sample skewness of `x`
var rs: RunningStat
rs.push(x)
result = rs.skewnessS()
proc kurtosis*[T](x: openArray[T]): float =
## computes the population kurtosis of `x`
var rs: RunningStat
rs.push(x)
result = rs.kurtosis()
proc kurtosisS*[T](x: openArray[T]): float =
## computes the sample kurtosis of `x`
var rs: RunningStat
rs.push(x)
result = rs.kurtosisS()
# ---------------------- Running Regression -----------------------------
proc clear*(r: var RunningRegress) =
## reset `r`
r.x_stats.clear()
r.y_stats.clear()
r.s_xy = 0.0
r.n = 0
proc push*(r: var RunningRegress, x, y: float) =
## pushes two values `x` and `y` for processing
r.s_xy += (r.x_stats.mean() - x)*(r.y_stats.mean() - y)*
toFloat(r.n) / toFloat(r.n + 1)
r.x_stats.push(x)
r.y_stats.push(y)
inc(r.n)
proc push*(r: var RunningRegress, x, y: int) {.inline.} =
## pushes two values `x` and `y` for processing.
##
## `x` and `y` are converted to ``float``
## and the other push operation is called.
r.push(toFloat(x), toFloat(y))
proc push*(r: var RunningRegress, x, y: openarray[float|int]) =
## pushes two sets of values `x` and `y` for processing.
assert(x.len == y.len)
for i in 0..<x.len:
r.push(x[i], y[i])
proc slope*(r: RunningRegress): float =
## computes the current slope of `r`
let s_xx = r.x_stats.varianceS()*toFloat(r.n - 1)
result = r.s_xy / s_xx
proc intercept*(r: RunningRegress): float =
## computes the current intercept of `r`
result = r.y_stats.mean() - r.slope()*r.x_stats.mean()
proc correlation*(r: RunningRegress): float =
## computes the current correlation of the two data
## sets pushed into `r`
let t = r.x_stats.standardDeviation() * r.y_stats.standardDeviation()
result = r.s_xy / ( toFloat(r.n) * t )
proc `+`*(a, b: RunningRegress): RunningRegress =
## combine two `RunningRegress` objects.
##
## Useful if performing parallel analysis of data series
## and need to re-combine parallel result sets
result.clear()
result.x_stats = a.x_stats + b.x_stats
result.y_stats = a.y_stats + b.y_stats
result.n = a.n + b.n
let delta_x = b.x_stats.mean() - a.x_stats.mean()
let delta_y = b.y_stats.mean() - a.y_stats.mean()
result.s_xy = a.s_xy + b.s_xy +
toFloat(a.n*b.n)*delta_x*delta_y/toFloat(result.n)
proc `+=`*(a: var RunningRegress, b: RunningRegress) =
## add RunningRegress `b` to `a`
a = a + b
{.pop.}
{.pop.}
when isMainModule:
proc clean(x: float): float =
result = round(1.0e8*x).float * 1.0e-8
var rs: RunningStat
rs.push(@[1.0, 2.0, 1.0, 4.0, 1.0, 4.0, 1.0, 2.0])
doAssert(rs.n == 8)
doAssert(clean(rs.mean) == 2.0)
doAssert(clean(rs.variance()) == 1.5)
doAssert(clean(rs.varianceS()) == 1.71428571)
doAssert(clean(rs.skewness()) == 0.81649658)
doAssert(clean(rs.skewnessS()) == 1.01835015)
doAssert(clean(rs.kurtosis()) == -1.0)
doAssert(clean(rs.kurtosisS()) == -0.7000000000000001)
var rs1, rs2: RunningStat
rs1.push(@[1.0, 2.0, 1.0, 4.0])
rs2.push(@[1.0, 4.0, 1.0, 2.0])
let rs3 = rs1 + rs2
doAssert(clean(rs3.mom2) == clean(rs.mom2))
doAssert(clean(rs3.mom3) == clean(rs.mom3))
doAssert(clean(rs3.mom4) == clean(rs.mom4))
rs1 += rs2
doAssert(clean(rs1.mom2) == clean(rs.mom2))
doAssert(clean(rs1.mom3) == clean(rs.mom3))
doAssert(clean(rs1.mom4) == clean(rs.mom4))
rs1.clear()
rs1.push(@[1.0, 2.2, 1.4, 4.9])
doAssert(rs1.sum == 9.5)
doAssert(rs1.mean() == 2.375)
var rr: RunningRegress
rr.push(@[0.0,1.0,2.8,3.0,4.0], @[0.0,1.0,2.3,3.0,4.0])
doAssert(rr.slope() == 0.9695585996955861)
doAssert(rr.intercept() == -0.03424657534246611)
doAssert(rr.correlation() == 0.9905100362239381)
var rr1, rr2: RunningRegress
rr1.push(@[0.0,1.0], @[0.0,1.0])
rr2.push(@[2.8,3.0,4.0], @[2.3,3.0,4.0])
let rr3 = rr1 + rr2
doAssert(rr3.correlation() == rr.correlation())
doAssert(clean(rr3.slope()) == clean(rr.slope()))
doAssert(clean(rr3.intercept()) == clean(rr.intercept()))

View file

@ -1210,22 +1210,21 @@ proc unescape*(s: string, prefix = "\"", suffix = "\""): string {.noSideEffect,
## If `s` does not begin with ``prefix`` and end with ``suffix`` a
## ValueError exception will be raised.
result = newStringOfCap(s.len)
var i = 0
var i = prefix.len
if not s.startsWith(prefix):
raise newException(ValueError,
"String does not start with a prefix of: " & prefix)
inc(i)
while true:
if i == s.len-suffix.len: break
case s[i]
of '\\':
case s[i+1]:
of 'x':
inc i
inc i, 2
var c: int
i += parseutils.parseHex(s, c, i)
i += parseutils.parseHex(s, c, i, maxLen=2)
result.add(chr(c))
inc(i, 2)
dec i, 2
of '\\':
result.add('\\')
of '\'':
@ -1281,7 +1280,7 @@ proc editDistance*(a, b: string): int {.noSideEffect,
# another special case:
if len1 == 1:
for j in s..len2-1:
for j in s..s+len2-1:
if a[s] == b[j]: return len2 - 1
return len2
@ -1344,8 +1343,8 @@ proc editDistance*(a, b: string): int {.noSideEffect,
# floating point formating:
proc c_sprintf(buf, frmt: cstring): cint {.header: "<stdio.h>",
when not defined(js):
proc c_sprintf(buf, frmt: cstring): cint {.header: "<stdio.h>",
importc: "sprintf", varargs, noSideEffect.}
type
@ -1370,29 +1369,44 @@ proc formatBiggestFloat*(f: BiggestFloat, format: FloatFormatMode = ffDefault,
## after the decimal point for Nim's ``biggestFloat`` type.
##
## If ``precision == 0``, it tries to format it nicely.
const floatFormatToChar: array[FloatFormatMode, char] = ['g', 'f', 'e']
var
frmtstr {.noinit.}: array[0..5, char]
buf {.noinit.}: array[0..2500, char]
L: cint
frmtstr[0] = '%'
if precision > 0:
frmtstr[1] = '#'
frmtstr[2] = '.'
frmtstr[3] = '*'
frmtstr[4] = floatFormatToChar[format]
frmtstr[5] = '\0'
L = c_sprintf(buf, frmtstr, precision, f)
when defined(js):
var res: cstring
case format
of ffDefault:
{.emit: "`res` = `f`.toString();".}
of ffDecimal:
{.emit: "`res` = `f`.toFixed(`precision`);".}
of ffScientific:
{.emit: "`res` = `f`.toExponential(`precision`);".}
result = $res
for i in 0 ..< result.len:
# Depending on the locale either dot or comma is produced,
# but nothing else is possible:
if result[i] in {'.', ','}: result[i] = decimalsep
else:
frmtstr[1] = floatFormatToChar[format]
frmtstr[2] = '\0'
L = c_sprintf(buf, frmtstr, f)
result = newString(L)
for i in 0 ..< L:
# Depending on the locale either dot or comma is produced,
# but nothing else is possible:
if buf[i] in {'.', ','}: result[i] = decimalsep
else: result[i] = buf[i]
const floatFormatToChar: array[FloatFormatMode, char] = ['g', 'f', 'e']
var
frmtstr {.noinit.}: array[0..5, char]
buf {.noinit.}: array[0..2500, char]
L: cint
frmtstr[0] = '%'
if precision > 0:
frmtstr[1] = '#'
frmtstr[2] = '.'
frmtstr[3] = '*'
frmtstr[4] = floatFormatToChar[format]
frmtstr[5] = '\0'
L = c_sprintf(buf, frmtstr, precision, f)
else:
frmtstr[1] = floatFormatToChar[format]
frmtstr[2] = '\0'
L = c_sprintf(buf, frmtstr, f)
result = newString(L)
for i in 0 ..< L:
# Depending on the locale either dot or comma is produced,
# but nothing else is possible:
if buf[i] in {'.', ','}: result[i] = decimalsep
else: result[i] = buf[i]
proc formatFloat*(f: float, format: FloatFormatMode = ffDefault,
precision: range[0..32] = 16; decimalSep = '.'): string {.
@ -1706,3 +1720,4 @@ when isMainModule:
doAssert isUpper("ABC")
doAssert(not isUpper("AAcc"))
doAssert(not isUpper("A#$"))
doAssert(unescape(r"\x013", "", "") == "\x013")

View file

@ -29,7 +29,7 @@
## echo "epochTime() float value: ", epochTime()
## echo "getTime() float value: ", toSeconds(getTime())
## echo "cpuTime() float value: ", cpuTime()
## echo "An hour from now : ", getLocalTime(getTime()) + initInterval(0,0,0,1)
## echo "An hour from now : ", getLocalTime(getTime()) + 1.hours
## echo "An hour from (UTC) now: ", getGmTime(getTime()) + initInterval(0,0,0,1)
{.push debugger:off.} # the user does not want to trace a part
@ -171,11 +171,6 @@ type
{.deprecated: [TMonth: Month, TWeekDay: WeekDay, TTime: Time,
TTimeInterval: TimeInterval, TTimeInfo: TimeInfo].}
proc miliseconds*(t: TimeInterval): int {.deprecated.} = t.milliseconds
proc `miliseconds=`*(t:var TimeInterval, milliseconds: int) {.deprecated.} =
t.milliseconds = milliseconds
proc getTime*(): Time {.tags: [TimeEffect], benign.}
## gets the current calendar time as a UNIX epoch value (number of seconds
## elapsed since 1970) with integer precission. Use epochTime for higher
@ -245,13 +240,59 @@ proc getStartMilsecs*(): int {.deprecated, tags: [TimeEffect], benign.}
proc initInterval*(milliseconds, seconds, minutes, hours, days, months,
years: int = 0): TimeInterval =
## creates a new ``TimeInterval``.
result.milliseconds = milliseconds
result.seconds = seconds
result.minutes = minutes
result.hours = hours
result.days = days
result.months = months
result.years = years
##
## You can also use the convenience procedures called ``milliseconds``,
## ``seconds``, ``minutes``, ``hours``, ``days``, ``months``, and ``years``.
##
## Example:
##
## .. code-block:: nim
##
## let day = initInterval(hours=24)
## let tomorrow = getTime() + day
## echo(tomorrow)
var carryO = 0
result.milliseconds = `mod`(milliseconds, 1000)
carryO = `div`(milliseconds, 1000)
result.seconds = `mod`(carryO + seconds, 60)
carryO = `div`(seconds, 60)
result.minutes = `mod`(carryO + minutes, 60)
carryO = `div`(minutes, 60)
result.hours = `mod`(carryO + hours, 24)
carryO = `div`(hours, 24)
result.days = carryO + days
carryO = 0
result.months = `mod`(months, 12)
carryO = `div`(months, 12)
result.years = carryO + years
proc `+`*(ti1, ti2: TimeInterval): TimeInterval =
## Adds two ``TimeInterval`` objects together.
var carryO = 0
result.milliseconds = `mod`(ti1.milliseconds + ti2.milliseconds, 1000)
carryO = `div`(ti1.milliseconds + ti2.milliseconds, 1000)
result.seconds = `mod`(carryO + ti1.seconds + ti2.seconds, 60)
carryO = `div`(ti1.seconds + ti2.seconds, 60)
result.minutes = `mod`(carryO + ti1.minutes + ti2.minutes, 60)
carryO = `div`(ti1.minutes + ti2.minutes, 60)
result.hours = `mod`(carryO + ti1.hours + ti2.hours, 24)
carryO = `div`(ti1.hours + ti2.hours, 24)
result.days = carryO + ti1.days + ti2.days
carryO = 0
result.months = `mod`(ti1.months + ti2.months, 12)
carryO = `div`(ti1.months + ti2.months, 12)
result.years = carryO + ti1.years + ti2.years
proc `-`*(ti1, ti2: TimeInterval): TimeInterval =
## Subtracts TimeInterval ``ti1`` from ``ti2``.
result = ti1
result.milliseconds -= ti2.milliseconds
result.seconds -= ti2.seconds
result.minutes -= ti2.minutes
result.hours -= ti2.hours
result.days -= ti2.days
result.months -= ti2.months
result.years -= ti2.years
proc isLeapYear*(year: int): bool =
## returns true if ``year`` is a leap year
@ -288,13 +329,22 @@ proc toSeconds(a: TimeInfo, interval: TimeInterval): float =
newinterv.months += interval.years * 12
var curMonth = anew.month
for mth in 1 .. newinterv.months:
result += float(getDaysInMonth(curMonth, anew.year) * 24 * 60 * 60)
if curMonth == mDec:
curMonth = mJan
anew.year.inc()
else:
curMonth.inc()
if newinterv.months < 0: # subtracting
for mth in countDown(-1 * newinterv.months, 1):
result -= float(getDaysInMonth(curMonth, anew.year) * 24 * 60 * 60)
if curMonth == mJan:
curMonth = mDec
anew.year.dec()
else:
curMonth.dec()
else: # adding
for mth in 1 .. newinterv.months:
result += float(getDaysInMonth(curMonth, anew.year) * 24 * 60 * 60)
if curMonth == mDec:
curMonth = mJan
anew.year.inc()
else:
curMonth.inc()
result += float(newinterv.days * 24 * 60 * 60)
result += float(newinterv.hours * 60 * 60)
result += float(newinterv.minutes * 60)
@ -302,28 +352,39 @@ proc toSeconds(a: TimeInfo, interval: TimeInterval): float =
result += newinterv.milliseconds / 1000
proc `+`*(a: TimeInfo, interval: TimeInterval): TimeInfo =
## adds ``interval`` time.
## adds ``interval`` time from TimeInfo ``a``.
##
## **Note:** This has been only briefly tested and it may not be
## very accurate.
let t = toSeconds(timeInfoToTime(a))
let secs = toSeconds(a, interval)
#if a.tzname == "UTC":
# result = getGMTime(fromSeconds(t + secs))
#else:
result = getLocalTime(fromSeconds(t + secs))
proc `-`*(a: TimeInfo, interval: TimeInterval): TimeInfo =
## subtracts ``interval`` time.
## subtracts ``interval`` time from TimeInfo ``a``.
##
## **Note:** This has been only briefly tested, it is inaccurate especially
## when you subtract so much that you reach the Julian calendar.
let t = toSeconds(timeInfoToTime(a))
let secs = toSeconds(a, interval)
#if a.tzname == "UTC":
# result = getGMTime(fromSeconds(t - secs))
#else:
result = getLocalTime(fromSeconds(t - secs))
var intval: TimeInterval
intval.milliseconds = - interval.milliseconds
intval.seconds = - interval.seconds
intval.minutes = - interval.minutes
intval.hours = - interval.hours
intval.days = - interval.days
intval.months = - interval.months
intval.years = - interval.years
let secs = toSeconds(a, intval)
result = getLocalTime(fromSeconds(t + secs))
proc miliseconds*(t: TimeInterval): int {.deprecated.} = t.milliseconds
proc `miliseconds=`*(t: var TimeInterval, milliseconds: int) {.deprecated.} =
## An alias for a misspelled field in ``TimeInterval``.
##
## **Warning:** This should not be used! It will be removed in the next
## version.
t.milliseconds = milliseconds
when not defined(JS):
proc epochTime*(): float {.rtl, extern: "nt$1", tags: [TimeEffect].}
@ -603,6 +664,69 @@ proc `$`*(m: Month): string =
"November", "December"]
return lookup[m]
proc milliseconds*(ms: int): TimeInterval {.inline.} =
## TimeInterval of `ms` milliseconds
##
## Note: not all time functions have millisecond resolution
initInterval(`mod`(ms,1000), `div`(ms,1000))
proc seconds*(s: int): TimeInterval {.inline.} =
## TimeInterval of `s` seconds
##
## ``echo getTime() + 5.second``
initInterval(0,`mod`(s,60), `div`(s,60))
proc minutes*(m: int): TimeInterval {.inline.} =
## TimeInterval of `m` minutes
##
## ``echo getTime() + 5.minutes``
initInterval(0,0,`mod`(m,60), `div`(m,60))
proc hours*(h: int): TimeInterval {.inline.} =
## TimeInterval of `h` hours
##
## ``echo getTime() + 2.hours``
initInterval(0,0,0,`mod`(h,24),`div`(h,24))
proc days*(d: int): TimeInterval {.inline.} =
## TimeInterval of `d` days
##
## ``echo getTime() + 2.days``
initInterval(0,0,0,0,d)
proc months*(m: int): TimeInterval {.inline.} =
## TimeInterval of `m` months
##
## ``echo getTime() + 2.months``
initInterval(0,0,0,0,0,`mod`(m,12),`div`(m,12))
proc years*(y: int): TimeInterval {.inline.} =
## TimeInterval of `y` years
##
## ``echo getTime() + 2.years``
initInterval(0,0,0,0,0,0,y)
proc `+=`*(t: var Time, ti: TimeInterval) =
## modifies `t` by adding the interval `ti`
t = timeInfoToTime(getLocalTime(t) + ti)
proc `+`*(t: Time, ti: TimeInterval): Time =
## adds the interval `ti` to Time `t`
## by converting to localTime, adding the interval, and converting back
##
## ``echo getTime() + 1.day``
result = timeInfoToTime(getLocalTime(t) + ti)
proc `-=`*(t: var Time, ti: TimeInterval) =
## modifies `t` by subtracting the interval `ti`
t = timeInfoToTime(getLocalTime(t) - ti)
proc `-`*(t: Time, ti: TimeInterval): Time =
## adds the interval `ti` to Time `t`
##
## ``echo getTime() - 1.day``
result = timeInfoToTime(getLocalTime(t) - ti)
proc formatToken(info: TimeInfo, token: string, buf: var string) =
## Helper of the format proc to parse individual tokens.
##
@ -1192,112 +1316,10 @@ proc timeToTimeInterval*(t: Time): TimeInterval =
# Milliseconds not available from Time
when isMainModule:
# $ date --date='@2147483647'
# Tue 19 Jan 03:14:07 GMT 2038
var t = getGMTime(fromSeconds(2147483647))
assert t.format("ddd dd MMM hh:mm:ss ZZZ yyyy") == "Tue 19 Jan 03:14:07 UTC 2038"
assert t.format("ddd ddMMMhh:mm:ssZZZyyyy") == "Tue 19Jan03:14:07UTC2038"
assert t.format("d dd ddd dddd h hh H HH m mm M MM MMM MMMM s" &
" ss t tt y yy yyy yyyy yyyyy z zz zzz ZZZ") ==
"19 19 Tue Tuesday 3 03 3 03 14 14 1 01 Jan January 7 07 A AM 8 38 038 2038 02038 0 00 00:00 UTC"
assert t.format("yyyyMMddhhmmss") == "20380119031407"
var t2 = getGMTime(fromSeconds(160070789)) # Mon 27 Jan 16:06:29 GMT 1975
assert t2.format("d dd ddd dddd h hh H HH m mm M MM MMM MMMM s" &
" ss t tt y yy yyy yyyy yyyyy z zz zzz ZZZ") ==
"27 27 Mon Monday 4 04 16 16 6 06 1 01 Jan January 29 29 P PM 5 75 975 1975 01975 0 00 00:00 UTC"
when not defined(JS):
when sizeof(Time) == 8:
var t3 = getGMTime(fromSeconds(889067643645)) # Fri 7 Jun 19:20:45 BST 30143
assert t3.format("d dd ddd dddd h hh H HH m mm M MM MMM MMMM s" &
" ss t tt y yy yyy yyyy yyyyy z zz zzz ZZZ") ==
"7 07 Fri Friday 6 06 18 18 20 20 6 06 Jun June 45 45 P PM 3 43 143 0143 30143 0 00 00:00 UTC"
assert t3.format(":,[]()-/") == ":,[]()-/"
var t4 = getGMTime(fromSeconds(876124714)) # Mon 6 Oct 08:58:34 BST 1997
assert t4.format("M MM MMM MMMM") == "10 10 Oct October"
# Interval tests
assert((t4 - initInterval(years = 2)).format("yyyy") == "1995")
assert((t4 - initInterval(years = 7, minutes = 34, seconds = 24)).format("yyyy mm ss") == "1990 24 10")
var s = "Tuesday at 09:04am on Dec 15, 2015"
var f = "dddd at hh:mmtt on MMM d, yyyy"
assert($s.parse(f) == "Tue Dec 15 09:04:00 2015")
# ANSIC = "Mon Jan _2 15:04:05 2006"
s = "Thu Jan 12 15:04:05 2006"
f = "ddd MMM dd HH:mm:ss yyyy"
assert($s.parse(f) == "Thu Jan 12 15:04:05 2006")
# UnixDate = "Mon Jan _2 15:04:05 MST 2006"
s = "Thu Jan 12 15:04:05 MST 2006"
f = "ddd MMM dd HH:mm:ss ZZZ yyyy"
assert($s.parse(f) == "Thu Jan 12 15:04:05 2006")
# RubyDate = "Mon Jan 02 15:04:05 -0700 2006"
s = "Thu Jan 12 15:04:05 -07:00 2006"
f = "ddd MMM dd HH:mm:ss zzz yyyy"
assert($s.parse(f) == "Thu Jan 12 15:04:05 2006")
# RFC822 = "02 Jan 06 15:04 MST"
s = "12 Jan 16 15:04 MST"
f = "dd MMM yy HH:mm ZZZ"
assert($s.parse(f) == "Tue Jan 12 15:04:00 2016")
# RFC822Z = "02 Jan 06 15:04 -0700" # RFC822 with numeric zone
s = "12 Jan 16 15:04 -07:00"
f = "dd MMM yy HH:mm zzz"
assert($s.parse(f) == "Tue Jan 12 15:04:00 2016")
# RFC850 = "Monday, 02-Jan-06 15:04:05 MST"
s = "Monday, 12-Jan-06 15:04:05 MST"
f = "dddd, dd-MMM-yy HH:mm:ss ZZZ"
assert($s.parse(f) == "Thu Jan 12 15:04:05 2006")
# RFC1123 = "Mon, 02 Jan 2006 15:04:05 MST"
s = "Thu, 12 Jan 2006 15:04:05 MST"
f = "ddd, dd MMM yyyy HH:mm:ss ZZZ"
assert($s.parse(f) == "Thu Jan 12 15:04:05 2006")
# RFC1123Z = "Mon, 02 Jan 2006 15:04:05 -0700" # RFC1123 with numeric zone
s = "Thu, 12 Jan 2006 15:04:05 -07:00"
f = "ddd, dd MMM yyyy HH:mm:ss zzz"
assert($s.parse(f) == "Thu Jan 12 15:04:05 2006")
# RFC3339 = "2006-01-02T15:04:05Z07:00"
s = "2006-01-12T15:04:05Z-07:00"
f = "yyyy-MM-ddTHH:mm:ssZzzz"
assert($s.parse(f) == "Thu Jan 12 15:04:05 2006")
f = "yyyy-MM-dd'T'HH:mm:ss'Z'zzz"
assert($s.parse(f) == "Thu Jan 12 15:04:05 2006")
# RFC3339Nano = "2006-01-02T15:04:05.999999999Z07:00"
s = "2006-01-12T15:04:05.999999999Z-07:00"
f = "yyyy-MM-ddTHH:mm:ss.999999999Zzzz"
assert($s.parse(f) == "Thu Jan 12 15:04:05 2006")
# Kitchen = "3:04PM"
s = "3:04PM"
f = "h:mmtt"
assert "15:04:00" in $s.parse(f)
when not defined(testing):
echo "Kitchen: " & $s.parse(f)
var ti = timeToTimeInfo(getTime())
echo "Todays date after decoding: ", ti
var tint = timeToTimeInterval(getTime())
echo "Todays date after decoding to interval: ", tint
# checking dayOfWeek matches known days
assert getDayOfWeek(21, 9, 1900) == dFri
assert getDayOfWeek(1, 1, 1970) == dThu
assert getDayOfWeek(21, 9, 1970) == dMon
assert getDayOfWeek(1, 1, 2000) == dSat
assert getDayOfWeek(1, 1, 2021) == dFri
# Julian tests
assert getDayOfWeekJulian(21, 9, 1900) == dFri
assert getDayOfWeekJulian(21, 9, 1970) == dMon
assert getDayOfWeekJulian(1, 1, 2000) == dSat
assert getDayOfWeekJulian(1, 1, 2021) == dFri
# toSeconds tests with GM and Local timezones
#var t4 = getGMTime(fromSeconds(876124714)) # Mon 6 Oct 08:58:34 BST 1997
var t4L = getLocalTime(fromSeconds(876124714))
assert toSeconds(timeInfoToTime(t4L)) == 876124714 # fromSeconds is effectively "localTime"
assert toSeconds(timeInfoToTime(t4L)) + t4L.timezone.float == toSeconds(timeInfoToTime(t4))
# this is testing non-exported function
var
t4 = getGMTime(fromSeconds(876124714)) # Mon 6 Oct 08:58:34 BST 1997
t4L = getLocalTime(fromSeconds(876124714))
assert toSeconds(t4, initInterval(seconds=0)) == 0.0
assert toSeconds(t4L, initInterval(milliseconds=1)) == toSeconds(t4, initInterval(milliseconds=1))
assert toSeconds(t4L, initInterval(seconds=1)) == toSeconds(t4, initInterval(seconds=1))
@ -1307,12 +1329,5 @@ when isMainModule:
assert toSeconds(t4L, initInterval(months=1)) == toSeconds(t4, initInterval(months=1))
assert toSeconds(t4L, initInterval(years=1)) == toSeconds(t4, initInterval(years=1))
# adding intervals
var
a1L = toSeconds(timeInfoToTime(t4L + initInterval(hours = 1))) + t4L.timezone.float
a1G = toSeconds(timeInfoToTime(t4)) + 60.0 * 60.0
assert a1L == a1G
# subtracting intervals
a1L = toSeconds(timeInfoToTime(t4L - initInterval(hours = 1))) + t4L.timezone.float
a1G = toSeconds(timeInfoToTime(t4)) - (60.0 * 60.0)
assert a1L == a1G
# Further tests are in tests/stdlib/ttime.nim
# koch test c stdlib

View file

@ -114,6 +114,7 @@ proc validateUtf8*(s: string): int =
if ord(s[i]) <=% 127:
inc(i)
elif ord(s[i]) shr 5 == 0b110:
if ord(s[i]) < 0xc2: return i # Catch overlong ascii representations.
if i+1 < L and ord(s[i+1]) shr 6 == 0b10: inc(i, 2)
else: return i
elif ord(s[i]) shr 4 == 0b1110:

View file

@ -96,7 +96,7 @@ proc parse(x: var XmlParser, errors: var seq[string]): XmlNode =
next(x)
of xmlEntity:
## &entity;
errors.add(errorMsg(x, "unknown entity: " & x.entityName))
result = newEntity(x.entityName)
next(x)
of xmlEof: discard
@ -143,17 +143,24 @@ proc loadXml*(path: string): XmlNode =
result = loadXml(path, errors)
if errors.len > 0: raiseInvalidXml(errors)
when not defined(testing) and isMainModule:
import os
when isMainModule:
when not defined(testing):
import os
var errors: seq[string] = @[]
var x = loadXml(paramStr(1), errors)
for e in items(errors): echo e
var errors: seq[string] = @[]
var x = loadXml(paramStr(1), errors)
for e in items(errors): echo e
var f: File
if open(f, "xmltest.txt", fmWrite):
f.write($x)
f.close()
var f: File
if open(f, "xmltest.txt", fmWrite):
f.write($x)
f.close()
else:
quit("cannot write test.txt")
else:
quit("cannot write test.txt")
block: # correctly parse ../../tests/testdata/doc1.xml
let filePath = "tests/testdata/doc1.xml"
var errors: seq[string] = @[]
var xml = loadXml(filePath, errors)
assert(errors.len == 0, "The file tests/testdata/doc1.xml should be parsed without errors.")

View file

@ -1,6 +1,6 @@
[Package]
name = "stdlib"
version = "0.9.0"
version = "0.13.0"
author = "Dominik Picheta"
description = "Nim's standard library."
license = "MIT"

View file

@ -232,8 +232,8 @@ proc low*[T](x: T): T {.magic: "Low", noSideEffect.}
##
## .. code-block:: nim
## var arr = [1,2,3,4,5,6,7]
## high(arr) #=> 0
## high(2) #=> -9223372036854775808
## low(arr) #=> 0
## low(2) #=> -9223372036854775808
type
range*{.magic: "Range".}[T] ## Generic type to construct range types.
@ -840,7 +840,7 @@ proc `div` *(x, y: int32): int32 {.magic: "DivI", noSideEffect.}
## 1 div 2 == 0
## 2 div 2 == 1
## 3 div 2 == 1
## 7 div 5 == 2
## 7 div 5 == 1
when defined(nimnomagic64):
proc `div` *(x, y: int64): int64 {.magic: "DivI", noSideEffect.}
@ -1808,10 +1808,10 @@ const
NimMajor*: int = 0
## is the major number of Nim's version.
NimMinor*: int = 12
NimMinor*: int = 13
## is the minor number of Nim's version.
NimPatch*: int = 1
NimPatch*: int = 0
## is the patch number of Nim's version.
NimVersion*: string = $NimMajor & "." & $NimMinor & "." & $NimPatch
@ -2584,11 +2584,7 @@ when not defined(JS): #and not defined(nimscript):
when hasAlloc:
var
strDesc: TNimType
strDesc.size = sizeof(string)
strDesc.kind = tyString
strDesc.flags = {ntfAcyclic}
strDesc = TNimType(size: sizeof(string), kind: tyString, flags: {ntfAcyclic})
when not defined(nimscript):
include "system/ansi_c"

View file

@ -40,7 +40,7 @@ when defined(emscripten):
MAP_PRIVATE = 2'i32 # Changes are private
var MAP_ANONYMOUS {.importc: "MAP_ANONYMOUS", header: "<sys/mman.h>".}: cint
type
type
PEmscriptenMMapBlock = ptr EmscriptenMMapBlock
EmscriptenMMapBlock {.pure, inheritable.} = object
realSize: int # size of previous chunk; for coalescing
@ -399,6 +399,9 @@ iterator allObjects(m: MemRegion): pointer {.inline.} =
let c = cast[PBigChunk](c)
yield addr(c.data)
proc iterToProc*(iter: typed, envType: typedesc; procName: untyped) {.
magic: "Plugin", compileTime.}
proc isCell(p: pointer): bool {.inline.} =
result = cast[ptr FreeCell](p).zeroField >% 1

View file

@ -201,6 +201,41 @@ iterator elements(t: CellSet): PCell {.inline.} =
inc(i)
r = r.next
when false:
type
CellSetIter = object
p: PPageDesc
i, w, j: int
proc next(it: var CellSetIter): PCell =
while true:
while it.w != 0: # test all remaining bits for zero
if (it.w and 1) != 0: # the bit is set!
result = cast[PCell]((it.p.key shl PageShift) or
(it.i shl IntShift +% it.j) *% MemAlign)
inc(it.j)
it.w = it.w shr 1
return
else:
inc(it.j)
it.w = it.w shr 1
# load next w:
if it.i >= high(it.p.bits):
it.i = 0
it.j = 0
it.p = it.p.next
if it.p == nil: return nil
else:
inc it.i
it.w = it.p.bits[i]
proc init(it: var CellSetIter; t: CellSet): PCell =
it.p = t.head
it.i = -1
it.w = 0
result = it.next
iterator elementsExcept(t, s: CellSet): PCell {.inline.} =
var r = t.head
while r != nil:

View file

@ -68,7 +68,10 @@ when defined(posix):
proc nimLoadLibrary(path: string): LibHandle =
result = dlopen(path, RTLD_NOW)
#c_fprintf(c_stdout, "%s\n", dlerror())
when defined(nimDebugDlOpen):
let error = dlerror()
if error != nil:
c_fprintf(c_stdout, "%s\n", error)
proc nimGetProcAddr(lib: LibHandle, name: cstring): ProcAddr =
result = dlsym(lib, name)
@ -105,7 +108,12 @@ elif defined(windows) or defined(dos):
proc nimGetProcAddr(lib: LibHandle, name: cstring): ProcAddr =
result = getProcAddress(cast[THINSTANCE](lib), name)
if result == nil: procAddrError(name)
if result != nil: return
for i in countup(0, 50):
var decorated = "_" & $name & "@" & $(i * 4)
result = getProcAddress(cast[THINSTANCE](lib), cstring(decorated))
if result != nil: return
procAddrError(name)
else:
{.error: "no implementation for dyncalls".}

View file

@ -558,7 +558,7 @@ proc growObj(old: pointer, newsize: int, gch: var GcHeap): pointer =
# we split the old refcount in 2 parts. XXX This is still not entirely
# correct if the pointer that receives growObj's result is on the stack.
# A better fix would be to emit the location specific write barrier for
# 'growObj', but this is lost of more work and who knows what new problems
# 'growObj', but this is lots of more work and who knows what new problems
# this would create.
res.refcount = rcIncrement
decRef(ol)

File diff suppressed because it is too large Load diff

View file

@ -33,6 +33,9 @@ type
lineNumber {.importc.}: int
message {.importc.}: cstring
stack {.importc.}: cstring
JSRef = ref RootObj # Fake type.
{.deprecated: [TSafePoint: SafePoint, TCallFrame: CallFrame].}
var
@ -282,61 +285,6 @@ proc eqStrings(a, b: string): bool {.asmNoStackFrame, compilerProc.} =
return true;
"""
type
Document {.importc.} = object of RootObj
write: proc (text: cstring) {.nimcall.}
writeln: proc (text: cstring) {.nimcall.}
createAttribute: proc (identifier: cstring): ref Node {.nimcall.}
createElement: proc (identifier: cstring): ref Node {.nimcall.}
createTextNode: proc (identifier: cstring): ref Node {.nimcall.}
getElementById: proc (id: cstring): ref Node {.nimcall.}
getElementsByName: proc (name: cstring): seq[ref Node] {.nimcall.}
getElementsByTagName: proc (name: cstring): seq[ref Node] {.nimcall.}
NodeType* = enum
ElementNode = 1,
AttributeNode,
TextNode,
CDATANode,
EntityRefNode,
EntityNode,
ProcessingInstructionNode,
CommentNode,
DocumentNode,
DocumentTypeNode,
DocumentFragmentNode,
NotationNode
Node* {.importc.} = object of RootObj
attributes*: seq[ref Node]
childNodes*: seq[ref Node]
data*: cstring
firstChild*: ref Node
lastChild*: ref Node
nextSibling*: ref Node
nodeName*: cstring
nodeType*: NodeType
nodeValue*: cstring
parentNode*: ref Node
previousSibling*: ref Node
appendChild*: proc (child: ref Node) {.nimcall.}
appendData*: proc (data: cstring) {.nimcall.}
cloneNode*: proc (copyContent: bool) {.nimcall.}
deleteData*: proc (start, len: int) {.nimcall.}
getAttribute*: proc (attr: cstring): cstring {.nimcall.}
getAttributeNode*: proc (attr: cstring): ref Node {.nimcall.}
getElementsByTagName*: proc (): seq[ref Node] {.nimcall.}
hasChildNodes*: proc (): bool {.nimcall.}
insertBefore*: proc (newNode, before: ref Node) {.nimcall.}
insertData*: proc (position: int, data: cstring) {.nimcall.}
removeAttribute*: proc (attr: cstring) {.nimcall.}
removeAttributeNode*: proc (attr: ref Node) {.nimcall.}
removeChild*: proc (child: ref Node) {.nimcall.}
replaceChild*: proc (newNode, oldNode: ref Node) {.nimcall.}
replaceData*: proc (start, len: int, text: cstring) {.nimcall.}
setAttribute*: proc (name, value: cstring) {.nimcall.}
setAttributeNode*: proc (attr: ref Node) {.nimcall.}
{.deprecated: [TNode: Node, TNodeType: NodeType, TDocument: Document].}
when defined(kwin):
proc rawEcho {.compilerproc, asmNoStackFrame.} =
asm """
@ -360,28 +308,28 @@ elif defined(nodejs):
"""
else:
var
document {.importc, nodecl.}: ref Document
proc ewriteln(x: cstring) =
var node = document.getElementsByTagName("body")[0]
if node != nil:
node.appendChild(document.createTextNode(x))
node.appendChild(document.createElement("br"))
else:
var node : JSRef
{.emit: "`node` = document.getElementsByTagName('body')[0];".}
if node.isNil:
raise newException(ValueError, "<body> element does not exist yet!")
{.emit: """
`node`.appendChild(document.createTextNode(`x`));
`node`.appendChild(document.createElement("br"));
""".}
proc rawEcho {.compilerproc.} =
var node = document.getElementsByTagName("body")[0]
if node == nil:
var node : JSRef
{.emit: "`node` = document.getElementsByTagName('body')[0];".}
if node.isNil:
raise newException(IOError, "<body> element does not exist yet!")
asm """
for (var i = 0; i < arguments.length; ++i) {
var x = `toJSStr`(arguments[i]);
`node`.appendChild(document.createTextNode(x))
}
"""
node.appendChild(document.createElement("br"))
{.emit: """
for (var i = 0; i < arguments.length; ++i) {
var x = `toJSStr`(arguments[i]);
`node`.appendChild(document.createTextNode(x));
}
`node`.appendChild(document.createElement("br"));
""".}
# Arithmetic:
proc addInt(a, b: int): int {.asmNoStackFrame, compilerproc.} =
@ -532,8 +480,6 @@ proc nimMax(a, b: int): int {.compilerproc.} = return if a >= b: a else: b
type NimString = string # hack for hti.nim
include "system/hti"
type JSRef = ref RootObj # Fake type.
proc isFatPointer(ti: PNimType): bool =
# This has to be consistent with the code generator!
return ti.base.kind notin {tyObject,
@ -587,15 +533,20 @@ proc nimCopy(dest, src: JSRef, ti: PNimType): JSRef =
nimCopyAux(result, src, ti.node)
of tySequence, tyArrayConstr, tyOpenArray, tyArray:
asm """
if (`dest` === null || `dest` === undefined) {
`dest` = new Array(`src`.length);
if (`src` === null) {
`result` = null;
}
else {
`dest`.length = `src`.length;
}
`result` = `dest`;
for (var i = 0; i < `src`.length; ++i) {
`result`[i] = nimCopy(`result`[i], `src`[i], `ti`.base);
if (`dest` === null || `dest` === undefined) {
`dest` = new Array(`src`.length);
}
else {
`dest`.length = `src`.length;
}
`result` = `dest`;
for (var i = 0; i < `src`.length; ++i) {
`result`[i] = nimCopy(`result`[i], `src`[i], `ti`.base);
}
}
"""
of tyString:

View file

@ -242,7 +242,7 @@ template task*(name: untyped; description: string; body: untyped): untyped =
## .. code-block:: nim
## task build, "default build is via the C backend":
## setCommand "c"
proc `name Task`() = body
proc `name Task`*() = body
let cmd = getCommand()
if cmd.len == 0 or cmd ==? "help":

View file

@ -50,10 +50,15 @@ proc captureStackTrace(f: PFrame, st: var StackTrace) =
inc(i)
b = b.prev
var
profilingRequestedHook*: proc (): bool {.nimcall, benign.}
## set this variable to provide a procedure that implements a profiler in
## user space. See the `nimprof` module for a reference implementation.
when defined(memProfiler):
type
MemProfilerHook* = proc (st: StackTrace, requestedSize: int) {.nimcall, benign.}
{.deprecated: [TMemProfilerHook: MemProfilerHook].}
var
profilerHook*: MemProfilerHook
## set this variable to provide a procedure that implements a profiler in
@ -65,17 +70,13 @@ when defined(memProfiler):
hook(st, requestedSize)
proc nimProfile(requestedSize: int) =
if not isNil(profilerHook):
if not isNil(profilingRequestedHook) and profilingRequestedHook():
callProfilerHook(profilerHook, requestedSize)
else:
const
SamplingInterval = 50_000
# set this to change the default sampling interval
var
profilerHook*: ProfilerHook
## set this variable to provide a procedure that implements a profiler in
## user space. See the `nimprof` module for a reference implementation.
gTicker {.threadvar.}: int
proc callProfilerHook(hook: ProfilerHook) {.noinline.} =
# 'noinline' so that 'nimProfile' does not perform the stack allocation
@ -86,16 +87,7 @@ else:
proc nimProfile() =
## This is invoked by the compiler in every loop and on every proc entry!
if gTicker == 0:
gTicker = -1
if not isNil(profilerHook):
# disable recursive calls: XXX should use try..finally,
# but that's too expensive!
let oldHook = profilerHook
profilerHook = nil
callProfilerHook(oldHook)
profilerHook = oldHook
gTicker = SamplingInterval
dec gTicker
if not isNil(profilingRequestedHook) and profilingRequestedHook():
callProfilerHook(profilerHook)
{.pop.}

View file

@ -259,8 +259,10 @@ when not defined(useNimRtl):
of tyInt16: add result, $int(cast[ptr int16](p)[])
of tyInt32: add result, $int(cast[ptr int32](p)[])
of tyInt64: add result, $(cast[ptr int64](p)[])
of tyUInt8: add result, $ze(cast[ptr int8](p)[])
of tyUInt16: add result, $ze(cast[ptr int16](p)[])
of tyUInt8: add result, $(cast[ptr uint8](p)[])
of tyUInt16: add result, $(cast[ptr uint16](p)[])
of tyUInt32: add result, $(cast[ptr uint32](p)[])
of tyUInt64: add result, $(cast[ptr uint64](p)[])
of tyFloat: add result, $(cast[ptr float](p)[])
of tyFloat32: add result, $(cast[ptr float32](p)[])

View file

@ -229,7 +229,7 @@ proc setLengthSeq(seq: PGenericSeq, elemSize, newLen: int): PGenericSeq {.
# we need to decref here, otherwise the GC leaks!
when not defined(boehmGC) and not defined(nogc) and
not defined(gcMarkAndSweep) and not defined(gogc):
when compileOption("gc", "v2"):
when false: # compileOption("gc", "v2"):
for i in newLen..result.len-1:
let len0 = gch.tempStack.len
forAllChildrenAux(cast[pointer](cast[ByteAddress](result) +%

View file

@ -139,7 +139,7 @@ struct linenoiseState {
int ofd; /* Terminal stdout file descriptor. */
char *buf; /* Edited line buffer. */
size_t buflen; /* Edited line buffer size. */
const char *prompt; /* Prompt to display. */
char *prompt; /* Prompt to display. */
size_t plen; /* Prompt length. */
size_t pos; /* Current cursor position. */
size_t oldpos; /* Previous refresh cursor position. */
@ -172,7 +172,7 @@ enum KEY_ACTION{
};
static void linenoiseAtExit(void);
int linenoiseHistoryAdd(const char *line);
int linenoiseHistoryAdd(char *line);
static void refreshLine(struct linenoiseState *l);
/* Debugging macro. */
@ -413,14 +413,14 @@ void linenoiseSetCompletionCallback(linenoiseCompletionCallback *fn) {
* in order to add completion options given the input string when the
* user typed <tab>. See the example.c source code for a very easy to
* understand example. */
void linenoiseAddCompletion(linenoiseCompletions *lc, const char *str) {
void linenoiseAddCompletion(linenoiseCompletions *lc, char *str) {
size_t len = strlen(str);
char *copy, **cvec;
copy = malloc(len+1);
copy = (char*)malloc(len+1);
if (copy == NULL) return;
memcpy(copy,str,len+1);
cvec = realloc(lc->cvec,sizeof(char*)*(lc->len+1));
cvec = (char**)realloc(lc->cvec,sizeof(char*)*(lc->len+1));
if (cvec == NULL) {
free(copy);
return;
@ -445,12 +445,12 @@ static void abInit(struct abuf *ab) {
ab->len = 0;
}
static void abAppend(struct abuf *ab, const char *s, int len) {
char *new = realloc(ab->b,ab->len+len);
static void abAppend(struct abuf *ab, char *s, int len) {
char *neww = (char*)realloc(ab->b,ab->len+len);
if (new == NULL) return;
memcpy(new+ab->len,s,len);
ab->b = new;
if (neww == NULL) return;
memcpy(neww+ab->len,s,len);
ab->b = neww;
ab->len += len;
}
@ -723,7 +723,7 @@ void linenoiseEditDeletePrevWord(struct linenoiseState *l) {
* when ctrl+d is typed.
*
* The function returns the length of the current buffer. */
static int linenoiseEdit(int stdin_fd, int stdout_fd, char *buf, size_t buflen, const char *prompt)
static int linenoiseEdit(int stdin_fd, int stdout_fd, char *buf, size_t buflen, char *prompt)
{
struct linenoiseState l;
@ -929,7 +929,7 @@ void linenoisePrintKeyCodes(void) {
/* This function calls the line editing function linenoiseEdit() using
* the STDIN file descriptor set in raw mode. */
static int linenoiseRaw(char *buf, size_t buflen, const char *prompt) {
static int linenoiseRaw(char *buf, size_t buflen, char *prompt) {
int count;
if (buflen == 0) {
@ -959,7 +959,7 @@ static int linenoiseRaw(char *buf, size_t buflen, const char *prompt) {
* for a blacklist of stupid terminals, and later either calls the line
* editing function or uses dummy fgets() so that you will be able to type
* something even in the most desperate of the conditions. */
char *linenoise(const char *prompt) {
char *linenoise(char *prompt) {
char buf[LINENOISE_MAX_LINE];
int count;
@ -1009,14 +1009,14 @@ static void linenoiseAtExit(void) {
* histories, but will work well for a few hundred of entries.
*
* Using a circular buffer is smarter, but a bit more complex to handle. */
int linenoiseHistoryAdd(const char *line) {
int linenoiseHistoryAdd(char *line) {
char *linecopy;
if (history_max_len == 0) return 0;
/* Initialization on first call. */
if (history == NULL) {
history = malloc(sizeof(char*)*history_max_len);
history = (char**)malloc(sizeof(char*)*history_max_len);
if (history == NULL) return 0;
memset(history,0,(sizeof(char*)*history_max_len));
}
@ -1043,14 +1043,14 @@ int linenoiseHistoryAdd(const char *line) {
* just the latest 'len' elements if the new history length value is smaller
* than the amount of items already inside the history. */
int linenoiseHistorySetMaxLen(int len) {
char **new;
char **neww;
if (len < 1) return 0;
if (history) {
int tocopy = history_len;
new = malloc(sizeof(char*)*len);
if (new == NULL) return 0;
neww = (char**)malloc(sizeof(char*)*len);
if (neww == NULL) return 0;
/* If we can't copy everything, free the elements we'll not use. */
if (len < tocopy) {
@ -1059,10 +1059,10 @@ int linenoiseHistorySetMaxLen(int len) {
for (j = 0; j < tocopy-len; j++) free(history[j]);
tocopy = len;
}
memset(new,0,sizeof(char*)*len);
memcpy(new,history+(history_len-tocopy), sizeof(char*)*tocopy);
memset(neww,0,sizeof(char*)*len);
memcpy(neww,history+(history_len-tocopy), sizeof(char*)*tocopy);
free(history);
history = new;
history = neww;
}
history_max_len = len;
if (history_len > history_max_len)
@ -1072,7 +1072,7 @@ int linenoiseHistorySetMaxLen(int len) {
/* Save the history in the specified file. On success 0 is returned
* otherwise -1 is returned. */
int linenoiseHistorySave(const char *filename) {
int linenoiseHistorySave(char *filename) {
FILE *fp = fopen(filename,"w");
int j;
@ -1088,7 +1088,7 @@ int linenoiseHistorySave(const char *filename) {
*
* If the file exists and the operation succeeded 0 is returned, otherwise
* on error -1 is returned. */
int linenoiseHistoryLoad(const char *filename) {
int linenoiseHistoryLoad(char *filename) {
FILE *fp = fopen(filename,"r");
char buf[LINENOISE_MAX_LINE];

View file

@ -39,30 +39,22 @@
#ifndef __LINENOISE_H
#define __LINENOISE_H
#ifdef __cplusplus
extern "C" {
#endif
typedef struct linenoiseCompletions {
size_t len;
char **cvec;
} linenoiseCompletions;
typedef void(linenoiseCompletionCallback)(const char *, linenoiseCompletions *);
typedef void(linenoiseCompletionCallback)(char *, linenoiseCompletions *);
void linenoiseSetCompletionCallback(linenoiseCompletionCallback *);
void linenoiseAddCompletion(linenoiseCompletions *, const char *);
void linenoiseAddCompletion(linenoiseCompletions *, char *);
char *linenoise(const char *prompt);
int linenoiseHistoryAdd(const char *line);
char *linenoise(char *prompt);
int linenoiseHistoryAdd(char *line);
int linenoiseHistorySetMaxLen(int len);
int linenoiseHistorySave(const char *filename);
int linenoiseHistoryLoad(const char *filename);
int linenoiseHistorySave(char *filename);
int linenoiseHistoryLoad(char *filename);
void linenoiseClearScreen(void);
void linenoiseSetMultiLine(int ml);
void linenoisePrintKeyCodes(void);
#ifdef __cplusplus
}
#endif
#endif /* __LINENOISE_H */

View file

@ -418,6 +418,7 @@ type
decimals*: cuint # Number of decimals in field
charsetnr*: cuint # Character set
ftype*: Enum_field_types # Type of field. See mysql_com.h for types
extension*: pointer
FIELD* = St_mysql_field
PFIELD* = ptr FIELD

View file

@ -641,11 +641,42 @@ const
ODBC_CONFIG_SYS_DSN* = 5
ODBC_REMOVE_SYS_DSN* = 6
SQL_ACTIVE_CONNECTIONS* = 0 # SQLGetInfo
SQL_DATA_SOURCE_NAME* = 2
SQL_DATA_SOURCE_READ_ONLY* = 25
SQL_DATABASE_NAME* = 2
SQL_DBMS_NAME* = 17
SQL_DBMS_VERSION* = 18
SQL_DRIVER_HDBC* = 3
SQL_DRIVER_HENV* = 4
SQL_DRIVER_HSTMT* = 5
SQL_DRIVER_NAME* = 6
SQL_DRIVER_VER* = 7
SQL_FETCH_DIRECTION* = 8
SQL_ODBC_VER* = 10
SQL_DRIVER_ODBC_VER* = 77
SQL_SERVER_NAME* = 13
SQL_ACTIVE_ENVIRONMENTS* = 116
SQL_ACTIVE_STATEMENTS* = 1
SQL_SQL_CONFORMANCE* = 118
SQL_DATETIME_LITERALS* = 119
SQL_ASYNC_MODE* = 10021
SQL_BATCH_ROW_COUNT* = 120
SQL_BATCH_SUPPORT* = 121
SQL_CATALOG_LOCATION* = 114
#SQL_CATALOG_NAME* = 10003
SQL_CATALOG_NAME_SEPARATOR* = 41
SQL_CATALOG_TERM* = 42
SQL_CATALOG_USAGE* = 92
#SQL_COLLATION_SEQ* = 10004
SQL_COLUMN_ALIAS* = 87
#SQL_USER_NAME* = 47
proc SQLAllocHandle*(HandleType: TSqlSmallInt, InputHandle: SqlHandle,
OutputHandlePtr: var SqlHandle): TSqlSmallInt{.
dynlib: odbclib, importc.}
proc SQLSetEnvAttr*(EnvironmentHandle: SqlHEnv, Attribute: TSqlInteger,
Value: SqlPointer, StringLength: TSqlInteger): TSqlSmallInt{.
Value: TSqlInteger, StringLength: TSqlInteger): TSqlSmallInt{.
dynlib: odbclib, importc.}
proc SQLGetEnvAttr*(EnvironmentHandle: SqlHEnv, Attribute: TSqlInteger,
Value: SqlPointer, BufferLength: TSqlInteger,
@ -807,5 +838,10 @@ proc SQLStatistics*(hstmt: SqlHStmt, CatalogName: PSQLCHAR,
NameLength3: TSqlSmallInt, Unique: SqlUSmallInt,
Reserved: SqlUSmallInt): TSqlSmallInt {.
dynlib: odbclib, importc.}
proc SQLErr*(henv: SqlHEnv, hdbc: SqlHDBC, hstmt: SqlHStmt,
szSqlState, pfNativeError, szErrorMsg: PSQLCHAR,
cbErrorMsgMax: TSqlSmallInt,
pcbErrorMsg: PSQLINTEGER): TSqlSmallInt {.
dynlib: odbclib, importc: "SQLError".}
{.pop.}

View file

@ -239,6 +239,8 @@ proc column_count*(pStmt: Pstmt): int32{.cdecl, dynlib: Lib,
importc: "sqlite3_column_count".}
proc column_name*(para1: Pstmt, para2: int32): cstring{.cdecl, dynlib: Lib,
importc: "sqlite3_column_name".}
proc column_table_name*(para1: Pstmt; para2: int32): cstring{.cdecl, dynlib: Lib,
importc: "sqlite3_column_table_name".}
proc column_name16*(para1: Pstmt, para2: int32): pointer{.cdecl, dynlib: Lib,
importc: "sqlite3_column_name16".}
proc column_decltype*(para1: Pstmt, i: int32): cstring{.cdecl, dynlib: Lib,