SlideShare une entreprise Scribd logo
1  sur  73
Télécharger pour lire hors ligne
PHP and IBM i – Database Alternatives



        Tony Cairns (adc@us.ibm.com)
        Software Developer
        IBM Systems & Technology Group, Systems Software




                                        8 Copyright IBM Corporation, 2009. All Rights Reserved.
                                        This publication may refer to products that are not currently
                                        available in your country. IBM makes no commitment to make
                                        available any products referred to herein.
Agenda
• Zend Toolkit
     – Native I/O Calls
     – Web Enabling DB2 with PHP using the i5 Toolkit
     –
• DB2 Language Extensions
     – SQL Compliant Syntax
•
• IBM DBi Storage Engine
•
•
•




                                                        © 2009 IBM Corporation
Web Enabling DB2 with PHP
     Native I/O Calls




                            © 2009 IBM Corporation
i5 Toolkit APIs

• Are shipped with Zend products
      – Zend core for i5/OS
      – Zend Platform for i5/OS
•
• Geared towards accessing i5 data and resources from PHP
      – Similar in purpose to the IBM Toolbox for Java
•
• Note: the I5_COMD job must be running in the ZEND subsystem
      – Use the Zend menu to start:
           • GO ZENDCORE/ZCMENU
           • Option 5 (Service Management menu)
           • Option 8 (Start I5_COMD service)




                                                                © 2009 IBM Corporation
PHP Toolkit for i5/OS – list of functions
 • Connection management         • Data retrieval         • Native file access
     –i5_connect                      – i5_fetch_array         – i5_open
     –i5_close                        – i5_fetch_assoc         – i5_addnew
     –i5_adopt_authority              – i5_fetch_object        – I5_edit
     –i5_error                        – i5_fetch_row           – I5_delete
     –i5_errno                                                 – i5_cancel_edit
                                      – i5_info
     –i5_errormsg                                              – i5_setvalue
                                      – i5_field_len
                           »                                   – i5_update
                                      – i5_field_name
 • Command calls                                               – i5_range_from
                                      – i5_field_scale
     –i5_command                                               – i5_range_to
                                      – i5_field_type
                           »                                   – i5_range_clear
 • Program calls                      – i5_list_fields
                                      – i5_num_fields          – i5_data_seek
      –i5_program_prepare
                                      – i5_result              – i5_seek
      –i5_program_prepare_PCML
                                                               – i5_bookmark
      –i5_program_call
      –i5_program_close                                        – i5_free_file
                                                               – i5_new_record
                                                               – i5_update_record
                                                               – i5_get_keys
                                                               –




                                                                                    © 2009 IBM Corporation
PHP Toolkit functions
• System values                  • Job logs                   • User space
     –i5_get_system_value             –i5_jobLog_list              –i5_userspace_crearte
                             »        –i5_jobLog_list_read         –i5_userspace_prepare
• Data areas                          –i5_jobLog_list_close        –i5_userspace_get
     –i5_data_area_prepare            –                            –i5_userspace_put
     –i5_data_area_receive       • Active jobs                     –
     –i5_data_area_send               –i5_job_list            • Data Queue
     –i5_data_area_close              –i5_job_list_read            –i5_dtaq_prepare
     –                                –i5_job_list_close           –i5_dtaq_recieve
• Print/Get spooled file              –                            –i5_dtaq_send
    –i5_spool_list               • Objects list                    –i5_dtaq-close
    –i5_spool_list_read               –i5_object_list              –
    –i5_spool_list_close              –i5_object_list_read
    –i5_spool_get_data                –i5_object_list_close
    –i5_spool_from_file               –
    –
    –
•
    –




                                                                                           © 2009 IBM Corporation
PHP Toolkit for i5/OS – list of functions
• Connection management         • Data retrieval         • Native file access
    –i5_connect                      – i5_fetch_array         – i5_open
    –i5_close                        – i5_fetch_assoc         – i5_addnew
    –i5_adopt_authority              – i5_fetch_object        – I5_edit
    –i5_error                        – i5_fetch_row           – I5_delete
    –i5_errno                                                 – i5_cancel_edit
                                     – i5_info
    –i5_errormsg                                              – i5_setvalue
                                     – i5_field_len
                          »                                   – i5_update
                                     – i5_field_name
• Command calls                                               – i5_range_from
                                     – i5_field_scale
    –i5_command                                               – i5_range_to
                                     – i5_field_type
                          »                                   – i5_range_clear
• Program calls                      – i5_list_fields
                                     – i5_num_fields          – i5_data_seek
     –i5_program_prepare
                                     – i5_result              – i5_seek
     –i5_program_prepare_PCML
                                                              – i5_bookmark
     –i5_program_call
     –i5_program_close                                        – i5_free_file
                                                              – i5_new_record
                                                              – i5_update_record
                                                              – i5_get_keys
                                                              –




                                                                                   © 2009 IBM Corporation
PHP Toolkit functions
• System values                  • Job logs                   • User space
     –i5_get_system_value             –i5_jobLog_list              –i5_userspace_crearte
                             »        –i5_jobLog_list_read         –i5_userspace_prepare
• Data areas                          –i5_jobLog_list_close        –i5_userspace_get
     –i5_data_area_prepare            –                            –i5_userspace_put
     –i5_data_area_receive       • Active jobs                     –
     –i5_data_area_send               –i5_job_list            • Data Queue
     –i5_data_area_close              –i5_job_list_read            –i5_dtaq_prepare
     –                                –i5_job_list_close           –i5_dtaq_recieve
• Print/Get spooled file              –                            –i5_dtaq_send
     –i5_spool_list              • Objects list                    –i5_dtaq-close
     –i5_spool_list_read              –i5_object_list              –
     –i5_spool_list_close             –i5_object_list_read
     –i5_spool_get_data               –i5_object_list_close
     –i5_spool_from_file              –
     –
     –
•
     –




                                                                                           © 2009 IBM Corporation
Record-level access

• i5_open
    – Pass in library/file and type of "open" mode
            • I5_OPEN_READ, IT_OPEN_READWRITE, I5_OPEN_COMMIT, I5_OPENSHRRD,
                 I5_OPEN_SHRUPD

            •
•
• i5_list_fields
    – Get an array of the names of the fields in the file
    –
• i5_fetch_row
    – Get an array of the fields in a record, passing in which record to fetch
            •   I5_READ_SEEK (current)
            •   I5_READ_NEXT (default)
            •   I5_READ_PREV
            •   I5_READ_FIRST
            •   I5_READ_LAST

                                                                                 © 2009 IBM Corporation
Record-level access: HTML code

• <form method="post" action=“i5_fetch.php">
•
• Library (schema): <input type="text" name="lib"><br>
•
• File (table): <input type="text" name="tbl"><br>
•
• <input type="reset"> <input type="submit"
   value="Display records">
•
• </form>




                                                     © 2009 IBM Corporation
Record-level access: PHP code
• // Get input data
• $dbfile = $_POST['tbl'];
• $dblib = $_POST['lib'];
•
• // Open the file
• $file = i5_open("$dblib/$dbfile", I5_OPEN_READ);
• if (!$file)
• die("Error while attempting to open file mode=READ");
•
• // Print a table row of field names
• $fields = i5_list_fields($file); // returns an array
• print "<table border=1 cellpadding=3><tr bgcolor=#CCCCCC>";
•
• // Parse the array
• foreach ($fields as $value) {
• print "<th>$value</th>";
•}
• print "</tr>n";

                                                          © 2009 IBM Corporation
Record-level access: PHP code

• // Print records
• $rec = i5_fetch_row($file, I5_READ_FIRST); // get array
• while ($rec) {      // while there is a record...
• // Print each field in the array
• foreach ($rec as $value) {
•        print “<td>$value</td>";
• }
• print "</tr>n";
• // Get next record
• $rec = i5_fetch_row($file, I5_READ_NEXT);
• } // loop
•
• print "</table>";   // finish off the table




                                                            © 2009 IBM Corporation
Record-level access: PHP code
Example Form and Output




                                © 2009 IBM Corporation
Web Enabling DB2 with PHP
   Using the i5 Toolkit




                            © 2009 IBM Corporation
PHP Get Records (i5 toolkit SQL model)

• Recall from our previous             function model_connect()

   discussion that before any of the   { global $MODEL;
                                         $db_options = array(I5_OPTIONS_JOBNAME=>"DVDSEARCH");
   APIs from the i5 toolkit can be       $MODEL ['conn'] = i5_pconnect
                                         ( $MODEL ['database'],
   used we must establish a                 $MODEL ['db_user'],
                                            $MODEL ['db_password'],
   connection to the i5 toolkit          );
                                            $db_options

   daemon process (PGM-                  if (! $MODEL ['conn'])
                                         { model_error_i5("Connect fail");
   EASYCOM job)                             return False;
                                         }
• The i5_pconnect() API is being       return model_chglibl();


   used to establish a persistent      NOTE: Each browser button click will
   connection and avoid costly IBM i   run back through the i5_pconnect()
   startup and termination             code; however since the connection is
   associated in the                   cached for each Apache worker job the
   i5_connect() and                    actual processing time is minimal.
   i5_close() APIs.
• Establish a connection is a fairly   NOTE: In this case each button click will
   common activity so a site           not find it’s way back to the same worker
   common include comes in handy       job (more on this later).
                                                                                     © 2009 IBM Corporation
i5 toolkit browser clicks route to different Apache
jobs
• Persistent i5 connections means
   that each “stateless” Apache job
   will have a common connection
   to the i5 toolkit
     – This common connection will be
         used by all PHP i5 toolkit
         applications running, not a
         single application or job
• Persistent connections does not
   mean that each browser click will
   return to the same job
     – Do not attempt commit
         transactions across multiple
         browser clicks!!




                                                © 2009 IBM Corporation
i5_pconnect() results in many PGM-EASYCOM jobs

• Supplying a user-id and
   password to the i5_pconnect()
   call will spawn multiple PGM-
   EASYCOM server processes
   for each different user profile
   specified
     – This behavior is dependent on
         the Apache user peak
         demand
•
• It is good practice to limit the
    number of “active” web profiles.
•
• A “private” connection can be
    used for traditional “state full”
    RPG applications (more on this
    later)

                                          © 2009 IBM Corporation
i5_prepare() and i5_execute()
• The i5 toolkit API combination of i5_prepare() and i5_execute() allows
   the DB2 engine to optimize and reuse SQL statements many times.
     – This can provide improved performance on high volume web sites that
         have many DB2 calls.
•
• The i5 toolkit SQL code shown on the following pages replaces the
   RPG back-end.
•
• The same function names are used in the i5 toolkit SQL MVC as the i5
   toolkit model function names (model_search)
     – When we replace the model code with i5 toolkit SQL the view and control
        code do not have to change (the 5250 code can also remain the
        same)
                                   Reminder: The i5 toolkit model used
                                   APIs to call the RPG model.




                                                                             © 2009 IBM Corporation
i5 Toolkit SQL
code example (slide 1 of x)
<?php
function model_search($browsetype, $browse_title, $browse_actor, $browse_category, $limit_num, &$items)
{ global $MODEL, $categories;
  if (!model_connect ())
  { False;
  }
  $items = array ( );
  $prepare = null;
  $sql = "";
  switch($browsetype)
  { case 'title':
       $sql .= "select * from products";
       $sql .= " where TITLE like '%$browse_title%'";
       $sql .= " FETCH FIRST $limit_num ROWS ONLY";
       $prepare = i5_prepare($sql);
       break;
    case 'actor':
       $sql .= "select * from products";
       $sql .= " where ACTOR like '%$browse_actor%'";
       $sql .= " FETCH FIRST $limit_num ROWS ONLY";
       $prepare = i5_prepare($sql);
       break;
    case 'category':
       // oops did not plan ahead for this
       for ($i=1;$i<=count($categories);$i++)
       { if ($categories[$i-1]==$browse_category)
         { break;
         }
       }




                                                                                                      © 2009 IBM Corporation
i5 Toolkit SQL code example (slide 2 of x)
        $sql .= "select * from products";
        $sql .= " where CATEGORY = $i";
        $sql .= " FETCH FIRST $limit_num ROWS ONLY";
        $prepare = i5_prepare($sql);
        break;
      default:
        break;
    }
    if (!$prepare)
    { model_error_i5("prepare $sql");
      return False;
    }
    // execute prepared statement
    $execute = i5_execute($prepare);
    if (!$execute)
    { model_error_i5("execute");
      return False;
    }
    // fetch the row data
    $i=0;
    while ($row = i5_fetch_assoc($prepare, I5_READ_NEXT))
    { array_push
        ( $items,
           array
           ( $row["PROD_ID"],
             $row["TITLE"],
             $row["ACTOR"],
             $row["PRICE"]
           )
        );
        $i++;
    }
    if (!$i)
    { model_error_i5 ( "No DVDs found" );
      return False;
    }
    return True;
}

                                                            © 2009 IBM Corporation
DB2 for i5/OS Language Extensions




                                    © 2009 IBM Corporation
Accessing DB2/400
 • Server/connection                                        • Errors
       – db2_bind_param
                           • Fetch
                                  – db2_fetch_array            – db2_conn_error
       – db2_client_info
                                  – db2_fetch_assoc            – db2_conn_errormsg
       – db2_close                – db2_fetch_both             – db2_stmt_error
       – db2_connect              – db2_fetch_object           – db2_stmt_errormsg
       – db2_cursor_type          – db2_fetch_row        • Column/Procedure
       – db2_exec          • Field information                 – db2_column_privileges
        db2_execute              – db2_field_display_
                                        size                   – db2_columns
        db2_prepare
                                  – db2_field_name             – db2_procedure_column
       – db2_pconnect                                               s
       – db2_server_info          – db2_field_num
                                                               – db2_procedures
       – db2_statistics           – db2_field_precision
                                                               – db2_special_columns
                                  – db2_field_scale
 • Result                                                • Table information
                                  – db2_field_type
       – db2_free_result                                       – db2_num_fields
                                  – db2_field_width
       – db2_next_result   • Key information                   – db2_num_rows
       – db2_result               – db2_foreign_keys           – db2_table_privileges
 • Commit/Rollback                – db2_primary_keys           – db2_tables
       – db2_autocommit    • Statement
       – db2_commit             – db2_free_stmt documented online at php.net
                                   All of these APIs are
       – db2_rollback           - Preferred db2_ SQL statement execution APIs (performance)
                               • - Non-preferred db2_ SQL statement execution API




                                                                                         © 2009 IBM Corporation
Server connection – db2_connect API

• There are few connections options in PHP
    – db2_connect(“”,””,””) – local server
            • The database connection job is create under the same user as the Apache httpd
                server profile NOBODY (*USER).
            •
    – db2_connect(“*LOCAL", “SAM", “PASSWROD")– local server
            • The database connection job is created under the user profile SAM/PASSWROD.
            •
    – db2_connect(“10.1.1.33", “SAM", “PASSWROD") – remote database
        connection job is created under the user server
            • The profile SAM/PASSWROD.
    –
    –




        ●   Tip: Use WRKRDBDIRE to get the database name on the remote


                                                                                              © 2009 IBM Corporation
i5 Toolkit vs. DB2 Extensions


• The i5 Toolkit represents functions for accessing native i5/OS resources
     – The toolkit is specific to the Zend products for i5/OS
     – The toolkit functions can only access local resources (i.e., resources on the same
          i5/OS partition as Zend Core) unless you setup remote connections (such as
          DDM) outside of the Zend Core environment.
     – All i5 Toolkit functions begin with the characters “i5_”
• The DB2 functions represent an extension to the PHP language
     – The DB2 extensions are starting to become part of the mainstream php language
          offering and will be available on platforms other the i5/OS
     – The DB2 extensions can access both local and remote DB2 resources through
          established network connections
     – All DB2 functions begin with the characters “db2_”




                                                                                       © 2009 IBM Corporation
i5 Toolkit vs. DB2 Extensions

                           i5_*
                         I5_COMD

     Zend Core                    I5/OS
                              Resource
       PASE               db2_*

                 i5/OS

                          TCP/IP    db2_*


                                  I5/OS
                              Resource



                                  i5/OS

                                            © 2009 IBM Corporation
Anatomy of a Web-Enabled DB2 for i5/OS (via
             PHP) application




                                          © 2009 IBM Corporation
Application Description


• We will take a look at a single PHP application.
•
• The application will create an employee information search and retrieval
   form
•
• All of the following code would be saved in a single file (ex:
   employee_update.php)
     – As you will see, we can use HTML form fields to allow the application to re-
         call itself for different functions within the application.




                                                                              © 2009 IBM Corporation
Employee Search Form


• When invoked (through a Web Browser) this code will generate a
   blank web page with a title bar of “Employee Update –
   employee_update.php”)

                  <html>
                  <?php
                  define("PAGE_TITLE", "Employee Update");
                  define("PHP_FILE_NAME", "employee_update.php")
                  ?>
                  <title><?= PAGE_TITLE . " - " . PHP_FILE_NAME; ?></title>
                  <body>
                  <?php
                  ?>
                  </body>
                  </html>




                                                                              © 2009 IBM Corporation
Employee Search Form


• The following block of code will cause the
   Employee Search form to be displayed.
    This block of code is inserted between
   the <?php tag and the corresponding ?
   > tag



                          /* Display the initial Employee Search form and the $_POST lastname field */
                          /* When the Search button is selected.                                   */
                          print '<h1>Enter the first few characters of the employee records you' .
                                'wish to view/edit in the last name field.</h1>';
                          print '<h2>For example, "JO":</h2>';
                          print '<form action="employee_update.php" method="POST">';
                          print 'Last Name: <input type="text" name="lastname" /> <br />';
                          print '<input type="submit" name="action" value="Search" />';
                          print <'/form>';


• This block of code outputs in the HTML stream instructs for what is to be entered
   into the form along with the HTML to generate the form information.




                                                                                               © 2009 IBM Corporation
Search Action

• The following block of code implements the search function
      – This function uses the values entered on the Employee Search Form to open the database and
           perform the search action
      – This block of code would be inserted between the <?php tag and the /* Display comment

                                     /* Include System i connection information */
                             1       if ((include "../i5_properties.php") == false) {
                                        echo("Problem encountered loading i5_properties.php include file");
                                     }

                                     /* Set up connection for each pass through this application */
                             2       $dbh = db2_connect($db, $user, $password);
                                     if (!$dbh) :
                             3       die ("Problems connecting to the database.");
                                     endif;


1. The if statement causes a separate PHP file that contains a number of
      variable definitions for the program (including database name, user
      credential information) to be included
2. The db2_connect() call is one of the calls from the DB2 for i5/OS
      extensions – it establishes a connection to the database server and
      returns a handle to the indicated database.
3. The if statement tests that a valid handle to the database was returned
      and if not the application terminates (via the die language construct)

                                                                                                      © 2009 IBM Corporation
Database Query
• The next section of code performs the database query and places
   the results on the Employee Search Results form.
     – The code would be placed between the </form> line and the
         second ?> php end} tag
                             else
                              {
                                  /* Query file using input from form and deliver results to client */
                                  /* Construct SQL statement, using lastname as the search substring */
                      1           $sql = 'select * from i5schema.employee where lastname like ''
                                         .$_POST["lastname"]. '%'';
                                  /* Execute the DB2 SQL statement, place results into $stmt */
                      2           $stmt = db2_exec($dbh, $sql, array('cursor' => DB2_SCROLLABLE));
                                  /* Print Employee Search Results header and table setup */
                                  print '<h1>Employee Records On System i starting with ' .
                                         $_POST["lastname"]. '</h1>';
                      3           print '<br><table border=1 cellpadding=5 cellspacing=5>';
                                  /* Iterate through result set, printing one table line per */
                                  /* record returned. Note that the customerNumber field will */
                                  /* be an "active field" which will $_GET the customerNumber */
                                  /* and reinvoke the employee_update.php application*/

1. This statement assigns an SQL select statement to the $sql variable. The SQL
       statement includes values entered on the Employee Search from
2. The db2_exec() statement causes the SQL statement to be executed against the
       database that was opened previously with the db2_connect() function call.
3. The print statements will output in the HTML stream for the first part of the
       search results form which includes a header line indicating what records are
       being displayed as well as a table that will contain the returned records
                                                                                               © 2009 IBM Corporation
Database                     4
                                            while ($row = db2_fetch_array($stmt))
                                            {
                                              if (!$row=="") {
Query                                           $customerNumber = $row[0];
                                                $customerName = $row[3];
                                                $customerFirst = $row[1];
                                                $workdept = $row[4];
                                                $job = $row[7];
                                                $salary = $row[11];
                                                print '<tr><td align=center><a
                             5                         href=employee_update.php?customerNumber=' .
                                                       $customerNumber . '>' . $customerNumber .
                                                       '</a><td>'. $customerName.'<td>'.
                                                       $customerFirst.'<td>'. $workdept.'<td>'.
                                                       $job.'<td>'. $salary.'</td></tr>';
                                            }
                                           }
                                           /* Close table */
                                           print '</table><br>';
                             6             /* Print the DB2 SQL statement which was executed - Informational */
                                           print "<p>Echo of dynamically-built sql: ".$sql."</p>";
                                       }

 4.   The while loop iterates through the records returned by the db2_exec() function call (as
          represented by the $stmt variable) and outputs them into the HTML table.
        •   Each time through the loop the db2_fetch_array() function will return an array of values
               representing a row in the result set
 5.   Notice that the employee number ($customerNumber) is output as an actionable tag (i.e., it
          will be a selectable link on the HTML page)
        •   This is accomplished through the ‘href’ statement which causes this php application to be re-called
                when the tag is selected
 6.   The HTML tag to close the HTML table is output along with outputting the SQL statement
          that was executed.

                                                                                                         © 2009 IBM Corporation
Database Query Notes
• Using the db2_exec() statement to insert PHP variable values into an
   SQL statement can be considered a security exposure.
     – Rather then using the db2_exec() statement the db2_prepare() statement
         could be used to prepare an SQL statement with parameter markers for
         input values.
     – Then the db2_execute() statement could be used rather then the
         db2_exec() statement to pass in the input values.
• When the PHP file is invoked (from a Web Browser) the search form is
   displayed. Entering data into the Last Name field and selecting the
   [Search] button causes the Query code to be executed and the results
   to be displayed in the web form




                                                                          © 2009 IBM Corporation
Database Query – Employee Record Edit


• This code will determine which record to place on the Edit form. The code
   is invoked when an employee number is selected from the search results
   window
     – This code would be placed between the $action = $_POST[“action”]
         and the if(!$action) lines

                            1       $customerNumber = $_GET["customerNumber"];2
                                       /* Determine which form to display based upon the state of the
                                          application */
                            2      if ($customerNumber == "") {




1. The employee number selected on the search results form is retrieved from the
      URL and assigned to the $customerNumber variable
2. The test of the $customerNumber field is performed to see if this block of code
      was reached after an employee number was selected from the search results
      window.
     •   Remember that this PHP code generates both the Employee Search as well as the
           Employee Search Results pages

                                                                                             © 2009 IBM Corporation
Edit Record
• The following code displays the values of a record and allows the user to edit
   those values
     – This code would be placed between the bracket (}) line and the second ?> php tag
                             } else { /* Edit the selected customer number record */
                               /* Construct SQL statement, using customerNumber to select the record */
                               $sql = 'select * from i5schema.employee where empno = ''
                     1                .$_GET["customerNumber"]. ''';
                               /* Execute the DB2 SQL statement, place results into $stmt */
                     2         $stmt = db2_exec($dbh, $sql, array('cursor' => DB2_SCROLLABLE));
                               /* Place the field information returned into array $row */
                               $row = db2_fetch_array($stmt);
                     3         /* If there is data, move information from result array into fields */
                               /* which will be used for screen display                            */
                               if (!$row=="") {
                                  $customerNumber = $row[0];
                     4            $customerName = $row[3];
                                  $customerFirst = $row[1];
                                  $workdept = $row[4];
                                  $job = $row[7];
                                  $salary = $row[11];
                               }

1. The select statement establishes the query using the customer number that the
      user selected from the HTML form
2. The db2_exec() function call executes the query
3. The db2_fetch_array() function call retrieves an array of fields values from a
      record in the return set from the db2_exec() function call
4. The if statement tests to ensure that a record was returned from the
      db2_fetch_array() call. If a record was returned, local variables are used to
      tore the field values.
                                                                                                © 2009 IBM Corporation
Edit Record
                   /* Display the Employee Edit form and $_GET all fields */
                     /* when Update button is selected.                      */
                     print '<h1>Edit an Employee record:</h1>';
                     print '<form action="employee_update.php" method="GET">';
                     print 'Customer Number: <input type="text" name="customerNumber"
                            value="'.$customerNumber.'" /> <br />';
                     print 'Last Name: <input type="text" name="customerName"
                            value="'.$customerName.'" /> <br />';
                     print 'First Name: <input type="text" name="customerFirst"
                            value="'.$customerFirst.'"/> <br />';
            5        print 'Work Department: <input type="text" name="workdept"
                            value="'.$workdept.'"/>';
                     print 'Job Type: <input type="text" name="job" value="'.$job.'"/> <br />';
                     print 'Salary: <input type="text" name="salary" value="'.$salary.'"/> <br
                            />';
                     print '<input type="submit" name="action" value="Update" /><a
                           href=employee_update.php?customerNumber=' .
                     $customerNumber .'&customerName='
                           .$customerName.'&customerFirst='.$customerFirst.'&workdept='.
                     $workdept.'&job='.$job.'&salary='.$salary.'&action='.$submit.'></a>';
                     print '</form>';
                   }

5. A series of print statements are used to output text labels as well as the contents
      of the returned record




                                                                                                  © 2009 IBM Corporation
Edit Record – Notes

• The print ‘<form’> along with the print ‘<input> line (italicized in the code
   for emphasis) will cause the program to place the “Update” action onto
   the URL line when the Update button is selected
     – Each of the field values are also placed into the URL string.
     – This causes the application to “call” itself again.
     – The URL string values are retrieved using the $_GET function and then
         used in the SQL update call (coming up)
• When the PHP program is invoked and a record is searched for and
   selected the Results form will display the Employee Edit Record form
   with the values from the selected record




                                                                            © 2009 IBM Corporation
Record Update

• This code is invoked when the [Update] button is selected from the Edit
   page.
• The code updates the DB2 for i5/OS table using the user modified fields.
• Once the record has been updated an HTML form is output with the SQL
   statement along with an indication of whether or not the update was
   successful.
• The first section of code determines the state of the application, that is has
   the user selected the [Update] button from the Edit page.
• The following code would replace the $action = $_POST[“action”] line of
   code which is just prior to the $customerNumber =
   $_GET[“customerNumber’] statement

                                  $action = $_GET["action"];
                                  /* if no value from $_GET, try $_POST */
                                  if (!$action) {
                                       $action = $_POST["action"];
                                  }




                                                                             © 2009 IBM Corporation
Record Update


• The section of code that resembles the following

            }
           } else { /* Edit the selected customer number record */




• Would be changed to resemble the following (i.e., remove the beginning
   close bracket on the else statement
             }
            else { /* Edit the selected customer number record */




                                                                     © 2009 IBM Corporation
Record Update

• The following block of code would be placed before the else { /* Edit
   the selected customer number record */ statement
                    1       } else { /* Update Employee table, or edit the selected customer record */
                               if ($action == "Update") {
                                  $sql = 'update i5schema.employee set lastname = 2
                                  ''.$_GET["customerName"].'','.
                                  ' firstnme = ''.$_GET["customerFirst"].'','.
                    2             ' workdept = ''.$_GET["workdept"].'','.
                                  ' job = ''.$_GET["job"].'','.3
                                  ' salary = '.$_GET["salary"].
                                  ' where empno = '' .$customerNumber.''';4
                    3             $result = db2_exec($dbh, $sql);




1. The if statement determines whether or not the user requested the Update action
      from the Edit Record form.
     •   If the update action was invoked then the remaining code is executed
2. The assignment statement generates the SQL update statement using values
      passed from the calling form
3. The db2_exec() function call executes the SQL update statement returning the
      results into the $result variable.


                                                                                                 © 2009 IBM Corporation
Record Update

                                  if (!$result) {
                    4                echo '<p>There was a problem inserting the user into the
                                           database.</p>';
                                     print '<p>Echo of dynamically-built sql:<br>'.$sql.'</p>';5
                                  } else {
                                     print '<h1>Update Successful</h1>';5
                    5                print '<form action="employee_update.php" method="POST">';
                                     print '<p>Echo of dynamically-built sql:<br>'.$sql.'</p>';
                                     print '<input type="submit" name="action" value="Continue" /><a
                                           href=employee_update.php></a>';
                                     print '</form>';
                                  }
                              }

4. The if statement tests the result returned from the db2_exec() call.
     •   If an error was encountered by the update statement an error message is output to the
              HTML form.
     •   If the update statement was successful a success message is output along with the
              actual SQL statement that was executed by the db2_exec() function call.
5. The <form> line along with the <input> line causes the program to place the
      “Continue” action onto the URL string when the [Continue] button is selected.
      The program will then be automatically re-invoked.

• In addition to the above code a line containing a closing bracket (}) would need to
    be placed before the second ?> php end tag at the bottom of the application


                                                                                                 © 2009 IBM Corporation
Record Update – Notes Page


• Now when the application is invoked, searching for a set of records,
   selecting a record from the returned set, edit the values and selecting
   the [Update] button will cause the record to be updated and a web page
   similar to the following to be displayed




                                                                      © 2009 IBM Corporation
Continue

• The final piece of code that we need to look at is the code that processes
    selection of the [Continue] button.
• When the [Continue] button is selected, the application will be reset back
    to the Employee Search form
• In the following section of code:
           /* Determine which form to display based upon the state of application */
           if ($customerNumber == "") {
           if (!$action) {


• The if (!$action) line would be replaced with the following
           if ((!$action) or ($action == "Continue")) {



• The check for the “Continue” action will cause the Search form to be
   displayed when the [Continue] button is selected from the Update
   database form.




                                                                                       © 2009 IBM Corporation
Why MVC
• Recall that we are using the MVC model to separate out the model, view,
   and controller components of the program
     – This makes it easy to replace one component without affecting the others.
• In this example, the same PHP view/control code as in the previous i5 call
    RPG program example is used.
     – Now a different include_once for the MVC ibm_db2 model is used

    <?php
    define('APP_BASE_DIR', '/www/zendcore/htdocs/qiwikiCode/');
    include_once APP_BASE_DIR.'101Config.php';      /* Site: site configuration */
    include_once APP_BASE_DIR.'101Connectdb2.php'; /* Model: ibm_db2 connect */
    include_once APP_BASE_DIR.'101DB2Model.php';    /* Model: ibm_db2 SQL */
    include_once APP_BASE_DIR.'101HtmlView.php';    /* View: plain old generated html */
    include_once APP_BASE_DIR.'101HtmlControl.php'; /* Control: main loop html forms */
    ?>



• In this example, the RPG back-end (the model) is being replaced with PHP
    ibm_db2
     – A new DB2 model include_once is used to handle the connections to
         ibm_db2


                                                                                           © 2009 IBM Corporation
PHP Get Records (ibm_db2 model)
Establish a connection
• In an earlier module we looked at the i5_toolkit calls which uses the
    i5_connect (and i5_pconnect) to connect to the PGM-EASYCOM jobs.
     – A daemon approach is used that monitors a socket for in-coming
         connections that are started by the Zend core menus and subsystems.
•
• The ibm_db2 language extension uses in-line calling and/or special
   memory shipping if going between QSQ server jobs and Apache worker
   jobs.
•
• Simply put, different connection methods are used by these two facilities
   as then come from different sources. The ibm_db2 language extension
   comes from IBM while the i5_toolkit calls comes from another PHP
   vendor.




                                                                          © 2009 IBM Corporation
PHP Get Records (ibm_db2 model)
Using db2_pconnect vs db2_connect
• The db2_pconnect() API supports           function model_connect()
                                            { global $MODEL;
   persistent connections                     $db_options =     array("i5_naming"=>DB2_I5_NAMING_ON);
                                              $MODEL ['conn'] = db2_pconnect
• The db2_connect() API does not              ( $MODEL ['database'],
                                                 $MODEL ['db_user'],
   support persistent connections                $MODEL ['db_password'],
                                                 $db_options
• Use of the db2_pconnect() API               );
                                              if (! $MODEL ['conn'])
   can improve performance of web-            { model_error_db2("Connect failed");
   sites when they are experiencing           }
                                                 return False;

   heavy user web demand                      return model_chglibl();
                                            }


• Notice the array(“i5_naming”=>DB2_I5_NAMING_ON)
     – libl changes via db2_set_option(“i5_libl”=>) will work with unqualified table
           names
     – It is likely that for stored procedure calls to an RPG program, the RPG program would
           also likely need the library list set.Use of persistent connections is absolutely
                                           required if the site services multiple requests
Tip! Setting DB2_I5_NAMING_ON
                                           per second.
for the whole set simply works better
on IBM i. Mixing
DB2_I5_NAMING_ON/OFF almost                The php.ini configuration file can be updated
always leads to problems on the site       to force all db2_connect() calls to use
                                           db2_pconnect() instead
                                                                                              © 2009 IBM Corporation
db2_connect() browser clicks route to different
Apache jobs
• The use of a persistent db2
   connection only means that each
   “stateless” Apache job will have
   a common connection to db2
     – This common connection will be
         used by all PHP ibm_db2
         applications running, not just
         a single application or “job”.
•
• Persistent connections does not
   mean that each browser click will
   return to the same job
                                          Tip! Do NOT attempt
                                          db2_commit() transactions across
                                          multiple browser clicks!!!



                                                                             © 2009 IBM Corporation
db2_pconnect() results in many QSQ jobs

• Supplying a user-id and password
   to the db2_pconnect() call will
   spawn a QSQ db2 server
   process for each different user
   profile specified for each Apache
   worker job

 Tip! It is a good practice to limit the
 number of “active” web profiles.


 NOTE: Once the QSQ job has been           Tip! Mixing db2_pconnect(“”,””,””)
 spawned there will be little              and db2_pconnect(“*LOCAL”,”uid”,”pwd”)
 difference between profiles               can lead to unpredictable results – pick a
 (assuming that db2_pconnect()             profile or two to handle web clients and
 was used)                                 stick with those.

                                                                               © 2009 IBM Corporation
What about db2_connect?


• Recall from the previous slide that a QSQ db2 server process is spawned
   for each different user profile for each Apache worker job
     – In the case of db2_connect, the QSQ job starts and stops with each
          db2_connect() and db2_close() call.
     – This is a performance degredation over the use of the persistent
          db2_pconnect() call – especially for those sites with heave traffic and
          many requests per second.
•




                                                                                © 2009 IBM Corporation
db2_prepare() and db2_execute() vs. db2_exec()

• Use of the db2_prepare() and db2_execute() API combination
   allows the DB2 engine to optimize and reuse SQL statements
   many times.
    – Use of these APIs on high volume web sites can provide a
        performance boast over db2_exec()
•
• The ibm_db2 code shown on the next slides replaces the RPG
   back end.
    – The MVC model function names have been kept exactly the same
        as the i5 toolkit model function names (model_search)
    – When the model code is replaced with ibm_db2 the view and
        control model do not change (the 5250 code will also remain the
        same)




                                                                          © 2009 IBM Corporation
db2_prepare() and db2_execute()
Code Sample (slide 1 of 2)
<?php
function model_search($browsetype, $browse_title, $browse_actor, $browse_category, $limit_num, &$items)
{ global $MODEL, $CATEGORIES;
  if (!model_connect ())
  { return False;
  }
  $link_id = $MODEL ['conn'];
  $items = array ( );
  $prepare = null;
  $sql = "";
  switch($browsetype)
  { case 'title':
      $sql .= "select * from products";
      $sql .= " where TITLE like '%$browse_title%'";
      $sql .= " FETCH FIRST $limit_num ROWS ONLY";
      $prepare = db2_prepare( $link_id, $sql);
      break;
    case 'actor':
      $sql .= "select * from products";
      $sql .= " where ACTOR like '%$browse_actor%'";
      $sql .= " FETCH FIRST $limit_num ROWS ONLY";
      $prepare = db2_prepare( $link_id, $sql);
      break;
    case 'category':
      // oops did not plan ahead for this
      for ($i=1;$i<=count($CATEGORIES);$i++)
      { if ($CATEGORIES[$i-1]==$browse_category)
        { break;
        }
      }
      $sql .= "select * from products";
      $sql .= " where CATEGORY = $i";
      $sql .= " FETCH FIRST $limit_num ROWS ONLY";
      $prepare = db2_prepare( $link_id, $sql);
      break;


                                                                                                          © 2009 IBM Corporation
db2_prepare() and db2_execute()
Code Sample (slide 2 of 2)
      default:
        break;
    }
    if (!$prepare)
    { model_error_db2("prepare $sql");
      return False;
    }
    // execute prepared statement
    $execute = db2_execute($prepare);
    if (!$execute)
    { model_error_db2("execute");
      return False;
    }
    // fetch the row data
    $i=0;
    while ($row = db2_fetch_assoc($prepare))
    { array_push
        ( $items,
           array
           ( $row["PROD_ID"],
             $row["TITLE"],
             $row["ACTOR"],
             $row["PRICE"]
           )
        );
        $i++;
    }
    if (!$i)
    { model_error_db2 ( "No DVDs found" );
      return False;
    }
    return True;
}




                                               © 2009 IBM Corporation
db2_prepare() and db2_execute()
Another Code Sample – Populate database table
• In this example, db2_prepare() and db2_execute() are being used to
    populate the database table
// Populate the products table
$products =
array
( // 1= Action
   array(1,   1,     'Death Car',          'Brad Baldwig',     3.22,   1),
   array(2,   1,     'Super Car',          'Gwen Midriff',     3.22,   1),
   // 2 = Animation
   array(3,   2,     'Happy Frog',         'Fred Flimflam',    6.22,   1),
   array(4,   2,     'Happy Toad',         'Fred Flinflam',    6.22,   1),
   // 3 = Horror
   array(5,   3,     'Creek of Doom',      'Chris Wock',      25.99,   1),
   array(6,   3,     'River of Doom',      'Chris Wock',      25.99,   1),
   // 4 = Sci-Fi
   array(7,   4,     'Alien of Troy',      'Chris Wock',      25.99,   1),
   // 5 = Sports
   array(8,   5,     'Runner Beware',      'Bruce Henpecked', 5.31,    1),
   // 6 = Travel
   array(9,   6,     'Ships, ships, ships','Bear Greenwood', 3.33,     1),
);
$insert = "INSERT INTO $lib.PRODUCTS";
$insert .= " (PROD_ID, CATEGORY, TITLE, ACTOR, PRICE, SPECIAL)";
$insert .= " VALUES (?,?,?,?,?,?)";
$stmt = db2_prepare($conn, $insert);




                                                                             © 2009 IBM Corporation
db2_prepare() and db2_execute()
Another Code Sample – Populate database table
• In this example, db2_prepare() and db2_execute() are being used to
    populate the database table

if($stmt)
{ foreach($products as $row)
  { $r = db2_execute($stmt, $row);
    if (!$r)
    { die("<br>bad insert ".db2_stmt_errormsg() );
    }
    else
    { $nrows = db2_num_rows($stmt);
      echo ("<br>INSERT $nrows row data = ".implode(",", $row) );
    }
  }
}




                                                                       © 2009 IBM Corporation
API Level Check

• A quick level check on some of the APIs that we’ve talked about:
•
• The db2_pconnect() API should be used over the db2_connect()
   API as db2_pconnect() supports persistent connections:
  $conn = db2_pconnect("","",""); // persistent connect




• The db2_prepare() and db2_execute() APIs should be used over
   the db2_exec() API for optimization and reuse of SQL statements
  $prep = db2_prepare($conn,"sql"); // prepare statement
  $retc = db2_execute($prep); // execute prepared statement




                                                                © 2009 IBM Corporation
Methods (APIs) for fetching data

• There are a number of choices in PHP for fetching data:

   db2_fetch_array()      Returns an array, index by column position, that
                          represents a row in a result set. Column indexing
                          starts at 0.
   db2_fetch_assoc()      Returns an array, indexed by column name, that
                          represents a row in a result set.
   db2_fetch_both()       Returns an array, indexed by both column name and
                          position, that represents a row in a result set. Keep in
                          mind that the row returned by db2_fetch_both() will
                          require more memory than the single-indexed arrays
                          returned by db2_fetch_assoc() or db2_fetch_array()
   db2_fetch_object()     Returns an object in which each property represents
                          a column returned in the row fetched from a result set.




                                                                                © 2009 IBM Corporation
Methods (APIs) for fetching data
• There are a number of choices in PHP for fetching data:
  while ($row = db2_fetch_array($prep)) // one syscall per row (SQLBindCol)
  { $name = $row[0];
    $breed = $row[1];
  }


  while ($row = db2_fetch_assoc($prep)) // one syscall per row (SQLBindCol)
  { $name = $row["NAME"];
    $breed = $row["BREED"];
  }



  while ($row = db2_fetch_both($prep)) // one syscall per row (SQLBindCol)
  { $name = $row[0];
  $breed = $row[1];
  // -- or –
  $name = $row["NAME"];
  $breed = $row["BREED"];
  }



  while ($row = db2_fetch_object($prep)) // one syscall per row (SQLBindCol)
  { $name = $row->NAME;
    $breed = $row->BREED;
  }


 NOTE: In all these different                            All of the db2_fetch options discussed bring the
 examples, $prep represents the                          contents of the entire row back to PHP in one
 resource that contains the result set.                  system call via the SQLBindCol interface


                                                                                                © 2009 IBM Corporation
What about db2_fetch_row() / db2_result()

• Second-tier APIs such as      $conn = db2_pconnect("","","");   // persistent connect

   db2_fetch_row() and          $stmt = db2_prepare($conn,"sql"); // prepare statement
                                $retc = db2_execute($prep);       // execute prepared statement
   db2_result() should be used { $name = db2_result($stmt, 0); // another syscall here
                                while (db2_fetch_row($stmt))      // syscall here


   only when other fetch        }
                                  $breed = db2_result($stmt, 1);  // another syscall here

   options fail.
•
• A performance issue with db2_fetch_row() / db2_result() will be
   encountered
      – Fields are retrieved by systems calls all the way through the i5 kernel via
          SQLGetData
•




                                                                                       © 2009 IBM Corporation
Avoid meta data APIs
• The following ibm_db2 APIs should be used as a last option as of the DB2 meta
   data APIs result in a heavy performance impact
     – If possible avoid use these in the main application flow path

     •   db2_client_info                      •   db2_get_option
     •   db2_column_privileges                •   db2_num_fields
     •   db2_columns                          •   db2_num_rows
     •   db2_cursor_type                      •   db2_primary_keys
     •   db2_escape_string                    •   db2_procedure_columns
     •   db2_field_display_ size              •   db2_procedures
     •   db2_field_name                       •   db2_server_info
     •   db2_field_num                        •   db2_set_option
     •   db2_field_precision                  •   db2_special_columns
     •   db2_field_scale                      •   db2_statistics
     •   db2_field_type                       •   db2_table_privileges
     •   db2_field_width                      •   db2_tables
     •   db2_foreign_keys
                                    NOTE: Detailed information on this APIs can be
                                    found in the online PHP manual
                                    (http://www.php.net/manual/en

                                                                               © 2009 IBM Corporation
IBMDB2i Storage Engine




                         © 2009 IBM Corporation
Storage Engines
   •
   • A key strength of MySQL is it’s pluggable storage architecture.
   • This architecture allows you to select a specialized storage engine for a particular
       application need
   • Different storage engines can be used for different tables within the same
       database schema



   • Key differentiations
         –   Concurrency/Locking
         –   Transaction Support
         –   Physical Storage
         –   Index Support
         –   Memory Caches
         –   Performance Aids




                                                                                       © 2009 IBM Corporation
Storage Engines

• Two tier approach
     – Upper tier includes the SQL parser and optimizer
     – Lower tier comprises a set of storage engines
•
• The SQL tier is free of dependencies on which storage engine manages any
   given table
•
• Clients do not need to be concerned about which engines are involved in
   processing SQL statements.




                                                                             © 2009 IBM Corporation
Integrating MySQL with DB2
• DB2 Storage Engine for MySQL supports open source applications while
   simplifying data management
     – Applications written to MySQL, but data stored in DB2
     – One database to manage, backup, and protect
• Integrates access to MySQL data from RPG,
    DB2 Web Query, …


  Application Written to             MySQL               MyISAM       Specialty
    MySQL (Example                   server              Storage       storage
       SugarCRM)                                         Engine       engines


                                                       DB2i Storage     DB2
            alter table tablename ENGINE=IBMDB2i         Engine       storage
                                                                      engine


                                                                         © 2009 IBM Corporation
IBMDB2i – the newest MySQL storage engine
 •   Developed to allow existing MySQL-based applications to use DB2
        for i for the data store.
 •   Only general-purpose storage engine designed to allow access to
        data from outside of MySQL
 •   Fully-featured, it implements all applicable storage engine
        operations (some occasional restrictions apply)
 •   Can be used as a drop-in replacement with most web applications
 •   Distributed and supported
        by MySQL                                    Existing PHP New RPG
                                                 Application       Application


                                                   MySQL
                                                   IBMDB2I



                                                             DB2 for i


                                                                         © 2009 IBM Corporation
Leveraging MySQL Data with DB2 Web Query
• SugarCRM is a PHP application
   written to MySQL
                                                         DB2 Web
• DB2 Storage Engine enables         SugarCRM
                                                          Query
   SugarCRM data to be stored
                                        Writes to
   in DB2                                                  Accesses
                                      MySQL                Data
• DB2 WebQuery can access
   SugarCRM data                        Data stored in

• Using DB2 Web Query SDK and               DB2 for i
   Report Broker you can integrate
   reporting into SugarCRM




                                                              © 2009 IBM Corporation
Storage Engine Comparison
•    Comparison of features between popular MySQL Storage Engines
                             MyISAM                   InnoDB                   IBMDB2I
     Usage                   Fastest for read heavy   Fully ACID compliant     Fully ACID compliant;
                             applications             transactions             data visible
                                                                               externally
     Locking                 Large-grain table        Multi-versioning, row-   Row-level locking
                             locks, no non-locking    level locking
                             reads
     Durability              Table recovery           Durability recovery      Durability recovery

     Supports                NO                       YES                      YES
     Transactions
     Supports foreign keys   NO                       YES                      YES

     Allows access           NO                     NO                         YES
     through DB2 for i
     interfaces Storage Engine Also supports
      • DB2
       Multiple character sets (ASCII, Far East, and Unicode)
       All MySQL replication configurations
       All widely-used data types (including LOB-based)
      •


                                                                                               © 2009 IBM Corporation
Getting started with IBMDB2I
 •   Requirements:
         –   IBM i 5.4 or later
         –   MySQL version 5.1 for IBM i (IBMDB2I plugin is included by default)
         –   Enabling PTFs (PTF numbers still pending)
 •
 •   Starting MySQL and installing the IBMDB2I storage engine plugin--
        just three easy steps
         1. Start the MySQL server
                     • bin/mysqld_safe &
         2. Start a client connection
                     • bin/mysql –u root
         3. Install the plugin (requires appropriate MySQL user authority)
                     • INSTALL PLUGIN ibmdb2i SONAME “ha_ibmdb2i.so”;
 •
 •   Now MySQL tables can be created directly into DB2
         –   CREATE TABLE test.t1 (i INT) ENGINE=ibmdb2i;
 •




                                                                                   © 2009 IBM Corporation
How does it work?
 •     An SQL statement on an IBMDB2I table is sent to the MySQL server.
 •     The server parses and optimizes the statement.
              –   No DB2 optimization involved

 •     The IBMDB2I storage engine is called by the server to perform operations
          associated with the statement.
 •     IBMDB2I passes the operation to the QSQSRVR job associated with the
          MySQL application connection
              –   DDL operations (CREATE TABLE, ADD INDEX, etc.) are re-constructed as DB2 SQL statements and
                       executed.
              –   I/O (read/insert/delete/update) is done row-by-row via a native I/O interface

 •     Results are returned to the server and then to the client.
 •


     Client                    MySQL                 QSQSRVR


     Client                            IBMDB2I       QSQSRVR




                                                                                                        © 2009 IBM Corporation
Usage Notes
 •   Most MySQL identifiers are stored in DB2 with outer quotes to preserve case
        sensitivity.
           –         create table db1.sales (orderno int) engine = ibmdb2i;
           –   The above creates the DB2 table ”sales” in schema “db1”.
 •
 •   DB2 triggers, constraints, and indexes can be added outside of MySQL but are not
        used by MySQL.
 •
 •   Errors are mapped to MySQL errors when possible.
 •
 •   However, DB2-specific errors are often reported as messages in the joblog of the
        corresponding QSQSRVR job. The MySQL error log contains detailed
        information about the error and the job (when possible).
 •
 •   Security on DB2 tables is handled by the normal IBM i and DB2 mechanisms
           –   All DB2 tables accessed by IBMDB2I are accessed under the profile used to start the mysqld
                    program.
           –   Existing MySQL user security mechanisms control access to the tables via MySQL. MySQL users are
                    distinct from IBM i user profiles.
 •



                                                                                                         © 2009 IBM Corporation
Getting MySQL to Recognize Existing DB2 tables

•   The IBMDB2I storage engine does not recognize DB2 tables and indexes that are
       created outside of MySQL
          –   The storage engine does not have the ability to auto-detect existing DB2 tables
•
•   To enable MySQL to access an existing DB2 table, need to dump the table definition
        & data from DB2 and create the table definition & data in MySQL
          –   There are data attributes and features in DB2 that are not supported in MySQL
•
•   Example Steps
          1. Generate the Data Definition Language from the DDS. This is done through the
                QSQGNDDL API
          2. Scrub the resulting DDL to make it compliant with MySQL
          3. Dump the records from the DB2 table into a CSV file. This is done using the copy from
                import file (CPYFRMIMPF) command.
          4. Using the MySQL command ‘mysqldump’ to import the table definition. Make sure that
                you have specified the DB2i storage engine
          5. Use the MySQL command ‘LOAD DATA INFILE’ to load the data into the MySQL data.
                Make sure that you have specified the DB2i storage engine




                                                                                                © 2009 IBM Corporation
Using the DB2 Storage Engine
•   --default_storage_engine=ibmdb2i
        –   Causes tables to be created with IBMDB2I if no engine is explicitly specified. Useful when
               installing third party applications. The default storage engine is MyISAM otherwise.
        –
•   --ibmdb2i_rdb_name=xxxxxx
        –   Causes IBMDB2I to use the specified RDB. Only tables in this RDB can be used when this
               option is specified. Default is QSYS.
        –
•   --ibmdb2i_transaction_unsafe=0/1
        –   Specifies whether transactions should be respected. Setting this to 1 causes IBMDB2I to
               approximate MyISAM transactional behavior. Default is 0.
        –
•   The storage engine associated with a table can be switched at any time
        –   As easy as ‘ALTER TABLE myisamtable ENGINE=ibmdb2i;’
        –   Causes all data to be copied over during the alter.
        –   Makes moving existing MySQL web app data into DB2 trivial.
•




                                                                                                  © 2009 IBM Corporation
Expanding Access To Data
                                            ILE Applications
                                           (RPG, Cobol, etc)


                                                 DB2
                                                server*
                                   DB2
                                 storage

                       IBMDB2I
     AMP Stack for i

        Apache
          PHP
         MySQL
         IBM i




                          * Leverage IBM i functions such as
                       journaling, SAV/RST and DB2 Web Query
                                                          © 2009 IBM Corporation
© 2009 IBM Corporation

Contenu connexe

En vedette

MySQL Manchester TT - 5.7 Whats new
MySQL Manchester TT - 5.7 Whats newMySQL Manchester TT - 5.7 Whats new
MySQL Manchester TT - 5.7 Whats newMark Swarbrick
 
PHP and Platform Independance in the Cloud
PHP and Platform Independance in the CloudPHP and Platform Independance in the Cloud
PHP and Platform Independance in the CloudZendCon
 
PHP on IBM i Tutorial
PHP on IBM i TutorialPHP on IBM i Tutorial
PHP on IBM i TutorialZendCon
 
Tiery Eyed
Tiery EyedTiery Eyed
Tiery EyedZendCon
 
PHP on Windows - What's New
PHP on Windows - What's NewPHP on Windows - What's New
PHP on Windows - What's NewZendCon
 
A Storage Story #ChefConf2013
A Storage Story #ChefConf2013A Storage Story #ChefConf2013
A Storage Story #ChefConf2013Kyle Bader
 
Oracle Compute Cloud Service快速实践
Oracle Compute Cloud Service快速实践Oracle Compute Cloud Service快速实践
Oracle Compute Cloud Service快速实践Zhaoyang Wang
 
Framework Shootout
Framework ShootoutFramework Shootout
Framework ShootoutZendCon
 
Oracle Compute Cloud Service介绍
Oracle Compute Cloud Service介绍Oracle Compute Cloud Service介绍
Oracle Compute Cloud Service介绍Zhaoyang Wang
 
Oracle cloud ravello介绍及测试账户申请
Oracle cloud ravello介绍及测试账户申请Oracle cloud ravello介绍及测试账户申请
Oracle cloud ravello介绍及测试账户申请Zhaoyang Wang
 
Zend Core on IBM i - Security Considerations
Zend Core on IBM i - Security ConsiderationsZend Core on IBM i - Security Considerations
Zend Core on IBM i - Security ConsiderationsZendCon
 
Why MySQL High Availability Matters
Why MySQL High Availability MattersWhy MySQL High Availability Matters
Why MySQL High Availability MattersMark Swarbrick
 
MySQL Manchester TT - Security
MySQL Manchester TT  - SecurityMySQL Manchester TT  - Security
MySQL Manchester TT - SecurityMark Swarbrick
 
MySQL Manchester TT - Replication Features
MySQL Manchester TT  - Replication FeaturesMySQL Manchester TT  - Replication Features
MySQL Manchester TT - Replication FeaturesMark Swarbrick
 
MySQL Tech Tour 2015 - 5.7 Connector/J/Net
MySQL Tech Tour 2015 - 5.7 Connector/J/NetMySQL Tech Tour 2015 - 5.7 Connector/J/Net
MySQL Tech Tour 2015 - 5.7 Connector/J/NetMark Swarbrick
 
Solving the C20K problem: Raising the bar in PHP Performance and Scalability
Solving the C20K problem: Raising the bar in PHP Performance and ScalabilitySolving the C20K problem: Raising the bar in PHP Performance and Scalability
Solving the C20K problem: Raising the bar in PHP Performance and ScalabilityZendCon
 
Digital Identity
Digital IdentityDigital Identity
Digital IdentityZendCon
 
MySQL Head to Head Performance
MySQL Head to Head PerformanceMySQL Head to Head Performance
MySQL Head to Head PerformanceKyle Bader
 

En vedette (20)

MySQL Manchester TT - 5.7 Whats new
MySQL Manchester TT - 5.7 Whats newMySQL Manchester TT - 5.7 Whats new
MySQL Manchester TT - 5.7 Whats new
 
PHP and Platform Independance in the Cloud
PHP and Platform Independance in the CloudPHP and Platform Independance in the Cloud
PHP and Platform Independance in the Cloud
 
PHP on IBM i Tutorial
PHP on IBM i TutorialPHP on IBM i Tutorial
PHP on IBM i Tutorial
 
Tiery Eyed
Tiery EyedTiery Eyed
Tiery Eyed
 
PHP on Windows - What's New
PHP on Windows - What's NewPHP on Windows - What's New
PHP on Windows - What's New
 
A Storage Story #ChefConf2013
A Storage Story #ChefConf2013A Storage Story #ChefConf2013
A Storage Story #ChefConf2013
 
Oracle Compute Cloud Service快速实践
Oracle Compute Cloud Service快速实践Oracle Compute Cloud Service快速实践
Oracle Compute Cloud Service快速实践
 
Framework Shootout
Framework ShootoutFramework Shootout
Framework Shootout
 
Oracle Compute Cloud Service介绍
Oracle Compute Cloud Service介绍Oracle Compute Cloud Service介绍
Oracle Compute Cloud Service介绍
 
Script it
Script itScript it
Script it
 
Oracle cloud ravello介绍及测试账户申请
Oracle cloud ravello介绍及测试账户申请Oracle cloud ravello介绍及测试账户申请
Oracle cloud ravello介绍及测试账户申请
 
Zend Core on IBM i - Security Considerations
Zend Core on IBM i - Security ConsiderationsZend Core on IBM i - Security Considerations
Zend Core on IBM i - Security Considerations
 
Why MySQL High Availability Matters
Why MySQL High Availability MattersWhy MySQL High Availability Matters
Why MySQL High Availability Matters
 
MySQL Manchester TT - Security
MySQL Manchester TT  - SecurityMySQL Manchester TT  - Security
MySQL Manchester TT - Security
 
MySQL Manchester TT - Replication Features
MySQL Manchester TT  - Replication FeaturesMySQL Manchester TT  - Replication Features
MySQL Manchester TT - Replication Features
 
MySQL Tech Tour 2015 - 5.7 Connector/J/Net
MySQL Tech Tour 2015 - 5.7 Connector/J/NetMySQL Tech Tour 2015 - 5.7 Connector/J/Net
MySQL Tech Tour 2015 - 5.7 Connector/J/Net
 
Solving the C20K problem: Raising the bar in PHP Performance and Scalability
Solving the C20K problem: Raising the bar in PHP Performance and ScalabilitySolving the C20K problem: Raising the bar in PHP Performance and Scalability
Solving the C20K problem: Raising the bar in PHP Performance and Scalability
 
MySQL Clusters
MySQL ClustersMySQL Clusters
MySQL Clusters
 
Digital Identity
Digital IdentityDigital Identity
Digital Identity
 
MySQL Head to Head Performance
MySQL Head to Head PerformanceMySQL Head to Head Performance
MySQL Head to Head Performance
 

Similaire à PHP and IBM i - Database Alternatives

Less13 performance
Less13 performanceLess13 performance
Less13 performanceAmit Bhalla
 
How To Start Up With PHP In IBM i
How To Start Up With PHP In IBM iHow To Start Up With PHP In IBM i
How To Start Up With PHP In IBM iSam Pinkhasov
 
How To Start Up With Php In Ibm I
How To Start Up With Php In Ibm IHow To Start Up With Php In Ibm I
How To Start Up With Php In Ibm IAlex Frenkel
 
MySQL performance webinar
MySQL performance webinarMySQL performance webinar
MySQL performance webinarAbel Flórez
 
Zend Products and PHP for IBMi
Zend Products and PHP for IBMi  Zend Products and PHP for IBMi
Zend Products and PHP for IBMi Shlomo Vanunu
 
Network Automation (NetDevOps) with Ansible
Network Automation (NetDevOps) with AnsibleNetwork Automation (NetDevOps) with Ansible
Network Automation (NetDevOps) with AnsibleAPNIC
 
MySQL 5.7: Focus on InnoDB
MySQL 5.7: Focus on InnoDBMySQL 5.7: Focus on InnoDB
MySQL 5.7: Focus on InnoDBMario Beck
 

Similaire à PHP and IBM i - Database Alternatives (8)

Less13 performance
Less13 performanceLess13 performance
Less13 performance
 
How To Start Up With PHP In IBM i
How To Start Up With PHP In IBM iHow To Start Up With PHP In IBM i
How To Start Up With PHP In IBM i
 
How To Start Up With Php In Ibm I
How To Start Up With Php In Ibm IHow To Start Up With Php In Ibm I
How To Start Up With Php In Ibm I
 
MySQL performance webinar
MySQL performance webinarMySQL performance webinar
MySQL performance webinar
 
December 2013 HUG: InfiniDB for Hadoop
December 2013 HUG: InfiniDB for HadoopDecember 2013 HUG: InfiniDB for Hadoop
December 2013 HUG: InfiniDB for Hadoop
 
Zend Products and PHP for IBMi
Zend Products and PHP for IBMi  Zend Products and PHP for IBMi
Zend Products and PHP for IBMi
 
Network Automation (NetDevOps) with Ansible
Network Automation (NetDevOps) with AnsibleNetwork Automation (NetDevOps) with Ansible
Network Automation (NetDevOps) with Ansible
 
MySQL 5.7: Focus on InnoDB
MySQL 5.7: Focus on InnoDBMySQL 5.7: Focus on InnoDB
MySQL 5.7: Focus on InnoDB
 

Plus de ZendCon

I18n with PHP 5.3
I18n with PHP 5.3I18n with PHP 5.3
I18n with PHP 5.3ZendCon
 
Cloud Computing: The Hard Problems Never Go Away
Cloud Computing: The Hard Problems Never Go AwayCloud Computing: The Hard Problems Never Go Away
Cloud Computing: The Hard Problems Never Go AwayZendCon
 
Planning for Synchronization with Browser-Local Databases
Planning for Synchronization with Browser-Local DatabasesPlanning for Synchronization with Browser-Local Databases
Planning for Synchronization with Browser-Local DatabasesZendCon
 
Magento - a Zend Framework Application
Magento - a Zend Framework ApplicationMagento - a Zend Framework Application
Magento - a Zend Framework ApplicationZendCon
 
Enterprise-Class PHP Security
Enterprise-Class PHP SecurityEnterprise-Class PHP Security
Enterprise-Class PHP SecurityZendCon
 
Insights from the Experts: How PHP Leaders Are Transforming High-Impact PHP A...
Insights from the Experts: How PHP Leaders Are Transforming High-Impact PHP A...Insights from the Experts: How PHP Leaders Are Transforming High-Impact PHP A...
Insights from the Experts: How PHP Leaders Are Transforming High-Impact PHP A...ZendCon
 
Joe Staner Zend Con 2008
Joe Staner Zend Con 2008Joe Staner Zend Con 2008
Joe Staner Zend Con 2008ZendCon
 
Make your PHP Application Software-as-a-Service (SaaS) Ready with the Paralle...
Make your PHP Application Software-as-a-Service (SaaS) Ready with the Paralle...Make your PHP Application Software-as-a-Service (SaaS) Ready with the Paralle...
Make your PHP Application Software-as-a-Service (SaaS) Ready with the Paralle...ZendCon
 
DB2 Storage Engine for MySQL and Open Source Applications Session
DB2 Storage Engine for MySQL and Open Source Applications SessionDB2 Storage Engine for MySQL and Open Source Applications Session
DB2 Storage Engine for MySQL and Open Source Applications SessionZendCon
 
Modernizing i5 Applications
Modernizing i5 ApplicationsModernizing i5 Applications
Modernizing i5 ApplicationsZendCon
 
Lesser Known Security Problems in PHP Applications
Lesser Known Security Problems in PHP ApplicationsLesser Known Security Problems in PHP Applications
Lesser Known Security Problems in PHP ApplicationsZendCon
 
Architecting for PHP5 - Why "Runs on PHP5" is not "Written for PHP5"
Architecting for PHP5 - Why "Runs on PHP5" is not "Written for PHP5"Architecting for PHP5 - Why "Runs on PHP5" is not "Written for PHP5"
Architecting for PHP5 - Why "Runs on PHP5" is not "Written for PHP5"ZendCon
 
SQL Query Tuning: The Legend of Drunken Query Master
SQL Query Tuning: The Legend of Drunken Query MasterSQL Query Tuning: The Legend of Drunken Query Master
SQL Query Tuning: The Legend of Drunken Query MasterZendCon
 
ZendCon 2008 Closing Keynote
ZendCon 2008 Closing KeynoteZendCon 2008 Closing Keynote
ZendCon 2008 Closing KeynoteZendCon
 
Top Zend Studio Secrets
Top Zend Studio SecretsTop Zend Studio Secrets
Top Zend Studio SecretsZendCon
 
VIM for (PHP) Programmers
VIM for (PHP) ProgrammersVIM for (PHP) Programmers
VIM for (PHP) ProgrammersZendCon
 
Test Driven Development
Test Driven DevelopmentTest Driven Development
Test Driven DevelopmentZendCon
 
Rickroll To Go With PHP, WURFL, and Other Open Source Tools
Rickroll To Go With PHP, WURFL, and Other Open Source ToolsRickroll To Go With PHP, WURFL, and Other Open Source Tools
Rickroll To Go With PHP, WURFL, and Other Open Source ToolsZendCon
 
PECL Picks - Extensions to make your life better
PECL Picks - Extensions to make your life betterPECL Picks - Extensions to make your life better
PECL Picks - Extensions to make your life betterZendCon
 
Static and Dynamic Analysis at Ning
Static and Dynamic Analysis at NingStatic and Dynamic Analysis at Ning
Static and Dynamic Analysis at NingZendCon
 

Plus de ZendCon (20)

I18n with PHP 5.3
I18n with PHP 5.3I18n with PHP 5.3
I18n with PHP 5.3
 
Cloud Computing: The Hard Problems Never Go Away
Cloud Computing: The Hard Problems Never Go AwayCloud Computing: The Hard Problems Never Go Away
Cloud Computing: The Hard Problems Never Go Away
 
Planning for Synchronization with Browser-Local Databases
Planning for Synchronization with Browser-Local DatabasesPlanning for Synchronization with Browser-Local Databases
Planning for Synchronization with Browser-Local Databases
 
Magento - a Zend Framework Application
Magento - a Zend Framework ApplicationMagento - a Zend Framework Application
Magento - a Zend Framework Application
 
Enterprise-Class PHP Security
Enterprise-Class PHP SecurityEnterprise-Class PHP Security
Enterprise-Class PHP Security
 
Insights from the Experts: How PHP Leaders Are Transforming High-Impact PHP A...
Insights from the Experts: How PHP Leaders Are Transforming High-Impact PHP A...Insights from the Experts: How PHP Leaders Are Transforming High-Impact PHP A...
Insights from the Experts: How PHP Leaders Are Transforming High-Impact PHP A...
 
Joe Staner Zend Con 2008
Joe Staner Zend Con 2008Joe Staner Zend Con 2008
Joe Staner Zend Con 2008
 
Make your PHP Application Software-as-a-Service (SaaS) Ready with the Paralle...
Make your PHP Application Software-as-a-Service (SaaS) Ready with the Paralle...Make your PHP Application Software-as-a-Service (SaaS) Ready with the Paralle...
Make your PHP Application Software-as-a-Service (SaaS) Ready with the Paralle...
 
DB2 Storage Engine for MySQL and Open Source Applications Session
DB2 Storage Engine for MySQL and Open Source Applications SessionDB2 Storage Engine for MySQL and Open Source Applications Session
DB2 Storage Engine for MySQL and Open Source Applications Session
 
Modernizing i5 Applications
Modernizing i5 ApplicationsModernizing i5 Applications
Modernizing i5 Applications
 
Lesser Known Security Problems in PHP Applications
Lesser Known Security Problems in PHP ApplicationsLesser Known Security Problems in PHP Applications
Lesser Known Security Problems in PHP Applications
 
Architecting for PHP5 - Why "Runs on PHP5" is not "Written for PHP5"
Architecting for PHP5 - Why "Runs on PHP5" is not "Written for PHP5"Architecting for PHP5 - Why "Runs on PHP5" is not "Written for PHP5"
Architecting for PHP5 - Why "Runs on PHP5" is not "Written for PHP5"
 
SQL Query Tuning: The Legend of Drunken Query Master
SQL Query Tuning: The Legend of Drunken Query MasterSQL Query Tuning: The Legend of Drunken Query Master
SQL Query Tuning: The Legend of Drunken Query Master
 
ZendCon 2008 Closing Keynote
ZendCon 2008 Closing KeynoteZendCon 2008 Closing Keynote
ZendCon 2008 Closing Keynote
 
Top Zend Studio Secrets
Top Zend Studio SecretsTop Zend Studio Secrets
Top Zend Studio Secrets
 
VIM for (PHP) Programmers
VIM for (PHP) ProgrammersVIM for (PHP) Programmers
VIM for (PHP) Programmers
 
Test Driven Development
Test Driven DevelopmentTest Driven Development
Test Driven Development
 
Rickroll To Go With PHP, WURFL, and Other Open Source Tools
Rickroll To Go With PHP, WURFL, and Other Open Source ToolsRickroll To Go With PHP, WURFL, and Other Open Source Tools
Rickroll To Go With PHP, WURFL, and Other Open Source Tools
 
PECL Picks - Extensions to make your life better
PECL Picks - Extensions to make your life betterPECL Picks - Extensions to make your life better
PECL Picks - Extensions to make your life better
 
Static and Dynamic Analysis at Ning
Static and Dynamic Analysis at NingStatic and Dynamic Analysis at Ning
Static and Dynamic Analysis at Ning
 

Dernier

From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationSafe Software
 
[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdfhans926745
 
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024BookNet Canada
 
Salesforce Community Group Quito, Salesforce 101
Salesforce Community Group Quito, Salesforce 101Salesforce Community Group Quito, Salesforce 101
Salesforce Community Group Quito, Salesforce 101Paola De la Torre
 
Scaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationScaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationRadu Cotescu
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonAnna Loughnan Colquhoun
 
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking MenDelhi Call girls
 
Breaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountBreaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountPuma Security, LLC
 
CNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of ServiceCNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of Servicegiselly40
 
Understanding the Laravel MVC Architecture
Understanding the Laravel MVC ArchitectureUnderstanding the Laravel MVC Architecture
Understanding the Laravel MVC ArchitecturePixlogix Infotech
 
Google AI Hackathon: LLM based Evaluator for RAG
Google AI Hackathon: LLM based Evaluator for RAGGoogle AI Hackathon: LLM based Evaluator for RAG
Google AI Hackathon: LLM based Evaluator for RAGSujit Pal
 
Swan(sea) Song – personal research during my six years at Swansea ... and bey...
Swan(sea) Song – personal research during my six years at Swansea ... and bey...Swan(sea) Song – personal research during my six years at Swansea ... and bey...
Swan(sea) Song – personal research during my six years at Swansea ... and bey...Alan Dix
 
Maximizing Board Effectiveness 2024 Webinar.pptx
Maximizing Board Effectiveness 2024 Webinar.pptxMaximizing Board Effectiveness 2024 Webinar.pptx
Maximizing Board Effectiveness 2024 Webinar.pptxOnBoard
 
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...Neo4j
 
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfThe Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfEnterprise Knowledge
 
SQL Database Design For Developers at php[tek] 2024
SQL Database Design For Developers at php[tek] 2024SQL Database Design For Developers at php[tek] 2024
SQL Database Design For Developers at php[tek] 2024Scott Keck-Warren
 
Histor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slideHistor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slidevu2urc
 
Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...
Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...
Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...gurkirankumar98700
 
Slack Application Development 101 Slides
Slack Application Development 101 SlidesSlack Application Development 101 Slides
Slack Application Development 101 Slidespraypatel2
 
The Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxThe Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxMalak Abu Hammad
 

Dernier (20)

From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
 
[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf
 
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
 
Salesforce Community Group Quito, Salesforce 101
Salesforce Community Group Quito, Salesforce 101Salesforce Community Group Quito, Salesforce 101
Salesforce Community Group Quito, Salesforce 101
 
Scaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationScaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organization
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt Robison
 
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
 
Breaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountBreaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path Mount
 
CNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of ServiceCNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of Service
 
Understanding the Laravel MVC Architecture
Understanding the Laravel MVC ArchitectureUnderstanding the Laravel MVC Architecture
Understanding the Laravel MVC Architecture
 
Google AI Hackathon: LLM based Evaluator for RAG
Google AI Hackathon: LLM based Evaluator for RAGGoogle AI Hackathon: LLM based Evaluator for RAG
Google AI Hackathon: LLM based Evaluator for RAG
 
Swan(sea) Song – personal research during my six years at Swansea ... and bey...
Swan(sea) Song – personal research during my six years at Swansea ... and bey...Swan(sea) Song – personal research during my six years at Swansea ... and bey...
Swan(sea) Song – personal research during my six years at Swansea ... and bey...
 
Maximizing Board Effectiveness 2024 Webinar.pptx
Maximizing Board Effectiveness 2024 Webinar.pptxMaximizing Board Effectiveness 2024 Webinar.pptx
Maximizing Board Effectiveness 2024 Webinar.pptx
 
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
 
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfThe Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
 
SQL Database Design For Developers at php[tek] 2024
SQL Database Design For Developers at php[tek] 2024SQL Database Design For Developers at php[tek] 2024
SQL Database Design For Developers at php[tek] 2024
 
Histor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slideHistor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slide
 
Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...
Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...
Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...
 
Slack Application Development 101 Slides
Slack Application Development 101 SlidesSlack Application Development 101 Slides
Slack Application Development 101 Slides
 
The Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxThe Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptx
 

PHP and IBM i - Database Alternatives

  • 1. PHP and IBM i – Database Alternatives Tony Cairns (adc@us.ibm.com) Software Developer IBM Systems & Technology Group, Systems Software 8 Copyright IBM Corporation, 2009. All Rights Reserved. This publication may refer to products that are not currently available in your country. IBM makes no commitment to make available any products referred to herein.
  • 2. Agenda • Zend Toolkit – Native I/O Calls – Web Enabling DB2 with PHP using the i5 Toolkit – • DB2 Language Extensions – SQL Compliant Syntax • • IBM DBi Storage Engine • • • © 2009 IBM Corporation
  • 3. Web Enabling DB2 with PHP Native I/O Calls © 2009 IBM Corporation
  • 4. i5 Toolkit APIs • Are shipped with Zend products – Zend core for i5/OS – Zend Platform for i5/OS • • Geared towards accessing i5 data and resources from PHP – Similar in purpose to the IBM Toolbox for Java • • Note: the I5_COMD job must be running in the ZEND subsystem – Use the Zend menu to start: • GO ZENDCORE/ZCMENU • Option 5 (Service Management menu) • Option 8 (Start I5_COMD service) © 2009 IBM Corporation
  • 5. PHP Toolkit for i5/OS – list of functions • Connection management • Data retrieval • Native file access –i5_connect – i5_fetch_array – i5_open –i5_close – i5_fetch_assoc – i5_addnew –i5_adopt_authority – i5_fetch_object – I5_edit –i5_error – i5_fetch_row – I5_delete –i5_errno – i5_cancel_edit – i5_info –i5_errormsg – i5_setvalue – i5_field_len » – i5_update – i5_field_name • Command calls – i5_range_from – i5_field_scale –i5_command – i5_range_to – i5_field_type » – i5_range_clear • Program calls – i5_list_fields – i5_num_fields – i5_data_seek –i5_program_prepare – i5_result – i5_seek –i5_program_prepare_PCML – i5_bookmark –i5_program_call –i5_program_close – i5_free_file – i5_new_record – i5_update_record – i5_get_keys – © 2009 IBM Corporation
  • 6. PHP Toolkit functions • System values • Job logs • User space –i5_get_system_value –i5_jobLog_list –i5_userspace_crearte » –i5_jobLog_list_read –i5_userspace_prepare • Data areas –i5_jobLog_list_close –i5_userspace_get –i5_data_area_prepare – –i5_userspace_put –i5_data_area_receive • Active jobs – –i5_data_area_send –i5_job_list • Data Queue –i5_data_area_close –i5_job_list_read –i5_dtaq_prepare – –i5_job_list_close –i5_dtaq_recieve • Print/Get spooled file – –i5_dtaq_send –i5_spool_list • Objects list –i5_dtaq-close –i5_spool_list_read –i5_object_list – –i5_spool_list_close –i5_object_list_read –i5_spool_get_data –i5_object_list_close –i5_spool_from_file – – – • – © 2009 IBM Corporation
  • 7. PHP Toolkit for i5/OS – list of functions • Connection management • Data retrieval • Native file access –i5_connect – i5_fetch_array – i5_open –i5_close – i5_fetch_assoc – i5_addnew –i5_adopt_authority – i5_fetch_object – I5_edit –i5_error – i5_fetch_row – I5_delete –i5_errno – i5_cancel_edit – i5_info –i5_errormsg – i5_setvalue – i5_field_len » – i5_update – i5_field_name • Command calls – i5_range_from – i5_field_scale –i5_command – i5_range_to – i5_field_type » – i5_range_clear • Program calls – i5_list_fields – i5_num_fields – i5_data_seek –i5_program_prepare – i5_result – i5_seek –i5_program_prepare_PCML – i5_bookmark –i5_program_call –i5_program_close – i5_free_file – i5_new_record – i5_update_record – i5_get_keys – © 2009 IBM Corporation
  • 8. PHP Toolkit functions • System values • Job logs • User space –i5_get_system_value –i5_jobLog_list –i5_userspace_crearte » –i5_jobLog_list_read –i5_userspace_prepare • Data areas –i5_jobLog_list_close –i5_userspace_get –i5_data_area_prepare – –i5_userspace_put –i5_data_area_receive • Active jobs – –i5_data_area_send –i5_job_list • Data Queue –i5_data_area_close –i5_job_list_read –i5_dtaq_prepare – –i5_job_list_close –i5_dtaq_recieve • Print/Get spooled file – –i5_dtaq_send –i5_spool_list • Objects list –i5_dtaq-close –i5_spool_list_read –i5_object_list – –i5_spool_list_close –i5_object_list_read –i5_spool_get_data –i5_object_list_close –i5_spool_from_file – – – • – © 2009 IBM Corporation
  • 9. Record-level access • i5_open – Pass in library/file and type of "open" mode • I5_OPEN_READ, IT_OPEN_READWRITE, I5_OPEN_COMMIT, I5_OPENSHRRD, I5_OPEN_SHRUPD • • • i5_list_fields – Get an array of the names of the fields in the file – • i5_fetch_row – Get an array of the fields in a record, passing in which record to fetch • I5_READ_SEEK (current) • I5_READ_NEXT (default) • I5_READ_PREV • I5_READ_FIRST • I5_READ_LAST © 2009 IBM Corporation
  • 10. Record-level access: HTML code • <form method="post" action=“i5_fetch.php"> • • Library (schema): <input type="text" name="lib"><br> • • File (table): <input type="text" name="tbl"><br> • • <input type="reset"> <input type="submit" value="Display records"> • • </form> © 2009 IBM Corporation
  • 11. Record-level access: PHP code • // Get input data • $dbfile = $_POST['tbl']; • $dblib = $_POST['lib']; • • // Open the file • $file = i5_open("$dblib/$dbfile", I5_OPEN_READ); • if (!$file) • die("Error while attempting to open file mode=READ"); • • // Print a table row of field names • $fields = i5_list_fields($file); // returns an array • print "<table border=1 cellpadding=3><tr bgcolor=#CCCCCC>"; • • // Parse the array • foreach ($fields as $value) { • print "<th>$value</th>"; •} • print "</tr>n"; © 2009 IBM Corporation
  • 12. Record-level access: PHP code • // Print records • $rec = i5_fetch_row($file, I5_READ_FIRST); // get array • while ($rec) { // while there is a record... • // Print each field in the array • foreach ($rec as $value) { • print “<td>$value</td>"; • } • print "</tr>n"; • // Get next record • $rec = i5_fetch_row($file, I5_READ_NEXT); • } // loop • • print "</table>"; // finish off the table © 2009 IBM Corporation
  • 13. Record-level access: PHP code Example Form and Output © 2009 IBM Corporation
  • 14. Web Enabling DB2 with PHP Using the i5 Toolkit © 2009 IBM Corporation
  • 15. PHP Get Records (i5 toolkit SQL model) • Recall from our previous function model_connect() discussion that before any of the { global $MODEL; $db_options = array(I5_OPTIONS_JOBNAME=>"DVDSEARCH"); APIs from the i5 toolkit can be $MODEL ['conn'] = i5_pconnect ( $MODEL ['database'], used we must establish a $MODEL ['db_user'], $MODEL ['db_password'], connection to the i5 toolkit ); $db_options daemon process (PGM- if (! $MODEL ['conn']) { model_error_i5("Connect fail"); EASYCOM job) return False; } • The i5_pconnect() API is being return model_chglibl(); used to establish a persistent NOTE: Each browser button click will connection and avoid costly IBM i run back through the i5_pconnect() startup and termination code; however since the connection is associated in the cached for each Apache worker job the i5_connect() and actual processing time is minimal. i5_close() APIs. • Establish a connection is a fairly NOTE: In this case each button click will common activity so a site not find it’s way back to the same worker common include comes in handy job (more on this later). © 2009 IBM Corporation
  • 16. i5 toolkit browser clicks route to different Apache jobs • Persistent i5 connections means that each “stateless” Apache job will have a common connection to the i5 toolkit – This common connection will be used by all PHP i5 toolkit applications running, not a single application or job • Persistent connections does not mean that each browser click will return to the same job – Do not attempt commit transactions across multiple browser clicks!! © 2009 IBM Corporation
  • 17. i5_pconnect() results in many PGM-EASYCOM jobs • Supplying a user-id and password to the i5_pconnect() call will spawn multiple PGM- EASYCOM server processes for each different user profile specified – This behavior is dependent on the Apache user peak demand • • It is good practice to limit the number of “active” web profiles. • • A “private” connection can be used for traditional “state full” RPG applications (more on this later) © 2009 IBM Corporation
  • 18. i5_prepare() and i5_execute() • The i5 toolkit API combination of i5_prepare() and i5_execute() allows the DB2 engine to optimize and reuse SQL statements many times. – This can provide improved performance on high volume web sites that have many DB2 calls. • • The i5 toolkit SQL code shown on the following pages replaces the RPG back-end. • • The same function names are used in the i5 toolkit SQL MVC as the i5 toolkit model function names (model_search) – When we replace the model code with i5 toolkit SQL the view and control code do not have to change (the 5250 code can also remain the same) Reminder: The i5 toolkit model used APIs to call the RPG model. © 2009 IBM Corporation
  • 19. i5 Toolkit SQL code example (slide 1 of x) <?php function model_search($browsetype, $browse_title, $browse_actor, $browse_category, $limit_num, &$items) { global $MODEL, $categories; if (!model_connect ()) { False; } $items = array ( ); $prepare = null; $sql = ""; switch($browsetype) { case 'title': $sql .= "select * from products"; $sql .= " where TITLE like '%$browse_title%'"; $sql .= " FETCH FIRST $limit_num ROWS ONLY"; $prepare = i5_prepare($sql); break; case 'actor': $sql .= "select * from products"; $sql .= " where ACTOR like '%$browse_actor%'"; $sql .= " FETCH FIRST $limit_num ROWS ONLY"; $prepare = i5_prepare($sql); break; case 'category': // oops did not plan ahead for this for ($i=1;$i<=count($categories);$i++) { if ($categories[$i-1]==$browse_category) { break; } } © 2009 IBM Corporation
  • 20. i5 Toolkit SQL code example (slide 2 of x) $sql .= "select * from products"; $sql .= " where CATEGORY = $i"; $sql .= " FETCH FIRST $limit_num ROWS ONLY"; $prepare = i5_prepare($sql); break; default: break; } if (!$prepare) { model_error_i5("prepare $sql"); return False; } // execute prepared statement $execute = i5_execute($prepare); if (!$execute) { model_error_i5("execute"); return False; } // fetch the row data $i=0; while ($row = i5_fetch_assoc($prepare, I5_READ_NEXT)) { array_push ( $items, array ( $row["PROD_ID"], $row["TITLE"], $row["ACTOR"], $row["PRICE"] ) ); $i++; } if (!$i) { model_error_i5 ( "No DVDs found" ); return False; } return True; } © 2009 IBM Corporation
  • 21. DB2 for i5/OS Language Extensions © 2009 IBM Corporation
  • 22. Accessing DB2/400 • Server/connection • Errors – db2_bind_param • Fetch – db2_fetch_array – db2_conn_error – db2_client_info – db2_fetch_assoc – db2_conn_errormsg – db2_close – db2_fetch_both – db2_stmt_error – db2_connect – db2_fetch_object – db2_stmt_errormsg – db2_cursor_type – db2_fetch_row • Column/Procedure – db2_exec • Field information – db2_column_privileges  db2_execute – db2_field_display_ size – db2_columns  db2_prepare – db2_field_name – db2_procedure_column – db2_pconnect s – db2_server_info – db2_field_num – db2_procedures – db2_statistics – db2_field_precision – db2_special_columns – db2_field_scale • Result • Table information – db2_field_type – db2_free_result – db2_num_fields – db2_field_width – db2_next_result • Key information – db2_num_rows – db2_result – db2_foreign_keys – db2_table_privileges • Commit/Rollback – db2_primary_keys – db2_tables – db2_autocommit • Statement – db2_commit – db2_free_stmt documented online at php.net All of these APIs are – db2_rollback  - Preferred db2_ SQL statement execution APIs (performance) • - Non-preferred db2_ SQL statement execution API © 2009 IBM Corporation
  • 23. Server connection – db2_connect API • There are few connections options in PHP – db2_connect(“”,””,””) – local server • The database connection job is create under the same user as the Apache httpd server profile NOBODY (*USER). • – db2_connect(“*LOCAL", “SAM", “PASSWROD")– local server • The database connection job is created under the user profile SAM/PASSWROD. • – db2_connect(“10.1.1.33", “SAM", “PASSWROD") – remote database connection job is created under the user server • The profile SAM/PASSWROD. – – ● Tip: Use WRKRDBDIRE to get the database name on the remote © 2009 IBM Corporation
  • 24. i5 Toolkit vs. DB2 Extensions • The i5 Toolkit represents functions for accessing native i5/OS resources – The toolkit is specific to the Zend products for i5/OS – The toolkit functions can only access local resources (i.e., resources on the same i5/OS partition as Zend Core) unless you setup remote connections (such as DDM) outside of the Zend Core environment. – All i5 Toolkit functions begin with the characters “i5_” • The DB2 functions represent an extension to the PHP language – The DB2 extensions are starting to become part of the mainstream php language offering and will be available on platforms other the i5/OS – The DB2 extensions can access both local and remote DB2 resources through established network connections – All DB2 functions begin with the characters “db2_” © 2009 IBM Corporation
  • 25. i5 Toolkit vs. DB2 Extensions i5_* I5_COMD Zend Core I5/OS Resource PASE db2_* i5/OS TCP/IP db2_* I5/OS Resource i5/OS © 2009 IBM Corporation
  • 26. Anatomy of a Web-Enabled DB2 for i5/OS (via PHP) application © 2009 IBM Corporation
  • 27. Application Description • We will take a look at a single PHP application. • • The application will create an employee information search and retrieval form • • All of the following code would be saved in a single file (ex: employee_update.php) – As you will see, we can use HTML form fields to allow the application to re- call itself for different functions within the application. © 2009 IBM Corporation
  • 28. Employee Search Form • When invoked (through a Web Browser) this code will generate a blank web page with a title bar of “Employee Update – employee_update.php”) <html> <?php define("PAGE_TITLE", "Employee Update"); define("PHP_FILE_NAME", "employee_update.php") ?> <title><?= PAGE_TITLE . " - " . PHP_FILE_NAME; ?></title> <body> <?php ?> </body> </html> © 2009 IBM Corporation
  • 29. Employee Search Form • The following block of code will cause the Employee Search form to be displayed. This block of code is inserted between the <?php tag and the corresponding ? > tag /* Display the initial Employee Search form and the $_POST lastname field */ /* When the Search button is selected. */ print '<h1>Enter the first few characters of the employee records you' . 'wish to view/edit in the last name field.</h1>'; print '<h2>For example, "JO":</h2>'; print '<form action="employee_update.php" method="POST">'; print 'Last Name: <input type="text" name="lastname" /> <br />'; print '<input type="submit" name="action" value="Search" />'; print <'/form>'; • This block of code outputs in the HTML stream instructs for what is to be entered into the form along with the HTML to generate the form information. © 2009 IBM Corporation
  • 30. Search Action • The following block of code implements the search function – This function uses the values entered on the Employee Search Form to open the database and perform the search action – This block of code would be inserted between the <?php tag and the /* Display comment /* Include System i connection information */ 1 if ((include "../i5_properties.php") == false) { echo("Problem encountered loading i5_properties.php include file"); } /* Set up connection for each pass through this application */ 2 $dbh = db2_connect($db, $user, $password); if (!$dbh) : 3 die ("Problems connecting to the database."); endif; 1. The if statement causes a separate PHP file that contains a number of variable definitions for the program (including database name, user credential information) to be included 2. The db2_connect() call is one of the calls from the DB2 for i5/OS extensions – it establishes a connection to the database server and returns a handle to the indicated database. 3. The if statement tests that a valid handle to the database was returned and if not the application terminates (via the die language construct) © 2009 IBM Corporation
  • 31. Database Query • The next section of code performs the database query and places the results on the Employee Search Results form. – The code would be placed between the </form> line and the second ?> php end} tag else { /* Query file using input from form and deliver results to client */ /* Construct SQL statement, using lastname as the search substring */ 1 $sql = 'select * from i5schema.employee where lastname like '' .$_POST["lastname"]. '%''; /* Execute the DB2 SQL statement, place results into $stmt */ 2 $stmt = db2_exec($dbh, $sql, array('cursor' => DB2_SCROLLABLE)); /* Print Employee Search Results header and table setup */ print '<h1>Employee Records On System i starting with ' . $_POST["lastname"]. '</h1>'; 3 print '<br><table border=1 cellpadding=5 cellspacing=5>'; /* Iterate through result set, printing one table line per */ /* record returned. Note that the customerNumber field will */ /* be an "active field" which will $_GET the customerNumber */ /* and reinvoke the employee_update.php application*/ 1. This statement assigns an SQL select statement to the $sql variable. The SQL statement includes values entered on the Employee Search from 2. The db2_exec() statement causes the SQL statement to be executed against the database that was opened previously with the db2_connect() function call. 3. The print statements will output in the HTML stream for the first part of the search results form which includes a header line indicating what records are being displayed as well as a table that will contain the returned records © 2009 IBM Corporation
  • 32. Database 4 while ($row = db2_fetch_array($stmt)) { if (!$row=="") { Query $customerNumber = $row[0]; $customerName = $row[3]; $customerFirst = $row[1]; $workdept = $row[4]; $job = $row[7]; $salary = $row[11]; print '<tr><td align=center><a 5 href=employee_update.php?customerNumber=' . $customerNumber . '>' . $customerNumber . '</a><td>'. $customerName.'<td>'. $customerFirst.'<td>'. $workdept.'<td>'. $job.'<td>'. $salary.'</td></tr>'; } } /* Close table */ print '</table><br>'; 6 /* Print the DB2 SQL statement which was executed - Informational */ print "<p>Echo of dynamically-built sql: ".$sql."</p>"; } 4. The while loop iterates through the records returned by the db2_exec() function call (as represented by the $stmt variable) and outputs them into the HTML table. • Each time through the loop the db2_fetch_array() function will return an array of values representing a row in the result set 5. Notice that the employee number ($customerNumber) is output as an actionable tag (i.e., it will be a selectable link on the HTML page) • This is accomplished through the ‘href’ statement which causes this php application to be re-called when the tag is selected 6. The HTML tag to close the HTML table is output along with outputting the SQL statement that was executed. © 2009 IBM Corporation
  • 33. Database Query Notes • Using the db2_exec() statement to insert PHP variable values into an SQL statement can be considered a security exposure. – Rather then using the db2_exec() statement the db2_prepare() statement could be used to prepare an SQL statement with parameter markers for input values. – Then the db2_execute() statement could be used rather then the db2_exec() statement to pass in the input values. • When the PHP file is invoked (from a Web Browser) the search form is displayed. Entering data into the Last Name field and selecting the [Search] button causes the Query code to be executed and the results to be displayed in the web form © 2009 IBM Corporation
  • 34. Database Query – Employee Record Edit • This code will determine which record to place on the Edit form. The code is invoked when an employee number is selected from the search results window – This code would be placed between the $action = $_POST[“action”] and the if(!$action) lines 1 $customerNumber = $_GET["customerNumber"];2 /* Determine which form to display based upon the state of the application */ 2 if ($customerNumber == "") { 1. The employee number selected on the search results form is retrieved from the URL and assigned to the $customerNumber variable 2. The test of the $customerNumber field is performed to see if this block of code was reached after an employee number was selected from the search results window. • Remember that this PHP code generates both the Employee Search as well as the Employee Search Results pages © 2009 IBM Corporation
  • 35. Edit Record • The following code displays the values of a record and allows the user to edit those values – This code would be placed between the bracket (}) line and the second ?> php tag } else { /* Edit the selected customer number record */ /* Construct SQL statement, using customerNumber to select the record */ $sql = 'select * from i5schema.employee where empno = '' 1 .$_GET["customerNumber"]. '''; /* Execute the DB2 SQL statement, place results into $stmt */ 2 $stmt = db2_exec($dbh, $sql, array('cursor' => DB2_SCROLLABLE)); /* Place the field information returned into array $row */ $row = db2_fetch_array($stmt); 3 /* If there is data, move information from result array into fields */ /* which will be used for screen display */ if (!$row=="") { $customerNumber = $row[0]; 4 $customerName = $row[3]; $customerFirst = $row[1]; $workdept = $row[4]; $job = $row[7]; $salary = $row[11]; } 1. The select statement establishes the query using the customer number that the user selected from the HTML form 2. The db2_exec() function call executes the query 3. The db2_fetch_array() function call retrieves an array of fields values from a record in the return set from the db2_exec() function call 4. The if statement tests to ensure that a record was returned from the db2_fetch_array() call. If a record was returned, local variables are used to tore the field values. © 2009 IBM Corporation
  • 36. Edit Record /* Display the Employee Edit form and $_GET all fields */ /* when Update button is selected. */ print '<h1>Edit an Employee record:</h1>'; print '<form action="employee_update.php" method="GET">'; print 'Customer Number: <input type="text" name="customerNumber" value="'.$customerNumber.'" /> <br />'; print 'Last Name: <input type="text" name="customerName" value="'.$customerName.'" /> <br />'; print 'First Name: <input type="text" name="customerFirst" value="'.$customerFirst.'"/> <br />'; 5 print 'Work Department: <input type="text" name="workdept" value="'.$workdept.'"/>'; print 'Job Type: <input type="text" name="job" value="'.$job.'"/> <br />'; print 'Salary: <input type="text" name="salary" value="'.$salary.'"/> <br />'; print '<input type="submit" name="action" value="Update" /><a href=employee_update.php?customerNumber=' . $customerNumber .'&customerName=' .$customerName.'&customerFirst='.$customerFirst.'&workdept='. $workdept.'&job='.$job.'&salary='.$salary.'&action='.$submit.'></a>'; print '</form>'; } 5. A series of print statements are used to output text labels as well as the contents of the returned record © 2009 IBM Corporation
  • 37. Edit Record – Notes • The print ‘<form’> along with the print ‘<input> line (italicized in the code for emphasis) will cause the program to place the “Update” action onto the URL line when the Update button is selected – Each of the field values are also placed into the URL string. – This causes the application to “call” itself again. – The URL string values are retrieved using the $_GET function and then used in the SQL update call (coming up) • When the PHP program is invoked and a record is searched for and selected the Results form will display the Employee Edit Record form with the values from the selected record © 2009 IBM Corporation
  • 38. Record Update • This code is invoked when the [Update] button is selected from the Edit page. • The code updates the DB2 for i5/OS table using the user modified fields. • Once the record has been updated an HTML form is output with the SQL statement along with an indication of whether or not the update was successful. • The first section of code determines the state of the application, that is has the user selected the [Update] button from the Edit page. • The following code would replace the $action = $_POST[“action”] line of code which is just prior to the $customerNumber = $_GET[“customerNumber’] statement $action = $_GET["action"]; /* if no value from $_GET, try $_POST */ if (!$action) { $action = $_POST["action"]; } © 2009 IBM Corporation
  • 39. Record Update • The section of code that resembles the following } } else { /* Edit the selected customer number record */ • Would be changed to resemble the following (i.e., remove the beginning close bracket on the else statement } else { /* Edit the selected customer number record */ © 2009 IBM Corporation
  • 40. Record Update • The following block of code would be placed before the else { /* Edit the selected customer number record */ statement 1 } else { /* Update Employee table, or edit the selected customer record */ if ($action == "Update") { $sql = 'update i5schema.employee set lastname = 2 ''.$_GET["customerName"].'','. ' firstnme = ''.$_GET["customerFirst"].'','. 2 ' workdept = ''.$_GET["workdept"].'','. ' job = ''.$_GET["job"].'','.3 ' salary = '.$_GET["salary"]. ' where empno = '' .$customerNumber.''';4 3 $result = db2_exec($dbh, $sql); 1. The if statement determines whether or not the user requested the Update action from the Edit Record form. • If the update action was invoked then the remaining code is executed 2. The assignment statement generates the SQL update statement using values passed from the calling form 3. The db2_exec() function call executes the SQL update statement returning the results into the $result variable. © 2009 IBM Corporation
  • 41. Record Update if (!$result) { 4 echo '<p>There was a problem inserting the user into the database.</p>'; print '<p>Echo of dynamically-built sql:<br>'.$sql.'</p>';5 } else { print '<h1>Update Successful</h1>';5 5 print '<form action="employee_update.php" method="POST">'; print '<p>Echo of dynamically-built sql:<br>'.$sql.'</p>'; print '<input type="submit" name="action" value="Continue" /><a href=employee_update.php></a>'; print '</form>'; } } 4. The if statement tests the result returned from the db2_exec() call. • If an error was encountered by the update statement an error message is output to the HTML form. • If the update statement was successful a success message is output along with the actual SQL statement that was executed by the db2_exec() function call. 5. The <form> line along with the <input> line causes the program to place the “Continue” action onto the URL string when the [Continue] button is selected. The program will then be automatically re-invoked. • In addition to the above code a line containing a closing bracket (}) would need to be placed before the second ?> php end tag at the bottom of the application © 2009 IBM Corporation
  • 42. Record Update – Notes Page • Now when the application is invoked, searching for a set of records, selecting a record from the returned set, edit the values and selecting the [Update] button will cause the record to be updated and a web page similar to the following to be displayed © 2009 IBM Corporation
  • 43. Continue • The final piece of code that we need to look at is the code that processes selection of the [Continue] button. • When the [Continue] button is selected, the application will be reset back to the Employee Search form • In the following section of code: /* Determine which form to display based upon the state of application */ if ($customerNumber == "") { if (!$action) { • The if (!$action) line would be replaced with the following if ((!$action) or ($action == "Continue")) { • The check for the “Continue” action will cause the Search form to be displayed when the [Continue] button is selected from the Update database form. © 2009 IBM Corporation
  • 44. Why MVC • Recall that we are using the MVC model to separate out the model, view, and controller components of the program – This makes it easy to replace one component without affecting the others. • In this example, the same PHP view/control code as in the previous i5 call RPG program example is used. – Now a different include_once for the MVC ibm_db2 model is used <?php define('APP_BASE_DIR', '/www/zendcore/htdocs/qiwikiCode/'); include_once APP_BASE_DIR.'101Config.php'; /* Site: site configuration */ include_once APP_BASE_DIR.'101Connectdb2.php'; /* Model: ibm_db2 connect */ include_once APP_BASE_DIR.'101DB2Model.php'; /* Model: ibm_db2 SQL */ include_once APP_BASE_DIR.'101HtmlView.php'; /* View: plain old generated html */ include_once APP_BASE_DIR.'101HtmlControl.php'; /* Control: main loop html forms */ ?> • In this example, the RPG back-end (the model) is being replaced with PHP ibm_db2 – A new DB2 model include_once is used to handle the connections to ibm_db2 © 2009 IBM Corporation
  • 45. PHP Get Records (ibm_db2 model) Establish a connection • In an earlier module we looked at the i5_toolkit calls which uses the i5_connect (and i5_pconnect) to connect to the PGM-EASYCOM jobs. – A daemon approach is used that monitors a socket for in-coming connections that are started by the Zend core menus and subsystems. • • The ibm_db2 language extension uses in-line calling and/or special memory shipping if going between QSQ server jobs and Apache worker jobs. • • Simply put, different connection methods are used by these two facilities as then come from different sources. The ibm_db2 language extension comes from IBM while the i5_toolkit calls comes from another PHP vendor. © 2009 IBM Corporation
  • 46. PHP Get Records (ibm_db2 model) Using db2_pconnect vs db2_connect • The db2_pconnect() API supports function model_connect() { global $MODEL; persistent connections $db_options = array("i5_naming"=>DB2_I5_NAMING_ON); $MODEL ['conn'] = db2_pconnect • The db2_connect() API does not ( $MODEL ['database'], $MODEL ['db_user'], support persistent connections $MODEL ['db_password'], $db_options • Use of the db2_pconnect() API ); if (! $MODEL ['conn']) can improve performance of web- { model_error_db2("Connect failed"); sites when they are experiencing } return False; heavy user web demand return model_chglibl(); } • Notice the array(“i5_naming”=>DB2_I5_NAMING_ON) – libl changes via db2_set_option(“i5_libl”=>) will work with unqualified table names – It is likely that for stored procedure calls to an RPG program, the RPG program would also likely need the library list set.Use of persistent connections is absolutely required if the site services multiple requests Tip! Setting DB2_I5_NAMING_ON per second. for the whole set simply works better on IBM i. Mixing DB2_I5_NAMING_ON/OFF almost The php.ini configuration file can be updated always leads to problems on the site to force all db2_connect() calls to use db2_pconnect() instead © 2009 IBM Corporation
  • 47. db2_connect() browser clicks route to different Apache jobs • The use of a persistent db2 connection only means that each “stateless” Apache job will have a common connection to db2 – This common connection will be used by all PHP ibm_db2 applications running, not just a single application or “job”. • • Persistent connections does not mean that each browser click will return to the same job Tip! Do NOT attempt db2_commit() transactions across multiple browser clicks!!! © 2009 IBM Corporation
  • 48. db2_pconnect() results in many QSQ jobs • Supplying a user-id and password to the db2_pconnect() call will spawn a QSQ db2 server process for each different user profile specified for each Apache worker job Tip! It is a good practice to limit the number of “active” web profiles. NOTE: Once the QSQ job has been Tip! Mixing db2_pconnect(“”,””,””) spawned there will be little and db2_pconnect(“*LOCAL”,”uid”,”pwd”) difference between profiles can lead to unpredictable results – pick a (assuming that db2_pconnect() profile or two to handle web clients and was used) stick with those. © 2009 IBM Corporation
  • 49. What about db2_connect? • Recall from the previous slide that a QSQ db2 server process is spawned for each different user profile for each Apache worker job – In the case of db2_connect, the QSQ job starts and stops with each db2_connect() and db2_close() call. – This is a performance degredation over the use of the persistent db2_pconnect() call – especially for those sites with heave traffic and many requests per second. • © 2009 IBM Corporation
  • 50. db2_prepare() and db2_execute() vs. db2_exec() • Use of the db2_prepare() and db2_execute() API combination allows the DB2 engine to optimize and reuse SQL statements many times. – Use of these APIs on high volume web sites can provide a performance boast over db2_exec() • • The ibm_db2 code shown on the next slides replaces the RPG back end. – The MVC model function names have been kept exactly the same as the i5 toolkit model function names (model_search) – When the model code is replaced with ibm_db2 the view and control model do not change (the 5250 code will also remain the same) © 2009 IBM Corporation
  • 51. db2_prepare() and db2_execute() Code Sample (slide 1 of 2) <?php function model_search($browsetype, $browse_title, $browse_actor, $browse_category, $limit_num, &$items) { global $MODEL, $CATEGORIES; if (!model_connect ()) { return False; } $link_id = $MODEL ['conn']; $items = array ( ); $prepare = null; $sql = ""; switch($browsetype) { case 'title': $sql .= "select * from products"; $sql .= " where TITLE like '%$browse_title%'"; $sql .= " FETCH FIRST $limit_num ROWS ONLY"; $prepare = db2_prepare( $link_id, $sql); break; case 'actor': $sql .= "select * from products"; $sql .= " where ACTOR like '%$browse_actor%'"; $sql .= " FETCH FIRST $limit_num ROWS ONLY"; $prepare = db2_prepare( $link_id, $sql); break; case 'category': // oops did not plan ahead for this for ($i=1;$i<=count($CATEGORIES);$i++) { if ($CATEGORIES[$i-1]==$browse_category) { break; } } $sql .= "select * from products"; $sql .= " where CATEGORY = $i"; $sql .= " FETCH FIRST $limit_num ROWS ONLY"; $prepare = db2_prepare( $link_id, $sql); break; © 2009 IBM Corporation
  • 52. db2_prepare() and db2_execute() Code Sample (slide 2 of 2) default: break; } if (!$prepare) { model_error_db2("prepare $sql"); return False; } // execute prepared statement $execute = db2_execute($prepare); if (!$execute) { model_error_db2("execute"); return False; } // fetch the row data $i=0; while ($row = db2_fetch_assoc($prepare)) { array_push ( $items, array ( $row["PROD_ID"], $row["TITLE"], $row["ACTOR"], $row["PRICE"] ) ); $i++; } if (!$i) { model_error_db2 ( "No DVDs found" ); return False; } return True; } © 2009 IBM Corporation
  • 53. db2_prepare() and db2_execute() Another Code Sample – Populate database table • In this example, db2_prepare() and db2_execute() are being used to populate the database table // Populate the products table $products = array ( // 1= Action array(1, 1, 'Death Car', 'Brad Baldwig', 3.22, 1), array(2, 1, 'Super Car', 'Gwen Midriff', 3.22, 1), // 2 = Animation array(3, 2, 'Happy Frog', 'Fred Flimflam', 6.22, 1), array(4, 2, 'Happy Toad', 'Fred Flinflam', 6.22, 1), // 3 = Horror array(5, 3, 'Creek of Doom', 'Chris Wock', 25.99, 1), array(6, 3, 'River of Doom', 'Chris Wock', 25.99, 1), // 4 = Sci-Fi array(7, 4, 'Alien of Troy', 'Chris Wock', 25.99, 1), // 5 = Sports array(8, 5, 'Runner Beware', 'Bruce Henpecked', 5.31, 1), // 6 = Travel array(9, 6, 'Ships, ships, ships','Bear Greenwood', 3.33, 1), ); $insert = "INSERT INTO $lib.PRODUCTS"; $insert .= " (PROD_ID, CATEGORY, TITLE, ACTOR, PRICE, SPECIAL)"; $insert .= " VALUES (?,?,?,?,?,?)"; $stmt = db2_prepare($conn, $insert); © 2009 IBM Corporation
  • 54. db2_prepare() and db2_execute() Another Code Sample – Populate database table • In this example, db2_prepare() and db2_execute() are being used to populate the database table if($stmt) { foreach($products as $row) { $r = db2_execute($stmt, $row); if (!$r) { die("<br>bad insert ".db2_stmt_errormsg() ); } else { $nrows = db2_num_rows($stmt); echo ("<br>INSERT $nrows row data = ".implode(",", $row) ); } } } © 2009 IBM Corporation
  • 55. API Level Check • A quick level check on some of the APIs that we’ve talked about: • • The db2_pconnect() API should be used over the db2_connect() API as db2_pconnect() supports persistent connections: $conn = db2_pconnect("","",""); // persistent connect • The db2_prepare() and db2_execute() APIs should be used over the db2_exec() API for optimization and reuse of SQL statements $prep = db2_prepare($conn,"sql"); // prepare statement $retc = db2_execute($prep); // execute prepared statement © 2009 IBM Corporation
  • 56. Methods (APIs) for fetching data • There are a number of choices in PHP for fetching data: db2_fetch_array() Returns an array, index by column position, that represents a row in a result set. Column indexing starts at 0. db2_fetch_assoc() Returns an array, indexed by column name, that represents a row in a result set. db2_fetch_both() Returns an array, indexed by both column name and position, that represents a row in a result set. Keep in mind that the row returned by db2_fetch_both() will require more memory than the single-indexed arrays returned by db2_fetch_assoc() or db2_fetch_array() db2_fetch_object() Returns an object in which each property represents a column returned in the row fetched from a result set. © 2009 IBM Corporation
  • 57. Methods (APIs) for fetching data • There are a number of choices in PHP for fetching data: while ($row = db2_fetch_array($prep)) // one syscall per row (SQLBindCol) { $name = $row[0]; $breed = $row[1]; } while ($row = db2_fetch_assoc($prep)) // one syscall per row (SQLBindCol) { $name = $row["NAME"]; $breed = $row["BREED"]; } while ($row = db2_fetch_both($prep)) // one syscall per row (SQLBindCol) { $name = $row[0]; $breed = $row[1]; // -- or – $name = $row["NAME"]; $breed = $row["BREED"]; } while ($row = db2_fetch_object($prep)) // one syscall per row (SQLBindCol) { $name = $row->NAME; $breed = $row->BREED; } NOTE: In all these different All of the db2_fetch options discussed bring the examples, $prep represents the contents of the entire row back to PHP in one resource that contains the result set. system call via the SQLBindCol interface © 2009 IBM Corporation
  • 58. What about db2_fetch_row() / db2_result() • Second-tier APIs such as $conn = db2_pconnect("","",""); // persistent connect db2_fetch_row() and $stmt = db2_prepare($conn,"sql"); // prepare statement $retc = db2_execute($prep); // execute prepared statement db2_result() should be used { $name = db2_result($stmt, 0); // another syscall here while (db2_fetch_row($stmt)) // syscall here only when other fetch } $breed = db2_result($stmt, 1); // another syscall here options fail. • • A performance issue with db2_fetch_row() / db2_result() will be encountered – Fields are retrieved by systems calls all the way through the i5 kernel via SQLGetData • © 2009 IBM Corporation
  • 59. Avoid meta data APIs • The following ibm_db2 APIs should be used as a last option as of the DB2 meta data APIs result in a heavy performance impact – If possible avoid use these in the main application flow path • db2_client_info • db2_get_option • db2_column_privileges • db2_num_fields • db2_columns • db2_num_rows • db2_cursor_type • db2_primary_keys • db2_escape_string • db2_procedure_columns • db2_field_display_ size • db2_procedures • db2_field_name • db2_server_info • db2_field_num • db2_set_option • db2_field_precision • db2_special_columns • db2_field_scale • db2_statistics • db2_field_type • db2_table_privileges • db2_field_width • db2_tables • db2_foreign_keys NOTE: Detailed information on this APIs can be found in the online PHP manual (http://www.php.net/manual/en © 2009 IBM Corporation
  • 60. IBMDB2i Storage Engine © 2009 IBM Corporation
  • 61. Storage Engines • • A key strength of MySQL is it’s pluggable storage architecture. • This architecture allows you to select a specialized storage engine for a particular application need • Different storage engines can be used for different tables within the same database schema • Key differentiations – Concurrency/Locking – Transaction Support – Physical Storage – Index Support – Memory Caches – Performance Aids © 2009 IBM Corporation
  • 62. Storage Engines • Two tier approach – Upper tier includes the SQL parser and optimizer – Lower tier comprises a set of storage engines • • The SQL tier is free of dependencies on which storage engine manages any given table • • Clients do not need to be concerned about which engines are involved in processing SQL statements. © 2009 IBM Corporation
  • 63. Integrating MySQL with DB2 • DB2 Storage Engine for MySQL supports open source applications while simplifying data management – Applications written to MySQL, but data stored in DB2 – One database to manage, backup, and protect • Integrates access to MySQL data from RPG, DB2 Web Query, … Application Written to MySQL MyISAM Specialty MySQL (Example server Storage storage SugarCRM) Engine engines DB2i Storage DB2 alter table tablename ENGINE=IBMDB2i Engine storage engine © 2009 IBM Corporation
  • 64. IBMDB2i – the newest MySQL storage engine • Developed to allow existing MySQL-based applications to use DB2 for i for the data store. • Only general-purpose storage engine designed to allow access to data from outside of MySQL • Fully-featured, it implements all applicable storage engine operations (some occasional restrictions apply) • Can be used as a drop-in replacement with most web applications • Distributed and supported by MySQL Existing PHP New RPG Application Application MySQL IBMDB2I DB2 for i © 2009 IBM Corporation
  • 65. Leveraging MySQL Data with DB2 Web Query • SugarCRM is a PHP application written to MySQL DB2 Web • DB2 Storage Engine enables SugarCRM Query SugarCRM data to be stored Writes to in DB2 Accesses MySQL Data • DB2 WebQuery can access SugarCRM data Data stored in • Using DB2 Web Query SDK and DB2 for i Report Broker you can integrate reporting into SugarCRM © 2009 IBM Corporation
  • 66. Storage Engine Comparison • Comparison of features between popular MySQL Storage Engines MyISAM InnoDB IBMDB2I Usage Fastest for read heavy Fully ACID compliant Fully ACID compliant; applications transactions data visible externally Locking Large-grain table Multi-versioning, row- Row-level locking locks, no non-locking level locking reads Durability Table recovery Durability recovery Durability recovery Supports NO YES YES Transactions Supports foreign keys NO YES YES Allows access NO NO YES through DB2 for i interfaces Storage Engine Also supports • DB2  Multiple character sets (ASCII, Far East, and Unicode)  All MySQL replication configurations  All widely-used data types (including LOB-based) • © 2009 IBM Corporation
  • 67. Getting started with IBMDB2I • Requirements: – IBM i 5.4 or later – MySQL version 5.1 for IBM i (IBMDB2I plugin is included by default) – Enabling PTFs (PTF numbers still pending) • • Starting MySQL and installing the IBMDB2I storage engine plugin-- just three easy steps 1. Start the MySQL server • bin/mysqld_safe & 2. Start a client connection • bin/mysql –u root 3. Install the plugin (requires appropriate MySQL user authority) • INSTALL PLUGIN ibmdb2i SONAME “ha_ibmdb2i.so”; • • Now MySQL tables can be created directly into DB2 – CREATE TABLE test.t1 (i INT) ENGINE=ibmdb2i; • © 2009 IBM Corporation
  • 68. How does it work? • An SQL statement on an IBMDB2I table is sent to the MySQL server. • The server parses and optimizes the statement. – No DB2 optimization involved • The IBMDB2I storage engine is called by the server to perform operations associated with the statement. • IBMDB2I passes the operation to the QSQSRVR job associated with the MySQL application connection – DDL operations (CREATE TABLE, ADD INDEX, etc.) are re-constructed as DB2 SQL statements and executed. – I/O (read/insert/delete/update) is done row-by-row via a native I/O interface • Results are returned to the server and then to the client. • Client MySQL QSQSRVR Client IBMDB2I QSQSRVR © 2009 IBM Corporation
  • 69. Usage Notes • Most MySQL identifiers are stored in DB2 with outer quotes to preserve case sensitivity. – create table db1.sales (orderno int) engine = ibmdb2i; – The above creates the DB2 table ”sales” in schema “db1”. • • DB2 triggers, constraints, and indexes can be added outside of MySQL but are not used by MySQL. • • Errors are mapped to MySQL errors when possible. • • However, DB2-specific errors are often reported as messages in the joblog of the corresponding QSQSRVR job. The MySQL error log contains detailed information about the error and the job (when possible). • • Security on DB2 tables is handled by the normal IBM i and DB2 mechanisms – All DB2 tables accessed by IBMDB2I are accessed under the profile used to start the mysqld program. – Existing MySQL user security mechanisms control access to the tables via MySQL. MySQL users are distinct from IBM i user profiles. • © 2009 IBM Corporation
  • 70. Getting MySQL to Recognize Existing DB2 tables • The IBMDB2I storage engine does not recognize DB2 tables and indexes that are created outside of MySQL – The storage engine does not have the ability to auto-detect existing DB2 tables • • To enable MySQL to access an existing DB2 table, need to dump the table definition & data from DB2 and create the table definition & data in MySQL – There are data attributes and features in DB2 that are not supported in MySQL • • Example Steps 1. Generate the Data Definition Language from the DDS. This is done through the QSQGNDDL API 2. Scrub the resulting DDL to make it compliant with MySQL 3. Dump the records from the DB2 table into a CSV file. This is done using the copy from import file (CPYFRMIMPF) command. 4. Using the MySQL command ‘mysqldump’ to import the table definition. Make sure that you have specified the DB2i storage engine 5. Use the MySQL command ‘LOAD DATA INFILE’ to load the data into the MySQL data. Make sure that you have specified the DB2i storage engine © 2009 IBM Corporation
  • 71. Using the DB2 Storage Engine • --default_storage_engine=ibmdb2i – Causes tables to be created with IBMDB2I if no engine is explicitly specified. Useful when installing third party applications. The default storage engine is MyISAM otherwise. – • --ibmdb2i_rdb_name=xxxxxx – Causes IBMDB2I to use the specified RDB. Only tables in this RDB can be used when this option is specified. Default is QSYS. – • --ibmdb2i_transaction_unsafe=0/1 – Specifies whether transactions should be respected. Setting this to 1 causes IBMDB2I to approximate MyISAM transactional behavior. Default is 0. – • The storage engine associated with a table can be switched at any time – As easy as ‘ALTER TABLE myisamtable ENGINE=ibmdb2i;’ – Causes all data to be copied over during the alter. – Makes moving existing MySQL web app data into DB2 trivial. • © 2009 IBM Corporation
  • 72. Expanding Access To Data ILE Applications (RPG, Cobol, etc) DB2 server* DB2 storage IBMDB2I AMP Stack for i Apache PHP MySQL IBM i * Leverage IBM i functions such as journaling, SAV/RST and DB2 Web Query © 2009 IBM Corporation
  • 73. © 2009 IBM Corporation