TTC Error when requesting XMLType with Oracle.ManagedDataAccess.Client - odp.net

I use a oracle12c database with ODAC from a .Net application
This 2 queries work in sqldevelopper :
SELECT *
FROM TABLE_A
WHERE (XMLCAST(XMLQUERY('/*/name' PASSING TABLE_A.XML_VALUE RETURNING CONTENT) AS NVARCHAR(225)) = 'admin')
SELECT *
FROM TABLE_A
WHERE (XMLCAST(XMLQUERY('count(/*)' PASSING TABLE_A.XML_VALUE RETURNING CONTENT) AS NUMBER) = 1)
But i have an TTC Error with these queries when i request via the Oracle.ManagedDataAccess.Client although this query work :
SELECT *
FROM TABLE_A
WHERE (XMLCAST(TABLE_A.XML_VALUE.extract('/*/name') AS NVARCHAR(225)) = 'admin')
Edit after Christian Shay comment
I don'use really SELECT *
I name all fields of TABLE_A and get the xmltype field with :
TABLE_A.XML_VALUE.getStringVal()
Second edit
The error message is not explicit, it's only : TTC Error
There is no InnerException
The StackTrace is :
à OracleInternal.TTC.TTCExecuteSql.ReceiveExecuteResponse(Accessor[]& defineAccessors, Accessor[] bindAccessors, Boolean bHasReturningParams, SQLMetaData& sqlMetaData, SqlStatementType statementType, Int64 noOfRowsFetchedLastTime, Int32 noOfRowsToFetch, Int32& noOfRowsFetched, Int64& queryId, Int32 longFetchSize, Int64 initialLOBFetchSize, Int64[] scnFromExecution, Boolean& bAllPureInputBinds, DataUnmarshaller& dataUnmarshaller, MarshalBindParameterValueHelper& marshalBindParamsHelper, Int64[]& rowsAffectedByArrayBind, Boolean bDefineDone, Boolean& bMoreThanOneRowAffectedByDmlWithRetClause, Boolean bLOBArrayFetchRequired)
à OracleInternal.ServiceObjects.OracleCommandImpl.ExecuteReader(String commandText, OracleParameterCollection paramColl, CommandType commandType, OracleConnectionImpl connectionImpl, OracleDataReaderImpl& rdrImpl, Int32 longFetchSize, Int64 clientInitialLOBFS, OracleDependencyImpl orclDependencyImpl, Int64[] scnForExecution, Int64[]& scnFromExecution, OracleParameterCollection& bindByPositionParamColl, Boolean& bBindParamPresent, Int64& internalInitialLOBFS, OracleException& exceptionForArrayBindDML, Boolean isDescribeOnly, Boolean isFromEF)
à Oracle.ManagedDataAccess.Client.OracleCommand.ExecuteReader(Boolean requery, Boolean fillRequest, CommandBehavior behavior)
à Oracle.ManagedDataAccess.Client.OracleCommand.ExecuteDbDataReader(CommandBehavior behavior)
à System.Data.Common.DbCommand.System.Data.IDbCommand.ExecuteReader()
After investigation, the problem is due to the sql parameter.
This query failed in TTC Error:
SELECT TABLE_A.XML_VALUE.GetStringVal()
FROM TABLE_A
WHERE (XMLCAST(XMLQUERY('count(/*)' PASSING TABLE_A.XML_VALUE RETURNING CONTENT) AS NUMBER) = :Criterion0)
But if i replace the sql parameter :Criterion0 by the value 1, the query works
Unit test
Version of Oracle.DataAccess.dll : 4.121.1.0 ODAC RELEASE 3 BETA
Script sql :
CREATE TABLE XMLTEST
(
ID NUMBER (10) NOT NULL,
XML_VALUE XMLTYPE
);
INSERT INTO XMLTEST (ID,XML_VALUE) VALUES (1,XMLType('<root><data>TEST1</data><data>TEST2</data></root>'));
Test method :
[TestMethod]
public void ParameterOnXMLTypeTest()
{
string connectionString = "User ID=troopers;Password=troopers;Data Source=MYTST;";
Oracle.ManagedDataAccess.Client.OracleConnection connection = new Oracle.ManagedDataAccess.Client.OracleConnection(connectionString);
Oracle.ManagedDataAccess.Client.OracleParameter parameter = new Oracle.ManagedDataAccess.Client.OracleParameter("Param1", 2);
string query = "SELECT XMLTEST.ID FROM XMLTEST WHERE XMLCAST(XMLQUERY('count(/*/*)' PASSING XMLTEST.XML_VALUE RETURNING CONTENT) AS NUMBER) = :Param1";
Oracle.ManagedDataAccess.Client.OracleCommand command = new Oracle.ManagedDataAccess.Client.OracleCommand(query, connection);
command.Parameters.Add(parameter);
connection.Open();
using(IDataReader reader = command.ExecuteReader())
{
Assert.IsTrue(reader.Read());
Assert.AreEqual(1, reader.GetInt32(reader.GetOrdinal("ID")));
Assert.IsFalse(reader.Read());
}
connection.Close();
}

XMLType is not currently supported in ODP.NET Fully Managed driver as of this writing. This will change in a future version.
http://docs.oracle.com/html/E41125_02/intro004.htm

Related

sqlException when use ActiveJDBC

I use ActiveJDBC and Oracle 11g DB. When I use saveIt, i get java.sql.Exception. When I get instance or list of it, everything ok.
What I do wrong?
Exception in thread "main" org.javalite.activejdbc.DBException: java.sql.SQLException: Invalid argument
зове, query: INSERT INTO dept (DEPTNO, DNAME, LOC) VALUES (?, ?, ?), params: 45, sdfa, fdg
at oracle.jdbc.driver.AutoKeyInfo.getNewSql(AutoKeyInfo.java:187)
at oracle.jdbc.driver.PhysicalConnection.prepareStatement(PhysicalConnection.java:5704)
at org.javalite.activejdbc.DB.execInsert(DB.java:598)
at org.javalite.activejdbc.Model.insert(Model.java:2698)
at org.javalite.activejdbc.Model.save(Model.java:2597)
at org.javalite.activejdbc.Model.saveIt(Model.java:2524)
at JavaHomeTask.Dept.addPersistence(Dept.java:72)
at JavaHomeTask.App.addRow(App.java:103)
at JavaHomeTask.App.main(App.java:50)
Caused by: java.sql.SQLException: Invalid argument
... 9 more
And here is my code:
public void addPersistence() throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
Dept d = new Dept();
String value;
for (String s : getAttributesNames()) {
System.out.println("Enter " + s + " and press Enter button:");
value = reader.readLine();
d.set(s, value);
}
d.saveIt();
}
public List<String> getAttributesNames() {
return Arrays.asList("DEPTNO", "DNAME", "LOC");
}
The reason of the probleb is that ActiveJDBC uses id column as a Primari Key in table for recognizing which operation - INSERT or UPDATE should be used. And if table doesn't has such column, programmer should specify PK manually using #IdName("nameOfColumn") annotation. More information you can find here

How to insert a record and return new id as an output parameter in PostgreSql

I'm trying to insert a new record while returning the new id as a out-parameter value using the following sql:
Insert into ERRORS ( ErrorId, Date, ErrorInfo ) values ( :ItemDate, :Text ) returning ErrorId into :ID
I get the following error:
Npgsql.NpgsqlException occurred
BaseMessage=syntax error at or near "into"
Code=42601
ColumnName=""
ConstraintName=""
DataTypeName=""
Detail=""
ErrorCode=-2147467259
ErrorSql=Insert into ERRORS ( ErrorId, Date, ErrorInfo ) values ( (('2014-04-02 08:16:36.045969')::timestamp), (('Test 333 4/2/2014 8:16:36 AM')::text) ) returning ErrorId into
File=src\backend\parser\scan.l
Hint=""
HResult=-2147467259
Line=1053
Message=ERROR: 42601: syntax error at or near "into"
Position=168
Routine=scanner_yyerror
SchemaName=""
Severity=ERROR
Source=Npgsql
TableName=""
Where=""
StackTrace:
at Npgsql.NpgsqlState.<ProcessBackendResponses_Ver_3>d__9.MoveNext()
at Npgsql.ForwardsOnlyDataReader.GetNextResponseObject(Boolean cleanup)
at Npgsql.ForwardsOnlyDataReader.GetNextRowDescription()
at Npgsql.ForwardsOnlyDataReader.NextResultInternal()
at Npgsql.ForwardsOnlyDataReader..ctor(IEnumerable`1 dataEnumeration, CommandBehavior behavior, NpgsqlCommand command, NotificationThreadBlock threadBlock, Boolean preparedStatement, NpgsqlRowDescription rowDescription)
at Npgsql.NpgsqlCommand.GetReader(CommandBehavior cb)
at Npgsql.NpgsqlCommand.ExecuteNonQuery()
at FrozenElephant.Symbiotic.ObjectWriter.Create(IDatabaseTypesFactory factory, Object value, IDbTransaction transaction, Boolean performCommitClose) in E:\Dev\FrozenElephant\SymbioticORM\Symbiotic\Symbiotic\ObjectWriter.vb:line 423
InnerException:
I also tried removing the last "into" but does not work either.
Instead of using an out parameter, you have to use ExecuteReader with npgsql:
NpgsqlCommand cmd = new NpgsqlCommand(#"Insert into ERRORS(Date, ErrorInfo) values(:ItemDate, :Text) returning ErrorId", conn);
...Add your inpout parameters...
NpgsqlDataReader reader = cmd.ExecuteReader();
int errorId;
while (reader.Read())
{
errorId = reader.GetInt32(0));
}
This example SQL will yield a result set with a single row containing the new ErrorId:
CREATE TABLE errors(ErrorId SERIAL, date TIMESTAMP, ErrorInfo VARCHAR(100));
INSERT INTO errors(Date, ErrorInfo)
VALUES (now(), 'abc')
RETURNING ErrorId
One problem with your expression is that you have ErrorId in the insert into list, but not a value for it.

NHibernate Update Column = Column - 1

I am unable to get the below update to execute in the context of nhibernate.
using(ITransaction transaction = session.BeginTransaction())
{
// FIRST I'm GETTING A LIST OF ITEMS IN A MANNER LIKE THIS
var itemsToDelete = session.QueryOver<Item>()
.Where(i => i.ReferenceObject.Id == otherIdValue)
.List<Item>();
// THEN I"M LOOPING THROUGH THEM
for(itemToDelete in itemsToDelete)
{
session.Delete(itemToDelete);
using (iDB2Command command = (iDB2Command)_session.Connection.CreateCommand())
{
command.CommandText = "update TABLE_NAME set sequence = (sequence - 1) where id = #someId and sequence > #sequenceNumberDeleted";
command.DeriveParameters();
command.Parameters["#someId"].Value = idValue;
command.Parameters["#sequenceNumberDeleted"].Value = itemToDelete.Sequence;
}
}
transaction.commit()
}
The problem seems to be with the sequence = (sequence - 1). Everytime the routine is called NHibernate is throwing an "unexpected row count" exception. While researching most articles I found related to this exception were caused by a trigger on the table updating other rows. In this case there aren't any triggers on the table. Additionally if I replace sequence = 5 or some other constant the update statement executes without any problems.
DATE TIME [10] ERROR App.Controllers.AController - Unexpected row count: 2; expected: 1
DATE TIME [10] ERROR App.Controllers.AController - at NHibernate.AdoNet.Expectations.BasicExpectation.VerifyOutcomeNonBatched(Int32 rowCount, IDbCommand statement)
at NHibernate.AdoNet.NonBatchingBatcher.AddToBatch(IExpectation expectation)
at NHibernate.Persister.Entity.AbstractEntityPersister.Delete(Object id, Object version, Int32 j, Object obj, SqlCommandInfo sql, ISessionImplementor session, Object[] loadedState)
at NHibernate.Persister.Entity.AbstractEntityPersister.Delete(Object id, Object version, Object obj, ISessionImplementor session)
at NHibernate.Action.EntityDeleteAction.Execute()
at NHibernate.Engine.ActionQueue.Execute(IExecutable executable)
at NHibernate.Engine.ActionQueue.ExecuteActions(IList list)
at NHibernate.Engine.ActionQueue.ExecuteActions()
at NHibernate.Event.Default.AbstractFlushingEventListener.PerformExecutions(IEventSource session)
at NHibernate.Event.Default.DefaultFlushEventListener.OnFlush(FlushEvent event)
at NHibernate.Impl.SessionImpl.Flush()
at NHibernate.Transaction.AdoTransaction.Commit()
at App.Controllers.AController.AMethod(Int32[] otherIdValues, Int32 someIdValue, String someReferenceValue) in <path>\App\Controllers\AController.cs:line 117
Can someone help point me in the right direction?
EDIT:
Diego is quite right. This is what I get for rushing.
The actual call to this code is inside a transaction and then I'm calling commit on the transaction.
I have since found the cause of the issue. The actual statements were not being generated sequentially as I thought they would be. Instead the update command was executing prior to the delete command and thus the delete command was throwing the unexpected row count exception, not the update command as I previously thought.

In SQL Server 2008 I am able to pass table-valued parameter to my stored procedure from NHibernate.How to achieve the same in Oracle

I have created a table as a type in SQL Server 2008.
As SQL Server 2008 supports passing table value parameter as IN parameter to stored procedure. It is working fine.
Now I have to perform the same approach in Oracle.
I did it through PLSQLAssociativeArray but the limitaion of Associative array is they are homogeneous (every element must be of the same type).
Where as in case of table-valued parameter of SQL Server 2008, it is possible.
How to achieve the same in Oracle.?
Following are my type and stored procedure in SQL Server 2008:
CREATE TYPE [dbo].[EmployeeType] AS TABLE(
[EmployeeID] [int] NULL,
[EmployeeName] [nvarchar](50) NULL
)
GO
CREATE PROCEDURE [dbo].[TestCustom] #location EmployeeType READONLY
AS
insert into Employee (EMP_ID,EMP_NAME)
SELECT EmployeeID,EmployeeName
FROM #location;
GO
Call from NHibernate
var dt = new DataTable();
dt.Columns.Add("EmployeeID", typeof(int));
dt.Columns.Add("EmployeeName", typeof(string));
dt.Rows.Add(new object[] { 255066, "Nachi11" });
dt.Rows.Add(new object[] { 255067, "Nachi12" });
ISQLQuery final = eventhistorysession.CreateSQLQuery("Call TestCustom #pLocation = :id");
IQuery result = final.SetStructured("id", dt);
IList finalResult = result.List();
CREATE OR REPLACE TYPE employeeType AS OBJECT (employeeId INT, employeeName VARCHAR2(50));
CREATE TYPE ttEmployeeType AS TABLE OF employeeType;
CREATE PROCEDURE testCustom (pLocation ttEmployeeType)
AS
BEGIN
INSERT
INTO employee (emp_id, emp_name)
SELECT *
FROM TABLE(pLocation);
END;
As I understand, it is not possible to use Oracle object table parameters (see #Quassnoi's answer for an example) using either nHibernate or ODP.NET. The only collection type supported by ODP.NET is PLSQLAssociativeArray.
However, one could easily achieve the same result as with SQL Server TVPs using associative arrays. The trick is to define an array for each parameter instead of a single one for the whole table.
I'm posting a complete proof-of-concept solution as I haven't been able to find one.
Oracle Schema
The schema includes a table and a packaged insert procedure. It treats each parameter as a column and assumes each array is at least as long as the first one.
create table test_table
(
foo number(9),
bar nvarchar2(64)
);
/
create or replace package test_package as
type number_array is table of number(9) index by pls_integer;
type nvarchar2_array is table of nvarchar2(64) index by pls_integer;
procedure test_proc(p_foo number_array, p_bar nvarchar2_array);
end test_package;
/
create or replace package body test_package as
procedure test_proc(p_foo number_array, p_bar nvarchar2_array) as
begin
forall i in p_foo.first .. p_foo.last
insert into test_table values (p_foo(i), p_bar(i));
end;
end test_package;
/
nHibernate Mapping
<sql-query name="test_proc">
begin test_package.test_proc(:foo, :bar); end;
</sql-query>
nHibernate Custom IType
I've borrowed the concept from a great SQL Server related answer and modified the class slightly to work with ODP.NET. As IType is huge, I only show the implemented methods; the rest throws NotImplementedException.
If anyone wants to use this in production code, please be aware that I've not tested this class extensively even if it does what I immediately need.
public class OracleArrayType<T> : IType
{
private readonly OracleDbType _dbType;
public OracleArrayType(OracleDbType dbType)
{
_dbType = dbType;
}
public SqlType[] SqlTypes(IMapping mapping)
{
return new []{ new SqlType(DbType.Object) };
}
public bool IsCollectionType
{
get { return true; }
}
public int GetColumnSpan(IMapping mapping)
{
return 1;
}
public void NullSafeSet(IDbCommand st, object value, int index, ISessionImplementor session)
{
var s = st as OracleCommand;
var v = value as T[];
if (s != null && v != null)
{
s.Parameters[index].CollectionType = OracleCollectionType.PLSQLAssociativeArray;
s.Parameters[index].OracleDbType = _dbType;
s.Parameters[index].Value = value;
s.Parameters[index].Size = v.Length;
}
else
{
throw new NotImplementedException();
}
}
// IType boiler-plate implementation follows.
The constructor parameter specifies the type of the base array type (i.e. if you passing an array of strings, pass OracleDbType.NVarchar2. There probably is a way to deduce the DB type from the value type, but I'm not sure yet how to do that.
Extension Method for IQuery
This wraps the type creation:
public static class OracleExtensions
{
public static IQuery SetArray<T>(this IQuery query, string name, OracleDbType dbType, T[] value)
{
return query.SetParameter(name, value, new OracleArrayType<T>(dbType));
}
}
Usage
To tie all this together, this is how the class is used:
using (var sessionFactory = new Configuration().Configure().BuildSessionFactory())
using (var session = sessionFactory.OpenSession())
{
session
.GetNamedQuery("test_proc")
.SetArray("foo", OracleDbType.Int32, new[] { 11, 21 })
.SetArray("bar", OracleDbType.NVarchar2, new [] { "bar0", "bar1" })
.ExecuteUpdate();
}
The result of select * from test_table after running the code:
FOO BAR
----------------
11 bar0
21 bar1

NHibernate 2nd lvl cache, custom query, sqldialect

I got trunk version of NH and FNH. When i try to add 2nd level cache, some parts of NHibernate forgets about chosen sqldialect.
Initial configuration:
var cfg = Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2008
.ConnectionString(connectionString)
.DefaultSchema("dbo")
.UseReflectionOptimizer()
.Mappings(m => ................);
Guilty custom query:
var sql = #"with Foo(col1,col2,col3)
as (select bla bla bla...)
Select bla bla bla from Foo";
list = Session.CreateSQLQuery(sql)
.AddEntity("fizz", typeof(Fizz))
.SomethingUnimportant();
When i change configuration to:
var cfg = Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2008
.ConnectionString(connectionString)
.DefaultSchema("dbo")
.UseReflectionOptimizer()
.Cache(c=>c
.UseQueryCache()
.ProviderClass<HashtableCacheProvider>())
.ShowSql())
.Mappings(m => ................);
Query throws error (WITH clause was added in mssql2008):
The query should start with 'SELECT' or 'SELECT DISTINCT'
[NotSupportedException: The query should start with 'SELECT' or 'SELECT DISTINCT']
NHibernate.Dialect.MsSql2000Dialect.GetAfterSelectInsertPoint(SqlString sql) +179
NHibernate.Dialect.MsSql2000Dialect.GetLimitString(SqlString querySqlString, Int32 offset, Int32 limit) +119
NHibernate.Dialect.MsSql2005Dialect.GetLimitString(SqlString querySqlString, Int32 offset, Int32 last) +127
NHibernate.Loader.Loader.PrepareQueryCommand(QueryParameters queryParameters, Boolean scroll, ISessionImplementor session) +725
NHibernate.Loader.Loader.DoQuery(ISessionImplementor session, QueryParameters queryParameters, Boolean returnProxies) +352
NHibernate.Loader.Loader.DoQueryAndInitializeNonLazyCollections(ISessionImplementor session, QueryParameters queryParameters, Boolean returnProxies) +114
NHibernate.Loader.Loader.DoList(ISessionImplementor session, QueryParameters queryParameters) +205
Any ideas what exactly confuses nhibernate and how to fix it?
Guilty NHibernate code (in NHibernate/Dialect/MsSql200Dialect.cs):
private static int GetAfterSelectInsertPoint(SqlString sql)
{
if (sql.StartsWithCaseInsensitive("select distinct"))
{
return 15;
}
else if (sql.StartsWithCaseInsensitive("select"))
{
return 6;
}
throw new NotSupportedException
("The query should start with 'SELECT' or 'SELECT DISTINCT'");
}
}
Looks that .SetMaxResults(123) causes this. Fortunately, i can unbound that query.
Hopefully that will fix this.
I repaired the bug using Alkampfer's solution, but I created my own SQL dialect rather than patching the NHibernate source directly:
public class Sql2008DialectWithBugFixes : MsSql2008Dialect
{
public override SqlString GetLimitString(SqlString querySqlString, int offset, int last)
{
if (offset == 0)
{
return querySqlString.Insert(GetAfterSelectInsertPoint(querySqlString), " top " + last);
}
return base.GetLimitString(querySqlString, offset, last);
}
private static int GetAfterSelectInsertPoint(SqlString sql)
{
Int32 selectPosition;
if ((selectPosition = sql.IndexOfCaseInsensitive("select distinct")) >= 0)
{
return selectPosition + 15; // "select distinct".Length;
}
if ((selectPosition = sql.IndexOfCaseInsensitive("select")) >= 0)
{
return selectPosition + 6; // "select".Length;
}
throw new NotSupportedException("The query should start with 'SELECT' or 'SELECT DISTINCT'");
}
}
I had a similar issue (removing SetMaxResults also helped but I needed paging) and found out that the following NHibernate configuration property was causing this bug:
<property name="use_sql_comments">true</property>
It's certainly a bug, because the GetAfterSelectInsertPoint method doesn't take into account that SQL comments may be prepended to the SQL query.
Just set the use_sql_comments property to false and the problem disappears.
Just had the same problem using a similar query which has a WITH clause.
Unfortunately, my query populates a grid, with paging, so I have to keep SetMaxResults.
My solution was to rewrite using a Derived Table:
var sql = #"with Foo(col1,col2,col3)
as (select x1, x2, x3 from x join y blabla)
Select col1, col2, col3 from Foo
join B on B.col1 = Foo.col1";
becomes
var sql = #"Select col1, col2, col3 from
(select x1 as col1, x2 as col2, x3 as col3
from x join y blabla) as Foo
join B on B.col1 = Foo.col1";
Just to allow NHibernate to insert the " TOP x " string after the "select" string (6 characters from the begining)... No comment :(
T
It seems that there is some strange bug in the routine used to find the place in the query to insert the TOP clause (GetAfterSelectInsertPoint ) as told by Sandor. You can fix it directly in nh source (I actually patched 2.1 version I'm using in a project, you can find details here). So if you absolutely needs to enable comments with use_sql_comments you can :)
I encountered this problem when upgrading from 1.2 to 3.2 (I know, BIG jump eh?).
The issue in my case was that there is a leading space in front of the select statement in the hql, e.g. String hql = " select "...
With SQL2005 Dialect, this crashes with a "System.NotSupportedException: The query should start with 'SELECT'..." message.
The solution is to
create a unit test that fails, a good Test Driven Developer
should :)
remove the leading space from the " select..." statement
build and run the unit test
Just as i predicted - unbounding select is acceptable workaround.
Deleted SetMaxResults and it works.
We ran into this issue when upgrading to NHibernate version 3.3, but for a different reason...whitespace. We had a lot of sql strings that looked like this:
var sql = #"
select col1 from MyTable";
or:
var sql = #" select col1 from My Table";
These resulted in the "The query should start with 'SELECT' or 'SELECT DISTINCT'" errors because NHibernate doesn't trim the string before validating it.
We created a new dialect that trims the string first to get around this:
public class Sql2008DialectCustom : MsSql2008Dialect
{
public override SqlString GetLimitString(SqlString queryString, SqlString offset, SqlString limit)
{
var trimmedQueryString = queryString.Trim();
return base.GetLimitString(trimmedQueryString, offset, limit);
}
}