Skip to content

Commit d1c86ee

Browse files
authored
fix: truncate should quote table name (#410)
* fix: truncate should quote table name * test: fix failed tests * test: fix failed tests * test: fix failed tests * test: fix failed tests * test: fix failed tests * test: fix failed tests * test: fix failed tests * test: fix failed tests * test: fix failed tests * docs: changelog
1 parent d303d72 commit d1c86ee

18 files changed

+303
-108
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ Types of changes
1414
- `Fixed` for any bug fixes.
1515
- `Security` in case of vulnerabilities.
1616

17+
## [3.6.1]
18+
19+
- `Fixed` push to tables with special characters in their names (PostgreSQL)
20+
1721
## [3.6.0]
1822

1923
- `Added` new push mode `upsert` to insert or update rows based on primary keys

internal/infra/commonsql/dialect.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,18 @@ type Dialect interface {
5454
// Quote identifier
5555
Quote(id string) string
5656

57+
// BlankTest generate a SQL test to check if a column is blank (spaces only)
58+
BlankTest(column string) string
59+
// EmptyTest generate a SQL test to check if a column is empty (zero length)
60+
EmptyTest(column string) string
61+
62+
// EnableConstraintsStatement generate statments to activate constraintes
63+
EnableConstraintsStatement(tableName string) string
64+
// DisableConstraintsStatement generate statments to deactivate constraintes
65+
DisableConstraintsStatement(tableName string) string
66+
// TruncateStatement generate statement to truncat table content
67+
TruncateStatement(tableName string) string
68+
5769
// Deprecated
5870
CreateSelect(sel string, where string, limit string, columns string, from string) string
5971
}

internal/infra/commonsql/dialect_db2.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,3 +135,28 @@ func (db2 Db2Dialect) CreateSelect(sel string, where string, limit string, colum
135135
func (db2 Db2Dialect) selectPresence(column string) string {
136136
return fmt.Sprintf("CASE WHEN %s IS NOT NULL THEN 1 ELSE NULL END AS %s", db2.Quote(column), db2.Quote(column))
137137
}
138+
139+
// BlankTest
140+
func (db2 Db2Dialect) BlankTest(column string) string {
141+
panic("unimplemented")
142+
}
143+
144+
// EmptyTest
145+
func (db2 Db2Dialect) EmptyTest(column string) string {
146+
panic("unimplemented")
147+
}
148+
149+
// EnableConstraintsStatement generate statments to activate constraintes
150+
func (db2 Db2Dialect) EnableConstraintsStatement(tableName string) string {
151+
panic("unimplemented")
152+
}
153+
154+
// DisableConstraintsStatement generate statments to deactivate constraintes
155+
func (db2 Db2Dialect) DisableConstraintsStatement(tableName string) string {
156+
panic("unimplemented")
157+
}
158+
159+
// TruncateStatement generate statement to truncat table content
160+
func (db2 Db2Dialect) TruncateStatement(tableName string) string {
161+
return fmt.Sprintf("TRUNCATE TABLE %s IMMEDIATE", db2.Quote(tableName))
162+
}

internal/infra/commonsql/dialect_mariadb.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,3 +135,28 @@ func (sd MariadbDialect) CreateSelect(sel string, where string, limit string, co
135135
func (sd MariadbDialect) selectPresence(column string) string {
136136
return fmt.Sprintf("CASE WHEN %s IS NOT NULL THEN 'TRUE' ELSE NULL END AS %s", sd.Quote(column), sd.Quote(column))
137137
}
138+
139+
// BlankTest implements SQLDialect.
140+
func (d MariadbDialect) BlankTest(column string) string {
141+
panic("unimplemented")
142+
}
143+
144+
// EmptyTest implements SQLDialect.
145+
func (d MariadbDialect) EmptyTest(column string) string {
146+
return fmt.Sprintf("%s = ''", column)
147+
}
148+
149+
// EnableConstraintsStatement generate statments to activate constraintes
150+
func (d MariadbDialect) EnableConstraintsStatement(tableName string) string {
151+
return "SET GLOBAL FOREIGN_KEY_CHECKS=1"
152+
}
153+
154+
// DisableConstraintsStatement generate statments to deactivate constraintes
155+
func (d MariadbDialect) DisableConstraintsStatement(tableName string) string {
156+
return "SET GLOBAL FOREIGN_KEY_CHECKS=0"
157+
}
158+
159+
// TruncateStatement generate statement to truncat table content
160+
func (d MariadbDialect) TruncateStatement(tableName string) string {
161+
return fmt.Sprintf("TRUNCATE TABLE %s", d.Quote(tableName))
162+
}

internal/infra/commonsql/dialect_oracle.go

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,3 +135,84 @@ func (od OracleDialect) CreateSelect(sel string, where string, limit string, col
135135
func (od OracleDialect) selectPresence(column string) string {
136136
return fmt.Sprintf("CASE WHEN %s IS NOT NULL THEN 'TRUE' ELSE NULL END AS %s", od.Quote(column), od.Quote(column))
137137
}
138+
139+
// BlankTest generate a SQL test to check if a column is blank (spaces only)
140+
func (od OracleDialect) BlankTest(column string) string {
141+
return fmt.Sprintf("TRIM(%s) IS NULL", od.Quote(column))
142+
}
143+
144+
// EmptyTest generate a SQL test to check if a column is empty (zero length)
145+
func (od OracleDialect) EmptyTest(column string) string {
146+
return fmt.Sprintf("%s IS NULL", od.Quote(column))
147+
}
148+
149+
// EnableConstraintsStatement generate statments to activate constraintes
150+
func (od OracleDialect) EnableConstraintsStatement(tableName string) string {
151+
schemaAndTable := strings.Split(tableName, ".")
152+
sql := &strings.Builder{}
153+
sql.WriteString(
154+
`BEGIN
155+
FOR c IN(
156+
SELECT c.owner, c.table_name, c.constraint_name
157+
FROM user_constraints c
158+
CONNECT BY PRIOR c.constraint_name = c.r_constraint_name
159+
START WITH c.constraint_name IN (
160+
SELECT c.constraint_name
161+
FROM user_constraints c
162+
WHERE c.status = 'DISABLED' AND c.table_name = '`)
163+
if len(schemaAndTable) == 2 {
164+
sql.WriteString(schemaAndTable[1])
165+
sql.WriteString("' AND c.owner = '")
166+
sql.WriteString(schemaAndTable[0])
167+
sql.WriteString("'")
168+
} else {
169+
sql.WriteString(schemaAndTable[0])
170+
sql.WriteString("' AND c.owner = sys_context( 'userenv', 'current_schema' )")
171+
}
172+
sql.WriteString(`)
173+
LOOP
174+
dbms_utility.exec_ddl_statement('alter table "' || c.owner || '"."' || c.table_name || '" disable constraint ' || c.constraint_name);
175+
END LOOP;
176+
END;`)
177+
return sql.String()
178+
}
179+
180+
// DisableConstraintsStatement generate statments to deactivate constraintes
181+
func (od OracleDialect) DisableConstraintsStatement(tableName string) string {
182+
schemaAndTable := strings.Split(tableName, ".")
183+
sql := &strings.Builder{}
184+
sql.WriteString(
185+
`BEGIN
186+
FOR c IN(
187+
SELECT c.owner, c.table_name, c.constraint_name
188+
FROM user_constraints c
189+
CONNECT BY PRIOR c.constraint_name = c.r_constraint_name
190+
START WITH c.constraint_name IN (
191+
SELECT c.constraint_name
192+
FROM user_constraints c
193+
WHERE c.status = 'ENABLED' AND c.table_name = '`)
194+
if len(schemaAndTable) == 2 {
195+
sql.WriteString(schemaAndTable[1])
196+
sql.WriteString("' AND c.owner = '")
197+
sql.WriteString(schemaAndTable[0])
198+
sql.WriteString("'")
199+
} else {
200+
sql.WriteString(schemaAndTable[0])
201+
sql.WriteString("' AND c.owner = sys_context( 'userenv', 'current_schema' )")
202+
}
203+
sql.WriteString(`)
204+
LOOP
205+
dbms_utility.exec_ddl_statement('alter table "' || c.owner || '"."' || c.table_name || '" disable constraint ' || c.constraint_name);
206+
END LOOP;
207+
END;`)
208+
return sql.String()
209+
}
210+
211+
// TruncateStatement generate statement to truncat table content
212+
func (od OracleDialect) TruncateStatement(tableName string) string {
213+
schemaAndTable := strings.Split(tableName, ".")
214+
if len(schemaAndTable) == 2 {
215+
return fmt.Sprintf("TRUNCATE TABLE %s.%s", od.Quote(schemaAndTable[0]), od.Quote(schemaAndTable[1]))
216+
}
217+
return fmt.Sprintf("TRUNCATE TABLE %s", od.Quote(tableName))
218+
}

internal/infra/commonsql/dialect_postgres.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,3 +135,40 @@ func (pgd PostgresDialect) CreateSelect(sel string, where string, limit string,
135135
func (pgd PostgresDialect) selectPresence(column string) string {
136136
return fmt.Sprintf("CASE WHEN (%s IS NOT NULL) THEN TRUE ELSE NULL END AS %s", pgd.Quote(column), pgd.Quote(column))
137137
}
138+
139+
// BlankTest implements SQLDialect.
140+
func (pgd PostgresDialect) BlankTest(column string) string {
141+
return fmt.Sprintf("TRIM(%s) = ''", pgd.Quote(column))
142+
}
143+
144+
// EmptyTest implements SQLDialect.
145+
func (pgd PostgresDialect) EmptyTest(column string) string {
146+
return fmt.Sprintf("%s = ''", pgd.Quote(column))
147+
}
148+
149+
// EnableConstraintsStatement generate statments to activate constraintes
150+
func (pgd PostgresDialect) EnableConstraintsStatement(tableName string) string {
151+
schemaAndTable := strings.Split(tableName, ".")
152+
if len(schemaAndTable) == 1 {
153+
return fmt.Sprintf("ALTER TABLE %s ENABLE TRIGGER ALL", pgd.Quote(tableName))
154+
}
155+
return fmt.Sprintf("ALTER TABLE %s.%s ENABLE TRIGGER ALL", pgd.Quote(schemaAndTable[0]), pgd.Quote(schemaAndTable[1]))
156+
}
157+
158+
// DisableConstraintsStatement generate statments to deactivate constraintes
159+
func (pgd PostgresDialect) DisableConstraintsStatement(tableName string) string {
160+
schemaAndTable := strings.Split(tableName, ".")
161+
if len(schemaAndTable) == 1 {
162+
return fmt.Sprintf("ALTER TABLE %s DISABLE TRIGGER ALL", pgd.Quote(tableName))
163+
}
164+
return fmt.Sprintf("ALTER TABLE %s.%s DISABLE TRIGGER ALL", pgd.Quote(schemaAndTable[0]), pgd.Quote(schemaAndTable[1]))
165+
}
166+
167+
// TruncateStatement generate statement to truncat table content
168+
func (pgd PostgresDialect) TruncateStatement(tableName string) string {
169+
schemaAndTable := strings.Split(tableName, ".")
170+
if len(schemaAndTable) == 1 {
171+
return fmt.Sprintf("TRUNCATE TABLE %s CASCADE", pgd.Quote(tableName))
172+
}
173+
return fmt.Sprintf("TRUNCATE TABLE %s.%s CASCADE", pgd.Quote(schemaAndTable[0]), pgd.Quote(schemaAndTable[1]))
174+
}

internal/infra/commonsql/dialect_sqlserver.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,3 +137,28 @@ func (sd SQLServerDialect) CreateSelect(sel string, where string, limit string,
137137
func (sd SQLServerDialect) selectPresence(column string) string {
138138
return fmt.Sprintf("CASE WHEN %s IS NOT NULL THEN 1 ELSE NULL END AS %s", sd.Quote(column), sd.Quote(column))
139139
}
140+
141+
// BlankTest implements SQLDialect.
142+
func (sd SQLServerDialect) BlankTest(column string) string {
143+
return fmt.Sprintf("LTRIM(RTRIM(%s)) = ''", column)
144+
}
145+
146+
// EmptyTest implements SQLDialect.
147+
func (sd SQLServerDialect) EmptyTest(column string) string {
148+
return fmt.Sprintf("%s IS NULL", column)
149+
}
150+
151+
// EnableConstraintsStatement generate statments to activate constraintes
152+
func (sd SQLServerDialect) EnableConstraintsStatement(tableName string) string {
153+
return fmt.Sprintf("ALTER TABLE %s CHECK CONSTRAINT ALL", sd.Quote(tableName))
154+
}
155+
156+
// DisableConstraintsStatement generate statments to deactivate constraintes
157+
func (sd SQLServerDialect) DisableConstraintsStatement(tableName string) string {
158+
return fmt.Sprintf("ALTER TABLE %s NOCHECK CONSTRAINT ALL", sd.Quote(tableName))
159+
}
160+
161+
// TruncateStatement generate statement to truncat table content
162+
func (sd SQLServerDialect) TruncateStatement(tableName string) string {
163+
return fmt.Sprintf("TRUNCATE TABLE %s", sd.Quote(tableName))
164+
}

internal/infra/push/datadestination_db2.go

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import (
2828
// import db2 connector
2929
_ "github.com/ibmdb/go_ibm_db"
3030

31+
"github.com/cgi-fr/lino/internal/infra/commonsql"
3132
"github.com/cgi-fr/lino/pkg/push"
3233
)
3334

@@ -41,11 +42,13 @@ func NewDb2DataDestinationFactory() *Db2DataDestinationFactory {
4142

4243
// New return a Db2 pusher
4344
func (e *Db2DataDestinationFactory) New(url string, schema string) push.DataDestination {
44-
return NewSQLDataDestination(url, schema, Db2Dialect{})
45+
return NewSQLDataDestination(url, schema, Db2Dialect{innerDialect: commonsql.Db2Dialect{}})
4546
}
4647

4748
// Db2Dialect inject oracle variations
48-
type Db2Dialect struct{}
49+
type Db2Dialect struct {
50+
innerDialect commonsql.Dialect
51+
}
4952

5053
// Placeholde return the variable format for postgres
5154
func (d Db2Dialect) Placeholder(position int) string {
@@ -54,17 +57,17 @@ func (d Db2Dialect) Placeholder(position int) string {
5457

5558
// EnableConstraintsStatement generate statments to activate constraintes
5659
func (d Db2Dialect) EnableConstraintsStatement(tableName string) string {
57-
panic(fmt.Errorf("Not implemented"))
60+
return d.innerDialect.EnableConstraintsStatement(tableName)
5861
}
5962

6063
// DisableConstraintsStatement generate statments to deactivate constraintes
6164
func (d Db2Dialect) DisableConstraintsStatement(tableName string) string {
62-
panic(fmt.Errorf("Not implemented"))
65+
return d.innerDialect.DisableConstraintsStatement(tableName)
6366
}
6467

6568
// TruncateStatement generate statement to truncat table content
6669
func (d Db2Dialect) TruncateStatement(tableName string) string {
67-
return fmt.Sprintf("TRUNCATE TABLE %s IMMEDIATE", tableName)
70+
return d.innerDialect.TruncateStatement(tableName)
6871
}
6972

7073
// InsertStatement generate insert statement
@@ -187,9 +190,9 @@ func (d Db2Dialect) SupportPreserve() []string {
187190

188191
// BlankTest implements SQLDialect.
189192
func (d Db2Dialect) BlankTest(name string) string {
190-
panic("unimplemented")
193+
return d.innerDialect.BlankTest(name)
191194
}
192195

193196
func (d Db2Dialect) EmptyTest(name string) string {
194-
panic("unimplemented")
197+
return d.innerDialect.EmptyTest(name)
195198
}

internal/infra/push/datadestination_db2_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"strings"
2525
"testing"
2626

27+
"github.com/cgi-fr/lino/internal/infra/commonsql"
2728
"github.com/cgi-fr/lino/pkg/push"
2829
_ "github.com/ibmdb/go_ibm_db"
2930
"github.com/stretchr/testify/assert"
@@ -45,6 +46,6 @@ func TestAppendColumnToSQLWithPreserveBlank(t *testing.T) {
4546
),
4647
}
4748

48-
err := appendColumnToSQL(column, sql, Db2Dialect{}, 0)
49+
err := appendColumnToSQL(column, sql, Db2Dialect{innerDialect: commonsql.Db2Dialect{}}, 0)
4950
assert.NotNil(t, err)
5051
}

internal/infra/push/datadestination_mariadb.go

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"fmt"
2222
"strings"
2323

24+
"github.com/cgi-fr/lino/internal/infra/commonsql"
2425
"github.com/cgi-fr/lino/pkg/push"
2526
"github.com/lib/pq"
2627
)
@@ -35,11 +36,13 @@ func NewMariadbDataDestinationFactory() *MariadbDataDestinationFactory {
3536

3637
// New return a Mariadb pusher
3738
func (e *MariadbDataDestinationFactory) New(url string, schema string) push.DataDestination {
38-
return NewSQLDataDestination(url, schema, MariadbDialect{})
39+
return NewSQLDataDestination(url, schema, MariadbDialect{innerDialect: commonsql.MariadbDialect{}})
3940
}
4041

4142
// MariadbDialect inject mariadb variations
42-
type MariadbDialect struct{}
43+
type MariadbDialect struct {
44+
innerDialect commonsql.Dialect
45+
}
4346

4447
// Placeholde return the variable format for mariadb
4548
func (d MariadbDialect) Placeholder(position int) string {
@@ -48,17 +51,17 @@ func (d MariadbDialect) Placeholder(position int) string {
4851

4952
// EnableConstraintsStatement generate statments to activate constraintes
5053
func (d MariadbDialect) EnableConstraintsStatement(tableName string) string {
51-
return "SET GLOBAL FOREIGN_KEY_CHECKS=1"
54+
return d.innerDialect.EnableConstraintsStatement(tableName)
5255
}
5356

5457
// DisableConstraintsStatement generate statments to deactivate constraintes
5558
func (d MariadbDialect) DisableConstraintsStatement(tableName string) string {
56-
return "SET GLOBAL FOREIGN_KEY_CHECKS=0"
59+
return d.innerDialect.DisableConstraintsStatement(tableName)
5760
}
5861

5962
// TruncateStatement generate statement to truncat table content (ON DELETE CASCADE must be set to TRUNCATE child tables)
6063
func (d MariadbDialect) TruncateStatement(tableName string) string {
61-
return fmt.Sprintf("TRUNCATE TABLE %s", tableName)
64+
return d.innerDialect.TruncateStatement(tableName)
6265
}
6366

6467
// InsertStatement generate insert statement
@@ -179,9 +182,9 @@ func (d MariadbDialect) SupportPreserve() []string {
179182

180183
// BlankTest implements SQLDialect.
181184
func (d MariadbDialect) BlankTest(name string) string {
182-
panic("unimplemented")
185+
return d.innerDialect.BlankTest(name)
183186
}
184187

185188
func (d MariadbDialect) EmptyTest(column string) string {
186-
return fmt.Sprintf("%s = ''", column)
189+
return d.innerDialect.EmptyTest(column)
187190
}

0 commit comments

Comments
 (0)