1/*2Maddy Mail Server - Composable all-in-one email server.3Copyright © 2019-2020 Max Mazurov <fox.cpp@disroot.org>, Maddy Mail Server contributors45This program is free software: you can redistribute it and/or modify6it under the terms of the GNU General Public License as published by7the Free Software Foundation, either version 3 of the License, or8(at your option) any later version.910This program is distributed in the hope that it will be useful,11but WITHOUT ANY WARRANTY; without even the implied warranty of12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the13GNU General Public License for more details.1415You should have received a copy of the GNU General Public License16along with this program. If not, see <https://www.gnu.org/licenses/>.17*/1819package table2021import (22 "context"23 "fmt"2425 "github.com/foxcpp/maddy/framework/config"26 "github.com/foxcpp/maddy/framework/module"27 _ "github.com/lib/pq"28)2930type SQLTable struct {31 modName string32 instName string3334 wrapped *SQL35}3637func NewSQLTable(modName, instName string, _, _ []string) (module.Module, error) {38 return &SQLTable{39 modName: modName,40 instName: instName,4142 wrapped: &SQL{43 modName: modName,44 instName: instName,45 },46 }, nil47}4849func (s *SQLTable) Name() string {50 return s.modName51}5253func (s *SQLTable) InstanceName() string {54 return s.instName55}5657func (s *SQLTable) Init(cfg *config.Map) error {58 var (59 driver string60 dsnParts []string61 tableName string62 keyColumn string63 valueColumn string64 )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 err72 }7374 // sql_table module literally wraps the sql_query module by generating a75 // configuration block for it.7677 var (78 useNamedArgs string7980 lookupQuery string81 addQuery string82 listQuery string83 setQuery string84 delQuery string85 )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 }101102 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 NULL141 )`, tableName, keyColumn, valueColumn)},142 },143 },144 }))145}146147func (s *SQLTable) Close() error {148 return s.wrapped.Close()149}150151func (s *SQLTable) Lookup(ctx context.Context, val string) (string, bool, error) {152 return s.wrapped.Lookup(ctx, val)153}154155func (s *SQLTable) LookupMulti(ctx context.Context, val string) ([]string, error) {156 return s.wrapped.LookupMulti(ctx, val)157}158159func (s *SQLTable) Keys() ([]string, error) {160 return s.wrapped.Keys()161}162163func (s *SQLTable) RemoveKey(k string) error {164 return s.wrapped.RemoveKey(k)165}166167func (s *SQLTable) SetKey(k, v string) error {168 return s.wrapped.SetKey(k, v)169}170171func init() {172 module.Register("table.sql_table", NewSQLTable)173}