diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml index 1837d23a..80a1be6a 100644 --- a/.github/workflows/publish-release.yml +++ b/.github/workflows/publish-release.yml @@ -44,6 +44,8 @@ jobs: sed -i 's/ballerinaLangVersion=\(.*\)-[0-9]\{8\}-[0-9]\{6\}-.*$/ballerinaLangVersion=\1/g' gradle.properties sed -i 's/stdlib\(.*\)=\(.*\)-SNAPSHOT/stdlib\1=\2/g' gradle.properties sed -i 's/stdlib\(.*\)=\(.*\)-[0-9]\{8\}-[0-9]\{6\}-.*$/stdlib\1=\2/g' gradle.properties + sed -i 's/observe\(.*\)=\(.*\)-SNAPSHOT/observe\1=\2/g' gradle.properties + sed -i 's/observe\(.*\)=\(.*\)-[0-9]\{8\}-[0-9]\{6\}-.*$/observe\1=\2/g' gradle.properties git add gradle.properties git commit -m "Move dependencies to stable version" || echo "No changes to commit" - name: Publish artifact diff --git a/ballerina/Dependencies.toml b/ballerina/Dependencies.toml index 6157d891..ff4ffa0c 100644 --- a/ballerina/Dependencies.toml +++ b/ballerina/Dependencies.toml @@ -250,7 +250,7 @@ dependencies = [ [[package]] org = "ballerina" name = "observe" -version = "1.0.0" +version = "1.0.1" scope = "testOnly" dependencies = [ {org = "ballerina", name = "jballerina.java"} diff --git a/ballerina/Module.md b/ballerina/Module.md index 3a21dc7a..0bbc94ea 100644 --- a/ballerina/Module.md +++ b/ballerina/Module.md @@ -1,27 +1,32 @@ ## Overview -This module provides the generic interface and functionality to interact with a SQL database. The corresponding database -clients can be created by using specific database modules such as `mysql` or using the Java Database Connectivity +This module provides the generic interface and functionality to interact with an SQL database. The corresponding database +clients can be created by using specific database modules such as `mysql` or using the Java Database Connectivity module `jdbc`. ### List of Database Modules Ballerina now has the [`jdbc` module](https://docs.central.ballerina.io/ballerinax/java.jdbc/latest) as the generic DB connector module to connect to any relational database by simply providing the JDBC URL and the other related properties. -Ballerina also provides specially designed various database-specific DB connectors so that you can work with different databases and you can access their DB-specific functionalities. + +Ballerina also provides specially designed various database-specific DB connectors so that you can work with different databases, and you can access their DB-specific functionalities. +* [`MySQL` module](https://central.ballerina.io/ballerinax/mysql) +* [`PostgreSQL` module](https://central.ballerina.io/ballerinax/postgresql) +* [`MSSQL` module](https://central.ballerina.io/ballerinax/mssql) +* [`OracleDB` module](https://central.ballerina.io/ballerinax/oracledb) ### Client -The database client should be created using any of the above-listed database modules and once it is created, the operations and functionality explained below can be used. +The database client should be created using any of the above-listed database modules and once it is created, the operations and functionality explained below can be used. #### Connection Pool Handling -All database modules share the same connection pooling concept and there are three possible scenarios for -connection pool handling. For its properties and possible values, see the [`sql:ConnectionPool`](https://docs.central.ballerina.io/ballerina/sql/latest/records/ConnectionPool). +All database modules share the same connection pooling concept and there are three possible scenarios for +connection pool handling. For its properties and possible values, see the [`sql:ConnectionPool`](https://docs.central.ballerina.io/ballerina/sql/latest/records/ConnectionPool). 1. Global, shareable, default connection pool - If you do not provide the `poolOptions` field when creating the database client, a globally-shareable pool will be - created for your database unless a connection pool matching with the properties you provided already exists. - The JDBC module sample below shows how the global connection pool is used. + If you do not provide the `poolOptions` field when creating the database client, a globally-shareable pool will be + created for your database unless a connection pool matching with the properties you provided already exists. + The `jdbc` module sample below shows how the global connection pool is used. ```ballerina jdbc:Client|sql:Error dbClient = @@ -31,9 +36,9 @@ connection pool handling. For its properties and possible values, see the [`sql 2. Client-owned, unsharable connection pool - If you define the `connectionPool` field inline when creating the database client with the `sql:ConnectionPool` type, - an unsharable connection pool will be created. The JDBC module sample below shows how the global - connection pool is used. + If you define the `connectionPool` field inline when creating the database client with the `sql:ConnectionPool` type, + an unsharable connection pool will be created. The `jdbc` module sample below shows how the global + connection pool is used. ```ballerina jdbc:Client|sql:Error dbClient = @@ -43,9 +48,8 @@ connection pool handling. For its properties and possible values, see the [`sql 3. Local, shareable connection pool - If you create a record of the `sql:ConnectionPool` type and reuse that in the configuration of multiple clients, - for each set of clients that connects to the same database instance with the same set of properties, a shared - connection pool will be created. The JDBC module sample below shows how the global connection pool is used. + If you create a record of the `sql:ConnectionPool` type and reuse that in the configuration of multiple clients, + a shared connection pool will be created for each set of clients that connects to the same database instance with the same set of properties. The `jdbc` module sample below shows how the global connection pool is used. ```ballerina sql:ConnectionPool connPool = {maxOpenConnections: 5}; @@ -60,11 +64,11 @@ connection pool handling. For its properties and possible values, see the [`sql new (url = "jdbc:mysql://localhost:3306/testdb", connectionPool = connPool); ``` - + #### Closing the Client Once all the database operations are performed, you can close the database client you have created by invoking the `close()` -operation. This will close the corresponding connection pool if it is not shared by any other database clients. +operation. This will close the corresponding connection pool if it is not shared by any other database clients. ```ballerina error? e = dbClient.close(); @@ -76,9 +80,9 @@ check dbClient.close(); ### Database Operations -Once the client is created, database operations can be executed through that client. This module defines the interface -and generic properties that are shared among multiple database clients. It also supports querying, inserting, deleting, -updating, and batch updating data. +Once the client is created, database operations can be executed through that client. This module defines the interface +and generic properties that are shared among multiple database clients. It also supports querying, inserting, deleting, +updating, and batch updating data. #### Parameterized Query @@ -151,10 +155,10 @@ sql:ExecutionResult result = #### Inserting Data -These samples show the data insertion by executing an `INSERT` statement using the `execute` remote function +These samples show the data insertion by executing an `INSERT` statement using the `execute` remote function of the client. -In this sample, the query parameter values are passed directly into the query statement of the `execute` +In this sample, the query parameter values are passed directly into the query statement of the `execute` remote function. ```ballerina @@ -162,10 +166,10 @@ sql:ExecutionResult result = check dbClient->execute(`INSERT INTO student(age, n VALUES (23, 'john')`); ``` -In this sample, the parameter values, which are in local variables are used to parameterize the SQL query in -the `execute` remote function. This parameterization can be performed with any primitive Ballerina type -like `string`, `int`, `float`, or `boolean` and in that case, the corresponding SQL type of the parameter is derived -from the type of the Ballerina variable that is passed in. +In this sample, the parameter values, which are assigned to local variables are used to parameterize the SQL query in +the `execute` remote function. This parameterization can be performed with any primitive Ballerina type +like `string`, `int`, `float`, or `boolean` and in that case, the corresponding SQL type of the parameter is derived +from the type of the Ballerina variable that is passed in. ```ballerina string name = "Anne"; @@ -176,8 +180,8 @@ sql:ParameterizedQuery query = `INSERT INTO student(age, name) sql:ExecutionResult result = check dbClient->execute(query); ``` -In this sample, the parameter values are passed as a `sql:TypedValue` to the `execute` remote function. Use the -corresponding subtype of the `sql:TypedValue` such as `sql:VarcharValue`, `sql:CharValue`, `sql:IntegerValue`, etc., when you need to +In this sample, the parameter values are passed as a `sql:TypedValue` to the `execute` remote function. Use the +corresponding subtype of the `sql:TypedValue` such as `sql:VarcharValue`, `sql:CharValue`, `sql:IntegerValue`, etc., when you need to provide more details such as the exact SQL type of the parameter. ```ballerina @@ -191,7 +195,7 @@ sql:ExecutionResult result = check dbClient->execute(query); #### Inserting Data With Auto-generated Keys -This sample demonstrates inserting data while returning the auto-generated keys. It achieves this by using the +This sample demonstrates inserting data while returning the auto-generated keys. It achieves this by using the `execute` remote function to execute the `INSERT` statement. ```ballerina @@ -220,8 +224,8 @@ Note the mapping of the database column to the returned record's property is cas record(i.e., the `ID` column in the result can be mapped to the `id` property in the record). Additional Column names added to the returned record as in the SQL query. If the record is defined as a close record, only defined fields in the record are returned or gives an error when additional columns present in the SQL query. Next, the `SELECT` query is executed -via the `query` remote function of the client. Once the query is executed, each data record can be retrieved by looping -the result set. The `stream` returned by the select operation holds a pointer to the actual data in the database and it +via the `query` remote function of the client. Once the query is executed, each data record can be retrieved by looping +the result set. The `stream` returned by the `SELECT` operation holds a pointer to the actual data in the database and it loads data from the table only when it is accessed. This stream can be iterated only once. ```ballerina @@ -242,14 +246,15 @@ sql:ParameterizedQuery query = `SELECT * FROM students stream resultStream = dbClient->query(query); // Iterating the returned table. -error? e = resultStream.forEach(function(Student student) { - //Can perform operations using the record 'student' of type `Student`. -}); +check from Student student in resultStream + do { + //Can perform operations using the record 'student' of type `Student`. + } ``` -Defining the return type is optional and you can query the database without providing the result type. Hence, -the above sample can be modified as follows with an open record type as the return type. The property name in the open record -type will be the same as how the column is defined in the database. +Defining the return type is optional, and you can query the database without providing the result type. Hence, +the above sample can be modified as follows with an open record type as the return type. The property name in the open record +type will be the same as how the column is defined in the database. ```ballerina // Select the data from the database table. The query parameters are passed @@ -262,14 +267,15 @@ sql:ParameterizedQuery query = `SELECT * FROM students stream resultStream = dbClient->query(query); // Iterating the returned table. -error? e = resultStream.forEach(function(record{} student) { - // Can perform operations using the record 'student'. - io:println("Student name: ", student.value["name"]); -}); +check from record{} student in resultStream + do { + // Can perform operations using the record 'student'. + io:println("Student name: ", student.value["name"]); + } ``` There are situations in which you may not want to iterate through the database and in that case, you may decide -to use the `sql:queryRow()` operation. If the provided return type is a record, this method returns only the first row +to use the `sql:queryRow()` operation. If the provided return type is a record, this method returns only the first row retrieved by the query as a record. ```ballerina @@ -278,7 +284,7 @@ sql:ParameterizedQuery query = `SELECT * FROM students WHERE id = ${id}`; Student retrievedStudent = check dbClient->queryRow(query); ``` -The `sql:queryRow()` operation can also be used to retrieve a single value from the database (e.g., when querying using +The `sql:queryRow()` operation can also be used to retrieve a single value from the database (e.g., when querying using `COUNT()` and other SQL aggregation functions). If the provided return type is not a record (i.e., a primitive data type) , this operation will return the value of the first column of the first row retrieved by the query. @@ -290,7 +296,7 @@ int youngStudents = check dbClient->queryRow(query); #### Updating Data -This sample demonstrates modifying data by executing an `UPDATE` statement via the `execute` remote function of +This sample demonstrates modifying data by executing an `UPDATE` statement via the `execute` remote function of the client. ```ballerina @@ -301,7 +307,7 @@ sql:ExecutionResult result = check dbClient->execute(query); #### Deleting Data -This sample demonstrates deleting data by executing a `DELETE` statement via the `execute` remote function of +This sample demonstrates deleting data by executing a `DELETE` statement via the `execute` remote function of the client. ```ballerina @@ -312,8 +318,8 @@ sql:ExecutionResult result = check dbClient->execute(query); #### Batch Updating Data -This sample demonstrates how to insert multiple records with a single `INSERT` statement that is executed via the -`batchExecute` remote function of the client. This is done by creating a `table` with multiple records and +This sample demonstrates how to insert multiple records with a single `INSERT` statement that is executed via the +`batchExecute` remote function of the client. This is done by creating a `table` with multiple records and a parameterized SQL query as same as the above `execute` operations. ```ballerina @@ -333,7 +339,7 @@ sql:ExecutionResult[] result = check dbClient->batchExecute(batch); #### Execute SQL Stored Procedures -This sample demonstrates how to execute a stored procedure with a single `INSERT` statement that is executed via the +This sample demonstrates how to execute a stored procedure with a single `INSERT` statement that is executed via the `call` remote function of the client. ```ballerina @@ -344,9 +350,10 @@ sql:ProcedureCallResult result = check dbClient->call(`call InsertPerson(${uid}, ${insertId})`); stream? resultStr = result.queryResult; if resultStr is stream { - sql:Error? e = resultStr.forEach(function(record{} result) { - // Can perform operations using the record 'result'. - }); + check from record{} value in resultStr + do { + // Can perform operations using the record 'result'. + } } check result.close(); ``` diff --git a/ballerina/client.bal b/ballerina/client.bal index 9bdb0bf0..b6e8b96f 100644 --- a/ballerina/client.bal +++ b/ballerina/client.bal @@ -16,7 +16,7 @@ import ballerina/jballerina.java; -# Represents a SQL client. +# Represents an SQL client. # public type Client client object { @@ -29,11 +29,11 @@ public type Client client object { returns stream ; # Executes the query, which is expected to return at most one row of the result. - # If the query does not return any results, `sql:NoRowsError` is returned + # If the query does not return any results, `sql:NoRowsError` is returned. # # + sqlQuery - The SQL query # + returnType - The `typedesc` of the record to which the result needs to be returned. - # It can be a basic type if the query contains only one column + # It can be a basic type if the query result contains only one column # + return - Result in the `returnType` type or an `sql:Error` remote isolated function queryRow(ParameterizedQuery sqlQuery, typedesc returnType = <>) returns returnType|Error; @@ -44,25 +44,25 @@ public type Client client object { # + return - Metadata of the query execution as an `sql:ExecutionResult` or an `sql:Error` remote isolated function execute(ParameterizedQuery sqlQuery) returns ExecutionResult|Error; - # Executes the SQL query with multiple sets of parameters in a batch. Only the metadata of the execution is returned (not results from the query). - # If one of the commands in the batch fails, this will return an `sql:BatchExecuteError`. However, the driver may + # Executes the SQL query with multiple sets of parameters in a batch. Only the metadata of the execution is returned (not the results from the query). + # If one of the commands in the batch fails, an `sql:BatchExecuteError` will be returned. However, the driver may # or may not continue to process the remaining commands in the batch after a failure. # # + sqlQueries - The SQL query with multiple sets of parameters # + return - Metadata of the query execution as an `sql:ExecutionResult[]` or an `sql:Error` remote isolated function batchExecute(ParameterizedQuery[] sqlQueries) returns ExecutionResult[]|Error; - # Executes a SQL query, which calls a stored procedure. This can either return results or nil. + # Executes an SQL query, which calls a stored procedure. This may or may not return results. # # + sqlQuery - The SQL query - # + rowTypes - The array `typedesc` of the records to which the results needs to be returned + # + rowTypes - `typedesc` array of the records to which the results need to be returned # + return - Summary of the execution and results are returned in an `sql:ProcedureCallResult`, or an `sql:Error` remote isolated function call(ParameterizedCallQuery sqlQuery, typedesc[] rowTypes = []) returns ProcedureCallResult|Error; # Closes the SQL client and shuts down the connection pool. # - # + return - Possible error when closing the client + # + return - Possible `sql:Error` when closing the client public isolated function close() returns Error?; }; diff --git a/ballerina/connection-pool.bal b/ballerina/connection-pool.bal index 599d6db0..cce52cc7 100644 --- a/ballerina/connection-pool.bal +++ b/ballerina/connection-pool.bal @@ -20,17 +20,17 @@ configurable int maxOpenConnections = 15; configurable decimal maxConnectionLifeTime = 1800.0; configurable int minIdleConnections = 15; -# Represents the properties which are used to configure DB connection pool. +# Represents the properties, which are used to configure a DB connection pool. # Default values of the fields can be set through the configuration API. # -# + maxOpenConnections - The maximum number of open connections that the pool is allowed to have including -# both idle and in-use connections. The default value is 15. This can be changed through the -# configuration API with the `ballerina.sql.maxOpenConnections` key +# + maxOpenConnections - The maximum number of open connections that the pool is allowed to have. +# Includes both idle and in-use connections. The default value is 15. This can be changed through +# the configuration API with the `ballerina.sql.maxOpenConnections` key # + maxConnectionLifeTime - The maximum lifetime (in seconds) of a connection in the pool. The default value is 1800 # seconds (30 minutes). This can be changed through the configuration API with the # `ballerina.sql.maxConnectionLifeTime` key. A value of 0 indicates an unlimited maximum # lifetime (infinite lifetime) -# + minIdleConnections - The minimum number of idle connections that the pool tries to maintain in it. The default value +# + minIdleConnections - The minimum number of idle connections that the pool tries to maintain. The default value # is the same as `maxOpenConnections` and it can be changed through the configuration # API with the `ballerina.sql.minIdleConnections` key public type ConnectionPool record {| diff --git a/ballerina/error.bal b/ballerina/error.bal index 12b6a9ca..555ffb43 100644 --- a/ballerina/error.bal +++ b/ballerina/error.bal @@ -43,17 +43,17 @@ public type Error distinct error; # database resource clean-up, and other similar scenarios. public type DatabaseError distinct (Error & error); -# Represents an error occurred during execution of batch queries. +# Represents an error that occurs during the execution of batch queries. public type BatchExecuteError distinct (Error & error); -# Represents an error that occurs when a query retrieves no rows when at most one row is expected. +# Represents an error that occurs when a query retrieves does not retrieve any rows when at least one row is expected. public type NoRowsError distinct Error; # Represents an error originating from application-level configurations. public type ApplicationError distinct Error; //Level 3 -# Represents an error during the processing of the parameters or returned results. +# Represents an error that occurs during the processing of the parameters or returned results. public type DataError distinct ApplicationError; // Level 4 @@ -63,7 +63,7 @@ public type TypeMismatchError distinct DataError; # Represents an error that occurs when a query retrieves a result that is corrupted and cannot be converted to the expected type. public type ConversionError distinct DataError; -# Represents an error that occurs when a query retrieves a result that cannot be casted to the expected record type. +# Represents an error that occurs when a query retrieves a result that cannot be mapped to the expected record type. public type FieldMismatchError distinct DataError; # Represents an error that occurs when an unsupported parameter type is added to the query. diff --git a/ballerina/tests/call-procedures-test.bal b/ballerina/tests/call-procedures-test.bal index c4b32875..2034f11d 100644 --- a/ballerina/tests/call-procedures-test.bal +++ b/ballerina/tests/call-procedures-test.bal @@ -70,22 +70,19 @@ function testCallWithStringTypes() returns error? { nvarcharmax_type from StringTypes where id = ${id};`; stream queryData = dbClient->query(sqlQuery); StringDataForCall? returnData = (); - error? e = queryData.forEach(function(StringDataForCall data) { - returnData = data; - }); - if e is error { - test:assertFail("Call procedure insert did not work properly"); - } else { - StringDataForCall expectedDataRow = { - varchar_type: "test1", - charmax_type: "test2 ", - char_type: "c", - charactermax_type: "test3 ", - character_type: "d", - nvarcharmax_type: "test4" + check from StringDataForCall data in queryData + do { + returnData = data; }; - test:assertEquals(returnData, expectedDataRow, "Call procedure insert and query did not match."); - } + StringDataForCall expectedDataRow = { + varchar_type: "test1", + charmax_type: "test2 ", + char_type: "c", + charactermax_type: "test3 ", + character_type: "d", + nvarcharmax_type: "test4" + }; + test:assertEquals(returnData, expectedDataRow, "Call procedure insert and query did not match."); check dbClient.close(); } @@ -101,22 +98,19 @@ function testCallWithStringTypesInParams() returns error? { nvarcharmax_type from StringTypes where id = ${id};`; stream queryData = dbClient->query(sqlQuery); StringDataForCall? returnData = (); - error? e = queryData.forEach(function(StringDataForCall data) { - returnData = data; - }); - if e is error { - test:assertFail("Call procedure insert did not work properly"); - } else { - StringDataForCall expectedDataRow = { - varchar_type: "test1", - charmax_type: "test2 ", - char_type: "c", - charactermax_type: "test3 ", - character_type: "d", - nvarcharmax_type: "test4" + check from StringDataForCall data in queryData + do { + returnData = data; }; - test:assertEquals(returnData, expectedDataRow, "Call procedure insert and query did not match."); - } + StringDataForCall expectedDataRow = { + varchar_type: "test1", + charmax_type: "test2 ", + char_type: "c", + charactermax_type: "test3 ", + character_type: "d", + nvarcharmax_type: "test4" + }; + test:assertEquals(returnData, expectedDataRow, "Call procedure insert and query did not match."); check dbClient.close(); } @@ -896,9 +890,10 @@ function testMultipleRecords() returns error? { record {}? returnData = (); if streamData is stream { - check streamData.forEach(function(record {} data) { - returnData = data; - }); + check from record{} data in streamData + do { + returnData = data; + }; } else { test:assertFail("streamData is nil."); } @@ -931,9 +926,10 @@ function testMultipleRecordsWithNoReturnType() returns error? { record {}? returnData = (); if streamData is stream { - check streamData.forEach(function(record {} data) { - returnData = data; - }); + check from record{} data in streamData + do { + returnData = data; + }; } else { test:assertFail("streamData is nil."); } diff --git a/ballerina/tests/complex-query-test.bal b/ballerina/tests/complex-query-test.bal index bcb68b92..16daad8d 100644 --- a/ballerina/tests/complex-query-test.bal +++ b/ballerina/tests/complex-query-test.bal @@ -292,19 +292,16 @@ function testMultipleRecordRetrieval() returns error? { ResultMap? mixTypesActual = (); int counter = 0; - error? e = streamData.forEach(function(record {} value) { - if value is ResultMap && counter == 0 { - mixTypesActual = value; - } - counter = counter + 1; - }); - if e is error { - test:assertFail("Error when iterating through records " + e.message()); - } + check from record{} value in streamData + do { + if value is ResultMap && counter == 0 { + mixTypesActual = value; + } + counter = counter + 1; + }; test:assertEquals(mixTypesActual, mixTypesExpected, "Expected record did not match."); test:assertEquals(counter, 4); check dbClient.close(); - } type ResultDates record { @@ -513,17 +510,15 @@ function testColumnAlias() returns error? { dt2int_type: 100 }; int counter = 0; - error? e = queryResult.forEach(function(record {} value) { - if value is ResultSetTestAlias { - test:assertEquals(value, expectedData, "Expected record did not match."); - counter = counter + 1; - } else { - test:assertFail("Expected data type is ResultSetTestAlias"); - } - }); - if e is error { - test:assertFail("Query failed"); - } + check from record{} value in queryResult + do { + if value is ResultSetTestAlias { + test:assertEquals(value, expectedData, "Expected record did not match."); + counter = counter + 1; + } else { + test:assertFail("Expected data type is ResultSetTestAlias"); + } + }; test:assertEquals(counter, 1, "Expected only one data row."); check dbClient.close(); } @@ -547,15 +542,13 @@ function testQueryRowId() returns error? { record {}? mixTypesActual = (); int counter = 0; - error? e = streamData.forEach(function(record {} value) { - if counter == 0 { - mixTypesActual = value; - } - counter = counter + 1; - }); - if e is error { - test:assertFail("Query failed"); - } + check from record{} value in streamData + do { + if counter == 0 { + mixTypesActual = value; + } + counter = counter + 1; + }; test:assertEquals(mixTypesActual, mixTypesExpected, "Expected record did not match."); test:assertEquals(counter, 4); check dbClient.close(); diff --git a/ballerina/tests/numerical-query-test.bal b/ballerina/tests/numerical-query-test.bal index 1d88577e..473cd8ca 100644 --- a/ballerina/tests/numerical-query-test.bal +++ b/ballerina/tests/numerical-query-test.bal @@ -51,9 +51,10 @@ function testQuery() returns error? { MockClient dbClient = check new (url = jdbcURL, user = user, password = password); stream streamData = dbClient->query(`SELECT * FROM NumericTypes`); record {}? returnData = (); - check streamData.forEach(function(record {} data) { - returnData = data; - }); + check from record{} data in streamData + do { + returnData = data; + }; check dbClient.close(); if !(returnData is ()) { @@ -80,9 +81,10 @@ function testQueryNumericTypeRecord() returns error? { MockClient dbClient = check new (url = jdbcURL, user = user, password = password); stream streamData = dbClient->query(`SELECT * FROM NumericTypes`); NumericTypeForQuery? returnData = (); - check streamData.forEach(function(NumericTypeForQuery data) { - returnData = data; - }); + check from NumericTypeForQuery data in streamData + do { + returnData = data; + }; check dbClient.close(); test:assertEquals(returnData?.id, 1); @@ -279,10 +281,11 @@ function testQueryFromNullTable() returns error? { stream streamData = dbClient->query(`SELECT * FROM NumericNullTypes`); record {} returnData = {}; int count = 0; - check streamData.forEach(function(record {} data) { - returnData = data; - count += 1; - }); + check from record{} data in streamData + do { + returnData = data; + count += 1; + }; check dbClient.close(); test:assertEquals(count, 2, "More than one record present"); test:assertEquals(returnData["ID"], 2); @@ -308,9 +311,10 @@ function testQueryDatabaseError() returns error? { stream streamData = >dbClient->query(`SELECT int_type from DataTable1`, DataTable); - Error? e = streamData.forEach(function(DataTable data) { - // No need to do anything - }); + error? e = from DataTable data in streamData + do { + // No need to do anything + }; check dbClient.close(); test:assertTrue(e is Error); } diff --git a/ballerina/tests/record-mapping-test.bal b/ballerina/tests/record-mapping-test.bal index 6546f4b7..5e86ae4f 100644 --- a/ballerina/tests/record-mapping-test.bal +++ b/ballerina/tests/record-mapping-test.bal @@ -148,9 +148,10 @@ function queryTypedRecordWithFieldsStream() returns error? { stream studentStream = dbClient->query(`SELECT * FROM students JOIN teachers ON students.supervisorId = teachers.id`); Student1? returnData = (); - check studentStream.forEach(function(Student1 data) { - returnData = data; - }); + check from Student1 data in studentStream + do { + returnData = data; + }; check dbClient.close(); diff --git a/ballerina/types.bal b/ballerina/types.bal index 0d9c2d96..5cd05ed6 100644 --- a/ballerina/types.bal +++ b/ballerina/types.bal @@ -624,7 +624,7 @@ public const EXECUTION_FAILED = -3; # Metadata of the query execution. # -# + affectedRowCount - Number of rows affected by the execution of the query. It may be one of the following, +# + affectedRowCount - Number of rows affected by the execution of the query. It may be one of the following, # (1) A number greater than or equal to zero, the count of affected rows after the successful execution of the query # (2) A value of the `SUCCESS_NO_INFO`, the count of affected rows is unknown after the successful execution of the query # (3) A value of the `EXECUTION_FAILED`, the query execution failed @@ -1355,9 +1355,9 @@ public type ParameterizedCallQuery distinct object { # The result iterator used to iterate results in stream returned from `query` function. # -# + customResultIterator - Any custom result iterator to be used overriding the default behaviour -# + err - Used to hold any error occurring at the instance of stream creation -# + isClosed - Indicated the stream state +# + customResultIterator - Any custom result iterator to be used to override the default behaviour +# + err - Used to hold any error that occurs at the instance of the stream creation +# + isClosed - Indicates the stream state public class ResultIterator { private boolean isClosed = false; private Error? err; @@ -1410,11 +1410,11 @@ public class ResultIterator { } } -# Represents the results from `call` method holding returned results or metadata of query execution. +# Represents the results from the `call` method holding the returned results or metadata of the query execution. # # + executionResult - Summary of the query execution # + queryResult - Results from the SQL query -# + customResultIterator - Any custom result iterator to be used overriding the default behaviour +# + customResultIterator - Any custom result iterator to be used to override the default behaviour public class ProcedureCallResult { public ExecutionResult? executionResult = (); public stream? queryResult = (); @@ -1425,7 +1425,7 @@ public class ProcedureCallResult { } # Updates `executionResult` or `queryResult` field with the succeeding result in the result list. This will also close the current - # results when called. + # result when called. # # + return - True if the next result is `queryResult` public isolated function getNextQueryResult() returns boolean|Error { @@ -1435,7 +1435,7 @@ public class ProcedureCallResult { return getNextQueryResult(self); } - # Releases the associated resources such as database connection, results, etc. + # Releases the associated resources such as the database connection, results, etc. # # + return - An `sql:Error` if any error occurred while cleanup public isolated function close() returns Error? { @@ -1443,7 +1443,7 @@ public class ProcedureCallResult { } } -# The iterator for the stream returned in `query` function to be used overriding the default behaviour of sql:ResultIterator. +# The iterator for the stream returned in the `query` function to be used to override the default behavior of the `sql:ResultIterator`. public type CustomResultIterator object { public isolated function nextResult(ResultIterator iterator) returns record {}|Error?; public isolated function getNextQueryResult(ProcedureCallResult callResult) returns boolean|Error; diff --git a/build.gradle b/build.gradle index 93e0953c..f53520a9 100644 --- a/build.gradle +++ b/build.gradle @@ -85,6 +85,7 @@ subprojects { ballerinaStdLibs "io.ballerina.stdlib:oauth2-ballerina:${stdlibOAuth2Version}" ballerinaStdLibs "io.ballerina.stdlib:url-ballerina:${stdlibUrlVersion}" ballerinaStdLibs "io.ballerina.stdlib:http-ballerina:${stdlibHttpVersion}" + ballerinaStdLibs "io.ballerina.stdlib:observe-ballerina:${observeVersion}" externalJars (group: 'com.zaxxer', name: 'HikariCP', version: "${hikkariLibVersion}") { transitive = false diff --git a/gradle.properties b/gradle.properties index f2288c42..2185974a 100644 --- a/gradle.properties +++ b/gradle.properties @@ -14,23 +14,24 @@ researchgateReleaseVersion=2.8.0 testngVersion=7.4.0 ballerinaGradlePluginVersion=0.14.1 -ballerinaLangVersion=2201.0.0-20220111-211400-cfd415aa +ballerinaLangVersion=2201.0.0-20220119-132800-f186f5e8 -stdlibIoVersion=1.2.0-20220111-233500-1e53cc5 -stdlibRegexVersion=1.2.0-20220111-233500-b8ac5e1 -stdlibOsVersion=1.2.0-20220111-235700-1c7739e -stdlibTimeVersion=2.2.0-20220111-233400-495b76d -stdlibLogVersion=2.2.0-20220111-235900-a00410f -stdlibFileVersion=1.2.0-20220112-001200-c05237b +stdlibIoVersion=1.2.0-20220119-150800-3865141 +stdlibRegexVersion=1.2.0-20220119-150200-e512485 +stdlibOsVersion=1.2.0-20220119-153100-766eb0d +stdlibTimeVersion=2.2.0-20220119-150400-e817a2e +stdlibLogVersion=2.2.0-20220119-155100-752dba5 +stdlibFileVersion=1.2.0-20220119-160200-80cd339 +observeVersion=1.0.1-20220119-150400-0106dc0 -stdlibTransactionVersion=1.0.18-20220112-013400-a596b49 -stdlibTaskVersion=2.2.0-20220112-000100-6a9eaa8 -stdlibCryptoVersion=2.2.0-20220111-235600-bddf28e -stdlibUuidVersion=1.2.0-20220112-001100-75a9fe9 -stdlibCacheVersion=3.2.0-20220112-001200-ed9a0a4 -stdlibMimeVersion=2.2.0-20220112-001200-d67a15e -stdlibAuthVersion=2.2.0-20220112-001900-46a2f4e -stdlibJwtVersion=2.2.0-20220112-002100-f558951 -stdlibOAuth2Version=2.2.0-20220112-002300-7109169 -stdlibUrlVersion=2.2.0-20220111-233800-53086e4 -stdlibHttpVersion=2.2.0-20220112-010100-abf517a +stdlibTransactionVersion=1.0.18-20220119-172600-33704a4 +stdlibTaskVersion=2.2.0-20220119-153600-e870ebe +stdlibCryptoVersion=2.2.0-20220119-153100-ba38c76 +stdlibUuidVersion=1.2.0-20220119-154700-a6e02fb +stdlibCacheVersion=3.2.0-20220119-154800-dd9806b +stdlibMimeVersion=2.2.0-20220119-160200-95f5266 +stdlibAuthVersion=2.2.0-20220119-160100-fa712e5 +stdlibJwtVersion=2.2.0-20220119-160100-cfdd632 +stdlibOAuth2Version=2.2.0-20220119-160400-02b7639 +stdlibUrlVersion=2.2.0-20220119-150400-f338a99 +stdlibHttpVersion=2.2.0-20220119-164400-0d746ac