Skip to content

Commit ea50ab0

Browse files
committed
Properly escape table names in model fetch
...
1 parent 8184377 commit ea50ab0

2 files changed

Lines changed: 58 additions & 9 deletions

File tree

Sources/ZeeQL/SQLite3Adaptor/SQLite3ModelFetch.swift

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -65,12 +65,19 @@ open class SQLite3ModelFetch: AdaptorModelFetch {
6565
// TBD: iterate on all returned describeDatabaseNames
6666
// (via dbname.sqlite_master)
6767
// ATTACH DATABASE 'DatabaseName' As 'Alias-Name';
68-
var sql = "SELECT name FROM sqlite_master WHERE type IN ('table', 'view')"
69-
if let like = like {
70-
sql += " AND name LIKE '" + like + "'"; // TODO: escape!
71-
}
7268
var names = [ String ]()
73-
try channel.select(sql) { ( name : String ) in names.append(name) }
69+
let expression = channel.expressionFactory.createExpression(nil)
70+
expression.statement =
71+
"SELECT name FROM sqlite_master WHERE type IN ('table', 'view')"
72+
if let like {
73+
expression.statement += " AND name LIKE ?"
74+
expression.bindVariables = [
75+
SQLExpression.BindVariable(attribute: nil, value: like)
76+
]
77+
}
78+
try channel.evaluateQueryExpression(expression, nil) { record in
79+
if let name = record[0] as? String { names.append(name) }
80+
}
7481
return names
7582
}
7683

@@ -89,17 +96,22 @@ open class SQLite3ModelFetch: AdaptorModelFetch {
8996

9097
func _fetchColumnsOfTable(_ table: String) throws -> [ AdaptorRecord ] {
9198
// keys: cid, name, type, notnull, dflt_value, pk
92-
let records : [ AdaptorRecord ] =
93-
try channel.querySQL("PRAGMA table_info(\(table))")
99+
let table = quotedIdentifier(table)
100+
let records = try channel.querySQL("PRAGMA table_info(\(table))")
94101
return records
95102
}
96103

97104
func _fetchForeignKeysOfTable(_ table: String) throws -> [ AdaptorRecord ] {
98105
// keys: id, seq, table, from, to, on_update, on_delete, match
99-
let records : [ AdaptorRecord ] =
100-
try channel.querySQL("PRAGMA foreign_key_list(\(table))")
106+
let table = quotedIdentifier(table)
107+
let records = try channel.querySQL("PRAGMA foreign_key_list(\(table))")
101108
return records
102109
}
110+
111+
private func quotedIdentifier(_ identifier: String) -> String {
112+
return channel.expressionFactory.createExpression(nil)
113+
.sqlStringFor(schemaObjectName: identifier)
114+
}
103115

104116
func primaryKeyNamesFromColumnInfos(_ columnInfos : [ AdaptorRecord ],
105117
_ attributes : [ Attribute ])

Tests/ZeeQLTests/SQLite3ModelTests.swift

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,39 @@ class SQLite3ModelTests: XCTestCase {
9797
XCTAssert(!tag4.isEqual(to: tag6))
9898
XCTAssert(!tag1.isEqual(to: tag6))
9999
}
100+
101+
func testDescribeTableNamesBindsLikePattern() throws {
102+
let channel = try SQLite3Adaptor(":memory:").openChannel()
103+
try channel.performSQL("CREATE TABLE normal(id INTEGER)")
104+
try channel.performSQL("CREATE TABLE \"odd'name\"(id INTEGER)")
105+
let fetch = SQLite3ModelFetch(channel: channel)
106+
107+
XCTAssertEqual(try fetch.describeTableNames(like: "odd'%"),
108+
[ "odd'name" ])
109+
XCTAssertTrue(
110+
try fetch.describeTableNames(like: "%' OR 1=1 --").isEmpty)
111+
}
112+
113+
func testDescribeEntityQuotesTableName() throws {
114+
let channel = try SQLite3Adaptor(":memory:").openChannel()
115+
try channel.performSQL(
116+
"CREATE TABLE \"parent table\"(\"id\" INTEGER PRIMARY KEY)")
117+
try channel.performSQL(
118+
"""
119+
CREATE TABLE "child)""table"(
120+
"parent id" INTEGER,
121+
FOREIGN KEY("parent id") REFERENCES "parent table"("id"))
122+
""")
123+
let fetch = SQLite3ModelFetch(channel: channel)
124+
125+
let entity = try fetch.describeEntityWithTableName("child)\"table")
126+
XCTAssertEqual(entity.externalName, "child)\"table")
127+
XCTAssertNotNil(entity[attribute: "parent id"])
128+
XCTAssertEqual(entity.relationships.count, 1)
129+
let relationship = try XCTUnwrap(
130+
entity.relationships[0] as? ModelRelationship)
131+
XCTAssertEqual(relationship.destinationEntityName, "parent table")
132+
}
100133

101134

102135
// MARK: - Non-ObjC Swift Support
@@ -106,5 +139,9 @@ class SQLite3ModelTests: XCTestCase {
106139
( "testDescribeOGoTableNames", testDescribeOGoTableNames ),
107140
( "testFetchModel", testFetchModel ),
108141
( "testSchemaTag", testSchemaTag ),
142+
( "testDescribeTableNamesBindsLikePattern",
143+
testDescribeTableNamesBindsLikePattern ),
144+
( "testDescribeEntityQuotesTableName",
145+
testDescribeEntityQuotesTableName ),
109146
]
110147
}

0 commit comments

Comments
 (0)