Geode/Gemfire OQL's return data is incorrect in transaction view - gemfire

Hello, I inserted a new entry to a region, and queried an OQL (SELECT * FROM /some_region_name) both within the same transaction, and I found that the new inserted entry was not returned in the OQL result. Dose anyone know what was going on with it?
Here are my testing codes:
ClientCache cache = (ClientCache) EcnSpringContext.getBean("gemfireCache");
Region<String, RUserEntity> userRegion = (Region<String, RUserEntity>) EcnSpringContext.getBean("r_user");
CacheTransactionManager txmgr = cache.getCacheTransactionManager();
EcnGeodeTemplate ecnGeodeTemplate = (EcnGeodeTemplate) EcnSpringContext.getBean("rUserTemplate");
String username = "forrest";
System.out.println("Transaction begin.");
txmgr.begin();
System.out.println("checking username[" + username + "] before added.");
RUserEntity found = (RUserEntity) userRegion.selectValue("SELECT * FROM /r_user WHERE username = '" + username + "'");
if (found == null)
System.out.println("rUserEntity NOT found");
else
System.out.println("rUserEntity found");
RUserEntity rUserEntity = new RUserEntity();
rUserEntity.setUsername("forrest");
rUserEntity.setId(username);
userRegion.put(username, rUserEntity);
System.out.println("checking username[" + username + "] after added.");
found = (RUserEntity) userRegion.selectValue("SELECT * FROM /r_user WHERE username = '" + username + "'");
if (found == null)
System.out.println("rUserEntity NOT found");
else
System.out.println("rUserEntity found");
txmgr.commit();
System.out.println("Transaction end.");
System.out.println("checking username[" + username + "] after transaction.");
found = (RUserEntity) userRegion.selectValue("SELECT * FROM /r_user WHERE username = '" + username + "'");
if (found == null)
System.out.println("rUserEntity NOT found");
else
System.out.println("rUserEntity found");
I expected the result:
Transaction begin.
checking username[forrest] before added.
rUserEntity NOT found
checking username[forrest] after added.
**rUserEntity found**
Transaction end.
checking username[forrest] after transaction.
rUserEntity found
But unfortunately I got this result:
Transaction begin.
checking username[forrest] before added.
rUserEntity NOT found
checking username[forrest] after added.
**rUserEntity NOT found**
Transaction end.
checking username[forrest] after transaction.
rUserEntity found

This is a known limitation. From the docs:
Queries and indexes reflect the cache contents and ignore the changes
made by ongoing transactions. If you do a query from inside a transaction,
the query does not reflect the changes made inside that transaction.

Related

Invalid use of group function Error in MariaDB - Update Query and SUM

So im trying to write an update query that either updates the funds of a user based on if they have visa or MasterCard.
What im trying to state here, and which im thinking is what causes the error, is to update the sum if the current balance and wanted withdraw amount is less than 10.000. If that is not the case and the visa balance becomes less than 0, im creating an intentional error so that i can use it later to redirect the user to an error page (i know its not the most PC way to do it).
This is how the code looks:
const connection = require('../models/loginrouters');
function takeMoney(amount, cardnumber) {
// prettier-ignore
console.log("db cardnumber is".cardnumber)
console.log('db amount is', amount);
connection.query(
"UPDATE users.usercards SET Balance = CASE WHEN type = 'visa' AND balance>'" +
amount +
"' THEN Balance - '" +
amount +
"' ELSE CASE WHEN type='mastercard' AND SUM(balance - '" +
amount +
"')<'-10000' THEN Balance - '" +
amount +
"' ELSE 'NEIN CASH' END END WHERE CardNumber = '" +
cardnumber +
"';",
function(err) {
if (err) {
console.log('You too poor');
console.log(err);
} else {
console.log('You got the cash');
}
}
);
}
module.exports = takeMoney;
When this query is run I get the following error:
Error: ER_INVALID_GROUP_FUNC_USE: Invalid use of group function
The query is posted to me as:
sql: 'UPDATE users.usercards SET Balance =
CASE WHEN type = \'visa\' AND balance>\'1000\'
THEN Balance - \'1000\'
ELSE
CASE WHEN type=\'mastercard\' AND SUM(balance - \'1000\')<\'-10000\'
THEN Balance - \'1000\'
ELSE \'NEIN CASH\'
END
END
WHERE CardNumber = \'123456\';'
In advance, thanks for your response!
So as Gordon Linoff stated in a comment, I cannot use SUM in an update query. I just wrapped it in a () and it works perfectly.
TLDR; DONT use SUM in an update query.
connection.query(
"UPDATE users.usercards SET Balance = CASE WHEN type = 'visa' AND balance>'" +
amount +
"' THEN Balance - '" +
amount +
"' ELSE CASE WHEN type='mastercard' AND (balance - '" +
amount +
"')>'-10000' THEN Balance - '" +
amount +
"' ELSE 'NEIN CASH' END END WHERE CardNumber = '" +
cardnumber +
"';",
function(err) {
if (err) {
console.log('You too poor');
} else {
console.log('You got the cash');
}
}
);
}

SQL Isolation level

We are using session isolation serializable in our application. The intended behavior is that when a user is going insert a new row, it should-should check for the presence of the row with the same key and update the same if row found. But I have found multiple rows created for the same key in SQL server. Is this issue with isolation or the way we are handling the case?
Following is the code I am using,
private int getNextNumber(String objectName, Connection sqlConnection) throws SQLException {
// TODO Auto-generated method stub
int number = 0;
try{
sqlConnection.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
System.out.println("##### Transaction isolation set : " + sqlConnection.getTransactionIsolation());
Statement stmt = sqlConnection.createStatement();
ResultSet rs = stmt.executeQuery("select * from [dbo].[db] where DocumentNumber = '" + objectName.toString() + "' FOR UPDATE");
while(rs.next()) {
printNumber = rs.getInt("PrintNumber");
}
System.out.println("#### Print number found from sql is : " + printNumber);
if(printNumber == 0) {
printNumber = printNumber + 1;
stmt.execute("INSERT INTO [dbo].[db] (number, DocumentNumber) VALUES (1 ,'" + objectName.toString() + "')");
} else {
number = number + 1;
stmt.execute("UPDATE [dbo].[db] SET Number =" + number + " WHERE DocumentNumber ='" + objectName.toString() + "'");
}
//sqlConnection.commit();
}catch(Exception e) {
sqlConnection.rollback();
e.printStackTrace();
} finally {
sqlConnection.commit();
}
return number;
}
Thanks,
Kishor Koli
It's an issue with the way your database is set up. You need a unique constraint to enforce uniqueness. You can check at insert time all you like but a unique constraint is the only way it's going to work 100% so it's just a waste of time selecting before inserting in the hope you'll prevent a duplicate. Insert, catch the exception/error or proceed.

Inserting data through GUI into sql server

I'm able to execute sql statements by writing the sql codes (Insert etc) on Eclipse and it is being displayed into sql server correctly. Connection has been done. But what should I do when a user wants to add data through a GUI interface (text field) and the data need to get stored into the database automatically ??
my code in the ADD button, but i'm getting the Error: java.lang.NullPointerException ! Help please..
try {
String pid = ProductID.getText();
String sql = "insert into Products_tbl values (' " +pid + " ')";
// Running the sql query
rs = st.executeQuery(sql);
int count = 0;
while (rs.next()) {
count = count + 1;
}
if (count == 1) {
JOptionPane.showMessageDialog(null, "Welcome");
}
else if (count > 1) {
JOptionPane.showMessageDialog(null,"Duplicate User Access Denied");
}
else {
JOptionPane.showMessageDialog(null, " User Not Found ");
}
}
catch (Exception ex) {
System.out.println("Error: " + ex);
}
1- Using (' " +pid + " ')" is not safe because SQL injection may occur. Use SqlParameters instead. Please check:
https://www.w3schools.com/sql/sql_injection.asp
2- I am pretty sure something is wrong with the line: rs = st.executeQuery(sql);
Here, I bet the value of st is null. Make sure that your connection variable is defined and set correctly and you created the statement like below:
st = connection.createStatement();
You can also try executeupdate(query) instead of executequery(query) like:
int flag = st.executeUpdate(query);
Ref: https://docs.oracle.com/javase/7/docs/api/java/sql/Statement.html#executeUpdate%28java.lang.String%29
3- Please use printStackTrace() method while printing the error in the catch blog, the error message would be more understandable.
System.out.println("Error: " + ex.printStackTrace());

How to solve Data truncated?

I insert the data from CSV file into MySql database, especially into one table.
I use CSVRead, and the CSV file format is :
ts,val
2013-03-31T23:45:00-04:00 New_York,10
And the table is hisdata(ts, val).
Here is my code:
try{
try {
CSVReader reader = new CSVReader(new FileReader(csvFile));
List<String[]> csvList;
csvList = reader.readAll();
System.out.println("Start: size is " + csvList.size());
for(int i = 0; i<csvList.size(); i++){
String[] eachStr = csvList.get(i);
int j = 0;
//insert(ts, val) into hisdata of sql
String sql = "INSERT INTO hisdata" + "(ts, val)" + " VALUES"
+ "('" + eachStr[j] + "', '" + eachStr[j+1] + "')";
Statement st = (Statement) conn.createStatement();
count = st.executeUpdate(sql);
}
System.out.println("access table is inserted: " + count
+ " records");
reader.close();
} catch(IOException e){
System.out.println("Error: " + e.getMessage());
}
conn.close();
} catch (SQLException e) {
System.out.println("insert is failure " + e.getMessage());
}
I think probably, the import data is too large. When I did size(), size is 8835.
Basically, I set connector. Then read CSV file and insert data line by line. Finally, I closed reader and connection.
Here is the Console print out:
Sql Connection starts
Driver loaded
Database connected
Start: size is 8835
insert is failure Data truncated for column 'val' at row 1
Is the problem the data is too large.
Please give help to solve this problem.
Add System.out.println(sql); execute the result in mysql.
if data is too large, increase column length, or get column length then reduce your data with substring.

query.next() returning false

I have used the query.next() function inside my code, but its returning false.
I even checked in the database, there are 4 records present. But the code shows only one.
However if i use query.next() before the code of query.valid() then it doesn't show any record
Please help
qDebug() << "entering payment: get all the unchecked invoices for user: " + user;
QStringList tmp;
QSqlQuery query(m_storageUserManager->database());
m_storageUserManager->dumpTable(m_invoiceInfoTable);
m_storageUserManager->dumpTable(m_invoiceUserTable);
qDebug()<<"THE NAME OF THE INVOICE USER TABLE_----=-----------------"<<m_invoiceInfoTable;
qDebug()<<"THE NAME OF THE INVOICE USER TABLE_----=-----------------"<<m_invoiceUserTable;
query.prepare("SELECT invoice FROM "+ m_invoiceInfoTable +" WHERE invoice = (SELECT
invoice FROM "+ m_invoiceUserTable +" WHERE user=:user)");
// query.prepare("SELECT invoice FROM " + m_invoiceInfoTable + ","+ m_invoiceUserTable +" WHERE " + m_invoiceInfoTable + ".user = " + m_invoiceUserTable + ".:user");
query.bindValue(":user", user);
query.exec();
query.first();
qDebug()<<"Unchecked invoices done!!! " ;
if(query.isValid()) {
do {
tmp.append(query.value(0).toString()); //as the value returned by value() is a QVariant so we need to change it to String.
} while(query.next());
} else
tmp.append("No Unchecked invoice in the database");
return tmp;
To check if the query was successful you should test the return value of either QSqlQuery::exec() or QSqlQuery::isActive() before trying to call first/next/last (when you pass the query string to the constructor of QSqlQuery, the query is already executed, so, you need to use QSqlQuery::isActive()).
first(), next() and last() return true if they positioned the query on a valid record, you don't have to test isValid() separately. Since first() is a positioning function too, you can read the value without calling next() directly after, unless you want to skip the first record.
Since you may want to add fields from the the "invoice-info" table to your query, I kept the subquery (with IN instead of = as Mat already answered in the comment).
query.prepare(QString("SELECT invoice FROM %1 WHERE invoice "
"IN (SELECT invoice FROM %2 WHERE user=:user)")
.arg(m_invoiceInfoTable, m_invoiceUserTable));
/*
// Or with an inner join
query.prepare(QString("SELECT %1.invoice FROM %1 "
"INNER JOIN %2 ON %1.invoice = %2.invoice "
"WHERE user=:user").arg(m_invoiceInfoTable, m_invoiceUserTable));*/
query.bindValue(":user", user);
if (!query.exec()) {
tmp.append("Query error: %1" + query.lastError().text());
} else if (!query.first()) {
tmp.append("No Unchecked invoice in the database");
} else {
do {
tmp.append(query.value(0).toString());
} while(query.next());
}
Try
query.prepare("SELECT invoice FROM " + m_invoiceInfoTable + " WHERE user=:user");