Skip to content

Commit

Permalink
Merge pull request #76 from ortus-boxlang/development
Browse files Browse the repository at this point in the history
Beta 4
  • Loading branch information
jclausen authored Jul 5, 2024
2 parents 9a169ef + 996e6ea commit e6b9a05
Show file tree
Hide file tree
Showing 21 changed files with 520 additions and 226 deletions.
18 changes: 7 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ Visit us at [BoxLang.io Plans](https://boxlang.io/plans) for more information.

## JDK Targets

* JDK 21 LTS is our compiled code JDK Baseline
* **JDK 21 LTS** is our compiled code JDK Baseline.

## VSCode Snippets

Expand Down Expand Up @@ -116,24 +116,20 @@ Here is a listing of all of our core dependencies. If you add one, make sure yo

| Dependency | Version | License | Description |
|------------|---------|---------|-------------|
| [apache-commons-lang3](https://commons.apache.org/proper/commons-lang/) | 3.12.0 | Apache2 | Used for many utilities, class helpers and more |
| [apache-commons-lang3](https://commons.apache.org/proper/commons-lang/) | 3.14.0 | Apache2 | Used for many utilities, class helpers and more |
| [boxlang-compiler](https://github.com/ortus-solutions-private/boxlang-compiler) | 1.0.0 | Apache2 | The BoxLang Parser, Compiler, and ByteCode Generator |
| [caffeine](https://mvnrepository.com/artifact/com.github.ben-manes.caffeine/caffeine) | 3.1.8| Apache2 | Caching engine |
| [slf4j-api](https://mvnrepository.com/artifact/org.slf4j/slf4j-api) | 2.0.11 | MIT | API for SLF4J (The Simple Logging Facade for Java) |
| [slf4j-jdk14](https://mvnrepository.com/artifact/org.slf4j/slf4j-jdk14) | 2.0.11 | MIT | SLF4J JDK14 Provider |
| [slf4j-api](https://mvnrepository.com/artifact/org.slf4j/slf4j-api) | 2.0.13 | MIT | API for SLF4J (The Simple Logging Facade for Java) |

### Compiler

| Dependency | Version | License | Description |
|------------|---------|---------|-------------|
| [antlr4-runtime](https://mvnrepository.com/artifact/org.antlr/antlr4-runtime) | 4.12.0 | BSD 3-clause | ANTLR parser |
| [commons-cli](https://mvnrepository.com/artifact/commons-cli/commons-cli) | 1.5.0 | Apache 2 | Apache Commons CLI provides a simple API for presenting, processing and validating a Command Line Interface. |
| [commons-io](https://mvnrepository.com/artifact/commons-io/commons-io) | 2.13.0 | Apache 2 | The Apache Commons IO library contains utility classes, stream implementations, file filters, file comparators, endian transformation classes, and much more. |
| [commons-text](https://mvnrepository.com/artifact/org.apache.commons/commons-text) | 1.10.0 | Apache 2 | The Commons Text library provides additions to the standard JDK text handling. It includes algorithms for string similarity and for calculating the distance between strings. |
| [javaparser-symbol-solver-core](https://github.com/javaparser/javaparser) | 3.25.4 | Apache 2 | Java 1-17 Parser and Abstract Syntax Tree for Java with advanced analysis functionalities. |
| [kolasu-core](https://github.com/Strumenta/kolasu) | 1.5.24 | Apache 2 | Kotlin Language Support – AST Library |
| [slf4j-api](https://mvnrepository.com/artifact/org.slf4j/slf4j-api) | 2.0.11 | MIT | API for SLF4J (The Simple Logging Facade for Java) |
| [slf4j-jdk14](https://mvnrepository.com/artifact/org.slf4j/slf4j-jdk14) | 2.0.11 | MIT | SLF4J JDK14 Provider |
| [commons-io](https://mvnrepository.com/artifact/commons-io/commons-io) | 2.16.1 | Apache 2 | The Apache Commons IO library contains utility classes, stream implementations, file filters, file comparators, endian transformation classes, and much more. |
| [commons-text](https://mvnrepository.com/artifact/org.apache.commons/commons-text) | 1.12.0 | Apache 2 | The Commons Text library provides additions to the standard JDK text handling. It includes algorithms for string similarity and for calculating the distance between strings. |
| [javaparser-symbol-solver-core](https://github.com/javaparser/javaparser) | 3.25.10 | Apache 2 | Java 1-17 Parser and Abstract Syntax Tree for Java with advanced analysis functionalities. |
| [slf4j-api](https://mvnrepository.com/artifact/org.slf4j/slf4j-api) | 2.0.13 | MIT | API for SLF4J (The Simple Logging Facade for Java) |

## Contributing

Expand Down
2 changes: 1 addition & 1 deletion changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [1.0.0-beta2] - 2024-06-21

- <https://boxlang.ortusbooks.com/readme/release-history/1.0.0-beta2>
- Release Notes: <https://boxlang.ortusbooks.com/readme/release-history/1.0.0-beta2>

## [1.0.0-beta1] - 2024-06-14

Expand Down
4 changes: 2 additions & 2 deletions gradle.properties
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#Fri Jun 21 19:27:23 UTC 2024
#Fri Jun 28 10:39:21 UTC 2024
antlrVersion=4.13.1
jdkVersion=21
version=1.0.0-beta3
version=1.0.0-beta4
Original file line number Diff line number Diff line change
Expand Up @@ -468,9 +468,17 @@ private void enableOutput( List<BoxAnnotation> annotations ) {
*/
private void renameTopLevelVars( BoxIdentifier id ) {
String name = id.getName().toLowerCase();
if ( identifierMap.containsKey( name ) ) {
if ( identifierMap.containsKey( name ) && !isInFunctionWithArgNamed( id, name ) ) {
id.setName( identifierMap.get( name ) );
}
}

// Check if a node is inside of a function with an argument of the given name
private boolean isInFunctionWithArgNamed( BoxNode node, String name ) {
return node.getFirstAncestorOfType(
BoxFunctionDeclaration.class,
funcDec -> funcDec.getArgs().stream().anyMatch( a -> a.getName().equalsIgnoreCase( name ) )
) != null;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,8 @@ public Object _invoke( IBoxContext context, ArgumentsScope arguments ) {
ExecutedQuery executedQuery = pendingQuery.execute( connectionManager );

if ( options.wantsResultStruct() ) {
assert options.getResultVariableName() != null;
ExpressionInterpreter.setVariable( context, options.getResultVariableName(), executedQuery.getResultStruct() );
assert options.resultVariableName != null;
ExpressionInterpreter.setVariable( context, options.resultVariableName, executedQuery.getResultStruct() );
}

// Encapsulate this into the executed query
Expand Down
45 changes: 22 additions & 23 deletions src/main/java/ortus/boxlang/runtime/components/jdbc/Query.java
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,25 @@ public Query() {
declaredAttributes = new Attribute[] {
new Attribute( Key._NAME, "string" ),
new Attribute( Key.datasource, "string" ),
new Attribute( Key.returnType, "string", "query", Set.of(
Validator.valueRequires( "struct", Key.columnKey )
) ),
new Attribute( Key.columnKey, "string" ),

// connection options
new Attribute( Key.maxRows, "numeric", -1 ),
new Attribute( Key.blockfactor, "numeric", Set.of( Validator.min( 1 ), Validator.max( 100 ) ), Set.of() ),
new Attribute( Key.fetchSize, "numeric", Set.of( Validator.min( 1 ), Validator.max( 100 ) ) ),
new Attribute( Key.timeout, "numeric" ),

// cache options
new Attribute( Key.cache, "boolean", false ),
new Attribute( Key.cacheTimeout, "duration" ),
new Attribute( Key.cacheLastAccessTimeout, "duration" ),
new Attribute( Key.cacheKey, "string" ),
new Attribute( Key.cacheProvider, "string" ),

// UNIMPLEMENTED query options:
new Attribute( Key.timezone, "string", Set.of(
Validator.NOT_IMPLEMENTED
) ),
Expand All @@ -67,29 +86,13 @@ public Query() {
new Attribute( Key.password, "string", Set.of(
Validator.NOT_IMPLEMENTED
) ),
new Attribute( Key.maxRows, "numeric", -1 ),
new Attribute( Key.blockfactor, "numeric", Set.of( Validator.min( 1 ), Validator.max( 100 ) ), Set.of() ),
new Attribute( Key.fetchSize, "numeric", Set.of( Validator.min( 1 ), Validator.max( 100 ) ) ),
new Attribute( Key.timeout, "numeric" ),
new Attribute( Key.cachedAfter, "date", Set.of(
Validator.NOT_IMPLEMENTED
) ),
new Attribute( Key.cachedWithin, "numeric", Set.of(
Validator.NOT_IMPLEMENTED
) ),
new Attribute( Key.debug, "boolean", false, Set.of(
Validator.NOT_IMPLEMENTED
) ),
new Attribute( Key.result, "string" ),
new Attribute( Key.ormoptions, "struct", Set.of(
Validator.NOT_IMPLEMENTED
) ),
new Attribute( Key.cacheID, "string", Set.of(
Validator.NOT_IMPLEMENTED
) ),
new Attribute( Key.cacheRegion, "string", Set.of(
Validator.NOT_IMPLEMENTED
) ),
new Attribute( Key.clientInfo, "struct", Set.of(
Validator.NOT_IMPLEMENTED
) ),
Expand All @@ -101,11 +104,7 @@ public Query() {
) ),
new Attribute( Key.psq, "boolean", false, Set.of(
Validator.NOT_IMPLEMENTED
) ),
new Attribute( Key.returnType, "string", "query", Set.of(
Validator.valueRequires( "struct", Key.columnKey )
) ),
new Attribute( Key.columnKey, "string" )
) )
};

}
Expand Down Expand Up @@ -141,8 +140,8 @@ public BodyResult _invoke( IBoxContext context, IStruct attributes, ComponentBod
ExecutedQuery executedQuery = pendingQuery.execute( connectionManager );

if ( options.wantsResultStruct() ) {
assert options.getResultVariableName() != null;
ExpressionInterpreter.setVariable( context, options.getResultVariableName(), executedQuery.getResultStruct() );
assert options.resultVariableName != null;
ExpressionInterpreter.setVariable( context, options.resultVariableName, executedQuery.getResultStruct() );
}

String variableName = StringCaster.cast( attributes.getOrDefault( Key._NAME, "bxquery" ) );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,7 @@ public Connection getConnection( DataSource datasource ) {
*/
public Connection getConnection( QueryOptions options ) {
if ( options.wantsUsernameAndPassword() ) {
return getConnection( getDataSource( options ), options.getUsername(), options.getPassword() );
return getConnection( getDataSource( options ), options.username, options.password );
} else {
return getConnection( getDataSource( options ) );
}
Expand All @@ -312,8 +312,8 @@ public Connection getConnection( QueryOptions options ) {
* Determines the datasource to use according to the options and/or BoxLang Defaults
*/
public DataSource getDataSource( QueryOptions options ) {
if ( options.getDataSource() != null ) {
var datasourceObject = options.getDataSource();
if ( options.datasource != null ) {
var datasourceObject = options.datasource;
CastAttempt<IStruct> datasourceAsStruct = StructCaster.attempt( datasourceObject );

// ON THE FLY DATASOURCE
Expand Down
112 changes: 92 additions & 20 deletions src/main/java/ortus/boxlang/runtime/jdbc/ExecutedQuery.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.time.Duration;

import ortus.boxlang.runtime.BoxRuntime;
import ortus.boxlang.runtime.events.BoxEvent;
Expand Down Expand Up @@ -72,6 +73,43 @@ public final class ExecutedQuery {
*/
private Boolean isCached;

/**
* String name of the cache provider used to cache the query.
*/
private String cacheProvider;

/**
* Cache key used to store the query in the cache.
* <p>
* This key can be used in manual query cache manipulations, i.e. for invalidation:
*
* <pre>
* // Execute a query and cache the results
* var myQuery = queryExecute( "SELECT * FROM table", {}, { cache: true, result : "myQueryResult" } );
* // Clear the cache for this query from the default cache
* getBoxCache().clear( myQueryResult.cacheKey );
* // or
* </pre>
*/
private String cacheKey;

/**
* Max time the query will be cached for.
* <p>
* This must be populated with a timespan value using `createTimespan()`.
*/
private Duration cacheTimeout;

/**
* Max time to wait for a cache to be accessed before it is considered stale and automatically removed from the BoxLang cache.
* <p>
* This must be populated with a timespan value using `createTimespan()`.
* <p>
* Consider a query with the following query options: `{ cache: true, cacheTimeout: createTimespan( 0, 0, 10, 0 ), cacheLastAccessTimeout: createTimespan( 0, 0, 1, 0 ) }`. This query has a 10-minute cache timeout, so after 10 minutes of intermittent
* use it will be removed from the cache. The `cacheLastAccessTimeout` is set to 1 minute, so if the query is not accessed for 1 minute, it will be removed from the cache.
*/
private Duration cacheLastAccessTimeout;

/**
* Creates an ExecutedQuery instance.
*
Expand Down Expand Up @@ -195,13 +233,6 @@ public int getRecordCount() {
return this.results.size();
}

/**
* Returns true if the query was cached.
*/
public Boolean getIsCached() {
return this.isCached;
}

/**
* Sets the query as cached.
* <p>
Expand All @@ -212,32 +243,73 @@ public ExecutedQuery setIsCached() {
return this;
}

/**
* Set the cache provider used to cache the query.
*/
public ExecutedQuery setCacheProvider( String cacheProvider ) {
this.cacheProvider = cacheProvider;
return this;
}

/**
* Set the cache key which uniquely identifies this query in the cache.
*/
public ExecutedQuery setCacheKey( String cacheKey ) {
this.cacheKey = cacheKey;
return this;
}

/**
* Set the cache timeout for the query.
*/
public ExecutedQuery setCacheTimeout( Duration cacheTimeout ) {
this.cacheTimeout = cacheTimeout;
return this;
}

/**
* Set the cache last access timeout for the query.
*/
public ExecutedQuery setCacheLastAccessTimeout( Duration cacheLastAccessTimeout ) {
this.cacheLastAccessTimeout = cacheLastAccessTimeout;
return this;
}

/**
* Returns the `result` struct returned from `queryExecute` and `query`.
*
* @return A `result` struct
* <p>
* The struct contains the following keys:
*
* <ul>
* <li>SQL: The SQL statement that was executed. (string)
* <li>SqlParameters: An ordered Array of queryparam values. (array)
* <li>RecordCount: Total number of records in the query. (numeric)
* <li>ColumnList: Column list, comma separated. (string)
* <li>ExecutionTime: Execution time for the SQL request. (numeric)
* <li>GENERATEDKEY: If the query was an INSERT with an identity or auto-increment value the value of that ID is placed in this variable.
* <li>Cached: If the query was cached. (boolean)
* </ul>
*
* @return A struct of query metadata, like original SQL, parameters, size, and cache info.
*/
public @Nonnull Struct getResultStruct() {
/*
* * SQL: The SQL statement that was executed. (string)
* * Cached: If the query was cached. (boolean)
* * SqlParameters: An ordered Array of queryparam values. (array)
* * RecordCount: Total number of records in the query. (numeric)
* * ColumnList: Column list, comma separated. (string)
* * ExecutionTime: Execution time for the SQL request. (numeric)
* * GENERATEDKEY: CF 9+ If the query was an INSERT with an identity or auto-increment value the value of that ID is placed in this variable.
*/
Struct result = new Struct();
result.put( "sql", this.pendingQuery.getOriginalSql() );
result.put( "cached", this.getIsCached() );
// cacheKey, cacheTimeout, cacheLastAccessTimeout, cacheRegion
result.put( "sqlParameters", Array.fromList( this.pendingQuery.getParameterValues() ) );
result.put( "recordCount", getRecordCount() );
result.put( "columnList", this.results.getColumnList() );
result.put( "executionTime", this.executionTime );
if ( this.generatedKey != null ) {
result.put( "generatedKey", this.generatedKey );
}

// cache info
result.put( "cached", this.isCached );
result.put( "cacheProvider", this.cacheProvider );
result.put( "cacheKey", this.cacheKey );
result.put( "cacheTimeout", this.cacheTimeout );
result.put( "cacheLastAccessTimeout", this.cacheLastAccessTimeout );

return result;
}

Expand Down
Loading

0 comments on commit e6b9a05

Please sign in to comment.