Skip to content

Commit

Permalink
feat(QueryBuilder): Add helper for CONCAT function
Browse files Browse the repository at this point in the history
The CONCAT function can be different between grammars (specifically
the SQLite grammar), so this function servers to build the correct
string to CONCAT for libraries like Quick.
  • Loading branch information
elpete committed Feb 6, 2023
1 parent 98fcc76 commit febe28a
Show file tree
Hide file tree
Showing 7 changed files with 75 additions and 2 deletions.
4 changes: 4 additions & 0 deletions models/Grammars/BaseGrammar.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,10 @@ component displayname="Grammar" accessors="true" singleton {
return select & columns.map( wrapColumn ).toList( ", " );
}

public string function compileConcat( required string alias, required array items ) {
return "CONCAT(#arrayToList( items )#) AS #wrapAlias( alias )#";
}

/**
* Compiles the table portion of a sql statement.
*
Expand Down
29 changes: 27 additions & 2 deletions models/Query/QueryBuilder.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -3490,6 +3490,22 @@ component displayname="QueryBuilder" accessors="true" {
return new qb.models.Query.Expression( arguments.sql, arguments.bindings );
}

/**
* Wraps up items into a CONCAT expression.
* This is provided since different engines have different syntax for CONCAT.
*
* @alias The alias for the CONCAT expression
* @items The items in the CONCAT expression, either a list or an array.
*
* @return qb.models.Query.Expression
*/
public Expression function concat( required string alias, required any items, array bindings = [] ) {
return new qb.models.Query.Expression(
variables.grammar.compileConcat( arguments.alias, arrayWrap( value = arguments.items, explodeList = true ) ),
arguments.bindings
);
}

/**
* Returns the Builder compiled to grammar-specific sql.
*
Expand Down Expand Up @@ -3683,8 +3699,17 @@ component displayname="QueryBuilder" accessors="true" {
* @doc_generic any
* @return [any]
*/
private array function arrayWrap( required any value ) {
return isArray( arguments.value ) ? arguments.value : [ arguments.value ];
private array function arrayWrap( required any value, boolean explodeList = false ) {
if ( isArray( arguments.value ) ) {
return arguments.value;
}

if ( arguments.explodeList ) {
// this handles lists with or without spaces after the comma
return arraySlice( arguments.value.split( ",\s*" ), 1 );
} else {
return [ arguments.value ];
}
}

/**
Expand Down
12 changes: 12 additions & 0 deletions tests/resources/AbstractQueryBuilderSpec.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,18 @@ component extends="testbox.system.BaseSpec" {
}, selectRawArray() );
} );

it( "provides a grammar-specific helper for concat", function() {
testCase( function( builder ) {
builder.select( builder.concat( "my_alias", "a,b,c,d" ) ).from( "users" );
}, selectConcat() );
} );

it( "concat can accept an array of values", function() {
testCase( function( builder ) {
builder.select( builder.concat( "my_alias", [ "a", "b", "c", "d" ] ) ).from( "users" );
}, selectConcatArray() );
} );

it( "can clear the selected columns for a query", function() {
testCase( function( builder ) {
builder
Expand Down
8 changes: 8 additions & 0 deletions tests/specs/Query/MySQLQueryBuilderSpec.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,14 @@ component extends="tests.resources.AbstractQueryBuilderSpec" {
return "SELECT substr( foo, 6 ), trim( bar ) FROM `users`";
}

function selectConcat() {
return "SELECT CONCAT(a,b,c,d) AS `my_alias` FROM `users`";
}

function selectConcatArray() {
return "SELECT CONCAT(a,b,c,d) AS `my_alias` FROM `users`";
}

function clearSelect() {
return "SELECT * FROM `users`";
}
Expand Down
8 changes: 8 additions & 0 deletions tests/specs/Query/OracleQueryBuilderSpec.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,14 @@ component extends="tests.resources.AbstractQueryBuilderSpec" {
return "SELECT substr( foo, 6 ), trim( bar ) FROM ""USERS""";
}

function selectConcat() {
return "SELECT CONCAT(a,b,c,d) AS ""MY_ALIAS"" FROM ""USERS""";
}

function selectConcatArray() {
return "SELECT CONCAT(a,b,c,d) AS ""MY_ALIAS"" FROM ""USERS""";
}

function clearSelect() {
return "SELECT * FROM ""USERS""";
}
Expand Down
8 changes: 8 additions & 0 deletions tests/specs/Query/PostgresQueryBuilderSpec.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,14 @@ component extends="tests.resources.AbstractQueryBuilderSpec" {
return "SELECT substr( foo, 6 ), trim( bar ) FROM ""users""";
}

function selectConcat() {
return "SELECT CONCAT(a,b,c,d) AS ""my_alias"" FROM ""users""";
}

function selectConcatArray() {
return "SELECT CONCAT(a,b,c,d) AS ""my_alias"" FROM ""users""";
}

function clearSelect() {
return "SELECT * FROM ""users""";
}
Expand Down
8 changes: 8 additions & 0 deletions tests/specs/Query/SqlServerQueryBuilderSpec.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,14 @@ component extends="tests.resources.AbstractQueryBuilderSpec" {
return "SELECT substr( foo, 6 ), trim( bar ) FROM [users]";
}

function selectConcat() {
return "SELECT CONCAT(a,b,c,d) AS [my_alias] FROM [users]";
}

function selectConcatArray() {
return "SELECT CONCAT(a,b,c,d) AS [my_alias] FROM [users]";
}

function clearSelect() {
return "SELECT * FROM [users]";
}
Expand Down

0 comments on commit febe28a

Please sign in to comment.