maddy

Fork https://github.com/foxcpp/maddy

git clone git://git.lin.moe/go/maddy.git

  1/*
  2Maddy Mail Server - Composable all-in-one email server.
  3Copyright © 2019-2020 Max Mazurov <fox.cpp@disroot.org>, Maddy Mail Server contributors
  4
  5This program is free software: you can redistribute it and/or modify
  6it under the terms of the GNU General Public License as published by
  7the Free Software Foundation, either version 3 of the License, or
  8(at your option) any later version.
  9
 10This program is distributed in the hope that it will be useful,
 11but WITHOUT ANY WARRANTY; without even the implied warranty of
 12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 13GNU General Public License for more details.
 14
 15You should have received a copy of the GNU General Public License
 16along with this program.  If not, see <https://www.gnu.org/licenses/>.
 17*/
 18
 19package table
 20
 21import (
 22	"context"
 23	"fmt"
 24
 25	"github.com/foxcpp/maddy/framework/config"
 26	"github.com/foxcpp/maddy/framework/module"
 27	_ "github.com/lib/pq"
 28)
 29
 30type SQLTable struct {
 31	modName  string
 32	instName string
 33
 34	wrapped *SQL
 35}
 36
 37func NewSQLTable(modName, instName string, _, _ []string) (module.Module, error) {
 38	return &SQLTable{
 39		modName:  modName,
 40		instName: instName,
 41
 42		wrapped: &SQL{
 43			modName:  modName,
 44			instName: instName,
 45		},
 46	}, nil
 47}
 48
 49func (s *SQLTable) Name() string {
 50	return s.modName
 51}
 52
 53func (s *SQLTable) InstanceName() string {
 54	return s.instName
 55}
 56
 57func (s *SQLTable) Init(cfg *config.Map) error {
 58	var (
 59		driver      string
 60		dsnParts    []string
 61		tableName   string
 62		keyColumn   string
 63		valueColumn string
 64	)
 65	cfg.String("driver", false, true, "", &driver)
 66	cfg.StringList("dsn", false, true, nil, &dsnParts)
 67	cfg.String("table_name", false, true, "", &tableName)
 68	cfg.String("key_column", false, false, "key", &keyColumn)
 69	cfg.String("value_column", false, false, "value", &valueColumn)
 70	if _, err := cfg.Process(); err != nil {
 71		return err
 72	}
 73
 74	// sql_table module literally wraps the sql_query module by generating a
 75	// configuration block for it.
 76
 77	var (
 78		useNamedArgs string
 79
 80		lookupQuery string
 81		addQuery    string
 82		listQuery   string
 83		setQuery    string
 84		delQuery    string
 85	)
 86	if driver == "sqlite3" {
 87		useNamedArgs = "yes"
 88		lookupQuery = fmt.Sprintf("SELECT %s FROM %s WHERE %s = :key", valueColumn, tableName, keyColumn)
 89		addQuery = fmt.Sprintf("INSERT INTO %s(%s, %s) VALUES(:key, :value)", tableName, keyColumn, valueColumn)
 90		listQuery = fmt.Sprintf("SELECT %s from %s", keyColumn, tableName)
 91		setQuery = fmt.Sprintf("UPDATE %s SET %s = :value WHERE %s = :key", tableName, valueColumn, keyColumn)
 92		delQuery = fmt.Sprintf("DELETE FROM %s WHERE %s = :key", tableName, keyColumn)
 93	} else {
 94		useNamedArgs = "no"
 95		lookupQuery = fmt.Sprintf("SELECT %s FROM %s WHERE %s = $1", valueColumn, tableName, keyColumn)
 96		addQuery = fmt.Sprintf("INSERT INTO %s(%s, %s) VALUES($1, $2)", tableName, keyColumn, valueColumn)
 97		listQuery = fmt.Sprintf("SELECT %s from %s", keyColumn, tableName)
 98		setQuery = fmt.Sprintf("UPDATE %s SET %s = $2 WHERE %s = $1", tableName, valueColumn, keyColumn)
 99		delQuery = fmt.Sprintf("DELETE FROM %s WHERE %s = $1", tableName, keyColumn)
100	}
101
102	return s.wrapped.Init(config.NewMap(cfg.Globals, config.Node{
103		Children: []config.Node{
104			{
105				Name: "driver",
106				Args: []string{driver},
107			},
108			{
109				Name: "dsn",
110				Args: dsnParts,
111			},
112			{
113				Name: "named_args",
114				Args: []string{useNamedArgs},
115			},
116			{
117				Name: "lookup",
118				Args: []string{lookupQuery},
119			},
120			{
121				Name: "add",
122				Args: []string{addQuery},
123			},
124			{
125				Name: "list",
126				Args: []string{listQuery},
127			},
128			{
129				Name: "set",
130				Args: []string{setQuery},
131			},
132			{
133				Name: "del",
134				Args: []string{delQuery},
135			},
136			{
137				Name: "init",
138				Args: []string{fmt.Sprintf(`CREATE TABLE IF NOT EXISTS %s (
139					%s TEXT PRIMARY KEY NOT NULL,
140					%s TEXT NOT NULL
141				)`, tableName, keyColumn, valueColumn)},
142			},
143		},
144	}))
145}
146
147func (s *SQLTable) Close() error {
148	return s.wrapped.Close()
149}
150
151func (s *SQLTable) Lookup(ctx context.Context, val string) (string, bool, error) {
152	return s.wrapped.Lookup(ctx, val)
153}
154
155func (s *SQLTable) LookupMulti(ctx context.Context, val string) ([]string, error) {
156	return s.wrapped.LookupMulti(ctx, val)
157}
158
159func (s *SQLTable) Keys() ([]string, error) {
160	return s.wrapped.Keys()
161}
162
163func (s *SQLTable) RemoveKey(k string) error {
164	return s.wrapped.RemoveKey(k)
165}
166
167func (s *SQLTable) SetKey(k, v string) error {
168	return s.wrapped.SetKey(k, v)
169}
170
171func init() {
172	module.Register("table.sql_table", NewSQLTable)
173}