Repository: ariga/atlas
Pull request #791 (2022-05-12 18:07:12)

sql/sqlite: simple file lock to implement schema.Locker for SQLite
This PR assumes, that SQLite files will most likely be run locally (which means never be accessed remotely) and therefore this implementation works for that cases. It aims to provide consistency with the other drivers.


Patch:
diff --git a/.golangci.yml b/.golangci.yml
index c9b9f330..7e736406 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -12,8 +12,16 @@ issues:
       linters:
         - gosec
 
+linters-settings:
+  goheader:
+    template: |-
+      Copyright 2021-present The Atlas Authors. All rights reserved.
+      This source code is licensed under the Apache 2.0 license found
+      in the LICENSE file in the root directory of this source tree.
+
 linters:
   disable-all: true
   enable:
     - gosec
     - revive
+    - goheader
diff --git a/cmd/atlas/doc.go b/cmd/atlas/doc.go
index bc422dc8..1640a691 100644
--- a/cmd/atlas/doc.go
+++ b/cmd/atlas/doc.go
@@ -1,3 +1,7 @@
+// Copyright 2021-present The Atlas Authors. All rights reserved.
+// This source code is licensed under the Apache 2.0 license found
+// in the LICENSE file in the root directory of this source tree.
+
 //go:build ignore
 // +build ignore
 
diff --git a/cmd/atlas/generate.go b/cmd/atlas/generate.go
index 437af339..92a6aacd 100644
--- a/cmd/atlas/generate.go
+++ b/cmd/atlas/generate.go
@@ -1,3 +1,7 @@
+// Copyright 2021-present The Atlas Authors. All rights reserved.
+// This source code is licensed under the Apache 2.0 license found
+// in the LICENSE file in the root directory of this source tree.
+
 package main
 
 //go:generate go run -mod=mod doc.go
diff --git a/cmd/atlas/main.go b/cmd/atlas/main.go
index aa1636ba..ea1ecd75 100644
--- a/cmd/atlas/main.go
+++ b/cmd/atlas/main.go
@@ -1,3 +1,7 @@
+// Copyright 2021-present The Atlas Authors. All rights reserved.
+// This source code is licensed under the Apache 2.0 license found
+// in the LICENSE file in the root directory of this source tree.
+
 package main
 
 import (
diff --git a/cmd/atlascmd/migrate/ent/schema/revision.go b/cmd/atlascmd/migrate/ent/schema/revision.go
index dcf3f4c0..a6ef6544 100644
--- a/cmd/atlascmd/migrate/ent/schema/revision.go
+++ b/cmd/atlascmd/migrate/ent/schema/revision.go
@@ -1,3 +1,7 @@
+// Copyright 2021-present The Atlas Authors. All rights reserved.
+// This source code is licensed under the Apache 2.0 license found
+// in the LICENSE file in the root directory of this source tree.
+
 package schema
 
 import (
diff --git a/internal/ci/main.go b/internal/ci/main.go
index aa885aa0..7823e544 100644
--- a/internal/ci/main.go
+++ b/internal/ci/main.go
@@ -1,3 +1,7 @@
+// Copyright 2021-present The Atlas Authors. All rights reserved.
+// This source code is licensed under the Apache 2.0 license found
+// in the LICENSE file in the root directory of this source tree.
+
 package main
 
 import (
diff --git a/internal/typedoc/main.go b/internal/typedoc/main.go
index 2356fa23..8eb48e61 100644
--- a/internal/typedoc/main.go
+++ b/internal/typedoc/main.go
@@ -1,3 +1,7 @@
+// Copyright 2021-present The Atlas Authors. All rights reserved.
+// This source code is licensed under the Apache 2.0 license found
+// in the LICENSE file in the root directory of this source tree.
+
 package main
 
 import (
diff --git a/schema/schemaspec/doc.go b/schema/schemaspec/doc.go
index 2d66b939..e6ed6b8c 100644
--- a/schema/schemaspec/doc.go
+++ b/schema/schemaspec/doc.go
@@ -1,3 +1,7 @@
+// Copyright 2021-present The Atlas Authors. All rights reserved.
+// This source code is licensed under the Apache 2.0 license found
+// in the LICENSE file in the root directory of this source tree.
+
 // Package schemaspec provides types designed to capture atlas schema elements, and create
 // type-safe extensions to the configuration language. This package is designed to provide
 // an intermediate representation between a serialized configuration language representation
diff --git a/schema/schemaspec/extension.go b/schema/schemaspec/extension.go
index 8d820ae6..c3f8edb8 100644
--- a/schema/schemaspec/extension.go
+++ b/schema/schemaspec/extension.go
@@ -1,3 +1,7 @@
+// Copyright 2021-present The Atlas Authors. All rights reserved.
+// This source code is licensed under the Apache 2.0 license found
+// in the LICENSE file in the root directory of this source tree.
+
 package schemaspec
 
 import (
diff --git a/schema/schemaspec/internal/schemautil/schemautil.go b/schema/schemaspec/internal/schemautil/schemautil.go
index cae78fde..938b80fa 100644
--- a/schema/schemaspec/internal/schemautil/schemautil.go
+++ b/schema/schemaspec/internal/schemautil/schemautil.go
@@ -1,3 +1,7 @@
+// Copyright 2021-present The Atlas Authors. All rights reserved.
+// This source code is licensed under the Apache 2.0 license found
+// in the LICENSE file in the root directory of this source tree.
+
 package schemautil
 
 import (
diff --git a/schema/schemaspec/schemahcl/hcl.go b/schema/schemaspec/schemahcl/hcl.go
index 8f2be5cb..6693326d 100644
--- a/schema/schemaspec/schemahcl/hcl.go
+++ b/schema/schemaspec/schemahcl/hcl.go
@@ -1,3 +1,7 @@
+// Copyright 2021-present The Atlas Authors. All rights reserved.
+// This source code is licensed under the Apache 2.0 license found
+// in the LICENSE file in the root directory of this source tree.
+
 package schemahcl
 
 import (
diff --git a/schema/schemaspec/schemahcl/opts.go b/schema/schemaspec/schemahcl/opts.go
index 30e3d5e5..5548d44c 100644
--- a/schema/schemaspec/schemahcl/opts.go
+++ b/schema/schemaspec/schemahcl/opts.go
@@ -1,3 +1,7 @@
+// Copyright 2021-present The Atlas Authors. All rights reserved.
+// This source code is licensed under the Apache 2.0 license found
+// in the LICENSE file in the root directory of this source tree.
+
 package schemahcl
 
 import (
diff --git a/schema/schemaspec/schemahcl/types.go b/schema/schemaspec/schemahcl/types.go
index d5f3d224..a63769ab 100644
--- a/schema/schemaspec/schemahcl/types.go
+++ b/schema/schemaspec/schemahcl/types.go
@@ -1,3 +1,7 @@
+// Copyright 2021-present The Atlas Authors. All rights reserved.
+// This source code is licensed under the Apache 2.0 license found
+// in the LICENSE file in the root directory of this source tree.
+
 package schemahcl
 
 import (
diff --git a/schema/schemaspec/spec.go b/schema/schemaspec/spec.go
index f5f50558..43a7273d 100644
--- a/schema/schemaspec/spec.go
+++ b/schema/schemaspec/spec.go
@@ -1,3 +1,7 @@
+// Copyright 2021-present The Atlas Authors. All rights reserved.
+// This source code is licensed under the Apache 2.0 license found
+// in the LICENSE file in the root directory of this source tree.
+
 package schemaspec
 
 import (
diff --git a/sql/internal/specutil/spec.go b/sql/internal/specutil/spec.go
index ea955488..9dfaae47 100644
--- a/sql/internal/specutil/spec.go
+++ b/sql/internal/specutil/spec.go
@@ -1,3 +1,7 @@
+// Copyright 2021-present The Atlas Authors. All rights reserved.
+// This source code is licensed under the Apache 2.0 license found
+// in the LICENSE file in the root directory of this source tree.
+
 package specutil
 
 import (
diff --git a/sql/mysql/sqlspec.go b/sql/mysql/sqlspec.go
index 9ea71d4b..9f06b9f1 100644
--- a/sql/mysql/sqlspec.go
+++ b/sql/mysql/sqlspec.go
@@ -1,3 +1,7 @@
+// Copyright 2021-present The Atlas Authors. All rights reserved.
+// This source code is licensed under the Apache 2.0 license found
+// in the LICENSE file in the root directory of this source tree.
+
 package mysql
 
 import (
diff --git a/sql/postgres/sqlspec.go b/sql/postgres/sqlspec.go
index 0d75e3d5..ed0596c8 100644
--- a/sql/postgres/sqlspec.go
+++ b/sql/postgres/sqlspec.go
@@ -1,3 +1,7 @@
+// Copyright 2021-present The Atlas Authors. All rights reserved.
+// This source code is licensed under the Apache 2.0 license found
+// in the LICENSE file in the root directory of this source tree.
+
 package postgres
 
 import (
diff --git a/sql/sqlite/driver.go b/sql/sqlite/driver.go
index 0a862111..7cd54b9f 100644
--- a/sql/sqlite/driver.go
+++ b/sql/sqlite/driver.go
@@ -6,9 +6,15 @@ package sqlite
 
 import (
 	"context"
+	"errors"
 	"fmt"
+	"io/ioutil"
 	"net/url"
+	"os"
+	"path/filepath"
+	"strconv"
 	"strings"
+	"time"
 
 	"ariga.io/atlas/sql/internal/sqlx"
 	"ariga.io/atlas/sql/migrate"
@@ -99,6 +105,39 @@ func (d *Driver) Clean(ctx context.Context) error {
 	return nil
 }
 
+// Lock implements the schema.Locker interface.
+func (d *Driver) Lock(_ context.Context, name string, timeout time.Duration) (schema.UnlockFunc, error) {
+	path := filepath.Join(os.TempDir(), name+".lock")
+	c, err := ioutil.ReadFile(path)
+	if errors.Is(err, os.ErrNotExist) {
+		return acquireLock(path, timeout)
+	}
+	if err != nil {
+		return nil, fmt.Errorf("sql/sqlite: reading lock dir: %w", err)
+	}
+	expires, err := strconv.ParseInt(string(c), 10, 64)
+	if err != nil {
+		return nil, fmt.Errorf("sql/sqlite: invalid lock file format: parsing expiration date: %w", err)
+	}
+	if time.Unix(0, expires).After(time.Now()) {
+		// Lock is still valid.
+		return nil, fmt.Errorf("sql/sqlite: lock on %q already taken", name)
+	}
+	return acquireLock(path, timeout)
+}
+
+func acquireLock(path string, timeout time.Duration) (schema.UnlockFunc, error) {
+	lock, err := os.Create(path)
+	if err != nil {
+		return nil, fmt.Errorf("sql/sqlite: creating lockfile %q: %w", path, err)
+	}
+	if _, err := lock.Write([]byte(strconv.FormatInt(time.Now().Add(timeout).UnixNano(), 10))); err != nil {
+		return nil, fmt.Errorf("sql/sqlite: writing to lockfile %q: %w", path, err)
+	}
+	defer lock.Close()
+	return func() error { return os.Remove(path) }, nil
+}
+
 // SQLite standard data types as defined in its codebase and documentation.
 // https://www.sqlite.org/datatype3.html
 // https://github.com/sqlite/sqlite/blob/master/src/global.c
diff --git a/sql/sqlite/sqlspec.go b/sql/sqlite/sqlspec.go
index 16eb6c0f..88170a6b 100644
--- a/sql/sqlite/sqlspec.go
+++ b/sql/sqlite/sqlspec.go
@@ -1,3 +1,7 @@
+// Copyright 2021-present The Atlas Authors. All rights reserved.
+// This source code is licensed under the Apache 2.0 license found
+// in the LICENSE file in the root directory of this source tree.
+
 package sqlite
 
 import (
diff --git a/sql/sqlspec/migratespec.go b/sql/sqlspec/migratespec.go
index fc72b5b9..c835defc 100644
--- a/sql/sqlspec/migratespec.go
+++ b/sql/sqlspec/migratespec.go
@@ -1,3 +1,7 @@
+// Copyright 2021-present The Atlas Authors. All rights reserved.
+// This source code is licensed under the Apache 2.0 license found
+// in the LICENSE file in the root directory of this source tree.
+
 package sqlspec
 
 import (
diff --git a/sql/sqlspec/sqlspec.go b/sql/sqlspec/sqlspec.go
index faeba647..974f261f 100644
--- a/sql/sqlspec/sqlspec.go
+++ b/sql/sqlspec/sqlspec.go
@@ -1,3 +1,7 @@
+// Copyright 2021-present The Atlas Authors. All rights reserved.
+// This source code is licensed under the Apache 2.0 license found
+// in the LICENSE file in the root directory of this source tree.
+
 package sqlspec
 
 import (


Test patch:
diff --git a/cmd/atlascmd/update/update_test.go b/cmd/atlascmd/update/update_test.go
index 84b1eb46..0096e8cd 100644
--- a/cmd/atlascmd/update/update_test.go
+++ b/cmd/atlascmd/update/update_test.go
@@ -1,3 +1,7 @@
+// Copyright 2021-present The Atlas Authors. All rights reserved.
+// This source code is licensed under the Apache 2.0 license found
+// in the LICENSE file in the root directory of this source tree.
+
 package update
 
 import (
diff --git a/internal/integration/hclsqlspec/hclsqlspec_test.go b/internal/integration/hclsqlspec/hclsqlspec_test.go
index 5cb97011..a228a87a 100644
--- a/internal/integration/hclsqlspec/hclsqlspec_test.go
+++ b/internal/integration/hclsqlspec/hclsqlspec_test.go
@@ -1,3 +1,7 @@
+// Copyright 2021-present The Atlas Authors. All rights reserved.
+// This source code is licensed under the Apache 2.0 license found
+// in the LICENSE file in the root directory of this source tree.
+
 package hclsqlspec
 
 import (
diff --git a/internal/integration/hclsqlspec/migrate_test.go b/internal/integration/hclsqlspec/migrate_test.go
index cc7e8355..3fd56328 100644
--- a/internal/integration/hclsqlspec/migrate_test.go
+++ b/internal/integration/hclsqlspec/migrate_test.go
@@ -1,3 +1,7 @@
+// Copyright 2021-present The Atlas Authors. All rights reserved.
+// This source code is licensed under the Apache 2.0 license found
+// in the LICENSE file in the root directory of this source tree.
+
 package hclsqlspec
 
 import (
diff --git a/internal/integration/integration_test.go b/internal/integration/integration_test.go
index 107e53e7..384bb1be 100644
--- a/internal/integration/integration_test.go
+++ b/internal/integration/integration_test.go
@@ -1,3 +1,7 @@
+// Copyright 2021-present The Atlas Authors. All rights reserved.
+// This source code is licensed under the Apache 2.0 license found
+// in the LICENSE file in the root directory of this source tree.
+
 package integration
 
 import (
diff --git a/schema/schemaspec/extension_test.go b/schema/schemaspec/extension_test.go
index 23af261d..e749fe3c 100644
--- a/schema/schemaspec/extension_test.go
+++ b/schema/schemaspec/extension_test.go
@@ -1,3 +1,7 @@
+// Copyright 2021-present The Atlas Authors. All rights reserved.
+// This source code is licensed under the Apache 2.0 license found
+// in the LICENSE file in the root directory of this source tree.
+
 package schemaspec_test
 
 import (
diff --git a/schema/schemaspec/schemahcl/hcl_test.go b/schema/schemaspec/schemahcl/hcl_test.go
index ce54a6e1..348401c3 100644
--- a/schema/schemaspec/schemahcl/hcl_test.go
+++ b/schema/schemaspec/schemahcl/hcl_test.go
@@ -1,3 +1,7 @@
+// Copyright 2021-present The Atlas Authors. All rights reserved.
+// This source code is licensed under the Apache 2.0 license found
+// in the LICENSE file in the root directory of this source tree.
+
 package schemahcl
 
 import (
diff --git a/schema/schemaspec/schemahcl/types_test.go b/schema/schemaspec/schemahcl/types_test.go
index 3cf4ed93..d4384b81 100644
--- a/schema/schemaspec/schemahcl/types_test.go
+++ b/schema/schemaspec/schemahcl/types_test.go
@@ -1,3 +1,7 @@
+// Copyright 2021-present The Atlas Authors. All rights reserved.
+// This source code is licensed under the Apache 2.0 license found
+// in the LICENSE file in the root directory of this source tree.
+
 package schemahcl
 
 import (
diff --git a/schema/schemaspec/spec_test.go b/schema/schemaspec/spec_test.go
index 8022d6fb..eef214e7 100644
--- a/schema/schemaspec/spec_test.go
+++ b/schema/schemaspec/spec_test.go
@@ -1,3 +1,7 @@
+// Copyright 2021-present The Atlas Authors. All rights reserved.
+// This source code is licensed under the Apache 2.0 license found
+// in the LICENSE file in the root directory of this source tree.
+
 package schemaspec
 
 import (
diff --git a/sql/internal/specutil/convert_test.go b/sql/internal/specutil/convert_test.go
index 568a1930..422d56e5 100644
--- a/sql/internal/specutil/convert_test.go
+++ b/sql/internal/specutil/convert_test.go
@@ -1,3 +1,7 @@
+// Copyright 2021-present The Atlas Authors. All rights reserved.
+// This source code is licensed under the Apache 2.0 license found
+// in the LICENSE file in the root directory of this source tree.
+
 package specutil
 
 import (
diff --git a/sql/mysql/sqlspec_test.go b/sql/mysql/sqlspec_test.go
index e8f7dafe..86ab2a4f 100644
--- a/sql/mysql/sqlspec_test.go
+++ b/sql/mysql/sqlspec_test.go
@@ -1,3 +1,7 @@
+// Copyright 2021-present The Atlas Authors. All rights reserved.
+// This source code is licensed under the Apache 2.0 license found
+// in the LICENSE file in the root directory of this source tree.
+
 package mysql
 
 import (
diff --git a/sql/postgres/sqlspec_test.go b/sql/postgres/sqlspec_test.go
index f8b752a4..f19c9967 100644
--- a/sql/postgres/sqlspec_test.go
+++ b/sql/postgres/sqlspec_test.go
@@ -1,3 +1,7 @@
+// Copyright 2021-present The Atlas Authors. All rights reserved.
+// This source code is licensed under the Apache 2.0 license found
+// in the LICENSE file in the root directory of this source tree.
+
 package postgres
 
 import (
diff --git a/sql/sqlite/driver_test.go b/sql/sqlite/driver_test.go
new file mode 100644
index 00000000..bc123cdf
--- /dev/null
+++ b/sql/sqlite/driver_test.go
@@ -0,0 +1,48 @@
+// Copyright 2021-present The Atlas Authors. All rights reserved.
+// This source code is licensed under the Apache 2.0 license found
+// in the LICENSE file in the root directory of this source tree.
+
+package sqlite
+
+import (
+	"context"
+	"os"
+	"path/filepath"
+	"strconv"
+	"testing"
+	"time"
+
+	"github.com/stretchr/testify/require"
+)
+
+func TestDriver_LockAcquired(t *testing.T) {
+	drv := &Driver{}
+
+	// Acquiring a lock does work.
+	unlock, err := drv.Lock(context.Background(), "lock", time.Second)
+	require.NoError(t, err)
+	require.NotNil(t, unlock)
+
+	// Acquiring a lock on the same value will fail.
+	_, err = drv.Lock(context.Background(), "lock", time.Second)
+	require.Error(t, err)
+
+	// After unlock it will succeed again.
+	require.NoError(t, unlock())
+	_, err = drv.Lock(context.Background(), "lock", time.Second)
+	require.NoError(t, err)
+	require.NotNil(t, unlock)
+
+	// Acquiring a lock on a value that has been expired works.
+	dir, err := os.UserCacheDir()
+	require.NoError(t, err)
+	require.NoError(t, os.WriteFile(
+		filepath.Join(dir, "lock.lock"),
+		[]byte(strconv.FormatInt(time.Now().Add(-time.Second).UnixNano(), 10)),
+		0666,
+	))
+	_, err = drv.Lock(context.Background(), "lock", time.Second)
+
+	// Acquiring a lock on another value works as well.
+	_, err = drv.Lock(context.Background(), "another", time.Second)
+}