Setting a var equal to SQL code - delphi

I have a school PAT project where I need to do something extra, and in order to do so I need to do the following (I am using delphi 7 and ms access):
I want to set sql code = to a variable in order to use the var for something like a calculation.
I am thinking something like this:
s(var):=ADOQuery1.SQL.Text:='SELECT Birthdate where username = '+edtUsername.text;
So basically I want to set a specific ms access cell = to a var in delphi.

You trying to do too much in one line.
You set the SQL.Text
Then run the Query.
Then read one line into your variable.
Furthermore never inject parameters directly into a query; this leads to SQL injection vulnerabilities. Use parameters instead.
In pseudo code:
ADOQuery1.SQL.Text:='SELECT Birthdate where username = :name';
ADOQuery1.Parameters.ParamByName('name'):= aname; <<-- save way to use parameters.
ADOQuery1.RunQuery;
var1:= ADOQuery1.FieldByName('BirthDate').AsDate;
Obviously you need to fix the SQL statement, because it is incomplete and tweak the code a little. But I'll leave that as an exercise.
Here's the documentation for TADOQuery: http://docwiki.embarcadero.com/Libraries/XE5/en/Data.Win.ADODB.TADOQuery

Related

SqlParameter vs array of object

I have a stored procedure that has many parameters and I've been using the following to return the results:
db.Database.SqlQuery<GetListCamera_Result>("Camera.sp_get_list_camera #room_name, #disk_status,
#signal_loss_reason, #department_id , #sortColumnName, #sortDirection, #start, #length",
new SqlParameter("room_name", room_name),
new SqlParameter("disk_status", disk_status),
new SqlParameter("department_id", department),
new SqlParameter("signal_loss_reason", reason),
new SqlParameter("sortColumnName", sortColumnName),
new SqlParameter("sortDirection", sortDirection),
new SqlParameter("start", start),
new SqlParameter("length", length)).ToList();
I saw one of my senior using these much more cleaner than mine:
db.Database.SqlQuery<GetLiquidbooks_Result>("sp_get_liquidbooks {0}, {1}, {2}, {3}, {4}",
new object[] { LiquidCode, LibID, LocPrefix, LocID, UserID }).ToList();
What are the differences and is there anything I need to be aware of if I'm switching to his
I think yours is a much safer way. However, if you'd like to make it simpler, you can refer to this article, you don't have to initialize a SqlParameter instance, but the #p1 #p2 syntax is still essential. In the first section of the article, it also mentions that this care should be taken.
AFAIK, SqlQuery doesn't prevent SQL Injection, which means if I pass the DROP command into your second sample, the table may be deleted permanently. Therefore, the one which the senior wrote might expose potential security risks, you should make sure that you use parameters in your query in the correct way to guard against such attacks.
About the second sample, consider using ObjectContext.ExecuteStoreQuery<T>(), it allows you to pass the query string with {0} {1} syntax and object array as the parameter into the method. This method actually invokes CreateStoreCommand which transforms your query and objects into a parameterized query. But SqlQuery seems not.
FYI:
ExecuteStoreQuery source code - You can take a look at this method to get deep into how it works.
SqlQuery source code - As aforementioned, I rechecked the source code, and I couldn't find any codes that help to turn it into parameterized SQL

How do I retrieve values from a filter query within a CosmosDb stored procedure?

I'm struggling to work out how to retrieve the property values from a filter query in a CosmosDb stored procedure. I'm sure this must be relatively simple, but just can't seem to find the right combination.
var result = __.chain()
.filter(function(doc) {
return doc.id ==="1stDocId";
})
.map(function(doc) {
return {
id: doc.id,
propertyA: doc.propertyA
};
})
.value();
if(!result.isAccepted) throw new Error("The call was not accepted");
Using the above code, or similar, I'm wanting to retrieve the value of propertyA, and then use it in a second query, and so on.
Using response.setBody to return the value of result at this point, I can see it is sat in an array, but can't work out the correct code to access it.
result[0].PropertyA, result[0][0].PropertyA don't work, can someone point me in the right direction?
I'm able to do this sort of thing using the queryDocuments function, but ultimately my stored procedure needs to execute 5 or 6 select statements, manipulating the retrieved values in between. Utilising nested callbacks seems very messy, and pretty confusing once you're a few levels deep. Is there a better way?
Thanks.
Well I gave in, and wrote the procedure using nested callbacks and 'queryDocuments'. Since at one point my procedure requires a switch statement, which either performs a db lookup, or provides a hard value, I had to duplicate the rest of the queryDocument calls in the db lookup branch callback, which feels plain wrong.
However, whilst not being pretty, my stored procedure now works, returning the correct values in all cases.

DataSnap using AutoInc key and refresh current record only after insert

I've not been able to find an answer on this anywhere. Using Delphi XE7 with TClientDataSet, DataSnap & SQL Server. I need to insert a record, apply updates and then refresh that record so I can get the Id and assign it to my object. Seems pretty basic requirement, but on the contrary it is proving to be a royal pain.
I've found the obvious stuff on EDN, SO and Dr Bob:
http://edn.embarcadero.com/article/20847
DataSnap and the autoinc field
http://www.drbob42.com/examines/examinC0.htm
However these seem to focus on a "Refresh" of the TClientDataSet to re-fetches the entire table/query. Whilst this does actually resolve the Id field itself (good!), it also moves the cursor off the current record which was just inserted and so I'm not able to get the Id and assign it to my object. Also, for performance over HTTP I don't really want to refetch the entire table every time a record is inserted, if there's 10,000 records this will consume too much bandwidth and be ridiculously slow!
Consider the following code:
function TRepository<I>.Insert(const AEntity: I): I;
begin
FDataSet.DisableControls;
try
FDataSet.Insert;
AssignEntityToDataSet(AEntity); // SET'S ALL THE RELEVANT FIELDS
FDataSet.Post;
FDataSet.ApplyUpdates(-1);
FDataSet.Refresh; // <--- I tried RefreshRecord here but it cannot resolve the record
AEntity.Id := FDataSet.FieldByName('Id').AsInteger; // <----- THIS NOW POINTS TO WRONG ROW
finally
FDataSet.EnableControls;
end;
end;
Does anyone know how to achieve this? I need to be able to refresh and stay on the current record otherwise I do not know the Id of the record just created and the GUI cannot stay focused on the current record.
Hopefully something obvious I'm missing.
Cheers.
Rick.
Assuming you can get hands on the new ID inside the AfterUpdateRecord event of your DataProvider, your event handler then may look like this (the current record of DeltaDS is the one just inserted into SourceDS):
if (UpdateKind = ukInsert) then begin
DeltaDS.FindField('Id').NewValue := <TheNewID>;
end;
Make sure to have the poPropogateChanges option set in the provider. This will transfer the changed Id field back to the ClientDataSet.
Now you can get rid of the FDataSet.Refresh call.
SQL Server does allow you to get the last identity it generated in several ways - there's no need to "refresh" the record/query which means re-issuing a SELECT and can generate undesiderable side-effects. You can use SELECT SCOPE_IDENTITY() or use an OUTPUT clause. If the Delphi database driver supports it, TField.AutogenerateValue should accomplish that task automatically (see http://docwiki.embarcadero.com/Libraries/XE7/en/Data.DB.TField.AutoGenerateValue)
Otherwise you have to put that new data into your delta (see Raabe answer - this has to be done on the datasnap server which actually talks to the database) after reading it, so it's sent back to the client. You also need to set properly and TField.ProviderFlags to ensure data are applied correctly (see http://docwiki.embarcadero.com/RADStudio/XE7/en/Influencing_How_Updates_Are_Applied), usually you don't want those field appear in an UPDATE.

How to pass a parameter to a query using dbExpress in Delphi

I want to use a dbExpress TSQLQuery component. But i do not know how to write the SQL to add a parameter. I will give an example maybe it will be more clear what my problem is.
In a TADOQuery the following works:
SELECT*
FROM sometable
WHERE sometable.id = :value;
Now in the above example you pass the parameter to the query using the colon (:) before the parameter name. But when I try and do that with the TSQLQuery, I get the following error:
dbExpress driver does not support the TDBXTypes.UNKNOWN data type. Vendor Error Message.
Now if this is not the way that you pass a parameter in a TSQLQuery component, can someone please assist me. This is new territory for me.
Im using a Firebird database, and Im using Delphi XE2
To set the properties of a parameter you must use the Params property. From here you can access each paramer using a index or the name, to set the value of the parameter use one of the properties AsString, AsInteger an so on, depeding of the type of the field.
Check this sample
var
LSQLQuery : TSQLQuery;
begin
LSQLQuery:=TSQLQuery.Create(nil);
try
LSQLQuery.SQLConnection:=SQLConnection1;
LSQLQuery.CommandText:='Select FIRST_NAME from EMPLOYEE Where EMP_NO=:Param1';
LSQLQuery.Params.ParamByName('Param1').AsInteger:=2;//or using the index of the parameter LSQLQuery.Params[0].AsInteger:=2;
LSQLQuery.Open;//Execute the query
ShowMessage(LSQLQuery.FieldByName('FIRST_NAME').AsString); //get the data
LSQLQuery.Close;
finally
LSQLQuery.Free;
end;
end;

ASP Classic - Recordset Object vs. Command Object

I am using ASP Classic and SQL Server 2000 to create dynamic websites.
I am a bit confused about when to use a recordset object and when to use a command object when querying the database.
I was told that if the stored procedure would be returning records from a SELCT statement then I should use a recordset, however if I am up updating or inserting then I should use a command object and pass all data as parameters to the stored procedure.
When using a recordset I often pass any required data like so:
rs.Source = "spTest " & id
I alway validate the data that I am passing to make sure it is what I am expecting and cast it to its correct type.
I have since been told however that the above method leaves my code open to SQL Injection attacks and that I should always use a command object.
Is this correct?
Thanks
Yes, that's right.
Imagine someone passing the string: '0; delete * from users;'
You query would then be:
spTest 0; delete * from users;
If you're lucky you won't have a users table. Personally, I would use the command object all the time for consistency. You can get everything you need from it.
Here is a quick example of how you might do it with the command object:
Dim oStoredProc : Set oStoredProc = Server.CreateObject("ADODB.Command")
With oStoredProc
.ActiveConnection = oDBConnection
.CommandType = adCmdStoredProc
.CommandText = "up_procname"
.Parameters.Append(.CreateParameter("#Param1", ADODB.adInteger, ADODB.adParamInput, 22, 11))
.Parameters.Append(.CreateParameter("#Param2", ADODB.adInteger, ADODB.adParamOutput, 22, 12)
Call .Execute()
myVal = .Parameters("#Param2")
End With
Set oStoredProc = Nothing
What you were told is correct indeed : you should always use commande objects to prevent SQL Injection. Using parameterized queries, you leave all the security and validation of parameters to the ADO layer (though you should still do your own proper validation), and you may even get some performance improvement (these parameterized queries are cached by SQL Server)
When you execute a command you have two options : either the SQL you execute returns rows (A SELECT Statement, or some stored procedures), then you have to use a recordset to store these rows, either it doesn't (UPDATES, DELETES, other procedures), then you juste execute the command and do not worry about recordsets.
Edit : just to make sure everything is clear for you, I used James Wiseman's code above and adapted it to your example :
Dim oStoredProc : Set oStoredProc = Server.CreateObject("ADODB.Command")
With oStoredProc
.ActiveConnection = oDBConnection
.CommandType = adCmdStoredProc
.CommandText = "spTest ?"
.Parameters.Append(.CreateParameter("id", ADODB.adInteger, ADODB.adParamInput, id, 11))
Dim rs : Set rs = .Execute()
End With
Set oStoredProc = Nothing
Didn't test it, but should be ok :-)
Last but not least : even though you're pretty well protected now, don't forget that if you're using dynamic SQL inside your stored procedure you may still have an SQL Injection security hole (as soon as you're concatenating strings to create SQL you may be vulnerable I would say) !

Resources