Zikula_Core
[ class tree: Zikula_Core ] [ index: Zikula_Core ] [ all elements ]

Source for file DBUtil.class.php

Documentation is available at DBUtil.class.php

  1. <?php
  2. /**
  3.  * Zikula Application Framework
  4.  *
  5.  * @copyright Robert Gasch
  6.  * @link http://www.zikula.org
  7.  * @version $Id: DBUtil.class.php 24406 2008-06-29 09:42:06Z Guite $
  8.  * @license GNU/GPL - http://www.gnu.org/copyleft/gpl.html
  9.  * @author Robert Gasch rgasch@gmail.com
  10.  * @package Zikula_Core
  11.  */
  12.  
  13. /**
  14.  * DBUtil
  15.  *
  16.  * @package Zikula_Core
  17.  * @subpackage DBUtil
  18.  */
  19. class DBUtil
  20. {
  21.     /**
  22.      * The strucuture of the parameters joinInfo and permFilter are described here globally
  23.      * rather then repeating this definition every time these parameters are used/referenced.
  24.      *
  25.      * The joinInfo parameter has to be an array structured as follows:
  26.      * $joinInfo[] = array ('join_table'         =>  'The tablekey to join to'
  27.      *                      'join_field'         =>  'The field key of the field to join, can also be an array of fields',
  28.      *                      'object_field_name'  =>  'The resulting field name, can also be an array if join_field is an array',
  29.      *                      'compare_field_table'=>  'The compare field key (select table)',
  30.      *                      'compare_field_join' =>  'The compare field key (join table)');
  31.      *
  32.      * The permissionFilter parameter has to be an array structured as follows:
  33.      * $permFilter[]  = array ('realm'            =>  'The realm to test in (usually 0)',
  34.      *                         'component_left'   =>  'The left part of the component test',
  35.      *                         'component_middle' =>  'The middle part of the component test',
  36.      *                         'component_right'  =>  'The right part of the component test',
  37.      *                         'instance_left'    =>  'The left object field of the instance test',
  38.      *                         'instance_middle'  =>  'The middle object field of the instance test',
  39.      *                         'instance_right'   =>  'The right object field of the instance test',
  40.      *                         'level'            =>  'The access level to check');
  41.      */
  42.  
  43.  
  44.     /**
  45.      * Execute SQL, check for errors and return result. Uses Adodb to generate DB-portable paging code.
  46.      *
  47.      * @param sql          The SQL statement to execute
  48.      * @param limitOffset  The lower limit bound (optional) (default=-1)
  49.      * @param limitNumRows The upper limit bound (optional) (default=-1)
  50.      * @param sql          The SQL statement to execute
  51.      * @param exitOnError  whether to exit on error (default=true) (optional)
  52.      * @param verbose      whether to be verbose (default=true) (optional)
  53.      *
  54.      * @return mixed The result set of the successfully executed query or false on error
  55.      */
  56.     function executeSQL ($sql$limitOffset=-1$limitNumRows=-1$exitOnError=true$verbose=true)
  57.     {
  58.         if (!$sql{
  59.             return pn_exit ('No SQL statement to execute in DBUtil::executeSQL()');
  60.         }
  61.  
  62.         $dbconn DBConnectionStack::getConnection();
  63.         if (!$dbconn && defined('_PNINSTALLVER')) {
  64.             return false;
  65.         }
  66.  
  67.         global $PNConfig;
  68.         $suid $PNConfig['Debug']['sql_user'];
  69.         $uid  SessionUtil::getVar('uid'0);
  70.         if (($PNConfig['Debug']['sql_time'|| $PNConfig['Debug']['sql_detail']||
  71.             (!$suid || ($suid && $suid === $uid))) {
  72.             static $timer;
  73.             if (!$timer{
  74.                 $timer new Timer();
  75.             }
  76.             else {
  77.                 $timer->reset ();
  78.             }
  79.         }
  80.  
  81.         if ($limitNumRows 0{
  82.             $tStr strtoupper(substr (trim($sql)06));
  83.             if ($tStr !== 'SELECT')
  84.                 return pn_exit('Paging parameters can only be used for SELECT statements');
  85.  
  86.             $result $dbconn->SelectLimit ($sql$limitNumRows$limitOffset);
  87.         }
  88.         else {
  89.             $result $dbconn->Execute($sql);
  90.         }
  91.  
  92.         if ($result{
  93.             global $PNRuntime;
  94.             $uid  SessionUtil::getVar('uid'0);
  95.             $suid $PNConfig['Debug']['sql_user'];
  96.  
  97.             if (!$suid || (is_array($suid&& in_array($uid)) || ($suid === $uid)) {
  98.                 if ($PNConfig['Debug']['sql_count']{
  99.                     $PNRuntime['sql_count_request'+= 1;
  100.                 }
  101.  
  102.                 if ($PNConfig['Debug']['sql_time'|| $PNConfig['Debug']['sql_detail']{
  103.                     $diff $timer->snap(true);
  104.                     $PNRuntime['sql_time_request'+= $diff['diff'];
  105.                     if ($PNConfig['Debug']['sql_detail']{
  106.                         $sqlstat array();
  107.                         $sqlstat['stmt']  $sql;
  108.                     }
  109.                     if ($limitNumRows 0{
  110.                         $sqlstat['limit'"$limitOffset$limitNumRows";
  111.                     }
  112.                     if ($PNConfig['Debug']['sql_detail']{
  113.                         $sqlstat['rows_affected'$result->_numOfRows;
  114.                         $sqlstat['time'$diff['diff'];
  115.                         $PNRuntime['sql'][$sqlstat;
  116.                     }
  117.                 }
  118.             }
  119.  
  120.             return $result;
  121.         }
  122.  
  123.         if ($verbose{
  124.             print '<br />' $dbconn->ErrorMsg('<br />' $sql '<br />';
  125.         }
  126.  
  127.         if ($exitOnError{
  128.             return pn_exit('Exiting after SQL-error');
  129.         }
  130.  
  131.         return false;
  132.     }
  133.  
  134.  
  135.     /**
  136.      * Set the gobal object fetch counter to the specified value
  137.      *
  138.      * This function is workaround for PHP4 limitations when passing default arguments by reference
  139.      *
  140.      * @param count        The value to set the object marhsall counter to
  141.      *
  142.      * @return Nothing, the global variable is assigned counter
  143.      */
  144.     function _setFetchedObjectCount ($count=0)
  145.     {
  146.         $GLOBALS['DBUtilFetchObjectCount'$count;
  147.     }
  148.  
  149.  
  150.     /**
  151.      * Get the gobal object fetch counter
  152.      *
  153.      * This function is workaround for PHP4 limitations when passing default arguments by reference
  154.      *
  155.      * @return The value held by the global
  156.      */
  157.     function _getFetchedObjectCount ()
  158.     {
  159.         if (isset($GLOBALS['DBUtilFetchObjectCount'])) {
  160.             return (int)$GLOBALS['DBUtilFetchObjectCount'];
  161.         }
  162.  
  163.         return false;
  164.     }
  165.  
  166.  
  167.     /**
  168.      * Set the gobal object marshall counter to the specified value
  169.      *
  170.      * This function is workaround for PHP4 limitations when passing default arguments by reference
  171.      *
  172.      * @param count        The value to set the object marhsall counter to
  173.      *
  174.      * @return Nothing, the global variable is assigned
  175.      */
  176.     function _setMarshalledObjectCount ($count=0)
  177.     {
  178.         $GLOBALS['DBUtilMarshallObjectCount'$count;
  179.     }
  180.  
  181.  
  182.     /**
  183.      * Add the specified value to the gobal object marshall counter
  184.      *
  185.      * This function is workaround for PHP4 limitations when passing default arguments by reference
  186.      *
  187.      * @param count        The value to add to the object marhsall counter
  188.      *
  189.      * @return Nothing, the global variable is incremented
  190.      */
  191.     function _addMarshalledObjectCount ($count)
  192.     {
  193.         $GLOBALS['DBUtilMarshallObjectCount'+= $count;
  194.     }
  195.  
  196.  
  197.     /**
  198.      * Get the gobal object marshall counter
  199.      *
  200.      * This function is workaround for PHP4 limitations when passing default arguments by reference
  201.      *
  202.      * @return Nothing, the global variable is incremented
  203.      */
  204.     function _getMarshalledObjectCount ()
  205.     {
  206.         if (isset($GLOBALS['DBUtilMarshallObjectCount']))
  207.         return (int)$GLOBALS['DBUtilMarshallObjectCount'];
  208.  
  209.         return false;
  210.     }
  211.  
  212.  
  213.     /**
  214.      * Convenience function to ensure that the where-clause starts with "WHERE"
  215.      *
  216.      * @param where        The original where clause
  217.      *
  218.      * @return The value held by the global counter
  219.      */
  220.     function _checkWhereClause ($where)
  221.     {
  222.         if (!strlen(trim($where))) {
  223.             return $where;
  224.         }
  225.  
  226.         $where trim ($where);
  227.         $upwhere strtoupper($where);
  228.         if (strstr($upwhere'WHERE'=== false || strpos($upwhere'WHERE'1{
  229.             $where 'WHERE ' $where;
  230.         }
  231.  
  232.         return $where;
  233.     }
  234.  
  235.  
  236.     /**
  237.      * Convenience function to ensure that the order-by-clause starts with "ORDER BY"
  238.      *
  239.      * @param orderby    The original order-by clause
  240.      * @param tablename  The tablename key for the PNTables structure, only used for oracle quote determination (optional) (default=null)
  241.      *
  242.      * @return The (potenitally) altered order-by-clause
  243.      */
  244.     function _checkOrderByClause ($orderby$tablename=null)
  245.     {
  246.         if (!strlen(trim($orderby))) {
  247.             return $orderby;
  248.         }
  249.  
  250.         $dbType DBConnectionStack::getConnectionDBType();
  251.  
  252.         // given that we use quotes in our generated SQL, oracle requires the same quotes in the order-by
  253.         if ($dbType=='oci8' || $dbType=='oracle'{
  254.             $t str_replace ('ORDER BY '''$orderby);        // remove "ORDER BY" for easier parsing
  255.             $t str_replace ('order by '''$t);              // remove "order by" for easier parsing
  256.  
  257.             $pntables pnDBGetTables();
  258.             $columns  $pntables["{$tablename}_column"];
  259.  
  260.             // anything which doesn't look like a basic ORDER BY clause (with possibly an ASC/DESC modifier)
  261.             // we don't touch. To use such stuff with Oracle, you'll have to apply the quotes yourself.
  262.  
  263.             $tokens explode (','$t);                         // split on comma
  264.             foreach ($tokens as $k=>$v{
  265.                 $v trim ($v);
  266.                 if (strpos ($v' '=== false{                // 1 word
  267.                     if (strpos ($v'('=== false{            // not a function call
  268.                         if (strpos ($v'"'=== false{        // not surrounded by quotes already
  269.                             if (isset($columns[$v])) {           // ensure that token is an alias
  270.                                 $tokens[$k'"' $v '"';    // surround it by quotes
  271.                             }
  272.                         }
  273.                     }
  274.                 }
  275.                 else {                                           // multiple words, perform a few basic hecks
  276.                     $ttok explode (' '$v);                   // split on space
  277.                     if (count($ttok== 2{                     // see if we have 2 tokens
  278.                         $t1 strtolower(trim($ttok[0]));
  279.                         $t2 strtolower(trim($ttok[1]));
  280.                         $haveQuotes strpos ($t1'"'=== false;
  281.                         $isAscDesc  (strpos($t2'asc')===|| strpos($t2'desc')===0);
  282.                         $isColumn   = isset($columns[$ttok[0]]);
  283.                         if ($haveQuotes && $isAscDesc && $isColumn{
  284.                             $ttok[0'"'.$ttok[0].'"';         // surround it by quotes
  285.                         }
  286.                     }
  287.                     $tokens[$kimplode (' '$ttok);
  288.                 }
  289.             }
  290.  
  291.             $orderby implode (', '$tokens);
  292.         }
  293.  
  294.         if (stristr($orderby'ORDER BY'=== false{
  295.             $orderby 'ORDER BY ' $orderby;
  296.         }
  297.  
  298.         return $orderby;
  299.     }
  300.  
  301.  
  302.     /**
  303.      * Convenience function to ensure that the field to be used as ORDER BY
  304.      * is not a CLOB/BLOB when using Oracle
  305.      *
  306.      * @param field     The field name to be used for order by
  307.      *
  308.      * @return string   the order-by-clause to be used, may be ''
  309.      */
  310.     function _checkOrderByField ($tablename=''$field='')
  311.     {
  312.         $orderby '';
  313.         if (!empty($field&& !empty($tablename)) {
  314.             $dbType      DBConnectionStack::getConnectionDBDriver();
  315.             $pntables    pnDBGetTables();
  316.             $columns     $pntables["{$tablename}_column"];
  317.             $columnsdef  $pntables["{$tablename}_column_def"];
  318.  
  319.             if ($dbType=='oci8' || $dbType=='oracle'{
  320.                 // we are using oracle - split up the field definition and check if it is defined as a LOB
  321.                 // oracle does not like LOBs in an ORDERBY
  322.                 $definition explode(' '$columnsdef[$field]);
  323.                 // [0] contains the dangerous information, either XL or B
  324.                 if (strtoupper($definition[0]<> 'XL' && strtoupper($definition[0]<> 'B'{
  325.                     // no LOB, no problem
  326.                     $orderby "ORDER BY $columns[$field]";
  327.                 }
  328.             else {
  329.                 $orderby "ORDER BY $columns[$field]";
  330.             }
  331.         }
  332.         return $orderby;
  333.     }
  334.  
  335.     /**
  336.      * Build a basic select clause for the specified table with the specified where and orderBy clause
  337.      *
  338.      * @param tablename      The tablename key for the PNTables structure
  339.      * @param where          The original where clause (optional) (default='')
  340.      * @param orderBy        The original order-by clause (optional) (default='')
  341.      * @param columnArray    The columns to marshall into the resulting object (optional) (default=null)
  342.      *
  343.      * @return Nothing, the order-by-clause is altered in place
  344.      */
  345.     function _getSelectAllColumnsFrom ($tablename$where=''$orderBy=''$columnArray=null)
  346.     {
  347.         $pntables pnDBGetTables();
  348.         $table    $pntables[$tablename];
  349.  
  350.         $query 'SELECT ' DBUtil::_getAllColumns($tablename$columnArray" FROM $table ";
  351.  
  352.         if (trim($where)) {
  353.             $query .= DBUtil::_checkWhereClause ($where' ';
  354.         }
  355.  
  356.         if (trim($orderBy)) {
  357.             $query .= DBUtil::_checkOrderByClause ($orderBy$tablename' ';
  358.         }
  359.  
  360.         return $query;
  361.     }
  362.  
  363.  
  364.     /**
  365.      * Same as PN Api function but without AS aliasing
  366.      *
  367.      * @param tablename      The tablename key for the PNTables structure
  368.      * @param columnArray    The columns to marshall into the resulting object (optional) (default=null)
  369.      *
  370.      * @return The generated sql string
  371.      */
  372.     function _getAllColumns ($tablename$columnArray=null)
  373.     {
  374.         $pntables pnDBGetTables();
  375.         $columns  $pntables["{$tablename}_column"];
  376.         $queries  array();
  377.  
  378.         if (!$columns{
  379.             return pn_exit ("Invalid table-key [$tablename] passed to _getAllColumns");
  380.         }
  381.  
  382.         $query '';
  383.         foreach ($columns as $key => $val{
  384.             if (!$columnArray || in_array($key$columnArray)) {
  385.                 $queries["$val AS \"$key\"";
  386.             }
  387.         }
  388.  
  389.         if (!$queries && $columnArray{
  390.             return pn_exit ("Empty query generated for [$tablename] filtered by columnArray: ");
  391.         }
  392.  
  393.         return implode (','$queries);
  394.     }
  395.  
  396.  
  397.     /**
  398.      * Same as PN Api function but returns fully qualified fieldnames
  399.      *
  400.      * @param tablename      The tablename key for the PNTables structure
  401.      * @param tablealias     The SQL table alias to use in the SQL statement
  402.      * @param columnArray    The columns to marshall into the resulting object (optional) (default=null)
  403.      *
  404.      * @return The generated sql string
  405.      */
  406.     function _getAllColumnsQualified ($tablename$tablealias$columnArray=null)
  407.     {
  408.         $pntables pnDBGetTables();
  409.         $columns  $pntables["{$tablename}_column"];
  410.         $queries  array();
  411.  
  412.         if (!$columns{
  413.             return pn_exit ("Invalid table-key [$tablename] passed to _getAllColumns");
  414.         }
  415.  
  416.         foreach ($columns as $key => $val{
  417.             if (!$columnArray || in_array($key$columnArray)) {
  418.                 $queries["$tablealias.$val AS \"$key\"";
  419.             }
  420.         }
  421.  
  422.         if (!$queries && $columnArray{
  423.             return pn_exit ("Empty query generated for [$tablename] filtered by columnArray: ");
  424.         }
  425.  
  426.         return implode (','$queries);
  427.     }
  428.  
  429.  
  430.     /**
  431.      * Format value for use in SQL statement
  432.      *
  433.      * Special handling for integers and booleans (the last is required for MySQL 5 strict mode)
  434.      * @param @value mixed the value to format
  435.      * @return string string ready to add to SQL statement
  436.      */
  437.     function _formatForStore($value)
  438.     {
  439.         if (is_int($value))
  440.             return (int)$value// No need to DataUtil::formatForStore when casted to int
  441.         else if ($value === false)   // Avoid SQL strict problems where false would be stored as ''
  442.             return 0;
  443.         else if ($value === true)
  444.             return 1;
  445.         else
  446.             return '\'' DataUtil::formatForStore((string)$value'\'';
  447.     }
  448.  
  449.  
  450.     /**
  451.      * return the column array for the given table
  452.      *
  453.      * @param tablename      The tablename key for the PNTables structure
  454.      * @param columnArray    The columns to marshall into the resulting object (optional) (default=null)
  455.      *
  456.      * @return The column array for the given table
  457.      */
  458.     function getColumnsArray ($tablename$columnArray=null)
  459.     {
  460.         $pntables pnDBGetTables();
  461.         $tkey     $tablename '_column';
  462.         $ca       array ();
  463.  
  464.         if (!isset($pntables[$tkey])) {
  465.             return $ca;
  466.         }
  467.  
  468.         $cols $pntables[$tkey];
  469.         foreach ($cols as $key => $val)
  470.         {
  471.             // since the key is plain name, we take it rather
  472.             // than the value to construct object fields from
  473.             //$ca[] = $column[$key];
  474.             if (!$columnArray || in_array($key$columnArray)) {
  475.                 $ca[$key;
  476.             }
  477.         }
  478.  
  479.         if (!$ca && $columnArray{
  480.             return pn_exit ("Empty column array generated for [$tablename] filtered by columnArray: ");
  481.         }
  482.  
  483.         return $ca;
  484.     }
  485.  
  486.  
  487.     /**
  488.      * Transform a SQL query result set into an object/array, optionally applying an PN permission filter
  489.      *
  490.      * @param result           The result set we wish to marshall
  491.      * @param objectColumns    The column array to map onto the result set
  492.      * @param closeResultSet   whether or not to close the supplied result set (optional) (default=true)
  493.      * @param assocKey         The key field to use to build the associative index (optional) (default='')
  494.      * @param clean            whether or not to clean up the fmarshalled data (optional) (default=true)
  495.      * @param permissionFilter The permission structure to use for permission checking (optional) (default=null)
  496.      * @param tablename        The tablename we're marshalling results for, used for mssql date conversion (optional) (default=null)
  497.      *
  498.      * @return The marhalled array of objects
  499.      */
  500.     function marshallObjects ($result$objectColumns$closeResultSet=true,
  501.                               $assocKey=''$clean=true$permissionFilter=null$tablename=null)
  502.     {
  503.         if (!$result{
  504.             return pn_exit ("Invalid result received");
  505.         }
  506.  
  507.         if (!$objectColumns{
  508.             return pn_exit ("Invalid objectColumns received");
  509.         }
  510.  
  511.         if ($assocKey && !in_array($assocKey$objectColumns)) {
  512.             return pn_exit ("Unable to find assocKey [$assocKey] in objectColumns for $tablename");
  513.         }
  514.  
  515.         // since the single-object selects don't need to init
  516.         // the paging logic, we ensure values are set here
  517.         // in order to avoid E_ALL issues
  518.         if (!isset($GLOBALS['DBUtilMarshallObjectCount'])) {
  519.             DBUtil::_setFetchedObjectCount (0);
  520.             DBUtil::_setMarshalledObjectCount (0);
  521.         }
  522.  
  523.         $object      array ();
  524.         $objectArray array ();
  525.         $cSize       count($objectColumns);
  526.         $fCount      0;
  527.         $dbType      DBConnectionStack::getConnectionDBType();
  528.         $haveCExt    extension_loaded('adodb');
  529.         for !$result->EOF($haveCExt adodb_movenext($result$result->MoveNext())$fCount++{
  530.             // in PHP 5 can do:
  531.             // $object = array_combine($objectColumns,$result->fields);
  532.             // for now we do:
  533.             for ($j=0$j<$cSize$j++{
  534.                 $col $objectColumns[$j];
  535.                 $object[$col$result->fields[$j];
  536.  
  537.                 if ($clean{
  538.                     // clean up MySql encoding
  539.                     if ($dbType=='mysql' || $dbType=='mysqli'{
  540.                         if ($GLOBALS['PNRuntime']['magic_quotes_runtime']{
  541.                             pnStripSlashes($object[$col]);
  542.                         }
  543.                     }
  544.                     else
  545.                     // HACK/TODO/FIXME: hack for mssql which returns ' ' for ''
  546.                     if ($dbType=='mssql'{
  547.                         if ($object[$col== ' '{
  548.                             $object[$col'';
  549.                         }
  550.                     }
  551.                     else
  552.                     // HACK/TODO/FIXME: hack for oracle
  553.                     if ($dbType=='oci8' || $dbType=='oracle'{
  554.                         if ($object[$col== '""'{
  555.                             $object[$col''// oracle equates empty string with NULL
  556.                         }
  557.                         else {
  558.                             $object[$colstr_replace ("''""'"$object[$col])// oracle returns "''" for what should be "'"
  559.                         }
  560.                     }
  561.                 }
  562.             }
  563.  
  564.             $havePerm true;
  565.             if ($permissionFilter{
  566.                 if (!is_array($permissionFilter)) {
  567.                     return pn_exit ('Permission filter is not an array');
  568.                 }
  569.  
  570.                 // we need an array of arrays, but this will fix a single array
  571.                 if (!is_array($permissionFilter[0])) {
  572.                     $permissionFilter array($permissionFilter);
  573.                 }
  574.  
  575.                 $ak array_keys ($permissionFilter);
  576.                 foreach ($ak as $k{   // einmal AK, immer AK ;-)
  577.                     $pf $permissionFilter[$k];
  578.                     if (!is_array($pf)) {
  579.                         return pn_exit ('Permission filter iterator did not return an array (must be an array of arrays)');
  580.                     }
  581.  
  582.                     $cl  (isset($pf['component_left'])   $pf['component_left']   '');
  583.                     $cm  (isset($pf['component_middle']$pf['component_middle''');
  584.                     $cr  (isset($pf['component_right'])  $pf['component_right']  '');
  585.                     $il  (isset($pf['instance_left'])    $pf['instance_left']    '');
  586.                     $im  (isset($pf['instance_middle'])  $pf['instance_middle']  '');
  587.                     $ir  (isset($pf['instance_right'])   $pf['instance_right']   '');
  588.                     $oil ($il && isset($object[$il]$object[$il'__PN_PERM_NO_SUCH_ITEM__');
  589.                     $oim ($im && isset($object[$im]$object[$im'__PN_PERM_NO_SUCH_ITEM__');
  590.                     $oir ($ir && isset($object[$ir]$object[$ir'__PN_PERM_NO_SUCH_ITEM__');
  591.                     // not really needed
  592.                     // $realm = (isset($pf['realm']) && $pf['realm']>0 ? $pf['realm'] : 0);
  593.                     $level (isset($pf['level']&& $pf['level'$pf['level'false);
  594.  
  595.                     if (!$cl && !$cm && !$cr{
  596.                         return pn_exit ("Permission filter component is empty: [$cl], [$cm], [$cr]");
  597.                     }
  598.  
  599.                     if (!$il && !$im && !$ir{
  600.                         return pn_exit ("Permission filter instance is empty: [$il], [$im], [$ir]");
  601.                     }
  602.  
  603.                     if ($oil=='__PN_PERM_NO_SUCH_ITEM__' && $oim=='__PN_PERM_NO_SUCH_ITEM__' && $oir=='__PN_PERM_NO_SUCH_ITEM__'{
  604.                         return pn_exit ("Permission filter instance is invalid: [$oil], [$oim], [$oir]");
  605.                     }
  606.  
  607.                     if (!$level{
  608.                         return pn_exit ("Permission filter level is invalid: [$level]");
  609.                     }
  610.  
  611.                     if (!SecurityUtil::checkPermission("$cl:$cm:$cr""$oil:$oim:$oir"$level)) {
  612.                         $havePerm false;
  613.                         break;
  614.                     }
  615.                 }
  616.             }
  617.  
  618.             if ($havePerm{
  619.                 if ($assocKey{
  620.                     $key $object[$assocKey];
  621.                     $objectArray[$key$object;
  622.                 else {
  623.                     $objectArray[$object;
  624.                 }
  625.             }
  626.         }
  627.  
  628.         global $PNConfig;
  629.         global $PNRuntime;
  630.         $uid  SessionUtil::getVar('uid'0);
  631.         $suid $PNConfig['Debug']['sql_user'];
  632.  
  633.         if ($PNConfig['Debug']['sql_detail']{
  634.             if (!$suid || (is_array($suid&& in_array($uid)) || ($suid === $uid)) {
  635.                 $last count($PNRuntime['sql']);
  636.                 $PNRuntime['sql'][$last-1]['rows_marshalled'count($objectArray);
  637.             }
  638.         }
  639.  
  640.         if ($PNConfig['Debug']['sql_data']{
  641.             if (!$suid || (is_array($suid&& in_array($uid)) || ($suid === $uid)) {
  642.                 $last count($PNRuntime['sql']);
  643.                 $PNRuntime['sql'][$last-1]['rows'$objectArray;
  644.             }
  645.         }
  646.  
  647.         if ($closeResultSet{
  648.             $result->Close();
  649.         }
  650.  
  651.         DBUtil::_addMarshalledObjectCount(count($objectArray));
  652.         DBUtil::_setFetchedObjectCount($fCount);
  653.  
  654.         return $objectArray;
  655.     }
  656.  
  657.  
  658.     /**
  659.      * Transform a result set into an array of field values
  660.      *
  661.      * @param result          The result set we wish to marshall
  662.      * @param closeResultSet  whether or not to close the supplied result set (optional) (default=true)
  663.      * @param assocKey        The key field to use to build the associative index (optional) (default='')
  664.      *
  665.      * @return The resulting field array
  666.      */
  667.     function marshallFieldArray ($result$closeResultSet=true$assocKey=''$clean=true)
  668.     {
  669.         if (!$result{
  670.             return pn_exit ("Invalid result received");
  671.         }
  672.  
  673.         $fieldArray array ();
  674.         $dbType     DBConnectionStack::getConnectionDBType();
  675.         $haveCExt   extension_loaded('adodb');
  676.         for ($i=0!$result->EOF($haveCExt adodb_movenext($result$result->MoveNext())$i++{
  677.             $t $result->fields[0];
  678.  
  679.             if ($clean{
  680.                 // clean up MySql encoding
  681.                 if ($dbType=='mysql' || $dbType=='mysqli'{
  682.                     if ($GLOBALS['PNRuntime']['magic_quotes_runtime']{
  683.                         pnStripSlashes($t);
  684.                     }
  685.                 }
  686.                 else
  687.                 // HACK/TODO/FIXME: hack for mssql which returns ' ' for ''
  688.                 if ($dbType=='mssql'{
  689.                     if ($t == ' '{
  690.                         $t '';
  691.                     }
  692.                 }
  693.                 else
  694.                 // HACK/TODO/FIXME: hack for oracle
  695.                 if ($dbType=='oci8' || $dbType=='oracle'{
  696.                     if ($t == '""'{
  697.                         $t ''// oracle equates empty string with NULL
  698.                     }
  699.                     else {
  700.                         $t str_replace ("''""'"$t)// oracle returns "''" for what should be "'"
  701.                     }
  702.                 }
  703.             }
  704.  
  705.             if ($assocKey{
  706.                 $f1 $result->fields[1];
  707.                 $fieldArray[$f1$t;
  708.             else {
  709.                 $fieldArray[$i$t;
  710.             }
  711.         }
  712.  
  713.         if ($closeResultSet{
  714.             $result->Close();
  715.         }
  716.  
  717.         global $PNConfig;
  718.         if ($PNConfig['Debug']['sql_detail']{
  719.             $uid  SessionUtil::getVar('uid'0);
  720.             $suid $PNConfig['Debug']['sql_user'];
  721.             if (!$suid || (is_array($suid&& in_array($uid)) || ($suid === $uid)) {
  722.                 global $PNRuntime;
  723.                 $last count($PNRuntime['sql']);
  724.                 $PNRuntime['sql'][$last-1]['rows_marshalled'count($fieldArray);
  725.             }
  726.         }
  727.  
  728.         return $fieldArray;
  729.     }
  730.  
  731.  
  732.     /**
  733.      * Build a list of objects which are mapped to the specified categories
  734.      *
  735.      * @param categoryFilter   The category list to use for filtering
  736.      * @param returnArray      Whether or not to return an array (optional) (default=false)
  737.      *
  738.      * @return The resulting string or array
  739.      */
  740.     function _generateCategoryFilter ($tablename$categoryFilter$returnArray=false)
  741.     {
  742.         if (!$categoryFilter)
  743.             return '';
  744.  
  745.         if (!pnModDBInfoLoad('Categories'))
  746.             return '';
  747.  
  748.         // check the meta data
  749.         if (isset($categoryFilter['__META__']['module'])) {
  750.             $modname $categoryFilter['__META__']['module'];
  751.             unset($categoryFilter['__META__']);
  752.         else {
  753.             $modname pnModGetName();
  754.         }
  755.  
  756.         // get the properties IDs in the category register
  757.         Loader::loadClass('CategoryRegistryUtil');
  758.         $propids CategoryRegistryUtil::getRegisteredModuleCategoriesIds($modname$tablename);
  759.  
  760.         // build the where clause
  761.         $where array();
  762.         foreach ($categoryFilter as $property => $category{
  763.             // this allows to have an array of categories IDs
  764.             if (is_array($category)) {
  765.                 $wherecat array();
  766.                 foreach ($category as $cat{
  767.                     $wherecat["cmo_category_id='".DataUtil::formatForStore($cat)."'";
  768.                 }
  769.                 $wherecat '('.implode(' OR '$wherecat).')';
  770.  
  771.             // if there's only one category ID
  772.             else {
  773.                 $wherecat "cmo_category_id='".DataUtil::formatForStore($category)."'";
  774.             }
  775.             $where["(cmo_reg_id='".DataUtil::formatForStore($propids[$property])."' AND $wherecat)";
  776.         }
  777.         $where  "cmo_table='".DataUtil::formatForStore($tablename)."' AND (".implode(' OR '$where).')';
  778.  
  779.         // perform the query
  780.         $objIds DBUtil::selectFieldArray ('categories_mapobj''obj_id'$where);
  781.  
  782.         // this ensures that we return an empty set if no objects are mapped to the requested categories
  783.         if (!$objIds)
  784.             $objIds[= -1;
  785.  
  786.         if ($returnArray)
  787.             return $objIds;
  788.  
  789.         return implode (','$objIds);
  790.     }
  791.  
  792.  
  793.     /**
  794.      * Append the approriate category filter where-clause to the given where clause.
  795.      *
  796.      * @param tablename        The tablename key for the PNTables structure
  797.      * @param where            The where clause (optional) (default='')
  798.      * @param categoryFilter   The category list to use for filtering
  799.      * @param returnArray      Whether or not to return an array (optional) (default=false)
  800.      * @param usesJoin         Whether a join is used (if yes, then a prefix is prepended to the column name) (optional) (default=false)
  801.      *
  802.      * @return The resulting string or array
  803.      */
  804.     function generateCategoryFilterWhere ($tablename$where$categoryFilter$returnArray=false$usesJoin=false)
  805.     {
  806.         $idlist DBUtil::_generateCategoryFilter ($tablename$categoryFilter);
  807.         if ($idlist)
  808.         {
  809.             $pntables pnDBGetTables();
  810.             $cols     $pntables["{$tablename}_column"];
  811.             $idcol    = isset($pntables["{$tablename}_primary_key_column"]$pntables["{$tablename}_primary_key_column"'id';
  812.  
  813.             $and      ($where ' AND ' '');
  814.             $tblName  ($usesJoin 'tbl.' ''$cols[$idcol];
  815.             $where   .= "$and $tblName IN ($idlist)";
  816.         }
  817.  
  818.         return $where;
  819.     }
  820.  
  821.  
  822.     /**
  823.      * Select & return a field array
  824.      *
  825.      * @param tablename    The tablename key for the PNTables structure
  826.      * @param field        The name of the field we wish to marshall
  827.      * @param where        The where clause (optional) (default='')
  828.      * @param orderby      The orderby clause (optional) (default='')
  829.      * @param distinct     whether or not to add a 'DISTINCT' clause (optional) (default=false)
  830.      * @param assocKey     The key field to use to build the associative index (optional) (default='')
  831.      *
  832.      * @return The resulting field array
  833.      */
  834.     function selectFieldArray ($tablename$field$where=''$orderby=''$distinct=false$assocKey='')
  835.     {
  836.         $pntables pnDBGetTables();
  837.         $columns  $pntables["{$tablename}_column"];
  838.         $table    $pntables[$tablename];
  839.         $where    DBUtil::_checkWhereClause ($where);
  840.  
  841.         if ($orderby{
  842.             $orderby DBUtil::_checkOrderByClause ($orderby$tablename);
  843.         else {
  844.             $orderby DBUtil::_checkOrderByField($tablename$field)// "ORDER BY $columns[$field]";
  845.         }
  846.  
  847.         $dSql  ($distinct "DISTINCT($columns[$field])"$columns[$field]");
  848.         $assoc ($assocKey "$columns[$assocKey]'');
  849.         $sql   "SELECT $dSql $assoc FROM $table $where $orderby";
  850.         $res   DBUtil::executeSQL ($sql);
  851.         if ($res === false{
  852.             return $res;
  853.         }
  854.  
  855.         return DBUtil::marshallFieldArray ($restrue$assocKey);
  856.     }
  857.  
  858.  
  859.     /**
  860.      * Select & return an array of field by an ID-field value
  861.      *
  862.      * @param tablename    The tablename key for the PNTables structure
  863.      * @param field        The field we wish to select
  864.      * @param id           The ID value we wish to select with
  865.      * @param idfield       The idfield to use (optional) (default='id');
  866.      *
  867.      * @return The resulting field value
  868.      */
  869.     function selectFieldArrayByID ($tablename$field$id$idfield='id')
  870.     {
  871.         $pntables pnDBGetTables();
  872.         $cols     $pntables["{$tablename}_column"];
  873.         $where    $cols[$idfield"='" DataUtil::formatForStore($id"'";
  874.  
  875.         return DBUtil::selectFieldArray ($tablename$field$where);
  876.     }
  877.  
  878.  
  879.     /**
  880.      * Select & return an expanded field array
  881.      *
  882.      * @param tablename        The tablename key for the PNTables structure
  883.      * @param joinInfo         The array containing the extended join information
  884.      * @param field            The name of the field we wish to marshall
  885.      * @param where            The where clause (optional) (default='')
  886.      * @param orderby          The orderby clause (optional) (default='')
  887.      * @param distinct         whether or not to add a 'DISTINCT' clause (optional) (default=false)
  888.      * @param assocKey         The key field to use to build the associative index (optional) (default='')
  889.      * @param permissionFilter The permission filter to use for permission checking (optional) (default=null)
  890.      *
  891.      * @return The resulting field array
  892.      */
  893.     function selectExpandedFieldArray ($tablename$joinInfo$field$where=''$orderby='',
  894.                                        $distinct=false$assocKey=''$permissionFilter=null)
  895.     {
  896.         DBUtil::_setFetchedObjectCount (0);
  897.         DBUtil::_setMarshalledObjectCount (0);
  898.  
  899.         $pntables pnDBGetTables();
  900.         $columns  $pntables["{$tablename}_column"];
  901.         $table    $pntables[$tablename];
  902.  
  903.         $sqlJoinArray DBUtil::_processJoinArray($tablename$joinInfo);
  904.         $sqlJoin $sqlJoinArray[0];
  905.         $sqlJoinFieldList $sqlJoinArray[1];
  906.  
  907.         $where   DBUtil::_checkWhereClause ($where);
  908.         $orderby DBUtil::_checkOrderByClause ($orderby$tablename);
  909.  
  910.         $dSql  ($distinct "DISTINCT($columns[$field])"$columns[$field]");
  911.         $sqlStart "SELECT $dSql ";
  912.         $sqlFrom  "FROM $table AS tbl ";
  913.  
  914.         $sql "$sqlStart $sqlJoinFieldList $sqlFrom $sqlJoin $where $orderby";
  915.         $res DBUtil::executeSQL ($sql);
  916.         if ($res === false{
  917.             return $res;
  918.         }
  919.  
  920.         return DBUtil::marshallFieldArray ($restrue$assocKey);
  921.     }
  922.  
  923.  
  924.     /**
  925.      * Select & return the max/min value of a field
  926.      *
  927.      * @param tablename     The tablename key for the PNTables structure
  928.      * @param field         The name of the field we wish to marshall
  929.      * @param option        MIN, MAX, SUM or COUNT (optional) (default='MAX')
  930.      * @param where         The where clause (optional) (default='')
  931.      *
  932.      * @return The resulting min/max value
  933.      */
  934.     function selectFieldMax ($tablename$field$option='MAX'$where='')
  935.     {
  936.         $pntables pnDBGetTables();
  937.         $columns  $pntables["{$tablename}_column"];
  938.         $table    $pntables[$tablename];
  939.  
  940.         $sql "SELECT $option($columns[$field]) FROM $table";
  941.         $where DBUtil::_checkWhereClause ($where);
  942.  
  943.         $sql $sql ' ' $where;
  944.  
  945.         $res DBUtil::executeSQL ($sql);
  946.         if ($res === false{
  947.             return false;
  948.         }
  949.  
  950.         $max false;
  951.         if (!$res->EOF{
  952.             $max $res->fields[0];
  953.         }
  954.  
  955.         return $max;
  956.     }
  957.  
  958.  
  959.     /**
  960.      * Select & return the max/min array of a field grouped by the associated key
  961.      *
  962.      * @param tablename     The tablename key for the PNTables structure
  963.      * @param field         The name of the field we wish to marshall
  964.      * @param option        MIN, MAX, SUM or COUNT (optional) (default='MAX')
  965.      * @param where         The where clause (optional) (default='')
  966.      * @param assocKey      The key field to use to build the associative index (optional) (default='' which defaults to the primary key)
  967.      *
  968.      * @return The resulting min/max value
  969.      */
  970.     function selectFieldMaxArray ($tablename$field$option='MAX'$where=''$assocKey='')
  971.     {
  972.         $pntables pnDBGetTables();
  973.         $columns  $pntables["{$tablename}_column"];
  974.         $table    $pntables[$tablename];
  975.  
  976.         if (!$assocKey{
  977.             $assocKey = isset($pntables["{$tablename}_primary_key_column"]$pntables["{$tablename}_primary_key_column"'id';
  978.         }
  979.  
  980.         $sql "SELECT $assocKey AS $assocKey$option($columns[$field]) AS $option FROM $table";
  981.         $where DBUtil::_checkWhereClause ($where);
  982.  
  983.         $sql .= ' ' $where;
  984.         $sql .= ' ' "GROUP BY $assocKey";
  985.  
  986.         $res DBUtil::executeSQL ($sql);
  987.         if ($res === false{
  988.             return false;
  989.         }
  990.  
  991.         $objArray array();
  992.         foreach ($res as $row{
  993.             $objArray[$row[0]] $row[1];
  994.         }
  995.  
  996.         return $objArray;
  997.     }
  998.  
  999.  
  1000.     /**
  1001.      * Select & return a field by an ID-field value
  1002.      *
  1003.      * @param tablename    The tablename key for the PNTables structure
  1004.      * @param field        The field we wish to select
  1005.      * @param id           The ID value we wish to select with
  1006.      * @param idfield       The idfield to use (optional) (default='id');
  1007.      *
  1008.      * @return The resulting field value
  1009.      */
  1010.     function selectFieldByID ($tablename$field$id$idfield='id')
  1011.     {
  1012.         $pntables pnDBGetTables();
  1013.         $cols     $pntables["{$tablename}_column"];
  1014.         $where    $cols[$idfield"='" DataUtil::formatForStore($id"'";
  1015.  
  1016.         return DBUtil::selectField ($tablename$field$where);
  1017.     }
  1018.  
  1019.  
  1020.     /**
  1021.      * Select & return a field
  1022.      *
  1023.      * @param tablename     The tablename key for the PNTables structure
  1024.      * @param field         The name of the field we wish to marshall
  1025.      * @param where         The where clause (optional) (default='');
  1026.      *
  1027.      * @return The resulting field array
  1028.      */
  1029.     function selectField ($tablename$field$where='')
  1030.     {
  1031.         $fieldArray DBUtil::selectFieldArray ($tablename$field$where);
  1032.  
  1033.         if (count($fieldArray0{
  1034.             return $fieldArray[0];
  1035.         }
  1036.  
  1037.         return false;
  1038.     }
  1039.  
  1040.  
  1041.     /**
  1042.      * SQL cache interface
  1043.      *
  1044.      * Call without parameters to fetch cache. Call with $clear=true to clear the cache.
  1045.      * @param bool $clear 
  1046.      */
  1047.     function &SQLCache($clear=false)
  1048.     {
  1049.       static $SQLCache array();
  1050.  
  1051.       if ($clear{
  1052.           $SQLCache array();
  1053.       }
  1054.  
  1055.       return $SQLCache;
  1056.     }
  1057.  
  1058.  
  1059.     /**
  1060.      * Select & return a specific object using the given sql statement
  1061.      *
  1062.      * @param sql              The sql statement to execute for the selection
  1063.      * @param tablename        The tablename key for the PNTables structure
  1064.      * @param columnArray      The columns to marshall into the resulting object (optional) (default=null)
  1065.      * @param permissionFilter The permission filter to use for permission checking (optional) (default=null)
  1066.      *
  1067.      * @return The resulting object
  1068.      */
  1069.     function selectObjectSQL ($sql$tablename$columnArray=null$permissionFilter=null$cacheObject=true)
  1070.     {
  1071.         $cache =DBUtil::SQLCache();
  1072.  
  1073.         $permissionFilterKey '';
  1074.         if (is_array($permissionFilter)) {
  1075.             foreach($permissionFilter as $permissionRule{
  1076.                 $permissionFilterKey .= implode('_'$permissionRule);
  1077.             }
  1078.         }
  1079.         $key implode('_'array($sql$tablenameimplode('_'(array)$columnArray)$permissionFilterKey));
  1080.  
  1081.         if (!isset($cache[$key]|| ($cacheObject == false|| defined('_PNINSTALLVER')) {
  1082.             $res DBUtil::executeSQL ($sql01);
  1083.             if ($res === false{
  1084.                 return $res;
  1085.             }
  1086.             $ca DBUtil::getColumnsArray ($tablename$columnArray);
  1087.             $objArr DBUtil::marshallObjects ($res$catrue''true$permissionFilter$tablename);
  1088.             $cache[$key$objArr;
  1089.             if (count($objArr0{
  1090.                 return $objArr[0];
  1091.             }
  1092.         else {
  1093.             if (count($cache[$key]0{
  1094.                 return $cache[$key][0];
  1095.             }
  1096.             return $cache[$key];
  1097.         }
  1098.     }
  1099.  
  1100.  
  1101.     /**
  1102.      * Select & return a specific object based on a PN table definition
  1103.      *
  1104.      * @param tablename        The tablename key for the PNTables structure
  1105.      * @param where            The where clause (optional) (default='')
  1106.      * @param columnArray      The columns to marshall into the resulting object (optional) (default=null)
  1107.      * @param permissionFilter The permission filter to use for permission checking (optional) (default=null)
  1108.      * @param categoryFilter   The category list to use for filtering (optional) (default=null)
  1109.      *
  1110.      * @return The resulting object
  1111.      */
  1112.     function selectObject ($tablename$where=''$columnArray=null,
  1113.                            $permissionFilter=null$categoryFilter=null
  1114.                            $cacheObject=true)
  1115.     {
  1116.         $sql    DBUtil::_getSelectAllColumnsFrom($tablename$where''$columnArray);
  1117.         $object DBUtil::selectObjectSQL ($sql$tablename$columnArray$permissionFilter$cacheObject);
  1118.  
  1119.         // since we're dealing with a single object, we
  1120.         // just check it's presence in the category mapping array
  1121.         $idarr  DBUtil::_generateCategoryFilter ($tablename$categoryFiltertrue);
  1122.         $pntables pnDBGetTables();
  1123.         $idcol    = isset($pntables["{$tablename}_primary_key_column"]$pntables["{$tablename}_primary_key_column"'id';
  1124.         if ($idarr && $idcol && !in_array($object[$idcol]$idarr))
  1125.             return array();
  1126.  
  1127.         $object DBUtil::_selectPostProcess ($object$tablename$idcol);
  1128.         return $object;
  1129.     }
  1130.  
  1131.  
  1132.     /**
  1133.      * Return the number of rows affected
  1134.      *
  1135.      * @param tablename     The tablename key for the PNTables structure
  1136.      * @param where         The where clause (optional) (default='')
  1137.      * @param column        The column to place in the count phrase (optional) (default='*')
  1138.      * @param distinct      whether or not to count distinct entries (optional) (default='false')
  1139.      * @param categoryFilter   The category list to use for filtering (optional) (default=null)
  1140.      *
  1141.      * @return The resulting object count
  1142.      */
  1143.     function selectObjectCount ($tablename$where=''$column='1'$distinct=false$categoryFilter=null)
  1144.     {
  1145.         $pntables pnDBGetTables();
  1146.         $table    $pntables[$tablename];
  1147.         $cols     $pntables["{$tablename}_column"];
  1148.  
  1149.         $where DBUtil::generateCategoryFilterWhere ($tablename$where$categoryFilter);
  1150.         $where DBUtil::_checkWhereClause ($where);
  1151.  
  1152.         $dst ($distinct && $column!='1' 'DISTINCT' '');
  1153.         $col ($column === '1' '1' $cols[$column]);
  1154.         $sql "SELECT COUNT($dst $col) FROM $table $where";
  1155.  
  1156.         $res DBUtil::executeSQL ($sql);
  1157.         if ($res === false{
  1158.             return $res;
  1159.         }
  1160.  
  1161.         $count false;
  1162.         if (!$res->EOF{
  1163.             $count $res->fields[0];
  1164.         }
  1165.  
  1166.         return $count;
  1167.     }
  1168.  
  1169.  
  1170.     /**
  1171.      * Select an object count by ID
  1172.      *
  1173.      * @param tablename     The tablename key for the PNTables structure
  1174.      * @param id            The id value to match
  1175.      * @param field         The field to match the ID against (optional) (default='id')
  1176.      * @param transformFunc Transformation function to apply to $id (optional) (default=null)
  1177.      *
  1178.      * @return The resulting object count
  1179.      */
  1180.     function selectObjectCountByID ($tablename$id$field='id'$transformFunc='')
  1181.     {
  1182.         if (!$id{
  1183.             return pn_exit ('DBUtil::selectObjectCountByID: empty id supplied');
  1184.         }
  1185.  
  1186.         if ($field == 'id' && !is_numeric($id)) {
  1187.             return pn_exit ('DBUtil::selectObjectCountByID: non-numeric id supplied');
  1188.         }
  1189.  
  1190.         $pntables pnDBGetTables();
  1191.         $cols     $pntables["{$tablename}_column"];
  1192.         if ($transformFunc{
  1193.             $where "$transformFunc($cols[$field])='DataUtil::formatForStore($id"'";
  1194.         else 
  1195.             $where $cols[$field"='" DataUtil::formatForStore($id"'";
  1196.         }
  1197.  
  1198.         return DBUtil::selectObjectCount ($tablename$where$field);
  1199.     }
  1200.  
  1201.  
  1202.     /**
  1203.      * Return the number of rows affected
  1204.      *
  1205.      * @param tablename     The tablename key for the PNTables structure
  1206.      * @param joinInfo      The array containing the extended join information
  1207.      * @param where         The where clause (optional) (default='')
  1208.      * @param distinct      whether or not to count distinct entries (optional) (default='false')
  1209.      * @param categoryFilter   The category list to use for filtering (optional) (default=null)
  1210.      *
  1211.      * @return The resulting object count
  1212.      */
  1213.     function selectExpandedObjectCount ($tablename$joinInfo$where=''$distinct=false$categoryFilter=null)
  1214.     {
  1215.         DBUtil::_setFetchedObjectCount (0);
  1216.         DBUtil::_setMarshalledObjectCount (0);
  1217.  
  1218.         $pntables pnDBGetTables();
  1219.         $table    $pntables[$tablename];
  1220.         $columns  $pntables["{$tablename}_column"];
  1221.  
  1222.         $sqlJoinArray DBUtil::_processJoinArray($tablename$joinInfo);
  1223.         $sqlJoin $sqlJoinArray[0];
  1224.         $sqlJoinFieldList $sqlJoinArray[1];
  1225.  
  1226.         $where    DBUtil::generateCategoryFilterWhere ($tablename$where$categoryFilterfalsetrue);
  1227.         $where    DBUtil::_checkWhereClause ($where);
  1228.         $dst      ($distinct 'DISTINCT' '');
  1229.         $sqlStart "SELECT COUNT($dst *) ";
  1230.         $sqlFrom  "FROM $table AS tbl ";
  1231.  
  1232.         $sql "$sqlStart $sqlJoinFieldList $sqlFrom $sqlJoin $where GROUP BY NULL";
  1233.         $res DBUtil::executeSQL ($sql);
  1234.         if ($res === false{
  1235.             return $res;
  1236.         }
  1237.  
  1238.         $count false;
  1239.         if (!$res->EOF{
  1240.             $count $res->fields[0];
  1241.         }
  1242.  
  1243.         return $count;
  1244.     }
  1245.  
  1246.  
  1247.     /**
  1248.      * Return the sum of a column
  1249.      *
  1250.      * @param tablename     The tablename key for the PNTables structure
  1251.      * @param column        The column to place in the sum phrase
  1252.      * @param where         The where clause (optional) (default='')
  1253.      * @param categoryFilter   The category list to use for filtering (optional) (default=null)
  1254.      *
  1255.      * @return The resulting column sum
  1256.      */
  1257.     function selectObjectSum ($tablename$column$where=''$categoryFilter=null)
  1258.     {
  1259.         $pntables pnDBGetTables();
  1260.         $table    $pntables[$tablename];
  1261.         $cols     $pntables["{$tablename}_column"];
  1262.  
  1263.         $where DBUtil::generateCategoryFilterWhere ($tablename$where$categoryFilter);
  1264.         $where DBUtil::_checkWhereClause ($where);
  1265.  
  1266.         $col $cols[$column];
  1267.         $sql "SELECT SUM($col) FROM $table $where";
  1268.  
  1269.         $res DBUtil::executeSQL ($sql);
  1270.         if ($res === false{
  1271.             return $res;
  1272.         }
  1273.  
  1274.         $sum false;
  1275.         if (!$res->EOF{
  1276.             $sum $res->fields[0];
  1277.         }
  1278.  
  1279.         return $sum;
  1280.     }
  1281.  
  1282.  
  1283.     /**
  1284.      * Object cache interface
  1285.      *
  1286.      * Call without parameters to fetch cache. Call with $clear=true to clear the cache (optionally only
  1287.      * for one specific table).
  1288.      * @param bool $clear 
  1289.      * @param string $tablename 
  1290.      */
  1291.     function &objectCache($clear=false$tablename=null)
  1292.     {
  1293.       static $objectCache array();
  1294.  
  1295.       if ($clear)
  1296.       {
  1297.           if ($tablename == null)
  1298.               $objectCache array();
  1299.           else
  1300.               $objectCache[$tablenamearray();
  1301.  
  1302.           // Clear also underlying SQL cache
  1303.           DBUtil::SQLCache(true);
  1304.       }
  1305.  
  1306.       return $objectCache;
  1307.     }
  1308.  
  1309.  
  1310.     /**
  1311.      * Select & return a specific object by using the ID field
  1312.      *
  1313.      * @param tablename        The tablename key for the PNTables structure
  1314.      * @param id               The object ID to query
  1315.      * @param field            The field key which holds the ID value (optional) (default='id')
  1316.      * @param columnArray      The columns to marshall into the resulting object (optional) (default=null)
  1317.      * @param permissionFilter The permission structure to use for permission checking (optional) (default=null)
  1318.      * @param categoryFilter   The category list to use for filtering (optional) (default=null)
  1319.      * @param cacheObject      If true returns a cached object if available (optional) (default=true)
  1320.      * @param transformFunc    Transformation function to apply to $id (optional) (default=null)
  1321.      *
  1322.      * @return The resulting object
  1323.      */
  1324.     function selectObjectByID ($tablename$id$field='id'$columnArray=null,
  1325.                                $permissionFilter=null$categoryFilter=null$cacheObject=true$transformFunc=null)
  1326.     {
  1327.         if (!$id{
  1328.             return pn_exit ('DBUtil::selectObjectByID: empty id supplied');
  1329.         }
  1330.  
  1331.         if ($field == 'id' && !is_numeric($id)) {
  1332.             return pn_exit ('DBUtil::selectObjectByID: non-numeric id supplied');
  1333.         }
  1334.  
  1335.         // avoid double get for title hack, etc.
  1336.         $objectCache =DBUtil::objectCache();
  1337.         if (isset($objectCache[$tablename][$field][$id]&& !defined('_PNINSTALLVER'&& $cacheObject &&
  1338.             !$categoryFilter && !$permissionFilter{
  1339.             return $objectCache[$tablename][$field][$id];
  1340.         }
  1341.  
  1342.         $pntables pnDBGetTables();
  1343.         $cols     $pntables["{$tablename}_column"];
  1344.         if ($transformFunc{
  1345.             $where "$transformFunc($cols[$field])='DataUtil::formatForStore($id"'";
  1346.         else {
  1347.             $where $cols[$field"='" DataUtil::formatForStore($id"'";
  1348.         }
  1349.  
  1350.         $obj   DBUtil::selectObject ($tablename$where$columnArray$permissionFilter$categoryFilter$cacheObject);
  1351.         $idcol = isset($pntables["{$tablename}_primary_key_column"]$pntables["{$tablename}_primary_key_column"'id';
  1352.         $obj   DBUtil::_selectPostProcess ($obj$tablename$idcol);
  1353.  
  1354.         $objectCache[$tablename][$field][$id$obj;
  1355.         return $obj;
  1356.     }
  1357.  
  1358.  
  1359.     /**
  1360.      * Execute SQL select statement and return the value of the first column in the first row
  1361.      *
  1362.      * Mostly usefull for places where you want to do a "select count(*)" or similar scalar selection.
  1363.      *
  1364.      * @return mixed selected value
  1365.      */
  1366.     function selectScalar($sql$exitOnError=true)
  1367.     {
  1368.         $res DBUtil::executeSQL ($sql);
  1369.         if ($res === false{
  1370.             return false;
  1371.         }
  1372.  
  1373.         $value null;
  1374.         if ($res->EOF{
  1375.             if ($exitOnError)
  1376.                 return pn_exit ("DBUtil::selectScalar got no rows to select from ... ");
  1377.         }
  1378.         else if (count($res->fields1{
  1379.             if ($exitOnError)
  1380.                 return pn_exit ("DBUtil::selectScalar got no columns to select from ... ");
  1381.         }
  1382.         else {
  1383.             $value $res->fields[0];
  1384.         }
  1385.  
  1386.         $res->close();
  1387.         return $value;
  1388.     }
  1389.  
  1390.  
  1391.     /**
  1392.      * Select & return a object with it's left join fields filled in
  1393.      *
  1394.      * @param tablename         The tablename key for the PNTables structure
  1395.      * @param joinInfo          The array containing the extended join information
  1396.      * @param where             The where clause (optional)
  1397.      * @param columnArray       The columns to marshall into the resulting object (optional) (default=null)
  1398.      * @param permissionFilter  The permission structure to use for permission checking (optional) (default=null)
  1399.      * @param categoryFilter   The category list to use for filtering (optional) (default=null)
  1400.      *
  1401.      * @return The resulting object
  1402.      */
  1403.     function selectExpandedObject ($tablename$joinInfo$where=''$columnArray=null,
  1404.                                    $permissionFilter=null$categoryFilter=null)
  1405.     {
  1406.         $objects DBUtil::selectExpandedObjectArray ($tablename$joinInfo$where''01,
  1407.                                                       ''$permissionFilter$categoryFilter$columnArray);
  1408.  
  1409.         if (count($objects)) {
  1410.             return $objects[0];
  1411.         }
  1412.  
  1413.         return $objects;
  1414.     }
  1415.  
  1416.  
  1417.     /**
  1418.      * Select & return an object by it's ID  with it's left join fields filled in
  1419.      *
  1420.      * @param tablename        The tablename key for the PNTables structure
  1421.      * @param joinInfo         The array containing the extended join information
  1422.      * @param id               The ID value to use for object retrieval
  1423.      * @param field            The field key which holds the ID value (optional) (default='id')
  1424.      * @param columnArray      The columns to marshall into the resulting object (optional) (default=null)
  1425.      * @param permissionFilter The permission structure to use for permission checking (optional) (default=null)
  1426.      * @param categoryFilter   The category list to use for filtering (optional) (default=null)
  1427.      * @param transformFunc    Transformation function to apply to $id (optional) (default=null)
  1428.      *
  1429.      * @return The resulting object
  1430.      */
  1431.     function selectExpandedObjectByID ($tablename$joinInfo$id$field='id'$columnArray=null,
  1432.                                        $permissionFilter=null$categoryFilter=null$transformFunc=null)
  1433.     {
  1434.         $pntables pnDBGetTables();
  1435.         $cols     $pntables["{$tablename}_column"];
  1436.  
  1437.         if ($transformFunc{
  1438.             $where "$transformFunc($cols[$field])='DataUtil::formatForStore($id"'";
  1439.         else {
  1440.             $where $cols[$field"='" DataUtil::formatForStore($id"'";
  1441.         }
  1442.  
  1443.         $object   DBUtil::selectExpandedObject ($tablename$joinInfo$where$columnArray$permissionFilter$categoryFilter);
  1444.         return $object;
  1445.     }
  1446.  
  1447.  
  1448.     /**
  1449.      * Select & return an object array based on a PN table definition
  1450.      *
  1451.      * @param tablename      The tablename key for the PNTables structure
  1452.      * @param where          The where clause (optional) (default='')
  1453.      * @param orderby        The order by clause (optional) (default='')
  1454.      * @param limitOffset    The lower limit bound (optional) (default=-1)
  1455.      * @param limitNumRows   The upper limit bound (optional) (default=-1)
  1456.      * @param assocKey       The key field to use to build the associative index (optional) (default='')
  1457.      * @param permissionFilter The permission filter to use for permission checking (optional) (default=null)
  1458.      * @param categoryFilter   The category list to use for filtering (optional) (default=null)
  1459.      * @param columnArray    The columns to marshall into the resulting object (optional) (default=null)
  1460.      *
  1461.      * @return The resulting object array
  1462.      */
  1463.     function selectObjectArray ($tablename$where=''$orderby=''$limitOffset=-1$limitNumRows=-1,
  1464.                                 $assocKey=''$permissionFilter=null$categoryFilter=null$columnArray=null)
  1465.     {
  1466.         DBUtil::_setFetchedObjectCount (0);
  1467.         DBUtil::_setMarshalledObjectCount (0);
  1468.  
  1469.         $where   DBUtil::generateCategoryFilterWhere ($tablename$where$categoryFilter);
  1470.         $where   DBUtil::_checkWhereClause ($where);
  1471.         $orderby DBUtil::_checkOrderByClause ($orderby$tablename);
  1472.  
  1473.         $objects array();
  1474.         $ca      DBUtil::getColumnsArray ($tablename$columnArray);
  1475.         $sql     DBUtil::_getSelectAllColumnsFrom($tablename$where$orderby$columnArray);
  1476.  
  1477.         do
  1478.         {
  1479.             $fc   DBUtil::_getFetchedObjectCount();
  1480.             $stmt $sql;
  1481.             $limitOffset += $fc;
  1482.  
  1483.             $res DBUtil::executeSQL ($stmt$limitOffset$limitNumRows);
  1484.             if ($res === false{
  1485.                 return $res;
  1486.             }
  1487.  
  1488.             $objArr DBUtil::marshallObjects ($res$catrue$assocKeytrue$permissionFilter$tablename);
  1489.             if ($objArr{
  1490.                 $objects $objects + (array)$objArr// append new array
  1491.             }
  1492.         }
  1493.         while ($permissionFilter && ($limitNumRows!=-&& $limitNumRows>0&&
  1494.                $fc>&& count($objects)<$limitNumRows);
  1495.  
  1496.         if ($limitNumRows != -&& count($objects$limitNumRows{
  1497.             $objects array_slice ($objects0$limitNumRows);
  1498.         }
  1499.  
  1500.         if (!DBConnectionStack::isDefaultConnection()) {
  1501.             return $objects;
  1502.         }
  1503.  
  1504.         $pntables pnDBGetTables();
  1505.         $idcolumn = isset($pntables["{$tablename}_primary_key_column"]$pntables["{$tablename}_primary_key_column"'id';
  1506.         $objects  DBUtil::_selectPostProcess ($objects$tablename$idcolumn);
  1507.  
  1508.         return $objects;
  1509.     }
  1510.  
  1511.  
  1512.     /**
  1513.      * Select & return an object array based on a PN table definition
  1514.      *
  1515.      * The result is filtered by a callback object passed into the function. This object must
  1516.      * have implemented a method called "checkResult" which is passed the resulting data rows
  1517.      * one by one. The "checkResult" function returns true if the datarow is ok, otherwise
  1518.      * it returns false.
  1519.      *
  1520.      * Example:
  1521.      * <code>
  1522.      * class myFilter
  1523.      * {
  1524.      *   var $userId;
  1525.      *
  1526.      *   function checkResult($datarow)
  1527.      *   {
  1528.      *     return $datarow['ownerUserId'] == $this->userId;
  1529.      *   }
  1530.      * }
  1531.      * </code>
  1532.      *
  1533.      * @param tablename      The tablename key for the PNTables structure
  1534.      * @param where          The where clause (optional) (default='')
  1535.      * @param orderby        The order by clause (optional) (default='')
  1536.      * @param limitOffset    The lower limit bound (optional) (default=-1)
  1537.      * @param limitNumRows   The upper limit bound (optional) (default=-1)
  1538.      * @param assocKey       The key field to use to build the associative index (optional) (default='')
  1539.      * @param filterCallback The filter callback object.
  1540.      * @param columnArray    The columns to marshall into the resulting object (optional) (default=null)
  1541.      *
  1542.      * @return The resulting object array
  1543.      */
  1544.     function selectObjectArrayFilter($tablename$where=''$orderby=''$limitOffset=-1$limitNumRows=-1,
  1545.                                      $assocKey=''$filterCallback$categoryFilter=null$columnArray=null)
  1546.     {
  1547.         DBUtil::_setFetchedObjectCount (0);
  1548.         DBUtil::_setMarshalledObjectCount (0);
  1549.  
  1550.         $where   DBUtil::generateCategoryFilterWhere ($tablename$where$categoryFilter);
  1551.         $where   DBUtil::_checkWhereClause ($where);
  1552.         $orderby DBUtil::_checkOrderByClause ($orderby$tablename);
  1553.  
  1554.         $objects array();
  1555.         $fc      0;
  1556.         $ca      DBUtil::getColumnsArray ($tablename$columnArray);
  1557.         $sql     DBUtil::_getSelectAllColumnsFrom($tablename$where$orderby$columnArray);
  1558.  
  1559.         do
  1560.         {
  1561.             $stmt $sql;
  1562.             $limitOffset += $fc;
  1563.  
  1564.             $res DBUtil::executeSQL ($stmt$limitOffset$limitNumRows);
  1565.             if ($res === false{
  1566.                 return $res;
  1567.             }
  1568.  
  1569.             $objArr DBUtil::marshallObjects ($res$catrue$assocKeytruenull$tablename);
  1570.             $fc DBUtil::_getFetchedObjectCount();
  1571.  
  1572.             for ($i=0,$cou=count($objArr)$i<$cou++$i)
  1573.             {
  1574.                 $obj $objArr[$i];
  1575.                 if ($filterCallback->checkResult($obj))
  1576.                     $objects[$obj;
  1577.             }
  1578.         }
  1579.         while ($limitNumRows!=-&& $limitNumRows>&& $fc>&& count($objects)<$limitNumRows);
  1580.  
  1581.         if (!DBConnectionStack::isDefaultConnection()) {
  1582.             return $objects;
  1583.         }
  1584.  
  1585.         $pntables pnDBGetTables();
  1586.         $idcolumn = isset($pntables["{$tablename}_primary_key_column"]$pntables["{$tablename}_primary_key_column"'id';
  1587.         $objects  DBUtil::_selectPostProcess ($objects$tablename$idcolumn);
  1588.  
  1589.         return $objects;
  1590.     }
  1591.  
  1592.  
  1593.     /**
  1594.      * Select & return an array of objects with it's left join fields filled in
  1595.      *
  1596.      * @param tablename     The tablename key for the PNTables structure
  1597.      * @param joinInfo      The array containing the extended join information
  1598.      * @param where         The where clause (optional) (default='')
  1599.      * @param orderby       The order by clause (optional) (default='')
  1600.      * @param limitOffset   The lower limit bound (optional) (default=-1)
  1601.      * @param limitNumRows  The upper limit bound (optional) (default=-1)
  1602.      * @param assocKey      The key field to use to build the associative index (optional) (default='')
  1603.      * @param permissionFilter  The permission filter to use for permission checking (optional) (default=null)
  1604.      * @param columnArray   The columns to marshall into the resulting object (optional) (default=null)
  1605.      *
  1606.      * @return The resulting object
  1607.      */
  1608.     function selectExpandedObjectArray ($tablename$joinInfo$where=''$orderby=''$limitOffset=-1$limitNumRows=-1,
  1609.                                         $assocKey=''$permissionFilter=null$categoryFilter=null$columnArray=null)
  1610.     {
  1611.         DBUtil::_setFetchedObjectCount (0);
  1612.         DBUtil::_setMarshalledObjectCount (0);
  1613.  
  1614.         $pntables pnDBGetTables();
  1615.         $columns  $pntables["{$tablename}_column"];
  1616.         $table    $pntables[$tablename];
  1617.  
  1618.         $sqlStart "SELECT " DBUtil::_getAllColumnsQualified ($tablename'tbl'$columnArray);
  1619.         $sqlFrom  "FROM $table AS tbl ";
  1620.  
  1621.         $sqlJoinArray DBUtil::_processJoinArray($tablename$joinInfo$columnArray);
  1622.         $sqlJoin $sqlJoinArray[0];
  1623.         $sqlJoinFieldList $sqlJoinArray[1];
  1624.         $ca $sqlJoinArray[2];
  1625.  
  1626.         $where DBUtil::generateCategoryFilterWhere ($tablename$where$categoryFilter);
  1627.  
  1628.         $where   DBUtil::_checkWhereClause ($where);
  1629.         $orderby DBUtil::_checkOrderByClause ($orderby$tablename);
  1630.  
  1631.         $objects array();
  1632.         $sql "$sqlStart $sqlJoinFieldList $sqlFrom $sqlJoin $where $orderby";
  1633.  
  1634.         do
  1635.         {
  1636.             $fc   DBUtil::_getFetchedObjectCount();
  1637.             $stmt $sql;
  1638.             $limitOffset += $fc;
  1639.  
  1640.             $res DBUtil::executeSQL ($stmt$limitOffset$limitNumRows);
  1641.             if ($res === false{
  1642.                 return $res;
  1643.             }
  1644.  
  1645.             $objArr  DBUtil::marshallObjects ($res$catrue$assocKeytrue$permissionFilter$tablename);
  1646.             if ($objArr{
  1647.                 $objects $objects + (array)$objArr// append new array
  1648.             }
  1649.         }
  1650.         while ($permissionFilter && ($limitNumRows!=-&& $limitNumRows>0&&
  1651.                $fc>&& count($objects)<$limitNumRows);
  1652.  
  1653.         if (count($objects$limitNumRows  &&  $limitNumRows>0{
  1654.             $objects array_slice ($objects0$limitNumRows);
  1655.         }
  1656.  
  1657.         if (!DBConnectionStack::isDefaultConnection()) {
  1658.             return $objects;
  1659.         }
  1660.  
  1661.         $idcolumn = isset($pntables["{$tablename}_primary_key_column"]$pntables["{$tablename}_primary_key_column"'id';
  1662.         $objects  DBUtil::_selectPostProcess ($objects$tablename$idcolumn);
  1663.         return $objects;
  1664.     }
  1665.  
  1666.  
  1667.     /**
  1668.      * Loop through the array and feed it to DBUtil::insertObject()
  1669.      *
  1670.      * @param objects       The objectArray we wish to insert
  1671.      * @param tablename     The tablename key for the PNTables structure
  1672.      * @param idcolumn      The column which stores the primary key (optional) (default='id')
  1673.      * @param preserve      whether or not to preserve existing/set standard fields (optional) (default=false)
  1674.      * @param force         whether or not to insert empty values as NULL (optional) (default=false)
  1675.      *
  1676.      * @return The result set from the last insert operation. The objects are updated with the newly generated ID.
  1677.      */
  1678.     function insertObjectArray (&$objects$tablename$idcolumn='id'$preserve=false$force=false)
  1679.     {
  1680.         if (!is_array ($objects)) {
  1681.             return pn_exit ('DBUtil::insertObjectArray: objects is not an array ... ');
  1682.         }
  1683.  
  1684.         $res false;
  1685.         $ak array_keys ($objects);
  1686.         foreach ($ak as $k{
  1687.             $res DBUtil::insertObject ($objects[$k]$tablename$idcolumn$preserve$force);
  1688.             if (!$res{
  1689.                 break;
  1690.             }
  1691.         }
  1692.  
  1693.         return $res;
  1694.     }
  1695.  
  1696.  
  1697.     /**
  1698.      * Generate and execute an insert SQL statement for the given object
  1699.      *
  1700.      * @param object        The object we wish to insert
  1701.      * @param tablename     The tablename key for the PNTables structure
  1702.      * @param idcolumn      The column which stores the primary key (optional) (default='id')
  1703.      * @param preserve      whether or not to preserve existing/set standard fields (optional) (default=false)
  1704.      * @param force         whether or not to insert empty values as NULL (optional) (default=false)
  1705.      *
  1706.      * @return The result set from the update operation. The object is updated with the newly generated ID.
  1707.      */
  1708.     function insertObject (&$object$tablename$idcolumn='id'$preserve=false$force=false)
  1709.     {
  1710.         if (!is_array ($object)) {
  1711.             return pn_exit ('DBUtil::insertObject: object is not an array ... ');
  1712.         }
  1713.  
  1714.         $dbconn   DBConnectionStack::getConnection();
  1715.         $pntables pnDBGetTables();
  1716.         $table    $pntables[$tablename];
  1717.         $sql      "INSERT INTO $table ";
  1718.  
  1719.         // set standard architecture fields
  1720.         ObjectUtil::setStandardFieldsOnObjectCreate ($object$preserve$idcolumn);
  1721.  
  1722.         // build the column list
  1723.         //$obj_id = $dbconn->GenID($pntables[$tablename]);
  1724.         $column $pntables["{$tablename}_column"];
  1725.         if (!is_array ($column)) {
  1726.             return pn_exit ("DBUtil::insertObject: [$tablename]_column is not an array ... ");
  1727.         }
  1728.  
  1729.         // When explicityly inserting an autoincrement value, MSSQL server needs to be told to allow this.
  1730.         // This property can only be set once per table and can only be set on 1 table at a time.
  1731.         $dbType DBConnectionStack::getConnectionDBType();
  1732.         static $identityInsertTbl null;
  1733.         if ($dbType=='mssql' && isset($object[$idcolumn]&& $object[$idcolumn]{
  1734.             if (!$identityInsertTbl || $identityInsertTbl != $table{
  1735.                 // TODO/FIXME: for some reason the data dictionary on mssql seems to be slightly broken and
  1736.                 // returns field information without the AUTO specification while also generating an
  1737.                 // include file error to the mssql datadict include file (which is in the correct place).
  1738.  
  1739.                 //$colType = DBUtil::metaColumnType($tablename, $idcolumn, true);
  1740.                 //if (strpos($colType, ' AUTO') !== false) { // ensure that this is set only for autoincrement fields
  1741.                     DBUtil::executeSQL ("SET IDENTITY_INSERT $table ON"-1-1falsefalse);
  1742.                     $identityInsertTbl $table;
  1743.                 //}
  1744.             }
  1745.         }
  1746.  
  1747.         // grab each key and value and append to the sql string
  1748.         $clobArray array();
  1749.         $cArray array();
  1750.         $vArray array();
  1751.         foreach ($column as $key => $val{
  1752.             if (isset($object[$key])) {
  1753.  
  1754.                 $skip    false;
  1755.                 $save    false;
  1756.                 $colType DBUtil::metaColumnType($tablename$key);
  1757.  
  1758.                 // oracle specific stuff
  1759.                 if ($dbType=='oci8' || $dbType=='oracle'{
  1760.                     $save null;
  1761.                     // oracle treats an empty string as NULL -> hack to avoid NULL for empty strings
  1762.                     if ($object[$key]==='' && ($colType=='C' || $colType=='X')) {
  1763.                         $save $object[$key];
  1764.                         $object[$key'""';
  1765.                     }
  1766.                     // oracle needs special treatment of CLOB columns
  1767.                     elseif ($colType=='XL'{
  1768.                         $skip true;
  1769.                         $clobArray[$column[$key]] DataUtil::formatForStore((string)$object[$key]);
  1770.                     }
  1771.                 }
  1772.                 else
  1773.                 // mssql doesn't understand DATEFORMAT_FIXED, we have to convert
  1774.                 if ($dbType=='mssql' && $colType=='T'{
  1775.                     $save $object[$key];
  1776.                     $object[$keyDateUtil::formatDatetime ($object[$key]'%Y%m%d %H:%M:%S');
  1777.                 }
  1778.  
  1779.                 // generate the actual insert values
  1780.                 if (!$skip{
  1781.                     $cArray[$column[$key];
  1782.                     $vArray[DBUtil::_formatForStore($object[$key]);
  1783.                 }
  1784.  
  1785.                 // for oracle empty strings restore original value
  1786.                 if (($dbType=='oci8' || $dbType=='oracle'&& $save==='' && ($colType=='C' || $colType=='X')) {
  1787.                     $object[$key$save;
  1788.                 }
  1789.                 else
  1790.                 // for mssql dates restore original value
  1791.                 if ($dbType=='mssql' && $colType=='T'{
  1792.                     $object[$key$save;
  1793.                 }
  1794.  
  1795.                 // ensure that international float numbers are stored with '.' rather than ',' for decimal separator
  1796.                 if ($colType=='F' || $colType=='N'{
  1797.                     if (is_float($object[$key]|| is_double($object[$key])) {
  1798.                         $object[$keynumber_format($object[$key]8'.''');
  1799.                     }
  1800.                 }
  1801.             }
  1802.             else {
  1803.                 if ($key == $idcolumn{
  1804.                     if ($dbType == 'postgres'{
  1805.                         $cArray[$column[$key];
  1806.                         $vArray['DEFAULT';
  1807.                     }
  1808.                 }
  1809.                 else
  1810.                 if ($force{
  1811.                     $cArray[$column[$key];
  1812.                     $vArray['NULL';
  1813.                 }
  1814.             }
  1815.         }
  1816.  
  1817.         if ($cArray && $vArray{
  1818.             $sql .= '(';
  1819.             $sql .= implode(','$cArray);
  1820.             $sql .= ')';
  1821.             $sql .= 'VALUES (';
  1822.             $sql .= implode(','$vArray);
  1823.             $sql .= ')';
  1824.  
  1825.             $res DBUtil::executeSQL ($sql);
  1826.             if ($res === false{
  1827.                 return $res;
  1828.             }
  1829.         else {
  1830.             return pn_exit ('DBUtil::insertObject: unable to find anything to insert in supplied object ... ');
  1831.         }
  1832.  
  1833.         if ((!$preserve || !isset($object[$idcolumn])) && isset($column[$idcolumn])) {
  1834.             $obj_id DBUtil::getInsertID ($tablename$idcolumn);
  1835.             $object[$idcolumn$obj_id;
  1836.         }
  1837.  
  1838.         if ($clobArray{
  1839.             $res DBUtil::_handleClobFields ($tablename$object$clobArray$idcolumn);
  1840.         }
  1841.  
  1842.         if (!DBConnectionStack::isDefaultConnection()) {
  1843.             return $object;
  1844.         }
  1845.  
  1846.         if ($cArray && $vArray{
  1847.              $object DBUtil::_savePostProcess ($object$tablename$idcolumn);
  1848.         }
  1849.  
  1850.         return $object;
  1851.     }
  1852.  
  1853.  
  1854.     /**
  1855.      * Generate and execute an update SQL statement for the given object
  1856.      *
  1857.      * @param object        The object we wish to update
  1858.      * @param tablename     The tablename key for the PNTables structure
  1859.      * @param where         The where clause (optional) (default='')
  1860.      * @param idcolumn      The column which stores the primary key (optional) (default='id')
  1861.      * @param force         whether or not to insert empty values as NULL (optional) (default=false)
  1862.      * @param updateid      Allow primary key to be updated (default=false)
  1863.      *
  1864.      * @return The result set from the update operation
  1865.      */
  1866.     function updateObject (&$object$tablename$where=''$idcolumn='id'$force=false$updateid=false)
  1867.     {
  1868.         if (!is_array ($object)) {
  1869.             return pn_exit ('DBUtil::updateObject: object is not an array ... ');
  1870.         }
  1871.  
  1872.         if (!isset($object[$idcolumn]&& !$where{
  1873.             return pn_exit ('DBUtil::updateObject: no object ID and no where ... ');
  1874.         }
  1875.  
  1876.         $pntables pnDBGetTables();
  1877.         $sql      "UPDATE $pntables[$tablename] SET ";
  1878.  
  1879.         // set standard architecture fields
  1880.         ObjectUtil::setStandardFieldsOnObjectUpdate ($object$force);
  1881.  
  1882.         // grab each key and value and append to the sql string
  1883.         $clobArray array();
  1884.         $tArray array();
  1885.         $column $pntables["{$tablename}_column"];
  1886.         $dbType DBConnectionStack::getConnectionDBType();
  1887.         foreach ($column as $key => $val{
  1888.             if ($key != $idcolumn || ($key == $idcolumn && $updateid == true)) {
  1889.                 if ($force || array_key_exists($key,$object)) {
  1890.  
  1891.                     $skip    false;
  1892.                     $colType DBUtil::metaColumnType($tablename$key);
  1893.  
  1894.                     // oracle specific stuff
  1895.                     if ($dbType=='oci8' || $dbType=='oracle'{
  1896.                         $save null;
  1897.                         // oracle treats an empty string as NULL -> hack to avoid NULL for empty strings
  1898.                         if ($object[$key]==='' && ($colType=='C' || $colType=='X')) {
  1899.                             $save $object[$key];
  1900.                             $object[$key'""';
  1901.                         }
  1902.                         // oracle needs special treatment of CLOB columns
  1903.                         elseif ($colType=='XL'{
  1904.                             $skip true;
  1905.                             $clobArray[$column[$key]] DataUtil::formatForStore((string)$object[$key]);
  1906.                         }
  1907.                     }
  1908.                     else
  1909.                     // mssql doesn't understand DATEFORMAT_FIXED, we have to convert
  1910.                     if ($dbType=='mssql' && $colType=='T'{
  1911.                         $save $object[$key];
  1912.                         $object[$keyDateUtil::formatDatetime ($object[$key]'%Y%m%d %H:%M:%S');
  1913.                     }
  1914.  
  1915.                     // generate the actual update values
  1916.                     if (!$skip{
  1917.                         $tArray["$val=(isset($object[$key]DBUtil::_formatForStore($object[$key]'NULL');
  1918.                     }
  1919.  
  1920.                     // for oracle empty strings restore original value
  1921.                     if (($dbType=='oci8' || $dbType=='oracle'&& $save==='' && ($colType=='C' || $colType=='X')) {
  1922.                         $object[$key$save;
  1923.                     }
  1924.                     else
  1925.                     // for mssql dates restore original value
  1926.                     if ($dbType=='mssql' && $colType=='T'{
  1927.                         $object[$key$save;
  1928.                     }
  1929.  
  1930.                     // ensure that international float numbers are stored with '.' rather than ',' for decimal separator
  1931.                     if ($colType=='F' || $colType=='N'{
  1932.                         if (is_float($object[$key]|| is_double($object[$key])) {
  1933.                             $object[$keynumber_format($object[$key]8'.''');
  1934.                         }
  1935.                     }
  1936.                 }
  1937.             }
  1938.         }
  1939.  
  1940.         if ($tArray{
  1941.             if (!$where{
  1942.                 $_where " WHERE $column[$idcolumn]='DataUtil::formatForStore($object[$idcolumn]"'";
  1943.             else {
  1944.                 $_where DBUtil::_checkWhereClause ($where);
  1945.             }
  1946.  
  1947.             $sql .= implode(','$tArray" $_where";
  1948.  
  1949.             $res DBUtil::executeSQL ($sql);
  1950.             if ($res === false{
  1951.                 return $res;
  1952.             }
  1953.         }
  1954.  
  1955.         if ($clobArray{
  1956.             // This section commented out - see patch [#4364] DBUtil fix for explanation
  1957.             // since a where clause may not correspond to the acutal ID, we have to fetch the ID here
  1958.             //if ($where) {
  1959.             //    $id  = DBUtil::selectField ($tablename, $idcolumn, $where);
  1960.             //    $object[$idcolumn] = $id;
  1961.             //}
  1962.             $res DBUtil::_handleClobFields ($tablename$object$clobArray$idcolumn);
  1963.         }
  1964.  
  1965.         if (!DBConnectionStack::isDefaultConnection()) {
  1966.             return $object;
  1967.         }
  1968.  
  1969.         $object DBUtil::_savePostProcess ($object$tablename$idcolumntrue);
  1970.  
  1971.         return $object;
  1972.     }
  1973.  
  1974.  
  1975.     /**
  1976.      * Loop through the array and feed it to DBUtil::updateObject()
  1977.      *
  1978.      * @param objects       The objectArray we wish to insert
  1979.      * @param tablename     The tablename key for the PNTables structure
  1980.      * @param idcolumn      The column which stores the primary key
  1981.      * @param force         whether or not to insert empty values as NULL
  1982.      *
  1983.      * @return The result set from the last insert operation. The objects are updated with the newly generated ID.
  1984.      */
  1985.     function updateObjectArray (&$objects$tablename$idcolumn='id'$force=false)
  1986.     {
  1987.         if (!is_array ($objects)) {
  1988.             return pn_exit ("DBUtil::updateObjectArray: objects is not an array ... ");
  1989.         }
  1990.  
  1991.         $ak array_keys ($objects);
  1992.         foreach ($ak as $k=>$v{
  1993.             $res DBUtil::updateObject ($objects[$k]$tablename''$idcolumn$force);
  1994.             if (!$res{
  1995.                 break;
  1996.             }
  1997.         }
  1998.  
  1999.         return $res;
  2000.     }
  2001.  
  2002.     /**
  2003.      * Increment a field by the given increment
  2004.      *
  2005.      * @param tablename     The tablename key for the PNTables structure
  2006.      * @param incfield      The column which stores the field to increment
  2007.      * @param id            The ID value of the object holding the field we wish to increment
  2008.      * @param idfield       The idfield to use (optional) (default='id')
  2009.      * @param inccount      The amount by which to increment the field (optional) (default=1);
  2010.      *
  2011.      * @return The result from the increment operation
  2012.      */
  2013.     function incrementObjectFieldByID ($tablename$incfield$id$idfield='id'$inccount=1)
  2014.     {
  2015.         $pntables pnDBGetTables();
  2016.         $column   $pntables["{$tablename}_column"];
  2017.  
  2018.         $sql      "UPDATE $pntables[$tablename] SET $column[$incfield] = $column[$incfield] + $inccount";
  2019.         $sql     .= " WHERE $column[$idfield] = 'DataUtil::formatForStore($id"'";
  2020.  
  2021.         $res DBUtil::executeSQL ($sql);
  2022.         if ($res === false{
  2023.             return false;
  2024.         }
  2025.  
  2026.         return $res;
  2027.     }
  2028.  
  2029.  
  2030.     /**
  2031.      * Loop through the array and feed it to DBUtil::insertObject()
  2032.      *
  2033.      * @param tablename     The tablename key for the PNTables structure
  2034.      * @param decfield      The column which stores the field to increment
  2035.      * @param id            The ID value of the object holding the field we wish to increment
  2036.      * @param idfield       The idfield to use (optional) (default='id')
  2037.      * @param deccount      The amount by which to decrement the field (optional) (default=1);
  2038.      *
  2039.      * @return The result from the decrement operation
  2040.      */
  2041.     function decrementObjectFieldByID ($tablename$decfield$id$idfield='id'$deccount=1)
  2042.     {
  2043.         return DBUtil::incrementObjectFieldByID($tablename$deccount$id$idfield$deccount);
  2044.     }
  2045.  
  2046.  
  2047.     /**
  2048.      * Delete (an) object(s) via a where clause
  2049.      *
  2050.      * @param tablename    The tablename key for the PNTables structure
  2051.      * @param where        The where-clause to use
  2052.      *
  2053.      * @return The result from the delete operation
  2054.      */
  2055.     function deleteWhere ($tablename$where)
  2056.     {
  2057.         if (!$where{
  2058.             return pn_exit ("DBUtil::deleteWhere: empty where clause passed ... ");
  2059.         }
  2060.  
  2061.         $object array();
  2062.         return DBUtil::deleteObject ($object$tablename$where);
  2063.     }
  2064.  
  2065.  
  2066.     /**
  2067.      * Delete an object by its ID.
  2068.      *
  2069.      * @param tablename   The tablename key for the PNTables structure
  2070.      * @param id          The ID of the object to delete
  2071.      * @param idcolumn    The column which contains the ID field (optional) (default='id')
  2072.      *
  2073.      * @return The result from the delete operation
  2074.      */
  2075.     function deleteObjectByID ($tablename$id$idcolumn='id')
  2076.     {
  2077.         $object array();
  2078.         $object[$idcolumn$id;
  2079.         return DBUtil::deleteObject ($object$tablename''$idcolumn);
  2080.     }
  2081.  
  2082.  
  2083.     /**
  2084.      * Generate and execute a delete SQL statement for the given object
  2085.      *
  2086.      * @param object       The object we wish to update
  2087.      * @param tablename    The tablename key for the PNTables structure
  2088.      * @param where        The where clause to use (optional) (default='')
  2089.      * @param idcolumn     The column which contains the ID field (optional) (default='id')
  2090.      *
  2091.      * @return The result from the delete operation
  2092.      */
  2093.     function deleteObject ($object$tablename$where=''$idcolumn='id')
  2094.     {
  2095.         if (!is_array ($object)  &&  $object != null{
  2096.             return pn_exit ("DBUtil::deleteObject: object is not an array or null ... ");
  2097.         }
  2098.  
  2099.         if ($object && $where{
  2100.             return pn_exit ("DBUtil::deleteObject: can't specify both object and where-clause ... ");
  2101.         }
  2102.  
  2103.         if (!$object && !$where{
  2104.             return pn_exit ("DBUtil::deleteObject: missing either object or where-clause ... ");
  2105.         }
  2106.  
  2107.         $pntables pnDBGetTables();
  2108.         $column   $pntables["{$tablename}_column"];
  2109.         $tab      $pntables[$tablename];
  2110.  
  2111.         $sql "DELETE FROM $tab ";
  2112.         if (!$where{
  2113.             if (!$object[$idcolumn]{
  2114.                 return pn_exit ("DBUtil::deleteObject: object does not have an ID ... ");
  2115.             }
  2116.  
  2117.             $sql .= "WHERE $column[$idcolumn]='".DataUtil::formatForStore($object[$idcolumn])."'";
  2118.         else {
  2119.             $sql .= DBUtil::_checkWhereClause ($where);
  2120.             $object['__fake_field__''Fake entry to mark deleteWhere() return as valid object';
  2121.         }
  2122.  
  2123.         $res DBUtil::executeSQL ($sql);
  2124.         if ($res === false{
  2125.             return $res;
  2126.         }
  2127.  
  2128.         // Attribution and logging only make sense if we do object-based deletion.
  2129.         // If we come from deleteWhere, we simply don't do any of this as in that
  2130.         // case we don't know the object ID to map attributes to.
  2131.         // TODO: there should be a dangling attribute cleanup function somewhere.
  2132.         if (!DBConnectionStack::isDefaultConnection(|| $where{
  2133.             return $object;
  2134.         }
  2135.  
  2136.         if ( ( (isset($pntables["{$tablename}_db_extra_enable_all"]&& $pntables["{$tablename}_db_extra_enable_all"]||
  2137.                (isset($pntables["{$tablename}_db_extra_enable_categorization"]&& $pntables["{$tablename}_db_extra_enable_categorization"])  ) &&
  2138.             pnConfigGetVar('PN_CONFIG_USE_OBJECT_CATEGORIZATION'&&
  2139.             strcmp($tablename'categories_'!== &&
  2140.             strcmp($tablename'objectdata_attributes'!== &&
  2141.             strcmp($tablename'objectdata_log'!== {
  2142.             ObjectUtil::deleteObjectCategories ($object$tablename$idcolumn);
  2143.         }
  2144.  
  2145.         if ( ( (isset($pntables["{$tablename}_db_extra_enable_all"]&& $pntables["{$tablename}_db_extra_enable_all"]||
  2146.                (isset($pntables["{$tablename}_db_extra_enable_attribution"]&& $pntables["{$tablename}_db_extra_enable_attribution"])  ) &&
  2147.             pnConfigGetVar('PN_CONFIG_USE_OBJECT_ATTRIBUTION'&&
  2148.             strcmp($tablename'objectdata_attributes'!== &&
  2149.             strcmp($tablename'objectdata_log'!== {
  2150.             ObjectUtil::deleteObjectAttributes ($object$tablename);
  2151.         }
  2152.  
  2153.         if ( ( (isset($pntables["{$tablename}_db_extra_enable_all"]&& $pntables["{$tablename}_db_extra_enable_all"]||
  2154.                (isset($pntables["{$tablename}_db_extra_enable_meta"]&& $pntables["{$tablename}_db_extra_enable_meta"])  ) &&
  2155.             pnConfigGetVar('PN_CONFIG_USE_OBJECT_META'&&
  2156.             strcmp($tablename'objectdata_attributes'!== &&
  2157.             strcmp($tablename'objectdata_meta'!== &&
  2158.             strcmp($tablename'objectdata_log'!== {
  2159.             ObjectUtil::deleteObjectMetaData ($object$tablename$idcolumn);
  2160.         }
  2161.  
  2162.         if ( ( (isset($pntables["{$tablename}_db_extra_enable_all"]&& $pntables["{$tablename}_db_extra_enable_all"]||
  2163.                (isset($pntables["{$tablename}_db_extra_enable_logging"]&& $pntables["{$tablename}_db_extra_enable_logging"])  ) &&
  2164.             pnConfigGetVar('PN_CONFIG_USE_OBJECT_LOGGING'&&
  2165.             strcmp ($tablename'objectdata_log'!== 0)
  2166.         {
  2167.             if (pnModDBInfoLoad('ObjectData')) {
  2168.                 $log                array();
  2169.                 $log['object_type'$tablename;
  2170.                 $log['object_id']   $object[$idcolumn];
  2171.                 $log['op']          'D';
  2172.                 $log['diff']        serialize($object);
  2173.  
  2174.                 DBUtil::insertObject ($log'objectdata_log');
  2175.             }
  2176.         }
  2177.  
  2178.         return $res;
  2179.     }
  2180.  
  2181.  
  2182.     /*
  2183.     * generate and execute a delete SQL statement
  2184.     */
  2185.     function deleteObjectsFromKeyArray ($keyarray$tablename$field='id')
  2186.     {
  2187.         if (!is_array ($keyarray)) {
  2188.             return pn_exit ('DBUtil::deleteObjectsFromKeyArray: keyarray is not an array ... ');
  2189.         }
  2190.  
  2191.         $pntables pnDBGetTables();
  2192.         $column   $pntables["{$tablename}_column"];
  2193.         $tab      $pntables[$tablename];
  2194.         $sql      "DELETE FROM $tab WHERE $column[$field] IN (";
  2195.         $sqlArray array();
  2196.  
  2197.         foreach ($keyarray as $key => $val{
  2198.             $sqlArray[$key;
  2199.         }
  2200.  
  2201.         $sql .= implode (','$sqlArray')';
  2202.         return $res DBUtil::executeSQL ($sql);
  2203.     }
  2204.  
  2205.  
  2206.     /*
  2207.     * returns the last inserted ID
  2208.     */
  2209.     function getInsertID ($tablename$field='id'$exitOnError=true$verbose=true)
  2210.     {
  2211.         $dbconn   DBConnectionStack::getConnection();
  2212.         $pntables pnDBGetTables();
  2213.         $table    $pntables[$tablename];
  2214.         $column   $pntables["{$tablename}_column"];
  2215.  
  2216.         if (!$resultID $dbconn->PO_Insert_ID($table$column[$field])) {
  2217.             if ($verbose{
  2218.                 print '<br />' $dbconn->ErrorMsg('<br />';
  2219.             }
  2220.  
  2221.             if ($exitOnError{
  2222.                 return pn_exit('Exiting after SQL-error');
  2223.             }
  2224.         }
  2225.  
  2226.         return $resultID;
  2227.     }
  2228.  
  2229.     /**
  2230.      * get the table definition from the pntables array
  2231.      *
  2232.      */
  2233.     function getTableDefinition($tablename)
  2234.     {
  2235.         if (empty ($tablename)) {
  2236.             return pn_exit ('DBUtil::getTableDefinition: table must specify table name... ');
  2237.         }
  2238.  
  2239.         $pntables pnDBGetTables();
  2240.  
  2241.         // try to read table definitions from $pntable array if present
  2242.         $tablecol $tablename '_column';
  2243.         $tabledef $tablename '_column_def';
  2244.         $flag false;
  2245.         $sql '';
  2246.         if (array_key_exists($tabledef$pntables&& is_array($pntables[$tabledef])) {
  2247.             // we have a {$tablename}_column_def array as defined in pntables.php. This is a real array, not
  2248.             // a string. The format is like "C(24) NOTNULL DEFAULT ''" which means we have to
  2249.             // prepend the field name now
  2250.             foreach($pntables[$tablecolas $id => $val{
  2251.                 // the (associative) column array might have different keys (id) pointing to the same
  2252.                 // table field (val) (like blanguage and language in the Blocks module)
  2253.                 if (array_key_exists($id$pntables[$tabledef])) {
  2254.                     if ($flag == true{
  2255.                         $sql .= ', ';
  2256.                     }
  2257.                     $sql .= $val ' ' trim($pntables[$tabledef][$id]);
  2258.                     $flag true;
  2259.                 }
  2260.             }
  2261.             return $sql;
  2262.         else {
  2263.             return pn_exit ('DBUtil::getTableDefinition: neither the sql parameter nor the pntable array contain the ADODB dictionary representation of table to create... ');
  2264.         }
  2265.     }
  2266.  
  2267.     /**
  2268.      * get the table constraints from the pntables array
  2269.      *
  2270.      * @author Jose Guevara, UnderMedia S.A
  2271.      */
  2272.     function getTableConstraints($tablename)
  2273.     {
  2274.         if (empty ($tablename)) {
  2275.             return pn_exit ('DBUtil::getTableConstraints: table must specify table name... ');
  2276.         }
  2277.  
  2278.         $pntables pnDBGetTables();
  2279.         $tablecol $tablename '_column';
  2280.         $tableopt $tablename '_constraints';
  2281.         $constraints '';
  2282.         if (array_key_exists($tableopt$pntables&& is_array($pntables[$tableopt])) {
  2283.             foreach ($pntables[$tableoptas $fk_column => $fk_reference){
  2284.                 $reference_table $pntables[$fk_reference['table']];
  2285.                 $reference_column $pntables[$fk_reference['table''_column'][$fk_reference['column']];
  2286.                 $original_column $pntables[$tablecol][$fk_column];
  2287.                 $constraints .= ", CONSTRAINT FOREIGN KEY($original_column) REFERENCES $reference_table ($reference_column$fk_reference[accion]";
  2288.             }
  2289.             return $constraints;
  2290.         }
  2291.     }
  2292.  
  2293.     /**
  2294.      * create a database table using ADODB dictionary method
  2295.      *
  2296.      * @author Drak
  2297.      * @param  tablename The tablename key for the PNTables structure
  2298.      * @param  sql ADODB dictionary representation of table (optional) (default=null)
  2299.      * @param  tabopt Table options specific to this table (optional) (default=null)
  2300.      * @return bool 
  2301.      */
  2302.     function createTable($tablename$sql=null$tabopt=null)
  2303.     {
  2304.         if (empty ($tablename)) {
  2305.             return pn_exit ('DBUtil::createTable: table must specify table name to create... ');
  2306.         }
  2307.  
  2308.         $pntables pnDBGetTables();
  2309.  
  2310.         if (empty ($sql)) {
  2311.             $sql DBUtil::getTableDefinition($tablename);
  2312.             if (!$sql{
  2313.                 return pn_exit ('DBUtil::createTable: neither the sql parameter nor the pntable array contain the ADODB dictionary representation of table to change... ');
  2314.             }
  2315.         }
  2316.  
  2317.         $dbconn   DBConnectionStack::getConnection();
  2318.         $dict     NewDataDictionary($dbconn);
  2319.         if (!isset($tabopt|| empty($tabopt)) {
  2320.             $tabopt pnDBGetTableOptions($tablename);
  2321.         }
  2322.         $tabopt['constraints'DBUtil::getTableConstraints($tablename);
  2323.         $table    $pntables[$tablename];
  2324.         $sqlarray $dict->ChangeTableSQL($table$sql$tabopt);
  2325.         $result   $dict->ExecuteSQLArray($sqlarray);
  2326.  
  2327.         // create additional indexes
  2328.         $tableidx $tablename '_column_idx';
  2329.         if (array_key_exists($tableidx$pntables&& is_array($pntables[$tableidx])) {
  2330.             foreach($pntables[$tableidxas $idx_name => $idx_definition{
  2331.                 DBUtil::createIndex($idx_name$tablename$idx_definition);
  2332.             }
  2333.         }
  2334.  
  2335.         $success ($result==2);
  2336.         if (!$success{
  2337.             $dberrmsg $dbconn->ErrorNo().' - '.$dbconn->ErrorMSg();
  2338.             LogUtil::registerError (_CREATETABLEFAILED  " ($tablename$result$dberrmsg)");
  2339.         }
  2340.         return $success;
  2341.     }
  2342.  
  2343.  
  2344.     /**
  2345.      * change database table using ADODB dictionary method
  2346.      *
  2347.      * @author Drak
  2348.      * @param  tablename The tablename key for the PNTables structure
  2349.      * @param  sql ADODB dictionary representation of table (optional) (default=null)
  2350.      * @param  tabopt Table options specific to this table (optional) (default=null)
  2351.      * @return bool 
  2352.      */
  2353.     function changeTable($tablename$sql=null$tabopt=null)
  2354.     {
  2355.         if (empty ($tablename)) {
  2356.             return pn_exit ('DBUtil::changeTable: table must specify table name to change... ');
  2357.         }
  2358.  
  2359.         $pntables pnDBGetTables();
  2360.  
  2361.         if (empty ($sql)) {
  2362.             $sql DBUtil::getTableDefinition($tablename);
  2363.             if (!$sql{
  2364.                 return pn_exit ('DBUtil::changeTable: neither the sql parameter nor the pntable array contain the ADODB dictionary representation of table to change... ');
  2365.             }
  2366.         }
  2367.  
  2368.         $dbconn   DBConnectionStack::getConnection();
  2369.         $dict     NewDataDictionary($dbconn);
  2370.         if (!isset($tabopt|| empty($tabopt)) {
  2371.             $tabopt pnDBGetTableOptions();
  2372.         }
  2373.         $tabopt['constraints'DBUtil::getTableConstraints($tablename);
  2374.         $table    $pntables[$tablename];
  2375.  
  2376.         // hack to override ADODB's faulty exclusion of DATETIME transforms
  2377.         $invalidResizeTypes4 $dict->invalidResizeTypes4;
  2378.         $dict->invalidResizeTypes4 array('CLOB','BLOB','TEXT');
  2379.         $sqlarray $dict->ChangeTableSQL($table$sql$tabopt);
  2380.         $result   $dict->ExecuteSQLArray($sqlarray);
  2381.  
  2382.         // restore previous values
  2383.         $dict->invalidResizeTypes4 $invalidResizeTypes4;
  2384.  
  2385.         // create additional indexes
  2386.         $tableidx $tablename '_column_idx';
  2387.         if (array_key_exists($tableidx$pntables&& is_array($pntables[$tableidx])) {
  2388.             $indexes DBUtil::metaIndexes($tablename);
  2389.             foreach($pntables[$tableidxas $idx_name => $idx_definition{
  2390.                 if (!isset($indexes[$idx_name])) {
  2391.                     DBUtil::createIndex($idx_name$tablename$idx_definition);
  2392.                 }
  2393.             }
  2394.         }
  2395.  
  2396.         $success ($result==2);
  2397.         if (!$success{
  2398.             $dberrmsg $dbconn->ErrorNo().' - '.$dbconn->ErrorMSg();
  2399.             LogUtil::registerError (_UPDATETABLEFAILED" ($tablename$result$dberrmsg)");
  2400.         }
  2401.         return $success;
  2402.     }
  2403.  
  2404.  
  2405.     /**
  2406.      * truncate database table
  2407.      *
  2408.      * @param  tablename The tablename key for the PNTables structure
  2409.      * @return bool 
  2410.      */
  2411.     function truncateTable($tablename)
  2412.     {
  2413.         if (empty ($tablename)) {
  2414.             return pn_exit ('DBUtil::truncateTable: table must specify table name to truncate ... ');
  2415.         }
  2416.  
  2417.         $pntables pnDBGetTables();
  2418.         $dbType   DBConnectionStack::getConnectionDBType();
  2419.         if ($dbType=='sqlite'{
  2420.             $sql "DELETE FROM $pntables[$tablename]";
  2421.         else {
  2422.             $sql "TRUNCATE TABLE $pntables[$tablename]";
  2423.         }
  2424.         return DBUtil::executeSQL ($sql);
  2425.     }
  2426.  
  2427.  
  2428.     /**
  2429.      * rename database table
  2430.      *
  2431.      * @param  tablename The tablename key for the PNTables structure
  2432.      * @return bool 
  2433.      */
  2434.     function renameTable($tablename$newtablename)
  2435.     {
  2436.         if (empty ($tablename)) {
  2437.             return pn_exit ('DBUtil::renameTable: table must specify table name to rename... ');
  2438.         }
  2439.         if (empty ($newtablename)) {
  2440.             return pn_exit ('DBUtil::renameTable: table must specify new table name... ');
  2441.         }
  2442.  
  2443.         $dbconn   DBConnectionStack::getConnection();
  2444.         $pntables pnDBGetTables();
  2445.         $dict     NewDataDictionary($dbconn);
  2446.         $table    $pntables[$tablename];
  2447.         $newtable $pntables[$newtablename];
  2448.         $sqlarray $dict->RenameTableSQL($table$newtable);
  2449.         $result   $dict->ExecuteSQLArray($sqlarray);
  2450.         $success  ($result==2);
  2451.         if (!$success{
  2452.             $dberrmsg $dbconn->ErrorNo().' - '.$dbconn->ErrorMSg();
  2453.             LogUtil::registerError (_RENAMETABLEFAILED" ($tablename$result$dberrmsg)");
  2454.         }
  2455.         return $success;
  2456.     }
  2457.  
  2458.     /**
  2459.      * delete database table
  2460.      *
  2461.      * @param  tablename The tablename key for the PNTables structure
  2462.      * @return bool 
  2463.      */
  2464.     function dropTable($tablename)
  2465.     {
  2466.         if (empty ($tablename)) {
  2467.             return pn_exit ('DBUtil::dropTable: table must specify table name to delete... ');
  2468.         }
  2469.  
  2470.         $dbconn   DBConnectionStack::getConnection();
  2471.         $pntables pnDBGetTables();
  2472.         $dict     NewDataDictionary($dbconn);
  2473.         $table    $pntables[$tablename];
  2474.         $sqlarray $dict->DropTableSQL($table);
  2475.         $result   $dict->ExecuteSQLArray($sqlarray);
  2476.  
  2477.         $dbType   DBConnectionStack::getConnectionDBType();
  2478.         $strict   true;
  2479.         if ($dbType == 'postgres'// postgres returns a "sequence does not exist error" but the query executes
  2480.             $strict false;
  2481.         }
  2482.  
  2483.         if ($strict{
  2484.             $success ($result==2);
  2485.         else {
  2486.             $success ($result>0);
  2487.         }
  2488.  
  2489.         if (!$success{
  2490.             $dberrmsg $dbconn->ErrorNo().' - '.$dbconn->ErrorMSg();
  2491.             LogUtil::registerError (_DELETETABLEFAILED" ($tablename$result$dberrmsg)");
  2492.         else {
  2493.             ObjectUtil::deleteAllObjectTypeAttributes ($tablename);
  2494.         }
  2495.  
  2496.         return $success;
  2497.     }
  2498.  
  2499.  
  2500.     /**
  2501.      * create index on table
  2502.      *
  2503.      * @author Drak
  2504.      * @param  idxname 
  2505.      * @param  tablename The tablename key for the PNTables structure
  2506.      * @param  flds string field name, or non-associative array of field names
  2507.      * @param  idxoptarray 
  2508.      *  return  bool
  2509.      */
  2510.     function createIndex($idxname$tablename$flds$idxoptarray=false)
  2511.     {
  2512.         if (empty ($idxname)) {
  2513.             return pn_exit ('DBUtil::createIndex: idxname must specify index name... ');
  2514.         }
  2515.  
  2516.         if (empty ($tablename)) {
  2517.             return pn_exit ('DBUtil::createIndex: table must specify table name... ');
  2518.         }
  2519.  
  2520.         if (empty ($flds)) {
  2521.             return pn_exit ('DBUtil::createIndex: flds must specify index field or fields as non-associative array... ');
  2522.         }
  2523.  
  2524.         if (!empty($idxoptarray&& !is_array($idxoptarray)) {
  2525.             return pn_exit ('DBUtil::createIndex: idxoptarray must be an array ... ');
  2526.         }
  2527.  
  2528.         $dbconn   DBConnectionStack::getConnection();
  2529.         $pntables pnDBGetTables();
  2530.         $dict     NewDataDictionary($dbconn);
  2531.         $table    $pntables[$tablename];
  2532.         $column   $pntables["{$tablename}_column"];
  2533.  
  2534.         if (!is_array($flds)) {
  2535.             $flds $column[$flds];
  2536.         else {
  2537.             $newflds array();
  2538.             foreach($flds as $fld{
  2539.                 if(is_array($fld)) {
  2540.                     // this adds support to specifying index lengths in your pntables. So you can say
  2541.                     // $flds[] = array('path', 100);
  2542.                     // $flds[] = array('name', 10);
  2543.                     // $idxoptarray['UNIQUE'] = true;
  2544.                     // DBUtil::createIndex($idxname, $table, $flds, $idxoptarray);
  2545.                     $newflds[''.$column[$fld[0]]."($fld[1])";
  2546.                 else {
  2547.                     $newflds[$column[$fld];
  2548.                 }
  2549.             }
  2550.             $flds $newflds;
  2551.         }
  2552.  
  2553.         $sqlarray $dict->CreateIndexSQL($idxname$table$flds$idxoptarray);
  2554.         $result   $dict->ExecuteSQLArray($sqlarray);
  2555.         $success  ($result==2);
  2556.         if (!$success{
  2557.             $dberrmsg $dbconn->ErrorNo().' - '.$dbconn->ErrorMSg();
  2558.             LogUtil::registerError (_CREATEINDEXFAILED  " ($idxname$tablename$result$dberrmsg)");
  2559.         }
  2560.         return $success;
  2561.     }
  2562.  
  2563.  
  2564.     /**
  2565.      * drop index on table
  2566.      *
  2567.      * @author Drak
  2568.      * @param  idxname index name
  2569.      * @param  tablename The tablename key for the PNTables structure
  2570.      *  return  bool
  2571.      */
  2572.     function dropIndex($idxname$tablename)
  2573.     {
  2574.         if (empty ($idxname)) {
  2575.             return pn_exit ('DBUtil::dropIndex: idxname must specify index name... ');
  2576.         }
  2577.  
  2578.         if (empty ($tablename)) {
  2579.             return pn_exit ('DBUtil::dropIndex: tablename must specify index name... ');
  2580.         }
  2581.  
  2582.         $dbconn   DBConnectionStack::getConnection();
  2583.         $pntables pnDBGetTables();
  2584.         $dict     NewDataDictionary($dbconn);
  2585.         $table    $pntables[$tablename];
  2586.         $sqlarray $dict->DropIndexSQL ($idxname$table);
  2587.         $result   $dict->ExecuteSQLArray($sqlarray);
  2588.         $success  ($result==2);
  2589.         if (!$success{
  2590.             $dberrmsg $dbconn->ErrorNo().' - '.$dbconn->ErrorMSg();
  2591.             LogUtil::registerError (_DROPINDEXFAILED  " ($idxname$tablename$result$dberrmsg)");
  2592.         }
  2593.         return $success;
  2594.     }
  2595.  
  2596.     /**
  2597.      * rename column(s) in a table
  2598.      *
  2599.      * @author Mark West
  2600.      * @param  tablename The tablename key for the PNTables structure
  2601.      * @param  oldcolumn The existing name of the column (full database name of column)
  2602.      * @param  newcolumn The new name of the column from the pntables array
  2603.      * @param  fields    field specific options (optional) (default=null)
  2604.      *  return  bool
  2605.      */
  2606.     function renameColumn($tablename$oldcolumn$newcolumn$fields=null)
  2607.     {
  2608.         if (empty ($tablename)) {
  2609.             return pn_exit ('DBUtil::renameColumn: tablename must specify table name... ');
  2610.         }
  2611.         if (empty ($oldcolumn)) {
  2612.             return pn_exit ('DBUtil::renameColumn: oldcolumn must specify existing column name... ');
  2613.         }
  2614.         if (empty ($newcolumn)) {
  2615.             return pn_exit ('DBUtil::renameColumn: newcolumn must specify new column name... ');
  2616.         }
  2617.  
  2618.         $dbconn   DBConnectionStack::getConnection();
  2619.         $pntables pnDBGetTables();
  2620.         $dict     NewDataDictionary($dbconn);
  2621.         $table    $pntables[$tablename];
  2622.         if (!isset($fields|| empty($fields)) {
  2623.             $fields $pntables["{$tablename}_column"][$newcolumn' ' $pntables["{$tablename}_column_def"][$newcolumn];
  2624.         }
  2625.         $oldcolumn = isset($pntables["{$tablename}_column"][$oldcolumn]$pntables["{$tablename}_column"][$oldcolumn$oldcolumn;
  2626.         $newcolumn $pntables["{$tablename}_column"][$newcolumn];
  2627.         $sqlarray $dict->RenameColumnSQL ($table$oldcolumn$newcolumn$fields);
  2628.         $result   $dict->ExecuteSQLArray($sqlarray);
  2629.         $success  ($result==2);
  2630.         if (!$success{
  2631.             $dberrmsg $dbconn->ErrorNo().' - '.$dbconn->ErrorMSg();
  2632.             LogUtil::registerError (_RENAMECOLUMNFAILED  " ($tablename$oldcolumn$newcolumn$dberrmsg)");
  2633.         }
  2634.         return $success;
  2635.     }
  2636.  
  2637.     /**
  2638.      * add column(s) to a table
  2639.      *
  2640.      * @author Robert Gasch
  2641.      * @param  tablename The tablename key for the PNTables structure
  2642.      * @param  fields    fields to add to the table
  2643.      *  return  bool
  2644.      */
  2645.     function addColumn($tablename$fields)
  2646.     {
  2647.         if (empty ($tablename)) {
  2648.             return pn_exit ('DBUtil::addColumn: tablename must specify table name... ');
  2649.         }
  2650.  
  2651.         if (empty ($fields)) {
  2652.             return pn_exit ('DBUtil::addColumn: fields must specify fields to add... ');
  2653.         }
  2654.  
  2655.         if (!is_array($fields)) {
  2656.             return pn_exit ('DBUtil::addColumn: fields must be an array (actually an array of field arrays)... ');
  2657.         }
  2658.  
  2659.         if (!is_array($fields[0])) {
  2660.             return pn_exit ('DBUtil::addColumn: fields must be an array of field arrays... ');
  2661.         }
  2662.  
  2663.         $dbconn   DBConnectionStack::getConnection();
  2664.         $pntables pnDBGetTables();
  2665.         $dict     NewDataDictionary($dbconn);
  2666.         $table    $pntables[$tablename];
  2667.         $sqlarray $dict->AddColumnSQL ($table$fields);
  2668.         $result   $dict->ExecuteSQLArray($sqlarray);
  2669.         $success  ($result==2);
  2670.         if (!$success{
  2671.             $dberrmsg $dbconn->ErrorNo().' - '.$dbconn->ErrorMSg();
  2672.             LogUtil::registerError (_DROPCOLUMNFAILED  " ($tablename$fields$dberrmsg)");
  2673.         }
  2674.         return $success;
  2675.     }
  2676.  
  2677.     /**
  2678.      * drop column(s) from a table
  2679.      *
  2680.      * @author Mark West
  2681.      * @param  tablename The tablename key for the PNTables structure
  2682.      * @param  fields    fields to drop from the table
  2683.      *  return  bool
  2684.      */
  2685.     function dropColumn($tablename$fields)
  2686.     {
  2687.         if (empty ($tablename)) {
  2688.             return pn_exit ('DBUtil::dropColumn: tablename must specify table name... ');
  2689.         }
  2690.  
  2691.         if (empty ($fields)) {
  2692.             return pn_exit ('DBUtil::dropColumn: fields must specify fields to drop... ');
  2693.         }
  2694.  
  2695.         $dbconn   DBConnectionStack::getConnection();
  2696.         $pntables pnDBGetTables();
  2697.         $dict     NewDataDictionary($dbconn);
  2698.         $table    $pntables[$tablename];
  2699.         $sqlarray $dict->DropColumnSQL ($table$fields);
  2700.         $result   $dict->ExecuteSQLArray($sqlarray);
  2701.         $success  ($result==2);
  2702.         if (!$success{
  2703.             $dberrmsg $dbconn->ErrorNo().' - '.$dbconn->ErrorMSg();
  2704.             LogUtil::registerError (_DROPCOLUMNFAILED  " ($tablename$fields$dberrmsg)");
  2705.         }
  2706.         return $success;
  2707.     }
  2708.  
  2709.  
  2710.     /**
  2711.      * get a list of databases available on the server
  2712.      *
  2713.      * @author Mark West
  2714.      *  return  array of databases
  2715.      */
  2716.     function metaDatabases()
  2717.     {
  2718.         $dbconn DBConnectionStack::getConnection();
  2719.         return $dbconn->MetaDatabases();
  2720.     }
  2721.  
  2722.  
  2723.     /**
  2724.      * get a list of tables in the currently connected database
  2725.      *
  2726.      * @author Mark West
  2727.      * @param  ttype type of 'tables' to get
  2728.      * @param  showSchema add the schema name to the table
  2729.      * @param  mask mask to apply to return result set
  2730.      *  return  array of tables
  2731.      */
  2732.     function metaTables($ttype=false$showSchema=false$mask=false)
  2733.     {
  2734.         $dbconn DBConnectionStack::getConnection();
  2735.         return $dbconn->MetaTables($ttype$showSchema$mask);
  2736.     }
  2737.  
  2738.  
  2739.     /**
  2740.      * get a list of columns in a table
  2741.      *
  2742.      * @author Mark West
  2743.      * @param  tablename The tablename key for the PNTables structure
  2744.      * @param  notcasesensitive normalize case of table name
  2745.      *  return  array of column objects
  2746.      */
  2747.     function metaColumns($tablename$assoc=false$notcasesensitive=true)
  2748.     {
  2749.         if (empty ($tablename)) {
  2750.             return pn_exit ('DBUtil::metaColumns: tablename must specify table name... ');
  2751.         }
  2752.  
  2753.         if ($assoc{
  2754.             global $ADODB_FETCH_MODE;
  2755.             $save $ADODB_FETCH_MODE;
  2756.             $ADODB_FETCH_MODE ADODB_FETCH_ASSOC;
  2757.         }
  2758.  
  2759.         $dbconn   DBConnectionStack::getConnection();
  2760.         $pntables pnDBGetTables();
  2761.         $metaCols $dbconn->MetaColumns($pntables[$tablename]$notcasesensitive);
  2762.  
  2763.         if ($assoc{
  2764.             $ADODB_FETCH_MODE $save;
  2765.         }
  2766.  
  2767.         return $metaCols;
  2768.     }
  2769.  
  2770.  
  2771.     /**
  2772.      * get a the ADODB meta-type for a table column
  2773.      *
  2774.      * @author Robert Gasch
  2775.      * @param  tablename The tablename key for the PNTables structure
  2776.      * @param  column the column we want to fetch the type for
  2777.      * @param  showAutoIncrement whether or not to display auto-increment information (optional) (default=false)
  2778.      * @param  showDefault whether or not to display default-value information (optional) (default=false)
  2779.      *  return  The meta-column type (default='N', false for a non-existing column)
  2780.      */
  2781.     function metaColumnType($tablename$column$showAutoIncrement=false$showDefault=false)
  2782.     {
  2783.         if (empty ($column)) {
  2784.             return pn_exit ('DBUtil::metaColumnType: column must specify a column name... ');
  2785.         }
  2786.  
  2787.         $pntables pnDBGetTables();
  2788.         $pncols   $pntables["{$tablename}_column"];
  2789.  
  2790.         if (!isset($pncols[$column])) {
  2791.             return false;
  2792.         }
  2793.  
  2794.         $pncol strtoupper($pncols[$column]);
  2795.  
  2796.         static $dCache null;
  2797.         static $mCache array();
  2798.         if (isset($mCache[$tablename]&& $dCache{
  2799.             $metaType $dCache->MetaType($mCache[$tablename][$pncol]->type);
  2800.             // quick hack to append auto-increment info to column type
  2801.             if ($showAutoIncrement && isset($mCache[$tablename][$pncol]->auto_increment&& $mCache[$tablename][$pncol]->auto_increment{
  2802.                 $metaType .= " AUTO";
  2803.             }
  2804.             // quick hack to append default value info to column type
  2805.             if ($showDefault && isset($mCache[$tablename][$pncol]->has_default&& $mCache[$tablename][$pncol]->has_default{
  2806.                 $defaultValue $mCache[$tablename][$pncol]->default_value;
  2807.                 $metaType .= " DEFAULT $defaultValue";
  2808.             }
  2809.             return $metaType;
  2810.         }
  2811.  
  2812.         // the following code may be expensive for frequent calls so we cache it
  2813.         $dbconn DBConnectionStack::getConnection();
  2814.         $dict   NewDataDictionary($dbconn);
  2815.  
  2816.         if (!$dict{
  2817.             return pn_exit ('Unable to instantiate data dictionary ...');
  2818.         }
  2819.  
  2820.         $metaCols DBUtil::metaColumns($tablenametrue);
  2821.         $metaType $dict->MetaType($metaCols[$pncol]->type);
  2822.  
  2823.         // quick hack to append auto-increment info to column type
  2824.         if ($showAutoIncrement && isset($metaCols[$pncol]->auto_increment&& $metaCols[$pncol]->auto_increment{
  2825.             $metaType .= ' AUTO';
  2826.         }
  2827.         // quick hack to append default value info to column type
  2828.         if ($showDefault && isset($metaCols[$pncol]->has_default&& $metaCols[$pncol]->has_default{
  2829.             $defaultValue $metaCols[$pncol]->default_value;
  2830.             $metaType .= " DEFAULT $defaultValue";
  2831.         }
  2832.  
  2833.         $dCache $dict;
  2834.         $mCache[$tablename$metaCols;
  2835.  
  2836.         return $metaType;
  2837.     }
  2838.  
  2839.  
  2840.     /**
  2841.      * get a list of column names in a table
  2842.      *
  2843.      * @author Mark West
  2844.      * @param  tablename The tablename key for the PNTables structure
  2845.      * @param  numericIndex use numeric keys
  2846.      *  return  array of column names
  2847.      */
  2848.     function metaColumnNames($tablename$numericIndex=false)
  2849.     {
  2850.         if (empty ($tablename)) {
  2851.             return pn_exit ('DBUtil::metaColumnNames: tablename must specify table name... ');
  2852.         }
  2853.  
  2854.         $dbconn   DBConnectionStack::getConnection();
  2855.         $pntables pnDBGetTables();
  2856.         return $dbconn->MetaColumnNames($pntables[$tablename]$numericIndex);
  2857.     }
  2858.  
  2859.  
  2860.     /**
  2861.      * get a list of primary keys for a table
  2862.      *
  2863.      * @author Mark West
  2864.      * @param  tablename The tablename key for the PNTables structure
  2865.      * @param  owner 
  2866.      * @todo   work out what owner param actually does
  2867.      *  return  array of column names
  2868.      */
  2869.     function metaPrimaryKeys($tablename$owner=false)
  2870.     {
  2871.         if (empty ($tablename)) {
  2872.             return pn_exit ('DBUtil::metaPrimaryKeys: tablename must specify table name... ');
  2873.         }
  2874.  
  2875.         $dbconn   DBConnectionStack::getConnection();
  2876.         $pntables pnDBGetTables();
  2877.         return $dbconn->MetaPrimaryKeys($pntables[$tablename]$owner);
  2878.     }
  2879.  
  2880.  
  2881.     /**
  2882.      * get a list of foreign keys for a table
  2883.      *
  2884.      * @author Mark West
  2885.      * @param  tablename The tablename key for the PNTables structure
  2886.      * @param  owner 
  2887.      * @param  upper upper case key names
  2888.      * @todo   work out what owner param actually does
  2889.      *  return  array of column names
  2890.      */
  2891.     function metaForeignKeys($tablename$owner=false$upper=false)
  2892.     {
  2893.         if (empty ($tablename)) {
  2894.             return pn_exit ('DBUtil::metaForeignKeys: tablename must specify table name... ');
  2895.         }
  2896.  
  2897.         $dbconn   DBConnectionStack::getConnection();
  2898.         $pntables pnDBGetTables();
  2899.         return $dbconn->MetaForeignKeys($pntables[$tablename]$owner$upper);
  2900.     }
  2901.  
  2902.  
  2903.     /**
  2904.      * get a list of indexes for a table
  2905.      *
  2906.      * @author Mark West
  2907.      * @param table table name
  2908.      * @param primary show only primary keys
  2909.      * @param owner 
  2910.      * @todo work out what owner param actually does
  2911.      *  return array of column names
  2912.      */
  2913.     function metaIndexes($tablename$primary=false$owner=false)
  2914.     {
  2915.         if (empty ($tablename)) {
  2916.             return pn_exit ('DBUtil::metaForeignKeys: table must specify table name... ');
  2917.         }
  2918.  
  2919.         $dbconn   DBConnectionStack::getConnection();
  2920.         $pntables pnDBGetTables();
  2921.         return $dbconn->MetaIndexes($pntables[$tablename]$primary$owner);
  2922.     }
  2923.  
  2924.  
  2925.     /**
  2926.      * return server information
  2927.      *
  2928.      * @author Mark West
  2929.      *  return array of server info
  2930.      */
  2931.     function serverInfo()
  2932.     {
  2933.         $dbconn DBConnectionStack::getConnection();
  2934.         return $dbconn->ServerInfo();
  2935.     }
  2936.  
  2937.  
  2938.     /**
  2939.      * create database
  2940.      *
  2941.      * @author Drak
  2942.      * @param  dbname the database name
  2943.      * @param  optionsarray the options array
  2944.      * @return bool 
  2945.      */
  2946.     function createDatabase($dbname$optionsarray=false)
  2947.     {
  2948.         if (empty($dbname)) {
  2949.             return pn_exit('DBUtil::createDatabase: must specify a database name');
  2950.         }
  2951.  
  2952.         $dbconn DBConnectionStack::getConnection();
  2953.         $dict   NewDataDictionary($dbconn);
  2954.         $sql    $dict->CreateDatabase($dbname$optionsarray);
  2955.         return $dict->ExecuteSQLArray($sql);
  2956.     }
  2957.  
  2958.  
  2959.     /**
  2960.      * limit the table name if necessary and prepend the prefix
  2961.      *
  2962.      * When using Oracle the object name may not be longer than 30 chars. Now ADODB uses TRIGGERS and SEQUENCEs to emulate the AUTOINCREMENT
  2963.      * which eats up to 9 chars (TRIG_SEQ_<prefix>_<tablename>) so we have to limit the length of the table name to
  2964.      * 30 - 9 - length(prefix) - separator.
  2965.      * We use this function as a central point to shorten table name (there might be restrictions in ' other RDBMS too). If the resulting tablename is
  2966.      * empty we will show an error. In this case the prefix is too long.
  2967.      *
  2968.      * @author landseer
  2969.      * @param  $tablename the tablename as send from pntables.php
  2970.      * @param  $dbdriver  (optional) the driver used for this DB
  2971.      * @return bool 
  2972.      */
  2973.     function getLimitedTablename($tablename$dbdriver='')
  2974.     {
  2975.         if (empty($tablename)) {
  2976.             return pn_exit('DBUtil::getLimitedTablename: must specify a table name');
  2977.         }
  2978.  
  2979.         if (empty($dbdriver)) {
  2980.             $dbdriver DBConnectionStack::getConnectionDBDriver();
  2981.         }
  2982.  
  2983.         $prefix pnDBGetTablePrefix($tablename);
  2984.         switch($dbdriver{
  2985.             case 'oci8':                                                        // Oracle
  2986.             case 'oracle':                                                      // Oracle
  2987.                  $maxlen     30;                                              // max length for a tablename
  2988.                  $_tablename $tablename;                                      // save for later if we need to show an error
  2989.                  $lenTable   strlen($tablename);
  2990.                  $lenPrefix  strlen($prefix);
  2991.                  if ($lenTable+$lenPrefix+10 $maxlen{                       // 10 for length of TRIG_SEQ_ + _
  2992.                      $tablename substr($tablename0$maxlen-10-$lenPrefix)// same as 20-strlen(), but easier to understand :-)
  2993.                  }
  2994.                  if (empty($tablename)) {
  2995.                      return pn_exit('DBUtil::getLimitedTablename: unable to limit tablename \'' DataUtil::formatForDisplay($_tablename'\'because database prefix is too long for Oracle, please shorten it (recommended length is 4 chars)');
  2996.                  }
  2997.                  break;
  2998.  
  2999.             default:     // no action necessary, use tablename as is
  3000.                  break;
  3001.         }
  3002.  
  3003.         // finally build the tablename
  3004.         $tablename $prefix '_' $tablename;
  3005.         return $tablename;
  3006.     }
  3007.  
  3008.  
  3009.     /**
  3010.      * Handle any CLOB fields which were contained in an insert or update
  3011.      * query. This method loops through the clob fields and updates them
  3012.      * one at a time.
  3013.      *
  3014.      * @author rgasch
  3015.      * @param  clobArray the array of CLOB column entries
  3016.      * @param  tablename the tablename the object is based on
  3017.      * @param  idcolumn the idcolumn for the object/table combination
  3018.      * @return the object with it's relevant sub-objects set
  3019.      */
  3020.     function _handleClobFields ($tablename$object$clobArray$idcolumn)
  3021.     {
  3022.         $ret true;
  3023.         $dbType DBConnectionStack::getConnectionDBType();
  3024.         if (!$clobArray || ($dbType!='oci8' && $dbType!='oracle')) {
  3025.             return $ret;
  3026.         }
  3027.  
  3028.         $dbconn   DBConnectionStack::getConnection();
  3029.         $pntables pnDBGetTables();
  3030.         $table    $pntables[$tablename];
  3031.         $columns  $pntables["{$tablename}_column"];
  3032.  
  3033.         // process CLOB values
  3034.         $dbconn  DBConnectionStack::getConnection();
  3035.         $colType DBUtil::metaColumnType($tablename$idcolumn);
  3036.         foreach ($clobArray as $k => $v{
  3037.             if ($colType == 'N' || strpos($colType'I')===0{
  3038.                 $kval $object[$idcolumn];
  3039.                 $flt  (is_numeric($kval&& (intval($kval)!=floatval($kval)));
  3040.                 $id   ($flt ? (float)$kval : (int)$kval);
  3041.                 $where "$columns[$idcolumn]='".DataUtil::formatForStore($id)."'";
  3042.             }
  3043.             else {
  3044.                 $where "$columns[$idcolumn]='".DataUtil::formatForStore($object[$idcolumn])."'";
  3045.             }
  3046.             $res $dbconn->UpdateClob($table$k$v$where);
  3047.             if (!$res{
  3048.                 $ret $res;
  3049.                 break;
  3050.             }
  3051.         }
  3052.  
  3053.         return $ret;
  3054.     }
  3055.  
  3056.     /**
  3057.      * This method creates the necessary sql information for retrieving
  3058.      * fields from joined tables defined by a joinInfo array described
  3059.      * at the top of this class.
  3060.      *
  3061.      * @author rgasch
  3062.      * @param  tablename    the tablename key for the PNTables structure
  3063.      * @param  joinInfo     the array containing the extended join information
  3064.      * @param  columnArray  the columns to marshall into the resulting object (optional) (default=null)
  3065.      * @return array ($sqlJoin, $sqlJoinFieldList, $ca)
  3066.      */
  3067.     function _processJoinArray($tablename$joinInfo$columnArray=null)
  3068.     {
  3069.         $pntables pnDBGetTables();
  3070.         $columns  $pntables["{$tablename}_column"];
  3071.  
  3072.         $ca       DBUtil::getColumnsArray ($tablename$columnArray);
  3073.         $alias    'a';
  3074.         $sqlJoin  '';
  3075.         $sqlJoinFieldList '';
  3076.         $ak array_keys($joinInfo);
  3077.         foreach ($ak as $k{
  3078.             $jt   $joinInfo[$k]['join_table'];
  3079.             $jf   $joinInfo[$k]['join_field'];
  3080.             $ofn  $joinInfo[$k]['object_field_name'];
  3081.             $cft  $joinInfo[$k]['compare_field_table'];
  3082.             $cfj  $joinInfo[$k]['compare_field_join'];
  3083.  
  3084.             $jtab $pntables[$jt];
  3085.             $jcol $pntables["{$jt}_column"];
  3086.  
  3087.             if (!is_array($jf)) {
  3088.                 $jf array ($jf);
  3089.             }
  3090.  
  3091.             if (!is_array($ofn)) {
  3092.                 $ofn array ($ofn);
  3093.             }
  3094.  
  3095.             // loop over all fields to select from the joined table
  3096.             foreach ($jf as $k => $v{
  3097.                 $currentColumn $jcol[$v];
  3098.                 // attempt to remove encoded table name in column list used by some PN tables
  3099.                 $t strstr ($currentColumn'.');
  3100.                 if ($t !== false{
  3101.                     $currentColumn substr ($t1);
  3102.                 }
  3103.  
  3104.                 $line  "$alias.$currentColumn AS $ofn[$k] ";
  3105.                 $sqlJoinFieldList .= $line;
  3106.  
  3107.                 $ca[$ofn[$k];
  3108.             }
  3109.  
  3110.             $compareColumn $jcol[$cfj];
  3111.             // attempt to remove encoded table name in column list used by some PN tables
  3112.             $t strstr ($compareColumn'.');
  3113.             if ($t !== false{
  3114.                 $compareColumn substr ($t1);
  3115.             }
  3116.  
  3117.             $t = isset($columns[$cft]"tbl.$columns[$cft]$cft// if not a column reference assume litereal column name
  3118.             $line  " LEFT JOIN $jtab $alias ON $alias.$compareColumn = $t ";
  3119.  
  3120.             $sqlJoin .= $line;
  3121.             ++$alias;
  3122.         }
  3123.         return array($sqlJoin$sqlJoinFieldList$ca);
  3124.     }
  3125.  
  3126.  
  3127.     /**
  3128.      * Post-processing after this object has been selected. This routine
  3129.      * is responsible for reading the 'extra' data (attributes, categories,
  3130.      * and meta data) from the database and inserting the relevant
  3131.      * sub-objects into the object.
  3132.      *
  3133.      * @author rgasch
  3134.      * @param  objects the object-array or the object we just selected
  3135.      * @param  tablename the tablename the object is based on
  3136.      * @param  idcolumn the idcolumn for the object/table combination
  3137.      * @return the object with it's relevant sub-objects set
  3138.      */
  3139.     function _selectPostProcess ($objects$tablename$idcolumn)
  3140.     {
  3141.         // nothing to do if objects is empty
  3142.         if (is_array($objects&& count($objects)==0){
  3143.             return $objects;
  3144.         }
  3145.  
  3146.         $pntables pnDBGetTables();
  3147.  
  3148.         if ( ( (isset($pntables["{$tablename}_db_extra_enable_all"]&& $pntables["{$tablename}_db_extra_enable_all"]||
  3149.                (isset($pntables["{$tablename}_db_extra_enable_categorization"]&& $pntables["{$tablename}_db_extra_enable_categorization"])  ) &&
  3150.             pnConfigGetVar('PN_CONFIG_USE_OBJECT_CATEGORIZATION'&&
  3151.             strcmp($tablename'categories_'!== &&
  3152.             strcmp($tablename'objectdata_attributes'!== &&
  3153.             strcmp($tablename'objectdata_log'!== &&
  3154.             pnModAvailable('Categories') )
  3155.         {
  3156.             if (is_array($objects)) {
  3157.                 $ak array_keys ($objects);
  3158.                 if ($ak && is_array($objects[$ak[0]])) {
  3159.                     ObjectUtil::expandObjectArrayWithCategories($objects$tablename$idcolumn);
  3160.                 else {
  3161.                     ObjectUtil::expandObjectWithCategories ($objects$tablename$idcolumn);
  3162.                 }
  3163.             }
  3164.         }
  3165.  
  3166.         if ( ( (isset($pntables["{$tablename}_db_extra_enable_all"]&& $pntables["{$tablename}_db_extra_enable_all"]||
  3167.                (isset($pntables["{$tablename}_db_extra_enable_attribution"]&& $pntables["{$tablename}_db_extra_enable_attribution"||
  3168.                pnConfigGetVar('PN_CONFIG_USE_OBJECT_ATTRIBUTION') ) &&
  3169.             strcmp($tablename'objectdata_attributes'!== &&
  3170.             strcmp($tablename'objectdata_log'!== &&
  3171.             pnModAvailable('ObjectData') )
  3172.         {
  3173.             if (is_array($objects)) {
  3174.                 $ak array_keys ($objects);
  3175.                 if ($ak && is_array($objects[$ak[0]])) {
  3176.                     foreach ($objects as $k=>$v{
  3177.                         ObjectUtil::expandObjectWithAttributes ($objects[$k]$tablename$idcolumn);
  3178.                     }
  3179.                 else {
  3180.                     ObjectUtil::expandObjectWithAttributes ($objects$tablename$idcolumn);
  3181.                 }
  3182.             }
  3183.         }
  3184.  
  3185.         if ( ( (isset($pntables["{$tablename}_db_extra_enable_all"]&& $pntables["{$tablename}_db_extra_enable_all"]||
  3186.                (isset($pntables["{$tablename}_db_extra_enable_meta"]&& $pntables["{$tablename}_db_extra_enable_meta"||
  3187.                pnConfigGetVar('PN_CONFIG_USE_OBJECT_META') ) &&
  3188.             strcmp($tablename'objectdata_attributes'!== &&
  3189.             strcmp($tablename'objectdata_meta'!== &&
  3190.             strcmp($tablename'objectdata_log'!== &&
  3191.             pnModAvailable('ObjectData') )
  3192.         {
  3193.             if (is_array($objects)) {
  3194.                 $ak array_keys ($objects);
  3195.                 if ($ak && is_array($objects[$ak[0]])) {
  3196.                     foreach ($objects as $k=>$v{
  3197.                         ObjectUtil::expandObjectWithMeta ($objects[$k]$tablename$idcolumn);
  3198.                     }
  3199.                 else {
  3200.                     ObjectUtil::expandObjectWithMeta ($objects$tablename$idcolumn);
  3201.                 }
  3202.             }
  3203.         }
  3204.  
  3205.         return $objects;
  3206.     }
  3207.  
  3208.  
  3209.     /**
  3210.      * Post-processing after this object has beens saved. This routine
  3211.      * is responsible for writing the 'extra' data (attributes, categories,
  3212.      * and meta data) to the database and the optionally creating an
  3213.      * entry in the object-log table
  3214.      *
  3215.      * @author rgasch
  3216.      * @param  object the object wehave just saved
  3217.      * @param  tablename the tablename the object is based on
  3218.      * @param  idcolumn the idcolumn for the object/table combination
  3219.      * @param  update whether or not this was an update (default=false, signifies operation was an insert).
  3220.      * @return the object
  3221.      */
  3222.     function _savePostProcess ($object$tablename$idcolumn$update=false)
  3223.     {
  3224.         $pntables pnDBGetTables();
  3225.  
  3226.         if ( ( (isset($pntables["{$tablename}_db_extra_enable_all"]&& $pntables["{$tablename}_db_extra_enable_all"]||
  3227.                (isset($pntables["{$tablename}_db_extra_enable_categorization"]&& $pntables["{$tablename}_db_extra_enable_categorization"])  ) &&
  3228.             pnConfigGetVar('PN_CONFIG_USE_OBJECT_CATEGORIZATION'&&
  3229.             strcmp($tablename'categories_'!== &&
  3230.             strcmp($tablename'objectdata_attributes'!== &&
  3231.             strcmp($tablename'objectdata_log'!== &&
  3232.             pnModAvailable('Categories') )
  3233.         {
  3234.             ObjectUtil::storeObjectCategories ($object$tablename$idcolumn);
  3235.         }
  3236.  
  3237.         if ( ( (isset($pntables["{$tablename}_db_extra_enable_all"]&& $pntables["{$tablename}_db_extra_enable_all"]||
  3238.                (isset($pntables["{$tablename}_db_extra_enable_attribution"]&& $pntables["{$tablename}_db_extra_enable_attribution"||
  3239.                pnConfigGetVar('PN_CONFIG_USE_OBJECT_ATTRIBUTION') ) &&
  3240.             strcmp($tablename'objectdata_attributes'!== &&
  3241.             strcmp($tablename'objectdata_log'!== &&
  3242.             pnModAvailable('ObjectData') )
  3243.         {
  3244.             ObjectUtil::storeObjectAttributes ($object$tablename$idcolumn);
  3245.         }
  3246.  
  3247.         if ( ( (isset($pntables["{$tablename}_db_extra_enable_all"]&& $pntables["{$tablename}_db_extra_enable_all"]||
  3248.                (isset($pntables["{$tablename}_db_extra_enable_meta"]&& $pntables["{$tablename}_db_extra_enable_meta"||
  3249.                pnConfigGetVar('PN_CONFIG_USE_OBJECT_META') ) &&
  3250.             $tablename <> 'objectdata_attributes' &&
  3251.             $tablename <> 'objectdata_meta' &&
  3252.             $tablename <> 'objectdata_log' &&
  3253.             pnModAvailable('ObjectData') )
  3254.         {
  3255.             ObjectUtil::updateObjectMetaData ($object$tablename$idcolumn);
  3256.         }
  3257.  
  3258.         if ( ( (isset($pntables["{$tablename}_db_extra_enable_all"]&& $pntables["{$tablename}_db_extra_enable_all"]||
  3259.                (isset($pntables["{$tablename}_db_extra_enable_logging"]&& $pntables["{$tablename}_db_extra_enable_logging"])  ) &&
  3260.             pnConfigGetVar('PN_CONFIG_USE_OBJECT_LOGGING'&&
  3261.             strcmp($tablename'objectdata_log'!== && !$where &&
  3262.             pnModAvailable('ObjectData') )
  3263.         {
  3264.             if (pnModDBInfoLoad('ObjectData'))
  3265.             {
  3266.                 $oldObj DBUtil::selectObjectByID ($tablename$object[$idcolumn]$idcolumn);
  3267.  
  3268.                 $log                array();
  3269.                 $log['object_type'$tablename;
  3270.                 $log['object_id']   $object[$idcolumn];
  3271.                 $log['op']          ($update 'U' 'I');
  3272.  
  3273.                 if ($update{
  3274.                     $log['diff'serialize(ObjectUtil::diffExtended ($oldObj$object$idcolumn));
  3275.                 else {
  3276.                     $log['diff'serialize($object);
  3277.                 }
  3278.  
  3279.                 DBUtil::insertObject ($log'objectdata_log');
  3280.             }
  3281.         }
  3282.  
  3283.         return $object;
  3284.     }
  3285. }

Documentation generated on Fri, 18 Jul 2008 21:44:49 +0200 by phpDocumentor 1.4.1