Skip to content
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
180 changes: 180 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
name: Release

on:
workflow_dispatch:
inputs:
tag:
description: 'Release tag (e.g., v0.1.0)'
required: true
type: string

permissions:
contents: write

jobs:
# ---------------------------------------------------------------------------
# Linux .so builds
# ---------------------------------------------------------------------------
build-linux:
name: Linux - PHP ${{ matrix.php }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
php: ['7.4', '8.0', '8.1', '8.2', '8.3', '8.4']

steps:
- uses: actions/checkout@v4

- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
extensions: mysqlnd
tools: phpize
ini-values: error_reporting=E_ALL

- name: Build extension
working-directory: ext/mariadb_profiler
run: |
phpize
./configure --enable-mariadb_profiler
make -j$(nproc)

- name: Verify extension is loadable
working-directory: ext/mariadb_profiler
run: |
php -d "extension=$(pwd)/modules/mariadb_profiler.so" -m | grep mariadb_profiler

- name: Package artifact
run: |
mkdir -p dist
cp ext/mariadb_profiler/modules/mariadb_profiler.so \
"dist/mariadb_profiler-php${{ matrix.php }}-linux-x86_64.so"

- uses: actions/upload-artifact@v4
with:
name: mariadb_profiler-php${{ matrix.php }}-linux-x86_64
path: dist/*.so

# ---------------------------------------------------------------------------
# Windows .dll builds
# ---------------------------------------------------------------------------
build-windows:
name: Windows - PHP ${{ matrix.php }} ${{ matrix.ts }} ${{ matrix.arch }}
runs-on: windows-${{ matrix.os }}
defaults:
run:
shell: cmd
strategy:
fail-fast: false
matrix:
php: ['7.4', '8.0', '8.1', '8.2', '8.3', '8.4']
arch: [x64]
ts: [nts, ts]
os: ['2022']
include:
# PHP 7.4-8.3 use VS16, PHP 8.4 uses VS17.
# windows-2022 runners have both toolsets.
- php: '7.4'
vs: vs16
- php: '8.0'
vs: vs16
- php: '8.1'
vs: vs16
- php: '8.2'
vs: vs16
- php: '8.3'
vs: vs16
- php: '8.4'
vs: vs17

steps:
- uses: actions/checkout@v4

- name: Setup PHP SDK
id: setup
uses: php/setup-php-sdk@v0.12
with:
version: ${{ matrix.php }}
arch: ${{ matrix.arch }}
ts: ${{ matrix.ts }}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

- name: Enable MSVC Developer Command Prompt
uses: ilammy/msvc-dev-cmd@v1
with:
arch: ${{ matrix.arch }}
toolset: ${{ steps.setup.outputs.toolset }}

- name: phpize
working-directory: ext\mariadb_profiler
run: phpize

- name: configure
working-directory: ext\mariadb_profiler
run: configure --enable-mariadb_profiler --with-prefix=%INSTALL_DIR%

- name: nmake
working-directory: ext\mariadb_profiler
run: nmake

- name: Package artifact
shell: pwsh
run: |
$ts = "${{ matrix.ts }}"
$arch = "${{ matrix.arch }}"
$phpVer = "${{ matrix.php }}"

# Find the built DLL
$dll = Get-ChildItem -Path ext\mariadb_profiler -Recurse -Filter "php_mariadb_profiler.dll" |
Select-Object -First 1
if (-not $dll) {
Write-Error "php_mariadb_profiler.dll not found"
exit 1
}
Write-Host "Found DLL: $($dll.FullName)"

New-Item -ItemType Directory -Force -Path dist | Out-Null
$name = "php_mariadb_profiler-php${phpVer}-${ts}-${arch}"
Copy-Item $dll.FullName "dist\${name}.dll"
Write-Host "Packaged as: dist\${name}.dll"

- uses: actions/upload-artifact@v4
with:
name: php_mariadb_profiler-php${{ matrix.php }}-${{ matrix.ts }}-${{ matrix.arch }}
path: dist\*.dll

# ---------------------------------------------------------------------------
# Create GitHub Release
# ---------------------------------------------------------------------------
release:
name: Create Release
needs: [build-linux, build-windows]
runs-on: ubuntu-latest
permissions:
contents: write

steps:
- uses: actions/checkout@v4

- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: artifacts

- name: Collect release assets
run: |
mkdir -p release
find artifacts -type f \( -name "*.so" -o -name "*.dll" \) -exec cp {} release/ \;
echo "=== Release assets ==="
ls -lh release/

- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ github.event.inputs.tag }}
name: ${{ github.event.inputs.tag }}
draft: false
prerelease: false
generate_release_notes: true
files: release/*
11 changes: 11 additions & 0 deletions ext/mariadb_profiler/config.w32
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// config.w32 for extension mariadb_profiler

ARG_ENABLE('mariadb_profiler', 'MariaDB Query Profiler support', 'no');

if (PHP_MARIADB_PROFILER != 'no') {
EXTENSION('mariadb_profiler',
'mariadb_profiler.c profiler_mysqlnd_plugin.c profiler_job.c profiler_log.c profiler_tag.c profiler_trace.c',
PHP_MARIADB_PROFILER_SHARED,
'/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1');
ADD_EXTENSION_DEP('mariadb_profiler', 'mysqlnd', true);
}
55 changes: 46 additions & 9 deletions ext/mariadb_profiler/mariadb_profiler.c
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,8 @@ static int profiler_ensure_log_dir(TSRMLS_D)
return FAILURE;
}

/* Try to create directory (mode 0777, umask will apply) */
if (mkdir(dir, 0777) == 0) {
/* Try to create directory (mode 0777, umask will apply on Unix) */
if (PROFILER_MKDIR(dir, 0777) == 0) {
return SUCCESS;
}

Expand All @@ -101,30 +101,67 @@ static int profiler_ensure_log_dir(TSRMLS_D)
char tmp[4096];
char *p;
size_t len;
char sep;

len = snprintf(tmp, sizeof(tmp), "%s", dir);
if (len >= sizeof(tmp)) {
return FAILURE;
}

/* Remove trailing slash */
if (tmp[len - 1] == '/') {
#ifdef PHP_WIN32
sep = '\\';
/* Normalise forward slashes to backslashes on Windows */
{
char *s;
for (s = tmp; *s; s++) {
if (*s == '/') *s = '\\';
}
}
#else
sep = '/';
#endif

/* Remove trailing separator */
if (tmp[len - 1] == sep) {
tmp[len - 1] = '\0';
}

for (p = tmp + 1; *p; p++) {
if (*p == '/') {
#ifdef PHP_WIN32
/* Skip drive prefix (e.g. "C:\") or UNC prefix (e.g. "\\server\share\") */
p = tmp;
if (((p[0] >= 'A' && p[0] <= 'Z') || (p[0] >= 'a' && p[0] <= 'z'))
&& p[1] == ':' && p[2] == sep) {
/* Drive letter path: start after "C:\" */
p += 3;
} else if (p[0] == sep && p[1] == sep) {
/* UNC path: skip past "\\server\share\" */
p += 2;
/* Skip server name */
while (*p && *p != sep) p++;
if (*p == sep) p++;
/* Skip share name */
while (*p && *p != sep) p++;
if (*p == sep) p++;
} else {
p++;
}
#else
p = tmp + 1;
#endif

for (; *p; p++) {
if (*p == sep) {
*p = '\0';
if (stat(tmp, &st) != 0) {
if (mkdir(tmp, 0777) != 0 && errno != EEXIST) {
if (PROFILER_MKDIR(tmp, 0777) != 0 && errno != EEXIST) {
return FAILURE;
Comment thread
39ff marked this conversation as resolved.
}
}
*p = '/';
*p = sep;
}
}

if (mkdir(tmp, 0777) != 0 && errno != EEXIST) {
if (PROFILER_MKDIR(tmp, 0777) != 0 && errno != EEXIST) {
php_error_docref(NULL TSRMLS_CC, E_WARNING,
"mariadb_profiler: failed to create log_dir '%s': %s",
dir, strerror(errno));
Expand Down
105 changes: 105 additions & 0 deletions ext/mariadb_profiler/php_mariadb_profiler_compat.h
Original file line number Diff line number Diff line change
Expand Up @@ -191,4 +191,109 @@ typedef long zend_long;
# define PROFILER_BOOL_T zend_bool
#endif

/*
* ---- Platform I/O compatibility (Windows) ----
*
* The extension uses POSIX APIs (flock, gettimeofday, localtime_r, open/read/close)
* that are not available on Windows. These macros and inline functions provide
* equivalent functionality using Win32 APIs.
*
* On Windows, php.h already includes <windows.h> and provides gettimeofday()
* via win32/time.h. We only need to handle the remaining POSIX-specific APIs.
*/
#ifdef PHP_WIN32
# include <io.h>
# include <direct.h>

/* ssize_t is not defined in MSVC */
typedef intptr_t profiler_ssize_t;

/* POSIX I/O function mapping */
# define profiler_open _open
# define profiler_read _read
# define profiler_close _close

/* S_ISDIR may not be defined on all MSVC versions */
# ifndef S_ISDIR
# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
# endif

/* mkdir: Windows _mkdir() takes only the path (no mode parameter) */
# define PROFILER_MKDIR(path, mode) _mkdir(path)

/* localtime_r: Windows has localtime_s with swapped argument order */
static inline struct tm *profiler_localtime_r(const time_t *timep, struct tm *result)
{
return (localtime_s(result, timep) == 0) ? result : NULL;
}
# define localtime_r profiler_localtime_r

/* File locking: Windows uses LockFileEx/UnlockFileEx instead of flock() */
# ifndef LOCK_SH
# define LOCK_SH 1
# endif
# ifndef LOCK_EX
# define LOCK_EX 2
# endif
# ifndef LOCK_NB
# define LOCK_NB 4
# endif
# ifndef LOCK_UN
# define LOCK_UN 8
# endif

static inline int profiler_flock(int fd, int operation)
{
HANDLE h = (HANDLE)_get_osfhandle(fd);
DWORD flags = 0;
OVERLAPPED ov = {0};

if (h == INVALID_HANDLE_VALUE) {
errno = EBADF;
return -1;
}

if (operation & LOCK_UN) {
if (UnlockFileEx(h, 0, MAXDWORD, MAXDWORD, &ov)) {
return 0;
}
_dosmaperr(GetLastError());
return -1;
}

if (operation & LOCK_EX) {
flags |= LOCKFILE_EXCLUSIVE_LOCK;
}
if (operation & LOCK_NB) {
flags |= LOCKFILE_FAIL_IMMEDIATELY;
}
if (LockFileEx(h, flags, 0, MAXDWORD, MAXDWORD, &ov)) {
return 0;
}
{
DWORD lasterr = GetLastError();
if ((operation & LOCK_NB) && lasterr == ERROR_LOCK_VIOLATION) {
errno = EWOULDBLOCK;
} else {
_dosmaperr(lasterr);
}
}
return -1;
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
# define flock profiler_flock

#else
/* Unix/Linux/macOS */

# include <unistd.h>
typedef ssize_t profiler_ssize_t;

# define profiler_open open
# define profiler_read read
# define profiler_close close

# define PROFILER_MKDIR(path, mode) mkdir(path, mode)

#endif /* PHP_WIN32 */
Comment thread
39ff marked this conversation as resolved.

#endif /* PHP_MARIADB_PROFILER_COMPAT_H */
Loading