From d4174e3d76fbf2e4d5cbd39ed989e4ace28e8956 Mon Sep 17 00:00:00 2001 From: Eric Peterson Date: Mon, 28 Nov 2016 14:12:18 -0700 Subject: [PATCH] Implement where exists --- models/Query/Builder.cfc | 40 +++++++++++++++++++++++++++++-- models/Query/Grammars/Grammar.cfc | 29 +++++++++++++++------- 2 files changed, 58 insertions(+), 11 deletions(-) diff --git a/models/Query/Builder.cfc b/models/Query/Builder.cfc index 2aca2e9a..ad1b1955 100644 --- a/models/Query/Builder.cfc +++ b/models/Query/Builder.cfc @@ -188,8 +188,10 @@ component displayname="Builder" accessors="true" { type = "basic" } ); - var binding = utils.extractBinding( arguments.value ); - arrayAppend( bindings.where, binding ); + if ( ! isInstanceOf( arguments.value, "Quick.models.Query.Expression" ) ) { + var binding = utils.extractBinding( arguments.value ); + arrayAppend( bindings.where, binding ); + } return this; } @@ -250,6 +252,40 @@ component displayname="Builder" accessors="true" { return whereColumn( argumentCollection = arguments ); } + public Builder function whereExists( callback, combinator = "and", negate = false ) { + var query = newQuery(); + callback( query ); + return addWhereExistsQuery( query, combinator, negate ); + } + + private Builder function addWhereExistsQuery( query, combinator, negate ) { + var type = negate ? "notExists" : "exists"; + variables.wheres.append( { + type = type, + query = arguments.query, + combinator = arguments.combinator + } ); + query.getBindings().each( function( binding ) { + variables.bindings.where.append( binding ); + } ); + return this; + } + + public Builder function orWhereExists( callback, negate = false ) { + arguments.combinator = "or"; + return whereExists( argumentCollection = arguments ); + } + + public Builder function whereNotExists( callback, combinator = "and" ) { + arguments.negate = true; + return whereExists( argumentCollection = arguments ); + } + public Builder function orWhereNotExists( callback ) { + arguments.combinator = "or"; + arguments.negate = true; + return whereExists( argumentCollection = arguments ); + } + private Builder function whereNested( required callback, combinator = "and" ) { var query = forNestedWhere(); callback( query ); diff --git a/models/Query/Grammars/Grammar.cfc b/models/Query/Grammars/Grammar.cfc index 5fea4db6..c481dcc8 100644 --- a/models/Query/Grammars/Grammar.cfc +++ b/models/Query/Grammars/Grammar.cfc @@ -12,7 +12,6 @@ component displayname="Grammar" accessors="true" { } public string function compileSelect( required Quick.models.Query.Builder query ) { - var sql = []; for ( var component in selectComponents ) { @@ -84,39 +83,51 @@ component displayname="Grammar" accessors="true" { return "WHERE #arrayToList( wheresArray, " " )#"; } - private function whereBasic( requried struct where, required Builder query ) { + private string function whereBasic( requried struct where, required Builder query ) { if ( ! isStruct( where ) ) { return; } - where.column = wrapValue( where.column ); + where.column = wrapColumn( where.column ); var placeholder = "?"; if ( where.operator == "in" || where.operator == "not in" ) { placeholder = "(#placeholder#)"; } + if ( isInstanceOf( where.value, "Quick.models.Query.Expression" ) ) { + placeholder = where.value.getSql(); + } + return "#where.column# #uCase( where.operator )# #placeholder#"; } - private function whereRaw( required struct where, required Builder query ) { + private string function whereRaw( required struct where, required Builder query ) { return where.sql; } - private function whereColumn( required struct where, required Builder query ) { + private string function whereColumn( required struct where, required Builder query ) { return "#wrapColumn( where.first )# #where.operator# #wrapColumn( where.second )#"; } - private function whereNested( required struct where, required Builder query ) { + private string function whereNested( required struct where, required Builder query ) { var sql = compileWheres( arguments.where.query, arguments.where.query.getWheres() ); // cut off the first 7 characters to account for the extra "WHERE" return "(#mid( sql, 7 )#)"; } - private function whereSub( required struct where, required Builder query ) { + private string function whereSub( required struct where, required Builder query ) { return "#wrapIdentifier( where.column )# #where.operator# (#compileSelect( where.query )#)"; } + private string function whereExists( required struct where, required Builder query ) { + return "EXISTS (#compileSelect( where.query )#)"; + } + + private string function whereNotExists( required struct where, required Builder query ) { + return "NOT EXISTS (#compileSelect( where.query )#)"; + } + private string function concatenate( required array sql ) { return arrayToList( arrayFilter( sql, function( item ) { return item != ""; @@ -130,13 +141,13 @@ component displayname="Grammar" accessors="true" { private string function wrapTable( required any table ) { var alias = ""; if ( table.find( " as " ) ) { - alias = table.listToArray( " as ", false, true )[ 2 ]; + alias = wrapTable( table.listToArray( " as ", false, true )[ 2 ] ); table = table.listToArray( " as ", false, true )[ 1 ]; } table = table.listToArray( "." ).map( function( tablePart, index ) { return wrapValue( index == 1 ? getTablePrefix() & tablePart : tablePart ); } ).toList( "." ); - return alias == "" ? table : table & " AS " & wrapTable( alias ); + return alias == "" ? table : table & " AS " & alias; } private string function wrapColumn( required any column ) {