11import re
22from decimal import Decimal
3+ from typing import Optional
4+ from typing import Any
35import intersystems_iris .dbapi ._DBAPI as dbapi
46from . import information_schema as ischema
57from sqlalchemy import exc
@@ -110,6 +112,7 @@ def check_constraints(cls):
110112 "TINYINT" : TINYINT ,
111113 "VARBINARY" : VARBINARY ,
112114 "VARCHAR" : VARCHAR ,
115+ "CHAR" : CHAR ,
113116}
114117
115118RESERVED_WORDS = set (
@@ -418,7 +421,10 @@ def limit_clause(self, select, **kw):
418421
419422 if limit_clause is not None :
420423 sql += " LIMIT %s" % self .process (limit_clause , ** kw )
421- elif offset_clause is not None and self .dialect .server_version_info < (2025 , 2 ):
424+ elif offset_clause is not None and self .dialect .server_version_info < (
425+ 2025 ,
426+ 2 ,
427+ ):
422428 # IRIS 2025.1 has a bug with offset without limit
423429 sql += " LIMIT 100"
424430 if offset_clause is not None :
@@ -499,7 +505,11 @@ def get_select_precolumns(self, select, **kw):
499505 else :
500506 text += "DISTINCT "
501507
502- if not self .dialect .supports_modern_pagination and select ._has_row_limiting_clause and self ._use_top (select ):
508+ if (
509+ not self .dialect .supports_modern_pagination
510+ and select ._has_row_limiting_clause
511+ and self ._use_top (select )
512+ ):
503513 text += "TOP %s " % self .process (self ._get_limit_or_fetch (select ), ** kw )
504514
505515 return text
@@ -635,7 +645,12 @@ def order_by_clause(self, select, **kw):
635645 order_by = self .process (select ._order_by_clause , ** kw )
636646 limit_clause = self ._get_limit_or_fetch (select )
637647
638- if order_by and not self .is_subquery () and limit_clause is None and select ._offset_clause is None :
648+ if (
649+ order_by
650+ and not self .is_subquery ()
651+ and limit_clause is None
652+ and select ._offset_clause is None
653+ ):
639654 return " ORDER BY " + order_by
640655 return ""
641656
@@ -645,11 +660,9 @@ def visit_concat_op_binary(self, binary, operator, **kw):
645660 self .process (binary .right , ** kw ),
646661 )
647662
648- def visit_concat_func (
649- self , func , ** kw
650- ):
663+ def visit_concat_func (self , func , ** kw ):
651664 args = [self .process (clause , ** kw ) for clause in func .clauses .clauses ]
652- return ' || ' .join (args )
665+ return " || " .join (args )
653666
654667 def visit_mod_binary (self , binary , operator , ** kw ):
655668 return (
@@ -712,7 +725,7 @@ def create_table_constraints(self, table, **kw):
712725 comment = table .comment
713726 if comment :
714727 # hack to keep \r, kind of
715- comment = comment .replace (' \r ' , ' \n \t ' )
728+ comment = comment .replace (" \r " , " \n \t " )
716729 literal = self .sql_compiler .render_literal_value (comment , sqltypes .String ())
717730 description = "%DESCRIPTION " + literal
718731
@@ -769,7 +782,7 @@ def get_column_specification(self, column, **kwargs):
769782
770783 comment = column .comment
771784 if comment is not None :
772- comment = comment .replace (' \r ' , ' \n \t ' )
785+ comment = comment .replace (" \r " , " \n \t " )
773786 literal = self .sql_compiler .render_literal_value (comment , sqltypes .String ())
774787 colspec .append ("%DESCRIPTION " + literal )
775788
@@ -816,11 +829,37 @@ def visit_BOOLEAN(self, type_, **kw):
816829 def visit_BIT (self , type_ , ** kw ):
817830 return "BIT"
818831
819- def visit_VARCHAR (self , type_ , ** kw ):
820- # If length is not specified, use 50 as default in IRIS
821- if type_ .length is None :
822- type_ = VARCHAR (50 )
823- return "VARCHAR(%d)" % type_ .length
832+ # def visit_VARCHAR(self, type_, **kw):
833+ # # If length is not specified, use 50 as default in IRIS
834+ # if type_.length is None:
835+ # type_ = VARCHAR(50)
836+ # return "VARCHAR(%d)" % type_.length
837+
838+ def _render_string_type (
839+ self , name : str , length : Optional [int ], collation : Optional [str ]
840+ ) -> str :
841+ text = name
842+ if length :
843+ text += f"({ length } )"
844+ # if collation:
845+ # text += f' COLLATE "{collation}"'
846+ return text
847+
848+ def visit_CHAR (self , type_ : sqltypes .CHAR , ** kw : Any ) -> str :
849+ return self ._render_string_type ("CHAR" , type_ .length , type_ .collation )
850+
851+ def visit_NCHAR (self , type_ : sqltypes .NCHAR , ** kw : Any ) -> str :
852+ return self ._render_string_type ("NCHAR" , type_ .length , type_ .collation )
853+
854+ def visit_VARCHAR (self , type_ : sqltypes .String , ** kw : Any ) -> str :
855+ return self ._render_string_type (
856+ "VARCHAR" , type_ .length , type_ .collation
857+ )
858+
859+ def visit_NVARCHAR (self , type_ : sqltypes .NVARCHAR , ** kw : Any ) -> str :
860+ return self ._render_string_type (
861+ "NVARCHAR" , type_ .length , type_ .collation
862+ )
824863
825864 def visit_TEXT (self , type_ , ** kw ):
826865 return "VARCHAR(65535)"
@@ -1015,7 +1054,9 @@ def on_connect(conn):
10151054 self .supports_vectors = False
10161055 self ._dictionary_access = False
10171056 with conn .cursor () as cursor :
1018- res = cursor .execute ("%CHECKPRIV SELECT ON %Dictionary.PropertyDefinition" )
1057+ res = cursor .execute (
1058+ "%CHECKPRIV SELECT ON %Dictionary.PropertyDefinition"
1059+ )
10191060 self ._dictionary_access = res == 0
10201061
10211062 # if not self.supports_vectors:
@@ -1034,7 +1075,7 @@ def on_connect(conn):
10341075
10351076 def _get_option (self , connection , option ):
10361077 with connection .cursor () as cursor :
1037- cursor .execute ("SELECT %SYSTEM_SQL.Util_GetOption(?)" , (option , ))
1078+ cursor .execute ("SELECT %SYSTEM_SQL.Util_GetOption(?)" , (option ,))
10381079 row = cursor .fetchone ()
10391080 return row [0 ] if row else None
10401081
@@ -1182,15 +1223,19 @@ def get_schema(self, schema=None):
11821223
11831224 @reflection .cache
11841225 def get_table_options (self , connection , table_name , schema = None , ** kw ):
1185- if not self .has_table (connection = connection , table_name = table_name , schema = schema ):
1226+ if not self .has_table (
1227+ connection = connection , table_name = table_name , schema = schema
1228+ ):
11861229 raise exc .NoSuchTableError (
11871230 f"{ schema } .{ table_name } " if schema else table_name
11881231 ) from None
11891232 return {}
11901233
11911234 @reflection .cache
11921235 def get_table_comment (self , connection , table_name , schema = None , ** kw ):
1193- if not self .has_table (connection = connection , table_name = table_name , schema = schema ):
1236+ if not self .has_table (
1237+ connection = connection , table_name = table_name , schema = schema
1238+ ):
11941239 raise exc .NoSuchTableError (
11951240 f"{ schema } .{ table_name } " if schema else table_name
11961241 ) from None
@@ -1207,9 +1252,9 @@ def get_table_comment(self, connection, table_name, schema=None, **kw):
12071252 comment = connection .execute (s ).scalar ()
12081253 if comment :
12091254 # make it as \r
1210- comment = comment .replace (' \t \t \t \t ' , ' \r ' )
1255+ comment = comment .replace (" \t \t \t \t " , " \r " )
12111256 # restore \n
1212- comment = comment .replace (' \t \t \t ' , ' \n ' )
1257+ comment = comment .replace (" \t \t \t " , " \n " )
12131258 return {"text" : comment }
12141259
12151260 @reflection .cache
@@ -1761,15 +1806,16 @@ def get_multi_columns(
17611806 comment = row [columns .c .description ]
17621807 if comment :
17631808 # make it as \r
1764- comment = comment .replace (' \t \t \t \t ' , ' \r ' )
1809+ comment = comment .replace (" \t \t \t \t " , " \r " )
17651810 # restore \n
1766- comment = comment .replace (' \t \t \t ' , ' \n ' )
1811+ comment = comment .replace (" \t \t \t " , " \n " )
17671812
17681813 coltype = self .ischema_names .get (type_ , None )
17691814
17701815 kwargs = {}
17711816 if coltype in (
17721817 VARCHAR ,
1818+ CHAR ,
17731819 BINARY ,
17741820 TEXT ,
17751821 VARBINARY ,
0 commit comments