Connexion à un serveur MySQL avec C ++

J’essaie de me connecter à un serveur MySQL en utilisant C ++ avec le pilote ODBC 5.1 MySQL sous Visual C ++ 2008 Express Edition.

Je suis ces instructions de MSDN:

  • SQLConnect
  • SQLGetData
  • SQLFetch

La seule différence est que je dois convertir tout le SQLCHAR en SQLWCHAR , pour correspondre aux parameters de la fonction, espérons que cela n’affecte pas la chaîne de connexion.

Chaque fois que je me connecte, je reçois SQL_ERROR comme valeur de retour. Donc, je suppose qu’il y a quelque chose qui ne va pas avec la chaîne de connexion ou la déclaration de connexion.

J’ai essayé

DNS=TestConnection; UID=user; PSW=password

et

SERVER=localhost; DRIVER={MySQL ODBC 5.1 Driver}; PORT=3306; UID=user; PSW=password; DATABASE=dbo; et d’autres chaînes de connexion similaires.

Le DNS appelé TestConnection a les mêmes informations que la dernière chaîne de connexion.

Le schéma est dbo et testfire une table appelée testfire avec les spécifications de colonne suivantes:

 TEST_ID( INT(11), PRIMARY, AUTO INCREMENT) TEST_STRING( VARCHAR(50) ) TEST_INTEGER( INT(11) ) TEST_FLOAT( FLOAT ) TEST_DATE( DATETIME ) 

Avec 3 rangées:

  ID STRING INT FLOAT DATE ------------------------------------------------------ | 1 | Test 1 | 1 | 0.1 | 2001-01-01 00:00:00 | | 2 | Test 2 | 2 | 0.2 | 2002-01-01 00:00:00 | | 3 | Test 3 | 3 | 0.3 | 2003-01-01 00:00:00 | ------------------------------------------------------ 

J’ai tenté de récupérer les données à l’aide d’une connexion Excel, principalement pour voir si le pilote fonctionne. Excel a correctement récupéré les données sans problème. Le DNS nommé TestConnection est donc valide, de même que les informations d’identification.

  • Qu’est-ce que je fais mal?
  • Que devrais-je changer?
  • Est-ce la conversion en MYSQLWCHAR * qui gâche la chaîne de connexion?
  • Existe-t-il une approche différente, peut-être meilleure et plus efficace? (sauf peut-être l’encapsulation de la classe, c’est ce que je vais faire après le test.)

Oh, et le compilateur ne donne aucune erreur ni aucun avertissement, le code est compilé et s’exécute sans aucun problème.

Alors, voici le code de test, qui renvoie “Erreur d’exécution de la requête”:

 #include  #include  #include  #include  #include  using namespace std; int main(){ SQLHENV henv; SQLHDBC hdbc; SQLHSTMT hstmt; SQLRETURN retcode; HWND desktopHandle = GetDesktopWindow(); SQLWCHAR OutConnStr[255]; SQLSMALLINT OutConnStrLen; SQLWCHAR szDNS[2048] ={0}; // Allocate environment handle retcode = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv); // Set the ODBC version environment atsortingbute if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) { retcode = SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0); // Allocate connection handle if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) { retcode = SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc); // Set login timeout to 5 seconds if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) { SQLSetConnectAttr(hdbc, SQL_LOGIN_TIMEOUT, (SQLPOINTER)5, 0); // Connect to data source retcode = SQLDriverConnect( hdbc, desktopHandle, (SQLWCHAR*)"driver=MySQL Server", _countof("driver=MySQL Server"), OutConnStr, 255, &OutConnStrLen, SQL_DRIVER_PROMPT ); // Allocate statement handle if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) { retcode = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt); // Process data retcode = SQLExecDirect(hstmt, (SQLWCHAR*)"SELECT TEST_STRING, TEST_INTEGER, TEST_FLOAT FROM dbo.testfire", SQL_NTS); if (retcode == SQL_SUCCESS) { SQLINTEGER sTestInt, cbTestStr, cbTestInt, cbTestFloat; SQLFLOAT dTestFloat; SQLCHAR szTestStr[200]; while (TRUE) { cout<<"Inside loop"; retcode = SQLFetch(hstmt); if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO) { cout<<"An error occurred"; } if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO){ SQLGetData(hstmt, 1, SQL_C_CHAR, szTestStr, 200, &cbTestStr); SQLGetData(hstmt, 2, SQL_C_ULONG, &sTestInt, 0, &cbTestInt); SQLGetData(hstmt, 3, SQL_C_FLOAT, &dTestFloat, 0,&cbTestFloat); /* Print the row of data */ cout<<szTestStr<<endl; cout<<sTestInt<<endl; cout<<dTestFloat<<endl; } else { break; } } }else{ cout<<"Query execution error."<<endl; SQLWCHAR SqlState[6], Msg[SQL_MAX_MESSAGE_LENGTH]; SQLINTEGER NativeError; SQLSMALLINT i, MsgLen; SQLRETURN rc2; // Get the status records. i = 1; while ((rc2 = SQLGetDiagRec(SQL_HANDLE_STMT, hstmt, i, SqlState, &NativeError, Msg, sizeof(Msg), &MsgLen)) != SQL_NO_DATA) { cout<<SqlState<<endl; cout<<NativeError<<endl; cout<<Msg<<endl; cout<<MsgLen<<endl; i++; } } if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) { SQLFreeHandle(SQL_HANDLE_STMT, hstmt); } SQLDisconnect(hdbc); }else{ cout<<"Connection error."<<endl; } SQLFreeHandle(SQL_HANDLE_DBC, hdbc); } } SQLFreeHandle(SQL_HANDLE_ENV, henv); } system("pause"); return 0; } 

METTRE À JOUR

Après la mise à jour du code (et de la publication) en utilisant les arguments corrects pour SQLDriverConnect partir de la documentation fournie par Mat (voir les commentaires ci-dessous), la connexion fonctionne. Comment puis-je faire la même chose sans avoir à demander le nom DNS? Mettre la poignée de fenêtre comme nulle et …?

Il échoue maintenant dans SQLExecDirect(hstmt, (SQLWCHAR*)"SELECT TEST_STRING, TEST_INTEGER, TEST_FLOAT FROM dbo.testfire", SQL_NTS) , mais la requête est correcte. Alors, quel est le problème?

Le message d’erreur exact renvoyé est:

 Sql State: 42000 Native Error: 1064 Message: Message Length: 211 42000: Syntax error or access violation *StatementText contained an SQL statement that was not preparable or contained a syntax error. The user did not have permission to execute the SQL statement contained in *StatementText. 

Alors qu’est-ce que cela signifie? Comment puis-je ne pas avoir la permission? Comment cela peut-il générer une erreur de syntaxe, c’est clairement une requête valide?

Avec un peu d’aide de Mat, j’ai pu comprendre le problème, mais comme il ne l’a pas répondu sous forme de réponse, je devrai y répondre de manière à pouvoir le partager avec ceux qui ont le même problème, et aussi pour marquer comme répondu.

Donc, mon problème était que je ne pouvais pas me connecter à la firebase database. Comme Mat l’a suggéré, je devrais utiliser les informations d’erreur étendues, connues sous le nom de SQLGetDiagRec et corriger les arguments conformément à la documentation. Il m’a SQLGetDiagRec un moment pour apprendre le fonctionnement de la fonction SQLGetDiagRec , mais une fois que j’ai réussi à convertir le wchar_t en char * j’ai pu voir l’erreur SQLGetDiagRec .

La tentative de connexion m’a donné l’erreur Data source not found and no default driver specified . Cela m’a donné un indice, indiquant que j’avais écrit la chaîne de connexion incorrecte ou que la chaîne de texte avait été mal interprétée ou endommagée.

Faire des recherches sur le réseau m’a permis de comprendre que la chaîne était mal interprétée et que pour la réparer, je devais en faire une chaîne littérale. Assez sûrement, mettre un L devant la ficelle l’a résolu!

 retcode = SQLDriverConnect(hdbc, 0, (SQLWCHAR*)L"DSN=TestConnection;SERVER=localhost;UID=user;PWD=password;DRIVER=MySQL Server;", _countof(L"DSN=TestConnection;SERVER=localhost;UID=user;PWD=password;DRIVER=MySQL Server;"), OutConnStr, 255, &OutConnStrLen, SQL_DRIVER_COMPLETE); 

En même temps, j’ai appris à me débarrasser de l’invite, ce qui était assez facile à comprendre après avoir corrigé le problème initial. Spécifiez null pour le handle de fenêtre, définissez l’achèvement du pilote sur SQL_DRIVER_COMPLETE et assurez-vous d’append toutes les informations nécessaires dans la chaîne de connexion.

Ainsi, le problème suivant SQLExecDirect à la requête avec SQLExecDirect était une erreur indiquant une erreur de Syntax error or access violation . Le problème était évidemment le même que pour la chaîne de connexion. Sûrement assez

 retcode = SQLExecDirect(hstmt, (SQLWCHAR*)L"SELECT TEST_STRING, TEST_INTEGER, TEST_FLOAT FROM dbo.testfire", SQL_NTS); 

Travaillé comme un charme.

Voici le code dans son intégralité, entièrement fonctionnel:

 #include  #include  #include  #include  #include  #include  using namespace std; int main(){ SQLHENV henv; SQLHDBC hdbc; SQLHSTMT hstmt; SQLRETURN retcode; SQLWCHAR OutConnStr[255]; SQLSMALLINT OutConnStrLen; // Allocate environment handle retcode = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv); // Set the ODBC version environment atsortingbute if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) { retcode = SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0); // Allocate connection handle if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) { retcode = SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc); // Set login timeout to 5 seconds if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) { SQLSetConnectAttr(hdbc, SQL_LOGIN_TIMEOUT, (SQLPOINTER)5, 0); // Connect to data source retcode = SQLDriverConnect( hdbc, 0, (SQLWCHAR*)L"DSN=TestConnection;SERVER=localhost;UID=root;PWD=never140;DRIVER=MySQL Server;", _countof(L"DSN=TestConnection;SERVER=localhost;UID=root;PWD=never140;DRIVER=MySQL Server;"), OutConnStr, 255, &OutConnStrLen, SQL_DRIVER_COMPLETE ); // Allocate statement handle if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) { retcode = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt); // Process data retcode = SQLExecDirect(hstmt, (SQLWCHAR*)L"SELECT TEST_STRING, TEST_INTEGER, TEST_FLOAT FROM dbo.testfire", SQL_NTS); if (retcode == SQL_SUCCESS) { SQLINTEGER sTestInt, cbTestStr, cbTestInt, cbTestFloat, iCount = 1; SQLFLOAT dTestFloat; SQLCHAR szTestStr[200]; while (TRUE) { retcode = SQLFetch(hstmt); if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO) { cout<<"An error occurred"; } if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO){ SQLGetData(hstmt, 1, SQL_C_CHAR, szTestStr, 200, &cbTestStr); SQLGetData(hstmt, 2, SQL_C_ULONG, &sTestInt, 0, &cbTestInt); SQLGetData(hstmt, 3, SQL_C_DOUBLE, &dTestFloat, 0,&cbTestFloat); /* Print the row of data */ cout<<"Row "< 

Cela va de soi, même la plus petite chose peut tout faire échouer.

Merci Mat pour ton aide.

changer (SQLWCHAR *) en L. cela me convient