I've been getting a "java.sql.SQLException: Invalid column name" error when I try to select each row with an attribute name that contains a colon (for example "item:one".
In my code the following line is called:
if (!(matGroup == null || matGroup.equals(''))) {
sql.eachRow("select distinct da.item_name from defined_attribute da where da.attribute2=$matGroup") {
selectItems << it.item_name
}
}
If I run the same select command directly to the database (select distinct da.item_name from defined_attribute da where da.attribute2='item:one'), I receive no errors. I have also tried the code with item names containing no colon and those seem are retrieved correctly.
I've found here: http://groovy.329449.n5.nabble.com/Sql-is-it-possible-to-escape-the-colon-td5155389.html that adding "sql.enableNamedParameters = false" might solve the problem, however, I'm constrained to use groovy 2.1.6, and that is not supported.
I've also tried other things to work around the colon, but neither have been successful. Things I've tried include:
Defining the parameter in a collection
def params = [matGroup]
sql.eachRow("select distinct da.item_name from defined_attribute da where da.attribute2=?", params)
Escaping the colon before passing it to eachRow()
def params = [matGroup.replaceAll(":", "\\:")]
sql.eachRow("select distinct da.item_name from defined_attribute da where da.attribute2=?", params)
Defining the command as a string before passing it to eachRow()
def command = "select distinct da.item_name from defined_attribute da where da.attribute2='" + matGroup + "\'"
sql.eachRow(command.toString())
Is there a way to make this work for groovy 2.1.6?
Related
I have an SQL statement that I'm executing through OleDb, the statement is something like this:
INSERT INTO mytable (name, dept) VALUES (#name, #dept);
I'm adding parameters to the OleDbCommand like this:
OleDbCommand Command = new OleDbCommand();
Command.Connection = Connection;
OleDbParameter Parameter1 = new OleDbParameter();
Parameter1.OleDbType = OleDbType.VarChar;
Parameter1.ParamterName = "#name";
Parameter1.Value = "Bob";
OleDbParameter Parameter2 = new OleDbParameter();
Parameter2.OleDbType = OleDbType.VarChar;
Parameter2.ParamterName = "#dept";
Parameter2.Value = "ADept";
Command.Parameters.Add(Parameter1);
Command.Parameters.Add(Parameter2);
The problem I've got is, if I add the parameters to command the other way round, then the columns are populated with the wrong values (i.e. name is in the dept column and vice versa)
Command.Parameters.Add(Parameter2);
Command.Parameters.Add(Parameter1);
My question is, what is the point of the parameter names if parameters values are just inserted into the table in the order they are added command? The parameter names seems redundant?
The Problem is that OleDb (and Odbc too) does not support named parameters.
It only supports what's called positional parameters.
In other words: The name you give a parameter when adding it to the commands parameters list does not matter. It's only used internally by the OleDbCommand class so it can distinguish and reference the parameters.
What matters is the order in which you add the parameters to the list. It must be the same order as the parameters are referenced in the SQL statement via the question mark character (?).
But here is a solution that allows you to use named parameters in the SQL statement. It basically replaces all parameter references in the SQL statement with question marks and reorders the parameters list accordingly.
It works the same way for the OdbcCommand class, you just need to replace "OleDb" with "Odbc" in the code.
Use the code like this:
command.CommandText = "SELECT * FROM Contact WHERE FirstName = #FirstName";
command.Parameters.AddWithValue("#FirstName", "Mike");
command.ConvertNamedParametersToPositionalParameters();
And here is the code
public static class OleDbCommandExtensions
{
public static void ConvertNamedParametersToPositionalParameters(this OleDbCommand command)
{
//1. Find all occurrences of parameter references in the SQL statement (such as #MyParameter).
//2. Find the corresponding parameter in the commands parameters list.
//3. Add the found parameter to the newParameters list and replace the parameter reference in the SQL with a question mark (?).
//4. Replace the commands parameters list with the newParameters list.
var newParameters = new List<OleDbParameter>();
command.CommandText = Regex.Replace(command.CommandText, "(#\\w*)", match =>
{
var parameter = command.Parameters.OfType<OleDbParameter>().FirstOrDefault(a => a.ParameterName == match.Groups[1].Value);
if (parameter != null)
{
var parameterIndex = newParameters.Count;
var newParameter = command.CreateParameter();
newParameter.OleDbType = parameter.OleDbType;
newParameter.ParameterName = "#parameter" + parameterIndex.ToString();
newParameter.Value = parameter.Value;
newParameters.Add(newParameter);
}
return "?";
});
command.Parameters.Clear();
command.Parameters.AddRange(newParameters.ToArray());
}
}
Parameter NAMES are generic in the SQL support system (i.e. not OleDb specific). Pretty much ONLY OleDb / Odbc do NOT use them. They are there because OleDb is a specific implementation of the generic base classes.
Am I missing an error with the following to insert into a table with four columns, message_id (auto-incremented) message_sender, message_reciever, message_body. I have checked similar questions and haven't found the solution.
'''
<% String sender_id = request.getParameter("message_sender");
String reciever_id = request.getParameter("message_reciever");
String message = request.getParameter("message_body");
try{
Class.forName("com.mysql.jdbc.Driver");
Connection con = DriverManager.getConnection("jdbc:mysql://localhost:3306/fyp", "root", "Kmd116352323!");
Statement st = con.createStatement();
st.executeUpdate("INSERT INTO messages(message_sender,message_reciever,message_body) VALUES('"+sender_id+", "+reciever_id+" , "+message+" ')");
out.println("Your request has been noted."
+ " Please return to your user profile or log out");
} catch(Exception e){
out.println(e);
}
%>
You should not concatenate values into a query string. It makes your code vulnerable to SQL injection, or mistakes like forgetting quotes around values. The specific problem in your case is that you have a quote before the first value and a quote after the last value, which makes it a single value instead of three separate values.
However, instead of fixing the immediate problem by adding those missing quotes, you should switch to using a prepared statement:
try (PreparedStatement pstmt = connection.prepareStatement(
"INSERT INTO messages(message_sender,message_reciever,message_body) VALUES(?, ?, ?)") {
pstmt.setString(1, sender_id);
pstmt.setString(2, reciever_id);
pstmt.setString(3, message);
pstmt.executeUpdate();
}
As an aside, you really should not put data access in a JSP. It belongs in a DAO or service class.
This is incorrect:
VALUES('"+sender_id+", "+reciever_id+" , "+message+" ')
You have a ' wrapping the all the values... Which means sql will think you are only sending 1 value. If they are all text values it should be like this:
VALUES('"+sender_id+"', '"+reciever_id+"' , '"+message+"')
If you know some of the values are of INT type then you do not need the ' for that value:
VALUES("+sender_id+", "+reciever_id+" , '"+message+"')
Text values need to be wrapped with a '
I am using sql.firstRow to check if a row exists in the postgres database based on some criteria.
def cur = sql.firstRow(r, '''
SELECT "some_thing"
FROM "my_table"
WHERE "customer_name" = :customer_name
AND "sad_date" = :sad_date
AND "forgiver" = :forgiver
''')
I find that this works:
if (cur){
log.debug("Found Some thing " + cur["some_thing"])
log.debug("Cur: " + cur.keySet())
}
however this lets in any rows that don't have some_field inside it.
ISSUE
To avoid this, when we try and check for the existance of a non empty value for some_field on the result row like this:
if (cur && "${cur.some_thing}" ){
log.debug("Found Some thing " + cur["some_thing"])
}
ERROR
I get an error suggesting that:
No signature of `String.positive` for argument types for the given type.
I have read this question and changed from cur.some_thing and cur['some_thing'] to "${cur.some_thing}" but the error does not go away
I have also tried this post and tried to use cur.getProperty("some_thing") and it still throws the same error.
I am trying to do create a where clause to pass as a parameter to an Oracle command and it's proving to be more difficult than I thought. What I want to do is create a big where query based off user input from our application. That where query is to be the single parameter for the statement and will have multiple AND, OR conditions in it. This code here works however isn't exactly what I require:
string conStr = "User Id=testschema;Password=pass12341;Data Source=orapdex01";
Console.WriteLine("About to connect to Database with Connection String: " + conStr);
OracleConnection con = new OracleConnection(conStr);
con.Open();
Console.WriteLine("Connected to the Database..." + Environment.NewLine + "Press enter to continue");
Console.ReadLine();
// Assume the connection is correct because it works already without the parameterization
String block = "SELECT * FROM TEMP_VIEW WHERE NAME = :1";
// set command to create anonymous PL/SQL block
OracleCommand cmd = new OracleCommand();
cmd.CommandText = block;
cmd.Connection = con;
// since execurting anonymous pl/sql blcok, setting the command type
// as text instead of stored procedure
cmd.CommandType = CommandType.Text;
// Setting Oracle Parameter
// Bind the parameter as OracleDBType.Varchar2
OracleParameter param = cmd.Parameters.Add("whereTxt", OracleDbType.Varchar2);
param.Direction = ParameterDirection.Input;
param.Value = "MY VALUE";
// Get returned values from select statement
OracleDataReader dr = cmd.ExecuteReader();
// Read the identifier for each result and display it
while (dr.Read())
{
Console.WriteLine(dr.GetValue(0));
}
Console.WriteLine("Selected successfully !");
Console.WriteLine("");
Console.WriteLine("***********************************************************");
Console.ReadKey();
If I change the lines below to be the type of result I want then I get an error "ORA-00933: SQL command not properly ended":
String block = "SELECT * FROM TEMP_VIEW :1";
...
...
param.Value = "WHERE NAME = 'MY VALUE' AND ID = 5929";
My question is how do I accomplish adding my big where query dynamically without causing this error?
Sadly there is no easy way to achieve this.
One thing you will need to understand with parameterised SQL in general is that bind parameters can only be used for values, such as strings, numbers or dates. You cannot put bits of SQL in them, such as column names or WHERE clauses.
Once the database has the SQL text, it will attempt to parse it and figure out whether it is valid, and it will do this without taking any look at the bind parameter values. It won't be able to execute the SQL without all of the values.
The SQL string SELECT * FROM TEMP_VIEW :1 can never be valid, as Oracle isn't expecting a value to immediately follow FROM TEMP_VIEW.
You will need to build up your SQL as a string and also build up the list of bind parameters at the same time. If you find that you need to add a condition on the column NAME, you add WHERE NAME = :1 to the SQL string and a parameter with name :1 and the value you wish to add. If you have a second condition to add, you append AND ID = :2 to the SQL string and a parameter with name :2.
Hopefully the following code should explain a little better:
// Initialise SQL string and parameter list.
String sql = "SELECT * FROM DUAL";
var oracleParams = new List<OracleParameter>();
// Build up SQL string and list of parameters.
// (There's only one in this somewhat simplistic example. If you have
// more than one parameter, it might be easier to start the query with
// "SELECT ... FROM some_table WHERE 1=1" and then append
// " AND some_column = :1" or similar. Don't forget to add spaces!)
sql += " WHERE DUMMY = :1";
oracleParams.Add(new OracleParameter(":1", OracleDbType.Varchar2, "X", ParameterDirection.Input));
using (var connection = new OracleConnection() { ConnectionString = "..."})
{
connection.Open();
// Create the command, setting the SQL text and the parameters.
var command = new OracleCommand(sql, connection);
command.Parameters.AddRange(oracleParams.ToArray());
using (OracleDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
// Do stuff with the data read...
}
}
}
I want to perform the following query using Dapper, which currently doesn't return expected results (I think it must be treating the #pName param as literal text within the single quotes?):
var q = "SELECT * FROM Users WHERE Name LIKE '#pName%'";
#pName is the param I assign a value to upon executing the query.
Things work if I just build the SQL like:
var q = "SELECT * FROM Users WHERE Name LIKE '" + name + "%'";
.. but I would prefer to use a param if possible.
I am executing the query using the following code:
o = _cn.Query<User>(q, new { pName = new DbString { Value = name, IsFixedLength = false, Length = 25, IsAnsi = true } }).ToList();
How do I got about this using Dapper?
SELECT * FROM Users WHERE Name LIKE #pName + '%'
I would like to add here another possible solution:
var results = cn.Query("SELECT * FROM Table WHERE Column LIKE #value", new { value = value + "%" });
The wildcard is inside the string var itself, and then we reference that var in the SQL. Applies to any wildcard pattern you want.