Skip to content

Parameter order is sometimes wrong in SQLite #6607

@hediet

Description

@hediet

Thanks for creating and maintaining typeorm!

Issue type:

[x] bug report
[ ] feature request
[ ] documentation issue

Database system/driver:

[ ] cordova
[ ] mongodb
[ ] mssql
[ ] mysql / mariadb
[ ] oracle
[ ] postgres
[ ] cockroachdb
[x] sqlite
[ ] sqljs
[ ] react-native
[ ] expo

TypeORM version:

[x] latest
[ ] @next
[ ] 0.x.x (or put your version here)

Steps to reproduce or a small repository showing the problem:

I have this typescript query:

const result = await this.connection.versionLanguages
			.createQueryBuilder("lang")
			/* (1) */    .where({ owningVersionId: v.id })
			/* (2) */ // .where("lang.owningVersionId = :whatTheFuck", { whatTheFuck: v.id })
			.leftJoin("lang.translatedFormats", "translation")
			.leftJoin(
				"translation.owningFormat",
				"translatable",
				"NOT(translatable.isStale)"
			)
			.leftJoin(
				"translatable.translatedFormats",
				"defaultTranslation",
				"defaultTranslation.owningLanguageId = :defaultLang AND translation.acceptedTranslation NOT NULL",
				{ defaultLang: v.defaultLanguage.id }
			)
			.groupBy("languageCode")
			.select("languageCode")
			.addSelect("SUM(translation.words)", "translatedWords")
			.addSelect(
				"SUM(defaultTranslation.words)",
				"defaultTranslatedWords"
			)
			.getRawMany<{
				languageCode: string;
				translatedWords: number;
				defaultTranslatedWords: number;
			}>();

This query mixes up the parameters v.id and v.defaultLanguage.id. When sent to the db, the parameters end up in the wrong order i.e. the query is complete garbage.
When commenting out (1) and uncommenting (2), it works as expected.

Luckily, this query happened to be a select query and not a delete query where messing up condition parameters could delete really important stuff.

I guess the problem is here:

createParameter(parameterName: string, index: number): string {
    // return "$" + (index + 1);
    return "?";
    // return "$" + parameterName;
}

Sqlite parameters are positional. You cannot reorder a query after you created the parameters with this method.

This method is called here immediately when calling .where on the query builder:

this.expressionMap.nativeParameters[parameterName] = parameterValue;
parameterIndex++;
const parameter = this.connection.driver.createParameter(parameterName, parameterIndex - 1);
return `${aliasPath} = ${parameter}`;

This becomes a problem here, since joins are processed before where conditions, effectively swapping ? with ?:

getQuery(): string {
    let sql = this.createSelectExpression();
    sql += this.createJoinExpression();
    sql += this.createWhereExpression();
    sql += this.createGroupByExpression();
    sql += this.createHavingExpression();
    sql += this.createOrderByExpression();
    sql += this.createLimitOffsetExpression();
    sql += this.createLockExpression();
    sql = sql.trim();
    if (this.expressionMap.subQuery)
        sql = "(" + sql + ")";
    return sql;
}

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions