Problem with SELECT * in MySQL through ODBC from Microsoft SQL Server - mysql

I have a MySQL server as a linked server in Microsoft SQL Server 2008. For the link I use MySQL ODBC Connector version 5.1.8. When invoking queries using OPENQUERY (the only way I found of performing queries), problems occur. Simple queries, such as
SELECT * FROM OPENQUERY(MYSQL, 'SHOW TABLES')
work fine. Selection of individual columns, e.g.,
SELECT * FROM OPENQUERY(MYSQL, 'SELECT nr FROM letter')
works fine as well, but SELECT * syntax does not work. The query:
SELECT * FROM OPENQUERY(MYSQL, 'SELECT * FROM mytable')
raises an error:
Msg 7347, Level 16, State 1, Line 6
OLE DB provider 'MSDASQL' for linked
server 'MYSQL' returned data that does
not match expected data length for
column '[MSDASQL].let_nr'. The
(maximum) expected data length is 40,
while the returned data length is 0.
How can I make the SELECT * syntax work?

This problem happens if you are querying a MySQL linked server and the table you query has a datatype char(). This means fixed length and NOT varchar(). This happens when your fixed length field has a shorter string than the maximum length that SQL Server expected to get from the ODBC.
To fix this go to your MySQL server and change the datatype to varchar() leaving the length as it is. Example change char(10) to varchar(10).

Executing the following command before queries seems to help:
DBCC TRACEON(8765)
The error messages go away and queries seem to be working fine.
I'm not sure what it does though; I found it here: http://bugs.mysql.com/bug.php?id=46857
Strangely, SQL Server becomes unstable, stops responding to queries and finally crashes with scary-looking dumps in the logs a few minutes after several queries to the MySQL server. I am not sure if this has to do anything with the DBCC command, so I'm still interested in other possible solutions to this problem.

What I did to fix this since I can't modify the MySQL database structure is just create a view with a cast ex: CAST(call_history.calltype AS CHAR(8)) AS Calltype,
and select my view from MSSQL in my linked server.
The reason behind is that some strange types don't work well with the linked server (in my case the MySQL enum)

I found this
"The problem is that one of the fields
being returned is a blank or NULL CHAR
field. To resolve this in the Mysql
ODBC settings select the option "Pad
CHAR to Full Length"
Look at the last post here

An alternative would be to use the trim() function in your SELECT statement within OPENQUERY. The downside is you have to list each field individually, but what I did was create a view that calls OPENQUERY and then perfrom select * on the view.
Not ideal, but better than changing data types on tables!

Here is a crappy solution I came up with because I am unable to change the datatype to varchar as the db admin for the MySQL server is afraid it will cause issues with his scripts.
in my MySQL select query I run a case statement checking the character length of the string and add a filler character in front of the string "filling it up" to the max (in my case its a char(6)). then in the select statement of the openquery I strip the character back off.
Select replace(gradeid,'0','') as gradeid from openquery(LINKEDTOMYSQL, '
SELECT case when char_length(gradeid) = 0 then concat("000000", gradeID)
when char_length(gradeID) = 1 then concat("00000", gradeID)
when char_length(gradeID) = 2 then concat("0000", gradeID)
when char_length(gradeID) = 3 then concat("000", gradeID)
when char_length(gradeID) = 4 then concat("00", gradeID)
when char_length(gradeID) = 5 then concat("0", gradeID)
else gradeid end as gradeid
FROM sometableofmine')
it works but it probably is slower...
maybe you can make a MySQL function that will do the same logic, or come up with a more elegant solution.

I had the similar problem to this myself, which I resolved by wrapping the column-names in single ` style quotes.
Instead of...
column_name
...use...
`column_name`
Doing this helps the MySql query-engine should the column-name clash with a key or reserved-word.*
Instead of using SELECT * FROM TABLE_NAME, try to use all column names with quotes:
SELECT `column1`, `column2`, ... FROM TABLE_NAME
Example for normal datatype columns
SELECT * FROM OPENQUERY(MYSQL, 'SELECT `column1`, `column2`,...,`columnN` FROM mytable')
Example for ENUM datatype columns
SELECT * FROM OPENQUERY(MYSQL, 'SELECT `column1`, trim(`column2`) `column2`, `column3`,...,`columnN` FROM mytable')
*For those used to Sql Server, it is the MySql equivalent of wrapping a value in square-brackets, [ and ].

Related

"SELECT EXISTS" giving incorrect result, corrects upon resetting connection

I'm connected to a MySQL server on two different connections. On one of them, I can execute:
query = 'SELECT EXISTS(SELECT * FROM TABLE WHERE COLUMN = "VALUE")'
c.execute(query)
In: c.fetchall()[0][0]
Out: 1
Connected through a second machine, using identical values, I get the following:
query = 'SELECT EXISTS(SELECT * FROM TABLE WHERE COLUMN = "VALUE")'
c.execute(query)
In: c.fetchall()[0][0]
Out: 0
The only thing that seems to fix it is closing the connection and restarting it. It is causing some problems. Is there anything I can do to fix this?
Logically speaking, the same exists query with the same underlying data should return the same result on all machines. However, I see a problem with your query. You are using double quotes around VALUE, which you intend to be a string literal. This could be causing MySQL to view VALUE as an identifier (e.g. as a column). Try using single quotes instead:
query = "SELECT EXISTS (SELECT 1 FROM TABLE WHERE COLUMN = 'VALUE')"
c.execute(query)
In: c.fetchall()[0][0]

Common Table Expressions -- Using a Variable in the Predicate

I've written a common table expression to return hierarchical information and it seems to work without issue if I hard code a value into the WHERE statement. If I use a variable (even if the variable contains the same information as the hard coded value), I get the error The maximum recursion 100 has been exhausted before statement completion.
This is easier shown with a simple example (note, I haven't included the actual code for the CTE just to keep things clearer. If you think it's useful, I can certainly add it).
This Works
WITH Blder
AS
(-- CODE IS HERE )
SELECT
*
FROM Blder as b
WHERE b.PartNo = 'ABCDE';
This throws the Max Recursion Error
DECLARE #part CHAR(25);
SET #part = 'ABCDE'
WITH Blder
AS
(-- CODE IS HERE )
SELECT
*
FROM Blder as b
WHERE b.PartNo = #part;
Am I missing something silly? Or does the SQL engine handle hardcoded values and parameter values differently in this type of scenario?
Kindly put semicolon at the end of your variable assignment statement
SET #part ='ABCDE';
Your SELECT statement is written incorrectly: the SQL Server Query Optimizer is able to optimize away the potential cycle if fed the literal string, but not when it's fed a variable, which uses the plan that developed from the statistics.
SQL Server 2016 improved on the Query Optimizer, so if you could migrate your DB to SQL Server 2016 or newer, either with the DB compatibility level set to 130 or higher (for SQL Server 2016 and up), or have it kept at 100 (for SQL Server 2008) but with OPTION (USE HINT ('ENABLE_QUERY_OPTIMIZER_HOTFIXES')) added to the bottom of your SELECT statement, you should get the desired result without the max recursion error.
If you are stuck on SQL Server 2008, you could also add OPTION (RECOMPILE) to the bottom of your SELECT statement to create an ad hoc query plan that would be similar to the one that worked correctly.

BIRT - org.eclipse.birt.data.engine.odaconsumer.OdaDataException: Cannot get the result set metadata

In BIRT, When i try to fetch the records from my localhost, its working fine. But when i try to work with remote connection i am getting error as specified below:
Error :
org.eclipse.birt.data.engine.odaconsumer.OdaDataException: Cannot get the result set metadata.
org.eclipse.birt.report.data.oda.jdbc.JDBCException: SQL statement does not return a ResultSet object.
SQL error #1:Table 'test.TBLUSERS' doesn't exist ... 63 more
Caused by: com.mysql.jdbc.exceptions.MySQLSyntaxErrorException: Table 'testbms.TBLUSERS' doesn't exist
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:936)
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:2985)
Note:
Tablenames are automatically changing to capital letters, is that because of it.
Because client server is linux and is it acting with case sensitive.
But it displays column names but not the records. As soon as i click
on finish, i get the error as specified in the below images.
Reference Image:
As you can see in the above image, it has populated the table columns in the second row
Is their any special configurations need to be done for remote connection or am i doing anything wrong?
As you stated, it is probably a case of case-sensitivity:
http://dev.mysql.com/doc/refman/5.0/en/identifier-case-sensitivity.html
Although database and table names are not case sensitive on some
platforms, you should not refer to a given database or table using
different cases within the same statement. The following statement
would not work because it refers to a table both as my_table and as
MY_TABLE: mysql> SELECT * FROM my_table WHERE MY_TABLE.col=1;
If your development box isn't case sensitive then when you change the case of your tablename to match that on production you'll still be able to test. There might also be a way in MySQL using system tables. (See the following query for an example of querying to see if a table exists.):
SELECT count(*)
FROM information_schema.tables
WHERE table_schema = <schema-or-db-name>
AND table_name = <table-or-view-name>
but more realistically, your target database should be passed to your report through a variable that you can check in the scripting of the dataset. Set the "this.query" value to equal the appropriate query based on that variable's value.
E.G.:
if ( params["source_db"].value == "Server=myProductionAddress;Database=myProductionDB;Uid=myUsername;Pwd=myPassword;" )
{
this.query = "SELECT .... prodTableName";
}
else
{
this.query = "SELECT .... devTableName";
}

Hibernate SQL Injection

I'm auditing a project and I found a way to inject data in a query.
The project uses Hibernate and for this piece of code Session.createSqlQuery() and then a .list()
The SQL is something like : "SELECT * FROM tablename ORDER BY column XXXXXX"
XXXXXX can be modified using Fiddler. So I tried
SELECT * FROM tablename ORDER BY column DESC; truncate table tablename;
Unfortunately (well only for my injection attempt) it's not working and I'm getting :
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 'truncate table tablename'
My question is, since they're using createSQLQuery, are they safe from injection. If they're not, could you give me an example to highlight the issue.
I tried using %08 (Backspace character) thinking I would be able to delete previous query characters for example (It didn't work ;) )
Thanks.
After some research it seems I won't be able to modify data with this security hole, however using ORDER BY (CASE WHEN ...) would allow to "scan" the tables and the data.
Is the column name specified using a parameterized statement or are you just concatenating text?
ex: in perl::DBI, the drivers support the following syntax:
$dbh->do("SELECt * FROM asdf ORDER BY ?", undef, $order_by);
The ? there is a form of parameterized statement which sanitizes the input automatically.

SQL returns no rows via myODB, but in SQL there are rows

I have an SQL table called "tbl_einheit". phpmyadmin shows more than 14.000 rows in the table. When accessing via webpage, the table is empty "eof".
I minimized the SQL Statment, and deleted all WHERE, ORDER BY elements, so that simply
SELECT * FROM tbl_einheit
is the statement. But it still returns an empty result set. I also tried
SELECT E . * FROM tbl_einheit E, ( SELECT #a := NULL ) AS init LIMIT 0,30
but also empty.
Any suggestions?
the reason is you have some data type in your mysql dtaabse that ADODB connector in ASSp can't recognize, so asp thinks it is EOF.
use CAST in MySQL to convert data type to something asp can understand, example:
SELECT CAST(SUM(Entry_Data_1) as UNSIGNED) as score FROM contests_entries
Put a trace in your code to make sure you are executing the code you think you are.
Double-check your connection string.