Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Escape column name #168

Open
wants to merge 15 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
- [t]—test suite improvement
- [d]—docs improvement


## 2.5.2 (WIP)

- [r] Logging: refactored `log` module to not trigger warnings when `normDebug` is not defined.
Expand Down
2 changes: 1 addition & 1 deletion src/norm/model.nim
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ func table*(T: typedesc[Model]): string =
func col*(T: typedesc[Model], fld: string): string =
## Get column name for a `Model`_ field, which is just the field name.

fld
"\"" & fld & "\""

func col*[T: Model](obj: T, fld: string): string =
## Get column name for a `Model`_ instance field.
Expand Down
6 changes: 3 additions & 3 deletions src/norm/postgres.nim
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ proc createTables*[T: Model](dbConn; obj: T) =
uniqueGroupCols.add obj.col(fld)

if val.isModel:
var fkGroup = "FOREIGN KEY($#) REFERENCES $#($#)" %
var fkGroup = """FOREIGN KEY($#) REFERENCES $#($#)""" %
[obj.col(fld), typeof(get val.model).table, typeof(get val.model).col("id")]

when obj.dot(fld).hasCustomPragma(onDelete):
Expand All @@ -123,9 +123,9 @@ proc createTables*[T: Model](dbConn; obj: T) =
const selfTableName = '"' & T.getCustomPragmaVal(tableName) & '"'
else:
const selfTableName = '"' & $T & '"'
fkGroups.add "FOREIGN KEY ($#) REFERENCES $#(id)" % [fld, selfTableName]
fkGroups.add """FOREIGN KEY ("$#") REFERENCES $#(id)""" % [fld, selfTableName]
else:
fkGroups.add "FOREIGN KEY ($#) REFERENCES $#(id)" % [fld, (obj.dot(fld).getCustomPragmaVal(fk)).table]
fkGroups.add """FOREIGN KEY ("$#") REFERENCES $#(id)""" % [fld, (obj.dot(fld).getCustomPragmaVal(fk)).table]
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had to add quotation marks here because otherwise the FK constraints wouldn't have any.
Please note that I ONLY added the quotation marks in these two lines.
There is a third line dealing with FK constraints, line 107 further up, where I DID NOT add quotation marks around the field, because when that line gets called by tests the field suddenly already has quotationmarks (possibly because it calls the col proc where the others don't).

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when that line gets called by tests the field suddenly already has quotationmarks (possibly because it calls the col proc where the others don't).

Oof, that's dangerous behavior. col proc should be called always.

Copy link
Collaborator Author

@PhilippMDoerner PhilippMDoerner Aug 23, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just tried it, replacing fld on there with obj.col(fld) does work.
I'm not sure if the code that generates is correct, as stated I haven't read through this code in any intensity in order to understand what's going on, but it does pass the tests.


colGroups.add colShmParts.join(" ")

Expand Down
34 changes: 17 additions & 17 deletions tests/common/tmodel.nim
Original file line number Diff line number Diff line change
Expand Up @@ -16,26 +16,26 @@ suite "Getting table and columns from Model":
pet = newPet("cat", toy)
person = newPerson("Alice", pet)

check person.col("name") == "name"
check pet.col("species") == "species"
check person.col("name") == "\"name\""
check pet.col("species") == "\"species\""

check person.cols == @["name", "pet"]
check person.cols(force = true) == @["name", "pet", "id"]
check person.cols == @["\"name\"", "\"pet\""]
check person.cols(force = true) == @["\"name\"", "\"pet\"", "\"id\""]

check person.fCol("name") == """"Person".name"""
check pet.fCol("species") == """"Pet".species"""
check person.fCol("name") == """"Person"."name""""
check pet.fCol("species") == """"Pet"."species""""

check person.rfCols == @[
""""Person".name""",
""""Person".pet""",
""""pet".species""",
""""pet".favToy""",
""""pet_favToy".price""",
""""pet_favToy".id""",
""""pet".id""",
""""Person".id"""
""""Person"."name"""",
""""Person"."pet"""",
""""pet"."species"""",
""""pet"."favToy"""",
""""pet_favToy"."price"""",
""""pet_favToy"."id"""",
""""pet"."id"""",
""""Person"."id""""
]
check toy.rfCols == @[""""Toy".price""", """"Toy".id"""]
check toy.rfCols == @[""""Toy"."price"""", """"Toy"."id""""]

test "Join groups":
let
Expand All @@ -44,8 +44,8 @@ suite "Getting table and columns from Model":
person = newPerson("Alice", pet)

check person.joinGroups == @[
(""""Pet"""", """"pet"""", """"Person".pet""", """"pet".id"""),
(""""Toy"""", """"pet_favToy"""", """"pet".favToy""", """"pet_favToy".id""")
(""""Pet"""", """"pet"""", """"Person"."pet"""", """"pet"."id""""),
(""""Toy"""", """"pet_favToy"""", """"pet"."favToy"""", """"pet_favToy"."id"""")
]

test "When related model has field with the type of the given model, expect name of that field as a string":
Expand Down
3 changes: 1 addition & 2 deletions tests/postgres/tdbtypes.nim
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import norm/[model, postgres, types]

import ../models


const
dbHost = "postgres"
dbUser = "postgres"
Expand Down Expand Up @@ -33,7 +32,7 @@ suite "Import dbTypes from norm/private/postgres/dbtypes":

test "dbValue[DateTime] is imported":
let users = @[newUser()].dup:
dbConn.select("""lastLogin <= $1""", ?now())
dbConn.select(""""lastLogin" <= $1""", ?now())
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An example for suddenly required quotation marks, this time not on an FK field as lastLogin is of type DateTime. Not sure what that is about.


check len(users) == 0

Expand Down
10 changes: 6 additions & 4 deletions tests/postgres/tfkpragma.nim
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import std/[unittest, times, strutils]
import std/[unittest, times, strutils, logging]

import norm/[model, postgres]

import ../models

addHandler(newConsoleLogger(levelThreshold = lvlDebug))


const
dbHost = "postgres"
Expand Down Expand Up @@ -41,8 +43,8 @@ suite "``fk`` pragma":
check customer.id > 0

let
userRows = dbConn.getAllRows(sql"""SELECT lastLogin, id FROM "User"""")
customerRows = dbConn.getAllRows(sql"""SELECT userId, email, id FROM "Customer"""")
userRows = dbConn.getAllRows(sql"""SELECT "lastLogin", "id" FROM "User" """)
customerRows = dbConn.getAllRows(sql"""SELECT "userId", "email", "id" FROM "Customer" """)

check userRows.len == 1
check userRows[0][1] == ?user.id
Expand Down Expand Up @@ -70,6 +72,6 @@ suite "``fk`` pragma":
for inpCustomer in inpCustomers.mitems:
dbConn.insert inpCustomer

dbConn.select(outCustomers, """"userid" = $1""", userA.id)
dbConn.select(outCustomers, """"userId" = $1""", userA.id)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An example for sudden case-sensitivity, though that is when comparing column names and not during actual queries, not sure how much that matters.


check outCustomers === inpCustomers[0..^2]
5 changes: 3 additions & 2 deletions tests/postgres/trows.nim
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import std/[unittest, with, strutils, sugar, options]


import norm/[model, postgres]

import ../models
Expand Down Expand Up @@ -67,7 +68,7 @@ suite "Row CRUD":

let
personRows = dbConn.getAllRows(sql"""SELECT name, pet, id FROM "Person"""")
petRows = dbConn.getAllRows(sql"""SELECT species, favToy, id FROM "Pet"""")
petRows = dbConn.getAllRows(sql"""SELECT species, "favToy", id FROM "Pet"""")
toyRows = dbConn.getAllRows(sql"""SELECT price, id FROM "Toy"""")

check personRows.len == 1
Expand Down Expand Up @@ -219,7 +220,7 @@ suite "Row CRUD":

let
personRow = get dbConn.getRow(sql"""SELECT name, pet, id FROM "Person" WHERE id = $1""", person.id)
petRow = get dbConn.getRow(sql"""SELECT species, favToy, id FROM "Pet" WHERE id = $1""", pet.id)
petRow = get dbConn.getRow(sql"""SELECT species, "favToy", id FROM "Pet" WHERE id = $1""", pet.id)
Copy link
Collaborator Author

@PhilippMDoerner PhilippMDoerner Aug 23, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An example for suddenly required quotation marks, necessary only on the FK field "favToy", not on the others.

Same for Line 71

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if you put the rest in quotations for consistency? Does it break the test?

This is so weird 🤔 Possibly, this has to do with mixing cases. If the column name uses mixed case, it must be quoted. But that's just a wild guess.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changing the line to this: sql"""SELECT "species", "favToy", "id" FROM "Pet" WHERE id = $1"""
Still works perfectly fine.

An observation I made though, is that this:
sql"""SELECT "sPecies", "favToy", "id" FROM "Pet" WHERE id = $1"""
Will cause the test to fail while this:
sql"""SELECT sPecies, "favToy", "id" FROM "Pet" WHERE id = $1"""
works perfectly fine.

I'm getting the impression that not only are suddenly the quotation marks sometimes mandatory, they cause the column names to suddenly be case-sensitive.

toyRow = get dbConn.getRow(sql"""SELECT price, id FROM "Toy" WHERE id = $1""", pet.favToy.id)

check personRow == @[?"Bob", ?pet.id, ?person.id]
Expand Down
4 changes: 2 additions & 2 deletions tests/postgres/ttables.nim
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ suite "Table creation":

check dbConn.getAllRows(qry, "FurnitureTable") == @[
@[?"id", ?"bigint"],
@[?"legcount", ?dftDbInt]
@[?"legCount", ?dftDbInt]
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An example for sudden case-sensitivity, though that is when comparing column names and not during actual queries, not sure how much that matters.

]

test "Create tables":
Expand All @@ -80,7 +80,7 @@ suite "Table creation":
]

check dbConn.getAllRows(qry, "Pet") == @[
@[?"favtoy", ?"bigint"],
@[?"favToy", ?"bigint"],
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An example for sudden case-sensitivity, though that is when comparing column names and not during actual queries, not sure how much that matters.

@[?"id", ?"bigint"],
@[?"species", ?"text"]
]
Expand Down