Adding to an MS Access database using pyodbc - ms-access

I am trying to add to a table called Users in an Access database I have created. I am using the following code to do it (which has been copied from here):
import pyodbc
def createAccount():
conn = pyodbc.connect(r"Driver={Microsoft Access Driver (*.mdb, *.accdb)};DBQ=myPath\User Database.accdb;")
cursor = conn.cursor()
cursor.execute("""
INSERT INTO Users(Username, Password, Chips)
VALUES("User 5", "Pass 5", 7800)""")
conn.commit()
but I get this error:
pyodbc.Error: ('07002', '[07002] [Microsoft][ODBC Microsoft Access Driver] Too few parameters.
Expected 2. (-3010) (SQLExecDirectW)')
I've seen other posts which say to check spelling of all names used and there isn't anything wrong there. So why isn't this code working?

There are some subtle differences in default behaviour between Access SQL queries executed from within the Access application itself (MSACCESS.EXE) and queries executed from external applications via the Access ODBC driver or the Access OLEDB provider.
From within Microsoft Access itself, both double-quotes (") and single-quotes (') can be used to delimit string literals. This has been true since the earliest versions of Access.
However, the ODBC driver and OLEDB provider at least try to more closely conform to ANSI SQL, so single-quotes (') are used to delimit string literals while double-quotes (") are used to delimit table and column names. Therefore "User 5" will be interpreted as a column name or a parameter name depending on whether such a column actually exists.

Related

Difference in behavior on CONCAT_WS between systems

So, this is a simple situation but I wanted to understand what's causing this issue. I have the following code (modified for example):
SELECT `Transactions`.*, CONCAT_WS(" ", `People`.`first_name`, `People`.`last_name`) AS full_name ...
On my local machine I have:
Windows 10
Apache 2.4.25
PHP 7.4.11
MySQL 5.7.25
With that combination the following code works fine.
On the remote server I have:
Ubuntu 20.04.1 LTS
Apache 2.4.41
PHP 7.4.3
MySQL 8.0.19
So, I have a section which uses a data table and the data table uses server-side processing to obtain the information. On my local it shows the information correctly, but on my remote server I always got an empty array. So I tried executing the same SQL command in my remote server and I got this error:
#1054 - Unknown column ' ' in 'field list'
My SQL was correctly formed so I thought maybe the problem was related to the CONCAT_WS function.
So I decided to modify it to:
SELECT `Transactions`.*, CONCAT_WS(' ', `People`.`first_name`, `People`.`last_name`) AS full_name ...
I basically changed CONCAT_WS(" ", to CONCAT_WS(' ', and the code worked as intended.
I am not sure if this affects in some way, but is this a MySQL change in requirements for the usage of CONCAT_WS or something else?
Is it ok if I use it with single quotes elsewhere?
I suggest you run this on both systems:
SELECT ##sql_mode;
You will find that on your 8.0 server, the sql mode includes either the modes ANSI or ANSI_QUOTES.
Explanation:
The double-quotes have different meanings in MySQL depending on which sql mode is in effect.
By default, double-quotes are the same as single-quotes: they delimit a string literal.
If the sql mode is ANSI or ANSI_QUOTES, then the double-quotes delimit an identifier, acting like the back-ticks.
So the same code can behave differently on different MySQL instances. This has nothing to do with the difference between 5.7 and 8.0, because the sql mode behaves the same on these two versions. Neither version enables the ANSI or ANSI_QUOTES modes by default, so you or someone else must have enabled that mode on your 8.0 server.
This is why in this expression:
CONCAT_WS(" ", ...)
The first argument " " is treated as a string literal on one server, and on the other server it is treated as a column whose name is (which is legal SQL, even if it's weird).
It's safest to always use single-quotes to delimit a string literal, and to always use back-ticks to delimit an identifier.
Never use double-quotes for either case in MySQL, because your code that uses double-quotes in SQL queries will break if someone changes the sql mode.

Handling Japanese characters in query to MS access database (in RStudio)

I would like to know how to handle Japanese characters in a query to a Microsoft Access database. I am trying to use a query selecting variable names written in Japanese using the function odbcQuery from RODBC package in R.
I am working with Windows. My version of RStudio is 1.1.383, and my version of Access is 14.0.7015.1000 (32-bit).
I think R understands the Japanese characters in my query, but when I try to actually carry out the query I get the following error message:
> query <- "SELECT [LOA-FTD_1_5_1_CALCULATE_LOA_query].月日 FROM [LOA-FTD_1_5_1_CALCULATE_LOA_query]"
> sqlQuery(channel,query)
[1] "42000 -3100 [Microsoft][ODBC Microsoft Access Driver] Syntax error in query expression '[LOA-FTD_1_5_1_CALCULATE_LOA_query].<U+6708><U+65E5>'."
[2] "[RODBC] ERROR: Could not SQLExecDirect 'SELECT [LOA-FTD_1_5_1_CALCULATE_LOA_query].<U+6708><U+65E5> FROM [LOA-FTD_1_5_1_CALCULATE_LOA_query]'"
Here, 月日 was converted into U+6708 and U+65E5 in the error message. These are the UTF-8 codes for the two characters, so I guess the string is sent encoded in UTF-8 to MS Access, but MS Access is then unable to read it? Is MS Access even part of the process of carrying out the query?
So it must be an encoding issue, where RStudio and MS Access do not understand each other. When I looked at similar issues with Japanese characters, the problem was usually to display values in a table. Here the variable names are in Japanese, so the query does not work at all.
I am quite lost, so I am open to any idea or remark.
Thank you.
I found an answer that works for me in this post.
The trick (at least in my case) was to set locale to Japanese_Japan.932 before any data importing.
Here is the code for this command:
Sys.setlocale("LC_ALL", locale = "Japanese_Japan.932")
Then I imported my data from Access without having to change encoding, and the Japanese characters are displayed correctly in the resulting data frame. Moreover, this allows Japanese characters in the query to be understood.

Mysql jdbc connector Database Metadata quoted identifier issue

I was connecing to Mysql database via Mysql jdbc connector 5.1.15.
The table contains quoted identifiers( column name with special characters). I need to insert values into table. I was trying to get the Metadata of the database and tried to get the storesLowerCaseQuotedIdentifiers(), storesMixedCaseQuotedIdentifiers and storesUpperCaseQuotedIdentifiers.
Among the three storesMixedCaseQuotedIdentifiers returns false where as storesLowerCaseQuotedIdentifiers and .storesUpperCaseQuotedIdentifiers returns true. If Mysql supports both upper and lower case quoted identifiers then it can return true for MixedCaseQuoted identifier. Why it is returning true for both Loqwer and UppercasequotedIdentifiers?
Thanks in advance. Correct me if I am wrong.

MS Access CREATE TABLE "WITH COMPRESSION" syntax?

I'm trying to write a CREATE TABLE statement for Microsoft Access (to be executed via a C# / .NET app using an OleDbConnection), utilizing the WITH COMPRESSION attribute to cause character columns (TEXT) to be created using single-byte characters rather than Unicode double-byte characters, as documented on MSDN here.
The WITH COMPRESSION attribute can be used only with the CHARACTER and MEMO (also known as TEXT) data types and their synonyms.
The WITH COMPRESSION attribute was added for CHARACTER columns because of the change to the Unicode character representation format. Unicode characters uniformly require two bytes for each character. For existing Microsoft® Jet databases that contain predominately character data, this could mean that the database file would nearly double in size when converted to the Microsoft Access database engine format. However, Unicode representation of many character sets, those formerly denoted as Single-Byte Character Sets (SBCS) can easily be compressed to a single byte. If you define a CHARACTER column with this attribute, data will automatically be compressed as it is stored and uncompressed when retrieved from the column.
When I try to execute the following statement (which I believe to be syntactically correct per MSDN) via an OleDbConnection, I get a syntax error.
CREATE TABLE [Foo] ([COL1] TEXT(255) WITH COMPRESSION)
Likewise, executing the same statement directly within MS Access 2013 as a query gives a syntax error at WITH.
Executing
CurrentProject.Connection.Execute("CREATE TABLE [Foo1] ([COL1] TEXT(255) WITH COMPRESSION)")
from Access VBA does work, however.
If I take out the WITH COMPRESSION attribute, the statement executes without error both via OleDb and directly in MS Access.
Any ideas what I'm doing wrong?
My problem turned out to be a syntax error that wasn't reflected properly in my original question.
However, solving that problem revealed that the documentation for MS Access CREATE TABLE on MSDN https://msdn.microsoft.com/en-us/library/office/ff837200.aspx is incorrect regarding the sequence of attributes for the CREATE TABLE statement. According to the documentation, the syntax is:
CREATE [TEMPORARY] TABLE table (field1 type [(size)] [NOT NULL] [WITH COMPRESSION | WITH COMP] [index1] [, field2 type [(size)] [NOT NULL] [index2] [, …]] [, CONSTRAINT multifieldindex [, …]])
but in fact, [WITH COMPRESSION | WITH COMP] must appear before [NOT NULL] or you get a syntax error.
Additionally, it's not possible to execute the CREATE TABLE statement using the WITH COMPRESSION attribute from a query directly within MS Access. You have to either use VBA or (as in my case) an external program via OleDbConnection.
My experience with "WITH COMPRESSION" and MS-ACCESS 2013
Impossible to run such a script from query window.
Possible from VBA but with limitations:
currentdb.Execute "... WITH COMPRESSION" -> "Syntax error in CREATE
TABLE" CurrentProject.Connection.Execute " ..." - > Ok
I confirm what "Mr. T" says: WITH COMPRESSION must appear before NOT NULL

MS Access - prevent SQL injection in connection string

I have an Access database that must connect to Oracle programmatically to create a linked table. The connection string is of the form:
ODBC;Driver={Microsoft ODBC for Oracle};Pwd=<Password>;UID=<User>;Server=<Server>
Currently the login info is hardcoded.
I now have to have the tool connect to different databases. I was going to simply let the user enter the <User>, <Password>, and <Server> and then just concatenate it all together into a single connection string. I'm pretty sure this is SQL Injection safe because the connection doesn't actually exist at this point, but I'm not 100% certain - is this a valid concernt, and if so how would I sanitize these inputs (which come from free-form text fields)?
This is not called SQL Injection because the connection string doesn't allow execution of arbitrary SQL code.
If you are giving users access to the database from the desktop then SQL Injection probably isn't a very relevant concern anyway. Why would anyone bother trying to inject SQL through an application vulnerability when it's much easier for him just to create a connection himself using his valid credentials?
It appears that your concern is valid, as evidenced by the fact that ADO.NET has a set of Connection String Builder classes (though it's more accurate to call it "connection string injection" vs. "SQL injection" since there's no SQL involved). Since you're not using .NET, the next best option is input sanitization and escaping special characters. The MSDN reference on OLEDB connection string syntax states that:
To include values that contain a
semicolon, single-quote character, or
double-quote character, the value must
be enclosed in double quotes.
and
If the value contains both
single-quote and double-quote
characters, the quote character used
to enclose the value must be doubled
each time it occurs within the value.
This is a VBScript I put together which attempts to implement the two guidelines above:
Option Explicit
Dim pw, connStr, conn
pw = InputBox("Enter password")
' Sanitize double quotes in the input string
pw = Replace(pw, Chr(34), Chr(34) & Chr(34))
' Notice how pw is surrounded by double quote characters
connStr = "Provider=SQLOLEDB;Data Source=.\SQLEXPRESS;User ID=test_user;Password=" & Chr(34) & pw & Chr(34)
' Test the connection. We'll get a runtime error if it didn't work
Set conn = CreateObject("ADODB.Connection")
conn.Open connStr
conn.Close
WScript.Echo "OK!"
If my password were app"le'\, the connection string would end up as:
Provider=SQLOLEDB;Data Source=.\SQLEXPRESS;User ID=test_user;Password="app""le'\"
However, this doesn't work for all possible inputs. For example, the test script gives an error when the password contains a double quote before a semicolon. It could be that I'm interpreting the guidelines incorrectly. I'm not sure, but hopefully, this at least gets you started.