Is there a nice way to assign a string to a variable when it spans many lines?
The reason for this is I have some large SQL statements (which I want in the pas) but it's annoying like this
var
sql : string;
begin
sql := 'SELECT * ' +
'FROM foo ' +
'WHERE `this`=0';
That is annoying to copy and paste into terminal / another program because I have to remove the ' and ' + etc.
Is there a way to so something like...
var
sql : string;
begin
sql := ""SELECT *
FROM foo
WHERE `this`=0"";
So some way to assign a block of text/string with new lines without having to concat it.
As there is no way of expressing strings in this way in SQL, I normally use the RegEx search and replace available in the Delphi IDE to format strings in the required way.
SELECT *
FROM foo
WHERE `this`=0
This replaces any line with the line enclosed in quotes, followed by + sLineBreak +
sql :=
' SELECT *' + sLineBreak +
' FROM foo' + sLineBreak +
' WHERE `this`=0' + sLineBreak +
I then just tidy up the last line:
sql :=
' SELECT *' + sLineBreak +
' FROM foo' + sLineBreak +
' WHERE `this`=0';
Of course the same can be done with any preceding or trailing text, such as qry.SQL.Add('\0');
Your question is:
Can a Delphi string literal span multiple lines?
The answer is no.
Not that I know of (at least not out of the box). Anyway, you might want to take a look at this:
How to assign a multiline string value without quoting each line?
You could keep the SQL in a component which has a TStrings property like TSQLQuery, but my solution for longer / complex statements is to keep an 'example' copy as a source code comment, which has actual parameters to make tests easier, and keep both version in sync.
If you like the way C# does it (like I do), then don't forget to vote for this QC report:
http://qc.embarcadero.com/wc/qcmain.aspx?d=2012
It suggests to make your example look like this:
var
sql : string;
begin
sql := #'SELECT *
FROM foo
WHERE `this`=0';
If you install GExperts in Delphi, the IDE will automatically insert a '+ after pressing >enter< if you're inside a string and haven't closed it yet.
Download link: http://www.gexperts.org/download.html
Related
My installer has Components which come associated with downloadable files. These things are changing from build to build, so I'm using #insert to create the [Components] section as well as the appropriate entries in the [Files] section.
Some of these components rely on common downloadable files.
To now include the correct urls in the downloads page, I'm currently defining array variables that are named like the component and have as values the names of the required downloadable files, for example:
#dim myfeature[2] {"01aed27862e2087bd117e9b677a8685aebb0be09744723b4a948ba78d6011bac", "677756ac5969f814fd01ae677dbb832ab2e642513fea44ea0a529430d5ec1fdc"}
In the code for the download page I'm checking which components where selected via WizardSelectedComponents() and after converting the string to an array of strings, I'm trying to get to the previously defined variable and that is where I'm failing:
function GetDownloads(): Array of String;
var
Downloads: Array of String;
SelectedComponents: String;
SelectedArray: Array of String;
begin
SelectedComponents := WizardSelectedComponents(False);
// a custom procedure to parse the comma seperated string
SelectedArray := ParseArray(SelectedComponents, SelectedArray);
// trying to get to the constant array now this works:
MsgBox(ExpandConstant('{#myfeature[0]}'), mbInformation, MB_OK);
// same but trying to use the selected component value returns this as a literal
// '+SelectedArray[0]+' instead the expanded value
MsgBox(ExpandConstant('{#' + SelectedArray[0] + '[0]}'), mbInformation, MB_OK);
end;
So I understand something is up with the # mark but I could not find a way to solve this properly.
Thank you!
Markus
ExpandConstant expands Inno Setup "constants", not preprocessor values. See also Evaluate preprocessor macro on run time in Inno Setup Pascal Script.
You cannot access elements of a preprocessor compile-time array using run-time indexes.
If you know C/C++, it's like if you were trying to do:
#define VALUE1 123
#define VALUE2 456
int index = 1;
int value = VALUE ## index
I'm not really sure I completely understand what are you doing. But it seems that you need to create an array on compile time from various sources and use it on runtime.
There are several approaches that can be used for that. But you definitely need runtime array initialized on run time. But the code that initializes it can be generated on compile time.
An example of the approach follows (and some links to other approaches are at the end).
At the beginning of your script, define these support functions:
[Code]
var
FeatureDownloads: TStrings;
function AddFeature(
Feature: Integer; CommaSeparatedListOfDownloads: string): Boolean;
begin
if not Assigned(FeatureDownloads) then
begin
FeatureDownloads := TStringList.Create();
end;
while FeatureDownloads.Count <= Feature do
FeatureDownloads.Add('');
if FeatureDownloads[Feature] <> '' then
RaiseException('Downloads for feature already defined');
FeatureDownloads[Feature] := CommaSeparatedListOfDownloads;
Result := True;
end;
#define AddFeature(Feature, CommaSeparatedListOfDownloads) \
"<event('InitializeSetup')>" + NewLine + \
"function InitializeSetupFeature" + Str(Feature) + "(): Boolean;" + NewLine + \
"begin" + NewLine + \
" Result := AddFeature(" + Str(Feature) + ", '" + CommaSeparatedListOfDownloads + "');" + NewLine + \
"end;"
In your components include files, do:
#emit AddFeature(2, "01aed27862e2087bd117e9b677a8685aebb0be09744723b4a948ba78d6011bac,677756ac5969f814fd01ae677dbb832ab2e642513fea44ea0a529430d5ec1fdc")
If you add:
#expr SaveToFile(AddBackslash(SourcePath) + "Preprocessed.iss")
to the end of your main script, you will see in the Preprocessed.iss generated by the preprocessor/compiler that the #emit directive expands to:
<event('InitializeSetup')>
function InitializeSetupFeature2(): Boolean;
begin
Result := AddFeature(2, '01aed27862e2087bd117e9b677a8685aebb0be09744723b4a948ba78d6011bac,677756ac5969f814fd01ae677dbb832ab2e642513fea44ea0a529430d5ec1fdc');
end;
Now you have FeatureDownloads Pascal Script runtime variable that you can access using FeatureDownloads[SelectedArray[0]] to get comma-separated string, which you can parse to the individual downloads.
This can be optimimized/improved a lot, but I do not know/understand the extent of your task. But I believe that once you grasp the concept (it might be difficult at the beginning), you will be able to do it yourself.
Another similar questions:
Evaluate a collection of data from preprocessor on run time in Inno Setup Pascal Script (simple example that be easier to grasp initially)
Scripting capabilities in the Registry section (slightly different approach from times event attributes were not available yet – and that's YOUR question)
Very new to SQL so I appreciate your patience in advance.
I have a column in a table that stores a particular set of instructions; each instruction is encapsulated by a carriage return.
eg: char(13)+ #instruction1 + char(13)...
#Instruction1 is a string of variable length but I do know a certain part of the string eg: #instruction1 = some string + #knownstring + some string.
So we have char(13) + (some string + #knownstring + some string) +char(13).
I want to replace this entire line with ''.
Identifying it just using the #knownstring.
Is this possible?
Thanking you all again, I really appreciate your assistance
select replace(replace(column,#knownsting,''),char(13),'')
from table
where key=1235
Replaces only the #knownstring but I also need to replace the surrounding text between the two char(13)
You might try something along this:
DECLARE #KnownString VARCHAR(50)='Keep This'
DECLARE #YourString VARCHAR(MAX)='blah' + CHAR(13) + 'dummy keep this dummy more' + CHAR(13) + 'Something without the known part' + CHAR(13) + 'Again with Keep THIS';
SELECT STUFF(
(
SELECT CHAR(13) + CASE WHEN CHARINDEX(#KnownString,LineText)>0 THEN #KnownString ELSE LineText END
FROM (SELECT CAST('<x>' + REPLACE(#YourString,CHAR(13),'</x><x>') + '</x>' AS XML)) A(Casted)
CROSS APPLY Casted.nodes('/x') B(fragment)
OUTER APPLY (SELECT fragment.value('text()[1]','nvarchar(max)')) C(LineText)
FOR XML PATH(''),TYPE
).value('.','nvarchar(max)'),1,1,'');
The result
blah
Keep This
Something without the known part
Keep This
The idea
The string is transformed to XML by replacing the line breaks with XML tags. Now we can query all text lines separately, check them for the known string, do the needed manipulation, and finally reconcatenate all fragments using the XML-trick (together with STUFF to get rid of the leading CHAR(13)).
Remarks
Using v2016 I'd use the split-string approach with OPENJSON and starting with v2017 there is STRING_AGG() to make the reconcatenation easier.
This question already has an answer here:
Escape Variables with Printf
(1 answer)
Closed 3 years ago.
I have an SQL query that looks like this:
SELECT name FROM sessions WHERE name ILIKE 'org_name.%';
but I'm actually interested in replacing 'org_name' with format string (%s).
I was trying to do something like this:
query := fmt.Sprintf("SELECT name FROM sessions WHERE name ILIKE '%s.%'", "org_name2")
but go seems to not like it, since writing %' isn't valid as format string.
I know I can solve it with do it in that way:
orgName := "org_name2"
condition := fmt.Sprintf("%s", orgName) + ".%"
query := fmt.Sprintf("SELECT name FROM sessions WHERE name ILIKE '%s'", condition)
but, I'd rather not, since the variable here is solely the org_name.
Is there a solution for this?
Thanks!
As documented in the fmt package, a literal % can be represented by %% in a printf format string:
query := fmt.Sprintf("SELECT name FROM sessions WHERE name ILIKE '%s.%%'", orgName)
But be aware, you should NEVER, EVER build your SQL queries this way! You are potentially opening yourself for SQL injection attacks. Instead, you should pass parameterized arguments:
query := "SELECT name FROM sessions WHERE name ILIKE ?"
rows, err := db.Query(query, orgName + ".%")
In go it is just fmt.Printf("%s is how you write a %%", "This")
https://play.golang.org/p/RIJKRADYzCk
I would like to copy the first words of multiple lines.
Example of code :
apiKey := fmt.Sprintf("&apiKey=%s", args.ApiKey)
maxCount := fmt.Sprintf("&maxCount=%d", args.MaxCount)
id := fmt.Sprintf("&id=%s", args.Id)
userid := fmt.Sprintf("&userid=%s", args.Userid)
requestFields := fmt.Sprintf("&requestFields=%s", args.RequestFields)
I would like to have this in my clipboard :
apiKey
maxCount
id
userid
requestFields
I tried with ctrl-v and after e, but it copies like on the image :
You could append every first word to an empty register (let's say q) using
:'<,'>norm! "Qyiw
That is, in every line of the visual selection, execute the "Qyiw sequence of normal commands to append (the first) "inner word" to the q register.
You need to have > in cpoptions for the newline to be added in between yanks (:set cpoptions+=>), otherwise the words will be concatenated on a single line.
If you want to quickly empty a register you can use qqq in normal mode (or qaq to empty register a).
Note: the unnamed register ("") will also contain what you want at the end of the operation, so you don't need to "qp to paste it, p will do.
I think the chosen answer is a really good one, the idea of appending matches to registers can be pretty useful in other scenarios as well.
That said, an alternative way to get this done might be to align the right-hand side first, do the copying and then undo the alignment. You can use a tool like tabular, Align or easy-align.
With tabular, marking the area and executing :Tab/: would result in this:
apiKey : = fmt.Sprintf("&apiKey=%s", args.ApiKey)
maxCount : = fmt.Sprintf("&maxCount=%d", args.MaxCount)
id : = fmt.Sprintf("&id=%s", args.Id)
userid : = fmt.Sprintf("&userid=%s", args.Userid)
requestFields : = fmt.Sprintf("&requestFields=%s", args.RequestFields)
You can now use visual block mode to select the first part, and then use u to undo the alignment.
Relying on the external cut program:
:'<,'>!cut -d' ' -f1
I am trying to convert a string in Oracle into a modified string that is compatible with a specific API.
I would like to leave all alphanumeric characters intact, replace all spaces with the + character, and replace all special characters with % plus their hex code.
For example,
Project 1: Nuts & Bolts
should become
Project+1%3A+Nuts+%26+Bolts
Is there any way to do this using only SQL?
I don't think you can get there with plain SQL without nested replace calls. You can get your sample value with the utl_url.escape() function, but because you have to pass it a second parameter and that is a boolean, you have to do it in an PL/SQL block:
set define off
begin
dbms_output.put_line(replace(utl_url.escape('Project 1: Nuts & Bolts', true),
'%20', '+'));
end;
/
Project+1%3A+Nuts+%26+Bolts
The url_utl.escape function converts the spaces to %20:
Project%201%3A%20Nuts%20%26%20Bolts
... and the single replace call converts those to +.
As Ed Gibbs said, you can make that a function so you can at least call it from plain SQL:
create or replace function my_escape(str in varchar2) return varchar2 is
begin
return replace(utl_url.escape(str, true), '%20', '+');
end;
/
set define off
select my_escape('Project 1: Nuts & Bolts') from dual;
MY_ESCAPE('PROJECT1:NUTS&BOLTS')
--------------------------------
Project+1%3A+Nuts+%26+Bolts
You probably need to check the legal and reserved character lists to see if there's anything else that needs special handling.
(I've used set define off to stop my client treating the ampersand as a substitution variable; your client or application might not need that, e.g. if calling over JDBC).
apex_util.url_encode should work.