Showing converted Base64 (from hex) in an existing SQL Server 2019 view - image

I do voluntary work at an animal shelter. We have an application which uses a SQL Server 2019 database. I have created a view that includes a varbinary(max) column. The value in this column is a picture, stored in hexadecimal-format. I would like to convert this Hex-value to a base64-binary file and add these to the view as an extra column.
I found the perfect solution for my situation in SQL Server : hex to base64. The example provided converts 1 single hex-value into 1 base64-value. I now need to add this solution to my view, but I'm not having any success.
The offered solution:
DECLARE #TestBinHex varchar(max), #TestBinary varbinary(max), #Statement nvarchar(max);
SELECT #TestBinHex = '0x012345';
SELECT #Statement = N'SELECT #binaryResult = ' + #TestBinHex;
EXECUTE sp_executesql #Statement, N'#binaryResult varbinary(max) OUTPUT', #binaryResult=#TestBinary OUTPUT;
SELECT
CAST(N'' AS XML).value(
'xs:base64Binary(xs:hexBinary(sql:column("bin")))'
, 'VARCHAR(MAX)'
) Base64Encoding
FROM
(SELECT #TestBinary AS bin) AS bin_sql_server_temp;
A simplified version of my view:
SELECT
a.cat_id, a.catname, s.cat_id,
s.stay_id, s.shelter_handler, s.shelter_kennel, s.picture
FROM
dbo.animal AS a
OUTER APPLY
(SELECT TOP 1 *
FROM dbo.shelterdata
WHERE a.cat_id = s.cat_id
ORDER BY s.stay_id DESC) AS S
WHERE
(a.cat_id IS NOT NULL) AND (s.leave_date IS NULL)
The view shows an overview of all cats currently present in the shelter (leave_date is NULL). The reason for the TOP 1 is that sometimes shelter animals get returned, and the application then assigns a new stay_id. To prevent duplicate values from the join, I only return the value of the most recent stay_id.
What I am trying to achieve: the second table (dbo.shelterdata) includes the picture, stored in hex value. I'd like to add a column Base64Encoding to the view which includes the converted value.
My attempts
I was successful in replacing the static value '0x012345' by a SELECT statement. But the way the solution is formatted, it only allows for one input value. So I had to restrict it with a WHERE clause. It is obvious to me that I need to make a subquery which inputs the hex value based on the unique cat_id. However, it has been many years since I worked with variable, so I'm struggling with the formatting of the statement.
My request
Does anyone have a suggestion how to build the conversion into the view?
Any assistance would be greatly appreciated.

After searching for a few more hours, I stumbled onto the solution. Maybe it will help someone else in the future. The solution is remarkably simple, as is often the case.
My view, mentioned above, is called dbo.shelter_view
select sv.picture,sv.cat_id,
cast('' as xml).value(
'xs:base64Binary(sql:column("sv.picture"))', 'varchar(max)'
) as Base64Encoding
from dbo.shelter_view as SV

Related

How to pass more than one string in bind variable in a Oracle Report formula column? [duplicate]

This question already has answers here:
how to convert csv to table in oracle
(5 answers)
Closed 4 years ago.
We’ve got a page that displays a list of records, and the user is allowed to check the desired lines, and then get a report printed of those selected records. I’m trying to define the Oracle Report to have a single parameter which would be a comma-delimited list of the IDs to be printed. However it is not working. The SQL is something like this:
Select * from CEP_TABLE where Table_ID in (:P_IDLIST)
If I define the parameter to be numeric, I get an “invalid parameter input” error when trying to give it 654,655 – it doesn’t like having the comma.
If I define the parameter to be character, it accepts the parameter but then the database gives an “invalid number” error. It appears it is substituting the bind variable with the entire parameter in quotes – e.g.
Select * from CEP_TABLE where Table_ID in (‘654,655’)
But I want it without the quotes:
Select * from CEP_TABLE where Table_ID in (654,655)
I can get the report to work if I define multiple parameters – e.g. Table_ID in (:P1,:P2,:P3,:P4) but they could have a hundred items they want to include so it seems crazy to define 100 parameters – and really… I don’t want any limit.
Has anyone tackled this problem before? – it seems like it would be pretty common. I could have the page write out the selected ids to some temporary table and then define the query to join to that table, but that also seems excessive.
There's a hero that comes to rescue, and his name is lexical parameter.
You said that your query looks like this:
select *
from CEP_TABLE
where Table_ID in (:P_IDLIST)
Report already contains parameter named p_idlist. Now create another one, manually; let's call it lex_idlist. Its datatype should be character, its length somewhat larger than p_idlist parameter's (for example, if p_idlist is character(50), make lex_idlist character(70)).
Then rewrite query as follows:
select *
from cep_table
&lex_idlist
Go to After Parameter Form trigger which should look like this:
function AfterPForm return boolean is
begin
:lex_idlist := 'where table_id in (' || :p_idlist || ')';
return (TRUE);
end;
That's all - compile the report and run it. Enter some values into p_idlist parameter (such as 654,655). The trigger will dynamically create the WHERE clause, store it into the lexical parameter which will then "act" as if it was a real WHERE clause.
Read more about lexical parameters in online Reports Help system.
This is a very common and non-trivial question.
You need to split a single input string into its component tokens. Since I don't have your table, I illustrate how it's done on the emp table in the standard scott schema; here is how you can get the rows for the list of department numbers given in a single comma-separated string, such as '10,30':
select * from scott.emp
where deptno in
(select to_number(regexp_substr(:p_deptno, '\d+', 1, level)) from dual
connect by level <= regexp_count(:p_deptno, '\d+')
)
;
In fairness, there are also methods that don't split the string, and instead play silly games with string comparisons; but those will be quite ineffective - for example they would not allow you to use an index you may have on table_id (or on deptno in my example).

How to identify column types during sql injection?

Situation is following:
I have identified sql injection attack vector, and have following information about target table:
It has six columns. (Identified using "order by").
I can see output of 3 of them (table is displayed). two seems kind of enum value (integer in database?), and one is a date. I have very strong suspicion that col #6 is date column.
I'm almost sure the database is oracle. (ROWNUM works and LIMIT gives error).
I don't have error messages (always generic text is returned - "something went wrong").
Frontend is PHP if that matters. But there might be middle layer between it and database (e.g. java service), so I'm not sure where the query is being constructed.
E.g. following search query works as expected:
test' AND ROWNUM <= 5 ORDER BY 6--
EDIT-FROM-HERE:
Ok after help from comments, following query works:
test' UNION ALL SELECT null,null,null,null,null,null FROM dual--
(I was missing FROM dual part. Thank you #kordirko very much!)
This query adds one empty record in the output table (it is visually visible), so I'm definitely on the right track!
Now following line also works:
test' UNION ALL SELECT null,null,null,n't',null,null FROM dual--
I correctly identified 4th column and now it displays uppercase(?) letter T where I expected it to appear. So far so good. But it gives error when I input any string longer than 1 char! So following gives an error:
test' UNION ALL SELECT null,null,null,n'test',null,null FROM dual--
I'm no expert in SQL injection, and especially ORACLE (though have experience with MsSql).
I think the problem is something unicode-ansi-whateverencoding-related. For other rows (selected by original query before my UNION ALL SELECT addition) the 4th column gives multi-character normal strings. But when I try to inject desired string, it only works if it's one character, and also misteriously displays it in uppercase. I think this must be some encoding problem. I just discovered I needed n prefix for unicode string after 1 hour of searching and struggling. Maybe some Oracle gurus can quickly spot what mistake do I have in my query?

Process SQL result set entirely

I need to work with a SQL result set in order to do some processing for each column (medians, standard deviations, several control statements included)
The SQL is dynamic so I don't know the number of columns, rows.
First I tried to use temporary tables, views, etc to store the results, however I did not manage to overcome the 30 character limit of Oracle columns when using the below sql:
create table (or view or global temporary table) as select * from (
SELECT
DMTTBF_MAT_MATURATO_BILL_POS.MAT_V_COD_ANNOMESE,
SUM(DMTTBF_MAT_MATURATO_BILL_POS.MAT_N_NUM_EVENTI_CHZ +DMTTBF_MAT_MATURATO_BILL_POS. MAT_N_NUM_EVENTI) <-- exceeds the 30 character limit
FROM DMTTBF_MAT_MATURATO_BILL_POS
WHERE DMTTBF_MAT_MATURATO_BILL_POS.MAT_V_COD_ANNOMESE >= '201301'
GROUP BY DMTTBF_MAT_MATURATO_BILL_POS.MAT_V_COD_ANNOMESE
)
Second choice was to use some PL/SQL types to store the entire table information, so I could call it like in other programming languages (e.g. a matrix result[i][j]) but I could not find anything similar.
Third variant, using files for reading and writing: i did not try it yet; i'm still expecting a more elegant pl/sql solution
It's possible that I have the wrong approach here so any advice is more than welcome.
UPDATE: Modifying the input SQL is not an option. The program has to accept any select statement.
Note that you can alias both tables and fields. Using a table alias keeps references to it from producing walls of text in the query. Using one for a field gives it a new name in the output.
SELECT A.LONG_FIELD_NAME_HERE AS SHORTNAME
FROM REALLY_LONG_TABLE_NAME_HERE A
The auto naming adds _1 and _2 etc to differentiate the same column name coming from different table references. This often puts a field already borderline over the limit. Giving the fields names yourself bypasses this.
You can put the alias also in dynamic SQL:
sqlstr := 'create table (or view or global temporary table) as select * from (
SELECT
DMTTBF_MAT_MATURATO_BILL_POS.MAT_V_COD_ANNOMESE,
SUM(DMTTBF_MAT_MATURATO_BILL_POS.MAT_N_NUM_EVENTI_CHZ + DMTTBF_MAT_MATURATO_BILL_POS.MAT_N_NUM_EVENTI) AS '||SUBSTR('SUM(DMTTBF_MAT_MATURATO_BILL_POS.MAT_N_NUM_EVENTI_CHZ +DMTTBF_MAT_MATURATO_BILL_POS.MAT_N_NUM_EVENTI)', 1, 30)
||' FROM DMTTBF_MAT_MATURATO_BILL_POS
WHERE DMTTBF_MAT_MATURATO_BILL_POS.MAT_V_COD_ANNOMESE >= ''201301''
GROUP BY DMTTBF_MAT_MATURATO_BILL_POS.MAT_V_COD_ANNOMESE
)'

MS SQL statement using PARTIAL and DISTINCT in VB.NET

I have a table called Postcodes which has over half a million records.
Inside this table i have a column called PostCodeText which has the following:
NG1 1AA
NG1 1AB
NG1 1AC
NG2 1AA
NG2 5TH
NG17 3LP
DE15 4BP
NG17 5GL
DE19 4EE...
What I need is a MSSQL statement that return DISTINCT matches based on a partial string. For example: If I wanted to find all distinct NG postcodes I would want to return:
NG1
NG2
NG17
I've tried something like:
SELECT DISTINCT postcodetext
FROM postcodes
WHERE (postcode_text LIKE 'NG%')
ORDER BY postcodetext
I feel I might be close to the answer but its not there yet, any help would be much appricated.
Also I heard that using LIKE is a slower option then using = is there a faster way then doing this?
Try
SELECT LEFT(postcodetext,CHARINDEX(' ',postcodetext,0))
FROM postcodes
WHERE postcodetext like 'NG%'
GROUP BY LEFT(postcodetext,CHARINDEX(' ',postcodetext,0))
ORDER BY postcodetext
OK, I think I'm missing something here, the above statement work fine (if I drop the ORDER BY), when I run it in management studio. But when I run it in my VB.net code I'm having an issue:
After running the statement and assigning the values into my datatable, I then use the following code :
For Each rows In dt.Rows
Select Case opt
Case 1 '
frmSettings.lstAllPostcodes.Items.Add(rows.Item("postcodetext"))
frmSettings.ProgBarSettings.Value = i
i = i + 1
End Select
Next
It fails when trying to reference rows.item("postcodetext"). I realise that has something to do with the SELECT statement using the LEFT of. I would like my listbox to hold the partial results (ie NG1, NG2, NG17 etc etc)

Crystal Reports Issue turning a string into a number

I'm having one of those throw the computer out the window days.
I am working on a problem involving Crystal Reports (Version 10) and an Oracle Database (11g).
I am taking a view from the database that returns a string (varcahr2(50)) which is actually a number, when a basic SELECT * query is run on this view I get the number back in the format 000000000000100.00.
When this view is then used in Crystal Reports I can view the field data, but I can't sum the data as it is not a number.
I began, by attempting to using ToNumber on the field, to which Crystal's response was that the string was not numeric text. Ok fair enough, I went back to the view and ran TO_NUMBER, when this was then used in crystal it did not return any results. I also attempted to run TO_CHAR on the view so that I could hopefully import the field as text and then perform a ToNumber, yet the same as with the TO_NUMBER no records were displayed.
I've started new reports, I've started new views. No avail.
This seems to have something to do with how I am retrieving the data for the view.
In simplistic terms I'm pulling data from a table looking at two fields a Foreign Key and a Value field.
SELECT PRIMARY_KEY,
NVL(MAX(DECODE(FOREIGN_KEY, FOREIGN_KEY_OF_VALUE_I_NEED, VALUE_FIELD)), 0)
FROM MY_TABLE
GROUP BY PRIMARY_KEY
When I attempted to put modify the result using TO_NUMBER or TO_CHAR I have used it around the VALUE_FIELD itself and the entire expression, wither way works when the run in a SQL statement. However any TO_NUMBER or TO_CHAR modification to the statement returns no results in Crystal Reports when the view is used.
This whole problem smacks of something that is a tick box or equivalent that I have overlooked.
Any suggestions of how to solve this issue or where I could go to look for an answer would be greatly appreciated.
I ran this query in SQL Developer:
SELECT xxx, to_number(xxx) yyy
FROM (
SELECT '000000000000100.00' XXX FROM DUAL
)
Which resulted in:
XXX YYY
000000000000100.00 100
If your field is truly numeric, you could create a SQL Expression field to do the conversion:
-- {%NUMBER_FIELD}
TO_NUMBER(TABLE.VALUE_FIELD)
This turned out to be an issue with how Crystal Reports deals with queries from a database. All I needed to do was contain my SQL statement within another Select Statement and on this instance of the column apply the TO_NUMBER so that Crystal Reports would recognize the column values as numbers.
Hopefully this helps someone out, as this was a terrible waste of an afternoon.

Resources