Klasse DB_Sql {
var $Debug = false;
var $Home = "/u01/app/Oracle/PRoduct/8.0.4";
var $Remote = 1;
/* Diese Abfrage wird direkt nach der ersten Verbindung gesendet
Beispiel:
var $ConnectQuery="ALTER session SET nls_date_lingual=german nls_date_format='DD.MM.RRRR'";
-> Legen Sie das Datumsformat für diese Sitzung fest. Dies ist in Ordnung, wenn Ihre Ora-Rolle
ist
kann nicht geändert werden */
var $ConnectQuery='';
/* Aufgrund eines seltsamen Fehlers mit Oracle 8.0.5, Apache und php3.0.6
Sie müssen die ENV nicht festlegen – auf meinem System Apache
wird sich in einen Zombie verwandeln, wenn ich dies nicht auf FALSE setze!
Stattdessen habe ich diese ENV-Variablen vor dem Start von Apache festgelegt.
Wenn Sie unsicher sind, probieren Sie es aus, ob es funktioniert. */
var $OraPutEnv = true;
var $Database = "";
var $User = "";
var $PassWord = "";
var $Link_ID = 0;
var $Query_ID = 0;
var $Record = array();
var $Row;
var $Errno = 0;
var $Error = "";
var $ora_no_next_fetch=false;
/* der Vollständigkeit halber aus db_MySQL kopiert */
/* public: Identifikationskonstante. Ändere das niemals. */
var $type = "oracle";
var $revision = "Revision: 1.3";
var $Halt_On_Error = "yes"; ## „Ja“ (mit Meldung anhalten), „Nein“ (Fehler stillschweigend ignorieren), „Bericht“ (Fehler ignorieren, aber eine Warnung ausgeben)
/* public: Konstruktor */
Funktion DB_Sql($query = "") {
$this->query($query);
}
/* öffentlich: einige triviale Berichterstattung */
Funktion link_id() {
return $this->Link_ID;
}
Funktion query_id() {
return $this->Query_ID;
}
Funktion connect() {
## siehe oben, warum wir das tun
if ($this->OraPutEnv) {
PutEnv("ORACLE_SID=$this->Database");
PutEnv("ORACLE_HOME=$this->Home");
}
if ( 0 == $this->Link_ID ) {
if($this->Debug) {
printf("
Connect()ing to $this->Database...
n");
}
if($this->Remote) {
if($this->Debug) {
printf("
connect() $this->User/******@$this->Database
n");
}
$this->Link_ID=ora_plogon
("$this->Benutzer/$this->Passwort@$this->Datenbank","");
/****************** (Kommentar von SSilk)
Das funktioniert auf meinem System nicht:
$this->Link_ID=ora_plogon
("$this->User@$this->Database.world","$this->Password");
***************/
} else {
if($this->Debug) {
printf("
connect() $this->Benutzer, $this->Passwort
n");
}
$this->Link_ID=ora_plogon("$this->Benutzer","$this->Passwort");
/* (Kommentar von SSilk: Ich weiß nicht, wie das funktionieren könnte, aber ich lasse es unberührt!) */
}
if($this->Debug) {
printf("
connect() Link_ID: $this->Link_ID
n");
}
if (!$this->Link_ID) {
$this->halt("connect() Link-ID == false " .
"($this->Link_ID), ora_plogon fehlgeschlagen");
} else {
//echo "commit on
";
ora_commiton($this->Link_ID);
}
if($this->Debug) {
printf("
connect() Habe die Link_ID erhalten: $this->Link_ID
n");
}
## Connect-Abfrage ausführen
if ($this->ConnectQuery) {
$this->query($this->ConnectQuery);
}
}
}
## Um die Anzahl der Cursor pro System/Benutzer zu erhöhen, bearbeiten Sie das
## init.ora-Datei und erhöhen Sie den Parameter max_open_cursors. Deins ist auf
## der Standardwert, 100 pro Benutzer.
## Wir haben versucht, das Verhalten von query() so zu ändern, dass es
versucht
##, um Cursor zu sichern, aber seien Sie andererseits vorsichtig damit, dass Sie
## kein altes Ergebnis verwenden.
##
## Sie können ->disconnect() auch ausgiebig nutzen!
## Die nicht verwendeten QueryIDs werden manchmal recycelt.
Funktionsabfrage($Query_String)
{
/* Bitte keine leere Abfrage. */
if (empty($Query_String))
{
0 zurückgeben;
}
$this->connect();
$this->lastQuery=$Query_String;
if (!$this->Query_ID) {
$this->Query_ID= ora_open($this->Link_ID);
}
if($this->Debug) {
printf("Debug: query = %s
n", $Query_String);
printf("
Debug: Query_ID: %d
n", $this->Query_ID);
}
if(!@ora_parse($this->Query_ID,$Query_String)) {
$this->Errno=ora_errorcode($this->Query_ID);
$this->Error=ora_error($this->Query_ID);
$this->halt("
ora_parse() failed:
$Query_String
Snap & paste this to sqlplus!");
} elseif (!@ora_exec($this->Query_ID)) {
$this->Errno=ora_errorcode($this->Query_ID);
$this->Error=ora_error($this->Query_ID);
$this->halt("
n$Query_Stringn
Einrasten und in sqlplus einfügen!");
}
$this->Row=0;
if(!$this->Query_ID) {
$this->halt("Ungültiges SQL: ".$Query_String);
}
return $this->Query_ID;
}
Funktion next_record() {
if (!$this->ora_no_next_fetch &&
0 == ora_fetch($this->Query_ID)) {
if ($this->Debug) {
printf("
next_record(): ID: %d Zeile: %d
n",
$this->Query_ID,$this->Row 1);
// weitere Informationen zu $this->Row 1 ist $this->num_rows(),
// funktioniert aber nicht in allen Fällen (komplizierte Auswahl)
// und es ist hier sehr langsam
}
$this->Row =1;
$errno=ora_errorcode($this->Query_ID);
if(1403 == $errno) { # 1043 bedeutet, dass keine weiteren Datensätze gefunden wurden
$this->Errno=0;
$this->Error="";
$this->disconnect();
$stat=0;
} else {
$this->Error=ora_error($this->Query_ID);
$this->Errno=$errno;
if($this->Debug) {
printf("
%d Fehler: %s",
$this->Errno,
$this->Error);
}
$stat=0;
}
} else {
$this->ora_no_next_fetch=false;
for($ix=0;$ix
$col=strtolower(ora_columnname($this->Query_ID,$ix));
$value=ora_getcolumn($this->Query_ID,$ix);
$this->Record[ "$col" ] = $value;
$this->Record[ $ix ] = $value;
#DBG echo"[$col]: $value
n";
}
$stat=1;
}
return $stat;
}
##seek() funktioniert nur für $pos - 1 und $pos
## Vielleicht mache ich eine eigene Implementierung, aber meine
## Meiner Meinung nach sollte dies mit PHP3 erledigt werden
Funktion see($pos) {
if ($this->Row - 1 == $pos) {
$this->ora_no_next_fetch=true;
} elseif ($this->Row == $pos ) {
## nichts tun
} else {
$this->halt("Ungültige Suche(): Die Position kann nicht von der API verarbeitet werden.
".
„In dieser Version ist nur eine Suche bis zum letzten Element zulässig
“.
„Unterschied zu groß. Gesucht: $pos Aktuelle Position: $this->Row“);
}
if ($this->Debug) echo "
Debug: see = $pos
";
$this->Row=$pos;
}
Funktionssperre($table, $mode = "write") {
if ($mode == "write") {
$result = ora_do($this->Link_ID, "Tabelle $table im zeilenexklusiven Modus sperren");
} else {
$result = 1;
}
$result zurückgeben;
}
Funktion unlock() {
return ora_do($this->Link_ID, "commit");
}
// Wichtiger Hinweis: Diese Funktion funktioniert nicht mit Oracle-Datenbank-Links!
// Es steht Ihnen frei, eine bessere Methode zu erhalten. :)
Funktionsmetadaten($table,$full=false) {
$count = 0;
$id = 0;
$res = array();
/*
* Aufgrund von Kompatibilitätsproblemen mit Table haben wir das Verhalten geändert
* von Metadaten();
* Abhängig von $full geben Metadaten die folgenden Werte zurück:
*
* – full ist falsch (Standard):
* $result[]:
* [0]["table"] Tabellenname
* [0]["name"] Feldname
* [0]["type"] Feldtyp
* [0]["len"] Feldlänge
* [0]["flags"] Feldflags ("NOT NULL", "INDEX")
* [0]["Format"] Genauigkeit und Skala der Zahl (z. B. "10,2") oder leer
* [0]["index"] Name des Index (falls vorhanden)
* [0]["chars"] Anzahl der Zeichen (falls vorhanden)
*
* – voll ist wahr
* $result[]:
* ["num_fields"] Anzahl der Metadatensätze
* [0]["table"] Tabellenname
* [0]["name"] Feldname
* [0]["type"] Feldtyp
* [0]["len"] Feldlänge
* [0]["flags"] Feldflags ("NOT NULL", "INDEX")
* [0]["format"] Genauigkeit und Skala der Zahl (z. B. "10,2") oder leer
* [0]["index"] Name des Index (falls vorhanden)
* [0]["chars"] Anzahl der Zeichen (falls vorhanden)
* [0]["php_type"] der entsprechende PHP-Typ
* [0]["php_subtype"] der Subtyp des PHP-Typs
* ["meta"][Feldname] Index des Feldes mit dem Namen "Feldname"
* Dies könnte verwendet werden, wenn Sie den Namen, aber keine Indexnummer haben – sehr schnell
* Test: if (isset($result['meta']['myfield'])) {} ...
*/
$this->connect();
## Dies ist ein RIGHT OUTER JOIN: „( )“, wenn Sie sehen möchten, was
## Diese Abfrageergebnisse versuchen Folgendes:
## $table = neue Tabelle; $db = new my_DB_Sql; # du musst
machen
## # deine eigene Klasse
## $table->show_results($db->query(see query vvvvvv))
##
$this->query("SELECT T.table_name,T.column_name,T.data_type,".
„T.data_length,T.data_precision,T.data_scale,T.nullable,“.
„T.char_col_decl_length,I.index_name“.
„FROM ALL_TAB_COLUMNS T,ALL_IND_COLUMNS I“.
„WHERE T.column_name=I.column_name ()“.
„ AND T.table_name=I.table_name ( )“.
" AND T.table_name=UPPER('$table') ORDER BY T.column_id");
$i=0;
while ($this->next_record()) {
$res[$i]["table"] = $this->Record[table_name];
$res[$i]["name"] = strtolower($this->Record[column_name]);
$res[$i]["type"] = $this->Record[data_type];
$res[$i]["len"] = $this->Record[data_length];
if ($this->Record[index_name]) $res[$i]["flags"] = "INDEX ";
$res[$i]["flags"] .= ( $this->Record[nullable] == 'N') ? '': 'NICHT NULL';
$res[$i]["format"]= (int)$this->Record[data_precision].",".
(int)$this->Record[data_scale];
if ("0,0"==$res[$i]["format"]) $res[$i]["format"]='';
$res[$i]["index"] = $this->Record[index_name];
$res[$i]["chars"] = $this->Record[char_col_decl_length];
if ($full) {
$j=$res[$i]["name"];
$res["meta"][$j] = $i;
$res["meta"][strtoupper($j)] = $i;
switch ($res[$i]["type"]) {
Fall „VARCHAR2“:
Fall „VARCHAR“ :
case „CHAR“ :
$res["php_type"]="string";
$res["php_subtype"]="";
brechen;
Fall „DATUM“ :
$res["php_type"]="string";
$res["php_subtype"]="Datum";
brechen;
Fall „BLOB“ :
Fall „CLOB“ :
Fall „BFILE“:
Fall „RAW“:
Fall „LONG“ :
Fall „LONG RAW“:
$res["php_type"]="string";
$res["php_subtype"]="blob";
brechen;
case „NUMBER“ :
if ($res[$i]["format"]) {
$res["php_type"]="double";
$res["php_subtype"]="";
} else {
$res["php_type"]="int";
$res["php_subtype"]="";
}
brechen;
Standard:
$this->halt("metadata(): Typ ist kein gültiger Wert: '$res[$i][type]'");
brechen;
}
}
if ($full) $res["meta"][$res[$i]["name"]] = $i;
$i ;
}
if ($full) $res["num_fields"]=$i;
# $this->disconnect();
return $res;
}
## DIESE FUNKTION IST NICHT GETESTET!
Funktion Affect_rows() {
if ($this->Debug) echo "
Debug: Affect_rows=". ora_numrows($this->Query_ID)."
";
return ora_numrows($this->Query_ID);
}
## Bekannte Fehler: Es funktioniert nicht für SELECT DISTINCT und alle
## andere Konstrukte, die von den resultierenden Zeilen abhängen.
## Sie müssen also *wirklich* jede von Ihnen gestellte Abfrage überprüfen, ob sie
ist
## wird damit funktionieren!
##
## Außerdem müssen Sie für einen qualifizierten Ersatz das
analysieren
## Auswahl, da dies fehlschlägt: „SELECT id, from FROM ...“).
## „from“ ist – soweit ich weiß ein Schlüsselwort in Oracle, also kann es
## darf nur auf diese Weise verwendet werden. Aber Sie wurden gewarnt.
Funktion num_rows() {
$curs=ora_open($this->Link_ID);
## Das ist der wichtige Teil und es ist auch der HACK!
if (eregi("^[[:space:]]*SELECT[[:space:]]",$this->lastQuery) )
{
# Das funktioniert bei allen?? Fälle, einschließlich SELECT DISTINCT-Fall.
# Wir wählen einfach count(*) aus dem ursprünglichen SQL-Ausdruck
aus
# und entfernen Sie ORDER BY (falls vorhanden), um die Geschwindigkeit zu erhöhen
# Ich mag auch reguläre Ausdrücke ;-)))
$q = sprintf("SELECT COUNT(*) FROM (%s)",
@eregi_Replace("ORDER[[:space:]] BY[^)]*()*)", "1",
$this->lastQuery)
);
# funktioniert auch für Unterauswahlen:
# if (eregi("[[:space:]] FROM([[:space:]] .*[[:space:]] FROM)",$this->lastQuery,$r))
# $areplace=$r[1];
# $q=eregi_Replace("^[[:space:]]*SELECT[[:space:]] ".
# ".*[[:space:]] FROM",
# "SELECT COUNT(*) FROM$areplace",
# $this->lastQuery);
if ($this->Debug) echo "
Debug: num_rows: $q
";
ORA_parse($curs,$q);
ORA_exec($curs);
ORA_fetch($curs);
$result = ORA_getcolumn($curs,0);
ORA_close($curs);
if ($this->Debug)
{
echo "
Debug: ID ".$this->QueryID.
" num_rows=". $result ."
";
}
$result zurückgeben;
}
sonst
{
$this->halt("Letzte Abfrage war keine SELECT: $this->lastQuery");
}
}
Funktion num_fields() {
if ($this->Debug) echo "
Debug: num_fields=". ora_numcols($this->Query_ID) . "
";
return ora_numcols($this->Query_ID);
}
Funktion nf() {
return $this->num_rows();
}
Funktion np() {
print $this->num_rows();
}
Funktion f($Name) {
return $this->Record[$Name];
}
Funktion p($Name) {
print $this->Record[$Name];
}
/* öffentlich: Sequenznummer */
Funktion nextid($seq_name)
{
$this->connect();
/* Unabhängige Abfrage_ID */
$Query_ID = ora_open($this->Link_ID);
if(!@ora_parse($Query_ID,"SELECT $seq_name.NEXTVAL FROM DUAL"))
{
// Es gibt noch keine solche Sequenz, dann erstelle sie
if(!@ora_parse($Query_ID,"CREATE SEQUENCE $seq_name")
||
!@ora_exec($Query_ID)
)
{
$this->halt("
nextid()-Funktion – Sequenz kann nicht erstellt werden");
0 zurückgeben;
}
@ora_parse($Query_ID,"SELECT $seq_name.NEXTVAL FROM DUAL");
}
if (!@ora_exec($Query_ID)) {
$this->halt("
ora_exec() failed:
nextID function");
}
if (@ora_fetch($Query_ID) ) {
$next_id = ora_getcolumn($Query_ID, 0);
}
sonst {
$next_id = 0;
}
if ( $Query_ID > 0 ) {
ora_close($Query_ID);
}
return $next_id;
}
Funktion „disconnect()“ {
if($this->Debug) {
echo "Debug: Verbindung zu $this->Query_ID...
n wird getrennt";
}
if ( $this->Query_ID < 1 ) {
echo "Warnung: connected(): ID $this->Query_IDn kann nicht freigegeben werden";
# zurückkehren();
}
ora_close($this->Query_ID);
$this->Query_ID=0;
}
/* privat: Fehlerbehandlung */
Funktion halt($msg) {
if ($this->Halt_On_Error == "no")
zurückkehren;
$this->haltmsg($msg);
if ($this->Halt_On_Error != "report")
die("Sitzung angehalten.");
}
Funktion haltmsg($msg) {
printf("
Datenbankfehler: %s
n", $msg);
printf("Oracle-Fehler: %s (%s)
n",
$this->Errno,
$this->Error);
}
Funktion table_names() {
$this->connect();
$this->query("
SELECT Tabellenname, Tabellenbereichsname
FROM user_tables");
$i=0;
while ($this->next_record())
{
$info[$i]["table_name"] =$this->Record["table_name"];
$info[$i]["tablespace_name"]=$this->Record["tablespace_name"];
$i ;
}
$info zurückgeben;
}
// Etwas Transaktionsunterstützung
// Methoden werden in ct_oracle.inc
verwendet
Funktion begin_transaction()
{
$this->connect();
// Jetzt Autocommit deaktivieren
Ora_CommitOff($this->Link_ID);
if ($this->Debug)
{
print „TRANSAKTION BEGINNEN
“;
}
}
Funktion end_transaction()
{
if ($this->Debug)
{
print „TRANSAKTION BEGINNEN
“;
}
$res = 1;
if(!@Ora_Commit($this->Link_ID))
{
Ora_CommitOn($this->Link_ID);
$this->halt("Transaktion konnte nicht abgeschlossen werden");
$res = 0;
}
// Autocommit erneut aktivieren
Ora_CommitOn($this->Link_ID);
if ($this->Debug)
{
print „TRANSAKTION ENDE: $res
“;
}
return $res;
}
}
?>