diff --git a/generator/template/sql_builder_template.go b/generator/template/sql_builder_template.go index 68cc688..3651078 100644 --- a/generator/template/sql_builder_template.go +++ b/generator/template/sql_builder_template.go @@ -242,8 +242,10 @@ func sqlToColumnType(columnMetaData metadata.Column) string { return "Int8Range" case "numrange": return "NumericRange" + case "geometry": + return "Geometry" default: - fmt.Println("- [SQL Builder] Unsupported sql column '" + columnMetaData.Name + " " + columnMetaData.DataType.Name + "', using StringColumn instead.") + fmt.Println("- [SQL Builder] Unsupported sql column '" + columnMetaData.Name + "' with type '" + columnMetaData.DataType.Name + "', using StringColumn instead.") return "String" } } diff --git a/internal/jet/column.go b/internal/jet/column.go index 7d108df..37db9db 100644 --- a/internal/jet/column.go +++ b/internal/jet/column.go @@ -2,10 +2,6 @@ package jet -import ( - "github.com/go-jet/jet/v2/internal/3rdparty/snaker" -) - // Column is common column interface for all types of columns. type Column interface { Name() string @@ -28,101 +24,3 @@ type ColumnExpression interface { Expression } -// ColumnExpressionImpl is base type for sql columns. -type ColumnExpressionImpl struct { - ExpressionInterfaceImpl - - name string - tableName string - - subQuery SelectTable -} - -// NewColumnImpl creates new ColumnExpressionImpl -func NewColumnImpl(name string, tableName string, root ColumnExpression) *ColumnExpressionImpl { - newColumn := &ColumnExpressionImpl{ - name: name, - tableName: tableName, - } - - if root != nil { - newColumn.ExpressionInterfaceImpl.Root = root - } else { - newColumn.ExpressionInterfaceImpl.Root = newColumn - } - - return newColumn -} - -// Name returns name of the column -func (c *ColumnExpressionImpl) Name() string { - return c.name -} - -// TableName returns column table name -func (c *ColumnExpressionImpl) TableName() string { - return c.tableName -} - -func (c *ColumnExpressionImpl) setTableName(table string) { - c.tableName = table -} - -func (c *ColumnExpressionImpl) setSubQuery(subQuery SelectTable) { - c.subQuery = subQuery -} - -func (c *ColumnExpressionImpl) defaultAlias() string { - if c.tableName != "" { - return c.tableName + "." + c.name - } - - return c.name -} - -func (c *ColumnExpressionImpl) serializeForOrderBy(statement StatementType, out *SQLBuilder) { - if statement == SetStatementType { - // set Statement (UNION, EXCEPT ...) can reference only select projections in order by clause - out.WriteAlias(c.defaultAlias()) //always quote - return - } - - c.serialize(statement, out) -} - -func (c *ColumnExpressionImpl) serializeForProjection(statement StatementType, out *SQLBuilder) { - c.serialize(statement, out) - - out.WriteString("AS") - - out.WriteAlias(c.defaultAlias()) -} - -func (c *ColumnExpressionImpl) serializeForJsonObjEntry(statement StatementType, out *SQLBuilder) { - out.WriteJsonObjKey(snaker.SnakeToCamel(c.name, false)) - c.Root.serializeForJsonValue(statement, out) -} - -func (c *ColumnExpressionImpl) serializeForRowToJsonProjection(statement StatementType, out *SQLBuilder) { - c.Root.serializeForJsonValue(statement, out) - - out.WriteString("AS") - - out.WriteAlias(snaker.SnakeToCamel(c.name, false)) -} - -func (c *ColumnExpressionImpl) serialize(statement StatementType, out *SQLBuilder, options ...SerializeOption) { - - if c.subQuery != nil { - out.WriteIdentifier(c.subQuery.Alias()) - out.WriteByte('.') - out.WriteIdentifier(c.defaultAlias()) - } else { - if c.tableName != "" && !contains(options, ShortName) { - out.WriteIdentifier(c.tableName) - out.WriteByte('.') - } - - out.WriteIdentifier(c.name) - } -} diff --git a/internal/jet/column_expression_geometry_impl.go b/internal/jet/column_expression_geometry_impl.go new file mode 100644 index 0000000..3038b26 --- /dev/null +++ b/internal/jet/column_expression_geometry_impl.go @@ -0,0 +1,109 @@ +package jet + +import ( + "github.com/go-jet/jet/v2/internal/3rdparty/snaker" +) + + +// ColumnExpressionGeometryImpl is base type for sql columns. +type ColumnExpressionGeometryImpl struct { + ExpressionInterfaceImpl + + name string + tableName string + + subQuery SelectTable +} + +// Name returns name of the column +func (c *ColumnExpressionGeometryImpl) Name() string { + return c.name +} + +// TableName returns column table name +func (c *ColumnExpressionGeometryImpl) TableName() string { + return c.tableName +} + +func (c *ColumnExpressionGeometryImpl) setTableName(table string) { + c.tableName = table +} + +func (c *ColumnExpressionGeometryImpl) setSubQuery(subQuery SelectTable) { + c.subQuery = subQuery +} + +func (c *ColumnExpressionGeometryImpl) defaultAlias() string { + if c.tableName != "" { + return c.tableName + "." + c.name + } + + return c.name +} + +func (c *ColumnExpressionGeometryImpl) serializeForOrderBy(statement StatementType, out *SQLBuilder) { + if statement == SetStatementType { + // set Statement (UNION, EXCEPT ...) can reference only select projections in order by clause + out.WriteAlias(c.defaultAlias()) //always quote + return + } + + c.serialize(statement, out) +} + +func (c *ColumnExpressionGeometryImpl) serializeForProjection(statement StatementType, out *SQLBuilder) { + c.serialize(statement, out) + + out.WriteString("AS") + + out.WriteAlias(c.defaultAlias()) +} + +func (c *ColumnExpressionGeometryImpl) serializeForJsonObjEntry(statement StatementType, out *SQLBuilder) { + out.WriteJsonObjKey(snaker.SnakeToCamel(c.name, false)) + c.Root.serializeForJsonValue(statement, out) +} + +func (c *ColumnExpressionGeometryImpl) serializeForRowToJsonProjection(statement StatementType, out *SQLBuilder) { + c.Root.serializeForJsonValue(statement, out) + + out.WriteString("AS") + + out.WriteAlias(snaker.SnakeToCamel(c.name, false)) +} + +func (c *ColumnExpressionGeometryImpl) serialize(statement StatementType, out *SQLBuilder, options ...SerializeOption) { + + if c.subQuery != nil { + out.WriteString("ST_AsGeoJSON(") + out.WriteIdentifier(c.subQuery.Alias()) + out.WriteByte('.') + out.WriteIdentifier(c.defaultAlias()) + out.WriteString(")") + } else { + out.WriteString("ST_AsGeoJSON(") + if c.tableName != "" && !contains(options, ShortName) { + out.WriteIdentifier(c.tableName) + out.WriteByte('.') + } + + out.WriteIdentifier(c.name) + out.WriteString(")") + } +} + +func NewColumnGeometryImpl(name string, tableName string, root ColumnExpression) *ColumnExpressionGeometryImpl { + newColumn := &ColumnExpressionGeometryImpl{ + name: name, + tableName: tableName, + } + + if root != nil { + newColumn.ExpressionInterfaceImpl.Root = root + } else { + newColumn.ExpressionInterfaceImpl.Root = newColumn + } + + return newColumn +} + diff --git a/internal/jet/column_expression_impl.go b/internal/jet/column_expression_impl.go new file mode 100644 index 0000000..d9d034d --- /dev/null +++ b/internal/jet/column_expression_impl.go @@ -0,0 +1,107 @@ +// Modeling of columns + +package jet + +import ( + "github.com/go-jet/jet/v2/internal/3rdparty/snaker" +) + + +// ColumnExpressionImpl is base type for sql columns. +type ColumnExpressionImpl struct { + ExpressionInterfaceImpl + + name string + tableName string + + subQuery SelectTable +} + +// Name returns name of the column +func (c *ColumnExpressionImpl) Name() string { + return c.name +} + +// TableName returns column table name +func (c *ColumnExpressionImpl) TableName() string { + return c.tableName +} + +func (c *ColumnExpressionImpl) setTableName(table string) { + c.tableName = table +} + +func (c *ColumnExpressionImpl) setSubQuery(subQuery SelectTable) { + c.subQuery = subQuery +} + +func (c *ColumnExpressionImpl) defaultAlias() string { + if c.tableName != "" { + return c.tableName + "." + c.name + } + + return c.name +} + +func (c *ColumnExpressionImpl) serializeForOrderBy(statement StatementType, out *SQLBuilder) { + if statement == SetStatementType { + // set Statement (UNION, EXCEPT ...) can reference only select projections in order by clause + out.WriteAlias(c.defaultAlias()) //always quote + return + } + + c.serialize(statement, out) +} + +func (c *ColumnExpressionImpl) serializeForProjection(statement StatementType, out *SQLBuilder) { + c.serialize(statement, out) + + out.WriteString("AS") + + out.WriteAlias(c.defaultAlias()) +} + +func (c *ColumnExpressionImpl) serializeForJsonObjEntry(statement StatementType, out *SQLBuilder) { + out.WriteJsonObjKey(snaker.SnakeToCamel(c.name, false)) + c.Root.serializeForJsonValue(statement, out) +} + +func (c *ColumnExpressionImpl) serializeForRowToJsonProjection(statement StatementType, out *SQLBuilder) { + c.Root.serializeForJsonValue(statement, out) + + out.WriteString("AS") + + out.WriteAlias(snaker.SnakeToCamel(c.name, false)) +} + +func (c *ColumnExpressionImpl) serialize(statement StatementType, out *SQLBuilder, options ...SerializeOption) { + + if c.subQuery != nil { + out.WriteIdentifier(c.subQuery.Alias()) + out.WriteByte('.') + out.WriteIdentifier(c.defaultAlias()) + } else { + if c.tableName != "" && !contains(options, ShortName) { + out.WriteIdentifier(c.tableName) + out.WriteByte('.') + } + + out.WriteIdentifier(c.name) + } +} +// NewColumnImpl creates new ColumnExpressionImpl +func NewColumnImpl(name string, tableName string, root ColumnExpression) *ColumnExpressionImpl { + newColumn := &ColumnExpressionImpl{ + name: name, + tableName: tableName, + } + + if root != nil { + newColumn.ExpressionInterfaceImpl.Root = root + } else { + newColumn.ExpressionInterfaceImpl.Root = newColumn + } + + return newColumn +} + diff --git a/internal/jet/column_types.go b/internal/jet/column_types.go index 5d63485..84d4d12 100644 --- a/internal/jet/column_types.go +++ b/internal/jet/column_types.go @@ -573,3 +573,48 @@ func RangeColumn[T Expression](name string) ColumnRange[T] { return rangeColumn } + +//------------------------------------------------------// + +// ColumnGeometry is interface of PostGIS +type ColumnGeometry interface { + GeometryExpression + Column + + From(subQuery SelectTable) ColumnGeometry + SET(timestampzExp GeometryExpression) ColumnAssigment +} + +type geometryColumnImpl struct { + geometryInterfaceImpl + *ColumnExpressionGeometryImpl +} + +func (i *geometryColumnImpl) fromImpl(subQuery SelectTable) Projection { + return i.From(subQuery) +} + +func (i *geometryColumnImpl) From(subQuery SelectTable) ColumnGeometry { + newGeometryColumn := GeometryColumn(i.name) + newGeometryColumn.setTableName(i.tableName) + newGeometryColumn.setSubQuery(subQuery) + + return newGeometryColumn +} + +func (i *geometryColumnImpl) SET(geometryExp GeometryExpression) ColumnAssigment { + return columnAssigmentImpl{ + column: i, + toAssign: geometryExp, + } +} + +// GeometryColumn creates named timestamp with time zone column. +func GeometryColumn(name string) ColumnGeometry { + geometryColumn := &geometryColumnImpl{} + geometryColumn.geometryInterfaceImpl.root = geometryColumn + geometryColumn.ColumnExpressionGeometryImpl = NewColumnGeometryImpl(name, "", geometryColumn) + + return geometryColumn +} + diff --git a/internal/jet/geometry_expression.go b/internal/jet/geometry_expression.go new file mode 100644 index 0000000..6eb8eb5 --- /dev/null +++ b/internal/jet/geometry_expression.go @@ -0,0 +1,94 @@ +package jet + +// GeometryExpression interface +type GeometryExpression interface { + Expression + + EQ(rhs GeometryExpression) BoolExpression + NOT_EQ(rhs GeometryExpression) BoolExpression + IS_DISTINCT_FROM(rhs GeometryExpression) BoolExpression + IS_NOT_DISTINCT_FROM(rhs GeometryExpression) BoolExpression + + LT(rhs GeometryExpression) BoolExpression + LT_EQ(rhs GeometryExpression) BoolExpression + GT(rhs GeometryExpression) BoolExpression + GT_EQ(rhs GeometryExpression) BoolExpression + BETWEEN(min, max GeometryExpression) BoolExpression + NOT_BETWEEN(min, max GeometryExpression) BoolExpression + + ADD(rhs Interval) GeometryExpression + SUB(rhs Interval) GeometryExpression +} + +type geometryInterfaceImpl struct { + root GeometryExpression +} + +func (t *geometryInterfaceImpl) EQ(rhs GeometryExpression) BoolExpression { + return Eq(t.root, rhs) +} + +func (t *geometryInterfaceImpl) NOT_EQ(rhs GeometryExpression) BoolExpression { + return NotEq(t.root, rhs) +} + +func (t *geometryInterfaceImpl) IS_DISTINCT_FROM(rhs GeometryExpression) BoolExpression { + return IsDistinctFrom(t.root, rhs) +} + +func (t *geometryInterfaceImpl) IS_NOT_DISTINCT_FROM(rhs GeometryExpression) BoolExpression { + return IsNotDistinctFrom(t.root, rhs) +} + +func (t *geometryInterfaceImpl) LT(rhs GeometryExpression) BoolExpression { + return Lt(t.root, rhs) +} + +func (t *geometryInterfaceImpl) LT_EQ(rhs GeometryExpression) BoolExpression { + return LtEq(t.root, rhs) +} + +func (t *geometryInterfaceImpl) GT(rhs GeometryExpression) BoolExpression { + return Gt(t.root, rhs) +} + +func (t *geometryInterfaceImpl) GT_EQ(rhs GeometryExpression) BoolExpression { + return GtEq(t.root, rhs) +} + +func (t *geometryInterfaceImpl) BETWEEN(min, max GeometryExpression) BoolExpression { + return NewBetweenOperatorExpression(t.root, min, max, false) +} + +func (t *geometryInterfaceImpl) NOT_BETWEEN(min, max GeometryExpression) BoolExpression { + return NewBetweenOperatorExpression(t.root, min, max, true) +} + +func (t *geometryInterfaceImpl) ADD(rhs Interval) GeometryExpression { + return GeometryExp(Add(t.root, rhs)) +} + +func (t *geometryInterfaceImpl) SUB(rhs Interval) GeometryExpression { + return GeometryExp(Sub(t.root, rhs)) +} + +//------------------------------------------------- + +type geometryExpressionWrapper struct { + geometryInterfaceImpl + Expression +} + +func newGeometryExpressionWrap(expression Expression) GeometryExpression { + geometryExpressionWrap := &geometryExpressionWrapper{Expression: expression} + geometryExpressionWrap.geometryInterfaceImpl.root = geometryExpressionWrap + expression.setRoot(geometryExpressionWrap) + return geometryExpressionWrap +} + +// GeometryExp is timestamp with time zone expression wrapper around arbitrary expression. +// Allows go compiler to see any expression as timestamp with time zone expression. +// Does not add sql cast to generated sql builder output. +func GeometryExp(expression Expression) GeometryExpression { + return newGeometryExpressionWrap(expression) +} diff --git a/postgres/columns.go b/postgres/columns.go index 01af0d7..48bac27 100644 --- a/postgres/columns.go +++ b/postgres/columns.go @@ -77,6 +77,9 @@ type ColumnInterval = jet.ColumnInterval // IntervalColumn creates named interval column var IntervalColumn = jet.IntervalColumn +type ColumnGeometry = jet.ColumnGeometry +var GeometryColumn = jet.GeometryColumn + // ColumnDateRange is interface of SQL date range column type ColumnDateRange = jet.ColumnRange[DateExpression] @@ -112,3 +115,4 @@ type ColumnInt8Range jet.ColumnRange[jet.Int8Expression] // Int8RangeColumn creates named range with range column var Int8RangeColumn = jet.RangeColumn[jet.Int8Expression] +