Unable to get MS SQL Server paths using python - python

I am trying to get sql server data and log path using SERVERPROPERTY.
When I run below stmt in SSMS, I get paths.
SELECT SERVERPROPERTY('InstanceDefaultLogPath') ,SERVERPROPERTY('InstanceDefaultDataPath')
But when I try to run the same query from python using pyodbc. it gives me:
result = connsql.cursor().execute(query).fetchone()
pyodbc.ProgrammingError: ('ODBC SQL type -150 is not yet supported. column-index=0 type=-150', 'HY106')
Any idea how to get the paths in python?
Code:
def getSQLServerPath(self):
try:
print("Into function..")
connsql = self.sql_connection()
query = "SELECT SERVERPROPERTY('InstanceDefaultLogPath') ,SERVERPROPERTY('InstanceDefaultDataPath') "
result = connsql.cursor().execute(query).fetchone()
print(result)
connsql.cursor().commit()
connsql.close()
# return path
except Exception:
logging.exception("getSQLServerPath function: Something went wrong.")

The error is actually telling you the problem here, it's the data type being returned that's the problem. The expression SERVERPROPERTY('InstanceDefaultLogPath') returns the data type sql_variant, which almost nothing apart from SQL Server supports. You can check this with the below SQL:
SELECT system_type_name
FROM sys.dm_exec_describe_first_result_set('SELECT SERVERPROPERTY(''InstanceDefaultLogPath'');',NULL, NULL);
As a result, you need to explicitly CONVERT the values to datatypes that ODBC does support. As these are both file paths, an nvarchar would seem the correct choice:
SELECT CONVERT(nvarchar(260),SERVERPROPERTY('InstanceDefaultLogPath')) AS InstanceDefaultLogPath,
CONVERT(nvarchar(260),SERVERPROPERTY('InstanceDefaultDataPath')) AS InstanceDefaultDataPath;

Related

Pyodbc doesn't run the procedure correctly without even throwing any error [duplicate]

I can't figure out what's wrong with the following code,
The syntax IS ok (checked with SQL Management Studio), i have access as i should so that works too.. but for some reason as soon as i try to create a table via PyODBC then it stops working.
import pyodbc
def SQL(QUERY, target = '...', DB = '...'):
cnxn = pyodbc.connect('DRIVER={SQL Server};SERVER=' + target + DB+';UID=user;PWD=pass')
cursor = cnxn.cursor()
cursor.execute(QUERY)
cpn = []
for row in cursor:
cpn.append(row)
return cpn
print SQL("CREATE TABLE dbo.Approvals (ID SMALLINT NOT NULL IDENTITY PRIMARY KEY, HostName char(120));")
It fails with:
Traceback (most recent call last):
File "test_sql.py", line 25, in <module>
print SQL("CREATE TABLE dbo.Approvals (ID SMALLINT NOT NULL IDENTITY PRIMARY KEY, HostName char(120));")
File "test_sql.py", line 20, in SQL
for row in cursor:
pyodbc.ProgrammingError: No results. Previous SQL was not a query.
Anyone have any idea to why this is?
I got a "SQL Server" driver installed (it's default), running Windows 7 against a Windows 2008 SQL Server environment (Not a express database).
Just in case some lonely net nomad comes across this issue, the solution by Torxed didn't work for me. But the following worked for me.
I was calling an SP which inserts some values into a table and then returns some data back. Just add the following to the SP :
SET NOCOUNT ON
It'll work just fine :)
The Python code :
query = "exec dbo.get_process_id " + str(provider_id) + ", 0"
cursor.execute(query)
row = cursor.fetchone()
process_id = row[0]
The SP :
USE [DBNAME]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER procedure [dbo].[GET_PROCESS_ID](
#PROVIDER_ID INT,
#PROCESS_ID INT OUTPUT
)
AS
BEGIN
SET NOCOUNT ON
INSERT INTO processes(provider_id) values(#PROVIDER_ID)
SET #PROCESS_ID= SCOPE_IDENTITY()
SELECT #PROCESS_ID AS PROCESS_ID
END
Using the "SET NOCOUNT ON" value at the top of the script will not always be sufficient to solve the problem.
In my case, it was also necessary to remove this line:
Use DatabaseName;
Database was SQL Server 2012,
Python 3.7,
SQL Alchemy 1.3.8
Hope this helps somebody.
I got this because I was reusing a cursor that I was looping over:
rows = cursor.execute(...)
for row in rows:
# run query that returns nothing
cursor.execute(...)
# next iteration of this loop will throw 'Previous SQL' error when it tries to fetch next row because we re-used the cursor with a query that returned nothing
Use 2 different cursors instead
rows = cursor1.execute(...)
for row in rows:
cursor2.execute(...)
or get all results of the first cursor before using it again:
Use 2 different cursors instead
rows = cursor.execute(...)
for row in list(rows):
cursor.execute(...)
As others covered, SET NOCOUNT ON will take care of extra resultsets inside a stored procedure, however other things can also cause extra output that NOCOUNT will not prevent (and pyodbc will see as a resultset) such as forgetting to remove a print statement after debugging your stored procedure.
As Travis and others have mentioned, other things can also cause extra output that SET NOCOUNT ON will not prevent.
I had SET NOCOUNT ON at the start of my procedure but was receiving warning messages in my results set.
I set ansi warnings off at the beginning of my script in order to remove the error messages.
SET ANSI_WARNINGS OFF
Hopefully this helps someone.
If your stored procedure calls RAISERROR, pyodbc may create a set for that message.
CREATE PROCEDURE some_sp
AS
BEGIN
RAISERROR ('Some error!', 1, 1) WITH NOWAIT
RETURN 777
END
In python, you need to skip the first sets until you find one containing some results (see https://github.com/mkleehammer/pyodbc/issues/673#issuecomment-631206107 for details).
sql = """
SET NOCOUNT ON;
SET ANSI_WARNINGS OFF;
DECLARE #ret int;
EXEC #ret = some_sp;
SELECT #ret as ret;
"""
cursor = con.cursor()
cursor.execute(sql)
rows = None
#this section will only return the last result from the query
while cursor.nextset():
try:
rows = cursor.fetchall()
except Exception as e:
print("Skipping non rs message: {}".format(e))
continue
row = rows[0]
print(row[0]) # 777.
I think the root cause of the issue described above might be related with the fact that you receive the same error message when you execute for example a DELETE query which will not return a result. So if you run
result = cursor.fetchall()
you get this error, because a DELETE operation by definition does not return anything. Try to catch the exception as recommended here: How to check if a result set is empty?
In case your SQL is not Stored Proc.
usage of 'xyz != NULL' in query, will give the same error i.e. "pyodbc.ProgrammingError: No results. Previous SQL was not a query."
Use 'is not null' instead.
First off:
if you're running a Windows SQL Server 2008, use the "Native Client" that is included with the installation of the SQL software (it gets installed with the database and Toolkits so you need to install the SQL Management applicaton from Microsoft)
Secondly:
Use "Trusted_Connection=yes" in your SQL connection statement:
cnxn = pyodbc.connect('DRIVER={SQL Server Native Client 10.0};SERVER=ServerAddress;DATABASE=my_db;Trusted_Connection=yes')
This should do the trick!
I have solved this problem by splitting the use database and sql query into two execute statements.

pypyodbc error 'Associated statement is not prepared'

I am trying to create an 'upsert' function for pypyodbc SQL Server. I have validated that the query built up will run in SSMS with the desired outcome, but when trying to execute and commit with pypyodbc I receive the following error: 'HY007', '[HY007] [Microsoft][ODBC SQL Server Driver]Associated statement is not prepared'.
Here is the upsert function:
def sql_upsert(sql_connection, table, key_field, key_value, **kwargs):
keys = ["{key}".format(key=k) for k in kwargs]
values = ["'{value}'".format(value=v) for v in kwargs.values()]
update_columns = ["{key} = '{value}'".format(key=k, value=v) for k, v in kwargs.items()]
sql = list()
#update
sql.append("UPDATE {table} SET ".format(table=table))
sql.append(", ".join(update_columns))
sql.append(" WHERE {} = '{}'".format(key_field, key_value))
sql.append(" IF ##ROWCOUNT=0 BEGIN ")
# insert
sql.append("INSERT INTO {table} (".format(table=table))
sql.append(", ".join(keys))
sql.append(") VALUES (")
sql.append(", ".join(values))
sql.append(")")
sql.append(" END")
query = "".join(sql)
print(query)
The function builds up a query string in a format based on this other thread How to insert or update using single query?
Here is an example of the output:
UPDATE test SET name='john' WHERE id=3012
IF ##ROWCOUNT=0 BEGIN
INSERT INTO test(name) VALUES('john')
END
The error message you cited is produced by the ancient "SQL Server" ODBC driver that ships as part of Windows. A more up-to-date driver version like "ODBC Driver 17 for SQL Server" should produce a meaningful error message.
If you look here or here you'll see people complaining about this over a decade ago.
Apparently SQL Server's ODBC driver returns that error when you're executing two statements that fail due to a field value being too long, or perhaps due to foreign key violations.
Using SSMS to see which statement causes this problem, or better - stop using ODBC and use pymssql
This error may also come when you don't give correct permissions to stored procedure
Go the SQL server --> Right click on your sp-->properties-->permissions
Add required users and roles which are going to execute this sp
This may help resolving the issue

Too few parameters error, while no parameters placeholders used

I am trying to execute SQL query within Access database using PYODBC and I get following error:
pyodbc.Error: ('07002', '[07002] [Microsoft][ODBC Microsoft Access Driver]
Too few parameters. Expected 1. (-3010) (SQLExecDirectW)')
The problem is that I am not using any additional parameters. Here is the code:
access_con_string = r"Driver={};Dbq={};".format(driver, base)
cnn = pyodbc.connect(access_con_string)
db_cursor = cnn.cursor()
expression = """SELECT F_ARODES.ARODES_INT_NUM, F_ARODES.TEMP_ADRESS_FOREST,F_AROD_LAND_USE.ARODES_INT_NUM, F_ARODES.ARODES_TYP_CD
FROM F_ARODES LEFT JOIN F_AROD_LAND_USE ON F_ARODES.ARODES_INT_NUM = F_AROD_LAND_USE.ARODES_INT_NUM
WHERE (((F_AROD_LAND_USE.ARODES_INT_NUM) Is Null) AND ((F_ARODES.ARODES_TYP_CD)="wydziel") AND ((F_ARODES.TEMP_ACT_ADRESS)=True));"""
db_cursor.execute(expression)
Query itself, if used inside MS-Access works fine. Also, connection is OK, as other queries are executed properly.
What am I doing wrong?
Constants in such queries are problematic - you never know the exact underlying syntax for booleans, strings etc. - even if it works in MS-Access, it can be different inside the intermediary library you're using.
The safest way is to extract them as parameters anyway:
expression = """SELECT F_ARODES.ARODES_INT_NUM, F_ARODES.TEMP_ADRESS_FOREST,F_AROD_LAND_USE.ARODES_INT_NUM, F_ARODES.ARODES_TYP_CD FROM F_ARODES LEFT JOIN F_AROD_LAND_USE ON F_ARODES.ARODES_INT_NUM = F_AROD_LAND_USE.ARODES_INT_NUM WHERE (((F_AROD_LAND_USE.ARODES_INT_NUM) Is Null)
AND ((F_ARODES.ARODES_TYP_CD)=?) AND ((F_ARODES.TEMP_ACT_ADRESS)=?));"""
db_cursor.execute(expression, "wydziel", True)
I had a similar problem, with an update I was trying to perform with pyodbc. When executed in Access, the query worked fine, same for when using the application (it allows some queries from within the app). But when ran in python with pyodbc the same text would throw errors. I determined the problem is the double quote (OP's query has a set of them as well). The query began to work when I replaced them with single quotes.
This does not work:
Update ApplicationStandards Set ShortCutKey = "I" Where ShortName = "ISO"
This does:
Update ApplicationStandards Set ShortCutKey = 'I' Where ShortName = 'ISO'

python pyodbc error with query that works fine against ms sqlserver

In SQL Server Management Studio I can run this query:
select a.foo, colX, colY, colZ from tblA a
join tblB b
on (a.foo = b.foo)
Running the same query through pyodbc in python script, I get:
pyodbc.ProgrammingError: ('42000', '[42000] [Microsoft][ODBC SQL Server Driver][
SQL Server]"b" is not a recognized table hints option. If it is intended as a p
arameter to a table-valued function or to the CHANGETABLE function, ensure that
your database compatibility mode is set to 90. (321) (SQLExecDirectW)')
Can anyone explain why this query works directly against SQL Server but there seems to be a problem with the table alias when using pyodbc?
This is what I've been able to find out:
SQL Server running on Microsoft Windows NT 6.1 (7601)
SQL Server version is 11.0.5532.0
pyodbc library: pyodbc-3.0.7.win32-py2.7
1) I do not know how to produce a reproducible example and I'm not a DBA; I can just about get by in SQL. 2) I cannot create tables on this database. However: I can do the full query with 3 joins / 3 aliases works fine using the RODBC r package and the sqlQuery() call against the same db. Therefore the culprit is pyodbc so I will try to find a python dev to try reproduce and post back the findings.
UPDATE + FIX
The issue is caused by concatenation errors:
Got python developer help to find and print raw sql before it was executed in python:
select a.foo, colX, colY, colZ from tblA a join tblB bon (a.foo = b.foo)
...and similar issue at each join, caused by missing whitespace within the sql string:
sql = "select a.foo, colX, colY, colZ from tblA a "
sql += "join tblB" #no whitespace at end of string
sql += "on (a.foo = b.foo)" #runs into next segment.
Worked in r with RODBC because string in r built like:
sql = "
...full query...
"
...with no opportunity for concatenation errors.

Python MySQLdb Error 1062 exception not working

I am trying to update datasets in my MySQL database with a Python 2.7 script. I am updating a field, that has the unique option enabled. When I try to add a duplicate entry, Python does not give me an error message.
connection = MySQLdb.connect(
host=DB_HOST,
db=DB_DB,
user='root', passwd='',
charset="utf8"
)
cur = connection.cursor()
sql = "UPDATE type SET article_code='Duplicate_Code' where id=9"
try:
cur.execute(sql)
connection.commit()
print "No ERROR"
except:
print "ERROR"
connection.close()
OUTPUT: No ERROR
The dataset, however, is not updated. If I enter the same SQL Code in the phpMyAdmin interface, I get the following message:
#1062 - Duplicate entry 'Duplicate_Code' for key 'article_code'
I would like my Python script to go into the except option.
What am I doing wrong here?
if you read through examples here: http://mysql-python.sourceforge.net/MySQLdb.html#mysqldb
it states that you should use something more akin to this:
cur.execute("""UPDATE type SET article_code=%s where id=%s""", ('Duplicate_Code', 9))
as the 9 in your query will get converted to '9' which probably does not exist as a key hence your query does nothing, meaning always success.
Also as a side note it's advisable to always use this pattern as this way MySQLdb will escape your values whereas otherwise you/others may be tempted to do something that will open you up to SQL injections.
Edited out 'Duplicate_Code' from query body

Categories

Resources