FireDAC GetTableNames MySQL - mysql

I can't get the table names from databases other than the database specified in connection's params.
First, I used GetTableNames and it worked fine, but I was specifying the same database from connection's params.
DM.FDConnection.GetTableNames(ADatabse, '', APattern, tables, [osMy], [tkTable], False);
But when I tried to specify other database, I did not work. Then, I tried to use the TFDMetaInfoQuery, but it did not work either:
FDMetaInfoQuery := TFDMetaInfoQuery.Create(nil);
FDMetaInfoQuery.Connection := DM.FDConnection;
FDMetaInfoQuery.MetaInfoKind := mkTables;
FDMetaInfoQuery.CatalogName := 'databasename'
FDMetaInfoQuery.Open;
while not FDMetaInfoQuery.Eof do
begin
Result := Result + sLineBreak + FDMetaInfoQuery.FieldByName('TABLE_NAME').AsString;
FDMetaInfoQuery.Next;
end;
But I can get all the databases name:
FDMetaInfoQuery := TFDMetaInfoQuery.Create(nil);
FDMetaInfoQuery.Connection := DM.FDConnection;
FDMetaInfoQuery.MetaInfoKind := mkCatalogs;
FDMetaInfoQuery.Open;
while not FDMetaInfoQuery.Eof do
begin
Result := Result + sLineBreak + FDMetaInfoQuery.FieldByName('CATALOG_NAME').AsString;
FDMetaInfoQuery.Next;
end;
I already tried to specify those param in the connection, but nothing changed:
DM.FDConnection.Params.Add('MetaDefSchema=*');
DM.FDConnection.Params.Add('MetaDefCatalog=*');
DM.FDConnection.Params.Add('MetaCurSchema=*');
DM.FDConnection.Params.Add('MetaCurCatalog=*');
So, how should I get the table names from others databases?

I found out, my very question is the answer.. I should include osOther in the TFDPhysObjectScopes..
DM.FDConnection.GetTableNames(ADatabse, '', APattern, tables, [osMy, osOther], [tkTable], False);

Related

Delphi - DataSet creating too many connections in MySQL

I am having a problem using the TFDDataSet component in my application.
I have a function that fetch many times if a customer has new orders. If it returns empty the function ends.
...
fdm_XMLREsumo.Close;
fdm_XMLREsumo.Active := false;
_DataSetJSON := SM.GetXMLResumoChave( pEnt_ID, pChave); //See Edit 1
_DataSet.Close;
_DataSet := TFDJSONDataSetsReader.GetListValueByName( _DataSetJSON, sXMLResumo );
_DataSet.Open; <-- here's the problem
if not _DataSet.IsEmpty then begin
exit;
end;
fdm_XMLREsumo.AppendData( _DataSet );
...
The problem is every time it executes _DataSet.Open; it creates a new connection in my DB.Because of that I'm having a too many connections exception.I've checked in my Server Properties and it is like this:
I have tried Connection.Close,_DataSet.Close, _DataSet.Free and _DataSet.Destroy but nothing worked.I read this, and it explains that even if you do _DataSet.Close the connection still exists, because DataSets work in memory.There is also this guy having a similar issue, but using Query. Does anyone know how can I manage to solve this?I am using MySQL
EDIT 1
As #CraigYoung helped me saying my example needs MCVE
SM.GetXMLResumoChave method:
Here it uses a connection to the database that is closed at the end of the function. Already debugged, and here it does not leave an open connection in MySQL Proccess List
function TDAOXMLResumo.GetXMLResumoChave(xEnt_id: Integer; xChave: String): TFDJSONDataSets;
begin
if not oSM.FDConn.Connected then
oSM.FDConn.Connected := true;
QueryPesquisa.SQL.Clear;
QueryPesquisa.SQL.Text :=
' select * from table' +
' where ent_id = :ent_id ' +
' and xre_chNFe = :xre_chNFe ';
QueryPesquisa.ParamByName('ent_id').Asinteger := xEnt_id;
QueryPesquisa.ParamByName('xre_chNFe').Asstring := xChave;
Result := TFDJSONDataSets.Create;
//TFDJSONDataSetsWriter.ListAdd Opens the Query that is passed as parameter and store the data as JSON in the TFDJSONDataSets (Result) object
TFDJSONDataSetsWriter.ListAdd(Result, sXMLResumo, QueryPesquisa);
//Closing the Query
QueryPesquisa.Close;
//Closing the Connection
oSM.FDConn.Close;
end;`
Basically, the _DataSet is only receiving a JSON List here: _DataSet := TFDJSONDataSetsReader.GetListValueByName( _DataSetJSON, sXMLResumo );, and then open it to access the data in it.

Delphi SQL integration using UPDATE

I have a database application in Delphi and I am trying to open a record and update it. The following is how I do it now:
procedure TWebsiteRecord.UpdateRecord(Website : TWebsite);
var
SQL : string;
begin
RecordQuery.SQL.Clear;
SQL := 'UPDATE website SET Domain=:D, Template=:T, WebHost=:Wh, DomainRegistrar=:Dr, OrderDate=:Od, RenewalDate=:Rd, RenewalCost=:Rc, PaymentMethod=:Pm,' + 'OwnDomainStatus=:OStat, CancellationStatus=:CStat, ReminderStatus=:RStat, WebsiteNotes=:N, FTPUsername=:U1, FTPPassword=:P1, EmailPassword=:P2, PaidForYear=:PStat, CustomerID=:CID WHERE WebsiteID=:WID;';
RecordQuery.ParamCheck := True;
RecordQuery.SQL.Add(SQL);
RecordQuery.Params.ParamByName('D').AsString := Website.D;
RecordQuery.Params.ParamByName('T').AsString := Website.T;
RecordQuery.Params.ParamByName('Wh').AsString := Website.Wh;
RecordQuery.Params.ParamByName('Dr').AsString := Website.Dr;
RecordQuery.Params.ParamByName('Od').AsString := Website.Od;
RecordQuery.Params.ParamByName('Rd').AsString := Website.Rd;
RecordQuery.Params.ParamByName('Rc').AsInteger := Website.Rc;
RecordQuery.Params.ParamByName('Pm').AsString := Website.Pm;
RecordQuery.Params.ParamByName('OStat').AsInteger := Website.OStat;
RecordQuery.Params.ParamByName('CStat').AsString := Website.Cstat;
RecordQuery.Params.ParamByName('RStat').AsString := Website.Rstat;
RecordQuery.Params.ParamByName('N').AsString := Website.N;
RecordQuery.Params.ParamByName('U1').AsString := Website.U1;
RecordQuery.Params.ParamByName('P1').AsString := Website.P1;
RecordQuery.Params.ParamByName('P2').AsString := Website.P2;
RecordQuery.Params.ParamByName('PStat').AsInteger := Website.PStat;
RecordQuery.Params.ParamByName('CID').AsInteger := Website.CID;
RecordQuery.Params.ParamByName('WID').AsInteger := Website.WID;
RecordQuery.ExecSQL;
end;
and
procedure TWebsiteRecord.SaveBtnClick(Sender: TObject);
var
Website : TWebsite;
begin
if Validate then
begin
Website.D := DomainEdit.Text;
Website.T := TemplateEdit.Text;
Website.Wh := WebHostEdit.Text;
Website.Dr := DomainRegEdit.Text;
Website.Od := GetSQLDate(Date1Edit.Text);
Website.Rd := GetSQLDate(Date2Edit.Text);
if Website.Rd = '' then Website.Rd := '70/01/19';
if CostEdit.Text = '' then website.Rc := 0
else Website.Rc := strtoint(CostEdit.Text);
Website.Pm := GetPaymentMethod(PaymentMethodCombo.ItemIndex);
Website.OStat := integer(OwnDomainCheck.Checked);
if PendingCheck.Checked then Website.Cstat := 'P'
else if CancelledCheck.Checked then Website.Cstat := 'C'
else Website.Cstat := 'A';
Website.Rstat := GetSent(ReminderStatusCombo.ItemIndex);
Website.N := NotesMemo.Text;
Website.U1 := FTPUserEdit.Text;
Website.P1 := FTPPassEdit.Text;
Website.P2 := EmailPassEdit.Text;
Website.PStat := integer(PaidCheck.Checked);
Website.CID := strtoint(CustIDEdit.Text);
UpdateRecord(Website);
messagedlg('Website successfully updated, You will now be returned to the website table',mtinformation,[mbOK],0);
WebsiteTable.WebsiteCDS.Refresh;
Free;
end;
end;
There are no errors caused when this is executed, but the record is not updated and remains exactly the same as it was before. Does anyone know this problem? IIf so what can i do. I can provide more code if it is needed. Thanks in advance
As I can see your main problem is that you don't know what exact query is executed by the server.
It may seem a bit old fashioned, but if I were you I'd use the plain old FORMAT() function to debug the query and see what exactly is passed to the server. In this case your code will be something like that:
SQL := FORMAT('UPDATE website SET Domain="%s", Template="%s", WebHost="%s", DomainRegistrar="%s", OrderDate="%s", RenewalDate="%s", RenewalCost=%d, PaymentMethod="%s",' +
'OwnDomainStatus=%d, CancellationStatus="%s", ReminderStatus="%s", WebsiteNotes="%s", FTPUsername="%s", FTPPassword="%s", EmailPassword="%s", PaidForYear=%d, CustomerID=%d WHERE WebsiteID=%d',
[
RecordQuery.Params.ParamByName('D').AsString,
RecordQuery.Params.ParamByName('T').AsString,
RecordQuery.Params.ParamByName('Wh').AsString,
RecordQuery.Params.ParamByName('Dr').AsString,
RecordQuery.Params.ParamByName('Od').AsString,
RecordQuery.Params.ParamByName('Rd').AsString,
RecordQuery.Params.ParamByName('Rc').AsInteger,
RecordQuery.Params.ParamByName('Pm').AsString,
RecordQuery.Params.ParamByName('OStat').AsInteger,
RecordQuery.Params.ParamByName('CStat').AsString,
RecordQuery.Params.ParamByName('RStat').AsString,
RecordQuery.Params.ParamByName('N').AsString,
RecordQuery.Params.ParamByName('U1').AsString,
RecordQuery.Params.ParamByName('P1').AsString,
RecordQuery.Params.ParamByName('P2').AsString,
RecordQuery.Params.ParamByName('PStat').AsInteger,
RecordQuery.Params.ParamByName('CID').AsInteger,
RecordQuery.Params.ParamByName('WID').AsInteger
]);
and you could view the query, log it in a text file or just copy it and paste it somewhere to view it's execution like mysql command prompt or phpmyadmin.
If you have access to server's log file this is not needed - just open it and see the query inside.
Of cource once the query works as expected you may use it with parameters. And depending on what exactly connection type you are using there may be other (and faster) ways to get the actual query, e.g. this question

dbExpress how to show result from query?

var
Connection: TSQLConnection;
SqlSet:TSQLDataSet;
begin
Connection := TSQLConnection.Create(nil);
SqlSet := TSQLDataSet.Create(nil);
SqlSet.SQLConnection:=Connection;
Connection.DriverName := 'MySQL';
Connection.GetDriverFunc := 'getSQLDriverMYSQL';
Connection.LibraryName := 'dbxmys.dll';
Connection.VendorLib := 'libmysql.dll';
Connection.LoginPrompt:=False;
Connection.Params.Values['Database']:=('shadowxx1');
Connection.Params.Values['User_Name']:=('shadowxx1');
Connection.Params.Values['Password']:=('shadowxx1');
Connection.Params.Values['HostName']:=('shadowxx1');
Connection.Open;
Connection.Connected:=True;
SqlSet.CommandType:=ctQuery;
SqlSet.CommandText:= 'SELECT VERSION()';
SqlSet.ExecSQL;
Connection.Close;
Connection.Free;
SqlSet.Free;
end;
Code working , but , how to show result of query or extract it to the grid???
I simply dont find this information, in ADO it was smth like this
DataSrc := TDataSource.Create(Self);
DataSrc.DataSet := ADOQuery;
DataSrc.Enabled := true;
DBGrid1.DataSource := DataSrc;
If someone can - give some examples
And like this dont work
You're using the wrong method. In any of the TDataSet descendants that have it, ExecSQL is for executing queries that return no result set, such as INSERT, UPDATE, DELETE, or CREATE TABLE. See, for instance, TSQLQuery.ExecSQL (emphasis mine)
Executes a query that does not return a set of records.
Call ExecSQL to execute an SQL command that does not return a set of records. This command is a query other than a SELECT query, such as an INSERT, UPDATE, DELETE, or CREATE TABLE query.
Use TSQLQuery.Open to return rows from a SELECT. Something like this should work (untested - I don't use MySQL or DBExpress):
var
Qry: TSQLQuery;
VersionString: String;
// Set up your connection as above, and open it
Qry := TSQLQuery.Create(nil);
Qry.SQLConnection := Connection;
Qry.SQL.Text := 'SELECT VERSION() as DBVersion';
Qry.Open;
if not Qry.IsEmpty then
VersionString := Qry.FieldByName('DBVersion').AsString
else
VersionString := 'No results found';
Qry.Close;
For more information (including step-by-step tutorials), see Using DBExpress Components at the Delphi docwiki. (The one I've linked is for the current Delphi version, but the basic steps for DBExpress are the same since it was introduced.)
If you want a basic video tutorial for using DBExpress in Delphi, you can try DBExpress Data Access Components in Delphi - Delphi 101. I haven't watched it, but it was posted by Embarcadero, the makers of Delphi.

Delphi: Return database names from MySQL using Metadata

I want to know if there is a way to return database names from MySQL using Delphi object TSQLConnection, I know that there is some methods that return table names or field names:
TSQLConnection.getTableNames, TSQLConnection.GetFieldNames
but I can't find a way to get the databases on a specific server.
There s a method called OpenSchema in the TADOconnection object: TADOconnection.Openschema, that can return database names but in the TSQLConnection the method -protected not public- can't return database names.
P.S. I don't want to execute a query like 'show databases' or 'select * from information_schema.schemata'.
any body can help, thanks.
I tried this code and it worked, not sure if it will work for all MySQL, MariaDB versions and all Delphi versions but for me it workes, I am using delphi 6 and MySQL 4.0.25:
function GetMySQLDatabaseNames(AUserName, APassword, AHostName, APort: string; var
AErrorMessage: String): TStrings;
var SQLConnection: TSQLConnection;
ObjectCursor: ISQLCursor;
Status: SQLResult;
Counter: Integer;
Precision: Smallint;
Value: Pointer;
IsBlank: LongBool;
begin
Result:= TStringList.Create;
SQLConnection:= TSQLConnection.Create(nil);
with SQLConnection do
begin
ConnectionName:='dbnames';
DriverName := 'mysql';
Params.Clear;
Params.Values['User_Name'] := AUserName;
Params.Values['Password'] := APassword;
Params.Values['HostName'] := AHostName;
Params.Values['Database'] := 'mysql';
Params.Values['Port'] := APort;
LibraryName :='dbexpmda.dll';
VendorLib := 'not used';
GetDriverFunc :='getSQLDriverMySQLDirect';
LoginPrompt :=False;
try
Connected := True;
Status:= MetaData.getObjectList(eObjTypeDatabase, ObjectCursor);
while Status = SQL_SUCCESS do
begin
Status:= ObjectCursor.getColumnPrecision(4, Precision);
if Status = SQL_SUCCESS then
begin
Value:= AllocMem(Precision);
Status:= ObjectCursor.getString(4, Value, IsBlank);
if Status = SQL_SUCCESS then
if not IsBlank then
Result.Add(PChar(Value));
end;
Status:= ObjectCursor.Next;
end;
Connected := False;
Free;
except
on E: Exception do
begin
AErrorMessage:= AErrorMessage + E.Message+ sLineBreak;
end;
end;
end;
end;
With a query component you can get a list of databases with the following query:
SHOW DATABASES;
I have been looking for this answer for a longer time. Hopefully it will help others.

Using parameters with ADO Query (mysql/MyConnector)

Today I downloaded and installed MyConnector so I can use Mysql with ADO, everything installed, OK!, I can make connection with ODBC and do a connection from my delphi environment.
when I build my Query at runetime, I get an error saying :
Project Project1.exe raised exception class EOleException with message 'Arguments are of the wrong type, are out of acceptable range, or are in conflict with one another'. Process stopped. Use Step or Run to continue.
function TForm1.CreateSQL : TADOQuery;
begin
result := TADOQuery.create(self);
with Result do
begin
Connection := MainConnection;
CursorLocation := clUseServer;
CursorType := ctStatic;
CacheSize := 50;
AutoCalcFields := true;
ParamCheck := true;
Prepared := true;
end;
end;
procedure TForm1.login();
begin
with CreateSQL do
try
with SQL do
begin
add('SELECT ');
add(' * ');
add('FROM ');
add(' LisenswebUsers ');
add('WHERE ');
add(' UserName = :MyUsername '); // debugger exception here
add('AND ');
add(' UserPassword = :MyPassword '); // debugger exception here
with Parameters do
begin
ParamByName('MyUsername').value := txtLogin.text;
ParamByName('MyPassword').value := strmd5(txtPassword.text);
end;
Open;
if Recordcount <> 1 then
begin
lblLoggedinAs.Text := format('Du er logget inn som: %s (%s)',[FieldByName('Username').AsString,FieldByName('UserEmailaddress').AsString]);
MainPageControl.ActivePageIndex := 1;
end else
begin
txtPassword.Text := '';
txtPassword.SetFocus;
end;
end;
finally
free;
end;
end;
The strangest thing is that this works if I turn off debugging in delphi.
I would try adding SQL.BeginUpdate/SQL.EndUpdate around the Adds, otherwise the SQL text will be parsed every time you call "Add".
This is generally a good idea, as ADOQuery.SQL is a TStringList that has an OnChange event that sets the CommandText. SetCommandText text then end up calling TADOCommand.AssignCommandText which does a fair amount of work parsing params, and setting CommandObject.CommandText. Sometimes drivers will fail with partial SQL statements, but this stuff looks OK.
I had a similar problem many years ago - that's why I learnt about this stuff!
procedure TForm1.login();
var
Qry : TADOQuery;
begin
Qry := CreateSQL;
try
Qry.SQL.BeginUpdate;
Qry.SQL.Add('SELECT');
Qry.SQL.Add(' *');
Qry.SQL.Add('FROM');
Qry.SQL.Add(' LisenswebUsers');
Qry.SQL.Add('WHERE UserName = :MyUsername '); // debugger exception here
Qry.SQL.Add(' AND UserPassword = :MyPassword '); // debugger exception here
Qry.SQL.EndUpdate;
Qry.Parameters.ParamByName('MyUsername').value := txtLogin.text;
Qry.Parameters.ParamByName('MyPassword').value := strmd5(txtPassword.text);
Qry.Open;
if Qry.Recordcount <> 1 then
begin
lblLoggedinAs.Text := format('Du er logget inn som: %s (%s)',[FieldByName('Username').AsString,FieldByName('UserEmailaddress').AsString]);
MainPageControl.ActivePageIndex := 1;
end
else
begin
txtPassword.Text := '';
txtPassword.SetFocus;
end;
finally
Qry.Free;
end;
end;
BTW, the nested withs are really ugly (let the holy war begin)
I will sometimes use with, but would never nest three levels! If you are, at least reduce the scope of with SQL so it ends before with Parameters.
Try setting an explicit datatype :
CreateSql.Parameters.ParamByName('MyUserName').DataType := ftString;
In my case, defining the parameters and assigning the query string before assigning the connection corrected the problem. The query executes successfully in both cases, but the TADOQuery component internally raises (and subsequently swallows) the EOleException noted in OP if the connection is assigned before the parameterized query.
//LADOQuery.Connection := LADOConnection; // Exception # LADOQuery.Text:=...
Param := LADOQuery.Parameters.AddParameter;
Param.Name := 'rid';
Param.DataType := ftFixedChar;
Param := LADOQuery.Parameters.AddParameter;
Param.Name := 'qd';
Param.DataType := ftLongWord;
LADOQuery.SQL.Clear;
LADOQuery.SQL.Text:='SELECT Val FROM table WHERE v1=:rid AND v2=:qd';
LADOQuery.Connection := LADOConnection; // OK!
I'm open to explanations as to why this is the case - nothing in the documentation seems to suggest a need for this order of operations.
The exception is raised in ADODB.pas in TADOCommand.AssignCommandText here
try
// Retrieve additional parameter info from the server if supported
Parameters.InternalRefresh;
where this branch is only followed if the TADOQuery is attached to a live connection. InternalRefresh performs :
if OLEDBParameters.GetParameterInfo(ParamCount,
PDBPARAMINFO(ParamInfo),
#NamesBuffer) = S_OK then
for I := 0 to ParamCount - 1 do
with ParamInfo[I] do
begin
// When no default name, fabricate one like ADO does
if pwszName = nil then
Name := 'Param' + IntToStr(I+1) else // Do not localize
Name := pwszName;
// ADO maps DBTYPE_BYTES to adVarBinary
if wType = DBTYPE_BYTES then wType := adVarBinary;
// ADO maps DBTYPE_STR to adVarChar
if wType = DBTYPE_STR then wType := adVarChar;
// ADO maps DBTYPE_WSTR to adVarWChar
if wType = DBTYPE_WSTR then wType := adVarWChar;
Direction := dwFlags and $F;
// Verify that the Direction is initialized
if Direction = adParamUnknown then Direction := adParamInput;
Parameter := Command.CommandObject.CreateParameter(Name, wType, Direction, ulParamSize, EmptyParam);
Parameter.Precision := bPrecision;
Parameter.NumericScale := ParamInfo[I].bScale;
// EOleException raised here vvvvvvvvv
Parameter.Attributes := dwFlags and $FFFFFFF0; //Mask out Input/Output flags
AddParameter.FParameter := Parameter;
end;
The problem definitely seems to be at the OLE level, probably because the MySQL ODBC driver does not support returning this information (or returns invalid information). The exception is raised behind the _Parameter interface when setting
Parameter.Attributes := dwFlags and $FFFFFFF0;
using what seem to be invalid values (dwFlags = 320 -> bits set above DBPARAMFLAGSENUM defined length) returned from GetParameterInfo. Exception handling for flow control seems the only option given that the interface does not provide any mechanism to check values before setting them (and triggering exceptions).
Update :
It turns out there is an open QC about this : http://qc.embarcadero.com/wc/qcmain.aspx?d=107267
BeginUpdate/EndUpdate pair is not adequate. Use AddParameter to add parameters explicity before assigning sql command. Like:
var
Qry : TADOQuery;
begin
Qry := CreateSQL;
try
with Qry.Parameters.AddParameter do
begin
Name := 'MyUsername';
DataType := ftString;
end;
with Qry.Parameters.AddParameter do
begin
Name := 'MyPassword';
DataType := ftString;
end;
Qry.SQL.BeginUpdate;
Qry.SQL.Add('SELECT');
Qry.SQL.Add(' *');
Qry.SQL.Add('FROM');
Qry.SQL.Add(' LisenswebUsers');
Qry.SQL.Add('WHERE UserName = :MyUsername '); // debugger exception here
Qry.SQL.Add(' AND UserPassword = :MyPassword '); // debugger exception here
Qry.SQL.EndUpdate;
...