Browse Source

Added nawak micro-framework

Erwan Ameil 11 years ago
parent
commit
ec5bb1105b

+ 1 - 1
config/create-postgres.sql

@@ -2,7 +2,7 @@
 DROP TABLE IF EXISTS World;
 CREATE TABLE  World (
   id integer NOT NULL,
-  "randomNumber" integer NOT NULL default 0,
+  randomNumber integer NOT NULL default 0,
   PRIMARY KEY  (id)
 );
 

+ 28 - 0
nawak/benchmark_config

@@ -0,0 +1,28 @@
+{
+  "framework": "nawak",
+  "tests": [{
+    "default": {
+      "setup_file": "setup",
+      "json_url": "/json",
+      "db_url": "/db",
+      "query_url": "/queries?queries=",
+      "fortune_url": "/fortunes",
+      "update_url": "/updates?queries=",
+      "plaintext_url": "/plaintext",
+      "port": 6767,
+      "approach": "Realistic",
+      "classification": "Micro",
+      "database": "Postgres",
+      "framework": "nawak",
+      "language": "Nimrod",
+      "orm": "Raw",
+      "platform": "Nimrod",
+      "webserver": "mongrel2",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "nawak",
+      "notes": "",
+      "versus": ""
+    }
+  }]
+}

+ 9 - 0
nawak/conf/Makefile

@@ -0,0 +1,9 @@
+
+all: run
+
+run:
+	mkdir -p run logs tmp
+	m2sh load
+	sudo m2sh start -every
+
+.PHONY: run

+ 34 - 0
nawak/conf/mongrel2.conf

@@ -0,0 +1,34 @@
+# this is the minimal config required for mongrel2
+micro_framework = Handler(
+                          send_spec='tcp://127.0.0.1:9999',
+                          #send_spec='ipc:///tmp/requests.mongrel.ipc',
+                          send_ident='0138a43-micro-nimrod',
+                          recv_spec='tcp://127.0.0.1:9998',
+                          #recv_spec='ipc:///tmp/responses.mongrel.ipc',
+                          #protocol='tnetstring', # default to json
+                          recv_ident='')
+
+main = Server(
+    uuid="bd81667c-505a-41da-952c-1e672338db8a",
+    access_log="/logs/access.log",
+    error_log="/logs/error.log",
+    chroot="./",
+    default_host="localhost",
+    name="test",
+    pid_file="/run/mongrel2.pid",
+    port=6767,
+    hosts = [
+        Host(name="localhost", routes={
+            '/': micro_framework
+        })
+    ]
+)
+
+settings = {"zeromq.threads": 1,
+            "disable.access_logging": 1,
+            "limits.min_ping": 0,
+            "limits.min_write_rate": 0,
+            "limits.min_read_rate": 0,
+            "limits.kill_limit": 2}
+
+servers = [main]

+ 17 - 0
nawak/fortunes_tmpl.nim

@@ -0,0 +1,17 @@
+#! stdtmpl | standard
+#import lib/escape
+#from nawak_app import TFortune
+#proc fortunes_tmpl*(fortunes: openArray[TFortune]): string =
+#  result = ""
+<!DOCTYPE html>
+<html>
+  <head><title>Fortunes</title></head>
+<body>
+  <table>
+    <tr><th>id</th><th>message</th></tr>
+  #for fortune in items(fortunes):
+    <tr><td>${fortune.id}</th><th>${escape(fortune.message)}</th></tr>
+  #end for
+  </table>
+</body>
+</html>

+ 271 - 0
nawak/lib/db_postgres_redone.nim

@@ -0,0 +1,271 @@
+#
+#
+#            Nimrod's Runtime Library
+#        (c) Copyright 2012 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## A higher level `PostgreSQL`:idx: database wrapper. This interface 
+## is implemented for other databases too.
+
+import strutils, postgres_redone
+
+type
+  TDbConn* = PPGconn   ## encapsulates a database connection
+  TRow* = seq[string]  ## a row of a dataset. NULL database values will be
+                       ## transformed always to the empty string.
+  EDb* = object of EIO ## exception that is raised if a database error occurs
+  
+  TSqlQuery* = distinct string ## an SQL query string
+  TPreparedId* = 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.} =  
+  ## 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"``
+  ##
+  ## If assertions are turned off, it does nothing. If assertions are turned 
+  ## on, later versions will check the string for valid syntax.
+  result = TSqlQuery(query)
+ 
+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.} = 
+  ## raises an EDb exception with message `msg`.
+  var e: ref EDb
+  new(e)
+  e.msg = msg
+  raise e
+
+proc dbQuote(s: string): string =
+  result = "'"
+  for c in items(s):
+    if c == '\'': add(result, "''")
+    else: add(result, c)
+  add(result, '\'')
+
+proc dbFormat(formatstr: TSqlQuery, args: varargs[string]): string =
+  result = ""
+  var a = 0
+  for c in items(string(formatstr)):
+    if c == '?':
+      add(result, dbQuote(args[a]))
+      inc(a)
+    else: 
+      add(result, c)
+  
+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)
+  result = PQresultStatus(res) == PGRES_COMMAND_OK
+  PQclear(res)
+
+proc ExecNo*(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)
+  if PQresultStatus(res) != PGRES_COMMAND_OK: dbError(db)
+  PQclear(res)
+
+var nullPOid: POid
+var nowhere: ptr int32
+proc Exec*(db: TDbConn, stmtName: TPreparedId,
+          args: varargs[string]) {.tags: [FReadDB, FWriteDb].} =
+    var arr = allocCStringArray(args)
+
+    var res = PQexecPrepared(db, stmtName.string, int32(args.len), arr,
+                            nowhere, nowhere, 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 setupQueryNo(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: string,
+                args: varargs[string]): PPGresult =
+    # read this for details:
+    # http://www.postgresql.org/docs/9.3/interactive/libpq-exec.html
+    var arr = allocCStringArray(args)
+
+    result = PQexecParams(db, query, int32(args.len), nullPOid, arr, nowhere, nowhere, 0)
+
+    deallocCStringArray(arr)
+    if PQResultStatus(result) != PGRES_TUPLES_OK: dbError(db)
+
+proc setupPQuery(db: TDbConn, stmtName: TPreparedId,
+                args: varargs[string]): PPGresult =
+    var arr = allocCStringArray(args)
+
+    result = PQexecPrepared(db, stmtName.string, int32(args.len), arr,
+                            nowhere, nowhere, 0)
+
+    deallocCStringArray(arr)
+    if PQResultStatus(result) != PGRES_TUPLES_OK: dbError(db)
+
+proc prepare*(db: TDbConn; stmtName, query: string; nParams: int): TPreparedId =
+    var res = PQprepare(db, stmtName, query, int32(nParams), nullPOid)
+    if PQResultStatus(res) != PGRES_COMMAND_OK: dbError(db)
+    return TPreparedId(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)
+
+iterator FastPRows*(db: TDbConn, stmtName: TPreparedId,
+                   args: varargs[string, `$`]): TRow {.tags: [FReadDB].} =
+  ## 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 = setupPQuery(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)
+
+iterator FastRows*(db: TDbConn, query: string,
+                   args: varargs[string, `$`]): TRow {.tags: [FReadDB].} =
+  ## 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 = PQnfields(res)
+  var result = newRow(L)
+  for i in 0..PQntuples(res)-1:
+    setRow(res, result, i, L)
+    yield result
+  PQclear(res)
+
+proc getPRow*(db: TDbConn, stmtName: TPreparedId,
+              args: varargs[string, `$`]): TRow {.tags: [FReadDB].} =
+    var res = setupPQuery(db, stmtName, args)
+    var L = PQnfields(res)
+    result = newRow(L)
+    setRow(res, result, 0, L)
+    PQclear(res)
+
+proc getRow*(db: TDbConn, query: string,
+             args: varargs[string, `$`]): TRow {.tags: [FReadDB].} =
+  ## retrieves a single row. If the query doesn't return any rows, this proc
+  ## will return a TRow with empty strings for each column.
+  var res = setupQuery(db, query, args)
+  var L = PQnfields(res)
+  result = newRow(L)
+  setRow(res, result, 0, L)
+  PQclear(res)
+
+proc GetAllPRows*(db: TDbConn, stmtName: TPreparedId,
+                 args: varargs[string, `$`]): seq[TRow] {.tags: [FReadDB].} =
+  ## executes the query and returns the whole result dataset.
+  result = @[]
+  for r in FastPRows(db, stmtName, args):
+    result.add(r)
+
+proc GetAllRows*(db: TDbConn, query: string, 
+                 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: string, 
+               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: string, 
+               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: string, 
+                  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, query & " RETURNING id", 
+    args), 0, 0)
+  if not isNil(x):
+    result = ParseBiggestInt($x)
+  else:
+    result = -1
+
+proc InsertID*(db: TDbConn, query: string, 
+               args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} =
+  ## 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 = TryInsertID(db, query, args)
+  if result < 0: dbError(db)
+  
+proc ExecAffectedRows*(db: TDbConn, query: TSqlQuery, 
+                       args: varargs[string, `$`]): int64 {.tags: [
+                       FReadDB, FWriteDb].} = 
+  ## 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 Close*(db: TDbConn) {.tags: [FDb].} = 
+  ## closes the database connection.
+  if db != nil: PQfinish(db)
+
+proc Open*(connection, user, password, database: string): TDbConn {.
+  tags: [FDb].} =
+  ## opens a database connection. Raises `EDb` if the connection could not
+  ## be established.
+  ##
+  ## Clients can also use Postgres keyword/value connection strings to
+  ## connect.
+  ##
+  ## Example:
+  ##
+  ## .. code-block:: nimrod
+  ##
+  ##      con = Open("", "", "", "host=localhost port=5432 dbname=mydb")
+  ##
+  ## See http://www.postgresql.org/docs/current/static/libpq-connect.html#LIBPQ-CONNSTRING
+  ## for more information.
+  ##
+  ## Note that the connection parameter is not used but exists to maintain
+  ## the nimrod db api.
+  result = PQsetdbLogin(nil, nil, nil, nil, database, user, password)
+  if PQStatus(result) != CONNECTION_OK: dbError(result) # result = nil
+
+

+ 33 - 0
nawak/lib/escape.nim

@@ -0,0 +1,33 @@
+# from Nimrod's Runtime Library - xmltree
+# (c) Copyright 2012 Andreas Rumpf
+# Modifications by Erwan Ameil
+
+proc addEscaped*(result: var string, s: string) = 
+  ## same as ``result.add(escape(s))``, but more efficient.
+  for c in items(s):
+    case c
+    of '<': result.add("&lt;")
+    of '>': result.add("&gt;")
+    of '&': result.add("&amp;")
+    of '"': result.add("&quot;")
+    of '\'': result.add("&#x27;")
+    of '/': result.add("&#x2F;")
+    else: result.add(c)
+
+proc escape*(s: string): string = 
+  ## escapes `s` for inclusion into an XML document. 
+  ## Escapes these characters:
+  ##
+  ## ------------    -------------------
+  ## char            is converted to
+  ## ------------    -------------------
+  ##  ``<``          ``&lt;``
+  ##  ``>``          ``&gt;``
+  ##  ``&``          ``&amp;``
+  ##  ``"``          ``&quot;``
+  ##  ``'``          ``&#x27;``
+  ##  ``/``          ``&#x2F;``
+  ## ------------    -------------------
+  result = newStringOfCap(s.len)
+  addEscaped(result, s)
+ 

+ 352 - 0
nawak/lib/postgres_redone.nim

@@ -0,0 +1,352 @@
+# This module contains the definitions for structures and externs for
+# functions used by frontend postgres applications. It is based on
+# Postgresql's libpq-fe.h.
+#
+# It is for postgreSQL version 7.4 and higher with support for the v3.0
+# connection-protocol.
+#
+
+{.deadCodeElim: on.}
+
+when defined(windows): 
+  const 
+    dllName = "libpq.dll"
+elif defined(macosx): 
+  const 
+    dllName = "libpq.dylib"
+else: 
+  const 
+    dllName = "libpq.so(.5|)"
+type 
+  POid* = ptr Oid
+  Oid* = int32
+
+const 
+  ERROR_MSG_LENGTH* = 4096
+  CMDSTATUS_LEN* = 40
+
+type 
+  TSockAddr* = array[1..112, int8]
+  TPGresAttDesc*{.pure, final.} = object 
+    name*: cstring
+    adtid*: Oid
+    adtsize*: int
+
+  PPGresAttDesc* = ptr TPGresAttDesc
+  PPPGresAttDesc* = ptr PPGresAttDesc
+  TPGresAttValue*{.pure, final.} = object 
+    length*: int32
+    value*: cstring
+
+  PPGresAttValue* = ptr TPGresAttValue
+  PPPGresAttValue* = ptr PPGresAttValue
+  PExecStatusType* = ptr TExecStatusType
+  TExecStatusType* = enum 
+    PGRES_EMPTY_QUERY = 0, PGRES_COMMAND_OK, PGRES_TUPLES_OK, PGRES_COPY_OUT, 
+    PGRES_COPY_IN, PGRES_BAD_RESPONSE, PGRES_NONFATAL_ERROR, PGRES_FATAL_ERROR
+  TPGlobjfuncs*{.pure, final.} = object 
+    fn_lo_open*: Oid
+    fn_lo_close*: Oid
+    fn_lo_creat*: Oid
+    fn_lo_unlink*: Oid
+    fn_lo_lseek*: Oid
+    fn_lo_tell*: Oid
+    fn_lo_read*: Oid
+    fn_lo_write*: Oid
+
+  PPGlobjfuncs* = ptr TPGlobjfuncs
+  PConnStatusType* = ptr TConnStatusType
+  TConnStatusType* = enum 
+    CONNECTION_OK, CONNECTION_BAD, CONNECTION_STARTED, CONNECTION_MADE, 
+    CONNECTION_AWAITING_RESPONSE, CONNECTION_AUTH_OK, CONNECTION_SETENV, 
+    CONNECTION_SSL_STARTUP, CONNECTION_NEEDED
+  TPGconn*{.pure, final.} = object 
+    pghost*: cstring
+    pgtty*: cstring
+    pgport*: cstring
+    pgoptions*: cstring
+    dbName*: cstring
+    status*: TConnStatusType
+    errorMessage*: array[0..(ERROR_MSG_LENGTH) - 1, char]
+    Pfin*: TFile
+    Pfout*: TFile
+    Pfdebug*: TFile
+    sock*: int32
+    laddr*: TSockAddr
+    raddr*: TSockAddr
+    salt*: array[0..(2) - 1, char]
+    asyncNotifyWaiting*: int32
+    notifyList*: pointer
+    pguser*: cstring
+    pgpass*: cstring
+    lobjfuncs*: PPGlobjfuncs
+
+  PPGconn* = ptr TPGconn
+  TPGresult*{.pure, final.} = object 
+    ntups*: int32
+    numAttributes*: int32
+    attDescs*: PPGresAttDesc
+    tuples*: PPPGresAttValue
+    tupArrSize*: int32
+    resultStatus*: TExecStatusType
+    cmdStatus*: array[0..(CMDSTATUS_LEN) - 1, char]
+    binary*: int32
+    conn*: PPGconn
+
+  PPGresult* = ptr TPGresult
+  PPostgresPollingStatusType* = ptr PostgresPollingStatusType
+  PostgresPollingStatusType* = enum 
+    PGRES_POLLING_FAILED = 0, PGRES_POLLING_READING, PGRES_POLLING_WRITING, 
+    PGRES_POLLING_OK, PGRES_POLLING_ACTIVE
+  PPGTransactionStatusType* = ptr PGTransactionStatusType
+  PGTransactionStatusType* = enum 
+    PQTRANS_IDLE, PQTRANS_ACTIVE, PQTRANS_INTRANS, PQTRANS_INERROR, 
+    PQTRANS_UNKNOWN
+  PPGVerbosity* = ptr PGVerbosity
+  PGVerbosity* = enum 
+    PQERRORS_TERSE, PQERRORS_DEFAULT, PQERRORS_VERBOSE
+  PpgNotify* = ptr pgNotify
+  pgNotify*{.pure, final.} = object 
+    relname*: cstring
+    be_pid*: int32
+    extra*: cstring
+
+  PQnoticeReceiver* = proc (arg: pointer, res: PPGresult){.cdecl.}
+  PQnoticeProcessor* = proc (arg: pointer, message: cstring){.cdecl.}
+  Ppqbool* = ptr pqbool
+  pqbool* = char
+  P_PQprintOpt* = ptr PQprintOpt
+  PQprintOpt*{.pure, final.} = object 
+    header*: pqbool
+    align*: pqbool
+    standard*: pqbool
+    html3*: pqbool
+    expanded*: pqbool
+    pager*: pqbool
+    fieldSep*: cstring
+    tableOpt*: cstring
+    caption*: cstring
+    fieldName*: ptr cstring
+
+  P_PQconninfoOption* = ptr PQconninfoOption
+  PQconninfoOption*{.pure, final.} = object 
+    keyword*: cstring
+    envvar*: cstring
+    compiled*: cstring
+    val*: cstring
+    label*: cstring
+    dispchar*: cstring
+    dispsize*: int32
+
+  PPQArgBlock* = ptr PQArgBlock
+  PQArgBlock*{.pure, final.} = object 
+    length*: int32
+    isint*: int32
+    p*: pointer
+
+
+proc PQconnectStart*(conninfo: cstring): PPGconn{.cdecl, dynlib: dllName, 
+    importc: "PQconnectStart".}
+proc PQconnectPoll*(conn: PPGconn): PostgresPollingStatusType{.cdecl, 
+    dynlib: dllName, importc: "PQconnectPoll".}
+proc PQconnectdb*(conninfo: cstring): PPGconn{.cdecl, dynlib: dllName, 
+    importc: "PQconnectdb".}
+proc PQsetdbLogin*(pghost: cstring, pgport: cstring, pgoptions: cstring, 
+                   pgtty: cstring, dbName: cstring, login: cstring, pwd: cstring): PPGconn{.
+    cdecl, dynlib: dllName, importc: "PQsetdbLogin".}
+proc PQsetdb*(M_PGHOST, M_PGPORT, M_PGOPT, M_PGTTY, M_DBNAME: cstring): ppgconn
+proc PQfinish*(conn: PPGconn){.cdecl, dynlib: dllName, importc: "PQfinish".}
+proc PQconndefaults*(): PPQconninfoOption{.cdecl, dynlib: dllName, 
+    importc: "PQconndefaults".}
+proc PQconninfoFree*(connOptions: PPQconninfoOption){.cdecl, dynlib: dllName, 
+    importc: "PQconninfoFree".}
+proc PQresetStart*(conn: PPGconn): int32{.cdecl, dynlib: dllName, 
+    importc: "PQresetStart".}
+proc PQresetPoll*(conn: PPGconn): PostgresPollingStatusType{.cdecl, 
+    dynlib: dllName, importc: "PQresetPoll".}
+proc PQreset*(conn: PPGconn){.cdecl, dynlib: dllName, importc: "PQreset".}
+proc PQrequestCancel*(conn: PPGconn): int32{.cdecl, dynlib: dllName, 
+    importc: "PQrequestCancel".}
+proc PQdb*(conn: PPGconn): cstring{.cdecl, dynlib: dllName, importc: "PQdb".}
+proc PQuser*(conn: PPGconn): cstring{.cdecl, dynlib: dllName, importc: "PQuser".}
+proc PQpass*(conn: PPGconn): cstring{.cdecl, dynlib: dllName, importc: "PQpass".}
+proc PQhost*(conn: PPGconn): cstring{.cdecl, dynlib: dllName, importc: "PQhost".}
+proc PQport*(conn: PPGconn): cstring{.cdecl, dynlib: dllName, importc: "PQport".}
+proc PQtty*(conn: PPGconn): cstring{.cdecl, dynlib: dllName, importc: "PQtty".}
+proc PQoptions*(conn: PPGconn): cstring{.cdecl, dynlib: dllName, 
+    importc: "PQoptions".}
+proc PQstatus*(conn: PPGconn): TConnStatusType{.cdecl, dynlib: dllName, 
+    importc: "PQstatus".}
+proc PQtransactionStatus*(conn: PPGconn): PGTransactionStatusType{.cdecl, 
+    dynlib: dllName, importc: "PQtransactionStatus".}
+proc PQparameterStatus*(conn: PPGconn, paramName: cstring): cstring{.cdecl, 
+    dynlib: dllName, importc: "PQparameterStatus".}
+proc PQprotocolVersion*(conn: PPGconn): int32{.cdecl, dynlib: dllName, 
+    importc: "PQprotocolVersion".}
+proc PQerrorMessage*(conn: PPGconn): cstring{.cdecl, dynlib: dllName, 
+    importc: "PQerrorMessage".}
+proc PQsocket*(conn: PPGconn): int32{.cdecl, dynlib: dllName, 
+                                      importc: "PQsocket".}
+proc PQbackendPID*(conn: PPGconn): int32{.cdecl, dynlib: dllName, 
+    importc: "PQbackendPID".}
+proc PQclientEncoding*(conn: PPGconn): int32{.cdecl, dynlib: dllName, 
+    importc: "PQclientEncoding".}
+proc PQsetClientEncoding*(conn: PPGconn, encoding: cstring): int32{.cdecl, 
+    dynlib: dllName, importc: "PQsetClientEncoding".}
+when defined(USE_SSL): 
+  # Get the SSL structure associated with a connection  
+  proc PQgetssl*(conn: PPGconn): PSSL{.cdecl, dynlib: dllName, 
+                                       importc: "PQgetssl".}
+proc PQsetErrorVerbosity*(conn: PPGconn, verbosity: PGVerbosity): PGVerbosity{.
+    cdecl, dynlib: dllName, importc: "PQsetErrorVerbosity".}
+proc PQtrace*(conn: PPGconn, debug_port: TFile){.cdecl, dynlib: dllName, 
+    importc: "PQtrace".}
+proc PQuntrace*(conn: PPGconn){.cdecl, dynlib: dllName, importc: "PQuntrace".}
+proc PQsetNoticeReceiver*(conn: PPGconn, theProc: PQnoticeReceiver, arg: pointer): PQnoticeReceiver{.
+    cdecl, dynlib: dllName, importc: "PQsetNoticeReceiver".}
+proc PQsetNoticeProcessor*(conn: PPGconn, theProc: PQnoticeProcessor, 
+                           arg: pointer): PQnoticeProcessor{.cdecl, 
+    dynlib: dllName, importc: "PQsetNoticeProcessor".}
+proc PQexec*(conn: PPGconn, query: cstring): PPGresult{.cdecl, dynlib: dllName, 
+    importc: "PQexec".}
+proc PQexecParams*(conn: PPGconn, command: cstring, nParams: int32, 
+                   paramTypes: POid, paramValues: cstringArray, 
+                   paramLengths, paramFormats: ptr int32, resultFormat: int32): PPGresult{.
+    cdecl, dynlib: dllName, importc: "PQexecParams".}
+proc PQprepare*(conn: PPGconn, stmtName, query: cstring, nParams: int32,
+    paramTypes: POid): PPGresult{.cdecl, dynlib: dllName, importc: "PQprepare".}
+proc PQexecPrepared*(conn: PPGconn, stmtName: cstring, nParams: int32, 
+                     paramValues: cstringArray, 
+                     paramLengths, paramFormats: ptr int32, resultFormat: int32): PPGresult{.
+    cdecl, dynlib: dllName, importc: "PQexecPrepared".}
+proc PQsendQuery*(conn: PPGconn, query: cstring): int32{.cdecl, dynlib: dllName, 
+    importc: "PQsendQuery".}
+proc PQsendQueryParams*(conn: PPGconn, command: cstring, nParams: int32, 
+                        paramTypes: POid, paramValues: cstringArray, 
+                        paramLengths, paramFormats: ptr int32, 
+                        resultFormat: int32): int32{.cdecl, dynlib: dllName, 
+    importc: "PQsendQueryParams".}
+proc PQsendQueryPrepared*(conn: PPGconn, stmtName: cstring, nParams: int32, 
+                          paramValues: cstringArray, 
+                          paramLengths, paramFormats: ptr int32, 
+                          resultFormat: int32): int32{.cdecl, dynlib: dllName, 
+    importc: "PQsendQueryPrepared".}
+proc PQgetResult*(conn: PPGconn): PPGresult{.cdecl, dynlib: dllName, 
+    importc: "PQgetResult".}
+proc PQisBusy*(conn: PPGconn): int32{.cdecl, dynlib: dllName, 
+                                      importc: "PQisBusy".}
+proc PQconsumeInput*(conn: PPGconn): int32{.cdecl, dynlib: dllName, 
+    importc: "PQconsumeInput".}
+proc PQnotifies*(conn: PPGconn): PPGnotify{.cdecl, dynlib: dllName, 
+    importc: "PQnotifies".}
+proc PQputCopyData*(conn: PPGconn, buffer: cstring, nbytes: int32): int32{.
+    cdecl, dynlib: dllName, importc: "PQputCopyData".}
+proc PQputCopyEnd*(conn: PPGconn, errormsg: cstring): int32{.cdecl, 
+    dynlib: dllName, importc: "PQputCopyEnd".}
+proc PQgetCopyData*(conn: PPGconn, buffer: cstringArray, async: int32): int32{.
+    cdecl, dynlib: dllName, importc: "PQgetCopyData".}
+proc PQgetline*(conn: PPGconn, str: cstring, len: int32): int32{.cdecl, 
+    dynlib: dllName, importc: "PQgetline".}
+proc PQputline*(conn: PPGconn, str: cstring): int32{.cdecl, dynlib: dllName, 
+    importc: "PQputline".}
+proc PQgetlineAsync*(conn: PPGconn, buffer: cstring, bufsize: int32): int32{.
+    cdecl, dynlib: dllName, importc: "PQgetlineAsync".}
+proc PQputnbytes*(conn: PPGconn, buffer: cstring, nbytes: int32): int32{.cdecl, 
+    dynlib: dllName, importc: "PQputnbytes".}
+proc PQendcopy*(conn: PPGconn): int32{.cdecl, dynlib: dllName, 
+                                       importc: "PQendcopy".}
+proc PQsetnonblocking*(conn: PPGconn, arg: int32): int32{.cdecl, 
+    dynlib: dllName, importc: "PQsetnonblocking".}
+proc PQisnonblocking*(conn: PPGconn): int32{.cdecl, dynlib: dllName, 
+    importc: "PQisnonblocking".}
+proc PQflush*(conn: PPGconn): int32{.cdecl, dynlib: dllName, importc: "PQflush".}
+proc PQfn*(conn: PPGconn, fnid: int32, result_buf, result_len: ptr int32, 
+           result_is_int: int32, args: PPQArgBlock, nargs: int32): PPGresult{.
+    cdecl, dynlib: dllName, importc: "PQfn".}
+proc PQresultStatus*(res: PPGresult): TExecStatusType{.cdecl, dynlib: dllName, 
+    importc: "PQresultStatus".}
+proc PQresStatus*(status: TExecStatusType): cstring{.cdecl, dynlib: dllName, 
+    importc: "PQresStatus".}
+proc PQresultErrorMessage*(res: PPGresult): cstring{.cdecl, dynlib: dllName, 
+    importc: "PQresultErrorMessage".}
+proc PQresultErrorField*(res: PPGresult, fieldcode: int32): cstring{.cdecl, 
+    dynlib: dllName, importc: "PQresultErrorField".}
+proc PQntuples*(res: PPGresult): int32{.cdecl, dynlib: dllName, 
+                                        importc: "PQntuples".}
+proc PQnfields*(res: PPGresult): int32{.cdecl, dynlib: dllName, 
+                                        importc: "PQnfields".}
+proc PQbinaryTuples*(res: PPGresult): int32{.cdecl, dynlib: dllName, 
+    importc: "PQbinaryTuples".}
+proc PQfname*(res: PPGresult, field_num: int32): cstring{.cdecl, 
+    dynlib: dllName, importc: "PQfname".}
+proc PQfnumber*(res: PPGresult, field_name: cstring): int32{.cdecl, 
+    dynlib: dllName, importc: "PQfnumber".}
+proc PQftable*(res: PPGresult, field_num: int32): Oid{.cdecl, dynlib: dllName, 
+    importc: "PQftable".}
+proc PQftablecol*(res: PPGresult, field_num: int32): int32{.cdecl, 
+    dynlib: dllName, importc: "PQftablecol".}
+proc PQfformat*(res: PPGresult, field_num: int32): int32{.cdecl, 
+    dynlib: dllName, importc: "PQfformat".}
+proc PQftype*(res: PPGresult, field_num: int32): Oid{.cdecl, dynlib: dllName, 
+    importc: "PQftype".}
+proc PQfsize*(res: PPGresult, field_num: int32): int32{.cdecl, dynlib: dllName, 
+    importc: "PQfsize".}
+proc PQfmod*(res: PPGresult, field_num: int32): int32{.cdecl, dynlib: dllName, 
+    importc: "PQfmod".}
+proc PQcmdStatus*(res: PPGresult): cstring{.cdecl, dynlib: dllName, 
+    importc: "PQcmdStatus".}
+proc PQoidStatus*(res: PPGresult): cstring{.cdecl, dynlib: dllName, 
+    importc: "PQoidStatus".}
+proc PQoidValue*(res: PPGresult): Oid{.cdecl, dynlib: dllName, 
+                                       importc: "PQoidValue".}
+proc PQcmdTuples*(res: PPGresult): cstring{.cdecl, dynlib: dllName, 
+    importc: "PQcmdTuples".}
+proc PQgetvalue*(res: PPGresult, tup_num: int32, field_num: int32): cstring{.
+    cdecl, dynlib: dllName, importc: "PQgetvalue".}
+proc PQgetlength*(res: PPGresult, tup_num: int32, field_num: int32): int32{.
+    cdecl, dynlib: dllName, importc: "PQgetlength".}
+proc PQgetisnull*(res: PPGresult, tup_num: int32, field_num: int32): int32{.
+    cdecl, dynlib: dllName, importc: "PQgetisnull".}
+proc PQclear*(res: PPGresult){.cdecl, dynlib: dllName, importc: "PQclear".}
+proc PQfreemem*(p: pointer){.cdecl, dynlib: dllName, importc: "PQfreemem".}
+proc PQmakeEmptyPGresult*(conn: PPGconn, status: TExecStatusType): PPGresult{.
+    cdecl, dynlib: dllName, importc: "PQmakeEmptyPGresult".}
+proc PQescapeString*(till, `from`: cstring, len: int): int{.cdecl, 
+    dynlib: dllName, importc: "PQescapeString".}
+proc PQescapeBytea*(bintext: cstring, binlen: int, bytealen: var int): cstring{.
+    cdecl, dynlib: dllName, importc: "PQescapeBytea".}
+proc PQunescapeBytea*(strtext: cstring, retbuflen: var int): cstring{.cdecl, 
+    dynlib: dllName, importc: "PQunescapeBytea".}
+proc PQprint*(fout: TFile, res: PPGresult, ps: PPQprintOpt){.cdecl, 
+    dynlib: dllName, importc: "PQprint".}
+proc PQdisplayTuples*(res: PPGresult, fp: TFile, fillAlign: int32, 
+                      fieldSep: cstring, printHeader: int32, quiet: int32){.
+    cdecl, dynlib: dllName, importc: "PQdisplayTuples".}
+proc PQprintTuples*(res: PPGresult, fout: TFile, printAttName: int32, 
+                    terseOutput: int32, width: int32){.cdecl, dynlib: dllName, 
+    importc: "PQprintTuples".}
+proc lo_open*(conn: PPGconn, lobjId: Oid, mode: int32): int32{.cdecl, 
+    dynlib: dllName, importc: "lo_open".}
+proc lo_close*(conn: PPGconn, fd: int32): int32{.cdecl, dynlib: dllName, 
+    importc: "lo_close".}
+proc lo_read*(conn: PPGconn, fd: int32, buf: cstring, length: int): int32{.
+    cdecl, dynlib: dllName, importc: "lo_read".}
+proc lo_write*(conn: PPGconn, fd: int32, buf: cstring, length: int): int32{.
+    cdecl, dynlib: dllName, importc: "lo_write".}
+proc lo_lseek*(conn: PPGconn, fd: int32, offset: int32, whence: int32): int32{.
+    cdecl, dynlib: dllName, importc: "lo_lseek".}
+proc lo_creat*(conn: PPGconn, mode: int32): Oid{.cdecl, dynlib: dllName, 
+    importc: "lo_creat".}
+proc lo_tell*(conn: PPGconn, fd: int32): int32{.cdecl, dynlib: dllName, 
+    importc: "lo_tell".}
+proc lo_unlink*(conn: PPGconn, lobjId: Oid): int32{.cdecl, dynlib: dllName, 
+    importc: "lo_unlink".}
+proc lo_import*(conn: PPGconn, filename: cstring): Oid{.cdecl, dynlib: dllName, 
+    importc: "lo_import".}
+proc lo_export*(conn: PPGconn, lobjId: Oid, filename: cstring): int32{.cdecl, 
+    dynlib: dllName, importc: "lo_export".}
+proc PQmblen*(s: cstring, encoding: int32): int32{.cdecl, dynlib: dllName, 
+    importc: "PQmblen".}
+proc PQenv2encoding*(): int32{.cdecl, dynlib: dllName, importc: "PQenv2encoding".}
+proc PQsetdb(M_PGHOST, M_PGPORT, M_PGOPT, M_PGTTY, M_DBNAME: cstring): ppgconn = 
+  result = PQsetdbLogin(M_PGHOST, M_PGPORT, M_PGOPT, M_PGTTY, M_DBNAME, "", "")

+ 103 - 0
nawak/nawak_app.nim

@@ -0,0 +1,103 @@
+import strtabs, strutils, math, algorithm
+import nawak_mongrel, jdump
+
+# The following import belongs to the stdlib, but has been updated to support
+# queries with parameters (that are safer to counter SQL injections) and
+# prepared queries.
+# It will be merged eventually. For now, I included it in the repository.
+import lib/db_postgres_redone
+
+type THello = tuple[message: string]
+type TWorld = tuple[id: int, randomNumber: int]
+type TFortune* = tuple[id: int, message: string]
+
+proc unrowTWorld(x: TRow): TWorld =
+    result.id = parseInt(x[0])
+    result.randomNumber = parseInt(x[1])
+proc unrowTFortune(x: TRow): TFortune =
+    return (x[0].parseInt, x[1])
+
+import fortunes_tmpl  # Needs TFortune to be defined first
+
+var db = open("", "benchmarkdbuser", "benchmarkdbpass", "host=localhost port=5432 dbname=hello_world")
+
+const qworld = "SELECT id, randomNumber FROM World WHERE id = $1"
+const qfortunes = "SELECT id, message FROM Fortune"
+const qupdates = "UPDATE World SET randomNumber = $1 WHERE id = $2"
+
+# prepare queries
+let qworld_prepared = db.prepare("world", qworld, 1)
+let qfortunes_prepared = db.prepare("fortunes", qfortunes, 0)
+let qupdates_prepared = db.prepare("updates", qupdates, 2)
+
+
+get "/json":
+    var j: THello
+    j.message = "Hello, World!"
+    # jdump serialize any tuple as json
+    return response(jdump(j), "application/json")
+
+get "/plaintext":
+    return response("Hello, World!", "text/plain")
+
+
+get "/db":
+    let row = db.getPRow(qworld_prepared, random(10_000)+1)
+    var r = unrowTWorld(row)
+    return response(jdump(r), "application/json")
+
+#get "/db_unprepared":
+#    ## Yes, prepared queries are faster than unprepared ones
+#    var r = unrowTWorld( db.getRow(qworld, random(10_000) + 1) )
+#    return response(jdump(r), "application/json")
+
+get "/queries":
+    var queries = 1
+    if request.query.hasKey("queries"):
+        try:
+            queries = parseInt(request.query["queries"])
+        except EInvalidValue: discard
+        if queries < 1: queries = 1
+        elif queries > 500: queries = 500
+
+    var world: seq[TWorld]
+    world.newSeq(queries)
+    for i in 0.. <queries:
+        let row = db.getPRow(qworld_prepared, random(10_000) + 1)
+        world[i] = unrowTWorld(row)
+    return response(jdump(world), "application/json")
+
+get "/fortunes":
+    let rows = db.getAllPRows(qfortunes_prepared)
+    var fortunes: seq[TFortune]
+    fortunes.newSeq(rows.len)
+    for j, row in rows.pairs:
+        fortunes[j] = unrowTFortune(row)
+    let new_fortune: TFortune = (id: rows.len + 1,
+                                 message: "Additional fortune added at request time.")
+    fortunes.add new_fortune
+    sort(fortunes, proc(x, y: TFortune): int =
+        return cmp(x.message, y.message))
+
+    return response(fortunes_tmpl(fortunes), "text/html; charset=utf-8")
+
+get "/updates":
+    var queries = 1
+    if request.query.hasKey("queries"):
+        try:
+            queries = parseInt(request.query["queries"])
+        except EInvalidValue: discard
+        if queries < 1: queries = 1
+        elif queries > 500: queries = 500
+
+    var world: seq[TWorld]
+    world.newSeq(queries)
+    for i in 0.. <queries:
+        world[i] = unrowTWorld(db.getPRow(qworld_prepared, random(10_000) + 1))
+        world[i].randomNumber = random(10_000) + 1
+        db.Exec(qupdates_prepared, $world[i].randomNumber, $world[i].id)
+
+    return response(jdump(world), "application/json")
+
+
+run()

+ 37 - 0
nawak/setup.py

@@ -0,0 +1,37 @@
+import subprocess
+import sys
+import setup_util
+import os
+from os.path import expanduser
+
+home = expanduser("~")
+
+def start(args, logfile, errfile):
+  # compile the app
+  setup_util.replace_text("nawak/nawak_app.nim", "host=.* port=5432", "host=" + args.database_host + " port=5432")
+  subprocess.check_call("nimrod c -d:release --path:../installs/nawak/nawak nawak_app.nim",
+                        shell=True, cwd="nawak", stderr=errfile, stdout=logfile)
+  # launch mongrel2
+  subprocess.check_call("mkdir -p run logs tmp", shell=True, cwd="nawak/conf", stderr=errfile, stdout=logfile)
+  subprocess.check_call("m2sh load -config mongrel2.conf", shell=True, cwd="nawak/conf", stderr=errfile, stdout=logfile)
+  subprocess.check_call("sudo m2sh start -name test", shell=True, cwd="nawak/conf", stderr=errfile, stdout=logfile)
+  
+  for i in range(0, 2):
+    # launch workers
+    subprocess.Popen("./nawak_app " + str(i), shell=True, cwd="nawak", stderr=errfile, stdout=logfile)
+  return 0
+
+def stop(logfile, errfile):
+  subprocess.check_call("sudo m2sh stop -every", shell=True, cwd="nawak/conf", stderr=errfile, stdout=logfile)
+
+  p = subprocess.Popen(['ps', 'aux'], stdout=subprocess.PIPE)
+  out, err = p.communicate()
+  for line in out.splitlines():
+    if 'nawak_app' in line:
+      try:
+        pid = int(line.split(None, 2)[1])
+        os.kill(pid, 9)
+      except OSError:
+        pass
+
+  return 0

+ 29 - 6
toolset/setup/linux/installer.py

@@ -185,12 +185,14 @@ class Installer:
     #
     # Nimrod
     #
-    self.__download("http://www.nimrod-code.org/download/nimrod_0.9.2.zip")
-    self.__run_command("unzip nimrod_0.9.2.zip")
-    self.__run_command("chmod +x build.sh", cwd="nimrod")
-    self.__run_command("./build.sh", cwd="nimrod")
-    self.__run_command("chmod +x install.sh", cwd="nimrod")
-    self.__run_command("sudo ./install.sh /usr/bin", cwd="nimrod")
+    self.__run_command("git clone git://github.com/Araq/Nimrod.git nimrod", retry=True)
+    self.__run_command("git reset --hard \"43d12ef7495238977e4f236f6d25bce5bd687967\"", cwd="nimrod")
+    self.__run_command("git clone --depth 1 git://github.com/nimrod-code/csources.git", cwd="nimrod", retry=True)
+    self.__run_command("./build.sh", cwd="nimrod/csources")
+    self.__run_command("bin/nimrod c koch", cwd="nimrod")
+    self.__run_command("./koch boot -d:release", cwd="nimrod")
+    self.__run_command("sudo ./koch install /usr/bin", cwd="nimrod")
+
 
     #
     # Racket
@@ -245,6 +247,22 @@ class Installer:
     self.__run_command("mv conf/resin.xml conf/resin.xml.orig", cwd="resin-4.0.36")
     self.__run_command("cat ../config/resin.xml > resin-4.0.36/conf/resin.xml")
 
+    #
+    # Mongrel2
+    #
+    self.__download("http://download.zeromq.org/zeromq-4.0.3.tar.gz")
+    self.__run_command("tar xzf zeromq-4.0.3.tar.gz")
+    self.__run_command("./configure", cwd="zeromq-4.0.3")
+    self.__run_command("make", cwd="zeromq-4.0.3")
+    self.__run_command("sudo make install", cwd="zeromq-4.0.3")
+    self.__run_command("sudo apt-get install sqlite3 libsqlite3-dev uuid uuid-runtime uuid-dev")
+    self.__run_command("sudo ldconfig -v")
+    self.__run_command("git clone git://github.com/zedshaw/mongrel2.git mongrel2", retry=True)
+    # for zmq4, we update the following file manually (not in the stable master branch yet)
+    self.__download("https://raw.github.com/zedshaw/mongrel2/9b565eeea003783c47502c2d350b99c9684ce97c/src/zmq_compat.h")
+    self.__run_command("mv -f zmq_compat.h mongrel2/src/")
+    self.__run_command("make clean all && sudo make install", cwd="mongrel2")
+
     ##############################################################
     # Frameworks
     ##############################################################
@@ -304,6 +322,11 @@ class Installer:
     #
     self.__run_command("git clone git://github.com/dom96/jester.git jester/jester", retry=True)
 
+    #
+    # nawak
+    #
+    self.__run_command("git clone git://github.com/idlewan/nawak.git nawak/nawak", retry=True)
+
     print("\nINSTALL: Finished installing server software\n")
   ############################################################
   # End __install_server_software