From f8fa64feb0a33b5712c8822a6fc58de918dcdaa4 Mon Sep 17 00:00:00 2001 From: chriskl Date: Mon, 11 Feb 2002 09:32:47 +0000 Subject: [PATCH] Initial revision --- classes/database/ADODB_base.php | 298 +++ classes/database/BaseDB.php | 70 + classes/database/Postgres.php | 599 +++++ classes/database/Postgres71.php | 428 ++++ conf/config.inc | 62 + lang/english.php | 31 + lang/template.php | 16 + libraries/adodb/adodb-access.inc.php | 54 + libraries/adodb/adodb-ado.inc.php | 522 +++++ libraries/adodb/adodb-ado_access.inc.php | 38 + libraries/adodb/adodb-ado_mssql.inc.php | 36 + libraries/adodb/adodb-cryptsession.php | 245 ++ libraries/adodb/adodb-csv.inc.php | 147 ++ libraries/adodb/adodb-db2.inc.php | 163 ++ libraries/adodb/adodb-errorhandler.inc.php | 73 + libraries/adodb/adodb-errorpear.inc.php | 86 + libraries/adodb/adodb-fbsql.inc.php | 251 +++ libraries/adodb/adodb-ibase.inc.php | 349 +++ libraries/adodb/adodb-mssql.inc.php | 279 +++ libraries/adodb/adodb-mysql.inc.php | 352 +++ libraries/adodb/adodb-mysqlt.inc.php | 53 + libraries/adodb/adodb-oci8.inc.php | 503 +++++ libraries/adodb/adodb-odbc.inc.php | 369 ++++ libraries/adodb/adodb-odbc_mssql.inc.php | 38 + libraries/adodb/adodb-odbc_oracle.inc.php | 104 + libraries/adodb/adodb-oracle.inc.php | 243 ++ libraries/adodb/adodb-pear.inc.php | 348 +++ libraries/adodb/adodb-postgres.inc.php | 377 ++++ libraries/adodb/adodb-postgres7.inc.php | 62 + libraries/adodb/adodb-proxy.inc.php | 28 + libraries/adodb/adodb-session.php | 228 ++ libraries/adodb/adodb-sybase.inc.php | 215 ++ libraries/adodb/adodb-vfp.inc.php | 79 + libraries/adodb/adodb.gif | Bin 0 -> 1089 bytes libraries/adodb/adodb.inc.php | 2332 ++++++++++++++++++++ libraries/adodb/adodb.png | Bin 0 -> 29073 bytes libraries/adodb/benchmark.php | 78 + libraries/adodb/client.php | 194 ++ libraries/adodb/crypt.inc.php | 60 + libraries/adodb/license.txt | 161 ++ libraries/adodb/readme.htm | 1518 +++++++++++++ libraries/adodb/readme.txt | 57 + libraries/adodb/server.php | 92 + libraries/adodb/test.php | 436 ++++ libraries/adodb/test2.php | 52 + libraries/adodb/test3.php | 30 + libraries/adodb/test4.php | 64 + libraries/adodb/test5.php | 35 + libraries/adodb/testcache.php | 20 + libraries/adodb/testdatabases.inc.php | 164 ++ libraries/adodb/testoci8.php | 43 + libraries/adodb/testpaging.php | 36 + libraries/adodb/testpear.php | 17 + libraries/adodb/testsessions.php | 12 + libraries/adodb/tohtml.inc.php | 150 ++ libraries/adodb/tute.htm | 273 +++ public_html/browser.php | 46 + public_html/databases.php | 23 + public_html/index.php | 31 + public_html/intro.php | 22 + public_html/login.php | 68 + public_html/topbar.php | 24 + 62 files changed, 12784 insertions(+) create mode 100644 classes/database/ADODB_base.php create mode 100644 classes/database/BaseDB.php create mode 100755 classes/database/Postgres.php create mode 100644 classes/database/Postgres71.php create mode 100644 conf/config.inc create mode 100755 lang/english.php create mode 100644 lang/template.php create mode 100755 libraries/adodb/adodb-access.inc.php create mode 100755 libraries/adodb/adodb-ado.inc.php create mode 100755 libraries/adodb/adodb-ado_access.inc.php create mode 100755 libraries/adodb/adodb-ado_mssql.inc.php create mode 100755 libraries/adodb/adodb-cryptsession.php create mode 100755 libraries/adodb/adodb-csv.inc.php create mode 100755 libraries/adodb/adodb-db2.inc.php create mode 100755 libraries/adodb/adodb-errorhandler.inc.php create mode 100755 libraries/adodb/adodb-errorpear.inc.php create mode 100755 libraries/adodb/adodb-fbsql.inc.php create mode 100755 libraries/adodb/adodb-ibase.inc.php create mode 100755 libraries/adodb/adodb-mssql.inc.php create mode 100755 libraries/adodb/adodb-mysql.inc.php create mode 100755 libraries/adodb/adodb-mysqlt.inc.php create mode 100755 libraries/adodb/adodb-oci8.inc.php create mode 100755 libraries/adodb/adodb-odbc.inc.php create mode 100755 libraries/adodb/adodb-odbc_mssql.inc.php create mode 100755 libraries/adodb/adodb-odbc_oracle.inc.php create mode 100755 libraries/adodb/adodb-oracle.inc.php create mode 100755 libraries/adodb/adodb-pear.inc.php create mode 100755 libraries/adodb/adodb-postgres.inc.php create mode 100755 libraries/adodb/adodb-postgres7.inc.php create mode 100755 libraries/adodb/adodb-proxy.inc.php create mode 100755 libraries/adodb/adodb-session.php create mode 100755 libraries/adodb/adodb-sybase.inc.php create mode 100755 libraries/adodb/adodb-vfp.inc.php create mode 100755 libraries/adodb/adodb.gif create mode 100755 libraries/adodb/adodb.inc.php create mode 100755 libraries/adodb/adodb.png create mode 100755 libraries/adodb/benchmark.php create mode 100755 libraries/adodb/client.php create mode 100755 libraries/adodb/crypt.inc.php create mode 100755 libraries/adodb/license.txt create mode 100755 libraries/adodb/readme.htm create mode 100755 libraries/adodb/readme.txt create mode 100755 libraries/adodb/server.php create mode 100755 libraries/adodb/test.php create mode 100755 libraries/adodb/test2.php create mode 100755 libraries/adodb/test3.php create mode 100755 libraries/adodb/test4.php create mode 100755 libraries/adodb/test5.php create mode 100755 libraries/adodb/testcache.php create mode 100755 libraries/adodb/testdatabases.inc.php create mode 100755 libraries/adodb/testoci8.php create mode 100755 libraries/adodb/testpaging.php create mode 100755 libraries/adodb/testpear.php create mode 100755 libraries/adodb/testsessions.php create mode 100755 libraries/adodb/tohtml.inc.php create mode 100755 libraries/adodb/tute.htm create mode 100644 public_html/browser.php create mode 100755 public_html/databases.php create mode 100755 public_html/index.php create mode 100755 public_html/intro.php create mode 100755 public_html/login.php create mode 100755 public_html/topbar.php diff --git a/classes/database/ADODB_base.php b/classes/database/ADODB_base.php new file mode 100644 index 00000000..1fb1a22b --- /dev/null +++ b/classes/database/ADODB_base.php @@ -0,0 +1,298 @@ +conn->databaseType = $type; + $this->conn = &ADONewConnection($type); + $this->conn->setFetchMode($fetchMode); + } + + /** + * Cleans (escapes) a string + * @param $str The string to clean, by reference + * @return The cleaned string + */ + function clean(&$str) { + $str = addslashes($str); + return $str; + } + + /** + * Retrieves a ResultSet from a query + * @param $sql The SQL statement to be executed + * @return A recordset + */ + function selectSet($sql) { + // Execute the statement + $rs = $this->conn->Execute($sql); + + // If failure, return error value + if (!$rs) return $this->conn->ErrorNo(); + + return $rs; + } + + /** + * Retrieves a single value from a query + * + * @@ assumes that the query will return only one row - returns field value in the first row + * + * @param $sql The SQL statement to be executed + * @param $field The field name to be returned + * @return A single field value + * @return -1 No rows were found + */ + function selectField($sql, $field) { + // Execute the statement + $rs = $this->conn->Execute($sql); + + // If failure, or no rows returned, return error value + if (!$rs) return $this->conn->ErrorNo(); + elseif ($rs->RecordCount() == 0) return -1; + + return $rs->f[$field]; + } + + /** + * Delete from the database + * @param $table The name of the table + * @param $conditions (array) A map of field names to conditions + * @return 0 success + * @return -1 on referential integrity violation + * @return -2 on no rows deleted + */ + function delete($table, $conditions) { + $this->clean($table); + + reset($conditions); + + // Build clause + $sql = ''; + while(list($key, $value) = each($conditions)) { + $this->clean($key); + $this->clean($value); + if ($sql) $sql .= " AND {$key}='{$value}'"; + else $sql = "DELETE FROM {$table} WHERE {$key}='{$value}'"; + } + + // Check for failures + if (!$this->conn->Execute($sql)) { + // Check for referential integrity failure + if (stristr($this->conn->ErrorMsg(), 'referential')) + return -1; + } + + // Check for no rows modified + if ($this->conn->Affected_Rows() == 0) return -2; + + return $this->conn->ErrorNo(); + } + + /** + * Insert a set of values into the database + * @param $table The table to insert into + * @param $vars (array) A mapping of the field names to the values to be inserted + * @return 0 success + * @return -1 if a unique constraint is violated + * @return -2 if a referential constraint is violated + */ + function insert($table, $vars) { + $this->clean($table); + + reset($vars); + + // Build clause + $fields = ''; + $values = ''; + while(list($key, $value) = each($vars)) { + $this->clean($key); + $this->clean($value); + + if ($fields) $fields .= ", {$key}"; + else $fields = "INSERT INTO {$table} ({$key}"; + + if ($values) $values .= ", '{$value}'"; + else $values = ") VALUES ('{$value}'"; + } + + // Check for failures + if (!$this->conn->Execute($fields . $values . ')')) { + // Check for unique constraint failure + if (stristr($this->conn->ErrorMsg(), 'unique')) + return -1; + // Check for referential integrity failure + elseif (stristr($this->conn->ErrorMsg(), 'referential')) + return -2; + } + + return $this->conn->ErrorNo(); + } + + /** + * Update a row in the database + * @param $table The table that is to be updated + * @param $vars (array) A mapping of the field names to the values to be updated + * @param $where (array) A mapping of field names to values for the where clause + * @param $nulls (array, optional) An array of fields to be set null + * @return 0 success + * @return -1 if a unique constraint is violated + * @return -2 if a referential constraint is violated + * @return -3 on no rows deleted + */ + function update($table, $vars, $where, $nulls = array()) { + $this->clean($table); + + $setClause = ''; + $whereClause = ''; + + // Populate the syntax arrays + reset($vars); + while(list($key, $value) = each($vars)) { + $this->clean($key); + $this->clean($value); + if ($setClause) $setClause .= ", {$key}='{$value}'"; + else $setClause = "UPDATE {$table} SET {$key}='{$value}'"; + } + + reset($nulls); + while(list(, $value) = each($nulls)) { + $this->clean($value); + if ($setClause) $setClause .= ", {$value}=NULL"; + else $setClause = "UPDATE {$table} SET {$value}=NULL"; + } + + reset($where); + while(list($key, $value) = each($where)) { + $this->clean($key); + $this->clean($value); + if ($whereClause) $whereClause .= " AND {$key}='{$value}'"; + else $whereClause = " WHERE {$key}='{$value}'"; + } + + // Check for failures + if (!$this->conn->Execute($setClause . $whereClause)) { + // Check for unique constraint failure + if (stristr($this->conn->ErrorMsg(), 'unique')) + return -1; + // Check for referential integrity failure + elseif (stristr($this->conn->ErrorMsg(), 'referential')) + return -2; + } + + // Check for no rows modified + if ($this->conn->Affected_Rows() == 0) return -3; + + return $this->conn->ErrorNo(); + } + + /** + * Begin a transaction + * @return 0 success + */ + function beginTransaction() { + return !$this->conn->BeginTrans(); + } + + /** + * End a transaction + * @return 0 success + */ + function endTransaction() { + return !$this->conn->CommitTrans(); + } + + /** + * Roll back a transaction + * @return 0 success + */ + function rollbackTransaction() { + return !$this->conn->RollbackTrans(); + } + + // Type conversion routines + + /** + * Converts an array of identifiers into a bitset, given a bitset definition + * Duplicate flags are silently ignored. + * Note: This is not case insensitive. + * @param flags An array of identifiers + * @param allowed The definition of allowed identifiers, mapping identifier -> bit index + * @return A string representation of the bitset + */ + function dbBitSet(&$flags, &$allowed) { + $bitset = (int)0; + for ($i = 0; $i < sizeof($flags); $i++) { + if (isset($allowed[$flags[$i]])) { + $bitset |= (1 << $allowed[$flags[$i]]); + } + } + + // Left-pad the bitset to match the database size + // Important! + $bitset = decbin($bitset); + $bitset = str_pad($bitset, sizeof($allowed), '0', STR_PAD_LEFT); + + return 'b' . $bitset; + } + + /** + * Change an int from a database bitset field back into an array of identifiers + * @param $bitset The database bitset, in database format (B'10101') + * @param $allowed The definition of allowed identifiers, mapping identifier -> bit index + * @return An array of identifiers + */ + function phpBitSet($bitset, &$allowed) { + // @@ NOTE: Should we be doing better error handling here? + // @@ This condition will catch both NULL values (correctly) and borked values (incorrectly) + if (!ereg("^[01]+$", $bitset)) return array(); + + $intset = bindec($bitset); + + $temp = array(); + + reset($allowed); + while (list($k, $v) = each($allowed)) { + if ($intset & (1 << $v)) $temp[] = $k; + } + return $temp; + } + + /** + * Change the value of a parameter to 't' or 'f' depending on whether it evaluates to true or false + * @param $parameter the parameter + */ + function dbBool(&$parameter) { + if ($parameter) $parameter = 't'; + else $parameter = 'f'; + + return $parameter; + } + + /** + * Change a parameter from 't' or 'f' to a boolean, (others evaluate to false) + * @param $parameter the parameter + */ + function phpBool($parameter) { + $parameter = ($parameter == 't'); + return $parameter; + } + +} + +?> diff --git a/classes/database/BaseDB.php b/classes/database/BaseDB.php new file mode 100644 index 00000000..c5722abd --- /dev/null +++ b/classes/database/BaseDB.php @@ -0,0 +1,70 @@ +ADODB_base($type); + } + + /** + * Set object filtering for user + * @param $state (boolean) + */ + function setFilterTables($state) { + $this->$_filterTables = $state; + } + +/* + // Feature functions + + // Is "ALTER TABLE" with add column supported? + function supportsAlterTableWithAddColumn() {} + + // Is "ALTER TABLE" with drop column supported? + function supportsAlterTableWithDropColumn() + + // Are both data definition and data manipulation statements within a transaction supported? + function supportsDataDefinitionAndDataManipulationTransactions() + + // Are only data manipulation statements within a transaction supported? + function supportsDataManipulationTransactionsOnly() + + // Does the database treat mixed case unquoted SQL identifiers as case sensitive and as a result store them in mixed case? A JDBC CompliantTM driver will always return false. + function supportsMixedCaseIdentifiers() + + // Does the database treat mixed case quoted SQL identifiers as case sensitive and as a result store them in mixed case? A JDBC CompliantTM driver will always return true. + function supportsMixedCaseQuotedIdentifiers() + + // Can columns be defined as non-nullable? A JDBC CompliantTM driver always returns true. + function supportsNonNullableColumns() + + // Are stored procedure calls using the stored procedure escape syntax supported? + function supportsStoredProcedures() + + // Are transactions supported? If not, invoking the method commit is a noop and the isolation level is TRANSACTION_NONE. + function supportsTransactions() + + // Can you define your own aggregates? + function supportsAggregates() + + // Can you define your own operators? + function supportsOperators() + + // Database manipulation + +*/ +} + +?> \ No newline at end of file diff --git a/classes/database/Postgres.php b/classes/database/Postgres.php new file mode 100755 index 00000000..db43021b --- /dev/null +++ b/classes/database/Postgres.php @@ -0,0 +1,599 @@ +User = 'auadmin'; + $this->Password = 'bfd12hutz'; + + $this->BaseDB(); + } + + // Flag functions + + /** + * Retrieves the flags of the specified user + * @param $user_id The ID of the user + * @param $values (optional) Return array as values, rather than keys + * @return An array containing the flag names as it keys with values of true + */ + function &getUserFlags($user_id, $values = false) { + $this->clean($meal_id); + $sql = "SELECT fl.name FROM users_flags uf, medidiets_flags fl WHERE uf.flag_id = fl.flag_id AND uf.user_id = '{$user_id}'"; + $rs = $this->selectSet($sql); + + $flags = array(); + while (!$rs->EOF) { + if ($values) + $flags[] = $rs->f['name']; + else + $flags[$rs->f['name']] = true; + $rs->moveNext(); + } + + return $flags; + } + + /** + * Retrieves a user's profile and plan joined + * @param $user_id The ID of the user whose data is to be returned + * @return A recordset + */ + function &getProfileAndPlan($user_id) { + $this->clean($user_id); + + $sql = "SELECT * FROM users_profiles_tmp up, medidiets_plans mp WHERE up.user_id='$user_id' AND up.plan_id=mp.plan_id"; + + return $this->selectSet($sql); + } + + // Plan functions + + /** + * Returns all existing plans + * @return A recordset + */ + function &getPlans() { + $sql = "SELECT * FROM medidiets_plans ORDER BY calories"; + return $this->selectSet($sql); + } + + /* + * Finds a meal plan that matches the given number of calories most closely + * @param $cals The number of calories the plan should be + * @return (array) The ID of the plan found, and the actual cals + */ + function &findPlan($cals) { + $this->clean($cals); + + $sql = "SELECT plan_id, calories, ABS(calories::numeric - '$cals') AS diff FROM medidiets_plans ORDER BY diff LIMIT 1"; + $rs = $this->selectSet($sql); + + return array($rs->f['plan_id'], $rs->f['calories']); + } + + /* + * Finds all meals that adhere to the given options, randomly ordered + * @param $cals The number of calories the plan should be + * @param $when_id The meal of the day to find meals for + * @param $flags An array of flag names to be excluded + * @param $names (optional) Specify whether to return names as well, sorted + * @return A recordset + */ + function &findMeals($cals, $when_id, $flags, $names = false) { + $this->clean($cals); + $this->clean($when_id); + $flag_str = implode("','", $flags); + + if ($names) { + $params = 'meal_id, name'; + $order = 'name'; + } + else { + $params = 'meal_id'; + $order = 'RANDOM()'; + } + + $sql = " + SELECT + {$params} + FROM + medidiets_meals mm + WHERE + mm.calories='{$cals}' + AND mm.when_id='{$when_id}' + AND mm.visible + AND NOT mm.pending + AND mm.meal_id NOT IN ( + SELECT + DISTINCT (meal_id) + FROM + medidiets_flags_map mfp, medidiets_flags mf + WHERE + mfp.flag_id=mf.flag_id + AND mf.name IN ('{$flag_str}') + ) + ORDER BY {$order} + "; + + return $this->selectSet($sql); + } + + /* + * Inserts meals into a range of dates for a user + * @param $user_id The ID of the user we're inserting for + * @param $start_date The date at which to start the insertion + * @param $no_days The number of days to insert + * @return 0 success + * @return -1 transaction error + * @return -2 profile/plan retrieval error + * @return -3 meal list retrieval error + * @return -4 insertion failure + * @@ What happens if no breakfasts could be found?!?!? + */ + function createMealPlan($user_id, $start_date, $no_days) { + global $defaultCountryCode; + + // begin transaction + $status = $this->beginTransaction(); + if ($status != 0) return -1; + + // Get user data, joined with plan data? + $data = &$this->getProfileAndPlan($user_id); + $data->f['seasonal'] = $this->phpBool($data->f['seasonal']); + $flags = &$this->getUserFlags($user_id, true); + + // Seasonal menus + if ($data->f['seasonal']) { + if ($defaultCountryCode == 'AU') + $is_summer = in_array(date('m'), array(10, 11, 12, 1, 2, 3)); + else + $is_summer = in_array(date('m'), array(4, 5, 6, 7, 8, 9)); + + if ($is_summer) $flags[] = 'NOT_SUMMER'; + else $flags[] = 'NOT_WINTER'; + } + + // Convenience + $b_flags = $flags; + if ($data->f['mp_breakfast'] == 'C') $b_flags[] = 'NOT_CONVENIENT'; + $l_flags = $flags; + if ($data->f['mp_lunch'] == 'C') $l_flags[] = 'NOT_CONVENIENT'; + $d_flags = $flags; + if ($data->f['mp_dinner'] == 'C') $d_flags[] = 'NOT_CONVENIENT'; + + // Get lists of breakfasts, lunches, dinners and snacks, ordered + // randomly. + if ($data->f['breakfast'] > 0) { + $breakfasts = &$this->findMeals($data->f['breakfast'], 1, $b_flags); + if (!$breakfasts) { + $this->rollbackTransaction(); + return -3; + } + if ($breakfasts->recordCount() == 0) reportError("User {$user_id} could not find a {$data->f['breakfast']} cal breakfast for " . + implode(',', $b_flags) . " flags."); + } + if ($data->f['lunch'] > 0) { + $lunches = &$this->findMeals($data->f['lunch'], 2, $l_flags); + if (!$lunches) { + $this->rollbackTransaction(); + return -3; + } + if ($lunches->recordCount() == 0) reportError("User {$user_id} could not find a {$data->f['lunch']} cal lunch for " . + implode(',', $l_flags) . " flags."); + } + if ($data->f['dinner'] > 0) { + $dinners = &$this->findMeals($data->f['dinner'], 3, $d_flags); + if (!$dinners) { + $this->rollbackTransaction(); + return -3; + } + if ($dinners->recordCount() == 0) reportError("User {$user_id} could not find a {$data->f['dinner']} cal dinner for " . + implode(',', $d_flags) . " flags."); + } + if ($data->f['snack'] > 0) { + $snacks = &$this->findMeals($data->f['snack'], 4, $flags); + if (!$snacks) { + $this->rollbackTransaction(); + return -3; + } + if ($snacks->recordCount() == 0) reportError("User {$user_id} could not find a {$data->f['snack']} cal snack for " . + implode(',', $flags) . " flags."); + } + + // For each day, insert a breakfast, lunch, dinner and snack + list($sy, $sm, $sd) = explode('-', $start_date); + for ($i = 0; $i < $no_days; $i++) { + $temp = array(); + $temp['user_id'] = $user_id; + $temp['date'] = date('Y-m-d', mktime(0, 0, 0, $sm, $sd + $i, $sy)); + // Breakfast + if ($data->f['breakfast'] > 0 && $breakfasts->recordCount() > 0) { + $temp['breakfast_id'] = $breakfasts->f['meal_id']; + $breakfasts->moveNext(); + if ($breakfasts->EOF) $breakfasts->moveFirst(); + } + // Lunch + if ($data->f['lunch'] > 0 && $lunches->recordCount() > 0) { + $temp['lunch_id'] = $lunches->f['meal_id']; + $lunches->moveNext(); + if ($lunches->EOF) $lunches->moveFirst(); + } + // Dinner + if ($data->f['dinner'] > 0 && $dinners->recordCount() > 0) { + $temp['dinner_id'] = $dinners->f['meal_id']; + $dinners->moveNext(); + if ($dinners->EOF) $dinners->moveFirst(); + } + // Snack + if ($data->f['snack'] > 0 && $snacks->recordCount() > 0) { + $temp['snack_id'] = $snacks->f['meal_id']; + $snacks->moveNext(); + if ($snacks->EOF) $snacks->moveFirst(); + } + + $status = $this->insert('users_mealplans', $temp); + if ($status != 0) { + $this->rollbackTransaction(); + return -4; + } + unset($temp); + } + + // @@ Take care of overriding preferences + + return $this->endTransaction(); + } + + /** + * Records a user's meal substitution to the database + * @param $user_id The ID of the user to be updated + * @param $date The date to update + * @param $when_id The meal to replace + * @param $meal_id The new meal to substitute + * @return 0 success + */ + function setUserMeal($user_id, $date, $when_id, $meal_id) { + $fields = array('', 'breakfast_id', 'lunch_id', 'dinner_id', 'snack_id'); + return $this->update('users_mealplans', + array($fields[$when_id] => $meal_id), + array('user_id' => $user_id, 'date' => $date)); + } + + /** + * Retrieves a user's shopping list + * @param $user_id The ID fo the user to retrieve + * @param $date The date from which to retrieve + * @param $num_days The number of days from the date forward to retrieve + */ + function &getShoppingList($user_id, $from, $to) { + $this->clean($user_id); + $this->clean($date); + + $sql = " + SELECT description, food_id, name, measurement, base, type, name_pl, measurement_pl, qty, SUM(quantity) AS quantity FROM ( + (SELECT + mcf.description, mf.food_id, mf.name, mf.measurement, mf.base, mf.type, mf.name_pl, mf.measurement_pl, mf.quantity AS qty, mfm.quantity AS quantity + FROM + medidiets_categories_foods mcf, medidiets_foods mf, medidiets_foods_map mfm, users_mealplans um + WHERE + mcf.category_id=mf.category_id + AND mf.food_id=mfm.food_id + AND mfm.meal_id IN (um.breakfast_id, um.lunch_id, um.dinner_id, um.snack_id) + AND um.date BETWEEN '$from' AND '$to' + AND user_id='$user_id') + UNION ALL + (SELECT + mcf.description, mf.food_id, mf.name, mf.measurement, mf.base, mf.type, mf.name_pl, mf.measurement_pl, mf.quantity AS qty, mi.quantity AS quantity + FROM + medidiets_categories_foods mcf, medidiets_foods mf, medidiets_ingredients mi, medidiets_recipes_map mrm, users_mealplans um + WHERE + mcf.category_id=mf.category_id + AND mf.food_id=mi.food_id + AND mi.recipe_id=mrm.recipe_id + AND mrm.meal_id IN (um.breakfast_id, um.lunch_id, um.dinner_id, um.snack_id) + AND um.date BETWEEN '$from' AND '$to' + AND user_id='$user_id') + ) AS subselect + GROUP BY description, food_id, name, measurement, base, type, name_pl, measurement_pl, qty + "; + + return $this->selectSet($sql); + } + + // Miscellaneous functions + + /* + * Returns an array with the correct imperial or metric units + * @return Array containing measurements + */ + function getDefaultUnits() { + global $defaultMeasurements; + + if ($defaultMeasurement == 'imperial') { + $units = array('M' => 'oz', 'V' => 'fl.oz'); + } + else { + $units = array('M' => 'g', 'V' => 'mL'); + } + + return $units; + } + + /* + * A helper function to convert decimals to fractions + * @param A decimal number to convert + * @return The formatted version of the number + */ + function _fracConvert($num) { + $decpart = $num - floor($num); + + switch ($decpart * 4) { + case 0: + return number_format($num, 0); + break; + case 1: + if (floor($num) != 0) + return number_format(floor($num), 0) . ' 1/4'; + else + return '1/4'; + break; + case 2: + if (floor($num) != 0) + return number_format(floor($num), 0) . ' 1/2'; + else + return '1/2'; + break; + case 3: + if (floor($num) != 0) + return number_format(floor($num), 0) . ' 3/4'; + else + return '3/4'; + break; + default: + if ($decpart == 0.33) { + if (floor($num) != 0) + return number_format(floor($num), 0) . ' 1/3'; + else + return '1/3'; + } + elseif ($decpart == 0.66 || $decpart == 0.67) { + if (floor($num) != 0) + return number_format(floor($num), 0) . ' 2/3'; + else + return '2/3'; + } + else return number_format($num, 2); + + break; + } + } + + /** + * Return the string to be used to as the display for the ingredient + * + * The string is to be formatted depending upon the values of the measurement + * and base variables. The string will also use the $defaultMeasurement + * specified in the network config files. + * + * @param $name The name of the food of the ingredient + * @param $name The pluralised form of the name + * @param $quantity The amount of ingredient + * @param $measurement The measurement of the food of the ingredient ('' if none) + * @param $measurement The pluralised form of the measurement ('' if none) + * @param $base The base size of the food of the ingredient ('' if none) + * @param $type The base type of the food of the ingredient ('M' - mass, 'V' - volume) + * @param $show_qty (optional) If false, only gives the name, not the quantity + * @param $how How the ingredient is prepared. + * + */ + function getIngredientDisplay($name, $name_pl, $quantity, $measurement, $measurement_pl, $base, $type, $how, $show_qty = true) { + global $defaultMeasurement; + + // set display units depending on defaultMeasurement + $units = $this->getDefaultUnits(); + + // if quantity is an integer value show only the integer + if ($quantity == (int)$quantity) $quantity = (int)$quantity; + + // discard fractional part of base size, cos who can measure half a gram? + if ($base != '') { + $base = round($base); + if ($base == 0) $base = 1; + } + + if ($measurement != '' && $base != '') { + // both measurement and base are set + // eg. "2 1/2 375mL cans of Tomato Soup" + $display_qty = $this->_fracConvert($quantity) . ' '; + $display_nam = $base . $units[$type] . ' '; + $display_nam .= ($quantity > 1) ? $measurement_pl : $measurement; + $display_nam .= ' of ' . $name; + } + elseif ($measurement != '') { + // measurement is set but base is not + // eg. "4 2/3 slices of Bread" + $display_qty = $this->_fracConvert($quantity) . ' '; + $display_nam = ($quantity > 1) ? $measurement_pl : $measurement; + $display_nam .= ' of ' . $name; + } + elseif ($base != '') { + // base is set but measurement is not + // eg. "500g of Beef" + $display_qty = ceil($quantity * $base); + $display_nam = $units[$type] . ' of ' . $name; + } + else { + // neither measurement nor base is set + // eg. "1 1/2 Apples" + $display_qty = $this->_fracConvert($quantity) . ' '; + $display_nam = ($quantity > 1) ? $name_pl : $name; + } + // Add 'crumbed', etc. + if ($how != '') $display_nam .= ", {$how}"; + + if ($show_qty) + return $display_qty . $display_nam; + else + return $display_nam; + + } + + // Meal Manipulation Functions + + /** + * Retrieve the Nutritional Data for a Food associated with a given meal + * @param $meal_id The ID of the meal + * @param $food_id The ID of the food + * @return The set of nutritional data and food information + */ + function &getMealFood($meal_id, $food_id) { + $this->clean($meal_id); + $this->clean($food_id); + $sql = "SELECT f.*, fm.* FROM medidiets_foods_map fm, medidiets_foods f WHERE fm.food_id = f.food_id AND fm.meal_id = '{$meal_id}' AND fm.food_id = '{$food_id}'"; + return $this->selectSet($sql); + } + + /** + * Retrieves all categories in the database, with all fields, sorted + * alphabetically. This userland function excludes all categories with id's + * less than zero! + * @return A recordset + */ + function &getRecipeCategories() { + $sql = "SELECT * FROM medidiets_categories_rec WHERE category_id >= 0 ORDER BY description"; + return $this->selectSet($sql); + } + + /** + * Retrieve the Nutritional Data for a Food associated with a given recipe + * @param $recipe_id The ID of the recipe + * @param $food_id The ID of the food + * @return The set of nutritional data and food information + */ + function &getRecipeIngredient($recipe_id, $food_id) { + $this->clean($recipe_id); + $this->clean($food_id); + $sql = "SELECT f.*, fm.* FROM medidiets_ingredients fm, medidiets_foods f WHERE fm.food_id = f.food_id AND fm.recipe_id = '{$recipe_id}' AND fm.food_id = '{$food_id}'"; + return $this->selectSet($sql); + } + + /** + * Retrieve a particular recipe + * @param $recipe_id The ID of the recipe to retrieve + * @return a recordset + */ + function &getRecipe($recipe_id) { + $this->clean($recipe_id); + $sql = "SELECT * FROM medidiets_recipes WHERE recipe_id='{$recipe_id}'"; + return $this->selectSet($sql); + } + + /** + * Retrieve all recipes in a particular category, sorted alphabetically + * This userland version of the function doesn't show magic recipes. + * @param $category_id The ID of the category in which to search + * @param $data A boolean switch to retrieve the nutritional data for the recipe (default false) + * @return a recordset + */ + function &getRecipes($category_id, $data = false) { + $this->clean($category_id); + if ($data) { + $sql = "SELECT * FROM + medidiets_recipes r LEFT JOIN + (SELECT i.recipe_id, + sum(i.quantity * f.calories) AS calories, + sum(i.quantity * f.kilojoules) AS kilojoules, + sum(i.quantity * f.protein) AS protein, + sum(i.quantity * f.fat) AS fat, + sum(i.quantity * f.saturated_fat) AS saturated_fat, + sum(i.quantity * f.carbohydrate) AS carbohydrate, + sum(i.quantity * f.sugar) AS sugar, + sum(i.quantity * f.fiber) AS fiber, + sum(i.quantity * f.cholesterol) AS cholesterol, + sum(i.quantity * f.sodium) AS sodium, + sum(i.quantity * f.potassium) AS potassium, + sum(i.quantity * f.calcium) AS calcium, + sum(i.quantity * f.iron) AS iron, + sum(i.quantity * f.zinc) AS zinc + FROM + medidiets_ingredients i, + medidiets_foods f + WHERE i.food_id = f.food_id + GROUP BY i.recipe_id + ) AS sub + ON r.recipe_id = sub.recipe_id + AND r.category_id = '{$category_id}' + AND r.recipe_id >= 0 + ORDER BY r.name"; + } + else { + $sql = "SELECT * FROM medidiets_recipes WHERE category_id='{$category_id}' AND recipe_id >= 0 ORDER BY name"; + } + + return $this->selectSet($sql); + } + + /** + * Retrieve the nutritional data for the specified recipe + * @param $recipe_id The ID of the recipe to retrieve + * @return a recordset + */ + function &getRecipeData($recipe_id) { + $this->clean($recipe_id); + $sql = "SELECT * FROM + medidiets_recipes r LEFT JOIN + (SELECT i.recipe_id, + sum(i.quantity * f.calories) AS total_calories, + sum(i.quantity * f.kilojoules) AS total_kilojoules, + sum(i.quantity * f.protein) AS total_protein, + sum(i.quantity * f.fat) AS total_fat, + sum(i.quantity * f.saturated_fat) AS total_saturated_fat, + sum(i.quantity * f.carbohydrate) AS total_carbohydrate, + sum(i.quantity * f.sugar) AS total_sugar, + sum(i.quantity * f.fiber) AS total_fiber, + sum(i.quantity * f.cholesterol) AS total_cholesterol, + sum(i.quantity * f.sodium) AS total_sodium, + sum(i.quantity * f.potassium) AS total_potassium, + sum(i.quantity * f.calcium) AS total_calcium, + sum(i.quantity * f.iron) AS total_iron, + sum(i.quantity * f.zinc) AS total_zinc + FROM + medidiets_ingredients i, + medidiets_foods f + WHERE i.food_id = f.food_id + GROUP BY i.recipe_id + ) AS sub + ON r.recipe_id = sub.recipe_id + WHERE r.recipe_id = '{$recipe_id}'"; + return $this->selectSet($sql); + } + + /** + * Retrieve all ingredients for a recipe, sorted by their oid + * @param $recipe_id The ID of the recipe to which the ingredients are needed + * @return a recordset + */ + function &getRecipeIngredients($recipe_id) { + $this->clean($recipe_id); + // @@ i.quantity must be after the f.* in the select until f.quantity removed... + $sql = "SELECT f.*, i.quantity, i.how FROM medidiets_foods f, medidiets_ingredients i + WHERE f.food_id = i.food_id + AND i.recipe_id = '{$recipe_id}' + ORDER BY i.oid"; + + return $this->selectSet($sql); + } + +} + +?> \ No newline at end of file diff --git a/classes/database/Postgres71.php b/classes/database/Postgres71.php new file mode 100644 index 00000000..34239d76 --- /dev/null +++ b/classes/database/Postgres71.php @@ -0,0 +1,428 @@ +BaseDB('postgres7'); + +// $this->conn->host = $host; + //$this->Port = $port; + + $this->conn->connect($host, $user, $password, $database); + } + + /** + * Return all database available on the server + * @return A list of databases, sorted alphabetically + */ + function &getDatabases() { + $sql = "SELECT pdb.datname, pdb.datistemplate, pde.description FROM + pg_database pdb LEFT JOIN pg_description pde ON pdb.oid=pde.objoid + ORDER BY pdb.datname"; + return $this->selectSet($sql); + } + + /** + * Return all information about a particular database + * @param $database The name of the database to retrieve + * @return The database info + */ + function &getDatabase($database) { + $this->clean($database); + $sql = "SELECT * FROM pg_database WHERE datname='{$database}'"; + return $this->selectRow($sql); + } + + /** + * Drops a database + * @param $database The name of the database to retrieve + * @return 0 success + */ + function dropDatabase($database) { + $this->clean($database); + $sql = "DROP DATABASE \"{$database}\""; + } + + // Table functions + + /** + * Return all tables in current database + * @return All tables, sorted alphabetically + */ + function &getTables() { + $sql = "SELECT * FROM pg_class ORDER BY relname"; + return $this->selectSet($sql); + } + + /** + * Return all information relating to a table + * @param $table The name of the table + * @return Table information + */ + function &getTableByName($table) { + $this->clean($table); + $sql = "SELECT * FROM pg_class WHERE relname='{$table}'"; + return $this->selectRow($sql); + } + + // @@ Need create table - tricky!! + + /** + * Removes a table from the database + * @param $table + * @return 0 success + */ + function dropTable($table) { + $this->clean($table); + + $sql = "DROP TABLE \"{$table}\""; + + // @@ How do you do this? + return $this->execute($sql); + } + + /** + * Renames a table + * @param $table The table to be renamed + * @param $newName The new name for the table + * @return 0 success + */ + function renameTable($table, $newName) { + $this->clean($table); + $this->clean($newName); + $sql = "ALTER TABLE \"{$table}\" RENAME TO \"{$newName}\""; + + // @@ How do you do this? + return $this->execute($sql); + } + + /** + * Adds a check constraint to a table + * @param $table The table to which to add the check + * @param $definition The definition of the check + * @param $name (optional) The name to give the check, otherwise default name is assigned + * @return 0 success + */ + function addCheckConstraint($table, $definition, $name = '') { + $this->clean($table); + $this->clean($name); + // @@ how the heck do you clean definition??? + + if ($name != '') + $sql = "ALTER TABLE \"{$table}\" ADD CONSTRAINT \"{$name}\" CHECK ({$definition})"; + else + $sql = "ALTER TABLE \"{$table}\" ADD CHECK ({$definition})"; + + // @@ How do you do this? + return $this->execute($sql); + } + + /** + * Drops a check constraint from a table + * @param $table The table from which to drop the check + * @param $name The name of the check to be dropped + * @return 0 success + * @return -2 transaction error + * @return -3 lock error + * @return -4 check drop error + */ + function dropCheckConstraint($table, $name) { + $this->clean($table); + $this->clean($name); + + // Begin transaction + $status = $this->beginTransaction(); + if ($status != 0) return -2; + + // Properly lock the table + $sql = "LOCK TABLE \"{$table}\" IN ACCESS EXCLUSIVE MODE"; + $status = $this->execute($sql); + if ($status != 0) { + $this->rollbackTransaction(); + return -3; + } + + // Delete the check constraint + $sql = "DELETE FROM pg_relcheck WHERE rcrelid=(SELECT oid FROM pg_class WHERE relname='{$table}') AND rcname='{$name}'"; + $status = $this->execute($sql); + if ($status != 0) { + $this->rollbackTransaction(); + return -4; + } + + // Update the pg_class catalog to reflect the new number of checks + $sql = "UPDATE pg_class SET relchecks=(SELECT COUNT(*) FROM pg_relcheck WHERE + rcrelid=(SELECT oid FROM pg_class WHERE relname='{$table}')) + WHERE relname='{$table}'"; + $status = $this->execute($sql); + if ($status != 0) { + $this->rollbackTransaction(); + return -4; + } + + // Otherwise, close the transaction + return $this->endTransaction(); + } + + /** + * Adds a unique constraint to a table + * @param $table The table to which to add the unique + * @param $fields (array) An array of fields over which to add the unique + * @param $name (optional) The name to give the unique, otherwise default name is assigned + * @return 0 success + */ + function addUniqueConstraint($table, $fields, $name = '') { + $this->clean($table); + $this->arrayClean($fields); + $this->clean($name); + + if ($name != '') + $sql = "CREATE UNIQUE INDEX \"{$name}\" ON \"{$table}\"(\"" . join('","', $fields) . "\")"; + else return -99; // Not supported + + // @@ How do you do this? + return $this->execute($sql); + } + + /** + * Drops a unique constraint from a table + * @param $table The table from which to drop the unique + * @param $name The name of the unique + * @return 0 success + */ + function dropUniqueConstraint($table, $name) { + $this->clean($table); + $this->clean($name); + + $sql = "DROP INDEX \"{$name}\""; + + // @@ How do you do this? + return $this->execute($sql); + } + + /** + * Adds a primary key constraint to a table + * @param $table The table to which to add the primery key + * @param $fields (array) An array of fields over which to add the primary key + * @param $name (optional) The name to give the key, otherwise default name is assigned + * @return 0 success + */ + function addPrimaryKeyConstraint($table, $fields, $name = '') { + // This function can be faked with a unique index and a catalog twiddle, however + // how do we ensure that it's only used on NOT NULL fields? + return -99; // Not supported. + } + + /** + * Drops a primary key constraint from a table + * @param $table The table from which to drop the primary key + * @param $name The name of the primary key + * @return 0 success + */ + function dropPrimaryKeyConstraint($table, $name) { + $this->clean($table); + $this->clean($name); + + $sql = "DROP INDEX \"{$name}\""; + + // @@ How do you do this? + return $this->execute($sql); + } + + /** + * Changes the owner of a table + * @param $table The table whose owner is to change + * @param $owner The new owner (username) of the table + * @return 0 success + */ + function setOwnerOfTable($table, $owner) { + $this->clean($table); + $this->clean($owner); + + $sql = "ALTER TABLE \"{$table}\" OWNER TO \"{$owner}\""; + + // @@ How do you do this? + return $this->execute($sql); + } + + // Column Functions + + /** + * Add a new column to a table + * @param $table The table to add to + * @param $column The name of the new column + * @param $type The type of the column + * @param $size (optional) The optional size of the column (ie. 30 for varchar(30)) + * @return 0 success + */ + function addColumnToTable($table, $column, $type, $size = '') { + $this->clean($table); + $this->clean($column); + $this->clean($type); + $this->clean($size); + // @@ How the heck do you properly clean type and size? + + if ($size == '') + $sql = "ALTER TABLE \"{$table}\" ADD COLUMN \"{$column}\" {$type}"; + else + $sql = "ALTER TABLE \"{$table}\" ADD COLUMN \"{$column}\" {$type}({$size})"; + + // @@ How do you do this? + return $this->execute($sql); + } + + /** + * Drops a column from a table + * @param $table The table from which to drop + * @param $column The column name to drop + * @return 0 success + */ + function dropColumnFromTable($table, $column) { + return -99; // Not implemented + } + + /** + * Sets default value of a column + * @param $table The table from which to drop + * @param $column The column name to set + * @param $default The new default value + * @return 0 success + */ + function setColumnDefault($table, $column, $default) { + $this->clean($table); + $this->clean($column); + // @@ How the heck do you clean default clause? + + $sql = "ALTER TABLE \"{$table}\" ALTER COLUMN \"{$column}\" SET DEFAULT {$default}"; + + // @@ How do you do this? + return $this->execute($sql); + } + + /** + * Drops default value of a column + * @param $table The table from which to drop + * @param $column The column name to drop default + * @return 0 success + */ + function dropColumnDefault($table, $column) { + $this->clean($table); + $this->clean($column); + + $sql = "ALTER TABLE \"{$table}\" ALTER COLUMN \"{$column}\" DROP DEFAULT"; + + // @@ How do you do this? + return $this->execute($sql); + } + + /** + * Sets whether or not a column can contain NULLs + * @param $table The table that contains the column + * @param $column The column to alter + * @param $state True to set null, false to set not null + * @return 0 success + * @return -1 attempt to set not null, but column contains nulls + * @return -2 transaction error + * @return -3 lock error + * @return -4 update error + */ + function setColumnNull($table, $column, $state) { + $this->clean($table); + $this->clean($column); + + // Begin transaction + $status = $this->beginTransaction(); + if ($status != 0) return -2; + + // Properly lock the table + $sql = "LOCK TABLE \"{$table}\" IN ACCESS EXCLUSIVE MODE"; + $status = $this->execute($sql); + if ($status != 0) { + $this->rollbackTransaction(); + return -3; + } + + // Check for existing nulls + if (!$state) { + $sql = "SELECT COUNT(*) AS total FROM \"{$table}\" WHERE \"{$column}\" IS NULL"; + $result = $this->selectField($sql, 'total'); + if ($result > 0) { + $this->rollbackTransaction(); + return -1; + } + } + + // Otherwise update the table. Note the reverse-sensed $state variable + $sql = "UPDATE pg_attribute SET attnotnull = " . ($state) ? 'false' : 'true' . " + WHERE attrelid = (SELECT oid FROM pg_class WHERE relname = '{$table}') + AND attname = '{$column}'"; + $status = $this->execute($sql); + if ($status != 0) { + $this->rollbackTransaction(); + return -4; + } + + // Otherwise, close the transaction + return $this->endTransaction(); + } + + /** + * Renames a column in a table + * @param $table The table containing the column to be renamed + * @param $column The column to be renamed + * @param $newName The new name for the column + * @return 0 success + */ + function renameColumn($table, $column, $newName) { + $this->clean($table); + $this->clean($column); + $this->clean($newName); + + $sql = "ALTER TABLE \"{$table}\" RENAME COLUMN \"{$column}\" TO \"{$newName}\""; + + // @@ how? + return $this->execute($sql); + } +/* + function &getIndices() + function &getIndex() + function setIndex() + function delIndex() + + function &getSequences() + function &getSequence() + function setSequence() + function delSequence() + + // DML Functions + + function doSelect() + function doDelete() + function doUpdate() +*/ + + // Capabilities + function hasTables() { return true; } + function hasViews() { return true; } + function hasSequences() { return true; } + function hasFunctions() { return true; } + function hasTriggers() { return true; } + function hasOperators() { return true; } + function hasTypes() { return true; } + function hasAggregates() { return true; } + function hasRules() { return true; } + +} + +?> \ No newline at end of file diff --git a/conf/config.inc b/conf/config.inc new file mode 100644 index 00000000..617d1b49 --- /dev/null +++ b/conf/config.inc @@ -0,0 +1,62 @@ + \ No newline at end of file diff --git a/lang/english.php b/lang/english.php new file mode 100755 index 00000000..152d6b22 --- /dev/null +++ b/lang/english.php @@ -0,0 +1,31 @@ + + + + +<?= $appName ?> + + + + + + + + + + +<body> + <?= $strNoFrames ?> +</body> + + \ No newline at end of file diff --git a/lang/template.php b/lang/template.php new file mode 100644 index 00000000..a819a5f8 --- /dev/null +++ b/lang/template.php @@ -0,0 +1,16 @@ + \ No newline at end of file diff --git a/libraries/adodb/adodb-access.inc.php b/libraries/adodb/adodb-access.inc.php new file mode 100755 index 00000000..d8fe7faf --- /dev/null +++ b/libraries/adodb/adodb-access.inc.php @@ -0,0 +1,54 @@ +_connectionID); + $rs = new ADORecordSet_odbc($qid); + //print_r($rs); + $arr = &$rs->GetArray(); + + $arr2 = array(); + for ($i=0; $i < sizeof($arr); $i++) { + if ($arr[$i][2] && substr($arr[$i][2],0,4) != 'MSys') + $arr2[] = $arr[$i][2]; + } + return $arr2; + } +} + + +class ADORecordSet_access extends ADORecordSet_odbc { + + var $databaseType = "access"; + + function ADORecordSet_access($id) + { + return $this->ADORecordSet_odbc($id); + } +} +} // class +?> \ No newline at end of file diff --git a/libraries/adodb/adodb-ado.inc.php b/libraries/adodb/adodb-ado.inc.php new file mode 100755 index 00000000..73ef407f --- /dev/null +++ b/libraries/adodb/adodb-ado.inc.php @@ -0,0 +1,522 @@ +_affectedRows; + } + + function _connect($argHostname, $argUsername, $argPassword, $argProvider= 'MSDASQL') + { + $u = 'UID'; + $p = 'PWD'; + + $dbc = new COM('ADODB.Connection'); + if (! $dbc) return false; + /* // handle SQL server provider specially ? no need + if ($argProvider) { + if ($argProvider == "SQLOLEDB") { // SQL Server Provider + } + }*/ + if ($argProvider) $dbc->Provider = $argProvider; + else $dbc->Provider ='MSDASQL'; + + if ($argUsername) $argHostname .= ";$u=$argUsername"; + if ($argPassword)$argHostname .= ";$p=$argPassword"; + + if ($this->debug) print "

Host=".$argHostname."
version=$dbc->version

"; + // @ added below for php 4.0.1 and earlier + @$dbc->Open((string) $argHostname); + + $this->_connectionID = $dbc; + return $dbc != false; + } + + // returns true or false + function _pconnect($argHostname, $argUsername, $argPassword, $argProvider='MSDASQL') + { + $dbc = new COM("ADODB.Connection"); + if (! $dbc) return false; + + if ($argProvider) $dbc->Provider = $argProvider; + else $dbc->Provider ='MSDASQL'; + + if ($argUsername) $argHostname .= ";UID=$argUsername"; + if ($argPassword)$argHostname .= ";PWD=$argPassword"; + + if ($this->debug) print "

Host=".$argHostname."
version=$dbc->version

"; + $dbc->Open((string) $argHostname); + + $this->_connectionID = $dbc; + return $dbc != false; + } + +/* + adSchemaCatalogs = 1, + adSchemaCharacterSets = 2, + adSchemaCollations = 3, + adSchemaColumns = 4, + adSchemaCheckConstraints = 5, + adSchemaConstraintColumnUsage = 6, + adSchemaConstraintTableUsage = 7, + adSchemaKeyColumnUsage = 8, + adSchemaReferentialContraints = 9, + adSchemaTableConstraints = 10, + adSchemaColumnsDomainUsage = 11, + adSchemaIndexes = 12, + adSchemaColumnPrivileges = 13, + adSchemaTablePrivileges = 14, + adSchemaUsagePrivileges = 15, + adSchemaProcedures = 16, + adSchemaSchemata = 17, + adSchemaSQLLanguages = 18, + adSchemaStatistics = 19, + adSchemaTables = 20, + adSchemaTranslations = 21, + adSchemaProviderTypes = 22, + adSchemaViews = 23, + adSchemaViewColumnUsage = 24, + adSchemaViewTableUsage = 25, + adSchemaProcedureParameters = 26, + adSchemaForeignKeys = 27, + adSchemaPrimaryKeys = 28, + adSchemaProcedureColumns = 29, + adSchemaDBInfoKeywords = 30, + adSchemaDBInfoLiterals = 31, + adSchemaCubes = 32, + adSchemaDimensions = 33, + adSchemaHierarchies = 34, + adSchemaLevels = 35, + adSchemaMeasures = 36, + adSchemaProperties = 37, + adSchemaMembers = 38 + +*/ + + function MetaTables() + { + $arr= array(); + $dbc = $this->_connectionID; + + $adors=@$dbc->OpenSchema(20);//tables + if ($adors){ + $f = $adors->Fields(2);//table/view name + $t = $adors->Fields(3);//table type + while (!$adors->EOF){ + $tt=substr($t->value,0,6); + if ($tt!='SYSTEM' && $tt !='ACCESS') + $arr[]=$f->value; + //print $f->value . ' ' . $t->value.'
'; + $adors->MoveNext(); + } + $adors->Close(); + } + + return $arr; + } + + function MetaColumns($table) + { + $table = strtoupper($table); + $arr= array(); + $dbc = $this->_connectionID; + + $adors=@$dbc->OpenSchema(4);//tables + + if ($adors){ + $t = $adors->Fields(2);//table/view name + while (!$adors->EOF){ + + + if (strtoupper($t->Value) == $table) { + + $fld = new ADOFieldObject(); + $c = $adors->Fields(3); + $fld->name = $c->Value; + $fld->type = 'CHAR'; // cannot discover type in ADO! + $fld->max_length = -1; + $arr[strtoupper($fld->name)]=$fld; + } + + $adors->MoveNext(); + } + $adors->Close(); + } + + return $arr; + } + + /* returns queryID or false */ + function &_query($sql,$inputarr=false) + { + + $dbc = $this->_connectionID; + + // return rs + if ($inputarr) { + $oCmd = new COM('ADODB.Command'); + $oCmd->ActiveConnection = $dbc; + $oCmd->CommandText = $sql; + $oCmd->CommandType = 1; + + foreach($inputarr as $val) { + // name, type, direction 1 = input, len, + $this->adoParameterType = 130; + $p = $oCmd->CreateParameter('name',$this->adoParameterType,1,strlen($val),$val); + //print $p->Type.' '.$p->value; + $oCmd->Parameters->Append($p); + } + $p = false; + $rs = $oCmd->Execute(); + $e = $dbc->Errors; + if ($dbc->Errors->Count > 0) return false; + return $rs; + } + + $rs = @$dbc->Execute($sql,$this->_affectedRows); + + if ($dbc->Errors->Count > 0) return false; + return $rs; + } + + + function BeginTrans() + { + $o = $this->_connectionID->Properties("Transaction DDL"); + if (!$o) return false; + @$this->_connectionID->BeginTrans(); + return true; + } + function CommitTrans() { + @$this->_connectionID->CommitTrans(); + return true; + } + function RollbackTrans() { + @$this->_connectionID->RollbackTrans(); + return true; + } + + /* Returns: the last error message from previous database operation */ + + function ErrorMsg() + { + $errc = $this->_connectionID->Errors; + if ($errc->Count == 0) return ''; + $err = $errc->Item($errc->Count-1); + return $err->Description; + } + + function ErrorNo() + { + $errc = $this->_connectionID->Errors; + if ($errc->Count == 0) return 0; + $err = $errc->Item($errc->Count-1); + return $err->NativeError; + } + + // returns true or false + function _close() + { + if ($this->_connectionID) $this->_connectionID->Close(); + $this->_connectionID = false; + return true; + } + + +} + +/*-------------------------------------------------------------------------------------- + Class Name: Recordset +--------------------------------------------------------------------------------------*/ + +class ADORecordSet_ado extends ADORecordSet { + + var $bind = false; + var $databaseType = "ado"; + var $dataProvider = "ado"; + var $_tarr = false; // caches the types + var $_flds; // and field objects + var $canSeek = true; + var $hideErrors = true; + + function ADORecordSet_ado(&$id) + { + global $ADODB_FETCH_MODE; + + $this->fetchMode = $ADODB_FETCH_MODE; + return $this->ADORecordSet($id); + } + + + // returns the field object + function FetchField($fieldOffset = -1) { + $off=$fieldOffset+1; // offsets begin at 1 + + $o= new ADOFieldObject(); + $rs = $this->_queryID; + $f = $rs->Fields($fieldOffset); + $o->name = $f->Name; + $o->type = $f->Type; + $o->max_length = $f->DefinedSize; + + //print "off=$off name=$o->name type=$o->type len=$o->max_length
"; + return $o; + } + + /* Use associative array to get fields array */ + function Fields($colname) + { + if (!$this->bind) { + $this->bind = array(); + for ($i=0; $i < $this->_numOfFields; $i++) { + $o = $this->FetchField($i); + $this->bind[strtoupper($o->name)] = $i; + } + } + + return $this->fields[$this->bind[strtoupper($colname)]]; + } + + + function _initrs() + { + $rs = $this->_queryID; + $this->_numOfRows = $rs->RecordCount; + + $f = $rs->Fields; + $this->_numOfFields = $f->Count; + } + + + // should only be used to move forward as we normally use forward-only cursors + function _seek($row) + { + $rs = $this->_queryID; + // absoluteposition doesn't work -- my maths is wrong ? + // $rs->AbsolutePosition->$row-2; + // return true; + if ($this->_currentRow > $row) return false; + $rs->Move((integer)$row - $this->_currentRow-1); //adBookmarkFirst + return true; + } + +/* + OLEDB types + + enum DBTYPEENUM + { DBTYPE_EMPTY = 0, + DBTYPE_NULL = 1, + DBTYPE_I2 = 2, + DBTYPE_I4 = 3, + DBTYPE_R4 = 4, + DBTYPE_R8 = 5, + DBTYPE_CY = 6, + DBTYPE_DATE = 7, + DBTYPE_BSTR = 8, + DBTYPE_IDISPATCH = 9, + DBTYPE_ERROR = 10, + DBTYPE_BOOL = 11, + DBTYPE_VARIANT = 12, + DBTYPE_IUNKNOWN = 13, + DBTYPE_DECIMAL = 14, + DBTYPE_UI1 = 17, + DBTYPE_ARRAY = 0x2000, + DBTYPE_BYREF = 0x4000, + DBTYPE_I1 = 16, + DBTYPE_UI2 = 18, + DBTYPE_UI4 = 19, + DBTYPE_I8 = 20, + DBTYPE_UI8 = 21, + DBTYPE_GUID = 72, + DBTYPE_VECTOR = 0x1000, + DBTYPE_RESERVED = 0x8000, + DBTYPE_BYTES = 128, + DBTYPE_STR = 129, + DBTYPE_WSTR = 130, + DBTYPE_NUMERIC = 131, + DBTYPE_UDT = 132, + DBTYPE_DBDATE = 133, + DBTYPE_DBTIME = 134, + DBTYPE_DBTIMESTAMP = 135 + + ADO Types + + adEmpty = 0, + adTinyInt = 16, + adSmallInt = 2, + adInteger = 3, + adBigInt = 20, + adUnsignedTinyInt = 17, + adUnsignedSmallInt = 18, + adUnsignedInt = 19, + adUnsignedBigInt = 21, + adSingle = 4, + adDouble = 5, + adCurrency = 6, + adDecimal = 14, + adNumeric = 131, + adBoolean = 11, + adError = 10, + adUserDefined = 132, + adVariant = 12, + adIDispatch = 9, + adIUnknown = 13, + adGUID = 72, + adDate = 7, + adDBDate = 133, + adDBTime = 134, + adDBTimeStamp = 135, + adBSTR = 8, + adChar = 129, + adVarChar = 200, + adLongVarChar = 201, + adWChar = 130, + adVarWChar = 202, + adLongVarWChar = 203, + adBinary = 128, + adVarBinary = 204, + adLongVarBinary = 205, + adChapter = 136, + adFileTime = 64, + adDBFileTime = 137, + adPropVariant = 138, + adVarNumeric = 139 +*/ + function MetaType($t,$len=-1) + { + switch ($t) { + case 12: // variant + case 8: // bstr + case 129: //char + case 130: //wc + case 200: // varc + case 202:// varWC + case 128: // bin + case 204: // varBin + case 72: // guid + if ($len <= $this->blobSize) return 'C'; + + case 201: + case 203: + return 'X'; + case 128: + case 204: + case 205: + return 'B'; + case 7: + case 133: return 'D'; + + case 134: + case 135: return 'T'; + + case 11: return 'L'; + + case 16:// adTinyInt = 16, + case 2://adSmallInt = 2, + case 3://adInteger = 3, + case 4://adBigInt = 20, + case 17://adUnsignedTinyInt = 17, + case 18://adUnsignedSmallInt = 18, + case 19://adUnsignedInt = 19, + case 20://adUnsignedBigInt = 21, + return 'I'; + default: return 'N'; + } + } + + // time stamp not supported yet + function _fetch() + { + $rs = $this->_queryID; + if ($rs->EOF) return false; + $this->fields = array(); + + if (!$this->_tarr) { + $tarr = array(); + $flds = array(); + for ($i=0,$max = $this->_numOfFields; $i < $max; $i++) { + $f = $rs->Fields($i); + $flds[] = $f; + $tarr[] = $f->Type; + } + // bind types and flds only once + $this->_tarr = $tarr; + $this->_flds = $flds; + } + $t = reset($this->_tarr); + $f = reset($this->_flds); + + if ($this->hideErrors) $olde = error_reporting(E_ERROR|E_CORE_ERROR);// sometimes $f->value be null + for ($i=0,$max = $this->_numOfFields; $i < $max; $i++) { + + switch($t) { + + case 133:// A date value (yyyymmdd) + $val = $f->value; + $this->fields[] = substr($val,0,4).'-'.substr($val,4,2).'-'.substr($val,6,2); + break; + case 7: // adDate + $this->fields[] = date('Y-m-d',(integer)$f->value); + break; + case 1: // null + $this->fields[] = false; + break; + case 6: // currency is not supported properly; + print '
'.$f->Name.': currency type not supported by PHP
'; + $this->fields[] = (float) $f->value; + break; + default: + $this->fields[] = $f->value; + break; + } + //print " $f->value $t, "; + $f = next($this->_flds); + $t = next($this->_tarr); + } // for + if ($this->hideErrors) error_reporting($olde); + @$rs->MoveNext(); // @ needed for some versions of PHP! + + if ($this->fetchMode == ADODB_FETCH_ASSOC) { + $this->fields = $this->GetRowAssoc(false); + } + return true; + } + + + function _close() { + $this->_flds = false; + $this->_queryID = false; + } + +} + +?> \ No newline at end of file diff --git a/libraries/adodb/adodb-ado_access.inc.php b/libraries/adodb/adodb-ado_access.inc.php new file mode 100755 index 00000000..3a087e3b --- /dev/null +++ b/libraries/adodb/adodb-ado_access.inc.php @@ -0,0 +1,38 @@ +ADORecordSet_ado($id); + } +} +?> \ No newline at end of file diff --git a/libraries/adodb/adodb-ado_mssql.inc.php b/libraries/adodb/adodb-ado_mssql.inc.php new file mode 100755 index 00000000..024da4ca --- /dev/null +++ b/libraries/adodb/adodb-ado_mssql.inc.php @@ -0,0 +1,36 @@ +ADORecordSet_ado($id); + } +} +?> \ No newline at end of file diff --git a/libraries/adodb/adodb-cryptsession.php b/libraries/adodb/adodb-cryptsession.php new file mode 100755 index 00000000..e13c2e42 --- /dev/null +++ b/libraries/adodb/adodb-cryptsession.php @@ -0,0 +1,245 @@ + + Set tabs to 4 for best viewing. + + Latest version of ADODB is available at http://php.weblogs.com/adodb + ====================================================================== + + This file provides PHP4 session management using the ADODB database +wrapper library. + + Example + ======= + + GLOBAL $HTTP_SESSION_VARS; + include('adodb.inc.php'); + include('adodb-session.php'); + session_start(); + session_register('AVAR'); + $HTTP_SESSION_VARS['AVAR'] += 1; + print "

\$HTTP_SESSION_VARS['AVAR']={$HTTP_SESSION_VARS['AVAR']}

"; + + + Installation + ============ + 1. Create a new database in MySQL or Access "sessions" like +so: + + create table sessions ( + SESSKEY char(32) not null, + EXPIRY int(11) unsigned not null, + DATA text not null, + primary key (sesskey) + ); + + 2. Then define the following parameters in this file: + $ADODB_SESSION_DRIVER='database driver, eg. mysql or ibase'; + $ADODB_SESSION_CONNECT='server to connect to'; + $ADODB_SESSION_USER ='user'; + $ADODB_SESSION_PWD ='password'; + $ADODB_SESSION_DB ='database'; + $ADODB_SESSION_TBL = 'sessions' + + 3. Recommended is PHP 4.0.2 or later. There are documented +session bugs in + earlier versions of PHP. + +*/ + + +include_once('crypt.inc.php'); + +if (!defined('_ADODB_LAYER')) { + include ('adodb.inc.php'); +} + + + +if (!defined('ADODB_SESSION')) { + + define('ADODB_SESSION',1); + +GLOBAL $ADODB_SESSION_CONNECT, + $ADODB_SESSION_DRIVER, + $ADODB_SESSION_USER, + $ADODB_SESSION_PWD, + $ADODB_SESSION_DB, + $ADODB_SESS_CONN, + $ADODB_SESS_LIFE, + $ADODB_SESS_DEBUG, + $ADODB_SESS_INSERT; + + //$ADODB_SESS_DEBUG = true; + + /* SET THE FOLLOWING PARAMETERS */ +if (empty($ADODB_SESSION_DRIVER)) { + $ADODB_SESSION_DRIVER='mysql'; + $ADODB_SESSION_CONNECT='serverName'; + $ADODB_SESSION_USER ='PhpSessions'; + $ADODB_SESSION_PWD ='sessions'; + $ADODB_SESSION_DB ='sessions'; +} +if (empty($ADODB_SESSION_TBL)){ + $ADODB_SESSION_TBL = 'sessions'; +} + + +function ADODB_Session_Key() +{ +$ADODB_CRYPT_KEY = 'CRYPTED ADODB SESSIONS ROCK!'; + + /* USE THIS FUNCTION TO CREATE THE ENCRYPTION KEY FOR CRYPTED SESSIONS */ + /* Crypt the used key, $ADODB_CRYPT_KEY as key and session_ID as SALT */ + return crypt($ADODB_CRYPT_KEY, session_ID()); +} + +$ADODB_SESS_LIFE = get_cfg_var('session.gc_maxlifetime'); +if ($ADODB_SESS_LIFE <= 1) { + // bug in PHP 4.0.3 pl 1 -- how about other versions? + //print "

Session Error: PHP.INI setting session.gc_maxlifetimenot set: $ADODB_SESS_LIFE

"; + $ADODB_SESS_LIFE=1440; +} + +function adodb_sess_open($save_path, $session_name) +{ +GLOBAL $ADODB_SESSION_CONNECT, + $ADODB_SESSION_DRIVER, + $ADODB_SESSION_USER, + $ADODB_SESSION_PWD, + $ADODB_SESSION_DB, + $ADODB_SESS_CONN, + $ADODB_SESS_DEBUG; + + $ADODB_SESS_INSERT = false; + + if (isset($ADODB_SESS_CONN)) return true; + + $ADODB_SESS_CONN = ADONewConnection($ADODB_SESSION_DRIVER); + if (!empty($ADODB_SESS_DEBUG)) { + $ADODB_SESS_CONN->debug = true; + print" conn=$ADODB_SESSION_CONNECT user=$ADODB_SESSION_USER pwd=$ADODB_SESSION_PWD db=$ADODB_SESSION_DB "; + } + return $ADODB_SESS_CONN->PConnect($ADODB_SESSION_CONNECT, + $ADODB_SESSION_USER,$ADODB_SESSION_PWD,$ADODB_SESSION_DB); + +} + +function adodb_sess_close() +{ +global $ADODB_SESS_CONN; + + if ($ADODB_SESS_CONN) $ADODB_SESS_CONN->Close(); + return true; +} + +function adodb_sess_read($key) +{ +$Crypt = new MD5Crypt; +global $ADODB_SESS_CONN,$ADODB_SESS_INSERT,$ADODB_SESSION_TBL; + $rs = $ADODB_SESS_CONN->Execute("SELECT data FROM $ADODB_SESSION_TBL WHERE sesskey = '$key' AND expiry >= " . time()); + if ($rs) { + if ($rs->EOF) { + $ADODB_SESS_INSERT = true; + $v = ''; + } else { + // Decrypt session data + $v = rawurldecode($Crypt->Decrypt($rs->fields[0], ADODB_Session_Key())); + } + $rs->Close(); + return $v; + } + else $ADODB_SESS_INSERT = true; + + return false; +} + +function adodb_sess_write($key, $val) +{ +$Crypt = new MD5Crypt; + global $ADODB_SESS_INSERT,$ADODB_SESS_CONN, $ADODB_SESS_LIFE, $ADODB_SESSION_TBL; + + $expiry = time() + $ADODB_SESS_LIFE; + + // encrypt session data.. + $val = $Crypt->Encrypt(rawurlencode($val), ADODB_Session_Key()); + $qry = "UPDATE $ADODB_SESSION_TBL SET expiry=$expiry,data='$val' WHERE sesskey='$key'"; + $rs = $ADODB_SESS_CONN->Execute($qry); + if ($rs) $rs->Close(); + else print '

Session Update: '.$ADODB_SESS_CONN->ErrorMsg().'

'; + + if ($ADODB_SESS_INSERT || $rs === false) { + $qry = "INSERT INTO $ADODB_SESSION_TBL(sesskey,expiry,data) VALUES ('$key',$expiry,'$val')"; + $rs = $ADODB_SESS_CONN->Execute($qry); + if ($rs) $rs->Close(); + else print '

Session Insert: '.$ADODB_SESS_CONN->ErrorMsg().'

'; + } + // bug in access driver (could be odbc?) means that info is not commited + // properly unless select statement executed in Win2000 + if ($ADODB_SESS_CONN->databaseType == 'access') $rs = $ADODB_SESS_CONN->Execute("select sesskey from $ADODB_SESSION_TBL WHERE sesskey='$key'"); + + return isset($rs); +} + +function adodb_sess_destroy($key) +{ + global $ADODB_SESS_CONN, $ADODB_SESSION_TBL; + + $qry = "DELETE FROM $ADODB_SESSION_TBL WHERE sesskey = '$key'"; + $rs = $ADODB_SESS_CONN->Execute($qry); + if ($rs) $rs->Close(); + return $rs; +} + +function adodb_sess_gc($maxlifetime) { + global $ADODB_SESS_CONN, $ADODB_SESSION_TBL; + + $qry = "DELETE FROM $ADODB_SESSION_TBL WHERE expiry < " . time(); + $rs = $ADODB_SESS_CONN->Execute($qry); + if ($rs) $rs->Close(); + + // suggested by Cameron, "GaM3R" + if (defined('ADODB_SESSION_OPTIMIZE')) + { + switch( $ADODB_SESSION_DRIVER ) { + case 'mysql': + case 'mysqlt': + $opt_qry = 'OPTIMIZE TABLE '.$ADODB_SESSION_TBL; + break; + case 'postgresql': + case 'postgresql7': + $opt_qry = 'VACUUM '.$ADODB_SESSION_TBL; + break; + } + } + + return true; +} + +session_module_name('user'); +session_set_save_handler( + "adodb_sess_open", + "adodb_sess_close", + "adodb_sess_read", + "adodb_sess_write", + "adodb_sess_destroy", + "adodb_sess_gc"); +} + +/* TEST SCRIPT -- UNCOMMENT */ +/* +if (0) { +GLOBAL $HTTP_SESSION_VARS; + + session_start(); + session_register('AVAR'); + $HTTP_SESSION_VARS['AVAR'] += 1; + print "

\$HTTP_SESSION_VARS['AVAR']={$HTTP_SESSION_VARS['AVAR']}

"; +} +*/ +?> \ No newline at end of file diff --git a/libraries/adodb/adodb-csv.inc.php b/libraries/adodb/adodb-csv.inc.php new file mode 100755 index 00000000..e056df09 --- /dev/null +++ b/libraries/adodb/adodb-csv.inc.php @@ -0,0 +1,147 @@ +_insertid; + } + + function _affectedrows() + { + return $this->_affectedrows; + } + + function &MetaDatabases() + { + return false; + } + + + // returns true or false + function _connect($argHostname, $argUsername, $argPassword, $argDatabasename) + { + $this->_url = $argHostname; + return true; + } + + // returns true or false + function _pconnect($argHostname, $argUsername, $argPassword, $argDatabasename) + { + $this->_url = $argHostname; + return true; + } + + function &MetaColumns($table) + { + return false; + } + + + // parameters use PostgreSQL convention, not MySQL + function &SelectLimit($sql,$nrows=-1,$offset=-1,$arg3=false) + { + $url = $this->_url.'?sql='.urlencode($sql)."&nrows=$nrows&$offset=&offset&arg3=".urlencode($arg3); + $err = false; + $rs = csv2rs($url,$err,false); + if ($this->debug) print "$url
$err
"; + + $at = strpos($err,'::::'); + if ($at === false) { + $this->_errorMsg = $err; + $this->_errorNo = (integer)$err; + } else { + $this->_errorMsg = substr($err,$at+4,1024); + $this->_errorNo = -9999; + } + if (is_object($rs)) { + $rs->databaseType='csv'; + } + return $rs; + } + + // returns queryID or false + function Execute($sql,$inputarr=false,$arg3=false) + { + $url = $this->_url.'?sql='.urlencode($sql); + if ($arg3) $url .= "&arg3=".urlencode($arg3); + $err = false; + + $rs = csv2rs($url,$err,false); + if ($this->debug) print urldecode($url)."
$err
"; + $at = strpos($err,'::::'); + if ($at === false) { + $this->_errorMsg = $err; + $this->_errorNo = (integer)$err; + } else { + $this->_errorMsg = substr($err,$at+4,1024); + $this->_errorNo = -9999; + } + if (is_object($rs)) { + $this->_affectedrows = $rs->affectedrows; + $this->_insertid = $rs->insertid; + $rs->databaseType='csv'; + } + return $rs; + } + + /* Returns: the last error message from previous database operation */ + function ErrorMsg() + { + return $this->_errorMsg; + } + + /* Returns: the last error number from previous database operation */ + function ErrorNo() + { + return $this->_errorNo; + } + + // returns true or false + function _close() + { + return true; + } +} // class + +class ADORecordset_csv extends ADORecordset { + function ADORecordset_csv($id) + { + $this->ADORecordset($id); + } + + function _close() + { + return true; + } +} + +} // define + +?> \ No newline at end of file diff --git a/libraries/adodb/adodb-db2.inc.php b/libraries/adodb/adodb-db2.inc.php new file mode 100755 index 00000000..7bf57026 --- /dev/null +++ b/libraries/adodb/adodb-db2.inc.php @@ -0,0 +1,163 @@ +_connectionID = odbc_connect($argDSN,$argUsername,$argPassword); + $this->_errorMsg = $php_errormsg; + + //if ($this->_connectionID) odbc_autocommit($this->_connectionID,true); + return $this->_connectionID != false; + } + // returns true or false + function _pconnect($argDSN, $argUsername, $argPassword, $argDatabasename) + { + global $php_errormsg; + $php_errormsg = ''; + $this->_connectionID = odbc_pconnect($argDSN,$argUsername,$argPassword); + $this->_errorMsg = $php_errormsg; + + //if ($this->_connectionID) odbc_autocommit($this->_connectionID,true); + return $this->_connectionID != false; + } + + function &SelectLimit($sql,$nrows=-1,$offset=-1,$arg3=false) + { + if ($offset <= 0) { + // could also use " OPTIMIZE FOR $nrows ROWS " + $sql .= " FETCH FIRST $nrows ROWS ONLY "; + return $this->Execute($sql,false,$arg3); + } else + return ADOConnection::SelectLimit($sql,$nrows,$offset,$arg3); + } + +}; + + +class ADORecordSet_db2 extends ADORecordSet_odbc { + + var $databaseType = "db2"; + + function ADORecordSet_db2($id) + { + $this->ADORecordSet_odbc($id); + } + + function MetaType($t,$len=-1,$fieldobj=false) + { + switch (strtoupper($t)) { + case 'VARCHAR': + case 'CHAR': + case 'CHARACTER': + if ($len <= $this->blobSize) return 'C'; + + case 'LONGCHAR': + case 'TEXT': + case 'CLOB': + case 'DBCLOB': // double-byte + return 'X'; + + case 'BLOB': + case 'GRAPHIC': + case 'VARGRAPHIC': + return 'B'; + + case 'DATE': + return 'D'; + + case 'TIME': + case 'TIMESTAMP': + return 'T'; + + //case 'BOOLEAN': + //case 'BIT': + // return 'L'; + + //case 'COUNTER': + // return 'R'; + + case 'INT': + case 'INTEGER': + case 'BIGINT': + case 'SMALLINT': + return 'I'; + + default: return 'N'; + } + } +} + +} //define +?> \ No newline at end of file diff --git a/libraries/adodb/adodb-errorhandler.inc.php b/libraries/adodb/adodb-errorhandler.inc.php new file mode 100755 index 00000000..71e7449c --- /dev/null +++ b/libraries/adodb/adodb-errorhandler.inc.php @@ -0,0 +1,73 @@ +$s

"; + trigger_error($s,E_USER_ERROR); +} +?> diff --git a/libraries/adodb/adodb-errorpear.inc.php b/libraries/adodb/adodb-errorpear.inc.php new file mode 100755 index 00000000..19b049f2 --- /dev/null +++ b/libraries/adodb/adodb-errorpear.inc.php @@ -0,0 +1,86 @@ +!$s

"; +} + +/** +* Returns last PEAR_Error object. This error might be for an error that +* occured several sql statements ago. +*/ +function &ADODB_PEAR_Error() +{ +global $ADODB_Last_PEAR_Error; + + return $ADODB_Last_PEAR_Error; +} + +?> \ No newline at end of file diff --git a/libraries/adodb/adodb-fbsql.inc.php b/libraries/adodb/adodb-fbsql.inc.php new file mode 100755 index 00000000..a1f894f4 --- /dev/null +++ b/libraries/adodb/adodb-fbsql.inc.php @@ -0,0 +1,251 @@ +. + Set tabs to 8. +*/ + +if (! defined("_ADODB_FBSQL_LAYER")) { + define("_ADODB_FBSQL_LAYER", 1 ); + +class ADODB_fbsql extends ADOConnection { + var $databaseType = 'fbsql'; + var $hasInsertID = true; + var $hasAffectedRows = true; + var $metaTablesSQL = "SHOW TABLES"; + var $metaColumnsSQL = "SHOW COLUMNS FROM %s"; + var $fmtTimeStamp = "'Y-m-d H:i:s'"; + var $hasLimit = false; + + function ADODB_fbsql() + { + } + + function _insertid() + { + return fbsql_insert_id($this->_connectionID); + } + + function _affectedrows() + { + return fbsql_affected_rows($this->_connectionID); + } + + function &MetaDatabases() + { + $qid = fbsql_list_dbs($this->_connectionID); + $arr = array(); + $i = 0; + $max = fbsql_num_rows($qid); + while ($i < $max) { + $arr[] = fbsql_tablename($qid,$i); + $i += 1; + } + return $arr; + } + + // returns concatenated string + function Concat() + { + $s = ""; + $arr = func_get_args(); + $first = true; + + foreach($arr as $a) { + if ($first) { + $s = $a; + $first = false; + } else $s .= ','.$a; + } + if (sizeof($s) > 0) return "CONCAT($s)"; + else return ''; + } + + // returns true or false + function _connect($argHostname, $argUsername, $argPassword, $argDatabasename) + { + $this->_connectionID = fbsql_connect($argHostname,$argUsername,$argPassword); + if ($this->_connectionID === false) return false; + if ($argDatabasename) return $this->SelectDB($argDatabasename); + return true; + } + + // returns true or false + function _pconnect($argHostname, $argUsername, $argPassword, $argDatabasename) + { + $this->_connectionID = fbsql_pconnect($argHostname,$argUsername,$argPassword); + if ($this->_connectionID === false) return false; + if ($argDatabasename) return $this->SelectDB($argDatabasename); + return true; + } + + function &MetaColumns($table) + { + if ($this->metaColumnsSQL) { + + $rs = $this->Execute(sprintf($this->metaColumnsSQL,$table)); + + if ($rs === false) return false; + + $retarr = array(); + while (!$rs->EOF){ + $fld = new ADOFieldObject(); + $fld->name = $rs->fields[0]; + $fld->type = $rs->fields[1]; + + // split type into type(length): + if (preg_match("/^(.+)\((\d+)\)$/", $fld->type, $query_array)) { + $fld->type = $query_array[1]; + $fld->max_length = $query_array[2]; + } else { + $fld->max_length = -1; + } + $fld->not_null = ($rs->fields[2] != 'YES'); + $fld->primary_key = ($rs->fields[3] == 'PRI'); + $fld->auto_increment = (strpos($rs->fields[5], 'auto_increment') !== false); + $fld->binary = (strpos($fld->type,'blob') !== false); + + $retarr[strtoupper($fld->name)] = $fld; + $rs->MoveNext(); + } + $rs->Close(); + return $retarr; + } + return false; + } + + // returns true or false + function SelectDB($dbName) + { + $this->databaseName = $dbName; + if ($this->_connectionID) { + return @fbsql_select_db($dbName,$this->_connectionID); + } + else return false; + } + + + // returns queryID or false + function _query($sql,$inputarr) + { + return fbsql_query("$sql;",$this->_connectionID); + } + + /* Returns: the last error message from previous database operation */ + function ErrorMsg() + { + $this->_errorMsg = @fbsql_error($this->_connectionID); + return $this->_errorMsg; + } + + /* Returns: the last error number from previous database operation */ + function ErrorNo() + { + return @fbsql_errno($this->_connectionID); + } + + // returns true or false + function _close() + { + return @fbsql_close($this->_connectionID); + } + +} + +/*-------------------------------------------------------------------------------------- + Class Name: Recordset +--------------------------------------------------------------------------------------*/ + +class ADORecordSet_fbsql extends ADORecordSet{ + + var $databaseType = "fbsql"; + var $canSeek = true; + + function ADORecordSet_fbsql($queryID) { + return $this->ADORecordSet($queryID); + } + + function _initrs() + { + GLOBAL $ADODB_COUNTRECS; + $this->_numOfRows = ($ADODB_COUNTRECS) ? @fbsql_num_rows($this->_queryID):-1; + $this->_numOfFields = @fbsql_num_fields($this->_queryID); + } + + + + function &FetchField($fieldOffset = -1) { + if ($fieldOffset != -1) { + $o = @fbsql_fetch_field($this->_queryID, $fieldOffset); + //$o->max_length = -1; // fbsql returns the max length less spaces -- so it is unrealiable + $f = @fbsql_field_flags($this->_queryID,$fieldOffset); + $o->binary = (strpos($f,'binary')!== false); + } + else if ($fieldOffset == -1) { /* The $fieldOffset argument is not provided thus its -1 */ + $o = @fbsql_fetch_field($this->_queryID);// fbsql returns the max length less spaces -- so it is unrealiable + //$o->max_length = -1; + } + + return $o; + } + + function _seek($row) + { + return @fbsql_data_seek($this->_queryID,$row); + } + + function _fetch($ignore_fields=false) + { + $this->fields = @fbsql_fetch_array($this->_queryID); + return ($this->fields == true); + } + + function _close() { + return @fbsql_free_result($this->_queryID); + } + + function MetaType($t,$len=-1,$fieldobj=false) + { + $len = -1; // fbsql max_length is not accurate + switch (strtoupper($t)) { + case 'CHARACTER': + case 'CHARACTER VARYING': + case 'BLOB': + case 'CLOB': + case 'BIT': + case 'BIT VARYING': + if ($len <= $this->blobSize) return 'C'; + + // so we have to check whether binary... + case 'IMAGE': + case 'LONGBLOB': + case 'BLOB': + case 'MEDIUMBLOB': + return !empty($fieldobj->binary) ? 'B' : 'X'; + + case 'DATE': return 'D'; + + case 'TIME': + case 'TIME WITH TIME ZONE': + case 'TIMESTAMP': + case 'TIMESTAMP WITH TIME ZONE': return 'T'; + + case 'PRIMARY_KEY': + return 'R'; + case 'INTEGER': + case 'SMALLINT': + case 'BOOLEAN': + + if (!empty($fieldobj->primary_key)) return 'R'; + else return 'I'; + + default: return 'N'; + } + } + +} //class +} // defined +?> \ No newline at end of file diff --git a/libraries/adodb/adodb-ibase.inc.php b/libraries/adodb/adodb-ibase.inc.php new file mode 100755 index 00000000..2e5e7e7f --- /dev/null +++ b/libraries/adodb/adodb-ibase.inc.php @@ -0,0 +1,349 @@ + + changed transaction handling and added experimental blob stuff + + Docs to interbase at the website + http://www.synectics.co.za/php3/tutorial/IB_PHP3_API.html + + To use gen_id(), see + http://www.volny.cz/iprenosil/interbase/ip_ib_code.htm#_code_creategen + + $rs = $conn->Execute('select gen_id(adodb,1) from rdb$database'); + $id = $rs->fields[0]; + $conn->Execute("insert into table (id, col1,...) values ($id, $val1,...)"); +*/ + +class ADODB_ibase extends ADOConnection { + var $databaseType = "ibase"; + var $replaceQuote = "\'"; // string to use to replace quotes + var $fmtDate = "'Y-m-d'"; + var $fmtTimeStamp = "'Y-m-d, H:i:s'"; + var $concat_operator='||'; + var $_transactionID; + var $metaTablesSQL = "select rdb\$relation_name from rdb\$relations where rdb\$relation_name not like 'RDB\$%'"; + var $metaColumnsSQL = "select a.rdb\$field_name,b.rdb\$field_type,b.rdb\$field_length from rdb\$relation_fields a join rdb\$fields b on a.rdb\$field_source=b.rdb\$field_name where rdb\$relation_name ='%s'"; + var $ibasetrans = IBASE_DEFAULT; + var $hasGenID = true; + var $_bindInputArray = true; + + // note the generator starts from 0, unlike others which start from 1 + var $_genIDSQL = "SELECT Gen_ID(%s,1) FROM RDB\$DATABASE"; + var $_genSeqSQL = "INSERT INTO RDB\$GENERATORS (RDB\$GENERATOR_NAME) VALUES (UPPER('%s'))"; + + function ADODB_ibase() + { + ibase_timefmt('%Y-%m-%d'); + } + + function BeginTrans() + { + //return false; // currently not working properly + + $this->autoCommit = false; + $this->_transactionID = $this->_connectionID;//ibase_trans($this->ibasetrans, $this->_connectionID); + return $this->_transactionID; + } + + function CommitTrans() + { + $ret = false; + $this->autoCommit = true; + if ($this->_transactionID) { + //print ' commit '; + $ret = ibase_commit($this->_transactionID); + } + $this->_transactionID = false; + return $ret; + } + + function RollbackTrans() + { + $ret = false; + $this->autoCommit = true; + if ($this->_transactionID) + $ret = ibase_rollback($this->_transactionID); + $this->_transactionID = false; + + return $ret; + } + + function SelectDB($dbName) { + return false; + } + + function _handleerror() + { + $this->_errorMsg = ibase_errmsg($this->_connectionID); + } + + function ErrorNo() { + if (preg_match('/error code = ([\-0-9]*)/i', $this->_errorMsg,$arr)) return (integer) $arr[1]; + else return 0; + } + + function ErrorMsg() { + return $this->_errorMsg; + } + + // returns true or false + function _connect($argHostname, $argUsername, $argPassword, $argDatabasename) + { + if ($this->charSet) + $this->_connectionID = ibase_connect($argHostname,$argUsername,$argPassword,$this->charSet); + else + $this->_connectionID = ibase_connect($argHostname,$argUsername,$argPassword); + + if ($this->_connectionID === false) { + $this->_handleerror(); + return false; + } + + return true; + } + // returns true or false + function _pconnect($argHostname, $argUsername, $argPassword, $argDatabasename) + { + if ($this->charSet) + $this->_connectionID = ibase_pconnect($argHostname,$argUsername,$argPassword,$this->charSet); + else + $this->_connectionID = ibase_pconnect($argHostname,$argUsername,$argPassword); + + if ($this->_connectionID === false) { + $this->_handleerror(); + return false; + } + + return true; + } + + // returns query ID if successful, otherwise false + function _query($sql,$iarr=false) + { + if (!$this->autoCommit && $this->_transactionID) { + if (is_array($iarr)) { + switch(sizeof($iarr)) { + case 1: $ret = ibase_query($this->_transactionID,$sql,$iarr[0]); break; + case 2: $ret = ibase_query($this->_transactionID,$sql,$iarr[0],$iarr[1]); break; + case 3: $ret = ibase_query($this->_transactionID,$sql,$iarr[0],$iarr[1],$iarr[2]); break; + case 4: $ret = ibase_query($this->_transactionID,$sql,$iarr[0],$iarr[1],$iarr[2],$iarr[3]); break; + default: print "

Too many parameters to ibase query $sql

"; + case 5: $ret = ibase_query($this->_transactionID,$sql,$iarr[0],$iarr[1],$iarr[2],$iarr[3],$iarr[4]); break; + } + } else $ret = ibase_query($this->_transactionID,$sql); + } else { + if (is_array($iarr)) { + switch(sizeof($iarr)) { + case 1: $ret = ibase_query($this->_connectionID,$sql,$iarr[0]); break; + case 2: $ret = ibase_query($this->_connectionID,$sql,$iarr[0],$iarr[1]); break; + case 3: $ret = ibase_query($this->_connectionID,$sql,$iarr[0],$iarr[1],$iarr[2]); break; + case 4: $ret = ibase_query($this->_connectionID,$sql,$iarr[0],$iarr[1],$iarr[2],$iarr[3]); break; + default: print "

Too many parameters to ibase query $sql

"; + case 5: $ret = ibase_query($this->_connectionID,$sql,$iarr[0],$iarr[1],$iarr[2],$iarr[3],$iarr[4]); break; + + } + } else $ret = ibase_query($this->_connectionID,$sql); + if ($ret === true) ibase_commit($this->_connectionID); + } + $this->_handleerror(); + return $ret; + } + + // returns true or false + function _close() + { + if ($this->autoCommit) ibase_commit($this->_connectionID); + else ibase_rollback($this->_connectionID); + + return @ibase_close($this->_connectionID); + } + + // returns array of ADOFieldObjects for current table + function &MetaColumns($table) + { + + if ($this->metaColumnsSQL) { + + $rs = $this->Execute(sprintf($this->metaColumnsSQL,strtoupper($table))); + if ($rs === false) return false; + + $retarr = array(); + while (!$rs->EOF) { //print_r($rs->fields); + $fld = new ADOFieldObject(); + $fld->name = $rs->fields[0]; + $tt = $rs->fields[1]; + switch($tt) + { + case 7: + case 8: + case 9:$tt = 'INTEGER'; break; + case 10: + case 27: + case 11:$tt = 'FLOAT'; break; + default: + case 40: + case 14:$tt = 'CHAR'; break; + case 35:$tt = 'DATE'; break; + case 37:$tt = 'VARCHAR'; break; + case 261:$tt = 'BLOB'; break; + case 13: + case 35:$tt = 'TIMESTAMP'; break; + } + $fld->type = $tt; + $fld->max_length = $rs->fields[2]; + $retarr[strtoupper($fld->name)] = $fld; + + $rs->MoveNext(); + } + $rs->Close(); + return $retarr; + } + return false; + } + + // warning - this code is experimental and might not be available in the future + function &BlobEncode( $blob ) + { + $blobid = ibase_blob_create( $this->_connectionID); + ibase_blob_add( $blobid, $blob ); + return ibase_blob_close( $blobid ); + } + + // warning - this code is experimental and might not be available in the future + function &BlobDecode( $blob ) + { + $blobid = ibase_blob_open( $blob ); + $realblob = ibase_blob_get( $blobid,299999); // 2nd param is max size of blob -- Kevin Boillet + ibase_blob_close( $blobid ); + + return( $realblob ); + } + + /* + Insert a null into the blob field of the table first. + Then use UpdateBlob to store the blob. + + Usage: + + $conn->Execute('INSERT INTO blobtable (id, blobcol) VALUES (1, null)'); + $conn->UpdateBlob('blobtable','blobcol',$blob,'id=1'); + */ + function UpdateBlob($table,$column,$val,$where,$blobtype='BLOB') + { + $blob_id = ibase_blob_create($this->_connectionID); + ibase_blob_add($blob_id, $val); + $blob_id_str = ibase_blob_close($blob_id); + return $this->Execute("UPDATE $table SET $column=(?) WHERE $where",array($blob_id_str)) != false; + } +} + +/*-------------------------------------------------------------------------------------- + Class Name: Recordset +--------------------------------------------------------------------------------------*/ + +class ADORecordset_ibase extends ADORecordSet +{ + + var $databaseType = "ibase"; + var $bind=false; + + function ADORecordset_ibase($id) + { + return $this->ADORecordSet($id); + } + + /* Returns: an object containing field information. + Get column information in the Recordset object. fetchField() can be used in order to obtain information about + fields in a certain query result. If the field offset isn't specified, the next field that wasn't yet retrieved by + fetchField() is retrieved. */ + + function &FetchField($fieldOffset = -1) + { + $fld = new ADOFieldObject; + $ibf = ibase_field_info($this->_queryID,$fieldOffset); + $fld->name = $ibf['name']; + if (empty($fld->name)) $fld->name = $ibf['alias']; + $fld->type = $ibf['type']; + $fld->max_length = $ibf['length']; + if ($this->debug) print_r($fld); + return $fld; + } + + function _initrs() + { + $this->_numOfRows = -1; + $this->_numOfFields = @ibase_num_fields($this->_queryID); + } + + function _seek($row) + { + return false; + } + + function _fetch() { + + $f = ibase_fetch_row($this->_queryID); + if ($f === false) return false; + $this->fields = $f; + return true; + } + + /* Use associative array to get fields array */ + function Fields($colname) + { + if (!$this->bind) { + $this->bind = array(); + for ($i=0; $i < $this->_numOfFields; $i++) { + $o = $this->FetchField($i); + $this->bind[strtoupper($o->name)] = $i; + } + } + + return $this->fields[$this->bind[strtoupper($colname)]]; + + } + + + function _close() + { + return @ibase_free_result($this->_queryID); + } + + function MetaType($t,$len=-1) + { + switch (strtoupper($t)) { + case 'CHAR': + return 'C'; + + + case 'TEXT': + case 'VARCHAR': + case 'VARYING': + if ($len <= $this->blobSize) return 'C'; + return 'X'; + case 'BLOB': + return 'B'; + + case 'TIMESTAMP': + case 'DATE': return 'D'; + + //case 'T': return 'T'; + + //case 'L': return 'L'; + case 'INT': + case 'SHORT': + case 'INTEGER': return 'I'; + default: return 'N'; + } + } +} +?> diff --git a/libraries/adodb/adodb-mssql.inc.php b/libraries/adodb/adodb-mssql.inc.php new file mode 100755 index 00000000..b1c3a631 --- /dev/null +++ b/libraries/adodb/adodb-mssql.inc.php @@ -0,0 +1,279 @@ +Execute('select @@identity'); + if ($rs == false || $rs->EOF) return false; + $id = $rs->fields[0]; + $rs->Close(); + return $id; + } + // might require begintrans -- committrans + function _affectedrows() + { + $rs = $this->Execute('select @@rowcount'); + if ($rs == false || $rs->EOF) return false; + $id = $rs->fields[0]; + $rs->Close(); + return $id; + } + function BeginTrans() + { + $this->_hastrans = true; + $this->Execute('BEGIN TRAN'); + return true; + } + // Reserved for future expansion + function CommitTrans() + { + $this->_hastrans = false; + $this->Execute('COMMIT TRAN'); + return true; + } + // Reserved for future expansion + function RollbackTrans() + { + $this->_hastrans = false; + $this->Execute('ROLLBACK TRAN'); + return true; + } + + + //From: Fernando Moreira + function MetaDatabases() + { + if(@mssql_select_db("master")) { + $qry="select name from sysdatabases where name <> 'master'"; + if($rs=@mssql_query($qry)){ + $tmpAr=$ar=array(); + while($tmpAr=@mssql_fetch_row($rs)) + $ar[]=$tmpAr[0]; + @mssql_select_db($this->databaseName); + if(sizeof($ar)) + return($ar); + else + return(false); + } else { + @mssql_select_db($this->databaseName); + return(false); + } + } + return(false); + } + + function SelectDB($dbName) + { + $this->databaseName = $dbName; + if ($this->_connectionID) { + return @mssql_select_db($dbName); + } + else return false; + } + /* Returns: the last error message from previous database operation + Note: This function is NOT available for Microsoft SQL Server. */ + function ErrorMsg() + { + $this->_errorMsg = mssql_get_last_message(); + return $this->_errorMsg; + } + + function ErrorNo() + { + $id = @mssql_query("select @@ERROR",$this->_connectionID); + if (!$id) return false; + $arr = mssql_fetch_array($id); + @mssql_free_result($id); + if (is_array($arr)) return $arr[0]; + else return -1; + } + + // returns true or false + function _connect($argHostname, $argUsername, $argPassword, $argDatabasename) + { + $this->_connectionID = mssql_connect($argHostname,$argUsername,$argPassword); + if ($this->_connectionID === false) return false; + //$this->Execute('SET DATEFORMAT ymd'); + if ($argDatabasename) return $this->SelectDB($argDatabasename); + return true; + } + + + // returns true or false + function _pconnect($argHostname, $argUsername, $argPassword, $argDatabasename) + { + $this->_connectionID = mssql_pconnect($argHostname,$argUsername,$argPassword); + if ($this->_connectionID === false) return false; + //$this->Execute('SET DATEFORMAT ymd'); + if ($argDatabasename) return $this->SelectDB($argDatabasename); + return true; + } + + // returns query ID if successful, otherwise false + function _query($sql,$inputarr) + { + $val= mssql_query($sql,$this->_connectionID); + return $val; + } + + // returns true or false + function _close() + { + if ($this->_hastrans) $this->RollbackTrans(); + return @mssql_close($this->_connectionID); + } + + +} + +/*-------------------------------------------------------------------------------------- + Class Name: Recordset +--------------------------------------------------------------------------------------*/ +global $ADODB_mssql_mths; +$ADODB_mssql_mths = array('JAN'=>1,'FEB'=>2,'MAR'=>3,'APR'=>4,'MAY'=>5,'JUN'=>6,'JUL'=>7,'AUG'=>8,'SEP'=>9,'OCT'=>10,'NOV'=>11,'DEC'=>12); + +class ADORecordset_mssql extends ADORecordSet { + + var $databaseType = "mssql"; + var $canSeek = true; + // _mths works only in non-localised system + + function ADORecordset_mssql($id) + { + return $this->ADORecordSet($id); + } + + + /* Use associative array to get fields array */ + function Fields($colname) + { + if (!$this->bind) { + $this->bind = array(); + for ($i=0; $i < $this->_numOfFields; $i++) { + $o = $this->FetchField($i); + $this->bind[strtoupper($o->name)] = $i; + } + } + + return $this->fields[$this->bind[strtoupper($colname)]]; + } + + /* Returns: an object containing field information. + Get column information in the Recordset object. fetchField() can be used in order to obtain information about + fields in a certain query result. If the field offset isn't specified, the next field that wasn't yet retrieved by + fetchField() is retrieved. */ + + function FetchField($fieldOffset = -1) + { + if ($fieldOffset != -1) { + return @mssql_fetch_field($this->_queryID, $fieldOffset); + } + else if ($fieldOffset == -1) { /* The $fieldOffset argument is not provided thus its -1 */ + return @mssql_fetch_field($this->_queryID); + } + return null; + } + + function _initrs() + { + GLOBAL $ADODB_COUNTRECS; + $this->_numOfRows = ($ADODB_COUNTRECS)? @mssql_num_rows($this->_queryID):-1; + $this->_numOfFields = @mssql_num_fields($this->_queryID); + } + + function _seek($row) + { + return @mssql_data_seek($this->_queryID, $row); + } + + // INSERT UPDATE DELETE returns false even if no error occurs in 4.0.4 + // also the date format has been changed from YYYY-mm-dd to dd MMM YYYY in 4.0.4. Idiot! + function _fetch($ignore_fields=false) + { + $this->fields = @mssql_fetch_array($this->_queryID); + //print_r($this->fields); + return (!empty($this->fields)); + } + + /* close() only needs to be called if you are worried about using too much memory while your script + is running. All associated result memory for the specified result identifier will automatically be freed. */ + + function _close() { + return @mssql_free_result($this->_queryID); + } + + // mssql uses a default date like Dec 30 2000 12:00AM + function UnixDate($v) + { + global $ADODB_mssql_mths; + + //Dec 30 2000 12:00AM + if (!ereg( "([A-Za-z]{3})[-/\. ]+([0-9]{1,2})[-/\. ]+([0-9]{4}))" + ,$v, $rr)) return parent::UnixDate($v); + + if ($rr[3] <= 1970) return 0; + + $themth = substr(strtoupper($rr[1]),0,3); + $themth = $ADODB_mssql_mths[$themth]; + if ($themth <= 0) return false; + // h-m-s-MM-DD-YY + return mktime(0,0,0,$themth,$rr[2],$rr[3]); + } + + function UnixTimeStamp($v) + { + global $ADODB_mssql_mths; + + //Dec 30 2000 12:00AM + if (!ereg( "([A-Za-z]{3})[-/\. ]+([0-9]{1,2})[-/\. ]+([0-9]{4}) +([0-9]{1,2}):([0-9]{1,2}) *([apAP]{0,1})" + ,$v, $rr)) return parent::UnixTimeStamp($v); + if ($rr[3] <= 1970) return 0; + + $themth = substr(strtoupper($rr[1]),0,3); + $themth = $ADODB_mssql_mths[$themth]; + if ($themth <= 0) return false; + + if (strtoupper($rr[6]) == 'P') { + if ($rr[4]<12) $rr[4] += 12; + } else { + if ($rr[4]==12) $rr[4] = 0; + } + // h-m-s-MM-DD-YY + return mktime($rr[4],$rr[5],0,$themth,$rr[2],$rr[3]); + } +} +?> \ No newline at end of file diff --git a/libraries/adodb/adodb-mysql.inc.php b/libraries/adodb/adodb-mysql.inc.php new file mode 100755 index 00000000..c3c2e7d6 --- /dev/null +++ b/libraries/adodb/adodb-mysql.inc.php @@ -0,0 +1,352 @@ +_connectionID); + } + + function _affectedrows() + { + return mysql_affected_rows($this->_connectionID); + } + + // See http://www.mysql.com/doc/M/i/Miscellaneous_functions.html + // Reference on Last_Insert_ID on the recommended way to simulate sequences + var $_genIDSQL = "update %s set id=LAST_INSERT_ID(id+1);"; + var $_genSeqSQL = "create table %s (id int not null)"; + var $_genSeq2SQL = "insert into %s values (0)"; + + function GenID($seqname='adodbseq') + { + if (!$this->hasGenID) return false; + + $getnext = sprintf($this->_genIDSQL,$seqname); + $rs = @$this->Execute($getnext); + if (!$rs) { + $u = strtoupper($seqname); + $this->Execute(sprintf($this->_genSeqSQL,$seqname)); + $this->Execute(sprintf($this->_genSeq2SQL,$seqname)); + $rs = $this->Execute($getnext); + } + $this->genID = mysql_insert_id($this->_connectionID); + + if ($rs) $rs->Close(); + + return $this->genID; + } + + function &MetaDatabases() + { + $qid = mysql_list_dbs($this->_connectionID); + $arr = array(); + $i = 0; + $max = mysql_num_rows($qid); + while ($i < $max) { + $arr[] = mysql_tablename($qid,$i); + $i += 1; + } + return $arr; + } + + // returns concatenated string + function Concat() + { + $s = ""; + $arr = func_get_args(); + $first = true; + /* + foreach($arr as $a) { + if ($first) { + $s = $a; + $first = false; + } else $s .= ','.$a; + }*/ + + // suggestion by andrew005@mnogo.ru + $s = implode(',',$arr); + if (strlen($s) > 0) return "CONCAT($s)"; + else return ''; + } + + // returns true or false + function _connect($argHostname, $argUsername, $argPassword, $argDatabasename) + { + $this->_connectionID = mysql_connect($argHostname,$argUsername,$argPassword); + if ($this->_connectionID === false) return false; + if ($argDatabasename) return $this->SelectDB($argDatabasename); + return true; + } + + // returns true or false + function _pconnect($argHostname, $argUsername, $argPassword, $argDatabasename) + { + $this->_connectionID = mysql_pconnect($argHostname,$argUsername,$argPassword); + if ($this->_connectionID === false) return false; + if ($argDatabasename) return $this->SelectDB($argDatabasename); + return true; + } + + function &MetaColumns($table) + { + if ($this->metaColumnsSQL) { + + $rs = $this->Execute(sprintf($this->metaColumnsSQL,$table)); + + if ($rs === false) return false; + + $retarr = array(); + while (!$rs->EOF){ + $fld = new ADOFieldObject(); + $fld->name = $rs->fields[0]; + $fld->type = $rs->fields[1]; + + // split type into type(length): + if (preg_match("/^(.+)\((\d+)\)$/", $fld->type, $query_array)) { + $fld->type = $query_array[1]; + $fld->max_length = $query_array[2]; + } else { + $fld->max_length = -1; + } + $fld->not_null = ($rs->fields[2] != 'YES'); + $fld->primary_key = ($rs->fields[3] == 'PRI'); + $fld->auto_increment = (strpos($rs->fields[5], 'auto_increment') !== false); + $fld->binary = (strpos($fld->type,'blob') !== false); + + $retarr[strtoupper($fld->name)] = $fld; + $rs->MoveNext(); + } + $rs->Close(); + return $retarr; + } + return false; + } + + // returns true or false + function SelectDB($dbName) + { + $this->databaseName = $dbName; + if ($this->_connectionID) { + return @mysql_select_db($dbName,$this->_connectionID); + } + else return false; + } + + // parameters use PostgreSQL convention, not MySQL + function &SelectLimit($sql,$nrows=-1,$offset=-1,$inputarr=false, $arg3=false,$secs=0) + { + $offsetStr =($offset>=0) ? "$offset," : ''; + + return ($secs) ? $this->CacheExecute($secs,$sql." LIMIT $offsetStr$nrows",$inputarr,$arg3) + : $this->Execute($sql." LIMIT $offsetStr$nrows",$inputarr,$arg3); + + } + + // returns queryID or false + function _query($sql,$inputarr) + { + global $ADODB_COUNTRECS; + if($ADODB_COUNTRECS) return mysql_query($sql,$this->_connectionID); + else return mysql_unbuffered_query($sql,$this->_connectionID); // requires PHP >= 4.0.6 + } + + /* Returns: the last error message from previous database operation */ + function ErrorMsg() + { + if (!empty($this->_connectionID)) $this->_errorMsg = @mysql_error(); + else $this->_errorMsg = @mysql_error($this->_connectionID); + return $this->_errorMsg; + } + + /* Returns: the last error number from previous database operation */ + function ErrorNo() + { + if (empty($this->_connectionID)) return @mysql_errno(); + else return @mysql_errno($this->_connectionID); + } + + // returns true or false + function _close() + { + @mysql_close($this->_connectionID); + $this->_connectionID = false; + } + +} + +/*-------------------------------------------------------------------------------------- + Class Name: Recordset +--------------------------------------------------------------------------------------*/ + +class ADORecordSet_mysql extends ADORecordSet{ + + var $databaseType = "mysql"; + var $canSeek = true; + + function ADORecordSet_mysql($queryID) { + GLOBAL $ADODB_FETCH_MODE; + + switch ($ADODB_FETCH_MODE) + { + case ADODB_FETCH_NUM: $this->fetchMode = MYSQL_NUM; break; + case ADODB_FETCH_ASSOC:$this->fetchMode = MYSQL_ASSOC; break; + default: + case ADODB_FETCH_DEFAULT: + case ADODB_FETCH_BOTH:$this->fetchMode = MYSQL_BOTH; break; + } + + $this->ADORecordSet($queryID); + } + + function _initrs() + { + GLOBAL $ADODB_COUNTRECS; + $this->_numOfRows = ($ADODB_COUNTRECS) ? @mysql_num_rows($this->_queryID):-1; + $this->_numOfFields = @mysql_num_fields($this->_queryID); + } + + function &FetchField($fieldOffset = -1) { + if ($fieldOffset != -1) { + $o = @mysql_fetch_field($this->_queryID, $fieldOffset); + $f = @mysql_field_flags($this->_queryID,$fieldOffset); + $o->max_length = @mysql_field_len($this->_queryID,$fieldOffset); // suggested by: Jim Nicholson (jnich@att.com) + //$o->max_length = -1; // mysql returns the max length less spaces -- so it is unrealiable + $o->binary = (strpos($f,'binary')!== false); + } + else if ($fieldOffset == -1) { /* The $fieldOffset argument is not provided thus its -1 */ + $o = @mysql_fetch_field($this->_queryID); + $o->max_length = @mysql_field_len($this->_queryID); // suggested by: Jim Nicholson (jnich@att.com) + //$o->max_length = -1; // mysql returns the max length less spaces -- so it is unrealiable + } + + return $o; + } + + function &GetRowAssoc($upper=true) + { + if ($this->fetchMode == MYSQL_ASSOC && !$upper) return $rs->fields; + return ADORecordSet::GetRowAssoc($upper); + } + + + function _seek($row) + { + return @mysql_data_seek($this->_queryID,$row); + } + + /*function &FetchRow() + { + if ($this->EOF) return false; + + $arr = $this->fields; + $this->_currentRow++; + // using & below slows things down by 20%! + $this->fields = @mysql_fetch_array($this->_queryID,$this->fetchMode); + + if (is_array($this->fields)) return $arr; + $this->EOF = true; + return false; + }*/ + + // 10% speedup to move MoveNext to child class + function MoveNext() + { + if (!$this->EOF) { + $this->_currentRow++; + // using & below slows things down by 20%! + $this->fields = @mysql_fetch_array($this->_queryID,$this->fetchMode); + + if (is_array($this->fields)) return true; + $this->EOF = true; + } + return false; + } + + function _fetch() + { + $this->fields = @mysql_fetch_array($this->_queryID,$this->fetchMode); + return (is_array($this->fields)); + } + + function _close() { + @mysql_free_result($this->_queryID); + $this->_queryID = false; + } + + function MetaType($t,$len=-1,$fieldobj=false) + { + $len = -1; // mysql max_length is not accurate + switch (strtoupper($t)) { + case 'STRING': + case 'CHAR': + case 'VARCHAR': + case 'TINYBLOB': + case 'TINYTEXT': + case 'ENUM': + case 'SET': + if ($len <= $this->blobSize) return 'C'; + + case 'TEXT': + case 'LONGTEXT': + case 'MEDIUMTEXT': + return 'X'; + + // php_mysql extension always returns 'blob' even if 'text' + // so we have to check whether binary... + case 'IMAGE': + case 'LONGBLOB': + case 'BLOB': + case 'MEDIUMBLOB': + return !empty($fieldobj->binary) ? 'B' : 'X'; + + case 'DATE': return 'D'; + + case 'DATETIME': + case 'TIMESTAMP': return 'T'; + + case 'INT': + case 'INTEGER': + case 'BIGINT': + case 'TINYINT': + case 'MEDIUMINT': + case 'SMALLINT': + + if (!empty($fieldobj->primary_key)) return 'R'; + else return 'I'; + + default: return 'N'; + } + } + +} +} +?> \ No newline at end of file diff --git a/libraries/adodb/adodb-mysqlt.inc.php b/libraries/adodb/adodb-mysqlt.inc.php new file mode 100755 index 00000000..3b1a72bd --- /dev/null +++ b/libraries/adodb/adodb-mysqlt.inc.php @@ -0,0 +1,53 @@ + + + Requires mysql client. Works on Windows and Unix. +*/ + + +include_once(ADODB_DIR."/adodb-mysql.inc.php"); + + +class ADODB_mysqlt extends ADODB_mysql { + var $databaseType = 'mysqlt'; + + function BeginTrans() + { + $this->Execute('SET AUTOCOMMIT=0'); + $this->Execute('BEGIN'); + return true; + } + + function CommitTrans() + { + $this->Execute('COMMIT'); + $this->Execute('SET AUTOCOMMIT=1'); + return true; + } + + function RollbackTrans() + { + $this->Execute('ROLLBACK'); + $this->Execute('SET AUTOCOMMIT=1'); + return true; + } + +} + +class ADORecordSet_mysqlt extends ADORecordSet_mysql{ + var $databaseType = "mysqlt"; + + function ADORecordSet_mysqlt($queryID) { + return $this->ADORecordSet_mysql($queryID); + } +} +?> \ No newline at end of file diff --git a/libraries/adodb/adodb-oci8.inc.php b/libraries/adodb/adodb-oci8.inc.php new file mode 100755 index 00000000..98f62ef3 --- /dev/null +++ b/libraries/adodb/adodb-oci8.inc.php @@ -0,0 +1,503 @@ + + + 13 Nov 2000 jlim - removed all ora_* references. +*/ + +/* +NLS_Date_Format +Allows you to use a date format other than the Oracle Lite default. When a literal +character string appears where a date value is expected, the Oracle Lite database +tests the string to see if it matches the formats of Oracle, SQL-92, or the value +specified for this parameter in the POLITE.INI file. Setting this parameter also +defines the default format used in the TO_CHAR or TO_DATE functions when no +other format string is supplied. +For Oracle the default is dd-mon-yy or dd-mon-yyyy, and for SQL-92 the default is +yy-mm-dd or yyyy-mm-dd. +Using 'RR' in the format forces two-digit years less than or equal to 49 to be +interpreted as years in the 21st century (2000–2049), and years over 50 as years in +the 20th century (1950–1999). Setting the RR format as the default for all two-digit +year entries allows you to become year-2000 compliant. For example: +NLS_DATE_FORMAT='RR-MM-DD' +You can also modify the date format using the ALTER SESSION command. See the +Oracle Lite SQL Reference for more information. +*/ +class ADODB_oci8 extends ADOConnection { + var $databaseType = 'oci8'; + var $dataProvider = 'oci8'; + var $replaceQuote = "''"; // string to use to replace quotes + var $concat_operator='||'; + var $_stmt; + var $_commit = OCI_COMMIT_ON_SUCCESS; + var $_initdate = true; // init date to YYYY-MM-DD + var $metaTablesSQL = "select table_name from cat where table_type in ('TABLE','VIEW')"; + var $metaColumnsSQL = "select cname,coltype,width from col where tname='%s' order by colno"; + var $_bindInputArray = true; + var $hasGenID = true; + var $_genIDSQL = "SELECT %s.nextval FROM DUAL"; + var $_genSeqSQL = "CREATE SEQUENCE %s"; + var $hasAffectedRows = true; + + function ADODB_oci8() { + } + + function Affected_Rows() + { + return OCIRowCount($this->_stmt); + } + + // format and return date string in database date format + function DBDate($d) + { + return 'TO_DATE('.date($this->fmtDate,$d).",'YYYY-MM-DD')"; + } + + // format and return date string in database timestamp format + function DBTimeStamp($ts) + { + return 'TO_DATE('.date($this->fmtTimeStamp,$ts).",'RRRR-MM-DD, HH:MI:SS AM')"; + } + + function BeginTrans() + { + $this->autoCommit = false; + $this->_commit = OCI_DEFAULT; + return true; + } + + function CommitTrans() + { + $ret = OCIcommit($this->_connectionID); + $this->_commit = OCI_COMMIT_ON_SUCCESS; + $this->autoCommit = true; + return $ret; + } + + function RollbackTrans() + { + $ret = OCIrollback($this->_connectionID); + $this->_commit = OCI_COMMIT_ON_SUCCESS; + $this->autoCommit = true; + return $ret; + } + + function SelectDB($dbName) + { + return false; + } + + /* there seems to be a bug in the oracle extension -- always returns ORA-00000 - no error */ + function ErrorMsg() + { + $arr = @OCIerror($this->_stmt); + if ($arr === false) { + $arr = @OCIerror($this->_connectionID); + if ($arr === false) $arr = @OCIError(); + if ($arr === false) return ''; + } + $this->_errorMsg = $arr['message']; + return $this->_errorMsg; + } + + function ErrorNo() + { + if (is_resource($this->_stmt)) + $arr = @ocierror($this->_stmt); + else { + $arr = @ocierror($this->_connectionID); + if ($arr === false) $arr = @ocierror(); + if ($arr == false) return ''; + } + return $arr['code']; + } +/* +NATSOFT.DOMAIN = + (DESCRIPTION = + (ADDRESS_LIST = + (ADDRESS = (PROTOCOL = TCP)(HOST = kermit)(PORT = 1523)) + ) + (CONNECT_DATA = + (SERVICE_NAME = natsoft.domain) + ) + ) +*/ + + // returns true or false + function _connect($argHostname, $argUsername, $argPassword, $argDatabasename) + { + + if($argHostname) { // added by Jorma Tuomainen + if(strpos($argHostname,":")) { + $argHostinfo=explode(":",$argHostname); + $argHostname=$argHostinfo[0]; + $argHostport=$argHostinfo[1]; + } else { + $argHostport="1521"; + } + + $argDatabasename="(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=".$argHostname + .")(PORT=$argHostport))(CONNECT_DATA=(SERVICE_NAME=$argDatabasename)))"; + } + + //if ($argHostname) print "

Connect: 1st argument should be left blank for $this->databaseType

"; + $this->_connectionID = OCIlogon($argUsername,$argPassword, $argDatabasename); + if ($this->_connectionID === false) return false; + if ($this->_initdate) { + $this->Execute("ALTER SESSION SET NLS_DATE_FORMAT='YYYY-MM-DD'"); + } + return true; + } + // returns true or false + function _pconnect($argHostname, $argUsername, $argPassword, $argDatabasename) + { + if($argHostname) { // added by Jorma Tuomainen + if(strpos($argHostname,":")) { + $argHostinfo=explode(":",$argHostname); + $argHostname=$argHostinfo[0]; + $argHostport=$argHostinfo[1]; + } else { + $argHostport="1521"; + } + + $argDatabasename="(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=".$argHostname + .")(PORT=".$argHostport."))(CONNECT_DATA=(SERVICE_NAME=".$argDatabasename.")))"; + } + $this->_connectionID = OCIplogon($argUsername,$argPassword, $argDatabasename); + if ($this->_connectionID === false) return false; + if ($this->_initdate) { + $this->Execute("ALTER SESSION SET NLS_DATE_FORMAT='YYYY-MM-DD'"); + } + return true; + } + + + /** + * Usage: + * Store BLOBs and CLOBs + * + * Example: to store $var in a blob + * + * $conn->Execute('insert into TABLE (id,ablobb) values(12,empty_blob())'); + * $conn->UpdateBlob('TABLE', 'COLUMN', $var, 'ID=1', 'BLOB'); + * + * $blobtype supports 'BLOB' and 'CLOB' + * + * to get length of LOB: + * select DBMS_LOB.GETLENGTH(ablob) from TABLE + */ + + function UpdateBlob($table,$column,$val,$where,$blobtype='BLOB') + { + switch(strtoupper($blobtype)) { + default: print "UpdateBlob: Unknown blobtype=$blobtype
"; return false; + case 'BLOB': $type = OCI_B_BLOB; break; + case 'CLOB': $type = OCI_B_CLOB; break; + } + + $sql = "UPDATE $table set $column=EMPTY_{$blobtype}() WHERE $where RETURNING $column INTO :blob"; + + $desc = OCINewDescriptor($this->_connectionID, OCI_D_LOB); + $arr['blob'] = array($desc,-1,$type); + + $this->BeginTrans(); + $rs = $this->Execute($sql,$arr); + $rez = !empty($rs); + $desc->save($val); + $desc->free(); + $this->CommitTrans(); + + if ($rez) $rs->Close(); + return $rez; + } + + /* + Example of usage: + + $stmt = $this->Prepare('insert into emp (empno, ename) values (:empno, :ename)'); + */ + function &Prepare($sql) + { + return $sql; + /* + ## doesn't work reliably, so emulate + if ($this->debug) { + print "
($this->databaseType): ".htmlspecialchars($sql)."
"; + } + return OCIParse($this->_connectionID,$sql); + */ + } + + // returns query ID if successful, otherwise false + function _query($sql,$inputarr) + { + //if (!is_resource($sql)) + $stmt=OCIParse($this->_connectionID,$sql); + //else $stmt = $sql; + + $this->_stmt = $stmt; + if (!$stmt) return false; + if (is_array($inputarr)) { + foreach($inputarr as $k => $v) { + if (is_array($v)) { + OCIBindByName($stmt,":$k",$inputarr[$k][0],$v[1],$v[2]); + //print_r($v); + } else { + $len = -1; + if ($inputarr[$k] === ' ') $len = 1; + OCIBindByName($stmt,":$k",$inputarr[$k],$len); + //print " :$k $len "; + } + } + } + if (OCIExecute($stmt,$this->_commit)) { + /* Now this could be an Update/Insert or Delete */ + if (strtoupper(substr($sql,0,6)) !== 'SELECT') return true; + return $stmt; + } + return false; + } + + // returns true or false + function _close() + { + if (!$this->autoCommit) OCIRollback($this->_connectionID); + OCILogoff($this->_connectionID); + $this->_stmt = false; + $this->_connectionID = false; + } + + function MetaPrimaryKeys($table) + { + // tested with oracle 8.1.7 + $table = strtoupper($table); + $sql = "SELECT /*+ RULE */ distinct b.column_name + FROM ALL_CONSTRAINTS a + , ALL_CONS_COLUMNS b + WHERE ( UPPER(b.table_name) = ('$table')) + AND (UPPER(a.table_name) = ('$table') and a.constraint_type = 'P') + AND (a.constraint_name = b.constraint_name)"; + $rs = $this->Execute($sql); + if ($rs && !$rs->EOF) { + $arr = $rs->GetArray(); + $a = array(); + foreach($arr as $v) { + $a[] = $v[0]; + } + return $a; + } + else return false; + } + +} + +/*-------------------------------------------------------------------------------------- + Class Name: Recordset +--------------------------------------------------------------------------------------*/ + +class ADORecordset_oci8 extends ADORecordSet { + + var $databaseType = 'oci8'; + var $bind=false; + var $_fieldobjs; + + function ADORecordset_oci8($queryID) + { + global $ADODB_FETCH_MODE; + + switch ($ADODB_FETCH_MODE) + { + default: + case ADODB_FETCH_NUM: $this->fetchMode = OCI_NUM+OCI_RETURN_NULLS+OCI_RETURN_LOBS; break; + case ADODB_FETCH_ASSOC:$this->fetchMode = OCI_ASSOC+OCI_RETURN_NULLS+OCI_RETURN_LOBS; break; + case ADODB_FETCH_DEFAULT: + case ADODB_FETCH_BOTH:$this->fetchMode = OCI_NUM+OCI_ASSOC+OCI_RETURN_NULLS+OCI_RETURN_LOBS; break; + } + + + $this->_inited = true; + $this->_queryID = $queryID; + $this->fields = array(); + if ($queryID) { + $this->_currentRow = 0; + $this->EOF = !$this->_fetch(); + @$this->_initrs(); + } else { + $this->_numOfRows = 0; + $this->_numOfFields = 0; + $this->EOF = true; + } + } + + + + /* Returns: an object containing field information. + Get column information in the Recordset object. fetchField() can be used in order to obtain information about + fields in a certain query result. If the field offset isn't specified, the next field that wasn't yet retrieved by + fetchField() is retrieved. */ + + function &_FetchField($fieldOffset = -1) + { + $fld = new ADOFieldObject; + $fieldOffset += 1; + $fld->name =OCIcolumnname($this->_queryID, $fieldOffset); + $fld->type = OCIcolumntype($this->_queryID, $fieldOffset); + $fld->max_length = OCIcolumnsize($this->_queryID, $fieldOffset); + if ($fld->type == 'NUMBER') { + //$p = OCIColumnPrecision($this->_queryID, $fieldOffset); + $sc = OCIColumnScale($this->_queryID, $fieldOffset); + if ($sc == 0) $fld->type = 'INT'; + } + return $fld; + } + + /* For some reason, OCIcolumnname fails when called after _initrs() so we cache it */ + function &FetchField($fieldOffset = -1) + { + return $this->_fieldobjs[$fieldOffset]; + } + + /** + * return recordset as a 2-dimensional array. + * + * @param [nRows] is the number of rows to return. -1 means every row. + * + * @return an array indexed by the rows (0-based) from the recordset + */ + function &GetArray($nRows = -1) + { + $results = array(); + /*if ($nRows == -1) { + // doesnt work becoz it creates 2D array by column instead of row + $n = OCIFetchStatement($this->_queryID,$results); + print_r($results); + $this->EOF = true; + $this->_currentRow = $n; + return $results; + }*/ + + $results = array(); + $cnt = 0; + while (!$this->EOF && $nRows != $cnt) { + $results[$cnt++] = $this->fields; + $this->MoveNext(); + } + + return $results; + } + + // 10% speedup to move MoveNext to child class + function MoveNext() + { + if (!$this->EOF) { + $this->_currentRow++; + if(@OCIfetchinto($this->_queryID,$this->fields,$this->fetchMode)) + return true; + + $this->EOF = true; + } + return false; + } + + /* Optimize SelectLimit() by using OCIFetch() instead of OCIFetchInto() */ + function &GetArrayLimit($nrows,$offset=-1) + { + if ($offset <= 0) return $this->GetArray($nrows); + for ($i=1; $i < $offset; $i++) + if (!@OCIFetch($this->_queryID)) return array(); + + if (!@OCIfetchinto($this->_queryID,$this->fields,$this->fetchMode)) return array(); + $results = array(); + $cnt = 0; + while (!$this->EOF && $nrows != $cnt) { + $results[$cnt++] = $this->fields; + $this->MoveNext(); + } + + return $results; + } + + /* Use associative array to get fields array */ + function Fields($colname) + { + if (!$this->bind) { + $this->bind = array(); + for ($i=0; $i < $this->_numOfFields; $i++) { + $o = $this->FetchField($i); + $this->bind[strtoupper($o->name)] = $i; + } + } + + return $this->fields[$this->bind[strtoupper($colname)]]; + + } + + function _initrs() + { + $this->_numOfRows = -1; + $this->_numOfFields = OCInumcols($this->_queryID); + if ($this->_numOfFields>0) { + $this->_fieldobjs = array(); + $max = $this->_numOfFields; + for ($i=0;$i<$max; $i++) $this->_fieldobjs[] = $this->_FetchField($i); + } + //print_r($this->_fieldobjs); + } + + + function _seek($row) + { + return false; + } + + function _fetch() { + return @OCIfetchinto($this->_queryID,$this->fields,$this->fetchMode); + } + + /* close() only needs to be called if you are worried about using too much memory while your script + is running. All associated result memory for the specified result identifier will automatically be freed. */ + + function _close() { + OCIFreeStatement($this->_queryID); + $this->_queryID = false; + } + + function MetaType($t,$len=-1) + { + switch (strtoupper($t)) { + case 'VARCHAR': + case 'VARCHAR2': + case 'CHAR': + case 'VARBINARY': + case 'BINARY': + if ($len <= $this->blobSize) return 'C'; + case 'LONG': + case 'LONG VARCHAR': + case 'CLOB'; + return 'X'; + + case 'LONG RAW': + case 'LONG VARBINARY': + case 'BLOB': + return 'B'; + + case 'DATE': return 'D'; + + //case 'T': return 'T'; + + case 'BIT': return 'L'; + case 'INT': + case 'SMALLINT': + case 'INTEGER': return 'I'; + default: return 'N'; + } + } +} +?> diff --git a/libraries/adodb/adodb-odbc.inc.php b/libraries/adodb/adodb-odbc.inc.php new file mode 100755 index 00000000..dda609da --- /dev/null +++ b/libraries/adodb/adodb-odbc.inc.php @@ -0,0 +1,369 @@ +_haserrorfunctions = (strnatcmp(PHP_VERSION,'4.0.5')>=0); + } + + function ErrorMsg() + { + if ($this->_haserrorfunctions) { + // print_r($this->_connectionID); + if (empty($this->_connectionID)) return @odbc_errormsg(); + return @odbc_errormsg($this->_connectionID); + } else return ADOConnection::ErrorMsg(); + } + function ErrorNo() + { + if ($this->_haserrorfunctions) { + if (empty($this->_connectionID)) $e = @odbc_error(); + else $e = @odbc_error($this->_connectionID); + + // bug in 4.0.6, error number can be corrupted string (should be 6 digits) + // so we check and patch + if (strlen($e)<=2) return 0; + return $e; + } else return ADOConnection::ErrorNo(); + } + + + // returns true or false + function _connect($argDSN, $argUsername, $argPassword, $argDatabasename) + { + global $php_errormsg; + + $php_errormsg = ''; + $this->_connectionID = odbc_connect($argDSN,$argUsername,$argPassword,$this->curmode); + $this->_errorMsg = $php_errormsg; + + //if ($this->_connectionID) odbc_autocommit($this->_connectionID,true); + return $this->_connectionID != false; + } + + // returns true or false + function _pconnect($argDSN, $argUsername, $argPassword, $argDatabasename) + { + global $php_errormsg; + $php_errormsg = ''; + $this->_connectionID = odbc_pconnect($argDSN,$argUsername,$argPassword,$this->curmode); + $this->_errorMsg = $php_errormsg; + + //if ($this->_connectionID) odbc_autocommit($this->_connectionID,true); + return $this->_connectionID != false; + } + + function BeginTrans() + { + return odbc_autocommit($this->_connectionID,false); + } + + function CommitTrans() + { + $ret = odbc_commit($this->_connectionID); + odbc_autocommit($this->_connectionID,true); + return $ret; + } + + function RollbackTrans() + { + $ret = odbc_rollback($this->_connectionID); + odbc_autocommit($this->_connectionID,true); + return $ret; + } + + function &MetaTables() + { + $qid = odbc_tables($this->_connectionID); + $rs = new ADORecordSet_odbc($qid); + //print_r($rs); + $arr = &$rs->GetArray(); + $rs->Close(); + $arr2 = array(); + for ($i=0; $i < sizeof($arr); $i++) { + if ($arr[$i][2]) $arr2[] = $arr[$i][2]; + } + return $arr2; + } + +/* +/ SQL data type codes / +#define SQL_UNKNOWN_TYPE 0 +#define SQL_CHAR 1 +#define SQL_NUMERIC 2 +#define SQL_DECIMAL 3 +#define SQL_INTEGER 4 +#define SQL_SMALLINT 5 +#define SQL_FLOAT 6 +#define SQL_REAL 7 +#define SQL_DOUBLE 8 +#if (ODBCVER >= 0x0300) +#define SQL_DATETIME 9 +#endif +#define SQL_VARCHAR 12 + +/ One-parameter shortcuts for date/time data types / +#if (ODBCVER >= 0x0300) +#define SQL_TYPE_DATE 91 +#define SQL_TYPE_TIME 92 +#define SQL_TYPE_TIMESTAMP 93 +*/ + function ODBCTypes($t) + { + switch ((integer)$t) { + case 1: + case 12: + case 0: + return 'C'; + case -1: //text + return 'X'; + case -4: //image + return 'B'; + + case 91: + case 11: + return 'D'; + + case 92: + case 93: + case 9: return 'T'; + case 4: + case 5: + case -6: + return 'I'; + + case -11: // uniqidentifier + return 'R'; + case -7: //bit + return 'L'; + + default: + return 'N'; + } + } + + function &MetaColumns($table) + { + $table = strtoupper($table); + + /* // for some reason, cannot view only 1 table with odbc_columns -- bug? + $qid = odbc_tables($this->_connectionID); + $rs = new ADORecordSet_odbc($qid); + if (!$rs) return false; + while (!$rs->EOF) { + if ($table == strtoupper($rs->fields[2])) { + $q = $rs->fields[0]; + $o = $rs->fields[1]; + break; + } + $rs->MoveNext(); + } + $rs->Close(); + + $qid = odbc_columns($this->_connectionID,$q,$o,strtoupper($table),'%'); + */ + $qid = odbc_columns($this->_connectionID); + $rs = new ADORecordSet_odbc($qid); + if (!$rs) return false; + + $retarr = array(); + while (!$rs->EOF) { + if (strtoupper($rs->fields[2]) == $table) { + $fld = new ADOFieldObject(); + $fld->name = $rs->fields[3]; + $fld->type = $this->ODBCTypes($rs->fields[4]); + $fld->max_length = $rs->fields[7]; + $retarr[strtoupper($fld->name)] = $fld; + } else if (sizeof($retarr)>0) + break; + $rs->MoveNext(); + } + $rs->Close(); //-- crashes 4.03pl1 -- why? + + return $retarr; + } + + function &Prepare($sql) + { + if ($this->debug) { + print "
($this->databaseType): ".htmlspecialchars($sql)."
"; + } + return odbc_prepare($this->_connectionID,$sql); + } + + /* returns queryID or false */ + function _query($sql,$inputarr=false) + { + GLOBAL $php_errormsg; + $php_errormsg = ''; + $this->_error = ''; + + if ($inputarr) { + if (is_resource($sql)) $stmtid = $sql; + else $stmtid = odbc_prepare($this->_connectionID,$sql); + if ($stmtid == false) { + $this->_errorMsg = $php_errormsg; + return false; + } + //print_r($inputarr); + if (! odbc_execute($stmtid,$inputarr)) { + @odbc_free_result($stmtid); + return false; + } + + } else + $stmtid = odbc_exec($this->_connectionID,$sql); + + if ($stmtid) { + odbc_binmode($stmtid,$this->binmode); + odbc_longreadlen($stmtid,$this->maxblobsize); + } + $this->_errorMsg = $php_errormsg; + return $stmtid; + } + + /* + Insert a null into the blob field of the table first. + Then use UpdateBlob to store the blob. + + Usage: + + $conn->Execute('INSERT INTO blobtable (id, blobcol) VALUES (1, null)'); + $conn->UpdateBlob('blobtable','blobcol',$blob,'id=1'); + */ + function UpdateBlob($table,$column,$val,$where,$blobtype='BLOB') + { + return $this->Execute("UPDATE $table SET $column=? WHERE $where",array($val)) != false; + } + + // returns true or false + function _close() + { + return @odbc_close($this->_connectionID); + } + + function _affectedrows() + { + return odbc_num_rows($this->_queryID); + } + +} + +/*-------------------------------------------------------------------------------------- + Class Name: Recordset +--------------------------------------------------------------------------------------*/ + +class ADORecordSet_odbc extends ADORecordSet { + + var $bind = false; + var $databaseType = "odbc"; + var $dataProvider = "odbc"; + + function ADORecordSet_odbc($id) + { + global $ADODB_FETCH_MODE; + + $this->fetchMode = $ADODB_FETCH_MODE; + return $this->ADORecordSet($id); + } + + + // returns the field object + function &FetchField($fieldOffset = -1) { + + $off=$fieldOffset+1; // offsets begin at 1 + + $o= new ADOFieldObject(); + $o->name = @odbc_field_name($this->_queryID,$off); + $o->type = @odbc_field_type($this->_queryID,$off); + $o->max_length = @odbc_field_len($this->_queryID,$off); + + return $o; + } + + /* Use associative array to get fields array */ + function Fields($colname) + { + if (!$this->bind) { + $this->bind = array(); + for ($i=0; $i < $this->_numOfFields; $i++) { + $o = $this->FetchField($i); + $this->bind[strtoupper($o->name)] = $i; + } + } + + return $this->fields[$this->bind[strtoupper($colname)]]; + + } + + + function _initrs() + { + $this->_numOfRows = @odbc_num_rows($this->_queryID); + $this->_numOfFields = @odbc_num_fields($this->_queryID); + } + + function _seek($row) + { + return false; + } + + function MoveNext() + { + if ($this->_numOfRows != 0 && !$this->EOF) { + $this->_currentRow++; + $row = 0; + if (odbc_fetch_into($this->_queryID,$row,$this->fields)) return true; + } + $this->EOF = true; + return false; + } + + function _fetch() + { + $row = 0; + $rez = odbc_fetch_into($this->_queryID,$row,$this->fields); + if ($rez && $this->fetchMode == ADODB_FETCH_ASSOC) { + $this->fields = $this->GetRowAssoc(false); + } + return $rez; + } + + function _close() { + + return @odbc_free_result($this->_queryID); + } + + + +} + +?> \ No newline at end of file diff --git a/libraries/adodb/adodb-odbc_mssql.inc.php b/libraries/adodb/adodb-odbc_mssql.inc.php new file mode 100755 index 00000000..80452814 --- /dev/null +++ b/libraries/adodb/adodb-odbc_mssql.inc.php @@ -0,0 +1,38 @@ +ADORecordSet_odbc($id); + } +} +?> \ No newline at end of file diff --git a/libraries/adodb/adodb-odbc_oracle.inc.php b/libraries/adodb/adodb-odbc_oracle.inc.php new file mode 100755 index 00000000..5d8301f3 --- /dev/null +++ b/libraries/adodb/adodb-odbc_oracle.inc.php @@ -0,0 +1,104 @@ +metaTablesSQL) { + $rs = $this->Execute($this->metaTablesSQL); + if ($rs === false) return false; + $arr = $rs->GetArray(); + $arr2 = array(); + for ($i=0; $i < sizeof($arr); $i++) { + $arr2[] = $arr[$i][0]; + } + $rs->Close(); + return $arr2; + } + return false; + } + + function &MetaColumns($table) + { + if (!empty($this->metaColumnsSQL)) { + + $rs = $this->Execute(sprintf($this->metaColumnsSQL,strtoupper($table))); + if ($rs === false) return false; + + $retarr = array(); + while (!$rs->EOF) { //print_r($rs->fields); + $fld = new ADOFieldObject(); + $fld->name = $rs->fields[0]; + $fld->type = $rs->fields[1]; + $fld->max_length = $rs->fields[2]; + $retarr[strtoupper($fld->name)] = $fld; + + $rs->MoveNext(); + } + $rs->Close(); + return $retarr; + } + return false; + } + + // returns true or false + function _connect($argDSN, $argUsername, $argPassword, $argDatabasename) + { + global $php_errormsg; + + $php_errormsg = ''; + $this->_connectionID = odbc_connect($argDSN,$argUsername,$argPassword,SQL_CUR_USE_ODBC ); + $this->_errorMsg = $php_errormsg; + + $this->Execute("ALTER SESSION SET NLS_DATE_FORMAT='YYYY-MM-DD HH24:MI:SS'"); + //if ($this->_connectionID) odbc_autocommit($this->_connectionID,true); + return $this->_connectionID != false; + } + // returns true or false + function _pconnect($argDSN, $argUsername, $argPassword, $argDatabasename) + { + global $php_errormsg; + $php_errormsg = ''; + $this->_connectionID = odbc_pconnect($argDSN,$argUsername,$argPassword,SQL_CUR_USE_ODBC ); + $this->_errorMsg = $php_errormsg; + + $this->Execute("ALTER SESSION SET NLS_DATE_FORMAT='YYYY-MM-DD HH24:MI:SS'"); + //if ($this->_connectionID) odbc_autocommit($this->_connectionID,true); + return $this->_connectionID != false; + } +} + +class ADORecordSet_odbc_oracle extends ADORecordSet_odbc { + + var $databaseType = 'odbc_oracle'; + + function ADORecordSet_odbc_oracle($id) + { + return $this->ADORecordSet_odbc($id); + } +} +?> \ No newline at end of file diff --git a/libraries/adodb/adodb-oracle.inc.php b/libraries/adodb/adodb-oracle.inc.php new file mode 100755 index 00000000..5dce538e --- /dev/null +++ b/libraries/adodb/adodb-oracle.inc.php @@ -0,0 +1,243 @@ +fmtDate,$d).",'YYYY-MM-DD')"; + } + + // format and return date string in database timestamp format + function DBTimeStamp($ts) + { + return 'TO_DATE('.date($this->fmtTimeStamp,$ts).",'YYYY-MM-DD, HH:RR:SSAM')"; + } + + function BeginTrans() + { + $this->autoCommit = false; + ora_commitoff($this->_connectionID); + return true; + } + + function CommitTrans() + { + $ret = ora_commit($this->_connectionID); + ora_commiton($this->_connectionID); + return $ret; + } + + function RollbackTrans() + { + $ret = ora_rollback($this->_connectionID); + ora_commiton($this->_connectionID); + return $ret; + } + + function SelectDB($dbName) { + return false; + } + + /* there seems to be a bug in the oracle extension -- always returns ORA-00000 - no error */ + function ErrorMsg() { + $this->_errorMsg = @ora_error($this->_curs); + if (!$this->_errorMsg) $this->_errorMsg = @ora_error($this->_connectionID); + return $this->_errorMsg; + } + + function ErrorNo() { + $err = @ora_errorcode($this->_curs); + if (!$err) return @ora_errorcode($this->_connectionID); + } + + + // returns true or false + function _connect($argHostname, $argUsername, $argPassword, $argDatabasename) + { + if ($argHostname) putenv("ORACLE_HOME=$argHostname"); + //if ($argHostname) print "

Connect: 1st argument should be left blank for $this->databaseType

"; + $this->_connectionID = ora_logon($argUsername,$argPassword); + if ($this->_connectionID === false) return false; + if ($this->autoCommit) ora_commiton($this->_connectionID); + if ($this->_initdate) $this->Execute("ALTER SESSION SET NLS_DATE_FORMAT='YYYY-MM-DD'"); + + return true; + } + // returns true or false + function _pconnect($argHostname, $argUsername, $argPassword, $argDatabasename) + { + if ($argHostname) putenv("ORACLE_HOME=$argHostname"); + //if ($argHostname) print "

PConnect: 1st argument should be left blank for $this->databaseType

"; + $this->_connectionID = ora_plogon($argUsername,$argPassword); + if ($this->_connectionID === false) return false; + if ($this->autoCommit) ora_commiton($this->_connectionID); + if ($this->_initdate) $this->Execute("ALTER SESSION SET NLS_DATE_FORMAT='YYYY-MM-DD'"); + + return true; + } + + // returns query ID if successful, otherwise false + function _query($sql,$inputarr) + { + $curs = ora_open($this->_connectionID); + + if ($curs === false) return false; + $this->_curs = $curs; + if (!ora_parse($curs,$sql)) return false; + if (ora_exec($curs)) return $curs; + + @ora_close($curs); + return false; + } + + // returns true or false + function _close() + { + if (!$this->autoCommit) ora_rollback($this->_connectionID); + return @ora_close($this->_connectionID); + } + + +} + +/*-------------------------------------------------------------------------------------- + Class Name: Recordset +--------------------------------------------------------------------------------------*/ + +class ADORecordset_oracle extends ADORecordSet { + + var $databaseType = "oracle"; + var $bind = false; + + function ADORecordset_oracle($queryID) + { + $this->_queryID = $queryID; + + $this->_inited = true; + $this->fields = array(); + if ($queryID) { + $this->_currentRow = 0; + $this->EOF = !$this->_fetch(); + @$this->_initrs(); + } else { + $this->_numOfRows = 0; + $this->_numOfFields = 0; + $this->EOF = true; + } + + return $this->_queryID; + } + + + + /* Returns: an object containing field information. + Get column information in the Recordset object. fetchField() can be used in order to obtain information about + fields in a certain query result. If the field offset isn't specified, the next field that wasn't yet retrieved by + fetchField() is retrieved. */ + + function FetchField($fieldOffset = -1) + { + $fld = new ADOFieldObject; + $fld->name = ora_columnname($this->_queryID, $fieldOffset); + $fld->type = ora_columntype($this->_queryID, $fieldOffset); + $fld->max_length = ora_columnsize($this->_queryID, $fieldOffset); + return $fld; + } + + /* Use associative array to get fields array */ + function Fields($colname) + { + if (!$this->bind) { + $this->bind = array(); + for ($i=0; $i < $this->_numOfFields; $i++) { + $o = $this->FetchField($i); + $this->bind[strtoupper($o->name)] = $i; + } + } + + return $this->fields[$this->bind[strtoupper($colname)]]; + + } + + function _initrs() + { + $this->_numOfRows = -1; + $this->_numOfFields = @ora_numcols($this->_queryID); + } + + + function _seek($row) + { + return false; + } + + function _fetch($ignore_fields=false) { + // should remove call by reference, but ora_fetch_into requires it in 4.0.3pl1 + return @ora_fetch_into($this->_queryID,&$this->fields,ORA_FETCHINTO_NULLS); + } + + /* close() only needs to be called if you are worried about using too much memory while your script + is running. All associated result memory for the specified result identifier will automatically be freed. */ + + function _close() { + return @ora_close($this->_queryID); + } + + function MetaType($t,$len=-1) + { + switch (strtoupper($t)) { + case 'VARCHAR': + case 'VARCHAR2': + case 'CHAR': + case 'VARBINARY': + case 'BINARY': + if ($len <= $this->blobSize) return 'C'; + case 'LONG': + case 'LONG VARCHAR': + case 'CLOB': + return 'X'; + case 'LONG RAW': + case 'LONG VARBINARY': + case 'BLOB': + return 'B'; + + case 'DATE': return 'D'; + + //case 'T': return 'T'; + + case 'BIT': return 'L'; + case 'INT': + case 'SMALLINT': + case 'INTEGER': return 'I'; + default: return 'N'; + } + } +} +?> \ No newline at end of file diff --git a/libraries/adodb/adodb-pear.inc.php b/libraries/adodb/adodb-pear.inc.php new file mode 100755 index 00000000..6d8385d9 --- /dev/null +++ b/libraries/adodb/adodb-pear.inc.php @@ -0,0 +1,348 @@ + | + * and Tomas V.V.Cox + */ + + /* + We support: + + DB_Common + --------- + query - returns PEAR_Error on error + limitQuery - return PEAR_Error on error + prepare - does not return PEAR_Error on error + execute - does not return PEAR_Error on error + setFetchMode - supports ASSOC and ORDERED + errorNative + quote + nextID + free + + DB_Result + --------- + numRows - returns -1 if not supported + numCols + fetchInto - does not support passing of fetchmode + fetchRows - does not support passing of fetchmode + free + */ + +define('ADODB_PEAR',dirname(__FILE__)); +require_once "PEAR.php"; +require_once ADODB_PEAR."/adodb-errorpear.inc.php"; +require_once ADODB_PEAR."/adodb.inc.php"; + +if (!defined('DB_OK')) { +define("DB_OK", 0); +define("DB_ERROR",-1); +/** + * This is a special constant that tells DB the user hasn't specified + * any particular get mode, so the default should be used. + */ + +define('DB_FETCHMODE_DEFAULT', 0); + +/** + * Column data indexed by numbers, ordered from 0 and up + */ + +define('DB_FETCHMODE_ORDERED', 1); + +/** + * Column data indexed by column names + */ + +define('DB_FETCHMODE_ASSOC', 2); + +/* for compatibility */ + +define('DB_GETMODE_ORDERED', DB_FETCHMODE_ORDERED); +define('DB_GETMODE_ASSOC', DB_FETCHMODE_ASSOC); + +/** + * these are constants for the tableInfo-function + * they are bitwised or'ed. so if there are more constants to be defined + * in the future, adjust DB_TABLEINFO_FULL accordingly + */ + +define('DB_TABLEINFO_ORDER', 1); +define('DB_TABLEINFO_ORDERTABLE', 2); +define('DB_TABLEINFO_FULL', 3); +} + +/** + * The main "DB" class is simply a container class with some static + * methods for creating DB objects as well as some utility functions + * common to all parts of DB. + * + */ + +class DB +{ + /** + * Create a new DB object for the specified database type + * + * @param $type string database type, for example "mysql" + * + * @return object a newly created DB object, or a DB error code on + * error + */ + + function &factory($type) + { + @include_once("adodb-$type.inc.php"); + + $classname = "DB_${type}"; + + if (!class_exists($classname)) { + return PEAR::raiseError(null, DB_ERROR_NOT_FOUND, + null, null, null, 'DB_Error', true); + } + + @$obj =& new $classname; + + return $obj; + } + + /** + * Create a new DB object and connect to the specified database + * + * @param $dsn mixed "data source name", see the DB::parseDSN + * method for a description of the dsn format. Can also be + * specified as an array of the format returned by DB::parseDSN. + * + * @param $options mixed if boolean (or scalar), tells whether + * this connection should be persistent (for backends that support + * this). This parameter can also be an array of options, see + * DB_common::setOption for more information on connection + * options. + * + * @return object a newly created DB connection object, or a DB + * error object on error + * + * @see DB::parseDSN + * @see DB::isError + */ + function &connect($dsn, $options = false) + { + if (is_array($dsn)) { + $dsninfo = $dsn; + } else { + $dsninfo = DB::parseDSN($dsn); + } + switch ($dsninfo["phptype"]) { + case 'pgsql': $type = 'postgres7'; break; + default: $type = $dsninfo["phptype"]; break; + } + + if (is_array($options) && isset($options["debug"]) && + $options["debug"] >= 2) { + // expose php errors with sufficient debug level + @include_once("adodb-$type.inc.php"); + } else { + @include_once("adodb-$type.inc.php"); + } + + @$obj =& NewADOConnection($type); + + if (is_array($options)) { + $persist = !empty($options['persistent']); + } else { + $persist = true; + } + + if($persist) $ok = $obj->PConnect($dsninfo['hostspec'], $dsninfo['username'],$dsninfo['password'],$dsninfo['database']); + else $ok = $obj->Connect($dsninfo['hostspec'], $dsninfo['username'],$dsninfo['password'],$dsninfo['database']); + + if (!$ok) return ADODB_PEAR_Error(); + return $obj; + } + + /** + * Return the DB API version + * + * @return int the DB API version number + */ + function apiVersion() + { + return 2; + } + + /** + * Tell whether a result code from a DB method is an error + * + * @param $value int result code + * + * @return bool whether $value is an error + */ + function isError($value) + { + return (is_object($value) && + (get_class($value) == 'db_error' || + is_subclass_of($value, 'db_error'))); + } + + + /** + * Tell whether a result code from a DB method is a warning. + * Warnings differ from errors in that they are generated by DB, + * and are not fatal. + * + * @param $value mixed result value + * + * @return bool whether $value is a warning + */ + function isWarning($value) + { + return is_object($value) && + (get_class( $value ) == "db_warning" || + is_subclass_of($value, "db_warning")); + } + + /** + * Parse a data source name + * + * @param $dsn string Data Source Name to be parsed + * + * @return array an associative array with the following keys: + * + * phptype: Database backend used in PHP (mysql, odbc etc.) + * dbsyntax: Database used with regards to SQL syntax etc. + * protocol: Communication protocol to use (tcp, unix etc.) + * hostspec: Host specification (hostname[:port]) + * database: Database to use on the DBMS server + * username: User name for login + * password: Password for login + * + * The format of the supplied DSN is in its fullest form: + * + * phptype(dbsyntax)://username:password@protocol+hostspec/database + * + * Most variations are allowed: + * + * phptype://username:password@protocol+hostspec:110//usr/db_file.db + * phptype://username:password@hostspec/database_name + * phptype://username:password@hostspec + * phptype://username@hostspec + * phptype://hostspec/database + * phptype://hostspec + * phptype(dbsyntax) + * phptype + * + * @author Tomas V.V.Cox + */ + function parseDSN($dsn) + { + if (is_array($dsn)) { + return $dsn; + } + + $parsed = array( + 'phptype' => false, + 'dbsyntax' => false, + 'protocol' => false, + 'hostspec' => false, + 'database' => false, + 'username' => false, + 'password' => false + ); + + // Find phptype and dbsyntax + if (($pos = strpos($dsn, '://')) !== false) { + $str = substr($dsn, 0, $pos); + $dsn = substr($dsn, $pos + 3); + } else { + $str = $dsn; + $dsn = NULL; + } + + // Get phptype and dbsyntax + // $str => phptype(dbsyntax) + if (preg_match('|^(.+?)\((.*?)\)$|', $str, $arr)) { + $parsed['phptype'] = $arr[1]; + $parsed['dbsyntax'] = (empty($arr[2])) ? $arr[1] : $arr[2]; + } else { + $parsed['phptype'] = $str; + $parsed['dbsyntax'] = $str; + } + + if (empty($dsn)) { + return $parsed; + } + + // Get (if found): username and password + // $dsn => username:password@protocol+hostspec/database + if (($at = strpos($dsn,'@')) !== false) { + $str = substr($dsn, 0, $at); + $dsn = substr($dsn, $at + 1); + if (($pos = strpos($str, ':')) !== false) { + $parsed['username'] = urldecode(substr($str, 0, $pos)); + $parsed['password'] = urldecode(substr($str, $pos + 1)); + } else { + $parsed['username'] = urldecode($str); + } + } + + // Find protocol and hostspec + // $dsn => protocol+hostspec/database + if (($pos=strpos($dsn, '/')) !== false) { + $str = substr($dsn, 0, $pos); + $dsn = substr($dsn, $pos + 1); + } else { + $str = $dsn; + $dsn = NULL; + } + + // Get protocol + hostspec + // $str => protocol+hostspec + if (($pos=strpos($str, '+')) !== false) { + $parsed['protocol'] = substr($str, 0, $pos); + $parsed['hostspec'] = urldecode(substr($str, $pos + 1)); + } else { + $parsed['hostspec'] = urldecode($str); + } + + // Get dabase if any + // $dsn => database + if (!empty($dsn)) { + $parsed['database'] = $dsn; + } + + return $parsed; + } + + /** + * Load a PHP database extension if it is not loaded already. + * + * @access public + * + * @param $name the base name of the extension (without the .so or + * .dll suffix) + * + * @return bool true if the extension was already or successfully + * loaded, false if it could not be loaded + */ + function assertExtension($name) + { + if (!extension_loaded($name)) { + $dlext = (substr(PHP_OS, 0, 3) == 'WIN') ? '.dll' : '.so'; + @dl($name . $dlext); + } + if (!extension_loaded($name)) { + return false; + } + return true; + } +} + +?> \ No newline at end of file diff --git a/libraries/adodb/adodb-postgres.inc.php b/libraries/adodb/adodb-postgres.inc.php new file mode 100755 index 00000000..4ccff2f6 --- /dev/null +++ b/libraries/adodb/adodb-postgres.inc.php @@ -0,0 +1,377 @@ + + jlim - changed concat operator to || and data types to MetaType to match documented pgsql types + see http://www.postgresql.org/devel-corner/docs/postgres/datatype.htm + 22 Nov 2000 jlim - added changes to FetchField() and MetaTables() contributed by "raser" + 27 Nov 2000 jlim - added changes to _connect/_pconnect from ideas by "Lennie" + 15 Dec 2000 jlim - added changes suggested by Additional code changes by "Eric G. Werk" egw@netguide.dk. + 31 Jan 2001 jlim - finally installed postgresql. testing + 01 Mar 2001 jlim - Freek Dijkstra changes, also support for text type +*/ + +class ADODB_postgres extends ADOConnection{ + var $databaseType = 'postgres'; + var $hasInsertID = true; + var $_resultid = false; + var $concat_operator='||'; + var $metaTablesSQL = "select tablename from pg_tables where tablename not like 'pg_%' order by 1"; + +/* +# show tables and views suggestion +"SELECT c.relname AS tablename FROM pg_class c + WHERE (c.relhasrules AND (EXISTS ( + SELECT r.rulename FROM pg_rewrite r WHERE r.ev_class = c.oid AND bpchar(r.ev_type) = '1' + ))) OR (c.relkind = 'v') AND c.relname NOT LIKE 'pg_%' +UNION +SELECT tablename FROM pg_tables WHERE tablename NOT LIKE 'pg_%' ORDER BY 1" +*/ + var $metaColumnsSQL = "SELECT a.attname,t.typname,a.attlen,a.atttypmod,a.attnotnull FROM pg_class c, pg_attribute a,pg_type t WHERE relkind = 'r' AND c.relname='%s' AND a.attnum > 0 AND a.atttypid = t.oid AND a.attrelid = c.oid ORDER BY a.attnum"; + // get primary key etc -- from Freek Dijkstra + var $metaKeySQL = "SELECT ic.relname AS index_name, a.attname AS column_name,i.indisunique AS unique_key, i.indisprimary AS primary_key FROM pg_class bc, pg_class ic, pg_index i, pg_attribute a WHERE bc.oid = i.indrelid AND ic.oid = i.indexrelid AND (i.indkey[0] = a.attnum OR i.indkey[1] = a.attnum OR i.indkey[2] = a.attnum OR i.indkey[3] = a.attnum OR i.indkey[4] = a.attnum OR i.indkey[5] = a.attnum OR i.indkey[6] = a.attnum OR i.indkey[7] = a.attnum) AND a.attrelid = bc.oid AND bc.relname = '%s'"; + + var $_hastrans = false; + var $hasAffectedRows = true; + var $hasTop = false; + var $hasLimit = false; // set to true for pgsql 7 only. support pgsql/mysql SELECT * FROM TABLE LIMIT 10 + // below suggested by Freek Dijkstra + var $true = 't'; // string that represents TRUE for a database + var $false = 'f'; // string that represents FALSE for a database + var $fmtDate = "'Y-m-d'"; // used by DBDate() as the default date format used by the database + var $fmtTimeStamp = "'Y-m-d h:i:s'"; // used by DBTimeStamp as the default timestamp fmt. + var $hasMoveFirst = true; + var $hasGenID = true; + var $_genIDSQL = "SELECT NEXTVAL('%s')"; + var $_genSeqSQL = "CREATE SEQUENCE %s"; + + // The last (fmtTimeStamp is not entirely correct: + // PostgreSQL also has support for time zones, + // and writes these time in this format: "2001-03-01 18:59:26+02". + // There is no code for the "+02" time zone information, so I just left that out. + // I'm not familiar enough with both ADODB as well as Postgres + // to know what the concequences are. The other values are correct (wheren't in 0.94) + // -- Freek Dijkstra + + function ADODB_postgres() + { + } + + // get the last id - never tested + function pg_insert_id($tablename,$fieldname) + { + $result=pg_exec($this->_connectionID, "SELECT last_value FROM ${tablename}_${fieldname}_seq"); + if ($result) { + $arr = @pg_fetch_row($result,0); + pg_freeresult($result); + if (isset($arr[0])) return $arr[0]; + } + return false; + } + +/* Warning from http://www.php.net/manual/function.pg-getlastoid.php: +Using a OID as a unique identifier is not generally wise. +Unless you are very careful, you might end up with a tuple having +a different OID if a database must be reloaded. */ + function _insertid() + { + return pg_getlastoid($this->_resultid); + } + +// I get this error with PHP before 4.0.6 - jlim +// Warning: This compilation does not support pg_cmdtuples() in d:/inetpub/wwwroot/php/adodb/adodb-postgres.inc.php on line 44 + function _affectedrows() + { + return pg_cmdtuples($this->_resultid); + } + + + // returns true/false + function BeginTrans() + { + $this->_hastrans = true; + return @pg_Exec($this->_connectionID, "begin"); + } + + // returns true/false. + function CommitTrans() + { + $this->_hastrans = false; + return @pg_Exec($this->_connectionID, "commit"); + } + + // returns true/false + function RollbackTrans() + { + $this->_hastrans = false; + return @pg_Exec($this->_connectionID, "rollback"); + } + + // converts table to lowercase + function &MetaColumns($table) + { + if (!empty($this->metaColumnsSQL)) { + // the following is the only difference -- we lowercase it + $rs = $this->Execute(sprintf($this->metaColumnsSQL,strtolower($table))); + if ($rs === false) return false; + + if (!empty($this->metaKeySQL)) { + // If we want the primary keys, we have to issue a separate query + // Of course, a modified version of the metaColumnsSQL query using a + // LEFT JOIN would have been much more elegant, but postgres does + // not support OUTER JOINS. So here is the clumsy way. + $rskey = $this->Execute(sprintf($this->metaKeySQL,strtolower($table))); + // fetch all result in once for performance. + $keys = $rskey->GetArray(); + $rskey->Close(); + unset($rskey); + } + + $retarr = array(); + while (!$rs->EOF) { //print_r($rs->fields); + $fld = new ADOFieldObject(); + $fld->name = $rs->fields[0]; + $fld->type = $rs->fields[1]; + $fld->max_length = $rs->fields[2]; + if ($fld->max_length <= 0) $fld->max_length = $rs->fields[3]-4; + if ($fld->max_length <= 0) $fld->max_length = -1; + + //Freek + if ($rs->fields[4] == $this->true) { + $fld->not_null = true; + } + + // Freek + if (is_array($keys)) { + reset ($keys); + while (list($x,$key) = each($keys)) { + if ($fld->name == $key['column_name'] AND $key['primary_key'] == $this->true) + $fld->primary_key = true; + if ($fld->name == $key['column_name'] AND $key['unique_key'] == $this->true) + $fld->unique = true; // What name is more compatible? + } + } + + $retarr[strtoupper($fld->name)] = $fld; + + $rs->MoveNext(); + } + $rs->Close(); + return $retarr; + } + return false; + } + + + // returns true or false + // + // examples: + // $db->Connect("host=host1 user=user1 password=secret port=4341"); + // $db->Connect('host1','user1','secret'); + function _connect($str,$user='',$pwd='',$db='') + { + if ($user || $pwd || $db) { + if ($str) { + $host = split(":", $str); + if ($host[0]) $str = "host=$host[0]"; + else $str = 'localhost'; + if (isset($host[1])) $str .= " port=$host[1]"; + } + if ($user) $str .= " user=".$user; + if ($pwd) $str .= " password=".$pwd; + if ($db) $str .= " dbname=".$db; + } + + //if ($user) $linea = "user=$user host=$linea password=$pwd dbname=$db port=5432"; + $this->_connectionID = pg_connect($str); + if ($this->_connectionID === false) return false; + $this->Execute("set datestyle='ISO'"); + return true; + } + + // returns true or false + // + // examples: + // $db->PConnect("host=host1 user=user1 password=secret port=4341"); + // $db->PConnect('host1','user1','secret'); + function _pconnect($str,$user='',$pwd='',$db='') + { + if ($user || $pwd || $db) { + if ($str) { + $host = split(":", $str); + if ($host[0]) $str = "host=$host[0]"; + else $str = 'localhost'; + if (isset($host[1])) $str .= " port=$host[1]"; + } + if ($user) $str .= " user=".$user; + if ($pwd) $str .= " password=".$pwd; + if ($db) $str .= " dbname=".$db; + }//print $str; + $this->_connectionID = pg_pconnect($str); + if ($this->_connectionID === false) return false; + $this->Execute("set datestyle='ISO'"); + return true; + } + + // returns queryID or false + function _query($sql,$inputarr) + { + $this->_resultid= pg_Exec($this->_connectionID,$sql); + return $this->_resultid; + } + + + /* Returns: the last error message from previous database operation */ + function ErrorMsg() + { + if (empty($this->_connectionID)) $this->_errorMsg = @pg_errormessage(); + else $this->_errorMsg = @pg_errormessage($this->_connectionID); + return $this->_errorMsg; + } + + // returns true or false + function _close() + { + if ($this->_hastrans) $this->RollbackTrans(); + @pg_close($this->_connectionID); + $this->_resultid = false; + $this->_connectionID = false; + return true; + } + +} + +/*-------------------------------------------------------------------------------------- + Class Name: Recordset +--------------------------------------------------------------------------------------*/ + +class ADORecordSet_postgres extends ADORecordSet{ + + var $databaseType = "postgres"; + var $canSeek = true; + function ADORecordSet_postgres($queryID) { + global $ADODB_FETCH_MODE; + + switch ($ADODB_FETCH_MODE) + { + case ADODB_FETCH_NUM: $this->fetchMode = PGSQL_NUM; break; + case ADODB_FETCH_ASSOC:$this->fetchMode = PGSQL_ASSOC; break; + default: + case ADODB_FETCH_DEFAULT: + case ADODB_FETCH_BOTH:$this->fetchMode = PGSQL_BOTH; break; + } + + $this->ADORecordSet($queryID); + } + + function &GetRowAssoc($upper=true) + { + if ($this->fetchMode == PGSQL_ASSOC && !$upper) return $rs->fields; + return ADORecordSet::GetRowAssoc($upper); + } + + function _initrs() + { + global $ADODB_COUNTRECS; + $this->_numOfRows = ($ADODB_COUNTRECS)? @pg_numrows($this->_queryID):-1; + $this->_numOfFields = @pg_numfields($this->_queryID); + } + + function &FetchField($fieldOffset = 0) + { + $off=$fieldOffset; // offsets begin at 0 + + $o= new ADOFieldObject(); + $o->name = @pg_fieldname($this->_queryID,$off); + $o->type = @pg_fieldtype($this->_queryID,$off); + $o->max_length = @pg_fieldsize($this->_queryID,$off); + //print_r($o); + //print "off=$off name=$o->name type=$o->type len=$o->max_length
"; + return $o; + } + + function _seek($row) + { + return @pg_fetch_row($this->_queryID,$row); + } + + // 10% speedup to move MoveNext to child class + function MoveNext() + { + if (!$this->EOF) { + $this->_currentRow++; + $this->fields = @pg_fetch_array($this->_queryID,$this->_currentRow,$this->fetchMode); + if (is_array($this->fields)) return true; + } + $this->EOF = true; + return false; + } + function _fetch() + { + $this->fields = @pg_fetch_array($this->_queryID,$this->_currentRow,$this->fetchMode); + return (is_array($this->fields)); + } + + function _close() { + return @pg_freeresult($this->_queryID); + } + + function MetaType($t,$len=-1,$fieldobj=false) + { + switch (strtoupper($t)) { + case 'CHAR': + case 'CHARACTER': + case 'VARCHAR': + case 'NAME': + case 'BPCHAR': + if ($len <= $this->blobSize) return 'C'; + + case 'TEXT': + return 'X'; + + case 'IMAGE': // user defined type + case 'BLOB': // user defined type + case 'BIT': // This is a bit string, not a single bit, so don't return 'L' + case 'VARBIT': + case 'BYTEA': + return 'B'; + + case 'BOOL': + case 'BOOLEAN': + return 'L'; + + case 'DATE': + return 'D'; + + case 'TIME': + case 'DATETIME': + case 'TIMESTAMP': + return 'T'; + + case 'SMALLINT': + case 'BIGINT': + case 'INTEGER': + case 'INT8': + case 'INT4': + case 'INT2': + if (isset($fieldobj) && + empty($fieldobj->primary_key) && empty($fieldobj->unique)) return 'I'; + + case 'OID': + case 'SERIAL': + return 'R'; + + default: + return 'N'; + } + } + +} +?> diff --git a/libraries/adodb/adodb-postgres7.inc.php b/libraries/adodb/adodb-postgres7.inc.php new file mode 100755 index 00000000..d5293277 --- /dev/null +++ b/libraries/adodb/adodb-postgres7.inc.php @@ -0,0 +1,62 @@ += 0) ? " OFFSET $offset" : ''; + $limitStr = ($nrows >= 0) ? " LIMIT $nrows" : ''; + return $secs2cache ? + $this->CacheExecute($secs2cache,$sql."$limitStr$offsetStr",$inputarr,$arg3) + : + $this->Execute($sql."$limitStr$offsetStr",$inputarr,$arg3); + } + + // 10% speedup to move MoveNext to child class + function MoveNext() + { + if (!$this->EOF) { + $this->_currentRow++; + $this->fields = @pg_fetch_array($this->_queryID,$this->_currentRow,$this->fetchMode); + if (is_array($this->fields)) return true; + } + $this->EOF = true; + return false; + } +} + +/*-------------------------------------------------------------------------------------- + Class Name: Recordset +--------------------------------------------------------------------------------------*/ + +class ADORecordSet_postgres7 extends ADORecordSet_postgres{ + + var $databaseType = "postgres7"; + + function ADORecordSet_postgres7($queryID) + { + $this->ADORecordSet_postgres($queryID); + } + +} +?> diff --git a/libraries/adodb/adodb-proxy.inc.php b/libraries/adodb/adodb-proxy.inc.php new file mode 100755 index 00000000..02a6fca4 --- /dev/null +++ b/libraries/adodb/adodb-proxy.inc.php @@ -0,0 +1,28 @@ + \ No newline at end of file diff --git a/libraries/adodb/adodb-session.php b/libraries/adodb/adodb-session.php new file mode 100755 index 00000000..821c1a74 --- /dev/null +++ b/libraries/adodb/adodb-session.php @@ -0,0 +1,228 @@ +\$HTTP_SESSION_VARS['AVAR']={$HTTP_SESSION_VARS['AVAR']}

"; + + + Installation + ============ + 1. Create a new database in MySQL or Access "sessions" like +so: + + create table sessions ( + SESSKEY char(32) not null, + EXPIRY int(11) unsigned not null, + DATA text not null, + primary key (sesskey) + ); + + 2. Then define the following parameters in this file: + $ADODB_SESSION_DRIVER='database driver, eg. mysql or ibase'; + $ADODB_SESSION_CONNECT='server to connect to'; + $ADODB_SESSION_USER ='user'; + $ADODB_SESSION_PWD ='password'; + $ADODB_SESSION_DB ='database'; + $ADODB_SESSION_TBL = 'sessions' + + 3. Recommended is PHP 4.0.2 or later. There are documented +session bugs in + earlier versions of PHP. + +*/ + +if (!defined('_ADODB_LAYER')) { + include ('adodb.inc.php'); +} + + + +if (!defined('ADODB_SESSION')) { + + define('ADODB_SESSION',1); + +GLOBAL $ADODB_SESSION_CONNECT, + $ADODB_SESSION_DRIVER, + $ADODB_SESSION_USER, + $ADODB_SESSION_PWD, + $ADODB_SESSION_DB, + $ADODB_SESS_CONN, + $ADODB_SESS_LIFE, + $ADODB_SESS_DEBUG, + $ADODB_SESS_INSERT; + + //$ADODB_SESS_DEBUG = true; + + /* SET THE FOLLOWING PARAMETERS */ +if (empty($ADODB_SESSION_DRIVER)) { + $ADODB_SESSION_DRIVER='mysql'; + $ADODB_SESSION_CONNECT='localhost'; + $ADODB_SESSION_USER ='root'; + $ADODB_SESSION_PWD =''; + $ADODB_SESSION_DB ='xphplens_2'; +} +if (empty($ADODB_SESSION_TBL)){ + $ADODB_SESSION_TBL = 'sessions'; +} + +$ADODB_SESS_LIFE = get_cfg_var('session.gc_maxlifetime'); +if ($ADODB_SESS_LIFE <= 1) { + // bug in PHP 4.0.3 pl 1 -- how about other versions? + //print "

Session Error: PHP.INI setting session.gc_maxlifetimenot set: $ADODB_SESS_LIFE

"; + $ADODB_SESS_LIFE=1440; +} + +function adodb_sess_open($save_path, $session_name) +{ +GLOBAL $ADODB_SESSION_CONNECT, + $ADODB_SESSION_DRIVER, + $ADODB_SESSION_USER, + $ADODB_SESSION_PWD, + $ADODB_SESSION_DB, + $ADODB_SESS_CONN, + $ADODB_SESS_DEBUG; + + $ADODB_SESS_INSERT = false; + + if (isset($ADODB_SESS_CONN)) return true; + + $ADODB_SESS_CONN = ADONewConnection($ADODB_SESSION_DRIVER); + if (!empty($ADODB_SESS_DEBUG)) { + $ADODB_SESS_CONN->debug = true; + print" conn=$ADODB_SESSION_CONNECT user=$ADODB_SESSION_USER pwd=$ADODB_SESSION_PWD db=$ADODB_SESSION_DB "; + } + return $ADODB_SESS_CONN->PConnect($ADODB_SESSION_CONNECT, + $ADODB_SESSION_USER,$ADODB_SESSION_PWD,$ADODB_SESSION_DB); + +} + +function adodb_sess_close() +{ +global $ADODB_SESS_CONN; + + if ($ADODB_SESS_CONN) $ADODB_SESS_CONN->Close(); + return true; +} + +function adodb_sess_read($key) +{ +global $ADODB_SESS_CONN,$ADODB_SESS_INSERT,$ADODB_SESSION_TBL; + $rs = $ADODB_SESS_CONN->Execute("SELECT data FROM $ADODB_SESSION_TBL WHERE sesskey = '$key' AND expiry >= " . time()); + if ($rs) { + if ($rs->EOF) { + $ADODB_SESS_INSERT = true; + $v = ''; + } else + $v = rawurldecode($rs->fields[0]); + + $rs->Close(); + return $v; + } + else $ADODB_SESS_INSERT = true; + + return false; +} + +function adodb_sess_write($key, $val) +{ + global $ADODB_SESS_INSERT,$ADODB_SESS_CONN, $ADODB_SESS_LIFE, $ADODB_SESSION_TBL; + + $expiry = time() + $ADODB_SESS_LIFE; + + $val = rawurlencode($val); + $qry = "UPDATE $ADODB_SESSION_TBL SET expiry=$expiry,data='$val' WHERE sesskey='$key'"; + $rs = $ADODB_SESS_CONN->Execute($qry); + if ($rs) $rs->Close(); + else print '

Session Update: '.$ADODB_SESS_CONN->ErrorMsg().'

'; + + if ($ADODB_SESS_INSERT || $rs === false) { + $qry = "INSERT INTO $ADODB_SESSION_TBL(sesskey,expiry,data) VALUES ('$key',$expiry,'$val')"; + $rs = $ADODB_SESS_CONN->Execute($qry); + if ($rs) $rs->Close(); + else print '

Session Insert: '.$ADODB_SESS_CONN->ErrorMsg().'

'; + } + // bug in access driver (could be odbc?) means that info is not commited + // properly unless select statement executed in Win2000 + if ($ADODB_SESS_CONN->databaseType == 'access') $rs = $ADODB_SESS_CONN->Execute("select sesskey from $ADODB_SESSION_TBL WHERE sesskey='$key'"); + + return isset($rs); +} + +function adodb_sess_destroy($key) +{ + global $ADODB_SESS_CONN, $ADODB_SESSION_TBL; + + $qry = "DELETE FROM $ADODB_SESSION_TBL WHERE sesskey = '$key'"; + $rs = $ADODB_SESS_CONN->Execute($qry); + if ($rs) $rs->Close(); + return $rs; +} + +function adodb_sess_gc($maxlifetime) { + global $ADODB_SESS_CONN, $ADODB_SESSION_TBL; + + $qry = "DELETE FROM $ADODB_SESSION_TBL WHERE expiry < " . time(); + $rs = $ADODB_SESS_CONN->Execute($qry); + if ($rs) $rs->Close(); + + // suggested by Cameron, "GaM3R" + if (defined('ADODB_SESSION_OPTIMIZE')) + { + switch( $ADODB_SESSION_DRIVER ) { + case 'mysql': + case 'mysqlt': + $opt_qry = 'OPTIMIZE TABLE '.$ADODB_SESSION_TBL; + break; + case 'postgresql': + case 'postgresql7': + $opt_qry = 'VACUUM '.$ADODB_SESSION_TBL; + break; + } + } + + return true; +} + +session_module_name('user'); +session_set_save_handler( + "adodb_sess_open", + "adodb_sess_close", + "adodb_sess_read", + "adodb_sess_write", + "adodb_sess_destroy", + "adodb_sess_gc"); +} + +/* TEST SCRIPT -- UNCOMMENT */ + +if (0) { +GLOBAL $HTTP_SESSION_VARS; + + session_start(); + session_register('AVAR'); + $HTTP_SESSION_VARS['AVAR'] += 1; + print "

\$HTTP_SESSION_VARS['AVAR']={$HTTP_SESSION_VARS['AVAR']}

"; +} + +?> diff --git a/libraries/adodb/adodb-sybase.inc.php b/libraries/adodb/adodb-sybase.inc.php new file mode 100755 index 00000000..1bd01b00 --- /dev/null +++ b/libraries/adodb/adodb-sybase.inc.php @@ -0,0 +1,215 @@ +Execute('select @@identity'); + if ($rs == false || $rs->EOF) return false; + $id = $rs->fields[0]; + $rs->Close(); + return $id; + } + // might require begintrans -- committrans + function _affectedrows() + { + $rs = $this->Execute('select @@rowcount'); + if ($rs == false || $rs->EOF) return false; + $id = $rs->fields[0]; + $rs->Close(); + return $id; + } + + + function BeginTrans() + { + $this->Execute('BEGIN TRAN'); + return true; + } + + function CommitTrans() + { + $this->Execute('COMMIT TRAN'); + return true; + } + + function RollbackTrans() + { + $this->Execute('ROLLBACK TRAN'); + return true; + } + + + function SelectDB($dbName) { + $this->databaseName = $dbName; + if ($this->_connectionID) { + return @sybase_select_db($dbName); + } + else return false; + } + + /* Returns: the last error message from previous database operation + Note: This function is NOT available for Microsoft SQL Server. */ + + function ErrorMsg() { + $this->_errorMsg = sybase_get_last_message(); + return $this->_errorMsg; + } + + // returns true or false + function _connect($argHostname, $argUsername, $argPassword, $argDatabasename) + { + $this->_connectionID = sybase_connect($argHostname,$argUsername,$argPassword); + if ($this->_connectionID === false) return false; + if ($argDatabasename) return $this->SelectDB($argDatabasename); + return true; + } + // returns true or false + function _pconnect($argHostname, $argUsername, $argPassword, $argDatabasename) + { + $this->_connectionID = sybase_pconnect($argHostname,$argUsername,$argPassword); + if ($this->_connectionID === false) return false; + if ($argDatabasename) return $this->SelectDB($argDatabasename); + return true; + } + + // returns query ID if successful, otherwise false + function _query($sql,$inputarr) + { + //@sybase_free_result($this->_queryID); + return sybase_query($sql,$this->_connectionID); + } + + // returns true or false + function _close() + { + return @sybase_close($this->_connectionID); + } + + +} + +/*-------------------------------------------------------------------------------------- + Class Name: Recordset +--------------------------------------------------------------------------------------*/ +global $ADODB_sybase_mths; +$ADODB_sybase_mths = array('JAN'=>1,'FEB'=>2,'MAR'=>3,'APR'=>4,'MAY'=>5,'JUN'=>6,'JUL'=>7,'AUG'=>8,'SEP'=>9,'OCT'=>10,'NOV'=>11,'DEC'=>12); + +class ADORecordset_sybase extends ADORecordSet { + + var $databaseType = "sybase"; + var $canSeek = true; + // _mths works only in non-localised system + var $_mths = array('JAN'=>1,'FEB'=>2,'MAR'=>3,'APR'=>4,'MAY'=>5,'JUN'=>6,'JUL'=>7,'AUG'=>8,'SEP'=>9,'OCT'=>10,'NOV'=>11,'DEC'=>12); + + function ADORecordset_sybase($id) + { + return $this->ADORecordSet($id); + } + + + /* Returns: an object containing field information. + Get column information in the Recordset object. fetchField() can be used in order to obtain information about + fields in a certain query result. If the field offset isn't specified, the next field that wasn't yet retrieved by + fetchField() is retrieved. */ + function &FetchField($fieldOffset = -1) + { + if ($fieldOffset != -1) { + $o = @sybase_fetch_field($this->_queryID, $fieldOffset); + } + else if ($fieldOffset == -1) { /* The $fieldOffset argument is not provided thus its -1 */ + $o = @sybase_fetch_field($this->_queryID); + } + // older versions of PHP did not support type, only numeric + if ($o && !isset($o->type)) $o->type = ($o->numeric) ? 'float' : 'varchar'; + return $o; + } + + function _initrs() + { + global $ADODB_COUNTRECS; + $this->_numOfRows = ($ADODB_COUNTRECS)? @sybase_num_rows($this->_queryID):-1; + $this->_numOfFields = @sybase_num_fields($this->_queryID); + } + + function _seek($row) + { + return @sybase_data_seek($this->_queryID, $row); + } + + function _fetch($ignore_fields=false) { + $this->fields = @sybase_fetch_array($this->_queryID); + return ($this->fields == true); + } + + /* close() only needs to be called if you are worried about using too much memory while your script + is running. All associated result memory for the specified result identifier will automatically be freed. */ + function _close() { + return @sybase_free_result($this->_queryID); + } + + // sybase/mssql uses a default date like Dec 30 2000 12:00AM + function UnixDate($v) + { + global $ADODB_sybase_mths; + //Dec 30 2000 12:00AM + // added fix by Toni for day 15 Mar 2001 + if (!ereg( "([A-Za-z]{3})[-/\. ]([0-9 ]{1,2})[-/\. ]([0-9]{4}))" + ,$v, $rr)) return parent::UnixDate($v); + + if ($rr[3] <= 1970) return 0; + + $themth = substr(strtoupper($rr[1]),0,3); + $themth = $ADODB_sybase_mths[$themth]; + if ($themth <= 0) return false; + // h-m-s-MM-DD-YY + return mktime(0,0,0,$themth,$rr[2],$rr[3]); + } + + function UnixTimeStamp($v) + { + global $ADODB_sybase_mths; + //Dec 30 2000 12:00AM + if (!ereg( "([A-Za-z]{3})[-/\. ]([0-9]{1,2})[-/\. ]([0-9]{4}) +([0-9]{1,2}):([0-9]{1,2}) *([apAP]{0,1})" + ,$v, $rr)) return parent::UnixTimeStamp($v); + if ($rr[3] <= 1970) return 0; + + $themth = substr(strtoupper($rr[1]),0,3); + $themth = $ADODB_sybase_mths[$themth]; + if ($themth <= 0) return false; + + if (strtoupper($rr[6]) == 'P') { + if ($rr[4]<12) $rr[4] += 12; + } else { + if ($rr[4]==12) $rr[4] = 0; + } + // h-m-s-MM-DD-YY + return mktime($rr[4],$rr[5],0,$themth,$rr[2],$rr[3]); + } + +} +?> \ No newline at end of file diff --git a/libraries/adodb/adodb-vfp.inc.php b/libraries/adodb/adodb-vfp.inc.php new file mode 100755 index 00000000..dcde32cf --- /dev/null +++ b/libraries/adodb/adodb-vfp.inc.php @@ -0,0 +1,79 @@ +replaceQuote,$s))."'"; + return "'".$s."'"; + } + + // TOP requires ORDER BY for VFP + function &SelectLimit($sql,$nrows=-1,$offset=-1, $inputarr=false,$arg3=false,$secs2cache=0) + { + if (!preg_match('/ORDER[ \t\r\n]+BY/i',$sql)) $sql .= ' ORDER BY 1'; + return ADOConnection::SelectLimit($sql,$nrows,$offset,$inputarr,$arg3,$secs2cache); + } + +}; + + +class ADORecordSet_vfp extends ADORecordSet_odbc { + + var $databaseType = "vfp"; + + + function ADORecordSet_vfp($id) + { + return $this->ADORecordSet_odbc($id); + } + + function MetaType($t,$len=-1) + { + switch (strtoupper($t)) { + case 'C': + if ($len <= $this->blobSize) return 'C'; + case 'M': + return 'X'; + + case 'D': return 'D'; + + case 'T': return 'T'; + + case 'L': return 'L'; + + case 'I': return 'I'; + + default: return 'N'; + } + } +} + +} //define +?> \ No newline at end of file diff --git a/libraries/adodb/adodb.gif b/libraries/adodb/adodb.gif new file mode 100755 index 0000000000000000000000000000000000000000..47b6f2d42a1b530ce904b99a2b932fdbd6f5c27b GIT binary patch literal 1089 zcmb7<{ZrC+0DwP$3Wz4Y<_juc6{5tq^p-kn%3Qv$J5D{dW|GM8B}HfRimHS}m1Y3jkHAd|OoX(8s52a8SkLJ=19R z8H_pr;>^u6uvjJlp_!SY>S}RVSPdG@Adw1UVj5(!1rLv6ESBZ#TMht4BAHE16+%#c zYwJXQe$TOE46%5TN^P{V$^yXeGYo!|^FM(;=;)dOS3o}Uj-G&?R&u@cK|om`uRZH| zns1A;(<{!MK*n7ls%On0b#HFCYlC8C&nhzMJMSuhfYEZo51te#w*Jww3k%R_2xo3i zLE)pxWQfDD)7MA+Qp>I567aRPc#^Tby|rC)2V}!-f(YEsP6PsJXo#}G5~03+J@8@HZ-JIzW>i3=~J zZ;(6t^NWTFx-nJGL|8048xwq}Z=6unTa3iYi*xZMFq-~Q)YPM!_Wf~&{{AZoG+qx@ zYTzwrk|9s56n8Mpyjt&+dw-u!~=8WA4o-!B{ydl4+Z5qWjF zi7`5ei+Fr@Sjxd#${)ltV`G<}9{$R|lc$*#$QN7^)c34cFK0m)Yvd8G5q)hw`o*6H z$h3zl9I@&|^n;#PIzM}a4!!77-w>oajxOia&r09Df*dipUHD+-aZ5%4>w{txQ}I(y zfz6&<{q{_yAm$VMZ`t$Xmci@$3$T~oMoRA4kB4dLia!TAQR8ZYDx>NwPmQ?p@`vhl zIGqg;7m{w7W*Xa$wIEg=U(@ogwg}$G>#39_HQBq3OBmGf-M2QmnoAuEex{QuTplDM zgh!6Ebl<>c&^*{pOo;MI%&BHNzsS=SP>Rh8QB6US(L)aK+rKALZrc@djJ|@^2TATE zp8k5tjow_&s8h9vuORtXKG4X)IH4i;_1 zT8V^w;@YX!79u3%kC@D@@}KRLuXl^>D%W6(_$YtaUnJ+0zUt4b_giO075AHDCRi6e z+iW~%V7g*0esfr`le{X(Va!WVj%r$&TiLsNTO)`2>szA$N!GOO5hYTnjNPu08l2hC zjA4Vw9>Ku_N($=aL81i}m$R`wfiETROghzsy8#y#3pGx7w6QbgUK+lI3IO2Ce*p+p B)@%R( literal 0 HcmV?d00001 diff --git a/libraries/adodb/adodb.inc.php b/libraries/adodb/adodb.inc.php new file mode 100755 index 00000000..299a4f9f --- /dev/null +++ b/libraries/adodb/adodb.inc.php @@ -0,0 +1,2332 @@ +Bad $rs in %s. Connection or SQL invalid. Try using $connection->debug=true;

'); + + define('ADODB_FETCH_DEFAULT',0); + define('ADODB_FETCH_NUM',1); + define('ADODB_FETCH_ASSOC',2); + define('ADODB_FETCH_BOTH',3); + + GLOBAL + $ADODB_vers, // database version + $ADODB_Database, // last database driver used + $ADODB_COUNTRECS, // count number of records returned - slows down query + $ADODB_CACHE_DIR, // directory to cache recordsets + $ADODB_FETCH_MODE; // DEFAULT, NUM, ASSOC or BOTH. Default follows native driver default... + + $ADODB_FETCH_MODE = ADODB_FETCH_DEFAULT; + /** + * SET THE VALUE BELOW TO THE DIRECTORY WHERE THIS FILE RESIDES + * ADODB_RootPath has been renamed ADODB_DIR + */ + if (!defined('ADODB_DIR')) define('ADODB_DIR',dirname(__FILE__)); + + if (!isset($ADODB_CACHE_DIR)) $ADODB_CACHE_DIR = '/tmp'; + else if (strpos($ADODB_CACHE_DIR,':/') !== false) die("Illegal \$ADODB_CACHE_DIR"); + + //============================================================================================== + // CHANGE NOTHING BELOW UNLESS YOU ARE CODING + //============================================================================================== + + // using PHPCONFIG protocol overrides ADODB_DIR + //if (isset($PHPCONFIG_DIR)) $ADODB_DIR=$PHPCONFIG_DIR.'/../adodb'; + + srand(((double)microtime())*1000000); + /** + * Name of last database driver loaded into memory. + */ + $ADODB_Database = ''; + + /** + * ADODB version as a string. + */ + $ADODB_vers = 'V1.53 13 Nov 2001 (c) 2000, 2001 John Lim (jlim@natsoft.com.my). All rights reserved. Released BSD & LGPL.'; + + /** + * Determines whether recordset->RecordCount() is used. + * Set to false for highest performance -- RecordCount() will always return -1 then. + */ + $ADODB_COUNTRECS = true; + + /** + * Helper class for FetchFields -- holds info on a column + */ + class ADOFieldObject { + var $name = ''; + var $max_length=0; + var $type=""; + } + + + /** + * Connection object. For connecting to databases, and executing queries. + */ + class ADOConnection { + /* + * PUBLIC VARS + */ + var $dataProvider = 'native'; + var $databaseType = ''; // RDBMS currently in use, eg. odbc, mysql, mssql + var $database = ''; // Name of database to be used. + var $host = ''; //The hostname of the database server + var $user = ''; // The username which is used to connect to the database server. + var $password = ''; // Password for the username + var $debug = false; // if set to true will output sql statements + var $maxblobsize = 8000; // maximum size of blobs or large text fields -- some databases die otherwise like foxpro + var $concat_operator = '+'; // default concat operator -- change to || for Oracle/Interbase + var $fmtDate = "'Y-m-d'"; // used by DBDate() as the default date format used by the database + var $fmtTimeStamp = "'Y-m-d, h:i:s A'"; // used by DBTimeStamp as the default timestamp fmt. + var $true = '1'; // string that represents TRUE for a database + var $false = '0'; // string that represents FALSE for a database + var $replaceQuote = "\\'"; // string to use to replace quotes + var $hasInsertID = false; // supports autoincrement ID? + var $hasAffectedRows = false; // supports affected rows for update/delete? + var $autoCommit = true; + var $charSet=false; // character set to use - only for interbase + var $metaTablesSQL = ''; + var $hasTop = false; // support mssql/access SELECT TOP 10 * FROM TABLE + var $hasLimit = false; // support pgsql/mysql SELECT * FROM TABLE LIMIT 10 + var $readOnly = false; // this is a readonly database ? + var $hasMoveFirst = false; + var $hasGenID = false; // can generate sequences using GenID(); + var $genID = false; // sequence id used by GenID(); + var $raiseErrorFn = false; + + /* + * PRIVATE VARS + */ + var $_connectionID = false; // The returned link identifier whenever a successful database connection is made. */ + + var $_errorMsg = ''; // A variable which was used to keep the returned last error message. The value will + //then returned by the errorMsg() function + + var $_queryID = false; // This variable keeps the last created result link identifier. */ + + var $_isPersistentConnection = false; // A boolean variable to state whether its a persistent connection or normal connection. */ + + var $_bindInputArray = false; // set to true if ADOConnection.Execute() permits binding of array parameters. + + /** + * Constructor + */ + function ADOConnection() + { + die('Virtual Class -- cannot instantiate'); + } + + /** + * Connect to database + * + * @param [argHostname] Host to connect to + * @param [argUsername] Userid to login + * @param [argPassword] Associated password + * @param [argDatabaseName] database + * + * @return true or false + */ + function Connect($argHostname = "", $argUsername = "", $argPassword = "", $argDatabaseName = "") { + if ($argHostname != "") $this->host = $argHostname; + if ($argUsername != "") $this->user = $argUsername; + if ($argPassword != "") $this->password = $argPassword; // not stored for security reasons + if ($argDatabaseName != "") $this->database = $argDatabaseName; + + if ($this->_connect($this->host, $this->user, $this->password, $this->database)) + return true; + + if ($fn = $this->raiseErrorFn) { + $fn($this->databaseType,'CONNECT',$this->ErrorNo(),$this->ErrorMsg(),$this->host,$this->database); + } + if ($this->debug) print $this->host.': '.$this->ErrorMsg().'
'; + + return false; + } + + /** + * Establish persistent connect to database + * + * @param [argHostname] Host to connect to + * @param [argUsername] Userid to login + * @param [argPassword] Associated password + * @param [argDatabaseName] database + * + * @return return true or false + */ + function PConnect($argHostname = "", $argUsername = "", $argPassword = "", $argDatabaseName = "") + { + if ($argHostname != "") $this->host = $argHostname; + if ($argUsername != "") $this->user = $argUsername; + if ($argPassword != "") $this->password = $argPassword; + if ($argDatabaseName != "") $this->database = $argDatabaseName; + + if ($this->_pconnect($this->host, $this->user, $this->password, $this->database)) { + $this->_isPersistentConnection = true; + return true; + } + if ($fn = $this->raiseErrorFn) { + $fn($this->databaseType,'PCONNECT', $this->ErrorNo(),$this->ErrorMsg(),$this->host,$this->database); + } + if ($this->debug) print $this->host.': '.$this->ErrorMsg().'
'; + + return false; + } + + /** + * Should prepare the sql statement and return the stmt resource. + * For databases that do not support this, we return the $sql. To ensure + * compatibility with databases that do not support prepare: + * + * $stmt = $db->Prepare("insert into table (id, name) values (?,?)"); + * $db->Execute($stmt,array(1,'Jill')) or die('insert failed'); + * $db->Execute($stmt,array(2,'Joe')) or die('insert failed'); + * + * @param sql SQL to send to database + * + * @return return TRUE or FALSE, or the $sql. + * + */ + function Prepare($sql) + { + return $sql; + } + + /** + * PEAR DB Compat - do not use internally. + */ + function Quote($s) + { + return $this->qstr($s); + } + + /** + * PEAR DB Compat - do not use internally. + */ + function ErrorNative() + { + return $this->ErrorNo(); + } + + /** + * PEAR DB Compat - do not use internally. + */ + function nextId($seq_name) + { + return $this->GenID($seq_name); + } + + /** + * PEAR DB Compat - do not use internally. + * + * Appears that the fetch modes for NUMERIC and ASSOC are identical! + */ + function SetFetchMode($mode) + { + global $ADODB_FETCH_MODE; + $ADODB_FETCH_MODE = $mode; + } + + /** + * PEAR DB Compat - do not use internally. + */ + function &Query($sql, $inputarr=false) + { + $rs = &$this->Execute($sql, $inputarr); + if (!$rs && defined('ADODB_PEAR')) return ADODB_PEAR_Error(); + return $rs; + } + + /** + * PEAR DB Compat - do not use internally + */ + function &LimitQuery($sql, $offset, $count) + { + $rs = &$this->SelectLimit($sql, $count, $offset); // swap + if (!$rs && defined('ADODB_PEAR')) return ADODB_PEAR_Error(); + return $rs; + } + + /** + * PEAR DB Compat - do not use internally + */ + function Free() + { + return $this->Close(); + } + + /** + * Execute SQL + * + * @param sql SQL statement to execute + * @param [inputarr] holds the input data to bind to. Null elements will be set to null. + * @param [arg3] reserved for john lim for future use + * @return RecordSet or false + */ + function &Execute($sql,$inputarr=false,$arg3=false) + { + if (!$this->_bindInputArray && $inputarr) { + $sqlarr = explode('?',$sql); + $sql = ''; + $i = 0; + foreach($inputarr as $v) { + + $sql .= $sqlarr[$i]; + // from Ron Baldwin + // Only quote string types + if (gettype($v) == 'string') + $sql .= "'".$v."'"; + else if ($v === null) + $sql .= 'NULL'; + else + $sql .= $v; + $i += 1; + + } + $sql .= $sqlarr[$i]; + if ($i+1 != sizeof($sqlarr)) print "Input Array does not match ?: ".htmlspecialchars($sql); + $inputarr = false; + } + + if ($this->debug) { + $ss = ''; + if ($inputarr) { + //$inputarr[7] = '2003-01-01'; + foreach ($inputarr as $kk => $vv) { + if (is_string($vv) && strlen($vv)>100) $vv = substr($vv,0,64).'...'; + $ss .= "($kk=>'$vv') "; + } + $ss = "[ $ss ]"; + } + print "
($this->databaseType): ".htmlspecialchars($sql)."   $ss
"; + $this->_queryID = $this->_query($sql,$inputarr,$arg3); + if ($this->databaseType == 'mssql') { // ErrorNo is a slow function call in mssql + if($this->ErrorMsg()) { + $err = $this->ErrorNo(); + if ($err) print $err.': '.$this->ErrorMsg().'
'; + } + } else + if ($this->ErrorNo()) print $this->ErrorNo().': '.$this->ErrorMsg().'
'; + + } else + $this->_queryID =@$this->_query($sql,$inputarr,$arg3); + + if ($this->_queryID === false) { + if ($fn = $this->raiseErrorFn) { + $fn($this->databaseType,'EXECUTE',$this->ErrorNo(),$this->ErrorMsg(),$sql,$inputarr); + } + return false; + } else if ($this->_queryID === true){ + $rs = new ADORecordSet_empty(); + return $rs; + } + $rsclass = "ADORecordSet_".$this->databaseType; + + $rs = new $rsclass($this->_queryID); // &new not supported by older PHP versions + $rs->Init(); + //$this->_insertQuery(&$rs); PHP4 handles closing automatically + + if (is_string($sql)) $rs->sql = $sql; + return $rs; + } + + /** + * Generates a sequence id and stores it in $this->genID; + * GenID is only available if $this->hasGenID = true; + * + * @seqname name of sequence to use + * + * @return false if not supported, otherwise a sequence id + */ + + function GenID($seqname='adodbseq') + { + if (!$this->hasGenID) return false; + + $getnext = sprintf($this->_genIDSQL,$seqname); + $rs = @$this->Execute($getnext); + if (!$rs) { + $u = strtoupper($seqname); + $createseq = + $this->Execute(sprintf($this->_genSeqSQL,$seqname)); + $rs = $this->Execute($getnext); + } + if ($rs && !$rs->EOF) $this->genID = (integer) $rs->fields[0]; + else $this->genID = false; + + if ($rs) $rs->Close(); + + return $this->genID; + } + + + /** + * @return the last inserted ID. Not all databases support this. + */ + function Insert_ID() + { + if ($this->hasInsertID) return $this->_insertid(); + if ($this->debug) print '

Insert_ID error

'; + return false; + } + + /** + * @return # rows affected by UPDATE/DELETE + */ + function Affected_Rows() + { + if ($this->hasAffectedRows) { + $val = $this->_affectedrows(); + return ($val < 0) ? false : $val; + } + + if ($this->debug) print '

Affected_Rows error

'; + return false; + } + + /** + * @return the last error message + */ + function ErrorMsg() + { + return '!! '.strtoupper($this->dataProvider.' '.$this->databaseType).': '.$this->_errorMsg; + } + + + /** + * @return the last error number. Normally 0 means no error. + */ + function ErrorNo() + { + return ($this->_errorMsg) ? -1 : 0; + } + + /** + * @returns an array with the primary key columns in it. + */ + function MetaPrimaryKeys($table) + { + return false; + } + + /** + * Choose a database to connect to. Many databases do not support this. + * + * @param dbName is the name of the database to select + * @return true or false + */ + function SelectDB($dbName) + {return false;} + + + /** + * Will select, getting rows from $offset (1-based), for $nrows. + * This simulates the MySQL "select * from table limit $offset,$nrows" , and + * the PostgreSQL "select * from table limit $nrows offset $offset". Note that + * MySQL and PostgreSQL parameter ordering is the opposite of the other. + * eg. + * SelectLimit('select * from table',3); will return rows 1 to 3 (1-based) + * SelectLimit('select * from table',3,2); will return rows 3 to 5 (1-based) + * + * Uses SELECT TOP for Microsoft databases, and FIRST_ROWS CBO hint for Oracle 8+ + * BUG: Currently SelectLimit fails with $sql with LIMIT or TOP clause already set + * + * @param sql + * @param [offset] is the row to start calculations from (1-based) + * @param [rows] is the number of rows to get + * @param [inputarr] array of bind variables + * @param [arg3] is a private parameter only used by jlim + * @param [secs2cache] is a private parameter only used by jlim + * @return the recordset ($rs->databaseType == 'array') + */ + function &SelectLimit($sql,$nrows=-1,$offset=-1, $inputarr=false,$arg3=false,$secs2cache=0) + { + if ($this->hasTop && $nrows > 0 && $offset <= 0) { + // suggested by Reinhard Balling. Access requires top after distinct + $sql = eregi_replace( + '(^select[\\t\\n ]*(distinct|distinctrow )?)','\\1 top '.$nrows.' ',$sql); + if ($secs2cache>0) return $this->CacheExecute($secs2cache, $sql,$inputarr,$arg3); + else return $this->Execute($sql,$inputarr,$arg3); + } else if ($this->dataProvider == 'oci8') { + $sql = eregi_replace('^select','SELECT /*+FIRST_ROWS*/',$sql); + if ($offset <= 0) { + /*if (preg_match('/\bwhere\b/i',$sql)) { + $sql = preg_replace('/where/i',"where rownum <= $nrows and ",$sql); + } else*/ + // doesn't work becoz sort is after rownum is executed - also preg_replace + // is a heuristic that might not work if there is an or + $sql = "select * from ($sql) where rownum <= :lens_sellimit_rownum"; + $inputarr['lens_sellimit_rownum'] = $nrows; + }/* else { + # THIS DOES NOT WORK -- WHY? + $sql = "select * from ($sql) where rownum between :lens_sellimit_rownum1 and :lens_sellimit_rownum2"; + $end = $offset+$nrows; + if ($offset < $end) { + $inputarr['lens_sellimit_rownum1'] = $offset; + $inputarr['lens_sellimit_rownum2'] = $end; + } else { + $inputarr['lens_sellimit_rownum1'] = $end; + $inputarr['lens_sellimit_rownum2'] = $offset; + } + $offset = -1; + $this->debug=true; + }*/ + } + if ($secs2cache>0) $rs = &$this->CacheExecute($secs2cache,$sql,$inputarr,$arg3); + else $rs = &$this->Execute($sql,$inputarr,$arg3); + if ($rs && !$rs->EOF) { + return $this->_rs2rs($rs,$nrows,$offset); + } + //print_r($rs); + return $rs; + } + + /** + * Convert recordset to an array recordset + * input recordset's cursor should be at beginning, and + * old $rs will be closed. + * + * @param rs the recordset to copy + * @param [nrows] number of rows to retrieve (optional) + * @param [offset] offset by number of rows (optional) + * @return the new recordset + */ + function &_rs2rs(&$rs,$nrows=-1,$offset=-1) + { + + $arr = &$rs->GetArrayLimit($nrows,$offset); + $flds = array(); + for ($i=0, $max=$rs->FieldCount(); $i < $max; $i++) + $flds[] = &$rs->FetchField($i); + + $rs->Close(); + $rs2 = new ADORecordSet_array(); + + $rs2->InitArrayFields($arr,$flds); + return $rs2; + } + + /** + * Return first element of first row of sql statement. Recordset is disposed + * for you. + * + * @param sql SQL statement + * @param [inputarr] input bind array + */ + function GetOne($sql,$inputarr=false) + { + $rs = $this->Execute($sql,$inputarr); + if ($rs && !$rs->EOF) { + $rs->Close(); + return $rs->fields[0]; + } + return false; + } + + /** + * Return all rows. Compat with PEAR DB + * + * @param sql SQL statement + * @param [inputarr] input bind array + */ + function GetAll($sql,$inputarr=false) + { + $rs = $this->Execute($sql,$inputarr); + if (!$rs) + if (defined('ADODB_PEAR')) return ADODB_PEAR_Error(); + else return false; + return $rs->GetArray(); + } + + /** + * Return one row of sql statement. Recordset is disposed for you. + * + * @param sql SQL statement + * @param [inputarr] input bind array + */ + function GetRow($sql,$inputarr=false) + { + $rs = $this->Execute($sql,$inputarr); + if ($rs && !$rs->EOF) { + $rs->Close(); + return $rs->fields; + } + return false; + } + + + + /** + * Will select, getting rows from $offset (1-based), for $nrows. + * This simulates the MySQL "select * from table limit $offset,$nrows" , and + * the PostgreSQL "select * from table limit $nrows offset $offset". Note that + * MySQL and PostgreSQL parameter ordering is the opposite of the other. + * eg. + * CacheSelectLimit(15,'select * from table',3); will return rows 1 to 3 (1-based) + * CacheSelectLimit(15,'select * from table',3,2); will return rows 3 to 5 (1-based) + * + * BUG: Currently CacheSelectLimit fails with $sql with LIMIT or TOP clause already set + * + * @param secs2cache seconds to cache data, set to 0 to force query + * @param sql + * @param [offset] is the row to start calculations from (1-based) + * @param [nrows] is the number of rows to get + * @param [inputarr] array of bind variables + * @param [arg3] is a private parameter only used by jlim + * @return the recordset ($rs->databaseType == 'array') + */ + function &CacheSelectLimit($secs2cache,$sql,$nrows=-1,$offset=-1,$inputarr=false, $arg3=false) + { + return $this->SelectLimit($sql,$nrows,$offset,$inputarr,$arg3,$secs2cache); + } + + function CacheFlush($sql) + { + $f = $this->_gencachename($sql); + adodb_write_file($f,''); + @unlink($f); + } + + function _gencachename($sql) + { + global $ADODB_CACHE_DIR; + + return $ADODB_CACHE_DIR.'/adodb_'.md5($sql.$this->databaseType.$this->database.$this->user).'.cache'; + } + /** + * Execute SQL, caching recordsets. + * + * @param secs2cache seconds to cache data, set to 0 to force query + * @param sql SQL statement to execute + * @param [inputarr] holds the input data to bind to + * @param [arg3] reserved for john lim for future use + * @return RecordSet or false + */ + function &CacheExecute($secs2cache,$sql,$inputarr=false,$arg3=false) + { + // cannot cache if $inputarr set + if ($inputarr) return $this->Execute($sql, $inputarr, $arg3); + + $md5file = $this->_gencachename($sql); + $err = ''; + + if ($secs2cache > 0)$rs = &csv2rs($md5file,$err,$secs2cache); + else { + $err='Timeout 1'; + $rs = false; + } + + if (!$rs) { + if ($this->debug) print " $md5file cache failure: $err
"; + $rs = &$this->Execute($sql,$inputarr,$arg3); + if ($rs) { + $eof = $rs->EOF; + $rs = &$this->_rs2rs($rs); + $txt = &rs2csv($rs,false,$sql); + + if (!adodb_write_file($md5file,$txt,$this->debug) && $this->debug) print ' Cache write error
'; + if ($rs->EOF && !$eof) { + $rs = &csv2rs($md5file,$err); + } + } else + @unlink($md5file); + }else if ($this->debug){ + $ttl = $rs->timeCreated + $secs2cache - time(); + print " $md5file success ttl=$ttl
"; + } + return $rs; + } + + /** + * Generates an Update Query based on an existing recordset. + * $arrFields is an associative array of fields with the value + * that should be assigned. + * + * Note: This function should only be used on a recordset + * that is run against a single table. + * + * "Jonathan Younger" + */ + function GetUpdateSQL(&$rs, $arrFields,$forceUpdate=false) + { + if (!$rs) { + printf(ADODB_BAD_RS,'GetUpdateSQL'); + return false; + } + + // Get the table name from the existing query. + eregi("FROM ([]0-9a-z_\[-]*)", $rs->sql, $tableName); + + // Get the full where clause excluding the word "WHERE" from + // the existing query. + eregi("WHERE ([]0-9a-z=' \(\)\[\t\r\n_-]*)", $rs->sql, $whereClause); + + // updateSQL will contain the full update query when all + // processing has completed. + $updateSQL = "UPDATE " . $tableName[1] . " SET "; + + // Loop through all of the fields in the recordset + for ($i=0, $max=$rs->FieldCount(); $i < $max; $i++) { + + // Get the field from the recordset + $field = $rs->FetchField($i); + + // If the recordset field is one + // of the fields passed in then process. + if (isset($arrFields[$field->name])) { + + // If the existing field value in the recordset + // is different from the value passed in then + // go ahead and append the field name and new value to + // the update query. + + if ($forceUpdate || strcmp($rs->fields[$i], $arrFields[$field->name])) { + // Set the counter for the number of fields that will be updated. + $fieldUpdatedCount++; + + // Based on the datatype of the field + // Format the value properly for the database + switch($rs->MetaType($field->type)) { + case "C": + case "X": + $updateSQL .= $field->name . " = " . $this->qstr($arrFields[$field->name]) . ", "; + break; + case "D": + $updateSQL .= $field->name . " = " . $this->DBDate($arrFields[$field->name]) . ", "; + break; + case "T": + $updateSQL .= $field->name . " = " . $this->DBTimeStamp($arrFields[$field->name]) . ", "; + break; + default: + $updateSQL .= $field->name . " = " . $arrFields[$field->name] . ", "; + break; + }; + }; + }; + }; + + // If there were any modified fields then build the rest of the update query. + if ($fieldUpdatedCount > 0 || $forceUpdate) { + // Strip off the comma and space on the end of the update query. + $updateSQL = substr($updateSQL, 0, -2); + + // If the recordset has a where clause then use that same where clause + // for the update. + if ($whereClause[1]) $updateSQL .= " WHERE " . $whereClause[1]; + + return $updateSQL; + } else { + return false; + }; + } + + + /** + * Generates an Insert Query based on an existing recordset. + * $arrFields is an associative array of fields with the value + * that should be assigned. + * + * Note: This function should only be used on a recordset + * that is run against a single table. + */ + function GetInsertSQL(&$rs, $arrFields) + { + if (!$rs) { + printf(ADODB_BAD_RS,'GetInsertSQL'); + return false; + } + // Get the table name from the existing query. + eregi("FROM ([0-9a-z_-]*)", $rs->sql, $tableName); + + // Loop through all of the fields in the recordset + for ($i=0, $max=$rs->FieldCount(); $i < $max; $i++) { + + // Get the field from the recordset + $field = $rs->FetchField($i); + // If the recordset field is one + // of the fields passed in then process. + if (isset($arrFields[$field->name])) { + + // Set the counter for the number of fields that will be inserted. + $fieldInsertedCount++; + + // Get the name of the fields to insert + $fields .= $field->name . ", "; + + // Based on the datatype of the field + // Format the value properly for the database + switch($rs->MetaType($field->type)) { + case "C": + case "X": + $values .= $this->qstr($arrFields[$field->name]) . ", "; + break; + case "D": + $values .= $this->DBDate($arrFields[$field->name]) . ", "; + break; + case "T": + $values .= $this->DBTimeStamp($arrFields[$field->name]) . ", "; + break; + default: + $values .= $arrFields[$field->name] . ", "; + break; + }; + }; + }; + + // If there were any inserted fields then build the rest of the insert query. + if ($fieldInsertedCount > 0) { + + // Strip off the comma and space on the end of both the fields + // and their values. + $fields = substr($fields, 0, -2); + $values = substr($values, 0, -2); + + // Append the fields and their values to the insert query. + $insertSQL = "INSERT INTO " . $tableName[1] . " ( $fields ) VALUES ( $values )"; + + return $insertSQL; + + } else { + return false; + }; + } + + /** + * Usage: + * UpdateBlob('TABLE', 'COLUMN', $var, 'ID=1', 'BLOB'); + * + * $blobtype supports 'BLOB' and 'CLOB' + * + * $conn->Execute('INSERT INTO blobtable (id, blobcol) VALUES (1, null)'); + * $conn->UpdateBlob('blobtable','blobcol',$blob,'id=1'); + */ + function UpdateBlob($table,$column,$val,$where,$blobtype='BLOB') + { + $sql = "UPDATE $table SET $column=".$this->qstr($val)." where $where"; + $rs = $this->Execute($sql); + + $rez = !empty($rs); + if ($rez) $rs->Close(); + return $rez; + } + + /** + * Usage: + * UpdateBlob('TABLE', 'COLUMN', $var, 'ID=1', 'CLOB'); + * + * $conn->Execute('INSERT INTO clobtable (id, clobcol) VALUES (1, null)'); + * $conn->UpdateClob('clobtable','clobcol',$clob,'id=1'); + */ + function UpdateClob($table,$column,$val,$where) + { + return $this->UpdateBlob($table,$column,$val,$where,'CLOB'); + } + + + function BlankRecordSet($id=false) + { + $rsclass = "ADORecordSet_".$this->databaseType; + return new $rsclass($id); + } + + + /** + * Close Connection + */ + function Close() + { + return $this->_close(); + + // "Simon Lee" reports that persistent connections need + // to be closed too! + //if ($this->_isPersistentConnection != true) return $this->_close(); + //else return true; + } + + /** + * Begin a Transaction. Must be followed by CommitTrans() or RollbackTrans(). + * + * @return true if succeeded or false if database does not support transactions + */ + function BeginTrans() {return false;} + + /** + * If database does not support transactions, always return true as data always commited + * + * @return true/false. + */ + function CommitTrans() + { return true;} + + /** + * If database does not support transactions, rollbacks always fail, so return false + * + * @return true/false. + */ + function RollbackTrans() + { return false;} + + + /** + * return the databases that the driver can connect to. + * Some databases will return an empty array. + * + * @return an array of database names. + */ + function &MetaDatabases() + {return false;} + + /** + * @return array of tables for current database. + */ + function &MetaTables() + { + if ($this->metaTablesSQL) { + $rs = $this->Execute($this->metaTablesSQL); + if ($rs === false) return false; + $arr = $rs->GetArray(); + $arr2 = array(); + for ($i=0; $i < sizeof($arr); $i++) { + $arr2[] = $arr[$i][0]; + } + $rs->Close(); + return $arr2; + } + return false; + } + + /** + * List columns in a database as an array of ADOFieldObjects. + * See top of file for definition of object. + * + * @params table table name to query + * + * @return array of ADOFieldObjects for current table. + */ + function &MetaColumns($table) + { + + if (!empty($this->metaColumnsSQL)) { + + $rs = $this->Execute(sprintf($this->metaColumnsSQL,strtoupper($table))); + if ($rs === false) return false; + + $retarr = array(); + while (!$rs->EOF) { //print_r($rs->fields); + $fld = new ADOFieldObject(); + $fld->name = $rs->fields[0]; + $fld->type = $rs->fields[1]; + $fld->max_length = $rs->fields[2]; + $retarr[strtoupper($fld->name)] = $fld; + + $rs->MoveNext(); + } + $rs->Close(); + return $retarr; + } + return false; + } + + + /** + * Different SQL databases used different methods to combine strings together. + * This function provides a wrapper. + * + * @param s variable number of string parameters + * + * Usage: $db->Concat($str1,$str2); + * + * @return concatenated string + */ + function Concat() + { + $arr = func_get_args(); + return implode($this->concat_operator, $arr); + } + + /** + * Converts a date "d" to a string that the database can understand. + * + * @param d a date in Unix date time format. + * + * @return date string in database date format + */ + function DBDate($d) + { + // note that we are limited to 1970 to 2038 + return date($this->fmtDate,$d); + } + + /** + * Converts a timestamp "ts" to a string that the database can understand. + * + * @param ts a timestamp in Unix date time format. + * + * @return timestamp string in database timestamp format + */ + function DBTimeStamp($ts) + { + return date($this->fmtTimeStamp,$ts); + } + + /** + * Converts a timestamp "ts" to a string that the database can understand. + * An example is $db->qstr("Don't bother",magic_quotes_runtime()); + * + * @param s the string to quote + * @param [magic_quotes] if $s is GET/POST var, set to get_magic_quotes_gpc(). + * This undoes the stupidity of magic quotes for GPC. + * + * @return quoted string to be sent back to database + */ + function qstr($s,$magic_quotes=false) + { + $nofixquotes=false; + if (!$magic_quotes) { + + if ($this->replaceQuote[0] == '\\'){ + $s = str_replace('\\','\\\\',$s); + } + return "'".str_replace("'",$this->replaceQuote,$s)."'"; + } + + // undo magic quotes for " + $s = str_replace('\\"','"',$s); + + if ($this->replaceQuote == "\\'") // ' already quoted, no need to change anything + return "'$s'"; + else {// change \' to '' for sybase/mssql + $s = str_replace('\\\\','\\',$s); + return "'".str_replace("\\'",$this->replaceQuote,$s)."'"; + } + } + + + /** + * Will select the supplied $page number from a recordset, given that it is paginated in pages of + * $nrows rows per page. It also saves two boolean values saying if the given page is the first + * and/or last one of the recordset. Added by Iván Oliva to provide recordset pagination. + * + * See readme.htm#ex8 for an example of usage. + * + * @param sql + * @param nrows is the number of rows per page to get + * @param page is the page number to get (1-based) + * @param [inputarr] array of bind variables + * @param [arg3] is a private parameter only used by jlim + * @param [secs2cache] is a private parameter only used by jlim + * @return the recordset ($rs->databaseType == 'array') + * + * NOTE: phpLens uses a different algorithm and does not use PageExecute(). + * + */ + +function &PageExecute($sql, $nrows, $page, $inputarr=false, $arg3=false, $secs2cache=0) +{ + $atfirstpage = false; + $atlastpage = false; + + if (!isset($page) || $page <= 1) { // If page number <= 1, then we are at the first page + $page = 1; + $atfirstpage = true; + } + if ($nrows <= 0) $nrows = 10; // If an invalid nrows is supplied, we assume a default value of 10 rows per page + + // ***** Here we check whether $page is the last page or whether we are trying to retrieve a page number greater than + // the last page number. + $pagecounter = $page + 1; + $pagecounteroffset = ($pagecounter * $nrows) - $nrows; + if ($secs2cache>0) $rstest = &$this->CacheSelectLimit($secs2cache, $sql, $nrows, $pagecounteroffset, $inputarr, $arg3); + else $rstest = &$this->SelectLimit($sql, $nrows, $pagecounteroffset, $inputarr, $arg3, $secs2cache); + if ($rstest) { + while ($rstest->EOF && $pagecounter>0) { + $atlastpage = true; + $pagecounter--; + $pagecounteroffset = $pagecounter * ($nrows-1); + if ($secs2cache>0) $rstest = &$this->CacheSelectLimit($secs2cache, $sql, $nrows, $pagecounteroffset, $inputarr, $arg3); + else $rstest = &$this->SelectLimit($sql, $nrows, $pagecounteroffset, $inputarr, $arg3, $secs2cache); + } + $rstest->Close(); + } + if ($atlastpage) { // If we are at the last page or beyond it, we are going to retrieve it + $page = $pagecounter; + if ($page == 1) $atfirstpage = true; // We have to do this again in case the last page is the same as the first + //... page, that is, the recordset has only 1 page. + } + + // We get the data we want + $offset = $page * ($nrows-1); + if ($secs2cache > 0) $rsreturn = &$this->CacheSelectLimit($secs2cache, $sql, $nrows, $offset, $inputarr, $arg3); + else $rsreturn = &$this->SelectLimit($sql, $nrows, $offset, $inputarr, $arg3, $secs2cache); + + // Before returning the RecordSet, we set the pagination properties we need + if ($rsreturn) { + $rsreturn->AbsolutePage($page); + $rsreturn->AtFirstPage($atfirstpage); + $rsreturn->AtLastPage($atlastpage); + } + return $rsreturn; + + } + + + /** + * Will select the supplied $page number from a recordset, given that it is paginated in pages of + * $nrows rows per page. It also saves two boolean values saying if the given page is the first + * and/or last one of the recordset. Added by Iván Oliva to provide recordset pagination. + * + * @param secs2cache seconds to cache data, set to 0 to force query + * @param sql + * @param nrows is the number of rows per page to get + * @param page is the page number to get (1-based) + * @param [inputarr] array of bind variables + * @param [arg3] is a private parameter only used by jlim + * @return the recordset ($rs->databaseType == 'array') + */ + function &CachePageExecute($secs2cache, $sql, $nrows, $page,$inputarr=false, $arg3=false) { + return $this->PageExecute($sql, $nrows, $page, $inputarr, $arg3,$secs2cache); + } + +} // end class ADOConnection + + + + //============================================================================================== + + /** + * Used by ADORecordSet->FetchObj() + */ + class ADOFetchObj { + }; + + + /** + * Lightweight recordset when there are no records to be returned + */ + class ADORecordSet_empty + { + var $dataProvider = 'empty'; + var $EOF = true; + var $_numOfRows = 0; + var $fields = false; + var $f = false; + function RowCount() {return 0;} + function RecordCount() {return 0;} + function Close(){return true;} + } + + /** + * RecordSet class that represents the dataset returned by the database. + * To keep memory overhead low, this class holds only the current row in memory. + * No prefetching of data is done, so the RecordCount() can return -1 (not known). + */ + class ADORecordSet { + /* + * public variables + */ + var $dataProvider = "native"; + var $fields = false; // holds the current row data + var $f = false; + var $blobSize = 64; // any varchar/char field this size or greater is treated as a blob + // in other words, we use a text area for editting. + var $canSeek = false; // indicates that seek is supported + var $sql; // sql text + var $EOF = false; /* Indicates that the current record position is after the last record in a Recordset object. */ + + var $emptyTimeStamp = ' '; // what to display when $time==0 + var $emptyDate = ' '; // what to display when $time==0 + var $debug = false; + var $timeCreated=0; // datetime in Unix format rs created -- for cached recordsets + + var $bind = false; // used by Fields() to hold array - should be private? + var $fetchMode; // default fetch mode + /* + * private variables + */ + var $_numOfRows = -1; + var $_numOfFields = -1; + var $_queryID = -1; /* This variable keeps the result link identifier. */ + var $_currentRow = -1; /* This variable keeps the current row in the Recordset. */ + var $_closed = false; /* has recordset been closed */ + var $_inited = false; /* Init() should only be called once */ + var $_obj; /* Used by FetchObj */ + var $_names; + + var $_currentPage = -1; /* Added by Iván Oliva to implement recordset pagination */ + var $_atFirstPage = false; /* Added by Iván Oliva to implement recordset pagination */ + var $_atLastPage = false; /* Added by Iván Oliva to implement recordset pagination */ + + + /** + * Constructor + * + * @param queryID this is the queryID returned by ADOConnection->_query() + * + */ + function ADORecordSet(&$queryID) + { + $this->_queryID = $queryID; + $this->f = &$this->fields; + } + + function Init() + { + if ($this->_inited) return; + $this->_inited = true; + + if ($this->_queryID) @$this->_initrs(); + else { + $this->_numOfRows = 0; + $this->_numOfFields = 0; + } + if ($this->_numOfRows != 0 && $this->_numOfFields && $this->_currentRow == -1) { + $this->_currentRow = 0; + $this->EOF = ($this->_fetch() === false); + } else + $this->EOF = true; + } + /** + * Generate a + * @param [defstr] the value to hilite. Use an array for multiple hilites for listbox. + * @param [blank1stItem] true to leave the 1st item in list empty + * @param [multiple] true for listbox, false for popup + * @param [size] #rows to show for listbox. not used by popup + * @param [selectAttr] additional attributes to defined for "; + if ($blank1stItem) $s .= "\n"; + + if ($this->FieldCount() > 1) $hasvalue=true; + else $compareFields0 = true; + + while(!$this->EOF) { + $zval = trim($this->fields[0]); + $selected = trim($this->fields[$compareFields0 ? 0 : 1]); + + if ($blank1stItem && $zval=="") { + $this->MoveNext(); + continue; + } + if ($hasvalue) + $value = 'value="'.htmlspecialchars(trim($this->fields[1])).'"'; + + + if (is_array($defstr)) { + + if (in_array($selected,$defstr)) + $s .= "'; + else + $s .= "\n'; + } + else { + if (strcasecmp($selected,$defstr)==0) + $s .= "'; + else + $s .= "\n'; + } + $this->MoveNext(); + } // while + + return $s ."\n\n"; + } + /** + * Generate a +