@@ -67,6 +67,24 @@ cdef str DBO_CACHE_SQL_GET_METADATA_FOR_NAME = """
6767 end if;
6868 end;"""
6969
70+ cdef str DBO_CACHE_SQL_GET_COLUMNS = """
71+ select
72+ column_name,
73+ data_type,
74+ data_type_owner,
75+ case
76+ when data_type in
77+ ('CHAR', 'NCHAR', 'VARCHAR2', 'NVARCHAR2', 'RAW')
78+ then data_length
79+ else 0
80+ end,
81+ nvl(data_precision, 0),
82+ nvl(data_scale, 0)
83+ from all_tab_columns
84+ where owner = :owner
85+ and table_name = substr(:name, 1, length(:name) - 8)
86+ order by column_id"""
87+
7088cdef str DBO_CACHE_SQL_GET_ELEM_TYPE_WITH_PACKAGE = """
7189 select elem_type_name
7290 from all_plsql_coll_types
@@ -113,17 +131,17 @@ cdef class ThinDbObjectTypeSuperCache:
113131cdef class BaseThinDbObjectTypeCache:
114132
115133 cdef:
134+ object meta_cursor, columns_cursor, attrs_ref_cursor_var, version_var
116135 object return_value_var, full_name_var, oid_var, tds_var
117- object meta_cursor, attrs_ref_cursor_var, version_var
118136 object schema_var, package_name_var, name_var
119137 BaseThinConnImpl conn_impl
120138 dict types_by_oid
121139 dict types_by_name
122140 list partial_types
123141
124- cdef int _clear_meta_cursor (self ) except - 1 :
142+ cdef int _clear_cursors (self ) except - 1 :
125143 """
126- Clears the cursor used for searching metadata. This is needed when
144+ Clears the cursors used for searching metadata. This is needed when
127145 returning a connection to the pool since user-level objects are
128146 retained.
129147 """
@@ -139,6 +157,9 @@ cdef class BaseThinDbObjectTypeCache:
139157 self .schema_var = None
140158 self .package_name_var = None
141159 self .name_var = None
160+ if self .columns_cursor is not None :
161+ self .columns_cursor.close()
162+ self .columns_cursor = None
142163
143164 cdef str _get_full_name(self , ThinDbObjectTypeImpl typ_impl):
144165 """
@@ -163,6 +184,17 @@ cdef class BaseThinDbObjectTypeCache:
163184 self .partial_types = []
164185 self .conn_impl = conn_impl
165186
187+ cdef int _init_columns_cursor(self , object conn) except - 1 :
188+ """
189+ Initializes the cursor that fetches the columns for a table or view.
190+ The input values come from the meta cursor that has been initialized
191+ and executed earlier.
192+ """
193+ cursor = conn.cursor()
194+ cursor.setinputsizes(owner = self .schema_var, name = self .name_var)
195+ cursor.prepare(DBO_CACHE_SQL_GET_COLUMNS)
196+ self .columns_cursor = cursor
197+
166198 cdef int _init_meta_cursor(self , object conn) except - 1 :
167199 """
168200 Initializes the cursor that fetches the type metadata.
@@ -338,8 +370,9 @@ cdef class BaseThinDbObjectTypeCache:
338370 Populate the type information given the name of the type.
339371 """
340372 cdef:
373+ ssize_t start_pos, end_pos, name_length
341374 ThinDbObjectAttrImpl attr_impl
342- ssize_t pos, name_length
375+ str data_type
343376 typ_impl.version = self .version_var.getvalue()
344377 if typ_impl.oid is None :
345378 typ_impl.oid = self .oid_var.getvalue()
@@ -352,25 +385,52 @@ cdef class BaseThinDbObjectTypeCache:
352385 (typ_impl.schema == " SYS" and typ_impl.name == " XMLTYPE" )
353386 typ_impl.attrs = []
354387 typ_impl.attrs_by_name = {}
355- for cursor_version, attr_name, attr_num, attr_type_name, \
356- attr_type_owner, attr_type_package, attr_type_oid, \
357- attr_instantiable, attr_super_type_owner, \
358- attr_super_type_name in attrs:
359- if attr_name is None :
360- continue
361- attr_impl = ThinDbObjectAttrImpl.__new__ (ThinDbObjectAttrImpl)
362- attr_impl.name = attr_name
363- if attr_type_owner is not None :
364- attr_impl.dbtype = DB_TYPE_OBJECT
365- attr_impl.objtype = self .get_type_for_info(attr_type_oid,
366- attr_type_owner,
367- attr_type_package,
368- attr_type_name)
369- else :
370- attr_impl.dbtype = DbType._from_ora_name(attr_type_name)
371- typ_impl.attrs.append(attr_impl)
372- typ_impl.attrs_by_name[attr_name] = attr_impl
373- return self ._parse_tds(typ_impl, self .tds_var.getvalue())
388+ if typ_impl.is_row_type:
389+ for name, data_type, data_type_owner, max_size, precision, \
390+ scale in attrs:
391+ attr_impl = ThinDbObjectAttrImpl.__new__ (ThinDbObjectAttrImpl)
392+ attr_impl.name = name
393+ if data_type_owner is not None :
394+ attr_impl.dbtype = DB_TYPE_OBJECT
395+ attr_impl.objtype = self .get_type_for_info(None ,
396+ data_type_owner,
397+ None ,
398+ data_type)
399+ else :
400+ start_pos = data_type.find(" (" )
401+ if start_pos > 0 :
402+ end_pos = data_type.find(" )" )
403+ if end_pos > start_pos:
404+ data_type = data_type[:start_pos] + \
405+ data_type[end_pos + 1 :]
406+ attr_impl.dbtype = DbType._from_ora_name(data_type)
407+ attr_impl.max_size = max_size
408+ attr_impl.precision = precision
409+ attr_impl.scale = scale
410+ typ_impl.attrs.append(attr_impl)
411+ typ_impl.attrs_by_name[name] = attr_impl
412+ else :
413+ for cursor_version, attr_name, attr_num, attr_type_name, \
414+ attr_type_owner, attr_type_package, attr_type_oid, \
415+ attr_instantiable, attr_super_type_owner, \
416+ attr_super_type_name in attrs:
417+ if attr_name is None :
418+ continue
419+ attr_impl = ThinDbObjectAttrImpl.__new__ (ThinDbObjectAttrImpl)
420+ attr_impl.name = attr_name
421+ if attr_type_owner is not None :
422+ attr_impl.dbtype = DB_TYPE_OBJECT
423+ attr_impl.objtype = self .get_type_for_info(
424+ attr_type_oid,
425+ attr_type_owner,
426+ attr_type_package,
427+ attr_type_name
428+ )
429+ else :
430+ attr_impl.dbtype = DbType._from_ora_name(attr_type_name)
431+ typ_impl.attrs.append(attr_impl)
432+ typ_impl.attrs_by_name[attr_name] = attr_impl
433+ return self ._parse_tds(typ_impl, self .tds_var.getvalue())
374434
375435 cdef ThinDbObjectTypeImpl get_type_for_info(self , bytes oid, str schema,
376436 str package_name, str name):
@@ -452,7 +512,8 @@ cdef class ThinDbObjectTypeCache(BaseThinDbObjectTypeCache):
452512 typ_impl.element_objtype = self .get_type_for_info(None , schema,
453513 package_name, name)
454514
455- cdef list _lookup_type(self , object conn, str name):
515+ cdef list _lookup_type(self , object conn, str name,
516+ ThinDbObjectTypeImpl typ_impl):
456517 """
457518 Lookup the type given its name and return the list of attributes for
458519 further processing. The metadata cursor execution will populate the
@@ -464,24 +525,33 @@ cdef class ThinDbObjectTypeCache(BaseThinDbObjectTypeCache):
464525 self .meta_cursor.execute(None )
465526 if self .return_value_var.getvalue() != 0 :
466527 errors._raise_err(errors.ERR_INVALID_OBJECT_TYPE_NAME, name = name)
467- attrs_rc = self .attrs_ref_cursor_var.getvalue()
468- return attrs_rc.fetchall()
528+ if name.endswith(" %R OWTYPE" ):
529+ typ_impl.is_row_type = True
530+ if self .columns_cursor is None :
531+ self ._init_columns_cursor(conn)
532+ self .columns_cursor.execute(None )
533+ return self .columns_cursor.fetchall()
534+ else :
535+ attrs_rc = self .attrs_ref_cursor_var.getvalue()
536+ return attrs_rc.fetchall()
469537
470538 cdef ThinDbObjectTypeImpl get_type(self , object conn, str name):
471539 """
472540 Returns the database object type given its name. The cache is first
473541 searched and if it is not found, the database is searched and the
474542 result stored in the cache.
475543 """
476- cdef ThinDbObjectTypeImpl typ_impl
544+ cdef:
545+ ThinDbObjectTypeImpl typ_impl
546+ bint is_rowtype
477547 typ_impl = self .types_by_name.get(name)
478548 if typ_impl is None :
479- attrs = self ._lookup_type(conn, name)
480549 typ_impl = ThinDbObjectTypeImpl.__new__ (ThinDbObjectTypeImpl)
481550 typ_impl._conn_impl = self .conn_impl
551+ attrs = self ._lookup_type(conn, name, typ_impl)
552+ self ._populate_type_info(name, attrs, typ_impl)
482553 self .types_by_oid[typ_impl.oid] = typ_impl
483554 self .types_by_name[name] = typ_impl
484- self ._populate_type_info(name, attrs, typ_impl)
485555 self .populate_partial_types(conn)
486556 return typ_impl
487557
@@ -499,7 +569,7 @@ cdef class ThinDbObjectTypeCache(BaseThinDbObjectTypeCache):
499569 while self .partial_types:
500570 typ_impl = self .partial_types.pop()
501571 full_name = self ._get_full_name(typ_impl)
502- attrs = self ._lookup_type(conn, full_name)
572+ attrs = self ._lookup_type(conn, full_name, typ_impl )
503573 self ._populate_type_info(full_name, attrs, typ_impl)
504574
505575
@@ -549,7 +619,8 @@ cdef class AsyncThinDbObjectTypeCache(BaseThinDbObjectTypeCache):
549619 typ_impl.element_objtype = self .get_type_for_info(None , schema,
550620 package_name, name)
551621
552- async def _lookup_type(self , object conn, str name):
622+ async def _lookup_type(self , object conn, str name,
623+ ThinDbObjectTypeImpl typ_impl):
553624 """
554625 Lookup the type given its name and return the list of attributes for
555626 further processing. The metadata cursor execution will populate the
@@ -561,8 +632,15 @@ cdef class AsyncThinDbObjectTypeCache(BaseThinDbObjectTypeCache):
561632 await self .meta_cursor.execute(None )
562633 if self .return_value_var.getvalue() != 0 :
563634 errors._raise_err(errors.ERR_INVALID_OBJECT_TYPE_NAME, name = name)
564- attrs_rc = self .attrs_ref_cursor_var.getvalue()
565- return await attrs_rc.fetchall()
635+ if name.endswith(" %R OWTYPE" ):
636+ typ_impl.is_row_type = True
637+ if self .columns_cursor is None :
638+ self ._init_columns_cursor(conn)
639+ await self .columns_cursor.execute(None )
640+ return await self .columns_cursor.fetchall()
641+ else :
642+ attrs_rc = self .attrs_ref_cursor_var.getvalue()
643+ return await attrs_rc.fetchall()
566644
567645 async def get_type(self , object conn, str name):
568646 """
@@ -573,14 +651,14 @@ cdef class AsyncThinDbObjectTypeCache(BaseThinDbObjectTypeCache):
573651 cdef ThinDbObjectTypeImpl typ_impl
574652 typ_impl = self .types_by_name.get(name)
575653 if typ_impl is None :
576- attrs = await self ._lookup_type(conn, name)
577654 typ_impl = ThinDbObjectTypeImpl.__new__ (ThinDbObjectTypeImpl)
578655 typ_impl._conn_impl = self .conn_impl
579- self .types_by_oid[typ_impl.oid] = typ_impl
580- self .types_by_name[name] = typ_impl
656+ attrs = await self ._lookup_type(conn, name, typ_impl)
581657 coroutine = self ._populate_type_info(name, attrs, typ_impl)
582658 if coroutine is not None :
583659 await coroutine
660+ self .types_by_oid[typ_impl.oid] = typ_impl
661+ self .types_by_name[name] = typ_impl
584662 await self .populate_partial_types(conn)
585663 return typ_impl
586664
@@ -598,7 +676,7 @@ cdef class AsyncThinDbObjectTypeCache(BaseThinDbObjectTypeCache):
598676 while self .partial_types:
599677 typ_impl = self .partial_types.pop()
600678 full_name = self ._get_full_name(typ_impl)
601- attrs = await self ._lookup_type(conn, full_name)
679+ attrs = await self ._lookup_type(conn, full_name, typ_impl )
602680 coroutine = self ._populate_type_info(full_name, attrs, typ_impl)
603681 if coroutine is not None :
604682 await coroutine
0 commit comments