Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
29 changes: 21 additions & 8 deletions DBTool/C_Database.pas
Original file line number Diff line number Diff line change
Expand Up @@ -2365,30 +2365,43 @@ function TDbToolDatabase.SQL_Escape_TableName(sTableName: String): string;
case FDatabaseType of
dtSqlServer:
begin
// SQL Server uses [name] quoting; escape ] by doubling it
result := '';
ary := SplitString(sTableName, '.');
for i := 0 to Length(ary) - 1 do
begin
if i <> 0 then
result := result + '.';
ary[i] := StringReplace(ary[i], '[', '[[]', [rfReplaceAll]);
// TODO: Geht nicht... deshalb d�rfen Tabellennamen vorerst keine Klammern haben
ary[i] := StringReplace(ary[i], ']', ']]', [rfReplaceAll]);
result := result + '[' + ary[i] + ']';
end;
end;

dtMySql:
begin
result := '`' + sTableName + '`';
// MySQL uses backtick quoting; escape backticks by doubling them
result := '`' + StringReplace(sTableName, '`', '``', [rfReplaceAll]) + '`';
end;

{$IFNDEF WIN64}
dtLocal, // Nicht getestet Unbekannt, ob es Escaping gibt.
dtLocal:
begin
// BDE/Paradox/dBase: use square bracket quoting like Access
result := '[' + StringReplace(sTableName, ']', ']]', [rfReplaceAll]) + ']';
end;
{$ENDIF}
dtInterbase, // Nicht getestet Unbekannt, ob es Escaping gibt.
dtFirebird, // Nicht getestet Unbekannt, ob es Escaping gibt.
dtAccess: // Nicht getestet. Unbekannt, ob es Escaping gibt.
result := sTableName;
dtInterbase,
dtFirebird:
begin
// InterBase/Firebird use SQL-standard double-quote identifier quoting
result := '"' + StringReplace(sTableName, '"', '""', [rfReplaceAll]) + '"';
end;

dtAccess:
begin
// Access uses square bracket quoting; escape ] by doubling it
result := '[' + StringReplace(sTableName, ']', ']]', [rfReplaceAll]) + ']';
end;
else
raise Exception.Create('(TDbToolDatabase.SQL_Escape_TableName) ' +
SInternalError);
Expand Down
142 changes: 142 additions & 0 deletions Tests/TestEscapeTableNameIntegration.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
#!/bin/bash
# Integration test: verify table name escaping against real databases
set -euo pipefail

PASS=0
FAIL=0
TMPDIR=$(mktemp -d)

assert_eq() {
local test_name="$1" expected="$2" actual="$3"
if [ "$expected" = "$actual" ]; then
echo "[PASS] $test_name"
((PASS++)) || true
else
echo "[FAIL] $test_name"
echo " Expected: '$expected'"
echo " Actual: '$actual'"
((FAIL++)) || true
fi
}

assert_contains() {
local test_name="$1" needle="$2" haystack="$3"
if echo "$haystack" | grep -qi "$needle"; then
echo "[PASS] $test_name"
((PASS++)) || true
else
echo "[FAIL] $test_name"
echo " Expected to contain: '$needle'"
echo " Actual: '$haystack'"
((FAIL++)) || true
fi
}

fb_exec() {
docker exec -i firebird-test /usr/local/firebird/bin/isql \
-user SYSDBA -password masterkey localhost:/firebird/data/testdb 2>&1
}

my_file() {
docker cp "$1" mysql-test:/tmp/query.sql
docker exec mysql-test mariadb -uroot -ptestpass testdb -sN -e "source /tmp/query.sql" 2>&1
}

echo "=== Firebird: Table Name Escaping ==="
echo "Waiting for Firebird..."
for i in $(seq 1 30); do
if echo "SELECT 1 FROM RDB\$DATABASE;" | fb_exec > /dev/null 2>&1; then
echo "Firebird ready"; break
fi; sleep 2
done

# FB1: Double-quoted identifier works
fb_exec << 'SQL'
CREATE TABLE "My Table" (id INTEGER);
INSERT INTO "My Table" VALUES (1);
COMMIT;
SQL
result=$(echo 'SELECT id FROM "My Table";' | fb_exec | tr -s ' ' | grep "1" | sed "s/^ *//;s/ *$//" | head -1)
assert_eq "Firebird: double-quoted table name with space works" "1" "$result"

# FB2: Double-quote inside identifier escaped with ""
fb_exec << 'SQL'
CREATE TABLE "My""Table" (id INTEGER);
INSERT INTO "My""Table" VALUES (2);
COMMIT;
SQL
result=$(echo 'SELECT id FROM "My""Table";' | fb_exec | tr -s ' ' | grep "2" | sed "s/^ *//;s/ *$//" | head -1)
assert_eq "Firebird: escaped double-quote in table name works" "2" "$result"

# FB3: Unquoted table name with special chars must fail
fb3_result=$(echo 'CREATE TABLE My Table (id INTEGER);' | fb_exec 2>&1 || true)
assert_contains "Firebird: unquoted name with space rejected" "error\|token\|unknown" "$fb3_result"

echo ""
echo "=== MariaDB: Table Name Escaping ==="
echo "Waiting for MariaDB..."
for i in $(seq 1 30); do
if docker exec mysql-test mariadb -uroot -ptestpass testdb -e "SELECT 1;" > /dev/null 2>&1; then
echo "MariaDB ready"; break
fi; sleep 2
done

# MY1: Backtick-quoted identifier works
cat > "$TMPDIR/my1.sql" << 'SQLEOF'
CREATE TABLE `My Table` (id INT);
INSERT INTO `My Table` VALUES (1);
SQLEOF
my_file "$TMPDIR/my1.sql"
result=$(docker exec mysql-test mariadb -uroot -ptestpass testdb -sN -e "SELECT id FROM \`My Table\`;")
assert_eq "MySQL: backtick-quoted table name with space works" "1" "$result"

# MY2: Backtick inside identifier escaped with ``
cat > "$TMPDIR/my2.sql" << 'SQLEOF'
CREATE TABLE `My``Table` (id INT);
INSERT INTO `My``Table` VALUES (2);
SQLEOF
my_file "$TMPDIR/my2.sql"
cat > "$TMPDIR/my2q.sql" << 'SQLEOF'
SELECT id FROM `My``Table`;
SQLEOF
result=$(my_file "$TMPDIR/my2q.sql")
assert_eq "MySQL: escaped backtick in table name works" "2" "$result"

# MY3: Unquoted table name with space must fail
my3_result=$(docker exec mysql-test mariadb -uroot -ptestpass testdb -e "CREATE TABLE My Table (id INT);" 2>&1 || true)
assert_contains "MySQL: unquoted name with space rejected" "error\|ERROR" "$my3_result"

echo ""
echo "=== SQL Server: Table Name Escaping ==="
echo "Waiting for SQL Server..."
SQLCMD="docker exec mssql-test /opt/mssql-tools18/bin/sqlcmd -S localhost -U sa -P TestPass123! -C -d tempdb"
for i in $(seq 1 30); do
if $SQLCMD -Q "SELECT 1" > /dev/null 2>&1; then
echo "SQL Server ready"; break
fi; sleep 2
done

# MS1: Bracket-quoted identifier with space
$SQLCMD -Q "CREATE TABLE [My Table] (id INT); INSERT INTO [My Table] VALUES (1);" > /dev/null
result=$($SQLCMD -h -1 -Q "SET NOCOUNT ON; SELECT id FROM [My Table];" | tr -d ' ' | head -1)
assert_eq "SQL Server: bracket-quoted table name with space works" "1" "$result"

# MS2: ] escaped with ]]
$SQLCMD -Q "CREATE TABLE [My]]Table] (id INT); INSERT INTO [My]]Table] VALUES (2);" > /dev/null
result=$($SQLCMD -h -1 -Q "SET NOCOUNT ON; SELECT id FROM [My]]Table];" | tr -d ' ' | head -1)
assert_eq "SQL Server: escaped ] in table name works" "2" "$result"

# MS3: [ inside brackets needs NO escaping (only ] must be escaped)
$SQLCMD -Q "CREATE TABLE [My[Table] (id INT); INSERT INTO [My[Table] VALUES (3);" > /dev/null
result=$($SQLCMD -h -1 -Q "SET NOCOUNT ON; SELECT id FROM [My[Table];" | tr -d ' ' | head -1)
assert_eq "SQL Server: [ needs no escaping inside brackets" "3" "$result"

# MS4: Unquoted name with space must fail
ms4_result=$($SQLCMD -Q "CREATE TABLE My Table (id INT);" 2>&1 || true)
assert_contains "SQL Server: unquoted name with space rejected" "syntax\|error\|Msg" "$ms4_result"

echo ""
echo "=== Results: $((PASS + FAIL)) tests, $PASS passed, $FAIL failed ==="
rm -rf "$TMPDIR"

[ "$FAIL" -eq 0 ]
Loading