summary refs log tree commit diff stats
path: root/lib
diff options
context:
space:
mode:
authorAndreas Rumpf <andreas@andreas-desktop>2009-11-26 00:32:07 +0100
committerAndreas Rumpf <andreas@andreas-desktop>2009-11-26 00:32:07 +0100
commit196ef92c86d8b8971d4b316f7c18e404842c4b9b (patch)
tree03506a36422f8ae7543b492978067263beea8502 /lib
parent3710309d39f65718ab5990d53a977acb241432a9 (diff)
downloadNim-196ef92c86d8b8971d4b316f7c18e404842c4b9b.tar.gz
bug concerning constant evaluation fixed
Diffstat (limited to 'lib')
-rwxr-xr-xlib/impure/db_postgres.nim137
-rwxr-xr-xlib/impure/zipfiles.nim2
-rwxr-xr-xlib/pure/pegs.nim4
-rwxr-xr-xlib/pure/re.nim2
-rwxr-xr-xlib/system/profiler.nim8
-rwxr-xr-xlib/wrappers/postgres.nim2
6 files changed, 98 insertions, 57 deletions
diff --git a/lib/impure/db_postgres.nim b/lib/impure/db_postgres.nim
index 197250d9c..b9d0b068f 100755
--- a/lib/impure/db_postgres.nim
+++ b/lib/impure/db_postgres.nim
@@ -4,11 +4,20 @@
 import strutils, postgres
 
 type
-  TDbHandle* = PGconn
-  TRow* = seq[string]
-  EDb* = object of EIO
+  TDbConn* = PPGconn  ## encapsulates a database connection
+  TRow* = seq[string]  ## a row of a dataset
+  EDb* = object of EIO ## exception that is raised if a database error occurs
   
-proc dbError(db: TDbHandle) {.noreturn.} = 
+  TSqlQuery* = distinct string ## an SQL query string
+  
+proc sql*(query: string): TSqlQuery {.noSideEffect, inline.} =  
+  ## constructs a TSqlQuery from the string `query`: If assertions are turned 
+  ## off, it does nothing. If assertions are turned on, the string is checked
+  ## for SQL security holes. This is supposed to be used as a 
+  ## raw-string-literal modifier: ``sql"update user set counter = counter + 1"``
+  result = TSqlQuery(query)
+ 
+proc dbError(db: TDbConn) {.noreturn.} = 
   ## raises an EDb exception.
   var e: ref EDb
   new(e)
@@ -23,13 +32,13 @@ proc dbError*(msg: string) {.noreturn.} =
   raise e
 
 when false:
-  proc dbQueryOpt*(db: TDbHandle, query: string, args: openarray[string]) =
+  proc dbQueryOpt*(db: TDbConn, query: string, args: openarray[string]) =
     var stmt = mysql_stmt_init(db)
     if stmt == nil: dbError(db)
     if mysql_stmt_prepare(stmt, query, len(query)) != 0: 
       dbError(db)
     var 
-      bind: seq[MYSQL_BIND]
+      bindings: seq[MYSQL_BIND]
     discard mysql_stmt_close(stmt)
 
 proc dbQuote(s: string): string =
@@ -39,91 +48,123 @@ proc dbQuote(s: string): string =
     else: add(result, c)
   add(result, '\'')
 
-proc dbFormat(formatstr: string, args: openarray[string]): string =
+proc dbFormat(formatstr: TSqlQuery, args: openarray[string]): string =
   result = ""
   var a = 0
-  for c in items(formatstr):
+  for c in items(string(formatstr)):
     if c == '?':
       add(result, dbQuote(args[a]))
       inc(a)
     else: 
       add(result, c)
   
-proc dbTryQuery*(db: TDbHandle, query: string, args: openarray[string]): bool =
+proc dbTryQuery*(db: TDbConn, query: TSqlQuery, 
+                 args: openarray[string]): bool =
+  ## tries to execute the query and returns true if successful, false otherwise.
   var q = dbFormat(query, args)
   var res = PQExec(db, q)
   result = PQresultStatus(res) == PGRES_COMMAND_OK
   PQclear(res)
 
-proc dbQuery*(db: TDbHandle, query: string, args: openarray[string]) =
-  var q = dbFormat(query, args)
-  var res = PQExec(db, q)
-  if PQresultStatus(res) != PGRES_COMMAND_OK: dbError(db)
-  PQclear(res)
-  
-proc dbTryInsertID*(db: TDbHandle, query: string, 
-                    args: openarray[string]): int64 =
-  var q = dbFormat(query, args)
-  
-  
-  if mysqlRealQuery(db, q, q.len) != 0'i32: 
-    result = -1'i64
-  else:
-    result = mysql_insert_id(db)
-  LAST_INSERT_ID()
-
-proc dbInsertID*(db: TDbHandle, query: string, args: openArray[string]): int64 = 
-  result = dbTryInsertID(db, query, args)
-  if result < 0: dbError(db)
-  
-proc dbQueryAffectedRows*(db: TDbHandle, query: string, 
-                          args: openArray[string]): int64 = 
-  ## runs the query (typically "UPDATE") and returns the
-  ## number of affected rows
+proc dbQuery*(db: TDbConn, query: TSqlQuery, args: openarray[string]) =
+  ## executes the query and raises EDB if not successful.
   var q = dbFormat(query, args)
   var res = PQExec(db, q)
   if PQresultStatus(res) != PGRES_COMMAND_OK: dbError(db)
-  result = parseBiggestInt($PQcmdTuples(res))
   PQclear(res)
   
 proc newRow(L: int): TRow =
   newSeq(result, L)
   for i in 0..L-1: result[i] = ""
   
-iterator dbFastRows*(db: TDbHandle, query: string,
-                     args: openarray[string]): TRow =
+proc setupQuery(db: TDbConn, query: TSqlQuery, 
+                args: openarray[string]): PPGresult = 
   var q = dbFormat(query, args)
-  var res = PQExec(db, q)
-  if PQresultStatus(res) != PGRES_TUPLES_OK: dbError(db)
+  result = PQExec(db, q)
+  if PQresultStatus(result) != PGRES_TUPLES_OK: dbError(db)
+  
+proc setRow(res: PPGresult, r: var TRow, line, cols: int) =
+  for col in 0..cols-1:
+    setLen(r[col], 0)
+    add(r[col], PQgetvalue(res, line, cols))    
+  
+iterator dbFastRows*(db: TDbConn, query: TSqlQuery,
+                     args: openarray[string]): TRow =
+  ## 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.
+  var res = setupQuery(db, query, args)
   var L = int(PQnfields(res))
   var result = newRow(L)
   for i in 0..PQntuples(res)-1:
-    for j in 0..L-1:
-      setLen(result[j], 0)
-      add(result[j], PQgetvalue(res, i, j))
+    setRow(res, result, i, L)
     yield result
   PQclear(res)
 
-proc dbGetAllRows*(db: TDbHandle, query: string, 
+proc dbGetAllRows*(db: TDbConn, query: TSqlQuery, 
                    args: openarray[string]): seq[TRow] =
+  ## executes the query and returns the whole result dataset.
   result = @[]
   for r in dbFastRows(db, query, args):
     result.add(r)
 
-iterator dbRows*(db: TDbHandle, query: string, 
+iterator dbRows*(db: TDbConn, query: TSqlQuery, 
                  args: openarray[string]): TRow =
+  ## same as `dbFastRows`, but slower and safe.
   for r in items(dbGetAllRows(db, query, args)): yield r
 
-proc dbGetValue*(db: TDbHandle, query: string, 
+proc dbGetValue*(db: TDbConn, query: TSqlQuery, 
                  args: openarray[string]): string = 
+  ## executes the query and returns the result dataset's the first column 
+  ## of the first row. Returns "" if the dataset contains no rows. This uses
+  ## `dbFastRows`, so it inherits its fragile behaviour.
   result = ""
   for row in dbFastRows(db, query, args): 
     result = row[0]
     break
+  
+proc dbTryInsertID*(db: TDbConn, query: TSqlQuery, 
+                    args: openarray[string]): int64 =
+  ## 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 val = dbGetValue(db, TSqlQuery(string(query) & " RETURNING id"), args)
+  if val.len > 0:
+    result = ParseBiggestInt(val)
+  else:
+    result = -1
+  #if mysqlRealQuery(db, q, q.len) != 0'i32: 
+  #  result = -1'i64
+  #else:
+  #  result = mysql_insert_id(db)
+  #LAST_INSERT_ID()
+
+proc dbInsertID*(db: TDbConn, query: TSqlQuery, 
+                 args: openArray[string]): int64 = 
+  ## 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
+  ## named ``id``. 
+  result = dbTryInsertID(db, query, args)
+  if result < 0: dbError(db)
+  
+proc dbQueryAffectedRows*(db: TDbConn, query: TSqlQuery, 
+                          args: openArray[string]): int64 = 
+  ## executes the query (typically "UPDATE") and returns the
+  ## number of affected rows.
+  var q = dbFormat(query, args)
+  var res = PQExec(db, q)
+  if PQresultStatus(res) != PGRES_COMMAND_OK: dbError(db)
+  result = parseBiggestInt($PQcmdTuples(res))
+  PQclear(res)
 
-proc dbClose*(db: TDbHandle) = 
+proc dbClose*(db: TDbConn) = 
+  ## closes the database connection.
   if db != nil: PQfinish(db)
 
-proc dbOpen*(connection, user, password, database: string): TDbHandle =
+proc dbOpen*(connection, user, password, database: string): TDbConn =
+  ## opens a database connection. Returns nil in case of an error.
   result = PQsetdbLogin(nil, nil, nil, nil, database, user, password)
-  if PQStatus(result) != CONNECTION_OK: result = nil
+  if PQStatus(result) != CONNECTION_OK: dbError(result) # result = nil
+
diff --git a/lib/impure/zipfiles.nim b/lib/impure/zipfiles.nim
index e48b0f08e..07fe41a04 100755
--- a/lib/impure/zipfiles.nim
+++ b/lib/impure/zipfiles.nim
@@ -43,7 +43,7 @@ proc close*(z: var TZipArchive) =
   zip_close(z.w)
  
 proc createDir*(z: var TZipArchive, dir: string) = 
-  ## Creates a directory within the `z` archive. This does not fails if the
+  ## Creates a directory within the `z` archive. This does not fail if the
   ## directory already exists. Note that for adding a file like 
   ## ``"path1/path2/filename"`` it is not necessary
   ## to create the ``"path/path2"`` subdirectories - it will be done 
diff --git a/lib/pure/pegs.nim b/lib/pure/pegs.nim
index 21205bdb2..488e42c7d 100755
--- a/lib/pure/pegs.nim
+++ b/lib/pure/pegs.nim
@@ -148,7 +148,7 @@ proc sequence*(a: openArray[TPeg]): TPeg =
   multipleOp(pkSequence, addSequence)
  
 proc `?`*(a: TPeg): TPeg =
-  ## constructs an optional piece with the PEG `a`
+  ## constructs an optional for the PEG `a`
   if a.kind in {pkOption, pkGreedyRep, pkGreedyAny, pkGreedyRepChar,
                 pkGreedyRepSet}:
     # a* ?  --> a*
@@ -159,7 +159,7 @@ proc `?`*(a: TPeg): TPeg =
     result.sons = @[a]
 
 proc `*`*(a: TPeg): TPeg =
-  ## constructs a "greedy repetition" piece the PEG `a`
+  ## constructs a "greedy repetition" for the PEG `a`
   case a.kind
   of pkGreedyRep, pkGreedyRepChar, pkGreedyRepSet, pkGreedyAny, pkOption:
     assert false
diff --git a/lib/pure/re.nim b/lib/pure/re.nim
index f854c07e5..87fe79a04 100755
--- a/lib/pure/re.nim
+++ b/lib/pure/re.nim
@@ -224,7 +224,7 @@ proc replace*(s: string, sub: TRegEx, by: string): string =
   ## with the notation ``$i`` and ``$#`` (see strutils.`%`). Examples:
   ##
   ## .. code-block:: nimrod
-  ##   "var1=key; var2=key2".replace(p"{\ident}'='{\ident}", "$1<-$2$2")
+  ##   "var1=key; var2=key2".replace(re"{\ident}'='{\ident}", "$1<-$2$2")
   ##
   ## Results in:
   ##
diff --git a/lib/system/profiler.nim b/lib/system/profiler.nim
index 97c7dcfeb..b87b30d4a 100755
--- a/lib/system/profiler.nim
+++ b/lib/system/profiler.nim
@@ -41,10 +41,10 @@ proc writeProfile() {.noconv.} =
   var i = 0
   var f: TFile
   var j = 1
-  while openFile(f, filename & $j & ".txt"):
-    closeFile(f)
+  while open(f, filename & $j & ".txt"):
+    close(f)
     inc(j)
-  if openFile(f, filename & $j & ".txt", fmWrite):
+  if open(f, filename & $j & ".txt", fmWrite):
     var N = 0
     # we have to compute the actual length of the array:
     while profileData[N].procname != nil: inc(N)
@@ -56,6 +56,6 @@ proc writeProfile() {.noconv.} =
       write(f, ": ")
       writeln(f, profileData[i].total)
       inc(i)
-    closeFile(f)
+    close(f)
 
 addQuitProc(writeProfile)
diff --git a/lib/wrappers/postgres.nim b/lib/wrappers/postgres.nim
index d3c5e265b..26526b6c8 100755
--- a/lib/wrappers/postgres.nim
+++ b/lib/wrappers/postgres.nim
@@ -11,7 +11,7 @@ when defined(windows):
 elif defined(macosx):
   const dllName = "libpq.dylib"
 else:
-  const dllName = "libpq.so"
+  const dllName = "libpq.so(.5|)"
   
 type
   POid* = ptr Oid