Subversion-Projekte lars-tiefland.prado

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
<?php
2
/**
3
 * TOracleMetaData class file.
4
 *
5
 * @author Marcos Nobre <marconobre[at]gmail[dot]com>
6
 * @link http://www.pradosoft.com/
7
 * @copyright Copyright &copy; 2005-2008 PradoSoft
8
 * @license http://www.pradosoft.com/license/
9
 * @version $Id: TOracleMetaData.php 2482 2008-07-30 02:07:13Z knut $
10
 * @package System.Data.Common.Oracle
11
 */
12
 
13
 
14
Prado::using('System.Data.Common.Oracle.TOracleTableInfo');
15
Prado::using('System.Data.Common.Oracle.TOracleTableColumn');
16
 
17
/**
18
 * TOracleMetaData loads Oracle database table and column information.
19
 *
20
 * @author Marcos Nobre <marconobre[at]gmail[dot]com>
21
 * @version $Id: TOracleMetaData.php 2482 2008-07-30 02:07:13Z knut $
22
 * @package System.Data.Common.Oracle
23
 * @since 3.1
24
 */
25
class TOracleMetaData extends TComponent
26
{
27
	private $_tableInfoCache=array();
28
	private $_connection;
29
	private $_defaultSchema = 'system';
30
 
31
	/**
32
	 * @param TDbConnection database connection.
33
	 */
34
	public function __construct($conn)
35
	{
36
		$this->_connection=$conn;
37
	}
38
 
39
	/**
40
	 * @return TDbConnection database connection.
41
	 */
42
	public function getDbConnection()
43
	{
44
		return $this->_connection;
45
	}
46
 
47
	/**
48
	 * @return string TDbTableInfo class name.
49
	 */
50
	protected function getTableInfoClass()
51
	{
52
		return 'TOracleTableInfo';
53
	}
54
 
55
	/**
56
	 * @param string default schema.
57
	 */
58
	public function setDefaultSchema($schema)
59
	{
60
		$this->_defaultSchema=$schema;
61
	}
62
 
63
	/**
64
	 * @return string default schema.
65
	 */
66
	public function getDefaultSchema()
67
	{
68
		return $this->_defaultSchema;
69
	}
70
 
71
	/**
72
	 * Obtains table meta data information for the current connection and given table name.
73
	 * @param string table or view name
74
	 * @return TDbTableInfo table information.
75
	 */
76
	public function getTableInfo($tableName=null)
77
	{
78
		$key = $tableName===null?$this->getDbConnection()->getConnectionString():$tableName;
79
		if(!isset($this->_tableInfoCache[$key]))
80
		{
81
			$class = $this->getTableInfoClass();
82
			$tableInfo = $tableName===null ? new $class : $this->createTableInfo($tableName);
83
			$this->_tableInfoCache[$key] = $tableInfo;
84
		}
85
		return $this->_tableInfoCache[$key];
86
	}
87
 
88
	/**
89
	 * @param string table name with optional schema name prefix, uses default schema name prefix is not provided.
90
	 * @return array tuple as ($schemaName,$tableName)
91
	 */
92
	protected function getSchemaTableName($table)
93
	{
94
		if(count($parts= explode('.', str_replace('"','',$table))) > 1)
95
			return array($parts[0], $parts[1]);
96
		else
97
			return array($this->getDefaultSchema(),$parts[0]);
98
	}
99
 
100
	/**
101
	 * Get the column definitions for given table.
102
	 * @param string table name.
103
	 * @return TOracleTableInfo table information.
104
	 */
105
	protected function createTableInfo($table)
106
	{
107
		list($schemaName,$tableName) = $this->getSchemaTableName($table);
108
 
109
		// This query is made much more complex by the addition of the 'attisserial' field.
110
		// The subquery to get that field checks to see if there is an internally dependent
111
		// sequence on the field.
112
		$sql =
113
<<<EOD
114
		SELECT
115
			a.COLUMN_ID,
116
			LOWER(a.COLUMN_NAME) as attname,
117
			a.DATA_TYPE || DECODE( a.DATA_TYPE, 'NUMBER', '('||a.DATA_PRECISION||','||DATA_SCALE||')' , '') as type,
118
			a.DATA_LENGTH as atttypmod,
119
			DECODE(a.NULLABLE, 'Y', '1', '0') as attnotnull,
120
			DECODE(a.DEFAULT_LENGTH, NULL, '0', '1') as atthasdef,
121
			DATA_DEFAULT as adsrc,
122
			'0' AS attisserial
123
		FROM
124
			ALL_TAB_COLUMNS a
125
		WHERE
126
			TABLE_NAME = '{$tableName}'
127
			AND OWNER = '{$schemaName}'
128
		ORDER BY a.COLUMN_ID
129
EOD;
130
		$this->getDbConnection()->setActive(true);
131
		$this->getDbConnection()->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER);
132
		$command = $this->getDbConnection()->createCommand($sql);
133
		//$command->bindValue(':table', $tableName);
134
		//$command->bindValue(':schema', $schemaName);
135
		$tableInfo = $this->createNewTableInfo($schemaName, $tableName);
136
		$index=0;
137
		foreach($command->query() as $col)
138
		{
139
			$col['index'] = $index++;
140
			$this->processColumn($tableInfo, $col);
141
		}
142
		if($index===0)
143
			throw new TDbException('dbmetadata_invalid_table_view', $table);
144
		return $tableInfo;
145
	}
146
 
147
	/**
148
	 * @param string table schema name
149
	 * @param string table name.
150
	 * @return TOracleTableInfo
151
	 */
152
	protected function createNewTableInfo($schemaName,$tableName)
153
	{
154
		$info['SchemaName'] = $this->assertIdentifier($schemaName);
155
		$info['TableName']	= $this->assertIdentifier($tableName);
156
		$info['IsView'] 	= false;
157
		if($this->getIsView($schemaName,$tableName)) $info['IsView'] = true;
158
		list($primary, $foreign) = $this->getConstraintKeys($schemaName, $tableName);
159
		$class = $this->getTableInfoClass();
160
		return new $class($info,$primary,$foreign);
161
	}
162
 
163
	/**
164
	 * @param string table name, schema name or column name.
165
	 * @return string a valid identifier.
166
	 * @throws TDbException when table name contains a double quote (").
167
	 */
168
	protected function assertIdentifier($name)
169
	{
170
		if(strpos($name, '"')!==false)
171
		{
172
			$ref = 'http://www.oracle.com';
173
			throw new TDbException('dbcommon_invalid_identifier_name', $name, $ref);
174
		}
175
		return $name;
176
	}
177
 
178
	/**
179
	 * @param string table schema name
180
	 * @param string table name.
181
	 * @return boolean true if the table is a view.
182
	 */
183
	protected function getIsView($schemaName,$tableName)
184
	{
185
		$this->getDbConnection()->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER);
186
		$sql =
187
<<<EOD
188
		select	OBJECT_TYPE
189
		from 	ALL_OBJECTS
190
		where	OBJECT_NAME = '{$tableName}'
191
		and 	OWNER = '{$schemaName}'
192
EOD;
193
		$this->getDbConnection()->setActive(true);
194
		$command = $this->getDbConnection()->createCommand($sql);
195
		//$command->bindValue(':schema',$schemaName);
196
		//$command->bindValue(':table', $tableName);
197
		return intval($command->queryScalar()) === 'VIEW';
198
	}
199
 
200
	/**
201
	 * @param TOracleTableInfo table information.
202
	 * @param array column information.
203
	 */
204
	protected function processColumn($tableInfo, $col)
205
	{
206
		$columnId = strtolower($col['attname']); //use column name as column Id
207
 
208
		//$info['ColumnName'] 	= '"'.$columnId.'"'; //quote the column names!
209
		$info['ColumnName'] 	= $columnId; //NOT quote the column names!
210
		$info['ColumnId'] 		= $columnId;
211
		$info['ColumnIndex'] 	= $col['index'];
212
		if(! (bool)$col['attnotnull'] ) $info['AllowNull'] = true;
213
		if(in_array($columnId, $tableInfo->getPrimaryKeys())) $info['IsPrimaryKey'] = true;
214
		if($this->isForeignKeyColumn($columnId, $tableInfo)) $info['IsForeignKey'] = true;
215
		if( (int)$col['atttypmod'] > 0 ) $info['ColumnSize'] =  $col['atttypmod']; // - 4;
216
		if( (bool)$col['atthasdef'] ) $info['DefaultValue'] = $col['adsrc'];
217
		//
218
		// For a while Oracle Tables has no  associated AutoIncrement Triggers
219
		//
220
		/*
221
		if( $col['attisserial'] )
222
		{
223
			if(($sequence = $this->getSequenceName($tableInfo, $col['adsrc']))!==null)
224
			{
225
				$info['SequenceName'] = $sequence;
226
				unset($info['DefaultValue']);
227
			}
228
		}
229
		*/
230
		$matches = array();
231
		if(preg_match('/\((\d+)(?:,(\d+))?+\)/', $col['type'], $matches))
232
		{
233
			$info['DbType'] = preg_replace('/\(\d+(?:,\d+)?\)/','',$col['type']);
234
			if($this->isPrecisionType($info['DbType']))
235
			{
236
				$info['NumericPrecision'] = intval($matches[1]);
237
				if(count($matches) > 2)
238
					$info['NumericScale'] = intval($matches[2]);
239
			}
240
			else
241
				$info['ColumnSize'] = intval($matches[1]);
242
		}
243
		else
244
			$info['DbType'] = $col['type'];
245
		$tableInfo->Columns[$columnId] = new TOracleTableColumn($info);
246
	}
247
 
248
	/**
249
	 * @return string serial name if found, null otherwise.
250
	 */
251
	protected function getSequenceName($tableInfo,$src)
252
	{
253
		$matches = array();
254
		if(preg_match('/nextval\([^\']*\'([^\']+)\'[^\)]*\)/i',$src,$matches))
255
		{
256
			if(is_int(strpos($matches[1], '.')))
257
				return $matches[1];
258
			else
259
				return $tableInfo->getSchemaName().'.'.$matches[1];
260
		}
261
	}
262
 
263
	/**
264
	 * @return boolean true if column type if "numeric", "interval" or begins with "time".
265
	 */
266
	protected function isPrecisionType($type)
267
	{
268
		$type = strtolower(trim($type));
269
		return $type==='number'; // || $type==='interval' || strpos($type, 'time')===0;
270
	}
271
 
272
	/**
273
	 * Gets the primary and foreign key column details for the given table.
274
	 * @param string schema name
275
	 * @param string table name.
276
	 * @return array tuple ($primary, $foreign)
277
	 */
278
	protected function getConstraintKeys($schemaName, $tableName)
279
	{
280
		$this->getDbConnection()->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER);
281
//		select decode( a.CONSTRAINT_TYPE, 'P', 'PRIMARY KEY (', 'FOREIGN KEY (' )||b.COLUMN_NAME||')' as consrc,
282
		$sql =
283
<<<EOD
284
		select b.COLUMN_NAME as consrc,
285
			   a.CONSTRAINT_TYPE as contype
286
		from ALL_CONSTRAINTS a, ALL_CONS_COLUMNS b
287
 		where (a.constraint_name = b.constraint_name AND a.table_name = b.table_name AND a.owner = b.owner)
288
		and	  a.TABLE_NAME = '{$tableName}'
289
		and   a.OWNER = '{$schemaName}'
290
		and   a.CONSTRAINT_TYPE in ('P','R')
291
EOD;
292
		$this->getDbConnection()->setActive(true);
293
		$command = $this->getDbConnection()->createCommand($sql);
294
		//$command->bindValue(':table', $tableName);
295
		//$command->bindValue(':schema', $schemaName);
296
		$primary = array();
297
		$foreign = array();
298
		foreach($command->query() as $row)
299
		{
300
			switch( strtolower( $row['contype'] ) )
301
			{
302
				case 'p':
303
					$primary = array_merge( $primary, array(strtolower( $row['consrc'] )) );
304
					/*
305
					$arr = $this->getPrimaryKeys($row['consrc']);
306
					$primary = array_merge( $primary, array(strtolower( $arr[0] )) );
307
					*/
308
					break;
309
				case 'r':
310
					$foreign = array_merge( $foreign, array(strtolower( $row['consrc'] )) );
311
					/*
312
					// if(($fkey = $this->getForeignKeys($row['consrc']))!==null)
313
					$fkey = $this->getForeignKeys( $row['consrc'] );
314
					$foreign = array_merge( $foreign, array(strtolower( $fkey )) );
315
					*/
316
					break;
317
			}
318
		}
319
		return array($primary,$foreign);
320
	}
321
 
322
	/**
323
	 * Gets the primary key field names
324
	 * @param string Oracle primary key definition
325
	 * @return array primary key field names.
326
	 */
327
	protected function getPrimaryKeys($src)
328
	{
329
		$matches = array();
330
		if(preg_match('/PRIMARY\s+KEY\s+\(([^\)]+)\)/i', $src, $matches))
331
			return preg_split('/,\s+/',$matches[1]);
332
		return array();
333
	}
334
 
335
	/**
336
	 * Gets foreign relationship constraint keys and table name
337
	 * @param string Oracle foreign key definition
338
	 * @return array foreign relationship table name and keys, null otherwise
339
	 */
340
	protected function getForeignKeys($src)
341
	{
342
		$matches = array();
343
		$brackets = '\(([^\)]+)\)';
344
		$find = "/FOREIGN\s+KEY\s+{$brackets}\s+REFERENCES\s+([^\(]+){$brackets}/i";
345
		if(preg_match($find, $src, $matches))
346
		{
347
			$keys = preg_split('/,\s+/', $matches[1]);
348
			$fkeys = array();
349
			foreach(preg_split('/,\s+/', $matches[3]) as $i => $fkey)
350
				$fkeys[$keys[$i]] = $fkey;
351
			return array('table' => str_replace('"','',$matches[2]), 'keys' => $fkeys);
352
		}
353
	}
354
 
355
	/**
356
	 * @param string column name.
357
	 * @param TOracleTableInfo table information.
358
	 * @return boolean true if column is a foreign key.
359
	 */
360
	protected function isForeignKeyColumn($columnId, $tableInfo)
361
	{
362
		foreach($tableInfo->getForeignKeys() as $fk)
363
		{
364
			if( $fk==$columnId )
365
			//if(in_array($columnId, array_keys($fk['keys'])))
366
				return true;
367
		}
368
		return false;
369
	}
370
}
371
 
372
?>