first commit
This commit is contained in:
@@ -0,0 +1,4 @@
|
|||||||
|
|
||||||
|
/release
|
||||||
|
/release.zip
|
||||||
|
/dbadmin.n
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
Haxe DB Admin
|
||||||
|
=============
|
||||||
|
|
||||||
|
Database Admin interface for Haxe Records
|
||||||
|
|
||||||
|
* list all tables and enable to browse / search / edit / insert / delete
|
||||||
|
* view haxe and neko serialized data in clear and allow edit with -lib hscript
|
||||||
|
* allow to sync changes made in database schema : fields added/removed/changed type/renamed
|
||||||
|
* only tested with Neko/MySQL and PHP/MySQL
|
||||||
|
|
||||||
|
How to setup :
|
||||||
|
|
||||||
|
* map sys.db.Admin.handler() to "/db" URL on your website
|
||||||
|
* make sure it's only accessible by power users
|
||||||
|
* for best usage copy `db.css` from dbadmin directory to your website /css directory
|
||||||
|
* you can also use sys.db.Admin.initializeDatabase() which replaces sys.db.TableCreate
|
||||||
|
* enjoy !
|
||||||
|
|
||||||
@@ -0,0 +1,167 @@
|
|||||||
|
body, table {
|
||||||
|
margin : 0px;
|
||||||
|
font-family : "Trebuchet MS";
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main {
|
||||||
|
padding : 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
color : rgb(244, 136, 33);
|
||||||
|
padding : 5px;
|
||||||
|
background-color : black;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color : #b32;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
input,
|
||||||
|
textarea,
|
||||||
|
select,
|
||||||
|
input.dfile {
|
||||||
|
border : 1px solid #bbb;
|
||||||
|
padding:4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input.dint,
|
||||||
|
input.duint,
|
||||||
|
input.ddate,
|
||||||
|
input.dfloat {
|
||||||
|
width : 80px;
|
||||||
|
}
|
||||||
|
|
||||||
|
img.dfile {
|
||||||
|
position : absolute;
|
||||||
|
left : 800px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input.button {
|
||||||
|
color : white;
|
||||||
|
font-weight : bold;
|
||||||
|
min-width : 100px;
|
||||||
|
background-color : #b32;
|
||||||
|
cursor : pointer;
|
||||||
|
border : 0px;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input.ddatetime {
|
||||||
|
width : 120px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input.dstring {
|
||||||
|
width : 400px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input.dtinytext {
|
||||||
|
width : 700px;
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea.dtext {
|
||||||
|
width : 700px;
|
||||||
|
height : 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input:focus,
|
||||||
|
textarea:focus,
|
||||||
|
select:focus {
|
||||||
|
background-color : #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
width : 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input.button:hover {
|
||||||
|
background-color : rgb(244, 136, 33);
|
||||||
|
}
|
||||||
|
|
||||||
|
input.button:focus {
|
||||||
|
background-color : rgb(244, 136, 33);
|
||||||
|
}
|
||||||
|
|
||||||
|
.derror {
|
||||||
|
text-align : center;
|
||||||
|
background-color : #fee;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.results {
|
||||||
|
border-collapse : collapse;
|
||||||
|
border-spacing : 0px;
|
||||||
|
/*font-size : 90%;*/
|
||||||
|
white-space : nowrap;
|
||||||
|
margin-bottom : 20px;
|
||||||
|
td {
|
||||||
|
padding : 1px;
|
||||||
|
border-left : 1px solid #bbb;
|
||||||
|
border-right : 1px solid #bbb;
|
||||||
|
}
|
||||||
|
tr.header {
|
||||||
|
background-color : #b32;
|
||||||
|
color : white;
|
||||||
|
a {
|
||||||
|
color : white;
|
||||||
|
text-decoration : underline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tr {
|
||||||
|
background-color : white;
|
||||||
|
}
|
||||||
|
tr.odd {
|
||||||
|
background-color : #eee;
|
||||||
|
}
|
||||||
|
tr:hover td {
|
||||||
|
background-color : #fee;
|
||||||
|
}
|
||||||
|
tr.odd:hover td {
|
||||||
|
background-color : #ffd7d7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
div.links {
|
||||||
|
position : absolute;
|
||||||
|
width : 100%;
|
||||||
|
height : 22px;
|
||||||
|
top : 40px;
|
||||||
|
left : 0px;
|
||||||
|
color : #e82;
|
||||||
|
background-color : #e82;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.links a:first-child {
|
||||||
|
margin-left : 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.links a {
|
||||||
|
padding-left : 8px;
|
||||||
|
padding-right : 8px;
|
||||||
|
|
||||||
|
|
||||||
|
font-weight : bold;
|
||||||
|
line-height : 22px;
|
||||||
|
color : white;
|
||||||
|
text-decoration : none;
|
||||||
|
}
|
||||||
|
|
||||||
|
table{
|
||||||
|
border-collapse:collapse;
|
||||||
|
border-spacing:0;
|
||||||
|
|
||||||
|
}
|
||||||
|
table td,table th{
|
||||||
|
padding:8px;
|
||||||
|
}
|
||||||
|
table td{
|
||||||
|
border-top: 1px solid #EEE;
|
||||||
|
}
|
||||||
|
table th{
|
||||||
|
background-color:#EEE;
|
||||||
|
font-weight:bold;
|
||||||
|
color:#666;
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
-neko dbadmin.n
|
||||||
|
-D haxe3
|
||||||
|
-lib hscript
|
||||||
|
sys.db.Admin
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"name": "dbadmin",
|
||||||
|
"url": "http://github.com/ncannasse/dbadmin",
|
||||||
|
"license": "MIT",
|
||||||
|
"description": "Database Admin interface for Haxe DB Records",
|
||||||
|
"version": "1.1.0",
|
||||||
|
"releasenote": "Haxe 3 update",
|
||||||
|
"contributors": ["ncannasse"]
|
||||||
|
}
|
||||||
+1161
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,373 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c)2012 Nicolas Cannasse
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
* a copy of this software and associated documentation files (the
|
||||||
|
* "Software"), to deal in the Software without restriction, including
|
||||||
|
* without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
* permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
* the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be
|
||||||
|
* included in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
package sys.db;
|
||||||
|
|
||||||
|
import haxe.macro.Context;
|
||||||
|
#if !macro
|
||||||
|
import sys.db.TableInfos.TableType;
|
||||||
|
#end
|
||||||
|
|
||||||
|
#if neko
|
||||||
|
import neko.Lib;
|
||||||
|
import neko.Web;
|
||||||
|
#elseif php
|
||||||
|
import php.Lib;
|
||||||
|
import php.Web;
|
||||||
|
#end
|
||||||
|
|
||||||
|
class MacroHelper {
|
||||||
|
|
||||||
|
public macro static function importFile( file : String ) {
|
||||||
|
var data = try sys.io.File.getContent(Context.resolvePath(file)) catch( e : Dynamic ) null;
|
||||||
|
return Context.makeExpr(data,Context.currentPos());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !macro
|
||||||
|
|
||||||
|
class AdminStyle {
|
||||||
|
|
||||||
|
public static var BASE_URL = "/db/";
|
||||||
|
public static var CSS = {
|
||||||
|
var file = MacroHelper.importFile("db.css");
|
||||||
|
if( file == null )
|
||||||
|
null
|
||||||
|
else
|
||||||
|
'<style type="text/css">'+file+'</style>';
|
||||||
|
}
|
||||||
|
public static var HTML_BOTTOM = "";
|
||||||
|
|
||||||
|
var isNull : Bool;
|
||||||
|
var value : String;
|
||||||
|
var isHeader : Bool;
|
||||||
|
var table : TableInfos;
|
||||||
|
|
||||||
|
public function new(t) {
|
||||||
|
this.table = t;
|
||||||
|
}
|
||||||
|
|
||||||
|
function out(str : String,?params : Dynamic) {
|
||||||
|
if( params != null ) {
|
||||||
|
for( x in Reflect.fields(params) )
|
||||||
|
str = str.split("@"+x).join(Reflect.field(params,x));
|
||||||
|
}
|
||||||
|
Sys.println(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function text(str,?title) {
|
||||||
|
str = StringTools.htmlEscape(str);
|
||||||
|
if( title != null ) str = '<span title="'+StringTools.htmlEscape(title)+'">'+str+'</span>';
|
||||||
|
out(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function begin( title ) {
|
||||||
|
out('<html><head><title>@title</title>',{ title: title });
|
||||||
|
if( CSS != null )
|
||||||
|
out(CSS);
|
||||||
|
out('<meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>');
|
||||||
|
out('
|
||||||
|
<script lang="text/javascript">
|
||||||
|
function updateLink(name,url,value) {
|
||||||
|
document.getElementById(name+"__goto").href = (value == "")?"#":("@base" + url + value);
|
||||||
|
}
|
||||||
|
function updateImage(name,url,value) {
|
||||||
|
updateLink(name,url,value);
|
||||||
|
document.getElementById(name+"__img").src = "'+getFileURL('::f::')+'".split("::f::").join(value);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
',{ base : BASE_URL });
|
||||||
|
out('</head><body>');
|
||||||
|
out('<h1>@title</h1><div class="main">',{ title : title });
|
||||||
|
}
|
||||||
|
|
||||||
|
public function end() {
|
||||||
|
out('<div class="links">');
|
||||||
|
out('<a href="/">Exit</a> | <a href="@url">Database</a>',{ url : BASE_URL });
|
||||||
|
if( table != null )
|
||||||
|
out('| <a href="@url@table/search">Search</a>',{ url : BASE_URL, table : table.className });
|
||||||
|
if( table != null )
|
||||||
|
out('| <a href="@url@table/insert">Insert</a>',{ url : BASE_URL, table : table.className });
|
||||||
|
out('</div></div>');
|
||||||
|
out(HTML_BOTTOM);
|
||||||
|
out('</body></html>');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function beginList() {
|
||||||
|
out("<ul>");
|
||||||
|
}
|
||||||
|
|
||||||
|
public function endList() {
|
||||||
|
out("</ul>");
|
||||||
|
}
|
||||||
|
|
||||||
|
public function beginItem() {
|
||||||
|
out("<li>");
|
||||||
|
}
|
||||||
|
|
||||||
|
public function endItem() {
|
||||||
|
out("</li>");
|
||||||
|
}
|
||||||
|
|
||||||
|
public function goto(url) {
|
||||||
|
Web.redirect(BASE_URL+url);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function link( url, name ) {
|
||||||
|
out('<a href="@url">@name</a>',{ url : BASE_URL+url, name : name });
|
||||||
|
}
|
||||||
|
|
||||||
|
public function linkConfirm( url, name ) {
|
||||||
|
out('<a href="@url" onclick="return confirm(\'Please confirm this action\')">@name</a>',{ url : BASE_URL+url, name : name });
|
||||||
|
}
|
||||||
|
|
||||||
|
public function beginForm(url,?file,?id) {
|
||||||
|
out('<form id="@id" action="@url" method="POST"@enc>',{ id:id, url : BASE_URL+url, enc : if( file ) ' enctype="multipart/form-data"' else "" });
|
||||||
|
beginTable();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function endForm() {
|
||||||
|
endTable();
|
||||||
|
out('</form>');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function beginTable( ?css ) {
|
||||||
|
if( css != null )
|
||||||
|
out('<table class="@css">',{ css : css });
|
||||||
|
else
|
||||||
|
out('<table>');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function endTable() {
|
||||||
|
out('</table>');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function beginLine( ?isHeader, ?css ) {
|
||||||
|
var str = '<tr';
|
||||||
|
if( css != null )
|
||||||
|
str += ' class="'+css+'"';
|
||||||
|
str += '>';
|
||||||
|
str += if( isHeader ) '<th>' else '<td>';
|
||||||
|
out(str);
|
||||||
|
this.isHeader = isHeader;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function nextRow( ?isHeader ) {
|
||||||
|
out((if( this.isHeader ) '</th>' else '</td>')+(if( isHeader ) '<th>' else '<td>'));
|
||||||
|
this.isHeader = isHeader;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function endLine() {
|
||||||
|
out((if( this.isHeader ) '</th>' else '</td>')+'</tr>');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addSubmit( name, ?url, ?confirm, ?iname ) {
|
||||||
|
beginLine();
|
||||||
|
nextRow();
|
||||||
|
out('<input type="submit" class="button" value="@name"',{ name : name });
|
||||||
|
if( iname != null )
|
||||||
|
out(' name="@name"',{ name : iname });
|
||||||
|
if( url != null ) {
|
||||||
|
var conf = if( confirm ) "if( confirm('Please confirm this action') )" else "";
|
||||||
|
out(' onclick="@conf document.location = \'@url\'; return false"', { conf : conf, url : BASE_URL + url });
|
||||||
|
} else if( confirm )
|
||||||
|
out(' onclick="return confirm(\'Please confirm this action\');"');
|
||||||
|
out('/>');
|
||||||
|
endLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function checkBox(name,checked) {
|
||||||
|
out('<input name="@name" type="checkbox" class="dcheck"',{ name : name });
|
||||||
|
if( checked )
|
||||||
|
out(' checked="1"');
|
||||||
|
out('/>');
|
||||||
|
}
|
||||||
|
|
||||||
|
function input(name,css,?options : Dynamic) {
|
||||||
|
if( options == null )
|
||||||
|
options = {};
|
||||||
|
beginLine(true);
|
||||||
|
out(name);
|
||||||
|
nextRow();
|
||||||
|
if( isNull )
|
||||||
|
checkBox(name+"__data",value != null);
|
||||||
|
out('<input name="@name" class="@css"',{ name : name, css : css });
|
||||||
|
if( options.size != null )
|
||||||
|
out(' maxlength="@size"',options);
|
||||||
|
if( options.isCheck )
|
||||||
|
out(' type="checkbox"');
|
||||||
|
if( value != null ) {
|
||||||
|
if( options.isCheck ) {
|
||||||
|
if( Std.string(value) != "false" ) out(' checked="1"');
|
||||||
|
} else
|
||||||
|
out(' value="@v"',{ v : Std.string(value).split("\"").join(""") });
|
||||||
|
}
|
||||||
|
out('/>');
|
||||||
|
endLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getFileURL( v : String ) {
|
||||||
|
return "/file/" + v + ".png";
|
||||||
|
}
|
||||||
|
|
||||||
|
function inputText(name, css, ?noWrap ) {
|
||||||
|
beginLine(true);
|
||||||
|
out(name);
|
||||||
|
nextRow();
|
||||||
|
if( isNull )
|
||||||
|
checkBox(name+"__data",value != null);
|
||||||
|
out('<textarea name="@name" class="@css"@noWrap>@value</textarea>',{ noWrap : noWrap?' wrap="off"':'', name : name, css : css, value : if( value != null ) StringTools.htmlEscape(value) else "" });
|
||||||
|
endLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function inputField( name : String, type : TableType, isNull, value ) {
|
||||||
|
this.isNull = isNull;
|
||||||
|
this.value = value;
|
||||||
|
switch( type ) {
|
||||||
|
case DId, DUId, DBigId:
|
||||||
|
infoField(name,if( value == null ) "#ID" else value);
|
||||||
|
case DInt:
|
||||||
|
input(name,"dint",{ size : 10 });
|
||||||
|
case DBigInt:
|
||||||
|
input(name,"dbigint",{ size : 20 });
|
||||||
|
case DUInt:
|
||||||
|
input(name,"duint",{ size : 10 });
|
||||||
|
case DTinyInt:
|
||||||
|
input(name, "dtint", { size : 4 } );
|
||||||
|
case DTinyUInt, DSmallInt, DSmallUInt, DMediumInt, DMediumUInt:
|
||||||
|
input(name, "dint", { size : 10 } );
|
||||||
|
case DFloat, DSingle:
|
||||||
|
input(name,"dfloat",{ size : 10 });
|
||||||
|
case DBool:
|
||||||
|
input(name,"dbool",{ isCheck : true });
|
||||||
|
case DString(n):
|
||||||
|
input(name,"dstring",{ size : n });
|
||||||
|
case DTinyText:
|
||||||
|
input(name,"dtinytext");
|
||||||
|
case DDate:
|
||||||
|
if( value != null )
|
||||||
|
this.value = try value.toString().substr(0,10) catch( e : Dynamic ) "#INVALID";
|
||||||
|
input(name,"ddate",{ size : 10 });
|
||||||
|
case DDateTime, DTimeStamp:
|
||||||
|
if( value != null )
|
||||||
|
this.value = try value.toString() catch( e : Dynamic ) "#INVALID";
|
||||||
|
input(name, "ddatetime", { size : 19 } );
|
||||||
|
case DText, DSmallText:
|
||||||
|
inputText(name, "dtext");
|
||||||
|
case DSerialized, DNekoSerialized:
|
||||||
|
inputText(name, "dtext", true);
|
||||||
|
case DData:
|
||||||
|
inputText(name, "dtext", true);
|
||||||
|
case DEnum(_):
|
||||||
|
// todo : use a select box with possible constructors
|
||||||
|
input(name, "dtint", { size : 4 } );
|
||||||
|
case DEncoded:
|
||||||
|
input(name,"denc",{ size : 6 });
|
||||||
|
case DFlags(fl,_):
|
||||||
|
beginLine(true);
|
||||||
|
out(name);
|
||||||
|
nextRow();
|
||||||
|
if( isNull )
|
||||||
|
checkBox(name+"__data",value != null);
|
||||||
|
var vint = Std.parseInt(value);
|
||||||
|
if( vint == null ) vint = 0;
|
||||||
|
var pos = 0;
|
||||||
|
for( i in 0...fl.length ) {
|
||||||
|
out('<input name="@name" class="@css"',{ name : name + "_" + fl[i], css : "dbool" });
|
||||||
|
out(' type="checkbox"');
|
||||||
|
if( vint & (1 << i) != 0 ) out(' checked="1"');
|
||||||
|
out('/>');
|
||||||
|
out(fl[i]);
|
||||||
|
}
|
||||||
|
endLine();
|
||||||
|
case DBinary, DSmallBinary, DLongBinary, DBytes(_), DNull, DInterval:
|
||||||
|
throw "NotSupported";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function binField( name : String, isNull, value : String, url : Void -> String ) {
|
||||||
|
beginLine(true);
|
||||||
|
out(name);
|
||||||
|
nextRow();
|
||||||
|
if( isNull )
|
||||||
|
checkBox(name+"__data",value != null);
|
||||||
|
if( value != null )
|
||||||
|
text("["+value.length+" bytes]");
|
||||||
|
else if( url != null )
|
||||||
|
text("null");
|
||||||
|
out('<input type="file" class="dfile" name="@name"/>',{ name : name });
|
||||||
|
if( value != null && url != null )
|
||||||
|
link(url(),"download");
|
||||||
|
endLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function infoField( name : String, value ) {
|
||||||
|
beginLine(true);
|
||||||
|
out(name);
|
||||||
|
nextRow();
|
||||||
|
out(value);
|
||||||
|
endLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function choiceField( name : String, values : List<{ id : String, str : String }>, def : String, link, ?disabled : Bool, ?isImage: Bool ) {
|
||||||
|
beginLine(true);
|
||||||
|
out(name);
|
||||||
|
nextRow();
|
||||||
|
var infos = {
|
||||||
|
func : if( isImage ) "updateImage" else "updateLink",
|
||||||
|
name : name,
|
||||||
|
link : link,
|
||||||
|
size : if( values != null && values.length > 15 ) 10 else 1,
|
||||||
|
dis : if( disabled ) 'disabled="yes"' else "",
|
||||||
|
def : if( def == "null" ) "" else def,
|
||||||
|
};
|
||||||
|
if( values == null )
|
||||||
|
out('<input id="@name" name="@name" class="dint" value="@def" @dis onchange="@func(\'@name\',\'@link\',this.value)"/>',infos);
|
||||||
|
else {
|
||||||
|
out('<select id="@name" name="@name" class="dselect" size="@size" @dis onchange="@func(\'@name\',\'@link\',this.value)">',infos);
|
||||||
|
out('<option value="">---- none -----</option>');
|
||||||
|
for( v in values )
|
||||||
|
out('<option value="@id"@sel>@str</option>',{ id : v.id, str : v.str, sel : if( v.id == def ) ' selected="yes"' else "" });
|
||||||
|
out('</select>');
|
||||||
|
}
|
||||||
|
out('<a id="@name__goto" href="#">goto</a>',{ name : name });
|
||||||
|
if( isImage )
|
||||||
|
out('<img class="dfile" id="@name__img" src="@file"/>',{ name : name, file : getFileURL(def) });
|
||||||
|
out('<script lang="text/javascript">document.getElementById("@name").onchange()</script>',{ name : name });
|
||||||
|
endLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function errorField( message ) {
|
||||||
|
beginLine(true);
|
||||||
|
nextRow();
|
||||||
|
error(message);
|
||||||
|
endLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function error( message ) {
|
||||||
|
out('<div class="derror">@msg</div>',{ msg : message });
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#end
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c)2012 Nicolas Cannasse
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
* a copy of this software and associated documentation files (the
|
||||||
|
* "Software"), to deal in the Software without restriction, including
|
||||||
|
* without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
* permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
* the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be
|
||||||
|
* included in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
package sys.db;
|
||||||
|
|
||||||
|
typedef SearchInfos = {
|
||||||
|
var fields : Array<String>;
|
||||||
|
var names : Array<String>;
|
||||||
|
var values : Array<Dynamic>;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef RightsInfos = {
|
||||||
|
var readOnly : Array<String>;
|
||||||
|
var invisible : Array<String>;
|
||||||
|
var can : {
|
||||||
|
var insert : Bool;
|
||||||
|
var modify : Bool;
|
||||||
|
var delete : Bool;
|
||||||
|
var truncate : Bool;
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c)2012 Nicolas Cannasse
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
* a copy of this software and associated documentation files (the
|
||||||
|
* "Software"), to deal in the Software without restriction, including
|
||||||
|
* without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
* permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
* the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be
|
||||||
|
* included in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
package sys.db;
|
||||||
|
|
||||||
|
class Id {
|
||||||
|
|
||||||
|
public static function encode( id : String ) : Int {
|
||||||
|
var l = id.length;
|
||||||
|
if( l > 6 )
|
||||||
|
throw "Invalid identifier '"+id+"'";
|
||||||
|
var k = 0;
|
||||||
|
var p = l;
|
||||||
|
while( p > 0 ) {
|
||||||
|
var c = id.charCodeAt(--p) - 96;
|
||||||
|
if( c < 1 || c > 26 ) {
|
||||||
|
c = c + 96 - 48;
|
||||||
|
if( c >= 1 && c <= 5 )
|
||||||
|
c += 26;
|
||||||
|
else
|
||||||
|
throw "Invalid character "+id.charCodeAt(p)+" in "+id;
|
||||||
|
}
|
||||||
|
k <<= 5;
|
||||||
|
k += c;
|
||||||
|
}
|
||||||
|
return k;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function decode( id : Int ) : String {
|
||||||
|
var s = new StringBuf();
|
||||||
|
if( id < 1 ) {
|
||||||
|
if( id == 0 ) return "";
|
||||||
|
throw "Invalid ID "+id;
|
||||||
|
}
|
||||||
|
while( id > 0 ) {
|
||||||
|
var k = id & 31;
|
||||||
|
if( k < 27 )
|
||||||
|
s.addChar(k + 96);
|
||||||
|
else
|
||||||
|
s.addChar(k + 22);
|
||||||
|
id >>= 5;
|
||||||
|
}
|
||||||
|
return s.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,747 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c)2012 Nicolas Cannasse
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
* a copy of this software and associated documentation files (the
|
||||||
|
* "Software"), to deal in the Software without restriction, including
|
||||||
|
* without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
* permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
* the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be
|
||||||
|
* included in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
package sys.db;
|
||||||
|
|
||||||
|
#if hscript
|
||||||
|
import hscript.Expr;
|
||||||
|
#end
|
||||||
|
|
||||||
|
private enum Errors {
|
||||||
|
Invalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
private typedef Current = {
|
||||||
|
var old : Current;
|
||||||
|
var lines : Array<String>;
|
||||||
|
var totalSize : Int;
|
||||||
|
var maxSize : Int;
|
||||||
|
var prefix : String;
|
||||||
|
var sep : String;
|
||||||
|
var buf : StringBuf;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Serialized {
|
||||||
|
|
||||||
|
var value : String;
|
||||||
|
var pos : Int;
|
||||||
|
var buf : StringBuf;
|
||||||
|
var shash : Map<String,Int>;
|
||||||
|
var scount : Int;
|
||||||
|
var scache : Array<String>;
|
||||||
|
var useEnumIndex : Bool;
|
||||||
|
|
||||||
|
var cur : Current;
|
||||||
|
var tabs : Int;
|
||||||
|
|
||||||
|
static var IDENT = " ";
|
||||||
|
static var ident = ~/^[A-Za-z_][A-Za-z0-9_]*$/;
|
||||||
|
static var clname = ~/^[A-Za-z_][A-Z.a-z0-9_]*$/;
|
||||||
|
static var BASE64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789%:";
|
||||||
|
|
||||||
|
public function new(v) {
|
||||||
|
this.value = v;
|
||||||
|
pos = 0;
|
||||||
|
tabs = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function encode() : String {
|
||||||
|
#if !hscript
|
||||||
|
throw "You can't edit this without -lib hscript";
|
||||||
|
return null;
|
||||||
|
#else
|
||||||
|
if( value == "" )
|
||||||
|
return "";
|
||||||
|
var p = new hscript.Parser();
|
||||||
|
p.allowJSON = true;
|
||||||
|
var e = p.parse(new haxe.io.StringInput(value));
|
||||||
|
buf = new StringBuf();
|
||||||
|
shash = new Map();
|
||||||
|
scount = 0;
|
||||||
|
encodeRec(e);
|
||||||
|
return buf.toString();
|
||||||
|
#end
|
||||||
|
}
|
||||||
|
|
||||||
|
#if hscript
|
||||||
|
|
||||||
|
inline function expr( e ){
|
||||||
|
#if hscriptPos
|
||||||
|
return e.e;
|
||||||
|
#else
|
||||||
|
return e;
|
||||||
|
#end
|
||||||
|
}
|
||||||
|
|
||||||
|
function getString( e : Expr ) {
|
||||||
|
return (e == null) ? null : switch( expr(e) ) {
|
||||||
|
case EConst(v):
|
||||||
|
switch(v) {
|
||||||
|
case CString(s): s;
|
||||||
|
default: null;
|
||||||
|
}
|
||||||
|
default: null;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPath( e : Expr ) {
|
||||||
|
if( e == null )
|
||||||
|
return null;
|
||||||
|
switch( expr(e) ) {
|
||||||
|
case EConst(v):
|
||||||
|
return switch(v) {
|
||||||
|
case CString(s): s;
|
||||||
|
default: null;
|
||||||
|
}
|
||||||
|
case EIdent(v):
|
||||||
|
return v;
|
||||||
|
case EField(e, f):
|
||||||
|
var path = "." + f;
|
||||||
|
while( true ) {
|
||||||
|
switch( expr(e) ) {
|
||||||
|
case EIdent(i): return i + path;
|
||||||
|
case EField(p, f): path = "." + f + path; e = p;
|
||||||
|
default: return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function encodeRec( e : Expr ) {
|
||||||
|
switch( expr(e) ) {
|
||||||
|
case EConst(v):
|
||||||
|
switch(v) {
|
||||||
|
case CString(s):
|
||||||
|
encodeString(s);
|
||||||
|
case CInt(v):
|
||||||
|
if( v == 0 ) {
|
||||||
|
buf.add("z");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
buf.add("i");
|
||||||
|
buf.add(v);
|
||||||
|
case CFloat(v):
|
||||||
|
if( Math.isNaN(v) )
|
||||||
|
buf.add("k");
|
||||||
|
else if( !Math.isFinite(v) )
|
||||||
|
buf.add(if( v < 0 ) "m" else "p");
|
||||||
|
else {
|
||||||
|
buf.add("d");
|
||||||
|
buf.add(v);
|
||||||
|
}
|
||||||
|
#if !haxe3
|
||||||
|
case CInt32(i):
|
||||||
|
buf.add("d");
|
||||||
|
buf.add(v);
|
||||||
|
#end
|
||||||
|
}
|
||||||
|
case EUnop(op, _, es):
|
||||||
|
if( op == "-" )
|
||||||
|
switch( expr(es) ) {
|
||||||
|
case EConst(v):
|
||||||
|
switch(v) {
|
||||||
|
case CInt(i):
|
||||||
|
#if hscriptPos
|
||||||
|
encodeRec({e: EConst(CInt(-i)), pmin: es.pmin, pmax: es.pmax});
|
||||||
|
#else
|
||||||
|
encodeRec(EConst(CInt(-i)));
|
||||||
|
#end
|
||||||
|
return;
|
||||||
|
case CFloat(f):
|
||||||
|
#if hscriptPos
|
||||||
|
encodeRec({e: EConst(CFloat(-f)), pmin: es.pmin, pmax: es.pmax});
|
||||||
|
#else
|
||||||
|
encodeRec(EConst(CFloat(-f)));
|
||||||
|
#end
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
throw "Unsupported " + Type.enumConstructor(expr(e));
|
||||||
|
case EIdent(v):
|
||||||
|
switch( v ) {
|
||||||
|
case "null":
|
||||||
|
buf.add("n");
|
||||||
|
case "true":
|
||||||
|
buf.add("t");
|
||||||
|
case "false":
|
||||||
|
buf.add("f");
|
||||||
|
case "NaN":
|
||||||
|
buf.add("k");
|
||||||
|
case "Inf":
|
||||||
|
buf.add("p");
|
||||||
|
case "NegInf":
|
||||||
|
buf.add("m");
|
||||||
|
default:
|
||||||
|
throw "Unknown identifier " + v;
|
||||||
|
}
|
||||||
|
case EArrayDecl(el):
|
||||||
|
var ucount = 0;
|
||||||
|
buf.add("a");
|
||||||
|
for( e in el ) {
|
||||||
|
switch( expr(e) ) {
|
||||||
|
case EIdent(i):
|
||||||
|
if( i == "null" ) {
|
||||||
|
ucount++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
if( ucount > 0 ) {
|
||||||
|
if( ucount == 1 )
|
||||||
|
buf.add("n");
|
||||||
|
else {
|
||||||
|
buf.add("u");
|
||||||
|
buf.add(ucount);
|
||||||
|
}
|
||||||
|
ucount = 0;
|
||||||
|
}
|
||||||
|
encodeRec(e);
|
||||||
|
}
|
||||||
|
if( ucount > 0 ) {
|
||||||
|
if( ucount == 1 )
|
||||||
|
buf.add("n");
|
||||||
|
else {
|
||||||
|
buf.add("u");
|
||||||
|
buf.add(ucount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buf.add("h");
|
||||||
|
case EObject(fields):
|
||||||
|
buf.add("o");
|
||||||
|
for( f in fields ) {
|
||||||
|
encodeString(f.name);
|
||||||
|
encodeRec(f.e);
|
||||||
|
}
|
||||||
|
buf.add("g");
|
||||||
|
case ECall(e, params):
|
||||||
|
switch( expr(e) ) {
|
||||||
|
case EIdent(call):
|
||||||
|
switch(call) {
|
||||||
|
case "empty":
|
||||||
|
if( params.length == 0 )
|
||||||
|
return;
|
||||||
|
case "invalid":
|
||||||
|
var str = getString(params[0]);
|
||||||
|
if( params.length == 1 && str != null ) {
|
||||||
|
buf.add(str);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case "list":
|
||||||
|
buf.add("l");
|
||||||
|
for( e in params )
|
||||||
|
encodeRec(e);
|
||||||
|
buf.add("h");
|
||||||
|
return;
|
||||||
|
case "date":
|
||||||
|
var str = getString(params[0]);
|
||||||
|
// check format
|
||||||
|
if( params.length == 1 && str != null ) {
|
||||||
|
var d = Date.fromString(str);
|
||||||
|
buf.add("v");
|
||||||
|
buf.add(d.toString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case "now":
|
||||||
|
if( params.length == 0 ) {
|
||||||
|
buf.add("v");
|
||||||
|
buf.add(Date.now());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case "error":
|
||||||
|
if( params.length == 1 ) {
|
||||||
|
buf.add("x");
|
||||||
|
encodeRec(params[0]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case "hash":
|
||||||
|
if( params.length == 1 )
|
||||||
|
switch( expr(params[0]) ) {
|
||||||
|
case EObject(fields):
|
||||||
|
buf.add("b");
|
||||||
|
for( f in fields ) {
|
||||||
|
encodeString(f.name);
|
||||||
|
encodeRec(f.e);
|
||||||
|
}
|
||||||
|
buf.add("h");
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
case "inthash":
|
||||||
|
if( params.length == 1 )
|
||||||
|
switch( expr(params[0]) ) {
|
||||||
|
case EObject(fields):
|
||||||
|
buf.add("q");
|
||||||
|
for( f in fields ) {
|
||||||
|
if( !~/^-?[0-9]+$/.match(f.name) )
|
||||||
|
throw "Invalid IntHash key '"+f.name+"'";
|
||||||
|
buf.add(":");
|
||||||
|
buf.add(f.name);
|
||||||
|
encodeRec(f.e);
|
||||||
|
}
|
||||||
|
buf.add("h");
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
case "bytes":
|
||||||
|
var str = getString(params[0]);
|
||||||
|
if( params.length == 1 && str != null ) {
|
||||||
|
for( i in 0...str.length )
|
||||||
|
if( BASE64.indexOf(str.charAt(i)) == -1 )
|
||||||
|
throw "Invalid Base64 char";
|
||||||
|
buf.add("s");
|
||||||
|
buf.add(str.length);
|
||||||
|
buf.add(":");
|
||||||
|
buf.add(str);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case "indexes":
|
||||||
|
if( params.length == 1 ) {
|
||||||
|
useEnumIndex = true;
|
||||||
|
encodeRec(params[0]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case "ref":
|
||||||
|
if( params.length == 1 ) {
|
||||||
|
switch( expr(params[0]) ) {
|
||||||
|
case EConst(v):
|
||||||
|
switch(v) {
|
||||||
|
case CInt(i):
|
||||||
|
buf.add("r");
|
||||||
|
buf.add(i);
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw "Unsupported call '"+call+"'";
|
||||||
|
}
|
||||||
|
case EField(e, f):
|
||||||
|
encodeEnum(e, f, params);
|
||||||
|
return;
|
||||||
|
case EArray(e, index):
|
||||||
|
encodeEnum(e, index, params);
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
throw "Unsupported call";
|
||||||
|
case EField(e, f):
|
||||||
|
encodeEnum(e, f, []);
|
||||||
|
case EArray(e,index):
|
||||||
|
encodeEnum(e, index, []);
|
||||||
|
case ENew(c, params):
|
||||||
|
var fields = null, cname = null;
|
||||||
|
if( c == "class" ) {
|
||||||
|
if( params.length == 2 ) {
|
||||||
|
cname = getString(params[0]);
|
||||||
|
fields = switch( expr(params[1]) ) { case EObject(fields): fields; default : null; }
|
||||||
|
}
|
||||||
|
} else if( c == "custom" ) {
|
||||||
|
cname = getPath(params[0]);
|
||||||
|
if( cname != null ) {
|
||||||
|
buf.add("C");
|
||||||
|
encodeString(cname);
|
||||||
|
for( i in 1...params.length )
|
||||||
|
encodeRec(params[i]);
|
||||||
|
buf.add("g");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if( params.length == 1 ) {
|
||||||
|
cname = c;
|
||||||
|
fields = switch( expr(params[0]) ) { case EObject(fields): fields; default : null; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if( cname == null || fields == null )
|
||||||
|
throw "Invalid 'new'";
|
||||||
|
buf.add("c");
|
||||||
|
encodeString(cname);
|
||||||
|
for( f in fields ) {
|
||||||
|
encodeString(f.name);
|
||||||
|
encodeRec(f.e);
|
||||||
|
}
|
||||||
|
buf.add("g");
|
||||||
|
default:
|
||||||
|
throw "Unsupported " + Type.enumConstructor(expr(e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function encodeEnum( e : Expr, ?name : String, ?eindex : Expr, args : Array<Expr> ) {
|
||||||
|
var ename = getPath(e);
|
||||||
|
if( ename == null )
|
||||||
|
throw "Invalid enum path";
|
||||||
|
var index : Null<Int> = null;
|
||||||
|
if( eindex != null ) {
|
||||||
|
switch( expr(eindex) ) {
|
||||||
|
case EConst(c):
|
||||||
|
switch( c ) {
|
||||||
|
case CInt(i): index = i;
|
||||||
|
case CString(s): name = s;
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
if( index == null && name == null ) throw "Invalid enum index";
|
||||||
|
}
|
||||||
|
if( name != null ) {
|
||||||
|
var e = try Type.resolveEnum(ename) catch( e : Dynamic ) null;
|
||||||
|
if( e == null ) {
|
||||||
|
if( useEnumIndex )
|
||||||
|
throw "Unknown enum '" + ename + "' : use index";
|
||||||
|
} else {
|
||||||
|
index = Lambda.indexOf(Type.getEnumConstructs(e), name);
|
||||||
|
if( index < 0 ) throw name + " is not part of enum " + ename + "(" + Type.getEnumConstructs(e).join(",") + ")";
|
||||||
|
if( useEnumIndex ) name = null else index = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buf.add((index != null)?"j":"w");
|
||||||
|
encodeString(ename);
|
||||||
|
if( index != null ) {
|
||||||
|
buf.add(":");
|
||||||
|
buf.add(index);
|
||||||
|
} else
|
||||||
|
encodeString(name);
|
||||||
|
buf.add(":");
|
||||||
|
buf.add(args.length);
|
||||||
|
for( a in args )
|
||||||
|
encodeRec(a);
|
||||||
|
}
|
||||||
|
|
||||||
|
function encodeString( s : String ) {
|
||||||
|
var x = shash.get(s);
|
||||||
|
if( x != null ) {
|
||||||
|
buf.add("R");
|
||||||
|
buf.add(x);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
shash.set(s,scount++);
|
||||||
|
buf.add("y");
|
||||||
|
s = StringTools.urlEncode(s);
|
||||||
|
buf.add(s.length);
|
||||||
|
buf.add(":");
|
||||||
|
buf.add(s);
|
||||||
|
}
|
||||||
|
#end
|
||||||
|
|
||||||
|
function quote( s : String, ?r : EReg ) {
|
||||||
|
if( r != null && r.match(s) )
|
||||||
|
return s;
|
||||||
|
return "'"+s.split("\\").join("\\\\").split("'").join("\\'").split("\n").join("\\n").split("\r").join("\\r").split("\t").join("\\t")+"'";
|
||||||
|
}
|
||||||
|
|
||||||
|
public function escape() {
|
||||||
|
if( value == "" )
|
||||||
|
return "empty()";
|
||||||
|
buf = new StringBuf();
|
||||||
|
scache = new Array();
|
||||||
|
try loop() catch( e : Errors ) pos = -1;
|
||||||
|
if( pos != value.length )
|
||||||
|
return "invalid(" + quote(value) + ")";
|
||||||
|
var str = buf.toString();
|
||||||
|
if( useEnumIndex )
|
||||||
|
str = "indexes(" + str + ")";
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline function get(pos) {
|
||||||
|
return value.charCodeAt(pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
function readDigits() {
|
||||||
|
var k = 0;
|
||||||
|
var s = false;
|
||||||
|
var fpos = pos;
|
||||||
|
while( true ) {
|
||||||
|
var c = get(pos);
|
||||||
|
if( c == null )
|
||||||
|
break;
|
||||||
|
if( c == "-".code ) {
|
||||||
|
if( pos != fpos )
|
||||||
|
break;
|
||||||
|
s = true;
|
||||||
|
pos++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if( c < "0".code || c > "9".code )
|
||||||
|
break;
|
||||||
|
k = k * 10 + (c - "0".code);
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
if( s )
|
||||||
|
k *= -1;
|
||||||
|
return k;
|
||||||
|
}
|
||||||
|
|
||||||
|
function loop() {
|
||||||
|
switch( get(pos++) ) {
|
||||||
|
case "n".code:
|
||||||
|
buf.add(null);
|
||||||
|
case "i".code:
|
||||||
|
buf.add(readDigits());
|
||||||
|
case "z".code:
|
||||||
|
buf.add(0);
|
||||||
|
case "t".code:
|
||||||
|
buf.add(true);
|
||||||
|
case "f".code:
|
||||||
|
buf.add(false);
|
||||||
|
case "k".code:
|
||||||
|
buf.add("NaN");
|
||||||
|
case "p".code:
|
||||||
|
buf.add("Inf");
|
||||||
|
case "m".code:
|
||||||
|
buf.add("NegInf");
|
||||||
|
case "d".code:
|
||||||
|
var p1 = pos;
|
||||||
|
while( true ) {
|
||||||
|
var c = get(pos);
|
||||||
|
// + - . , 0-9
|
||||||
|
if( (c >= 43 && c < 58) || c == "e".code || c == "E".code )
|
||||||
|
pos++;
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
buf.add(value.substr(p1, pos - p1));
|
||||||
|
case "a".code:
|
||||||
|
open("[",", ");
|
||||||
|
while( true ) {
|
||||||
|
var c = get(pos);
|
||||||
|
if( c == "h".code ) {
|
||||||
|
pos++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if( c == "u".code ) {
|
||||||
|
pos++;
|
||||||
|
for( i in 0...readDigits() - 1 ) {
|
||||||
|
buf.add("null");
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
buf.add("null");
|
||||||
|
} else
|
||||||
|
loop();
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
close("]");
|
||||||
|
case "y".code, "R".code:
|
||||||
|
pos--;
|
||||||
|
buf.add(quote(readString()));
|
||||||
|
case "l".code:
|
||||||
|
open("list(",", ");
|
||||||
|
while( get(pos) != "h".code ) {
|
||||||
|
loop();
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
close(")");
|
||||||
|
pos++;
|
||||||
|
case "v".code:
|
||||||
|
buf.add("date(");
|
||||||
|
buf.add(quote(value.substr(pos, 19)));
|
||||||
|
buf.add(")");
|
||||||
|
pos += 19;
|
||||||
|
case "x".code:
|
||||||
|
buf.add("error(");
|
||||||
|
loop();
|
||||||
|
buf.add(")");
|
||||||
|
case "o".code:
|
||||||
|
loopObj("g".code);
|
||||||
|
case "b".code:
|
||||||
|
buf.add("hash(");
|
||||||
|
loopObj("h".code);
|
||||||
|
buf.add(")");
|
||||||
|
case "q".code:
|
||||||
|
buf.add("inthash(");
|
||||||
|
open("{",", "," ");
|
||||||
|
var c = get(pos++);
|
||||||
|
while( c == ":".code ) {
|
||||||
|
buf.add("'"+readDigits()+"'");
|
||||||
|
buf.add(" : ");
|
||||||
|
loop();
|
||||||
|
c = get(pos++);
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
if( c != "h".code )
|
||||||
|
throw Invalid;
|
||||||
|
close("}", " ");
|
||||||
|
buf.add(")");
|
||||||
|
case "s".code:
|
||||||
|
var len = readDigits();
|
||||||
|
if( get(pos++) != ":".code || value.length - pos < len )
|
||||||
|
throw Invalid;
|
||||||
|
buf.add("bytes(");
|
||||||
|
buf.add(quote(value.substr(pos, len)));
|
||||||
|
buf.add(")");
|
||||||
|
pos += len;
|
||||||
|
case "w".code:
|
||||||
|
buf.add(quote(readString(), clname));
|
||||||
|
var constr = readString();
|
||||||
|
if( ident.match(constr) )
|
||||||
|
buf.add("." + constr);
|
||||||
|
else
|
||||||
|
buf.add("["+quote(constr)+"]");
|
||||||
|
if( get(pos++) != ":".code )
|
||||||
|
throw Invalid;
|
||||||
|
var nargs = readDigits();
|
||||||
|
if( nargs > 0 ) {
|
||||||
|
buf.add("(");
|
||||||
|
for( i in 0...nargs ) {
|
||||||
|
if( i > 0 ) buf.add(", ");
|
||||||
|
loop();
|
||||||
|
}
|
||||||
|
buf.add(")");
|
||||||
|
}
|
||||||
|
case "j".code:
|
||||||
|
var cl = readString();
|
||||||
|
buf.add(quote(cl, clname));
|
||||||
|
if( get(pos++) != ":".code )
|
||||||
|
throw Invalid;
|
||||||
|
var index = readDigits();
|
||||||
|
var e = Type.resolveEnum(cl);
|
||||||
|
if( e == null )
|
||||||
|
buf.add("["+index+"]");
|
||||||
|
else {
|
||||||
|
useEnumIndex = true;
|
||||||
|
buf.add("."+Type.getEnumConstructs(e)[index]);
|
||||||
|
}
|
||||||
|
if( get(pos++) != ":".code )
|
||||||
|
throw Invalid;
|
||||||
|
var nargs = readDigits();
|
||||||
|
if( nargs > 0 ) {
|
||||||
|
buf.add("(");
|
||||||
|
for( i in 0...nargs ) {
|
||||||
|
if( i > 0 ) buf.add(", ");
|
||||||
|
loop();
|
||||||
|
}
|
||||||
|
buf.add(")");
|
||||||
|
}
|
||||||
|
case "c".code:
|
||||||
|
buf.add("new ");
|
||||||
|
var cl = readString();
|
||||||
|
if( clname.match(cl) )
|
||||||
|
buf.add(cl + "(");
|
||||||
|
else {
|
||||||
|
buf.add("class(");
|
||||||
|
buf.add(quote(cl));
|
||||||
|
buf.add(",");
|
||||||
|
}
|
||||||
|
loopObj("g".code);
|
||||||
|
buf.add(")");
|
||||||
|
case "C".code:
|
||||||
|
open("new custom(",", ");
|
||||||
|
buf.add(quote(readString(), clname));
|
||||||
|
next();
|
||||||
|
while( get(pos) != "g".code ) {
|
||||||
|
loop();
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
close(")");
|
||||||
|
pos++;
|
||||||
|
case "r".code:
|
||||||
|
buf.add("ref("+readDigits()+")");
|
||||||
|
default:
|
||||||
|
throw Invalid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function readString() : String {
|
||||||
|
switch( value.charCodeAt(pos++) ) {
|
||||||
|
case "y".code:
|
||||||
|
var len = readDigits();
|
||||||
|
if( get(pos++) != ":".code || value.length - pos < len )
|
||||||
|
throw Invalid;
|
||||||
|
var s = value.substr(pos,len);
|
||||||
|
pos += len;
|
||||||
|
s = StringTools.urlDecode(s);
|
||||||
|
scache.push(s);
|
||||||
|
return s;
|
||||||
|
case "R".code:
|
||||||
|
var n = readDigits();
|
||||||
|
if( n < 0 || n >= scache.length )
|
||||||
|
throw "Invalid string reference";
|
||||||
|
return scache[n];
|
||||||
|
default:
|
||||||
|
throw Invalid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function loopObj(eof) {
|
||||||
|
open("{",", "," ");
|
||||||
|
while( true ) {
|
||||||
|
if( pos >= value.length )
|
||||||
|
throw Invalid;
|
||||||
|
if( get(pos) == eof )
|
||||||
|
break;
|
||||||
|
buf.add(quote(readString(), ident));
|
||||||
|
buf.add(" : ");
|
||||||
|
loop();
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
close("}"," ");
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
|
||||||
|
function open(str, sep, ?prefix) {
|
||||||
|
buf.add(str);
|
||||||
|
tabs++;
|
||||||
|
cur = { old : cur, sep : sep, prefix : prefix, lines : [], buf : buf, totalSize : 0, maxSize : 0 };
|
||||||
|
buf = new StringBuf();
|
||||||
|
}
|
||||||
|
|
||||||
|
function next() {
|
||||||
|
var line = buf.toString();
|
||||||
|
if( line.length > cur.maxSize ) cur.maxSize = line.length;
|
||||||
|
cur.totalSize += line.length;
|
||||||
|
cur.lines.push(line);
|
||||||
|
buf = new StringBuf();
|
||||||
|
}
|
||||||
|
|
||||||
|
function close(end,?postfix) {
|
||||||
|
buf = cur.buf;
|
||||||
|
var t = "\n";
|
||||||
|
for( i in 0...tabs-1 )
|
||||||
|
t += IDENT;
|
||||||
|
if( t.length + cur.totalSize > 80 && cur.maxSize > 10 ) {
|
||||||
|
var first = true;
|
||||||
|
for( line in cur.lines ) {
|
||||||
|
if( first ) first = false else buf.add(cur.sep);
|
||||||
|
buf.add(t + IDENT + line);
|
||||||
|
}
|
||||||
|
buf.add(t);
|
||||||
|
buf.add(end);
|
||||||
|
} else {
|
||||||
|
if( cur.prefix != null && cur.lines.length > 0 ) buf.add(cur.prefix);
|
||||||
|
var first = true;
|
||||||
|
for( line in cur.lines ) {
|
||||||
|
if( first ) first = false else buf.add(cur.sep);
|
||||||
|
buf.add(line);
|
||||||
|
}
|
||||||
|
if( !first && postfix != null ) buf.add(postfix);
|
||||||
|
buf.add(end);
|
||||||
|
}
|
||||||
|
cur = cur.old;
|
||||||
|
tabs--;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,560 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c)2012 Nicolas Cannasse
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
* a copy of this software and associated documentation files (the
|
||||||
|
* "Software"), to deal in the Software without restriction, including
|
||||||
|
* without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
* permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
* the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be
|
||||||
|
* included in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
package sys.db;
|
||||||
|
|
||||||
|
import sys.db.Object;
|
||||||
|
import sys.db.Manager;
|
||||||
|
|
||||||
|
typedef TableType = sys.db.RecordInfos.RecordType;
|
||||||
|
|
||||||
|
typedef ManagerAccess = {
|
||||||
|
private var table_name : String;
|
||||||
|
private var table_keys : Array<String>;
|
||||||
|
private function quote( v : Dynamic ) : String;
|
||||||
|
private function quoteField( f : String ) : String;
|
||||||
|
private function addKeys( s : StringBuf, x : {} ) : Void;
|
||||||
|
function all( ?lock : Bool ) : List<Object>;
|
||||||
|
function dbClass() : Class<Dynamic>;
|
||||||
|
}
|
||||||
|
|
||||||
|
private typedef TableRelation = {
|
||||||
|
var prop : String;
|
||||||
|
var key : String;
|
||||||
|
var lock : Bool;
|
||||||
|
var manager : ManagerAccess;
|
||||||
|
var className : String;
|
||||||
|
var cascade : Bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
class TableInfos {
|
||||||
|
|
||||||
|
public static var ENGINE = "InnoDB";
|
||||||
|
public static var OLD_COMPAT = false; // only set for old DBs !
|
||||||
|
|
||||||
|
public var primary(default,null) : List<String>;
|
||||||
|
public var cl(default,null) : Class<Object>;
|
||||||
|
public var name(default,null) : String;
|
||||||
|
public var className(default,null) : String;
|
||||||
|
public var hfields(default,null) : Map<String,TableType>;
|
||||||
|
public var fields(default,null) : List<{ name : String, type : TableType }>;
|
||||||
|
public var nulls(default,null) : Map<String,Bool>;
|
||||||
|
public var relations(default,null) : Array<TableRelation>;
|
||||||
|
public var indexes(default,null) : List<{ keys : List<String>, unique : Bool }>;
|
||||||
|
public var manager : Manager<Object>;
|
||||||
|
|
||||||
|
public function new( cname : String ) {
|
||||||
|
hfields = new Map();
|
||||||
|
fields = new List();
|
||||||
|
nulls = new Map();
|
||||||
|
cl = cast Type.resolveClass("db."+cname);
|
||||||
|
if( cl == null )
|
||||||
|
cl = cast Type.resolveClass(cname);
|
||||||
|
else
|
||||||
|
cname = "db."+cname;
|
||||||
|
if( cl == null )
|
||||||
|
throw "Class not found : "+cname;
|
||||||
|
manager = untyped cl.manager;
|
||||||
|
if( manager == null )
|
||||||
|
throw "No static manager for "+cname;
|
||||||
|
className = cname;
|
||||||
|
if( className.substr(0,3) == "db." ) className = className.substr(3);
|
||||||
|
var a = cname.split(".");
|
||||||
|
name = a.pop();
|
||||||
|
processClass();
|
||||||
|
}
|
||||||
|
|
||||||
|
function processClass() {
|
||||||
|
var rtti = haxe.rtti.Meta.getType(cl).rtti;
|
||||||
|
if( rtti == null )
|
||||||
|
throw "Class "+name+" does not have RTTI";
|
||||||
|
var infos : sys.db.RecordInfos = haxe.Unserializer.run(rtti[0]);
|
||||||
|
name = infos.name;
|
||||||
|
primary = Lambda.list(infos.key);
|
||||||
|
for( f in infos.fields ) {
|
||||||
|
fields.add({ name : f.name, type : f.t });
|
||||||
|
hfields.set(f.name, f.t);
|
||||||
|
if( f.isNull ) nulls.set(f.name, true);
|
||||||
|
}
|
||||||
|
relations = new Array();
|
||||||
|
for( r in infos.relations ) {
|
||||||
|
var t = Type.resolveClass(r.type);
|
||||||
|
if( t == null ) throw "Missing type " + r.type + " for relation " + name + "." + r.prop;
|
||||||
|
var manager : ManagerAccess = Reflect.field(t, "manager");
|
||||||
|
if( manager == null ) throw r.type + " does not have a static field manager";
|
||||||
|
relations.push( { prop : r.prop, key : r.key, lock : r.lock, manager : manager, className : Type.getClassName(manager.dbClass()), cascade : r.cascade } );
|
||||||
|
}
|
||||||
|
indexes = new List();
|
||||||
|
for( i in infos.indexes )
|
||||||
|
indexes.push( { keys : Lambda.list(i.keys), unique : i.unique } );
|
||||||
|
}
|
||||||
|
|
||||||
|
function escape( name : String ) {
|
||||||
|
var m : ManagerAccess = manager;
|
||||||
|
return m.quoteField(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function unescape( field : String ) {
|
||||||
|
if( field.length > 1 && field.charAt(0) == '`' && field.charAt(field.length-1) == '`' )
|
||||||
|
return field.substr(1,field.length-2);
|
||||||
|
return field;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isRelationActive( r : Dynamic ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createRequest( full : Bool ) {
|
||||||
|
var str = "CREATE TABLE "+escape(name)+" (\n";
|
||||||
|
var keys = fields.iterator();
|
||||||
|
for( f in keys ) {
|
||||||
|
str += escape(f.name)+" "+fieldInfos(f);
|
||||||
|
if( keys.hasNext() )
|
||||||
|
str += ",";
|
||||||
|
str += "\n";
|
||||||
|
}
|
||||||
|
if( primary != null )
|
||||||
|
str += ", PRIMARY KEY ("+primary.map(escape).join(",")+")\n";
|
||||||
|
if( full ) {
|
||||||
|
for( r in relations )
|
||||||
|
if( isRelationActive(r) )
|
||||||
|
str += ", "+relationInfos(r);
|
||||||
|
for( i in indexes )
|
||||||
|
str += ", "+(if( i.unique ) "UNIQUE " else "")+"KEY "+escape(name+"_"+i.keys.join("_"))+"("+i.keys.map(escape).join(",")+")\n";
|
||||||
|
}
|
||||||
|
str += ")";
|
||||||
|
if( ENGINE != null )
|
||||||
|
str += " ENGINE="+ENGINE;
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
function relationInfos(r : TableRelation) {
|
||||||
|
if( r.manager.table_keys.length != 1 )
|
||||||
|
throw "Relation on a multiple-keys table";
|
||||||
|
var rq = "CONSTRAINT "+escape(name+"_"+r.prop)+" FOREIGN KEY ("+escape(r.key)+") REFERENCES "+escape(r.manager.table_name)+"("+escape(r.manager.table_keys[0])+") ";
|
||||||
|
rq += "ON DELETE "+(if( nulls.get(r.key) && r.cascade != true ) "SET NULL" else "CASCADE")+"\n";
|
||||||
|
return rq;
|
||||||
|
}
|
||||||
|
|
||||||
|
function fieldInfos(f) {
|
||||||
|
return (switch( f.type ) {
|
||||||
|
case DId: "INT AUTO_INCREMENT";
|
||||||
|
case DUId: "INT UNSIGNED AUTO_INCREMENT";
|
||||||
|
case DInt, DEncoded: "INT";
|
||||||
|
case DFlags(fl, auto): auto ? (fl.length <= 8 ? "TINYINT UNSIGNED" : (fl.length <= 16 ? "SMALLINT UNSIGNED" : (fl.length <= 24 ? "MEDIUMINT UNSIGNED" : "INT"))) : "INT";
|
||||||
|
case DTinyInt: "TINYINT";
|
||||||
|
case DUInt: "INT UNSIGNED";
|
||||||
|
case DSingle: "FLOAT";
|
||||||
|
case DFloat: "DOUBLE";
|
||||||
|
case DBool: "TINYINT(1)";
|
||||||
|
case DString(n): "VARCHAR("+n+")";
|
||||||
|
case DDate: "DATE";
|
||||||
|
case DDateTime: "DATETIME";
|
||||||
|
case DTimeStamp: "TIMESTAMP"+(nulls.exists(f.name) ? " NULL DEFAULT NULL" : " DEFAULT 0");
|
||||||
|
case DTinyText: "TINYTEXT";
|
||||||
|
case DSmallText: "TEXT";
|
||||||
|
case DText, DSerialized: "MEDIUMTEXT";
|
||||||
|
case DSmallBinary: "BLOB";
|
||||||
|
case DBinary, DNekoSerialized: "MEDIUMBLOB";
|
||||||
|
case DData: "MEDIUMBLOB";
|
||||||
|
case DEnum(_): "TINYINT UNSIGNED";
|
||||||
|
case DLongBinary: "LONGBLOB";
|
||||||
|
case DBigInt: "BIGINT";
|
||||||
|
case DBigId: "BIGINT AUTO_INCREMENT";
|
||||||
|
case DBytes(n): "BINARY(" + n + ")";
|
||||||
|
case DTinyUInt: "TINYINT UNSIGNED";
|
||||||
|
case DSmallInt: "SMALLINT";
|
||||||
|
case DSmallUInt: "SMALLINT UNSIGNED";
|
||||||
|
case DMediumInt: "MEDIUMINT";
|
||||||
|
case DMediumUInt: "MEDIUMINT UNSIGNED";
|
||||||
|
case DNull, DInterval: throw "assert";
|
||||||
|
}) + if( nulls.exists(f.name) ) "" else " NOT NULL";
|
||||||
|
}
|
||||||
|
|
||||||
|
public function dropRequest() {
|
||||||
|
return "DROP TABLE "+escape(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function truncateRequest() {
|
||||||
|
return "TRUNCATE TABLE "+escape(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function descriptionRequest() {
|
||||||
|
return "SHOW CREATE TABLE "+escape(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function existsRequest() {
|
||||||
|
return "SELECT * FROM "+escape(name)+" LIMIT 0";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function countRequest( m : ManagerAccess, max : Int ) {
|
||||||
|
return "SELECT " + m.quoteField(m.table_keys[0]) + " FROM " + m.quoteField(m.table_name) + " LIMIT " + max;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addFieldRequest( fname : String ) {
|
||||||
|
var ftype = hfields.get(fname);
|
||||||
|
if( ftype == null )
|
||||||
|
throw "No field "+fname;
|
||||||
|
var rq = "ALTER TABLE "+escape(name)+" ADD ";
|
||||||
|
return rq + escape(fname)+" "+fieldInfos({ name : fname, type : ftype });
|
||||||
|
}
|
||||||
|
|
||||||
|
public function removeFieldRequest( fname : String ) {
|
||||||
|
return "ALTER TABLE "+escape(name)+" DROP "+escape(fname);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function renameFieldRequest( old : String, newname : String ) {
|
||||||
|
var ftype = hfields.get(newname);
|
||||||
|
if( ftype == null )
|
||||||
|
throw "No field "+newname;
|
||||||
|
var rq = "ALTER TABLE "+escape(name)+" CHANGE "+escape(old)+" ";
|
||||||
|
return rq + escape(newname) + " " + fieldInfos({ name : newname, type : ftype });
|
||||||
|
}
|
||||||
|
|
||||||
|
public function updateFieldRequest( fname : String ) {
|
||||||
|
var ftype = hfields.get(fname);
|
||||||
|
if( ftype == null )
|
||||||
|
throw "No field "+fname;
|
||||||
|
var rq = "ALTER TABLE "+escape(name)+" MODIFY ";
|
||||||
|
return rq + escape(fname)+" "+fieldInfos({ name : fname, type : ftype });
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addRelationRequest( key : String, prop : String ) {
|
||||||
|
for( r in relations )
|
||||||
|
if( r.key == key && r.prop == prop )
|
||||||
|
return "ALTER TABLE "+escape(name)+" ADD "+relationInfos(r);
|
||||||
|
return throw "No such relation : "+prop+"("+key+")";
|
||||||
|
}
|
||||||
|
|
||||||
|
public function deleteRelationRequest( rel : String ) {
|
||||||
|
return "ALTER TABLE "+escape(name)+" DROP FOREIGN KEY "+escape(rel);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function indexName( idx : Array<String> ) {
|
||||||
|
return name+"_"+idx.join("_");
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addIndexRequest( idx : Array<String>, unique : Bool ) {
|
||||||
|
var eidx = new Array();
|
||||||
|
for( i in idx ) {
|
||||||
|
var k = escape(i);
|
||||||
|
var f = hfields.get(i);
|
||||||
|
if( f != null )
|
||||||
|
switch( f ) {
|
||||||
|
case DTinyText, DSmallText, DText, DSmallBinary, DLongBinary, DBinary:
|
||||||
|
k += "(4)"; // index size
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
eidx.push(k);
|
||||||
|
}
|
||||||
|
return "ALTER TABLE "+escape(name)+" ADD "+(if( unique ) "UNIQUE " else "")+"INDEX "+escape(indexName(idx))+"("+eidx.join(",")+")";
|
||||||
|
}
|
||||||
|
|
||||||
|
public function deleteIndexRequest( idx : String ) {
|
||||||
|
return "ALTER TABLE "+escape(name)+" DROP INDEX "+escape(idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function updateFields( o : {}, fields : List<{ name : String, value : Dynamic }> ) {
|
||||||
|
var me = this;
|
||||||
|
var s = new StringBuf();
|
||||||
|
s.add("UPDATE ");
|
||||||
|
s.add(escape(name));
|
||||||
|
s.add(" SET ");
|
||||||
|
var first = true;
|
||||||
|
for( f in fields ) {
|
||||||
|
if( first )
|
||||||
|
first = false;
|
||||||
|
else
|
||||||
|
s.add(", ");
|
||||||
|
s.add(escape(f.name));
|
||||||
|
s.add(" = ");
|
||||||
|
Manager.cnx.addValue(s,f.value);
|
||||||
|
}
|
||||||
|
s.add(" WHERE ");
|
||||||
|
var m : ManagerAccess = manager;
|
||||||
|
m.addKeys(s,o);
|
||||||
|
return s.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function identifier( o : Object ) : String {
|
||||||
|
if( primary == null )
|
||||||
|
throw "No primary key";
|
||||||
|
return primary.map(function(p) { return Std.string(Reflect.field(o,p)).split(".").join("~"); }).join("@");
|
||||||
|
}
|
||||||
|
|
||||||
|
public function fromIdentifier( id : String ) : Object {
|
||||||
|
var ids = id.split("@");
|
||||||
|
if( primary == null )
|
||||||
|
throw "No primary key";
|
||||||
|
if( ids.length != primary.length )
|
||||||
|
throw "Invalid identifier";
|
||||||
|
var keys = {};
|
||||||
|
for( p in primary )
|
||||||
|
Reflect.setField(keys, p, makeNativeValue(hfields.get(p), ids.shift().split("~").join(".")));
|
||||||
|
return manager.unsafeGetWithKeys(keys);
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeNativeValue( t : TableType, v : String ) : Dynamic {
|
||||||
|
return switch( t ) {
|
||||||
|
case DInt, DUInt, DId, DUId, DEncoded, DFlags(_), DTinyInt: cast Std.parseInt(v);
|
||||||
|
case DTinyUInt, DSmallInt, DSmallUInt, DMediumUInt, DMediumInt: cast Std.parseInt(v);
|
||||||
|
case DFloat, DSingle, DBigInt, DBigId: cast Std.parseFloat(v);
|
||||||
|
case DDate, DDateTime, DTimeStamp: cast Date.fromString(v);
|
||||||
|
case DBool: cast (v == "true");
|
||||||
|
case DText, DString(_), DSmallText, DTinyText, DBinary, DSmallBinary, DLongBinary, DSerialized, DNekoSerialized, DBytes(_): cast v;
|
||||||
|
case DData: cast v;
|
||||||
|
case DEnum(_): cast v;
|
||||||
|
case DNull, DInterval: throw "assert";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public function fromSearch( params : Map<String,String>, order : String, pos : Int, count : Int ) : List<Object> {
|
||||||
|
var rop = ~/^([<>]=?)(.+)$/;
|
||||||
|
var cond = "TRUE";
|
||||||
|
var m : ManagerAccess = manager;
|
||||||
|
for( p in params.keys() ) {
|
||||||
|
var f = hfields.get(p);
|
||||||
|
var v = params.get(p);
|
||||||
|
if( f == null )
|
||||||
|
continue;
|
||||||
|
cond += " AND " + escape(p);
|
||||||
|
if( v == null || v == "NULL" )
|
||||||
|
cond += " IS NULL";
|
||||||
|
else switch( f ) {
|
||||||
|
case DEncoded:
|
||||||
|
cond += " = "+(try Id.encode(v) catch( e : Dynamic ) 0);
|
||||||
|
case DString(_),DTinyText,DSmallText,DText:
|
||||||
|
cond += " LIKE "+m.quote(v);
|
||||||
|
case DBool:
|
||||||
|
cond += " = "+((v == "true") ? 1 : 0);
|
||||||
|
case DId,DUId,DInt,DUInt,DSingle,DFloat,DDate,DDateTime,DBigInt,DBigId:
|
||||||
|
if( rop.match(v) )
|
||||||
|
cond += " "+rop.matched(1)+" "+m.quote(rop.matched(2));
|
||||||
|
else
|
||||||
|
cond += " = "+m.quote(v);
|
||||||
|
default:
|
||||||
|
cond += " = "+m.quote(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if( order != null ) {
|
||||||
|
if( order.charAt(0) == "-" )
|
||||||
|
cond += " ORDER BY "+escape(order.substr(1))+" DESC";
|
||||||
|
else
|
||||||
|
cond += " ORDER BY "+escape(order);
|
||||||
|
}
|
||||||
|
|
||||||
|
var sql = "SELECT * FROM " + escape(name) + " WHERE " + cond + " LIMIT " + pos + "," + count;
|
||||||
|
return manager.unsafeObjects(sql, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
static function fromTypeDescription( desc : String ) {
|
||||||
|
var fdesc = desc.toUpperCase().split(" ");
|
||||||
|
var ftype = fdesc.shift();
|
||||||
|
var tparam = ~/^([A-Za-z]+)\(([0-9]+)\)$/;
|
||||||
|
var param = null;
|
||||||
|
if( tparam.match(ftype) ) {
|
||||||
|
ftype = tparam.matched(1);
|
||||||
|
param = Std.parseInt(tparam.matched(2));
|
||||||
|
}
|
||||||
|
var nullable = true;
|
||||||
|
var t = switch( ftype ) {
|
||||||
|
case "VARCHAR","CHAR":
|
||||||
|
if( param == null )
|
||||||
|
null;
|
||||||
|
else
|
||||||
|
DString(param);
|
||||||
|
case "INT":
|
||||||
|
if( param == 11 && fdesc.remove("AUTO_INCREMENT") )
|
||||||
|
DId
|
||||||
|
else if( param == 10 && fdesc.remove("UNSIGNED") ) {
|
||||||
|
if( fdesc.remove("AUTO_INCREMENT") )
|
||||||
|
DUId
|
||||||
|
else
|
||||||
|
DUInt;
|
||||||
|
} else if( param == 11 )
|
||||||
|
DInt;
|
||||||
|
else
|
||||||
|
null;
|
||||||
|
case "BIGINT":
|
||||||
|
if( fdesc.remove("AUTO_INCREMENT") ) DBigId else DBigInt;
|
||||||
|
case "DOUBLE": DFloat;
|
||||||
|
case "FLOAT": DSingle;
|
||||||
|
case "DATE": DDate;
|
||||||
|
case "DATETIME": DDateTime;
|
||||||
|
case "TIMESTAMP": DTimeStamp;
|
||||||
|
case "TINYTEXT": DTinyText;
|
||||||
|
case "TEXT": DSmallText;
|
||||||
|
case "MEDIUMTEXT": DText;
|
||||||
|
case "BLOB": DSmallBinary;
|
||||||
|
case "MEDIUMBLOB": DBinary;
|
||||||
|
case "LONGBLOB": DLongBinary;
|
||||||
|
case "TINYINT":
|
||||||
|
switch( param ) {
|
||||||
|
case 1:
|
||||||
|
fdesc.remove("UNSIGNED");
|
||||||
|
DBool;
|
||||||
|
case 4:
|
||||||
|
DTinyInt;
|
||||||
|
case 3:
|
||||||
|
if( fdesc.remove("UNSIGNED") ) DTinyUInt else null;
|
||||||
|
default:
|
||||||
|
if( OLD_COMPAT )
|
||||||
|
DInt;
|
||||||
|
else
|
||||||
|
null;
|
||||||
|
}
|
||||||
|
case "SMALLINT":
|
||||||
|
fdesc.remove("UNSIGNED") ? DSmallUInt : DSmallInt;
|
||||||
|
case "MEDIUMINT":
|
||||||
|
fdesc.remove("UNSIGNED") ? DMediumUInt : DMediumInt;
|
||||||
|
case "BINARY":
|
||||||
|
if( param == null )
|
||||||
|
null;
|
||||||
|
else
|
||||||
|
DBytes(param);
|
||||||
|
default:
|
||||||
|
null;
|
||||||
|
}
|
||||||
|
if( t == null )
|
||||||
|
return null;
|
||||||
|
while( fdesc.length > 0 ) {
|
||||||
|
var d = fdesc.shift();
|
||||||
|
switch( d ) {
|
||||||
|
case "NOT":
|
||||||
|
if( fdesc.shift() != "NULL" )
|
||||||
|
return null;
|
||||||
|
nullable = false;
|
||||||
|
case "DEFAULT":
|
||||||
|
var v = fdesc.shift();
|
||||||
|
if( nullable ) {
|
||||||
|
if( v == "NULL" )
|
||||||
|
continue;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
var def = switch( t ) {
|
||||||
|
case DId, DUId, DInt, DUInt, DBool, DSingle, DFloat, DEncoded, DBigInt, DBigId, DFlags(_), DTinyInt: "'0'";
|
||||||
|
case DTinyUInt, DSmallInt, DSmallUInt, DMediumUInt, DMediumInt: "'0'";
|
||||||
|
case DTinyText, DText, DString(_), DSmallText, DSerialized: "''";
|
||||||
|
case DDateTime,DTimeStamp:
|
||||||
|
if( v.length > 0 && v.charAt(v.length-1) != "'" )
|
||||||
|
v += " "+fdesc.shift();
|
||||||
|
"'0000-00-00 00:00:00'";
|
||||||
|
case DDate: "'0000-00-00'";
|
||||||
|
case DSmallBinary, DBinary, DLongBinary, DNekoSerialized, DBytes(_), DNull, DInterval: null;
|
||||||
|
case DData: null;
|
||||||
|
case DEnum(_): "'0'";
|
||||||
|
}
|
||||||
|
if( v != def && !OLD_COMPAT )
|
||||||
|
return null;
|
||||||
|
case "NULL":
|
||||||
|
if( !nullable ) return null;
|
||||||
|
nullable = true;
|
||||||
|
continue;
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { t : t, nullable : nullable };
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function fromDescription( desc : String ) {
|
||||||
|
var r = ~/^CREATE TABLE `([^`]*)` \((.*)\)( ENGINE=([^ ]+))?( AUTO_INCREMENT=[^ ]+)?( DEFAULT CHARSET=.*)?$/sm;
|
||||||
|
if( !r.match(desc) )
|
||||||
|
throw "Invalid "+desc;
|
||||||
|
var tname = r.matched(1);
|
||||||
|
if( r.matched(4).toUpperCase() != "INNODB" )
|
||||||
|
throw "Table "+tname+" should be INNODB";
|
||||||
|
var matches = r.matched(2).split(",\n");
|
||||||
|
var field_r = ~/^[ \r\n]*`(.*)` (.*)$/;
|
||||||
|
var primary_r = ~/^[ \r\n]*PRIMARY KEY +\((.*)\)[ \r\n]*$/;
|
||||||
|
var index_r = ~/^[ \r\n]*(UNIQUE )?KEY `(.*)` \((.*)\)[ \r\n]*$/;
|
||||||
|
var foreign_r = ~/^[ \r\n]*CONSTRAINT `(.*)` FOREIGN KEY \(`(.*)`\) REFERENCES `(.*)` \(`(.*)`\) ON DELETE (SET NULL|CASCADE)[ \r\n]*$/;
|
||||||
|
var index_key_r = ~/^`?(.*?)`?(\([0-9+]\))?$/;
|
||||||
|
var fields = new Map();
|
||||||
|
var nulls = new Map();
|
||||||
|
var indexes = new Map();
|
||||||
|
var relations = new Array();
|
||||||
|
var primary = null;
|
||||||
|
for( f in matches ) {
|
||||||
|
if( field_r.match(f) ) {
|
||||||
|
var fname = field_r.matched(1);
|
||||||
|
var ftype = fromTypeDescription(field_r.matched(2));
|
||||||
|
if( ftype == null )
|
||||||
|
throw "Unknown description '"+field_r.matched(2)+"'";
|
||||||
|
fields.set(fname,ftype.t);
|
||||||
|
if( ftype.nullable )
|
||||||
|
nulls.set(fname,true);
|
||||||
|
} else if( primary_r.match (f) ) {
|
||||||
|
if( primary != null )
|
||||||
|
throw "Duplicate primary key";
|
||||||
|
primary = primary_r.matched(1).split(",");
|
||||||
|
for( i in 0...primary.length ) {
|
||||||
|
var k = unescape(primary[i]);
|
||||||
|
primary[i] = k;
|
||||||
|
}
|
||||||
|
} else if( index_r.match(f) ) {
|
||||||
|
var unique = index_r.matched(1);
|
||||||
|
var idxname = index_r.matched(2);
|
||||||
|
var fs = Lambda.list(index_r.matched(3).split(","));
|
||||||
|
indexes.set(idxname,{ keys : fs.map(function(r) {
|
||||||
|
if( !index_key_r.match(r) ) throw "Invalid index key "+r;
|
||||||
|
return index_key_r.matched(1);
|
||||||
|
}), unique : unique != "" && unique != null, name : idxname });
|
||||||
|
} else if( foreign_r.match(f) ) {
|
||||||
|
var name = foreign_r.matched(1);
|
||||||
|
var key = foreign_r.matched(2);
|
||||||
|
var table = foreign_r.matched(3);
|
||||||
|
table = table.substr(0,1).toUpperCase() + table.substr(1); // hack for MySQL on windows
|
||||||
|
var id = foreign_r.matched(4);
|
||||||
|
var setnull = if( foreign_r.matched(5) == "SET NULL" ) true else null;
|
||||||
|
relations.push({ name : name, key : key, table : table, id : id, setnull : setnull });
|
||||||
|
} else
|
||||||
|
throw "Invalid "+f+" in "+desc;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
table : tname,
|
||||||
|
fields : fields,
|
||||||
|
nulls : nulls,
|
||||||
|
indexes : indexes,
|
||||||
|
relations : relations,
|
||||||
|
primary : primary,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function sameDBStorage( dt : TableType, rt : TableType ) {
|
||||||
|
return switch( rt ) {
|
||||||
|
case DEncoded: dt == DInt;
|
||||||
|
case DFlags(fl, auto): auto ? (fl.length <= 8 ? dt == DTinyUInt : (fl.length <= 16 ? dt == DSmallUInt : (fl.length <= 24 ? dt == DMediumUInt : dt == DInt))) : (dt == DInt);
|
||||||
|
case DSerialized: (dt == DText);
|
||||||
|
case DNekoSerialized: (dt == DBinary);
|
||||||
|
case DData: dt == DBinary;
|
||||||
|
case DEnum(_): dt == DTinyUInt;
|
||||||
|
default: false;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function allTablesRequest() {
|
||||||
|
return "SHOW TABLES";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
Reference in New Issue
Block a user