Added support for a multi-level partitioned table. Fixes #2554.
Before Width: | Height: | Size: 247 KiB After Width: | Height: | Size: 228 KiB |
BIN
docs/en_US/images/table_partition_tree.png
Normal file
After Width: | Height: | Size: 35 KiB |
@ -9,6 +9,7 @@ This release contains a number of bug fixes and new features since the release o
|
||||
New features
|
||||
************
|
||||
|
||||
| `Issue #2554 <https://redmine.postgresql.org/issues/2554>`_ - Added support for a multi-level partitioned table.
|
||||
| `Issue #3452 <https://redmine.postgresql.org/issues/3452>`_ - Added a Schema Diff tool to compare two schemas and generate a diff script.
|
||||
|
||||
Housekeeping
|
||||
|
@ -26,6 +26,8 @@ Use the fields in the *General* tab to identify the table:
|
||||
drop-down listbox in the *Schema* field.
|
||||
* Use the drop-down listbox in the *Tablespace* field to specify the tablespace
|
||||
in which the table will be stored.
|
||||
* Move the *Partitioned Table?* switch to the *Yes* in case you want to create a
|
||||
partitioned table. Option is available for PostgreSQL 10 and above.
|
||||
* Store notes about the table in the *Comment* field.
|
||||
|
||||
Click the *Columns* tab to continue.
|
||||
@ -449,6 +451,21 @@ icon (+) to add each partition:
|
||||
* If partition type is Hash then *Modulus* and *Remainder* fields will be
|
||||
enabled.
|
||||
|
||||
Users can create a partition and define them as a partitioned table. Click
|
||||
the *Edit* icon to expand the properties of a partition.
|
||||
Use the *Partition* tab to create that partition as a partitioned table.
|
||||
|
||||
* Move the *Partitioned Table?* switch to the *Yes* in case you want to create a
|
||||
partitioned table.
|
||||
* Select a partition type from the *Partition Type* selection box.
|
||||
* Use the *Partition Keys* panel to define the partition keys.
|
||||
|
||||
View of multi level Partitioned Table in browser tree:
|
||||
|
||||
.. image:: images/table_partition_tree.png
|
||||
:alt: Table dialog partition tree
|
||||
:align: center
|
||||
|
||||
Click the *Parameter* tab to continue.
|
||||
|
||||
.. image:: images/table_parameter.png
|
||||
|
@ -19,5 +19,12 @@ class BasePartitionTable:
|
||||
|
||||
def get_icon_css_class(self, table_info, default_val='icon-table'):
|
||||
if self.is_table_partitioned(table_info):
|
||||
return 'icon-partition'
|
||||
return 'icon-partition_table'
|
||||
return default_val
|
||||
|
||||
def get_partition_icon_css_class(self, table_info,
|
||||
default_val='icon-partition'):
|
||||
if 'is_sub_partitioned' in table_info and \
|
||||
table_info['is_sub_partitioned']:
|
||||
return 'icon-sub_partition_table'
|
||||
return default_val
|
||||
|
@ -153,6 +153,24 @@ class PartitionsModule(CollectionNodeModule):
|
||||
"""
|
||||
return False
|
||||
|
||||
@property
|
||||
def csssnippets(self):
|
||||
"""
|
||||
Returns a snippet of css to include in the page
|
||||
"""
|
||||
snippets = [
|
||||
render_template(
|
||||
"partitions/css/partition.css",
|
||||
node_type=self.node_type,
|
||||
_=gettext
|
||||
)
|
||||
]
|
||||
|
||||
for submodule in self.submodules:
|
||||
snippets.extend(submodule.csssnippets)
|
||||
|
||||
return snippets
|
||||
|
||||
|
||||
blueprint = PartitionsModule(__name__)
|
||||
|
||||
@ -194,7 +212,7 @@ class PartitionsView(BaseTableView, DataTypeReader, VacuumSettings,
|
||||
operations = dict({
|
||||
'obj': [
|
||||
{'get': 'properties', 'delete': 'delete', 'put': 'update'},
|
||||
{'get': 'list', 'post': 'create'}
|
||||
{'get': 'list', 'post': 'create', 'delete': 'delete'}
|
||||
],
|
||||
'delete': [{'delete': 'delete'}, {'delete': 'delete'}],
|
||||
'nodes': [{'get': 'nodes'}, {'get': 'nodes'}],
|
||||
@ -287,11 +305,12 @@ class PartitionsView(BaseTableView, DataTypeReader, VacuumSettings,
|
||||
return internal_server_error(errormsg=rset)
|
||||
|
||||
def browser_node(row):
|
||||
icon = self.get_partition_icon_css_class(row)
|
||||
return self.blueprint.generate_browser_node(
|
||||
row['oid'],
|
||||
tid,
|
||||
row['name'],
|
||||
icon=self.get_icon_css_class({}),
|
||||
icon=icon,
|
||||
tigger_count=row['triggercount'],
|
||||
has_enable_triggers=row['has_enable_triggers'],
|
||||
is_partitioned=row['is_partitioned'],
|
||||
|
@ -1 +1,35 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-1{fill:#def4fd;}.cls-2{fill:#717f8e;}.cls-3,.cls-5,.cls-6{fill:none;stroke-width:0.75px;}.cls-3{stroke:#717f8e;stroke-linecap:round;}.cls-3,.cls-5{stroke-linejoin:round;}.cls-4{fill:#2195e7;}.cls-5{stroke:#c1cbd5;}.cls-6{stroke:#2195e7;stroke-miterlimit:10;}</style></defs><title>coll-partition</title><g id="_2" data-name="2"><path class="cls-1" d="M3.5,13.13a.63.63,0,0,1-.62-.62V11.13l8.25,0V12.5a.63.63,0,0,1-.62.63Z"/><path class="cls-2" d="M3.25,11.5l7.5,0v1a.25.25,0,0,1-.25.25h-7a.25.25,0,0,1-.25-.25v-1m-.75-.75V12.5a1,1,0,0,0,1,1h7a1,1,0,0,0,1-1V10.78l-9,0Z"/><path class="cls-1" d="M2.88,7.13V3.5a.63.63,0,0,1,.63-.62h7a.63.63,0,0,1,.63.63V7.13Z"/><path class="cls-2" d="M10.5,3.25a.25.25,0,0,1,.25.25V6.75H3.25V3.5a.25.25,0,0,1,.25-.25h7m0-.75h-7a1,1,0,0,0-1,1v4h9v-4a1,1,0,0,0-1-1Z"/><polygon class="cls-1" points="2.88 7.13 2.88 5.13 11.13 5.16 11.13 7.13 2.88 7.13"/><path class="cls-2" d="M3.25,5.5l7.5,0V6.75H3.25V5.5M2.5,4.75V7.5h9V4.78l-9,0Z"/><polygon class="cls-1" points="2.88 11.13 2.88 9.13 11.13 9.15 11.13 11.13 2.88 11.13"/><path class="cls-2" d="M3.25,9.5l7.5,0v1.22H3.25V9.5M2.5,8.75V11.5h9V8.78l-9,0Z"/><line class="cls-3" x1="7" y1="2.89" x2="7" y2="7.05"/><line class="cls-3" x1="7" y1="9.58" x2="7" y2="13.05"/><polygon class="cls-1" points="4.88 11.13 4.88 9.13 13.13 9.15 13.13 11.13 4.88 11.13"/><path class="cls-4" d="M5.25,9.5l7.5,0v1.22H5.25V9.5M4.5,8.75V11.5h9V8.78l-9,0Z"/><path class="cls-1" d="M4.5,7.5v-4a1,1,0,0,1,1-1h7a1,1,0,0,1,1,1v4Z"/><line class="cls-5" x1="9" y1="5.5" x2="9" y2="7"/><path class="cls-4" d="M5.25,5.5l7.5,0V6.75H5.25V5.5M4.5,4.75V7.5h9V4.78l-9,0Z"/><line class="cls-6" x1="9" y1="2.5" x2="9" y2="5.5"/><path class="cls-4" d="M12.5,3.25a.25.25,0,0,1,.25.25V4.75H5.25V3.5a.25.25,0,0,1,.25-.25h7m0-.75h-7a1,1,0,0,0-1,1v2h9v-2a1,1,0,0,0-1-1Z"/><path class="cls-1" d="M12.5,13.5h-7a1,1,0,0,1-1-1v-1h9v1A1,1,0,0,1,12.5,13.5Z"/><line class="cls-5" x1="9" y1="11.5" x2="9" y2="12.97"/><line class="cls-5" x1="9" y1="9.5" x2="9" y2="11.97"/><path class="cls-4" d="M5.25,11.5l7.5,0v1a.25.25,0,0,1-.25.25h-7a.25.25,0,0,1-.25-.25v-1m-.75-.75V12.5a1,1,0,0,0,1,1h7a1,1,0,0,0,1-1V10.78l-9,0Z"/></g></svg>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 24.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#F2F2F2;}
|
||||
.st1{fill:none;stroke:#C1CBD5;stroke-width:0.75;stroke-linejoin:round;}
|
||||
.st2{fill:#2195E7;}
|
||||
</style>
|
||||
<title>partition</title>
|
||||
<g>
|
||||
<g id="_2_1_">
|
||||
<path class="st0" d="M0.5,7V1c0-0.6,0.4-1,1-1h9c0.6,0,1,0.4,1,1v6H0.5z"/>
|
||||
<line class="st1" x1="6" y1="4" x2="6" y2="6.5"/>
|
||||
<g>
|
||||
<path class="st2" d="M10.5,0H6.4H5.6H1.5c-0.6,0-1,0.4-1,1v2.2l0,0V4v3h11V4V3.3V1C11.5,0.4,11.1,0,10.5,0z M10.5,0.8
|
||||
c0.1,0,0.2,0.1,0.2,0.2v2.2H6.4V0.8H10.5z M1.2,1c0-0.1,0.1-0.2,0.2-0.2h4.1v2.5H1.2V1z M10.8,6.2H1.2V4h4.4h0.8h4.4
|
||||
C10.8,4,10.8,6.2,10.8,6.2z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="_2">
|
||||
<path class="st0" d="M2.5,9V3c0-0.6,0.4-1,1-1h9c0.6,0,1,0.4,1,1v6H2.5z"/>
|
||||
<line class="st1" x1="8" y1="6" x2="8" y2="8.5"/>
|
||||
<path class="st0" d="M12.5,14h-9c-0.6,0-1-0.4-1-1v-2h11v2C13.5,13.6,13.1,14,12.5,14z"/>
|
||||
<line class="st1" x1="8" y1="10.5" x2="8" y2="13.5"/>
|
||||
<g>
|
||||
<path class="st2" d="M12.5,2H8.4H7.6H3.5c-0.6,0-1,0.4-1,1v2.2l0,0V6v3h11V6V5.3V3C13.5,2.4,13.1,2,12.5,2z M12.5,2.8
|
||||
c0.1,0,0.2,0.1,0.2,0.2v2.2H8.4V2.8H12.5z M3.2,3c0-0.1,0.1-0.2,0.2-0.2h4.1v2.5H3.2V3z M12.8,8.2H3.2V6h4.4h0.8h4.4
|
||||
C12.8,6,12.8,8.2,12.8,8.2z"/>
|
||||
<path class="st2" d="M2.5,13c0,0.6,0.4,1,1,1h9c0.6,0,1-0.4,1-1v-2.7h-11V13z M3.2,11h9.5v2c0,0.1-0.1,0.2-0.2,0.2h-9
|
||||
c-0.1,0-0.2-0.1-0.2-0.2L3.2,11L3.2,11z"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 1.7 KiB |
@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 24.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#F2F2F2;}
|
||||
.st1{fill:#C1CBD5;}
|
||||
.st2{fill:#2195E7;}
|
||||
.st3{fill:#F2F2F2;stroke:#34495E;stroke-width:0.5;stroke-linejoin:bevel;stroke-miterlimit:10;}
|
||||
.st4{fill:#34495E;}
|
||||
</style>
|
||||
<title>inherits</title>
|
||||
<g>
|
||||
<path class="st0" d="M3.5,3h9c0.6,0,1,0.4,1,1v8c0,0.6-0.4,1-1,1h-9c-0.6,0-1-0.4-1-1V4C2.5,3.4,2.9,3,3.5,3z"/>
|
||||
<polygon class="st1" points="13,9.1 8.4,9.1 8.4,6 7.6,6 7.6,9.1 3,9.1 3,9.9 7.6,9.9 7.6,13 8.4,13 8.4,9.9 13,9.9 "/>
|
||||
<path class="st2" d="M12.5,3h-9c-0.6,0-1,0.4-1,1v8c0,0.6,0.4,1,1,1h9c0.6,0,1-0.4,1-1V4C13.5,3.4,13.1,3,12.5,3z M12.5,3.8
|
||||
c0.1,0,0.2,0.1,0.2,0.2v2.1H8.4V3.8H12.5z M3.5,3.8h4.1v2.4H3.2V4C3.2,3.9,3.4,3.8,3.5,3.8z M12.5,12.2h-9c-0.1,0-0.2-0.1-0.2-0.2
|
||||
V6.9h9.5V12C12.8,12.1,12.6,12.2,12.5,12.2z"/>
|
||||
<path class="st3" d="M4.1,7.6c-2,0-3.6-1.6-3.6-3.5c0-2,1.6-3.5,3.6-3.5c1.9,0,3.5,1.5,3.5,3.5C7.6,6,6,7.6,4.1,7.6L4.1,7.6z"/>
|
||||
<path class="st4" d="M4.5,2.2H3.7v0H2.9v4h0.8V4.8h0.8c0.7,0,1.3-0.6,1.3-1.3S5.2,2.2,4.5,2.2z M4.5,4.1H3.7V2.9h0.9
|
||||
c0.3,0,0.6,0.3,0.6,0.6S4.9,4.1,4.5,4.1z"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.4 KiB |
@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 24.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#F2F2F2;}
|
||||
.st1{fill:none;stroke:#C1CBD5;stroke-width:0.75;stroke-linejoin:round;}
|
||||
.st2{fill:#2195E7;}
|
||||
.st3{fill:#F2F2F2;stroke:#34495E;stroke-width:0.5;stroke-linejoin:bevel;stroke-miterlimit:10;}
|
||||
.st4{fill:#34495E;}
|
||||
</style>
|
||||
<title>partition</title>
|
||||
<g>
|
||||
<path class="st0" d="M2.5,10.1v-6c0-0.6,0.4-1,1-1h9c0.6,0,1,0.4,1,1v6H2.5z"/>
|
||||
<line class="st1" x1="8" y1="7.1" x2="8" y2="9.6"/>
|
||||
<path class="st2" d="M12.5,3.1H8.4H7.6H3.5c-0.6,0-1,0.4-1,1v2.2l0,0v0.8v3h11v-3V6.4V4.1C13.5,3.5,13.1,3.1,12.5,3.1z M12.5,3.9
|
||||
c0.1,0,0.2,0.1,0.2,0.2v2.2H8.4V3.9H12.5z M3.2,4.1c0-0.1,0.1-0.2,0.2-0.2h4.1v2.5H3.2V4.1z M12.8,9.3H3.2V7.1h4.4h0.8h4.4
|
||||
C12.8,7.1,12.8,9.3,12.8,9.3z"/>
|
||||
<g>
|
||||
<path class="st0" d="M12.5,14.5h-9c-0.6,0-1-0.4-1-1v-2h11v2C13.5,14.1,13.1,14.5,12.5,14.5z"/>
|
||||
<line class="st1" x1="8" y1="11" x2="8" y2="14"/>
|
||||
<path class="st2" d="M2.5,13.5c0,0.6,0.4,1,1,1h9c0.6,0,1-0.4,1-1v-2.7h-11V13.5z M3.2,11.5h9.5v2c0,0.1-0.1,0.2-0.2,0.2h-9
|
||||
c-0.1,0-0.2-0.1-0.2-0.2L3.2,11.5L3.2,11.5z"/>
|
||||
</g>
|
||||
<path class="st3" d="M5,8.5C3,8.5,1.4,6.9,1.4,5C1.4,3,3,1.5,5,1.5C6.9,1.5,8.5,3,8.5,5C8.5,6.9,6.9,8.5,5,8.5L5,8.5z"/>
|
||||
<path class="st4" d="M5.4,3.1H4.6l0,0H3.8v4h0.8V5.7h0.8c0.7,0,1.3-0.6,1.3-1.3S6.1,3.1,5.4,3.1z M5.4,5H4.6V3.8h0.9
|
||||
c0.3,0,0.6,0.3,0.6,0.6S5.8,5,5.4,5z"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.6 KiB |
@ -562,7 +562,9 @@ function(
|
||||
control: 'unique-col-collection',
|
||||
columns : ['name', 'columns'],
|
||||
canAdd: function(m) {
|
||||
if (m.get('is_partitioned')) {
|
||||
if (m.get('is_partitioned') && !_.isUndefined(m.top.node_info) && !_.isUndefined(m.top.node_info.server)
|
||||
&& !_.isUndefined(m.top.node_info.server.version) &&
|
||||
m.top.node_info.server.version < 110000) {
|
||||
setTimeout(function() {
|
||||
var coll = m.get('primary_key');
|
||||
coll.remove(coll.filter(function() { return true; }));
|
||||
@ -861,7 +863,7 @@ function(
|
||||
editable: true, type: 'collection',
|
||||
group: 'partition', mode: ['edit', 'create'],
|
||||
deps: ['is_partitioned', 'partition_type'],
|
||||
canEdit: false, canDelete: true,
|
||||
canEdit: true, canDelete: true,
|
||||
customDeleteTitle: gettext('Detach Partition'),
|
||||
customDeleteMsg: gettext('Are you sure you wish to detach this partition?'),
|
||||
columns:['is_attach', 'partition_name', 'is_default', 'values_from', 'values_to', 'values_in', 'values_modulus', 'values_remainder'],
|
||||
|
@ -0,0 +1,36 @@
|
||||
.icon-coll-partition {
|
||||
background-image: url('{{ url_for('NODE-partition.static', filename='img/coll-partition.svg') }}') !important;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 20px !important;
|
||||
align-content: center;
|
||||
vertical-align: middle;
|
||||
height: 1.3em;
|
||||
}
|
||||
|
||||
.icon-partition {
|
||||
background-image: url('{{ url_for('NODE-partition.static', filename='img/partition.svg') }}') !important;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 20px !important;
|
||||
align-content: center;
|
||||
vertical-align: middle;
|
||||
height: 1.3em;
|
||||
}
|
||||
|
||||
.icon-partition_table {
|
||||
background-image: url('{{ url_for('NODE-partition.static', filename='img/partition_table.svg') }}') !important;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 20px !important;
|
||||
align-content: center;
|
||||
vertical-align: middle;
|
||||
height: 1.3em;
|
||||
}
|
||||
|
||||
.icon-sub_partition_table {
|
||||
background-image: url('{{ url_for('NODE-partition.static', filename='img/sub_partition_table.svg') }}') !important;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 20px !important;
|
||||
align-content: center;
|
||||
vertical-align: middle;
|
||||
border-radius: 10px;
|
||||
height: 1.3em;
|
||||
}
|
@ -248,10 +248,13 @@ define('pgadmin.node.table_partition_utils', [
|
||||
values_in: undefined,
|
||||
values_modulus: undefined,
|
||||
values_remainder: undefined,
|
||||
is_sub_partitioned: false,
|
||||
sub_partition_type: 'range',
|
||||
},
|
||||
keys:['partition_name'],
|
||||
schema: [{
|
||||
id: 'oid', label: gettext('OID'), type: 'text',
|
||||
mode: ['properties'],
|
||||
},{
|
||||
id: 'is_attach', label:gettext('Operation'), cell: 'switch', type: 'switch',
|
||||
options: {'onText': gettext('Attach'), 'offText': gettext('Create'), 'width': 65},
|
||||
@ -261,6 +264,11 @@ define('pgadmin.node.table_partition_utils', [
|
||||
return true;
|
||||
return false;
|
||||
},
|
||||
disabled: function(m) {
|
||||
if (m instanceof Backbone.Model && m.isNew() && !m.top.isNew())
|
||||
return false;
|
||||
return true;
|
||||
},
|
||||
},{
|
||||
id: 'partition_name', label: gettext('Name'), type: 'text', cell:'string',
|
||||
cellHeaderClasses: 'width_percent_15',
|
||||
@ -269,6 +277,11 @@ define('pgadmin.node.table_partition_utils', [
|
||||
return true;
|
||||
return false;
|
||||
}, cellFunction: getPartitionCell,
|
||||
disabled: function(m) {
|
||||
if (m instanceof Backbone.Model && m.isNew())
|
||||
return false;
|
||||
return true;
|
||||
},
|
||||
},{
|
||||
id: 'is_default', label: gettext('Default'), type: 'switch', cell:'switch',
|
||||
cellHeaderClasses: 'width_percent_5', min_version: 110000,
|
||||
@ -283,6 +296,16 @@ define('pgadmin.node.table_partition_utils', [
|
||||
return true;
|
||||
return false;
|
||||
},
|
||||
disabled: function(m) {
|
||||
if(m.handler && m.handler.top &&
|
||||
m.handler.top.attributes &&
|
||||
(m.handler.top.attributes.partition_type === 'range' ||
|
||||
m.handler.top.attributes.partition_type === 'list') &&
|
||||
m instanceof Backbone.Model && m.isNew() &&
|
||||
m.handler.top.node_info.server.version >= 110000)
|
||||
return false;
|
||||
return true;
|
||||
},
|
||||
},{
|
||||
id: 'values_from', label: gettext('From'), type:'text',
|
||||
cell:Backgrid.Extension.StringDepCell, deps: ['is_default'],
|
||||
@ -295,6 +318,14 @@ define('pgadmin.node.table_partition_utils', [
|
||||
return true;
|
||||
return false;
|
||||
},
|
||||
disabled: function(m) {
|
||||
if(m.handler && m.handler.top &&
|
||||
m.handler.top.attributes &&
|
||||
m.handler.top.attributes.partition_type === 'range' &&
|
||||
m instanceof Backbone.Model && m.isNew() && m.get('is_default') !== true)
|
||||
return false;
|
||||
return true;
|
||||
},
|
||||
},{
|
||||
id: 'values_to', label: gettext('To'), type:'text',
|
||||
cell:Backgrid.Extension.StringDepCell, deps: ['is_default'],
|
||||
@ -307,6 +338,14 @@ define('pgadmin.node.table_partition_utils', [
|
||||
return true;
|
||||
return false;
|
||||
},
|
||||
disabled: function(m) {
|
||||
if(m.handler && m.handler.top &&
|
||||
m.handler.top.attributes &&
|
||||
m.handler.top.attributes.partition_type === 'range' &&
|
||||
m instanceof Backbone.Model && m.isNew() && m.get('is_default') !== true)
|
||||
return false;
|
||||
return true;
|
||||
},
|
||||
},{
|
||||
id: 'values_in', label: gettext('In'), type:'text',
|
||||
cell:Backgrid.Extension.StringDepCell, deps: ['is_default'],
|
||||
@ -319,6 +358,14 @@ define('pgadmin.node.table_partition_utils', [
|
||||
return true;
|
||||
return false;
|
||||
},
|
||||
disabled: function(m) {
|
||||
if(m.handler && m.handler.top &&
|
||||
m.handler.top.attributes &&
|
||||
m.handler.top.attributes.partition_type === 'list' &&
|
||||
m instanceof Backbone.Model && m.isNew() && m.get('is_default') !== true)
|
||||
return false;
|
||||
return true;
|
||||
},
|
||||
},{
|
||||
id: 'values_modulus', label: gettext('Modulus'), type:'int',
|
||||
cell:Backgrid.Extension.StringDepCell,
|
||||
@ -331,6 +378,14 @@ define('pgadmin.node.table_partition_utils', [
|
||||
return true;
|
||||
return false;
|
||||
},
|
||||
disabled: function(m) {
|
||||
if(m.handler && m.handler.top &&
|
||||
m.handler.top.attributes &&
|
||||
m.handler.top.attributes.partition_type === 'hash' &&
|
||||
m instanceof Backbone.Model && m.isNew())
|
||||
return false;
|
||||
return true;
|
||||
},
|
||||
},{
|
||||
id: 'values_remainder', label: gettext('Remainder'), type:'int',
|
||||
cell:Backgrid.Extension.StringDepCell,
|
||||
@ -343,6 +398,131 @@ define('pgadmin.node.table_partition_utils', [
|
||||
return true;
|
||||
return false;
|
||||
},
|
||||
disabled: function(m) {
|
||||
if(m.handler && m.handler.top &&
|
||||
m.handler.top.attributes &&
|
||||
m.handler.top.attributes.partition_type === 'hash' &&
|
||||
m instanceof Backbone.Model && m.isNew())
|
||||
return false;
|
||||
return true;
|
||||
},
|
||||
},{
|
||||
id: 'is_sub_partitioned', label:gettext('Partitioned table?'), cell: 'switch',
|
||||
group: 'Partition', type: 'switch', mode: ['properties', 'create', 'edit'],
|
||||
deps: ['is_attach'],
|
||||
disabled: function(m) {
|
||||
if(!m.isNew())
|
||||
return true;
|
||||
|
||||
if (m.get('is_attach')) {
|
||||
setTimeout( function() {
|
||||
m.set('is_sub_partitioned', false);
|
||||
}, 10);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
},{
|
||||
id: 'sub_partition_type', label:gettext('Partition Type'),
|
||||
editable: false, type: 'select2', select2: {allowClear: false},
|
||||
group: 'Partition', deps: ['is_sub_partitioned'],
|
||||
options: function() {
|
||||
var options = [{
|
||||
label: gettext('Range'), value: 'range',
|
||||
},{
|
||||
label: gettext('List'), value: 'list',
|
||||
}];
|
||||
|
||||
if(!_.isUndefined(this.node_info) && !_.isUndefined(this.node_info.server)
|
||||
&& !_.isUndefined(this.node_info.server.version) &&
|
||||
this.node_info.server.version >= 110000) {
|
||||
options.push({
|
||||
label: gettext('Hash'), value: 'hash',
|
||||
});
|
||||
}
|
||||
return options;
|
||||
},
|
||||
visible: function(m) {
|
||||
if (m.isNew())
|
||||
return true;
|
||||
return false;
|
||||
},
|
||||
disabled: function(m) {
|
||||
if (!m.isNew() || !m.get('is_sub_partitioned'))
|
||||
return true;
|
||||
return false;
|
||||
},
|
||||
},{
|
||||
id: 'sub_partition_keys', label:gettext('Partition Keys'),
|
||||
model: Backform.PartitionKeyModel,
|
||||
subnode: Backform.PartitionKeyModel,
|
||||
editable: true, type: 'collection',
|
||||
group: 'Partition', mode: ['properties', 'create', 'edit'],
|
||||
deps: ['is_sub_partitioned', 'sub_partition_type'],
|
||||
canEdit: false, canDelete: true,
|
||||
control: 'sub-node-collection',
|
||||
canAdd: function(m) {
|
||||
if (m.isNew() && m.get('is_sub_partitioned'))
|
||||
return true;
|
||||
return false;
|
||||
},
|
||||
canAddRow: function(m) {
|
||||
var columns = m.top.get('columns'),
|
||||
typename = m.top.get('typname'),
|
||||
columns_exist= false;
|
||||
|
||||
var max_row_count = 1000;
|
||||
if (m.get('sub_partition_type') && m.get('sub_partition_type') == 'list')
|
||||
max_row_count = 1;
|
||||
|
||||
/* If columns are not specified by the user then it may be
|
||||
* possible that he/she selected 'OF TYPE', so we should check
|
||||
* for that as well.
|
||||
*/
|
||||
if (columns.length <= 0 && !_.isUndefined(typename)
|
||||
&& !_.isNull(typename) && m.of_types_tables.length > 0){
|
||||
_.each(m.of_types_tables, function(data) {
|
||||
if (data.label == typename && data.oftype_columns.length > 0){
|
||||
columns_exist = true;
|
||||
}
|
||||
});
|
||||
} else if (columns.length > 0) {
|
||||
columns_exist = _.some(columns.pluck('name'));
|
||||
}
|
||||
|
||||
return (m.get('sub_partition_keys') &&
|
||||
m.get('sub_partition_keys').length < max_row_count && columns_exist
|
||||
);
|
||||
|
||||
},
|
||||
disabled: function(m) {
|
||||
if (m.get('sub_partition_keys') && m.get('sub_partition_keys').models.length > 0) {
|
||||
setTimeout(function () {
|
||||
var coll = m.get('sub_partition_keys');
|
||||
coll.remove(coll.filter(function() { return true; }));
|
||||
|
||||
}, 10);
|
||||
}
|
||||
},
|
||||
visible: function(m) {
|
||||
if (m.isNew())
|
||||
return true;
|
||||
return false;
|
||||
},
|
||||
},{
|
||||
id: 'sub_partition_scheme', label: gettext('Partition Scheme'),
|
||||
type: 'note', group: 'Partition', mode: ['edit'],
|
||||
visible: function(m) {
|
||||
if (!m.isNew() && !_.isUndefined(m.get('sub_partition_scheme')) &&
|
||||
m.get('sub_partition_scheme') != '')
|
||||
return true;
|
||||
return false;
|
||||
},
|
||||
disabled: function(m) {
|
||||
if (!m.isNew()) {
|
||||
this.text = m.get('sub_partition_scheme');
|
||||
}
|
||||
},
|
||||
}],
|
||||
validate: function() {
|
||||
var partition_name = this.get('partition_name'),
|
||||
@ -352,11 +532,14 @@ define('pgadmin.node.table_partition_utils', [
|
||||
values_in = this.get('values_in'),
|
||||
values_modulus = this.get('values_modulus'),
|
||||
values_remainder = this.get('values_remainder'),
|
||||
is_sub_partitioned = this.get('is_sub_partitioned'),
|
||||
sub_partition_keys = this.get('sub_partition_keys'),
|
||||
msg;
|
||||
|
||||
// Have to clear existing validation before initiating current state
|
||||
// validation only
|
||||
this.errorModel.clear();
|
||||
this.top.errorModel.clear();
|
||||
|
||||
if (_.isUndefined(partition_name) || _.isNull(partition_name) ||
|
||||
String(partition_name).replace(/^\s+|\s+$/g, '') === '') {
|
||||
@ -365,6 +548,13 @@ define('pgadmin.node.table_partition_utils', [
|
||||
return msg;
|
||||
}
|
||||
|
||||
if (is_sub_partitioned && this.isNew() &&
|
||||
!_.isNull(sub_partition_keys) && sub_partition_keys.length <= 0) {
|
||||
msg = gettext('Please specify at least one key for partitioned table.');
|
||||
this.top.errorModel.set('sub_partition_keys', msg);
|
||||
return msg;
|
||||
}
|
||||
|
||||
if (this.top.get('partition_type') === 'range') {
|
||||
if (is_default !== true && (_.isUndefined(values_from) ||
|
||||
_.isNull(values_from) || String(values_from).replace(/^\s+|\s+$/g, '') === '')) {
|
||||
|
@ -1004,7 +1004,7 @@ define('pgadmin.node.table', [
|
||||
editable: true, type: 'collection',
|
||||
group: 'partition', mode: ['edit', 'create'],
|
||||
deps: ['is_partitioned', 'partition_type', 'typname'],
|
||||
canEdit: false, canDelete: true,
|
||||
canEdit: true, canDelete: true,
|
||||
customDeleteTitle: gettext('Detach Partition'),
|
||||
customDeleteMsg: gettext('Are you sure you wish to detach this partition?'),
|
||||
columns:['is_attach', 'partition_name', 'is_default', 'values_from', 'values_to', 'values_in', 'values_modulus', 'values_remainder'],
|
||||
|
@ -5,7 +5,9 @@ SELECT rel.oid, rel.relname AS name,
|
||||
rel.relnamespace AS schema_id,
|
||||
nsp.nspname AS schema_name,
|
||||
(CASE WHEN rel.relkind = 'p' THEN true ELSE false END) AS is_partitioned,
|
||||
(CASE WHEN rel.relkind = 'p' THEN pg_get_partkeydef(rel.oid::oid) ELSE '' END) AS partition_scheme
|
||||
(CASE WHEN rel.relkind = 'p' THEN true ELSE false END) AS is_sub_partitioned,
|
||||
(CASE WHEN rel.relkind = 'p' THEN pg_get_partkeydef(rel.oid::oid) ELSE '' END) AS partition_scheme,
|
||||
(CASE WHEN rel.relkind = 'p' THEN pg_get_partkeydef(rel.oid::oid) ELSE '' END) AS sub_partition_scheme
|
||||
FROM
|
||||
(SELECT * FROM pg_inherits WHERE inhparent = {{ tid }}::oid) inh
|
||||
LEFT JOIN pg_class rel ON inh.inhrelid = rel.oid
|
||||
|
@ -5,7 +5,9 @@ SELECT rel.oid, rel.relname AS name,
|
||||
rel.relnamespace AS schema_id,
|
||||
nsp.nspname AS schema_name,
|
||||
(CASE WHEN rel.relkind = 'p' THEN true ELSE false END) AS is_partitioned,
|
||||
(CASE WHEN rel.relkind = 'p' THEN pg_get_partkeydef(rel.oid::oid) ELSE '' END) AS partition_scheme
|
||||
(CASE WHEN rel.relkind = 'p' THEN true ELSE false END) AS is_sub_partitioned,
|
||||
(CASE WHEN rel.relkind = 'p' THEN pg_get_partkeydef(rel.oid::oid) ELSE '' END) AS partition_scheme,
|
||||
(CASE WHEN rel.relkind = 'p' THEN pg_get_partkeydef(rel.oid::oid) ELSE '' END) AS sub_partition_scheme
|
||||
FROM
|
||||
(SELECT * FROM pg_inherits WHERE inhparent = {{ tid }}::oid) inh
|
||||
LEFT JOIN pg_class rel ON inh.inhrelid = rel.oid
|
||||
|
@ -67,13 +67,13 @@ class TestBasePartitionTable(BaseTestGenerator):
|
||||
|
||||
|
||||
('#get_icon_css_class when table is partitioned '
|
||||
'it returns icon-partition class',
|
||||
'it returns icon-partition_table class',
|
||||
dict(
|
||||
test='get_icon_css_class',
|
||||
input_parameters=dict(
|
||||
is_partitioned=True
|
||||
),
|
||||
expected_return='icon-partition'
|
||||
expected_return='icon-partition_table'
|
||||
)),
|
||||
('#get_icon_css_class when table is not partitioned '
|
||||
'it returns icon-table class',
|
||||
|
@ -44,6 +44,15 @@ class TableAddTestCase(BaseTestGenerator):
|
||||
'PPAS/PG 10.0 and below.'
|
||||
)
|
||||
),
|
||||
('Create Multilevel Range partitioned table with subpartition table',
|
||||
dict(url='/browser/table/obj/',
|
||||
server_min_version=100000,
|
||||
partition_type='range',
|
||||
multilevel_partition=True,
|
||||
skip_msg='Partitioned table are not supported by '
|
||||
'PPAS/PG 10.0 and below.'
|
||||
)
|
||||
),
|
||||
('Create List partitioned table with 2 partitions',
|
||||
dict(url='/browser/table/obj/',
|
||||
server_min_version=100000,
|
||||
@ -52,6 +61,15 @@ class TableAddTestCase(BaseTestGenerator):
|
||||
'PPAS/PG 10.0 and below.'
|
||||
)
|
||||
),
|
||||
('Create Multilevel List partitioned table with subpartition table',
|
||||
dict(url='/browser/table/obj/',
|
||||
server_min_version=100000,
|
||||
partition_type='list',
|
||||
multilevel_partition=True,
|
||||
skip_msg='Partitioned table are not supported by '
|
||||
'PPAS/PG 10.0 and below.'
|
||||
)
|
||||
),
|
||||
('Create Hash partitioned table with 2 partitions',
|
||||
dict(url='/browser/table/obj/',
|
||||
server_min_version=110000,
|
||||
@ -205,63 +223,20 @@ class TableAddTestCase(BaseTestGenerator):
|
||||
data['partition_type'] = self.partition_type
|
||||
data['is_partitioned'] = True
|
||||
if self.partition_type == 'range':
|
||||
data['partitions'] = \
|
||||
[{'values_from': "'2010-01-01'",
|
||||
'values_to': "'2010-12-31'",
|
||||
'is_attach': False,
|
||||
'partition_name': 'emp_2010'
|
||||
},
|
||||
{'values_from': "'2011-01-01'",
|
||||
'values_to': "'2011-12-31'",
|
||||
'is_attach': False,
|
||||
'partition_name': 'emp_2011'
|
||||
}]
|
||||
if hasattr(self, 'is_default'):
|
||||
data['partitions'] = \
|
||||
[{'values_from': "'2010-01-01'",
|
||||
'values_to': "'2010-12-31'",
|
||||
'is_attach': False,
|
||||
'partition_name': 'emp_2010_def'
|
||||
},
|
||||
{'values_from': "'2011-01-01'",
|
||||
'values_to': "'2011-12-31'",
|
||||
'is_attach': False,
|
||||
'partition_name': 'emp_2011_def'
|
||||
},
|
||||
{'values_from': "",
|
||||
'values_to': "",
|
||||
'is_attach': False,
|
||||
'is_default': True,
|
||||
'partition_name': 'emp_2012_def'
|
||||
}]
|
||||
data['partition_keys'] = \
|
||||
[{'key_type': 'column', 'pt_column': 'DOJ'}]
|
||||
tables_utils.get_range_partitions_data(data, 'Default')
|
||||
elif hasattr(self, 'multilevel_partition'):
|
||||
tables_utils.get_range_partitions_data(
|
||||
data, None, True)
|
||||
else:
|
||||
tables_utils.get_range_partitions_data(data)
|
||||
elif self.partition_type == 'list':
|
||||
data['partitions'] = \
|
||||
[{'values_in': "'2012-01-01', '2012-12-31'",
|
||||
'is_attach': False,
|
||||
'partition_name': 'emp_2012'
|
||||
},
|
||||
{'values_in': "'2013-01-01', '2013-12-31'",
|
||||
'is_attach': False,
|
||||
'partition_name': 'emp_2013'
|
||||
}]
|
||||
data['partition_keys'] = \
|
||||
[{'key_type': 'column', 'pt_column': 'DOJ'}]
|
||||
if hasattr(self, 'multilevel_partition'):
|
||||
tables_utils.get_list_partitions_data(data, True)
|
||||
else:
|
||||
tables_utils.get_list_partitions_data(data)
|
||||
else:
|
||||
data['partitions'] = \
|
||||
[{'values_modulus': "24",
|
||||
'values_remainder': "3",
|
||||
'is_attach': False,
|
||||
'partition_name': 'emp_2016'
|
||||
},
|
||||
{'values_modulus': "8",
|
||||
'values_remainder': "2",
|
||||
'is_attach': False,
|
||||
'partition_name': 'emp_2017'
|
||||
}]
|
||||
data['partition_keys'] = \
|
||||
[{'key_type': 'column', 'pt_column': 'empno'}]
|
||||
tables_utils.get_hash_partitions_data(data)
|
||||
|
||||
# Add table
|
||||
response = self.tester.post(
|
||||
|
@ -33,6 +33,14 @@ class TableUpdateTestCase(BaseTestGenerator):
|
||||
mode='create'
|
||||
)
|
||||
),
|
||||
('Create partitions with partition table of existing range '
|
||||
'partitioned table',
|
||||
dict(url='/browser/table/obj/',
|
||||
server_min_version=100000,
|
||||
partition_type='range',
|
||||
mode='multilevel'
|
||||
)
|
||||
),
|
||||
('Create partitions of existing list partitioned table',
|
||||
dict(url='/browser/table/obj/',
|
||||
server_min_version=100000,
|
||||
@ -40,6 +48,14 @@ class TableUpdateTestCase(BaseTestGenerator):
|
||||
mode='create'
|
||||
)
|
||||
),
|
||||
('Create partitions with partition table of existing list '
|
||||
'partitioned table',
|
||||
dict(url='/browser/table/obj/',
|
||||
server_min_version=100000,
|
||||
partition_type='list',
|
||||
mode='multilevel'
|
||||
)
|
||||
),
|
||||
('Detach partition from existing range partitioned table',
|
||||
dict(url='/browser/table/obj/',
|
||||
server_min_version=100000,
|
||||
|
@ -190,6 +190,28 @@ def set_partition_data(server, db_name, schema_name, table_name,
|
||||
}]
|
||||
}
|
||||
)
|
||||
if partition_type == 'range' and mode == 'multilevel':
|
||||
data['partitions'].update(
|
||||
{'added': [{'values_from': "'2014-01-01'",
|
||||
'values_to': "'2014-12-31'",
|
||||
'is_attach': False,
|
||||
'partition_name': 'sale_2014_sub_part',
|
||||
'is_sub_partitioned': True,
|
||||
'sub_partition_type': 'range',
|
||||
'sub_partition_keys':
|
||||
[{'key_type': 'column', 'pt_column': 'sales'}]
|
||||
},
|
||||
{'values_from': "'2015-01-01'",
|
||||
'values_to': "'2015-12-31'",
|
||||
'is_attach': False,
|
||||
'partition_name': 'sale_2015_sub_part',
|
||||
'is_sub_partitioned': True,
|
||||
'sub_partition_type': 'list',
|
||||
'sub_partition_keys':
|
||||
[{'key_type': 'column', 'pt_column': 'sales'}]
|
||||
}]
|
||||
}
|
||||
)
|
||||
elif partition_type == 'list' and mode == 'create':
|
||||
data['partitions'].update(
|
||||
{'added': [{'values_in': "'2016-01-01', '2016-12-31'",
|
||||
@ -201,6 +223,26 @@ def set_partition_data(server, db_name, schema_name, table_name,
|
||||
}]
|
||||
}
|
||||
)
|
||||
elif partition_type == 'list' and mode == 'multilevel':
|
||||
data['partitions'].update(
|
||||
{'added': [{'values_in': "'2016-01-01', '2016-12-31'",
|
||||
'is_attach': False,
|
||||
'partition_name': 'sale_2016_sub_part',
|
||||
'is_sub_partitioned': True,
|
||||
'sub_partition_type': 'list',
|
||||
'sub_partition_keys':
|
||||
[{'key_type': 'column', 'pt_column': 'sales'}]
|
||||
},
|
||||
{'values_in': "'2017-01-01', '2017-12-31'",
|
||||
'is_attach': False,
|
||||
'partition_name': 'sale_2017_sub_part',
|
||||
'is_sub_partitioned': True,
|
||||
'sub_partition_type': 'list',
|
||||
'sub_partition_keys':
|
||||
[{'key_type': 'column', 'pt_column': 'sales'}]
|
||||
}]
|
||||
}
|
||||
)
|
||||
elif partition_type == 'range' and mode == 'detach':
|
||||
partition_id = create_table_for_partition(server, db_name, schema_name,
|
||||
table_name, 'partition',
|
||||
@ -320,3 +362,124 @@ def get_table_common_data():
|
||||
"name": "autovacuum_freeze_table_age"
|
||||
}]
|
||||
}
|
||||
|
||||
|
||||
def get_range_partitions_data(data, mode=None, multilevel_partition=False):
|
||||
"""
|
||||
This function returns the partitions data for range partition.
|
||||
:param data:
|
||||
:param mode:
|
||||
:param multilevel_partition:
|
||||
:return:
|
||||
"""
|
||||
data['partitions'] = \
|
||||
[{'values_from': "'2010-01-01'",
|
||||
'values_to': "'2010-12-31'",
|
||||
'is_attach': False,
|
||||
'partition_name': 'emp_2010'
|
||||
},
|
||||
{'values_from': "'2011-01-01'",
|
||||
'values_to': "'2011-12-31'",
|
||||
'is_attach': False,
|
||||
'partition_name': 'emp_2011'
|
||||
}]
|
||||
if mode == 'Default':
|
||||
data['partitions'] = \
|
||||
[{'values_from': "'2010-01-01'",
|
||||
'values_to': "'2010-12-31'",
|
||||
'is_attach': False,
|
||||
'partition_name': 'emp_2010_def'
|
||||
},
|
||||
{'values_from': "'2011-01-01'",
|
||||
'values_to': "'2011-12-31'",
|
||||
'is_attach': False,
|
||||
'partition_name': 'emp_2011_def'
|
||||
},
|
||||
{'values_from': "",
|
||||
'values_to': "",
|
||||
'is_attach': False,
|
||||
'is_default': True,
|
||||
'partition_name': 'emp_2012_def'
|
||||
}]
|
||||
if multilevel_partition:
|
||||
data['partitions'] = \
|
||||
[{'values_from': "'2010-01-01'",
|
||||
'values_to': "'2010-12-31'",
|
||||
'is_attach': False,
|
||||
'partition_name': 'emp_2010_multi_level',
|
||||
'is_sub_partitioned': True,
|
||||
'sub_partition_type': 'range',
|
||||
'sub_partition_keys':
|
||||
[{'key_type': 'column', 'pt_column': 'empno'}]
|
||||
},
|
||||
{'values_from': "'2011-01-01'",
|
||||
'values_to': "'2011-12-31'",
|
||||
'is_attach': False,
|
||||
'partition_name': 'emp_2011_multi_level',
|
||||
'is_sub_partitioned': True,
|
||||
'sub_partition_type': 'list',
|
||||
'sub_partition_keys':
|
||||
[{'key_type': 'column', 'pt_column': 'empno'}]
|
||||
}]
|
||||
data['partition_keys'] = \
|
||||
[{'key_type': 'column', 'pt_column': 'DOJ'}]
|
||||
|
||||
|
||||
def get_list_partitions_data(data, multilevel_partition=False):
|
||||
"""
|
||||
This function returns the partitions data for list partition.
|
||||
:param data:
|
||||
:param multilevel_partition:
|
||||
:return:
|
||||
"""
|
||||
data['partitions'] = \
|
||||
[{'values_in': "'2012-01-01', '2012-12-31'",
|
||||
'is_attach': False,
|
||||
'partition_name': 'emp_2012'
|
||||
},
|
||||
{'values_in': "'2013-01-01', '2013-12-31'",
|
||||
'is_attach': False,
|
||||
'partition_name': 'emp_2013'
|
||||
}]
|
||||
|
||||
if multilevel_partition:
|
||||
data['partitions'] = \
|
||||
[{'values_in': "'2012-01-01', '2012-12-31'",
|
||||
'is_attach': False,
|
||||
'partition_name': 'emp_2012_multi_level',
|
||||
'is_sub_partitioned': True,
|
||||
'sub_partition_type': 'list',
|
||||
'sub_partition_keys':
|
||||
[{'key_type': 'column', 'pt_column': 'empno'}]
|
||||
},
|
||||
{'values_in': "'2013-01-01', '2013-12-31'",
|
||||
'is_attach': False,
|
||||
'partition_name': 'emp_2013_multi_level',
|
||||
'is_sub_partitioned': True,
|
||||
'sub_partition_type': 'range',
|
||||
'sub_partition_keys':
|
||||
[{'key_type': 'column', 'pt_column': 'empno'}]
|
||||
}]
|
||||
data['partition_keys'] = \
|
||||
[{'key_type': 'column', 'pt_column': 'DOJ'}]
|
||||
|
||||
|
||||
def get_hash_partitions_data(data):
|
||||
"""
|
||||
This function returns the partitions data for hash partition.
|
||||
:param data:
|
||||
:return:
|
||||
"""
|
||||
data['partitions'] = \
|
||||
[{'values_modulus': "24",
|
||||
'values_remainder': "3",
|
||||
'is_attach': False,
|
||||
'partition_name': 'emp_2016'
|
||||
},
|
||||
{'values_modulus': "8",
|
||||
'values_remainder': "2",
|
||||
'is_attach': False,
|
||||
'partition_name': 'emp_2017'
|
||||
}]
|
||||
data['partition_keys'] = \
|
||||
[{'key_type': 'column', 'pt_column': 'empno'}]
|
||||
|
@ -676,17 +676,19 @@ class BaseTableView(PGChildNodeView, BasePartitionTable):
|
||||
|
||||
def get_partition_scheme(self, data):
|
||||
partition_scheme = None
|
||||
if 'partition_type' in data \
|
||||
and data['partition_type'] == 'range':
|
||||
part_type = 'sub_partition_type' if 'sub_partition_type' in data \
|
||||
else 'partition_type'
|
||||
part_keys = 'sub_partition_keys' if 'sub_partition_keys' in data \
|
||||
else 'partition_keys'
|
||||
|
||||
if part_type in data and data[part_type] == 'range':
|
||||
partition_scheme = 'RANGE ('
|
||||
elif 'partition_type' in data \
|
||||
and data['partition_type'] == 'list':
|
||||
elif part_type in data and data[part_type] == 'list':
|
||||
partition_scheme = 'LIST ('
|
||||
elif 'partition_type' in data \
|
||||
and data['partition_type'] == 'hash':
|
||||
elif part_type in data and data[part_type] == 'hash':
|
||||
partition_scheme = 'HASH ('
|
||||
|
||||
for row in data['partition_keys']:
|
||||
for row in data[part_keys]:
|
||||
if row['key_type'] == 'column':
|
||||
partition_scheme += self.qtIdent(
|
||||
self.conn, row['pt_column']) + ', '
|
||||
@ -694,7 +696,7 @@ class BaseTableView(PGChildNodeView, BasePartitionTable):
|
||||
partition_scheme += row['expression'] + ', '
|
||||
|
||||
# Remove extra space and comma
|
||||
if len(data['partition_keys']) > 0:
|
||||
if len(data[part_keys]) > 0:
|
||||
partition_scheme = partition_scheme[:-2]
|
||||
partition_scheme += ')'
|
||||
|
||||
@ -1235,7 +1237,9 @@ class BaseTableView(PGChildNodeView, BasePartitionTable):
|
||||
'partition_name': partition_name,
|
||||
'values_from': range_from,
|
||||
'values_to': range_to,
|
||||
'is_default': is_default
|
||||
'is_default': is_default,
|
||||
'is_sub_partitioned': row['is_sub_partitioned'],
|
||||
'sub_partition_scheme': row['sub_partition_scheme']
|
||||
})
|
||||
elif data['partition_type'] == 'list':
|
||||
if row['partition_value'] == 'DEFAULT':
|
||||
@ -1251,7 +1255,9 @@ class BaseTableView(PGChildNodeView, BasePartitionTable):
|
||||
'oid': row['oid'],
|
||||
'partition_name': partition_name,
|
||||
'values_in': range_in,
|
||||
'is_default': is_default
|
||||
'is_default': is_default,
|
||||
'is_sub_partitioned': row['is_sub_partitioned'],
|
||||
'sub_partition_scheme': row['sub_partition_scheme']
|
||||
})
|
||||
else:
|
||||
range_part = row['partition_value'].split(
|
||||
@ -1265,7 +1271,9 @@ class BaseTableView(PGChildNodeView, BasePartitionTable):
|
||||
'oid': row['oid'],
|
||||
'partition_name': partition_name,
|
||||
'values_modulus': range_modulus,
|
||||
'values_remainder': range_remainder
|
||||
'values_remainder': range_remainder,
|
||||
'is_sub_partitioned': row['is_sub_partitioned'],
|
||||
'sub_partition_scheme': row['sub_partition_scheme']
|
||||
})
|
||||
|
||||
data['partitions'] = partitions
|
||||
@ -1339,6 +1347,11 @@ class BaseTableView(PGChildNodeView, BasePartitionTable):
|
||||
+ ', REMAINDER ' +\
|
||||
remainder_str + ')'
|
||||
|
||||
# Check if partition is again declare as partitioned table.
|
||||
if 'is_sub_partitioned' in row and row['is_sub_partitioned']:
|
||||
part_data['partition_scheme'] = self.get_partition_scheme(row)
|
||||
part_data['is_partitioned'] = True
|
||||
|
||||
if 'is_attach' in row and row['is_attach']:
|
||||
partition_sql = render_template(
|
||||
"/".join([self.partition_template_path, 'attach.sql']),
|
||||
|