Categorygithub.com/sub0zero/go-sqlbuilder
modulepackage
1.0.0
Repository: https://github.com/sub0zero/go-sqlbuilder.git
Documentation: pkg.go.dev

# README

Jet

Go Report Card Documentation codecov CircleCI

Jet is a framework for writing type-safe SQL queries for PostgreSQL in Go, with ability to easily convert database query result to desired arbitrary structure.
*Support for additional databases will be added in future jet releases.

Contents

Features

  1. Auto-generated type-safe SQL Builder
    • Types - boolean, integers(smallint, integer, bigint), floats(real, numeric, decimal, double precision), strings(text, character, character varying), date, time(z), timestamp(z) and enums.
    • Statements:
      • SELECT (DISTINCT, FROM, WHERE, GROUP BY, HAVING, ORDER BY, LIMIT, OFFSET, FOR, UNION, INTERSECT, EXCEPT, sub-queries)
      • INSERT (VALUES, query, RETURNING),
      • UPDATE (SET, WHERE, RETURNING),
      • DELETE (WHERE, RETURNING),
      • LOCK (IN, NOWAIT)
  2. Auto-generated Data Model types - Go types mapped to database type (table or enum), used to store result of database queries. Can be combined to create desired query result destination.
  3. Query execution with result mapping to arbitrary destination structure.

Getting Started

Prerequisites

To install Jet package, you need to install Go and set your Go workspace first.

Go version 1.8+ is required

Installation

Use the bellow command to install jet

$ go get -u github.com/go-jet/jet

Install jet generator to GOPATH bin folder. This will allow generating jet files from the command line.

go install github.com/go-jet/jet/cmd/jet

Make sure GOPATH bin folder is added to the PATH environment variable.

Quick Start

For this quick start example we will use sample dvd rental database. Full database dump can be found in ./tests/init/data/dvds.sql. Schema diagram of interest for example can be found here.

Generate SQL Builder and Model files

To generate jet SQL Builder and Data Model files from postgres database we need to call jet generator with postgres connection parameters and root destination folder path for generated files.
Assuming we are running local postgres database, with user jetuser, user password jetpass, database jetdb and schema dvds we will use this command:

jet -host=localhost -port=5432 -user=jetuser -password=jetpass -dbname=jetdb -schema=dvds -path=./gen
Connecting to postgres database: host=localhost port=5432 user=jetuser password=jetpass dbname=jetdb sslmode=disable 
Retrieving schema information...
    FOUND 15  table(s),  1  enum(s)
Destination directory: ./gen/jetdb/dvds
Cleaning up schema destination directory...
Generating table sql builder files...
Generating table model files...
Generating enum sql builder files...
Generating enum model files...
Done

*User has to have a permission to read information schema tables

As command output suggest, Jet will:

  • connect to postgres database and retrieve information about the tables and enums of dvds schema
  • delete everything in schema destination folder - ./gen/jetdb/dvds,
  • and finally generate SQL Builder and Model files for each schema table and enum.

Generated files folder structure will look like this:

|-- gen                               # -path
|   `-- jetdb                         # database name
|       `-- dvds                      # schema name
|           |-- enum                  # sql builder folder for enums
|           |   |-- mpaa_rating.go
|           |-- table                 # sql builder folder for tables
|               |-- actor.go
|               |-- address.go
|               |-- category.go
|               ...
|           |-- model                 # model files for each table and enum
|           |   |-- actor.go
|           |   |-- address.go
|           |   |-- mpaa_rating.go
|           |   ...

Types from table and enum are used to write type safe SQL in Go, and model types can be combined to store results of the SQL queries.

Lets write some SQL queries in Go

First we need to import jet and generated files from previous step:

import (
	// dot import so that Go code would resemble as much as native SQL
	// dot import is not mandatory
	. "github.com/go-jet/jet"                                           
	. "github.com/go-jet/jet/examples/quick-start/gen/jetdb/dvds/table" 

	"github.com/go-jet/jet/examples/quick-start/gen/jetdb/dvds/model"
)

Lets say we want to retrieve the list of all actors that acted in films longer than 180 minutes, film language is 'English' and film category is not 'Action'.

stmt := SELECT(
    Actor.ActorID, Actor.FirstName, Actor.LastName, Actor.LastUpdate,  // or just Actor.AllColumns
    Film.AllColumns,                                                  
    Language.AllColumns,
    Category.AllColumns,
).FROM(
    Actor.
        INNER_JOIN(FilmActor, Actor.ActorID.EQ(FilmActor.ActorID)).  
        INNER_JOIN(Film, Film.FilmID.EQ(FilmActor.FilmID)).          
        INNER_JOIN(Language, Language.LanguageID.EQ(Film.LanguageID)).
        INNER_JOIN(FilmCategory, FilmCategory.FilmID.EQ(Film.FilmID)).
        INNER_JOIN(Category, Category.CategoryID.EQ(FilmCategory.CategoryID)),
).WHERE(
    Language.Name.EQ(String("English")).             
        AND(Category.Name.NOT_EQ(String("Action"))).  
        AND(Film.Length.GT(Int(180))),               
).ORDER_BY(
    Actor.ActorID.ASC(),
    Film.FilmID.ASC(),
)

With package(dot) import above statements looks almost the same as native SQL. Note that every column has a type. String column Language.Name and Category.Name can be compared only with string columns and expressions. Actor.ActorID, FilmActor.ActorID, Film.Length are integer columns and can be compared only with integer columns and expressions.

How to get parametrized SQL query from statement?

query, args, err := stmt.Sql()

query - parametrized query
args - parameters for the query

Click to see `query` and `args`
SELECT actor.actor_id AS "actor.actor_id",
     actor.first_name AS "actor.first_name",
     actor.last_name AS "actor.last_name",
     actor.last_update AS "actor.last_update",
     film.film_id AS "film.film_id",
     film.title AS "film.title",
     film.description AS "film.description",
     film.release_year AS "film.release_year",
     film.language_id AS "film.language_id",
     film.rental_duration AS "film.rental_duration",
     film.rental_rate AS "film.rental_rate",
     film.length AS "film.length",
     film.replacement_cost AS "film.replacement_cost",
     film.rating AS "film.rating",
     film.last_update AS "film.last_update",
     film.special_features AS "film.special_features",
     film.fulltext AS "film.fulltext",
     language.language_id AS "language.language_id",
     language.name AS "language.name",
     language.last_update AS "language.last_update",
     category.category_id AS "category.category_id",
     category.name AS "category.name",
     category.last_update AS "category.last_update"
FROM dvds.actor
     INNER JOIN dvds.film_actor ON (actor.actor_id = film_actor.actor_id)
     INNER JOIN dvds.film ON (film.film_id = film_actor.film_id)
     INNER JOIN dvds.language ON (language.language_id = film.language_id)
     INNER JOIN dvds.film_category ON (film_category.film_id = film.film_id)
     INNER JOIN dvds.category ON (category.category_id = film_category.category_id)
WHERE ((language.name = $1) AND (category.name != $2)) AND (film.length > $3)
ORDER BY actor.actor_id ASC, film.film_id ASC;
[English Action 180]

How to get debug SQL from statement?

debugSql, err := stmt.DebugSql()

debugSql - query string that can be copy pasted to sql editor and executed. It's not intended to be used in production.

Click to see debug sql
SELECT actor.actor_id AS "actor.actor_id",
     actor.first_name AS "actor.first_name",
     actor.last_name AS "actor.last_name",
     actor.last_update AS "actor.last_update",
     film.film_id AS "film.film_id",
     film.title AS "film.title",
     film.description AS "film.description",
     film.release_year AS "film.release_year",
     film.language_id AS "film.language_id",
     film.rental_duration AS "film.rental_duration",
     film.rental_rate AS "film.rental_rate",
     film.length AS "film.length",
     film.replacement_cost AS "film.replacement_cost",
     film.rating AS "film.rating",
     film.last_update AS "film.last_update",
     film.special_features AS "film.special_features",
     film.fulltext AS "film.fulltext",
     language.language_id AS "language.language_id",
     language.name AS "language.name",
     language.last_update AS "language.last_update",
     category.category_id AS "category.category_id",
     category.name AS "category.name",
     category.last_update AS "category.last_update"
FROM dvds.actor
     INNER JOIN dvds.film_actor ON (actor.actor_id = film_actor.actor_id)
     INNER JOIN dvds.film ON (film.film_id = film_actor.film_id)
     INNER JOIN dvds.language ON (language.language_id = film.language_id)
     INNER JOIN dvds.film_category ON (film_category.film_id = film.film_id)
     INNER JOIN dvds.category ON (category.category_id = film_category.category_id)
WHERE ((language.name = 'English') AND (category.name != 'Action')) AND (film.length > 180)
ORDER BY actor.actor_id ASC, film.film_id ASC;

Execute query and store result

Well formed SQL is just a first half the job. Lets see how can we make some sense of result set returned executing above statement. Usually this is the most complex and tedious work, but with Jet it is the easiest.

First we have to create desired structure to store query result set. This is done be combining autogenerated model types or it can be done manually(see wiki for more information).

Let's say this is our desired structure, created by combining auto-generated model types:

var dest []struct {
    model.Actor
    
    Films []struct {
        model.Film
        
        Language    model.Language
        Categories  []model.Category
    }
}

Because one actor can act in multiple films, Films field is a slice, and because each film belongs to one language Langauge field is just a single model struct.
*There is no limitation of how big or nested destination structure can be.

Now lets execute a above statement on open database connection db and store result into dest.

err := stmt.Query(db, &dest)
handleError(err)

And thats it.

dest now contains the list of all actors(with list of films acted, where each film has information about language and list of belonging categories) that acted in films longer than 180 minutes, film language is 'English' and film category is not 'Action'.

Lets print dest as a json to see:

jsonText, _ := json.MarshalIndent(dest, "", "\t")
fmt.Println(string(jsonText))
[
	{
		"ActorID": 1,
		"FirstName": "Penelope",
		"LastName": "Guiness",
		"LastUpdate": "2013-05-26T14:47:57.62Z",
		"Films": [
			{
				"FilmID": 499,
				"Title": "King Evolution",
				"Description": "A Action-Packed Tale of a Boy And a Lumberjack who must Chase a Madman in A Baloon",
				"ReleaseYear": 2006,
				"LanguageID": 1,
				"RentalDuration": 3,
				"RentalRate": 4.99,
				"Length": 184,
				"ReplacementCost": 24.99,
				"Rating": "NC-17",
				"LastUpdate": "2013-05-26T14:50:58.951Z",
				"SpecialFeatures": "{Trailers,\"Deleted Scenes\",\"Behind the Scenes\"}",
				"Fulltext": "'action':5 'action-pack':4 'baloon':21 'boy':10 'chase':16 'evolut':2 'king':1 'lumberjack':13 'madman':18 'must':15 'pack':6 'tale':7",
				"Language": {
					"LanguageID": 1,
					"Name": "English             ",
					"LastUpdate": "2006-02-15T10:02:19Z"
				},
				"Categories": [
					{
						"CategoryID": 8,
						"Name": "Family",
						"LastUpdate": "2006-02-15T09:46:27Z"
					}
				]
			}
		]
	},
	{
		"ActorID": 3,
		"FirstName": "Ed",
		"LastName": "Chase",
		"LastUpdate": "2013-05-26T14:47:57.62Z",
		"Films": [
			{
				"FilmID": 996,
				"Title": "Young Language",
				"Description": "A Unbelieveable Yarn of a Boat And a Database Administrator who must Meet a Boy in The First Manned Space Station",
				"ReleaseYear": 2006,
				"LanguageID": 1,
				"RentalDuration": 6,
				"RentalRate": 0.99,
				"Length": 183,
				"ReplacementCost": 9.99,
				"Rating": "G",
				"LastUpdate": "2013-05-26T14:50:58.951Z",
				"SpecialFeatures": "{Trailers,\"Behind the Scenes\"}",
				"Fulltext": "'administr':12 'boat':8 'boy':17 'databas':11 'first':20 'languag':2 'man':21 'meet':15 'must':14 'space':22 'station':23 'unbeliev':4 'yarn':5 'young':1",
				"Language": {
					"LanguageID": 1,
					"Name": "English             ",
					"LastUpdate": "2006-02-15T10:02:19Z"
				},
				"Categories": [
					{
						"CategoryID": 6,
						"Name": "Documentary",
						"LastUpdate": "2006-02-15T09:46:27Z"
					}
				]
			}
		]
	},
	//...(125 more items)
]

What if, we also want to have list of films per category and actors per category, where films are longer than 180 minutes, film language is 'English' and film category is not 'Action'.
In that case we can reuse above statement stmt, and just change our destination:

var dest2 []struct {
    model.Category

    Films []model.Film
    Actors []model.Actor
}

err = stmt.Query(db, &dest2)
handleError(err)
Click to see `dest2` json
[
	{
		"CategoryID": 8,
		"Name": "Family",
		"LastUpdate": "2006-02-15T09:46:27Z",
		"Films": [
			{
				"FilmID": 499,
				"Title": "King Evolution",
				"Description": "A Action-Packed Tale of a Boy And a Lumberjack who must Chase a Madman in A Baloon",
				"ReleaseYear": 2006,
				"LanguageID": 1,
				"RentalDuration": 3,
				"RentalRate": 4.99,
				"Length": 184,
				"ReplacementCost": 24.99,
				"Rating": "NC-17",
				"LastUpdate": "2013-05-26T14:50:58.951Z",
				"SpecialFeatures": "{Trailers,\"Deleted Scenes\",\"Behind the Scenes\"}",
				"Fulltext": "'action':5 'action-pack':4 'baloon':21 'boy':10 'chase':16 'evolut':2 'king':1 'lumberjack':13 'madman':18 'must':15 'pack':6 'tale':7"
			},
			{
				"FilmID": 50,
				"Title": "Baked Cleopatra",
				"Description": "A Stunning Drama of a Forensic Psychologist And a Husband who must Overcome a Waitress in A Monastery",
				"ReleaseYear": 2006,
				"LanguageID": 1,
				"RentalDuration": 3,
				"RentalRate": 2.99,
				"Length": 182,
				"ReplacementCost": 20.99,
				"Rating": "G",
				"LastUpdate": "2013-05-26T14:50:58.951Z",
				"SpecialFeatures": "{Commentaries,\"Behind the Scenes\"}",
				"Fulltext": "'bake':1 'cleopatra':2 'drama':5 'forens':8 'husband':12 'monasteri':20 'must':14 'overcom':15 'psychologist':9 'stun':4 'waitress':17"
			}
		],
		"Actors": [
			{
				"ActorID": 1,
				"FirstName": "Penelope",
				"LastName": "Guiness",
				"LastUpdate": "2013-05-26T14:47:57.62Z"
			},
			{
				"ActorID": 20,
				"FirstName": "Lucille",
				"LastName": "Tracy",
				"LastUpdate": "2013-05-26T14:47:57.62Z"
			},
			{
				"ActorID": 36,
				"FirstName": "Burt",
				"LastName": "Dukakis",
				"LastUpdate": "2013-05-26T14:47:57.62Z"
			},
			{
				"ActorID": 70,
				"FirstName": "Michelle",
				"LastName": "Mcconaughey",
				"LastUpdate": "2013-05-26T14:47:57.62Z"
			},
			{
				"ActorID": 118,
				"FirstName": "Cuba",
				"LastName": "Allen",
				"LastUpdate": "2013-05-26T14:47:57.62Z"
			},
			{
				"ActorID": 187,
				"FirstName": "Renee",
				"LastName": "Ball",
				"LastUpdate": "2013-05-26T14:47:57.62Z"
			},
			{
				"ActorID": 198,
				"FirstName": "Mary",
				"LastName": "Keitel",
				"LastUpdate": "2013-05-26T14:47:57.62Z"
			}
		]
	},
    //...
]

Complete code example can be found at ./examples/quick-start/quick-start.go

This example represent probably the most common use case. Detail info about additional features and use cases can be found at project wiki page.

Benefits

What are the benefits of writing SQL in Go using Jet? The biggest benefit is speed.
Speed is improved in 3 major areas:

Speed of development

Writing SQL queries is much easier directly from Go, because programmer has the help of SQL code completion and SQL type safety directly in Go. Writing code is much faster and code is more robust. Automatic scan to arbitrary structure removes a lot of headache and boilerplate code needed to structure database query result.

Speed of execution

Common web and database server usually are not on the same physical machine, and there is some latency between them. Latency can vary from 5ms to 50+ms. In majority of cases query executed on database is simple query lasting no more than 1ms. In those cases web server handler execution time is directly proportional to latency between server and database. This is not such a big problem if handler calls database couple of times, but what if web server is using ORM to retrieve data from database. ORM sometimes can access the database once for every object needed. Now lets say latency is 30ms and there are 100 different objects required from the database. This handler will last 3 seconds !!!.

With Jet, handler time lost on latency between server and database is constant. Because we can write complex query and return result in one database call. Handler execution will be only proportional to the number of rows returned from database. ORM example replaced with jet will take just 30ms + 'result scan time' = 31ms (rough estimate).

With Jet you can even join the whole database and store the whole structured result in in one query call. This is exactly what is being done in one of the tests: TestJoinEverything. The whole test database is joined and query result(~10,000 rows) is stored in a structured variable in less than 0.7s.

How quickly bugs are found

The most expensive bugs are the one on the production and the least expensive are those found during development. With automatically generated type safe SQL not only queries are written faster but bugs are found sooner.
Lets return to quick start example, and take closer look at a line:

AND(Film.Length.GT(Int(180))),

Lets say someone changes column length to duration from film table. The next go build will fail at that line and the bug will be caught at compile time.

Lets say someone changes the type of length column to some non integer type. Build will also fail at the same line because integer columns and expressions can be only compered to other integer columns and expressions.

Without Jet these bugs will have to be either caught by some test or by manual testing.

Dependencies

At the moment Jet dependence only of:

  • github.com/google/uuid (Used for debug purposes and in data model files)
  • github.com/lib/pq (Used by Jet to read information about database schema types)

To run the tests, additional dependencies are required:

  • github.com/pkg/profile
  • gotest.tools/assert

Versioning

SemVer is used for versioning. For the versions available, see the releases.

License

Copyright 2019 Goran Bjelanovic
Licensed under the Apache License, Version 2.0.

# Packages

No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author

# Functions

ABSf calculates absolute value from float expression.
ABSi calculates absolute value from int expression.
AVG is aggregate function used to calculate avg value from numeric expression.
BIT_AND is aggregate function used to calculates the bitwise AND of all non-null input values, or null if none.
BIT_LENGTH returns number of bits in string expression.
BIT_NOT inverts every bit in integer expression result.
BIT_OR is aggregate function used to calculates the bitwise OR of all non-null input values, or null if none.
Bool creates new bool literal expression.
BOOL_AND is aggregate function.
BOOL_OR is aggregate function.
BoolColumn creates named bool column.
BoolExp is bool expression wrapper around arbitrary expression.
BTRIM removes the longest string consisting only of characters in characters (a space by default) from the start and end of string.
CASE create CASE operator with optional list of expressions.
CAST wraps expression for casting.
CBRT calculates cube root of numeric expression.
CEIL calculates ceil of float expression.
CHAR_LENGTH returns number of characters in string expression.
CHR returns character with the given code.
COALESCE function returns the first of its arguments that is not null.
CONVERT converts string to dest_encoding.
CONVERT_FROM converts string to the database encoding.
CONVERT_TO converts string to dest_encoding.
COUNT is aggregate function.
CURRENT_DATE returns current date.
CURRENT_TIME returns current time with time zone.
CURRENT_TIMESTAMP returns current timestamp with time zone.
Date creates new date expression.
DateColumn creates named date column.
DateExp is date expression wrapper around arbitrary expression.
DECODE decodes binary data from textual representation in string.
ENCODE encodes binary data into a textual representation.
EVERY is aggregate function.
EXCEPT returns all rows that are in the result of query lhs but not in the result of query rhs.
EXCEPT_ALL returns all rows that are in the result of query lhs but not in the result of query rhs.
EXISTS checks for existence of the rows in subQuery.
Float creates new float literal expression.
FloatColumn creates named float column.
FloatExp is date expression wrapper around arbitrary expression.
FLOOR calculates floor of float expression.
GREATEST selects the largest value from a list of expressions.
INITCAP converts the first letter of each word to upper case and the rest to lower case.
Int is constructor for integer expressions literals.
IntegerColumn creates named integer column.
INTERSECT returns all rows that are in query results.
INTERSECT_ALL returns all rows that are in query results.
IntExp is int expression wrapper around arbitrary expression.
LEAST selects the smallest value from a list of expressions.
LEFT returns first n characters in the string.
LENGTH returns number of characters in string with a given encoding.
LN calculates natural algorithm of float expression.
LOCALTIME returns local time of day using optional precision.
LOCALTIMESTAMP returns current date and time using optional precision.
LOCK creates lock statement for list of tables.
LOG calculates logarithm of float expression.
LOWER returns string expression in lower case.
LPAD fills up the string to length length by prepending the characters fill (a space by default).
LTRIM removes the longest string containing only characters from characters (a space by default) from the start of string.
MAXf is aggregate function.
MAXi is aggregate function.
MD5 calculates the MD5 hash of string, returning the result in hexadecimal.
MINf is aggregate function.
MINi is aggregate function.
NewEnumValue creates new named enum value.
NewTable creates new table with schema name, table name and list of columns.
NOT returns negation of bool expression result.
NOW returns current date and time.
NULLIF function returns a null value if value1 equals value2; otherwise it returns value1.
OCTET_LENGTH returns number of bytes in string expression.
RAW can be used for any unsupported functions, operators or expressions.
REPEAT repeats string the specified number of times.
REPLACE replaces all occurrences in string of substring from with substring to.
REVERSE returns reversed string.
RIGHT returns last n characters in the string.
ROUND calculates round of a float expressions with optional precision.
ROW is construct one table row from list of expressions.
RPAD fills up the string to length length by appending the characters fill (a space by default).
RTRIM removes the longest string containing only characters from characters (a space by default) from the end of string.
SELECT creates new SelectStatement with list of projections.
SIGN returns sign of float expression.
SQRT calculates square root of numeric expression.
String creates new string literal expression.
StringColumn creates named string column.
StringExp is string expression wrapper around arbitrary expression.
STRPOS returns location of specified substring (same as position(substring in string), but note the reversed argument order).
SUBSTR extracts substring.
SUMf is aggregate function.
SUMi is aggregate function.
Time creates new time literal expression.
TimeColumn creates named time column.
TimeExp is time expression wrapper around arbitrary expression.
Timestamp creates new timestamp literal expression.
TimestampColumn creates named timestamp column.
TimestampExp is timestamp expression wrapper around arbitrary expression.
Timestampz creates new timestamp with time zone literal expression.
TimestampzColumn creates named timestamp with time zone column.
TimestampzExp is timestamp with time zone expression wrapper around arbitrary expression.
Timez creates new time with time zone literal expression.
TimezColumn creates named time with time zone column.
TimezExp is time with time zone expression wrapper around arbitrary expression.
TO_ASCII convert string to ASCII from another encoding.
TO_CHAR converts expression to string with format.
TO_DATE converts string to date using format.
TO_HEX converts number to its equivalent hexadecimal representation.
TO_NUMBER converts string to numeric using format.
TO_TIMESTAMP converts string to time stamp with time zone using format.
TRUNC calculates trunc of float expression with optional precision.
UNION effectively appends the result of sub-queries(select statements) into single query.
UNION_ALL effectively appends the result of sub-queries(select statements) into single query.
UPPER returns string expression in upper case.
WRAP wraps list of expressions with brackets '(' and ')'.

# Constants

DEFAULT is jet equivalent of SQL DEFAULT.
Lock types for LockStatement.
Lock types for LockStatement.
Lock types for LockStatement.
Lock types for LockStatement.
Lock types for LockStatement.
Lock types for LockStatement.
Lock types for LockStatement.
Lock types for LockStatement.

# Variables

Select statements lock types.
Select statements lock types.
NULL is jet equivalent of SQL NULL.
Select statements lock types.
STAR is jet equivalent of SQL *.
Select statements lock types.

# Interfaces

BoolExpression interface.
CaseOperator is interface for SQL case operator.
Column is common column interface for all types of columns.
ColumnBool is interface for SQL boolean columns.
ColumnDate is interface of SQL date columns.
ColumnFloat is interface for SQL real, numeric, decimal or double precision column.
ColumnInteger is interface for SQL smallint, integer, bigint columns.
ColumnString is interface for SQL text, character, character varying bytea, uuid columns and enums types.
ColumnTime is interface for SQL time column.
ColumnTimestamp is interface of SQL timestamp columns.
ColumnTimestampz is interface of SQL timestamp with timezone columns.
ColumnTimez is interface of SQL time with time zone columns.
DateExpression is interface for all SQL date expressions.
DeleteStatement is interface for SQL DELETE statement.
Expression is common interface for all expressions.
FloatExpression is interface for SQL float columns.
InsertStatement is interface for SQL INSERT statements.
IntegerExpression interface.
LockStatement interface for SQL LOCK statement.
NumericExpression is common interface for all integer and float expressions.
ReadableTable interface.
SelectLock is interface for SELECT statement locks.
SelectStatement is interface for SQL SELECT statements.
SelectTable is interface for SELECT sub-queries.
Statement is common interface for all statements(SELECT, INSERT, UPDATE, DELETE, LOCK).
StringExpression interface.
Table interface.
TimeExpression interface.
TimestampExpression interface.
TimestampzExpression interface.
TimezExpression interface 'time with time zone'.
UpdateStatement is interface of SQL UPDATE statement.
WritableTable interface.

# Type aliases

ColumnList is redefined type to support list of columns as single projection.
ProjectionList is a redefined type, so that ProjectionList can be used as a projection.
TableLockMode is a type of possible SQL table lock.