Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions ModuleConfig.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ component {
"validateQueryExecuteReturnType": false,
"collectQueryLog": true,
"convertEmptyStringsToNull": true,
"shouldWrapValues": true,
"validateQueryParamStructKeys": true,
"numericSQLType": "NUMERIC",
"integerSQLType": "INTEGER",
Expand Down Expand Up @@ -84,6 +85,13 @@ component {
.map( alias = "SchemaBuilder@qb", force = true )
.to( "qb.models.Schema.SchemaBuilder" )
.initArg( name = "grammar", ref = settings.defaultGrammar );

// Apply shouldWrapValues setting to the configured grammar singleton.
// When defaultGrammar is AutoDiscover@qb, the setting is forwarded via
// onMissingMethod to whatever concrete grammar AutoDiscover resolves at runtime.
if ( structKeyExists( settings, "shouldWrapValues" ) ) {
wirebox.getInstance( settings.defaultGrammar ).setShouldWrapValues( settings.shouldWrapValues );
}
}

}
35 changes: 28 additions & 7 deletions models/Grammars/AutoDiscover.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,53 @@ component singleton {

property name="wirebox" inject="wirebox";
property name="grammar";
property name="shouldWrapValues";

function autoDiscoverGrammar() {
cfdbinfo( type = "Version", name = "local.dbInfo" );

var discoveredGrammar = "";
switch ( dbInfo.DATABASE_PRODUCTNAME ) {
case "MySQL":
case "MariaDB":
return wirebox.getInstance( "MySQLGrammar@qb" );
discoveredGrammar = wirebox.getInstance( "MySQLGrammar@qb" );
break;
case "Derby":
return wirebox.getInstance( "DerbyGrammar@qb" );
discoveredGrammar = wirebox.getInstance( "DerbyGrammar@qb" );
break;
case "PostgreSQL":
return wirebox.getInstance( "PostgresGrammar@qb" );
discoveredGrammar = wirebox.getInstance( "PostgresGrammar@qb" );
break;
case "Microsoft SQL Server":
return wirebox.getInstance( "SQLServerGrammar@qb" );
discoveredGrammar = wirebox.getInstance( "SQLServerGrammar@qb" );
break;
case "Oracle":
return wirebox.getInstance( "OracleGrammar@qb" );
discoveredGrammar = wirebox.getInstance( "OracleGrammar@qb" );
break;
case "SQLite":
return wirebox.getInstance( "SQLiteGrammar@qb" );
discoveredGrammar = wirebox.getInstance( "SQLiteGrammar@qb" );
break;
default:
return wirebox.getInstance( "BaseGrammar@qb" );
discoveredGrammar = wirebox.getInstance( "BaseGrammar@qb" );
}

return discoveredGrammar;
}

public AutoDiscover function setShouldWrapValues( required boolean shouldWrapValues ) {
variables.shouldWrapValues = arguments.shouldWrapValues;
if ( !isNull( variables.grammar ) ) {
variables.grammar.setShouldWrapValues( arguments.shouldWrapValues );
}
return this;
}

function onMissingMethod( missingMethodName, missingMethodArguments ) {
if ( isNull( variables.grammar ) || !structKeyExists( variables, "grammar" ) ) {
variables.grammar = autoDiscoverGrammar();
if ( !isNull( variables.shouldWrapValues ) ) {
variables.grammar.setShouldWrapValues( variables.shouldWrapValues );
}
}
return invoke( variables.grammar, missingMethodName, missingMethodArguments );
}
Expand Down
91 changes: 91 additions & 0 deletions tests/specs/Query/ShouldWrapValuesSpec.cfc
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
component extends="testbox.system.BaseSpec" {

function run() {
describe( "shouldWrapValues setting", function() {
it( "does not eagerly discover a grammar when configured", function() {
var grammar = getMockBox().createMock( "qb.models.Grammars.PostgresGrammar" ).init();
var autoDiscover = getMockBox()
.createMock( "qb.models.Grammars.AutoDiscover" )
.$( "autoDiscoverGrammar", grammar );

autoDiscover.setShouldWrapValues( false );

expect( autoDiscover.$count( "autoDiscoverGrammar" ) ).toBe( 0 );
expect( autoDiscover.onMissingMethod( "wrapValue", { "value": "users" } ) ).toBe( "users" );
expect( autoDiscover.$count( "autoDiscoverGrammar" ) ).toBe( 1 );
expect( grammar.getShouldWrapValues() ).toBeFalse();
} );

it( "defaults to true in BaseGrammar", function() {
var utils = getMockBox().createMock( "qb.models.Query.QueryUtils" ).init();
var grammar = getMockBox().createMock( "qb.models.Grammars.PostgresGrammar" ).init( utils );

expect( grammar.getShouldWrapValues() ).toBeTrue( "shouldWrapValues should default to true" );
} );

it( "wraps identifiers in double quotes when shouldWrapValues is true", function() {
var utils = getMockBox().createMock( "qb.models.Query.QueryUtils" ).init();
var grammar = getMockBox().createMock( "qb.models.Grammars.PostgresGrammar" ).init( utils );
grammar.setShouldWrapValues( true );

var builder = getMockBox().createMock( "qb.models.Query.QueryBuilder" ).init( grammar );

var sql = builder
.from( "users" )
.select( "name" )
.toSQL();

expect( sql ).toBe( "SELECT ""name"" FROM ""users""" );

sql = builder
.from( "users" )
.select( "id" )
.where( "email", "test@test.com" )
.toSQL( withBindings = true );

expect( sql ).toBe( "SELECT ""id"" FROM ""users"" WHERE ""email"" = ?" );
} );

it( "does not wrap identifiers when shouldWrapValues is false", function() {
var utils = getMockBox().createMock( "qb.models.Query.QueryUtils" ).init();
var grammar = getMockBox().createMock( "qb.models.Grammars.PostgresGrammar" ).init( utils );
grammar.setShouldWrapValues( false );

var builder = getMockBox().createMock( "qb.models.Query.QueryBuilder" ).init( grammar );

var sql = builder
.from( "users" )
.select( "name" )
.toSQL();

expect( sql ).toBe( "SELECT name FROM users" );

sql = builder
.from( "users" )
.select( "id" )
.where( "email", "test@test.com" )
.toSQL();

expect( sql ).toBe( "SELECT id FROM users WHERE email = ?" );
} );

it( "per-query withoutWrappingValues overrides grammar default", function() {
var utils = getMockBox().createMock( "qb.models.Query.QueryUtils" ).init();
var grammar = getMockBox().createMock( "qb.models.Grammars.PostgresGrammar" ).init( utils );
grammar.setShouldWrapValues( true );

var builder = getMockBox().createMock( "qb.models.Query.QueryBuilder" ).init( grammar );

// Grammar default is true, but per-query override to false
var sql = builder
.withoutWrappingValues()
.from( "users" )
.select( "name" )
.toSQL();

expect( sql ).toBe( "SELECT name FROM users" );
} );
} );
}

}