Skip to content
Open
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
205 changes: 205 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
name: Java CI Combined

on:
push:
pull_request:
workflow_dispatch:
workflow_call:
inputs:
LUCEE_TEST_JAVA_VERSION:
required: true
type: string
default: '21'
LUCEE_VERSION_QUERY:
required: true
type: string
default: '["6.2/snapshot/light","7/snapshot/light"]'

jobs:
setup:
runs-on: ubuntu-latest
outputs:
version: ${{ steps.extract-version.outputs.VERSION }}
lucee-versions: ${{ steps.convert-lucee-versions.outputs.LUCEE_VERSIONS }}
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Set up JDK 17
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '17'

- name: Cache Maven packages
uses: actions/cache@v4
with:
path: ~/.m2
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
restore-keys: |
${{ runner.os }}-maven-

- name: Extract version number
id: extract-version
run: |
VERSION=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout)
echo "VERSION=$VERSION" >> $GITHUB_OUTPUT

- name: Convert Lucee versions
id: convert-lucee-versions
env:
REPO_VERSIONS: ${{ vars.LUCEE_TEST_VERSIONS_PLUS }}
INPUT_VERSIONS: ${{ inputs.LUCEE_VERSION_QUERY }}
run: |
# Use input versions if provided, otherwise use repo versions
SIMPLE_VERSIONS="${INPUT_VERSIONS:-$REPO_VERSIONS}"
# Convert simple format to complex format
echo "$SIMPLE_VERSIONS" | jq -c 'map({version: ("light-" + split("/")[0] + ".0.0-SNAPSHOT"), query: .})' > lucee_versions.json
LUCEE_VERSIONS=$(cat lucee_versions.json)
echo "LUCEE_VERSIONS=$LUCEE_VERSIONS" >> $GITHUB_OUTPUT

- name: Cache Lucee files
uses: actions/cache@v4
with:
path: ~/work/_actions/lucee/script-runner/main/lucee-download-cache
key: lucee-downloads

- name: Import GPG key
run: |
echo "$GPG_PRIVATE_KEY" | base64 --decode | gpg --batch --import
env:
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}

build:
runs-on: ubuntu-latest
needs: setup
steps:
- uses: actions/checkout@v4

- name: Set up JDK 21
uses: actions/setup-java@v4
with:
java-version: ${{ inputs.LUCEE_TEST_JAVA_VERSION || '21' }}
distribution: 'temurin'

- name: Cache Maven packages
uses: actions/cache@v4
with:
path: ~/.m2
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
restore-keys: |
${{ runner.os }}-maven-

- name: Build and Install with Maven
run: |
echo "------- Maven Install -------";
mvn -B -e -f pom.xml clean install

- name: Upload Artifact
uses: actions/upload-artifact@v4
with:
name: cockroachdb-lex
path: target/*.lex

test:
runs-on: ubuntu-latest
needs: [setup, build]
strategy:
fail-fast: false
matrix:
lucee: ${{ fromJSON(needs.setup.outputs.lucee-versions) }}
steps:
- uses: actions/checkout@v4

- name: Start CockroachDB
run: |
docker run -d --name cockroachdb \
-p 26257:26257 -p 8080:8080 \
cockroachdb/cockroach:v23.1.11 start-single-node --insecure

- name: Wait for CockroachDB and setup database
run: |
# Download cockroach CLI
curl -L https://binaries.cockroachdb.com/cockroach-v23.1.11.linux-amd64.tgz | tar -xz
sudo cp cockroach-v23.1.11.linux-amd64/cockroach /usr/local/bin/

# Wait for CockroachDB to be ready
for i in {1..10}; do
cockroach sql --insecure --host=localhost:26257 --execute="SELECT 1;" && break || sleep 5;
done

# Create database
cockroach sql --insecure --host=localhost:26257 --execute="CREATE DATABASE IF NOT EXISTS testdb;"

- name: Download built artifact
uses: actions/download-artifact@v4
with:
name: cockroachdb-lex
path: target/

- name: Checkout Lucee
uses: actions/checkout@v4
with:
repository: lucee/lucee
path: lucee

- name: Run Lucee Test Suite
uses: lucee/script-runner@main
with:
webroot: ${{ github.workspace }}/lucee/test
execute: /bootstrap-tests.cfm
luceeVersionQuery: ${{ matrix.lucee.query }}
extensionDir: ${{ github.workspace }}/target
env:
testLabels: cockroachdb
testServices: none # we don't need any services for this test, avoid errors due to bundle download
LUCEE_ENABLE_BUNDLE_DOWNLOAD: false # we want to test the extension we just built
COCKROACHDB_HOST: localhost
COCKROACHDB_USERNAME: root
COCKROACHDB_PORT: 26257
COCKROACHDB_DATABASE: testdb
LUCEE_LOGGING_FORCE_APPENDER: console
LUCEE_LOGGING_FORCE_LEVEL: info
testAdditional: ${{ github.workspace }}/tests

deploy:
runs-on: ubuntu-latest
needs: [test]
if: always() && needs.test.result == 'success' && github.ref == 'refs/heads/main'
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Set up JDK 17
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '17'

- name: Cache Maven packages
uses: actions/cache@v4
with:
path: ~/.m2
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
restore-keys: |
${{ runner.os }}-maven-

- name: Import GPG key
run: |
echo "$GPG_PRIVATE_KEY" | base64 --decode | gpg --batch --import
env:
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}

- name: Build and Deploy with Maven
env:
MAVEN_USERNAME: ${{ secrets.MAVEN_USERNAME }}
MAVEN_PASSWORD: ${{ secrets.MAVEN_PASSWORD }}
GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
run: |
if [[ "${{ needs.setup.outputs.version }}" == *-SNAPSHOT ]]; then
echo "------- Maven Deploy snapshot on ${{ github.event_name }} -------";
mvn -B -e -f pom.xml clean deploy --settings maven-settings.xml
else
echo "------- Maven Deploy release on ${{ github.event_name }} -------";
mvn -B -e -f pom.xml clean deploy -DperformRelease=true --settings maven-settings.xml
fi
12 changes: 12 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/dist
/notUsedYet
**/Test.java
**/.DS_Store
**/.svn/
/*.project
va specific
*.class
target
target/*
*.lock
*.DS_Store
7 changes: 7 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"workbench.colorCustomizations": {
"activityBar.background": "#023608",
"titleBar.activeBackground": "#034B0B",
"titleBar.activeForeground": "#ECFEEE"
}
}
86 changes: 86 additions & 0 deletions CockroachDB.cfc
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
component extends="types.Driver" output="no" implements="types.IDatasource" {

fields = array(
field("SSL Mode", "sslmode", "disable,require,verify-ca,verify-full", "disable", "SSL mode to use for the connection.", "radio"),
field("Application Name", "application_name", "", false, "Optional application name for the connection."),
field("Timezone", "timezone", "", false, "Set the session timezone for the connection."),
field("Retry Transient Errors", "retryTransientErrors", "true,false", "false", "Automatically retry serialization failures during transactions.", "radio"),
field("Implicit SELECT FOR UPDATE", "implicitSelectForUpdate", "true,false", "false", "Automatically append FOR UPDATE to qualified SELECT statements to reduce contention.", "radio"),
field("Rewrite Batched Inserts", "reWriteBatchedInserts", "true,false", "false", "Enable array-based rewrites for bulk INSERT operations.", "radio"),
field("Rewrite Batched Updates", "reWriteBatchedUpdates", "true,false", "false", "Enable array-based rewrites for bulk UPDATE operations.", "radio"),
field("Rewrite Batched Upserts", "reWriteBatchedUpserts", "true,false", "false", "Enable array-based rewrites for bulk UPSERT operations.", "radio"),
field("Retry Max Attempts", "retryMaxAttempts", "", "15", "Maximum number of retry attempts for transient errors (default: 15)."),
field("Retry Max Backoff Time", "retryMaxBackoffTime", "", "30s", "Maximum backoff time between retries (default: 30s).")
);

this.type.port = this.TYPE_FREE;
this.value.host = "localhost";
this.value.port = 26257;
this.className = "{class-name}";
this.bundleName = "{bundle-name}";
this.dsn = "{connString}";

/**
* Custom parameter syntax for CockroachDB JDBC URLs
*/
public struct function customParameterSyntax() {
return {leadingdelimiter:'?', delimiter:'&', separator:'='};
}

/**
* Validate field combinations before saving
*/
public void function onBeforeUpdate() {
// Validate retry settings
if (len(form.custom_retryMaxAttempts ?: "")) {
var attempts = val(form.custom_retryMaxAttempts);
if (attempts < 1 || attempts > 100) {
throw message="Retry Max Attempts must be between 1 and 100";
}
}

// Validate backoff time format
if (len(form.custom_retryMaxBackoffTime ?: "")) {
var backoffTime = form.custom_retryMaxBackoffTime;
if (!reFindNoCase("^\d+[smh]?$", backoffTime)) {
throw message="Retry Max Backoff Time must be in format like '30s', '5m', or '1h'";
}
}
}

/**
* returns display name of the driver
*/
public string function getName() {
return "{label}";
}

/**
* returns the id of the driver
*/
public string function getId() {
return "{id}";
}

/**
* returns the description of the driver
*/
public string function getDescription() {
return "{description}";
}

/**
* returns array of fields
*/
public array function getFields() {
return fields;
}

public boolean function literalTimestampWithTSOffset() {
return false;
}

public boolean function alwaysSetTimeout() {
return true;
}
}
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Lucee CockroachDB JDBC Extension

![CockroachDB Logo](logo.png)

[![Java CI](https://github.com/lucee/extension-jdbc-cockroachdb/actions/workflows/main.yml/badge.svg)](https://github.com/lucee/extension-jdbc-cockroachdb/actions/workflows/main.yml)

Issues: https://luceeserver.atlassian.net/issues/?jql=labels%20%3D%20cockroachdb

## Coackroach DB

https://www.cockroachlabs.com/product/overview/

## JDBC client

While you can just use the Postgres JDBC client, this JDBC client adds some extra Cockroachdb specific support (it does bundle the postgres jdbc client and extends it)

https://github.com/cloudneutral/cockroachdb-jdbc

## Requirements

**Java 17 or higher** is required for this extension due to the CockroachDB JDBC driver 2.0.1+ dependency.
**Lucee 6.2.2.91 or newer**
Loading