what the $platform->exec does:
- it adds cmd in front of the command so all $cmd are executed by cmd.exe (don't ask me why)
- it adds double quotes around each arg
so let's first try to check if the double quotes are the problem, try:
list ($success, $outputArray) = $platform->exec(array(array($cmd . $fileName)));
insteaf of
list ($success, $outputArray) = $platform->exec(array(array($cmd, $fileName)));
if that doesn't work, try the normal exec prepended with cmd /c
Larry Menard
Joined: 2005-10-01
Posts: 757
Posted: Sun, 2005-11-27 15:02
> list ($success, $outputArray) = $platform->exec(array(array($cmd . $fileName)));
$cmd = "cmd.exe /c db2cmd /c /w /i db2 -vf C:\WINDOWS\TEMP\db22E7D.tmp".
array(21) { [0]=> string(38) " CONNECT TO gallery2 USER g2user USING" [1]=> string(0) "" [2]=> string(34) " Database Connection Information" [3]=> string(0) "" [4]=> string(38) " Database server = DB2/NT 8.2.2" [5]=> string(32) " SQL authorization ID = G2USER" [6]=> string(34) " Local database alias = GALLERY2" [7]=> string(0) "" [8]=> string(0) "" [9]=> string(34) "CALL SQLJ.REMOVE_JAR('g2_db2_jar')" [10]=> string(76) "SQL20201N The install, replace or remove of "G2USER .G2_DB2_JAR" failed as" [11]=> string(40) "the jar name is invalid. SQLSTATE=46002" [12]=> string(0) "" [13]=> string(0) "" [14]=> string(135) "CALL SQLJ.INSTALL_JAR('file:C:\My Server\gallery2\modules\core\classes\GalleryStorage\DatabaseStorage\g2_db2_bit_or.jar', 'g2_db2_jar')" [15]=> string(50) "DB20000I The CALL command completed successfully." [16]=> string(0) "" [17]=> string(0) "" [18]=> string(13) "CONNECT RESET" [19]=> string(49) "DB20000I The SQL command completed successfully." [20]=> string(0) "" }
I just noticed something. Look at the path in which the temp file(s) are being created... "C:\WINDOWS\TEMP". Isn't that supposed to be something under "C:\G2DATA"? But then again, it is successfully creating the SQL file in "C:\WINDOWS\TEMP", so perhaps that's not the issue either. I just thought the temp files were supposed to be created under "\g2data".
Larry Menard
Joined: 2005-10-01
Posts: 757
Posted: Sun, 2005-11-27 15:10
> cmd /c
Just to be clear, I tried both "cmd /c" and "cmd.exe /c".
Larry Menard
Joined: 2005-10-01
Posts: 757
Posted: Sun, 2005-11-27 17:14
Another observation:
Even
list ($success, $outputArray) = $platform->exec(array(array('dir', $fileName)));
returns nothing in $outputArray.
valiant
Joined: 2003-01-04
Posts: 32509
Posted: Sun, 2005-11-27 17:21
@temp in c:\windows\temp:
that surprises me too, but it doesn't hurt.
the docs at http://ch2.php.net/tempnam say, it chooses the system's temp dir if the dir that we specify doesn't exist already.
that would mean, that your tmp in g2data doesn't exist.
maybe that means that $gallery->getConfig('data.gallery.tmp') returns an empty string in the install core module step..
that's not the problem, i'm quite sure. we can write and read to the system's tmp dir, that's fine...for now.
@cmd:
hmm, so the cmd /c thing is ok.
@$cmd . $fileName:
so it's not a problem with adding quotes around the argument.
quoting a post from you, page 7:
> 2005-11-26 15:55:54 [] Executing: cmd /c " "db2cmd /c /w /i db2 -vf " "C:\WINDOWS\TEMP\db22D8E.tmp" 2> "C:\WINDOWS\TEMP\g2d2D90.tmp"
is it maybe the error output redirection (2>) ?
when you add 2> $anotherTempFileName to your "normal exec", does it work?
when you look at the install.log, and you're using the g2 platform->exec, what's the exact output? there should be Regular output: ..Error output: Status:
Larry Menard
Joined: 2005-10-01
Posts: 757
Posted: Sun, 2005-11-27 17:31
Another observation:
I added some debug code in WinNtPlatform::exec, and this doesn't look right:
Back in Db2DatabaseStorage::configureStore, $outputArray = array(0) { }
Why is the temporary file name specified twice? I don't quite understand the foreach loops that construct $command.
valiant
Joined: 2003-01-04
Posts: 32509
Posted: Sun, 2005-11-27 17:45
please post the complete configureStore code such that i can see how you call it.
and @my last post: you can see the exact call that we use in your install.log. no need to add debugging output in exec()
Larry Menard
Joined: 2005-10-01
Posts: 757
Posted: Sun, 2005-11-27 17:59
I'm calling it exactly the way you posted yesterday.
/*
* Need to install the JAR file for the java UDF here
* because the path to the jar file is variable, so can't be hard-coded in
* 'createDb2Udfs.sql'.
*
* The INSTALL call looks like it's unsupported in CLI (and therefore PHP)
* so we have to use (shudder) CLP.
*
* And DB2 considers the CLP call to be a separate application, so it requires
* its own CONNECT and CONNECT RESET. Which means the userid and password need
* to be included in the CLP script.
*/
// Create a temporary file to hold the sql
$fileName = $platform->tempnam($gallery->getConfig('data.gallery.tmp'), 'db2_');
$pathToJar = dirname(__FILE__) . $platform->getDirectorySeparator() . 'g2_db2_bit_or.jar';
$buf = sprintf("CONNECT TO gallery2 USER %s USING %s\n
CALL SQLJ.REMOVE_JAR('g2_db2_jar')\n
CALL SQLJ.INSTALL_JAR('file:%s', 'g2_db2_jar')\n
CONNECT RESET", $this->_username, $this->_password, $pathToJar);
$platform->atomicWrite($fileName, $buf);
if (GalleryUtilities::isA($platform, 'WinNtPlatform'))
{
$cmd = "db2cmd /c /w /i db2 -vf $fileName";
}
else
{
$cmd = "db2 -vf $fileName";
}
list ($success, $outputArray) = $platform->exec(array(array($cmd, $fileName)));
printf("<br>Back in Db2DatabaseStorage::configureStore, \$outputArray = ");
var_dump($outputArray);
BTW: I hadn't created a "g2data\tmp". That explains the Windows directory thing.
Larry Menard
Joined: 2005-10-01
Posts: 757
Posted: Sun, 2005-11-27 18:11
Sorry, just realized I'd forgotten to remove the $fileName from the $cmd that I build (that was a debugging step a while back).
It now says:
In WinNtPlatform::exec, $command = "cmd /c " "db2cmd /c /w /i db2 -vf " "C:\g2data\tmp\db22EF1.tmp" 2> "C:\g2data\tmp\g2d2EF3.tmp" "".
In WinNtPlatform::exec, $results = array(0) { }
Back in Db2DatabaseStorage::configureStore, $outputArray = array(0) { }
So the command execution in $platform->exec() is failing.
valiant
Joined: 2003-01-04
Posts: 32509
Posted: Sun, 2005-11-27 18:13
you don't have to create the tmp dir, it should create that in the storage setup step in the installer.
if it didn't, then there's something wrong.
(that has nothing to do with db2)
it should create these subdirs in g2data:
'albums',
'cache',
'locks',
'sessions',
'tmp',
'plugins_data',
'plugins_data/modules',
'plugins_data/themes',
'smarty',
'smarty/templates_c'
@configureStore code:
ah! i posted alternatives and you used both. sorry, that was a misunderstanding.
either use $fileName in
$cmd = "db2cmd /c /w /i db2 -vf $fileName";
or leave it there out and use it in
list ($success, $outputArray) = $platform->exec(array(array($cmd, $fileName)));
I found the problem. My bad. One of my scripts was cleaning out the g2data directory after the Storage Setup ran.
> array("db2cmd", "/c", "/w", "/i", "db2", "-vf")
Yeah, that works. I'm getting really tired of re-running the Installer.
Is this WAD? If so, I presume this is the ultimate solution for this particular problem.
valiant
Joined: 2003-01-04
Posts: 32509
Posted: Sun, 2005-11-27 19:29
this array approach of defining each arg separately is actually how it should be done. sorry, my bad. didn't realize this sooner. on linux, it would have worked without that, but the windows command line needs " " around each arg.
this is the final solution for this cmd fun.
WAD = without a doubt? (my mother tongue isn't English)
Larry Menard
Joined: 2005-10-01
Posts: 757
Posted: Sun, 2005-11-27 19:38
No prob,I appreciate your help figuring this out.
WAD = "Working As Designed". Sorry, it's a very common acronym in English. I'm not sure how true this is, but there are people who say the word "acronym" is also an acronym... "A Contrived Reduction Of Names Yeilding Mnemonics".
OK, next question.
For the error checking, exactly what do I return if the error checking detects a problem? Just something like:
return "some error description\n";
?
valiant
Joined: 2003-01-04
Posts: 32509
Posted: Sun, 2005-11-27 19:44
@error:
hmm, if we get there an error, is it a platform (filesystem / exec) or a storage (db) failure?
let's use platform error there...
If an error occurred there, it'd most likely be a database error. Does that change things? Do we have a database object at that point?
valiant
Joined: 2003-01-04
Posts: 32509
Posted: Sun, 2005-11-27 19:58
yeah, well, then replace
return GalleryStatus::error(ERROR_PLATFORM_FAILURE, __FILE__, __LINE__);
with
return GalleryStatus::error(ERROR_STORAGE_FAILURE, __FILE__, __LINE__);
Larry Menard
Joined: 2005-10-01
Posts: 757
Posted: Sun, 2005-11-27 20:01
> Also, I just tested an error condition. Nowhere can I find the error message from DB2.
> Would it be possible to print $outputArray[$ctr} at the point where it dies?
Never mind, I found the error in the install.log. Perfect.
Larry Menard
Joined: 2005-10-01
Posts: 757
Posted: Sun, 2005-11-27 21:05
OK, I now have a completely working install of DB2. Basic G2 functionality seems fine to me, based on what I did a few days ago (and I haven't changed any SQL since then).
Bear in mind that:
1) This is an implementation of the 'last-resort' CLP solution to the problem of DB2's CLI not handling INSTALL_JAR. If Dan (Scott) is willing to pursue this problem through appropriate channels we can pursue a better solution. (I can't pursue it through formal channels myself because, well, I don't exactly have a license for my copy of DB2 :-O).
2) We're still using DB2's Unified ODBC, not Dan's 'ibm_db2' PHP extension. As a result, the Unit Tests are horribly contaminated with CLI0005W warnings, to the point that it is impossible to make sense of the test results. I'm now going to begin working on developing a driver file for ADOdb that will support Dan's 'ibm_db2' PHP extension, and when it's ready I'll do the migration and revisit the Unit Tests.
My personal suspicion is that even if Dan does agree to pursue the INSTALL_JAR problem, it will probably take many, many months to get a fix for it. Therefore, I'd suggest we checkin the files I have now. I'm attaching them here. The Unit Tests for DB2 will still be broken, but it shouldn't impact any other databases.
If you have no major concerns with this, could you please checkin these changes and let me know when they can be re-confirmed?
Thanks.
valiant
Joined: 2003-01-04
Posts: 32509
Posted: Sun, 2005-11-27 23:45
Quote:
CREATE FUNCTION G2_BIT_OR (VARCHAR(32))
EXTERNAL NAME 'g2_db2_jar:g2_db2_bit_or!g2_db2_bit_or'
RETURNS VARCHAR(64) FOR BIT DATA
why return varchar(64) i'd say return varchar(32), but i don't bother, it's good to be ready for the future (but then we'd need to extend the input to 64 chars too).
Quote:
CIA-12 andy_st * gallery2/modules/core/ (8 files in 3 dirs):
CIA-12 DB2 progress (kudos go to Larry Menard):
CIA-12 - added the BIT_OR aggregate function
CIA-12 - G2 should be fully functional, but we can't run unit tests yet, because of odbc / CLI issues. Larry Menard is working on a ibm-db2 adodb driver (replacing the odbc based db2 driver) which should resolve these issues
CIA-12 - MISSING: The database setup step still needs to be updated (exec() check, map database name field to hostname and don't use hostname)
@db setup step:
i didn't update it yet. do you need only the database name field (i'll make sure that i use it internally as hostname, as you described) and you don't need the hostname input field at all?
also, i didn't add any docs yet. referring to your:
> 1) Creation of the java UDF requires DBADM permissions. Users/installers of G2 should be informed of that.
since anonymous cvs is still out of date (sf.net is still having issues), you should get tomorrows nightly snapshot (in 6h from now).
valiant
Joined: 2003-01-04
Posts: 32509
Posted: Sun, 2005-11-27 23:56
ps:
- i rewrite your configureStore code a little to comply with g2 coding standards. so i might have broken it since i can't test it.
- i didn't copy your new createDb2Udfs.sql , i copied the last ... lines (the create g2_bit_or stuff)
Larry Menard
Joined: 2005-10-01
Posts: 757
Posted: Mon, 2005-11-28 03:56
Thanks, Valiant.
> db setup step
I'm not sure exactly how to handle that. If I understand ODBC correctly, it is possible to connect to a database two ways:
2) something that uses a string something like "driver={IBM db2 odbc DRIVER};Database=$dbname;hostname=$hostname;port=$portnum;protocol=TCPIP;uid=$userid; pwd=$password"
(Bharat made reference to this on 2005-10-07 20:38, page 2 of this topic).
I have no experience with the latter, so I might be completely off the mark with this next question. . Does G2 support this type of connection string? If so, we'll still need a 'hostname' (and 'Port #') field. If not, then I guess we don't need the 'hostname' field.
> varchar(32/64)
That's what I thought too at first, but I very clearly recall getting errors about truncation until I increased it to varchar(64). I've just now reduced it to 32, and it ran clean. So yes, for consistency perhaps we should change that to VARCHAR(32). We'll worry about 64 and 128 bit when the time comes.
Thanks, Valiant.
P.S.: FYI: I still play sports with some of my friends from when I worked at IBM. I asked them after volleyball tonight if any of them would be willing to open a formal defect record for the bugs that I've found (in case Dan Scott is hesitant), and they agreed to do so. But even so, I'm sure it will take many months to get fixes for them.
valiant
Joined: 2003-01-04
Posts: 32509
Posted: Mon, 2005-11-28 09:56
@connect / hostname:
yeah, there seem to be 2 methods.
adodb just uses
if ($this->debug && $argDatabasename && $this->databaseType != 'vfp') {
ADOConnection::outp("For odbc Connect(), $argDatabasename is not used. Place dsn in 1st parameter.");
}
odbc_connect($argDSN,$argUsername,$argPassword);
and the 1st argument to _connect is $hostname for other adodb drivers.
Actually, $argDSN is exactly what I'd expect from an ODBC database. The darabase / hostname management is done in the ODBC manager of Windowx. There you create a new data source name and configure it (ser db driver, hostname, db name, ...). that's what i did a couple of years ago to access with MS Access oracle 8i databases.
so we'd have a single input field for DB2 when using the current adodb db2 driver. and we'd call it Data Source Name.
i guess we could also pass the other method to ODBC. we'd have to assemble the DSN-less long string on our own...
what do you plan to do for ibm-db2?
Larry Menard
Joined: 2005-10-01
Posts: 757
Posted: Mon, 2005-11-28 22:10
I gather $argDSN is that long connect string? So it looks like we will have to support the DSN string functionality?
In DB2, one connects to a "database", regardless of the underlying connection mechanism. A lot of people won't be familiar with the ODBC terminology like "DSN". For that reason, I'd recommend not committing G2 to using ODBC terminology. I'd suggest calling the field "Database Name (or ODBC DSN)" so that it's clear that it's multi-purpose.
For 'ibm_db2', I see my task as being to provide a layer that takes what ADOdb puts out and making it compatible with what 'ibm_db2' takes in. 'ibm_db2's db2_connect() looks like:
db2_connect($db, $userId, $password[, $options])
- where:
- $db is either a dbName or a DSN string
- $options is an associative array of supported options (e.g., autocommit)
So it looks like I will have to support the long connection string in 'ibm_db2' too.
valiant
Joined: 2003-01-04
Posts: 32509
Posted: Tue, 2005-11-29 00:34
$argDSN can be the long connect string (i guess). but it can also be (and that's the normal usage) just the "data source name", a simple name, which is an ODBC data source name, registered in your windows odbc manager.
no we don't have to support the long DSN-less functionality.
adodb db2 / odbc does nothing special about the the non-DSN form (the long long string). it just passes the userinput to the odbc_connect call.
you can do the same in your adodb ibm-db2 driver.
Maybe let's call it "Database Name / ODBC Data Source Name" in the installer, and you can put additional docs on codex.gallery2.org .
Larry Menard
Joined: 2005-10-01
Posts: 757
Posted: Tue, 2005-11-29 19:34
I just got the latest build and tested it. There are a number things wrong.
1) (not fatal) In 'createDb2Udfs.sql', the return type of G2_BIT_OR is still 64-bit. It should be 32, for consistency.
2) In 'db2DatabaseStorage.class', the path to the jar file should be dirname(__FILE__) . $slash . 'g2_db2.jar', not dirname(__FILE__) . $slash . 'g2_db2_bit_or.jar'. I decided to make the name more generic, but must have sent you an incomplete update for checkin. Sorry.
3) In 'db2DatabaseStorage.class', your check of $success is wrong. You're testing it as if 'true' means it worked, but in fact 'true' means it failed. So it was never even reaching the line-by-line error checking.
4) In 'db2DatabaseStorage.class', there's an error in the line-by-line error-checking after calling SQLJ.INSTALL_JAR. You should be looping through array $outputArray, not $result.
5) In 'db2DatabaseStorage.class', there are some typos. $checkNextline should be $checkNextLine (uppercase 'L').
And even after I fix those, there's still a problem that I can't figure out. The line-by-line check stops after checking only the first line. I can't see why. I updated the code as follows:
while ($line = array_shift($outputArray))
{
printf("<br>\$line = \"%s\".\n", $line);
if (!$checkNextLine && strpos(strtoupper($line), 'CALL SQLJ.INSTALL_JAR(') === 0)
{
printf ("<br>Found the 'CALL SQLJ.INSTALL_JAR'.\n");
$checkNextLine = true;
}
else
{
if ($checkNextLine && strpos($line, 'DB20000I ') === 0)
{
printf ("<br>Found the 'DB20000I ' after the CALL SQLJ.INSTALL_JAR.\n");
$ok = true;
break;
}
else
{
printf ("<br>Nothing to see here... move along.\n");
$checkNextLine = false;
}
}
}
And it prints out only:
Quote:
$line = " CONNECT TO gallery2 USER g2user USING".
Nothing to see here... move along.
valiant
Joined: 2003-01-04
Posts: 32509
Posted: Tue, 2005-11-29 20:02
1) ok, fixed.
2) thanks, fixed
3) no. can't follow you there. IMO it's correct. (use the nightly snapshots and not cvs, cvs is 2 weeks old or so)
i'm checking
if (!$success) {
error
}
! means NOT so if i test if (! boolean) then it tests if the boolean value is false and if so, it enters the if block.
4), 5) wow, sorry.
@still problems:
after fixing 4/5, i used an example $outputArray to check the parser and it works...
please try the next nightly:
here's the new section i'm using:
/* 2. Execute the command */
list ($success, $outputArray) = $platform->exec(array($cmdArgs));
/*
* 3. Cleanup: Delete the SQL file since it contains the DB username / password and
* it's not needed anymore
*/
@$platform->unlink($fileName);
/* 4. Check for successful installation of the UDF */
if (!$success) {
return GalleryStatus::error(ERROR_PLATFORM_FAILURE, __FILE__, __LINE__);
}
/* We expect a success code (DB20000I) right after the INSTALL_JAR line */
$ok = $checkNextLine = false;
while ($line = array_shift($outputArray)) {
if (!$checkNextLine && strpos(strtoupper($line), 'CALL SQLJ.INSTALL_JAR(') === 0) {
$checkNextLine = true;
} else if ($checkNextLine && strpos($line, 'DB20000I ') === 0) {
$ok = true;
break;
} else {
$checkNextLine = false;
}
}
if (!$ok) {
return GalleryStatus::error(ERROR_STORAGE_FAILURE, __FILE__, __LINE__);
}
Larry Menard
Joined: 2005-10-01
Posts: 757
Posted: Tue, 2005-11-29 21:42
3) Sorry, what's happening is that $success is NOT true, it is ''. But I think I know why. In the DB2 CLP file we generate I include a "CALL SQLJ.REMOVE_JAR()" statement just to ensure a clean slate. If that jar file has not been previously installed (as is the case in a newly-created database), that statement will fail. I believe the DB2 CLP will return a non-zero rc to the calling application in this case. To test this, I removed the "CALL SQLJ.REMOVE_JAR()" statement, and $success is now "1". So please remove that call from 'db2DatabaseStorage.class'.
> still failing
I did the same thing as you... I copied the code from 'db2DatabaseStorage.class' and created an array containing the output that would be generated by the CLP commands. It worked. (Script attached here.) But it's not working in G2 itself. However, I guarantee you that the code I originally sent you does work.
Note that some lines of output (most notably, the first line) are followed by a 0-byte line. Could that be fooling your array_shift()?
valiant
Joined: 2003-01-04
Posts: 32509
Posted: Tue, 2005-11-29 22:29
i see. yes, that's the problem.
that's fulling the while () condition, not array_shift.
since a zero length string is considered "false".
replace the while line with
while (!is_null($line = array_shift($outputArray))) {
valiant
Joined: 2003-01-04
Posts: 32509
Posted: Tue, 2005-11-29 22:36
@"CALL SQLJ.REMOVE_JAR()" :
i thought it would just return a DB2 error code line and then advance to the next call and return a db2 success code for the install call.
please verify with the new code that it really doesn't work when the remove call is included in the script. if so, we'll have to remove it from the generated SQL file.
Larry Menard
Joined: 2005-10-01
Posts: 757
Posted: Tue, 2005-11-29 22:43
That's it... it's working now. I'm attaching my current 'db2DatabaseStorage.class' here.
Thanks again, Valiant.
valiant
Joined: 2003-01-04
Posts: 32509
Posted: Tue, 2005-11-29 23:08
i've already updated the file in cvs with the last changes, but i didn't remove the "call remove ..jar " from the generated sql file.
could you confirm that this is absolutely needed?
Larry Menard
Joined: 2005-10-01
Posts: 757
Posted: Tue, 2005-11-29 23:39
Confirmed. $success is '' (non-true) with the REMOVE_JAR() call statement enabled. In the code I originally sent you it worked because I never tested $success, I relied on the line-by-line check.
There are certain things about DB2 CLP that I recall from my years in DB2 Test. Among those things:
1) DB2 CLP returns to its caller a return code of non-zero if it encounters any non-zero sqlcode.
2) Some lines of DB2 CLP output have malformed end of line sequences on Windows. Instead of x'0D0A' they might have x'0D0A0A'. That explains the 0-byte lines of output.
valiant
Joined: 2003-01-04
Posts: 32509
Posted: Wed, 2005-11-30 00:07
ok then, i've removed the remove call.
but that also means that unless you can't use the erase option of the installer. (which would remove the whole installation such that you can re-install just with the installer). in the install core module step, we'd then receive an error, because the install jar error will error out.
unless you find a "install or replace" call, we'd have to copy the generate sql file, execute, parse results for cleanStore() too.
Larry Menard
Joined: 2005-10-01
Posts: 757
Posted: Wed, 2005-11-30 01:01
Yes, that's correct. I had already added a comment to cleanStore to that effect.
However, I just now realized that I've broken cleanStore for DB2. I suspect that all the DROP FUNCTION statements will not be error-checked. I haven't actually tested that functionality yet.
Larry Menard
Joined: 2005-10-01
Posts: 757
Posted: Wed, 2005-11-30 01:51
To fix the four UDF removals in cleanStore(), do you have any problems with using dynamic variables, like the following?
$this->_traceStart();
$sql1 = 'DROP FUNCTION G2_BIT_AND(CHAR(32), CHAR(32))';
$sql2 = 'DROP FUNCTION BIT_AND(SMALLINT, SMALLINT)';
$sql3 = 'DROP FUNCTION G2_LIKE(VARCHAR(4000), VARCHAR(4000))';
$sql4 = 'DROP FUNCTION G2_BIT_OR(CHAR(32))';
/*
* These SQLJ.*_JAR() calls blow up, looks like CLI can't handle it.
* See configureStore() for a CLP workaround.
* $recordSet5 = $tmpDb->Execute("CALL SQLJ.REMOVE_JAR ('g2_db2_jar')");
*/
for ($ctr = 1; $ctr <= 4; $ctr++)
{
$runMe = "recordSet" . $ctr;
$sqlToRun = "sql" . $ctr;
${$runMe} = $tmpDb->Execute("${$sqlToRun}");
if (empty(${$runMe})) {
return GalleryStatus::error(ERROR_STORAGE_FAILURE, __FILE__, __LINE__);
}
}
$tmpDb->Close();
$this->_traceStop();
(I haven't tested this yet, wanted to check with you first.)
valiant
Joined: 2003-01-04
Posts: 32509
Posted: Wed, 2005-11-30 02:15
there's no need to use such complicated code, there are arrays ;)
$statements = array();
$statements[] = 'DROP FUNCTION G2_BIT_AND(CHAR(32), CHAR(32))';
$statements[] = 'DROP FUNCTION BIT_AND(SMALLINT, SMALLINT)';
$statements[] = 'DROP FUNCTION G2_LIKE(VARCHAR(4000), VARCHAR(4000))';
$statements[] = 'DROP FUNCTION G2_BIT_OR(CHAR(32))';
foreach ($statements as $sql) {
$recordSet = $tmpDb->Execute($sql);
if (empty($recordSet)) {
return GalleryStatus::error(ERROR_STORAGE_FAILURE, __FILE__, __LINE__);
}
}
should i commit that?
Larry Menard
Joined: 2005-10-01
Posts: 757
Posted: Wed, 2005-11-30 02:23
Yes, much nicer.
I guess you can commit it if you like. I haven't tested it yet, but cleanStore() is already broken anyway.
valiant
Joined: 2003-01-04
Posts: 32509
Posted: Wed, 2005-11-30 02:28
i don't understand why it's broken. just 'cause of the jar that we fail to delete?
Larry Menard
Joined: 2005-10-01
Posts: 757
Posted: Wed, 2005-11-30 02:44
It's broken because $recordSet1 through $recordSet4 are currently not tested. Only $recordSet is tested, and it was never created.
I screwed this up when I added the DROP FUNCTION statements for my UDFs. Originally there was only $recordSet, but when I added the other UDFs I renamed that to $recordSet1, and incremented each new statement by 1. But I forgot about the if (empty(...)) checking.
valiant
Joined: 2003-01-04
Posts: 32509
Posted: Wed, 2005-11-30 05:38
yeah. the error checking is broken, but not the functionality
fixed in cvs.
Larry Menard
Joined: 2005-10-01
Posts: 757
Posted: Fri, 2005-12-02 15:38
I'm trying to test these changes to cleanStore(), but I can't figure out how to invoke that code. I tried the "Start Over" link in the installer, but I don't think it invokes that code.
How can I test cleanStore()?
valiant
Joined: 2003-01-04
Posts: 32509
Posted: Fri, 2005-12-02 16:36
install g2.
then run the installer again, in step 5 (db setup step), after hitting save, you'll get 2 choices: erase | reuse.
erase will call cleanStore
Larry Menard
Joined: 2005-10-01
Posts: 757
Posted: Fri, 2005-12-02 16:54
Hmm... the only options I see are "Redo this step" and "Continue to Step 6".
valiant
Joined: 2003-01-04
Posts: 32509
Posted: Fri, 2005-12-02 17:08
yep, after installing g2 completely. enter the installer again. either start the installer from scratch or go directly to step 5 if possible.
and choose redo this step. then hit save, then you get the choice.
Larry Menard
Joined: 2005-10-01
Posts: 757
Posted: Fri, 2005-12-02 17:27
Sorry, nope... I close my browser and open a new instance, fire up the installer, at Step 5 I take "Redo", click "Save" again, but I still get exactly the same thing... "Redo this step" or "Continue to Step 6".
Posts: 32509
what the $platform->exec does:
- it adds cmd in front of the command so all $cmd are executed by cmd.exe (don't ask me why)
- it adds double quotes around each arg
so let's first try to check if the double quotes are the problem, try:
list ($success, $outputArray) = $platform->exec(array(array($cmd . $fileName)));
insteaf of
list ($success, $outputArray) = $platform->exec(array(array($cmd, $fileName)));
if that doesn't work, try the normal exec prepended with cmd /c
Posts: 757
> list ($success, $outputArray) = $platform->exec(array(array($cmd . $fileName)));
> normal exec prepended with cmd /c
I just noticed something. Look at the path in which the temp file(s) are being created... "C:\WINDOWS\TEMP". Isn't that supposed to be something under "C:\G2DATA"? But then again, it is successfully creating the SQL file in "C:\WINDOWS\TEMP", so perhaps that's not the issue either. I just thought the temp files were supposed to be created under "\g2data".
Posts: 757
> cmd /c
Just to be clear, I tried both "cmd /c" and "cmd.exe /c".
Posts: 757
Another observation:
Even
list ($success, $outputArray) = $platform->exec(array(array('dir', $fileName)));
returns nothing in $outputArray.
Posts: 32509
@temp in c:\windows\temp:
that surprises me too, but it doesn't hurt.
the docs at http://ch2.php.net/tempnam say, it chooses the system's temp dir if the dir that we specify doesn't exist already.
that would mean, that your tmp in g2data doesn't exist.
maybe that means that $gallery->getConfig('data.gallery.tmp') returns an empty string in the install core module step..
that's not the problem, i'm quite sure. we can write and read to the system's tmp dir, that's fine...for now.
@cmd:
hmm, so the cmd /c thing is ok.
@$cmd . $fileName:
so it's not a problem with adding quotes around the argument.
quoting a post from you, page 7:
> 2005-11-26 15:55:54 [] Executing: cmd /c " "db2cmd /c /w /i db2 -vf " "C:\WINDOWS\TEMP\db22D8E.tmp" 2> "C:\WINDOWS\TEMP\g2d2D90.tmp"
is it maybe the error output redirection (2>) ?
when you add 2> $anotherTempFileName to your "normal exec", does it work?
when you look at the install.log, and you're using the g2 platform->exec, what's the exact output? there should be Regular output: ..Error output: Status:
Posts: 757
Another observation:
I added some debug code in WinNtPlatform::exec, and this doesn't look right:
Why is the temporary file name specified twice? I don't quite understand the foreach loops that construct $command.
Posts: 32509
please post the complete configureStore code such that i can see how you call it.
and @my last post: you can see the exact call that we use in your install.log. no need to add debugging output in exec()
Posts: 757
I'm calling it exactly the way you posted yesterday.
/* * Need to install the JAR file for the java UDF here * because the path to the jar file is variable, so can't be hard-coded in * 'createDb2Udfs.sql'. * * The INSTALL call looks like it's unsupported in CLI (and therefore PHP) * so we have to use (shudder) CLP. * * And DB2 considers the CLP call to be a separate application, so it requires * its own CONNECT and CONNECT RESET. Which means the userid and password need * to be included in the CLP script. */ // Create a temporary file to hold the sql $fileName = $platform->tempnam($gallery->getConfig('data.gallery.tmp'), 'db2_'); $pathToJar = dirname(__FILE__) . $platform->getDirectorySeparator() . 'g2_db2_bit_or.jar'; $buf = sprintf("CONNECT TO gallery2 USER %s USING %s\n CALL SQLJ.REMOVE_JAR('g2_db2_jar')\n CALL SQLJ.INSTALL_JAR('file:%s', 'g2_db2_jar')\n CONNECT RESET", $this->_username, $this->_password, $pathToJar); $platform->atomicWrite($fileName, $buf); if (GalleryUtilities::isA($platform, 'WinNtPlatform')) { $cmd = "db2cmd /c /w /i db2 -vf $fileName"; } else { $cmd = "db2 -vf $fileName"; } list ($success, $outputArray) = $platform->exec(array(array($cmd, $fileName))); printf("<br>Back in Db2DatabaseStorage::configureStore, \$outputArray = "); var_dump($outputArray);
BTW: I hadn't created a "g2data\tmp". That explains the Windows directory thing.
Posts: 757
Sorry, just realized I'd forgotten to remove the $fileName from the $cmd that I build (that was a debugging step a while back).
It now says:
So the command execution in $platform->exec() is failing.
Posts: 32509
you don't have to create the tmp dir, it should create that in the storage setup step in the installer.
if it didn't, then there's something wrong.
(that has nothing to do with db2)
it should create these subdirs in g2data:
'albums',
'cache',
'locks',
'sessions',
'tmp',
'plugins_data',
'plugins_data/modules',
'plugins_data/themes',
'smarty',
'smarty/templates_c'
@configureStore code:
ah! i posted alternatives and you used both. sorry, that was a misunderstanding.
either use $fileName in
$cmd = "db2cmd /c /w /i db2 -vf $fileName";
or leave it there out and use it in
list ($success, $outputArray) = $platform->exec(array(array($cmd, $fileName)));
but don't specify it twice.
i recommend:
if (GalleryUtilities::isA($platform, 'WinNtPlatform'))
{
$cmd = "db2cmd /c /w /i db2 -vf";
}
else
{
$cmd = "db2 -vf";
}
list ($success, $outputArray) = $platform->exec(array(array($cmd, $fileName)));
please run the installer again from scratch (before empty g2data, drop the db). and then post the install.log. thank you!
Posts: 32509
ah, ok.
please run the installer again (or if you just did, you don't have to of course) and post the install log, thanks
Posts: 757
Ah-haa... when I remove the "cmd /c" it works.
Posts: 757
Just to summarize:
- Removing the "cmd /c" works.
- It is not specific to "db2cmd" either... even a "dir" command behaves the same.
- install.log is attached here (having both the "dir" and "db2cmd" commands).
Posts: 32509
please try:
sorry for all this...
Posts: 757
> g2data subdirs
I found the problem. My bad. One of my scripts was cleaning out the g2data directory after the Storage Setup ran.
> array("db2cmd", "/c", "/w", "/i", "db2", "-vf")
Yeah, that works. I'm getting really tired of re-running the Installer.
Is this WAD? If so, I presume this is the ultimate solution for this particular problem.
Posts: 32509
this array approach of defining each arg separately is actually how it should be done. sorry, my bad. didn't realize this sooner. on linux, it would have worked without that, but the windows command line needs " " around each arg.
this is the final solution for this cmd fun.
WAD = without a doubt? (my mother tongue isn't English)
Posts: 757
No prob,I appreciate your help figuring this out.
WAD = "Working As Designed". Sorry, it's a very common acronym in English. I'm not sure how true this is, but there are people who say the word "acronym" is also an acronym... "A Contrived Reduction Of Names Yeilding Mnemonics".
OK, next question.
For the error checking, exactly what do I return if the error checking detects a problem? Just something like:
return "some error description\n";
?
Posts: 32509
@error:
hmm, if we get there an error, is it a platform (filesystem / exec) or a storage (db) failure?
let's use platform error there...
return GalleryStatus::error(ERROR_PLATFORM_FAILURE, __FILE__, __LINE__);
Posts: 757
If an error occurred there, it'd most likely be a database error. Does that change things? Do we have a database object at that point?
Posts: 32509
yeah, well, then replace
return GalleryStatus::error(ERROR_PLATFORM_FAILURE, __FILE__, __LINE__);
with
return GalleryStatus::error(ERROR_STORAGE_FAILURE, __FILE__, __LINE__);
Posts: 757
> Also, I just tested an error condition. Nowhere can I find the error message from DB2.
> Would it be possible to print $outputArray[$ctr} at the point where it dies?
Never mind, I found the error in the install.log. Perfect.
Posts: 757
OK, I now have a completely working install of DB2. Basic G2 functionality seems fine to me, based on what I did a few days ago (and I haven't changed any SQL since then).
Bear in mind that:
1) This is an implementation of the 'last-resort' CLP solution to the problem of DB2's CLI not handling INSTALL_JAR. If Dan (Scott) is willing to pursue this problem through appropriate channels we can pursue a better solution. (I can't pursue it through formal channels myself because, well, I don't exactly have a license for my copy of DB2 :-O).
2) We're still using DB2's Unified ODBC, not Dan's 'ibm_db2' PHP extension. As a result, the Unit Tests are horribly contaminated with CLI0005W warnings, to the point that it is impossible to make sense of the test results. I'm now going to begin working on developing a driver file for ADOdb that will support Dan's 'ibm_db2' PHP extension, and when it's ready I'll do the migration and revisit the Unit Tests.
My personal suspicion is that even if Dan does agree to pursue the INSTALL_JAR problem, it will probably take many, many months to get a fix for it. Therefore, I'd suggest we checkin the files I have now. I'm attaching them here. The Unit Tests for DB2 will still be broken, but it shouldn't impact any other databases.
If you have no major concerns with this, could you please checkin these changes and let me know when they can be re-confirmed?
Thanks.
Posts: 32509
why return varchar(64) i'd say return varchar(32), but i don't bother, it's good to be ready for the future (but then we'd need to extend the input to 64 chars too).
@db setup step:
i didn't update it yet. do you need only the database name field (i'll make sure that i use it internally as hostname, as you described) and you don't need the hostname input field at all?
also, i didn't add any docs yet. referring to your:
> 1) Creation of the java UDF requires DBADM permissions. Users/installers of G2 should be informed of that.
since anonymous cvs is still out of date (sf.net is still having issues), you should get tomorrows nightly snapshot (in 6h from now).
Posts: 32509
ps:
- i rewrite your configureStore code a little to comply with g2 coding standards. so i might have broken it since i can't test it.
- i didn't copy your new createDb2Udfs.sql , i copied the last ... lines (the create g2_bit_or stuff)
Posts: 757
Thanks, Valiant.
> db setup step
I'm not sure exactly how to handle that. If I understand ODBC correctly, it is possible to connect to a database two ways:
1) odbc_connect($dbname, $userid, $password [, $cursorMode])
2) something that uses a string something like "driver={IBM db2 odbc DRIVER};Database=$dbname;hostname=$hostname;port=$portnum;protocol=TCPIP;uid=$userid; pwd=$password"
(Bharat made reference to this on 2005-10-07 20:38, page 2 of this topic).
I have no experience with the latter, so I might be completely off the mark with this next question. . Does G2 support this type of connection string? If so, we'll still need a 'hostname' (and 'Port #') field. If not, then I guess we don't need the 'hostname' field.
> varchar(32/64)
That's what I thought too at first, but I very clearly recall getting errors about truncation until I increased it to varchar(64). I've just now reduced it to 32, and it ran clean. So yes, for consistency perhaps we should change that to VARCHAR(32). We'll worry about 64 and 128 bit when the time comes.
Thanks, Valiant.
P.S.: FYI: I still play sports with some of my friends from when I worked at IBM. I asked them after volleyball tonight if any of them would be willing to open a formal defect record for the bugs that I've found (in case Dan Scott is hesitant), and they agreed to do so. But even so, I'm sure it will take many months to get fixes for them.
Posts: 32509
@connect / hostname:
yeah, there seem to be 2 methods.
adodb just uses
_connect($argDSN, $argUsername, $argPassword, $argDatabasename)
if ($this->debug && $argDatabasename && $this->databaseType != 'vfp') {
ADOConnection::outp("For odbc Connect(), $argDatabasename is not used. Place dsn in 1st parameter.");
}
odbc_connect($argDSN,$argUsername,$argPassword);
and the 1st argument to _connect is $hostname for other adodb drivers.
Actually, $argDSN is exactly what I'd expect from an ODBC database. The darabase / hostname management is done in the ODBC manager of Windowx. There you create a new data source name and configure it (ser db driver, hostname, db name, ...). that's what i did a couple of years ago to access with MS Access oracle 8i databases.
so we'd have a single input field for DB2 when using the current adodb db2 driver. and we'd call it Data Source Name.
i guess we could also pass the other method to ODBC. we'd have to assemble the DSN-less long string on our own...
what do you plan to do for ibm-db2?
Posts: 757
I gather $argDSN is that long connect string? So it looks like we will have to support the DSN string functionality?
In DB2, one connects to a "database", regardless of the underlying connection mechanism. A lot of people won't be familiar with the ODBC terminology like "DSN". For that reason, I'd recommend not committing G2 to using ODBC terminology. I'd suggest calling the field "Database Name (or ODBC DSN)" so that it's clear that it's multi-purpose.
For 'ibm_db2', I see my task as being to provide a layer that takes what ADOdb puts out and making it compatible with what 'ibm_db2' takes in. 'ibm_db2's db2_connect() looks like:
db2_connect($db, $userId, $password[, $options])
- where:
- $db is either a dbName or a DSN string
- $options is an associative array of supported options (e.g., autocommit)
So it looks like I will have to support the long connection string in 'ibm_db2' too.
Posts: 32509
$argDSN can be the long connect string (i guess). but it can also be (and that's the normal usage) just the "data source name", a simple name, which is an ODBC data source name, registered in your windows odbc manager.
no we don't have to support the long DSN-less functionality.
adodb db2 / odbc does nothing special about the the non-DSN form (the long long string). it just passes the userinput to the odbc_connect call.
you can do the same in your adodb ibm-db2 driver.
Maybe let's call it "Database Name / ODBC Data Source Name" in the installer, and you can put additional docs on codex.gallery2.org .
Posts: 757
I just got the latest build and tested it. There are a number things wrong.
1) (not fatal) In 'createDb2Udfs.sql', the return type of G2_BIT_OR is still 64-bit. It should be 32, for consistency.
2) In 'db2DatabaseStorage.class', the path to the jar file should be dirname(__FILE__) . $slash . 'g2_db2.jar', not dirname(__FILE__) . $slash . 'g2_db2_bit_or.jar'. I decided to make the name more generic, but must have sent you an incomplete update for checkin. Sorry.
3) In 'db2DatabaseStorage.class', your check of $success is wrong. You're testing it as if 'true' means it worked, but in fact 'true' means it failed. So it was never even reaching the line-by-line error checking.
4) In 'db2DatabaseStorage.class', there's an error in the line-by-line error-checking after calling SQLJ.INSTALL_JAR. You should be looping through array $outputArray, not $result.
5) In 'db2DatabaseStorage.class', there are some typos. $checkNextline should be $checkNextLine (uppercase 'L').
And even after I fix those, there's still a problem that I can't figure out. The line-by-line check stops after checking only the first line. I can't see why. I updated the code as follows:
And it prints out only:
Posts: 32509
1) ok, fixed.
2) thanks, fixed
3) no. can't follow you there. IMO it's correct. (use the nightly snapshots and not cvs, cvs is 2 weeks old or so)
i'm checking
if (!$success) {
error
}
! means NOT so if i test if (! boolean) then it tests if the boolean value is false and if so, it enters the if block.
4), 5) wow, sorry.
@still problems:
after fixing 4/5, i used an example $outputArray to check the parser and it works...
please try the next nightly:
here's the new section i'm using:
Posts: 757
3) Sorry, what's happening is that $success is NOT true, it is ''. But I think I know why. In the DB2 CLP file we generate I include a "CALL SQLJ.REMOVE_JAR()" statement just to ensure a clean slate. If that jar file has not been previously installed (as is the case in a newly-created database), that statement will fail. I believe the DB2 CLP will return a non-zero rc to the calling application in this case. To test this, I removed the "CALL SQLJ.REMOVE_JAR()" statement, and $success is now "1". So please remove that call from 'db2DatabaseStorage.class'.
> still failing
I did the same thing as you... I copied the code from 'db2DatabaseStorage.class' and created an array containing the output that would be generated by the CLP commands. It worked. (Script attached here.) But it's not working in G2 itself. However, I guarantee you that the code I originally sent you does work.
Posts: 757
Hmmm, a var_dump($outputArray) shows:
$outputArray = array(16) { [0]=> string(38) " CONNECT TO gallery2 USER g2user USING" [1]=> string(0) "" [2]=> string(34) " Database Connection Information" [3]=> string(0) "" [4]=> string(38) " Database server = DB2/NT 8.2.2" [5]=> string(32) " SQL authorization ID = G2USER" [6]=> string(34) " Local database alias = GALLERY2" [7]=> string(0) "" [8]=> string(0) "" [9]=> string(128) "CALL SQLJ.INSTALL_JAR('file:C:\My Server\gallery2\modules\core\classes\GalleryStorage\DatabaseStorage\g2_db2.jar', 'g2_db2_jar')" [10]=> string(50) "DB20000I The CALL command completed successfully." [11]=> string(0) "" [12]=> string(0) "" [13]=> string(13) "CONNECT RESET" [14]=> string(49) "DB20000I The SQL command completed successfully." [15]=> string(0) "" }
Note that some lines of output (most notably, the first line) are followed by a 0-byte line. Could that be fooling your array_shift()?
Posts: 32509
i see. yes, that's the problem.
that's fulling the while () condition, not array_shift.
since a zero length string is considered "false".
replace the while line with
while (!is_null($line = array_shift($outputArray))) {
Posts: 32509
@"CALL SQLJ.REMOVE_JAR()" :
i thought it would just return a DB2 error code line and then advance to the next call and return a db2 success code for the install call.
please verify with the new code that it really doesn't work when the remove call is included in the script. if so, we'll have to remove it from the generated SQL file.
Posts: 757
That's it... it's working now. I'm attaching my current 'db2DatabaseStorage.class' here.
Thanks again, Valiant.
Posts: 32509
i've already updated the file in cvs with the last changes, but i didn't remove the "call remove ..jar " from the generated sql file.
could you confirm that this is absolutely needed?
Posts: 757
Confirmed. $success is '' (non-true) with the REMOVE_JAR() call statement enabled. In the code I originally sent you it worked because I never tested $success, I relied on the line-by-line check.
There are certain things about DB2 CLP that I recall from my years in DB2 Test. Among those things:
1) DB2 CLP returns to its caller a return code of non-zero if it encounters any non-zero sqlcode.
2) Some lines of DB2 CLP output have malformed end of line sequences on Windows. Instead of x'0D0A' they might have x'0D0A0A'. That explains the 0-byte lines of output.
Posts: 32509
ok then, i've removed the remove call.
but that also means that unless you can't use the erase option of the installer. (which would remove the whole installation such that you can re-install just with the installer). in the install core module step, we'd then receive an error, because the install jar error will error out.
unless you find a "install or replace" call, we'd have to copy the generate sql file, execute, parse results for cleanStore() too.
Posts: 757
Yes, that's correct. I had already added a comment to cleanStore to that effect.
However, I just now realized that I've broken cleanStore for DB2. I suspect that all the DROP FUNCTION statements will not be error-checked. I haven't actually tested that functionality yet.
Posts: 757
To fix the four UDF removals in cleanStore(), do you have any problems with using dynamic variables, like the following?
(I haven't tested this yet, wanted to check with you first.)
Posts: 32509
there's no need to use such complicated code, there are arrays ;)
should i commit that?
Posts: 757
Yes, much nicer.
I guess you can commit it if you like. I haven't tested it yet, but cleanStore() is already broken anyway.
Posts: 32509
i don't understand why it's broken. just 'cause of the jar that we fail to delete?
Posts: 757
It's broken because $recordSet1 through $recordSet4 are currently not tested. Only $recordSet is tested, and it was never created.
I screwed this up when I added the DROP FUNCTION statements for my UDFs. Originally there was only $recordSet, but when I added the other UDFs I renamed that to $recordSet1, and incremented each new statement by 1. But I forgot about the if (empty(...)) checking.
Posts: 32509
yeah. the error checking is broken, but not the functionality
fixed in cvs.
Posts: 757
I'm trying to test these changes to cleanStore(), but I can't figure out how to invoke that code. I tried the "Start Over" link in the installer, but I don't think it invokes that code.
How can I test cleanStore()?
Posts: 32509
install g2.
then run the installer again, in step 5 (db setup step), after hitting save, you'll get 2 choices: erase | reuse.
erase will call cleanStore
Posts: 757
Hmm... the only options I see are "Redo this step" and "Continue to Step 6".
Posts: 32509
yep, after installing g2 completely. enter the installer again. either start the installer from scratch or go directly to step 5 if possible.
and choose redo this step. then hit save, then you get the choice.
Posts: 757
Sorry, nope... I close my browser and open a new instance, fire up the installer, at Step 5 I take "Redo", click "Save" again, but I still get exactly the same thing... "Redo this step" or "Continue to Step 6".