3535
3636import java .sql .ResultSet ;
3737import java .sql .SQLException ;
38+ import java .util .ArrayList ;
39+ import java .util .List ;
3840
3941/**
4042 * Reader for PostgreSQL tables.
4345public final class TablesReader extends JdbcReader {
4446
4547 private static final String CREATE_TABLE = "CREATE TABLE noname () " ;
48+ private static final String GP_SYSTEM_OPTIONS = "'format', 'formatter', 'delimiter', 'null', 'escape', 'quote'," +
49+ " 'header', 'newline', 'fill_missing_fields', 'force_not_null', 'is_writable', 'location_uris'," +
50+ " 'execute_on', 'log_errors', 'reject_limit', 'reject_limit_type', 'encoding'" ;
4651
4752 /**
4853 * Creates a new TablesReader.
@@ -58,45 +63,9 @@ protected void processResult(ResultSet res, AbstractSchema schema) throws SQLExc
5863 String schemaName = schema .getName ();
5964 String tableName = res .getString ("relname" );
6065 loader .setCurrentObject (new GenericColumn (schemaName , tableName , DbObjType .TABLE ));
61- String partitionBound = null ;
62- String partitionGpBound = null ;
63- String partitionGpTemplate = null ;
6466
65- if (SupportedPgVersion .GP_VERSION_7 .isLE (loader .getVersion ()) &&
66- res .getBoolean ("relispartition" )) {
67- partitionBound = res .getString ("partition_bound" );
68- checkObjectValidity (partitionBound , DbObjType .TABLE , tableName );
69- }
70- if (loader .isGreenplumDb () && !SupportedPgVersion .GP_VERSION_7 .isLE (loader .getVersion ())) {
71- partitionGpBound = res .getString ("partclause" );
72- partitionGpTemplate = res .getString ("parttemplate" );
73- }
74-
75- AbstractPgTable t ;
76- String serverName = res .getString ("server_name" );
7767 long ofTypeOid = res .getLong ("of_type" );
78- if (serverName != null ) {
79- if (partitionBound == null ) {
80- t = new SimpleForeignPgTable (tableName , serverName );
81- } else {
82- t = new PartitionForeignPgTable (tableName , serverName , partitionBound );
83- }
84- t .addDep (new GenericColumn (serverName , DbObjType .SERVER ));
85- } else if (ofTypeOid != 0 ) {
86- JdbcType jdbcOfType = loader .getCachedTypeByOid (ofTypeOid );
87- String ofType = jdbcOfType .getFullName ();
88- t = new TypedPgTable (tableName , ofType );
89- jdbcOfType .addTypeDepcy (t );
90- } else if (partitionBound != null ) {
91- t = new PartitionPgTable (tableName , partitionBound );
92- } else if (partitionGpBound != null ) {
93- t = createGpParttionTable (tableName , partitionGpBound , partitionGpTemplate );
94- } else if (loader .isGreenplumDb () && res .getString ("exloc" ) != null ) {
95- t = new GpExternalTable (tableName );
96- readExternalTable ((GpExternalTable ) t , res );
97- } else {
98- t = new SimplePgTable (tableName );
99- }
68+ AbstractPgTable t = getTable (res , tableName , ofTypeOid );
10069
10170 if (SupportedPgVersion .GP_VERSION_7 .isLE (loader .getVersion ()) && t instanceof AbstractRegularTable regTable ) {
10271 String accessMethod = res .getString ("access_method" );
@@ -105,11 +74,6 @@ protected void processResult(ResultSet res, AbstractSchema schema) throws SQLExc
10574 }
10675 }
10776
108- String [] foptions = getColArray (res , "ftoptions" , true );
109- if (foptions != null ) {
110- ParserAbstract .fillOptionParams (foptions , t ::addOption , false , true , false );
111- }
112-
11377 // PRIVILEGES, OWNER
11478 loader .setOwner (t , res .getLong ("relowner" ));
11579 loader .setPrivileges (t , res .getString ("aclarray" ), schemaName );
@@ -151,8 +115,57 @@ protected void processResult(ResultSet res, AbstractSchema schema) throws SQLExc
151115 schema .addTable (t );
152116 }
153117
154- private AbstractPgTable createGpParttionTable (String tableName , String partitionGpBound ,
155- String partitionGpTemplate ) {
118+ private AbstractPgTable getTable (ResultSet res , String tableName , long ofTypeOid ) throws SQLException {
119+ if (loader .isGreenplumDb () && res .getString ("exloc" ) != null ) {
120+ return createExternalTable (tableName , res );
121+ }
122+
123+ String partitionBound = null ;
124+ String partitionGpBound = null ;
125+ String partitionGpTemplate = null ;
126+
127+ if (SupportedPgVersion .GP_VERSION_7 .isLE (loader .getVersion ())) {
128+ if (res .getBoolean ("relispartition" )) {
129+ partitionBound = res .getString ("partition_bound" );
130+ checkObjectValidity (partitionBound , DbObjType .TABLE , tableName );
131+ }
132+ } else {
133+ partitionGpBound = res .getString ("partclause" );
134+ partitionGpTemplate = res .getString ("parttemplate" );
135+ }
136+
137+ AbstractPgTable t ;
138+ String serverName = res .getString ("server_name" );
139+ if (serverName != null ) {
140+ if (partitionBound == null ) {
141+ t = new SimpleForeignPgTable (tableName , serverName );
142+ } else {
143+ t = new PartitionForeignPgTable (tableName , serverName , partitionBound );
144+ }
145+ t .addDep (new GenericColumn (serverName , DbObjType .SERVER ));
146+ } else if (ofTypeOid != 0 ) {
147+ JdbcType jdbcOfType = loader .getCachedTypeByOid (ofTypeOid );
148+ String ofType = jdbcOfType .getFullName ();
149+ t = new TypedPgTable (tableName , ofType );
150+ jdbcOfType .addTypeDepcy (t );
151+ } else if (partitionBound != null ) {
152+ t = new PartitionPgTable (tableName , partitionBound );
153+ } else if (partitionGpBound != null ) {
154+ t = createGpPartitionTable (tableName , partitionGpBound , partitionGpTemplate );
155+ } else {
156+ t = new SimplePgTable (tableName );
157+ }
158+
159+ String [] foptions = getColArray (res , "ftoptions" , true );
160+ if (foptions != null ) {
161+ ParserAbstract .fillOptionParams (foptions , t ::addOption , false , true , false );
162+ }
163+
164+ return t ;
165+ }
166+
167+ private AbstractPgTable createGpPartitionTable (String tableName , String partitionGpBound ,
168+ String partitionGpTemplate ) {
156169 PartitionGpTable table = new PartitionGpTable (tableName );
157170
158171 loader .submitAntlrTask (CREATE_TABLE + partitionGpBound + ';' ,
@@ -350,11 +363,7 @@ private void readColumns(ResultSet res, AbstractPgTable t, long ofTypeOid,
350363 }
351364
352365 if (colGenerated != null && !colGenerated [i ].isEmpty ()) {
353- if ("s" .equals (colGenerated [i ])) {
354- column .setGenerationOption ("STORED" );
355- } else {
356- column .setGenerationOption ("VIRTUAL" );
357- }
366+ column .setGenerationOption ("s" .equals (colGenerated [i ]) ? "STORED" : "VIRTUAL" );
358367 }
359368
360369 if (colCompression != null ) {
@@ -401,7 +410,9 @@ private void readColumns(ResultSet res, AbstractPgTable t, long ofTypeOid,
401410 }
402411 }
403412
404- private void readExternalTable (GpExternalTable extTable , ResultSet res ) throws SQLException {
413+ private GpExternalTable createExternalTable (String tableName , ResultSet res ) throws SQLException {
414+ var extTable = new GpExternalTable (tableName );
415+
405416 String rowUrLoc = res .getString ("urloc" );
406417 if (rowUrLoc != null ) {
407418 if (rowUrLoc .startsWith ("{http" )) {
@@ -446,27 +457,49 @@ private void readExternalTable(GpExternalTable extTable, ResultSet res) throws S
446457 break ;
447458 }
448459
449- String options = PgDiffUtils .unquoteQuotedString (res .getString ("options" ), 1 );
450- if (!options .isBlank ()) {
451- ParserAbstract .fillOptionParams (options .split ("," ), extTable ::addOption , false , true , false );
452- }
460+ String formatOptions = res .getString ("fmtopts" );
461+ if (SupportedPgVersion .GP_VERSION_7 .isLE (loader .getVersion ())) {
462+ String [] ftoptionNames = getColArray (res , "ftoption_names" );
463+ String [] ftoptionValues = getColArray (res , "ftoption_values" );
464+
465+ int splitIndex = ftoptionNames .length != 0 ? formatOptions .indexOf (ftoptionNames [0 ]) : formatOptions .length ();
466+ // format options in fmtopts go before options defined in OPTIONS block
467+ String formatOptionsPart = formatOptions .substring (0 , splitIndex ).trim ();
468+ extTable .setFormatOptions (appendFormatOptions (formatOptionsPart ));
469+
470+ List <String > options = new ArrayList <>();
471+ for (int i = 0 ; i < ftoptionNames .length ; i ++) {
472+ String optionName = ftoptionNames [i ];
473+ options .add (optionName + "=" + ftoptionValues [i ]);
474+ }
453475
454- extTable .setFormatOptions (appendFormatOptions (res ));
476+ ParserAbstract .fillOptionParams (options .toArray (String []::new ), extTable ::addOption ,
477+ false , true , false );
478+ } else {
479+ String options = PgDiffUtils .unquoteQuotedString (res .getString ("options" ), 1 );
480+ if (!options .isBlank ()) {
481+ ParserAbstract .fillOptionParams (options .split ("," ), extTable ::addOption , false , true , false );
482+ }
483+ extTable .setFormatOptions (appendFormatOptions (formatOptions ));
484+ }
455485
456- extTable .setRejectLimit (res .getInt ("rejlim" ));
457- extTable .setIsLogErrors (res .getBoolean ("logerrors" ));
458- extTable .setRowReject (!"p" .equals (res .getString ("rejtyp" )));
486+ var rejtype = res .getString ("rejtype" );
487+ if (rejtype != null ) {
488+ extTable .setRejectLimit (res .getInt ("rejlim" ));
489+ extTable .setIsLogErrors (res .getBoolean ("logerrors" ));
490+ extTable .setRowReject (!"p" .equals (rejtype ));
491+ }
459492 extTable .setEncoding (PgDiffUtils .quoteString (res .getString ("enc" )));
460493 extTable .setWritable (res .getBoolean ("writable" ));
461494
462495 String distribution = res .getString ("distribution" );
463496 if (distribution != null && !distribution .isBlank ()) {
464497 extTable .setDistribution (distribution );
465498 }
499+ return extTable ;
466500 }
467501
468- private String appendFormatOptions (ResultSet res ) throws SQLException {
469- String formatOptions = res .getString ("fmtopts" );
502+ private String appendFormatOptions (String formatOptions ) {
470503 if (formatOptions .contains ("formatter" )) {
471504 return formatOptions .trim ().replace (" " , "=" );
472505 }
@@ -521,8 +554,6 @@ protected void fillQueryBuilder(QueryBuilder builder) {
521554 .column ("res.relispartition" )
522555 .column ("pg_catalog.pg_get_partkeydef(res.oid) AS partition_by" )
523556 .column ("pg_catalog.pg_get_expr(res.relpartbound, res.oid) AS partition_bound" );
524- } else {
525- builder .column ("res.relhasoids AS has_oids" );
526557 }
527558
528559 if (loader .isGreenplumDb ()) {
@@ -534,15 +565,22 @@ protected void fillQueryBuilder(QueryBuilder builder) {
534565 .column ("x.fmtopts" )
535566 .column ("x.command" )
536567 .column ("x.rejectlimit AS rejlim" )
537- .column ("x.rejectlimittype AS rejtyp " )
568+ .column ("x.rejectlimittype AS rejtype " )
538569 .column ("x.logerrors" )
539570 .column ("x.options" )
540571 .column ("pg_catalog.pg_encoding_to_char(x.encoding) AS enc" )
541572 .column ("x.writable" )
542573 .join ("LEFT JOIN pg_exttable x ON res.oid = x.reloid" );
543574
544- if (!SupportedPgVersion .GP_VERSION_7 .isLE (loader .getVersion ())) {
575+ if (SupportedPgVersion .GP_VERSION_7 .isLE (loader .getVersion ())) {
576+ builder .column (("ARRAY(SELECT option_name FROM pg_options_to_table(ftbl.ftoptions) " +
577+ "WHERE option_name NOT IN (%s)) AS ftoption_names" ).formatted (GP_SYSTEM_OPTIONS ));
578+ builder .column (("ARRAY(SELECT option_value FROM pg_options_to_table(ftbl.ftoptions) " +
579+ "WHERE option_name NOT IN (%s)) AS ftoption_values" ).formatted (GP_SYSTEM_OPTIONS ));
580+ } else {
545581 builder
582+ .column ("res.relhasoids AS has_oids" )
583+ .column ("x.options" )
546584 .column ("CASE WHEN pl.parlevel = 0 THEN (SELECT pg_get_partition_def(res.oid, true, false)) END AS partclause" )
547585 .column ("CASE WHEN pl.parlevel = 0 THEN (SELECT pg_get_partition_template_def(res.oid, true, false)) END as parttemplate" )
548586 .join ("LEFT JOIN pg_partitions ps on (res.relname = ps.partitiontablename)" )
0 commit comments