Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,33 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.115] - 2026-05-8
### Added
- support for partitioned tables

## [1.114]
### Fixed
- create index with include and where

## [1.113]
### Fixed
- recreate_postgres single view with dependencies

## [1.112]
### Fixed
- schema:generate - integer column on postgresql

# [1.110]
### Fixed
- create procedures order

# [1.109]
### Added
- Support index include columns
- View creation with topological sort
### Fixed
- schema:recreate_postgres function order

## [1.108] - 2025-06-26
### Fixed
- check constraints on postgresql 17
Expand Down
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ mainClassName = "br.com.bluesoft.bee.Bee"

group = 'br.com.bluesoft.bee'
def artifact = 'bee'
version = '1.114'
version = '1.115'

def javaVersion = JavaVersion.VERSION_1_8
sourceCompatibility = javaVersion;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import br.com.bluesoft.bee.model.Schema
import br.com.bluesoft.bee.model.Sequence
import br.com.bluesoft.bee.model.Table
import br.com.bluesoft.bee.model.TableColumn
import br.com.bluesoft.bee.model.TablePartition
import br.com.bluesoft.bee.model.Trigger
import br.com.bluesoft.bee.model.View
import br.com.bluesoft.bee.util.VersionHelper
Expand Down Expand Up @@ -87,23 +88,30 @@ class PostgresDatabaseReader implements DatabaseReader {
fillIndexes(tables, objectName, databaseVersion)
fillCostraints(tables, objectName, databaseVersion)
fillCostraintsColumns(tables, objectName)
fillPartitions(tables, objectName)
return tables
}

static final def TABLES_QUERY = '''
select t.table_name, 'N'as temporary, description
select t.table_name, 'N'as temporary, description,
pg_get_partkeydef(to_regclass(t.table_name)::regclass::oid) partitioned
from information_schema.tables t
left join pg_description d on d.objoid = to_regclass(t.table_name)::regclass::oid
where t.table_type = 'BASE TABLE' and table_schema not in ('pg_catalog', 'information_schema')
order by table_name
left join pg_inherits i on i.inhrelid = to_regclass(t.table_name)::regclass::oid
where t.table_type = 'BASE TABLE' and table_schema not in ('pg_catalog', 'information_schema')
and i.inhrelid is null
order by table_name
'''
static final def TABLES_QUERY_BY_NAME = '''
select t.table_name, 'N'as temporary, description
select t.table_name, 'N'as temporary, description,
pg_get_partkeydef(to_regclass(t.table_name)::regclass::oid) partitioned
from information_schema.tables t
left join pg_description d on d.objoid = to_regclass(t.table_name)::regclass::oid
where t.table_type = 'BASE TABLE' and table_schema not in ('pg_catalog', 'information_schema')
and t.table_name = ?
order by table_name
left join pg_inherits i on i.inhrelid = to_regclass(t.table_name)::regclass::oid
where t.table_type = 'BASE TABLE' and table_schema not in ('pg_catalog', 'information_schema')
and i.inhrelid is null
and t.table_name like ?
order by table_name
'''

private def fillTables(objectName) {
Expand All @@ -118,7 +126,8 @@ class PostgresDatabaseReader implements DatabaseReader {
def name = it.table_name.toLowerCase()
def temporary = it.temporary == 'Y' ? true : false
def comment = it.description
tables[name] = new Table(name: name, temporary: temporary, comment: comment)
def partitioned = it.partitioned
tables[name] = new Table(name: name, temporary: temporary, comment: comment, partitioned: partitioned)
})
return tables
}
Expand Down Expand Up @@ -172,19 +181,21 @@ class PostgresDatabaseReader implements DatabaseReader {
}
rows.each({
def table = tables[it.table_name.toLowerCase()]
def column = new TableColumn()
column.name = it.column_name.toLowerCase()
column.type = getColumnType(it.data_type)
column.size = it.data_size
column.scale = it.data_scale == null ? 0 : it.data_scale
column.nullable = it.nullable == 'NO' ? false : true
column.virtual = it.is_generated == 'ALWAYS'
column.comment = it.comments
def defaultValue = it.data_default
if (defaultValue) {
column.defaultValue = defaultValue?.trim()?.toUpperCase() == 'NULL' ? null : defaultValue?.trim()
if (table) {
def column = new TableColumn()
column.name = it.column_name.toLowerCase()
column.type = getColumnType(it.data_type)
column.size = it.data_size
column.scale = it.data_scale == null ? 0 : it.data_scale
column.nullable = it.nullable == 'NO' ? false : true
column.virtual = it.is_generated == 'ALWAYS'
column.comment = it.comments
def defaultValue = it.data_default
if (defaultValue) {
column.defaultValue = defaultValue?.trim()?.toUpperCase() == 'NULL' ? null : defaultValue?.trim()
}
table.columns[column.name] = column
}
table.columns[column.name] = column
})
}

Expand Down Expand Up @@ -419,6 +430,41 @@ class PostgresDatabaseReader implements DatabaseReader {
})
}

final static def PARTITIONS_QUERY = '''
select i.inhparent::regclass::text parent, i.inhrelid::regclass::text table,
pg_catalog.pg_get_expr(c.relpartbound, c.oid) partition
from pg_catalog.pg_inherits i
join pg_catalog.pg_class c on c.oid = i.inhrelid
where c.relkind = 'r'
order by pg_catalog.pg_get_expr(c.relpartbound, c.oid) = 'default', c.oid::pg_catalog.regclass::pg_catalog.text
'''

final static def PARTITIONS_BY_TABLE_QUERY = '''
select i.inhparent::regclass::text parent, i.inhrelid::regclass::text table,
pg_catalog.pg_get_expr(c.relpartbound, c.oid) partition
from pg_catalog.pg_inherits i
join pg_catalog.pg_class c on c.oid = i.inhrelid
where c.relkind = 'r'
and i.inhparent::regclass::text = ?
order by pg_catalog.pg_get_expr(c.relpartbound, c.oid) = 'default', c.oid::pg_catalog.regclass::pg_catalog.text
'''

private def fillPartitions(tables, objectName) {
def rows
if (objectName) {
rows = sql.rows(PARTITIONS_BY_TABLE_QUERY, [objectName])
} else {
rows = sql.rows(PARTITIONS_QUERY)
}

rows.each {
def table = tables[it.parent.toLowerCase()]
if (table) {
table.partitions << new TablePartition(name: it.table, value: it.partition)
}
}
}

final static def SEQUENCES_QUERY = '''
select c.relname as sequence_name, '1' as min_value
from pg_class c
Expand Down
4 changes: 3 additions & 1 deletion src/main/groovy/br/com/bluesoft/bee/model/Table.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,15 @@ import com.fasterxml.jackson.annotation.JsonAutoDetect
import groovy.transform.AutoClone

@AutoClone
@com.fasterxml.jackson.annotation.JsonAutoDetect(isGetterVisibility = JsonAutoDetect.Visibility.NONE, getterVisibility = JsonAutoDetect.Visibility.ANY)
@JsonAutoDetect(isGetterVisibility = JsonAutoDetect.Visibility.NONE, getterVisibility = JsonAutoDetect.Visibility.ANY)
class Table implements Validator {

String name
Boolean temporary = false
String comment
String distStyle
String partitioned
List<TablePartition> partitions = []

Map<String, TableColumn> columns = [:] as LinkedHashMap
Map<String, Index> indexes = [:]
Expand Down
22 changes: 22 additions & 0 deletions src/main/groovy/br/com/bluesoft/bee/model/TablePartition.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package br.com.bluesoft.bee.model;

public class TablePartition {
private String name;
private String value;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getValue() {
return value;
}

public void setValue(String value) {
this.value = value;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,16 @@ import br.com.bluesoft.bee.util.CsvUtil

class BeePostgresSchemaCreator extends BeeSchemaCreator {

def createTablePartitions(def table) {
def result = ""

table.partitions.each {
result += "create table ${it.name} partition of ${table.name} ${it.value};\n"
}

return result
}

def createColumn(def column) {
def result = " ${column.name} ${column.type}"
if (column.type in ['character', 'character varying']) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,19 @@ abstract class BeeSchemaCreator {
return result
}

def createTablePartitions(def table) {
}

def createTable(def table) {
def columns = []
table.columns.each({
columns << createColumn(it.value)
})
def temp = table.temporary ? " global temporary" : ""
def result = "create${temp} table ${table.name} (\n" + columns.join(",\n") + "\n);\n"
def partition = table.partitioned ? " partition by ${table.partitioned}" : ""
def result = "create${temp} table ${table.name} (\n" + columns.join(",\n") + "\n)${partition};\n"

result += createTablePartitions(table)

if (table.comment) {
result += "comment on table ${table.name} is '${table.comment}';\n"
Expand Down