Add sql_read_and_hydrate utility
This commit is contained in:
parent
0e1d27d690
commit
d3b267ab02
8
install/SYS_PRD_BND.PyPi.sql
Normal file
8
install/SYS_PRD_BND.PyPi.sql
Normal file
@ -0,0 +1,8 @@
|
||||
USE SYS_PRD_BND;
|
||||
CREATE TABLE `PyPi` (
|
||||
`LibName` varchar(255) NOT NULL,
|
||||
`AliasName` varchar(255) DEFAULT NULL,
|
||||
`LastUpdated` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
|
||||
PRIMARY KEY (`LibName`)
|
||||
);
|
||||
|
||||
51
plt/generatePythonTriggerCode.inc.php
Normal file
51
plt/generatePythonTriggerCode.inc.php
Normal file
@ -0,0 +1,51 @@
|
||||
<?php
|
||||
/**
|
||||
* Generates Python code for sandbox execution based on provided trigger code and row data.
|
||||
*
|
||||
* @param string $functionName The name of the Python function to generate.
|
||||
* @param string $pyCode The Python code to embed within the generated function.
|
||||
* @param array $row The row data to pass to the generated function.
|
||||
*
|
||||
* @return string The complete Python code ready for sandbox execution.
|
||||
*/
|
||||
function generatePythonTriggerCode($functionName, $pyCode, $row) {
|
||||
// Build constant definitions
|
||||
$constants = '';
|
||||
foreach (sql("SELECT Name, Type, Value FROM SYS_PRD_BND.Constants") as $const) {
|
||||
switch ($const['Type']) {
|
||||
case 'String':
|
||||
$val = "'" . str_replace("'", "\\'", $const['Value']) . "'";
|
||||
break;
|
||||
case 'Json':
|
||||
$val = 'json.loads(' . json_encode($const['Value']) . ')';
|
||||
break;
|
||||
default: // Int or Double
|
||||
$val = $const['Value'];
|
||||
}
|
||||
$constants .= "{$const['Name']} = {$val}\n";
|
||||
}
|
||||
|
||||
// Prepare imports
|
||||
$imports = "import json\n" . getPythonImports();
|
||||
|
||||
// Prepare row data for Python
|
||||
$rowJson = str_replace("'", "\\'", json_encode($row));
|
||||
|
||||
// Indent user-provided Python code
|
||||
$indentedCode = implode("\n", array_map(fn($line) => ' ' . $line, explode("\n", $pyCode)));
|
||||
|
||||
// Assemble complete Python script
|
||||
$pythonCode = <<<PY
|
||||
$imports
|
||||
$constants
|
||||
def $functionName(data, error):
|
||||
$indentedCode
|
||||
|
||||
data = json.loads('$rowJson')
|
||||
error = None
|
||||
$functionName(data, error)
|
||||
print(json.dumps(data))
|
||||
PY;
|
||||
|
||||
return $pythonCode;
|
||||
}
|
||||
49
plt/runSandboxedPython.inc.php
Normal file
49
plt/runSandboxedPython.inc.php
Normal file
@ -0,0 +1,49 @@
|
||||
<?php
|
||||
/**
|
||||
* Runs dynamically generated Python code in a sandboxed environment.
|
||||
*
|
||||
* @param string $code The Python code to execute.
|
||||
* @param string &$stdout Captured standard output from the execution.
|
||||
* @param string &$stderr Captured standard error from the execution.
|
||||
*
|
||||
* @return int Exit code of the executed Python script (0 means success).
|
||||
*/
|
||||
function runSandboxedPython($code, &$stdout = null, &$stderr = null) {
|
||||
// Create a temporary file to hold the Python code
|
||||
$tempFile = tempnam(sys_get_temp_dir(), 'sandboxed_') . '.py';
|
||||
|
||||
// Write the generated Python code to the temporary file
|
||||
file_put_contents($tempFile, $code);
|
||||
|
||||
// Prepare the command for executing the Python code
|
||||
$command = "/usr/bin/python3 " . escapeshellarg($tempFile);
|
||||
|
||||
// Descriptor spec to capture stdout and stderr
|
||||
$descriptorspec = [
|
||||
1 => ['pipe', 'w'], // stdout
|
||||
2 => ['pipe', 'w'], // stderr
|
||||
];
|
||||
|
||||
// Execute the Python code using proc_open
|
||||
$process = proc_open($command, $descriptorspec, $pipes);
|
||||
|
||||
if (!is_resource($process)) {
|
||||
unlink($tempFile);
|
||||
throw new Exception('Failed to execute sandboxed Python code.');
|
||||
}
|
||||
|
||||
// Capture stdout and stderr
|
||||
$stdout = stream_get_contents($pipes[1]);
|
||||
fclose($pipes[1]);
|
||||
|
||||
$stderr = stream_get_contents($pipes[2]);
|
||||
fclose($pipes[2]);
|
||||
|
||||
// Get the exit code
|
||||
$exitCode = proc_close($process);
|
||||
|
||||
// Cleanup temporary file
|
||||
unlink($tempFile);
|
||||
|
||||
return $exitCode;
|
||||
}
|
||||
68
sys.php
68
sys.php
@ -66,3 +66,71 @@ function getTblPrimaryKeyColName($tblName) {
|
||||
return $cols;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a SELECT query and hydrates any foreign key columns with the
|
||||
* referenced row data.
|
||||
*
|
||||
* The query should target a single table (e.g., "SELECT * FROM db.table WHERE …").
|
||||
* For each foreign key in that table, an additional key named
|
||||
* `<column>_ref` is added to each returned row containing the referenced row
|
||||
* from the foreign table.
|
||||
*
|
||||
* @param string $query SQL SELECT statement for a single table.
|
||||
* @return array Result set with hydrated foreign key data.
|
||||
*/
|
||||
function sql_read_and_hydrate($query) {
|
||||
$rows = sql_read($query);
|
||||
if (empty($rows)) {
|
||||
return $rows;
|
||||
}
|
||||
|
||||
if (!preg_match('/FROM\s+([`\w\.]+)/i', $query, $m)) {
|
||||
return $rows; // Unable to determine table
|
||||
}
|
||||
|
||||
$table = str_replace('`', '', $m[1]);
|
||||
if (strpos($table, '.') !== false) {
|
||||
list($dbName, $tblName) = explode('.', $table, 2);
|
||||
} else {
|
||||
$dbName = DB_NAME;
|
||||
$tblName = $table;
|
||||
}
|
||||
|
||||
$fks = sql_read(
|
||||
"SELECT COLUMN_NAME, REFERENCED_TABLE_SCHEMA, REFERENCED_TABLE_NAME, " .
|
||||
"REFERENCED_COLUMN_NAME FROM information_schema.KEY_COLUMN_USAGE " .
|
||||
"WHERE TABLE_SCHEMA = '$dbName' AND TABLE_NAME = '$tblName' " .
|
||||
"AND REFERENCED_TABLE_NAME IS NOT NULL"
|
||||
);
|
||||
|
||||
if (empty($fks)) {
|
||||
return $rows; // no foreign keys
|
||||
}
|
||||
|
||||
foreach ($rows as &$row) {
|
||||
foreach ($fks as $fk) {
|
||||
$col = $fk['COLUMN_NAME'];
|
||||
if (!isset($row[$col])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$val = $row[$col];
|
||||
if ($val === null) {
|
||||
$row[$col . '_ref'] = null;
|
||||
continue;
|
||||
}
|
||||
|
||||
$refTable = $fk['REFERENCED_TABLE_SCHEMA'] . '.' . $fk['REFERENCED_TABLE_NAME'];
|
||||
$refCol = $fk['REFERENCED_COLUMN_NAME'];
|
||||
$condition = is_numeric($val)
|
||||
? "$refCol = $val"
|
||||
: "$refCol = '" . str_replace("'", "''", $val) . "'";
|
||||
$refRow = sql_read("SELECT * FROM $refTable WHERE $condition LIMIT 1");
|
||||
$row[$col . '_ref'] = $refRow[0] ?? null;
|
||||
}
|
||||
}
|
||||
unset($row);
|
||||
|
||||
return $rows;
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user