diff options
Diffstat (limited to 'lib')
-rwxr-xr-x | lib/impure/db_postgres.nim | 137 | ||||
-rwxr-xr-x | lib/impure/zipfiles.nim | 2 | ||||
-rwxr-xr-x | lib/pure/pegs.nim | 4 | ||||
-rwxr-xr-x | lib/pure/re.nim | 2 | ||||
-rwxr-xr-x | lib/system/profiler.nim | 8 | ||||
-rwxr-xr-x | lib/wrappers/postgres.nim | 2 |
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 |