@@ -67,6 +67,24 @@ cdef str DBO_CACHE_SQL_GET_METADATA_FOR_NAME = """
67
67
end if;
68
68
end;"""
69
69
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
+
70
88
cdef str DBO_CACHE_SQL_GET_ELEM_TYPE_WITH_PACKAGE = """
71
89
select elem_type_name
72
90
from all_plsql_coll_types
@@ -113,17 +131,17 @@ cdef class ThinDbObjectTypeSuperCache:
113
131
cdef class BaseThinDbObjectTypeCache:
114
132
115
133
cdef:
134
+ object meta_cursor, columns_cursor, attrs_ref_cursor_var, version_var
116
135
object return_value_var, full_name_var, oid_var, tds_var
117
- object meta_cursor, attrs_ref_cursor_var, version_var
118
136
object schema_var, package_name_var, name_var
119
137
BaseThinConnImpl conn_impl
120
138
dict types_by_oid
121
139
dict types_by_name
122
140
list partial_types
123
141
124
- cdef int _clear_meta_cursor (self ) except - 1 :
142
+ cdef int _clear_cursors (self ) except - 1 :
125
143
"""
126
- Clears the cursor used for searching metadata. This is needed when
144
+ Clears the cursors used for searching metadata. This is needed when
127
145
returning a connection to the pool since user-level objects are
128
146
retained.
129
147
"""
@@ -139,6 +157,9 @@ cdef class BaseThinDbObjectTypeCache:
139
157
self .schema_var = None
140
158
self .package_name_var = None
141
159
self .name_var = None
160
+ if self .columns_cursor is not None :
161
+ self .columns_cursor.close()
162
+ self .columns_cursor = None
142
163
143
164
cdef str _get_full_name(self , ThinDbObjectTypeImpl typ_impl):
144
165
"""
@@ -163,6 +184,17 @@ cdef class BaseThinDbObjectTypeCache:
163
184
self .partial_types = []
164
185
self .conn_impl = conn_impl
165
186
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
+
166
198
cdef int _init_meta_cursor(self , object conn) except - 1 :
167
199
"""
168
200
Initializes the cursor that fetches the type metadata.
@@ -338,8 +370,9 @@ cdef class BaseThinDbObjectTypeCache:
338
370
Populate the type information given the name of the type.
339
371
"""
340
372
cdef:
373
+ ssize_t start_pos, end_pos, name_length
341
374
ThinDbObjectAttrImpl attr_impl
342
- ssize_t pos, name_length
375
+ str data_type
343
376
typ_impl.version = self .version_var.getvalue()
344
377
if typ_impl.oid is None :
345
378
typ_impl.oid = self .oid_var.getvalue()
@@ -352,25 +385,52 @@ cdef class BaseThinDbObjectTypeCache:
352
385
(typ_impl.schema == " SYS" and typ_impl.name == " XMLTYPE" )
353
386
typ_impl.attrs = []
354
387
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())
374
434
375
435
cdef ThinDbObjectTypeImpl get_type_for_info(self , bytes oid, str schema,
376
436
str package_name, str name):
@@ -452,7 +512,8 @@ cdef class ThinDbObjectTypeCache(BaseThinDbObjectTypeCache):
452
512
typ_impl.element_objtype = self .get_type_for_info(None , schema,
453
513
package_name, name)
454
514
455
- cdef list _lookup_type(self , object conn, str name):
515
+ cdef list _lookup_type(self , object conn, str name,
516
+ ThinDbObjectTypeImpl typ_impl):
456
517
"""
457
518
Lookup the type given its name and return the list of attributes for
458
519
further processing. The metadata cursor execution will populate the
@@ -464,24 +525,33 @@ cdef class ThinDbObjectTypeCache(BaseThinDbObjectTypeCache):
464
525
self .meta_cursor.execute(None )
465
526
if self .return_value_var.getvalue() != 0 :
466
527
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()
469
537
470
538
cdef ThinDbObjectTypeImpl get_type(self , object conn, str name):
471
539
"""
472
540
Returns the database object type given its name. The cache is first
473
541
searched and if it is not found, the database is searched and the
474
542
result stored in the cache.
475
543
"""
476
- cdef ThinDbObjectTypeImpl typ_impl
544
+ cdef:
545
+ ThinDbObjectTypeImpl typ_impl
546
+ bint is_rowtype
477
547
typ_impl = self .types_by_name.get(name)
478
548
if typ_impl is None :
479
- attrs = self ._lookup_type(conn, name)
480
549
typ_impl = ThinDbObjectTypeImpl.__new__ (ThinDbObjectTypeImpl)
481
550
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)
482
553
self .types_by_oid[typ_impl.oid] = typ_impl
483
554
self .types_by_name[name] = typ_impl
484
- self ._populate_type_info(name, attrs, typ_impl)
485
555
self .populate_partial_types(conn)
486
556
return typ_impl
487
557
@@ -499,7 +569,7 @@ cdef class ThinDbObjectTypeCache(BaseThinDbObjectTypeCache):
499
569
while self .partial_types:
500
570
typ_impl = self .partial_types.pop()
501
571
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 )
503
573
self ._populate_type_info(full_name, attrs, typ_impl)
504
574
505
575
@@ -549,7 +619,8 @@ cdef class AsyncThinDbObjectTypeCache(BaseThinDbObjectTypeCache):
549
619
typ_impl.element_objtype = self .get_type_for_info(None , schema,
550
620
package_name, name)
551
621
552
- async def _lookup_type(self , object conn, str name):
622
+ async def _lookup_type(self , object conn, str name,
623
+ ThinDbObjectTypeImpl typ_impl):
553
624
"""
554
625
Lookup the type given its name and return the list of attributes for
555
626
further processing. The metadata cursor execution will populate the
@@ -561,8 +632,15 @@ cdef class AsyncThinDbObjectTypeCache(BaseThinDbObjectTypeCache):
561
632
await self .meta_cursor.execute(None )
562
633
if self .return_value_var.getvalue() != 0 :
563
634
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()
566
644
567
645
async def get_type(self , object conn, str name):
568
646
"""
@@ -573,14 +651,14 @@ cdef class AsyncThinDbObjectTypeCache(BaseThinDbObjectTypeCache):
573
651
cdef ThinDbObjectTypeImpl typ_impl
574
652
typ_impl = self .types_by_name.get(name)
575
653
if typ_impl is None :
576
- attrs = await self ._lookup_type(conn, name)
577
654
typ_impl = ThinDbObjectTypeImpl.__new__ (ThinDbObjectTypeImpl)
578
655
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)
581
657
coroutine = self ._populate_type_info(name, attrs, typ_impl)
582
658
if coroutine is not None :
583
659
await coroutine
660
+ self .types_by_oid[typ_impl.oid] = typ_impl
661
+ self .types_by_name[name] = typ_impl
584
662
await self .populate_partial_types(conn)
585
663
return typ_impl
586
664
@@ -598,7 +676,7 @@ cdef class AsyncThinDbObjectTypeCache(BaseThinDbObjectTypeCache):
598
676
while self .partial_types:
599
677
typ_impl = self .partial_types.pop()
600
678
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 )
602
680
coroutine = self ._populate_type_info(full_name, attrs, typ_impl)
603
681
if coroutine is not None :
604
682
await coroutine
0 commit comments