Open yoloz opened 1 year ago
Please try adding useServerPrepStmts=true
to JDBC URL. Such as
jdbc:mysql://localhost:3307/shadow_db?useUnicode=true&characterEncoding=UTF-8&useServerPrepStmts=true
Please try adding
useServerPrepStmts=true
to JDBC URL. Such asjdbc:mysql://localhost:3307/shadow_db?useUnicode=true&characterEncoding=UTF-8&useServerPrepStmts=true
thanks a lot
apache-shardingsphere-5.0.0-shardingsphere-proxy
client error :
java.sql.SQLException: Unsupported command: [COM_STMT_SEND_LONG_DATA]
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:129)
at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:122)
at com.mysql.cj.jdbc.ServerPreparedStatement.serverExecute(ServerPreparedStatement.java:633)
at com.mysql.cj.jdbc.ServerPreparedStatement.executeInternal(ServerPreparedStatement.java:417)
at com.mysql.cj.jdbc.ClientPreparedStatement.executeUpdateInternal(ClientPreparedStatement.java:1098)
at com.mysql.cj.jdbc.ClientPreparedStatement.executeUpdateInternal(ClientPreparedStatement.java:1046)
at com.mysql.cj.jdbc.ClientPreparedStatement.executeLargeUpdate(ClientPreparedStatement.java:1371)
at com.mysql.cj.jdbc.ClientPreparedStatement.executeUpdate(ClientPreparedStatement.java:1031)
at BlobClobTest.writeStream(BlobClobTest.java:70)
at BlobClobTest.main(BlobClobTest.java:30)
proxy-log:
[ERROR] 2022-10-19 11:27:39.799 [Connection-1-ThreadExecutor] o.a.s.p.f.c.CommandExecutorTask - Exception occur:
org.apache.shardingsphere.proxy.frontend.exception.UnsupportedCommandException: null
at org.apache.shardingsphere.proxy.frontend.mysql.command.generic.MySQLUnsupportedCommandExecutor.execute(MySQLUnsupportedCommandExecutor.java:38)
at org.apache.shardingsphere.proxy.frontend.command.CommandExecutorTask.executeCommand(CommandExecutorTask.java:99)
at org.apache.shardingsphere.proxy.frontend.command.CommandExecutorTask.run(CommandExecutorTask.java:72)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
apache-shardingsphere-5.2.0-shardingsphere-proxy success 😊
maybe can help #3024 #21518 @jitawangzi @xriqyu https://github.com/yoloz/shardingsphere/tree/supportJdbcBlob
mysql-connector-java.jar
;MySQL Connector/Python
has no label to distinguish, maybe can use column type and sql parse result to do;This is how Connector/J convert InputStream to _binary literal. https://github.com/mysql/mysql-connector-j/blob/27603148f10a5f47467bec7ad26a5ca28da63c72/src/main/protocol-impl/java/com/mysql/cj/protocol/a/InputStreamValueEncoder.java#L71-L137
This is how Connector/J convert InputStream to _binary literal. https://github.com/mysql/mysql-connector-j/blob/27603148f10a5f47467bec7ad26a5ca28da63c72/src/main/protocol-impl/java/com/mysql/cj/protocol/a/InputStreamValueEncoder.java#L71-L137
yeah, if use hex nothing to do(type bytes
use it), but escap will change byte
There are 2 reasons that the binary value is damaged:
Proxy reads COM_QUERY SQL and convert it with charset
(which usually UTF-8
in Proxy). The byte[] to UTF-8 String conversion may damage values of literal prefixed with _binary
.
https://github.com/apache/shardingsphere/blob/e5c8dc7778f79d544935803a7093aee53ea91bf2/db-protocol/mysql/src/main/java/org/apache/shardingsphere/db/protocol/mysql/packet/command/query/text/query/MySQLComQueryPacket.java#L44
https://github.com/apache/shardingsphere/blob/e5c8dc7778f79d544935803a7093aee53ea91bf2/db-protocol/mysql/src/main/java/org/apache/shardingsphere/db/protocol/mysql/payload/MySQLPacketPayload.java#L436-L440
SQL String to bytes conversion in MySQL Connector/J damaged the _binary literal in SQL. This happened at ShardingSphere-Proxy sending SQL to MySQL server.
serverEncoding
is usually utf8mb4
or utf8
.
https://github.com/mysql/mysql-connector-j/blob/ad86f36e100e104cd926c6b81c8cab9565750116/src/com/mysql/jdbc/Buffer.java#L670
To fix this issue, my idea is:
Implement visitBlobValue
in org.apache.shardingsphere.sql.parser.mysql.visitor.statement.impl.MySQLDMLStatementSQLVisitor
and record all the _binary literal when parsing SQL statement.
https://github.com/apache/shardingsphere/blob/e5c8dc7778f79d544935803a7093aee53ea91bf2/sql-parser/dialect/mysql/src/main/antlr4/imports/mysql/DMLStatement.g4#L103-L105
Read the SQL from COM_QUERY by new String(sql, StandardCharsets.ISO_8859_1)
. Parse SQL and check whether the SQL statement is allowed to prepare. org.apache.shardingsphere.proxy.frontend.mysql.command.query.binary.prepare.MySQLComStmtPrepareChecker
If the SQL is allowed to be prepared, extract _binary literal from SQL and rewrite _binary literal to placeholder ?
. Executing SQL as prepared statement.
Hi @yoloz Your PR #21922 did the step 3 of my idea. But checking raw bytes may not a elegant way. What do you think of my idea? Are you interested in fixing this issue?
Hi @yoloz Your PR #21922 did the step 3 of my idea. But checking raw bytes may not a elegant way. What do you think of my idea? Are you interested in fixing this issue?
@TeslaCN you're right, checking raw bytes not the best way, like MySQL Connector/Python
doesn't work,It would be better based on global to provide an api impl
@TeslaCN you're right, checking raw bytes not the best way, like
MySQL Connector/Python
doesn't work,It would be better based on global to provide an api impl
@yoloz So would you like to fix this? Or I will fix it later.
@TeslaCN
thanks a lot,
MySQL Connector/Python
doesn't contain any literal@TeslaCN you're right, checking raw bytes not the best way, like
MySQL Connector/Python
doesn't work,It would be better based on global to provide an api impl@yoloz So would you like to fix this? Or I will fix it later.
OK, I will fix this issue.
@yoloz Could you describe some details about MySQL Connector/Python? If the Python driver using binary protocol like COM_SEND_LONG_DATA
, the Proxy can already handle it.
Thanks!
OK, I will fix this issue. @yoloz Could you describe some details about MySQL Connector/Python? If the Python driver using binary protocol like
COM_SEND_LONG_DATA
, the Proxy can already handle it. Thanks!
@TeslaCN you can use table structure above and code follows:
import mysql.connector
def convertToBinaryData(filename):
# Convert digital data to binary format
with open(filename, 'rb') as file:
binaryData = file.read()
return binaryData
def convertToStringData(filename):
# Convert digital data to string
with open(filename, "r", encoding="utf-8") as file:
stringData = file.read()
return stringData
def insertBLOB(id, bfile, cfile):
print("inserting bytes to table")
try:
connection = mysql.connector.connect(host='127.0.0.1',
port='3307',
database='shadow_db',
user='root',
password='root')
cursor = connection.cursor()
sql_insert_blob_query = """ insert into blob_clob(id,b_blob,c_clob) VALUES (%s,%s,%s)"""
b_data = convertToBinaryData(bfile)
c_data = convertToStringData(cfile)
# Convert data into tuple format
insert_blob_tuple = (id, b_data, c_data)
result = cursor.executemany(sql_insert_blob_query, insert_blob_tuple)
connection.commit()
print("inserted successfully to table", result)
except mysql.connector.Error as error:
print("Failed inserting to table {}".format(error))
finally:
if connection.is_connected():
cursor.close()
connection.close()
print("MySQL connection is closed")
insertBLOB(8, "/home/xxx/test/test.png", "/home/xxx/test/test.txt")
Proxy output sql like insert into blob_clob(id,b_blob,c_clob) values(1,'......','........')
Thanks @yoloz ! Your input reminds me that the Python driver does not support server prepared statement. I will take it consideration. Thanks again.
Thanks @yoloz ! Your input reminds me that the Python driver does not support server prepared statement. I will take it consideration. Thanks again.
welcome,waiting for upgrade 🤝
MySQL Connector/Python invoke API of MySQL Connector/C to escape string.
So maybe we have to de escape all the string literal in SQL.
So maybe we have to de escape all the string literal in SQL.
may be trouble to distinguish it, so i find result from server code, a little guess, join sql parse and column type
Maybe we should fix this issue by Test Driven Development. Add some integration test cases.
Bug Report
Which version of ShardingSphere did you use?
Which project did you use? ShardingSphere-JDBC or ShardingSphere-Proxy?
ShardingSphere-Proxy
Behavior
above it,
id==1
insert by proxy andid==2
insert by direct,maybe you think this tool(dbeaver) not support fine, i read this record and write to file,record(id==1
)open failReason analyze (If you can)
config(seriver.yml) show sql, can see
insert into blob_clob(id,b_blob,c_clob) values(1,_binary'......','........')
,maybe get sql from packet ?Steps to reproduce the behavior, such as: SQL to execute, sharding rule configuration, when exception occur etc.
props:
max-connections-size-per-query: 1
kernel-executor-size: 16 # Infinite by default.
proxy-frontend-flush-threshold: 128 # The default value is 128.
proxy-hint-enabled: false
sql-show: true
check-table-metadata-enabled: false
...