22// This code is distributed under MIT license terms.
33// See the License.txt file in the project root for more information.
44
5+ using System ;
6+ using System . Data . Common ;
7+ using System . Linq ;
8+ using System . Text . RegularExpressions ;
59using Xtensive . Sql . Model ;
610
711namespace Xtensive . Sql . Drivers . PostgreSql . v12_0
@@ -13,7 +17,10 @@ protected override void BuildPgCatalogSchema(Schema schema)
1317 {
1418 base . BuildPgCatalogSchema ( schema ) ;
1519 var defaultValuesTable = schema . Tables [ "pg_attrdef" ] ;
16- defaultValuesTable . CreateColumn ( "adbin" , new SqlValueType ( SqlType . Binary ) ) ;
20+ _ = defaultValuesTable . CreateColumn ( "adbin" , new SqlValueType ( SqlType . Binary ) ) ;
21+
22+ var indexesTable = schema . Tables [ "pg_index" ] ;
23+ CreateInt2Column ( indexesTable , "indnkeyatts" ) ;
1724 }
1825
1926 /// <inheritdoc/>
@@ -77,6 +84,126 @@ protected override ISqlCompileUnit BuildExtractTableAndDomainConstraintsQuery(Ex
7784 return select ;
7885 }
7986
87+ /// <inheritdoc />
88+ protected override ISqlCompileUnit BuildExtractTableIndexesQuery ( ExtractionContext context )
89+ {
90+ var tableMap = context . TableMap ;
91+ var tableSpacesTable = PgTablespace ;
92+ var relationsTable = PgClass ;
93+ var indexTable = PgIndex ;
94+ var dependencyTable = PgDepend ;
95+
96+ //subselect that index was not created automatically
97+ var subSelect = SqlDml . Select ( dependencyTable ) ;
98+ subSelect . Where = dependencyTable [ "classid" ] == PgClassOid &&
99+ dependencyTable [ "objid" ] == indexTable [ "indexrelid" ] &&
100+ dependencyTable [ "deptype" ] == 'i' ;
101+ subSelect . Columns . Add ( dependencyTable [ 0 ] ) ;
102+
103+ //not automatically created indexes of our tables
104+ var select = SqlDml . Select ( indexTable
105+ . InnerJoin ( relationsTable , relationsTable [ "oid" ] == indexTable [ "indexrelid" ] )
106+ . LeftOuterJoin ( tableSpacesTable , tableSpacesTable [ "oid" ] == relationsTable [ "reltablespace" ] ) ) ;
107+ select . Where = SqlDml . In ( indexTable [ "indrelid" ] , CreateOidRow ( tableMap . Keys ) ) && ! SqlDml . Exists ( subSelect ) ;
108+ select . Columns . Add ( indexTable [ "indrelid" ] ) ;
109+ select . Columns . Add ( indexTable [ "indexrelid" ] ) ;
110+ select . Columns . Add ( relationsTable [ "relname" ] ) ;
111+ select . Columns . Add ( indexTable [ "indisunique" ] ) ;
112+ select . Columns . Add ( indexTable [ "indisclustered" ] ) ;
113+ select . Columns . Add ( indexTable [ "indkey" ] ) ;
114+ select . Columns . Add ( tableSpacesTable [ "spcname" ] ) ;
115+ select . Columns . Add ( indexTable [ "indnatts" ] ) ;
116+ select . Columns . Add ( indexTable [ "indnkeyatts" ] ) ;
117+ select . Columns . Add ( SqlDml . FunctionCall ( "pg_get_expr" , indexTable [ "indexprs" ] , indexTable [ "indrelid" ] , true ) ,
118+ "indexprstext" ) ;
119+ select . Columns . Add ( SqlDml . FunctionCall ( "pg_get_expr" , indexTable [ "indpred" ] , indexTable [ "indrelid" ] , true ) ,
120+ "indpredtext" ) ;
121+ select . Columns . Add ( SqlDml . FunctionCall ( "pg_get_indexdef" , indexTable [ "indexrelid" ] ) , "inddef" ) ;
122+ AddSpecialIndexQueryColumns ( select , tableSpacesTable , relationsTable , indexTable , dependencyTable ) ;
123+ return select ;
124+ }
125+
126+ /// <inheritdoc />
127+ protected override int ReadTableIndexData ( DbDataReader dataReader , ExtractionContext context )
128+ {
129+ var tableMap = context . TableMap ;
130+ var tableColumns = context . TableColumnMap ;
131+
132+ var maxColumnNumber = 0 ;
133+ var tableIdentifier = Convert . ToInt64 ( dataReader [ "indrelid" ] ) ;
134+ var indexIdentifier = Convert . ToInt64 ( dataReader [ "indexrelid" ] ) ;
135+ var indexName = dataReader [ "relname" ] . ToString ( ) ;
136+ var isUnique = dataReader . GetBoolean ( dataReader . GetOrdinal ( "indisunique" ) ) ;
137+ var indexKey = ( short [ ] ) dataReader [ "indkey" ] ;
138+
139+ var tablespaceName = ( dataReader [ "spcname" ] != DBNull . Value ) ? dataReader [ "spcname" ] . ToString ( ) : null ;
140+ var filterExpression = ( dataReader [ "indpredtext" ] != DBNull . Value )
141+ ? dataReader [ "indpredtext" ] . ToString ( )
142+ : string . Empty ;
143+
144+ var table = tableMap [ tableIdentifier ] ;
145+
146+ var fullTextRegex =
147+ @"(?<=CREATE INDEX \S+ ON \S+ USING (?:gist|gin)(?:\s|\S)*)to_tsvector\('(\w+)'::regconfig, \(*(?:(?:\s|\)|\(|\|)*(?:\(""(\S+)""\)|'\s')::text)+\)" ;
148+ var indexScript = dataReader [ "inddef" ] . ToString ( ) ;
149+ var matches = Regex . Matches ( indexScript , fullTextRegex , RegexOptions . Compiled ) ;
150+ if ( matches . Count > 0 ) {
151+ // Fulltext index
152+ var fullTextIndex = table . CreateFullTextIndex ( indexName ) ;
153+ foreach ( Match match in matches ) {
154+ var columnConfigurationName = match . Groups [ 1 ] . Value ;
155+ foreach ( Capture capture in match . Groups [ 2 ] . Captures ) {
156+ var columnName = capture . Value ;
157+ var fullTextColumn = fullTextIndex . Columns [ columnName ]
158+ ?? fullTextIndex . CreateIndexColumn ( table . Columns . Single ( column => column . Name == columnName ) ) ;
159+ if ( fullTextColumn . Languages [ columnConfigurationName ] == null )
160+ fullTextColumn . Languages . Add ( new Language ( columnConfigurationName ) ) ;
161+ }
162+ }
163+ }
164+ else {
165+ //Regular index
166+ var index = table . CreateIndex ( indexName ) ;
167+ index . IsBitmap = false ;
168+ index . IsUnique = isUnique ;
169+ index . Filegroup = tablespaceName ;
170+ if ( ! string . IsNullOrEmpty ( filterExpression ) )
171+ index . Where = SqlDml . Native ( filterExpression ) ;
172+
173+ // Expression-based index
174+ var some = dataReader [ "indexprstext" ] ;
175+ if ( some != DBNull . Value ) {
176+ context . ExpressionIndexMap [ indexIdentifier ] = new ExpressionIndexInfo ( index , indexKey ) ;
177+ maxColumnNumber = dataReader . GetInt16 ( dataReader . GetOrdinal ( "indnatts" ) ) ;
178+ }
179+ else {
180+ var keyColumnNumber = dataReader . GetInt16 ( dataReader . GetOrdinal ( "indnkeyatts" ) ) ;
181+ for ( int j = 0 ; j < indexKey . Length ; j ++ ) {
182+ if ( j < keyColumnNumber ) {
183+ int colIndex = indexKey [ j ] ;
184+ if ( colIndex > 0 ) {
185+ _ = index . CreateIndexColumn ( tableColumns [ tableIdentifier ] [ colIndex ] , true ) ;
186+ }
187+ else {
188+ //column index is 0
189+ //this means that this index column is an expression
190+ //which is not possible with SqlDom tables
191+ }
192+ }
193+ else {
194+ int colIndex = indexKey [ j ] ;
195+ index . NonkeyColumns . Add ( tableColumns [ tableIdentifier ] [ colIndex ] ) ;
196+ }
197+ }
198+ }
199+
200+ ReadSpecialIndexProperties ( dataReader , index ) ;
201+ }
202+
203+ return maxColumnNumber ;
204+ }
205+
206+
80207 // Constructors
81208
82209 public Extractor ( SqlDriver driver )
0 commit comments