TYPO3 Querybuilder unsigned integer in createNamedParameter - typo3

The TYPO3 QueryBuilder method createNamedParameter does only support a signed integer but no unsigned integer. There is not even a PDO constant for an unsigned integer. And TYPO3 seems to have no other constants like for the arrays Connection::PARAM_INT_ARRAY.
static public function checkDoublePostExist ($table, $doublePostField, $key)
{
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table);
$queryBuilder->setRestrictions(GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\Query\Restriction\FrontendRestrictionContainer::class));
$result =
$queryBuilder
->count('*')
->from($table)
->where(
$queryBuilder->expr()->eq($doublePostField, $queryBuilder->createNamedParameter($key, \PDO::PARAM_INT))
)
->execute()
->fetchColumn(0);
return $result;
}
The SQL field is defined as:
doublePostCheck int(11) unsigned DEFAULT '0' NOT NULL
The generated SQL will be like this:
SELECT COUNT(*) FROM `tt_board` WHERE (`doublePostCheck` = -1018532669) AND (`tt_board`.`deleted` = 0)
This is weird, because no negative values are allowed for this field. I am afraid that this query will not do the right thing.
Can the string format be used for an unsigned integer?
->where(
$queryBuilder->expr()->eq($doublePostField, $queryBuilder->createNamedParameter($key, \PDO::PARAM_STR))
)
The generated SQL would be correct:
SELECT COUNT(*) FROM `tt_board` WHERE (`doublePostCheck` = '3276434627') AND (`tt_board`.`deleted` = 0)
Which solution is the best for unsigned integers?

The problem is that your integer value is higher as the field int(11) can work with.
The maximum value for unsigned fields is 2147483647, see https://dev.mysql.com/doc/refman/8.0/en/integer-types.html. Your value (3276434627) is quite higher than this.
As PHP's integer maximum value is the same, it begins to start in negative area when the value is higher than maximum value.
The solution would be to change the database field to use bigint.

Related

Check if character varying is between range of numbers

I hava data in my database and i need to select all data where 1 column number is between 1-100.
Im having problems, because i cant use - between 1 and 100; Because that column is character varying, not integer. But all data are numbers (i cant change it to integer).
Code;
dst_db1.eachRow("Select length_to_fault from diags where length_to_fault between 1 AND 100")
Error - operator does not exist: character varying >= integer
Since your column supposed to contain numeric values but is defined as text (or version of text) there will be times when it does not i.e. You need 2 validations: that the column actually contains numeric data and that it falls into your value restriction. So add the following predicates to your query.
and length_to_fault ~ '^\+?\d+(\.\d*)?$'
and length_to_fault::numeric <# ('[1.0,100.0]')::numrange;
The first builds a regexp that insures the column is a valid floating point value. The second insures the numeric value fall within the specified numeric range. See fiddle.
I understand you cannot change the database, but this looks like a good place for a check constraint esp. if n/a is the only non-numeric are allowed. You may want to talk with your DBA ans see about the following constraint.
alter table diags
add constraint length_to_fault_check
check ( lower(length_to_fault) = 'n/a'
or ( length_to_fault ~ '^\+?\d+(\.\d*)?$'
and length_to_fault::numeric <# ('[1.0,100.0]')::numrange
)
);
Then your query need only check that:
lower(lenth_to_fault) != 'n/a'
The below PostgreSQL query will work
SELECT length_to_fault FROM diags WHERE regexp_replace(length_to_fault, '[\s+]', '', 'g')::numeric BETWEEN 1 AND 100;

Postgres describe of aggregate function returning type modification value of -1 for user defined type

I've created an aggregate function with the following:
CREATE FUNCTION rtrim(mychar) RETURNS mychar
AS '$libdir/libmy_pgmod', 'mycharrtrim'
LANGUAGE C IMMUTABLE STRICT;
CREATE OR REPLACE FUNCTION mychar_max( mychar, mychar ) RETURNS mychar
AS '$libdir/libmy_pgmod', 'mychar_max'
LANGUAGE C IMMUTABLE STRICT;
CREATE OR REPLACE FUNCTION mychar_min( mychar, mychar ) RETURNS mychar
AS '$libdir/libmy_pgmod', 'mychar_min'
LANGUAGE C IMMUTABLE STRICT;
CREATE AGGREGATE MAX( lzchar ) (
SFUNC = mychar_max,
STYPE = mychar,
SORTOP = >
);
CREATE AGGREGATE MIN( mychar ) (
SFUNC = mychar_min,
STYPE = mychar,
SORTOP = <
);
The mychar is a type that is defined with 2 type modifiers. The first type modifier is the length of the string and the 2nd is the CCSID of the string since we are tying to simulate a zOS string. I then create at table like the following:
create table t1 (c1 mychar(20, 1208), c2 char(20));
Within my C code I then try to do a describe of the following statement:
select c1, max(c1), max(c2) from t1 group by c1;
The describe returns fine, however, when I try to retrieve the data from the describe using the following code:
char *colName = PQfname( result, hvNum );
int colTmod = PQfmod( result, hvNum );
int colSize = PQfsize( result, hvNum );
Oid oid = PQftype( result, hvNum );
Oid tblOid = PQftable( result, hvNum );
For the first column I get the expected values (colName, colTmod, oid and tblOid). For the 2nd column (max(c1)) it returns max as the colName (which I expected), it also correctly returns the correct oid. However, for colTmod it returns -1. Is there something that I need to do to get the proper colTmod value returned in this case? For the max(c2) column which is a native char it correctly returns everything as expected including the colTmod as 24. There must be something I am doing incorrectly that results in my implementation of the char or the aggregate function not returning the type modification value correctly.
I am not 100% certain, but I am pretty sure that the result of an aggregate function has no type modifiers.
I tried your experiment with a column defined as numeric(7,2), and like you I got -1 for PQfmod when I queried the maximum.
numeric's max aggregate is defined using numeric_larger, which is defined as:
Datum
numeric_larger(PG_FUNCTION_ARGS)
{
Numeric num1 = PG_GETARG_NUMERIC(0);
Numeric num2 = PG_GETARG_NUMERIC(1);
/*
* Use cmp_numerics so that this will agree with the comparison operators,
* particularly as regards comparisons involving NaN.
*/
if (cmp_numerics(num1, num2) > 0)
PG_RETURN_NUMERIC(num1);
else
PG_RETURN_NUMERIC(num2);
}
So it is as simple as it can get and returns one of the input values.
If type modifiers are not preserved there, I'd guess they are never preserved in an aggregate.

convert varchar to bigint function

I want to create a function that converts a string of characters to bigint. If the conversion is not possible the function should return null. I want the function to work for normal representation (example '10000') and mantissa-exponent representation ('1e1+10') Here is what I have written so far:
ALTER FUNCTION [dbo].[udf_get_bigint]
(
#character varchar(100)
)
RETURNS bigint
AS
BEGIN
if ISNUMERIC(#character)=0 return null
if LEN(ltrim(rtrim(#character)))>25 return null
declare #nr numeric(25,4)
if charindex('e',lower(#character))>0
begin
declare #real real
**set #nr=CONVERT(real,#character)**
if #nr not between convert(numeric(25),-9223372036854775808) and
convert(numeric(25),9223372036854775807)
return null
set #real = convert(real, #nr)
return convert(bigint,convert(numeric(25),#real))
end
else
set #nr=CONVERT(numeric(25,4),#character)
if #nr between convert(numeric(25),-9223372036854775808) and
convert(numeric(25),9223372036854775807) return convert(bigint,#nr)
return null
END
Now the only problem appears when I need to deal with overflows for mantissa exponent representation. The bolded conversion falls in case of overflow; but what I want it to do is to return null.
How can I put some preconditions on that conversion so that it does not fall anymore.
call example : select dbo.udf_get_bigint('3e0210')
output: Arithmetic overflow error converting expression to data type real.
Use float instead of real. It may contradict the variable name, but it makes that part of the script work
declare #real float
This code will verify
select CONVERT(float,'3e0210')

SSRS Parameters. Allowing "All" or "Null"

SSRS parameters are a pain. I want to be able to re-use reports for many different needs by allowing the users access to many different parameters and making them optional.
So, if I start out with code such as:
Select * from mytable myt
where myt.date between '1/1/2010' and '12/31/2010'
and year(myt.date) = '2010'
and myt.partnumber = 'XYZ-123'
I want those parameters to be optional so my first attempts were to make the parameters default to null such as:
and (myt.partnumber = (#PartNumber) or (#PartNumber) is null)
That has problems because if the database fields in question are nullable then you will drop records because null does not equal null.
I then used code such as this:
DECLARE #BeginDate AS DATETIME
DECLARE #EndDate AS DATETIME
DECLARE #PartNumber AS VARCHAR(25)
SET #Year = '..All'
SET #BeginDate = '1/1/2005'
SET #EndDate = '12/31/2010'
SET #PartNumber = '..All'
SET #Year = '..All'
Select * from mytable myt
where (myt.date between (#BeginDate) and (#EndDate))
and (year(myt.date) = (#Year) or (#Year) = '..All' )
and (myt.partnumber = (#PartNumber) or (#PartNumber) = '..All')
That doesn't work because Year(myt.date) is an integer and #Year is not.
So, here are my questions.
How can I make my dates optional? Is the best way to simply default them to dates outside of a practical range so I return all values?
What is the best way to handle the null or '..All' options to make my queries as readable as possible and allow my users to have optional parameters for most data types? I'd rather not use null for
Go ahead and allow nulls, which indicates the filter should not be applied. Then, you can use the following:
SELECT *
FROM mytable myt
WHERE COALESCE(myt.date, '1/1/1900') between COALESCE(#BeginDate, myt.date, '1/1/1900') and COALESCE(#EndDate, myt.date, '1/1/1900')
AND COALESCE(YEAR(myt.date), -1) = COALESCE(#Year, YEAR(myt.date), -1)
AND COALESCE(myt.partnumber, -1) = COALESCE(#PartNumber, myt.partnumber, -1)
In summary, if any variable value is NULL, then compare the column value to itself, which effectively ignores the condition. More specifically, when testing myt.date, if #BeginDate is NULL then set the lower range value equal to the myt.date value. Do the same substitution with the #EndDate value. Even, if both #BeginDate and #EndDate are NULL, the condition will be true.
A similar approach is used for YEAR(myt.date) and myt.partnumber. If the variable value is NULL, then compare the column value to itself, which is always true.
UPDATE:
Added a default value to each COALESCE to handle the situation where the column value is NULL.
I like your third code block. It seems like your WHERE clause could be corrected to work with a non-int value. The AND clause for the year line would look like this--not my best T-SQL, but it should get you pointed in the right direction:
and 1 = CASE #Year WHEN '..All' THEN 1 ELSE CASE WHEN year ( myt.date ) = CONVERT ( int, #Year ) THEN 1 ELSE 0 END END
This will allow you to have a string value of '..All' or an int value. Either will match correctly. You can do the same with partnumber.
try it like this, the key is to fix your null parameters values to surrogate nulls, also since sql server supports short circuit evaluation, putting the null check should generally perform better.
Select * from mytable myt
where (myt.date between (#BeginDate) and (#EndDate))
and (#Year IS NULL OR COALESCE(myt.date,'1900') = #Year)
and (#PartNumber IS NULL OR ISNULL(myt.partnumber, '<NULL>') = (#PartNumber)

Iphone: How to read return value of strftime SQLite function

I need to read the column value of the following query
SELECT strtime('%Y-%m', created_at) as field FROM table GROUP BY field
the type of column field is 3 (I assume it is blog), but I need string
How should I?
Updated
const char* sql =
"SELECT CAST(strftime('%Y', created_at) as INTEGER) as year FROM table GROUP BY year
if (SQLITE_OK == sqlite3_prepare_v2([AppDelegate db], sql, -1, &queryhandle, NULL)){
while(SQLITE_ROW == sqlite3_step(queryhandle)){
NSSTring* year = [NSString stringWithFormat:#"%d",sqlite3_column_int(queryhandle, 0)];
this code gives me 3460 as year
(have checked cast as integer, and cast as varchar and sqlite3_column_text function)
As long as created_at has a time string format as specified in the sqlite docs, it doesn't matter what storage type is used in a particular tuple (although you can find out using the typeof) function. The result of the strftime function (you aliased it as field) is either text or null.
CREATE TABLE t (x BLOB);
INSERT INTO t VALUES ("2010-03-29\x0"), ("2010-03-28");
SELECT strftime("%Y-%m", x), typeof(strftime("%Y-%m", x)), typeof(x) FROM t;
|null|text
2010-03|text|text