summary refs log tree commit diff stats
path: root/lib/impure
diff options
context:
space:
mode:
Diffstat (limited to 'lib/impure')
-rw-r--r--lib/impure/db_mysql.nim24
-rw-r--r--lib/impure/db_postgres.nim125
-rw-r--r--lib/impure/fenv.nim113
-rw-r--r--lib/impure/re.nim2
-rw-r--r--lib/impure/zipfiles.nim2
5 files changed, 229 insertions, 37 deletions
diff --git a/lib/impure/db_mysql.nim b/lib/impure/db_mysql.nim
index eec4daf00..ce48a32ed 100644
--- a/lib/impure/db_mysql.nim
+++ b/lib/impure/db_mysql.nim
@@ -57,7 +57,8 @@ when false:
       binding: seq[MYSQL_BIND]
     discard mysql_stmt_close(stmt)
 
-proc dbQuote(s: string): string =
+proc dbQuote*(s: string): string =
+  ## DB quotes the string.
   result = "'"
   for c in items(s):
     if c == '\'': add(result, "''")
@@ -69,7 +70,10 @@ proc dbFormat(formatstr: TSqlQuery, args: varargs[string]): string =
   var a = 0
   for c in items(string(formatstr)):
     if c == '?':
-      add(result, dbQuote(args[a]))
+      if args[a] == nil:
+        add(result, "NULL")
+      else:
+        add(result, dbQuote(args[a]))
       inc(a)
     else: 
       add(result, c)
@@ -115,7 +119,10 @@ iterator fastRows*(db: TDbConn, query: TSqlQuery,
       if row == nil: break
       for i in 0..L-1: 
         setLen(result[i], 0)
-        add(result[i], row[i])
+        if row[i] == nil:
+          result[i] = nil
+        else:
+          add(result[i], row[i])
       yield result
     properFreeResult(sqlres, row)
 
@@ -132,7 +139,10 @@ proc getRow*(db: TDbConn, query: TSqlQuery,
     if row != nil: 
       for i in 0..L-1: 
         setLen(result[i], 0)
-        add(result[i], row[i])
+        if row[i] == nil:
+          result[i] = nil
+        else:
+          add(result[i], row[i])
     properFreeResult(sqlres, row)
 
 proc getAllRows*(db: TDbConn, query: TSqlQuery, 
@@ -150,7 +160,11 @@ proc getAllRows*(db: TDbConn, query: TSqlQuery,
       if row == nil: break
       setLen(result, j+1)
       newSeq(result[j], L)
-      for i in 0..L-1: result[j][i] = $row[i]
+      for i in 0..L-1:
+        if row[i] == nil:
+          result[j][i] = nil
+        else:
+          result[j][i] = $row[i]
       inc(j)
     mysql.FreeResult(sqlres)
 
diff --git a/lib/impure/db_postgres.nim b/lib/impure/db_postgres.nim
index f6ae93303..510cb8e45 100644
--- a/lib/impure/db_postgres.nim
+++ b/lib/impure/db_postgres.nim
@@ -19,12 +19,13 @@ type
   EDb* = object of EIO ## exception that is raised if a database error occurs
   
   TSqlQuery* = distinct string ## an SQL query string
+  TSqlPrepared* = distinct string ## a identifier for the prepared queries
 
   FDb* = object of FIO ## 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
-  
-proc sql*(query: string): TSqlQuery {.noSideEffect, inline.} =  
+
+proc sql*(query: string): TSqlQuery {.noSideEffect, inline.} =
   ## constructs a TSqlQuery from the string `query`. This is supposed to be 
   ## used as a raw-string-literal modifier:
   ## ``sql"update user set counter = counter + 1"``
@@ -33,21 +34,22 @@ proc sql*(query: string): TSqlQuery {.noSideEffect, inline.} =
   ## on, later versions will check the string for valid syntax.
   result = TSqlQuery(query)
  
-proc dbError(db: TDbConn) {.noreturn.} = 
+proc dbError*(db: TDbConn) {.noreturn.} =
   ## raises an EDb exception.
   var e: ref EDb
   new(e)
   e.msg = $PQerrorMessage(db)
   raise e
 
-proc dbError*(msg: string) {.noreturn.} = 
+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.
   result = "'"
   for c in items(s):
     if c == '\'': add(result, "''")
@@ -59,43 +61,78 @@ proc dbFormat(formatstr: TSqlQuery, args: varargs[string]): string =
   var a = 0
   for c in items(string(formatstr)):
     if c == '?':
-      add(result, dbQuote(args[a]))
+      if args[a] == nil:
+        add(result, "NULL")
+      else:
+        add(result, dbQuote(args[a]))
       inc(a)
-    else: 
+    else:
       add(result, c)
   
-proc tryExec*(db: TDbConn, query: TSqlQuery, 
+proc tryExec*(db: TDbConn, query: TSqlQuery,
               args: varargs[string, `$`]): bool {.tags: [FReadDB, FWriteDb].} =
   ## tries to execute the query and returns true if successful, false otherwise.
-  var q = dbFormat(query, args)
-  var res = PQExec(db, q)
+  var arr = allocCStringArray(args)
+  var res = PQexecParams(db, query.string, int32(args.len), nil, arr,
+                        nil, nil, 0)
+  deallocCStringArray(arr)
   result = PQresultStatus(res) == PGRES_COMMAND_OK
   PQclear(res)
 
 proc exec*(db: TDbConn, query: TSqlQuery, args: varargs[string, `$`]) {.
   tags: [FReadDB, FWriteDb].} =
   ## executes the query and raises EDB if not successful.
-  var q = dbFormat(query, args)
-  var res = PQExec(db, q)
+  var arr = allocCStringArray(args)
+  var res = PQexecParams(db, query.string, int32(args.len), nil, arr,
+                        nil, nil, 0)
+  deallocCStringArray(arr)
   if PQresultStatus(res) != PGRES_COMMAND_OK: dbError(db)
   PQclear(res)
-  
+
+proc exec*(db: TDbConn, stmtName: TSqlPrepared,
+          args: varargs[string]) {.tags: [FReadDB, FWriteDb].} =
+  var arr = allocCStringArray(args)
+  var res = PQexecPrepared(db, stmtName.string, int32(args.len), arr,
+                           nil, nil, 0)
+  deallocCStringArray(arr)
+  if PQResultStatus(res) != PGRES_COMMAND_OK: dbError(db)
+  PQclear(res)
+
 proc newRow(L: int): TRow =
   newSeq(result, L)
   for i in 0..L-1: result[i] = ""
   
-proc setupQuery(db: TDbConn, query: TSqlQuery, 
-                args: varargs[string]): PPGresult = 
-  var q = dbFormat(query, args)
-  result = PQExec(db, q)
-  if PQresultStatus(result) != PGRES_TUPLES_OK: dbError(db)
-  
+proc setupQuery(db: TDbConn, query: TSqlQuery,
+                args: varargs[string]): PPGresult =
+  var arr = allocCStringArray(args)
+  result = PQexecParams(db, query.string, int32(args.len), nil, arr,
+                        nil, nil, 0)
+  deallocCStringArray(arr)
+  if PQResultStatus(result) != PGRES_TUPLES_OK: dbError(db)
+
+proc setupQuery(db: TDbConn, stmtName: TSqlPrepared,
+                 args: varargs[string]): PPGresult =
+  var arr = allocCStringArray(args)
+  result = PQexecPrepared(db, stmtName.string, int32(args.len), arr,
+                          nil, nil, 0)
+  deallocCStringArray(arr)
+  if PQResultStatus(result) != PGRES_TUPLES_OK: dbError(db)
+
+proc prepare*(db: TDbConn; stmtName: string, query: TSqlQuery;
+              nParams: int): TSqlPrepared =
+  var res = PQprepare(db, stmtName, query.string, int32(nParams), nil)
+  if PQResultStatus(res) != PGRES_COMMAND_OK: dbError(db)
+  return TSqlPrepared(stmtName)
+   
 proc setRow(res: PPGresult, r: var TRow, line, cols: int32) =
   for col in 0..cols-1:
     setLen(r[col], 0)
     var x = PQgetvalue(res, line, col)
-    add(r[col], x)
-  
+    if x == nil:
+      r[col] = nil
+    else:
+      add(r[col], x)
+
 iterator fastRows*(db: TDbConn, query: TSqlQuery,
                    args: varargs[string, `$`]): TRow {.tags: [FReadDB].} =
   ## executes the query and iterates over the result dataset. This is very 
@@ -109,6 +146,17 @@ iterator fastRows*(db: TDbConn, query: TSqlQuery,
     yield result
   PQclear(res)
 
+iterator fastRows*(db: TDbConn, stmtName: TSqlPrepared,
+                   args: varargs[string, `$`]): TRow {.tags: [FReadDB].} =
+  ## executes the prepared query and iterates over the result dataset.
+  var res = setupQuery(db, stmtName, args)
+  var L = PQnfields(res)
+  var result = newRow(L)
+  for i in 0..PQntuples(res)-1:
+    setRow(res, result, i, L)
+    yield result
+  PQclear(res)
+
 proc getRow*(db: TDbConn, query: TSqlQuery,
              args: varargs[string, `$`]): TRow {.tags: [FReadDB].} =
   ## retrieves a single row. If the query doesn't return any rows, this proc
@@ -119,40 +167,55 @@ proc getRow*(db: TDbConn, query: TSqlQuery,
   setRow(res, result, 0, L)
   PQclear(res)
 
-proc getAllRows*(db: TDbConn, query: TSqlQuery, 
+proc getRow*(db: TDbConn, stmtName: TSqlPrepared,
+              args: varargs[string, `$`]): TRow {.tags: [FReadDB].} =
+    var res = setupQuery(db, stmtName, args)
+    var L = PQnfields(res)
+    result = newRow(L)
+    setRow(res, result, 0, L)
+    PQclear(res)
+
+proc getAllRows*(db: TDbConn, query: TSqlQuery,
                  args: varargs[string, `$`]): seq[TRow] {.tags: [FReadDB].} =
   ## executes the query and returns the whole result dataset.
   result = @[]
   for r in FastRows(db, query, args):
     result.add(r)
 
-iterator rows*(db: TDbConn, query: TSqlQuery, 
+proc getAllRows*(db: TDbConn, stmtName: TSqlPrepared,
+                 args: varargs[string, `$`]): seq[TRow] {.tags: [FReadDB].} =
+  ## executes the prepared query and returns the whole result dataset.
+  result = @[]
+  for r in FastRows(db, stmtName, args):
+    result.add(r)
+
+iterator rows*(db: TDbConn, query: TSqlQuery,
                args: varargs[string, `$`]): TRow {.tags: [FReadDB].} =
   ## same as `FastRows`, but slower and safe.
   for r in items(GetAllRows(db, query, args)): yield r
 
-proc getValue*(db: TDbConn, query: TSqlQuery, 
-               args: varargs[string, `$`]): string {.tags: [FReadDB].} = 
+proc getValue*(db: TDbConn, query: TSqlQuery,
+               args: varargs[string, `$`]): string {.tags: [FReadDB].} =
   ## 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 tryInsertID*(db: TDbConn, query: TSqlQuery, 
+proc tryInsertID*(db: TDbConn, query: TSqlQuery,
                   args: varargs[string, `$`]): int64  {.tags: [FWriteDb].}=
   ## 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
   ## named ``id``. 
-  var x = PQgetvalue(setupQuery(db, TSqlQuery(string(query) & " RETURNING id"), 
+  var x = PQgetvalue(setupQuery(db, TSqlQuery(string(query) & " RETURNING id"),
     args), 0, 0)
   if not isNil(x):
     result = ParseBiggestInt($x)
   else:
     result = -1
 
-proc insertID*(db: TDbConn, query: TSqlQuery, 
+proc insertID*(db: TDbConn, query: TSqlQuery,
                args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} =
   ## executes the query (typically "INSERT") and returns the 
   ## generated ID for the row. For Postgre this adds
@@ -161,9 +224,9 @@ proc insertID*(db: TDbConn, query: TSqlQuery,
   result = TryInsertID(db, query, args)
   if result < 0: dbError(db)
   
-proc execAffectedRows*(db: TDbConn, query: TSqlQuery, 
+proc execAffectedRows*(db: TDbConn, query: TSqlQuery,
                        args: varargs[string, `$`]): int64 {.tags: [
-                       FReadDB, FWriteDb].} = 
+                       FReadDB, FWriteDb].} =
   ## executes the query (typically "UPDATE") and returns the
   ## number of affected rows.
   var q = dbFormat(query, args)
@@ -172,7 +235,7 @@ proc execAffectedRows*(db: TDbConn, query: TSqlQuery,
   result = parseBiggestInt($PQcmdTuples(res))
   PQclear(res)
 
-proc close*(db: TDbConn) {.tags: [FDb].} = 
+proc close*(db: TDbConn) {.tags: [FDb].} =
   ## closes the database connection.
   if db != nil: PQfinish(db)
 
diff --git a/lib/impure/fenv.nim b/lib/impure/fenv.nim
new file mode 100644
index 000000000..9d7c8809b
--- /dev/null
+++ b/lib/impure/fenv.nim
@@ -0,0 +1,113 @@
+#
+#
+#            Nimrod's Runtime Library
+#        (c) Copyright 2014 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## Floating-point environment. Handling of floating-point rounding and
+## exceptions (overflow, zero-devide, etc.).
+
+{.deadCodeElim:on.}
+
+when defined(Posix) and not defined(haiku):
+  {.passl: "-lm".}
+
+var
+  FE_DIVBYZERO* {.importc, header: "<fenv.h>".}: cint
+    ## division by zero
+  FE_INEXACT* {.importc, header: "<fenv.h>".}: cint
+    ## inexact result
+  FE_INVALID* {.importc, header: "<fenv.h>".}: cint
+    ## invalid operation
+  FE_OVERFLOW* {.importc, header: "<fenv.h>".}: cint
+    ## result not representable due to overflow
+  FE_UNDERFLOW* {.importc, header: "<fenv.h>".}: cint
+    ## result not representable due to underflow
+  FE_ALL_EXCEPT* {.importc, header: "<fenv.h>".}: cint
+    ## bitwise OR of all supported exceptions
+  FE_DOWNWARD* {.importc, header: "<fenv.h>".}: cint
+    ## round toward -Inf
+  FE_TONEAREST* {.importc, header: "<fenv.h>".}: cint
+    ## round to nearest
+  FE_TOWARDZERO* {.importc, header: "<fenv.h>".}: cint
+    ## round toward 0
+  FE_UPWARD* {.importc, header: "<fenv.h>".}: cint
+    ## round toward +Inf
+  FE_DFL_ENV* {.importc, header: "<fenv.h>".}: cint
+    ## macro of type pointer to fenv_t to be used as the argument
+    ## to functions taking an argument of type fenv_t; in this
+    ## case the default environment will be used
+
+type
+  TFloatClass* = enum ## describes the class a floating point value belongs to.
+                      ## This is the type that is returned by `classify`.
+    fcNormal,    ## value is an ordinary nonzero floating point value
+    fcSubnormal, ## value is a subnormal (a very small) floating point value
+    fcZero,      ## value is zero
+    fcNegZero,   ## value is the negative zero
+    fcNan,       ## value is Not-A-Number (NAN)
+    fcInf,       ## value is positive infinity
+    fcNegInf     ## value is negative infinity
+
+  Tfenv* {.importc: "fenv_t", header: "<fenv.h>", final, pure.} =
+    object ## Represents the entire floating-point environment. The
+           ## floating-point environment refers collectively to any
+           ## floating-point status flags and control modes supported
+           ## by the implementation.
+  Tfexcept* {.importc: "fexcept_t", header: "<fenv.h>", final, pure.} =
+    object ## Represents the floating-point status flags collectively,
+           ## including any status the implementation associates with the
+           ## flags. A floating-point status flag is a system variable
+           ## whose value is set (but never cleared) when a floating-point
+           ## exception is raised, which occurs as a side effect of
+           ## exceptional floating-point arithmetic to provide auxiliary
+           ## information. A floating-point control mode is a system variable
+           ## whose value may be set by the user to affect the subsequent
+           ## behavior of floating-point arithmetic.
+
+proc feclearexcept*(excepts: cint): cint {.importc, header: "<fenv.h>".}
+  ## Clear the supported exceptions represented by `excepts`.
+
+proc fegetexceptflag*(flagp: ptr Tfexcept, excepts: cint): cint {.
+  importc, header: "<fenv.h>".}
+  ## Store implementation-defined representation of the exception flags
+  ## indicated by `excepts` in the object pointed to by `flagp`.
+
+proc feraiseexcept*(excepts: cint): cint {.importc, header: "<fenv.h>".}
+  ## Raise the supported exceptions represented by `excepts`.
+
+proc fesetexceptflag*(flagp: ptr Tfexcept, excepts: cint): cint {.
+  importc, header: "<fenv.h>".}
+  ## Set complete status for exceptions indicated by `excepts` according to
+  ## the representation in the object pointed to by `flagp`.
+
+proc fetestexcept*(excepts: cint): cint {.importc, header: "<fenv.h>".}
+  ## Determine which of subset of the exceptions specified by `excepts` are
+  ## currently set.
+
+proc fegetround*(): cint {.importc, header: "<fenv.h>".}
+  ## Get current rounding direction.
+
+proc fesetround*(roundingDirection: cint): cint {.importc, header: "<fenv.h>".}
+  ## Establish the rounding direction represented by `roundingDirection`.
+
+proc fegetenv*(envp: ptr Tfenv): cint {.importc, header: "<fenv.h>".}
+  ## Store the current floating-point environment in the object pointed
+  ## to by `envp`.
+
+proc feholdexcept*(envp: ptr Tfenv): cint {.importc, header: "<fenv.h>".}
+  ## Save the current environment in the object pointed to by `envp`, clear
+  ## exception flags and install a non-stop mode (if available) for all
+  ## exceptions.
+
+proc fesetenv*(a1: ptr Tfenv): cint {.importc, header: "<fenv.h>".}
+  ## Establish the floating-point environment represented by the object
+  ## pointed to by `envp`.
+
+proc feupdateenv*(envp: ptr Tfenv): cint {.importc, header: "<fenv.h>".}
+  ## Save current exceptions in temporary storage, install environment
+  ## represented by object pointed to by `envp` and raise exceptions
+  ## according to saved exceptions.
diff --git a/lib/impure/re.nim b/lib/impure/re.nim
index f6511dab4..ac07b2d6b 100644
--- a/lib/impure/re.nim
+++ b/lib/impure/re.nim
@@ -243,7 +243,7 @@ template `=~` *(s: string, pattern: TRegex): expr =
   ##     echo("syntax error")
   ##
   bind maxSubPatterns
-  when not definedInScope(matches):
+  when not declaredInScope(matches):
     var matches {.inject.}: array[0..MaxSubpatterns-1, string]
   match(s, pattern, matches)
 
diff --git a/lib/impure/zipfiles.nim b/lib/impure/zipfiles.nim
index 1726449d8..b9f89dda0 100644
--- a/lib/impure/zipfiles.nim
+++ b/lib/impure/zipfiles.nim
@@ -56,6 +56,8 @@ proc addFile*(z: var TZipArchive, dest, src: string) =
   ## Adds the file `src` to the archive `z` with the name `dest`. `dest`
   ## may contain a path that will be created. 
   assert(z.mode != fmRead) 
+  if not fileExists(src):
+    raise newException(EIO, "File '" & src & "' does not exist")
   var zipsrc = zip_source_file(z.w, src, 0, -1)
   if zipsrc == nil:
     #echo("Dest: " & dest)