Inno Setup Microsoft Word Templates Subdirectory Install - ms-word

I am a programmer for the Small Business Administration. SBA creates and provides free some programs to borrowers and lenders. The install code below is for one off our free programs for the end-user machine.
I searched for a week for a solution to my problem and then decided I needed to learn how to program what I needed. I am a complete novice at Pascal and Inno Setup. I stole code and tested all of the examples in this site and others. Thanks for all the help. I modified and tested over and over. Below is my code.
Note: I am providing this in the hope that it will be useful to others, but it doesn't quite work as planned. Hopefully there will be a working version posted with help from others. I have provided the entire code to help others understand what I struggled to learn. ;-)
Question: The install works perfectly EXCEPT the the [Code] section repeats. It runs the GetInstallDirectory at least once before reaching the {code:GetInstallDirectory} call in the [Files] section.
It runs through the If statement for Word 14 once, and then runs through the [Files] section once. My message "MsgBox ('1. Setup..." appears twice. Previously it was appearing 3 or 4 times, but I made a change which I can't remember and now it runs only twice.
Can anyone help me find the problem? Perhaps it is with the If,Then,Else statements. Then again it could be the order of the statements. Thanks in advance.
; Script generated by the Inno Setup Script Wizard.
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!
; Script written by Daniel H. Smith, Lead Programmer/Analyst, Base Technologies
; Washington, DC
#define MyAppName "IDAP mini-Wizard"
#define MyAppVersion "v 2013"
#define MyAppPublisher "Small Business Administration"
#define MyAppURL "http://www.sba.gov/"
#define MyAppExeName "IDAPv2013.exe"
#define MyAppCopyright "(c) 2013-2014"
[Setup]
; NOTE: The value of AppId uniquely identifies this application.
; Do not use the same AppId value in installers for other applications.
; (To generate a new GUID, click Tools | Generate GUID inside the IDE.)
AppId={{459E8784-C110-4348-A294-229C58CB00D2}
AppName={#MyAppPublisher}'s {#MyAppName} {#MyAppVersion}
AppVersion={#MyAppVersion}
AppVerName={#MyAppName} {#MyAppVersion}
AppPublisher={#MyAppPublisher}
AppPublisherURL={#MyAppURL}
AppSupportURL={#MyAppURL}
AppUpdatesURL={#MyAppURL}
AppCopyright={#MyAppCopyright}
DefaultDirName={app}
CreateAppDir=false
OutputDir=C:\Users\Dan\Documents\IDAP Development\Setup
OutputBaseFilename=IDAPv2013Setup
SetupIconFile=C:\Users\Dan\Documents\My Shapes\_private\folder.ico
Compression=lzma
SolidCompression=true
UsePreviousGroup=false
UsePreviousAppDir=false
InfoBeforeFile=C:\Users\Dan\Documents\IDAP Development\Setup\IDAPInfo.txt
WizardImageFile=compiler:WizModernImage-IS.bmp
WizardSmallImageFile=compiler:WizModernSmallImage-IS.bmp
InfoAfterFile=C:\Users\Dan\Documents\IDAP Development\Setup\IDAPWord.txt
AppContact=Auth-IDAP#sba.gov
PrivilegesRequired=none
RestartIfNeededByRun=False
VersionInfoVersion=1.0
VersionInfoCompany={#MyAppPublisher}
VersionInfoCopyright={#MyAppCopyright}
[Languages]
Name: english; MessagesFile: compiler:Default.isl
[Files]
Source: "C:\Users\Dan\Documents\IDAP Development\IDAP 2013 Boilerplate_V10.Clean.pdf"; DestDir: "{userdocs}\IDAP Documents"; Flags: ignoreversion
Source: "C:\Users\Dan\Documents\IDAP Development\IDAP Borrower Certification (9-25-12)(initials) final.docx"; DestDir: "{userdocs}\IDAP Documents"; Flags: ignoreversion
Source: "C:\Users\Dan\Documents\IDAP Development\IDAP Loan Note (Draft July 25).docx"; DestDir: "{userdocs}\IDAP Documents"; Flags: ignoreversion
Source: "C:\Users\Dan\AppData\Roaming\Microsoft\Templates\IDAPLoan2013.dotm"; DestDir: "{code:GetInstallDirectory}"; DestName: "IDAPWizard2013v111.dotm"; Flags: confirmoverwrite
[Messages]
// define wizard title and tray status msg
// both are normally defined in innosetup's default.isl (install folder)
SetupAppTitle = Install {#MyAppName} {#MyAppVersion}
SetupWindowTitle = Install {#MyAppName} {#MyAppVersion} -- {#MyAppPublisher}
[Run]
//Opens Word if User leaves checkbox checked on Finish
Filename: "{code:GetWordDirectory}"; Flags: postinstall; Description: "Open Word after Finish"; StatusMsg: "Opening Word ..."
[Code]
function GetWordDirectory(S: string) : String;
Begin
IF RegKeyExists(HKCU, 'Software\Microsoft\Office\14.0\Common\General') THEN BEGIN
Result := ExpandConstant('{pf32}\Microsoft Office\Office14\winword.exe');
end Else begin
Result := ExpandConstant('{pf32}\Microsoft Office\Office12\winword.exe');
end;
End;
//--------
function GetInstallDirectory(S: String) : String;
var installDirectory : String;
Version: TWindowsVersion;
Begin
//Word 14 Start
IF RegKeyExists(HKCU, 'Software\Microsoft\Office\14.0\Common\General') THEN BEGIN
GetWindowsVersionEx(Version);
if RegQueryStringValue(HKCU, 'Software\Microsoft\Office\14.0\Common\General', 'UserTemplates', installDirectory) then begin
//Successfully read the registry value
If (Version.Major = 6) and (Version.Minor = 1) and (Version.ProductType = VER_NT_WORKSTATION) //Windows 7
then begin
MsgBox ('1. Setup is installing to Word User Templates directory for: '+#10+#13+#10+#13+'---> Word 2010 and Windows 7 <---', mbInformation, MB_OK);
Result := installDirectory
end else begin //begin c2
//FAILED READING USERTEMPLATES DIRECTORY
//Windows <= 6.0 Not Windows 7 and above
If (Version.Major = 6) and (Version.Minor = 0) and (Version.ProductType = VER_NT_WORKSTATION) //Windows Vista or XP
then begin
MsgBox ('2. Setup is installing to Word default template directory for: '+#10+#13+#10+#13+'---> Word 2010 and Windows 7 <---', mbInformation, MB_OK);
Result := ExpandConstant('C:\Users\{username}\AppData\Roaming\Microsoft\Templates');
end else
MsgBox ('3. Setup is installing to Word default template directory for: '+#10+#13+#10+#13+'---> Word 2010 and Windows Vista/XP <---', mbInformation, MB_OK);
Result := ExpandConstant('C:\Documents and Settings\{username}\Application Data\Microsoft\Templates');
end;
end;
//End Word 14
//Start Word 12
END ELSE IF RegKeyExists(HKCU, 'Software\Microsoft\Office\12.0\Common\General') THEN BEGIN
GetWindowsVersionEx(Version);
if RegQueryStringValue(HKCU, 'Software\Microsoft\Office\12.0\Common\General', 'UserTemplates', installDirectory) then begin
// Successfully read the value
If (Version.Major = 6) and (Version.Minor = 0) and (Version.ProductType = VER_NT_WORKSTATION) //Windows 7
then begin //c3
MsgBox ('4. Setup is installing to Word User Templates directory for: '+#10+#13+#10+#13+'---> Word 2007 and Windows 7 <---', mbInformation, MB_OK);
Result := installDirectory
end else begin //begin c4
//FAILED READING VALUE
If (Version.Major = 6) and (Version.Minor = 0) and (Version.ProductType = VER_NT_WORKSTATION) //Windows Vista/XP
then begin
MsgBox ('5. Setup is installing to Word User Templates directory for: '+#10+#13+#10+#13+'---> Word 2007 and Windows 7 <---', mbInformation, MB_OK);
Result := ExpandConstant('C:\Users\{username}\AppData\Roaming\Microsoft\Templates');
end else
MsgBox ('6. Setup is installing to Word default templates directory for: '+#10+#13+#10+#13+'---> Word 2007 and Windows XP or Vista <---', mbInformation, MB_OK);
Result := ExpandConstant('C:\Documents and Settings\{username}\Application Data\Microsoft\Templates');
end;
end;
END;
//End Word 12
end; //Function GetInstallDirectory end
//End All
//------------------------------------------------------------------------------
//Following are reference notes
//How to code a if-elseif-else
// if condition then begin
// ...
// end else if condition then begin
// ...
// end else begin
// ...
// end;
//+------------+------------+-------+-------+---------+
//| Version | PlatformId | Major | Minor | Release |
//+------------+------------+-------+-------+---------+
//| Win32s | 0 | ? | ? | |
//| Win95 | 1 | 4 | 0 | 1995 08 |
//| Win98 | 1 | 4 | 10 | 1998 06 |
//| WinME | 1 | 4 | 90 | 2000 09 |
//| WinNT351 | 2 | 3 | 51 | 1995 04 |
//| WinNT4 | 2 | 4 | 0 | 1996 07 |
//| Win2000 | 2 | 5 | 0 | 2000 02 |
//| WinXP | 2 | 5 | 1 | 2001 10 |
//| Win2003 | 2 | 5 | 2 | 2003 04 |
//| WinXPx64 | 2 | 5 | 2 | 2003 03 |
//| WinCE | 3 | ? | ? | |
//| Vista | 2 | 6 | 0 | 2007 01 |
//| Win2008 | 2 | 6 | 0 | 2008 02 |
//| Win2008R2 | 2 | 6 | 1 | 2009 10 |
//| Win7 | 2 | 6 | 1 | 2009 10 |
//+------------+------------+-------+-------+---------+
//Word Templates default subdirectory before Windows 7
//C:\Documents and Settings\"user name"\Application Data\Microsoft\Templates\
//Word Templates default subdirectory after Windows 7
//C:\Users\"username"\AppData\Roaming\Microsoft\Templates\
//
//For MS Word WinWord.Exe location and registry entry
//HKEY_CURRENT_USER\Software\Microsoft\Office\14.0\Word\Options
//C:\Program Files (x86)\Microsoft Office\Office14\ -- For Word 14
//MS Word 14 UserTemplates registry
//HKCU, 'Software\Microsoft\Office\14.0\Common\General', 'UserTemplates'
//MS Word 12 UserTemplates registry
//HKCU, 'Software\Microsoft\Office\12.0\Common\General', 'UserTemplates'
//------------------------------------------------------------------------------

Update:
You're right, code function called from the DestDir parameter is being called twice. First time when the installation is being prepared and second time when the files are processed. I don't know the reason (it's quite deep in the source code), but you don't actually need to care about that. That parameter function's used to be just for getting destination directory, not for any kind of user interaction, so you don't need to worry that it's called twice.
Although I've made a review to your code. Note that it's untested written from resources listed below. Something to point out:
use constants, it's easier to maintain one constant instead of many places in the script
don't rely on that all users will have Office installed in the default Program Files directory
the default template directory is in user's application data, which in InnoSetup you can substitute by the {userappdata} constant, that is in your case safe to use, since you're running your setup with PrivilegesRequired=none, so there's no risk with the forced administrator user difference
if you your users must have at least one of the Word versions installed check that before you show the wizard form, see the InitializeSetup event function below
...
[Run]
; the Check parameter here use only when you remove the InitializeSetup event
; function (see below), otherwise it's useless because the setup won't even start
; when there's not at least Word 2007 or 2010 version installed
Filename: "{code:GetLatestWordAppPath}"; Flags: postinstall; Description: "Open Word after Finish"; StatusMsg: "Opening Word ..."; Check: IsWordInstalled('12.0') or IsWordInstalled('14.0');
[Code]
// better for maintenance is to use constants
const
PathDefaultUserTemplates = '{userappdata}\Microsoft\Templates';
RegKeyOfficeTemplatesPath = 'SOFTWARE\Microsoft\Office\%s\Common\General';
RegKeyWordInstallRootPath = 'SOFTWARE\Microsoft\Office\%s\Word\InstallRoot';
// helper function for checking if the Word version specified by the input parameter
// is installed or not; inspired by https://stackoverflow.com/a/3267832/960757
function IsWordInstalled(const Version: string): Boolean;
begin
Result := RegValueExists(HKEY_LOCAL_MACHINE,
Format(RegKeyWordInstallRootPath, [Version]), 'Path');
end;
// this is used for the application path for the check box from the [Run] section
// you shouldn't rely on that everyone installs Office to Program Files directory
function GetLatestWordAppPath(S: string): string;
begin
Result := '';
if not RegQueryStringValue(HKEY_LOCAL_MACHINE,
Format(RegKeyWordInstallRootPath, ['14.0']), 'Path', Result)
then
RegQueryStringValue(HKEY_LOCAL_MACHINE,
Format(RegKeyWordInstallRootPath, ['12.0']), 'Path', Result);
end;
// helper function for getting the Office user templates path (if available)
function GetUserTemplatesPath(const Version: string; var Path: string): Boolean;
begin
Result := RegQueryStringValue(HKEY_CURRENT_USER,
Format(RegKeyOfficeTemplatesPath, [Version]), 'UserTemplates', Path);
end;
// function returning target installation directory
function GetInstallDirectory(S: string): string;
var
InstallDirectory: string;
begin
// initialize the Result first
Result := '';
// check if Word 2010 application is installed and if so, then...
if IsWordInstalled('14.0') then
begin
// try to get the Office 2010 user templates location from registry, if succeed then...
if GetUserTemplatesPath('14.0', InstallDirectory) then
begin
Result := InstallDirectory;
MsgBox('Office 2010 user templates directory was found in registry!' + #10#13 + #10#13 +
Result, mbInformation, MB_OK);
end
else
// the Office 2010 user templates location was not found in registry, so...
begin
// you're running your setup with PrivilegesRequired=none, so you can safely use {userappdata}
// constant with no risk of getting different user path (forced administrator)
Result := ExpandConstant(PathDefaultUserTemplates);
MsgBox('Office 2010 user templates directory was not found in registry!' + #10#13 + #10#13 +
Result, mbInformation, MB_OK);
end;
end
else
// Word 2010 was not found, check for Word 2007 and if it's found, then...
if IsWordInstalled('12.0') then
begin
// try to get the Office 2007 user templates location from registry, if succeed then...
if GetUserTemplatesPath('12.0', InstallDirectory) then
begin
Result := InstallDirectory;
MsgBox('Office 2007 user templates directory was found in registry!' + #10#13 + #10#13 +
Result, mbInformation, MB_OK);
end
else
// the Office 2007 user templates location was not found in registry, so...
begin
// you're running your setup with PrivilegesRequired=none, so you can safely use {userappdata}
// constant with no risk of getting different user path (forced administrator)
Result := ExpandConstant(PathDefaultUserTemplates);
MsgBox('Office 2007 user templates directory was not found in registry!' + #10#13 + #10#13 +
Result, mbInformation, MB_OK);
end;
end;
end;
// this event function is for the first check before the wizard form is shown if you
// return False to this event function, the setup will exit, what is here conditioned
// by presence of Word 2007 or Word 2010
function InitializeSetup: Boolean;
begin
Result := IsWordInstalled('12.0') or IsWordInstalled('14.0');
if not Result then
MsgBox('You don''t have installed neither Word 2007 nor Word 2010.' + #10#13 +
'Setup will now exit.', mbCriticalError, MB_OK);
end;
How to get path to the MS Office executables ?
How to manage templates in 2007 Office programs and in 2010 Office programs

Related

creating sql function implemented in java via flyway

I'm trying to create a function like:
--#SET TERMINATOR #
CREATE OR REPLACE FUNCTION NYA.QUINT_2_UINT( I VARCHAR(11))
RETURNS INTEGER
RETURNS NULL ON NULL INPUT
FENCED THREADSAFE
DETERMINISTIC
NO SQL
LANGUAGE JAVA
PARAMETER STYLE JAVA
EXTERNAL NAME
'StringUtil:se.uhr.nya.commons.db.procedures.Proquint!quint2uint'
NO EXTERNAL ACTION
#
from flyway. The jar file is already installed on the server, and I can create the function without problems via clp:
~]$ db2 -v -td# -f aa.sql
CREATE OR REPLACE FUNCTION NYA.QUINT_2_UINT( I VARCHAR(11))
RETURNS INTEGER
RETURNS NULL ON NULL INPUT
FENCED THREADSAFE
DETERMINISTIC
NO SQL
LANGUAGE JAVA
PARAMETER STYLE JAVA
EXTERNAL NAME
'StringUtil:se.uhr.nya.commons.db.procedures.Proquint!quint2uint'
NO EXTERNAL ACTION
DB20000I The SQL command completed successfully.
I also tried creating via dbeaver from my local machine without a problem:
Updated Rows 0
Query CREATE OR REPLACE FUNCTION NYA.QUINT_2_UINT( I VARCHAR(11))
RETURNS INTEGER
RETURNS NULL ON NULL INPUT
FENCED THREADSAFE
DETERMINISTIC
NO SQL
LANGUAGE JAVA
PARAMETER STYLE JAVA
EXTERNAL NAME
'StringUtil:se.uhr.nya.commons.db.procedures.Proquint!quint2uint'
NO EXTERNAL ACTION
Finish time Tue Jan 11 22:14:46 CET 2022
But if I try to run the same file from flyway:
]$ flyway -schemas=NYA_FLYWAY -table="FLYWAY_SCHEMA_HISTORY" -driver=${driver} -url=jdbc:db2://${host}:${port}/${db} -user=${username} -password=${passwd} -jarDirs=${jarDirs} -locations="filesystem:${upgradedir}" migrate
A new version of Flyway is available
Upgrade to Flyway 8.4.1: https://rd.gt/2X0gakb
Flyway Community Edition 8.0.5 by Redgate
Database: jdbc:db2://130.239.91.21:50000/NYA (DB2/LINUXX8664 11.5)
Successfully validated 36 migrations (execution time 00:00.027s)
Current version of schema "NYA_FLYWAY": 22.223.100.3
Migrating schema "NYA_FLYWAY" to version "22.223.100.4"
ERROR: Migration of schema "NYA_FLYWAY" to version "22.223.100.4" failed! Changes successfully rolled back.
ERROR: Migration V22.223.100.4__.sql failed
------------------------------------
SQL State : 46008
Error Code : -20204
Message : The user defined function or procedure "NYA.QUINT_2_UINT" was unable to map to a single Java method.. SQLCODE=-20204, SQLSTATE=46008, DRIVER=4.29.24
Location : /home/lejo0004/Project/db-legacy/nya/src/main/resources/db/migration/V22.223.100/V22.223.100.4__.sql (/home/lejo0004/Project/db-legacy/nya/src/main/resources/db/migration/V22.223.100/V22.223.100.4__.sql)
Line : 3
Statement : CREATE OR REPLACE FUNCTION NYA.QUINT_2_UINT( I VARCHAR(11))
RETURNS INTEGER
RETURNS NULL ON NULL INPUT
FENCED THREADSAFE
DETERMINISTIC
NO SQL
LANGUAGE JAVA
PARAMETER STYLE JAVA
EXTERNAL NAME
'StringUtil:se.uhr.nya.commons.db.procedures.Proquint!quint2uint'
NO EXTERNAL ACTION
Caused by: Migration V22.223.100.4__.sql failed
------------------------------------
SQL State : 46008
Error Code : -20204
Message : The user defined function or procedure "NYA.QUINT_2_UINT" was unable to map to a single Java method.. SQLCODE=-20204, SQLSTATE=46008, DRIVER=4.29.24
Location : /home/lejo0004/Project/db-legacy/nya/src/main/resources/db/migration/V22.223.100/V22.223.100.4__.sql (/home/lejo0004/Project/db-legacy/nya/src/main/resources/db/migration/V22.223.100/V22.223.100.4__.sql)
Line : 3
Statement : CREATE OR REPLACE FUNCTION NYA.QUINT_2_UINT( I VARCHAR(11))
RETURNS INTEGER
RETURNS NULL ON NULL INPUT
FENCED THREADSAFE
DETERMINISTIC
NO SQL
LANGUAGE JAVA
PARAMETER STYLE JAVA
EXTERNAL NAME
'StringUtil:se.uhr.nya.commons.db.procedures.Proquint!quint2uint'
NO EXTERNAL ACTION
Caused by: com.ibm.db2.jcc.am.SqlException: The user defined function or procedure "NYA.QUINT_2_UINT" was unable to map to a single Java method.. SQLCODE=-20204, SQLSTATE=46008, DRIVER=4.29.24
I'm using the same driver with dbeaver and flyway. I also tried running it from gradle with the same error. Any clue on what might cause this issue with flyway?
There are other functions in the jar as well and all that I have tried, share the same problem as described above
FWIW, the underlying function is A Proposal for Proquints: Identifiers that are Readable, Spellable, and Pronounceable
EDIT:
The jar file is normally installed in a similar fashion as Mark demonstrated in calling-sqlj-install-jar-from-jdbc using gradle.
But to eliminate any errors there, I manually installed the jarfile on the server as:
[db2inst1#nya-01 ~]$ db2 "call sqlj.install_jar('file:///opt/nya/users/db2inst1/STRINGUTIL.jar', 'STRINGUTIL')"
SQL20201N The install, replace or remove of "DB2INST1.STRINGUTIL" failed as
the jar name is invalid. SQLSTATE=46002
[db2inst1#nya-01 ~]$ db2 "call sqlj.replace_jar('file:///opt/nya/users/db2inst1/STRINGUTIL.jar', 'STRINGUTIL')"
DB20000I The CALL command completed successfully.
[db2inst1#nya-01 ~]$ db2 "call sqlj.refresh_classes()"
DB20000I The CALL command completed successfully.
Now, on the server I can:
[db2inst1#nya-01 ~]$ db2 connect
Database Connection Information
Database server = DB2/LINUXX8664 11.5.6.0
SQL authorization ID = DB2INST1
Local database alias = EKLN_N11
[db2inst1#nya-01 ~]$ db2 -v -td# -f aa.sql
CREATE OR REPLACE FUNCTION NYA.QUINT_2_UINT( I VARCHAR(11))
RETURNS INTEGER
RETURNS NULL ON NULL INPUT
FENCED THREADSAFE
DETERMINISTIC
NO SQL
LANGUAGE JAVA
PARAMETER STYLE JAVA
EXTERNAL NAME
'StringUtil:se.uhr.nya.commons.db.procedures.Proquint!quint2uint'
NO EXTERNAL ACTION
DB20000I The SQL command completed successfully.
db2 "VALUES NYA.QUINT_2_UINT('aabbccddee')"
1
-----------
17
1 record(s) selected.
Using dbeaver from my workstation, against the same server/database, also works fine (so jdbc does not seem to be a problem per se):
CREATE OR REPLACE FUNCTION NYA.QUINT_2_UINT( I VARCHAR(11))
RETURNS INTEGER
RETURNS NULL ON NULL INPUT
FENCED THREADSAFE
DETERMINISTIC
NO SQL
LANGUAGE JAVA
PARAMETER STYLE JAVA
EXTERNAL NAME
'StringUtil:se.uhr.nya.commons.db.procedures.Proquint!quint2uint'
NO EXTERNAL ACTION
#
VALUES NYA.QUINT_2_UINT('ddeeaabbccd') #
1 |
-------+
1114113|
But if I try to run it from flyway on my workstation against the same server/database I get the error:
SQL State : 46008
Error Code : -20204
Message : The user defined function or procedure "NYA.QUINT_2_UINT" was unable to map to a single Java method.. SQLCODE=-20204, SQLSTATE=46008, DRIVER=4.29.24
The java code itself is not very exciting:
package se.uhr.nya.commons.db.procedures;
public class Proquint {
...
static int quint2uint(String quint) {
long res = 0;
for (char c : quint.toCharArray()) {
int index = indexOf(uint2consonant, c);
if (index != -1) {
res <<= 4;
res += index;
} else {
index = indexOf(uint2vowel, c);
if (index != -1) {
res <<= 2;
res += index;
}
}
}
return (int) res;
}
I used the same user/passwd for all 3 cases (clp, dbeaver. flyway)
EDIT2:
I did a small test program:
import java.sql.*;
public class tst {
public static void main(String [] args) {
String urlPrefix = "jdbc:db2:";
String url;
String user;
String password;
String dummy;
Connection con;
Statement stmt;
ResultSet rs;
System. out.println ("**** Enter class tst" );
if (args.length !=3)
{
System. err.println ("Invalid value. First argument appended to " +
"jdbc:db2: must specify a valid URL." );
System. err.println ("Second argument must be a valid user ID." );
System. err.println ("Third argument must be the password for the user ID.");
System. exit(1);
}
url = urlPrefix + args[0];
user = args[1];
password = args[2];
try {
Class. forName("com.ibm.db2.jcc.DB2Driver");
System. out.println("**** Loaded the JDBC driver" );
con = DriverManager. getConnection(url, user, password);
con.setAutoCommit( false);
System. out.println("**** Created a JDBC connection to the data source");
stmt = con.createStatement();
System. out.println("**** Created JDBC Statement object" );
String s = "CREATE OR REPLACE FUNCTION NYA.QUINT_2_UINT( I VARCHAR(11)) ";
s += "RETURNS INTEGER ";
s += "RETURNS NULL ON NULL INPUT ";
s += "FENCED THREADSAFE ";
s += "DETERMINISTIC ";
s += "NO SQL LANGUAGE JAVA PARAMETER STYLE JAVA ";
s += "EXTERNAL NAME 'StringUtil:se.uhr.nya.commons.db.procedures.Proquint!quint2uint' ";
s += "NO EXTERNAL ACTION";
//stmt.executeUpdate(s);
stmt.execute(s);
System. out.println("**** Created function" );
s = "values NYA.QUINT_2_UINT('aabbccddeeg')";
rs = stmt.executeQuery(s);
while (rs.next()) {
dummy = rs.getString(1);
System. out.println("number = " + dummy);
}
System. out.println("**** Fetched all rows from JDBC ResultSet" );
rs.close();
System. out.println("**** Closed JDBC ResultSet" );
// Close the Statement
stmt.close();
System. out.println("**** Closed JDBC Statement" );
// Connection must be on a unit-of-work boundary to allow close
con.commit();
System. out.println ( "**** Transaction committed" );
con.close();
System. out.println("**** Disconnected from data source" );
System. out.println("**** JDBC Exit from class tst - no errors" );
}
catch(ClassNotFoundException e) {
System. err.println("Could not load JDBC driver" );
System. out.println("Exception: " + e);
e.printStackTrace();
}
catch(SQLException ex) {
System. err.println("SQLException information" );
while(ex!=null ) {
System. err.println ("Error msg: " + ex.getMessage());
System. err.println ("SQLSTATE: " + ex.getSQLState());
System. err.println ("Error code: " + ex.getErrorCode());
ex.printStackTrace();
ex = ex.getNextException(); // For drivers that support chained exceptions
}
}
}
}
and compared the jdbc trace for that with a jdbc trace for flyway. The flyway trace looks like:
[jcc][Time:2022-01-13-12:22:49.714][Thread:main][Statement#275fe372]setEscapeProcessing (false) called
[jcc][Thread:main][SystemMonitor:start]
[jcc][Time:2022-01-13-12:22:49.714][Thread:main][Statement#275fe372]execute (CREATE OR REPLACE FUNCTION NYA.QUINT_2_UINT( I VARCHAR(11)) RETURNS INTEGER RETURNS NULL ON NULL INPUT FENCED THREADSAFE DETERMINISTIC NO SQL LANGUAGE JAVA PARAMETER STYLE JAVA EXTERNAL NAME 'STRINGUTIL:se.uhr.nya.commons.db.procedures.Proquint!quint2uint' NO EXTERNAL ACTION) called
[jcc][Time:2022-01-13-12:22:49.714][Thread:main][Statement#275fe372]stmt_bidiTransform (CREATE OR REPLACE FUNCTION NYA.QUINT_2_UINT( I VARCHAR(11)) RETURNS INTEGER RETURNS NULL ON NULL INPUT FENCED THREADSAFE DETERMINISTIC NO SQL LANGUAGE JAVA PARAMETER STYLE JAVA EXTERNAL NAME 'STRINGUTIL:se.uhr.nya.commons.db.procedures.Proquint!quint2uint' NO EXTERNAL ACTION) called
[jcc][Time:2022-01-13-12:22:49.714][Thread:main][Statement#275fe372]stmt_bidiTransform not enabled (CREATE OR REPLACE FUNCTION NYA.QUINT_2_UINT( I VARCHAR(11)) RETURNS INTEGER RETURNS NULL ON NULL INPUT FENCED THREADSAFE DETERMINISTIC NO SQL LANGUAGE JAVA PARAMETER STYLE JAVA EXTERNAL NAME 'STRINGUTIL:se.uhr.nya.commons.db.procedures.Proquint!quint2uint' NO EXTERNAL ACTION) called
[jcc][t4][time:2022-01-13-12:22:49.714][Thread:main][tracepoint:1][Request.flush]
whereas the test program:
[jcc][Thread:main][SystemMonitor:start]
[jcc][Time:2022-01-13-12:22:28.089][Thread:main][Statement#1372ed45]execute (CREATE OR REPLACE FUNCTION NYA.QUINT_2_UINT( I VARCHAR(11)) RETURNS INTEGER RETURNS NULL ON NULL INPUT FENCED THREADSAFE DETERMINISTIC NO SQL LANGUAGE JAVA PARAMETER STYLE JAVA EXTERNAL NAME 'StringUtil:se.uhr.nya.commons.db.procedures.Proquint!quint2uint' NO EXTERNAL ACTION) called
[jcc][Time:2022-01-13-12:22:28.089][Thread:main][Statement#1372ed45]stmt_bidiTransform (CREATE OR REPLACE FUNCTION NYA.QUINT_2_UINT( I VARCHAR(11)) RETURNS INTEGER RETURNS NULL ON NULL INPUT FENCED THREADSAFE DETERMINISTIC NO SQL LANGUAGE JAVA PARAMETER STYLE JAVA EXTERNAL NAME 'StringUtil:se.uhr.nya.commons.db.procedures.Proquint!quint2uint' NO EXTERNAL ACTION) called
[jcc][Time:2022-01-13-12:22:28.089][Thread:main][Statement#1372ed45]stmt_bidiTransform not enabled (CREATE OR REPLACE FUNCTION NYA.QUINT_2_UINT( I VARCHAR(11)) RETURNS INTEGER RETURNS NULL ON NULL INPUT FENCED THREADSAFE DETERMINISTIC NO SQL LANGUAGE JAVA PARAMETER STYLE JAVA EXTERNAL NAME 'StringUtil:se.uhr.nya.commons.db.procedures.Proquint!quint2uint' NO EXTERNAL ACTION) called
>>> [jcc][t4] [time:2022-01-13-12:22:28.089][Thread:main]
>>> [tracepoint:10]SetClientPiggybackCommand: flowToServerNeeded() = true
>>> [jcc][t4] [time:2022-01-13-12:22:28.089][Thread:main]
>>> [tracepoint:10]SetClientPiggybackCommand: flowToServerNeeded() = true
[jcc][t4][time:2022-01-13-12:22:28.089][Thread:main][tracepoint:1][Request.flush]
I.e. the test program does:
SetClientPiggybackCommand: flowToServerNeeded() = true
before Request.flush
but whether that is relevant or not I don't know yet
EDIT3:
[db2inst1#nya-03 ~]$ ls -lR sqllib/function/
sqllib/function/:
totalt 0
lrwxrwxrwx. 1 root db2iadm1 35 14 sep 14.15 db2json -> /opt/ibm/db2/V11.5/function/db2json*
lrwxrwxrwx. 1 root db2iadm1 36 14 sep 14.15 db2psmds -> /opt/ibm/db2/V11.5/function/db2psmds*
lrwxrwxrwx. 1 root db2iadm1 35 14 sep 14.15 db2rtsc -> /opt/ibm/db2/V11.5/function/db2rtsc*
lrwxrwxrwx. 1 root db2iadm1 34 14 sep 14.15 fpeevm -> /opt/ibm/db2/V11.5/function/fpeevm*
drwxrwxr-x. 3 db2inst1 db2iadm1 22 1 mar 2021 jar/
lrwxrwxrwx. 1 root db2iadm1 37 14 sep 14.15 libdb2u.a -> /opt/ibm/db2/V11.5/function/libdb2u.a*
drwxrwsr-t. 2 db2inst1 db2iadm1 6 23 feb 2021 routine/
lrwxrwxrwx. 1 root db2iadm1 33 14 sep 14.15 tblpd -> /opt/ibm/db2/V11.5/function/tblpd*
drwxrwsr-t. 2 db2inst1 db2iadm1 37 14 sep 14.15 unfenced/
sqllib/function/jar:
totalt 0
drwxrwxr-x. 2 db2inst1 db2iadm1 28 13 jan 09.48 DB2INST1/
sqllib/function/jar/DB2INST1:
totalt 12
-rw-rw-r--. 1 db2inst1 db2iadm1 8934 13 jan 09.48 STRINGUTIL.jar
sqllib/function/routine:
totalt 0
sqllib/function/unfenced:
totalt 0
lrwxrwxrwx. 1 root db2iadm1 44 14 sep 14.15 asnqmon -> /opt/ibm/db2/V11.5/function/unfenced/asnqmon*
lrwxrwxrwx. 1 root db2iadm1 45 14 sep 14.15 db2gsead -> /opt/ibm/db2/V11.5/function/unfenced/db2gsead*
I think I have found what caused the problem, atleast I can now create the function without a problem. The root cause is that Flyway appears to:
SET CURRENT_SCHEMA = <variable assigned via property -schemas>
When this is changed from the instance owner, db2 is unable to find the jar file. If I change the migration to:
set CURRENT SCHEMA = 'DB2INST1' #
CREATE OR REPLACE FUNCTION NYA.QUINT_2_UINT( I VARCHAR(11))
RETURNS INTEGER
RETURNS NULL ON NULL INPUT
FENCED THREADSAFE DETERMINISTIC
NO SQL
LANGUAGE JAVA
PARAMETER STYLE JAVA
EXTERNAL NAME 'STRINGUTIL:se.uhr.nya.commons.db.procedures.Proquint!quint2uint'
NO EXTERNAL ACTION #
Everything works as expected. I'm not to excited about having to set current_path in the migration (and I'm not sure what effect it will have on Flyway), so I'll have a look if there is another property that can be used to assign a schema for FLYWAY_SCHEMA_HISTORY. But this works for now.
EDIT:
The following migration works if I remove -schemas as an argument to Flyway:
--#SET TERMINATOR #
values ('CURRENT SCHEMA', cast(CURRENT SCHEMA as varchar(100))) #
CREATE OR REPLACE FUNCTION NYA.QUINT_2_UINT( I VARCHAR(11)) RETURNS INTEGER RETURNS NULL ON NULL INPUT FENCED THREADSAFE DETERMINISTIC NO SQL LANGUAGE JAVA PARAMETER STYLE JAVA EXTERNAL NAME 'STRINGUTIL:se.uhr.nya.commons.db.procedures.Proquint!quint2uint' NO EXTERNAL ACTION #
values NYA.QUINT_2_UINT('aabbccddeef') #
flyway -table="FLYWAY_SCHEMA_HISTORY" -driver=${driver} -url=jdbc:db2://${host}:${port}/${db} -user=${username} -password=${passwd} -jarDirs=${jarDirs} -locations="filesystem:${upgradedir}" migrate
Flyway is up to date
Flyway Community Edition 8.4.1 by Redgate
Database: jdbc:db2://130.239.91.235:50000/nyax (DB2/LINUXX8664 11.5)
----------------------------------------
Flyway Teams features are enabled by default for the next 27 days. Learn more at https://rd.gt/3A4IWym
----------------------------------------
Successfully validated 1 migration (execution time 00:00.017s)
Creating Schema History table "DB2INST1"."FLYWAY_SCHEMA_HISTORY" ...
Current version of schema "DB2INST1": << Empty Schema >>
Migrating schema "DB2INST1" to version "22.223.100.4"
+----------------+----------+
| 1 | 2 |
+----------------+----------+
| CURRENT SCHEMA | DB2INST1 |
+----------------+----------+
+-----+
| 1 |
+-----+
| 274 |
+-----+
If I add a specific schema as in:
flyway -schemas=NYA_FLYWAY -table="FLYWAY_SCHEMA_HISTORY" -driver=${driver} -url=jdbc:db2://${host}:${port}/${db} -user=${username} -password=${passwd} -jarDirs=${jarDirs} -locations="filesystem:${upgradedir}" migrate
Flyway is up to date
Flyway Community Edition 8.4.1 by Redgate
Database: jdbc:db2://130.239.91.235:50000/nyax (DB2/LINUXX8664 11.5)
----------------------------------------
Flyway Teams features are enabled by default for the next 27 days. Learn more at https://rd.gt/3A4IWym
----------------------------------------
Creating schema "NYA_FLYWAY" ...
Creating Schema History table "NYA_FLYWAY"."FLYWAY_SCHEMA_HISTORY" ...
Current version of schema "NYA_FLYWAY": null
Migrating schema "NYA_FLYWAY" to version "22.223.100.4"
+----------------+------------+
| 1 | 2 |
+----------------+------------+
| CURRENT SCHEMA | NYA_FLYWAY |
+----------------+------------+
ERROR: Migration of schema "NYA_FLYWAY" to version "22.223.100.4" failed! Changes successfully rolled back.
CURRENT_SCHEMA is changed to NYA_FLYWAY and I assume this is why Db2 can not locate the jar file. If I explicitly set CURRENT_SCHEMA in the migration:
--#SET TERMINATOR #
set CURRENT_SCHEMA = 'DB2INST1' #
values ('CURRENT SCHEMA', cast(CURRENT SCHEMA as varchar(100))) #
...
It once again works as expected:
flyway -schemas=NYA_FLYWAY -table="FLYWAY_SCHEMA_HISTORY" -driver=${driver} -url=jdbc:db2://${host}:${port}/${db} -user=${username} -password=${passwd} -jarDirs=${jarDirs} -locations="filesystem:${upgradedir}" migrate
Flyway is up to date
Flyway Community Edition 8.4.1 by Redgate
Database: jdbc:db2://130.239.91.235:50000/nyax (DB2/LINUXX8664 11.5)
----------------------------------------
Flyway Teams features are enabled by default for the next 27 days. Learn more at https://rd.gt/3A4IWym
----------------------------------------
Successfully validated 2 migrations (execution time 00:00.018s)
Current version of schema "NYA_FLYWAY": null
Migrating schema "NYA_FLYWAY" to version "22.223.100.4"
+----------------+----------+
| 1 | 2 |
+----------------+----------+
| CURRENT SCHEMA | DB2INST1 |
+----------------+----------+
+-----+
| 1 |
+-----+
| 274 |
+-----+
Also, if I change my little test prog so that it sets current_schema to 'NYA_FLYWAY' before creating the function, I get the same error:
java -cp .:/home/lejo0004/db2jcc4.jar tst //nya-03.its.umu.se:50000/nyax db2inst1 $passwd
**** Enter class tst
**** Loaded the JDBC driver
**** Created a JDBC connection to the data source
**** Created JDBC Statement object
CURRENT SCHEMA = NYA_FLYWAY
SQLException information
Error msg: DB2 SQL Error: SQLCODE=-20204, SQLSTATE=46008, SQLERRMC=NYA.QUINT_2_UINT, DRIVER=4.29.24
SQLSTATE: 46008
Error code: -20204
com.ibm.db2.jcc.am.SqlException: DB2 SQL Error: SQLCODE=-20204, SQLSTATE=46008, SQLERRMC=NYA.QUINT_2_UINT, DRIVER=4.29.24
at com.ibm.db2.jcc.am.b7.a(b7.java:815)
at com.ibm.db2.jcc.am.b7.a(b7.java:66)
at com.ibm.db2.jcc.am.b7.a(b7.java:140)
at com.ibm.db2.jcc.am.k9.c(k9.java:2844)
at com.ibm.db2.jcc.am.k9.d(k9.java:2828)
at com.ibm.db2.jcc.am.k9.b(k9.java:2188)
at com.ibm.db2.jcc.t4.ab.k(ab.java:444)
at com.ibm.db2.jcc.t4.ab.c(ab.java:102)
at com.ibm.db2.jcc.t4.p.b(p.java:38)
at com.ibm.db2.jcc.t4.av.h(av.java:124)
at com.ibm.db2.jcc.am.k9.ak(k9.java:2183)
at com.ibm.db2.jcc.am.k9.a(k9.java:3387)
at com.ibm.db2.jcc.am.k9.e(k9.java:1135)
at com.ibm.db2.jcc.am.k9.execute(k9.java:1114)
at tst.main(tst.java:61)
FWIW, this can also be reproduced via clp:
[db2inst1#nya-03 ~]$ db2 "set current_schema = 'DB2INST1'"
DB20000I The SQL command completed successfully.
[db2inst1#nya-03 ~]$ db2 "CREATE OR REPLACE FUNCTION NYA.QUINT_2_UINT( I VARCHAR(11))
RETURNS INTEGER
RETURNS NULL ON NULL INPUT
FENCED THREADSAFE
DETERMINISTIC
NO SQL
LANGUAGE JAVA
PARAMETER STYLE JAVA
EXTERNAL NAME
'StringUtil:se.uhr.nya.commons.db.procedures.Proquint!quint2uint'
NO EXTERNAL ACTION"
DB20000I The SQL command completed successfully.
[db2inst1#nya-03 ~]$ db2 "set current_schema = 'NYA_FLYWAY'"
DB20000I The SQL command completed successfully.
[db2inst1#nya-03 ~]$ db2 "CREATE OR REPLACE FUNCTION NYA.QUINT_2_UINT( I VARCHAR(11))
RETURNS INTEGER
RETURNS NULL ON NULL INPUT
FENCED THREADSAFE
DETERMINISTIC
NO SQL
LANGUAGE JAVA
PARAMETER STYLE JAVA
EXTERNAL NAME
'StringUtil:se.uhr.nya.commons.db.procedures.Proquint!quint2uint'
NO EXTERNAL ACTION"
DB21034E The command was processed as an SQL statement because it was not a
valid Command Line Processor command. During SQL processing it returned:
SQL20204N The user defined function or procedure "NYA.QUINT_2_UINT" was
unable to map to a single Java method. LINE NUMBER=10. SQLSTATE=46008
One might ask why I insist on setting schema- and table- for flyway. As for the table name, the reason is that Flyway otherwise creates them with as quoted identifiers in lowercase. It is a painful experience having to quote these identifiers every time you need to query the table (especially when querying the table from sh). Unfortunately I have not found a way to influence Flyway to behave when it comes to the name for columns, so you will still have to quote them as in:
db2 "select \"version\" from ..."
I tried manually creating the table, but the flyway fails to handle it. I have not tried it yet, but I may add generated columns or a view for the purpose.
As for the schema name, I've got several products that run their own instance of Flyway. It feels much easier to use the schema + "_FLYWAY" for their instance. I don't want to put the flyway table in the product schema since I run validation scripts on the schema before each committed version.
I also would like to add that I only tried this with flyway community 8.4.1, there may be some other error with flyway community 8.0.5 which I started with
Many thanks to mao and Mark for their input (this will probably be removed but now I have added it:-)

Getting an error of 'Token Unknown' while Execute Stored Procedures

I'm new in learning stored procedures in SQL.
I want to create a stored procedure for inserting values from automatic data by calculation.
Table Attendance:
EMPL_KODE |EMPL_NAME |DATE_IN |TIME_IN |TIME_OUT|TIME_IN |TIME_OUT
001 | Michel |25.04.2016 |06:50 |15:40 | |
002 | Clara |25.04.2016 |06:15 |15:43 | |
003 | Rafael |25.04.2016 |06:25 |15:45 | |
001 | Michel |26.04.2016 |06:23 |15:42 | |
002 | Clara |26.04.2016 |06:10 |15:41 | |
003 | Rafael |26.04.2016 |06:30 |15:42 | |
001 | Michel |27.04.2016 |06:33 |15:42 | |
002 | Clara |27.04.2016 |06:54 |15:44 | |
003 | Rafael |27.04.2016 |07:00 |15:45 | |
I want to fill TIME_IN and TIME_OUT values automatically by creating a stored procedure. Here is the code :
CREATE PROCEDURE InsertTotalEmployee
#TOTAL_MINUTES int,
#TOTAL_HOURS float
AS
BEGIN
INSERT INTO ATTENDANCE (TOTAL_MINUTES, TOTAL_HOURS)
VALUES (
SELECT
DATEDIFF(MINUTE, ATTENDANCE.TIME_IN, ATTENDANCE.TIME_OUT),
DATEDIFF(MINUTE, ATTENDANCE.TIME_IN, ATTENDANCE.TIME_OUT) / 60.0
)
END
After I write and execute my statement, a message error occurs:
Token unknown - line 2, column 5 #
I run the code using Flamerobin.
It looks like you are trying to use Microsoft SQL Server syntax in Firebird, that is not going to work.
For one, the # is not allowed like that in identifiers (unless you use double quotes around them), and the list of parameters must be enclosed in parentheses.
See the syntax of CREATE PROCEDURE. You need to change it to:
CREATE PROCEDURE InsertTotalEmployee(TOTAL_MINUTES int, TOTAL_HOURS float)
You also might want to change the datatype float to double precision, and the body of your stored procedure seems to be incomplete because you are selecting from nothing (a select requires a table to select from), and are missing a semicolon at the end of the statement.
All in all I suggest you study the Firebird language reference, then try to create a functioning insert and only then create a stored procedure around it.
Also note that when creating a stored procedure in Flamerobin, that you must switch statement terminators using set term otherwise Flamerobin can't send the stored procedure correctly, see also the first section in Procedural SQL (PSQL) Statements.

postgres search in column with escapes

Is there a way to do something like a grep for "site" from the following select (so that only "site=*" is returned from thedata)?
rr=# select thename,encode(thedata, 'escape') from
management_data.datas limit 2;
thename | thedata
-----------------------------------------------------------------------------------
Alexander | #
+
| #Fri Mar 15 14:58:18 PDT 2014
+
| BUs=ALL
+
| site=33$36$354$380$357$360$36$353$36$38$39$34$31$355
+
Anthony | #
+
| #Mon Jan 05 13:33:00 PST 2015
+
| mem=12000
+
| site=50$5$1$50
+
|
Given test data round-tripped successfully:
WITH somerow(name, blobofgarble) AS (
SELECT
TEXT 'Alexander',
BYTEA E'#\n#Fri Mar 15 14:58:18 PDT 2014\nBUs=ALL\nsite=33$36$354$380$357$360$36$353$36$38$39$34$31$355\n'
)
SELECT name, encode(blobofgarble, 'escape') FROM somerow;
Now, I can't possibly imagine why you'd store this information as bytea not a text field, but ... well, I guess there must be a reason. I'm going to rely on the simplifying assumption that the data, when escaped, can be treated as fairly sanely formed text, since otherwise the whole concept of "lines" is garbage and your question makes no sense.
With that assumption it's possible to use regexp_split_to_table to split on newlines, getting somewhat more sanely formed data:
WITH (...)
SELECT name, garblepart FROM somerow, regexp_split_to_table(encode(blobofgarble, 'escape'), E'\n') AS garblepart;
name | garblepart
-----------+------------------------------------------------------
Alexander | #
Alexander | #Fri Mar 15 14:58:18 PDT 2014
Alexander | BUs=ALL
Alexander | site=33$36$354$380$357$360$36$353$36$38$39$34$31$355
Alexander |
(5 rows)
(this is an implicit LATERAL query, so it'll only work in PostgreSQL 9.3 and above).
Now you can use pretty ordinary operations to find the row of interest and extract the desired part, in this case with some more pattern matching:
WITH (...)
SELECT
name, substring(garblepart from '=(.*$)')
FROM somerow,
regexp_split_to_table(encode(blobofgarble, 'escape'), E'\n') AS garblepart
WHERE garblepart LIKE 'site=%';
name | substring
-----------+-------------------------------------------------
Alexander | 33$36$354$380$357$360$36$353$36$38$39$34$31$355
(1 row)
Now go fix your schema so that you store your data sanely and don't have to do this.

EXISTS(select 1 from t1) vs EXISTS(select * from t1) [duplicate]

I used to write my EXISTS checks like this:
IF EXISTS (SELECT * FROM TABLE WHERE Columns=#Filters)
BEGIN
UPDATE TABLE SET ColumnsX=ValuesX WHERE Where Columns=#Filters
END
One of the DBA's in a previous life told me that when I do an EXISTS clause, use SELECT 1 instead of SELECT *
IF EXISTS (SELECT 1 FROM TABLE WHERE Columns=#Filters)
BEGIN
UPDATE TABLE SET ColumnsX=ValuesX WHERE Columns=#Filters
END
Does this really make a difference?
No, SQL Server is smart and knows it is being used for an EXISTS, and returns NO DATA to the system.
Quoth Microsoft:
http://technet.microsoft.com/en-us/library/ms189259.aspx?ppud=4
The select list of a subquery
introduced by EXISTS almost always
consists of an asterisk (*). There is
no reason to list column names because
you are just testing whether rows that
meet the conditions specified in the
subquery exist.
To check yourself, try running the following:
SELECT whatever
FROM yourtable
WHERE EXISTS( SELECT 1/0
FROM someothertable
WHERE a_valid_clause )
If it was actually doing something with the SELECT list, it would throw a div by zero error. It doesn't.
EDIT: Note, the SQL Standard actually talks about this.
ANSI SQL 1992 Standard, pg 191 http://www.contrib.andrew.cmu.edu/~shadow/sql/sql1992.txt
3) Case:
a) If the <select list> "*" is simply contained in a <subquery> that
is immediately contained in an <exists predicate>, then the <select list> is
equivalent to a <value expression>
that is an arbitrary <literal>.
The reason for this misconception is presumably because of the belief that it will end up reading all columns. It is easy to see that this is not the case.
CREATE TABLE T
(
X INT PRIMARY KEY,
Y INT,
Z CHAR(8000)
)
CREATE NONCLUSTERED INDEX NarrowIndex ON T(Y)
IF EXISTS (SELECT * FROM T)
PRINT 'Y'
Gives plan
This shows that SQL Server was able to use the narrowest index available to check the result despite the fact that the index does not include all columns. The index access is under a semi join operator which means that it can stop scanning as soon as the first row is returned.
So it is clear the above belief is wrong.
However Conor Cunningham from the Query Optimiser team explains here that he typically uses SELECT 1 in this case as it can make a minor performance difference in the compilation of the query.
The QP will take and expand all *'s
early in the pipeline and bind them to
objects (in this case, the list of
columns). It will then remove
unneeded columns due to the nature of
the query.
So for a simple EXISTS subquery like
this:
SELECT col1 FROM MyTable WHERE EXISTS (SELECT * FROM Table2 WHERE MyTable.col1=Table2.col2) The * will be
expanded to some potentially big
column list and then it will be
determined that the semantics of the
EXISTS does not require any of those
columns, so basically all of them can
be removed.
"SELECT 1" will avoid having to
examine any unneeded metadata for that
table during query compilation.
However, at runtime the two forms of
the query will be identical and will
have identical runtimes.
I tested four possible ways of expressing this query on an empty table with various numbers of columns. SELECT 1 vs SELECT * vs SELECT Primary_Key vs SELECT Other_Not_Null_Column.
I ran the queries in a loop using OPTION (RECOMPILE) and measured the average number of executions per second. Results below
+-------------+----------+---------+---------+--------------+
| Num of Cols | * | 1 | PK | Not Null col |
+-------------+----------+---------+---------+--------------+
| 2 | 2043.5 | 2043.25 | 2073.5 | 2067.5 |
| 4 | 2038.75 | 2041.25 | 2067.5 | 2067.5 |
| 8 | 2015.75 | 2017 | 2059.75 | 2059 |
| 16 | 2005.75 | 2005.25 | 2025.25 | 2035.75 |
| 32 | 1963.25 | 1967.25 | 2001.25 | 1992.75 |
| 64 | 1903 | 1904 | 1936.25 | 1939.75 |
| 128 | 1778.75 | 1779.75 | 1799 | 1806.75 |
| 256 | 1530.75 | 1526.5 | 1542.75 | 1541.25 |
| 512 | 1195 | 1189.75 | 1203.75 | 1198.5 |
| 1024 | 694.75 | 697 | 699 | 699.25 |
+-------------+----------+---------+---------+--------------+
| Total | 17169.25 | 17171 | 17408 | 17408 |
+-------------+----------+---------+---------+--------------+
As can be seen there is no consistent winner between SELECT 1 and SELECT * and the difference between the two approaches is negligible. The SELECT Not Null col and SELECT PK do appear slightly faster though.
All four of the queries degrade in performance as the number of columns in the table increases.
As the table is empty this relationship does seem only explicable by the amount of column metadata. For COUNT(1) it is easy to see that this gets rewritten to COUNT(*) at some point in the process from the below.
SET SHOWPLAN_TEXT ON;
GO
SELECT COUNT(1)
FROM master..spt_values
Which gives the following plan
|--Compute Scalar(DEFINE:([Expr1003]=CONVERT_IMPLICIT(int,[Expr1004],0)))
|--Stream Aggregate(DEFINE:([Expr1004]=Count(*)))
|--Index Scan(OBJECT:([master].[dbo].[spt_values].[ix2_spt_values_nu_nc]))
Attaching a debugger to the SQL Server process and randomly breaking whilst executing the below
DECLARE #V int
WHILE (1=1)
SELECT #V=1 WHERE EXISTS (SELECT 1 FROM ##T) OPTION(RECOMPILE)
I found that in the cases where the table has 1,024 columns most of the time the call stack looks like something like the below indicating that it is indeed spending a large proportion of the time loading column metadata even when SELECT 1 is used (For the case where the table has 1 column randomly breaking didn't hit this bit of the call stack in 10 attempts)
sqlservr.exe!CMEDAccess::GetProxyBaseIntnl() - 0x1e2c79 bytes
sqlservr.exe!CMEDProxyRelation::GetColumn() + 0x57 bytes
sqlservr.exe!CAlgTableMetadata::LoadColumns() + 0x256 bytes
sqlservr.exe!CAlgTableMetadata::Bind() + 0x15c bytes
sqlservr.exe!CRelOp_Get::BindTree() + 0x98 bytes
sqlservr.exe!COptExpr::BindTree() + 0x58 bytes
sqlservr.exe!CRelOp_FromList::BindTree() + 0x5c bytes
sqlservr.exe!COptExpr::BindTree() + 0x58 bytes
sqlservr.exe!CRelOp_QuerySpec::BindTree() + 0xbe bytes
sqlservr.exe!COptExpr::BindTree() + 0x58 bytes
sqlservr.exe!CScaOp_Exists::BindScalarTree() + 0x72 bytes
... Lines omitted ...
msvcr80.dll!_threadstartex(void * ptd=0x0031d888) Line 326 + 0x5 bytes C
kernel32.dll!_BaseThreadStart#8() + 0x37 bytes
This manual profiling attempt is backed up by the VS 2012 code profiler which shows a very different selection of functions consuming the compilation time for the two cases (Top 15 Functions 1024 columns vs Top 15 Functions 1 column).
Both the SELECT 1 and SELECT * versions wind up checking column permissions and fail if the user is not granted access to all columns in the table.
An example I cribbed from a conversation on the heap
CREATE USER blat WITHOUT LOGIN;
GO
CREATE TABLE dbo.T
(
X INT PRIMARY KEY,
Y INT,
Z CHAR(8000)
)
GO
GRANT SELECT ON dbo.T TO blat;
DENY SELECT ON dbo.T(Z) TO blat;
GO
EXECUTE AS USER = 'blat';
GO
SELECT 1
WHERE EXISTS (SELECT 1
FROM T);
/* ↑↑↑↑
Fails unexpectedly with
The SELECT permission was denied on the column 'Z' of the
object 'T', database 'tempdb', schema 'dbo'.*/
GO
REVERT;
DROP USER blat
DROP TABLE T
So one might speculate that the minor apparent difference when using SELECT some_not_null_col is that it only winds up checking permissions on that specific column (though still loads the metadata for all). However this doesn't seem to fit with the facts as the percentage difference between the two approaches if anything gets smaller as the number of columns in the underlying table increases.
In any event I won't be rushing out and changing all my queries to this form as the difference is very minor and only apparent during query compilation. Removing the OPTION (RECOMPILE) so that subsequent executions can use a cached plan gave the following.
+-------------+-----------+------------+-----------+--------------+
| Num of Cols | * | 1 | PK | Not Null col |
+-------------+-----------+------------+-----------+--------------+
| 2 | 144933.25 | 145292 | 146029.25 | 143973.5 |
| 4 | 146084 | 146633.5 | 146018.75 | 146581.25 |
| 8 | 143145.25 | 144393.25 | 145723.5 | 144790.25 |
| 16 | 145191.75 | 145174 | 144755.5 | 146666.75 |
| 32 | 144624 | 145483.75 | 143531 | 145366.25 |
| 64 | 145459.25 | 146175.75 | 147174.25 | 146622.5 |
| 128 | 145625.75 | 143823.25 | 144132 | 144739.25 |
| 256 | 145380.75 | 147224 | 146203.25 | 147078.75 |
| 512 | 146045 | 145609.25 | 145149.25 | 144335.5 |
| 1024 | 148280 | 148076 | 145593.25 | 146534.75 |
+-------------+-----------+------------+-----------+--------------+
| Total | 1454769 | 1457884.75 | 1454310 | 1456688.75 |
+-------------+-----------+------------+-----------+--------------+
The test script I used can be found here
Best way to know is to performance test both versions and check out the execution plan for both versions. Pick a table with lots of columns.
There is no difference in SQL Server and it has never been a problem in SQL Server. The optimizer knows that they are the same. If you look at the execution plans, you will see that they are identical.
Personally I find it very, very hard to believe that they don't optimize to the same query plan. But the only way to know in your particular situation is to test it. If you do, please report back!
Not any real difference but there might be a very small performance hit. As a rule of thumb you should not ask for more data than you need.

Construction of a class with task property in Ada 2005

I have a class Test_Class in Ada 2005 which has a parent-linked task property called Primary, from type Primary_Task, defined as:
type Test_Class is tagged limited
record
Info : Integer;
Value : Float;
Primary : Primary_Task (Test_Class'Access);
end record;
I need build a one-step constructor for my class in the form
function Construct (T : access Test_Class) return Test_Class_Ptr is
begin
return new Test_Class'(Info => T.Info + 1,
Value => 0.0,
Primary => [WHAT I WANNA KNOW]);
end Construct;
Currently my code is:
-- test_pkg.ads
package Test_Pkg is
type Test_Class;
type Test_Class_Ptr is access all Test_Class;
task type Primary_Task (This_Test : access Test_Class) is
pragma Storage_Size (1000);
end Primary_Task;
type Test_Class is tagged limited
record
Info : Integer;
Value : Float;
Primary : Primary_Task (Test_Class'Access);
end record;
function Construct (T : access Test_Class) return Test_Class_Ptr;
end Test_Pkg;
-- test_pkg.adb
with Text_IO; use Text_IO;
package body Test_Pkg is
[...]
function Construct (T : access Test_Class) return Test_Class_Ptr is
T_Ptr : constant Test_Class_Ptr := new Test_Class;
begin
T_Ptr.Info := T.Info + 1;
T_Ptr.Value := 0.0;
return T_Ptr;
end Construct;
end Test_Pkg;
So, how can I code it? What should I put in Primary => [...] code? Should I change the definition of Primary : Primary_Task (Test_Class'Access); in Test_Class definition?
I got an answer from Randy Brukardt (thank you) on comp.lang.ada:
In Ada 2005 or later, use "<>" to default initialize a component in an
aggregate (which is the only thing you can do with a task).
(...)
function Construct (T : access Test_Class) return Test_Class_Ptr is
begin
return new Test_Class'(Info => T.Info + 1,
Value => 0.0,
Primary => <>);
end Construct;
However, I tried to compile it using GNAT GPL 2011 and got the GNATBUG below
c:\tst>gnatmake -gnat12 test_pkg.adb
gcc -c -gnat12 test_pkg.adb
+===========================GNAT BUG DETECTED==============================+
| GPL 2011 (20110428) (i686-pc-mingw32) GCC error: |
| in create_tmp_var, at gimplify.c:505 |
| Error detected around test_pkg.adb:20:29 |
| Please submit a bug report by email to report#adacore.com. |
| GAP members can alternatively use GNAT Tracker: |
| http://www.adacore.com/ section 'send a report'. |
| See gnatinfo.txt for full info on procedure for submitting bugs. |
| Use a subject line meaningful to you and us to track the bug. |
| Include the entire contents of this bug box in the report. |
| Include the exact gcc or gnatmake command that you entered. |
| Also include sources listed below in gnatchop format |
| (concatenated together with no headers between files). |
| Use plain ASCII or MIME attachment. |
+==========================================================================+
Please include these source files with error report
Note that list may not be accurate in some cases,
so please double check that the problem can still
be reproduced with the set of files listed.
Consider also -gnatd.n switch (see debug.adb).
test_pkg.adb
test_pkg.ads
raised TYPES.UNRECOVERABLE_ERROR : comperr.adb:423
gnatmake: "test_pkg.adb" compilation error
So GNAT GPL users may have to wait for the next release to use this solution.