I am building a Batch table in an Access database to save operations from a form to be processed after the user clicks the submit button (on the form).
My only concern is that the SQL Statements themselves will have text qualifiers in them. When i submit the sql statement to be stored in the database i have to wrap the sql string in a text qualifier and i want to make sure that the statement qualifiers will not be escaped when performing a Insert statement into the batch table.
Example:
SQL Statement (operational statement)
INSERT INTO tblGrpLoc (gid, txt) VALUES (2, 'Select * From tblInformation')
SQL Statement (batch storage)
INSERT INTO tblBatch(act, sql) VALUES (0, 'INSERT INTO tblGrpLoc (gid, txt) VALUES (2, 'Select * From tblInformation')')
Eventually i would iterate through the Batch table and only execute the field sql and update another field to denote its execution but i want to make sure that the sql field itself will be homogenous with the sql statement to be executed with no loss of string qualifiers.
Edited (2012-08-13 # 13:42pm CST)
To give you an idea of how this nesting is being incorporated here is the method:
Public Sub BatchAdd(ByRef db As Database, action As BatchAction, sql As String)
Dim bsql As String
Dim bact As Integer: bact = CInt(action)
bsql = SQLInsert("tblBatchTransaction", _
"action, txt", _
(CStr(bact) & ",'" & sql & "'"))
db.Execute bsql
End Sub
SLQInsert simply builds a SQL Insert statment. No you can see how i might have a String Qualifier issue arise.
If I understand your question correctly, you want to store your SQL statement in such a way that it can be run as-is. However, the internal single quotes are getting in the way. Try replacing the outer single quotes with double quotes:
INSERT INTO tblBatch(act, sql) VALUES (0, "INSERT INTO tblGrpLoc (gid, txt) VALUES (2, 'Select * From tblInformation')")
Related
I'm using hibernate and mysql
when I run the following statement in mysql it works perfectly:
INSERT INTO table1 (name, is_visited) VALUES ('visit our site \'n\' days',true);
However, When I run with hibernate native query I get error:
com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'days',true)' at line 1.
Here is the java code:
String query = "INSERT INTO table1 (name, is_visited) VALUES ('visit our site \'n\' days',true)";
Query nativeQuery = getEntityManager().createNativeQuery(query);
nativeQuery.executeUpdate();
When I change the query statement to
"INSERT INTO table1 (name, is_visited) VALUES ('visit our site \'n days',true)";
it works.
looks like there is an issue with \'n\'
any idea?
What is happening here is that the following query is correctly escaping the single quotes when run directly on MySQL:
INSERT INTO table1 (name, is_visited) VALUES ('visit our site \'n\' days', true);
This works on MySQL because one valid way to escape literal quotes on MySQL is to escape them with backslash. However, doing this inside a Java string means that the \' are not being passed to MySQL. Instead, Java consumes the single backslash, and just the single quotes make it across to the database. I suggest using the other method of escaping single quotes here, which is to double them up:
String query = "INSERT INTO table1 (name, is_visited) VALUES ('visit our site ''n''x days', true)";
While it might be possible to escape the backslashes from Java, that seems confusing to me, because it requires keeping track of escaping across both your Java and database layer.
As another general comment, if you used the Hibernate ORM layer to do the insert, or used a prepared statement, you wouldn't have to worry about this escaping problem.
I am trying to accomplish a simple licensing system in golang and have tried numerous ways to get it to work. Basically, I have input a couple of random licensing keys into my database and my golang program should check to see if the user-input key exists and if it does then add the user specified username and password into the database to login later.
This is the code that I have that hasn't been working:
"IF EXISTS (SELECT * FROM login WHERE LK = "+reglicenceEntry.Text()+") THEN
INSERT INTO `login` (`Username`, `Password`, `LK`) VALUES
('"+regusernameEntry.Text()+"', '"+regpasswordEntry.Text()+"', ''); "
This is the golang error:
Error 1064: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'IF EXISTS (SELECT * FROM login WHERE LK = '5qp515YHXEmSDzwqgoJh') THEN INSERT IN' at line 1
Thanks so much!
MySQL syntax doesn't support IF...THEN constructs except within stored routines and triggers and events. See https://dev.mysql.com/doc/refman/8.0/en/sql-syntax-compound-statements.html
I suggest an alternative solution for your code:
INSERT INTO `login` (`Username`, `Password`, `LK`)
SELECT ?, ?, ''
FROM `login`
WHERE `LK` = ?
LIMIT 1
If your login table does not have the LK value, the SELECT above will return 0 rows, therefore it will not insert anything.
If your login table has the LK value, the SELECT above will return at least 1 row (and I limit it to 1), therefore it will insert a row. The row it inserts is comprised of your username and password, and a blank string for the LK.
I showed use of parameter placeholders. You should use parameters in SQL instead of concatenating variables into your query. This is good practice to avoid accidental SQL injection. See http://go-database-sql.org/prepared.html for examples.
The purpose of using parameters is to avoid SQL injection problems. See my answer to What is SQL injection? for an explanation of SQL injection.
Or my presentation SQL Injection Myths and Fallacies (or youtube video).
When using parameters, you do two steps.
The first step to prepare a query with placeholders (?) where you would otherwise concatenate variables into your SQL query.
The second step is to execute the prepared query, and this is the time you pass the variables to fill in the placeholders.
The point is to keep variables separate from your query, so if there's anything in the variable that could unintentionally change your SQL syntax (like imbalanced quotes), it is never combined with the SQL. After you do the prepare, the SQL has already been parsed by the MySQL server, and there's no way to change the syntax after that.
MySQL remembers which parts of the query need to be filled in, and when you pass variables during the execute step, MySQL fills in the missing parts of the query using your values — but this happens within the MySQL server, not in your application.
Thus the dynamic parts of the query — your variables — are kept separate from the SQL syntax and you avoid SQL injection problems.
For your task described in your question, it would look something like this (I have not tested this Go code, but it should put you on the right path).
stmt, err := tx.Prepare("INSERT INTO `login` (`Username`, `Password`, `LK`) SELECT ?, ?, '' FROM `login` WHERE `LK` = ? LIMIT 1")
if err != nil {
log.Fatal(err)
}
defer stmt.Close()
_, err = stmt.Exec(regusernameEntry.Text(), regpasswordEntry.Text(), reglicenceEntry.Text())
if err != nil {
log.Fatal(err)
}
The order of parameters is important. The variables you pass to Exec() must be in the same order that the ? placeholders appear in your prepared SQL statement. They are matched up, one for one, in the same order, by the MySQL server.
Do not put quotes around the placeholders in your prepared SQL statement. That will work as a literal string '?' in SQL. Use an unquoted ? character for a placeholder. When it gets combined by MySQL in the server, it will work as if you had put quotes around the value like a string — but with no risk of SQL injection even if that string value containing special characters.
Here's another site that gives more code examples: https://github.com/go-sql-driver/mysql/wiki/Examples
The Exec() function is for executing SQL that has no result set, like INSERT, UPDATE, DELETE. There are other functions in the Go SQL driver like Query() and QueryRow() that also accept parameter arguments. You'd use these if your SQL returns a result set.
I am trying to implement SQL insertion protection to SQL select statements using RMySQL. I have been trying to prepend an escape character (i.e. a backslash) in front of each risk character - i.e. a quote (" or ') or backslash (\). I am using the RMySQL function, dbEscapeStrings which appears to be similar to PHP's mysql_real_escape_string function.
I suspect I am missing something very obvious but, as MySQL requires character strings in the WHERE statement to be enclosed by quotes, using dbEscapeStrings to apply escape characters to quotes in the select statement is throwing an error blocking all string queries, not just the injection attacks. For example,
user <- "'peter'"
tmp <- sprintf("select * from users where username = %s", user)
sql <- dbEscapeStrings(con, tmp)
dbGetQuery(con, sql)
dbEscapeStrings inserts a double backslash in front of each quote (i.e. the sql variable produced is "select * from users where username = \\'peter\\'") which throws a syntax error on the MySQL server when dbGetQuery is run.
Any suggestions appreciated on how to get the above to work or implement an alternative SQL insertion protection using RMySQL? Does RMySQL provide for using prepared statements that could prevent insertion attacks?
The safest (and easiest) way would be to use a parameterised query (this isn't available on the CRAN release, but is in the dev version)
dbGetQuery(myconnection, "SELECT * FROM users WHERE username = ?", list(user))
I'm trying to modify and existing Access application to use MySQL as a database via ODBC with the minimal amount of recoding.
The current code will often insert a new record using DAO then obtain the ID by using LastModified. This doesn't work with MySQL. Instead I'm trying to use the approach using
SELECT * FROM tbl_name WHERE auto_col IS NULL
Suggested for Access in the MySQL documentation. However if I set up a sample table consisting of just an id and text data field and execute this
CurrentDb.Execute ("INSERT INTO tbl_scratch (DATA) VALUES ('X')")
Set rst = CurrentDb.OpenRecordset("SELECT id FROM tbl_scratch WHERE id IS NULL")
myid = rst!id
Id is returned as null. However if I execute
INSERT INTO tbl_scratch (DATA) VALUES ('X');
SELECT id FROM tbl_scratch WHERE id IS NULL;
using a direct MySQL editor then id is returned correctly, so my database and approach is fine but my implementation inside Access must be incorrect. Frustratingly the MySQL documentation gives the SQL statement to retrieve the id as an example that works in Access (as it states LAST_INSERT_ID() doesn't) but gives no further details.
How might I fix this?
Solved (and blogged) as below
I've been implementing an upgrade for a set of Access databases to replace the Access file database with MySQL linked by ODBC. Everything seems to be going remarkably smoothly except for the common concept of inserting a record into a table with an auto-increment id column and retrieving the value of the id just created. Of course on PHP or the like one would just use the
SELECT LAST_INSERT_ID()
Function to retrieve the ID. However the MySQL documentation itself says that this doesn't work for certain ODBC applications such as Delphi or Access and suggests using
SELECT * FROM tbl WHERE auto IS NULL;
Unfortunately this simply didn't work for me when called from inside the Access application I was working with, and there seems to be several comments around the web that indeed this is unreliable as Access may drop the data connection and reconnect behind the scenes - thus invalidating the call.
As an alternative I decided to use a MySQL function to add a blank record to the table, returning the id which Access would then use to update the record (which fits well with the code-style of the existing application). Unfortunately this apparently straightforward work-around fails to be simple either as long-standing bugs in MySQL make finding valid code that can both send and return a variable something of a challenge. Several examples on the web will work within the limited domain of using either just IN or OUT variables but fail to work with both.
My final solution, which works on the MySQL 5.1 and Access 2003 combination I am deploying, is as follows
MySQL procedure
DELIMITER $$
CREATE
PROCEDURE `alicedata`.`sp_ins_identity`(IN tablename VARCHAR(50))
BEGIN
SET #result = 0;
SET #sqlproc = CONCAT("INSERT INTO ",tablename," () VALUES ();");
PREPARE s1 FROM #sqlproc;
EXECUTE s1;
DEALLOCATE PREPARE s1;
SELECT LAST_INSERT_ID();
END$$
This procedure is useful in that it will insert a row and return the id for any table where a row contains all null fields or non-null fields with defaults defined. To call this I use the following function:
Public Function InsertMySQLIdentityRow(DSN As String, Tablename As String) As Integer
On Error GoTo Err_InsertMySQLIdentity
Dim cnnSQL As New ADODB.Connection
Dim cmdSQL As ADODB.Command
Dim pid As Integer
Dim rs
Dim strSQL As String
' initialize
pid = 0
' set up ADO connection
Set cnnSQL = New ADODB.Connection
cnnSQL.Open DSN
' execute the procedure - note that assembling by parameter fails to handle IN or OUT correctly
Set cmdSQL = New ADODB.Command
cmdSQL.ActiveConnection = cnnSQL
strSQL = "call sp_ins_identity('" & Tablename & "');"
Set rs = CreateObject("ADODB.Recordset")
Set rs = cnnSQL.Execute(strSQL)
If Not rs.EOF Then
pid = rs(0)
End If
' clean up
Set rs = Nothing
Set cmdSQL = Nothing
cnnSQL.Close
Set cnnSQL = Nothing
Exit_InsertMySQLIdentity:
InsertMySQLIdentityRow = pid
Exit Function
Err_InsertMySQLIdentity:
MsgBox Err.Number & Err.Description
Resume Exit_InsertMySQLIdentity
End Function
This code is somewhat unusual in that normally, on MSSQL, you would use a parametrized procedure call, but due to bugs in the MySQL ODBC (or at least incompatibilities with Access) the above seems to be the only way that allows both data to be passed and returned.
I have a text file with a few SQL statements in it that I want to run
on an Access database. I thought that should be possible with Access'
Query Editor. So, I go into this editor and paste the statements:
insert into aFewYears (yr) values ('2000')
insert into aFewYears (yr) values ('2001')
insert into aFewYears (yr) values ('2002')
insert into aFewYears (yr) values ('2003')
Trying to run them (by hitting the red exclamation mark) I receive a
Missing semicolon (;) at end of SQL statement.
This could be taken as an indication that the editor would allow to execute
multiple statements. So, I change the statements and append such a
semicolon at the end:
insert into aFewYears (yr) values ('2000');
insert into aFewYears (yr) values ('2001');
insert into aFewYears (yr) values ('2002');
insert into aFewYears (yr) values ('2003');
Then I get a
Characters found after end of SQL statement.
which probably could be taken as an indication that it is not possible
to execute multiple statements.
Ok, so the question: is it possible to execute multiple statements in the
query editor, or is it possible to somehow batch-execute sql statements in a
file in/on/against Access.
Thanks / Rene
edit The insert statements were used as an example and I realize that they are less than perfect, because they all go to the same table and such a thing can obviously somehow be solved by using one statement that has a union or something. In my actual case that I am trying to solve, the file contains not only insert statements but also create table statements and insert statements with different underlying tables. So I hoped (and still hope) that there is something like my beloved SQL*Plus for Oracle that can execute a file with all kinds of SQL Statements.
You can easily write a bit code that will read in a file. You can either assume one sql statement per line, or assume the ;
So, assuming you have a text file such as:
insert into tblTest (t1) values ('2000');
update tbltest set t1 = '2222'
where id = 5;
insert into tblTest (t1,t2,t3)
values ('2001','2002','2003');
Note the in the above text file we free to have sql statements on more then one line.
the code you can use to read + run the above script is:
Sub SqlScripts()
Dim vSql As Variant
Dim vSqls As Variant
Dim strSql As String
Dim intF As Integer
intF = FreeFile()
Open "c:\sql.txt" For Input As #intF
strSql = input(LOF(intF), #intF)
Close intF
vSql = Split(strSql, ";")
On Error Resume Next
For Each vSqls In vSql
CurrentDb.Execute vSqls
Next
End Sub
You could expand on placing some error msg if the one statement don't work, such as
if err.number <> 0 then
debug.print "sql err" & err.Descripiton & "-->" vSqls
end dif
Regardless, the above split() and string read does alow your sql to be on more then one line...
Unfortunately, AFAIK you cannot run multiple SQL statements under one named query in Access in the traditional sense.
You can make several queries, then string them together with VBA (DoCmd.OpenQuery if memory serves).
You can also string a bunch of things together with UNION if you wish.
Better just create a XLSX file with field names on top row.
Create it manually or using Mockaroo.
Export it to Excel(or CSV) and then import it to Access using New Data Source -> From File
IMHO it's the best and most performant way to do it in Access.
"I hoped (and still hope) that there is something like my beloved SQL*Plus for Oracle that can execute a file with all kinds of SQL Statements."
If you're looking for a simple program that can import a file and execute the SQL statements in it, take a look at DBWConsole (freeware). I have used it to process DDL scripts (table schema) as well as action queries. It does not return data sets so it's not useful for SELECT queries. It supports single line comments prefixed by -- but not multi-line comments wrapped in /* */. It supports command line parameters.
If you want an interactive UI like Oracle SQL Developer or SSMS for Access then Matthew Lock's reference to WinSQL is what you should try.
You might find it better to use a 3rd party program to enter the queries into Access such as WinSQL I think from memory WinSQL supports multiple queries via it's batch feature.
I ultimately found it easier to just write a program in perl to do bulk INSERTS into an Access via ODBC. You could use vbscript or any language that supports ODBC though.
You can then do anything you like and have your own complicated logic to handle the importing.
create a macro like this
Option Compare Database
Sub a()
DoCmd.RunSQL "DELETE * from TABLENAME where CONDITIONS"
DoCmd.RunSQL "DELETE * from TABLENAME where CONDITIONS"
End Sub