Readonly connection with mssql and pyodbc - python

I'm having some trouble with trying to create a read-only connection and not sure if it's a bug or my error.
Pyodbc's documentation indicates that it is possible to create a readonly connection. See https://mkleehammer.github.io/pyodbc/api-module.html
When running the following though I receive no errors and it runs as if the READONLY keyword was not present at all (the update goes through).
import pyodbc
readonly_conn_str = "DRIVER={SQL Server Native Client 10.0};SERVER=...;DATABASE=...;UID=...;PWD=...;READONLY=True;"
conn = pyodbc.connect(readonly_conn_str)
cursor = conn.cursor()
result = cursor.execute(update_query)
cursor.commit()
print(result.rowcount)
If I try using function keywords I get the same thing.
...
conn = pyodbc.connect(conn_str, readonly=True)
...

When we invoke pyodbc.connect with readonly=True, pyodbc dutifully calls the ODBC function
ret = SQLSetConnectAttr(cnxn->hdbc, SQL_ATTR_ACCESS_MODE, (SQLPOINTER)SQL_MODE_READ_ONLY, 0);
SQL_MODE_READ_ONLY is one of the standard ODBC SQL_ATTR_ACCESS_MODE attributes. How that actually affects the behaviour of the ODBC connection is up to the ODBC driver. As stated in the ODBC documentation:
SQL_MODE_READ_ONLY is used by the driver or data source as an indicator that the connection is not required to support SQL statements that cause updates to occur. This mode can be used to optimize locking strategies, transaction management, or other areas as appropriate to the driver or data source. The driver is not required to prevent such statements from being submitted to the data source. The behavior of the driver and data source when asked to process SQL statements that are not read-only during a read-only connection is implementation-defined.
In other words, pyodbc has passed the "read only" attribute to the ODBC driver. It is up to the driver to decide whether it should be interpreted as a hint, a hard limitation, or something to be simply ignored.

Related

Pyodbc to SQLAlchemy connection string for Sage 50

I am trying to switch a pyodbc connection to sqlalchemy engine. My working pyodbc connection is:
con = pyodbc.connect('DSN=SageLine50v23;UID=#####;PWD=#####;')
This is what I've tried.
con = create_engine('pyodbc://'+username+':'+password+'#'+url+'/'+db_name+'?driver=SageLine50v23')
I am trying to connect to my Sage 50 accounting data but just can't work out how to build the connection string. This is where I downloaded the odbc driver https://my.sage.co.uk/public/help/askarticle.aspx?articleid=19136.
I got some orginal help for the pyodbc connection using this website (which is working) https://www.cdata.com/kb/tech/sageuk-odbc-python-linux.rst but would like to use SQLAlchemy for it connection with pandas. Any ideas? Assume the issue is with this part pyodbc://
According to this thread Sage 50 uses MySQL to store its data. However, Sage also provides its own ODBC driver which may or may not use the same SQL dialect as MySQL itself.
SQLAlchemy needs to know which SQL dialect to use, so you could try using the mysql+pyodbc://... prefix for your connection URI. If that doesn't work (presumably because "Sage SQL" is too different from "MySQL SQL") then you may want to ask Sage support if they know of a SQLAlchemy dialect for their product.

why should we set the local_infile=1 in sqlalchemy to load local file? Load file not allowed issue in sqlalchemy

I am using sqlalchemy to connect to MySQL database and found a strange behavior.
If I query
LOAD DATA LOCAL INFILE
'C:\\\\Temp\\\\JaydenW\\\\iata_processing\\\\icer\\\\rename\\\\ICER_2017-10-
12T09033
7Z023870.csv
It pops an error:
sqlalchemy.exc.InternalError: (pymysql.err.InternalError) (1148, u'The used
command is not allowed with this MySQL versi
on') [SQL: u"LOAD DATA LOCAL INFILE
'C:\\\\Temp\\\\JaydenW\\\\iata_processing\\\\icer\\\\rename\\\\ICER_2017-10-
12T090337Z023870.csv' INTO TABLE genie_etl.iata_icer_etl LINES TERMINATED BY
'\\n'
IGNORE 1 Lines (rtxt);"] (Background on this error at:
http://sqlalche.me/e/2j85)
And I find the reason is that:
I need to set the parameter as
args = "mysql+pymysql://"+username+":"+password+"#"+hostname+"/"+database+"?
local_infile=1"
If I use MySQL official connection library. I do not need to do so.
myConnection = MySQLdb.connect(host=hostname, user=username, passwd=password, db=database)
Can anyone help me to understand the difference between the two mechanisms?
The reason is that the mechanisms use different drivers.
In SQLAlchemy you appear to be using the pymysql engine, which uses the PyMySQL Connection class to create the DB connection. That one requires the user to explicitly pass the local_infile parameter if they want to use the LOAD DATA LOCAL command.
The other example uses MySQLdb, which is basically a wrapper around the MySQL C API (and to my knowledge not the official connection library; that would be MySQL Connector Python, which is also available on SQLAlchemy as mysqlconnector). This one apparently creates the connection in a way that the LOAD DATA LOCAL is enabled by default.

pyodbc always connects to master database

I am using pyodbc to connect to an azure sql database. My source code looks like this:
import pyodbc
server = 'sqlserver.database.windows.net'
database = 'database'
username = 'username'
password = 'password'
conn= pyodbc.connect('DRIVER={ODBC Driver 13 for SQL Server}'+';SERVER='+server+';PORT=1443;DATABASE='+database+';UID='+username+';PWD='+ password ';Encrypt=yes;TrustServerCertificate=no;Connection Timeout=30;')
cursor = conn.cursor()
cursor.execute("query")
I am able to connect to a sql database. The only thing which is not working properly is that pyodbc does not connect to the database I have specified in the database variable. It always connects to the master database.
What I have tried so far is to print the name of the databases on the target sql server using SELECT * FROM sys.databases while being connected to the master database. I was able to see the database I am trying to connect to. Anyone got an idea what goes wrong in my source code?
In general, I would assume that the connection string needs to be different in your case.
As per pyodbc docs:
[...]the most important thing to remember is that pyodbc does not even look at the connection string. It is passed directly to the database driver unmodified (through SQLDriverConnect). Connection strings are therefore driver-specific and all ODBC connection string documentation should be valid.
https://github.com/mkleehammer/pyodbc/wiki/Connecting-to-databases
However, since you are connecting to db OK and your connection string seems to be ignored, I would say that if you are using Windows then the connection parameters seem likely to be defined in the ODBC DSN, which can be changed in Control panel. If that is the case, and you have ODBC parameters defined in DSN, most likely your connection string is ignored, except for the choice of the DSN.

SQLAlchemy orm styles, how to make special drive to your connection string

I use pypy, pypyodbc and SQLAlchemy.
I have problem of odbc connections.
I use:
engine = create_engine('mssql+pyodbc://dbuser:dbpasswd#localhost/dbname', echo = False)
Session = sessionmaker(bind=engine)
style try to connect the database.
The error is:
C:\pypy\site-packages\sqlalchemy\connectors\pypyodbc.py:82: SAWarning: No driver
name specified; this is expected by PyODBC when using DSN-less connections
"No driver name specified; "
The reason of this error, I find the connect parameter
DRIVER={SQL Server Native Client}
is not transmit to the engine, in other word, I want to know how to set DRIVER string for this connections style for SQLALchemy.
I've run into the same issue with Sybase ASE and after looking at the pyodbc.py source code you can pass GET-like parameters in your url. As an example (working for me):
sybase+pyodbc://username:password#hostname:5000/dbname?driver=Adaptive Server Enterprise
It's also sort of documented here with the connection string syntax being dialect://user:password#host/dbname[?key=value..]
Hope that helps
You may have also be been suffering from updating your SqlAlchemy version. As of the latest release (v1.0) you need to explicitly define your driver in the connection string for Microsoft SQL Server.
See:
Changed in version 1.0.0: Hostname-based PyODBC connections now require the SQL Server driver name specified explicitly. SQLAlchemy cannot choose an optimal default here as it varies based on platform and installed drivers.
http://docs.sqlalchemy.org/en/latest/dialects/mssql.html#hostname-connections
See:
Connecting to database using SQLAlchemy
I had this error message and fixed it by adding this to the end of the connection string:
"?driver=SQL+Server+Native+Client+10.0"

Connect to SQL Server instance using pymssql

I'm attempting to connect to a SQL Server instance from a Windows box using pymssql (version 2.0.0b1-dev-20111019 with Python 2.7.1). I've tried the most basic approach from the console:
import pymssql
c = pymssql.connect(host = r'servername\instance',
user = 'username',
password = 'userpassword')
In response to this, I get the very helpful error: InterfaceError: Connection to the database failed for an unknown reason.
I am reasonably confident that the connection information is correct, as it works when I use adodbapi, with the following commands:
import adodbapi
c = adodbapi.connect(r'Provider=sqloledb;Data Source=servername\instance;User ID=username;password=userpassword;'
c.close
I've tried adding the port number to the host parameter, with the same result. Does anyone have a suggestion on how to go about resolving this issue?
Incidentally, I've read the responses at "Unable to connect to SQL Server via pymssql". The OP eventually resolved his issue by correctly configuring FreeTDS, which, from what I can tell, is not used by pymssql on Windows.
Based on #cha0site's recommendation, I have tried using just the hostname, rather than the hostname and instance. This resulted in the same error, but it seemed to take longer to generate the error (though the traceback still indicates the same line). The reason I have been specifying the instance is that I was not able to connect using SSMS unless I specified the instance, so I assumed that it would be necessary for other connections.
I've now also tried pymssql.connect(host='servername', user='username', password='userpassword', database='instance') with the same result (based on #Sid's comment). Based on the pymssql documentation, I believe the database parameter is used to specify the initial database that the user is to be connected to, rather than the instance.
Just to clarify, "instance" is the name provided during installation of SQL Server, not a database within that installation. It occurs to me that it's possible that pymssql does not support this notation, so I will look into re-configuring the SQL Server instance so that it is not required.
I've now re-installed SQL Server as a default instance, rather than a named instance, which allows me to connect without specifying the instance name. adodbapi still works (without /instance), but pymssql still returns the same error. I've also removed and re-installed pymssql from a freshly downloaded archive (still the same version).
Check your freetds.conf file and see if you have set the port 1219., then check again the connexion:
DB = pymssql.connect(host='DB',user='youruser',password='yourpwd',database='yourDBname')
Edit: example of my freetds.conf file Python:
host = 'IP'
port = 1219
To specify host=servername\instance or server=servername\instance, the SQL Server Browser service must be on the SQL Server machine.

Categories