Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hacking demo #71

Merged
merged 15 commits into from
Sep 22, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ bin
/tidb-server/tidb-server
/tidb-server/debug
coverage.out
coverage.txt
.idea/
*.iml
*.swp
Expand Down
6 changes: 5 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,10 @@ parser:

dev: checklist check test

# Skip explain-test
dev-tmp: checklist check test_part_2
@echo "Great, all tests passed."

build:
$(GOBUILD)

Expand Down Expand Up @@ -137,8 +141,8 @@ vet:
@echo "vet"
$(GO) vet -all $(PACKAGES) 2>&1 | $(FAIL_ON_STDOUT)

# limit the static check tool to an early version as the newer version require higher go version
staticcheck:
#limit the tool to an early version as the newer version require higher go version
$(GO) get honnef.co/go/tools/cmd/staticcheck@v0.0.1-2020.1.6
$(STATICCHECK) ./...

Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

[![LICENSE](https://img.shields.io/github/license/pingcap/tidb.svg)](https://github.com/DigitalChinaOpenSource/TiDB-for-PostgreSQL/blob/main/LICENSE)
[![Language](https://img.shields.io/badge/Language-Go-blue.svg)](https://golang.org/)
[![Build Status](http://tidb4pgci.eastasia.cloudapp.azure.com:8080/buildStatus/icon?job=jenkins-tidb4pg-build)](http://tidb4pgci.eastasia.cloudapp.azure.com/:8080/job/jenkins-tidb4pg-build/)
[![Build Status](http://tidb4pgci.eastasia.cloudapp.azure.com/buildStatus/icon?job=jenkins-tidb4pg-build)](http://tidb4pgci.eastasia.cloudapp.azure.com/job/jenkins-tidb4pg-build/)
[![Go Report Card](https://goreportcard.com/badge/github.com/DigitalChinaOpenSource/TiDB-for-PostgreSQL)](https://goreportcard.com/report/github.com/DigitalChinaOpenSource/TiDB-for-PostgreSQL)
[![codecov](https://codecov.io/gh/DigitalChinaOpenSource/TiDB-for-PostgreSQL/branch/main/graph/badge.svg?token=OZ16DNE6JH)](https://codecov.io/gh/DigitalChinaOpenSource/TiDB-for-PostgreSQL)
![GitHub commit activity](https://img.shields.io/github/commit-activity/w/DigitalChinaOpenSource/TiDB-for-PostgreSQL)
Expand Down Expand Up @@ -95,10 +95,10 @@ First, download the official binary package file and unzip it.
wget http://download.pingcap.org/tidb-v4.0.11-linux-amd64.tar.gz
wget http://download.pingcap.org/tidb-v4.0.11-linux-amd64.sha256

sha256sum -c tidb-latest-linux-amd64.sha256
sha256sum -c tidb-v4.0.11-linux-amd64.sha256

tar -xzf tidb-latest-linux-amd64.tar.gz
cd tidb-latest-linux-amd64/bin
tar -xzf tidb-v4.0.11-linux-amd64.tar.gz
cd tidb-v4.0.11-linux-amd64/bin
```

Second, deploy each node in the cluster in order. According to the cluster architecture of TiDB for PostgreSQL, pd nodes are deployed first, then the tikv node, and finally the TiDB for PostgreSQL node. The number of nodes in the cluster is not specified, but there is at least one of each type.
Expand Down
2 changes: 1 addition & 1 deletion cmd/ddltest/ddl_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,7 @@ func (s *TestDDLSuite) startServer(i int, fp *os.File) (*server, error) {
sleepTime := time.Millisecond * 250
startTime := time.Now()
for i := 0; i < s.retryCount; i++ {
db, err = sql.Open("mysql", fmt.Sprintf("root@(%s)/test_ddl", addr))
db, err = sql.Open("postgres", fmt.Sprintf("postgres://root@%s/test_ddl?sslmode=disable", addr))
if err != nil {
log.Warnf("open addr %v failed, retry count %d err %v", addr, i, err)
continue
Expand Down
8 changes: 4 additions & 4 deletions cmd/explaintest/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,10 @@ import (
"strings"
"time"

_ "github.com/go-sql-driver/mysql"
"github.com/DigitalChinaOpenSource/DCParser/ast"
_ "github.com/lib/pq"
"github.com/pingcap/errors"
"github.com/pingcap/log"
"github.com/DigitalChinaOpenSource/DCParser/ast"
"github.com/pingcap/tidb/session"
"github.com/pingcap/tidb/sessionctx"
"github.com/pingcap/tidb/util/logutil"
Expand Down Expand Up @@ -615,8 +615,8 @@ func main() {
}

mdb, err = openDBWithRetry(
"mysql",
"root@tcp(localhost:4001)/"+dbName+"?allowAllFiles=true",
"postgres",
"postgres://root@localhost:4001/"+dbName+"?sslmode=disable",
)
if err != nil {
log.Fatal("open DB failed", zap.Error(err))
Expand Down
8 changes: 4 additions & 4 deletions cmd/importer/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ import (
"strconv"
"strings"

_ "github.com/go-sql-driver/mysql"
"github.com/DigitalChinaOpenSource/DCParser/mysql"
_ "github.com/lib/pq"
"github.com/pingcap/errors"
"github.com/pingcap/log"
"github.com/DigitalChinaOpenSource/DCParser/mysql"
"go.uber.org/zap"
)

Expand Down Expand Up @@ -316,8 +316,8 @@ func execSQL(db *sql.DB, sql string) error {
}

func createDB(cfg DBConfig) (*sql.DB, error) {
dbDSN := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8", cfg.User, cfg.Password, cfg.Host, cfg.Port, cfg.Name)
db, err := sql.Open("mysql", dbDSN)
dbDSN := fmt.Sprintf("//postgres://%s:%s@%s:%d/%s?sslmode=disable", cfg.User, cfg.Password, cfg.Host, cfg.Port, cfg.Name)
db, err := sql.Open("postgres", dbDSN)
if err != nil {
return nil, errors.Trace(err)
}
Expand Down
2 changes: 1 addition & 1 deletion ddl/util/syncer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ func TestSyncerSimple(t *testing.T) {
t.Fatalf("get chan events count less than 1")
}
checkRespKV(t, 1, DDLGlobalSchemaVersion, fmt.Sprintf("%v", currentVer), resp.Events[0].Kv)
case <-time.After(3 * time.Second):
case <-time.After(12 * time.Second):
t.Fatalf("get udpate version failed")
}
}()
Expand Down
215 changes: 215 additions & 0 deletions docs/tidb4pg/Transaction Statement.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
# Adding Support for Transaction Statement in DCParser

---

### PostgreSQL Transaction Statement

#### BEGIN

```
BEGIN [ WORK | TRANSACTION ] [ transaction_mode [, ...] ]

where transaction_mode is one of:

ISOLATION LEVEL { SERIALIZABLE | REPEATABLE READ | READ COMMITTED | READ UNCOMMITTED }
READ WRITE | READ ONLY
[ NOT ] DEFERRABLE
```

#### SET TRANSACTION

The `SET TRANSACTION` command sets the characteristics of the current transaction. It has no effect on any subsequent transactions. `SET SESSION CHARACTERISTICS` sets the default transaction characteristics for subsequent transactions of a session. These defaults can be overridden by `SET TRANSACTION` for an individual transaction.

```
SET TRANSACTION transaction_mode [, ...]
SET TRANSACTION SNAPSHOT snapshot_id
SET SESSION CHARACTERISTICS AS TRANSACTION transaction_mode [, ...]

where transaction_mode is one of:

ISOLATION LEVEL { SERIALIZABLE | REPEATABLE READ | READ COMMITTED | READ UNCOMMITTED }
READ WRITE | READ ONLY
[ NOT ] DEFERRABLE
```



### MySQL Transaction Statement

#### BEGIN / START TRANSACTION

```
START TRANSACTION
[transaction_characteristic [, transaction_characteristic] ...]

transaction_characteristic: {
WITH CONSISTENT SNAPSHOT
| READ WRITE
| READ ONLY
}

BEGIN [WORK]
COMMIT [WORK] [AND [NO] CHAIN] [[NO] RELEASE]
ROLLBACK [WORK] [AND [NO] CHAIN] [[NO] RELEASE]
SET autocommit = {0 | 1}
```



#### SET TRANSACTION

```
SET [GLOBAL | SESSION] TRANSACTION
transaction_characteristic [, transaction_characteristic] ...

transaction_characteristic: {
ISOLATION LEVEL level
| access_mode
}

level: {
REPEATABLE READ
| READ COMMITTED
| READ UNCOMMITTED
| SERIALIZABLE
}

access_mode: {
READ WRITE
| READ ONLY
}
```

SET TRANSACTION (without specifying scope), only works for the **next** transaction, and cannot be used during transaction

```
mysql> START TRANSACTION;
Query OK, 0 rows affected (0.02 sec)

mysql> SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
ERROR 1568 (25001): Transaction characteristics can't be changed
while a transaction is in progress
```

### TiDB

TiDB only actually supports `Read Committed` and `Repeatable Read` (`Snapshot Isolation`).

And to start a transaction in Read Committed, the transaction must be in pessimistic transaction mode

> The Read Committed isolation level only takes effect in the [pessimistic transaction mode](https://docs.pingcap.com/tidb/stable/pessimistic-transaction). In the [optimistic transaction mode](https://docs.pingcap.com/tidb/stable/optimistic-transaction), setting the transaction isolation level to `Read Committed` does not take effect and transactions still use the Repeatable Read isolation level.

https://docs.pingcap.com/zh/tidb/stable/sql-statement-set-transaction#set-transaction

https://docs.pingcap.com/zh/tidb/stable/transaction-isolation-levels

> Note that TiDB's definition for `Snapshot Isolation` and `Read Committed` is different from ANSI standard and MySQL

### Modification Plan

#### Adding Parser Supports for `BEGIN ISOLATION LEVEL`

In yacc file:

```
| "BEGIN" "ISOLATION" "LEVEL" IsolationLevel
{
$$ = &ast.BeginStmt{
IsolationLevel: $4,
}
}
```

Where IsolationLevel is a non-terminal used by `SET` statement that resolve actual isolation level strings.

Then we have to change `BeginStmt` node structure in AST tree, so it can carry the isolation level we resolved.

```
type BeginStmt struct {
// Other stuff
IsolationLevel string
}
```

We also have to change the corresponding restore function to pass the Unit Tests

``` go
func (n *BeginStmt) Restore(ctx *format.RestoreCtx) error {
if n.Mode == "" {
if n.ReadOnly {
// read only stuff
} else {
ctx.WriteKeyWord("START TRANSACTION")
if n.IsolationLevel != "" {
switch n.IsolationLevel {
case ReadCommitted:
ctx.WriteKeyWord(" ISOLATION LEVEL READ COMMITTED")
case ReadUncommitted:
ctx.WriteKeyWord(" ISOLATION LEVEL READ UNCOMMITTED")
case Serializable:
ctx.WriteKeyWord(" ISOLATION LEVEL SERIALIZABLE")
case RepeatableRead:
ctx.WriteKeyWord(" ISOLATION LEVEL REPEATABLE READ")
}

}
}
} else {
// begin with mode
}
return nil
}
```

Note that since we are restoring it to "`START TRANSACTION ISOLATION LEVEL` ...", we have to add similar yacc rule for `START TRANSACTION`, even though we are not using it.

```
| "START" "TRANSACTION" "ISOLATION" "LEVEL" IsolationLevel
{
$$ = &ast.BeginStmt{
IsolationLevel: $5,
}
}
```



### Change Begin Executor's behavior in TiDB

All that's left to do is to set transaction level when starting the transaction. In `executeBegin` function under `executor/simple.go`:

``` go
func (e *SimpleExec) executeBegin(ctx context.Context, s *ast.BeginStmt) error {
// ...

// if the isolation level is set during the transaction start
// it will overwrite the previously set isolation level
if s.IsolationLevel != "" {
e.ctx.GetSessionVars().TxnCtx.Isolation = s.IsolationLevel
}

// ...
}
```

Note that here we are putting at the bottom of the execution sequence, since it should take the highest priority.

MySQL (Or Original TiDB), has six layer of isolation level definition, and they take on different values. The priority from low to high is here:

1. Value defined in the configuration file(s).
2. Value used in the command line option used to start *`mysqld`*.
3. The global transaction isolation level.
4. The session transaction isolation level.
5. The level that will be used by the very next transaction that is created.
6. The level being used by the current transaction.

The `begin` statement actually directly set the isolation at the 6th layer, which has the highest priority.

### Validation

Verifying the effect of setting transaction level in begin statement is actually quite problematic. There is no way to view isolation level set in level 5 and 6 in MySQL, and TiDB inherits that.

> This is actually considered a bug: https://bugs.mysql.com/bug.php?id=53341

So the only way to access the value is through integrated test or viewing value directly through debugging tools.

4 changes: 2 additions & 2 deletions executor/adapter.go
Original file line number Diff line number Diff line change
Expand Up @@ -388,9 +388,9 @@ func (a *ExecStmt) Exec(ctx context.Context) (_ sqlexec.RecordSet, err error) {
if handled, result, err := a.handleNoDelay(ctx, e, isPessimistic); handled {
if returningRS != nil {
return returningRS, err
} else {
return result, err
}

return result, err
}

var txnStartTS uint64
Expand Down
Loading