OCI8 with CDbConnection

In this case I suggest you wait until Nov. 14 when 1.1.5 will be released as boss says here.

I’m saying both drivers works well, but they must be used separately - you have to choose one of them. Also there is some limitations and known bugs in pdo_oci (http://us2.php.net/pdo_oci), so pay attension if you will use this one.

Nothing special php.ini, just loaded pdo and pdo_oci extensions, and pointed path to oracle’s client in environment variables. As I see you are using pdo_oci right now, that is enough :)

What depends on your issue - I think you can try to set in your db connection settings initSQLs or execute command right before using other queries. I had case:




$connection=Yii::app()->db;

$command=$connection->createCommand('alter session set NLS_SORT=BINARY_CI')->execute();

$command=$connection->createCommand('alter session set NLS_COMP=LINGUISTIC')->execute();



While this is not common case I don’t think this will be fixed in framework’s core, but your are free to report a bug.

Regards, Tomas

Thanks for all the help…

I was able to find a work around for getting Gii to generate the code. The bug (which I believe is an oracle issue) was in the query itself.

In the ‘framework/db/schema/oci/COciSchema.php’ file there is a function to find table contraints. I listed the actual SQL above.

When running that SQL by hand I get an oracle error and here is why and the fix:




SELECT 

  D.constraint_type as CONSTRAINT_TYPE, 

  C.COLUMN_NAME, 

  C.position, 

  D.r_constraint_name, 

  E.table_name as table_ref, 

  f.column_name as column_ref, /* <--- added comma so I can add the following line */

  C.table_name                 /* <--- If we do not also select this line we cause oracle error */

FROM 

  ALL_CONS_COLUMNS C 

inner join 

  ALL_constraints D on D.OWNER = C.OWNER 

and 

  D.constraint_name = C.constraint_name 

left join 

  ALL_constraints E on E.OWNER = D.r_OWNER 

and 

  E.constraint_name = D.r_constraint_name 

left join 

  ALL_cons_columns F on F.OWNER = E.OWNER 

and 

  F.constraint_name = E.constraint_name 

and 

  F.position = c.position WHERE C.OWNER = 'NETOPS_SCHEMA' 

and 

  C.table_name = 'NETOPS_HOSTS'

and 

  D.constraint_type <> 'P' 

order by 

  d.constraint_name, 

  c.position



For some reason we have to add ‘C.table_name’ to the columns to select or Oracle will generate an error and core dump!

This should not be necessary, using a join on columns not viewed is acceptable in SQL as far as I know. I do know that adding the columns acts as a work around though.

The actual code in the file now works for model/crud generation and looks like this:




		$sql=<<<EOD

		SELECT D.constraint_type as CONSTRAINT_TYPE, C.COLUMN_NAME, C.position, D.r_constraint_name,

                E.table_name as table_ref, f.column_name as column_ref, C.TABLE_NAME

        FROM ALL_CONS_COLUMNS C

        inner join ALL_constraints D on D.OWNER = C.OWNER and D.constraint_name = C.constraint_name

        left join ALL_constraints E on E.OWNER = D.r_OWNER and E.constraint_name = D.r_constraint_name

        left join ALL_cons_columns F on F.OWNER = E.OWNER and F.constraint_name = E.constraint_name and F.position = c.position

        WHERE C.OWNER = '{$table->schemaName}'

           and C.table_name = '{$table->name}'

           and D.constraint_type <> 'P'

        order by d.constraint_name, c.position

EOD;



*NOTE the added ‘, C.TABLE_NAME’ at the end of the line before the ‘FROM’ part starts.

The next error I am now having is that the generated code fails with the following error:

Doing more back tracking, I placed a vardump in the code in the COciSchema.php file at the end of the function ‘findColumns’.

Here is where it gets odd again… I added a var_dump and an exit right above the return line and var_dumped the resulting $table object. This var_dump showed ALL columns and the properties for them. So it clearly does find the table despite the fact that the error says ‘table or view does not exist’.

I am still backtracking to see where that error is generated and why and will update on my findings as I find them.

<EDIT>

I am not seeing the problem. I have dug deep into the CActiveRecord, CDbCommandBuilder, CDbSchema, and others.

From inside the protected|views|nETOPSHOSTS|index.php, if I comment out the CListView section and var_dump the $dataProvider variable I get a dumped view of all columns and column descriptions along with other data.

This tells me that the table is seen and can be queried (at least for details on structure).

However CListView (using the same $dataProvider variable returns the error ‘table or view does not exist’

I am stumped… any help here would be appreciated.

</EDIT>

Hello David,

So, are you saying that the data provider you are using with the CListView widget is returning the correct data as expected?..i.e.:

print_r($yourDataProvider->getData());

gives you what you would expect from your table?

I have not tried ‘$yourDataProvider->getData()’ but I have tried print_r($dataProvider). Since that is what is passed to CListView that is what I dumped.

The answer to that question though is yes. It actually shows the tables column names, their field types, etc…

I was thinking of posting a bug on this but I am not sure this is relevant to Yii.

As I stated above, this is more of an Oracle bug that I happen to have found a valid workaround for that will not break other patched versions of oracle.

At least for Gii anyway, I am not sure where the SQL is generated for using the CListView but I am sure a similar workaround can be used.

I just changed:

print_r($dataProvider);

to

print_r($dataProvider->getData());

print_r($dataProvider) returns a detailed structure of the table…

Obviously there is quite a bit more but I chopped the rest to make this post smaller.

print_r($dataProvider->getData()) however returns an oracle error:

Also, FWIW, if your application is in debug mode:

define(‘YII_DEBUG’,true);

in your main index.php file, then Yii should be logging ALL queries to the default logging route location: protected/runtime/application.log

Clearly it is getting a db error when trying to execute the query. You might want to take a look in this file and see what, exactly, is being executed.

The error log shows:

The query ‘SELECT COUNT(*) FROM NETOPS_HOSTS t’ I believe is trying to get the number of records for pagination in the CListView. NETOPS_HOSTS is a valid table in the schema NETOPS_SCHEMA however I do not see that it is trying to use the schema.

<EDIT>

No, since I am trying to dump the contents of $dataProvider->getData() with print_r this should not have anything to do with CListView. I am not sure what getData() does behind the scenes but it does look like it is trying to get a count of all records and not get the actual records. Maybe it does both… count first then load data in return.

</EDIT>

When I built the model/crud I used ‘NETOPS_SCHEMA.NETOPS_HOSTS’ so it should have stored that schema and used it correct?

Before this attempt I also tried setting $_defaultSchema in COciSchema to ‘NETOPS_SCHEMA’ as well. Same results. It seems like the schema just gets ignored.

Actually I was slightly in error in my previous post.

The errors above are only obtained if I set $_defaultSchema in COciSchema.php to my schema.

If I do not set $_defaultSchema in that file I end up with this in logs:

Note there is no SQL listed here and the error is no longer ‘table or view’ error but now it is now…

‘The table “NETOPS_HOSTS” for active record class “NETOPSHOSTS” cannot be found in the database.’

This does seem to indicate that the schema is being used but why would it fail to find a table that was found using Gii?

This code was all generated by the successful run of Gii.

Hello again,

I think schema is taken from your login credentials (username) and there is no need to define schema when you creating models. I have created 70+ models without defining schema.

SomeModel.php




	public function tableName()

	{

		return 'SOME';

	}



But I had same problem with caching enabled - that is - if you made some changes in schema, but schema attributes is taken from cache then “it seems like the schema just gets ignored” :)




//comment out all caching features

//        'cache'=>array(

//            'class'=>'CDbCache',

//        ),


		'db'=>array(

			'connectionString' => 'oci:dbname=192.168.0.1/WEBAPP;charset=utf8',

			'username' => 'DEV_SCHEMA',

			'password' => '<img src='http://www.yiiframework.com/forum/public/style_emoticons/default/ohmy.gif' class='bbc_emoticon' alt=':o' />)',

            //'schemaCachingDuration'=>3600,

		),



In our implementation we have 2 schemas accessible with the same login.

NETOPS_APP

NETOPS_SCHEMA

They store different data for different purposes but the key here is that the username matches neither.

I am trying to access the latter ‘NETOPS_SCHEMA’ but the default schema for this username is the first schema ‘NETOPS_APP’.

From NetBeans (using connection manager) I can access this schema and run queries using the same credentials. I can also drop out to command line and use sqlplus and use the same credentials and get to tables in this schema. I just always have to prefix all table names with the schema name (NETOPS_SCHEMA.NETOPS_HOSTS) as I did with the model builder in Yii.

That said however, that should be the purpose of the $_defaultSchema I believe. This should be just for this purpose is it not?

Since this is a freshly cretaed webapp there is nothing beyond the database connection and enabling Gii done to the main.php file. So caching is not an issue here.

I think you’d better go that way (from http://www.yiiframework.com/doc/guide/1.1/en/database.ar#defining-ar-class)

If I can drop a line into discussion…

Maybe this article will help. But I must admit that I reported (solved) there only basic problems with using Yii and Oracle.

@Tova

I think you misunderstood… I am not trying to use 2 databases or schemas with this app. I am only using 1 schema on 1 oracle server. It just so happens that the username has access to 2 schemas on that server.

The application should only ever use 1 of these schemas and never use the other.

Basically this app is being written for the data in NETOPS_HOSTS not for the data in NETOPS_APP. The NETOPS_APP has a whole different purpose and is used by another app written in java.

@Trejder

Thank you I have read that article. However this is not an error with the dsn. The dsn is fine and in fact was used to build the model and crud in Gii. I also use the same credentials and connection info in NetBeans and in sqlplus.

@Jeff

Also, as for the error log reporting the query as ‘select count(*) from NETOPS_HOSTS t’ I was looking at that to see why getData would would try to get a count of records vs getting the records themselves as the name suggests.

Here is what I see and I have a question on the core code…

File: <YII_HOME>/framework/db/schema/CDbCommandBuilder.php

Code:




if(is_string($criteria->select) && stripos($criteria->select,'count')===0)

                                $sql="SELECT ".$criteria->select;

                        else if($criteria->distinct)

                        {

                                if(is_array($table->primaryKey))

                                {

                                        $pk=array();

                                        foreach($table->primaryKey as $key)

                                                $pk[]=$alias.'.'.$key;

                                        $pk=implode(', ',$pk);

                                }

                                else

                                        $pk=$alias.'.'.$table->primaryKey;

                                $sql="SELECT COUNT(DISTINCT $pk)";

                        }

                        else

                                $sql="SELECT COUNT(*)";

                        $sql.=" FROM {$table->rawName} $alias";

                        $sql=$this->applyJoin($sql,$criteria->join);



Shouldn’t this be:




//<--- note the false in the next line and not 0

if(is_string($criteria->select) && stripos($criteria->select,'count')===false) 

                                $sql="SELECT ".$criteria->select;

                        else if($criteria->distinct)

                        {

                                if(is_array($table->primaryKey))

                                {

                                        $pk=array();

                                        foreach($table->primaryKey as $key)

                                                $pk[]=$alias.'.'.$key;

                                        $pk=implode(', ',$pk);

                                }

                                else

                                        $pk=$alias.'.'.$table->primaryKey;

                                $sql="SELECT COUNT(DISTINCT $pk)";

                        }

                        else

                                $sql="SELECT COUNT(*)";

                        $sql.=" FROM {$table->rawName} $alias";

                        $sql=$this->applyJoin($sql,$criteria->join);



I read this on ‘stripos’ at ‘http://php.net/manual/en/function.stripos.php’:

when dumping the variable ‘$criteria’ using prin_r right before that if, and then the ‘$sql’ variable right after, I see an issue. The $criteria->select is ‘STRING’ and it contains ‘’ not 'count()’.

The resulting SQL should have been ‘select * from’ and not ‘select count(*) from’. Because stripos returns false and not 0 as the code is checking for we will always go into the else which wraps the criteria in a ‘count()’.

In all other occurrences of stripos in code you do use false in stead of 0 so is it safe to assume that this is a typo or was missed in previous changes?

That’s true, I misunderstood, sorry.

Last try and I give up - look here http://www.yiiframework.com/forum/index.php?/topic/2179-postgresql-schemas-not-possible/, at post #6.

Just change for oracle "ALTER SESSION SET CURRENT_SCHEMA = NETOPS_HOSTS;"

The getData() method in the dataprovider is doing what it says…it is getting the appropriate data to display. ‘Appropriate’ meaning having applied the proper “limit” to the sql query if pagination is being used. So, it is not getting a ALL of the data and then limiting it, it is using the faster statistical query to get the count and then applying a limit to the db criteria itself before the query for the data is made.

I am assuming you are using CActiveDataProvider as the data provider component to your CListView, is that assumption correct? This takes in an AR class name and the underlying db component (defaults to the ‘db’ application component unless otherwise specified) is what is used to connect to the db. This is where it would determine what schema to use.

If you want to provide the code you are attempting to execute, like the creation of your data provider and the way you are using CListView, it might help to track down your issue.

Also, the methods

CActiveDataProvider::fetchData()

and

CActiveDataProvider::calculateTotalItemCount()

which can be found in the class file framework/web/CActiveDataProvider.php

may be of some use in helping you troubleshoot this issue.

@Tova

That looks very promising.

It does fail though. First try failed on invalid character.

This was due to semicolon (’;’) in ALTER SESSION statement.

Then I removed the semicolon and I get this error:

the user has not changed so something is not right here either.

@Jeff

Yes, I believe so.

I am not doing anything more than what Gii generates. The view (and the CListView inside the view) was created by Gii so this should be the standard CActiveDataProvider.

This is where the problem exists I believe. The main.php ‘db’ section does not allow for a schema to be set. As suggested by Tova the schema is obtained by username which is incorrect (at least in our implementation). Many DBA’s setup access to multiple different schemas from 1 username. Our username does not match the schema that we are trying to access. I have no access to the DB to change that fact, and would nto be allowed to even if I had those privileges. We have other apps that use that username and make use of the other schema owned by that username. This is not uncommon and in fact is standard in many Oracle implementatinos. While this app only uses 1 of the accessable schemas the username does not match the schema and this schema is not the default for this username.

Just to backtrack for added info, I am a new Yii user that is just getting the hang of setting up Yii to use a DB. I have not experimented much beyond the examples in your book and in the demo app. I have not extended any class or tried using another DataProvider. This is a brand new app that was generated using ‘yiic webapp /var/www/html/testora’ command. All of the code to connect to the DB for crud was created by Gii not by myself. I have only modified it for testing and then I change right back. When I do modify it, I comment the original lines and do not remove them. When returning to original code I uncomment the original lines and delete my additions.

I did just try to use Tova’s suggestion but it does not seem to work for me so I will be going back to original code created by Gii now.

The code is default code for CListView generated by Gii, however I have commented it now and I am simply doing as you suggeted and trying to print_r the data returned from $dataProvider->getData().




<?php

$this->breadcrumbs=array(

	'Hosts',

);


$this->menu=array(

	array('label'=>'Create new host', 'url'=>array('create')),

	array('label'=>'Manage Hosts', 'url'=>array('admin')),

);

?>


<h1>Hosts</h1>


<?php

print_r($dataProvider->getData());

//$data=$dataProvider->getData();

//                exit;

//$this->widget('zii.widgets.CListView', array(

//	'dataProvider'=>$dataProvider,

//	'itemView'=>'_view',

//)); ?>



I think we might be over-complicating the question.

A simple way to look at the problem (which does not mean there is a simple answer by the way) is:

Why does the creation of the object $dataProvider find the table and load all the column names details into the object but then the objects getData method fails to find the table/data?

This suggests a breakdown in code to me.

On one hand you not only see the table but can get all details about the column lengths, types, etc… and then using the same DB classes you can not find the same table to run a quick ‘select *’ on it.

Maybe I am incorrect in the simplicity of the problem but the facts remain true, I have provided examples above showing that the object $dataProvider is loaded with the table details correctly so it does find the table on object creation!

If your username has a default schema other than the schema you are trying to access you must make these changes…

SCHEMA ISSUES…

fixed in Yii-1.1.5.r2664 (SVN)

Will be available in stable release 1.1.6

QUOTING ISSUES…

fixed in Yii-1.1.5.r2664 (SVN)

Will be available in stable release 1.1.6

This is to provide a work around for the oracle bug that is in 11g dealing with self joins. Note the ‘, C.table_name’ at the top of the select before the FROM line.

COciSchema.php (findConstraints method):




$sql=<<<EOD

		SELECT D.constraint_type as CONSTRAINT_TYPE, C.COLUMN_NAME, C.position, D.r_constraint_name,

                E.table_name as table_ref, f.column_name as column_ref, C.table_name

        FROM ALL_CONS_COLUMNS C

        inner join ALL_constraints D on D.OWNER = C.OWNER and D.constraint_name = C.constraint_name

        left join ALL_constraints E on E.OWNER = D.r_OWNER and E.constraint_name = D.r_constraint_name

        left join ALL_cons_columns F on F.OWNER = E.OWNER and F.constraint_name = E.constraint_name and F.position = c.position

        WHERE C.OWNER = '{$table->schemaName}'

           and C.table_name = '{$table->name}'

           and D.constraint_type <> 'P'

        order by d.constraint_name, c.position

EOD;



Fix Gii generated code if Primary Key is NOT an int…

Gii Generated Controller NETOPSHOSTSController.php (starting at line 156):




public function loadModel($id)

{

	$model=NETOPSHOSTS::model()->findByPk($id); // Changed from findByPk((int)$id)

	if($model===null)

		throw new CHttpException(404,'The requested page does not exist.');

	return $model;

}



[EDIT]

All schema name issues are addressed in bug 1775 and fixed by 1.1.5.r2664 (current SVN trunk).

SVN r2664 page

Yii Bug 1775 page

All quoting issues are also resolved in this trunk revision.

These changes will be in the stable 1.1.6 version of Yii as well.

The workaround for the oracle bug is not yet tracked as a bug since it is not a bug in Yii code.

[/EDIT]