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

LDEV-3615 QoQ mishandles null and boolean column aliases #1397

Merged
merged 2 commits into from Oct 29, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
14 changes: 12 additions & 2 deletions core/src/main/java/lucee/runtime/db/QoQ.java
Expand Up @@ -270,7 +270,7 @@ private Query executeSingle(PageContext pc, Select select, Query source, Query p

// For a union all, we just slam all the rows together, keeping any duplicate record
if (isUnion && !select.isUnionDistinct()) {
return doUnionAll(previous, target);
return doUnionAll(previous, target, sql);
}
// If this is a select following a "union" or "union distinct", then everything gets
// distincted. Load up the partitions with all the existing rows in the target thus far
Expand All @@ -289,14 +289,19 @@ else if (isUnion && select.isUnionDistinct()) {
* @return Combined Query with potential duplicate rows
* @throws PageException
*/
private Query doUnionAll(Query previous, Query target) throws PageException {
private Query doUnionAll(Query previous, Query target, SQL sql) throws PageException {
// If this is the first select in a series of unions, just return it directly. It's column
// names now get set in stone as the column names the next union(s) will use!
if (previous.getRecordcount() == 0) {
return target;
}
Collection.Key[] previousColKeys = previous.getColumnNames();
Collection.Key[] targetColKeys = target.getColumnNames();

if( previousColKeys.length != targetColKeys.length ) {
throw new DatabaseException("Cannot perform union as number of columns in selects do not match.", null, sql, null);
}

// Queries being joined need to have the same number of columns and the data is full
// realized, so just copy it over positionally. The column names may not match, but that's
// fine.
Expand Down Expand Up @@ -327,6 +332,11 @@ private Query doUnionDistinct(PageContext pc, Query previous, Query target, SQL
}
Collection.Key[] previousColKeys = previous.getColumnNames();
Collection.Key[] targetColKeys = target.getColumnNames();

if( previousColKeys.length != targetColKeys.length ) {
throw new DatabaseException("Cannot perform union as number of columns in selects do not match.", null, sql, null);
}

Expression[] selectExpressions = new Expression[previousColKeys.length];
// We want the exact columns from the previous query, but not necessarily all the data. Make
// a new target and copy the columns
Expand Down
6 changes: 3 additions & 3 deletions core/src/main/java/lucee/runtime/sql/SelectParser.java
Expand Up @@ -560,9 +560,9 @@ private Expression column(ParserString raw) throws SQLParserException {
String name = identifier(raw, hb);
if (name == null) return null;
if (!hb.toBooleanValue()) {
if ("true".equalsIgnoreCase(name)) return ValueBoolean.TRUE;
if ("false".equalsIgnoreCase(name)) return ValueBoolean.FALSE;
if ("null".equalsIgnoreCase(name)) return ValueNull.NULL;
if ("true".equalsIgnoreCase(name)) return new ValueBoolean(true);
if ("false".equalsIgnoreCase(name)) return new ValueBoolean(false);
if ("null".equalsIgnoreCase(name)) return new ValueNull();
}

ColumnExpression column = new ColumnExpression(name, name.equals("?") ? columnIndex++ : 0);
Expand Down
Expand Up @@ -22,12 +22,9 @@

public class ValueBoolean extends ValueSupport implements Literal {

public static final ValueBoolean TRUE = new ValueBoolean(true);
public static final ValueBoolean FALSE = new ValueBoolean(false);

private boolean value;

private ValueBoolean(boolean value) {
public ValueBoolean(boolean value) {
super(value ? "TRUE" : "FALSE");
this.value = value;
}
Expand Down
Expand Up @@ -22,11 +22,7 @@

public class ValueNull extends ValueSupport implements Literal {

public static final ValueNull NULL = new ValueNull();

// private boolean value;

private ValueNull() {
public ValueNull() {
super("NULL");
}

Expand Down
60 changes: 59 additions & 1 deletion test/tickets/LDEV3615.cfc
@@ -1,4 +1,4 @@
component extends="org.lucee.cfml.test.LuceeTestCase" labels="qoq" skip=true {
component extends="org.lucee.cfml.test.LuceeTestCase" labels="qoq" {

function testQoQunion (){
var q1 = queryNew(
Expand Down Expand Up @@ -26,5 +26,63 @@ component extends="org.lucee.cfml.test.LuceeTestCase" labels="qoq" skip=true {
```
expect( serializeJson( local.result ) ).toBe('{"COLUMNS":["subtype","subject"],"DATA":[["RECORD3_TEMPLATE","RECORD3_TEMPLATE"],["",""]]}');
}

function testNullAliases (){
var q1 = queryNew( "col" )

```
<cfquery name="local.result" dbtype="query">
SELECT NULL AS a,
NULL AS b
FROM q1
</cfquery>

```
expect( local.result.columnList ).toBe('A,B');
}

function testNullNoAlias (){
var q1 = queryNew( "col" )

```
<cfquery name="local.result" dbtype="query">
SELECT NULL,
NULL
FROM q1
</cfquery>

```
expect( local.result.columnList ).toBe('COLUMN_0,COLUMN_1');
}

function testBooleanAliases (){
var q1 = queryNew( "col" )

```
<cfquery name="local.result" dbtype="query">
SELECT true AS a,
true AS b,
false AS c,
false AS d
FROM q1
</cfquery>

```
expect( local.result.columnList ).toBe('A,B,C,D');
}

function testBooleanNoAlias (){
var q1 = queryNew( "col" )

```
<cfquery name="local.result" dbtype="query">
SELECT true,
false
FROM q1
</cfquery>

```
expect( local.result.columnList ).toBe('COLUMN_0,COLUMN_1');
}

}