Parcourir la source

added Storage route

Piotr Labudda il y a 10 ans
Parent
commit
eda9a83be5

+ 135 - 0
SE/schema/types.xsd

@@ -0,0 +1,135 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
+	xmlns:p5TypeParam="http://biuro.biall-net.pl/p5/schema/typeParams"
+	xmlns:p5Type="http://biuro.biall-net.pl/p5/schema/types" elementFormDefault="qualified"
+	targetNamespace="http://biuro.biall-net.pl/p5/schema/types">
+	<xs:import namespace="http://biuro.biall-net.pl/p5/schema/typeParams" schemaLocation="http://biuro.biall-net.pl/p5/schema/typeParams.xsd"/>
+
+	<xs:simpleType name="string">
+		<xs:restriction base="xs:string"/>
+	</xs:simpleType>
+	<xs:simpleType name="text">
+		<xs:restriction base="xs:string"/>
+	</xs:simpleType>
+	<xs:simpleType name="enumeration">
+		<xs:restriction base="xs:string"/>
+	</xs:simpleType>
+	<xs:simpleType name="set">
+		<xs:annotation>
+			<xs:documentation>
+Example appinfo for `CRM_PRZYPADEK`.`FORM_TREAT` field defined as set('', 'R', 'W', 'X', 'C', 'S', 'O', 'V', 'E'):
+<xs:appinfo>
+	<p5TypeParam:param name="enumeration">
+		<p5TypeParam:enumeration value="0" label="" description="Brak"/>
+		<p5TypeParam:enumeration value="2" label="R" description="Read"/>
+		<p5TypeParam:enumeration value="4" label="W" description="Write"/>
+		<p5TypeParam:enumeration value="8" label="X" description="Execute"/>
+		<p5TypeParam:enumeration value="16" label="C" description="Create"/>
+		<p5TypeParam:enumeration value="32" label="S" description="..."/>
+		<p5TypeParam:enumeration value="64" label="O" description="..."/>
+		<p5TypeParam:enumeration value="124" label="V" description="..."/>
+		<p5TypeParam:enumeration value="256" label="E" description="Export"/>
+	</p5TypeParam:param>
+</xs:appinfo>
+
+SELECT ID, `FORM_TREAT`, CAST(`FORM_TREAT` AS UNSIGNED) FROM `CRM_PRZYPADEK`;
+
+	IF 8 == 'X' THEN
+		insert into `CRM_PRZYPADEK`(`FORM_TREAT`) values('X');
+	IS EQUAL TO
+		insert into `CRM_PRZYPADEK`(`FORM_TREAT`) values(8);
+
+			</xs:documentation>
+		</xs:annotation>
+		<xs:restriction base="xs:integer"/>
+	</xs:simpleType>
+	<xs:simpleType name="integer">
+		<xs:restriction base="xs:integer"/>
+	</xs:simpleType>
+	<xs:simpleType name="decimal">
+		<xs:restriction base="xs:decimal"/>
+	</xs:simpleType>
+	<xs:simpleType name="double">
+		<xs:restriction base="xs:double"/>
+	</xs:simpleType>
+	<xs:simpleType name="float">
+		<xs:restriction base="xs:double"/>
+	</xs:simpleType>
+	<xs:simpleType name="binary">
+		<xs:restriction base="xs:hexBinary"/>
+	</xs:simpleType>
+	<xs:simpleType name="hexBinary">
+		<xs:restriction base="xs:hexBinary"/>
+	</xs:simpleType>
+
+	<xs:simpleType name="date">
+		<xs:union memberTypes="xs:date p5Type:dateZero"/>
+	</xs:simpleType>
+	<xs:simpleType name="dateZero">
+		<xs:restriction base="xs:string">
+			<xs:enumeration value="0000-00-00"/>
+			<xs:enumeration value="CURRENT_TIMESTAMP"/>
+		</xs:restriction>
+	</xs:simpleType>
+
+	<xs:simpleType name="dateTime">
+		<xs:union memberTypes="xs:dateTime p5Type:dateTimeZero"/>
+	</xs:simpleType>
+	<xs:simpleType name="dateTimeZero">
+		<xs:restriction base="xs:string">
+			<xs:enumeration value="0000-00-00 00:00:00"/>
+			<xs:enumeration value="CURRENT_TIMESTAMP"/>
+		</xs:restriction>
+	</xs:simpleType>
+
+	<xs:simpleType name="time">
+		<xs:union memberTypes="xs:time p5Type:timeZero"/>
+	</xs:simpleType>
+	<xs:simpleType name="timeZero">
+		<xs:restriction base="xs:string">
+			<xs:enumeration value="00:00:00"/>
+			<xs:enumeration value="CURRENT_TIMESTAMP"/>
+		</xs:restriction>
+	</xs:simpleType>
+
+	<xs:simpleType name="year">
+		<xs:union memberTypes="xs:gYear p5Type:yearZero"/>
+	</xs:simpleType>
+	<xs:simpleType name="yearZero">
+		<xs:restriction base="xs:string">
+			<xs:enumeration value="0000"/>
+			<xs:enumeration value="CURRENT_TIMESTAMP"/>
+		</xs:restriction>
+	</xs:simpleType>
+
+<!--
+TODO: Geometry
+TODO: MultiPolygon
+TODO: MultiPoint
+TODO: MultiLineString
+TODO: GeometryCollection
+
+	<gml:MultiPolygon>
+		<gml:polygonMember>
+			<gml:Polygon>
+	...
+-->
+	<xs:simpleType name="geometry">
+		<xs:restriction base="xs:string"/>
+	</xs:simpleType>
+	<xs:simpleType name="polygon">
+		<xs:restriction base="xs:string">
+			<xs:pattern value="(\-?\d+\.?\d*,-?\d+\.?\d*)( (\-?\d+\.?\d*,-?\d+\.?\d*))+"/>
+		</xs:restriction>
+	</xs:simpleType>
+	<xs:simpleType name="point">
+		<xs:restriction base="xs:string">
+			<xs:pattern value="\-?\d\.?\d*,\-?\d\.?\d*"/>
+		</xs:restriction>
+	</xs:simpleType>
+	<xs:simpleType name="lineString">
+		<xs:restriction base="xs:string">
+			<xs:pattern value="(\-?\d+\.?\d*,\-?\d+\.?\d*)( (\-?\d+\.?\d*,\-?\d+\.?\d*))+"/>
+		</xs:restriction>
+	</xs:simpleType>
+</xs:schema>

+ 276 - 0
SE/se-lib/Core/Storage/Mysql.php

@@ -0,0 +1,276 @@
+<?php
+
+Lib::loadClass('Core_StorageBase');
+Lib::loadClass('Core_StorageFactory');
+
+class Core_Storage_Mysql extends Core_StorageBase {
+
+	protected $_databaseName = '';
+	protected $_zasob_id = 0;
+	protected $_pdo = null;
+
+	public function __construct($pdo) {
+		$this->_pdo = $pdo;
+		$this->_databaseName = $pdo->getDatabaseName();
+		$this->_zasob_id = $pdo->getZasobId();
+	}
+
+	public function fetchAllAssoc($sql) {
+		if (empty($sql)) {
+			throw new Exception("Empty query");
+		}
+		$stmt = $this->_pdo->query($sql);
+		return $stmt->fetchAll(PDO::FETCH_ASSOC);
+	}
+
+	public function query($query) {
+		throw new Exception("Storage " . __FUNCTION__ . " not defined");
+		$res = mysql_query($query, $this->_conn);
+		if (!$res) {
+			$this->_set_error('SQL QUERY FAILED: '.mysql_error($this->_conn));
+			return $null;
+		}
+	}
+
+	public function getTableStruct($tblName) {
+		$sql = "
+			-- show fields from {$tblName}
+			select cols.COLUMN_NAME as name
+				, cols.DATA_TYPE as type
+				, if('YES' = cols.IS_NULLABLE, 1, 0) as is_nullable
+				, cols.COLUMN_DEFAULT as default_value
+				, if(cols.COLUMN_DEFAULT is null, 1, 0) as default_is_null
+				, cols.CHARACTER_MAXIMUM_LENGTH as max_length
+				, cols.NUMERIC_PRECISION as num_precision
+				, cols.NUMERIC_SCALE as num_scale
+				, cols.CHARACTER_SET_NAME as char_encoding -- latin2
+				, cols.COLLATION_NAME as char_collation -- latin2_general_ci
+				, cols.EXTRA as extra
+				, cols.COLUMN_TYPE as raw_storage_type
+		--		, cols.*
+			from INFORMATION_SCHEMA.COLUMNS cols
+			where cols.TABLE_SCHEMA like :db_name
+				and cols.TABLE_NAME like :tbl_name
+		";
+		$sth = $this->_pdo->prepare($sql);
+		$sth->bindValue(':db_name', $this->_pdo->getDatabaseName(), PDO::PARAM_STR);
+		$sth->bindValue(':tbl_name', $tblName, PDO::PARAM_STR);
+		$sth->execute();
+		$structRaw = $sth->fetchAll();
+		if (empty($structRaw)) throw new Exception("Empty struct for table '{$tblName}'");
+		foreach ($structRaw as $field) {
+			$struct[$field['name']] = $field;
+		}
+		return $struct;
+	}
+
+	protected function _getTableStruct($tblName) {
+		$tblStructRaw = $this->getTableStructRaw($tblName);
+		$tblStruct = array();
+		foreach ($tblStructRaw as $fieldStruct) {
+			$fldName = $fieldStruct['COLUMN_NAME'];
+			$storageType = '';
+			/*
+BOOLEAN xsd: {true, false, 1, 0}
+SERIAL is an alias for BIGINT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE
+SET
+			*/
+			if ('varchar' == $fieldStruct['DATA_TYPE']
+					|| 'char' == $fieldStruct['DATA_TYPE']
+				 ) {
+				$storageType = Core_StorageFactory::getType('String');
+				if ($fieldStruct['CHARACTER_MAXIMUM_LENGTH'] > 0) {
+					$storageType->setMaxLength($fieldStruct['CHARACTER_MAXIMUM_LENGTH']);
+				}
+			}
+			else if ('text' == $fieldStruct['DATA_TYPE']
+							 || 'longtext' == $fieldStruct['DATA_TYPE']
+							 || 'mediumtext' == $fieldStruct['DATA_TYPE']
+							 || 'tinytext' == $fieldStruct['DATA_TYPE']
+							 ) {
+				$storageType = Core_StorageFactory::getType('Text');
+			}
+			else if ('int' == $fieldStruct['DATA_TYPE']
+							 || 'tinyint' == $fieldStruct['DATA_TYPE']
+							 || 'smallint' == $fieldStruct['DATA_TYPE']
+							 || 'mediumint' == $fieldStruct['DATA_TYPE']
+							 || 'bigint' == $fieldStruct['DATA_TYPE']
+							 || 'bit' == $fieldStruct['DATA_TYPE']
+							) {
+				$storageType = Core_StorageFactory::getType('Integer');
+				$storageType->setTotalDigits($fieldStruct['NUMERIC_PRECISION']);
+			}
+			else if ('decimal' == $fieldStruct['DATA_TYPE']) {
+				$storageType = Core_StorageFactory::getType('Decimal');
+				if ($fieldStruct['NUMERIC_SCALE'] > 0) {
+					$storageType->setFractionDigits($fieldStruct['NUMERIC_SCALE']);
+				}
+				if ($fieldStruct['NUMERIC_PRECISION'] > 0) {
+					$storageType->setTotalDigits($fieldStruct['NUMERIC_PRECISION']);
+				}
+			}
+			else if ('double' == $fieldStruct['DATA_TYPE']
+							 || 'real' == $fieldStruct['DATA_TYPE']
+							 || 'float' == $fieldStruct['DATA_TYPE']
+							) {
+				$storageType = Core_StorageFactory::getType('Double');
+			}
+			else if ('binary' == $fieldStruct['DATA_TYPE']
+							 || 'varbinary' == $fieldStruct['DATA_TYPE']
+							 || 'blob' == $fieldStruct['DATA_TYPE']
+							 || 'tinyblob' == $fieldStruct['DATA_TYPE']
+							 || 'longblob' == $fieldStruct['DATA_TYPE']
+							 || 'mediumblob' == $fieldStruct['DATA_TYPE']
+							) {
+				$storageType = Core_StorageFactory::getType('Binary');
+				$storageType->setRestriction('maxExclusive', $fieldStruct['CHARACTER_MAXIMUM_LENGTH']);
+			}
+			else if ('enum' == $fieldStruct['DATA_TYPE']) {
+				$storageType = Core_StorageFactory::getType('Enumeration');
+				$enumValues = explode(',', str_replace(array('(',')',"'",'"'), '', substr($fieldStruct['COLUMN_TYPE'], 5)));
+				$enumList = array();
+				foreach ($enumValues as $value) {
+					$enumList[] = $value;
+				}
+				$storageType->setEnumeration($enumList);
+			}
+			else if ('timestamp' == $fieldStruct['DATA_TYPE']) {
+				$storageType = Core_StorageFactory::getType('DateTime');
+				$storageType->setFormat('yyyy-MM-dd hh:mm:ss');
+			}
+			else if ('datetime' == $fieldStruct['DATA_TYPE']) {
+				$storageType = Core_StorageFactory::getType('DateTime');
+				$storageType->setFormat('yyyy-MM-dd hh:mm');
+			}
+			else if ('date' == $fieldStruct['DATA_TYPE']) {
+				$storageType = Core_StorageFactory::getType('Date');
+				$storageType->setFormat('yyyy-MM-dd');
+			}
+			else if ('time' == $fieldStruct['DATA_TYPE']) {
+				$storageType = Core_StorageFactory::getType('Time');
+				$storageType->setFormat('hh:mm:ss');
+			}
+			else if ('year' == $fieldStruct['DATA_TYPE']) {
+				$storageType = Core_StorageFactory::getType('Year');
+				$storageType->setFormat('yyyy');
+			}
+			else if ('point' == $fieldStruct['DATA_TYPE']) {
+				$storageType = Core_StorageFactory::getType('Point');
+			}
+			else if ('linestring' == $fieldStruct['DATA_TYPE']) {
+				$storageType = Core_StorageFactory::getType('Linestring');
+			}
+			else if ('polygon' == $fieldStruct['DATA_TYPE']) {
+				$storageType = Core_StorageFactory::getType('Polygon');
+			}
+			else if ('geometry' == $fieldStruct['DATA_TYPE']
+							 || 'multipoint' == $fieldStruct['DATA_TYPE']
+							 || 'multilinestring' == $fieldStruct['DATA_TYPE']
+							 || 'multipolygon' == $fieldStruct['DATA_TYPE']
+							 || 'geometrycollection' == $fieldStruct['DATA_TYPE']
+							) {
+				$storageType = Core_StorageFactory::getType('Geometry');
+				$storageType->setSpatialType($fieldStruct['COLUMN_TYPE']);
+			}
+			else {
+				$storageType = Core_StorageFactory::getType('Unknown');
+				$storageType->setRawType($fieldStruct['COLUMN_TYPE']);
+			}
+
+			if (!$storageType) {
+				echo "<div class=\"alert alert-warning\">Storage type '{$fieldStruct['COLUMN_TYPE']}' not recognized</div>";
+				//TODO: throw new Exception("Storage type '{$fieldStruct['COLUMN_TYPE']}' not recognized");
+			}
+
+			if ('PRI' == $fieldStruct['COLUMN_KEY']) {
+				$storageType->setPrimaryKey(true);
+			}
+
+			$vFldNillable = false;
+			if ('YES' == $fieldStruct['IS_NULLABLE']) {
+				$vFldNillable = true;
+			} else if ('NO' == $fieldStruct['IS_NULLABLE']
+								 && 'PRI' == $fieldStruct['COLUMN_KEY']
+								 && 'auto_increment' == $fieldStruct['EXTRA']
+								) {
+				$vFldNillable = true;
+			}
+			$storageType->setNillable($vFldNillable);
+
+			if (!empty($fieldStruct['COLUMN_DEFAULT'])) {
+				$storageType->setDefault($fieldStruct['COLUMN_DEFAULT']);
+			} else if ('0' === $fieldStruct['COLUMN_DEFAULT']) {
+				$storageType->setDefault(0);
+			} else if ('' === $fieldStruct['COLUMN_DEFAULT']) {
+				$storageType->setDefault('');
+			} else if (null === $fieldStruct['COLUMN_DEFAULT']
+								 && 'YES' == $fieldStruct['IS_NULLABLE']
+								 && 1 == $fieldStruct['COLUMN_DEFAULT_IS_NULL']
+								) {
+				$storageType->setDefault(null);
+			}
+
+			$tblStruct[$fldName] = $storageType;
+		}
+		return $tblStruct;
+	}
+
+	public function getTableStructRaw($tblName) {
+		$tblNamePattern = '/^[a-zA-Z0-9_]+$/';
+		if (!preg_match($tblNamePattern, $tblName)) {
+			throw new Exception("Table name not allowed");
+		}
+		$sqlTblName = $tblName;
+		$sql = "show fields from `{$sqlTblName}` ";
+		$sql = "select cols.*
+		--		, `IS_NULLABLE` as `Nullable`
+		--		, `COLUMN_DEFAULT` as `DEFAULT`
+				, IF(cols.`IS_NULLABLE`='YES' and cols.`COLUMN_DEFAULT` is null, 1, 0) as `COLUMN_DEFAULT_IS_NULL`
+			from `INFORMATION_SCHEMA`.`COLUMNS` cols
+			where cols.`TABLE_NAME`='{$sqlTblName}'
+				and cols.`TABLE_SCHEMA`='{$this->_databaseName}'
+		";
+		return $this->fetchAllAssoc($sql);
+	}
+
+	public function getViewStructTODO($viewName) {// TODO: getViewStruct
+		$viewNamePattern = '/^[a-zA-Z0-9_]+$/';
+		if (!preg_match($viewNamePattern, $viewName)) {
+			throw new Exception("View name not allowed");
+		}
+	}
+
+	public function getViewStructRaw($viewName) {
+		$viewNamePattern = '/^[a-zA-Z0-9_]+$/';
+		if (!preg_match($viewNamePattern, $viewName)) {
+			throw new Exception("View name not allowed");
+		}
+		$sqlViewName = $viewName;
+		$sql = "show create view `{$sqlViewName}` ";
+		return $this->fetchAllAssoc($sql);
+	}
+
+	public function getTableListWithInfo() {
+		return $this->getTableFullList();
+	}
+
+	public function getTableList() {
+		return array_filter($this->getTableFullList(), function($row) {
+			return ('BASE TABLE' == $row['table_type']);
+		});
+	}
+
+	public function getViewList() {
+		return array_filter($this->getTableFullList(), function($row) {
+			return ('VIEW' == $row['table_type']);
+		});
+	}
+
+	public function getTableFullList() {
+		return array_map(function($row) {
+			$values = array_values($row);
+			return array('table_name' => strtolower($values[0]), 'table_type' => $values[1]);
+		}, $this->_pdo->fetchAll('show full tables'));
+	}
+
+}

+ 271 - 0
SE/se-lib/Core/Storage/Pgsql.php

@@ -0,0 +1,271 @@
+<?php
+
+Lib::loadClass('Core_StorageBase');
+Lib::loadClass('Core_StorageFactory');
+
+class Core_Storage_Pgsql extends Core_StorageBase {
+
+	protected $_databaseName = '';
+	protected $_zasob_id = 0;
+	protected $_pdo = null;
+
+	public function __construct($pdo) {
+		$this->_pdo = $pdo;
+		$this->_databaseName = $pdo->getDatabaseName();
+		$this->_zasob_id = $pdo->getZasobId();
+	}
+
+	public function getTableStruct($tblName) {
+		throw new Exception("TODO: F." . __FUNCTION__);
+
+		$sql = "
+			-- show fields from {$tblName}
+			select cols.COLUMN_NAME as name
+				, cols.DATA_TYPE as type
+				, if('YES' = cols.IS_NULLABLE, 1, 0) as is_nullable
+				, cols.COLUMN_DEFAULT as default_value
+				, if(cols.COLUMN_DEFAULT is null, 1, 0) as default_is_null
+				, cols.CHARACTER_MAXIMUM_LENGTH as max_length
+				, cols.NUMERIC_PRECISION as num_precision
+				, cols.NUMERIC_SCALE as num_scale
+				, cols.CHARACTER_SET_NAME as char_encoding -- latin2
+				, cols.COLLATION_NAME as char_collation -- latin2_general_ci
+				, cols.EXTRA as extra
+				, cols.COLUMN_TYPE as raw_storage_type
+		--		, cols.*
+			from INFORMATION_SCHEMA.COLUMNS cols
+			where cols.TABLE_SCHEMA like :db_name
+				and cols.TABLE_NAME like :tbl_name
+		";
+		$sth = $this->_pdo->prepare($sql);
+		$sth->bindValue(':db_name', $this->_pdo->getDatabaseName(), PDO::PARAM_STR);
+		$sth->bindValue(':tbl_name', $tblName, PDO::PARAM_STR);
+		$sth->execute();
+		$structRaw = $sth->fetchAll();
+		if (empty($structRaw)) throw new Exception("Empty struct for table '{$tblName}'");
+		foreach ($structRaw as $field) {
+			$struct[$field['name']] = $field;
+		}
+		return $struct;
+	}
+
+	protected function _getTableStruct($tblName) {// ?
+		$tblStructRaw = $this->getTableStructRaw($tblName);
+		$tblStruct = array();
+		foreach ($tblStructRaw as $fieldStruct) {
+			$fldName = $fieldStruct['column_name'];
+			$storageType = '';
+			if ('integer' == $fieldStruct['data_type']
+					|| 'bigint' == $fieldStruct['data_type']
+					|| 'smallint' == $fieldStruct['data_type']
+				 ) {
+				$storageType = Core_StorageFactory::getType('Integer');
+			}
+			else if ('character varying' == $fieldStruct['data_type']
+								|| 'character' == $fieldStruct['data_type']
+								) {
+				$storageType = Core_StorageFactory::getType('String');
+				$storageType->setMaxLength($fieldStruct['character_maximum_length']);
+			}
+			else if ('numeric' == $fieldStruct['data_type']
+							 || 'decimal' == $fieldStruct['data_type']
+							) {
+				$storageType = Core_StorageFactory::getType('Decimal');
+			}
+			else if ('real' == $fieldStruct['data_type']
+							 || 'double precision' == $fieldStruct['data_type']
+							) {
+				$storageType = Core_StorageFactory::getType('Double');
+			}
+			else if ('text' == $fieldStruct['data_type']) {
+				$storageType = Core_StorageFactory::getType('Text');
+			}
+			else if ('"char"' == $fieldStruct['data_type']) {
+				$storageType = Core_StorageFactory::getType('String');
+				$storageType->setMaxLength(1);
+			}
+			else if ('date' == $fieldStruct['data_type']) {
+				$storageType = Core_StorageFactory::getType('Date');
+			}
+			else if ('timestamp without time zone' == $fieldStruct['data_type']) {
+				$storageType = Core_StorageFactory::getType('DateTime');
+			}
+			else if ('timestamp with time zone' == $fieldStruct['data_type']) {
+				$storageType = Core_StorageFactory::getType('DateTime');
+			}
+			else if ('USER-DEFINED' == $fieldStruct['data_type']) {
+				if ('geometry' == $fieldStruct['udt_name']) {
+					$storageType = Core_StorageFactory::getType('Geometry');
+					$rawStructure = $this->_getStructureRaw('public', $tblName, $fldName);
+					if (!empty($rawStructure[0]['type'])) {
+						$spatialType = $rawStructure[0]['type'];
+						$storageType->setSpatialType($spatialType);
+					}
+				} else {
+					$storageType = Core_StorageFactory::getType('Unknown');
+					$storageType->setRawType(array('data_type'=>$fieldStruct['data_type'], 'udt_name'=>$fieldStruct['udt_name']));
+				}
+			}
+			else {
+				$storageType = Core_StorageFactory::getType('Unknown');
+				$storageType->setRawType(array('data_type'=>$fieldStruct['data_type'], 'udt_name'=>$fieldStruct['udt_name']));
+			}
+
+			if (!$storageType) {
+				echo "<div class=\"alert alert-warning\">Storage type '{$fieldStruct['Type']}' not recognized</div>";
+				// TODO: throw new Exception("Storage type '{$fieldStruct['Type']}' not recognized");
+			}
+
+			if ($fieldStruct['is_primary_key'] > 0) {
+				$storageType->setPrimaryKey(true);
+			}
+
+			$vFldNillable = false;
+			if ('YES' == $fieldStruct['is_nullable']) {
+				$vFldNillable = true;
+			} else if ('NO' == $fieldStruct['is_nullable']
+								 && $fieldStruct['is_primary_key'] > 0
+								 && 'nextval(' == substr($fieldStruct['column_default'], 0, 8)
+								) {
+				$vFldNillable = true;
+			}
+			$storageType->setNillable($vFldNillable);
+
+			$vFldDefault = $fieldStruct['column_default'];
+			if (!empty($vFldDefault)) {
+				if ('::"char"' == substr($vFldDefault, -8)) {
+					$vFldDefault = substr($vFldDefault, 1, -9);
+					$storageType->setDefault($vFldDefault);
+				} else if ('::"text"' == substr($vFldDefault, -8)) {
+					$vFldDefault = substr($vFldDefault, 1, -9);
+					$storageType->setDefault($vFldDefault);
+				} else if ('::bpchar' == substr($vFldDefault, -8)) {
+					$vFldDefault = substr($vFldDefault, 1, -9);
+					$storageType->setDefault($vFldDefault);
+				} else if ('::character varying' == substr($vFldDefault, -19)) {
+					$vFldDefault = substr($vFldDefault, 1, -20);
+					$storageType->setDefault($vFldDefault);
+				} else if (is_numeric($vFldDefault)) {
+					$storageType->setDefault($vFldDefault);
+				} else if ('now()' == $vFldDefault) {
+					// TODO: default = now()
+				}
+			} else if ('0' === $vFldDefault) {
+				$storageType->setDefault('0');
+			}
+
+			$tblStruct[$fldName] = $storageType;
+		}
+		return $tblStruct;
+	}
+
+	public function getTableStructRaw($tblName) {// ?
+		$tblNamePattern = '/^[a-zA-Z0-9_]+$/';
+		if (!preg_match($tblNamePattern, $tblName)) {
+			throw new Exception("Table name not allowed");
+		}
+		$sqlTblName = $tblName;
+		$sql = "select * from information_schema.columns where table_name='{$sqlTblName}'";
+		$sqlAllPrimaryKeys = "
+			select tc.table_schema, tc.table_name, kc.column_name
+			from
+					information_schema.table_constraints tc,
+					information_schema.key_column_usage kc
+			where
+					tc.constraint_type = 'PRIMARY KEY'
+					and kc.table_name = tc.table_name
+					and kc.table_schema = tc.table_schema
+					and kc.constraint_name = tc.constraint_name
+			order by 1, 2;
+		";
+		$sql = "select cols.*
+				, (select count(kc.column_name)
+					from information_schema.table_constraints tc
+						join information_schema.key_column_usage kc on(kc.table_name = tc.table_name
+							and kc.table_schema = tc.table_schema
+							and kc.constraint_name = tc.constraint_name)
+					where tc.constraint_type = 'PRIMARY KEY'
+						and tc.table_name = cols.table_name
+						and kc.column_name = cols.column_name
+				) as is_primary_key
+			from information_schema.columns cols
+			where cols.table_name='{$sqlTblName}'
+		";
+		return $this->fetchAllAssoc($sql);
+	}
+
+	public function getTableListWithInfo() {
+		return $this->_getPgsqlStructureRaw();
+	}
+
+	public function _getPgsqlStructureRaw($schemaName = 'public', $tableName = '', $fieldName = '') {
+		$rawStructureList = array();
+		$sqlWhereAndSchemaName = ($schemaName) ? " and n.nspname='{$schemaName}' " : '';
+		$sqlWhereAndTableName  = ($tableName) ?  " and c.relname='{$tableName}' " : '';
+		$sqlWhereAndFieldName  = ($fieldName) ?  " and f.attname='{$fieldName}' " : '';
+		$sql = "
+			select
+				c.relname as table_name,
+				f.attnum as number,
+				f.attname as name,
+				f.attnum,
+				f.attnotnull as notnull,
+				f.atttypid,
+				f.atttypmod,
+				pg_catalog.format_type(f.atttypid,f.atttypmod) as type,
+				case when p.contype = 'p' then 1 else 0 end as primarykey,
+				case when p.contype = 'u' then 1 else 0 end as uniquekey,
+				case when p.contype = 'f' then g.relname end as foreignkey,
+				case when p.contype = 'f' then p.confkey end as foreignkey_fieldnum,
+				case when p.contype = 'f' then g.relname end as foreignkey,
+				case when p.contype = 'f' then p.conkey end as foreignkey_connnum,
+				case when f.atthasdef = 't' then d.adsrc end as default
+			from pg_attribute f
+				join pg_class c on c.oid = f.attrelid
+				join pg_type t on t.oid = f.atttypid
+				left join pg_attrdef d on d.adrelid = c.oid and d.adnum = f.attnum
+				left join pg_namespace n on n.oid = c.relnamespace
+				left join pg_constraint p on p.conrelid = c.oid and f.attnum = any (p.conkey)
+				left join pg_class as g on p.confrelid = g.oid
+			where c.relkind = 'r'::char
+				{$sqlWhereAndSchemaName}
+				{$sqlWhereAndTableName}
+				{$sqlWhereAndFieldName}
+				and f.attnum > 0
+			order by c.relname, number
+		";
+		return $this->_pdo->fetchAll($sql);
+	}
+
+	public function getTableList() {
+		return array_filter($this->getTableFullList(), function($row) {
+			return ('BASE TABLE' == $row['table_type']);
+		});
+	}
+
+	public function getViewList() {
+		return array_filter($this->getTableFullList(), function($row) {
+			return ('VIEW' == $row['table_type']);
+		});
+	}
+
+	public function getTableFullList() {
+		return array_map(function($row) {
+			$row['table_name'] = strtolower($row['table_name']);
+			return $row;
+		}, $this->_pdo->fetchAll("
+			select table_name, table_type
+			from information_schema.tables
+			where table_schema not in ('pg_catalog', 'information_schema')
+		"));
+	}
+
+	public function fetchAllAssoc($sql) {// ?
+		if (empty($sql)) {
+			throw new Exception("Empty query");
+		}
+		$stmt = $this->_pdo->query($sql);
+		return $stmt->fetchAll(PDO::FETCH_ASSOC);
+	}
+
+}

+ 83 - 0
SE/se-lib/Core/StorageBase.php

@@ -0,0 +1,83 @@
+<?php
+
+Lib::loadClass('Core_StorageObject');
+Lib::loadClass('Core_StorageField');
+
+class Core_StorageBase {
+
+	protected $_zasob_id = 0;
+	protected $_cache = array();
+
+	public function __construct($pdo) {
+		throw new Exception("Storage constructor not defined");
+	}
+
+	public function fetchAllAssoc($sql) {
+		throw new Exception("Storage " . __FUNCTION__ . " not defined");
+	}
+
+	public function getZasobId() {
+		return $this->_zasob_id;
+	}
+
+	public function getType() {
+		return $this->_pdo->getType();
+	}
+
+	public function getObject($tableName) {
+		$storageZasobId = $this->getZasobId();
+		$storageObject = new Core_StorageObject("{$storageZasobId}/{$tableName}");
+		$tableStruct = $this->getTableStruct($tableName);
+		$pkFieldName = null;
+		foreach ($tableStruct as $fldName => $vFldType) {
+			$vField = new Core_StorageField("{$storageZasobId}/{$tableName}/{$fldName}");
+			$vField->setName($fldName);
+			$storageObject->setField($vField);
+			if ($vFldType->isPrimaryKey()) {
+				$pkFieldName = $fldName;
+			}
+		}
+		if ($pkFieldName) {
+			$storageObject->setPrimaryKeyFieldName($pkFieldName);
+		}
+		return $storageObject;
+	}
+
+	public function getTableStruct($tableName) {
+		$storageZasobId = $this->getZasobId();
+		$tblUri = "{$storageZasobId}/{$tableName}";
+		if (!array_key_exists($tblUri, $this->_cache)) {
+			$this->_cache[$tblUri] = $this->_getTableStruct($tableName);
+		}
+		return $this->_cache[$tblUri];
+	}
+
+	protected function _getTableStruct($tableName) {
+		throw new Exception("Storage " . __FUNCTION__ . " not defined");
+	}
+
+	public function getTableStructRaw($tableName) {
+		throw new Exception("Storage " . __FUNCTION__ . " not defined");
+	}
+
+	public function getTableList() {
+		throw new Exception("Storage " . __FUNCTION__ . " not defined");
+	}
+
+	public function getViewStruct($viewName) {
+		throw new Exception("Storage " . __FUNCTION__ . " not defined");
+	}
+
+	public function getViewStructRaw($viewName) {
+		throw new Exception("Storage " . __FUNCTION__ . " not defined");
+	}
+
+	public function getViewList() {
+		throw new Exception("Storage " . __FUNCTION__ . " not defined");
+	}
+
+	public function getTableListWithInfo() {
+		throw new Exception("Storage " . __FUNCTION__ . " not defined");
+	}
+
+}

+ 169 - 0
SE/se-lib/Core/StorageFactory.php

@@ -0,0 +1,169 @@
+<?php
+
+Lib::loadClass('Config');
+Lib::loadClass('Core_StorageBase');
+Lib::loadClass('Core_StorageField');
+Lib::loadClass('Core_StorageObject');
+
+class Core_StorageFactory {
+
+	public static function getStorage($storageId = null) {
+		static $_instance;
+		if (!is_array($_instance)) {
+			$_instance = array();
+		}
+		$dbConfName = 'default_db';
+		if (is_numeric($storageId) && $storageId > 0) {
+			$dbConfName = "zasob_{$storageId}";
+		} else if ($storageId == 'import_db') {
+			$dbConfName = "import_db";
+		} else if ($storageId == 'test_db') {
+			$dbConfName = "test_db";
+		}  else if ($storageId == 'billing_db') {
+			$dbConfName = "billing_db";
+		} 
+
+		if (!array_key_exists($dbConfName, $_instance)) {
+			$_instance[$dbConfName] = null;
+
+			Lib::loadClass('Config');
+			$conf = Config::getConfFile($dbConfName);
+			if (!$conf) {
+				throw new Exception("Config for {$dbConfName} not found");
+			}
+			$params = array();
+			$params['type'] = V::get('type', 'mysql', $conf);
+			$params['host'] = V::get('host', '', $conf);
+			$params['port'] = V::get('port', '', $conf);
+			$params['user'] = V::get('user', '', $conf);
+			$params['pass'] = V::get('pass', '', $conf);
+			$params['zasob_id'] = V::get('zasob_id', '', $conf);
+			$params['database'] = V::get('database', '', $conf);
+			$params['tdsver'] = V::get('tdsver', '', $conf);
+			$params['names'] = 'utf8';
+
+			if (empty($params['type'])) {
+				throw new Exception("Storage config error: type not defined");
+			}
+
+			$storageClassName = 'Core_Storage_' . ucfirst($params['type']);
+			Lib::loadClass($storageClassName);
+			if (class_exists($storageClassName)) {
+				$_instance[$dbConfName] = new $storageClassName($params);
+			}
+		}
+		return $_instance[$dbConfName];
+	}
+
+	public static function getObjectSchema($objectUri) {
+		if (false === strpos($objectUri, ':')) {
+			throw new Exception("Wrong uri '{$objectUri}'");
+		}
+		$uriParts = explode(':', $objectUri);
+		if (count($uriParts) != 2) throw new Exception("Wrong object uri");
+		$objectName = array_pop($uriParts);
+
+		require_once APP_PATH_SCHEMA . DS . 'objectsFromXsd.php';
+		$objectClassName = "SchemaObject_{$objectName}";
+		if (!class_exists($objectClassName)) {
+			throw new Exception("Object definition not exists '{$objectName}'.");
+		}
+		$storageObject = new $objectClassName("p5:{$objectName}");
+		return $storageObject;
+	}
+
+	public static function getObject($objectName) {
+		require_once APP_PATH_SCHEMA . DS . 'objectsFromXsd.php';
+		$objectClassName = "SchemaObject_{$objectName}";
+		if (!class_exists($objectClassName)) {
+			throw new Exception("Object definition not exists '{$objectName}'.");
+		}
+		$storageObject = new Core_StorageObject("p5:{$objectName}");
+		return $storageObject;
+	}
+
+	/**
+	 * @param string $objectUri
+	 *        examples:
+	 *                  'default_db:TEST_PERMS' - xsd from main storage
+	 *                  'p5_913:TEST_PERMS' - xsd from storage id 913
+	 *                  'Projekt' - main xsd file
+	 */
+	public static function getObjectFromStructure($objectUri) {
+		if (false !== strpos($objectUri, ':')) {
+			$uriParts = explode(':', $objectUri, 2);
+			if (count($uriParts) != 2) throw new Exception("Wrong object uri");
+			$storageName = array_shift($uriParts);
+			$tableName = array_shift($uriParts);
+			$storage = Core_StorageFactory::getStorage($storageName);
+			$storageZasobId = $storage->getZasobId();
+			$storageObject = new Core_StorageObject("{$storageZasobId}/{$tableName}");
+			$tableStruct = $storage->getTableStruct($tableName);
+			$pkFieldName = null;
+			foreach ($tableStruct as $fldName => $vFldType) {
+				$vField = new Core_StorageField("{$storageZasobId}/{$tableName}/{$fldName}");
+				$vField->setName($fldName);
+				$storageObject->setField($vField);
+				if ($vFldType->isPrimaryKey()) {
+					$pkFieldName = $fldName;
+				}
+			}
+			if ($pkFieldName) {
+				$storageObject->setPrimaryKeyFieldName($pkFieldName);
+			}
+		} else {
+			throw new Exception('TODO: getObject from main schema');
+		}
+		return $storageObject;
+	}
+
+	public static function getType($type) {
+		$storageClassName = 'Core_StorageType_' . ucfirst($type);
+		Lib::loadClass($storageClassName);
+		return new $storageClassName();
+	}
+
+	public function getTypeByUri($uri) {
+		$uriParts = explode('/', $uri);
+		$storageId = $uriParts[0];
+		$tableName = $uriParts[1];
+		$fieldName = $uriParts[2];
+		$storage = Core_StorageFactory::getStorage($storageId);
+		$tableStruct = $storage->getTableStruct($tableName);
+		$storageType = (array_key_exists($fieldName, $tableStruct)) ? $tableStruct[$fieldName] : null;
+		return $storageType;
+	}
+
+	/**
+	 * Cache - write
+	 */
+	public static function serializeTableStructure($tableStructure) {
+		$tableStructureRaw = array();
+		$tableStructureRaw['_classMap'] = array();
+		$tableStructureRaw['_rawData'] = array();
+		foreach ($tableStructure as $fldName => $fldType) {
+			$tableStructureRaw['_rawData'][$fldName] = $fldType->getRawData();
+			$tableStructureRaw['_classMap'][$fldName] = get_class($fldType);
+		}
+		return $tableStructureRaw;
+	}
+
+	/**
+	 * Cache - read
+	 */
+	public static function unserializeTableStructure($tableStructureRaw) {
+		$tableStructure = array();
+		if (empty($tableStructureRaw['_classMap'])) {
+			//throw new Exception('Wrong structure in cache');
+			return;
+		}
+		foreach ($tableStructureRaw['_classMap'] as $fldName => $fldClassName) {
+			Lib::loadClass($fldClassName);
+			$fldType = new $fldClassName();
+			$fldType->rebuildFromRawData($tableStructureRaw['_rawData'][$fldName]);
+			$tableStructure[$fldName] = $fldType;
+		}
+		return $tableStructure;
+	}
+
+}

+ 13 - 0
SE/se-lib/DB.php

@@ -145,6 +145,19 @@ class DB {
 		return $_instance[$dbConfName];
 	}
 
+	public static function getStorage($db = null) {
+		$pdo = self::getPDO($db);
+		switch ($pdo->getType()) {
+			case 'mysql':
+				Lib::loadClass('Core_Storage_Mysql');
+				return new Core_Storage_Mysql($pdo);
+			case 'pgsql':
+				Lib::loadClass('Core_Storage_Pgsql');
+				return new Core_Storage_Pgsql($pdo);
+			default: throw new Exception("Storage for type '{$this->_type}' not implemented");
+		}
+	}
+
 	public static function getPDO($db = null) {
 		static $_instance;
 		if (!is_array($_instance)) $_instance = array();

+ 488 - 0
SE/se-lib/Route/Storage.php

@@ -0,0 +1,488 @@
+<?php
+// @requires $_SERVER['SERVER_NAME']
+
+Lib::loadClass('RouteBase');
+Lib::loadClass('Schema_TableFactory');
+
+/*
+# Storage:
+- [ ] view available storage (from Zasoby - type 'BAZA_DANYCH', 'DATABASE_MYSQL', ...)
+- [ ] check config for connection
+- [ ] add cells to Zasoby
+- [ ] create cells in Storage
+- [x] use PDO
+
+*/
+class Route_Storage extends RouteBase {
+
+	public function handleAuth() {
+		if (!User::logged()) {
+			User::authByRequest();
+		}
+	}
+
+	public function defaultAction() {
+		SE_Layout::gora();
+		SE_Layout::menu();
+		try {
+			$storageList = $this->getStorageList();
+			if (empty($storageList)) throw new Exception("No storage defined");
+			$storageMenu = array();
+			foreach ($storageList as $storage) {
+				$menuItem = array();
+				$menuItem['id'] = $id = $storage['ID'];
+				$menuItem['nazwa'] = $storage['DESC'];
+				$menuItem['typ'] = $storage['TYPE'];
+				$menuItem['tabele i widoki'] = '<a href="index.php?_route=Storage&_task=tableList&idStorage=' . $id . '">' . "tabele" . '</a>';
+				//$menuItem['views'] = '<a href="index.php?_route=Storage&_task=viewList&idStorage=' . $id . '">' . "views" . '</a>';
+				$menuItem['raw info'] = '<a href="index.php?_route=Storage&_task=rawInfo&idStorage=' . $id . '">' . "raw info" . '</a>';
+				$menuItem['xsd'] = '<a href="index.php?_route=Storage&_task=xsd&idStorage=' . $id . '" target="_blank">' . "xsd" . '</a>';
+				$storageMenu[] = $menuItem;
+			}
+			$this->navView();
+			DBG::table("storageMenu", $storageMenu, __CLASS__, __FUNCTION__, __LINE__);
+		} catch (Exception $e) {
+			SE_Layout::alert('danger', "Error #" . $e->getCode() .  "|" . $e->getLine() .  ": " . $e->getMessage());
+		}
+		SE_Layout::dol();
+	}
+
+	public function tableListAction() {
+		SE_Layout::gora();
+		SE_Layout::menu();
+		try {
+			$idStorage = V::get('idStorage', 0, $_REQUEST, 'int');
+			if (empty($idStorage)) throw new Exception("No id storage");
+			$storageList = $this->getStorageList();
+			if (empty($storageList)) throw new Exception("No storage defined");
+			if (!array_key_exists($idStorage, $storageList)) throw new Exception("No id storage not exists");
+
+			$this->navView();
+			$storagePdo = DB::getStorage($idStorage);
+			$viewRealList = $storagePdo->getViewList();
+			$tableRealList = $storagePdo->getTableList();
+			$tableZasobList = array();
+			foreach (DB::getPDO()->fetchAllByKey("
+					select z.ID, z.`DESC`, z.A_STATUS
+					from CRM_LISTA_ZASOBOW z
+					where z.PARENT_ID = '{$idStorage}'
+				", $key = 'DESC') as $ind => $row) {
+				$tableZasobList[strtolower($ind)] = $row;
+			}
+
+			$emptyItem = array();
+			$emptyItem['nazwa'] = '';//->urlTblStruct = $this->buildActionUrl('tableStruct', array('storageId'=>$storageId, 'tblName'=>$tblName));
+			$emptyItem['type'] = '';
+			$emptyItem['dodaj zasób'] = '';//->urlTblAddToZasoby = $this->buildAjaxActionUrl('addTableToZasoby', $urlParams);
+			$emptyItem['struktura'] = '';//->urlTblRawStruct = $this->buildActionUrl('tableStructRaw', array('storageId'=>$storageId, 'tblName'=>$tblName));
+			$emptyItem['objectTest'] = '';//->urlObjectTest = $this->buildActionUrl('objectTest', array('storageId'=>$storageId, 'tblName'=>$tblName));
+			$emptyItem['xsd'] = '';//->urlTableXsd = $this->buildActionUrl('tableXsd', array('storageId'=>$storageId, 'tblName'=>$tblName, 'HEADER_NOT_INIT'=>'YES'));
+			$emptyItem['id_zasob'] = '';//->zasobId = $zasobTableId;
+			$emptyItem['isHist'] = '';//->isHistTable = ('_HIST' == substr($tblName, -5)) ? true : false;
+			$emptyItem['uwagi'] = '';
+
+			$tableList = array();
+			foreach ($tableRealList as $row) {
+				$tblName = $row['table_name'];
+				$tblItem = V::cloneArray($emptyItem);
+				$tblItem['nazwa'] = $tblName;
+				$tblItem['type'] = $row['table_type'];
+				$tblItem['struktura'] = '<a href="index.php?_route=Storage&_task=tableStruct&idStorage=' . $idStorage . '&table=' . $tblName . '">' . "struct" . '</a>';
+				$tblItem['xsd'] = '<a href="index.php?_route=Storage&_task=tableXsd&idStorage=' . $idStorage . '&table=' . $tblName . '" target="_blank">' . "xsd" . '</a>';
+				$tblItem['objectTest'] = '<a href="index.php?_route=Storage&_task=objectTest&idStorage=' . $idStorage . '&table=' . $tblName . '">' . "objectTest" . '</a>';
+				$tblItem['isHist'] = ('_hist' == substr($tblName, -5));
+				$tblItem['uwagi'] = '';
+				$tblZasob = V::get($tblName, '', $tableZasobList);
+				if ($tblZasob) {
+					$tableZasobList[$tblName]['_checked'] = true;
+					$tblItem['id_zasob'] = $tblZasob['ID'];
+				} else {
+					$tblItem['uwagi'] .= 'TODO: ADD ZASOB';
+					$tblItem['dodaj zasób'] = '<a href="#">TODO: ADD ZASOB</a>';
+				}
+				$tableList[] = $tblItem;
+			}
+			foreach ($viewRealList as $row) {
+				$tblName = $row['table_name'];
+				$tblItem = V::cloneArray($emptyItem);
+				$tblItem['nazwa'] = $tblName;
+				$tblItem['type'] = $row['table_type'];
+				$tblItem['struktura'] = '<a href="index.php?_route=Storage&_task=viewStruct&idStorage=' . $idStorage . '&table=' . $tblName . '">' . "struct" . '</a>';
+				$tblItem['uwagi'] = '';
+				$tblZasob = V::get($tblName, '', $tableZasobList);
+				if ($tblZasob) {
+					$tableZasobList[$tblName]['_checked'] = true;
+					$tblItem['id_zasob'] = $tblZasob['ID'];
+				} else {
+					$tblItem['uwagi'] .= 'TODO: ADD ZASOB';
+					$tblItem['dodaj zasób'] = '<a href="#">TODO: ADD ZASOB</a>';
+				}
+				$tableList[] = $tblItem;
+			}
+			foreach ($tableZasobList as $tblName => $row) {
+				if (!$row['_checked']) {
+					$tblItem = V::cloneArray($emptyItem);
+					$tblItem['nazwa'] = $tblName;
+					$tblItem['id_zasob'] = $row['ID'];
+					$tblItem['type'] = 'unknown';
+					$tblItem['uwagi'] = 'TODO: nie istnieje w bazie danych';
+					$tableList[] = $tblItem;
+				}
+			}
+			DBG::table("tableList", $tableList, __CLASS__, __FUNCTION__, __LINE__);
+		} catch (Exception $e) {
+			SE_Layout::alert('danger', "Error #" . $e->getCode() .  "|" . $e->getLine() .  ": " . $e->getMessage());
+		}
+		SE_Layout::dol();
+	}
+
+	public function rawInfoAction() {
+		SE_Layout::gora();
+		SE_Layout::menu();
+		try {
+			$idStorage = V::get('idStorage', 0, $_REQUEST, 'int');
+			if (empty($idStorage)) throw new Exception("No id storage");
+			$storageList = $this->getStorageList();
+			if (empty($storageList)) throw new Exception("No storage defined");
+			if (!array_key_exists($idStorage, $storageList)) throw new Exception("No id storage not exists");
+
+			$this->navView();
+			$storagePdo = DB::getStorage($idStorage);
+			$rawInfo = $storagePdo->getTableListWithInfo();
+			DBG::table("rawInfo", $rawInfo, __CLASS__, __FUNCTION__, __LINE__);
+		} catch (Exception $e) {
+			SE_Layout::alert('danger', "Error #" . $e->getCode() .  "|" . $e->getLine() .  ": " . $e->getMessage());
+		}
+		SE_Layout::dol();
+	}
+
+	public function tableStructAction() {
+		SE_Layout::gora();
+		SE_Layout::menu();
+		try {
+			$idStorage = V::get('idStorage', 0, $_REQUEST, 'int');
+			if (empty($idStorage)) throw new Exception("No id storage");
+			$storageList = $this->getStorageList();
+			if (empty($storageList)) throw new Exception("No storage defined");
+			if (!array_key_exists($idStorage, $storageList)) throw new Exception("No id storage not exists");
+			$tblName = V::get('table', '', $_REQUEST, 'word');
+			if (empty($tblName)) throw new Exception("No table name");
+
+			$this->navView();
+			$storagePdo = DB::getStorage($idStorage);
+			$tblStruct = $storagePdo->getTableStruct($tblName);
+			DBG::table("tblStruct", $tblStruct, __CLASS__, __FUNCTION__, __LINE__);
+		} catch (Exception $e) {
+			SE_Layout::alert('danger', "Error #" . $e->getCode() .  "|" . $e->getLine() .  ": " . $e->getMessage());
+		}
+		SE_Layout::dol();
+	}
+
+	public function xsdAction() {
+		$idStorage = V::get('idStorage', 0, $_REQUEST, 'int');
+		$storage = DB::getStorage($idStorage);
+		$tableRealList = $storage->getTableList();
+		$srvName = $_SERVER['SERVER_NAME'];
+		$storageZasobId = $storage->getZasobId();
+		$objNs = "p5_{$storageZasobId}_{$tblName}";
+		$objNsUri = "https://biuro.biall-net.pl/api/{$storageZasobId}/{$tblName}";
+		$p5TypePrefix = "p5Type";
+		$p5TypeNsUri = "http://biuro.biall-net.pl/p5/schema/types";
+		$p5TypeNsLocation = "http://biuro.biall-net.pl/p5/schema/types.xsd";
+		header('Content-type: text/plain; charset=utf-8');// TODO: test
+
+		//header('Content-type: application/xml; charset=utf-8');
+		$xmlWriter = new XMLWriter();
+		$xmlWriter->openUri('php://output');
+		$xmlWriter->setIndent(true);
+		if (!$xmlWriter) throw new HttpException("Error no XMLWriter", 404);
+		$xmlWriter->startDocument('1.0', 'UTF-8');
+		$xmlWriter->startElement('xs:schema');
+		$xmlWriter->writeAttribute('xmlns:xs', 'http://www.w3.org/2001/XMLSchema');
+		$xmlWriter->writeAttribute('xmlns:vc', 'http://www.w3.org/2007/XMLSchema-versioning');
+		$xmlWriter->writeAttribute("xmlns:{$p5TypePrefix}", $p5TypeNsUri);
+		$xmlWriter->writeAttribute('elementFormDefault', 'qualified');
+		$xmlWriter->writeAttribute('targetNamespace', $objNsUri);
+		$xmlWriter->writeAttribute('vc:minVersion', '1.1');
+
+		$xmlWriter->startElement('xs:import');
+		$xmlWriter->writeAttribute('namespace', $p5TypeNsUri);
+		$xmlWriter->writeAttribute('schemaLocation', $p5TypeNsLocation);
+		$xmlWriter->endElement();// xs:import
+
+		foreach ($tableRealList as $tbl) {
+			$tblName = $tbl['table_name'];
+			$this->tableXsdViewXmlWriter($xmlWriter, $idStorage, $tblName);
+		}
+
+		$xmlWriter->endElement();// xs:schema
+		$xmlWriter->endDocument();
+	}
+
+	public function tableXsdAction() {
+		$idStorage = V::get('idStorage', '', $_GET);
+		$tblName = V::get('table', '', $_GET, 'word');
+		if (empty($tblName)) die("Wrong table name");
+		header('Content-type: text/plain; charset=utf-8');// TODO: test
+
+		$storage = DB::getStorage($idStorage);
+		$storageZasobId = $storage->getZasobId();
+		$objNs = "p5_{$storageZasobId}_{$tblName}";
+		$objNsUri = "https://biuro.biall-net.pl/api/{$storageZasobId}/{$tblName}";
+		$p5TypePrefix = "p5Type";
+		$p5TypeNsUri = "http://biuro.biall-net.pl/p5/schema/types";
+		$p5TypeNsLocation = "http://biuro.biall-net.pl/p5/schema/types.xsd";
+
+		//header('Content-type: application/xml; charset=utf-8');
+		$xmlWriter = new XMLWriter();
+		$xmlWriter->openUri('php://output');
+		$xmlWriter->setIndent(true);
+		if (!$xmlWriter) throw new HttpException("Error no XMLWriter", 404);
+		$xmlWriter->startDocument('1.0', 'UTF-8');
+		$xmlWriter->startElement('xs:schema');
+		$xmlWriter->writeAttribute('xmlns:xs', 'http://www.w3.org/2001/XMLSchema');
+		$xmlWriter->writeAttribute('xmlns:vc', 'http://www.w3.org/2007/XMLSchema-versioning');
+		$xmlWriter->writeAttribute("xmlns:{$p5TypePrefix}", $p5TypeNsUri);
+		$xmlWriter->writeAttribute('elementFormDefault', 'qualified');
+		$xmlWriter->writeAttribute('targetNamespace', $objNsUri);
+		$xmlWriter->writeAttribute('vc:minVersion', '1.1');
+
+		$xmlWriter->startElement('xs:import');
+		$xmlWriter->writeAttribute('namespace', $p5TypeNsUri);
+		$xmlWriter->writeAttribute('schemaLocation', $p5TypeNsLocation);
+		$xmlWriter->endElement();// xs:import
+		$this->tableXsdViewXmlWriter($xmlWriter, $idStorage, $tblName);
+		$xmlWriter->endElement();// xs:schema
+		$xmlWriter->endDocument();
+	}
+
+	public function tableXsdViewXmlWriter(&$xmlWriter, $idStorage, $tblName) {
+		$storage = DB::getStorage($idStorage);
+		$schema = Schema_TableFactory::build($tblName, $idStorage, $_SERVER['SERVER_NAME']);
+		$struct = $schema->getStruct();
+		DBG::_('DBG', '>1', "struct", $struct, __CLASS__, __FUNCTION__, __LINE__);
+
+		$typeName = "{$tblName}Type";
+
+		$xmlWriter->startElement('xs:complexType');
+		$xmlWriter->writeAttribute('name', $typeName);
+		$xmlWriter->startElement('xs:sequence');
+		foreach ($struct as $field) {
+			$xmlWriter->startElement('xs:element');
+			$xmlWriter->writeAttribute('name', $field['name']);
+			$xmlWriter->writeAttribute('minOccurs', 0);// TODO: set minOccurs by default, etc.
+			if ($field['is_nullable']) $xmlWriter->writeAttribute('nillable', 'true');
+			if (null !== $field['default_value']) {
+				$xmlWriter->writeAttribute('default', $field['default_value']);
+			} else if (null === $field['default_value'] && $field['is_nullable']) {
+				$xmlWriter->writeAttribute('default', $field['default_value']);
+			} else {
+				// TODO: Schema BUG?
+			}
+
+			if (empty($field['p5_restrictions'])) {
+				$xmlWriter->writeAttribute('type', "p5Type:{$field['p5_type']}");
+			} else {
+				$xmlWriter->startElement('xs:simpleType');
+				$xmlWriter->writeAttribute('base', "p5Type:{$field['p5_type']}");
+				$xmlWriter->startElement('xs:restriction');
+				if (!empty($field['p5_restrictions']['enumeration'])) {
+					foreach ($field['p5_restrictions']['enumeration'] as $enumValue) {
+						$xmlWriter->startElement('xs:enumeration');
+						$xmlWriter->writeAttribute('value', $enumValue);
+						$xmlWriter->endElement();// xs:enumeration
+					}
+				} else {
+					// TODO: another restrictions...
+				}
+				$xmlWriter->endElement();// xs:restriction
+				$xmlWriter->endElement();// xs:simpleType
+			}
+			$xmlWriter->endElement();// xs:element
+		}
+		$xmlWriter->endElement();// xs:sequence
+		$xmlWriter->endElement();// xs:complexType
+		return;
+
+		// <xs:element maxOccurs="1" minOccurs="0" name="{$fldName}" nillable="true" type="xs:integer"/>
+		$pKeyField = 'ID';//$storageObject->getPrimaryKeyFieldName();
+		//DBG::_(true, true, "struct", $struct, __CLASS__, __FUNCTION__, __LINE__);
+		foreach ($struct as $field) {
+			$fldName = $vField->getName();
+			$fldType = $vField->getType();
+			$xsdType = $fldType->getTypeForXsd();
+
+			if ($fldType->hasDefault()) {
+				$fldDefault = $fldType->getDefault();
+				if (!empty($fldDefault) || '0' === $fldDefault) {
+					$elNode->setAttribute('default', $fldDefault);
+				}
+			}
+
+			$fldRestrictions = $fldType->getRestrictions();
+			if (empty($fldRestrictions)) {
+				$elNode->setAttribute('type', "{$p5TypePrefix}:{$xsdType}");
+			} else {
+				$sType = $dom->createElementNS('http://www.w3.org/2001/XMLSchema', 'xs:simpleType');
+				$elNode->appendChild($sType);
+				$sTypeRes = $dom->createElementNS('http://www.w3.org/2001/XMLSchema', 'xs:restriction');
+				$sType->appendChild($sTypeRes);
+				$sTypeRes->setAttribute('base', "{$p5TypePrefix}:{$xsdType}");
+
+				$enumList = $fldType->getEnumeration();
+				if (empty($enumList)) {
+					foreach ($fldRestrictions as $restricionName => $restrictionValue) {
+						if ('maxLength' == $restricionName) {
+							$sTypeResMaxLength = $dom->createElementNS('http://www.w3.org/2001/XMLSchema', 'xs:maxLength');
+							$sTypeRes->appendChild($sTypeResMaxLength);
+							$sTypeResMaxLength->setAttribute('value', $restrictionValue);
+						} else if ('minLength' == $restricionName) {
+							$sTypeResMinLength = $dom->createElementNS('http://www.w3.org/2001/XMLSchema', 'xs:minLength');
+							$sTypeRes->appendChild($sTypeResMinLength);
+							$sTypeResMinLength->setAttribute('value', $restrictionValue);
+						} else if ('pattern' == $restricionName) {
+							$sTypeResPattern = $dom->createElementNS('http://www.w3.org/2001/XMLSchema', 'xs:pattern');
+							$sTypeRes->appendChild($sTypeResPattern);
+							$sTypeResPattern->setAttribute('value', $restrictionValue);
+						} else if ('fractionDigits' == $restricionName) {
+							$sTypeResFractionDigits = $dom->createElementNS('http://www.w3.org/2001/XMLSchema', 'xs:fractionDigits');
+							$sTypeRes->appendChild($sTypeResFractionDigits);
+							$sTypeResFractionDigits->setAttribute('value', $restrictionValue);
+						} else if ('totalDigits' == $restricionName) {
+							$sTypeResTotalDigits = $dom->createElementNS('http://www.w3.org/2001/XMLSchema', 'xs:totalDigits');
+							$sTypeRes->appendChild($sTypeResTotalDigits);
+							$sTypeResTotalDigits->setAttribute('value', $restrictionValue);
+						} else if ('maxExclusive' == $restricionName) {
+							$sTypeResMaxExclusive = $dom->createElementNS('http://www.w3.org/2001/XMLSchema', 'xs:maxExclusive');
+							$sTypeRes->appendChild($sTypeResMaxExclusive);
+							$sTypeResMaxExclusive->setAttribute('value', $restrictionValue);
+						} else if ('minExclusive' == $restricionName) {
+							$sTypeResMinExclusive = $dom->createElementNS('http://www.w3.org/2001/XMLSchema', 'xs:minExclusive');
+							$sTypeRes->appendChild($sTypeResMinExclusive);
+							$sTypeResMinExclusive->setAttribute('value', $restrictionValue);
+						} else if ('maxInclusive' == $restricionName) {
+							$sTypeResMaxInclusive = $dom->createElementNS('http://www.w3.org/2001/XMLSchema', 'xs:maxInclusive');
+							$sTypeRes->appendChild($sTypeResMaxInclusive);
+							$sTypeResMaxInclusive->setAttribute('value', $restrictionValue);
+						} else if ('minInclusive' == $restricionName) {
+							$sTypeResMinInclusive = $dom->createElementNS('http://www.w3.org/2001/XMLSchema', 'xs:minInclusive');
+							$sTypeRes->appendChild($sTypeResMinInclusive);
+							$sTypeResMinInclusive->setAttribute('value', $restrictionValue);
+						}
+						/* TODO: xsd restrictions:
+	enumeration	Defines a list of acceptable values
+	fractionDigits	Specifies the maximum number of decimal places allowed. Must be equal to or greater than zero
+	length	Specifies the exact number of characters or list items allowed. Must be equal to or greater than zero
+	maxExclusive	Specifies the upper bounds for numeric values (the value must be less than this value)
+	maxInclusive	Specifies the upper bounds for numeric values (the value must be less than or equal to this value)
+	maxLength	Specifies the maximum number of characters or list items allowed. Must be equal to or greater than zero
+	minExclusive	Specifies the lower bounds for numeric values (the value must be greater than this value)
+	minInclusive	Specifies the lower bounds for numeric values (the value must be greater than or equal to this value)
+	minLength	Specifies the minimum number of characters or list items allowed. Must be equal to or greater than zero
+	pattern	Defines the exact sequence of characters that are acceptable
+	totalDigits	Specifies the exact number of digits allowed. Must be greater than zero
+	whiteSpace	Specifies how white space (line feeds, tabs, spaces, and carriage returns) is handled
+						*/
+					}
+				} else {
+					foreach ($enumList as $enumValue) {
+						$sTypeResEnum = $dom->createElementNS('http://www.w3.org/2001/XMLSchema', 'xs:enumeration');
+						$sTypeRes->appendChild($sTypeResEnum);
+						$sTypeResEnum->setAttribute('value', $enumValue);
+					}
+				}
+			}
+		}
+
+		$elNode = $dom->createElementNS('http://www.w3.org/2001/XMLSchema', 'xs:element');
+		$rootNode->appendChild($elNode);
+		$elNode->setAttribute('name', $tblName);
+		$elNode->setAttribute('type', "{$objNs}:{$typeName}");
+
+		header('Content-type: application/xml');
+		echo $dom->saveXML();
+		exit;
+	}
+
+	public function navView() {
+		$backLabel = 'back';
+		$backLink = 'index.php?_route=Storage';
+		$backDisabled = true;
+		$currentLabel = 'Storage';
+		$currentLink = 'index.php?_route=Storage';
+		if ($task = V::get('_task', '', $_REQUEST)) {
+			$currentLink = "index.php?_route=Storage&_task={$task}";
+			$backDisabled = false;
+			$idStorage = V::get('idStorage', 0, $_REQUEST, 'int');
+			$tblName = V::get('table', '', $_REQUEST, 'word');
+			switch ($task) {
+				case 'tableList':
+				case 'viewList':
+				case 'rawInfo':
+					$backLabel = 'Storage';
+					$backLink = 'index.php?_route=Storage';
+					$currentLink = "index.php?_route=Storage&_task={$task}&idStorage={$idStorage}";
+					break;
+				case 'tableStruct':
+					$backLabel = "Tabele [{$idStorage}]";
+					$backLink = "index.php?_route=Storage&_task=tableList&idStorage={$idStorage}";
+					$currentLink = "index.php?_route=Storage&_task={$task}&idStorage={$idStorage}&table={$tblName}";
+					break;
+			}
+			switch ($task) {
+				case 'tableList': $currentLabel = "Tabele [{$idStorage}]"; break;
+				case 'viewList':  $currentLabel = "Widoki [{$idStorage}]"; break;
+				case 'rawInfo':   $currentLabel = "Raw info [{$idStorage}]"; break;
+				case 'tableStruct': $currentLabel = "Struktura tabeli '{$tblName}'"; break;
+			}
+		}
+		?>
+<nav class="navbar navbar-default navbar-static-top">
+  <div class="container-fluid">
+    <div class="navbar-left">
+			<ul class="nav navbar-nav navbar-center">
+				<li><a href="<?php echo $backLink; ?>" class="btn <?php echo ($backDisabled)? 'disabled' : ''; ?>"><i class="glyphicon glyphicon-chevron-left"></i> <?php echo $backLabel; ?></a></li>
+			</ul>
+		</div>
+    <div class="navbar-left">
+			<ul class="nav navbar-nav navbar-center">
+				<li><a class="btn" href="<?php echo $currentLink; ?>"><?php echo $currentLabel; ?></a></li>
+			</ul>
+		</div>
+    <div class="navbar-right">
+<!--
+			<ul class="nav navbar-nav navbar-right">
+				<li><a href="#">Link</a></li>
+				<li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Dropdown <span class="caret"></span></a>
+					<ul class="dropdown-menu">
+						<li><a href="#">Action</a></li>
+						<li><a href="#">Another action</a></li>
+						<li><a href="#">Something else here</a></li>
+						<li role="separator" class="divider"></li>
+						<li><a href="#">Separated link</a></li>
+					</ul>
+				</li>
+			</ul>
+-->
+		</div>
+  </div>
+</nav>
+		<?php
+	}
+
+	public function getStorageList() {
+		$storageList = array();
+		$sth = DB::getPDO()->prepare("
+			select z.ID, z.`DESC`, z.`TYPE`
+			from CRM_LISTA_ZASOBOW z
+			where z.TYPE in('BAZA_DANYCH','DATABASE_MYSQL','DATABASE_POSTGRESQL')
+		");
+		$sth->execute();
+		$rows = $sth->fetchAll();
+		foreach ($rows as $row) {
+			$storageList[$row['ID']] = $row;
+		}
+		return $storageList;
+	}
+
+}

+ 141 - 0
SE/se-lib/Schema/TableBase.php

@@ -35,4 +35,145 @@ class Schema_TableBase {
 		return $types;
 	}
 
+	public function getStruct() {
+		if (!empty($this->_struct)) return $this->_struct;
+		$storage = DB::getStorage($this->idDatabase);
+		$struct = $storage->getTableStruct($this->tblName);
+		$this->_struct = $struct;
+		foreach ($this->_struct as $fldName => $field) {
+			$this->_struct[$fldName] = $this->convertStorageTypeToXsdP5Type($storage->getType(), $field);
+		}
+		return $this->_struct;
+	}
+
+	public function convertStorageTypeToXsdP5Type($storageType, $field) {
+		/* cat SE/schema/types.xsd |grep 'xs:simpleType name='
+			<xs:simpleType name="string">
+			<xs:simpleType name="text">
+			<xs:simpleType name="enumeration">
+			<xs:simpleType name="set">
+			<xs:simpleType name="integer">
+			<xs:simpleType name="decimal">
+			<xs:simpleType name="double">
+			<xs:simpleType name="float">
+			<xs:simpleType name="binary">
+			<xs:simpleType name="hexBinary">
+			<xs:simpleType name="date">
+				<xs:simpleType name="dateZero">
+			<xs:simpleType name="dateTime">
+				<xs:simpleType name="dateTimeZero">
+			<xs:simpleType name="time">
+				<xs:simpleType name="timeZero">
+			<xs:simpleType name="year">
+				<xs:simpleType name="yearZero">
+			<xs:simpleType name="geometry">
+			<xs:simpleType name="polygon">
+			<xs:simpleType name="point">
+			<xs:simpleType name="lineString">
+		*/
+		$field['p5_isComplexType'] = false;
+		$field['p5_type'] = 'unknown';// TODO: default type or leave undefined?
+		$field['p5_restrictions'] = array();
+		$field['p5_props'] = array();
+		if ('mysql' == $storageType) {
+			switch ($field['type']) {
+				case 'tinyint':
+				case 'smallint':
+				case 'bigint':
+				case 'int': {
+						$field['p5_type'] = 'integer';
+					} break;
+				case 'decimal': {
+						$field['p5_type'] = $field['type'];
+					} break;
+				case 'float':
+				case 'double': {
+						$field['p5_type'] = $field['type'];
+					} break;
+				case 'char':
+				case 'varchar': {
+						$field['p5_type'] = 'string';
+					} break;
+				case 'mediumtext':
+				case 'longtext':
+				case 'text': {
+						$field['p5_type'] = $field['type'];
+					} break;
+				case 'datetime': {
+						$field['p5_type'] = 'dateTime';
+					} break;
+				case 'date': {
+						$field['p5_type'] = $field['type'];
+					} break;
+				case 'year': {
+						$field['p5_type'] = $field['type'];
+					} break;
+				case 'time': {
+						$field['p5_type'] = $field['type'];
+					} break;
+				case 'timestamp': {
+						$field['p5_type'] = 'integer';
+					} break;
+				case 'point':
+				case 'polygon':
+				case 'geometry': {
+						$field['p5_type'] = $field['type'];
+					} break;
+				case 'linestring': {
+						$field['p5_type'] = 'lineString';
+					} break;
+				case 'multipoint': {
+						$field['p5_type'] = 'multiPoint';// TODO: create multiPoint in types.xsd
+					} break;
+				case 'multipolygon': {
+						$field['p5_type'] = 'multiPolygon';// TODO: create multiPolygon in types.xsd
+					} break;
+				case 'multilinestring': {
+						$field['p5_type'] = 'multiLineString';// TODO: create multiLineString in types.xsd
+					} break;
+				case 'enum': {
+						$field['p5_type'] = 'string';
+						$enumValues = substr($field['raw_storage_type'], 6, -2);// "enum('R','W','X','C')"
+						$enumValues = explode("','", $enumValues);
+						$field['p5_restrictions']['enumeration'] = $enumValues;
+					} break;
+				case 'set': {
+						$field['p5_type'] = 'set';
+						$enumValues = substr($field['raw_storage_type'], 5, -2);// "set('R','W','X','C')"
+						$enumValues = explode("','", $enumValues);
+						// TODO: enumerations with all combinations
+						// $baseComb = array(); foreach ($enumValues as $enumValue) { if ('' !== $enumValue) $baseComb[] = $enumValue; };
+						// $sizeCombinations = count($baseComb);
+						// $allComb = pow(2, $sizeCombinations);
+						// //DBG::_(true, true, "baseCombinations", $baseComb, __CLASS__, __FUNCTION__, __LINE__);
+						// for ($b = 1; $b < $allComb; $b++) {
+						// 	$curValues = array();
+						// 	$valBinary = sprintf("%0{$sizeCombinations}b", $b);
+						// 	$binValues = str_split($valBinary);
+						// 	//DBG::_(true, true, "curValues({$b}/{$valBinary})", $curValues, __CLASS__, __FUNCTION__, __LINE__);
+						// 	foreach ($binValues as $idx => $isSet) {
+						// 		if ($isSet) $curValues[] = $baseComb[$idx];
+						// 	}
+						// 	$enumValues[] = implode(",", $curValues);
+						// }
+						//$field['p5_restrictions']['enumeration'] = $enumValues;// TODO: use p5_props
+						$field['p5_props']['multiple_enumeration'] = $enumValues;// TODO: use p5_props
+					} break;
+				case 'tinyblob':
+				case 'mediumblob':
+				case 'longblob':
+				case 'blob':
+				case 'binary':
+				case 'varbinary': {
+						$field['p5_type'] = 'hexBinary';
+					} break;
+				default:
+					DBG::_(true, true, "TODO: storageType({$storageType})  field({$field['name']})", $field, __CLASS__, __FUNCTION__, __LINE__);
+			}
+		} else if ('pgsql' == $storageType) {
+		} else {
+		}
+		return $field;
+	}
+
 }

+ 2 - 6
SE/se-lib/TableAcl.php

@@ -931,12 +931,8 @@ class TableAcl {
 	public function loadSchema() {
 		if ($this->_schemaLoaded) return;
 		$srvName = $_SERVER['SERVER_NAME'];
-		try {
-			Lib::loadClass('Schema_TableFactory');
-			$this->_schemaClass = Schema_TableFactory::build($this->_name, $this->_db, $srvName);
-		} catch (Exception $e) {
-			throw $e;
-		}
+		Lib::loadClass('Schema_TableFactory');
+		$this->_schemaClass = Schema_TableFactory::build($this->_name, $this->_db, $srvName);
 		$this->_schemaLoaded = true;
 	}