Sunday, December 11, 2011

File Checking - Perl to the rescue

One of my coworkers posted about an issue with one of the data files we receive from outside world. We have a batch program that processes these files and posts transactions into our system. The issue was that a file had one record that an extra byte and any amount of checks didn't reveal the "bad" row. After different checks, he decided to download to PC, take it into TextPad and turned on visual spaces to find which row had that extra space.  Very tedious, but it works.

As you may know, Perl is a very good scripting tool for such purposes. I've created a sample perl script that does basic checks from file size to record size. If it finds any discrepancy, it prints the line #s and records. (See sample output below). For those interested, I'm attaching a copy (text version of the script) here for reference.

(If you are uploading to unix, you need to chmod +x to make it executable).

Please let me know, if you want more information. Feel free to change the script as needed (but please send me a copy, so I can keep mine updated).

Sample Usage:

/tmp/ FIN_09022011_131353.txt 259

1st parameter is the file name and the second parm is expected record size.

Sample Output:
$ /tmp/ FIN_999905_BILLPAY_09022011_131353.txt 259
Name of the file              : FIN_09022011_131353.txt
File Size                     : 1554 bytes
Record Size expected          : 259
Total # of lines in file      : 6
File appears to be a DOS file. (contains carriage returns)
All rows match!

After creating a bad record (I just "fixed" one of the record to change it to 260 chars):
$ /tmp/ FIN_09022011_131353.txt 259
Name of the file              : FIN_09022011_131353.txt
File Size                     : 1555 bytes
Record Size expected          : 259
Total # of lines in file      : 6
File appears to be a DOS file. (contains carriage returns)

Following rows were unmatched:
+4 -  Size: 260 - << P0001CHK0000000000000374.4600099999990001400001111              011000015                                                                                     Sample Record                                                                                     <CR>>>

<CR>- The script translates CTRL-M to printable <CR>; otherwise it would have inserted just a blank line in the output!

Below file contains the perl script in PDF file format:

Friday, December 9, 2011

Enabling DBTrace in PB

Enabling DB Trace in PB

Powerbuilder has a nice feature to enable trace on a database connection. In a typical PB application this can be enabled by adding a "TRA" (for TRACE) before the DBMS name while setting connection parameters before making connection to the database.
SQLCA.ServerName="<Oracle Server>"
SQLCA.LogPass="<Oracle ID>"
SQLCA.LogPass=<Oracle Password>"

Trace can be abbreviated as TRA. In this example, the prefixing is done in Powerscript. SQLCA is the transaction object that is used in Powerscript to connect to database. In a typical application, however, these settings are read from an ini file or the Windows Registry. So depending on the design, you may have to add TRA to an .ini file or the Registry setting for the Application.

To disable the Trace, just remove TRA and restart application.

Enabling DB Trace in EA Server

I currently work with an n-tier PB application deployed in EA Server. The database connection happens only on the Server side. (It's still in PB code, but the PB code runs inside EA Server). So, it is slightly different from the traditional 2-tier PB application.

Typically in any EA Server application, connections to the database are made through Connection Caches (DB Pool). So, we can set the DB trace while connecting to the Connection Cache.

In our application, we have a package called its_systemservices that has some global service objects, including a Properties Manager component. We use this component to store and get all the system level settings, including the settings required by the connection Cache(s). The actual settings are stored as properties of the its_systemservices package. One of these settings is the DBMS property which is set to O10 (for Oracle 10g driver). By prefixing this with TRA(ce), we can enable the DB Trace.

In EA Server, every entity has a set of properties which are stored in a properties file for the entity. We can edit these properties in Jaguar Manager.

Package Properties_ its_systemservices

The above change gets saved in the properties file for the package (its_systemservices.props). Alternatively, you can also edit the file directly. But, be careful not to change any other settings.


To disable, just remove TRA and restart EA Server.

DB Trace File

By default, the trace output will be stored in a file called, dbtrace.log in C:\Windows. It has lots of valuable information including the SQLs that were executed. To capture all the SQLs from a PB screen in our application, I run EA Server in local mode and thus I will be able to capture SQLs from all the Server components.

PB appends to this file for every DB call and thus file becomes fairly large quickly. Do not forget to turn off this setting as soon as you got the trace needed, as the trace files are typically large. The file can be deleted at any time (provided it's not locked by the app) and PB will create a new file (If TRA option is still set).

DB Trace file name, location and what is being captured can be controlled. Previously, you can control the trace file name and location by setting it in PB.ini. With newer versions of PB (since PB 10.2), you can edit the registry to do this. For e.g., in PB 10.5, the following registry entry controls DB Trace.

Apart from being able to change file name and location, you can also turn tracing on/off dynamically using these settings. We can also control how much trace information is captured by setting appropriate trace settings under DBTrace in the registry.

Veteran PB developer, Bruce Armstrong, has an article on this nice feature here.

Note: This setting is only for capturing SQLs coming from PB objects (Datawindows, Embedded SQL etc) Obviously, SQLs in Database (such as those in Stored procedures etc) will not be in this trace file, but the calls to Stored procedures will be.

PB Gotcha - Embedded (inline) SQL - SELECT..INTO

Just a quick gotcha I stumbled on in PB code. This is about SELECT..INTO. As you know, SELECT..INTO expects one row and it works well as long as there is only one row.While researching another issue, I saw the below SQL in one of the objects in our application.


Date ld_CutOff
INTO :ld_Cutoff

There are 4 rows for this parameter in the SYSPARM table. I originally anticipated ld_Cutoff to be NULL (or 01/01/1900 - default for date) as I thought this SQL will error out because of too many rows. To my surprise, the variable had the value from the first row.

The gotcha here is that PB does not throw any exception when a DB error occurs. Even though the above SQL resulted in an error, PB silently fetches the first row's value into the variable and sets SQLCODE to -1 and error message in SQLErrText. (See attachment). 

Even though the host variable got value from 1st row, this is not guaranteed and is not a documented feature in PB or Oracle. If we anticipate more than 1 row for the SELECT, then try to add conditions (rownum, cursors) to filter to 1 row.

(Oracle SQL[PL/SQL] behavior: In Oracle the same SQL would generate a TOO_MANY_ROWS exception . There also the SQL seems to be setting the host variable with the value from the 1st row, but Oracle does not recommend relying on this. See usage notes in reference 1 below)

Irrespective of the # of rows fetched,  we always need to add proper error checks around the SQL. In PB, check the SQLCA.SQLCode, and print messages using SQLCA.SQLErrText. (In Oracle, handle TOO_MANY_ROWS exception).

Note: A lot of our SQLs getting SYSPARM are done this way. PR4297 was created to add corrections to these.

1. Oracle PL/SQL User Guide

Visual Expert survey on Powerbuilder

If you are interested in knowing where PB stands out there, there is a survey on PB here:

This is the company that makes Visual Expert for PB, we saw earlier. If you take the survey, they let you see their survey results for previous years.


Forward Slash in SQL*Plus

Note to the visitor: Thank you for stopping by. I see a lot of people trying to find out about forward slash in SQL*Plus. I want to clarify: This post only applies to Oracle SQL. If you are you are querying about a "/" usage in any other database, you are in the wrong place.

Forward Slash ("/") in Oracle SQL

This post is about using forward slash ("/") in Oracle SQL and PL/SQL. This gets a lot of developers (new and experienced alike) as it's not always needed and sometimes it shouldn't be used.

Have you ever tried typing "/" (without quotes), as soon as you logged into SQL*Plus? Try it. You will see something like below.
SQL> /
SP2-0103: Nothing in SQL buffer to run.

The above snippet explains it all pretty well. Forward slash ("/" ) is a shortcut for RUN command in SQL*Plus (like "go" in SqlServer or mysql). Since there is nothing to run yet, SQL*Plus returned an error message (prefixed with SP2-).

The message in the above example also shows there is a SQL buffer in SQL*Plus. Any SQL sent to DB ends up in this (one statement) buffer. Also, if there is a statement in the buffer, "/" would have simply re-executed it.

When you type a line and press ENTER, SQL*Plus checks if it's one of it's own commands, a SQL statement or just bad text. If it's a SQL (identified by keywords like SELECT etc), it starts storing it in the buffer until it sees a "/" or a semi-colon, at which time it executes it (actually sends it to the server to execute it).

Gotcha with Forward Slash (/)

SQL Statements typically end in Semi-colon. Semi-colon is the Statement Terminator in ANSI standard. But, in Oracle SQL, you can use "/" to run a SQL, instead of semicolon, like shown. (This is how SPANISH record got inserted in my attached example at the bottom of this post).
SQL> SELECT dummy FROM dual
2 /

But, here is the gotcha: If the statement itself contained a semi-colon, then "/" would have re-executed it!!!!
SQL> select dummy from dual;
SQL> /

Oops. Imagine this was an INSERT statement instead!!! (And this is what happened in my example script attached. If you notice the "/" before commit, this is what caused FRENCH to be inserted twice!!).

So be careful with your usage of "/". For plain SQL, it's a matter of choice. Typically, this is used in DDL statements and semi-colons in DML statements. Be consistent to avoid surprises.

Remember "/" is just a RUN (or push) command in the tool, not part of Oracle SQL itself. If you run my attached example script in SQLTools, you will get an error on "/".

Always, run your scripts in SQL*Plus to make sure it will run fine in production, as this is a tool of choice for several DBAs.

Forward Slash ("/") in PL/SQL

PL/SQL on the other hand is a group of SQL statements with embedded semicolons. In this case, a semicolon alone cannot be used to send to DB. It needs a push with "/"! So, in this case, it acts as a Statement Terminator as well.
SQL> list
4 SELECT 10 as num INTO x FROM dual;
5 Dbms_Output.put_line('x = ' || x);
7 END;

In the above example, Notice lines  7 - 13 are blank. This is because I pressed ENTER several times. Since SQL*Plus knows this a PL/SQL (declare, begin...end), it waits for me to signal the real end, which is..... a "/". Until I typed "/" on line 13, it didn't come out of the PL/SQL editing mode. "/" also pushed the PL/SQL block to server to execute it.

Even though this has a bunch of lines, this whole text is a single PL/SQL statement. Type list at the SQL*Plus Prompt. You will see the "single" statement listed in full (minus the "/").

SQL*Plus commands

SQL*Plus commands like SET, SHOW, SPOOL etc are typically one liners and should not be ended with semicolon (and don't need "/" either).
SQL> show serveroutput;
serveroutput OFF

These commands will be executed locally in the tool and won't be stored in SQL buffer. So, a "/" after a SQL*Plus command will not re-execute it. In fact, it will re-execute the previous SQL Statement in the buffer!!

(Note: A "/" must be the first non-whitespace character (space, tab etc) on a line by itself. It cannot be at the end of a line, like semi-colon can be).

I've also posted a more detailed description of SQL*Plus here.


  1. There is a subtle difference: RUN actually lists before it runs; / doesn't!

  2. This is a local message from SQL*Plus. If this was from Oracle database server, you would see a "ORA" prefix.

Example Script
-- Example data for SQL*Plus related blogs
DROP TABLE greetings;
CREATE TABLE greetings (id NUMBER, lang VARCHAR2(10), msg VARCHAR2(30));
INSERT INTO greetings VALUES (1, 'ENGLISH', 'Hello World');
INSERT INTO greetings VALUES (2, 'FRENCH', 'Bonjour le monde');
INSERT INTO greetings VALUES (3, 'GERMAN', 'Hallo Welt');
INSERT INTO greetings VALUES (4, 'SPANISH', 'Hola Mundo')
-- Here are some extra notes about this script:
-- Just Hello world in some languages. Translations are approx. from Google.
-- Even though we are submitting this script to SQL*Plus entirely,
--   it executes one SQL Statement at a time. List command shows this.

-- Forward Slash after FRENCH, reexecutes previous command. so you will see
-- 2 FRENCH records inserted.
-- Forward Slash after GERMAN was, by mistake, thought to run list, but instead it
-- it ran the previous SQL statement, thus we have 2 GERMANs!!
-- Forward Slash after SPANISH inserts only once - because it's missing semi-colon (;)
-- and / substitutes for it!!
-- Commit is required in Oracle SQL. By default, Autocommit is not turned on in SQL*Plus
-- To see/set default use SHOW/SET AUTOCOMMIT

Further Reading

  • See my Take 2 on this topic here.

  • If you are looking for any relationship between forward slash and commits (there is none), please see my post here.

  • If you are looking to see if Forward Slash somehow affects DDLs, please see here.

  •  Please see here for a more detailed description of SQL*Plus in general.


SQL BNF Grammars




Oracle SQL Gotcha - Commit in DDL

This happened to one of my fellow developers recently. She had an ALTER statement in the middle of a SQL script that produced an unexpected results when the SQL failed. The problem was mixing DMLs with DDLs in the same script. Read on!

As you probably know, Commits work differently in DMLs and DDLs in Oracle.

  • Typically* DML statements (like INSERT, UPDATE, MERGE, DELETE...) needs an explicit COMMIT statement to commit changes to DB.

  • DDL statements (like CREATE, CREATE TABLE AS (CTAS), DROP, TRUNCATE etc) don't require a COMMIT statement. DDL actually issues an implicit COMMIT before and after the statement is executed.  This also means, any COMMIT statement after a DDL is not required and has no effect.

So, if you are mixing DMLs and DDLs, be mindful of this. If you have a DML (insert/update...) statement, followed by a DDL statement, you might have inadvertently committed earlier DML. Any later rollbacks won't have any effect.

Here is an example. Test 1 doesn't mix DMLs and DDLs. (There is no DDL immediately after DML). Test 2 on the other hand has a bug in that it has a DDL after the DML statement which results in unpredictable results.

The gotcha here is that, since the commit is done implicitly before even the DDL is executed, this happens even if the DDL failed!! 

Test 1 -- to show the effect of rollback after a DML
-- 1. DDLs 
DROP TABLE test_commit;
CREATE TABLE test_commit (id int, name VARCHAR2(30));

-- 2. DML
INSERT INTO test_commit VALUES(1, 'Test 1');
-- this shows 1 row
SELECT * FROM test_commit;

-- 3. Rollback below reverses the inserts (no DDLs since last DML)

-- 4. This shows no rows
SELECT * FROM test_commit;

Test 2 - show the effect of an interspersed DDL on commit!
-- 1. DDLs
DROP TABLE test_commit;
CREATE TABLE test_commit (id int, name VARCHAR2(30));

-- 2. DML
INSERT INTO test_commit VALUES(1, 'Test 1');
-- This shows 1 row
SELECT * FROM test_commit;

-- 2a. This DDL implicitly commits, so above insert is now committed.
DROP TABLE temp_table2;

-- temp_table2 doesn't really exist - I just used it to show it really doesn't matter,
-- commit happens implicitly in the DDL

-- 3. This rollback is same as in Example 1, but this has no effect

-- 4. his shows 1 row; if the rollback above worked, we wouldn't see any rows here.
SELECT * FROM test_commit;

-- End of Test; cleanup
DROP TABLE test_commit;

* When Autocommit is off, which is the default in Oracle

Tuesday, October 25, 2011

Oracle SQL*Plus

Oracle SQL*Plus

I've posted a few SQL tricks earlier. Most of these were in Oracle and often times, we use SQL*Plus to run these.There are several sophisticated SQL clients available for Windows (Toad is a great tool and at work we use SQL Tools++ and Golden), many even free, but SQL*Plus still remains indispensable. In this post, I will explore this tool.

SQL*Plus is an interesting tool. It's an old, text based, SQL Client that has been in Oracle from the beginning. Whatever it's strengths, user interface isn't one of them. With a command line interface (there is a Windows version (SQLPLusw), but not a major improvement) and quirky commands, it is not exactly user friendly, at least not according to today's GUI standards. Would you believe that SQL*Plus is supposed to be an improvement over what Oracle called "User Friendly Interface"?

Oracle did try to come up with several tools to replace it- a browser version called iSQL*Plus, a java based tool called SQL*Developer, but SQL*Plus survived and may actually outlive those. (See here for the bad news for  iSQL*Plus and SQLPlusw in 11g. Among other things, they actually recommend SQL*Plus!!!).  See Lauren's blog for the official links about this announcement. Also don't forget to read up this blog setting up SQL*Plus nicely in windows, so you won't miss those little used tools.

Strengths of SQL*Plus

Here are some of the reasons why SQL*Plus is still used in production environments, in spite of it's limitations:

  • For one, it is a very stable product. This combined with it's simplicity and small memory footprint, makes it a tool of choice for production.

  • It is available on various platforms, and offers an almost identical user interface across different platforms.

  • The tool can be run in batch mode; combining this with spooling capability and some commands, we can automate running scripts.

  • The tool conforms to the Oracle SQL standards so closely, so it still remains as tool of choice when you want to ensure your SQL will really pass in the Oracle environment.

  • And to it's credit, it has added few features over the years. For e.g., you can actually spool it's output to an HTML file by adding a few directives (see my post here).

SQL*Plus as a Client/Server Tool

SQL*Plus, like any other SQL Client, is an interactive Client/Server tool. It has a simple editor (don't expect a fancy editor - the one built in is a rudimentary line editor), which can hold 1 statement (however long) in the buffer. You can open a session in SQL*Plus and start typing Statements at the prompt. After you type a line, you can press enter to go to the next line. SQL*Plus will show a line # to indicate it's ready for the next line. You can keep typing and when you are ready, you can press Enter twice, for it to leave edit mode. At this point, the Statement is in memory ready to be executed. When you are done entering/editing the contents, simply issue a “RUN” command (See below) and the contents (SQL) will be pushed to the DB (server) to execute and return results. When the statement is executed successfully, the statement is then put into the 1-statement buffer for reuse.

Below is my sincere effort to document how I see SQL*Plus is executing commands. It's only an educated guess, so use with caution. SQL*Plus by design is a simple tool that executes one command at a time.

[caption id="attachment_471" align="alignnone" width="984"] Fig 1. SQL*Plus Design - an educated guess[/caption]


As shown there are 3 different types of statements that are processed by the command processor. It uses the keyword and the end of the statement to decide where to send it. If the statement has a semi-colon (";") or a slash ("/") at the end (See below), it is sent to the Database engine. (Try typing anything with a keyword (like SELECT etc) and end it with a semi-colon, you will get a SQL Error, indicating it went to DB Engine). Rest of them are tried as a local tool command.

Image 744




Image 746


SQL*Plus as a batch tool

There also seems to be a loop in the command processor, that could read one command at a time from a file and pass it on to the command processor. This combined with few commands to SET and SPOOL makes it a good batch tool. A SQL file is just a combination of different types of statements (SQL, PL/SQL and tools commands).

To run a file with SQL statements you can use START command. The short-cut for this is @. So, run a SQL file, you simply have to type,

@<sqlfile>, where <sqlfile>is any text file with SQL statements.

You can mix tool commands and SQLs in the file. The thing to remember is when we execute a SQL file in SQL*Plus, it processes one statement at a time successively. This is key to understand any problem with your script. You are not passing the SQL file to Oracle, you are giving it to SQL*Plus, which reads and executes one statement at a time depending on the type of statement and terminators used.

Run command and Statement Separator

Oracle uses it's own Run command "(/") and SQL standard for Statement Terminator to execute SQLs.

  • Oracle uses Run command as a "statement pusher" to send to DB. This has a short-cut ("/").

  • Oracle also allows semicolon (SQL Standard) to terminate SQLs.

  • As yet another short-cut, SQL*Plus pushes SQL statements to DB right after a semicolon, so you could use either slash or semicolon there.

  • But, with PL/SQL you needed both, as the SQL block will need a semi-colon and to push it to DB, you need a "/".

Potential issues with Run Command

This flexibility in SQL*Plus typically leads to many errors in SQL Scripts. Particularly,

  • If you have a PL/SQL in a file and missed the "/", it won't execute the PL/SQL.

    • Imagine, you try to drop a table and recreate it with few changes. If the drop command failed, results of successive commands may become unpredictable

  • On the other hand, if you added a "/" after a SQL with semicolon, the Statement is run twice!!

    • For e.g., you could end up in inserting a record twice which could make some other Select/into to fail downstream.

(Different databases handle this issue differently. tools for SQL Server for e.g., uses "go" as "statement pusher" uniformly for all statements. If you miss a "go" you get an error. MySql handles this slightly differently It uses semicolon for simple SQLs and when it comes to block SQL, it allows us to define a temporary delimiter other than semicolon to mark end of SQL).

See my post here for more details on "/".

How to avoid issues

We can use standards to avoid surprises. I prefer to use semicolons after all SQL (DML and DDL) statements. "/" for PL/SQLs. You may find "/" used for DDLs as well.

If you are planning to use "/" after a SQL statement in a file, I suggest this only for the last SQL statement in the file. This is just a preference for readability.

Few related SQL*Plus gotchas

1. Though both semicolon and "/" could push a SQL statement to Oracle, there is a subtle difference. you can type multiple statements in 1 line separated by semicolon. "/" cannot be used thus.

2. The "/" must be the only character on the line by itself. No other character except spaces may be allowed, not even comments.

3. Sometimes inline comments that start with /* could be construed as "RUN" and may cause syntax errors.

4. Typically, you can include blank lines inside PL/SQL block. But SQL*Plus may frown if you had blank lines inside simple SQL statement. There is SQL*Plus setting (sqlblanklines) to avoid this, but I suggest not to use it to stick to standards.


As mentioned, SQL*Plus can execute SQL and PL/SQL statements and several of it's own commands. Oracle supports Standard SQL and has several extensions as well. Oracle has very good documentation on this topic and several others. See here for Oracle SQL reference and here for good reference on PL/SQL. See here for a complete reference on SQL*Plus from Oracle. refer to this site for the SQLPlus tool commands.


Saturday, August 6, 2011

Ubuntu - Thunderbird on Linux

I've used Mozilla Thunderbird as my mail client on Windows, for a while now. This worked out better for me than Eudora, Outlook express. Ever since I lost my account at Yahoo some years back, I store important messages on PC. I switched Gmail for the fact that they allowed POP client. (On Yahoo, I had to pay for that service!)

Since I moved to Linux last year, I've tried various software on it. I had Evolution as my mail client as it comes as default on Ubuntu. It was very similar to Thunderbird, but there were some issues: For some reason the address lookup never worked for me there. Also, it had some file permission problems constantly, so my sent mail will sit in outbox forever. Worse yet, it used to silently crash and restart itself every now and then. I am sure, I could have googled for a solution, but I decided to go back to Thunderbird. I love it.

(Googling for "ThunderBird vs Evolution" brings backs hundreds of threads like the one below. They also talk about Kontact, Claws etc. Must explore in the future. )

When I got Thunderbird, I set it up POP settings for Google. Since, I have 2 PCs connecting POP to Google mail, messages started disappearing on either, depending on which one got it first. (Nature of POP, once you downloaded a message, it's no longer available to download. See below link). Then I discovered IMAP. It's always been there, but suddenly it occurred to me that I could use that for a multi-client setup. I did that, and it's been working OK. One thing with IMAP is that it's a direct access to the Internet Mailbox. So, nothing is stored on your PC. But, IMAP has an option to store locally as well. So, on one PC I set it up to download and the other just connect directly. Below link has a nice article about IMAP vs POP mail.

Tuesday, August 2, 2011

Java & I - My Java Development experience

Over the years I worked with various versions of Java through 1.6. But, Java was never my main bread & butter. Irrespective of the environment there was always some flavor of Java used in the development environment.

Java Swing, Applet etc
I started working with Java 1.0, many years ago. All those books and web sites with applet code with flashy animations made it look really interesting. (Real applet experience was totally different though!). Since then, I've worked in Java on and off. For a few years, I worked in a Java swing building Java applet (2MB jar!!) against a Forte Server. This included working with Visual Cafe, Visigenic CORBA, IDL files etc. Then I had chance to upgrade the application Java 1.2.

Later, I got a chance to work on a J2EE application against Oracle. This ran in Apache and JBOSS. Java bean, EJB, Servlets, JSP and the whole nine-yards of typical Java developer's field. Then I got contracts to work on PB and VB. I kept in touch with Java though.

Java as a reporting tool
Couple of years ago, I got back into Java when I worked on a Java reporting package based around BIRT Java API for working with Excel files. (I think it's owned by Actuate now). The Java program was part of the batch environment on UNIX. We used it to generate Excel reports (with multiple worksheets) to be sent to users via e-mail. This was a challenging project.

Java Stored Procedures
Recently I got a change to work on yet another Java flavor. As mentioned elsewhere, where  I work currently, the environment is mainly in Powerbuilder and Oracle. Every now and then we find some surprises (in the last 18 months I've learned to expect there). There are a lot of Java packages/programs built around the PB application. These run on EA Server's Java container and use CORBA layer to talk to the PB application. The one I worked recently was slightly different. It was not the usual JDBC application, but a Java Stored procedure(s) that runs inside Oracle. I ended up in cleaning up the code, setting up a development environment around Eclipse and Ant and setting standards for other developers.

Java JSP, Coldfusion
We also have a lot of JSP pages that use CORBA to talk to our PB application through IIOP/CORBA. Recently we started building Coldfusion pages. While trying to help the developers to interface with our PB application, I realized that Coldfusion has been completely rewritten in Java. Coldfusion server was running on a Linux box. To get this to work with our Powerbuilder (EA) server, we needed a Visigenic product. (I eventually wrote a Java class that interfaces with the CORBA thus isolating Coldfusion from CORBA thus saving money for the company).

So many places, so many flavors. I've been having fun with Java. I hope to share some of these experiences here. In the coming posts, I will go into more detail about these Java experiences, hoping it would benefit some newcomers.

Saturday, July 30, 2011

Sybase to Oracle conversion

Background: Few years ago, I worked on a project where I had to convert close to 100 stored procedures from Sybase to Oracle. There are several differences between the 2 databases.

I was hired to be the Oracle expert in a team full of Sybase developers. I constantly worked with them to convert the databases and the code to Oracle. I worked on it from start to finish. I was asked to set up the environment, set the standards for Oracle coding and convert a lot of stored procedures. I participated in their migration project and eventually even supported their batch environment that ran in both Sybase and in the new Oracle environment. I even got a chance to work as a Oracle development DBA within the team. This gave me a lot of opportunity to learn about both the databases. I got to know their similarities (they both are SQLbased relational databases, aren't they?) and their differences (Did you know, Sybase database is the same as Oracle Schema and Oracle Database is the same as Sybase Server??).

There are a lot of sites out there discussing the differences and even migration from one to another. I benefited from a lot of those and I made a lot of notes at the time. When I was working on the project, I was so busy I couldn't post all those notes online. Now, that I have some time, I am dusting off my old notes and posting here. I hope it benefits someone doing similar kind of migration.

Sybase to Oracle Database conversion

This is a much bigger topic than I intend to cover in this post. My take on the conversion is mainly from a developer point of view. The DBAs will have a lot more to add about the database setup and architecture. Though, you will see references to database level differences, as I did perform the duties of a development DBA within the team. Below presentation lists out some of the differences while coding a stored procedure in the 2 databases. I took one of the procedures we migrated and annotated it with comments. I made this for the team at the time. I've changed the names of the tables and procedures to be more generic.

Note about the convention in the attached presentation: I wrote a complete Oracle Coding standards manual for the Sybase developers. I am following this convention in the attachment below. In general keywords are upper cased, other identifiers lower cased. The names of identifiers follow certain naming convention. I will post about this separately.

Sybase to oracle_conversion[slideshare id=8735258&w=668&h=714&sc=no]

View more documents from svaradar

Sunday, July 24, 2011

PB 101 - Contd... Project Workspace

As mentioned earlier, PB is geared up for developing applications for various platforms. We saw how the IDE is built around the Project Workspace, and the tools it provides to access every in the workspace. First let's define the various parts of a Powerbuilder based software development project.

PB 101 - Contd... Powerbuilder IDE

Powerbuilder IDE

PB IDE, just like any other windows application is an MDI application. It is made up of several windows and menus and toolbars. When you open the IDE, it opens up with no Workspace. You need to create/open a Workspace (using options in File menu) to work on a specific project. A  Workspace wraps around one or more Targets which in turn includes one or more Libraries. The libraries contain the objects. The objects themselves have properties and methods like in any Object oriented environment. The objects also contain events as Powerbuilder is an event driven programming environment.

Navigation through the project hierarchy is available through a tool called System Tree. The IDE also contains a full menu toolbar called Powerbar that lets you perform several tasks including creating various components in the project hierarchy. The IDE is made up of several individual tools and wizards, which are called Painters. There is a painter for each type of object and each task in PB.

System Tree & Powerbar

The System Tree is a project explorer. This let's you see the project hierarchy mentioned above, in a tree like fashion. The TreeView object fits this hierarchical structure perfectly. The topmost parent in the tree is the Project Workspace. You can drill down targets and libraries to object properties and methods here. You can expand or collapse at any level in the tree. You can open any existing objects in the System Tree (by double-clicking on them).

Powerbar is the Powerbuilder menu Toolbar containing buttons for important menu items.  You can create new objects by Clicking on the "New" button in Powerbar (or File->New option in the menu).

Clicking on New opens up a pop-up window that has several options (buttons) to open various wizards and tools in PB. You can create workspace, target, PB objects including windows, menus, datawindows from here. You can also open up several tools and wizards, such as Library explorer, Database wizard, File Editor, PB Profiler etc, by creating "New" instances of these. Every such wizard or tool in PB is called a Painter.

[caption id="attachment_139" align="aligncenter" width="1024" caption="Powerbuilder System Tree and Powerbar"][/caption]

Fig 1 - Powerbuilder System Tree & Powerbar
PB New Options - Here showing several types of PB Objects

Fig 2- New Option in Powerbar leads to "New" options Pop-up


As mentioned above Powerbuilder IDE is essentially made of several Painters. Each specific component type has it's own Painter (Object Editor) and several tasks have associated Painters (Tools or Wizards). So you will see Window Painter, Datawindow Painter, Database Painter, Library Painter etc. Each Painter is self-contained unit and has it's own menu (and toolbar), a control(s) list and properties sheet. As you can see, Powerbuilder IDE itself is designed in an object oriented way.

You open the individual component painters when you open an object in System Tree View or when Click on New on he menu bar.  Several task and tool painters can be opened in Powerbar.

Here are some of the Painters are available in PB:

  • Application Painter

  • Window Painter

  • Menu Painter

  • DataWindow Painter

  • Function Painter

  • User Object Painter

  • Database Painter

  • Library Painter

  • Project Painter (this is used to build applications - sort of compiler/linker options window; more about this later).

PB IDE - Other parts

Apart from these, the IDE UI has the following components:

An Output (child) Window which shows all the messages from the PB IDE environment, such as compiler outout, error messages etc.

A To-Do List Popup - Here you can enter a "todo" list. You can enter your own laundry list of things to be done in a project. Some of the wizards in PB also create To-Do entries automatically.

PB IDE includes a Browser window, which lists out all the objects in the project. This tool is very useful in locating objects in a large project with lot of libraries. As of version 10.5, it does not have a search capability yet, but you can navigate through libraries and types of objects to easily locate an object. Once found, you can "Edit" the object.

There are also several tools and wizards available in PB. Application Profiler, Debug Window, Database Painter, EA Server Profile etc. Each of these have special Toolbars attached to them.

See below link for more information (The link is for PB 11.0, but still applicable to any version > 8.x in general).

Saturday, July 23, 2011

PB 101 - Starting from scratch

the last few posts, we talked about PB a lot. My last post on Hello World gave an introduction to PB as a programming language. Now, it's about time we introduce Powerbuilder, the programming environment itself and how to use it to successfully develop a simple application.

PB as a n-tier tool

PB started as a simple client/server tool. We and PB are now in n-tier world. This means an application could be divided into multi-tiers,  such as a client and one or more application servers connecting to the database in the backend. Even the client can be multiple, as in you may have a PB application client in the office, a web page accessing the same servers etc. Dividing the application logic into logical blocks of client and server(s) is called application partitioning. Java J2EE is a great multi-tier development environment in 3GL. PB has grown into a n-tier tool in the 4GL world.  Sybase has come up with an Application Server called EA Server (originally called Jaguar in version 7). You can now use it to build not only windows applications, but middle tier applications (Application Servers) and Web Applications as well. To handle this new world, PB has completely changed it's own GUI as of version 7.


When you open PB (versions > 8.x), the first thing you will see is the workspace. A workspace comprises of several "Targets" each target representing a piece (partition) of the application that can be independently deployed. A target can be for different type of applications - a windows client application, a Jaguar component, a DLL, a JSP program, a .Net program etc.

To the target, you add the PB libraries, by adding it to the library list. A library list is not only for housekeeping of libraries in a target, but at run-time this serves as the search path for the application to find objects.

And to the libraries, you add objects such as Applications, windows, datawindows etc. When you create a windows client application, an application object will be automatically added to the library just created. Below images show the various parts of a workspace. We will start looking into each of these in detail in the coming posts.


Wednesday, June 22, 2011

Google Sites

At work we switched to Google mail some time back. We use Google docs to share docs. I started using Google sites just out of curiosity. Anyone that has a GMail account has automatic access to Google Docs and Google Sites. Google Sites is a simple web site builder. Google Site started as JotBot, a wiki tool that Google bought in 2006 and later developed into what it is today. See wiki article at for more info.

Creating a Site
Building a site using Google Site is very easy. Simply click on "Create New Site" button and select any site template available in the template gallery. Then select the Theme and visibility settings, voila... you have the new site up and running. Though, it can be used to build any web site, because of it's original wiki flavor, it's a great tool to create wiki pages. Google site also offers several gadgets that can be embedded thus you can include other web pages, graphics etc. Google Docs can easily be embedded as well.

Creating Web Pages
Once a site is created, pages can be added to it at any time. Google Site by default offers the following page templates:

Web Page
File Cabinets

Any of these pages can have embedded content. "Web page" template A simple web page. Each new posting to the site will be an additional page. "Announcements" template offers a Blog/wiki style post where each page will have multiple posts. So a wiki site may contain one page of type "Announcements" and multiple posts in it. It's like a blog, except the pages can be added/updated by anyone (See security settings) and thus it's like a wiki page. File Cabinets shows list of files uploaded. One can create other complex templates based on these.

Each page can have comments and attachments. (These options can be turned off for specific pages as well).

Managing the Site
Site settings can be changed at any time, by clicking on More Actions->Mange Site (Only owners can manage sites). This screen can be reached by clicking on "Edit Sidebar" link. Here several options are shown. Here several options are available. In Site Layout option, you can change the size and layout of the site. This also has a Navigation section. By adding pages (links) here, they can be displayed on the side bar. The theme used for the site can be changed at any time as well.

Security Settings
Google Site access is controlled by "Site Permissions". This can changed at any time. Typically permissions are,

1. Public - Any one can see it
2. Anyone with the link - If someone got hold of the link, they can see it
3. Private - Access only by invite; sign-in required to see the pages.

Google Site vs Wordpress
In many ways, Google Sites can be compared to blog pages. In both, you can create (web) pages quickly and use gadgets ( extensions) to add functionality. Both provide templates and themes to create web pages quickly. In general, Wordpress has a bigger developer community and is also a more matured product. It also has a lot more extensions available. Sites developed using Google Sites tend to be small personal sites. Since Google Sites had a wiki beginning it is stronger on collaboration of content. It allows each site to have multiple owners, contributors and viewers. This makes it more useful for development community to share information within a team.

My Google Sites
I created my first Google Site for our apartment community. It's a small community and I just created a page or 2 as a bulletin board. After that, I created and maintain a web page for our team to use at work. We create some developer documents which we used to dump on Network drive. The problem there was the files are not easily searchable and thus, after 100's files in the drive, we had to rely on individual's memory and notes. I tried to convert these to posts in the Google Site.

The current site I maintain at work has several pages, some with embedded Google Docs (converted Word, Powerpoint, Excel files). I also used a embedded widget to included an IFrame, so I can display an external web site. Since it's meant to be a collaborative site, I created several pages for various topics, each using "Announcement" template, so several posts can be made to the same page. For e.g., I have PB Wiki, for all posts related to our Powerbuilder development environment. Similarly a "Database Wiki" page for database related postings.

Now that we have site up and running, we need to back it up. I will discuss it in a separate post.

Monday, June 20, 2011

Pinging windows PC behind firewall

If your windows PC (XP, Vista) has firewall turned on, it may not respond to ping from other computers on the network. To allow pinging on this computer, try the following command:

netsh firewall set icmpsetting 8

To disable the same,

netsh firewall set icmpsetting 8 disable

Make sure you open command prompt as an Administrator.

Netsh is the command line utility available in windows command prompt (DOS box) to help with network configuration. See here for more on netsh.

The above settings can also be set in windows firewall settings GUI (screen) in control panel. Surprisingly it's a little more convoluted. See here

Dot... and Dotty

As a developer, one of the things I constantly do is documentation. This is not exactly a favorite task of mine, but I know it's a necessary evil. My programs are typically commented generously. Where necessary I create separate technical documents as well.

In order to make this mundane task a little more interesting, I always try to find tools to help with documentation. For e.g., I found Cppdoc, to document C++ programs, (similar to Javadoc), couple of years ago. While researching on this, I stumbled on Dotty. I found a great tool called GraphViz, but  I didn't do much with it at the time.

Recently when I had a need to create a flowchart for a work flow, again I looked for some tools. I could have used Visio and created some diagrams, but I wanted to automate the process of creating diagrams based on some (text) input. While researching, I re-discovered Dot and Dotty!

DOT and Dotty were originally developed by AT&T Bell Labs. DOT is a plain text graph description language. It's a definition language like CSS.  We can simply type up the information about a graph in human readable Text format, then feed it to a tool to generate a visual graph in various graphic formats. Dotty is a graph editor. AT&T actually bundled dotty and other tools as Graphviz. Graphviz is a collection of tools that interpret Dot structures and generate Graphs in various layouts.

In Dot language, Graphs can be defined as directed (DiGraph) or undirected (Graph). It also defines various elements in a graph such as nodes, arrows/lines or edges, labels etc. The syntax for the language includes various attributes of these elements as well. Thus by combining these elements and their attributes, one can build complex graphs/charts. Examples of such diagrams include, database schema diagrams, network nodes and connections, flow charts etc. The tools are capable of generating the diagrams in various graphic formats including JPG, PNG and SVG. A diagram is created in SVG format can be read by visio and other tools, thus sharing is easier.

The specification for DOT language is simple:

For e.g.,
graph {
Will generate

Each circle there is a node and the lines (with or without arrows) are called edges. Each of these have several attributes which can be used to control the presentation and to some extent actual layout.

Below is a simple directional graph (lines with arrows):


Following line creates a yellow colored oval node:

n[style=filled color="red" fillcolor="yellow"]

The same node as a rectangle:
n[shape=rectangle style=filled color="red" fillcolor="yellow"]

There are several other attributes that can be used to build really complex diagrams. See here for syntax. References listed at the bottom have more details.

The strength of Dot is that the language is small and text-based. Thus, we can write scripts or programs to generate such a text description of a graph. One such application would be to generate graphical calling tree for programs.

Dotty and other tools in Graphviz bundle all have similar command line syntax. A typical usage would be,


dot -O -Tpng <>

I found a great use for this tool, recently. At work, we have sort of a workflow system. The system keeps track of various types of Cases generated by the system or created by the users. Each case type is defined by it's own workflow. To track various steps  in the workflow, we used a Treeview structure in Powerbuilder.While I was doing some research on these flows, I had a need to document the flows in a flowchart. As always, I didn't want to just open Visio and draw a few. I wanted to script it, so it can be generated by text and better yet, automatically from the database with the information we have about the flows!

Since it's so easy to create graphs from the text files, we may even be able to build graphs dynamically, thus could represent state diagrams of running processes.

There are various resources available for DOT and dotty. Dotty is originally written in C. There is a Java API (Grappa) available that wraps around Dotty functions. Apart from Dot, there are also other languages available for representing graphs in text format.


Friday, March 11, 2011

PVCS - Source repository File Server

In PVCS, when we try to open the project database, it typically lists File Servers to select. The file server name listed there is not the actual server's name. Recently, we had to find out what server it was connecting to. This can be done in Admin->File Servers menu option in PVCS. You can add/delete/update server names here.

[caption id="attachment_4952" align="alignnone" width="300"]pvcs_server_list Where to add PVCS server list[/caption]

So, if the PVCS server is changed in the future and you need to connect to the new server, change it as mentioned above. This needs to be done on each developer machine or the ini file could be distributed. (The name of the ini file is, C:\Program Files\Merant\vm\common\bin\win32\servers.ini)

[caption id="attachment_4951" align="alignnone" width="300"]PVCS Servers Fig 2. server list is stored in servers.ini[/caption]

If you connect to PVCS from Powerbuilder (or any other MSSCCI compliant IDEs), you do not have to change anything there. PB actually reads the PVCS servers.ini file to get the PVCS Server name(s). After making the changes to File Server name in PVCS, we just need to disconnect and reconnect to merant version manager in Powerbuilder.

SQLPLUS - Spooling in HTML format

I used this recently and I thought of sharing this. If you don't know this already, SQL*PLUS has built in support for HTML. All you have to do is add markup option either on command line or inside the script. By the nature of it, it is only useful when you are spooling the output. Useful when you are dumping from multiple tables or generating simple reports. Just be aware that the output file will be larger than the usual text file spool.

Just add below to the script before spooling and the

HEAD "<TITLE>E-App Validation Report</TITLE> -
<STYLE type='text/css'> -
<!-- BODY {background: #FFFFFF} --> -
</STYLE>" -
BODY "TEXT='#000FF'" -

spool cleanup_eapp_data.html

SQLPlus - spool name with embedded timestamp

To be able to create a spool file, with filename embedded with timestamp:

— spool filename with timestamp
col sysdt noprint new_value sysdt_var
SELECT TO_CHAR(SYSDATE, ‘yyyymmdd_hh24miss’) sysdt FROM DUAL;
spool run_filename_&sysdt_var.Log

Update 12/07/12

This was confusing, as I had the same name for column name and the SQL variable created by new_value. I've renamed the two accordingly. This works.

Also, I posted about spooling to HTML some time back. You can combine the two, of course:

col spoolname new_value spoolname_var

select 'tablist_'||to_char(sysdate, 'yymmdd_hh24miss') || '.html' spoolname from dual;
prompt &spoolname_var

HEAD "<TITLE>Session Stats</TITLE> -
<STYLE type='text/css'> -
<!-- BODY {background: #FFFFFF} --> -
</STYLE>" -
BODY "TEXT='#000FF'" -

spool &spoolname_var

SELECT username, logon_time, status FROM v$session;
spool OFF;

creates a nice HTML file with a list of all users currently logged on to the database. This is saved in a HTML file with timestamp in it's name!

Thursday, March 10, 2011

TomCat + Sybase EA Server

Today another coworker of mine was trying to get XAMPP setup work with our EA Server. (We have PB application and Java objects deployed to this EA Server). He has a complete setup of XAMPP with Apache and Tomcat. He has a JSP page that uses the PB objects. This got to a point and crashed with "class not found" error. Clearly pointing to a classpath issue. But which one?

He mentioned that he had to set the EA Server first before he started XAMPP. Clearly there are some conflicts in the classes shared between the 2. EA Server is normally started using a batch file that reads the env classpath (windows settings in control panel) and adds its own classpaths locally. (SET LOCAL inside batch file). So whatever EA Server set is not visible to XAMPP but not the other way around. That's why he had to start EA Server first.

We then looked for the specific class that was "not found". This happened to a stub for the PB objects. In Jaguar manager (aka Sybase Central, EA Server admin) there is an option to generate stubs (and skeletons if needed). This typically points to SYBASEEASERVERHTMLCLASSES directory. We found out that he also similar classes in SYBASEEASERVERJAVACLASSES directory. We knew these were not used, as they were not in the class path. Then we searched for classes or jar files with similar names. We stumbled on a jar file that contains part of these stubs, inside XAMPP. (He uses OpenBD for coldfusion applications, which is why he has XAMPP in the first place). This jar was old and had part of the stubs classes and did not have the one we were having trouble with.

Apparently Tomcat was automatically loading this jar (Anything inside the lib directory gets loaded automatically). We found that when we tried to rename the file.

Now that we found out the classes in trouble, we thought we could just add the class paths needed to the environment classpath and hope Tomcat would pick it up. This did not happen. On the web, I saw a note about how Tomcat handles classpaths differently than regular Java. There was a suggestion to the classpath to the batch file that starts Tomcat. We didn't try that yet.

For now, we decided to copy the classes to the library folder, so Tomcat can automatically load the classes. So, we shut down XAMPP, regenerated the jar file (in EA Server stub generation, there is an option to generate a jar instead of class files) and copied to XAMPP lib folder and everything worked OK.

Wednesday, March 9, 2011

Linux: Wget, Yum

One of my co-workers was trying to install Oracle linux update to Fedora linux box. She was trying to do a wget command based on some web site instructions. Her wget failed and was not able to get the repository file at


Per suggestions on the web, we tried using host or nslookup for the website. They could not find the web site.

Where as, google site was always found. host and nslookup worked fine. But not to the oracle server. Not even showed up when we tried host, nslookup and ping commands.

After googling it up and trying to find solutions, we got to a point, where it pointed to some kind of network issue, probably proxy? I asked her to download the above file manually (she was always able to go to the web page directly in browser) and save to /etc/yum.repos.d directory. Then we tried,

yum list command.

This gave a different error:

IOError: <urlopen error (-2, 'Name or service not known')>

Some more web searches ( revealed this to be related to DNS. That was kind of strange. As we were able to ping and nslookup to and we were always able to go to the oracle site in browser.

Anyways, we decided to edit the /etc/resolve.conf file and add some public name servers to it.

nameserver <dns ip address>

This didn't help. Then she logged into a unix box and we looked at the /etc/resolve.conf there. It had only one name server. We copied to the file on Linux box. Bingo!! It worked. So, the problem was that the name servers she had listed in the resolve file didn't resolve oracle servers for some reason.

Yum, Wget etc

I really didn't know much about Yum and Wget until today. While working on this problem, learned a few things:

Wget is a cool utility to download files. Per the wikipedia page , wget (formerly geturl) is a non-interactive replacement for FTP, but it also works with other protocols like HTTP, HTTPS etc. It can download recursively.

Yum is a package manager/installer for Linux. See here for a definition:

Turns out the .repo file we were having problem with in the first place, is the repository that has some configurations to be used by Yum to download more files and install them.

Note: If you are editing any of the /etc/ files and you already have the console window open, chances the new settings will not be effective. Close all the console windows and start a new one.

Sunday, February 13, 2011

ubuntu & I

I've a long experience with the Unix world. When I first picked up Linux, it was all command line (I think it was Redhat). I kept hearing about it and I noticed that it's commands overlapped with Unix shells in many ways. I told myself, yeah I know Linux. This was more than 10 years ago.

Since then I've had chances to work on Linux command line versions several times when I worked on some web projects in PHP/MYSQL. Still the same impression. It's like Unix.

Few years ago, I was at Fry's. They had a PC for $125+. This was when a decent windows PC cost like $500 - $1000. I was thrilled. Picked up one and took it home. It had some flavor of Linux on it. To my surprise, it had a windowing version of Linux. I was thrilled. I took out my windows PC (I believe Win2K or XP) and plugged in the new PC. I connected to Ethernet, mouse, keyboard, speaker, monitor and even the printer. To my surprise Internet connected right away. So did the monitor, keyboard, mouse. After some fiddling I was able to get sound working also. I've worked with windows and experienced the typical windows problems like driver issues, software issues, hardware incompatibility issues before. Different versions of windows didn't work with the same hardware. Here I got a totally strange Operating System (Actually it was strange because the flavor of Linux I had was from Taiwan and it printed a lot of Chinese characters - that added to my surprise as well) and it worked like a charm. I was impressed. But, I didn't keep the PC for long, as there some other devices that failed with it.

Till recently It's been touch and go with Linux. Recently, my windows PC crashed. I must admit, I installed EASUS partitioning software and some registry compacter and these screwed up a lot of paths in the registry. I tried to restore from Restore points, alas it wouldn't. I tried to create one, it failed. I had to actually reinstall windows from the backup that comes with HP PC. This was frustrating experience. My PC had been slower and slower, which is what drove to touch the partition and registry in the first place. Now that I reinstalled windows, I was hoping my PC was going to perform better. To my disappointment, it really didn't.

My father was visiting me and in his spare he decided to build a PC. I had different old PC's hanging around. He got one working by replacing the motherboard with Intel Atom based board. It has a memory restriction of 1GB. We got an old Windows XP drive installed and was actually run Windows. (Not quite easily though - Windows seems to build configuration of the PC where it was first installed, so when I tried to use XP in a different PC, it failed to boot). I didn't remember the windows password. After looking around, we found a Linux based password extraction program!!! The entire operating system on a CD and we didn't even have to install it. It ran right out of the CD.

This is when we decided to give Ubuntu a shot. I eventually installed Ubuntu. The windowing interface it had, blew me away. I loved it. And as usual, the PC with Linux worked with all the devices right out of the box. Actually the Windows on the Atom PC did have problem with Audio and to my surprise, Audio worked with Ubuntu.

Then I dusted off an old 10GB drive and installed Ubuntu on it. Now, this is my main PC. Ever since, I am hooked to Ubuntu. Everyday I keep discovering new software to install and still have some more space to do other things. So far, this older PC with less memory and hard disk space than my 2-year old Windows Vista PC, the run for the money. Now, I don't use my Windows PC directly. When I want to I remote desktop from my Ubuntu PC.

I have plans to upgrade my Ubuntu PC. (I don't have the intention to get rid of Windows PC yet, as there are several software on it that I still use). But, with Wine installed, I am discovering new sides of Linux. I will write more about my experience with Linux here.