Our code has a SqlExpression, which at its bare minimum is something like:
var q = db.From<Users>();
q.Where(u => u.Age == 25);
totalRecords = db.Scalar<int>(q.ToCountStatement());
q.ToCountStatement() generates the following query:
SELECT COUNT(*) FROM "Users" WHERE ("Age" = #0)
However, db.Scalar() throws an exception: Must declare the scalar variable "#0". This has started occurring in recent versions (tested in 4.0.54). The same code was working fine until v4.0.50. I've checked the release notes, but couldn't find a related change.
Even passing a parameter throws the same exception:
totalRecords = db.Scalar<int>(q.ToCountStatement(), 25);
Is it a bug, or my oversight?
Secondly, is it possible to get q.ToCountStatement() to generate a more optimized query with COUNT(Age) or COUNT([PrimaryKey]) instead of COUNT(*)?
Now that OrmLite defaults to parameterized queries you also need to provide the queries db parameters when executing a query (if you've specified any params), e.g:
var q = db.From<Users>().Where(u => u.Age == 25);
var count = db.Scalar<int>(q.ToCountStatement(), q.Params);
You can also use OrmLite's explicit Count() API's, e.g:
db.Count<User>(x => x.Age == 25);
Or with a typed SqlExpression:
var q = db.From<User>().Where(x => x.Age == 25);
db.Count(q);
Otherwise another way to specify db params is to use an anonymous object, e.g:
db.Scalar<int>("SELECT COUNT(*) FROM Users WHERE Age=#age", new { age = 25});
Related
I'm looking for a way to dynamically build a SQL query for an unknown number of API parameters that may come back. As a simple example, consider the following query:
router.get("/someEndpoint", authorizationFunction, async (req, res) => {
let sql = `
SELECT *
FROM some_table
WHERE username = $1
${req.query.since !== undefined ? "AND hire_date >= $2" : ""}
`
const results = await pool.query(sql, [req.query.user, req.query.since]);
}
where pool is defined as
const Pool = require("pg").Pool;
const pool = new Pool({<connection parameters>});
The problem I'm having is that if req.query.since is not provided, then the SQL query only requires a single bound parameter ($1). This is presented as an error that says bind message supplies 2 parameters, but prepared statement "" requires 1. Since I don't know which parameters a user will provide until the time of the query, I'm under the impression that I need to provide all possible value, and let the query figure it out.
I've seen a lot of posts that point to pg-promise as a solution, but I'm wondering if that's necessary. Is there a way that I can solve this with my current setup? Perhaps I'm thinking about the problem incorrectly?
Thanks in advance!
Add a trivial expression text that contains $2 and evaluates to true instead of "", for example
SELECT * FROM some_table WHERE username = $1 AND
${req.query.since !== undefined ? " hire_date >= $2": " (true or $2 = $2)"}
The planner will remove it anyway. Added true or just in case $2 is null.
Still it would be cleaner like this
if (req.query.since !== undefined)
{
let sql = `SELECT * FROM some_table WHERE username = $1 AND hire_date >= $2`;
const results = await pool.query(sql, [req.query.user, req.query.since]);
}
else
{
let sql = `SELECT * FROM some_table WHERE username = $1`;
const results = await pool.query(sql, [req.query.user]);
}
What about SQLi risk BTW?
Here is my scenario, i am parsing via javascript a webpage and then post the result to an restApi to store the json in a db. The code works fine as long as all fields i defined in my script are send. Problem is over time they website might change names for fields and that would cause my code to crash.
Originally i used code like this
const mySchool = new mls.School();
mySchool.highSchoolDistrict = data["HIGH SCHOOL DISTRICT"].trim();
mySchool.elementary = data.ELEMENTARY.trim();
mySchool.elementaryOther = data["ELEMENTARY OTHER"].trim();
mySchool.middleJrHigh = data["MIDDLE/JR HIGH"].trim();
mySchool.middleJrHighOther = data["MIDDLE/JR HIGH OTHER"].trim();
mySchool.highSchool = data["HIGH SCHOOL"].trim();
mySchool.highSchoolOther = data["HIGH SCHOOL OTHER"].trim();
newListing.school = mySchool;
but when the element does not exist it complains about that it can not use trim of undefined. So to fix this i came up with this
if (data["PATIO/PORCH"]) {
newExterior.patioPorch = data["PATIO/PORCH"].trim();
}
this works but i am wondering if there is a more global approach then to go and check each field if it is defined ?
You could leverage a sort of helper function to check first if the item is undefined, and if not, return a trim()-ed version of the string.
var data = Array();
data["HIGH SCHOOL DISTRICT"] = " 123 ";
function trimString(inputStr) {
return (inputStr != undefined && typeof inputStr == "string") ? inputStr.trim() : undefined;
}
console.log(trimString(data["HIGH SCHOOL DISTRICT"]));
console.log(trimString(data["ELEMENTARY OTHER"]));
For an application I'm running a query on DocumentDb in .NET. For this used I wanted to use a parametrized query, like this:
var sqlString = "select p.Id, p.ActionType, p.Type, p.Region, a.TimeStamp, a.Action from History p join a in p.Actions where a.TimeStamp >= #StartTime and a.TimeStamp <= #EndTime and p.ClientId = #ClientId and p.ActionType = #ActionType";
if (actionType != "") { sqlString += actionTypeFilter; }
var queryObject = new SqlQuerySpec
{
QueryText = sqlString,
Parameters = new SqlParameterCollection()
{
new SqlParameter("#StartTime", startDate),
new SqlParameter("#EndTime", endDate),
new SqlParameter("#ClientId", clientId.ToString()),
new SqlParameter("#ActionType", actionType)
},
};
var dataListing = _documentDbClient.CreateDocumentQuery<PnrTransaction>(UriToPnrHistories, queryObject, new FeedOptions() { MaxItemCount = 1 });
When I execute this, I'm getting en empty dataset. But when I use the same query, and build it using classic string replace it works just fine.
Can anyone tell me what I'm doing wrong in my parametrized query?
If the code above is the running code, you still add the actiontypeFilter on the parameterized SQL string. Try to remove the if statement on line 2. Seems to me that may be your problem.
It would help if you could post a sample document from the server.
I usually see this syntax:
SqlParameterCollection parameters = new SqlParameterCollection();
parameters.Add(...);
parameters.Add(...);
Try that and see if you get different results. It might be that the list you use to initialize it in your answer needs to be typed differently to work.
I wrote this query and as my understanding of the business rules has improved I have modified it.
In this most recent iteration I was testing to see if indeed I had some redundancy that could be removed. Let me first give you the query then the error.
public List<ExternalForums> GetAllExternalForums(int extforumBoardId)
{
List<ExternalForums> xtrnlfrm = new List<ExternalForums>();
var query = _forumExternalBoardsRepository.Table
.Where(id => id.Id == extforumBoardId)
.Select(ExtForum => ExtForum.ExternalForums);
foreach (ExternalForums item in query)
{
xtrnlfrm.Add(new ExternalForums { Id = item.Id , ForumName = item.ForumName, ForumUrl = item.ForumUrl });
}
return xtrnlfrm;
}
Now in case it isn't obvious the query select is returning List of ExternalForums. I then loop through said list and add the items to another List of ExternalForums object. This is the redundancy I was expecting to remove.
Precompiler was gtg so I ran through it one time to very everything was kosher before refactoring and ran into a strange error as I began the loop.
Unable to cast object of System.Collections.Generic.HashSet
NamSpcA.NamSpcB.ExternalForums to type NamSpcA.NamSpcB.ExternalForums.
Huh? They are the same object types.
So am I doing something wrong in the way I am projecting my select?
TIA
var query = _forumExternalBoardsRepository.Table
.Where(id => id.Id == extforumBoardId)
.Select(ExtForum => ExtForum.ExternalForums);
This query returns IEnumerable<T> where T is type of ExtForum.ExternalForums property, which I would expect to be another collection, this time of ExternalForum. And the error message matches that, saying you have IEnumerable<HashSet<ExternalForums>>.
If you need that collection of collections to be flattened into one big collection of ExternalForums use SelectMany instead:
var query = _forumExternalBoardsRepository.Table
.Where(id => id.Id == extforumBoardId)
.SelectMany(ExtForum => ExtForum.ExternalForums);
here my issue:
I've an IQueryable object and I need to execute a new query on Db (just like a refresh) by launching the same contained query in my IQueryable obj.
An example:
myObj = objCtx.Person
.Where(p => p.IdPerson...)
.OrderBy(p => ...)
.Select(..some field..);
//...
// From another function I just want re-execute
// the same query
// -> Well, that retrieve full lambda
var et = this.myObj.Expression
// This doesn't work:
var anotherObj = objCtx.Person.Where(et);
//or..
var tmp = Expression.Lambda<Func<T, bool>>(et);
// This doesn't work too:
var anotherObj = objCtx.Person.Where(tmp);
Is it possibile to achieve? What am I missing?
Thanks
Ok:
I would be able to retrieve full query (select statement) from an IQueryable object and execute it in order to get all my updated data.
This is the original query:
this.myIQueryableObj = objCtx.Person
.Where(p => p.IdPerson...)
.OrderBy(p => ...)
.Select(..some field..);
//..And in some button click, I want execute again the above query, but all what I know is myIQueryableObjOnly. Can you help me?