fix: all 5 bugs — reserved words, cloneForConnection, SQL injection, SHOW TABLES, getRowPlain
CI / test (push) Has been cancelled
CI / verify (push) Has been cancelled
Clients CI / build-server (push) Has been cancelled
Clients CI / test-python (push) Has been cancelled
Clients CI / test-javascript (push) Has been cancelled
Clients CI / test-nim (push) Has been cancelled
Clients CI / test-rust (push) Has been cancelled

This commit is contained in:
2026-05-21 22:51:04 +03:00
parent 837b4d56fc
commit b25fea4d21
6 changed files with 271 additions and 133 deletions
@@ -167,6 +167,48 @@ proc formatSql*(sql: string, args: JsonNode): string =
return formatSql(sql, arr)
# ================================================================================
# Parameterized query helpers (SQL injection prevention)
# ================================================================================
proc jsonToWireValue*(val: JsonNode): WireValue =
## Convert a JsonNode to a WireValue for parameterized queries.
case val.kind
of JNull:
WireValue(kind: fkNull)
of JBool:
WireValue(kind: fkBool, boolVal: val.getBool())
of JInt:
let i = val.getInt()
if i >= int(low(int32)) and i <= int(high(int32)):
WireValue(kind: fkInt32, int32Val: int32(i))
else:
WireValue(kind: fkInt64, int64Val: i)
of JFloat:
WireValue(kind: fkFloat64, float64Val: val.getFloat())
of JString:
WireValue(kind: fkString, strVal: val.getStr())
of JArray:
var elems: seq[WireValue] = @[]
for elem in val.getElems():
elems.add(jsonToWireValue(elem))
WireValue(kind: fkArray, arrayVal: elems)
of JObject:
WireValue(kind: fkJson, jsonVal: val.pretty())
proc placeholdersToWireValues*(placeHolder: JsonNode): seq[WireValue] =
## Convert the query builder placeholder array to WireValue sequence.
result = @[]
for arg in placeHolder.items:
result.add(jsonToWireValue(arg["value"]))
proc placeholdersToWireValuesRaw*(args: seq[JsonNode]): seq[WireValue] =
## Convert a raw seq[JsonNode] to WireValue sequence (for RawBaradbQuery).
result = @[]
for arg in args:
result.add(jsonToWireValue(arg))
# ================================================================================
# toJson
# ================================================================================
@@ -223,8 +265,11 @@ proc getAllRows(self: BaradbQuery, queryString: string): Future[seq[JsonNode]] {
if connI == errorConnectionNum:
raisePoolTimeout(self)
let sql = formatSql(queryString, self.placeHolder)
let qr = await self.pools.conns[connI].client.query(sql)
let params = placeholdersToWireValues(self.placeHolder)
let qr = if params.len > 0:
await self.pools.conns[connI].client.query(queryString, params)
else:
await self.pools.conns[connI].client.query(queryString)
if qr.rowCount == 0:
self.log.echoErrorMsg(queryString)
@@ -242,8 +287,11 @@ proc getAllRowsPlain(self: BaradbQuery, queryString: string, args: JsonNode): Fu
if connI == errorConnectionNum:
raisePoolTimeout(self)
let sql = formatSql(queryString, args)
let qr = await self.pools.conns[connI].client.query(sql)
let params = placeholdersToWireValues(args)
let qr = if params.len > 0:
await self.pools.conns[connI].client.query(queryString, params)
else:
await self.pools.conns[connI].client.query(queryString)
return qr.rows
@@ -257,8 +305,11 @@ proc getRow(self: BaradbQuery, queryString: string): Future[Option[JsonNode]] {.
if connI == errorConnectionNum:
raisePoolTimeout(self)
let sql = formatSql(queryString, self.placeHolder)
let qr = await self.pools.conns[connI].client.query(sql)
let params = placeholdersToWireValues(self.placeHolder)
let qr = if params.len > 0:
await self.pools.conns[connI].client.query(queryString, params)
else:
await self.pools.conns[connI].client.query(queryString)
if qr.rowCount == 0:
self.log.echoErrorMsg(queryString)
@@ -266,7 +317,7 @@ proc getRow(self: BaradbQuery, queryString: string): Future[Option[JsonNode]] {.
return toJson(qr)[0].some()
proc getRowPlain(self: BaradbQuery, queryString: string, args: JsonNode): Future[seq[string]] {.async.} =
proc getRowPlain(self: BaradbQuery, queryString: string, args: JsonNode): Future[Option[seq[string]]] {.async.} =
var connI = self.transactionConn
if not self.isInTransaction:
connI = getFreeConn(self).await
@@ -276,11 +327,14 @@ proc getRowPlain(self: BaradbQuery, queryString: string, args: JsonNode): Future
if connI == errorConnectionNum:
raisePoolTimeout(self)
let sql = formatSql(queryString, args)
let qr = await self.pools.conns[connI].client.query(sql)
let params = placeholdersToWireValues(args)
let qr = if params.len > 0:
await self.pools.conns[connI].client.query(queryString, params)
else:
await self.pools.conns[connI].client.query(queryString)
if qr.rows.len > 0:
return qr.rows[0]
return @[]
return some(qr.rows[0])
return none(seq[string])
proc getAllRows(self: RawBaradbQuery, queryString: string): Future[seq[JsonNode]] {.async.} =
@@ -296,8 +350,11 @@ proc getAllRows(self: RawBaradbQuery, queryString: string): Future[seq[JsonNode]
var arr: seq[JsonNode]
for arg in self.placeHolder.items:
arr.add(arg)
let sql = formatSql(queryString, arr)
let qr = await self.pools.conns[connI].client.query(sql)
let params = placeholdersToWireValuesRaw(arr)
let qr = if params.len > 0:
await self.pools.conns[connI].client.query(queryString, params)
else:
await self.pools.conns[connI].client.query(queryString)
if qr.rowCount == 0:
self.log.echoErrorMsg(queryString)
@@ -318,8 +375,11 @@ proc getAllRowsPlain(self: RawBaradbQuery, queryString: string, args: JsonNode):
var arr: seq[JsonNode]
for arg in args.items:
arr.add(arg)
let sql = formatSql(queryString, arr)
let qr = await self.pools.conns[connI].client.query(sql)
let params = placeholdersToWireValuesRaw(arr)
let qr = if params.len > 0:
await self.pools.conns[connI].client.query(queryString, params)
else:
await self.pools.conns[connI].client.query(queryString)
return qr.rows
@@ -336,8 +396,11 @@ proc getRow(self: RawBaradbQuery, queryString: string): Future[Option[JsonNode]]
var arr: seq[JsonNode]
for arg in self.placeHolder.items:
arr.add(arg)
let sql = formatSql(queryString, arr)
let qr = await self.pools.conns[connI].client.query(sql)
let params = placeholdersToWireValuesRaw(arr)
let qr = if params.len > 0:
await self.pools.conns[connI].client.query(queryString, params)
else:
await self.pools.conns[connI].client.query(queryString)
if qr.rowCount == 0:
self.log.echoErrorMsg(queryString)
@@ -345,7 +408,7 @@ proc getRow(self: RawBaradbQuery, queryString: string): Future[Option[JsonNode]]
return toJson(qr)[0].some()
proc getRowPlain(self: RawBaradbQuery, queryString: string, args: JsonNode): Future[seq[string]] {.async.} =
proc getRowPlain(self: RawBaradbQuery, queryString: string, args: JsonNode): Future[Option[seq[string]]] {.async.} =
var connI = self.transactionConn
if not self.isInTransaction:
connI = getFreeConn(self).await
@@ -358,11 +421,14 @@ proc getRowPlain(self: RawBaradbQuery, queryString: string, args: JsonNode): Fut
var arr: seq[JsonNode]
for arg in args.items:
arr.add(arg)
let sql = formatSql(queryString, arr)
let qr = await self.pools.conns[connI].client.query(sql)
let params = placeholdersToWireValuesRaw(arr)
let qr = if params.len > 0:
await self.pools.conns[connI].client.query(queryString, params)
else:
await self.pools.conns[connI].client.query(queryString)
if qr.rows.len > 0:
return qr.rows[0]
return @[]
return some(qr.rows[0])
return none(seq[string])
proc exec(self: BaradbQuery, queryString: string) {.async.} =
@@ -375,8 +441,11 @@ proc exec(self: BaradbQuery, queryString: string) {.async.} =
if connI == errorConnectionNum:
raisePoolTimeout(self)
let sql = formatSql(queryString, self.placeHolder)
discard await self.pools.conns[connI].client.exec(sql)
let params = placeholdersToWireValues(self.placeHolder)
if params.len > 0:
discard await self.pools.conns[connI].client.query(queryString, params)
else:
discard await self.pools.conns[connI].client.exec(queryString)
proc exec(self: RawBaradbQuery, queryString: string, args: JsonNode) {.async.} =
@@ -392,8 +461,11 @@ proc exec(self: RawBaradbQuery, queryString: string, args: JsonNode) {.async.} =
var arr: seq[JsonNode]
for arg in args.items:
arr.add(arg)
let sql = formatSql(queryString, arr)
discard await self.pools.conns[connI].client.exec(sql)
let params = placeholdersToWireValuesRaw(arr)
if params.len > 0:
discard await self.pools.conns[connI].client.query(queryString, params)
else:
discard await self.pools.conns[connI].client.exec(queryString)
proc insertId(self: BaradbQuery, queryString: string, key: string): Future[string] {.async.} =
@@ -406,8 +478,12 @@ proc insertId(self: BaradbQuery, queryString: string, key: string): Future[strin
if connI == errorConnectionNum:
raisePoolTimeout(self)
let sql = formatSql(queryString, self.placeHolder) & &" RETURNING `{key}`"
let qr = await self.pools.conns[connI].client.query(sql)
let sql = queryString & &" RETURNING `{key}`"
let params = placeholdersToWireValues(self.placeHolder)
let qr = if params.len > 0:
await self.pools.conns[connI].client.query(sql, params)
else:
await self.pools.conns[connI].client.query(sql)
if qr.rowCount > 0 and qr.rows[0].len > 0:
return qr.rows[0][0]
return ""
@@ -423,11 +499,11 @@ proc getColumns(self: BaradbQuery, queryString: string, args = newJArray()): Fut
if connI == errorConnectionNum:
raisePoolTimeout(self)
var arr: seq[JsonNode]
for arg in args.items:
arr.add(arg["value"])
let sql = formatSql(queryString, arr)
let qr = await self.pools.conns[connI].client.query(sql)
let params = placeholdersToWireValues(args)
let qr = if params.len > 0:
await self.pools.conns[connI].client.query(queryString, params)
else:
await self.pools.conns[connI].client.query(queryString)
if qr.rowCount > 0:
return qr.rows[0]
return @[]
@@ -517,7 +593,9 @@ proc firstPlain*(self: BaradbQuery): Future[seq[string]] {.async.} =
let sql = self.selectFirstBuilder()
try:
self.log.logger(sql)
return await self.getRowPlain(sql, self.placeHolder)
let row = await self.getRowPlain(sql, self.placeHolder)
if row.isSome: return row.get()
return @[]
except CatchableError:
self.log.echoErrorMsg(sql)
self.log.echoErrorMsg(getCurrentExceptionMsg())
@@ -529,7 +607,9 @@ proc findPlain*(self: BaradbQuery, id: string, key = "id"): Future[seq[string]]
let sql = self.selectFindBuilder(key)
try:
self.log.logger(sql)
return await self.getRowPlain(sql, self.placeHolder)
let row = await self.getRowPlain(sql, self.placeHolder)
if row.isSome: return row.get()
return @[]
except CatchableError:
self.log.echoErrorMsg(sql)
self.log.echoErrorMsg(getCurrentExceptionMsg())
@@ -10,15 +10,25 @@ import ../../../query_builder/models/baradb/baradb_query
import ../../../query_builder/models/baradb/baradb_exec
proc getTableInfo(rdb: BaradbConnections): Future[Table[string, seq[tuple[name: string, typ: string]]]] {.async.} =
## Introspect BaraDB tables using native SHOW TABLES / SHOW COLUMNS commands.
var tablesInfo = initTable[string, seq[tuple[name: string, typ: string]]]()
let tables = await rdb.raw("SELECT table_name FROM information_schema.tables WHERE table_schema = 'public'").get()
# SHOW TABLES — get all table names from the server
let tables = await rdb.raw("SHOW TABLES").get()
for table in tables:
let tableName = table["table_name"].getStr()
let query = "SELECT column_name, data_type FROM information_schema.columns WHERE table_name = ? ORDER BY ordinal_position"
let columns = await rdb.raw(query, %*[tableName]).get()
let tableName = table["name"].getStr()
# Skip internal schema tables
if tableName.startsWith("_") and tableName != "_allographer_migrations":
continue
# SHOW COLUMNS FROM — get column definitions for this table
let descQuery = "SHOW COLUMNS FROM `" & tableName & "`"
let columns = await rdb.raw(descQuery).get()
var columnInfo: seq[tuple[name: string, typ: string]]
for col in columns:
columnInfo.add((name: col["column_name"].getStr(), typ: col["data_type"].getStr()))
let colName = col["column_name"].getStr()
let colType = col["data_type"].getStr()
columnInfo.add((name: colName, typ: colType))
tablesInfo[tableName] = columnInfo
return tablesInfo
@@ -33,15 +43,18 @@ proc generateSchemaCode(tablesInfo: Table[string, seq[tuple[name: string, typ: s
for col in columns:
let nimType =
case col.typ.toLower()
of "smallint", "integer", "bigint", "serial":
of "smallint", "integer", "bigint", "serial", "int", "int8", "int16", "int32", "int64":
"int"
of "character", "character varying", "text", "date", "timestamp without time zone", "time without time zone", "bytea", "varchar":
of "character", "character varying", "text", "date",
"timestamp without time zone", "time without time zone", "bytea",
"varchar", "string", "fkstring":
"string"
of "boolean":
of "boolean", "bool":
"bool"
of "numeric", "double precision", "real":
of "numeric", "double precision", "real", "float", "float32", "float64",
"double":
"float"
of "json", "jsonb":
of "json", "jsonb", "jsonnode":
"JsonNode"
else:
"string"