Skip to content

Commit

Permalink
Merge pull request #59 from hasathcharu/main
Browse files Browse the repository at this point in the history
Advanced annotation support for SQL databases for name, type, indexes and generated fields
  • Loading branch information
daneshk committed Mar 12, 2024
2 parents 8907ed0 + 71d61bc commit ecf3ece
Show file tree
Hide file tree
Showing 40 changed files with 2,210 additions and 69 deletions.
10 changes: 5 additions & 5 deletions ballerina/Ballerina.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
org = "ballerinax"
name = "persist.sql"
version = "1.2.2"
version = "1.3.0"
authors = ["Ballerina"]
keywords = ["persist", "sql", "mysql", "mssql", "sql-server"]
repository = "https://github.com/ballerina-platform/module-ballerinax-persist.sql"
Expand All @@ -15,14 +15,14 @@ graalvmCompatible = true
[[platform.java17.dependency]]
groupId = "io.ballerina.stdlib"
artifactId = "persist.sql-native"
version = "1.2.2"
path = "../native/build/libs/persist.sql-native-1.2.2-SNAPSHOT.jar"
version = "1.3.0"
path = "../native/build/libs/persist.sql-native-1.3.0-SNAPSHOT.jar"

[[platform.java17.dependency]]
groupId = "io.ballerina.stdlib"
artifactId = "persist-native"
version = "1.2.1"
path = "./lib/persist-native-1.2.1-20231129-200000-11c71ca.jar"
version = "1.3.0"
path = "./lib/persist-native-1.3.0-20240311-220000-f31520a.jar"

[[platform.java17.dependency]]
groupId = "io.ballerina.stdlib"
Expand Down
7 changes: 5 additions & 2 deletions ballerina/CompilerPlugin.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ id = "persist.sql-compiler-plugin"
class = "io.ballerina.stdlib.persist.sql.compiler.PersistSqlCompilerPlugin"

[[dependency]]
path = "../compiler-plugin/build/libs/persist.sql-compiler-plugin-1.2.2-SNAPSHOT.jar"
path = "../compiler-plugin/build/libs/persist.sql-compiler-plugin-1.3.0-SNAPSHOT.jar"

[[dependency]]
path = "./lib/persist-native-1.2.1-20231129-200000-11c71ca.jar"
path = "./lib/persist-native-1.3.0-20240311-220000-f31520a.jar"

[[dependency]]
path = "./lib/persist-compiler-plugin-1.3.0-20240311-220000-f31520a.jar"
27 changes: 12 additions & 15 deletions ballerina/Dependencies.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

[ballerina]
dependencies-toml-version = "2"
distribution-version = "2201.8.0"
distribution-version = "2201.9.0-20240229-103900-a949e6d4"

[[package]]
org = "ballerina"
Expand All @@ -23,7 +23,7 @@ dependencies = [
[[package]]
org = "ballerina"
name = "cache"
version = "3.7.0"
version = "3.7.1"
scope = "testOnly"
dependencies = [
{org = "ballerina", name = "constraint"},
Expand Down Expand Up @@ -66,7 +66,7 @@ dependencies = [
[[package]]
org = "ballerina"
name = "http"
version = "2.10.0"
version = "2.10.10"
scope = "testOnly"
dependencies = [
{org = "ballerina", name = "auth"},
Expand Down Expand Up @@ -100,9 +100,6 @@ dependencies = [
{org = "ballerina", name = "jballerina.java"},
{org = "ballerina", name = "lang.value"}
]
modules = [
{org = "ballerina", packageName = "io", moduleName = "io"}
]

[[package]]
org = "ballerina"
Expand Down Expand Up @@ -268,7 +265,7 @@ dependencies = [
[[package]]
org = "ballerina"
name = "observe"
version = "1.2.0"
version = "1.2.2"
dependencies = [
{org = "ballerina", name = "jballerina.java"}
]
Expand All @@ -286,7 +283,7 @@ dependencies = [
[[package]]
org = "ballerina"
name = "persist"
version = "1.2.1"
version = "1.3.0"
dependencies = [
{org = "ballerina", name = "jballerina.java"}
]
Expand All @@ -297,7 +294,7 @@ modules = [
[[package]]
org = "ballerina"
name = "sql"
version = "1.11.0"
version = "1.11.1"
dependencies = [
{org = "ballerina", name = "io"},
{org = "ballerina", name = "jballerina.java"},
Expand Down Expand Up @@ -325,6 +322,7 @@ version = "0.0.0"
scope = "testOnly"
dependencies = [
{org = "ballerina", name = "jballerina.java"},
{org = "ballerina", name = "lang.array"},
{org = "ballerina", name = "lang.error"}
]
modules = [
Expand Down Expand Up @@ -388,7 +386,7 @@ modules = [
[[package]]
org = "ballerinax"
name = "mssql"
version = "1.11.0"
version = "1.11.1"
scope = "testOnly"
dependencies = [
{org = "ballerina", name = "crypto"},
Expand All @@ -403,7 +401,7 @@ modules = [
[[package]]
org = "ballerinax"
name = "mssql.driver"
version = "1.5.0"
version = "1.6.0"
scope = "testOnly"
modules = [
{org = "ballerinax", packageName = "mssql.driver", moduleName = "mssql.driver"}
Expand All @@ -428,7 +426,7 @@ modules = [
[[package]]
org = "ballerinax"
name = "mysql.driver"
version = "1.5.0"
version = "1.6.0"
scope = "testOnly"
modules = [
{org = "ballerinax", packageName = "mysql.driver", moduleName = "mysql.driver"}
Expand All @@ -437,9 +435,8 @@ modules = [
[[package]]
org = "ballerinax"
name = "persist.sql"
version = "1.2.2"
version = "1.3.0"
dependencies = [
{org = "ballerina", name = "io"},
{org = "ballerina", name = "jballerina.java"},
{org = "ballerina", name = "log"},
{org = "ballerina", name = "persist"},
Expand Down Expand Up @@ -477,7 +474,7 @@ modules = [
[[package]]
org = "ballerinax"
name = "postgresql.driver"
version = "1.5.0"
version = "1.5.1"
scope = "testOnly"
modules = [
{org = "ballerinax", packageName = "postgresql.driver", moduleName = "postgresql.driver"}
Expand Down
86 changes: 86 additions & 0 deletions ballerina/annotations.bal
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// Copyright (c) 2024 WSO2 LLC. (http://www.wso2.com) All Rights Reserved.
//
// WSO2 LLC. licenses this file to you under the Apache License,
// Version 2.0 (the "License"); you may not use this file except
// in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

# Maps an entity/field name to a database table/column name.
#
# + name - name of the table/column in the database
public type MapConfig record {|
string name;
|};

# The Annotation used to specify the mapping of an entity/field to a database table/column.
public annotation MapConfig Mapping on type, record field;

# Marks the entity field as an index field.
#
# + names - array of index names associated with the database column
public type IndexConfig record {|
string[]? names = ();
|};

# The Annotation used to specify the index name associated with a database column.
public annotation IndexConfig Index on record field;

# The Annotation used to specify the unique index name associated with a database column.
public annotation IndexConfig UniqueIndex on record field;

# Defines a string field as a VARCHAR column and defines its max length.
#
# + length - max length of the VARCHAR column
public type VarCharConfig record {|
int length = 191;
|};

# Defines a string field as a CHAR column and defines its length.
#
# + length - length of the CHAR column
public type CharConfig record {|
int length = 10;
|};

# The Annotation used to specify the max length of a VARCHAR column.
public annotation VarCharConfig VarChar on record field;

# The Annotation used to specify the length of a CHAR column.
public annotation CharConfig Char on record field;

# Defines a custom precision and scale to a DECIMAL column.
#
# + precision - precision of the DECIMAL column as an array of two integers [precision, scale]
public type DecimalConfig record {|
[int, int] precision = [65, 30];
|};

# The Annotation used to specify a custom precision and scale of a DECIMAL column.
public annotation DecimalConfig Decimal on record field;

# Specifies your own foreign key column in the entity record.
#
# + refs - array of key fields in the entity
public type RelationConfig record {|
string[] refs;
|};

# The Annotation used to specify the foreign key column in the entity record.
public annotation RelationConfig Relation on record field;

# Denotes an entity field as a database generated value field.
#
public type GeneratedConfig record {|
|};

# The Annotation used to specify a database generated column in the entity record.
public annotation GeneratedConfig Generated on record field;
3 changes: 3 additions & 0 deletions ballerina/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ dependencies {
externalJars(group: 'io.ballerina.stdlib', name: 'persist-native', version: "${stdlibPersistVersion}") {
transitive = false
}
externalJars(group: 'io.ballerina.stdlib', name: 'persist-compiler-plugin', version: "${stdlibPersistVersion}") {
transitive = false
}
externalJars(group: 'io.ballerina.stdlib', name: 'sql-native', version: "${stdlibSqlVersion}") {
transitive = false
}
Expand Down
23 changes: 14 additions & 9 deletions ballerina/sql_client.bal
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,9 @@
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

import ballerina/log;
import ballerina/sql;
import ballerina/persist;
import ballerina/sql;

# The client used by the generated persist clients to abstract and
# execute SQL queries that are required to perform CRUD operations.
Expand Down Expand Up @@ -122,8 +121,8 @@ public isolated client class SQLClient {
# + groupByClause - The `GROUP BY` clause of the query
# + return - A stream of records in the `rowType` type or a `persist:Error` if the operation fails
public isolated function runReadQuery(typedesc<record {}> rowType, string[] fields = [], string[] include = [],
sql:ParameterizedQuery whereClause = ``, sql:ParameterizedQuery orderByClause = ``,
sql:ParameterizedQuery limitClause = ``, sql:ParameterizedQuery groupByClause = ``)
sql:ParameterizedQuery whereClause = ``, sql:ParameterizedQuery orderByClause = ``,
sql:ParameterizedQuery limitClause = ``, sql:ParameterizedQuery groupByClause = ``)
returns stream<record {}, sql:Error?>|persist:Error|error {
sql:ParameterizedQuery query;
if self.getManyRelationFields(include).length() > 0 {
Expand All @@ -138,7 +137,7 @@ public isolated client class SQLClient {
query = sql:queryConcat(query, ` WHERE `, whereClause);
}
if (groupByClause.strings.length() != 0) {
query = addClauseToQuery(query, groupByClause, ` GROUP BY `);
query = addClauseToQuery(query, groupByClause, ` GROUP BY `);
}
if (orderByClause.strings.length() != 0) {
query = addClauseToQuery(query, orderByClause, ` ORDER BY `);
Expand Down Expand Up @@ -420,11 +419,17 @@ public isolated client class SQLClient {
}

private isolated function getUpdateQuery(record {} updateRecord) returns sql:ParameterizedQuery|persist:Error {
return sql:queryConcat(`UPDATE `, stringToParameterizedQuery(self.escape(self.tableName)), ` SET `, check self.getSetClauses(updateRecord));
if self.dataSourceSpecifics == MSSQL_SPECIFICS {
return sql:queryConcat(`UPDATE `, stringToParameterizedQuery(self.escape(self.entityName)), ` SET `, check self.getSetClauses(updateRecord), ` FROM `, stringToParameterizedQuery(self.escape(self.tableName)), ` `, stringToParameterizedQuery(self.escape(self.entityName)));
}
return sql:queryConcat(`UPDATE `, stringToParameterizedQuery(self.escape(self.tableName)), ` AS `, stringToParameterizedQuery(self.escape(self.entityName)), ` SET `, check self.getSetClauses(updateRecord));
}

private isolated function getDeleteQuery() returns sql:ParameterizedQuery {
return sql:queryConcat(`DELETE FROM `, stringToParameterizedQuery(self.escape(self.tableName)));
if self.dataSourceSpecifics == MSSQL_SPECIFICS {
return sql:queryConcat(`DELETE `, stringToParameterizedQuery(self.escape(self.entityName)), ` FROM `, stringToParameterizedQuery(self.escape(self.tableName)), ` AS `, stringToParameterizedQuery(self.escape(self.entityName)));
}
return sql:queryConcat(`DELETE FROM `, stringToParameterizedQuery(self.escape(self.tableName)), ` AS `, stringToParameterizedQuery(self.escape(self.entityName)));
}

private isolated function getJoinFields(string[] include) returns string[] {
Expand Down Expand Up @@ -505,7 +510,7 @@ isolated function addClauseToQuery(sql:ParameterizedQuery query, sql:Parameteriz
string[] queryStrings = clauseQuery.strings;
int i = 0;
foreach sql:Value insertion in clauseQuery.insertions {
queryInString += queryStrings[i] + string `${insertion.toString()}`;
queryInString += queryStrings[i] + string `${insertion.toString()}`;
i += 1;
}
queryInString += queryStrings[i];
Expand All @@ -525,7 +530,7 @@ isolated function logQuery(string msg, sql:ParameterizedQuery|sql:ParameterizedQ
string stringValue = queries.strings.toBalString();
foreach sql:Value insertion in queries.insertions {
string:RegExp reg = re `","`;
stringValue = reg.replace(stringValue, string `${insertion.toString()}`);
stringValue = reg.replace(stringValue, string `${insertion.toString()}`);
}
log:printDebug(msg + stringValue);
}
52 changes: 32 additions & 20 deletions ballerina/tests/init-tests.bal
Original file line number Diff line number Diff line change
Expand Up @@ -53,26 +53,37 @@ configurable record {|
@test:BeforeSuite
function initTests() returns error? {
// MySQL
mysql:Client mysqlDbClient = check new (host = mysql.host, user = mysql.user, password = mysql.password, database = mysql.database, port = mysql.port);
_ = check mysqlDbClient->execute(`SET FOREIGN_KEY_CHECKS = 0`);
_ = check mysqlDbClient->execute(`TRUNCATE Employee`);
_ = check mysqlDbClient->execute(`TRUNCATE Workspace`);
_ = check mysqlDbClient->execute(`TRUNCATE Building`);
_ = check mysqlDbClient->execute(`TRUNCATE Department`);
_ = check mysqlDbClient->execute(`TRUNCATE OrderItem`);
_ = check mysqlDbClient->execute(`TRUNCATE AllTypes`);
_ = check mysqlDbClient->execute(`TRUNCATE FloatIdRecord`);
_ = check mysqlDbClient->execute(`TRUNCATE StringIdRecord`);
_ = check mysqlDbClient->execute(`TRUNCATE DecimalIdRecord`);
_ = check mysqlDbClient->execute(`TRUNCATE BooleanIdRecord`);
_ = check mysqlDbClient->execute(`TRUNCATE IntIdRecord`);
_ = check mysqlDbClient->execute(`TRUNCATE AllTypesIdRecord`);
_ = check mysqlDbClient->execute(`TRUNCATE CompositeAssociationRecord`);
_ = check mysqlDbClient->execute(`SET FOREIGN_KEY_CHECKS = 1`);
check mysqlDbClient.close();
check initMySqlTests();

//MSSQL
mssql:Client mssqlDbClient = check new (host = mssql.host, user = mssql.user, password = mssql.password, port = mssql.port);
check initMsSqlTests();

// PostgreSQL
check initPostgreSqlTests();
}

function initMySqlTests() returns error? {
mysql:Client mysqlDbClient = check new (host = mysql.host, user = mysql.user, password = mysql.password, database = mysql.database, port = mysql.port);
_ = check mysqlDbClient->execute(`SET FOREIGN_KEY_CHECKS = 0`);
_ = check mysqlDbClient->execute(`TRUNCATE Employee`);
_ = check mysqlDbClient->execute(`TRUNCATE Workspace`);
_ = check mysqlDbClient->execute(`TRUNCATE Building`);
_ = check mysqlDbClient->execute(`TRUNCATE Department`);
_ = check mysqlDbClient->execute(`TRUNCATE OrderItem`);
_ = check mysqlDbClient->execute(`TRUNCATE AllTypes`);
_ = check mysqlDbClient->execute(`TRUNCATE FloatIdRecord`);
_ = check mysqlDbClient->execute(`TRUNCATE StringIdRecord`);
_ = check mysqlDbClient->execute(`TRUNCATE DecimalIdRecord`);
_ = check mysqlDbClient->execute(`TRUNCATE BooleanIdRecord`);
_ = check mysqlDbClient->execute(`TRUNCATE IntIdRecord`);
_ = check mysqlDbClient->execute(`TRUNCATE AllTypesIdRecord`);
_ = check mysqlDbClient->execute(`TRUNCATE CompositeAssociationRecord`);
_ = check mysqlDbClient->execute(`SET FOREIGN_KEY_CHECKS = 1`);
check mysqlDbClient.close();
}

function initMsSqlTests() returns error? {
mssql:Client mssqlDbClient = check new (host = mssql.host, user = mssql.user, password = mssql.password, port = mssql.port);
_ = check mssqlDbClient->execute(`DROP DATABASE IF EXISTS test;`);
_ = check mssqlDbClient->execute(`CREATE DATABASE test`);
check mssqlDbClient.close();
Expand Down Expand Up @@ -221,9 +232,10 @@ function initTests() returns error? {
PRIMARY KEY(id)
);
`);
}

// PostgreSQL
postgresql:Client postgresqlDbClient = check new (host = postgresql.host, username = postgresql.user, password = postgresql.password, database = postgresql.database, port = postgresql.port);
function initPostgreSqlTests() returns error? {
postgresql:Client postgresqlDbClient = check new (host = postgresql.host, username = postgresql.user, password = postgresql.password, database = postgresql.database, port = postgresql.port);
_ = check postgresqlDbClient->execute(`TRUNCATE "Employee" CASCADE`);
_ = check postgresqlDbClient->execute(`TRUNCATE "Workspace" CASCADE`);
_ = check postgresqlDbClient->execute(`TRUNCATE "Building" CASCADE`);
Expand Down
Loading

0 comments on commit ecf3ece

Please sign in to comment.