diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 1fd3651c57..49dbe4ff10 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -15,6 +15,15 @@ on: - '**' # thus ignoring tagging pull_request: workflow_dispatch: + inputs: + LUCEE_BUILD_JAVA_VERSION: + required: true + type: string + default: '21' + LUCEE_TEST_JAVA_VERSION: + description: Optional Java Test version, default is the build java version + required: false + type: string #concurrency: # group: ${{ github.head_ref }} @@ -301,4 +310,4 @@ jobs: echo "Trigger Docker Build on Travis https://travis-ci.com/github/lucee/lucee-dockerfiles" chmod +x travis-docker-build.sh ./travis-docker-build.sh - \ No newline at end of file + diff --git a/core/src/main/java/lucee/runtime/config/ConfigAdmin.java b/core/src/main/java/lucee/runtime/config/ConfigAdmin.java index 034aee1580..f3868da538 100755 --- a/core/src/main/java/lucee/runtime/config/ConfigAdmin.java +++ b/core/src/main/java/lucee/runtime/config/ConfigAdmin.java @@ -980,7 +980,7 @@ public void removeRestMapping(String virtual) throws ExpressionException, Securi public void removeCustomTag(String virtual) throws SecurityException { checkWriteAccess(); - Array mappings = ConfigUtil.getAsArray("customTagMappings", root); + Array mappings = ConfigUtil.getAsArray(root, true, KeyConstants._virtual, KeyConstants._physical, false, "customTagMappings", "customTagPaths"); Key[] keys = mappings.keys(); Struct data; String v; @@ -1019,7 +1019,7 @@ private void _removeScheduledTask(String name) throws ExpressionException { public void removeComponentMapping(String virtual) throws SecurityException { checkWriteAccess(); - Array mappings = ConfigUtil.getAsArray("componentMappings", root); + Array mappings = ConfigUtil.getAsArray(root, true, KeyConstants._virtual, KeyConstants._physical, false, "componentMappings", "componentPaths"); Key[] keys = mappings.keys(); Struct data; String v; @@ -1070,7 +1070,7 @@ private void _updateCustomTag(String virtual, String physical, String archive, S throw new ExpressionException("physical must have a value when primary has value physical"); } - Array mappings = ConfigUtil.getAsArray("customTagMappings", root); + Array mappings = ConfigUtil.getAsArray(root, true, KeyConstants._virtual, KeyConstants._physical, false, "customTagMappings", "customTagPaths"); Key[] keys = mappings.keys(); // Update String v; @@ -1169,7 +1169,7 @@ private void _updateComponentMapping(String virtual, String physical, String arc throw new ExpressionException("physical must have a value when primary has value physical"); } - Array componentMappings = ConfigUtil.getAsArray("componentMappings", root); + Array componentMappings = ConfigUtil.getAsArray(root, true, KeyConstants._virtual, KeyConstants._physical, false, "componentMappings", "componentPaths"); Key[] keys = componentMappings.keys(); Struct el; diff --git a/core/src/main/java/lucee/runtime/config/ConfigFactoryImpl.java b/core/src/main/java/lucee/runtime/config/ConfigFactoryImpl.java index 9d1a6115c7..67d5ffab1e 100644 --- a/core/src/main/java/lucee/runtime/config/ConfigFactoryImpl.java +++ b/core/src/main/java/lucee/runtime/config/ConfigFactoryImpl.java @@ -2048,7 +2048,8 @@ private static void setDatasource(ConfigImpl config, Map dat public static Mapping[] loadCustomTagsMappings(ConfigImpl config, Struct root) { Mapping[] mappings = null; try { - Array ctMappings = ConfigUtil.getAsArray("customTagMappings", root); + Array ctMappings = ConfigUtil.getAsArray(root, true, KeyConstants._virtual, KeyConstants._physical, true, "customTagMappings", "customTagPaths"); + boolean hasDefault = false; // Web Mapping @@ -3509,7 +3510,7 @@ public static Mapping[] loadComponentMappings(ConfigImpl config, Struct root) { boolean hasSet = false; // Web Mapping - Array compMappings = ConfigUtil.getAsArray("componentMappings", root); + Array compMappings = ConfigUtil.getAsArray(root, true, KeyConstants._virtual, KeyConstants._physical, false, "componentMappings", "componentPaths"); hasSet = false; boolean hasDefault = false; if (compMappings.size() > 0) { diff --git a/core/src/main/java/lucee/runtime/config/ConfigUtil.java b/core/src/main/java/lucee/runtime/config/ConfigUtil.java index 4548161c84..336d134c09 100755 --- a/core/src/main/java/lucee/runtime/config/ConfigUtil.java +++ b/core/src/main/java/lucee/runtime/config/ConfigUtil.java @@ -943,6 +943,82 @@ public static Array getAsArray(String name, Struct sct) { return tmp; } + /** + * get an array that matches oe of the given key or creates the array in place + * + * @param input struct to look for the names + * @param convertStructToArray if true and the value is a struct, convert it to an array + * @param convertKey if the value is a struct the key of that strcut will be copied to the array + * with that name + * @param stringKey in case the array contains string values and this key is set, create a struct + * containing a key with the string as value + * @param names + * @return + * @throws PageException + */ + public static Array getAsArray(Struct input, boolean convertStructToArray, Key convertKey, Key stringKey, boolean replacePlaceHolder, String... names) { + Array arr = null; + if (input == null) return arr; + + Object obj; + for (String name: names) { + obj = input.get(KeyImpl.init(name), null); + if (obj instanceof Array && (arr = (Array) obj).size() > 0) { + if (name != names[0]) { + input.setEL(KeyImpl.init(names[0]), arr); + input.removeEL(KeyImpl.init(name)); + } + break; + } + if (arr == null && convertStructToArray && obj instanceof Struct) { + Struct sct = (Struct) obj; + arr = new ArrayImpl(); + input.setEL(KeyImpl.init(name), arr); + if (name != names[0]) { + input.setEL(KeyImpl.init(names[0]), arr); + input.removeEL(KeyImpl.init(name)); + } + Iterator> it = sct.entryIterator(); + Entry e; + Struct s; + Object v; + while (it.hasNext()) { + e = it.next(); + v = e.getValue(); + if (convertKey != null && v instanceof Struct) { + s = (Struct) v; + if (!s.containsKey(convertKey)) s.setEL(convertKey, e.getKey().getString()); + } + arr.appendEL(v); + } + break; + } + } + + // validate the array values + if (arr != null && stringKey != null) { + Key[] keys = arr.keys(); + Object val; + for (Key k: keys) { + val = arr.get(k, null); + if (val instanceof CharSequence) { + Struct sct = new StructImpl(); + sct.setEL(stringKey, val); + arr.setEL(k, sct); + } + } + } + else if (arr == null) { + arr = new ArrayImpl(); + input.setEL(KeyImpl.init(names[0]), arr); + return arr; + } + + if (replacePlaceHolder) return (Array) replaceConfigPlaceHolders(arr); + + return arr; + } + public static String getAsString(String name, Struct sct, String defaultValue) { if (sct == null) return defaultValue; Object obj = sct.get(KeyImpl.init(name), null); diff --git a/core/src/main/java/lucee/runtime/type/util/KeyConstants.java b/core/src/main/java/lucee/runtime/type/util/KeyConstants.java index 5648244cd1..6cf3720d4b 100644 --- a/core/src/main/java/lucee/runtime/type/util/KeyConstants.java +++ b/core/src/main/java/lucee/runtime/type/util/KeyConstants.java @@ -3007,6 +3007,7 @@ public class KeyConstants { public static final Key _stream = KeyImpl._const("stream"); public static final Key _temperature = KeyImpl._const("temperature"); public static final Key _purpose = KeyImpl._const("purpose"); + public static final Key _physical = KeyImpl._const("physical"); private static Map _____keys; diff --git a/core/src/main/java/resource/component/org/lucee/cfml/Query.cfc b/core/src/main/java/resource/component/org/lucee/cfml/Query.cfc index 671ad9bc89..6efed35407 100644 --- a/core/src/main/java/resource/component/org/lucee/cfml/Query.cfc +++ b/core/src/main/java/resource/component/org/lucee/cfml/Query.cfc @@ -27,7 +27,7 @@ component output="false" extends="HelperBase" accessors="true"{ if(structKeyExists(arguments,"sql") && len(arguments.sql)){ this.setSql(arguments.sql); - trace type="warning" var="arguments.sql"; + // trace type="warning" var="arguments.sql"; } //parse the sql into an array and save it diff --git a/loader/build.xml b/loader/build.xml index b46f4fc627..1a892742cc 100644 --- a/loader/build.xml +++ b/loader/build.xml @@ -2,7 +2,7 @@ - + diff --git a/loader/pom.xml b/loader/pom.xml index f2d56bd418..577475c465 100644 --- a/loader/pom.xml +++ b/loader/pom.xml @@ -3,7 +3,7 @@ org.lucee lucee - 7.0.0.74-SNAPSHOT + 7.0.0.75-SNAPSHOT jar Lucee Loader Build diff --git a/test/tickets/LDEV0224_1.cfc b/test/tickets/LDEV0224_1.cfc index 2d23623341..947450eda5 100644 --- a/test/tickets/LDEV0224_1.cfc +++ b/test/tickets/LDEV0224_1.cfc @@ -12,7 +12,7 @@ component extends="org.lucee.cfml.test.LuceeTestCase" labels="qoq"{ } function run( testResults , testBox ) { - describe( title='selecting 2 rows from QoQ' , body=function() { + //describe( title='selecting 2 rows from QoQ' , body=function() { describe( title='is possible using a hard coded list' , body=function() { it( title='of numerics' , body=function( currentSpec ) { var actual = QueryExecute( @@ -28,6 +28,7 @@ component extends="org.lucee.cfml.test.LuceeTestCase" labels="qoq"{ " ); expect( actual.RecordCount ).toBe( ListLen( interestingNumbersAsAList , ',' ) ); + expect( queryColumnData( actual, "id" ).toList() ).toBe( interestingNumbersAsAList ); }); it( title='of strings' , body=function( currentSpec ) { @@ -44,11 +45,12 @@ component extends="org.lucee.cfml.test.LuceeTestCase" labels="qoq"{ " ); expect( actual.RecordCount ).toBe( ListLen( interestingStringsAsAQuotedList , ',' ) ); + expect( queryColumnData( actual, "value" ).toList() ).toBe( interestingStringsAsAList ); }); }); - describe( title='using param list=true' , body=function() { - describe( title='with new Query()' , body=function() { + //describe( title='using param list=true' , body=function() { + describe( title='using param list=true, with new Query()' , body=function() { beforeEach( function( currentSpec ) { q = new Query( dbtype = 'query', @@ -64,9 +66,11 @@ component extends="org.lucee.cfml.test.LuceeTestCase" labels="qoq"{ value FROM queryWithDataIn WHERE id IN ( :needle ) + ORDER BY ID " ).getResult(); expect( actual.RecordCount ).toBe( ListLen( interestingNumbersAsAList , ',' ) ); + expect( queryColumnData( actual, "id" ).toList() ).toBe( interestingNumbersAsAList ); }); it( title='when using numeric params and a custom separator' , body=function( currentSpec ) { @@ -77,9 +81,11 @@ component extends="org.lucee.cfml.test.LuceeTestCase" labels="qoq"{ value FROM queryWithDataIn WHERE id IN ( :needle ) + ORDER BY ID " ).getResult(); expect( actual.RecordCount ).toBe( ListLen( interestingNumbersAsAList , ',' ) ); + expect( queryColumnData( actual, "id" ).toList() ).toBe( interestingNumbersAsAList ); }); it( title='when using string params' , body=function( currentSpec ) { @@ -90,12 +96,14 @@ component extends="org.lucee.cfml.test.LuceeTestCase" labels="qoq"{ value FROM queryWithDataIn WHERE value IN ( :needle ) + ORDER BY ID " ).getResult(); expect( actual.RecordCount ).toBe( ListLen( interestingStringsAsAList , ',' ) ); + expect( queryColumnData( actual, "value" ).toList() ).toBe( interestingStringsAsAList ); }); }); - describe( title='with query{} ( cfquery )' , body=function() { + describe( title='using param list=true, with query{} ( cfquery )' , body=function() { it( title='when using numeric params' , body=function( currentSpec ) { query name = 'actual' @@ -111,9 +119,12 @@ component extends="org.lucee.cfml.test.LuceeTestCase" labels="qoq"{ value = interestingNumbersAsAList sqltype = 'integer' list = true; - WriteOutput( " )" ); + WriteOutput( " ) + ORDER BY ID + " ); } expect( actual.RecordCount ).toBe( ListLen( interestingNumbersAsAList , ',' ) ); + expect( queryColumnData( actual, "id" ).toList() ).toBe( interestingNumbersAsAList ); }); it( title='when using numeric params and a custom separator' , body=function( currentSpec ) { @@ -132,9 +143,12 @@ component extends="org.lucee.cfml.test.LuceeTestCase" labels="qoq"{ sqltype = 'integer' list = true separator = '|'; - WriteOutput( " )" ); + WriteOutput( " ) + ORDER BY ID + " ); } expect( actual.RecordCount ).toBe( ListLen( interestingNumbersAsAList , ',' ) ); + expect( queryColumnData( actual, "id" ).toList() ).toBe( interestingNumbersAsAList ); }); it( title='when using string params' , body=function( currentSpec ) { @@ -152,14 +166,17 @@ component extends="org.lucee.cfml.test.LuceeTestCase" labels="qoq"{ value = interestingStringsAsAList sqltype = 'varchar' list = true; - WriteOutput( " )" ); + WriteOutput( " ) + ORDER BY ID + " ); } expect( actual.RecordCount ).toBe( ListLen( interestingStringsAsAList , ',' ) ); + expect( queryColumnData( actual, "value" ).toList() ).toBe( interestingStringsAsAList ); }); }); - describe( title='with QueryExecute' , body=function() { + describe( title='using param list=true, with QueryExecute' , body=function() { it( title='when using an array of numeric params' , body=function( currentSpec ) { var actual = QueryExecute( params = [ @@ -174,9 +191,11 @@ component extends="org.lucee.cfml.test.LuceeTestCase" labels="qoq"{ value FROM queryWithDataIn WHERE id IN ( :needle ) + ORDER BY ID " ); expect( actual.RecordCount ).toBe( ListLen( interestingNumbersAsAList , ',' ) ); + expect( queryColumnData( actual, "id" ).toList() ).toBe( interestingNumbersAsAList ); }); it( title='when using a struct of numeric params' , body=function( currentSpec ) { @@ -193,9 +212,11 @@ component extends="org.lucee.cfml.test.LuceeTestCase" labels="qoq"{ value FROM queryWithDataIn WHERE id IN ( :needle ) + ORDER BY ID " ); expect( actual.RecordCount ).toBe( ListLen( interestingNumbersAsAList , ',' ) ); + expect( queryColumnData( actual, "id" ).toList() ).toBe( interestingNumbersAsAList ); }); it( title='when using an array of string params' , body=function( currentSpec ) { @@ -212,9 +233,11 @@ component extends="org.lucee.cfml.test.LuceeTestCase" labels="qoq"{ value FROM queryWithDataIn WHERE value IN ( :needle ) + ORDER BY ID " ); expect( actual.RecordCount ).toBe( ListLen( interestingStringsAsAList , ',' ) ); + expect( queryColumnData( actual, "value" ).toList() ).toBe( interestingStringsAsAList ); }); it( title='when using a struct of string params' , body=function( currentSpec ) { @@ -231,9 +254,11 @@ component extends="org.lucee.cfml.test.LuceeTestCase" labels="qoq"{ value FROM queryWithDataIn WHERE value IN ( :needle ) + ORDER BY ID " ); expect( actual.RecordCount ).toBe( ListLen( interestingStringsAsAList , ',' ) ); + expect( queryColumnData( actual, "value" ).toList() ).toBe( interestingStringsAsAList ); }); it( title='when using numeric params and a custom separator' , body=function( currentSpec ) { @@ -250,12 +275,14 @@ component extends="org.lucee.cfml.test.LuceeTestCase" labels="qoq"{ value FROM queryWithDataIn WHERE id IN ( :needle ) + ORDER BY ID " ); expect( actual.RecordCount ).toBe( ListLen( interestingNumbersAsAList , ',' ) ); + expect( queryColumnData( actual, "id" ).toList() ).toBe( interestingNumbersAsAList ); }); }); - }); - }); +// }); + //}); } } \ No newline at end of file diff --git a/test/tickets/LDEV3188.cfc b/test/tickets/LDEV3188.cfc new file mode 100644 index 0000000000..4933a1799c --- /dev/null +++ b/test/tickets/LDEV3188.cfc @@ -0,0 +1,66 @@ +component extends = "org.lucee.cfml.test.LuceeTestCase" { + + + function beforeAll(){ + variables.preciseMath = getApplicationSettings().preciseMath; + variables.datasource = server.getDatasource( "h2", server._getTempDir( "ldev3188" ) ); + }; + + function afterAll(){ + application action="update" preciseMath=variables.preciseMath; + }; + + + function run( testResults, testBox ){ + describe( "Test case for LDEV3188", function(){ + it(title = "cfqueryparam sql integer types overflow, large number, preciseMath false", skip=true, body = function( currentSpec ){ + application action="update" preciseMath=false; + _test( false, "10000223372036854775807", true ); + _test( false, "10000223372036854775807", true ); + }); + + it(title = "cfqueryparam sql integer types overflow, large number, preciseMath true", skip=true, body = function( currentSpec ){ + application action="update" preciseMath=true; + _test( true, "10000223372036854775807", true ); + _test( true, "10000223372036854775807", true ); + }); + + it(title = "cfqueryparam sql integer types, preciseMath false", body = function( currentSpec ){ + application action="update" preciseMath=false; + _test( false, "123", false ); + _test( false, 123, false ); + }); + + it(title = "cfqueryparam sql integer types, small number, preciseMath true", body = function( currentSpec ){ + application action="update" preciseMath=true; + _test( true, "123", false ); + _test( true, 123, false ); + }); + }); + } + + private function _test( boolean precise, string num, boolean expectFail ){ + var sqltypes = [ "integer", "bigint", "tinyint", "smallint" ]; + + loop array=sqltypes index="local.type" { + var err = false; + try { + ``` + + select as result + + ``` + } catch ( e ){ + err = true; + } + if ( arguments.expectFail ){ + expect( err ).toBeTrue(); + expect( q.result ).toBe( arguments.num ); // should be unreachable + } else { + expect(err).toBeFalse(); + expect( q.result ).toBe( arguments.num ); + } + } + } + +}