obreron

package module
v1.0.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 2, 2023 License: MIT Imports: 3 Imported by: 0

README

Obreron

Sql Builder escrito en Go.


Dialectos soportados

  • Mysql
  • Postgresql

¿Por que?

Porqué escribir y usar SQL en go es aburrido y trabajoso. Sobre todo en el caso de tener que construirlas dinámicamente.

Hay varios proyectos similares, pero no encontramos ninguno que cumpliera con el requisito de ser liviano, o agregaban también una referencia a la conexión u otras dependencias.

Instalación

Puede instalar obreron usando go get

$ go get github.com/profe-ajedrez/obreron

Uso

Con obreron es fácil construir consultas. Por ejemplo el siguiente código

b = obreron.NewMaryBuilder()

q := b.Select(
    "id",
    "name",
    "mail",
    b.Quote("columna con espacios en el nombre"),
).From("users", "u").String()

produce la siguiente consulta en la variable q:

SELECT id,name,mail, `columna con espacios en el nombre` FROM users u 

Pero para eso no necesitamos un Sql Builder. Estos brillan cuando debemos construir consultas dinámicas, como para el caso de armar filtros.

b := NewMaryBuilder()

	// options es un struct que guarda las opciones para armar el filtro
	options := struct {
		useName     bool
		useFullName bool
		useAddress  bool
		status      int8
		limit       int64
	}{
		useName:     true, // agregar columna user_name
		useFullName: true, // agregar columna useFullName
		useAddress:  false, // NO agregar columna address
		status:      0, // para saber como filtrar users por su status
		limit:       25, // limite para la consulta
	}

    // Los campos user_id, user_mail y user_type serán agregados a la consulta
	b.Select(
		"user_id", "user_mail", "user_type",
	).From("users", "u").Where().Limit(options.limit)

    // Los campos user_name, user_fullname y user_address se agregaran a la consulta solo si la condición pasada es verdadera
	b.AddColumnIf(options.useName, "user_name", "").AddColumnIf(options.useFullName, "user_fullname", "")
	b.AddColumnIf(options.useAddress, "user_address", "")

    // Agregamos una condición en la que decimos que agregue el filtro por user_status si a opción correspondiente es > -1
	b.AndParamIf(options.status > -1, "user_status", "=", options.status)

    // tras construir la consulta, q contendrá la query y p los parámetros registrados para su uso
	q, p := b.Build()

El ejemplo anterior construye en q la consulta

SELECT user_id,user_mail,user_type,user_name,user_fullname FROM users u  WHERE 1=1  AND user_status = ? LIMIT 25 

y deja en p un []interface {}(int8) 0

Benchmarks

Presentamos el siguiente benchmark


func BenchmarkSlBuilder(b *testing.B) {
	b.ResetTimer()
	for i := 0; i <= b.N; i++ {
		heavyQueryBuild(b)
	}

}

func heavyQueryBuild(b testing.TB) (string, []interface{}) {
	bl := NewMaryBuilder()

	filterByClassification := true

	bl.Select(
		"dd.created_at_ms",
		"d.fecha_despacho AS fecha_movimiento",
		`CONCAT(
		p.nombre_producto, ' ', vp.descripcion_variante
		) AS nombre_producto`,
		"0 AS cantidad_entrada",
		"dd.cantidad_desp AS cantidad_salida",
		"dd.stock AS stock",
		"dd.costo AS costo",
		"vp.codigo",
		"vp.barras",
		"CASE WHEN vdt.estado = 0 THEN IFNULL(vdt.num_doc, '') ELSE '' END AS num_doc",
		"CASE WHEN vdt.estado = 0 THEN IFNULL(td.nombre_tipo, '') ELSE 'nulo' END AS nombre_tipo",
		`CASE WHEN vdt.estado = 0 THEN IFNULL(
		vdt.id_documento, 
		0
		) ELSE -99 END AS id_documento`,
		"IFNULL(dd.ids_detalle_ingreso, 0) AS id_detalle_ingreso",
		"0 AS id_consumo",
		`CONCAT(
		us.nombre_usuario, ' ', us.apellido_usuario
		) AS usuario_movimiento`,
		"d.id_despacho AS id_despacho",
		"td.uso_documento AS uso_documento",
		"dd.id_detalle_despacho",
		"vdt.estado_documento",
		"dd.numero_serie",
	).From(
		"detalle_desp", "dd",
	).Inner(
		"cart_it", "ci", "dd.id_cart_it=ci.id_cart_it",
	).Inner(
		"variante", "vp", "ci.id_variante= vp.id_variante",
	).Inner(
		"producto", "p", "vp.id_producto = p.id_producto",
	).Inner(
		"detalle_venta_documento_tributario", "dvdt", "dd.id_detalle_despacho = dvdt.id_detalle_despacho",
	).Inner(
		NewMaryBuilder().Select("*").From("documento", "vdt").Where().AndParam("vdt.id_documento", "=", 126), "vdt", "vdt.id_documento = dvdt.id_documento",
	).Where().AndParam("vp.id_variante IS NOT NULL", "", nil)

	bl.AndParam("d.id_sucursal", "=", 126)
	bl.AndParamIf(filterByClassification, "p.clasificacion", "!=", 3).GroupBy("dd.id_detalle_desp")

	bl2 := NewMaryBuilder()

	bl2.Select().From(bl, "out_detail")

	bl2.AddColumn("created_at_ms", "")
	bl2.AddColumn("fecha_movimiento", "")
	bl2.AddColumn("nombre_producto", "")
	bl2.AddColumn("cantidad_entrada", "")
	bl2.AddColumn("cantidad_salida", "")
	bl2.AddColumn("stock", "")
	bl2.AddColumn("costo", "")
	bl2.AddColumn("codigo_variante_producto", "")
	bl2.AddColumn("codigo_barras", "")
	bl2.AddColumn("num_doc_tributario", "")
	bl2.AddColumn("nombre_tipo_documento", "")
	bl2.AddColumn("id_venta_documento_tributario", "")
	bl2.AddColumn("id_detalle_ingreso_stock", "")
	bl2.AddColumn("id_consumo_stock", "")
	bl2.AddColumn("usuario_movimiento", "")
	bl2.AddColumn("id_despacho", "")
	bl2.AddColumn("uso_documento", "")
	bl2.AddColumn("numero_serie ", "")

	bl2.GroupBy("id_detalle_desp")
	bl2.OrderBy("id_despacho ASC")

	q, params := bl2.Build()

	return q, params

}

func BenchmarkJoin(b *testing.B) {
	b.ResetTimer()
	for i := 0; i <= b.N; i++ {
		testJoin(b)
	}
}

func testJoin(t testing.TB) string {
	b := NewMaryBuilder()

	b2 := NewMaryBuilder()

	b2.Select("SUM(monto_neto) AS monto_neto").From("table_c", "cc")

	return b.Select(
		"id",
		b2,
	).From("table_a", "t").Where().AndParam("'B' = 'B'", "", nil).String()
}

goos: linux
goarch: amd64
pkg: github.com/profe-ajedrez/obreron
cpu: Intel(R) Core(TM) i7-10700 CPU @ 2.90GHz
BenchmarkSlBuilder-3     1432620              6699 ns/op           14354 B/op        104 allocs/op
BenchmarkSlBuilder-3     1424514              6694 ns/op           14354 B/op        104 allocs/op
BenchmarkSlBuilder-3     1368060              6738 ns/op           14354 B/op        104 allocs/op
BenchmarkSlBuilder-3     1406500              6997 ns/op           14354 B/op        104 allocs/op
BenchmarkSlBuilder-3     1336514              7647 ns/op           14354 B/op        104 allocs/op
BenchmarkSlBuilder-6     1288227              7061 ns/op           14356 B/op        104 allocs/op
BenchmarkSlBuilder-6     1354748              7179 ns/op           14356 B/op        104 allocs/op
BenchmarkSlBuilder-6     1265295              7229 ns/op           14356 B/op        104 allocs/op
BenchmarkSlBuilder-6     1355659              7274 ns/op           14356 B/op        104 allocs/op
BenchmarkSlBuilder-6     1297155              7079 ns/op           14356 B/op        104 allocs/op
PASS
ok      github.com/profe-ajedrez/obreron        167.208s

Static checks

goreportcard-cli
~/go/bin/goreportcard-cli   
Grade .......... A+ 100.0%
Files .................. 4
Issues ................. 3
gofmt ............... 100%
go_vet .............. 100%
gocyclo ............. 100%
ineffassign ......... 100%
license ............. 100%
misspell ............. 25%

Documentation

Overview

Package obreron es un paquete que permite construir consultas sql dinámicamente

Index

Constants

View Source
const (
	// Enclose indica poner un `(` antes del siguiente estamento
	Enclose = parseOptsMul(1)

	// NoEnclose indica NO poner un `(` antes del siguiente estamento
	NoEnclose = parseOptsMul(2)

	// EncloseOnlyBuilders indica rodear de parentesis solo a SQLBuilders
	EncloseOnlyBuilders = parseOptsMul(3)

	// UseAs indica agregar una clausula `AS` antes del alias, si hubiera
	UseAs = parseOpts(true)

	// NoUseAs indica NO agregar una clausula `AS` antes del alias, si hubiera
	NoUseAs = parseOpts(false)

	// Quote indica si escapar con backticks el siguiente identificador
	Quote = parseOpts(true)

	// NoQuote indica NO escapar con backticks el siguiente identificador
	NoQuote = parseOpts(false)
)

Variables

This section is empty.

Functions

This section is empty.

Types

type Dialect

type Dialect interface {
	Quote(v interface{}) string
	ParamMark() string
	OpenEnclose() string
	CloseEnclose() string
}

Dialect representa el dialecto de la consulta

type Mysql

type Mysql struct{}

Mysql es un dialecto que permite construir consultas para mysql y mariadb

func (Mysql) CloseEnclose

func (m Mysql) CloseEnclose() string

CloseEnclose Agrega un cierre de parentesis ) la consulta

func (Mysql) OpenEnclose

func (m Mysql) OpenEnclose() string

OpenEnclose Agrega un abre parentesis ( la consulta

func (Mysql) ParamMark

func (m Mysql) ParamMark() string

ParamMark devuelve una marca de parámetro posicional

func (Mysql) Quote

func (m Mysql) Quote(v interface{}) string

Quote escapa a su argumento según el dialecto de la consulta

type SQLBuilder

type SQLBuilder struct {
	bytes.Buffer
	// contains filtered or unexported fields
}

SQLBuilder engloba bytes.Buffer y le da el poder de tener dialecto y parámetros

func (*SQLBuilder) AddParam

func (sb *SQLBuilder) AddParam(p ...interface{})

AddParam agrega un paràmetro al SQLBuilder

func (*SQLBuilder) AddParamIf added in v1.0.1

func (sb *SQLBuilder) AddParamIf(cond bool, p ...interface{})

AddParam agrega un paràmetro al SQLBuilder si cond es verdadera

func (*SQLBuilder) Build

func (sb *SQLBuilder) Build() (string, []interface{})

Build construye la consulta devolviendo una tupla conteniendola en un string y los parámetros registrados para su uso

func (*SQLBuilder) Dialect

func (sb *SQLBuilder) Dialect() Dialect

Dialect devuelve el dialecto de la consulta

func (*SQLBuilder) Params

func (sb *SQLBuilder) Params() []interface{}

Params devuelve el slice de parámetros del SQLBuilder

func (*SQLBuilder) ResetParams

func (sb *SQLBuilder) ResetParams()

ResetParams resetea el slices de parámetros

type Select

type Select struct {
	*SQLBuilder
	// contains filtered or unexported fields
}

Select es el builder para consultas que tienen datos

func NewMaryBuilder

func NewMaryBuilder() *Select

NewMaryBuilder devuelve un nuevo sql builder listo para trabajar

func (*Select) AddColumn

func (s *Select) AddColumn(c interface{}, a string) *Select

AddColumn agrega una columna con su alias. la columna c puede ser string u otro SQLBuilder. Puede omitir el alias pasando un string vacio

func (*Select) AddColumnIf

func (s *Select) AddColumnIf(cond bool, c interface{}, a string) *Select

AddColumnIf agrega una columna con su alias si se cumple condición cond. la columna c puede ser string u otro SQLBuilder. Puede omitir el alias pasando un string vacio

func (*Select) And

func (s *Select) And(c string) *Select

And Agrega una condición usando conector AND c es un strig conteniendo la condición completa

func (*Select) AndIf

func (s *Select) AndIf(cond bool, c string) *Select

AndIf Agrega una condición usando conector AND solo si cond es true c es un strig conteniendo la condición completa

func (*Select) AndParam

func (s *Select) AndParam(c interface{}, op string, param interface{}) *Select

AndParam Agrega una condición usando conector AND c puede ser la condición como string o como un SQLBuilder op es el operador y param el parámetro de la condición

func (*Select) AndParamIf

func (s *Select) AndParamIf(cond bool, c interface{}, op string, param interface{}) *Select

AndParamIf Agrega una condición usando conector AND solo si cond es true c puede ser la condición como string o como un SQLBuilder op es el operador y param el parámetro de la condición

func (*Select) Build

func (s *Select) Build() (string, []interface{})

Build construye la consulta devolviendo una tupla conteniendola en un string y los parámetros registrados para su uso

func (*Select) CloseEnclose

func (s *Select) CloseEnclose() string

CloseEnclose Agrega un cierre de parentesis ) la consulta

func (*Select) From

func (s *Select) From(source interface{}, a string) *Select

From define el origen para obtener los datos de la consulta. Puede ser un string u otro sql builder

func (*Select) GroupBy

func (s *Select) GroupBy(c string) *Select

GroupBy agrega la clausula GROUP BY al Sql Builder

func (*Select) Having

func (s *Select) Having(c string) *Select

Having agrega la clausula HAVING al Sql Builder

func (*Select) Inner

func (s *Select) Inner(c interface{}, a string, on string) *Select

Inner Agrega un inner join a la construcción de la query. El joinable c puede ser string o un SQLBuilder a es el alias, si no lo necesita puede pasarlo vacio. on contiene la condición para la clausula on, puede dejarla vacia para que no se agregue

func (*Select) InnerIf

func (s *Select) InnerIf(cond bool, c interface{}, a string, on string) *Select

InnerIf Agrega un inner join a la construcción de la query si la condición `cond` es verdadera. El joinable c puede ser string o un SQLBuilder a es el alias, si no lo necesita puede pasarlo vacio. on contiene la condición para la clausula on, puede dejarla vacia para que no se agregue

func (*Select) Left

func (s *Select) Left(c interface{}, a string, on string) *Select

Left Agrega un left join a la construcción de la query. El joinable c puede ser string o un SQLBuilder a es el alias, si no lo necesita puede pasarlo vacio. on contiene la condición para la clausula on, puede dejarla vacia para que no se agregue

func (*Select) LeftIf

func (s *Select) LeftIf(cond bool, c interface{}, a string, on string) *Select

LeftIf Agrega un left join a la construcción de la query si la condición `cond` es verdadera. El joinable c puede ser string o un SQLBuilder a es el alias, si no lo necesita puede pasarlo vacio. on contiene la condición para la clausula on, puede dejarla vacia para que no se agregue

func (*Select) Limit

func (s *Select) Limit(l int64) *Select

Limit establece el limite de la consulta. Si este valor es -1 no se agregara la clausula OFFSET a la query construida

func (*Select) Offset

func (s *Select) Offset(o int64) *Select

Offset establece el offset de la consulta. Si este valor es -1 no se agregara la clausula OFFSET a la query construida

func (*Select) OpenEnclose

func (s *Select) OpenEnclose() string

OpenEnclose Agrega un abre parentesis ( la consulta

func (*Select) OrParam

func (s *Select) OrParam(c interface{}, op string, param interface{}) *Select

OrParam Agrega una condición usando conector AND c puede ser la condición como string o como un SQLBuilder op es el operador y param el parámetro de la condición

func (*Select) OrderBy

func (s *Select) OrderBy(c string) *Select

OrderBy agrega la clausula ORDER BY al Sql Builder

func (*Select) Params

func (s *Select) Params() []interface{}

Params devuelve los paramétros registrados para los componentes de la consulta en el orden esperado para las distintistas clausulas

func (*Select) Quote

func (s *Select) Quote(c interface{}) string

Quote escapa a su argumento según el dialecto de la consulta

func (*Select) Reset

func (s *Select) Reset()

func (*Select) Right

func (s *Select) Right(c interface{}, a string, on string) *Select

Right Agrega un right join a la construcción de la query. El joinable c puede ser string o un SQLBuilder a es el alias, si no lo necesita puede pasarlo vacio. on contiene la condición para la clausula on, puede dejarla vacia para que no se agregue

func (*Select) RightIF

func (s *Select) RightIF(cond bool, c interface{}, a string, on string) *Select

RightIF Agrega un right join a la construcción de la query si la condición `cond` es verdadera. El joinable c puede ser string o un SQLBuilder a es el alias, si no lo necesita puede pasarlo vacio. on contiene la condición para la clausula on, puede dejarla vacia para que no se agregue

func (*Select) Select

func (s *Select) Select(cs ...interface{}) *Select

Select define consultas para la consulta. Cada ve que se llama resetea el buffer de construción

func (*Select) String

func (s *Select) String() string

func (*Select) Where

func (s *Select) Where() *Select

Where inicializa la clausula where

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL